From 975def9c76492b649469d21f6f67f654c826fe31 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Thu, 11 Jun 2026 00:48:54 +0400 Subject: [PATCH 01/20] Toolbar: support keyboard navigation according to APG W3C --- .../Toolbar/Angular/app/app.component.html | 1 + .../Toolbar/Angular/app/app.component.ts | 5 + .../Demos/DataGrid/Toolbar/React/App.tsx | 9 +- .../Demos/DataGrid/Toolbar/ReactJs/App.js | 4 + apps/demos/Demos/DataGrid/Toolbar/Vue/App.vue | 6 + .../Demos/DataGrid/Toolbar/jQuery/index.js | 3 + ...e_mode_menu_open (material.blue.light).png | Bin 38940 -> 36300 bytes .../common/columnReordering/visual.ts | 6 + .../tests/dataGrid/common/exportButton.ts | 6 + .../groupColumnReordering.visual.ts | 18 + .../dataGrid/sticky/common/withGrouping.ts | 3 + ...eyboard navigation (fluent.blue.light).png | Bin 0 -> 4318 bytes ... after mouse click (fluent.blue.light).png | Bin 0 -> 3769 bytes ...ith disabled items (fluent.blue.light).png | Bin 0 -> 6109 bytes .../navigation/toolbar/keyboard.nonAPG.ts | 170 + .../tests/navigation/toolbar/keyboard.ts | 301 + .../tests/navigation/toolbar/overflowMenu.ts | 5 + .../scheduler/common/header/viewSwitcher.ts | 1 + .../hotkeysBehaviour/hotkeysBehaviour.ts | 4 + .../src/ui/toolbar/index.ts | 21 + .../_index.scss} | 0 .../widgets/base/dropDownMenu/_mixins.scss | 22 + .../{_toolbar.scss => toolbar/_index.scss} | 2 +- .../scss/widgets/base/toolbar/_mixins.scss | 18 + .../widgets/fluent/dropDownMenu/_index.scss | 5 + .../scss/widgets/fluent/toolbar/_index.scss | 3 + .../scss/widgets/fluent/toolbar/_mixins.scss | 1 + .../widgets/generic/dropDownMenu/_index.scss | 9 + .../widgets/generic/dropDownMenu/_sizes.scss | 1 + .../scss/widgets/generic/toolbar/_index.scss | 3 + .../scss/widgets/generic/toolbar/_mixins.scss | 1 + .../widgets/material/dropDownMenu/_index.scss | 6 + .../widgets/material/dropDownMenu/_sizes.scss | 1 + .../scss/widgets/material/toolbar/_index.scss | 2 + .../widgets/material/toolbar/_mixins.scss | 1 + packages/devextreme-vue/src/toolbar.ts | 3 + .../core/utils/m_public_component.ts | 14 + .../js/__internal/core/widget/widget.ts | 2 +- .../card/__snapshots__/card.test.tsx.snap | 8 +- .../__snapshots__/options.test.ts.snap | 14 +- .../ui/chat/message_box/chat_text_area.ts | 1 + .../js/__internal/ui/check_box/check_box.tsx | 4 +- .../__internal/ui/context_menu/menu_base.ts | 4 +- .../js/__internal/ui/drop_down_button.ts | 2 +- .../ui/drop_down_editor/drop_down_editor.ts | 16 +- .../file_manager/ui.file_manager.toolbar.ts | 3 +- .../js/__internal/ui/list/list.base.ts | 2 +- .../js/__internal/ui/overlay/overlay.ts | 2 +- .../js/__internal/ui/popup/popup.ts | 1 + .../devextreme/js/__internal/ui/switch.ts | 2 +- .../js/__internal/ui/text_box/text_box.ts | 2 +- .../js/__internal/ui/toolbar/constants.ts | 15 + .../toolbar/internal/keyboard.navigation.ts | 486 ++ .../ui/toolbar/internal/roving.utils.ts | 202 + .../ui/toolbar/internal/toolbar.menu.list.ts | 152 +- .../ui/toolbar/internal/toolbar.menu.ts | 93 +- .../ui/toolbar/strategy/toolbar.multiline.ts | 2 + .../ui/toolbar/strategy/toolbar.singleline.ts | 14 +- .../js/__internal/ui/toolbar/toolbar.base.ts | 242 +- .../js/__internal/ui/toolbar/toolbar.ts | 51 +- .../js/__internal/ui/toolbar/toolbar.utils.ts | 88 +- packages/devextreme/js/ui/toolbar.d.ts | 6 + .../DevExpress.ui.widgets/popup.tests.js | 22 + .../toolbar.buttons.material.tests.js | 4 +- .../toolbar.disabled.tests.js | 705 +-- .../toolbar.kbn.tests.js | 5515 +++++++++++++++++ .../toolbar.menu.tests.js | 67 +- .../DevExpress.ui.widgets/toolbar.tests.js | 4 +- .../DevExpress.ui/defaultOptions.tests.js | 17 + packages/devextreme/ts/dx.all.d.ts | 4 + 70 files changed, 7880 insertions(+), 527 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/navigation/toolbar/etalons/Toolbar RTL focus border visible after keyboard navigation (fluent.blue.light).png create mode 100644 e2e/testcafe-devextreme/tests/navigation/toolbar/etalons/Toolbar RTL no focus border after mouse click (fluent.blue.light).png create mode 100644 e2e/testcafe-devextreme/tests/navigation/toolbar/etalons/Toolbar focus border on item after Arrow-Home-End navigation with disabled items (fluent.blue.light).png create mode 100644 e2e/testcafe-devextreme/tests/navigation/toolbar/keyboard.nonAPG.ts create mode 100644 e2e/testcafe-devextreme/tests/navigation/toolbar/keyboard.ts rename packages/devextreme-scss/scss/widgets/base/{_dropDownMenu.scss => dropDownMenu/_index.scss} (100%) create mode 100644 packages/devextreme-scss/scss/widgets/base/dropDownMenu/_mixins.scss rename packages/devextreme-scss/scss/widgets/base/{_toolbar.scss => toolbar/_index.scss} (99%) create mode 100644 packages/devextreme-scss/scss/widgets/base/toolbar/_mixins.scss create mode 100644 packages/devextreme/js/__internal/ui/toolbar/internal/keyboard.navigation.ts create mode 100644 packages/devextreme/js/__internal/ui/toolbar/internal/roving.utils.ts create mode 100644 packages/devextreme/testing/tests/DevExpress.ui.widgets/toolbar.kbn.tests.js diff --git a/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.html b/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.html index 8b80eb690a41..12a1f4a30eaa 100644 --- a/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.html +++ b/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.html @@ -3,6 +3,7 @@ [dataSource]="orders" keyExpr="ID" [showBorders]="true" + (onToolbarPreparing)="onToolbarPreparing($event)" > diff --git a/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.ts b/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.ts index 3ff5deb9da76..666fafd4990d 100644 --- a/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.ts +++ b/apps/demos/Demos/DataGrid/Toolbar/Angular/app/app.component.ts @@ -6,6 +6,7 @@ import { DxButtonModule, } from 'devextreme-angular'; import { DxButtonTypes } from 'devextreme-angular/ui/button'; +import { DxDataGridTypes } from 'devextreme-angular/ui/data-grid'; import { query } from 'devextreme-angular/common/data'; import { DxSelectBoxModule, DxSelectBoxTypes } from 'devextreme-angular/ui/select-box'; import { Service, Order } from './app.service'; @@ -88,6 +89,10 @@ export class AppComponent { toggleExpandAll() { this.expandAll = !this.expandAll; } + + onToolbarPreparing(e: DxDataGridTypes.ToolbarPreparingEvent) { + e.toolbarOptions.allowKeyboardNavigation = false; + } } bootstrapApplication(AppComponent, { diff --git a/apps/demos/Demos/DataGrid/Toolbar/React/App.tsx b/apps/demos/Demos/DataGrid/Toolbar/React/App.tsx index 06d59d536146..fa2231de0af1 100644 --- a/apps/demos/Demos/DataGrid/Toolbar/React/App.tsx +++ b/apps/demos/Demos/DataGrid/Toolbar/React/App.tsx @@ -4,7 +4,7 @@ import SelectBox, { type SelectBoxTypes } from 'devextreme-react/select-box'; import DataGrid, { Grouping, Column, ColumnChooser, LoadPanel, Toolbar, Item, } from 'devextreme-react/data-grid'; -import type { DataGridRef } from 'devextreme-react/data-grid'; +import type { DataGridRef, DataGridTypes } from 'devextreme-react/data-grid'; import { query } from 'devextreme-react/common/data'; import { orders } from './data.ts'; @@ -54,13 +54,18 @@ const App = () => { }, }), []); + const onToolbarPreparing = useCallback((e: DataGridTypes.ToolbarPreparingEvent) => { + e.toolbarOptions.allowKeyboardNavigation = false; + }, []); + return ( + showBorders={true} + onToolbarPreparing={onToolbarPreparing}> diff --git a/apps/demos/Demos/DataGrid/Toolbar/ReactJs/App.js b/apps/demos/Demos/DataGrid/Toolbar/ReactJs/App.js index c1c8e283c46e..b4e9d57a2c28 100644 --- a/apps/demos/Demos/DataGrid/Toolbar/ReactJs/App.js +++ b/apps/demos/Demos/DataGrid/Toolbar/ReactJs/App.js @@ -58,6 +58,9 @@ const App = () => { }), [], ); + const onToolbarPreparing = useCallback((e) => { + e.toolbarOptions.allowKeyboardNavigation = false; + }, []); return ( { dataSource={orders} keyExpr="ID" showBorders={true} + onToolbarPreparing={onToolbarPreparing} > diff --git a/apps/demos/Demos/DataGrid/Toolbar/Vue/App.vue b/apps/demos/Demos/DataGrid/Toolbar/Vue/App.vue index a0faeed71130..4d425969fb64 100644 --- a/apps/demos/Demos/DataGrid/Toolbar/Vue/App.vue +++ b/apps/demos/Demos/DataGrid/Toolbar/Vue/App.vue @@ -5,6 +5,7 @@ :data-source="orders" key-expr="ID" :show-borders="true" + @toolbar-preparing="onToolbarPreparing" > @@ -86,6 +87,7 @@ import { DxToolbar, DxItem, } from 'devextreme-vue/data-grid'; +import type { DxDataGridTypes } from 'devextreme-vue/data-grid'; import { DxSelectBox, type DxSelectBoxTypes } from 'devextreme-vue/select-box'; import { query } from 'devextreme-vue/common/data'; import { orders } from './data.ts'; @@ -130,6 +132,10 @@ const refreshButtonOptions = { dataGridRef.value!.instance!.refresh(); }, }; + +const onToolbarPreparing = (e: DxDataGridTypes.ToolbarPreparingEvent) => { + e.toolbarOptions.allowKeyboardNavigation = false; +};