fix: menu parentMenu error

pull/2682/head
tanjinzhou 2020-07-30 15:41:54 +08:00
parent d6a7c041b5
commit 60541d7d1b
12 changed files with 78 additions and 63 deletions

@ -1 +1 @@
Subproject commit e7feb6f5127f054c5ddac633b733369e182ab6f2
Subproject commit a234ba5ccfe4ab14f1619ddc156aef5bf05a7f18

View File

@ -1,3 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DropdownButton should support href like Button 1`] = `<div class="ant-btn-group ant-dropdown-button"><a href="https://ant.design" class="ant-btn ant-btn-default"></a><button type="button" class="ant-btn ant-btn-default ant-dropdown-trigger"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg viewBox="64 64 896 896" focusable="false" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></button></div>`;
exports[`DropdownButton should support href like Button 1`] = `
<div class="ant-btn-group ant-dropdown-button"><a href="https://ant.design" class="ant-btn ant-btn-default">
<!----></a>
<!----><button class="ant-dropdown-trigger ant-btn ant-btn-default" type="button">
<!----><span class="anticon anticon-ellipsis" role="img" aria-label="ellipsis"><svg class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896" focusable="false"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></button></div>
`;

View File

@ -11,36 +11,30 @@ describe('DropdownButton', () => {
disabled: false,
trigger: ['hover'],
visible: true,
onVisibleChange: () => {},
};
const wrapper = mount(Dropdown.Button, {
props,
});
const dropdownProps = wrapper.find({ name: 'ADropdown' }).props();
const dropdownProps = wrapper.findComponent({ name: 'ADropdown' }).props();
Object.keys(props).forEach(key => {
expect(dropdownProps[key]).toBe(props[key]);
expect(dropdownProps[key]).toStrictEqual(props[key]);
});
});
it("don't pass visible to Dropdown if it's not exits", () => {
const wrapper = mount({
render() {
return (
<Dropdown.Button
overlay={
<Menu>
<Menu.Item>foo</Menu.Item>
</Menu>
}
/>
);
const wrapper = mount(Dropdown.Button, {
props: {
overlay: (
<Menu>
<Menu.Item>foo</Menu.Item>
</Menu>
),
},
});
const dropdownProps = wrapper.find({ name: 'ADropdown' }).props();
const dropdownProps = wrapper.findComponent({ name: 'ADropdown' }).props();
expect('visible' in dropdownProps).toBe(false);
expect(dropdownProps.visible).toBe(undefined);
});
it('should support href like Button', () => {

View File

@ -1,5 +1,6 @@
import { provide, inject } from 'vue';
import Button from '../button';
import classNames from 'classnames';
import buttonTypes from '../button/buttonTypes';
import { ButtonGroupProps } from '../button/button-group';
import Dropdown from './dropdown';
@ -24,10 +25,14 @@ const DropdownButtonProps = {
placement: DropdownProps.placement.def('bottomRight'),
icon: PropTypes.any,
title: PropTypes.string,
onClick: PropTypes.func,
onVisibleChange: PropTypes.func,
'onUpdate:visible': PropTypes.func,
};
export { DropdownButtonProps };
export default {
name: 'ADropdownButton',
inheritAttrs: false,
props: DropdownButtonProps,
setup() {
return {
@ -41,10 +46,10 @@ export default {
savePopupRef(ref) {
this.popupRef = ref;
},
onClick(e) {
handleClick(e) {
this.$emit('click', e);
},
onVisibleChange(val) {
handleVisibleChange(val) {
this.$emit('update:visible', val);
this.$emit('visibleChange', val);
},
@ -53,17 +58,21 @@ export default {
const {
type,
disabled,
onClick,
htmlType,
class: className,
prefixCls: customizePrefixCls,
overlay,
trigger,
align,
visible,
onVisibleChange,
placement,
getPopupContainer,
href,
title,
...restProps
} = this.$props;
} = { ...this.$props, ...this.$attrs };
const icon = getComponent(this, 'icon') || <EllipsisOutlined />;
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
const getPrefixCls = this.configProvider.getPrefixCls;
@ -74,7 +83,7 @@ export default {
trigger: disabled ? [] : trigger,
placement,
getPopupContainer: getPopupContainer || getContextPopupContainer,
onVisibleChange: this.onVisibleChange,
onVisibleChange: this.handleVisibleChange,
};
if (hasProp(this, 'visible')) {
dropdownProps.visible = visible;
@ -82,7 +91,7 @@ export default {
const buttonGroupProps = {
...restProps,
class: prefixCls,
class: classNames(prefixCls, className),
};
return (
@ -90,7 +99,7 @@ export default {
<Button
type={type}
disabled={disabled}
onClick={this.onClick}
onClick={this.handleClick}
htmlType={htmlType}
href={href}
title={title}

View File

@ -3,6 +3,7 @@ import RcDropdown from '../vc-dropdown/src/index';
import DropdownButton from './dropdown-button';
import PropTypes from '../_util/vue-types';
import { cloneElement } from '../_util/vnode';
import classNames from 'classnames';
import {
getOptionProps,
getPropsData,
@ -17,12 +18,14 @@ import RightOutlined from '@ant-design/icons-vue/RightOutlined';
const DropdownProps = getDropdownProps();
const Dropdown = {
name: 'ADropdown',
inheritAttrs: false,
props: {
...DropdownProps,
prefixCls: PropTypes.string,
mouseEnterDelay: PropTypes.number.def(0.15),
mouseLeaveDelay: PropTypes.number.def(0.1),
placement: DropdownProps.placement.def('bottomLeft'),
onVisibleChange: PropTypes.func,
},
setup() {
return {
@ -69,6 +72,10 @@ const Dropdown = {
: overlay;
return fixedModeOverlay;
},
handleVisibleChange(val) {
this.$emit('update:visible', val);
this.$emit('visibleChange', val);
},
},
render() {
@ -78,14 +85,10 @@ const Dropdown = {
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
const dropdownTrigger = cloneElement(
getSlot(this),
{
class: `${prefixCls}-trigger`,
disabled,
},
false,
);
const dropdownTrigger = cloneElement(getSlot(this), {
class: classNames(this.$attrs?.class, `${prefixCls}-trigger`),
disabled,
});
const triggerActions = disabled ? [] : trigger;
let alignPoint;
if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) {
@ -94,11 +97,13 @@ const Dropdown = {
const dropdownProps = {
alignPoint,
...props,
...this.$attrs,
prefixCls,
getPopupContainer: getPopupContainer || getContextPopupContainer,
transitionName: this.getTransitionName(),
trigger: triggerActions,
overlay: this.renderOverlay(prefixCls),
onVisibleChange: this.handleVisibleChange,
};
return <RcDropdown {...dropdownProps}>{dropdownTrigger}</RcDropdown>;
},

View File

@ -1,5 +1,6 @@
export default {
name: 'MenuDivider',
inheritAttrs: false,
props: {
disabled: {
type: Boolean,
@ -9,6 +10,7 @@ export default {
},
render() {
const { rootPrefixCls } = this.$props;
return <li class={`${rootPrefixCls}-item-divider`} />;
const { class: className = '', style } = this.$attrs;
return <li class={`${className} ${rootPrefixCls}-item-divider`} style={style} />;
},
};

View File

@ -4,6 +4,7 @@ import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu';
import BaseMixin from '../_util/BaseMixin';
import hasProp, { getOptionProps, getComponent, filterEmpty } from '../_util/props-util';
import commonPropsType from './commonPropsType';
import { provide } from 'vue';
const Menu = {
name: 'Menu',
@ -35,6 +36,9 @@ const Menu = {
// this.isRootMenu = true // props
return {};
},
created() {
provide('parentMenu', this);
},
mounted() {
this.updateMiniStore();
},
@ -166,7 +170,6 @@ const Menu = {
expandIcon: getComponent(this, 'expandIcon', props),
overflowedIndicator: getComponent(this, 'overflowedIndicator', props) || <span>···</span>,
openTransitionName: this.getOpenTransitionName(),
parentMenu: this,
children: filterEmpty(props.children),
onClick: this.onClick,
onOpenChange: this.onOpenChange,

View File

@ -5,6 +5,7 @@ import scrollIntoView from 'dom-scroll-into-view';
import { connect } from '../_util/store';
import { noop, menuAllProps } from './util';
import { getComponent, getSlot, findDOMNode } from '../_util/props-util';
import { inject } from 'vue';
const props = {
attribute: PropTypes.object,
@ -18,7 +19,6 @@ const props = {
inlineIndent: PropTypes.number.def(24),
level: PropTypes.number.def(1),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
parentMenu: PropTypes.object,
multiple: PropTypes.bool,
value: PropTypes.any,
isSelected: PropTypes.bool,
@ -34,6 +34,9 @@ const MenuItem = {
props,
mixins: [BaseMixin],
isMenuItem: true,
setup() {
return { parentMenu: inject('parentMenu', undefined) };
},
created() {
this.prevActive = this.active;
// invoke customized ref to expose component to mixin
@ -41,9 +44,9 @@ const MenuItem = {
},
updated() {
this.$nextTick(() => {
const { active, parentMenu, eventKey } = this.$props;
const { active, parentMenu, eventKey } = this;
if (!this.prevActive && active && (!parentMenu || !parentMenu[`scrolled-${eventKey}`])) {
scrollIntoView(this.$refs.node, findDOMNode(this.parentMenu), {
scrollIntoView(findDOMNode(this.node), findDOMNode(parentMenu), {
onlyScrollIfNeeded: true,
});
parentMenu[`scrolled-${eventKey}`] = true;
@ -127,7 +130,9 @@ const MenuItem = {
getDisabledClassName() {
return `${this.getPrefixCls()}-disabled`;
},
saveNode(node) {
this.node = node;
},
callRef() {
if (this.manualRef) {
this.manualRef(this);
@ -182,7 +187,7 @@ const MenuItem = {
...props,
...attrs,
...mouseEvent,
ref: 'node',
ref: this.saveNode,
};
delete liProps.children;
return (

View File

@ -1,4 +1,4 @@
import { Transition } from 'vue';
import { Transition, inject, provide } from 'vue';
import omit from 'omit.js';
import PropTypes from '../_util/vue-types';
import Trigger from '../vc-trigger';
@ -36,7 +36,6 @@ const SubMenu = {
name: 'SubMenu',
inheritAttrs: false,
props: {
parentMenu: PropTypes.object,
title: PropTypes.any,
selectedKeys: PropTypes.array.def([]),
openKeys: PropTypes.array.def([]),
@ -77,6 +76,12 @@ const SubMenu = {
},
mixins: [BaseMixin],
isSubMenu: true,
setup() {
return { parentMenu: inject('parentMenu', undefined) };
},
created() {
provide('parentMenu', this);
},
data() {
const props = this.$props;
const store = props.store;
@ -127,7 +132,7 @@ const SubMenu = {
},
methods: {
handleUpdated() {
const { mode, parentMenu, manualRef } = this.$props;
const { mode, parentMenu, manualRef } = this;
// invoke customized ref to expose component to mixin
if (manualRef) {
@ -196,26 +201,15 @@ const SubMenu = {
},
onMouseLeave(e) {
const { eventKey, parentMenu } = this;
parentMenu.subMenuInstance = this;
// parentMenu.subMenuLeaveFn = () => {
// // trigger mouseleave
// this.__emit('mouseleave', {
// key: eventKey,
// domEvent: e,
// })
// }
const { eventKey } = this;
this.__emit('mouseleave', {
key: eventKey,
domEvent: e,
});
// prevent popup menu and submenu gap
// parentMenu.subMenuLeaveTimer = setTimeout(parentMenu.subMenuLeaveFn, 100)
},
onTitleMouseEnter(domEvent) {
const { eventKey: key } = this.$props;
// this.clearSubMenuTitleLeaveTimer()
this.__emit('itemHover', {
key,
hover: true,
@ -227,8 +221,7 @@ const SubMenu = {
},
onTitleMouseLeave(e) {
const { eventKey, parentMenu } = this;
parentMenu.subMenuInstance = this;
const { eventKey } = this;
this.__emit('itemHover', {
key: eventKey,
hover: false,
@ -356,7 +349,6 @@ const SubMenu = {
openTransitionName: props.openTransitionName,
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
parentMenu: this,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,

View File

@ -1,4 +1,4 @@
import { Comment } from 'vue';
import { Comment, inject } from 'vue';
import PropTypes from '../_util/vue-types';
import { connect } from '../_util/store';
import BaseMixin from '../_util/BaseMixin';
@ -89,7 +89,6 @@ const SubPopupMenu = {
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
openKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
visible: PropTypes.bool,
parentMenu: PropTypes.object,
eventKey: PropTypes.string,
store: PropTypes.object,
@ -131,6 +130,9 @@ const SubPopupMenu = {
),
mixins: [BaseMixin],
setup() {
return { parentMenu: inject('parentMenu', undefined) };
},
created() {
const props = getOptionProps(this);
this.prevProps = { ...props };
@ -293,7 +295,6 @@ const SubPopupMenu = {
renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls,
index: i,
parentMenu: props.parentMenu,
// customized ref function, need to be invoked manually in child's componentDidMount
manualRef: childProps.disabled ? noop : saveRef.bind(this, key),
eventKey: key,

View File

@ -74,7 +74,6 @@ export const menuAllProps = [
'defaultActiveFirst',
'prefixCls',
'inlineIndent',
'parentMenu',
'title',
'rootPrefixCls',
'eventKey',

View File

@ -4,7 +4,7 @@
</div>
</template>
<script>
import demo from '../antdv-demo/docs/drawer/demo/index';
import demo from '../antdv-demo/docs/dropdown/demo/index';
export default {
components: {