Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions docs/grid/accessibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`) |
Expand All @@ -22,17 +22,17 @@ 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.

As with any embeddable component, the accessibility of the final page also depends on the host application (see [Host-page responsibilities](#host-page-responsibilities)).

## 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

Expand All @@ -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 |
Expand All @@ -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 |
Expand Down Expand Up @@ -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 <kbd>Shift</kbd> is enabled only when `multiselection: true`; without it, <kbd>Shift</kbd>+arrow moves the active cell.

Expand All @@ -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; <kbd>Shift</kbd>+arrows grow or shrink the rectangle; <kbd>Delete</kbd> clears the range (when editing is enabled). This applies in **"range"** mode.

Expand All @@ -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

Expand Down Expand Up @@ -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`, andin 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

Expand All @@ -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
Expand All @@ -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:
Expand Down
154 changes: 149 additions & 5 deletions docs/grid/localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}",
}
~~~

Expand All @@ -42,4 +64,126 @@ const de = {
~~~jsx
dhx.i18n.setLocale("grid", de);
const grid = new dhx.Grid("grid_container");
~~~
~~~

## 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.
Loading