diff --git a/src/accessibility/RovingTabIndex.js b/src/accessibility/RovingTabIndex.js index 8924815f23..b481f08fe2 100644 --- a/src/accessibility/RovingTabIndex.js +++ b/src/accessibility/RovingTabIndex.js @@ -129,7 +129,7 @@ const reducer = (state, action) => { } }; -export const RovingTabIndexProvider = ({children, handleHomeEnd}) => { +export const RovingTabIndexProvider = ({children, handleHomeEnd, onKeyDown}) => { const [state, dispatch] = useReducer(reducer, { activeRef: null, refs: [], @@ -137,53 +137,43 @@ export const RovingTabIndexProvider = ({children, handleHomeEnd}) => { const context = useMemo(() => ({state, dispatch}), [state]); - if (handleHomeEnd) { - return - - { children } - - ; - } - - return - { children } - ; -}; -RovingTabIndexProvider.propTypes = { - handleHomeEnd: PropTypes.bool, -}; - -// Helper to handle Home/End to jump to first/last roving-tab-index for widgets such as treeview -export const HomeEndHelper = ({children}) => { - const context = useContext(RovingTabIndexContext); - - const onKeyDown = useCallback((ev) => { - // check if we actually have any items - if (context.state.refs.length <= 0) return; - - let handled = true; - switch (ev.key) { - case Key.HOME: - // move focus to first item - context.state.refs[0].current.focus(); - break; - case Key.END: - // move focus to last item - context.state.refs[context.state.refs.length - 1].current.focus(); - break; - default: - handled = false; + const onKeyDownHandler = useCallback((ev) => { + let handled = false; + if (handleHomeEnd) { + // check if we actually have any items + switch (ev.key) { + case Key.HOME: + handled = true; + // move focus to first item + if (context.state.refs.length > 0) { + context.state.refs[0].current.focus(); + } + break; + case Key.END: + handled = true; + // move focus to last item + if (context.state.refs.length > 0) { + context.state.refs[context.state.refs.length - 1].current.focus(); + } + break; + } } if (handled) { ev.preventDefault(); ev.stopPropagation(); + } else if (onKeyDown) { + return onKeyDown(ev); } - }, [context.state]); + }, [context.state, onKeyDown, handleHomeEnd]); - return
- { children } -
; + return + { children({onKeyDownHandler}) } + ; +}; +RovingTabIndexProvider.propTypes = { + handleHomeEnd: PropTypes.bool, + onKeyDown: PropTypes.func, }; // Hook to register a roving tab index diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 2d41abf902..600b418fe0 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -142,10 +142,6 @@ export default class RoomSubList extends React.PureComponent { onHeaderKeyDown = (ev) => { switch (ev.key) { - case Key.TAB: - // Prevent LeftPanel handling Tab if focus is on the sublist header itself - ev.stopPropagation(); - break; case Key.ARROW_LEFT: // On ARROW_LEFT collapse the room sublist if (!this.state.hidden && !this.props.forceExpand) { diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js index 3b15c6ff41..91c930525d 100644 --- a/src/components/views/groups/GroupInviteTile.js +++ b/src/components/views/groups/GroupInviteTile.js @@ -128,7 +128,8 @@ export default createReactClass({ 'mx_RoomTile_badgeShown': this.state.badgeHover || isMenuDisplayed, }); - const label =
+ // XXX: this is a workaround for Firefox giving this div a tabstop :( [tabIndex] + const label =
{ groupName }
; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index bd563b2f28..ee3100b535 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -777,21 +777,22 @@ export default createReactClass({ const subListComponents = this._mapSubListProps(subLists); - const {resizeNotifier, collapsed, searchFilter, ConferenceHandler, ...props} = this.props; // eslint-disable-line + const {resizeNotifier, collapsed, searchFilter, ConferenceHandler, onKeyDown, ...props} = this.props; // eslint-disable-line return ( -
- + + {({onKeyDownHandler}) =>
{ subListComponents } - -
+
} + ); }, }); diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 3b13001225..f4f5fa10fc 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -353,7 +353,8 @@ export default createReactClass({ }); subtextLabel = subtext ? { subtext } : null; - label =
{ name }
; + // XXX: this is a workaround for Firefox giving this div a tabstop :( [tabIndex] + label =
{ name }
; } else if (this.state.hover) { const Tooltip = sdk.getComponent("elements.Tooltip"); tooltip = ; diff --git a/test/accessibility/RovingTabIndex-test.js b/test/accessibility/RovingTabIndex-test.js index 2b55d1420c..8be4a2976c 100644 --- a/test/accessibility/RovingTabIndex-test.js +++ b/test/accessibility/RovingTabIndex-test.js @@ -47,7 +47,7 @@ const button4 = ; describe("RovingTabIndex", () => { it("RovingTabIndexProvider renders children as expected", () => { const wrapper = mount( -
Test
+ {() =>
Test
}
); expect(wrapper.text()).toBe("Test"); expect(wrapper.html()).toBe('
Test
'); @@ -55,9 +55,11 @@ describe("RovingTabIndex", () => { it("RovingTabIndexProvider works as expected with useRovingTabIndex", () => { const wrapper = mount( - { button1 } - { button2 } - { button3 } + {() => + { button1 } + { button2 } + { button3 } + } ); // should begin with 0th being active @@ -80,14 +82,14 @@ describe("RovingTabIndex", () => { // update the children, it should remain on the same button wrapper.setProps({ - children: [button1, button4, button2, button3], + children: () => [button1, button4, button2, button3], }); wrapper.update(); checkTabIndexes(wrapper.find("button"), [-1, -1, 0, -1]); // update the children, remove the active button, it should move to the next one wrapper.setProps({ - children: [button1, button4, button3], + children: () => [button1, button4, button3], }); wrapper.update(); checkTabIndexes(wrapper.find("button"), [-1, -1, 0]); @@ -95,13 +97,15 @@ describe("RovingTabIndex", () => { it("RovingTabIndexProvider works as expected with RovingTabIndexWrapper", () => { const wrapper = mount( - { button1 } - { button2 } - - {({onFocus, isActive, ref}) => - - } - + {() => + { button1 } + { button2 } + + {({onFocus, isActive, ref}) => + + } + + } ); // should begin with 0th being active