Skip to content

Commit

Permalink
feat: focus item when focusing menu (#464)
Browse files Browse the repository at this point in the history
* feat: focus item when focusing menu

* chore: update snapshot

* feat: focus active child

* fix: index > -1

* fix: use querySelecor instead
  • Loading branch information
MadCcc authored May 6, 2022
1 parent 51ad071 commit 4d4a6ad
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 11 deletions.
24 changes: 16 additions & 8 deletions src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import useAccessibility from './hooks/useAccessibility';
import useUUID from './hooks/useUUID';
import { PathRegisterContext, PathUserContext } from './context/PathContext';
import useKeyRecords, { OVERFLOW_KEY } from './hooks/useKeyRecords';
import { IdContext } from './context/IdContext';
import { getMenuId, IdContext } from './context/IdContext';
import PrivateContext from './context/PrivateContext';
import { useImperativeHandle } from 'react';

Expand Down Expand Up @@ -235,13 +235,6 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {

const containerRef = React.useRef<HTMLUListElement>();

useImperativeHandle(ref, () => ({
list: containerRef.current,
focus: (options?: FocusOptions) => {
containerRef.current?.focus(options);
},
}));

const uuid = useUUID(id);

const isRtl = direction === 'rtl';
Expand Down Expand Up @@ -360,6 +353,21 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
setMergedActiveKey(undefined);
});

useImperativeHandle(ref, () => ({
list: containerRef.current,
focus: options => {
const shouldFocusKey =
mergedActiveKey ?? childList.find(node => !node.props.disabled)?.key;
if (shouldFocusKey) {
containerRef.current
?.querySelector<HTMLLIElement>(
`li[data-menu-id='${getMenuId(uuid, shouldFocusKey as string)}']`,
)
?.focus?.(options);
}
},
}));

// ======================== Select ========================
// >>>>> Select keys
const [mergedSelectKeys, setMergedSelectKeys] = useMergedState(
Expand Down
4 changes: 4 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ export type SelectEventHandler = (info: SelectInfo) => void;
export type MenuClickEventHandler = (info: MenuInfo) => void;

export type MenuRef = {
/**
* Focus active child if any, or the first child which is not disabled will be focused.
* @param options
*/
focus: (options?: FocusOptions) => void;
list: HTMLUListElement;
};
18 changes: 16 additions & 2 deletions tests/Menu.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,14 +663,28 @@ describe('Menu', () => {
const menuRef = React.createRef();
const wrapper = mount(
<Menu ref={menuRef}>
<SubMenu key="bamboo" title="Disabled" disabled>
<MenuItem key="light">Disabled child</MenuItem>
</SubMenu>
<MenuItem key="light">Light</MenuItem>
</Menu>,
);
menuRef.current?.focus();

expect(document.activeElement).toBe(
wrapper.find('ul').first().getDOMNode(),
expect(document.activeElement).toBe(wrapper.find('li').last().getDOMNode());
});

it('should focus active item through ref', () => {
const menuRef = React.createRef();
const wrapper = mount(
<Menu ref={menuRef} activeKey="cat">
<MenuItem key="light">Light</MenuItem>
<MenuItem key="cat">Cat</MenuItem>
</Menu>,
);
menuRef.current?.focus();

expect(document.activeElement).toBe(wrapper.find('li').last().getDOMNode());
});
});
/* eslint-enable */
2 changes: 1 addition & 1 deletion tests/__snapshots__/SubMenu.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Array [
<div>
<div
class="rc-menu-submenu rc-menu-submenu-popup custom-className"
style="opacity: 0; pointer-events: none;"
style="opacity: 0;"
>
<ul
class="rc-menu rc-menu-sub rc-menu-vertical"
Expand Down

0 comments on commit 4d4a6ad

Please sign in to comment.