Skip to content

Commit de679c6

Browse files
authored
DataGrid - NVDA reads filter menu items as "Search box 1 of 8" (T1290386) (#30016)
1 parent 47805ab commit de679c6

File tree

2 files changed

+56
-14
lines changed

2 files changed

+56
-14
lines changed

e2e/testcafe-devextreme/tests/dataGrid/filterRow/filterRow.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,35 @@ test('Focus overlay should be visible in filter row when focusedRowEnabled is en
193193
}).after(async () => {
194194
await changeTheme('generic.light');
195195
});
196+
197+
test('DataGrid - NVDA reads filter menu items as "Search box 1 of 8" (T1290386)', async (t) => {
198+
const dataGrid = new DataGrid('#container');
199+
const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox);
200+
201+
await dataGrid.isReady();
202+
203+
await t
204+
.expect(filterEditor.menuButton.getAttribute('aria-label'))
205+
.eql('Search box');
206+
207+
await t
208+
.click(filterEditor.menuButton);
209+
210+
const itemCount = await filterEditor.menu.getItemCount();
211+
212+
for (let i = 0; i < itemCount; i += 1) {
213+
const item = filterEditor.menu.getItemByIndex(i);
214+
await t.expect(item.getAttribute('aria-label')).eql(null);
215+
}
216+
217+
await t
218+
.click(filterEditor.menu.getItemByText('Equals'))
219+
.expect(filterEditor.menuButton.getAttribute('aria-label'))
220+
.eql('Equals');
221+
}).before(async () => createWidget('dxDataGrid', {
222+
dataSource: getData(5, 1),
223+
keyExpr: 'field_0',
224+
filterRow: {
225+
visible: true,
226+
},
227+
}));

packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -582,21 +582,27 @@ const columnHeadersView = (Base: ModuleType<ColumnHeadersView>) => class ColumnH
582582
const editorFactoryController = this._editorFactoryController;
583583

584584
that._createComponent($menu, Menu, {
585-
// @ts-expect-error
586585
integrationOptions: {},
587586
activeStateEnabled: false,
588587
selectionMode: 'single',
589588
cssClass: `${that.getWidgetContainerClass()} ${CELL_FOCUS_DISABLED_CLASS} ${FILTER_MENU}`,
590589
showFirstSubmenuMode: 'onHover',
591590
hideSubmenuOnMouseLeave: true,
592591
items: [{
592+
// @ts-expect-error
593+
name: getColumnSelectedFilterOperation(that, column) || ARIA_SEARCH_BOX,
593594
disabled: !(column.filterOperations && column.filterOperations.length),
594595
icon: OPERATION_ICONS[getColumnSelectedFilterOperation(that, column) || 'default'],
595596
selectable: false,
596597
items: that._getFilterOperationMenuItems(column),
597598
}],
598-
onItemRendered: ({ itemElement }) => {
599-
this.setAria('label', ARIA_SEARCH_BOX, $(itemElement));
599+
onItemRendered: ({ itemElement, itemData }) => {
600+
// @ts-expect-error
601+
if (itemData?.items && itemData?.name) {
602+
// @ts-expect-error
603+
const labelText = that._getOperationDescriptionFromDescriptor(itemData.name) || ARIA_SEARCH_BOX;
604+
this.setAria('label', labelText, $(itemElement));
605+
}
600606
},
601607
onItemClick(properties) {
602608
// @ts-expect-error
@@ -690,20 +696,15 @@ const columnHeadersView = (Base: ModuleType<ColumnHeadersView>) => class ColumnH
690696
const that = this;
691697
let result = [{}];
692698
const filterRowOptions = that.option('filterRow');
693-
const operationDescriptions = filterRowOptions && filterRowOptions.operationDescriptions || {};
694699

695700
if (column.filterOperations && column.filterOperations.length) {
696701
const availableFilterOperations = column.filterOperations.filter((value) => isDefined(OPERATION_DESCRIPTORS[value]));
697-
result = map(availableFilterOperations, (value) => {
698-
const descriptionName = OPERATION_DESCRIPTORS[value];
699-
700-
return {
701-
name: value,
702-
selected: (getColumnSelectedFilterOperation(that, column) || column.defaultFilterOperation) === value,
703-
text: operationDescriptions[descriptionName],
704-
icon: OPERATION_ICONS[value],
705-
};
706-
});
702+
result = map(availableFilterOperations, (value) => ({
703+
name: value,
704+
selected: (getColumnSelectedFilterOperation(that, column) || column.defaultFilterOperation) === value,
705+
text: that._getOperationDescriptionFromDescriptor(value),
706+
icon: OPERATION_ICONS[value],
707+
}));
707708

708709
result.push({
709710
name: null,
@@ -715,6 +716,15 @@ const columnHeadersView = (Base: ModuleType<ColumnHeadersView>) => class ColumnH
715716
return result;
716717
}
717718

719+
private _getOperationDescriptionFromDescriptor(value) {
720+
const filterRowOptions = this.option('filterRow');
721+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
722+
const operationDescriptions = filterRowOptions?.operationDescriptions || {};
723+
const descriptionName = OPERATION_DESCRIPTORS[value];
724+
725+
return operationDescriptions[descriptionName];
726+
}
727+
718728
protected _handleDataChanged(e) {
719729
const dataSource = this._dataController?.dataSource?.();
720730
const lastLoadOptions = dataSource?.lastLoadOptions?.();

0 commit comments

Comments
 (0)