diff --git a/docs/grid/accessibility.md b/docs/grid/accessibility.md index afa742ac..1bad0d19 100644 --- a/docs/grid/accessibility.md +++ b/docs/grid/accessibility.md @@ -6,14 +6,14 @@ description: You can learn about accessibility and keyboard navigation in DHTMLX # Accessibility in DHTMLX Grid -DHTMLX Grid is built to be operated entirely from the keyboard and to expose its structure and state to assistive technology. WAI-ARIA semantics are part of the rendered markup, and a single, coherent focus model spans the header, body, and footer. The semantics are always present — there is **no** configuration flag to disable them. +DHTMLX Grid is built to be operated entirely from the keyboard and to expose its structure and state to assistive technology. WAI-ARIA semantics are part of the rendered markup, and a single, coherent focus model spans the header, body, and footer. The semantics are always present, and there is **no** configuration flag to disable them. ## Capabilities | Area | Support | | ---- | ------- | | Keyboard operation | Full: cell navigation, editing, sorting, range selection, tree expand/collapse, and clipboard all have keyboard equivalents | -| WAI-ARIA semantics | Built-in (`grid` / `treegrid` model), enabled always — no opt-in flag | +| WAI-ARIA semantics | Built-in (`grid` / `treegrid` model), enabled always, no opt-in flag | | Focus model | A single tab stop per zone; focus moves between header, body, and footer | | Selection model | Two modes: single-cell/row (`selection`) and spreadsheet-style range (`blockSelection`) | | High-contrast display | Light and dark high-contrast themes (`contrast-light` / `contrast-dark`) | @@ -22,9 +22,9 @@ DHTMLX Grid is built to be operated entirely from the keyboard and to expose its This documentation covers the Grid widget: -- the **data body** — cells and rows, including tree mode (`type: "tree"`) -- the **column header** — sortable headers and in-header filters -- the **footer** — summaries and footer filters +- the **data body**: cells and rows, including tree mode (`type: "tree"`) +- the **column header**: sortable headers and in-header filters +- the **footer**: summaries and footer filters - the **inline editors** - the keyboard model that connects these zones. @@ -32,7 +32,7 @@ As with any embeddable component, the accessibility of the final page also depen ## WAI-ARIA support -WAI-ARIA roles and attributes are added to the component markup automatically and are **on by default** — there is no flag to turn them off. The Grid exposes itself to assistive technology as an interactive grid (or treegrid) of rows and cells, with a separate group for the header and footer. The semantics are applied per structural part of the widget, so each part is announced with the correct role and state. +WAI-ARIA roles and attributes are added to the component markup automatically and are **on by default**. There is no flag to turn them off. The Grid exposes itself to assistive technology as an interactive grid (or treegrid) of rows and cells, with a separate group for the header and footer. The semantics are applied per structural part of the widget, so each part is announced with the correct role and state. ### Grid container @@ -42,7 +42,7 @@ The following table lists the container role and the grid-wide attributes applie | -------- | ---------------- | ------- | | `.dhx_grid` | `role="grid"` | Standard grid | | `.dhx_grid` | `role="treegrid"` | TreeGrid (`type: "tree"`) | -| `.dhx_grid` | `aria-rowcount` | Total number of data rows | +| `.dhx_grid` | `aria-rowcount` | Total number of rows, including header rows | | `.dhx_grid` | `aria-colcount` | Number of visible columns | | `.dhx_grid` | `aria-readonly` | `"true"` when the grid is not editable | | `.dhx_grid` | `aria-multiselectable` | `"true"` when multi-selection is enabled | @@ -54,7 +54,7 @@ The following table lists the roles and the position and state attributes applie | Selector | Role / attribute | Purpose | | -------- | ---------------- | ------- | | Data row | `role="row"` | A row of the grid | -| Data row | `aria-rowindex` | 1-based row position | +| Data row | `aria-rowindex` | 1-based row position within the whole grid, including header rows | | Data cell | `role="gridcell"` | A data cell | | Data cell | `aria-colindex` | 1-based column position | | Data cell | `aria-readonly` | `"true"` when the cell is not editable | @@ -96,7 +96,7 @@ Resizer grips, sort icons, sort-order counters, drag ghosts, drop indicators, an The keyboard behavior of the body depends on which selection system is enabled. The two are independent and drive different ARIA output and shortcut semantics. -### 1. Cell / row selection — selection +### 1. Cell / row selection (`selection`) A single active cell (or row) moves with the arrow keys. This populates `aria-selected` on the focused cell or row. Extending the selection with Shift is enabled only when `multiselection: true`; without it, Shift+arrow moves the active cell. @@ -119,7 +119,7 @@ const grid = new dhx.Grid("grid_container", { | `true` | Equivalent to cell selection | | *falsy / unset* | Selection (and `aria-selected`) disabled | -### 2. Range / block selection — blockSelection +### 2. Range / block selection (`blockSelection`) Spreadsheet-style rectangular ranges. The arrow keys move the range anchor; Shift+arrows grow or shrink the rectangle; Delete clears the range (when editing is enabled). This applies in **"range"** mode. @@ -144,7 +144,7 @@ Both systems coexist with the same navigation keys; the Grid responds to whichev Keyboard navigation is on by default (`keyNavigation: true`); set `keyNavigation: false` to opt out. Focus enters the Grid through hidden focus sentinels placed before the header and after the footer, which direct it into the correct zone. Within each zone a single cell is the tab stop, and the arrow keys move between cells from there. -Shortcuts are organized into **zones** — body, header, footer — and resolved by where focus currently is. The full reference is in the [Keyboard navigation](grid/configuration.md#keyboard-navigation) article; the tables below summarize it. +Shortcuts are organized into **zones** (body, header, footer) and resolved by where focus currently is. The full reference is in the [Keyboard navigation](grid/configuration.md#keyboard-navigation) article; the tables below summarize it. ### Grid body @@ -196,7 +196,7 @@ Navigation is **span-aware**: movement across merged (colspan/rowspan) header an ## Assistive technology -What the Grid exposes to assistive technology is driven entirely by the ARIA markup above. Rows and cells carry their position (`aria-rowindex` / `aria-colindex`) against the grid totals (`aria-rowcount` / `aria-colcount`), so position is announced even when rows are virtualized. Selection is exposed through `aria-selected`, editability through `aria-readonly`, sort state through `aria-sort`, and — in tree mode (`type: "tree"`) — hierarchy through `aria-level` and `aria-expanded`. +What the Grid exposes to assistive technology is driven entirely by the ARIA markup above. Rows and cells carry their position (`aria-rowindex` / `aria-colindex`) against the grid totals (`aria-rowcount` / `aria-colcount`), so position is announced even when rows are virtualized. Selection is exposed through `aria-selected`, editability through `aria-readonly`, sort state through `aria-sort`, and, in TreeGrid mode (`type: "tree"`), hierarchy through `aria-level` and `aria-expanded`. ## High contrast and focus @@ -212,7 +212,7 @@ new dhx.Grid("grid_container", { selection: "complex", multiselection: true, // Shift+Arrow multi-select keyNavigation: true, // default - sortable: true // default — keyboard sort in headers + sortable: true // default, keyboard sort in headers }); // B. Spreadsheet-style range selection @@ -230,7 +230,7 @@ new dhx.Grid("grid_container", { selection: "complex" }); -// WAI-ARIA semantics are always emitted — there is no flag to toggle them. +// WAI-ARIA semantics are always emitted; there is no flag to toggle them. ~~~ Related articles: diff --git a/docs/grid/localization.md b/docs/grid/localization.md index 5817e75e..9e38ab7d 100644 --- a/docs/grid/localization.md +++ b/docs/grid/localization.md @@ -6,10 +6,6 @@ description: You can explore how to work with Grid in the documentation of the D # Localization -:::tip pro version only -The described functionality requires PRO version of the DHTMLX Grid (or DHTMLX Suite) package. -::: - You can apply different languages to the interface of dhtmlxGrid. You just need to translate the corresponding strings for Grid labels and apply a ready locale to the component. ## Default locale @@ -22,6 +18,32 @@ const en = { groupText: "Group", dropAreaTitle: "Group by:", dropAreaPlaceholder: "Drag the column header here", + aria_sortedAscending: "Sorted by {column}, ascending", + aria_sortedDescending: "Sorted by {column}, descending", + aria_filterApplied: "{count} rows match the filter", + aria_filterCleared: "Filter cleared, {count} rows", + aria_rowsLoaded: "{count} rows loaded", + aria_valueOutOfRange: "Value must be between {min} and {max}", + aria_valueBelowMin: "Value must be greater than {min}", + aria_valueAboveMax: "Value must be less than {max}", + aria_valueClamped: "Value corrected to {value}", + aria_filter: "Filter {column}", + aria_filterByDate: "Filter by date: {column}", + aria_sortBy: "Sort by {column}", + aria_expandGroup: "Expand group", + aria_collapseGroup: "Collapse group", + aria_expandRow: "Expand row", + aria_collapseRow: "Collapse row", + aria_subRow: "Details for row {id}", + aria_editContent: "Edit content", + aria_enterGrid: "Enter grid", + aria_exitGrid: "Exit grid", + aria_dragPanel: "Drag panel", + aria_panelHeader: "Panel header", + aria_panelFooter: "Panel footer", + aria_panelContent: "Panel content", + aria_draggableRows: "Draggable rows", + aria_rowId: "Row id {id}", } ~~~ @@ -42,4 +64,126 @@ const de = { ~~~jsx dhx.i18n.setLocale("grid", de); const grid = new dhx.Grid("grid_container"); -~~~ \ No newline at end of file +~~~ + +## Announcements + +The Grid announces sorting, filtering, data loading, and editor validation to screen readers. These announcements are part of the UI content, so you translate them through the locale like any other label. + +### Sorting, filtering and loading + +| Key | Default | Announced when | +| --- | ------- | -------------- | +| `aria_sortedAscending` | `Sorted by {column}, ascending` | After sorting | +| `aria_sortedDescending` | `Sorted by {column}, descending` | After sorting | +| `aria_filterApplied` | `{count} rows match the filter` | After a header or footer filter runs | +| `aria_filterCleared` | `Filter cleared, {count} rows` | After the last filter is removed | +| `aria_rowsLoaded` | `{count} rows loaded` | After data is loaded into the grid | + +The `{column}` and `{count}` placeholders are substituted at render time. Keep them in the translated string. + +~~~jsx +dhx.i18n.setLocale("grid", { + aria_sortedAscending: "Sortiert nach {column}, aufsteigend", + aria_sortedDescending: "Sortiert nach {column}, absteigend", + aria_filterApplied: "{count} Zeilen entsprechen dem Filter", + aria_filterCleared: "Filter zurückgesetzt, {count} Zeilen", + aria_rowsLoaded: "{count} Zeilen geladen", +}); + +const grid = new dhx.Grid("grid_container", { columns, data }); +~~~ + +### Validation messages + +| Key | Default | Announced when | +| --- | ------- | -------------- | +| `aria_valueOutOfRange` | `Value must be between {min} and {max}` | Both bounds are set | +| `aria_valueBelowMin` | `Value must be greater than {min}` | Only `min` is set | +| `aria_valueAboveMax` | `Value must be less than {max}` | Only `max` is set | +| `aria_valueClamped` | `Value corrected to {value}` | The value was clamped on commit | + +The wording follows the bounds the editor actually applies: inclusive when both `min` and `max` are set, exclusive when only one of them is. + +~~~jsx +dhx.i18n.setLocale("grid", { + aria_valueOutOfRange: "Wert muss zwischen {min} und {max} liegen", + aria_valueBelowMin: "Wert muss größer als {min} sein", + aria_valueAboveMax: "Wert muss kleiner als {max} sein", + aria_valueClamped: "Wert auf {value} korrigiert", +}); + +const grid = new dhx.Grid("grid_container", { + editable: true, + columns: [{ id: "price", header: [{ text: "Price" }], type: "number", editorConfig: { min: 0, max: 100 } }], + data, +}); +~~~ + +## Accessible names + +The Grid gives its interactive controls accessible names, which screen readers read out instead of the visual icon. These names come from the locale as well. + +### Control names + +| Key | Default | Applied to | +| --- | ------- | ---------- | +| `aria_filter` | `Filter {column}` | Header and footer input and select filters | +| `aria_filterByDate` | `Filter by date: {column}` | Header and footer date filter | +| `aria_sortBy` | `Sort by {column}` | The sort trigger in a header cell | +| `aria_expandGroup` | `Expand group` | Expand toggle of a collapsed grouping row | +| `aria_collapseGroup` | `Collapse group` | Collapse toggle of an expanded grouping row | +| `aria_expandRow` | `Expand row` | Expand toggle of a tree branch or a sub-row | +| `aria_collapseRow` | `Collapse row` | Collapse toggle of a tree branch or a sub-row | +| `aria_editContent` | `Edit content` | Content wrapper of an `htmlEnable` cell | +| `aria_enterGrid` | `Enter grid` | Focus sentinel before the header | +| `aria_exitGrid` | `Exit grid` | Focus sentinel after the footer | +| `aria_dragPanel` | `Drag panel` | The drag panel region | +| `aria_panelHeader` | `Panel header` | Drag panel header | +| `aria_panelFooter` | `Panel footer` | Drag panel footer | +| `aria_panelContent` | `Panel content` | Drag panel content area | +| `aria_draggableRows` | `Draggable rows` | Drag panel row list | +| `aria_rowId` | `Row id {id}` | A single drag panel item | + +The `{column}` and `{id}` placeholders are substituted when the name is built. Keep them in the translated string. + +~~~jsx +dhx.i18n.setLocale("grid", { + aria_filter: "{column} filtern", + aria_filterByDate: "Nach Datum filtern: {column}", + aria_sortBy: "Nach {column} sortieren", + aria_expandGroup: "Gruppe aufklappen", + aria_collapseGroup: "Gruppe zuklappen", + aria_expandRow: "Zeile aufklappen", + aria_collapseRow: "Zeile zuklappen", + aria_editContent: "Inhalt bearbeiten", + aria_enterGrid: "Tabelle betreten", + aria_exitGrid: "Tabelle verlassen", +}); + +const grid = new dhx.Grid("grid_container", { columns, data }); +~~~ + +### Sub-row name + +The `aria_subRow` name applies to grids with the [`subRow`](grid/api/grid_subrow_config.md) configuration. + +| Key | Default | Applied to | +| --- | ------- | ---------- | +| `aria_subRow` | `Details for row {id}` | The sub-row region (`.dhx_grid_subrow__container`) | + +The `{id}` placeholder is the row id. + +~~~jsx +dhx.i18n.setLocale("grid", { + aria_subRow: "Details zu Zeile {id}", +}); + +const grid = new dhx.Grid("grid_container", { + columns, + data, + subRow: row => `Details: ${row.company}`, +}); +~~~ + +For additional information, refer to the [Accessibility](grid/accessibility.md) guide. diff --git a/docs/whatsnew.md b/docs/whatsnew.md index 8c1d0a73..15f8eefb 100644 --- a/docs/whatsnew.md +++ b/docs/whatsnew.md @@ -8,6 +8,38 @@ description: You can explore what's new in DHTMLX Suite and its release history Before updating DHTMLX to the latest version, please check the [Migration to Newer Versions](migration.md) guide to avoid possible breakdowns. +## Version 9.3.11 + +Released on September 14, 2026 + +### Updates + +- Grid/Message/Popup. Modal focus trapping and management: + - Implemented focus trapping for all modal windows, message dialogs, and popups compliant with **WCAG 2.4.3**. + - Opening a modal dialog moves keyboard focus to the first actionable element and keeps it within the dialog boundaries until the dialog closes. + - Closing a dialog restores focus to the previously active cell or trigger element. + +- Grid. Assistive technology support for block selection: + - The Grid exposes the block selection state to screen readers (**WCAG 1.3.1**, **WCAG 4.1.2**). + - Selected cell boundaries and multi-cell ranges reflect their `aria-selected` status in the accessibility tree, providing real-time feedback during keyboard and pointer selection. + +- Grid. Localization for ARIA labels: + - Moved all hardcoded English [accessible names](grid/localization.md#accessible-names) and labels into the locale system. + - [Screen reader announcements](grid/localization.md#announcements), button descriptions, and status labels that developers can translate via the standard localization configuration. + +- Grid. Target size improvements for interactive controls: + - Enlarged hit areas for small interactive elements, including column resize handles and removal/action icons. + - Reduced accidental or missed clicks on touch-screen devices, accommodating users with motor precision needs. + +For additional information, refer to the [Grid accessibility](grid/accessibility.md) and [Grid localization](grid/localization.md) guides. + +### Fixes + +- Grid. Fixed a console error that occurred when starting block selection in numeric columns or number-formatted cell ranges +- Grid. Fixed broken `aria-labelledby` relationships in sub-rows, eliminating orphaned ID references +- Grid. Corrected invalid ARIA attributes and roles, and fixed the `aria-rowindex` and `aria-rowcount` calculations so that they reflect the overall grid structure, including header rows +- Grid. Fixed an issue where inline cell editors lost or failed to expose their accessible names on the first paint and on subsequent data updates + ## Version 9.3.10 Released on August 19, 2026 @@ -75,27 +107,27 @@ Released on July 1, 2026 ### Updates -- Grid. Full WAI-ARIA Accessibility Support: +- Grid. Full WAI-ARIA accessibility support: - Implemented a comprehensive accessibility layer compliant with **WCAG 2.2**. - Added support for `role="grid"` and `role="treegrid"`, including proper hierarchical attributes: `aria-level`, `aria-expanded`, and `aria-rowindex/colindex`. - Added descriptive `aria-label` attributes for inline editors, filter controls (input, select, combo), and sort triggers. - Implemented **Roving Tabindex** and focus sentinels for seamless navigation between the Grid and other page elements. - Improved screen reader experience by hiding decorative elements (resizers, sort icons, drag ghosts) from the accessibility tree via `aria-hidden`. -- Grid. Advanced Keyboard Navigation: - - Zone Navigation: Introduced three navigable focus zones: **Header**, **Body**, and **Footer**. Use Up/Down arrows or Tab to transition between zones. - - Global Shortcuts: Added support for PageUp / PageDown (page-wise movement), Home / End (first/last cell in a row), and Ctrl+Home / Ctrl+End (start/end of the grid). - - Selection & Range: Added Shift + navigation keys to extend selection in both `selection` and `blockSelection` modes. - - Editing & Interaction: +- Grid. Advanced keyboard navigation: + - Zone navigation: Introduced three navigable focus zones: **Header**, **Body**, and **Footer**. Use Up/Down arrows or Tab to transition between zones. + - Global shortcuts: Added support for PageUp / PageDown (page-wise movement), Home / End (first/last cell in a row), and Ctrl+Home / Ctrl+End (start/end of the grid). + - Selection and range: Added Shift + navigation keys to extend selection in both `selection` and `blockSelection` modes. + - Editing and interaction: - Added F2 as an alternative to Enter for opening editors. - Added Space / Enter to toggle boolean (checkbox) cells without opening a text editor. - Added Delete to clear content in `blockSelection` mode (when `editable: true`). - - Header/Footer Operations: Enabled column sorting via Enter / Space and multi-sorting via Shift+Enter. Filters can now be activated and navigated via keyboard. + - Header/Footer operations: Enabled column sorting via Enter / Space and multi-sorting via Shift+Enter. Filters can now be activated and navigated via keyboard. -- Grid. TreeGrid Keyboard Patterns: +- Grid. TreeGrid keyboard patterns: - Added standard tree navigation: ArrowRight to expand branches/move to children and ArrowLeft to collapse branches/move to parents. -- Grid. Performance & Internal Optimization: +- Grid. Performance and internal optimization: - Optimized selection state tracking by switching from $O(n)$ scans to $O(1)$ lookups. - Implemented caching for derived accessibility properties to maintain high rendering speeds in large datasets. - Improved focus restoration logic, ensuring focus returns to the correct cell after closing editors or switching zones.