Skip to content

Commit

Permalink
refactor: rc-menu with accessibility (#385)
Browse files Browse the repository at this point in the history
* start up

* more classNames

* add openKeys

* chore: Back of onClick

* feat: Motion back

* active key support

* pass active keys

* fix: mode pass logic

* support menu group

* docs: Fix definition

* disabled should not click

* chore: Add loop check of path

* chore: Back of nest selection

* wrap item prop as warning

* feat: support onSelect & onDeselect event

* revert useless onSelect & support itemIcon

* all rtl inlineIndent

* hover event

* popup data

* back of defaultMotions

* fix: Not lose active when hover disabled region

* feat: use rc-overflow

* fix: disabled should not trigger children active

* chore: Cache inline open keys

* fix cache logic

* inline mode no need pass active on subMenu

* chore: inline handle motion logic

* fix: inline open logic

* chore: Update comment

* fix open logic

* fix icon

* update test

* test: first test case

* fix: clean up timer logic

* test: More test case

* Divider

* test: update snapshot

* test: Back of snapshot

* test: clean up

* test: fix dir

* test: Update snapshot

* test: Update snapshot

* test: back of fragment test

* test: role

* test: selection & active

* fix: open keys

* test: More test case

* feat: Native focus logic

* move to correct key

* fix aria link

* menu item select

* fix: no active for invisible content

* trigger open

* feat: Go to sub list

* support nest level

* feat: Auto focus link

* inline use special operation

* test: Update snapshot

* test: back of key code

* fix: Not focus if activeKey not changed

* fix: active logic

* chore: Use activeKey as start element instead

* comment

* test: keyboard test

* test: defaultActiveFirst

* test: more MenuItem test

* test: fix rest props

* test: menuItem test

* test: Sub menu part test

* test: ltr & rtl

* test: Back of all test case

* docs: Update docs

* test: warning test

* test: keycode coverage

* test: click disabled coverage

* test: selectable coverage

* test: full coverage

* chore: add data-*

* chore: clean up

* chore: Split context

* chore: Use record context

* render use another contexr

* rm usePathData

* fix arrow logic

* patch match support overflow

* update snapshot

* test: Fix test case

* test: Fix key test

* test: Final test case

* test: Coverage

* fix: open logci

* fix: accessibility for focus

* chore: clean up
  • Loading branch information
zombieJ authored May 2, 2021
1 parent 127e17e commit 6e0849a
Show file tree
Hide file tree
Showing 50 changed files with 3,591 additions and 4,058 deletions.
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
"trailingComma": "all",
"arrowParens": "avoid"
}
22 changes: 22 additions & 0 deletions assets/menu.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@menuPrefixCls: ~'rc-menu';

.@{menuPrefixCls} {
&:focus-visible {
box-shadow: 0 0 5px green;
}

&-horizontal {
display: flex;
flex-wrap: nowrap;
}

&-submenu {
&-hidden {
display: none;
}
}

&-overflow-item {
flex: none;
}
}
3 changes: 3 additions & 0 deletions docs/demo/debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## debug

<code src="../examples/debug.tsx">
1 change: 0 additions & 1 deletion docs/examples/antd-switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const Demo = () => {
<CommonMenu
mode="inline"
openKeys={openKeys}
collapsedWidth={80}
onOpenChange={keys => {
console.error('Open Keys Changed:', keys);
setOpenKey(keys);
Expand Down
59 changes: 50 additions & 9 deletions docs/examples/antd.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
/* eslint-disable no-console, react/require-default-props, no-param-reassign */

import React from 'react';
import Menu, { SubMenu, Item as MenuItem, Divider } from 'rc-menu';
import type { CSSMotionProps } from 'rc-motion';
import Menu, { SubMenu, Item as MenuItem, Divider, MenuProps } from '../../src';
import '../../assets/index.less';

function handleClick(info) {
console.log(`clicked ${info.key}`);
console.log(info);
}

const collapseNode = () => ({ height: 0 });
const expandNode = node => ({ height: node.scrollHeight });
const collapseNode = () => {
return { height: 0 };
};
const expandNode = node => {
return { height: node.scrollHeight };
};

const horizontalMotion: CSSMotionProps = {
motionName: 'rc-menu-open-slide-up',
motionAppear: true,
motionEnter: true,
motionLeave: true,
};

export const inlineMotion = {
const verticalMotion: CSSMotionProps = {
motionName: 'rc-menu-open-zoom',
motionAppear: true,
motionEnter: true,
motionLeave: true,
};

const inlineMotion: CSSMotionProps = {
motionName: 'rc-menu-collapse',
motionAppear: true,
onAppearStart: collapseNode,
onAppearActive: expandNode,
onEnterStart: collapseNode,
Expand All @@ -22,6 +42,12 @@ export const inlineMotion = {
onLeaveActive: collapseNode,
};

const motionMap: Record<MenuProps['mode'], CSSMotionProps> = {
horizontal: horizontalMotion,
inline: inlineMotion,
vertical: verticalMotion,
};

const nestSubMenu = (
<SubMenu
title={<span className="submenu-title-wrapper">offset sub menu 2</span>}
Expand Down Expand Up @@ -95,8 +121,21 @@ const children2 = [

const customizeIndicator = <span>Add More Items</span>;

export class CommonMenu extends React.Component {
state = {
interface CommonMenuProps extends MenuProps {
triggerSubMenuAction?: MenuProps['triggerSubMenuAction'];
updateChildrenAndOverflowedIndicator?: boolean;
}

interface CommonMenuState {
children: React.ReactNode;
overflowedIndicator: React.ReactNode;
}

export class CommonMenu extends React.Component<
CommonMenuProps,
CommonMenuState
> {
state: CommonMenuState = {
children: children1,
overflowedIndicator: undefined,
};
Expand Down Expand Up @@ -149,21 +188,23 @@ function Demo() {
<CommonMenu
mode="horizontal"
// use openTransition for antd
openAnimation="slide-up"
defaultMotions={motionMap}
/>
);

const horizontalMenu2 = (
<CommonMenu
mode="horizontal"
// use openTransition for antd
openAnimation="slide-up"
defaultMotions={motionMap}
triggerSubMenuAction="click"
updateChildrenAndOverflowedIndicator
/>
);

const verticalMenu = <CommonMenu mode="vertical" openAnimation="zoom" />;
const verticalMenu = (
<CommonMenu mode="vertical" defaultMotions={motionMap} />
);

const inlineMenu = (
<CommonMenu mode="inline" defaultOpenKeys={['1']} motion={inlineMotion} />
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/custom-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable no-console, no-param-reassign */
import * as React from 'react';
import Menu, { SubMenu, Item as MenuItem, Divider } from 'rc-menu';
import Menu, { SubMenu, Item as MenuItem, Divider } from '../../src';
import '../../assets/index.less';

const getSvgIcon = (style = {}, text) => {
const getSvgIcon = (style = {}, text?: React.ReactNode) => {
if (text) {
return <i style={style}>{text}</i>;
}
Expand Down
158 changes: 158 additions & 0 deletions docs/examples/debug.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* eslint no-console:0 */

import React from 'react';
import type { CSSMotionProps } from 'rc-motion';
import Menu, { ItemGroup as MenuItemGroup } from '../../src';
import type { MenuProps } from '../../src';
import '../../assets/index.less';
import '../../assets/menu.less';
import type { MenuInfo } from '@/interface';

const collapseNode = () => {
return { height: 0 };
};
const expandNode = node => {
return { height: node.scrollHeight };
};

const horizontalMotion: CSSMotionProps = {
motionName: 'rc-menu-open-slide-up',
motionAppear: true,
motionEnter: true,
motionLeave: true,
};

const verticalMotion: CSSMotionProps = {
motionName: 'rc-menu-open-zoom',
motionAppear: true,
motionEnter: true,
motionLeave: true,
};

const inlineMotion: CSSMotionProps = {
motionName: 'rc-menu-collapse',
motionAppear: true,
onAppearStart: collapseNode,
onAppearActive: expandNode,
onEnterStart: collapseNode,
onEnterActive: expandNode,
onLeaveStart: expandNode,
onLeaveActive: collapseNode,
};

const motionMap: Record<MenuProps['mode'], CSSMotionProps> = {
horizontal: horizontalMotion,
inline: inlineMotion,
vertical: verticalMotion,
};

export default () => {
const [mode, setMode] = React.useState<MenuProps['mode']>('horizontal');
const [narrow, setNarrow] = React.useState(false);
const [inlineCollapsed, setInlineCollapsed] = React.useState(false);
const [forceRender, setForceRender] = React.useState(false);
const [openKeys, setOpenKeys] = React.useState<string[]>([]);

const onRootClick = (info: MenuInfo) => {
console.log('Root Menu Item Click:', info);
};

const onSubMenuClick = (info: MenuInfo) => {
console.log('Sub Menu Item Click:', info);
};

const onClick = (info: MenuInfo) => {
console.log('Menu Item Click:', info);
};

return (
<>
<div>
<select value={mode} onChange={e => setMode(e.target.value as any)}>
<option value="inline">Inline</option>
<option value="vertical">Vertical</option>
<option value="horizontal">Horizontal</option>
</select>

{/* Narrow */}
<button
onClick={() => {
setNarrow(!narrow);
}}
>
Narrow: {String(narrow)}
</button>

{/* InlineCollapsed */}
<button
onClick={() => {
setInlineCollapsed(!inlineCollapsed);
}}
>
Inline Collapsed: {String(inlineCollapsed)}
</button>

{/* forceRender */}
<button
onClick={() => {
setForceRender(!forceRender);
}}
>
Force Render: {String(forceRender)}
</button>
</div>

<div style={{ width: narrow ? 350 : undefined }}>
<Menu
// direction="rtl"
forceSubMenuRender={forceRender}
mode={mode}
style={{ width: mode === 'horizontal' ? undefined : 256 }}
onClick={onRootClick}
defaultMotions={motionMap}
inlineCollapsed={inlineCollapsed}
// openKeys={openKeys}
onOpenChange={newOpenKeys => setOpenKeys(newOpenKeys)}
>
<Menu.Item key="mail">
<a href="http://www.taobao.com">Navigation One</a>
</Menu.Item>
<Menu.Item key="next" onClick={onClick}>
Next Item
</Menu.Item>
<Menu.SubMenu title="Sub Menu" key="sub" onClick={onSubMenuClick}>
<Menu.Item key="sub1" onClick={onClick}>
Sub Item 1
</Menu.Item>
<Menu.Item key="sub2">Sub Item 2</Menu.Item>

<Menu.SubMenu title="Nest Menu" key="nest">
<MenuItemGroup title="group 1" key="grp1">
<Menu.Item key="21">2</Menu.Item>
<Menu.Item key="22">3</Menu.Item>
</MenuItemGroup>
<MenuItemGroup title="group 2" key="grp2">
<Menu.Item key="31">4</Menu.Item>
<Menu.Item key="32">5</Menu.Item>
</MenuItemGroup>
</Menu.SubMenu>
</Menu.SubMenu>
<Menu.Item key="disabled" disabled>
Disabled Item
</Menu.Item>

<Menu.SubMenu
title="Disabled Sub Menu"
key="disabled-sub"
onClick={onSubMenuClick}
disabled
>
<Menu.Item key="dis-sub1" onClick={onClick}>
Disabled Sub Item 1
</Menu.Item>
</Menu.SubMenu>
</Menu>
</div>
</>
);
};
Loading

0 comments on commit 6e0849a

Please sign in to comment.