From 4c0516e8a41a233e74ea3b7943e251c433e1dbea Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Wed, 18 Feb 2026 16:28:27 +0100 Subject: [PATCH 1/2] chore: update changelog --- .../datagrid-web/CHANGELOG.md | 2 + .../e2e/DataGridSelection.spec.js | 33 ++++++- .../datagrid-web/src/Datagrid.xml | 8 ++ .../src/components/CheckboxColumnHeader.tsx | 5 + .../__tests__/CheckboxColumnHeader.spec.tsx | 99 +++++++++++++++++++ .../src/model/services/Texts.service.ts | 4 + .../datagrid-web/typings/DatagridProps.d.ts | 2 + .../datagrid-web/typings/MainGateProps.ts | 1 + 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 packages/pluggableWidgets/datagrid-web/src/components/__tests__/CheckboxColumnHeader.spec.tsx diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md index 7144838bb3..f97b25911f 100644 --- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md @@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added +- We added accessibility support for column headers when single selection is enabled, making sure the purpose of the column is announced. + - We added missing Dutch translations for Datagrid 2. - We added a new property for export to excel. The new property allows to set the cell export type and also the format for type number and date. diff --git a/packages/pluggableWidgets/datagrid-web/e2e/DataGridSelection.spec.js b/packages/pluggableWidgets/datagrid-web/e2e/DataGridSelection.spec.js index b4a093a6f0..b5ee1fd855 100644 --- a/packages/pluggableWidgets/datagrid-web/e2e/DataGridSelection.spec.js +++ b/packages/pluggableWidgets/datagrid-web/e2e/DataGridSelection.spec.js @@ -1,4 +1,4 @@ -import { test, expect } from "@playwright/test"; +import { expect, test } from "@playwright/test"; import AxeBuilder from "@axe-core/playwright"; test.afterEach("Cleanup session", async ({ page }) => { @@ -55,6 +55,37 @@ test.describe("datagrid-web selection", async () => { await expect(page).toHaveScreenshot(`datagridMultiSelectionRowClick.png`); }); + test("checks single selection accessibility with sr-only text", async ({ page }) => { + await page.goto("/p/single-selection"); + await page.waitForLoadState("networkidle"); + + const singleSelectionCheckbox = page.locator(".mx-name-dgSingleSelectionCheckbox"); + await singleSelectionCheckbox.waitFor(); + + // Verify sr-only text is present in the selection column header + const srOnlyText = singleSelectionCheckbox.locator(".widget-datagrid-col-select .sr-only"); + await expect(srOnlyText).toHaveText(/Select single row/i); + + // Verify sr-only text is not visible but accessible + await expect(srOnlyText).toBeAttached(); + const isHidden = await srOnlyText.evaluate(el => { + const style = window.getComputedStyle(el); + return ( + style.position === "absolute" && (style.width === "1px" || style.clip === "rect(0px, 0px, 0px, 0px)") + ); + }); + expect(isHidden).toBe(true); + + // Run accessibility scan + const accessibilityScanResults = await new AxeBuilder({ page }) + .withTags(["wcag21aa"]) + .include(".mx-name-dgSingleSelectionCheckbox") + .exclude(".mx-name-navigationTree3") + .analyze(); + + expect(accessibilityScanResults.violations).toEqual([]); + }); + test("checks accessibility violations", async ({ page }) => { await page.goto("/p/multi-selection"); await page.waitForLoadState("networkidle"); diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml index 6067391165..8155f6f565 100644 --- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml +++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml @@ -446,6 +446,14 @@ Selecteer alle rijen + + Select single row label + If single selection is enabled, assistive technology will read this for the selection column header. + + Select single row + Selecteer enkele rij + + Selecting all label ARIA label for the progress dialog when selecting all items diff --git a/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx b/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx index 810c64fd3e..d55d986dda 100644 --- a/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx +++ b/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx @@ -6,6 +6,8 @@ import { useDatagridConfig, useSelectActions, useSelectionHelper, useTexts } fro export function CheckboxColumnHeader(): ReactElement { const { selectAllCheckboxEnabled, checkboxColumnEnabled } = useDatagridConfig(); + const { selectSingleRowLabel } = useTexts(); + const selectionHelper = useSelectionHelper(); if (checkboxColumnEnabled === false) { return ; @@ -16,6 +18,9 @@ export function CheckboxColumnHeader(): ReactElement { + + {selectSingleRowLabel || "Select single row"} + ); } diff --git a/packages/pluggableWidgets/datagrid-web/src/components/__tests__/CheckboxColumnHeader.spec.tsx b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/CheckboxColumnHeader.spec.tsx new file mode 100644 index 0000000000..c7a030f12f --- /dev/null +++ b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/CheckboxColumnHeader.spec.tsx @@ -0,0 +1,99 @@ +import { render, screen } from "@testing-library/react"; +import { Fragment } from "react"; + +describe("CheckboxColumnHeader", () => { + it("renders sr-only text with correct class and content for single selection", () => { + render( +
+ Select single row +
+ ); + + const srOnlyText = screen.getByText("Select single row"); + expect(srOnlyText).toBeInTheDocument(); + expect(srOnlyText).toHaveClass("sr-only"); + expect(srOnlyText.parentElement).toHaveClass("widget-datagrid-col-select"); + expect(srOnlyText.parentElement).toHaveAttribute("role", "columnheader"); + }); + + it("renders sr-only text with custom label", () => { + render( +
+ Choose one row +
+ ); + + const srOnlyText = screen.getByText("Choose one row"); + expect(srOnlyText).toBeInTheDocument(); + expect(srOnlyText).toHaveClass("sr-only"); + }); + + it("renders multiple column headers with sr-only text for single selection", () => { + render( +
+
+ Select single row +
+
+ Column 1 +
+
+ ); + + const srOnlyText = screen.getByText("Select single row"); + const columnHeader = screen.getByText("Column 1"); + + expect(srOnlyText).toBeInTheDocument(); + expect(columnHeader).toBeInTheDocument(); + expect(srOnlyText).toHaveClass("sr-only"); + expect(columnHeader).not.toHaveClass("sr-only"); + }); + + it("sr-only text has sr-only class applied", () => { + render( +
+ Select single row +
+ ); + + const srOnlyText = screen.getByText("Select single row"); + + // sr-only class typically hides content visually but keeps it available to screen readers + // Check that it has sr-only class applied + expect(srOnlyText).toHaveClass("sr-only"); + }); + + it("renders empty fragment when checkbox is disabled", () => { + const { container } = render(); + + expect(container.firstChild).toBeNull(); + expect(screen.queryByText("Select single row")).not.toBeInTheDocument(); + }); + + it("does not render sr-only text for multi-selection", () => { + render( +
+ +
+ ); + + const checkbox = screen.getByRole("checkbox"); + expect(checkbox).toBeInTheDocument(); + expect(screen.queryByText("Select single row")).not.toBeInTheDocument(); + }); + + it("columnheader contains only sr-only text for single selection", () => { + const { container } = render( +
+ Select single row +
+ ); + + const columnHeader = container.querySelector('[role="columnheader"]'); + const srOnlySpans = columnHeader?.querySelectorAll(".sr-only"); + + expect(columnHeader).toBeInTheDocument(); + expect(srOnlySpans).toHaveLength(1); + expect(srOnlySpans?.[0]).toHaveTextContent("Select single row"); + }); +}); diff --git a/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts b/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts index 424ee99b2f..0935e1579e 100644 --- a/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts +++ b/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts @@ -27,6 +27,10 @@ export class TextsService { return this.props.selectAllRowsLabel?.value; } + get selectSingleRowLabel(): string | undefined { + return this.props.selectSingleRowLabel?.value; + } + get headerAriaLabel(): string | undefined { return this.props.filterSectionTitle?.value; } diff --git a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts index 46af1c62d3..343775135b 100644 --- a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts +++ b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts @@ -142,6 +142,7 @@ export interface DatagridContainerProps { cancelExportLabel?: DynamicValue; selectRowLabel?: DynamicValue; selectAllRowsLabel?: DynamicValue; + selectSingleRowLabel?: DynamicValue; selectingAllLabel?: DynamicValue; cancelSelectionLabel?: DynamicValue; selectedCountTemplateSingular?: DynamicValue; @@ -208,6 +209,7 @@ export interface DatagridPreviewProps { cancelExportLabel: string; selectRowLabel: string; selectAllRowsLabel: string; + selectSingleRowLabel: string; selectingAllLabel: string; cancelSelectionLabel: string; selectedCountTemplateSingular: string; diff --git a/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts b/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts index 94460b547b..4e05411874 100644 --- a/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts +++ b/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts @@ -36,6 +36,7 @@ export type MainGateProps = Pick< | "selectAllText" | "selectionCounterPosition" | "selectRowLabel" + | "selectSingleRowLabel" | "showNumberOfRows" | "showPagingButtons" | "storeFiltersInPersonalization" From 632051a4120ddc1fa47f88035d852c5963a9ae32 Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Thu, 19 Feb 2026 16:52:10 +0100 Subject: [PATCH 2/2] fix: changelog update --- packages/pluggableWidgets/datagrid-web/CHANGELOG.md | 6 ++++-- .../datagrid-web/src/Datagrid.editorConfig.ts | 4 ++++ packages/pluggableWidgets/datagrid-web/src/Datagrid.xml | 4 ++-- .../datagrid-web/src/components/CheckboxColumnHeader.tsx | 4 ++-- .../datagrid-web/src/model/services/Texts.service.ts | 4 ++-- .../datagrid-web/typings/DatagridProps.d.ts | 4 ++-- .../pluggableWidgets/datagrid-web/typings/MainGateProps.ts | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md index f97b25911f..541ed6fecc 100644 --- a/packages/pluggableWidgets/datagrid-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added accessibility support for column headers when single selection is enabled, making sure the purpose of the column is announced. + ## [3.8.1] - 2026-02-19 ### Fixed @@ -26,8 +30,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -- We added accessibility support for column headers when single selection is enabled, making sure the purpose of the column is announced. - - We added missing Dutch translations for Datagrid 2. - We added a new property for export to excel. The new property allows to set the cell export type and also the format for type number and date. diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts index 350d1bff18..4263688dec 100644 --- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts +++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts @@ -170,6 +170,10 @@ function hideSelectionProperties(defaultProperties: Properties, values: Datagrid "enableSelectAll" ]); } + + if (itemSelection !== "Single") { + hidePropertyIn(defaultProperties, values, "singleSelectionColumnLabel"); + } } export const getPreview = ( diff --git a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml index 8155f6f565..ca8fa535b2 100644 --- a/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml +++ b/packages/pluggableWidgets/datagrid-web/src/Datagrid.xml @@ -446,8 +446,8 @@ Selecteer alle rijen
- - Select single row label + + Single selection column label If single selection is enabled, assistive technology will read this for the selection column header. Select single row diff --git a/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx b/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx index d55d986dda..be0db25093 100644 --- a/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx +++ b/packages/pluggableWidgets/datagrid-web/src/components/CheckboxColumnHeader.tsx @@ -6,7 +6,7 @@ import { useDatagridConfig, useSelectActions, useSelectionHelper, useTexts } fro export function CheckboxColumnHeader(): ReactElement { const { selectAllCheckboxEnabled, checkboxColumnEnabled } = useDatagridConfig(); - const { selectSingleRowLabel } = useTexts(); + const { singleSelectionColumnLabel } = useTexts(); const selectionHelper = useSelectionHelper(); if (checkboxColumnEnabled === false) { @@ -19,7 +19,7 @@ export function CheckboxColumnHeader(): ReactElement { - {selectSingleRowLabel || "Select single row"} + {singleSelectionColumnLabel || "Select single row"} ); diff --git a/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts b/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts index 0935e1579e..3297e86743 100644 --- a/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts +++ b/packages/pluggableWidgets/datagrid-web/src/model/services/Texts.service.ts @@ -27,8 +27,8 @@ export class TextsService { return this.props.selectAllRowsLabel?.value; } - get selectSingleRowLabel(): string | undefined { - return this.props.selectSingleRowLabel?.value; + get singleSelectionColumnLabel(): string | undefined { + return this.props.singleSelectionColumnLabel?.value; } get headerAriaLabel(): string | undefined { diff --git a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts index 343775135b..790d4402f3 100644 --- a/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts +++ b/packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts @@ -142,7 +142,7 @@ export interface DatagridContainerProps { cancelExportLabel?: DynamicValue; selectRowLabel?: DynamicValue; selectAllRowsLabel?: DynamicValue; - selectSingleRowLabel?: DynamicValue; + singleSelectionColumnLabel?: DynamicValue; selectingAllLabel?: DynamicValue; cancelSelectionLabel?: DynamicValue; selectedCountTemplateSingular?: DynamicValue; @@ -209,7 +209,7 @@ export interface DatagridPreviewProps { cancelExportLabel: string; selectRowLabel: string; selectAllRowsLabel: string; - selectSingleRowLabel: string; + singleSelectionColumnLabel: string; selectingAllLabel: string; cancelSelectionLabel: string; selectedCountTemplateSingular: string; diff --git a/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts b/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts index 4e05411874..0f907db9a7 100644 --- a/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts +++ b/packages/pluggableWidgets/datagrid-web/typings/MainGateProps.ts @@ -36,7 +36,7 @@ export type MainGateProps = Pick< | "selectAllText" | "selectionCounterPosition" | "selectRowLabel" - | "selectSingleRowLabel" + | "singleSelectionColumnLabel" | "showNumberOfRows" | "showPagingButtons" | "storeFiltersInPersonalization"