Skip to content

fix(datatable): improve sortable header accessibility#4340

Draft
edschema wants to merge 8 commits into
openedx:release-23.xfrom
Schema-Education:edschema/datatable-sort-header-a11y
Draft

fix(datatable): improve sortable header accessibility#4340
edschema wants to merge 8 commits into
openedx:release-23.xfrom
Schema-Education:edschema/datatable-sort-header-a11y

Conversation

@edschema

@edschema edschema commented Jun 18, 2026

Copy link
Copy Markdown

Description

This PR improves DataTable sortable header accessibility without changing the public DataTable API.

It addresses three header behavior gaps:

  • Sort state: sortable headers had visual affordances, but active sort state was not exposed programmatically. Sorted headers now set aria-sort="ascending" or aria-sort="descending"; inactive/unsorted headers omit aria-sort.
  • Keyboard operation: sort behavior was attached to the <th>, so sortable headers were not reachable by Tab. Sortable headers now render a native button inside the existing <th>, with React Table sort toggle props on the button for native Tab, Enter, and Space behavior.
  • Column scope: DataTable header cells did not explicitly declare column scope. TableHeaderCell now renders scope="col".

Supporting changes:

  • Add scoped DataTable header-content, sortable-header, and sort-button classes so the new native button matches the previous header layout and hit target without Bootstrap utility classes or token overrides.
  • Add focused tests for sort state, header scope, sortable button rendering, toggle prop placement, Tab focus, Enter activation, Space activation, and real DataTable sorting through the sortable header button.

In Scope

  • Expose active sort state with aria-sort on the sorted header cell.
  • Make sortable headers keyboard reachable and operable with native header buttons.
  • Make DataTable header scope explicit with scope="col".

Out of Scope / Future Work

  • Table caption, table identity, or shared sortable-header instruction API.
  • Pre-existing React defaultProps warnings. These warnings were observed during testing, but they existed before this PR and are not related to the a11y fixes here.

Reviewer Notes / Decisions

  • aria-sort is intentionally set only on the active sorted header cell. Inactive sortable headers omit aria-sort, following W3C/APG guidance that the attribute is set on the currently sorted column and moved when sorting changes.
  • Sortable headers use a native button instead of adding keyboard handling to the <th>. This follows the APG sortable-table pattern and keeps keyboard behavior native.
  • Non-sortable headers remain not buttons.
  • scope="col" is applied to all TableHeaderCell <th> elements because this component owns DataTable column-header markup.
  • getHeaderProps() may return a React key; this PR now applies that key directly to <th> and spreads the remaining header props to avoid the React key-spread warning.

Verification

  • npm test -- --runTestsByPath src/DataTable/tests/TableHeaderCell.test.jsx --coverage=false passed.
  • npm test -- --runTestsByPath src/DataTable/tests/Table.test.jsx --coverage=false passed.
  • npm test -- --runTestsByPath src/DataTable/tests/DataTable.test.jsx --coverage=false passed.
  • npm test -- --runTestsByPath src/DataTable/tests/TableHeaderCell.test.jsx src/DataTable/tests/DataTable.test.jsx --coverage=false passed.
  • npm run lint passed.
  • git diff --check passed.
  • Local Gatsby docs page verification passed at /components/datatable/:
    • Visual: header layout, sort icons, alignment, and focus treatment remained acceptable.
    • Keyboard: Tab reached sortable header buttons; Enter and Space toggled sorting; non-sortable headers were skipped.
    • DOM: scope="col" appeared on <th>; active sorted headers exposed aria-sort; unsorted headers omitted aria-sort; sortable headers rendered button type="button".

Deploy Preview

https://deploy-preview-4340--paragon-openedx-v23.netlify.app/components/datatable/

Merge Checklist

  • If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Netlify deploy preview, if applicable.
    • No intended visual change; local docs visual check passed. Netlify deploy preview review is pending.
  • Does your change adhere to the documented style conventions?
    • Added scoped pgn__ DataTable classes under the existing .pgn__data-table styles; the new CSS does not reference primitive tokens and uses inheritance for button font, color, and text alignment.
  • Do any prop types have missing descriptions in the Props API tables in the documentation site (check deploy preview)?
    • No public props or API surface were added.
  • Were your changes tested using all available themes (see theme switcher in the header of the deploy preview, under the "Settings" icon)?
    • Local docs verification passed; all-theme Netlify preview check is pending.
  • Were your changes tested in the example app?
    • Not run. DataTable docs were used for local visual, keyboard, and DOM verification.
  • Is there adequate test coverage for your changes?
    • Focused TableHeaderCell tests were added/updated and targeted DataTable tests passed.
  • Consider whether this change needs to reviewed/QA'ed for accessibility (a11y). If so, please request an a11y review for the PR in the #wg-paragon Open edX Slack channel.
    • A11y review is recommended because this changes table header semantics and keyboard behavior.

Post-merge Checklist

  • Verify your changes were released to NPM at the expected version.
  • If you'd like, share your contribution in #show-and-tell.
  • Celebrate! Thanks for your contribution.

AI Disclosure

Codex was used to help diagnose the issue, catalog the current behavior, suggest an implementation shape, execute the fix, and generate the framework for the PR text based on Open edX guidances in the docs. Each step required manual review.

@netlify

netlify Bot commented Jun 18, 2026

Copy link
Copy Markdown

Deploy Preview for paragon-openedx-v23 ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 24e6fa4
🔍 Latest deploy log https://app.netlify.com/projects/paragon-openedx-v23/deploys/6a346c00d8b7c300088e513b
😎 Deploy Preview https://deploy-preview-4340--paragon-openedx-v23.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Jun 18, 2026
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @edschema!

This repository is currently maintained by @openedx/committers-paragon.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Update the status of your PR

Your PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate.


Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.41%. Comparing base (9340b9c) to head (24e6fa4).
⚠️ Report is 5 commits behind head on release-23.x.

Additional details and impacted files
@@              Coverage Diff              @@
##           release-23.x    #4340   +/-   ##
=============================================
  Coverage         94.40%   94.41%           
=============================================
  Files               242      242           
  Lines              4309     4316    +7     
  Branches            981      986    +5     
=============================================
+ Hits               4068     4075    +7     
  Misses              237      237           
  Partials              4        4           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

edschema and others added 8 commits June 18, 2026 18:06
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
@edschema edschema force-pushed the edschema/datatable-sort-header-a11y branch from c87ad1a to 24e6fa4 Compare June 18, 2026 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open-source-contribution PR author is not from Axim or 2U

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

2 participants