diff --git a/src/MenuItem.jsx b/src/MenuItem.jsx index 3920b7b5..19e483a6 100644 --- a/src/MenuItem.jsx +++ b/src/MenuItem.jsx @@ -5,7 +5,7 @@ import KeyCode from 'rc-util/lib/KeyCode'; import classNames from 'classnames'; import scrollIntoView from 'dom-scroll-into-view'; import { connect } from 'mini-store'; -import { noop } from './util'; +import { noop, menuAllProps } from './util'; /* eslint react/no-is-mounted:0 */ @@ -139,7 +139,7 @@ export class MenuItem extends React.Component { } render() { - const props = this.props; + const props = { ...this.props }; const className = classNames(this.getPrefixCls(), props.className, { [this.getActiveClassName()]: !props.disabled && props.active, [this.getSelectedClassName()]: props.isSelected, @@ -167,8 +167,10 @@ export class MenuItem extends React.Component { if (props.mode === 'inline') { style.paddingLeft = props.inlineIndent * props.level; } + menuAllProps.forEach(key => delete props[key]); return (
  • delete props[key]); return ( -
  • +
  • - {props.title} + {title}
  • ); diff --git a/src/SubMenu.jsx b/src/SubMenu.jsx index 9c9c6bae..951fe578 100644 --- a/src/SubMenu.jsx +++ b/src/SubMenu.jsx @@ -12,6 +12,7 @@ import { noop, loopMenuItemRecursively, getMenuIdFromSubMenuEventKey, + menuAllProps, } from './util'; let guid = 0; @@ -404,7 +405,7 @@ export class SubMenu extends React.Component { } render() { - const props = this.props; + const props = { ...this.props }; const isOpen = props.isOpen; const prefixCls = this.getPrefixCls(); const isInlineMode = props.mode === 'inline'; @@ -469,9 +470,16 @@ export class SubMenu extends React.Component { props.parentMenu.props.getPopupContainer : triggerNode => triggerNode.parentNode; const popupPlacement = popupPlacementMap[props.mode]; const popupClassName = props.mode === 'inline' ? '' : props.popupClassName; - + const { + disabled, + triggerSubMenuAction, + subMenuOpenDelay, + forceSubMenuRender, + subMenuCloseDelay, + } = props; + menuAllProps.forEach(key => delete props[key]); return ( -
  • +
  • {isInlineMode && title} {isInlineMode && children} {!isInlineMode && ( @@ -483,11 +491,11 @@ export class SubMenu extends React.Component { popupPlacement={popupPlacement} popupVisible={isOpen} popup={children} - action={props.disabled ? [] : [props.triggerSubMenuAction]} - mouseEnterDelay={props.subMenuOpenDelay} - mouseLeaveDelay={props.subMenuCloseDelay} + action={disabled ? [] : [triggerSubMenuAction]} + mouseEnterDelay={subMenuOpenDelay} + mouseLeaveDelay={subMenuCloseDelay} onPopupVisibleChange={this.onPopupVisibleChange} - forceRender={props.forceSubMenuRender} + forceRender={forceSubMenuRender} > {title} diff --git a/src/SubPopupMenu.js b/src/SubPopupMenu.js index 7bcecb5b..cd3c71f1 100644 --- a/src/SubPopupMenu.js +++ b/src/SubPopupMenu.js @@ -4,7 +4,7 @@ import { connect } from 'mini-store'; import KeyCode from 'rc-util/lib/KeyCode'; import createChainedFunction from 'rc-util/lib/createChainedFunction'; import classNames from 'classnames'; -import { getKeyFromChildrenIndex, loopMenuItem, noop } from './util'; +import { getKeyFromChildrenIndex, loopMenuItem, noop, menuAllProps } from './util'; import DOMWrap from './DOMWrap'; function allDisabled(arr) { @@ -87,7 +87,7 @@ export class SubPopupMenu extends React.Component { level: PropTypes.number, mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']), triggerSubMenuAction: PropTypes.oneOf(['click', 'hover']), - inlineIndent: PropTypes.oneOfType(PropTypes.number, PropTypes.string), + inlineIndent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), manualRef: PropTypes.func, }; @@ -270,7 +270,10 @@ export class SubPopupMenu extends React.Component { eventKey: key, active: !childProps.disabled && isActive, multiple: props.multiple, - onClick: this.onClick, + onClick: (e) => { + (childProps.onClick || noop)(e); + this.onClick(e); + }, onItemHover: this.onItemHover, openTransitionName: this.getOpenTransitionName(), openAnimation: props.openAnimation, @@ -304,7 +307,7 @@ export class SubPopupMenu extends React.Component { }; render() { - const props = this.props; + const { ...props } = this.props; this.instanceArray = []; const className = classNames( props.prefixCls, @@ -323,19 +326,21 @@ export class SubPopupMenu extends React.Component { domProps.tabIndex = '0'; domProps.onKeyDown = this.onKeyDown; } + const { prefixCls, eventKey, visible } = props; + menuAllProps.forEach(key => delete props[key]); return ( // ESLint is not smart enough to know that the type of `children` was checked. /* eslint-disable */ {React.Children.map( props.children, - (c, i) => this.renderMenuItem(c, i, props.eventKey || '0-menu-'), + (c, i) => this.renderMenuItem(c, i, eventKey || '0-menu-'), )} /*eslint-enable */ diff --git a/src/util.js b/src/util.js index 82c07f4f..d4556ed6 100644 --- a/src/util.js +++ b/src/util.js @@ -36,8 +36,8 @@ export function loopMenuItemRecursively(children, keys, ret) { if (c) { const construct = c.type; if (!construct - || - !(construct.isSubMenu || construct.isMenuItem || construct.isMenuItemGroup) + || + !(construct.isSubMenu || construct.isMenuItem || construct.isMenuItemGroup) ) { return; } @@ -49,3 +49,48 @@ export function loopMenuItemRecursively(children, keys, ret) { } }); } + +export const menuAllProps = [ + 'defaultSelectedKeys', + 'selectedKeys', + 'defaultOpenKeys', + 'openKeys', + 'mode', + 'getPopupContainer', + 'onSelect', + 'onDeselect', + 'onDestroy', + 'openTransitionName', + 'openAnimation', + 'subMenuOpenDelay', + 'subMenuCloseDelay', + 'forceSubMenuRender', + 'triggerSubMenuAction', + 'level', + 'selectable', + 'multiple', + 'onOpenChange', + 'visible', + 'focusable', + 'defaultActiveFirst', + 'prefixCls', + 'inlineIndent', + 'parentMenu', + 'title', + 'rootPrefixCls', + 'eventKey', + 'active', + 'onItemHover', + 'onTitleMouseEnter', + 'onTitleMouseLeave', + 'onTitleClick', + 'isOpen', + 'renderMenuItem', + 'manualRef', + 'subMenuKey', + 'disabled', + 'index', + 'isSelected', + 'store', + 'activeKey', +]; diff --git a/tests/MenuItem.spec.js b/tests/MenuItem.spec.js index b87064c7..1e65bc5c 100644 --- a/tests/MenuItem.spec.js +++ b/tests/MenuItem.spec.js @@ -2,7 +2,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import KeyCode from 'rc-util/lib/KeyCode'; -import Menu, { MenuItem } from '../src'; +import Menu, { MenuItem, MenuItemGroup, SubMenu } from '../src'; import { MenuItem as NakedMenuItem } from '../src/MenuItem'; @@ -81,4 +81,35 @@ describe('MenuItem', () => { expect(onMouseLeave).toHaveBeenCalledWith({ key, domEvent }); }); }); + + describe('rest props', () => { + it('can render all props to sub component', () => { + const onClick = jest.fn(); + const restProps = { + onClick, + 'data-whatever': 'whatever', + title: 'title', + className: 'className', + style: { fontSize: 20 }, + }; + const wrapper = mount( + + 1 + + 3 + + + 4 + + + ); + expect(wrapper.render()).toMatchSnapshot(); + wrapper.find('MenuItem').at(0).simulate('click'); + expect(onClick).toHaveBeenCalledTimes(1); + wrapper.find('SubMenu').at(0).simulate('click'); + expect(onClick).toHaveBeenCalledTimes(2); + wrapper.find('MenuItemGroup').at(0).simulate('click'); + expect(onClick).toHaveBeenCalledTimes(3); + }); + }); }); diff --git a/tests/__snapshots__/MenuItem.spec.js.snap b/tests/__snapshots__/MenuItem.spec.js.snap new file mode 100644 index 00000000..3fb01614 --- /dev/null +++ b/tests/__snapshots__/MenuItem.spec.js.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MenuItem rest props can render all props to sub component 1`] = ` + +`;