Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit c0960d4

Browse files
authored
feat(filter): add collectionLazy callback to Column Filter (#495)
1 parent e049863 commit c0960d4

File tree

8 files changed

+185
-124
lines changed

8 files changed

+185
-124
lines changed

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
The [`docs`](https://github.com/ghiscoding/slickgrid-react/tree/master/docs) folder of Slickgrid-React is the one-stop-shop for all project related documentation.
44

5-
Feel free to contribution documentation fixes by editing any of the markdown files in the [`docs`](https://github.com/ghiscoding/slickgrid-react/tree/master/docs) folder.
5+
Feel free to contribute documentation fixes by editing any of the markdown files in the [`docs`](https://github.com/ghiscoding/slickgrid-react/tree/master/docs) folder.

docs/column-functionalities/editors/select-dropdown-editor.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const columnDefinitions = [
173173
```
174174
175175
### Collection Watch
176-
Sometime you wish that whenever you change your filter collection, you'd like the filter to be updated, it won't do that by default but you could use `enableCollectionWatch` for that purpose to add collection observers and re-render the Filter DOM element whenever the collection changes. Also note that using `collectionAsync` will automatically watch for changes, so there's no need to enable this flag for that particular use case.
176+
Sometime you wish that whenever you make a change in your filter collection, you'd like the filter to be updated but it won't do that by default. You could use `enableCollectionWatch` for that purpose which will add a collection observers and re-render the Filter DOM element whenever the collection changes. Also note that using `collectionAsync` will automatically watch for changes, so there's no need to enable this flag for that particular use case.
177177
178178
```typescript
179179
const columnDefinitions = [

docs/column-functionalities/filters/select-filter.md

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
- [How to add Translation](#how-to-add-translation)
77
- [How to filter empty values](#how-to-filter-empty-values)
88
- Collection Options
9-
- [Add Blank Entry](#collection-add-blank-entry)
10-
- [Add Custom Entry at Beginning/End of Collection](#collection-add-custom-entry-at-the-beginningend-of-the-collection)
11-
- [Custom Structure](#custom-structure-keylabel-pair)
12-
- [Custom Structure with Translation](#custom-structure-with-translation)
13-
- [Collection filterBy/sortBy](#collection-filterbysortby)
14-
- [Collection Label Prefix/Suffix](#collection-label-prefixsuffix)
15-
- [Collection Label Render HTML](#collection-label-render-html)
16-
- [Collection Async Load](#collection-async-load)
17-
- [Collection Watch](#collection-watch)
9+
- [Add Blank Entry](#collection-add-blank-entry)
10+
- [Add Custom Entry at Beginning/End of Collection](#collection-add-custom-entry-at-the-beginningend-of-the-collection)
11+
- [Custom Structure](#custom-structure-keylabel-pair)
12+
- [Custom Structure with Translation](#custom-structure-with-translation)
13+
- [Collection filterBy/sortBy](#collection-filterbysortby)
14+
- [Collection Label Prefix/Suffix](#collection-label-prefixsuffix)
15+
- [Collection Label Render HTML](#collection-label-render-html)
16+
- [Collection Async Load](#collection-async-load)
17+
- [Collection Lazy Load](#collection-lazy-load)
18+
- [Collection Watch](#collection-watch)
1819
- [`multiple-select.js` Options](#multiple-selectjs-options)
1920
- [Filter Options (`MultipleSelectOption` interface)](#filter-options-multipleselectoption-interface)
2021
- [Display shorter selected label text](#display-shorter-selected-label-text)
@@ -563,6 +564,24 @@ function addItem() {
563564
}
564565
```
565566

567+
### Collection Lazy Load
568+
In some cases, you might have a grid with a lot of columns and loading the collection only after opening the select dropdown (or never in some cases) might help speeding up the initial grid loading. So for that use case, defining a `collectionLazy` callback can help.
569+
570+
#### Load the collection through an Http callback
571+
572+
```ts
573+
const columnDefinitions = [
574+
{
575+
id: 'prerequisites', name: 'Prerequisites', field: 'prerequisites',
576+
filterable: true,
577+
filter: {
578+
collectionLazy: (col: Column) => this.http.fetch('api/data/pre-requisites'),
579+
model: Filters.multipleSelect,
580+
}
581+
}
582+
];
583+
```
584+
566585
### Collection Watch
567586
We can enable the collection watch via the column filter `enableCollectionWatch` flag, or if you use a `collectionAsync` then this will be enabled by default. The collection watch will basically watch for any changes applied to the collection (any mutation changes like `push`, `pop`, `unshift`, ...) and will also watch for the `filter.collection` array replace, when any changes happens then it will re-render the Select Filter with the updated collection list.
568587

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@
8181
"/src/slickgrid-react"
8282
],
8383
"dependencies": {
84-
"@slickgrid-universal/common": "~5.13.4",
85-
"@slickgrid-universal/custom-footer-component": "~5.13.4",
86-
"@slickgrid-universal/empty-warning-component": "~5.13.4",
84+
"@slickgrid-universal/common": "~5.14.0",
85+
"@slickgrid-universal/custom-footer-component": "~5.14.0",
86+
"@slickgrid-universal/empty-warning-component": "~5.14.0",
8787
"@slickgrid-universal/event-pub-sub": "~5.13.0",
88-
"@slickgrid-universal/pagination-component": "~5.13.4",
89-
"@slickgrid-universal/row-detail-view-plugin": "~5.13.4",
88+
"@slickgrid-universal/pagination-component": "~5.14.0",
89+
"@slickgrid-universal/row-detail-view-plugin": "~5.14.0",
9090
"dequal": "^2.0.3",
9191
"i18next": "^23.16.8",
9292
"sortablejs": "^1.15.6"
@@ -101,13 +101,13 @@
101101
"@formkit/tempo": "^0.1.2",
102102
"@popperjs/core": "^2.11.8",
103103
"@release-it/conventional-changelog": "^10.0.1",
104-
"@slickgrid-universal/composite-editor-component": "~5.13.4",
105-
"@slickgrid-universal/custom-tooltip-plugin": "~5.13.4",
106-
"@slickgrid-universal/excel-export": "~5.13.4",
107-
"@slickgrid-universal/graphql": "~5.13.4",
108-
"@slickgrid-universal/odata": "~5.13.4",
109-
"@slickgrid-universal/rxjs-observable": "~5.13.4",
110-
"@slickgrid-universal/text-export": "~5.13.4",
104+
"@slickgrid-universal/composite-editor-component": "~5.14.0",
105+
"@slickgrid-universal/custom-tooltip-plugin": "~5.14.0",
106+
"@slickgrid-universal/excel-export": "~5.14.0",
107+
"@slickgrid-universal/graphql": "~5.14.0",
108+
"@slickgrid-universal/odata": "~5.14.0",
109+
"@slickgrid-universal/rxjs-observable": "~5.14.0",
110+
"@slickgrid-universal/text-export": "~5.14.0",
111111
"@types/fnando__sparkline": "^0.3.7",
112112
"@types/i18next-xhr-backend": "^1.4.2",
113113
"@types/node": "^22.15.2",
@@ -155,4 +155,4 @@
155155
"resolutions": {
156156
"caniuse-lite": "1.0.30001715"
157157
}
158-
}
158+
}

src/examples/slickgrid/Example33.tsx

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import React, { useEffect, useRef, useState } from 'react';
2121
import './example33.scss';
2222

2323
const FAKE_SERVER_DELAY = 500;
24-
const NB_ITEMS = 500;
24+
const NB_ITEMS = 1000;
2525

2626
const Example33: React.FC = () => {
2727
const [columnDefinitions, setColumnDefinitions] = useState<Column[]>([]);
@@ -30,6 +30,7 @@ const Example33: React.FC = () => {
3030
const [serverWaitDelay, setServerWaitDelay] = useState<number>(FAKE_SERVER_DELAY);
3131
const [editCommandQueue] = useState<EditCommand[]>([]);
3232
const [hideSubTitle, setHideSubTitle] = useState(false);
33+
const [showLazyLoading, setShowLazyLoading] = useState(false);
3334

3435
const serverWaitDelayRef = useRef(serverWaitDelay);
3536
const reactGridRef = useRef<SlickgridReactInstance | null>(null);
@@ -240,18 +241,32 @@ const Example33: React.FC = () => {
240241
},
241242
filter: {
242243
// collectionAsync: fetch(SAMPLE_COLLECTION_DATA_URL),
243-
collectionAsync: new Promise((resolve) => {
244-
window.setTimeout(() => {
245-
resolve(Array.from(Array(dataset?.length).keys()).map(k => ({ value: k, label: `Task ${k}` })));
244+
// collectionAsync: new Promise((resolve) => {
245+
// window.setTimeout(() => {
246+
// resolve(Array.from(Array(dataset.value?.length).keys()).map((k) => ({ value: k, label: `Task ${k}` })));
247+
// });
248+
// }),
249+
collectionLazy: () => {
250+
setShowLazyLoading(true);
251+
252+
return new Promise((resolve) => {
253+
window.setTimeout(() => {
254+
setShowLazyLoading(false);
255+
resolve(Array.from(Array((dataset || []).length).keys()).map((k) => ({ value: k, label: `Task ${k}` })));
256+
}, serverWaitDelayRef.current);
246257
});
247-
}),
258+
},
259+
// onInstantiated: (msSelect) => console.log('ms-select instance', msSelect),
248260
customStructure: {
249261
label: 'label',
250262
value: 'value',
251263
labelPrefix: 'prefix',
252264
},
253265
collectionOptions: {
254-
separatorBetweenTextLabels: ' '
266+
separatorBetweenTextLabels: ' ',
267+
},
268+
filterOptions: {
269+
minHeight: 70,
255270
},
256271
model: Filters.multipleSelect,
257272
operator: OperatorType.inContains,
@@ -519,13 +534,17 @@ const Example33: React.FC = () => {
519534
</ul>
520535
</div>
521536

522-
523-
<div style={{ marginBottom: '20px' }}>
524-
<label htmlFor="pinned-rows">Simulated Server Delay (ms): </label>
525-
<input type="number" id="server-delay" data-test="server-delay" style={{ width: '60px' }}
526-
value={serverWaitDelay}
527-
onInput={($event) => handleServerDelayInputChange($event)}
528-
/>
537+
<div className="row">
538+
<div className="col" style={{ marginBottom: '20px' }}>
539+
<label htmlFor="pinned-rows">Simulated Server Delay (ms): </label>
540+
<input type="number" id="server-delay" data-test="server-delay" style={{ width: '60px' }}
541+
value={serverWaitDelay}
542+
onInput={($event) => handleServerDelayInputChange($event)}
543+
/>
544+
</div>
545+
<div className={`alert alert-info is-narrow col ${!showLazyLoading ? 'invisible' : ''}`} data-test="alert-lazy">
546+
Lazy loading collection...
547+
</div>
529548
</div>
530549

531550
<div id="smaller-container" style={{ width: '950px' }}>

src/styles.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ $primary-color: #0e6cfa;
4343
display: none;
4444
}
4545

46+
.invisible {
47+
opacity: 0;
48+
}
49+
4650
.btn-icon {
4751
display: inline-flex;
4852
align-items: center;

test/cypress/e2e/example33.cy.ts

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
describe('Example 33 - Regular & Custom Tooltips', () => {
2-
const titles = ['', 'Title', 'Duration', 'Description', 'Description 2', 'Cost', '% Complete', 'Start', 'Finish', 'Effort Driven', 'Prerequisites', 'Action'];
2+
const titles = [
3+
'',
4+
'Title',
5+
'Duration',
6+
'Description',
7+
'Description 2',
8+
'Cost',
9+
'% Complete',
10+
'Start',
11+
'Finish',
12+
'Effort Driven',
13+
'Prerequisites',
14+
'Action',
15+
];
316
const GRID_ROW_HEIGHT = 33;
417

518
it('should display Example title', () => {
@@ -15,7 +28,7 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
1528
});
1629

1730
it('should change server delay to 10ms for faster testing', () => {
18-
cy.get('[data-test="server-delay"]').type('{backspace}{backspace}{backspace}10');
31+
cy.get('[data-test="server-delay"]').clear().type('50');
1932
});
2033

2134
it('should mouse over 1st row checkbox column and NOT expect any tooltip to show since it is disabled on that column', () => {
@@ -98,8 +111,11 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
98111
cy.get('@desc6-cell').trigger('mouseover');
99112

100113
cy.get('.slick-custom-tooltip').should('be.visible');
101-
cy.get('.slick-custom-tooltip').should('not.contain', `regular tooltip (from title attribute)\nTask 6 cell value:\n\nThis is a sample task description.\nIt can be multiline\n\nAnother line...`);
102-
cy.get('.slick-custom-tooltip').should('contain', `This is a sample task description.\nIt can be multiline\n\nAnother line...`);
114+
cy.get('.slick-custom-tooltip').should(
115+
'not.contain',
116+
'regular tooltip (from title attribute)\nTask 6 cell value:\n\nThis is a sample task description.\nIt can be multiline\n\nAnother line...'
117+
);
118+
cy.get('.slick-custom-tooltip').should('contain', 'This is a sample task description.\nIt can be multiline\n\nAnother line...');
103119

104120
cy.get('@desc6-cell').trigger('mouseout');
105121
});
@@ -110,7 +126,10 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
110126
cy.get('@desc2-5-cell').trigger('mouseover');
111127

112128
cy.get('.slick-custom-tooltip').should('be.visible');
113-
cy.get('.slick-custom-tooltip').should('contain', `regular tooltip (from title attribute)\nTask 6 cell value:\n\nThis is a sample task description.\nIt can be multiline\n\nAnother line...`);
129+
cy.get('.slick-custom-tooltip').should(
130+
'contain',
131+
'regular tooltip (from title attribute)\nTask 6 cell value:\n\nThis is a sample task description.\nIt can be multiline\n\nAnother line...'
132+
);
114133

115134
cy.get('@desc2-5-cell').trigger('mouseout');
116135
});
@@ -189,27 +208,38 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
189208
cy.get('@finish-filter').trigger('mouseout');
190209
});
191210

211+
it('should open PreRequisite dropdown and expect it be lazily loaded', () => {
212+
cy.get('.slick-headerrow-columns .slick-headerrow-column:nth(10)').as('checkbox10-header');
213+
cy.get('@checkbox10-header').click();
214+
cy.get('[data-test="alert-lazy"]').should('be.visible');
215+
cy.wait(50);
216+
cy.get('@checkbox10-header').click();
217+
cy.get('[data-test="alert-lazy"]').should('not.be.visible');
218+
});
219+
192220
it('should mouse over header-row (filter) Prerequisite column and expect to see tooltip of selected filter options', () => {
193-
cy.get(`.slick-headerrow-columns .slick-headerrow-column:nth(10)`).as('checkbox10-header');
221+
cy.get('.slick-headerrow-columns .slick-headerrow-column:nth(10)').as('checkbox10-header');
194222
cy.get('@checkbox10-header').trigger('mouseover');
195223

196-
cy.get('.filter-prerequisites .ms-choice span').contains('15 of 500 selected');
224+
cy.get('.filter-prerequisites .ms-choice span').contains('15 of 1000 selected');
197225
cy.get('.slick-custom-tooltip').should('be.visible');
198-
cy.get('.slick-custom-tooltip').contains('Task 1, Task 3, Task 5, Task 7, Task 9, Task 12, Task 15, Task 18, Task 21, Task 25, Task 28, Task 29, Task 30, Task 32, Task 34');
226+
cy.get('.slick-custom-tooltip').contains(
227+
'Task 1, Task 3, Task 5, Task 7, Task 9, Task 12, Task 15, Task 18, Task 21, Task 25, Task 28, Task 29, Task 30, Task 32, Task 34'
228+
);
199229

200230
cy.get('@checkbox10-header').trigger('mouseout');
201231
});
202232

203233
it('should mouse over header title on 1st column with checkbox and NOT expect any tooltip to show since it is disabled on that column', () => {
204-
cy.get(`.slick-header-columns .slick-header-column:nth(0)`).as('checkbox-header');
234+
cy.get('.slick-header-columns .slick-header-column:nth(0)').as('checkbox-header');
205235
cy.get('@checkbox-header').trigger('mouseover');
206236

207237
cy.get('.slick-custom-tooltip').should('not.exist');
208238
cy.get('@checkbox-header').trigger('mouseout');
209239
});
210240

211241
it('should mouse over header title on 2nd column with Title name and expect a tooltip to show rendered from an headerFormatter', () => {
212-
cy.get(`.slick-header-columns .slick-header-column:nth(1)`).as('checkbox0-header');
242+
cy.get('.slick-header-columns .slick-header-column:nth(1)').as('checkbox0-header');
213243
cy.get('@checkbox0-header').trigger('mouseover');
214244

215245
cy.get('.slick-custom-tooltip').should('be.visible');
@@ -222,7 +252,7 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
222252
});
223253

224254
it('should mouse over header title on 2nd column with Finish name and NOT expect any tooltip to show since it is disabled on that column', () => {
225-
cy.get(`.slick-header-columns .slick-header-column:nth(8)`).as('finish-header');
255+
cy.get('.slick-header-columns .slick-header-column:nth(8)').as('finish-header');
226256
cy.get('@finish-header').trigger('mouseover');
227257

228258
cy.get('.slick-custom-tooltip').should('not.exist');
@@ -231,27 +261,16 @@ describe('Example 33 - Regular & Custom Tooltips', () => {
231261

232262
it('should click Prerequisite editor of 1st row (Task 2) and expect Task1 & 2 to be selected in the multiple-select drop', () => {
233263
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(10)`).as('prereq-cell');
234-
cy.get('@prereq-cell')
235-
.should('contain', 'Task 2, Task 1')
236-
.click();
264+
cy.get('@prereq-cell').should('contain', 'Task 2, Task 1').click();
237265

238-
cy.get('div.ms-drop[data-name=editor-prerequisites]')
239-
.find('li.selected')
240-
.should('have.length', 2);
266+
cy.get('div.ms-drop[data-name=editor-prerequisites]').find('li.selected').should('have.length', 2);
241267

242-
cy.get('div.ms-drop[data-name=editor-prerequisites]')
243-
.find('li.selected:nth(0) span')
244-
.should('contain', 'Task 1');
268+
cy.get('div.ms-drop[data-name=editor-prerequisites]').find('li.selected:nth(0) span').should('contain', 'Task 1');
245269

246-
cy.get('div.ms-drop[data-name=editor-prerequisites]')
247-
.find('li.selected:nth(1) span')
248-
.should('contain', 'Task 2');
270+
cy.get('div.ms-drop[data-name=editor-prerequisites]').find('li.selected:nth(1) span').should('contain', 'Task 2');
249271

250-
cy.get('div.ms-drop[data-name=editor-prerequisites]')
251-
.find('.ms-ok-button')
252-
.click();
272+
cy.get('div.ms-drop[data-name=editor-prerequisites]').find('.ms-ok-button').click();
253273

254-
cy.get('div.ms-drop[data-name=editor-prerequisites]')
255-
.should('not.exist');
274+
cy.get('div.ms-drop[data-name=editor-prerequisites]').should('not.exist');
256275
});
257276
});

0 commit comments

Comments
 (0)