Conversation
4932003 to
5d51e79
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR implements keyboard navigation for table cells using a roving tabindex strategy as part of issue #30. Key changes include:
- Introduction of the useFocus hook and FocusContext to manage keyboard events and focus state.
- Updates to table components (TableHeader, TableCorner, RowHeader, ColumnHeader, and Cell) to integrate focus handling.
- Integration of the FocusProvider in the HighTable component to wrap the table grid and handle keyboard events.
Reviewed Changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/useFocus.tsx | New hook to provide and manage focus state and handle keyboard events. |
| src/components/TableHeader/TableHeader.tsx | Updates to pass ariaRowIndex and compute ariaColIndex for header cells. |
| src/components/TableCorner/TableCorner.tsx | Updated to require ariaRowIndex and integrate cell focus on click. |
| src/components/RowHeader/RowHeader.tsx | Updates to pass ariaRowIndex to row headers and use cell focus. |
| src/components/HighTable/HighTable.tsx | Added FocusProvider and applied onKeyDown for keyboard navigation. |
| src/components/ColumnHeader/ColumnHeader.tsx | Integrated useCellFocus and updated aria attributes for header clarity. |
| src/components/Cell/Cell.tsx | Integrated useCellFocus and updated event handlers to trigger cell focus. |
| Tests (TableHeader and ColumnHeader) | Updated test cases to include ariaRowIndex in component props. |
Files not reviewed (1)
- src/components/HighTable/HighTable.module.css: Language not supported
Comments suppressed due to low confidence (1)
src/hooks/useFocus.tsx:115
- [nitpick] The dependency 'cell' in the useEffect hook does not affect the effect’s behavior and appears redundant. Consider removing it to simplify the dependency array.
useEffect(() => { if (ref.current && isFocused && document.activeElement !== ref.current) { ref.current.focus() } }, [ref, isFocused, ariaColIndex, ariaRowIndex, cell])
1c297f3 to
4459207
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR implements keyboard navigation for table cells using a roving tabindex approach while enhancing accessibility across the table components. Key changes include the addition of the useCellsNavigation hook to manage focus and keyboard events, updates to header and cell components to propagate aria indexes, and improved keyboard support for column resizing.
Reviewed Changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/useCellsNavigation.tsx | Introduces a context-based hook for managing cell navigation and keyboard events. |
| src/components/TableHeader/TableHeader.tsx | Updates header to include ariaRowIndex and passes correct aria properties to child components. |
| src/components/TableHeader/TableHeader.test.tsx | Updates tests to provide the necessary ariaRowIndex prop. |
| src/components/TableCorner/TableCorner.tsx | Integrates cell navigation for improved accessibility in the corner cell. |
| src/components/RowHeader/RowHeader.tsx | Applies cell navigation to row header, ensuring consistent focus handling. |
| src/components/HighTable/HighTable.tsx | Wraps table rendering in a CellsNavigationProvider and adds scrolling logic to focus cells. |
| src/components/ColumnResizer/ColumnResizer.tsx | Enhances column resizer to be keyboard accessible, with added focus management and key events. |
| src/components/ColumnHeader/ColumnHeader.tsx | Integrates useCellNavigation for accessible header cell focus and click handling. |
| src/components/ColumnHeader/ColumnHeader.test.tsx | Updates tests with default aria props for consistent behavior. |
| src/components/Cell/Cell.tsx | Augments cell component with accessible cell navigation and event handlers. |
Files not reviewed (1)
- src/components/HighTable/HighTable.module.css: Language not supported
Comments suppressed due to low confidence (1)
src/components/ColumnResizer/ColumnResizer.tsx:112
- Consider adding an accessible label (e.g., aria-label='Resize column') to the separator element to provide additional context for screen reader users.
<span role="separator" aria-busy={ariaBusy} onDoubleClick={handleDoubleClick} onMouseDown={onMouseDown} onClick={disableOnClick} onFocus={onFocus} onBlur={onBlur} onKeyDown={onKeyDown} tabIndex={tabIndex} />
There was a problem hiding this comment.
Pull Request Overview
This PR implements keyboard navigation for table cells using a roving tabindex strategy while addressing scrolling and focus issues. Key changes include:
- Integrating keyboard event handlers and navigation state via a new CellsNavigationProvider and related hooks.
- Updating multiple table components (header, cells, row/column headers, corner, resizer) to use aria attributes and keyboard callbacks.
- Adjusting tests and scroll behavior in HighTable for improved accessibility and user experience.
Reviewed Changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/useCellsNavigation.tsx | Introduces a context and hook for managing cell navigation state. |
| src/components/TableHeader/TableHeader.tsx | Adds ariaRowIndex support and updates ordering for header navigation. |
| src/components/TableCorner/TableCorner.tsx | Integrates cell navigation for table corner; now focusable via keyboard. |
| src/components/RowHeader/RowHeader.tsx | Updates to support cell navigation with aria indices. |
| src/components/HighTable/HighTable.tsx | Refactors focus and scroll logic; uses keyboard events from the context. |
| src/components/ColumnResizer/ColumnResizer.tsx | Enhances keyboard accessibility for resizing with new aria attributes. |
| src/components/ColumnHeader/ColumnHeader.tsx | Implements navigation on header click with updated aria labeling. |
| src/components/Cell/Cell.tsx | Updates cell behavior to incorporate keyboard navigation on events. |
Files not reviewed (1)
- src/components/HighTable/HighTable.module.css: Language not supported
|
Overall this is really nice. I like the behavior and the implementation. The one behavior that I think is pretty awkward is this one: When you use up/down to move outside of the viewport the table jumps half the page length. I think this would feel more natural if it worked the same way as Sheets and Excel where the selected cell stays at the top or bottom of the view when you do this. |
|
Thanks for the comments. I'll try to fix that |
|
Done, it was not too hard :) |
5dc803a to
3f294be
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR introduces keyboard navigation for table cells and headers using a roving tabindex approach, along with fixes for scrolling behavior and keyboard controls on column resizers. Key changes include:
- Implementation of a CellsNavigationContext to manage keyboard navigation state.
- Integration of keyboard event handlers across table headers, cells, and resizable columns.
- Updates to tests and accessibility attributes to support the new navigation features.
Reviewed Changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/useCellsNavigation.tsx | Added context and keyboard event handlers for roving tabindex navigation. |
| src/components/TableHeader/TableHeader.tsx | Updated to pass ariaRowIndex and compute correct aria attributes. |
| src/components/TableCorner/TableCorner.tsx | Integrated cell navigation on click using useCellsNavigation. |
| src/components/RowHeader/RowHeader.tsx | Updated to include cell navigation with proper aria properties. |
| src/components/HighTable/HighTable.tsx | Wrapped table content with the new navigation provider and adjusted scrolling. |
| src/components/ColumnResizer/ColumnResizer.tsx | Added keyboard accessibility support for resizing columns. |
| src/components/ColumnHeader/ColumnHeader.tsx | Updated to use the cell navigation hook and pass appropriate aria attributes. |
| src/components/Cell/Cell.tsx | Updated to include keyboard navigation within table cells. |
| Test files | Added ariaRowIndex to tests and updated expectations for column widths. |
Files not reviewed (1)
- src/components/HighTable/HighTable.module.css: Language not supported
Comments suppressed due to low confidence (1)
src/hooks/useCellsNavigation.tsx:80
- Consider adding explicit parentheses in the condition (e.g. (key === 'PageDown' || (key === ' ' && !event.shiftKey))) to clarify operator precedence.
else if (key === 'PageDown' || key === ' ' && !event.shiftKey ) {
platypii
left a comment
There was a problem hiding this comment.
Looking good! Played with it locally and everything seemed to work.
| return | ||
| } | ||
| if (width === undefined) { | ||
| // don't allow other keyboard events when width is not set |
There was a problem hiding this comment.
good helpful comments here 👍
| thead [role="separator"] { | ||
| position: absolute; | ||
| top: 0; | ||
| top: 1px; |
There was a problem hiding this comment.
why did this need to change?
There was a problem hiding this comment.
it was to ensure the separator is visually inside the cell header
| } | ||
| /* TODO(SL): add a global pending state? */ | ||
| /* .pending thead th::before { | ||
| animation: shimmer 2s infinite linear; |
There was a problem hiding this comment.
looks like shimmer animation is no longer referenced by anything
There was a problem hiding this comment.
yes, for a long time, it's not related to this PR. But I can look at it to add it again if needed
|
@rembrandtreyes eta on having #147 ready for merge? I feel like my comments there were mostly minor and the menu looked slick. Let me know if you want me to re-review. Not a rush I super appreciate the contribution! Trying to make life easier by not making you deal with the merge conflicts :-) @severo I'm fine with holding on keyboard navigation for a bit until that one merges. If PRs start stacking up maybe we start merging then. |
|
@platypii just wrapped up the last few PR comments I re-requested a review if you want to look it over again. 👍 |

Needed for #30
This PR adds the keyboard event handlers to navigate the table cells as expected. The actions in each cell will be implemented other PRs
The list of expected keyboard interactions are published in https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/grid_role#keyboard_interactions or https://www.w3.org/WAI/ARIA/apg/patterns/grid/#datagridsforpresentingtabularinformation
We must choose one of two options for handling the focus inside the table: "Roving tabindex" vs "aria-activedescendant". See https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#keyboardnavigationinsidecomponents.
We'll try "roving tabindex". The focus will be in one of the cells with
tabindex="0", and the other ones will havetabindex="-1"so that they can be selected with.focus()but are no part of the Tab path. An interesting implementation here: https://github.com/stevejay/react-roving-tabindex/blob/master/src/use-roving-tabindex.tsSome problems appeared:
<body>. In that case, you can't use the arrows keys anymore, while we would expect going back to the focused cell. Maybe we should separate the concepts of focused cell and selected/current cell. Fixed with 93369e6Screencast.From.2025-05-02.11-46-34.mp4
Should we consider implementing
aria-activedescendantinstead of tabindex roving, and let the table focused? From https://sarahmhigley.com/writing/activedescendant/, it seems like it's not a good idea.Screencast.From.2025-05-02.15-45-00.mp4
tabindex="0"on the resize separator as well when navigating to a column header cell, so that it's selectable with Tab, and then left and right arrow could be used to resize. It would fix the next point. Done with b384455Screencast.From.2025-05-02.18-51-15.mp4
Screencast.From.2025-05-05.12-09-55.mp4
Missing: