From 72ea550709b4274c4a7d44c0b6592cf33373f233 Mon Sep 17 00:00:00 2001 From: Gopal Goel Date: Fri, 12 Oct 2018 19:01:35 +0530 Subject: [PATCH 1/6] feat(menuItem: add menu prop) --- ...nuExampleVerticalWithSubmenu.shorthand.tsx | 25 ++++++++ .../MenuExampleWithSubmenu.shorthand.tsx | 25 ++++++++ .../examples/components/Menu/Types/index.tsx | 10 ++++ src/components/Menu/MenuItem.tsx | 58 ++++++++++++++----- 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx create mode 100644 docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx diff --git a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx new file mode 100644 index 0000000000..8853a08123 --- /dev/null +++ b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { Menu } from '@stardust-ui/react' + +const items = [ + { + key: 'editorials', + content: 'Editorials', + menu: { + items: [ + { key: '1', content: 'item1' }, + { + key: '2', + content: 'item2', + menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] }, + }, + ], + }, + }, + { key: 'review', content: 'Reviews' }, + { key: 'events', content: 'Upcoming Events' }, +] + +const MenuExampleVerticalWithSubmenu = () => + +export default MenuExampleVerticalWithSubmenu diff --git a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx new file mode 100644 index 0000000000..9005c3ce05 --- /dev/null +++ b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { Menu } from '@stardust-ui/react' + +const items = [ + { + key: 'editorials', + content: 'Editorials', + menu: { + items: [ + { key: '1', content: 'item1' }, + { + key: '2', + content: 'item2', + menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] }, + }, + ], + }, + }, + { key: 'review', content: 'Reviews' }, + { key: 'events', content: 'Upcoming Events' }, +] + +const MenuExampleWithSubMenu = () => + +export default MenuExampleWithSubMenu diff --git a/docs/src/examples/components/Menu/Types/index.tsx b/docs/src/examples/components/Menu/Types/index.tsx index 8ce62a8f8d..a589b627a9 100644 --- a/docs/src/examples/components/Menu/Types/index.tsx +++ b/docs/src/examples/components/Menu/Types/index.tsx @@ -19,6 +19,16 @@ const Types = () => ( description="A vertical menu displays elements vertically." examplePath="components/Menu/Types/MenuExampleVertical" /> + + ) diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 5691f45a8e..da8e91fd8a 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -5,6 +5,8 @@ import * as React from 'react' import { childrenExist, createShorthandFactory, customPropTypes, UIComponent } from '../../lib' import Icon from '../Icon' +import Menu from '../Menu' +import Popup from '../Popup' import { menuItemBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/interfaces' import IsFromKeyboard from '../../lib/isFromKeyboard' @@ -29,6 +31,7 @@ export interface IMenuItemProps { icon?: ShorthandValue iconOnly?: boolean index?: number + menu?: any onClick?: ComponentEventHandler pills?: boolean pointing?: boolean | 'start' | 'end' @@ -82,6 +85,9 @@ class MenuItem extends UIComponent, IMenuItemState> { /** MenuItem index inside Menu. */ index: PropTypes.number, + /** MenuItem's submenu */ + menu: PropTypes.any, + /** * Called on click. When passed, the component will render as an `a` * tag by default instead of a `div`. @@ -136,7 +142,7 @@ class MenuItem extends UIComponent, IMenuItemState> { state = IsFromKeyboard.initial renderComponent({ ElementType, classes, accessibility, rest }) { - const { children, content, icon, renderIcon } = this.props + const { children, menu, vertical, type } = this.props return ( , IMenuItemState> { > {childrenExist(children) ? ( children + ) : !menu ? ( + this.renderMenuItem(accessibility, classes) ) : ( - , + styles: { + padding: '0px', + border: '', + }, + }} + // content={} > - {icon && - Icon.create(this.props.icon, { - defaultProps: { xSpacing: !!content ? 'after' : 'none' }, - render: renderIcon, - })} - {content} - + {this.renderMenuItem(accessibility, classes)} + )} ) } - + private renderMenuItem = (accessibility, classes) => { + const { content, icon, renderIcon } = this.props + return ( + + {icon && + Icon.create(this.props.icon, { + defaultProps: { xSpacing: !!content ? 'after' : 'none' }, + render: renderIcon, + })} + {content} + + ) + } protected actionHandlers: AccessibilityActionHandlers = { performClick: event => this.handleClick(event), } From d4a3a0c3b22dcdf6ca80f838f06e213664634729 Mon Sep 17 00:00:00 2001 From: Gopal Goel Date: Mon, 15 Oct 2018 13:30:05 +0530 Subject: [PATCH 2/6] Only one submenu open at a time --- .../MenuExampleWithSubmenu.shorthand.tsx | 20 ++++++++++++++++++- src/components/Menu/Menu.tsx | 20 ++++++++++++++++++- src/components/Menu/MenuItem.tsx | 5 +++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx index 9005c3ce05..3829e7c908 100644 --- a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx +++ b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx @@ -5,6 +5,25 @@ const items = [ { key: 'editorials', content: 'Editorials', + menu: { + items: [ + { key: '1', content: 'item1' }, + { + key: '2', + content: 'item2', + menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] }, + }, + { + key: '3', + content: 'item3', + menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] }, + }, + ], + }, + }, + { + key: 'review', + content: 'Reviews', menu: { items: [ { key: '1', content: 'item1' }, @@ -16,7 +35,6 @@ const items = [ ], }, }, - { key: 'review', content: 'Reviews' }, { key: 'events', content: 'Upcoming Events' }, ] diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index b92919d8fa..c3e4e4eab8 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -121,12 +121,27 @@ class Menu extends AutoControlledComponent, any> { static Item = MenuItem + state = { + menuItemOpenKey: 0, + popupOpen: false, + activeIndex: '0', + } + handleItemOverrides = predefinedProps => ({ onClick: (e, itemProps) => { - const { index } = itemProps + const { index, menu } = itemProps this.trySetState({ activeIndex: index }) + if (menu) { + this.setState(prev => { + if (prev.menuItemOpenKey === index) { + return { popupOpen: !prev.popupOpen } + } + return { menuItemOpenKey: index, popupOpen: true } + }) + } + _.invoke(predefinedProps, 'onClick', e, itemProps) }, }) @@ -147,6 +162,9 @@ class Menu extends AutoControlledComponent, any> { vertical, index, active: parseInt(activeIndex, 10) === index, + popupOpen: this.state.popupOpen, + menuItemOpenKey: this.state.menuItemOpenKey, + // togglePopup: this.togglePopup, }, overrideProps: this.handleItemOverrides, render: renderItem, diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index da8e91fd8a..08758896b8 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -142,7 +142,7 @@ class MenuItem extends UIComponent, IMenuItemState> { state = IsFromKeyboard.initial renderComponent({ ElementType, classes, accessibility, rest }) { - const { children, menu, vertical, type } = this.props + const { children, index, menu, menuItemOpenKey, popupOpen, vertical, type } = this.props return ( , IMenuItemState> { this.renderMenuItem(accessibility, classes) ) : ( , IMenuItemState> { border: '', }, }} - // content={} + // content={} > {this.renderMenuItem(accessibility, classes)} From 85981f8a4f78898e1e80f08a84901baf4705e2ad Mon Sep 17 00:00:00 2001 From: Gopal Goel Date: Mon, 15 Oct 2018 13:35:35 +0530 Subject: [PATCH 3/6] Fix bug: submenu noe closes on clicking a menuItem with no submenu --- src/components/Menu/Menu.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index c3e4e4eab8..aaef995d0f 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -133,14 +133,12 @@ class Menu extends AutoControlledComponent, any> { this.trySetState({ activeIndex: index }) - if (menu) { - this.setState(prev => { - if (prev.menuItemOpenKey === index) { - return { popupOpen: !prev.popupOpen } - } - return { menuItemOpenKey: index, popupOpen: true } - }) - } + this.setState(prev => { + if (prev.menuItemOpenKey === index) { + return { popupOpen: !prev.popupOpen } + } + return { menuItemOpenKey: index, popupOpen: true } + }) _.invoke(predefinedProps, 'onClick', e, itemProps) }, From fa99935df12ab41d6e6c4e080f89bc4ede967dc8 Mon Sep 17 00:00:00 2001 From: Gopal Goel Date: Mon, 15 Oct 2018 14:12:15 +0530 Subject: [PATCH 4/6] Code cleanup --- src/components/Menu/Menu.tsx | 16 +++++++--------- src/components/Menu/MenuItem.tsx | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index aaef995d0f..f596aeeb9d 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -122,22 +122,23 @@ class Menu extends AutoControlledComponent, any> { static Item = MenuItem state = { - menuItemOpenKey: 0, popupOpen: false, - activeIndex: '0', + activeIndex: '', } handleItemOverrides = predefinedProps => ({ + popupOpen: this.state.popupOpen, + activeIndex: this.state.activeIndex, onClick: (e, itemProps) => { - const { index, menu } = itemProps + const { index } = itemProps - this.trySetState({ activeIndex: index }) + // this.trySetState({ activeIndex: index }) this.setState(prev => { - if (prev.menuItemOpenKey === index) { + if (prev.activeIndex === index) { return { popupOpen: !prev.popupOpen } } - return { menuItemOpenKey: index, popupOpen: true } + return { activeIndex: index, popupOpen: true } }) _.invoke(predefinedProps, 'onClick', e, itemProps) @@ -160,9 +161,6 @@ class Menu extends AutoControlledComponent, any> { vertical, index, active: parseInt(activeIndex, 10) === index, - popupOpen: this.state.popupOpen, - menuItemOpenKey: this.state.menuItemOpenKey, - // togglePopup: this.togglePopup, }, overrideProps: this.handleItemOverrides, render: renderItem, diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 08758896b8..33157571bf 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -142,7 +142,7 @@ class MenuItem extends UIComponent, IMenuItemState> { state = IsFromKeyboard.initial renderComponent({ ElementType, classes, accessibility, rest }) { - const { children, index, menu, menuItemOpenKey, popupOpen, vertical, type } = this.props + const { activeIndex, children, index, menu, popupOpen, vertical, type } = this.props return ( , IMenuItemState> { this.renderMenuItem(accessibility, classes) ) : ( Date: Mon, 15 Oct 2018 14:46:42 +0530 Subject: [PATCH 5/6] Remove the activeIndex prop passed to the menuItem --- src/components/Menu/Menu.tsx | 13 ++++++------- src/components/Menu/MenuItem.tsx | 8 ++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index f596aeeb9d..186ddd1ba2 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -122,25 +122,23 @@ class Menu extends AutoControlledComponent, any> { static Item = MenuItem state = { - popupOpen: false, + submenuOpen: false, activeIndex: '', } handleItemOverrides = predefinedProps => ({ - popupOpen: this.state.popupOpen, - activeIndex: this.state.activeIndex, onClick: (e, itemProps) => { const { index } = itemProps - // this.trySetState({ activeIndex: index }) - this.setState(prev => { if (prev.activeIndex === index) { - return { popupOpen: !prev.popupOpen } + return { submenuOpen: !prev.submenuOpen } } - return { activeIndex: index, popupOpen: true } + return { submenuOpen: true } }) + this.trySetState({ activeIndex: index }) + _.invoke(predefinedProps, 'onClick', e, itemProps) }, }) @@ -161,6 +159,7 @@ class Menu extends AutoControlledComponent, any> { vertical, index, active: parseInt(activeIndex, 10) === index, + submenuOpen: this.state.activeIndex === index ? this.state.submenuOpen : false, }, overrideProps: this.handleItemOverrides, render: renderItem, diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 33157571bf..a65ddcd652 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -36,6 +36,7 @@ export interface IMenuItemProps { pills?: boolean pointing?: boolean | 'start' | 'end' renderIcon?: ShorthandRenderFunction + submenuOpen?: boolean type?: 'primary' | 'secondary' underlined?: boolean vertical?: boolean @@ -106,6 +107,9 @@ class MenuItem extends UIComponent, IMenuItemState> { */ pointing: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['start', 'end'])]), + /** */ + submenuOpen: PropTypes.bool, + /** The menu can have primary or secondary type */ type: PropTypes.oneOf(['primary', 'secondary']), @@ -142,7 +146,7 @@ class MenuItem extends UIComponent, IMenuItemState> { state = IsFromKeyboard.initial renderComponent({ ElementType, classes, accessibility, rest }) { - const { activeIndex, children, index, menu, popupOpen, vertical, type } = this.props + const { children, menu, submenuOpen, vertical, type } = this.props return ( , IMenuItemState> { this.renderMenuItem(accessibility, classes) ) : ( Date: Tue, 13 Nov 2018 11:45:41 +0530 Subject: [PATCH 6/6] Remove Popup and implement submenu without it --- ...nuExampleVerticalWithSubmenu.shorthand.tsx | 18 ++++- .../MenuExampleWithSubmenu.shorthand.tsx | 18 ++++- src/components/Menu/Menu.tsx | 7 +- src/components/Menu/MenuItem.tsx | 75 +++++++++---------- .../teams/components/Menu/menuVariables.ts | 2 +- 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx index 8853a08123..1e8dae4410 100644 --- a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx +++ b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Menu } from '@stardust-ui/react' +import { Menu, Provider } from '@stardust-ui/react' const items = [ { @@ -20,6 +20,20 @@ const items = [ { key: 'events', content: 'Upcoming Events' }, ] -const MenuExampleVerticalWithSubmenu = () => +const MenuExampleVerticalWithSubmenu = () => ( + + + +) export default MenuExampleVerticalWithSubmenu diff --git a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx index 3829e7c908..187296aa78 100644 --- a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx +++ b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Menu } from '@stardust-ui/react' +import { Menu, Provider } from '@stardust-ui/react' const items = [ { @@ -38,6 +38,20 @@ const items = [ { key: 'events', content: 'Upcoming Events' }, ] -const MenuExampleWithSubMenu = () => +const MenuExampleWithSubMenu = () => ( + + + +) export default MenuExampleWithSubMenu diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 186ddd1ba2..0aea78c915 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -145,7 +145,7 @@ class Menu extends AutoControlledComponent, any> { renderItems = (variables: ComponentVariablesObject) => { const { iconOnly, items, pills, pointing, renderItem, type, underlined, vertical } = this.props - const { activeIndex } = this.state + const { activeIndex, submenuOpen } = this.state return _.map(items, (item, index) => MenuItem.create(item, { @@ -159,7 +159,10 @@ class Menu extends AutoControlledComponent, any> { vertical, index, active: parseInt(activeIndex, 10) === index, - submenuOpen: this.state.activeIndex === index ? this.state.submenuOpen : false, + ...(activeIndex === index && { submenuOpen }), + ...(item.menu && { + styles: { position: 'relative' }, + }), }, overrideProps: this.handleItemOverrides, render: renderItem, diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index a65ddcd652..bdce6028e2 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -6,7 +6,7 @@ import * as React from 'react' import { childrenExist, createShorthandFactory, customPropTypes, UIComponent } from '../../lib' import Icon from '../Icon' import Menu from '../Menu' -import Popup from '../Popup' +// import Provider from '../Provider' import { menuItemBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/interfaces' import IsFromKeyboard from '../../lib/isFromKeyboard' @@ -146,7 +146,7 @@ class MenuItem extends UIComponent, IMenuItemState> { state = IsFromKeyboard.initial renderComponent({ ElementType, classes, accessibility, rest }) { - const { children, menu, submenuOpen, vertical, type } = this.props + const { children } = this.props return ( , IMenuItemState> { {...accessibility.keyHandlers.root} {...rest} > - {childrenExist(children) ? ( - children - ) : !menu ? ( - this.renderMenuItem(accessibility, classes) - ) : ( - , - styles: { - padding: '0px', - border: '', - }, - }} - // content={} - > - {this.renderMenuItem(accessibility, classes)} - - )} + {childrenExist(children) ? children : this.renderMenuItem(accessibility, classes)} ) } private renderMenuItem = (accessibility, classes) => { - const { content, icon, renderIcon } = this.props + const { content, icon, renderIcon, menu, type, vertical, submenuOpen } = this.props return ( - - {icon && - Icon.create(this.props.icon, { - defaultProps: { xSpacing: !!content ? 'after' : 'none' }, - render: renderIcon, - })} - {content} - + <> + + {icon && + Icon.create(this.props.icon, { + defaultProps: { xSpacing: !!content ? 'after' : 'none' }, + render: renderIcon, + })} + {content} + + {menu && submenuOpen ? ( + + ) : null} + ) } protected actionHandlers: AccessibilityActionHandlers = { diff --git a/src/themes/teams/components/Menu/menuVariables.ts b/src/themes/teams/components/Menu/menuVariables.ts index d299a5356e..95bfc59ee4 100644 --- a/src/themes/teams/components/Menu/menuVariables.ts +++ b/src/themes/teams/components/Menu/menuVariables.ts @@ -24,7 +24,7 @@ export interface IMenuVariables { export default (siteVars: any): IMenuVariables => { return { defaultColor: siteVars.gray02, - defaultBackgroundColor: 'transparent', + defaultBackgroundColor: '#FFF', defaultActiveColor: siteVars.black, defaultActiveBackgroundColor: siteVars.gray10,