Skip to content

DataTable accessibility #1079

Open
benjaminLeongSK wants to merge 13 commits intomasterfrom
BL/1073
Open

DataTable accessibility #1079
benjaminLeongSK wants to merge 13 commits intomasterfrom
BL/1073

Conversation

@benjaminLeongSK
Copy link
Copy Markdown
Contributor

Type of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing apis or functionality to change)

Description of changes

  • Add aria-labels for data-table
  • Make clickable headers focusable
  • introduce scrollable and keyFieldLabel prop

Checklist

  • Looks good on mobile and tablet

  • Updated documentation

  • Screen reader annotates as per figma

  • Link to Figma

@benjaminLeongSK benjaminLeongSK requested a review from qroll April 13, 2026 02:21
Comment thread src/data-table/types.ts Outdated
Comment thread src/data-table/types.ts Outdated
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.tsx

const getHeaderCheckboxAriaLabel = (): string => {
const totalColumns = getTotalColumns();
return `Select all rows, Column 1 of ${totalColumns}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to read out the row/column info as that's typically done by the screen reader. can cmiiw if that's different on other platforms

Suggested change
return `Select all rows, Column 1 of ${totalColumns}`;
return `Select all rows`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it is working ok on macbook VO but android and windows wasnt sounding it out

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering how useful it is to read the column for a select all checkbox

Copy link
Copy Markdown
Contributor

@qroll qroll Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can drop it and defer to default screen reader behaviour. it seems alright in Talkback

Comment thread src/data-table/data-table.tsx Outdated
@qroll qroll added type: enhancement New feature or request a11y Accessibility labels Apr 13, 2026
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.tsx Outdated
Comment on lines +297 to +304
typeof label === "string"
? isSortable
? getSortButtonAriaLabel(
label,
fieldKey
)
: label
: undefined
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole logic could be moved to getSortButtonAriaLabel

also thinking that instead of checking for label string only, you could use aria-labelledby pointing to the header cell wrapper

Copy link
Copy Markdown
Contributor Author

@benjaminLeongSK benjaminLeongSK Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if i understood what you meant correctly. but wont aria-labelledby override aria-label? or you meant to use aria-labelledby instead of aria-label something like this?:

<VisuallyHidden>
                        <button
                            type="button"
                            aria-labelledby={
                                isSortable
                                    ? getSortButtonAriaLabel(label, fieldKey)
                                    : wrapperId
                            }
                            onClick={() => onHeaderClick?.(fieldKey)}
                        />
                    </VisuallyHidden>
                )}

                <HeaderCellWrapper id={wrapperId}>
                    {typeof label === "string" ? (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<HeaderCellWrapper id="headerCellWrapperId">
  {label} // <---------- original code
</HeaderCellWrapper>
<VisuallyHidden>
  <button>Sort <span aria-labelledby="headerCellWrapperId" /> by x order</button>
</VisuallyHidden>

Comment thread src/data-table/data-table.tsx
Comment thread src/data-table/data-table.tsx Outdated
Comment thread src/data-table/data-table.styles.tsx Outdated
Comment on lines +210 to +211
position: relative;
z-index: 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need these additional position styles on HeaderCell and HeaderCellWrapper?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about position relative btw?

Comment thread src/data-table/data-table.styles.tsx Outdated
const headerRef = useRef<HTMLTableSectionElement>(null);
const actionBarRef = useRef<HTMLDivElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
const internalId = useRef(id || SimpleIdGenerator.generate());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use state instead so that you don't have to access via .current each time

return `${internalId.current}-row-${rowId}-position`;
};

const getKeyColumnCellId = (rowId: string, fieldKey: string) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const getKeyColumnCellId = (rowId: string, fieldKey: string) => {
const getCellId = (rowId: string, fieldKey: string) => {

Comment on lines +440 to +442
<VisuallyHidden id={getRowPositionId(rowId)}>
{`Row ${index + 1} of ${rows?.length ?? 0}`}
</VisuallyHidden>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this too. should be handled by the screen reader

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm the screen reader wont read out the rows by default. unless we add it as part of the aria-describedBy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Accessibility type: enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants