react-data-grid
react-data-grid copied to clipboard
Cannot tab into cell contents
Describe the bug
RDG captures Tab key presses and manually implements navigation, suppressing the default browser behavior. This is problematic because cell contents such as links or buttons are not accessible by keyboard.
To Reproduce
- Render a link in column formatter
- Notice that you cannot access the link in the grid via keyboard
Link to code example: https://codesandbox.io/s/purple-glade-ipvy62
Expected behavior
Tabbing should use the browser behavior as much as possible so that cell contents can be tabbed into normally
Environment
react-data-gridversion: 7.0.0-beta.18react/react-domversion: 18.2.0
Additional context
This is by design so the grid can control cell navigation but it allows custom formatters to define focus behavior. Here is the updated example https://codesandbox.io/s/runtime-water-noucsp?file=/src/App.js
function LinkFormatter({ isCellSelected, row }) {
const { ref, tabIndex } = useFocusRef(isCellSelected);
return (
<a ref={ref} tabIndex={tabIndex} href={row.href}>
{row.name}
</a>
);
}
@amanmahajan7 thanks for the example! I didn't know about the useFocusRef hook.
I was able to get something working -- It's a bit more complicated than the example I posted since we actually have a complex component inside the cell, so there are multiple focusable elements, and we can't modify everything since it's from a library.
My workaround was to use the useFocusRef hook as in your example and then add an onKeyDown listener to selectively prevent tab events from bubbling up to RDG:
if (ev.key === "Tab" && ref.current) {
const focusableElements = ref.current.querySelectorAll(
'a:not([disabled]), area:not([disabled]), button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex="0"]:not([disabled])'
);
const focusedElement = document.activeElement;
// If we are focused on the root and will tab out, let the event bubble up.
if (focusedElement === ref.current && (ev.shiftKey || focusableElements.length === 0)) {
return;
}
// If we are tabbing forwards and are at the last focusable child, let the event bubble up.
if (
focusableElements.length > 0 &&
focusedElement === focusableElements[focusableElements.length - 1] &&
!ev.shiftKey
) {
return;
}
// Otherwise, prevent the event from bubbling to RDG so that the browser tabbing behavior is applied.
ev.stopPropagation();
}
It's not perfect (for example, tabbing backwards into a cell then requires you to tab forwards into the contents), but it will do for now.
If you have any other ideas for how to improve this, I would love to hear them. Otherwise I think this issue can be closed!
This is the best solution as RDG cannot handle all the cases. Closing it for now .