Skip to content

Commit 0a86645

Browse files
authored
🐞 T1284200. PivotGrid. Select all of field chooser doesn't work with remote dataSource (DevExpress#29655)
1 parent 09f233e commit 0a86645

File tree

3 files changed

+121
-18
lines changed

3 files changed

+121
-18
lines changed

e2e/testcafe-devextreme/tests/dataGrid/common/headerFilter/headerFilter.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,36 @@ test('Data should be filtered if True is selected in the header filter when case
283283
showBorders: true,
284284
headerFilter: { visible: true },
285285
}));
286+
287+
test('[T1284200] Should handle dxList "selectAll" when has unselected items on the second page', async (t) => {
288+
const dataGrid = new DataGrid(GRID_CONTAINER);
289+
const headerCell = dataGrid.getHeaders()
290+
.getHeaderRow(0)
291+
.getHeaderCell(0);
292+
const filterIconElement = headerCell.getFilterIcon();
293+
const headerFilter = new HeaderFilter();
294+
const list = headerFilter.getList();
295+
296+
await t
297+
.click(filterIconElement)
298+
.click(list.selectAll.checkBox.element);
299+
300+
await t.expect(list.selectAll.checkBox.isChecked).ok();
301+
302+
await t.click(list.selectAll.checkBox.element);
303+
304+
await t.expect(list.selectAll.checkBox.isChecked).notOk();
305+
}).before(async () => createWidget('dxDataGrid', {
306+
dataSource: new Array(100).fill(null).map((_, idx) => ({
307+
id: idx,
308+
})),
309+
keyExpr: 'id',
310+
columns: [{
311+
dataField: 'id',
312+
filterType: 'exclude',
313+
filterValues: [70],
314+
}],
315+
headerFilter: {
316+
visible: true,
317+
},
318+
}));

e2e/testcafe-devextreme/tests/pivotGrid/headerFilter.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createScreenshotsComparer } from 'devextreme-screenshot-comparer';
22
import PivotGrid from 'devextreme-testcafe-models/pivotGrid';
3+
import HeaderFilter from 'devextreme-testcafe-models/dataGrid/headers/headerFilter';
34
import { createWidget } from '../../helpers/createWidget';
45
import url from '../../helpers/getPageUrl';
56
import { testScreenshot } from '../../helpers/themeUtils';
@@ -8,9 +9,11 @@ import { sales } from './data';
89
fixture.disablePageReloads`pivotGrid_headerFilter`
910
.page(url(__dirname, '../container.html'));
1011

12+
const PIVOT_GRID_SELECTOR = '#container';
13+
1114
test.meta({ unstable: true })('Header filter popup', async (t) => {
1215
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
13-
const pivotGrid = new PivotGrid('#container');
16+
const pivotGrid = new PivotGrid(PIVOT_GRID_SELECTOR);
1417

1518
await t.click(pivotGrid.getColumnHeaderArea().getHeaderFilterIcon());
1619

@@ -52,3 +55,40 @@ test.meta({ unstable: true })('Header filter popup', async (t) => {
5255
},
5356
});
5457
});
58+
59+
test('[T1284200] Should handle dxList "selectAll" when has unselected items on the second page', async (t) => {
60+
const pivotGrid = new PivotGrid(PIVOT_GRID_SELECTOR);
61+
62+
const filterIconElement = pivotGrid.getColumnHeaderArea().getHeaderFilterIcon();
63+
const headerFilter = new HeaderFilter();
64+
const list = headerFilter.getList();
65+
66+
await t
67+
.click(filterIconElement)
68+
.click(list.selectAll.checkBox.element);
69+
70+
await t.expect(list.selectAll.checkBox.isChecked).ok();
71+
72+
await t.click(list.selectAll.checkBox.element);
73+
74+
await t.expect(list.selectAll.checkBox.isChecked).notOk();
75+
}).before(async () => createWidget('dxPivotGrid', {
76+
dataSource: {
77+
fields: [
78+
{
79+
dataField: 'id',
80+
area: 'column',
81+
filterType: 'exclude',
82+
filterValues: [70],
83+
},
84+
],
85+
store: new Array(100).fill(null).map((_, idx) => ({
86+
id: idx,
87+
})),
88+
},
89+
allowSorting: true,
90+
allowFiltering: true,
91+
fieldPanel: {
92+
visible: true,
93+
},
94+
}));

packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter_core.ts

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import '@ts/ui/list/modules/m_selection';
44

55
import messageLocalization from '@js/common/core/localization/message';
66
import $ from '@js/core/renderer';
7+
import type { DeferredObj } from '@js/core/utils/deferred';
78
import { extend } from '@js/core/utils/extend';
89
import { each } from '@js/core/utils/iterator';
9-
import { isDefined, isFunction } from '@js/core/utils/type';
10+
import { isDeferred, isDefined, isFunction } from '@js/core/utils/type';
11+
import type dxCheckBox from '@js/ui/check_box';
12+
import type dxList from '@js/ui/list';
1013
import List from '@js/ui/list_light';
1114
import Popup from '@js/ui/popup/ui.popup';
1215
import TreeView from '@js/ui/tree_view';
@@ -28,20 +31,45 @@ function resetChildrenItemSelection(items) {
2831
}
2932
}
3033

31-
function getSelectAllCheckBox(listComponent) {
34+
function getSelectAllCheckBox(listComponent): dxCheckBox {
3235
const selector = listComponent.NAME === 'dxTreeView' ? '.dx-treeview-select-all-item' : '.dx-list-select-all-checkbox';
3336

3437
return listComponent.$element().find(selector).dxCheckBox('instance');
3538
}
3639

37-
function updateListSelectAllState(e, filterValues) {
38-
if (e.component.option('searchValue')) {
40+
function updateListSelectAllState(
41+
// NOTE: In runtime dxList's "unselectAll" returns Deferred.
42+
// But in d.ts dxList has a void return type.
43+
listComponent: Omit<dxList, 'unselectAll'> & { unselectAll: () => (DeferredObj<void> | void) },
44+
filterValues: any[],
45+
): void {
46+
if (listComponent.option('searchValue')) {
3947
return;
4048
}
41-
const selectAllCheckBox = getSelectAllCheckBox(e.component);
4249

43-
if (selectAllCheckBox && filterValues && filterValues.length) {
50+
const selectAllCheckBox = getSelectAllCheckBox(listComponent);
51+
52+
if (selectAllCheckBox && filterValues?.length) {
4453
selectAllCheckBox.option('value', undefined);
54+
55+
// NOTE: T1284200 fix
56+
// We manually set checkbox state (value) above
57+
// So, list do nothing because inner list component's "select all" state
58+
// doesn't react to our manual update.
59+
// Therefore -> we should handle first "select all" checkbox click manually.
60+
// And after it return original "onValueChanged" handler back.
61+
const originalValueChanged = selectAllCheckBox.option('onValueChanged');
62+
selectAllCheckBox.option('onValueChanged', (event) => {
63+
selectAllCheckBox.option('onValueChanged', originalValueChanged);
64+
65+
const deferred = listComponent.unselectAll();
66+
67+
if (isDeferred(deferred)) {
68+
(deferred as DeferredObj<void>).always(() => { originalValueChanged?.(event); });
69+
} else {
70+
originalValueChanged?.(event);
71+
}
72+
});
4573
}
4674
}
4775

@@ -330,11 +358,12 @@ export class HeaderFilterView extends Modules.View {
330358
showSelectionControls: true,
331359
selectionMode: needShowSelectAllCheckbox ? 'all' : 'multiple',
332360
onOptionChanged,
333-
onSelectionChanged(e) {
334-
const items = e.component.option('items');
335-
const selectedItems = e.component.option('selectedItems');
361+
onSelectionChanged(event) {
362+
const { component: listComponent } = event;
363+
const items = listComponent.option('items');
364+
const selectedItems = listComponent.option('selectedItems');
336365

337-
if (!e.component._selectedItemsUpdating && !e.component.option('searchValue') && !options.isFilterBuilder) {
366+
if (!listComponent._selectedItemsUpdating && !listComponent.option('searchValue') && !options.isFilterBuilder) {
338367
const filterValues = options.filterValues || [];
339368
const isExclude = options.filterType === 'exclude';
340369
if (selectedItems.length === 0 && items.length && (filterValues.length <= 1 || isExclude && filterValues.length === items.length - 1)) {
@@ -366,23 +395,24 @@ export class HeaderFilterView extends Modules.View {
366395
}
367396
});
368397

369-
updateListSelectAllState(e, options.filterValues);
398+
updateListSelectAllState(listComponent, options.filterValues);
370399
},
371400
onContentReady(e) {
372-
const { component } = e;
373-
const items = component.option('items');
401+
const { component: listComponent } = e;
402+
const items = listComponent.option('items');
374403
const selectedItems: any = [];
375404

376405
each(items, function () {
377406
if (this.selected) {
378407
selectedItems.push(this);
379408
}
380409
});
381-
component._selectedItemsUpdating = true;
382-
component.option('selectedItems', selectedItems);
383-
component._selectedItemsUpdating = false;
384410

385-
updateListSelectAllState(e, options.filterValues);
411+
listComponent._selectedItemsUpdating = true;
412+
listComponent.option('selectedItems', selectedItems);
413+
listComponent._selectedItemsUpdating = false;
414+
415+
updateListSelectAllState(listComponent, options.filterValues);
386416
},
387417
}),
388418
);

0 commit comments

Comments
 (0)