From 7b588a607b55a567bd5a26be63315c43069b4713 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Fri, 26 Sep 2025 19:08:25 +0200 Subject: [PATCH] refactor(DataTable): enhance accessibility features and improve localization for selection and loading states --- .../src/components/DataTable/DataTable.tsx | 90 +++++++++++++++---- .../client/src/locales/en/translation.json | 7 +- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/packages/client/src/components/DataTable/DataTable.tsx b/packages/client/src/components/DataTable/DataTable.tsx index de7bca3d2..8f021c2ad 100644 --- a/packages/client/src/components/DataTable/DataTable.tsx +++ b/packages/client/src/components/DataTable/DataTable.tsx @@ -135,7 +135,12 @@ function DataTable, TValue>({ const selectColumn: ColumnDef, TValue> = { id: 'select', header: ({ table }) => ( -
+
table.toggleAllRowsSelected(value)} @@ -143,15 +148,25 @@ function DataTable, TValue>({ />
), - cell: ({ row }) => ( -
- row.toggleSelected(value)} - ariaLabel={`Select row ${row.index + 1}`} - /> -
- ), + cell: ({ row }) => { + const rowDescription = row.original.name + ? `named ${row.original.name}` + : `at position ${row.index + 1}`; + return ( +
+ row.toggleSelected(value)} + ariaLabel={localize(`com_ui_select_row`, { rowDescription })} + /> +
+ ); + }, meta: { className: 'w-12', }, @@ -292,6 +307,8 @@ function DataTable, TValue>({ 'h-[calc(100vh-8rem)] max-h-[80vh]', className, )} + role="region" + aria-label={localize('com_ui_data_table')} >
{shouldShowSearch && } @@ -312,8 +329,11 @@ function DataTable, TValue>({ overscrollBehavior: 'contain', } as React.CSSProperties } + role="region" + aria-label={localize('com_ui_data_table_scroll_area')} + aria-describedby={showSkeletons ? 'loading-status' : undefined} > - +
{headerGroups.map((headerGroup) => ( @@ -328,24 +348,47 @@ function DataTable, TValue>({ const isSelectHeader = header.id === 'select'; const meta = header.column.columnDef.meta as { className?: string } | undefined; + const canSort = header.column.getCanSort(); + const sortAriaLabel = canSort + ? `${header.column.columnDef.header} column, ${header.column.getIsSorted() === 'asc' ? 'ascending' : header.column.getIsSorted() === 'desc' ? 'descending' : 'sortable'}` + : undefined; + + const handleSortingKeyDown = (e: React.KeyboardEvent) => { + if (canSort && (e.key === 'Enter' || e.key === ' ')) { + e.preventDefault(); + header.column.toggleSorting(); + } + }; + return ( {isSelectHeader ? ( flexRender(header.column.columnDef.header, header.getContext()) ) : (
{flexRender(header.column.columnDef.header, header.getContext())} - {header.column.getCanSort() && ( - + {canSort && ( +
{!isLoading && !showSkeletons && rows.length === 0 && ( -
+
)} diff --git a/packages/client/src/locales/en/translation.json b/packages/client/src/locales/en/translation.json index b31ae2ed1..17305d7cb 100644 --- a/packages/client/src/locales/en/translation.json +++ b/packages/client/src/locales/en/translation.json @@ -4,7 +4,6 @@ "com_ui_no_results_found": "No results found", "com_ui_no_data_available": "No data available", "com_ui_select_all": "Select All", - "com_ui_select_row": "Select Row", "com_ui_no_selection": "No selection", "com_ui_confirm_bulk_delete": "Are you sure you want to delete the selected items? This action cannot be undone.", "com_ui_delete_success": "Items deleted successfully", @@ -15,5 +14,9 @@ "com_ui_delete_selected": "Delete Selected", "com_ui_search_table": "Search table", "com_ui_search_table_description": "Type to filter results", - "com_ui_search": "Search" + "com_ui_search": "Search", + "com_ui_data_table_scroll_area": "Scrollable data table area", + "com_ui_select_row": "Select Row {{0}}", + "com_ui_loading_more_data": "Loading more data...", + "com_ui_no_search_results": "No search results found" }