From 72ea550709b4274c4a7d44c0b6592cf33373f233 Mon Sep 17 00:00:00 2001
From: Gopal Goel
Date: Fri, 12 Oct 2018 19:01:35 +0530
Subject: [PATCH 01/55] 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 02/55] 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 03/55] 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 04/55] 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 05/55] 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 06/55] 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,
From bea260a87704d7c78bdf15abe92d678b4fea23b5 Mon Sep 17 00:00:00 2001
From: Juraj Kapsiar
Date: Thu, 29 Nov 2018 05:35:11 +0100
Subject: [PATCH 07/55] initial keyboard support
---
.../MenuExampleWithSubmenu.shorthand.tsx | 6 +-
src/components/Menu/Menu.tsx | 18 ++----
src/components/Menu/MenuItem.tsx | 64 ++++++++++++++++---
.../Behaviors/Menu/menuItemBehavior.ts | 8 +++
.../Behaviors/Menu/submenuBehavior.ts | 31 +++++++++
src/lib/accessibility/index.ts | 1 +
.../teams/components/Menu/menuStyles.ts | 2 +-
test/specs/behaviors/behavior-test.tsx | 2 +
8 files changed, 104 insertions(+), 28 deletions(-)
create mode 100644 src/lib/accessibility/Behaviors/Menu/submenuBehavior.ts
diff --git a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
index 187296aa78..d2d195cc3b 100644
--- a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
+++ b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
@@ -11,12 +11,12 @@ const items = [
{
key: '2',
content: 'item2',
- menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] },
+ menu: { items: [{ key: '1', content: 'item2.1' }, { key: '2', content: 'item2.2' }] },
},
{
key: '3',
content: 'item3',
- menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] },
+ menu: { items: [{ key: '1', content: 'item3.1' }, { key: '2', content: 'item3.2' }] },
},
],
},
@@ -30,7 +30,7 @@ const items = [
{
key: '2',
content: 'item2',
- menu: { items: [{ key: '1', content: 'item1' }, { key: '2', content: 'item2' }] },
+ menu: { items: [{ key: '1', content: 'item2.1' }, { key: '2', content: 'item2.2' }] },
},
],
},
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 6c6b6a4f79..30e313d2a7 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -109,7 +109,6 @@ class Menu extends AutoControlledComponent, any> {
static Item = MenuItem
state = {
- submenuOpen: false,
activeIndex: '',
}
@@ -117,13 +116,6 @@ class Menu extends AutoControlledComponent, any> {
onClick: (e, itemProps) => {
const { index } = itemProps
- this.setState(prev => {
- if (prev.activeIndex === index) {
- return { submenuOpen: !prev.submenuOpen }
- }
- return { submenuOpen: true }
- })
-
this.trySetState({ activeIndex: index })
_.invoke(predefinedProps, 'onClick', e, itemProps)
@@ -142,7 +134,7 @@ class Menu extends AutoControlledComponent, any> {
underlined,
vertical,
} = this.props
- const { activeIndex, submenuOpen } = this.state
+ const { activeIndex } = this.state
return _.map(items, (item, index) => {
const active = parseInt(activeIndex, 10) === index
@@ -158,11 +150,9 @@ class Menu extends AutoControlledComponent, any> {
vertical,
index,
active,
- ...(active && { submenuOpen }),
- ...(active &&
- submenuOpen && {
- styles: { position: 'relative' },
- }),
+ ...(active && {
+ styles: { position: 'relative' },
+ }),
},
overrideProps: this.handleItemOverrides,
render: renderItem,
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 051f0abaaf..e1d7ca86e3 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -3,11 +3,16 @@ import * as cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
-import { childrenExist, createShorthandFactory, customPropTypes, UIComponent } from '../../lib'
+import {
+ childrenExist,
+ createShorthandFactory,
+ customPropTypes,
+ AutoControlledComponent,
+} from '../../lib'
import Icon from '../Icon/Icon'
import Menu from '../Menu/Menu'
import Slot from '../Slot/Slot'
-import { menuItemBehavior } from '../../lib/accessibility'
+import { menuItemBehavior, submenuBehavior } from '../../lib/accessibility'
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
import IsFromKeyboard from '../../lib/isFromKeyboard'
@@ -27,6 +32,7 @@ import {
childrenComponentPropTypes,
contentComponentPropsTypes,
} from '../../lib/commonPropTypes'
+import { focusAsync } from 'src/lib/accessibility/FocusZone'
export interface MenuItemProps
extends UIComponentProps,
@@ -109,16 +115,20 @@ export interface MenuItemProps
/** Indicates if the submenu is open */
submenuOpen?: boolean
+
+ /** Default submenu open */
+ defaultSubmenuOpen?: boolean
}
export interface MenuItemState {
[IsFromKeyboard.propertyName]: boolean
+ submenuOpen: boolean
}
/**
* A menu item is an actionable navigation item within a menu.
*/
-class MenuItem extends UIComponent, MenuItemState> {
+class MenuItem extends AutoControlledComponent, MenuItemState> {
static displayName = 'MenuItem'
static className = 'ui-menu__item'
@@ -147,6 +157,7 @@ class MenuItem extends UIComponent, MenuItemState> {
renderWrapper: PropTypes.func,
menu: customPropTypes.itemShorthand,
submenuOpen: PropTypes.bool,
+ defaultSubmenuOpen: PropTypes.bool,
}
static defaultProps = {
@@ -155,7 +166,14 @@ class MenuItem extends UIComponent, MenuItemState> {
wrapper: { as: 'li' },
}
- state = IsFromKeyboard.initial
+ static autoControlledProps = ['submenuOpen']
+
+ state = {
+ ...IsFromKeyboard.initial,
+ submenuOpen: false,
+ }
+
+ private itemRef = React.createRef()
renderComponent({ ElementType, classes, accessibility, rest }) {
const {
@@ -166,11 +184,13 @@ class MenuItem extends UIComponent, MenuItemState> {
renderWrapper,
wrapper,
menu,
- submenuOpen,
type,
vertical,
+ active,
} = this.props
+ const { submenuOpen } = this.state
+
const menuItemInner = childrenExist(children) ? (
children
) : (
@@ -182,6 +202,7 @@ class MenuItem extends UIComponent, MenuItemState> {
{...accessibility.attributes.anchor}
{...accessibility.keyHandlers.anchor}
{...rest}
+ ref={this.itemRef}
>
{icon &&
Icon.create(this.props.icon, {
@@ -193,10 +214,10 @@ class MenuItem extends UIComponent, MenuItemState> {
)
const maybeSubmenu =
- menu && submenuOpen
+ menu && active && submenuOpen
? Menu.create(menu, {
overrideProps: {
- accessibility: null,
+ accessibility: submenuBehavior,
vertical: true,
type,
styles: {
@@ -214,12 +235,12 @@ class MenuItem extends UIComponent, MenuItemState> {
return Slot.create(wrapper, {
defaultProps: {
className: cx('ui-menu__item__wrapper', classes.wrapper),
- ...accessibility.attributes.root,
- ...accessibility.keyHandlers.root,
+ ...accessibility.attributes.wrapper,
+ ...accessibility.keyHandlers.wrapper,
},
render: renderWrapper,
overrideProps: () => ({
- children: [menuItemInner, maybeSubmenu],
+ children: [menuItemInner, menu ? '>' : undefined, maybeSubmenu],
}),
})
}
@@ -228,9 +249,15 @@ class MenuItem extends UIComponent, MenuItemState> {
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
+ closeMenu: event => this.closeMenu(event),
+ closeSubmenu: event => this.closeSubmenu(event),
}
private handleClick = e => {
+ const { active, menu } = this.props
+ if (menu) {
+ this.setState({ submenuOpen: active ? !this.state.submenuOpen : true })
+ }
_.invoke(this.props, 'onClick', e, this.props)
}
@@ -245,6 +272,23 @@ class MenuItem extends UIComponent, MenuItemState> {
_.invoke(this.props, 'onFocus', e, this.props)
}
+
+ private closeMenu = e => {
+ const { menu } = this.props
+ const { submenuOpen } = this.state
+ if (menu && submenuOpen) {
+ this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ }
+ }
+
+ private closeSubmenu = e => {
+ const { menu } = this.props
+ const { submenuOpen } = this.state
+ if (menu && submenuOpen) {
+ this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ e.stopPropagation()
+ }
+ }
}
MenuItem.create = createShorthandFactory(MenuItem, 'content')
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index f00c978834..8899b1f1f0 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -39,6 +39,14 @@ const menuItemBehavior: Accessibility = (props: any) => ({
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
},
+ wrapper: {
+ closeSubmenu: {
+ keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
+ },
+ closeMenu: {
+ keyCombinations: [{ keyCode: keyboardKey.Escape }],
+ },
+ },
},
})
diff --git a/src/lib/accessibility/Behaviors/Menu/submenuBehavior.ts b/src/lib/accessibility/Behaviors/Menu/submenuBehavior.ts
new file mode 100644
index 0000000000..2ef65e9e3e
--- /dev/null
+++ b/src/lib/accessibility/Behaviors/Menu/submenuBehavior.ts
@@ -0,0 +1,31 @@
+import { Accessibility, FocusZoneMode } from '../../types'
+import { FocusZoneDirection } from '../../FocusZone'
+
+/**
+ * @description
+ * The 'menu' role is used to identify an element that creates a list of common actions or functions that a user can invoke.
+ *
+ * @specification
+ * Adds role='menu'.
+ * Embeds FocusZone into component allowing circular arrow key navigation through the children of the component.
+ */
+
+const submenuBehavior: Accessibility = (props: any) => ({
+ attributes: {
+ root: {
+ role: 'menu',
+ },
+ },
+ focusZone: {
+ mode: FocusZoneMode.Embed,
+ props: {
+ isCircularNavigation: true,
+ preventDefaultWhenHandled: true,
+ shouldFocusFirstElementWhenReceivedFocus: true,
+ shouldFocusOnMount: true,
+ direction: FocusZoneDirection.vertical,
+ },
+ },
+})
+
+export default submenuBehavior
diff --git a/src/lib/accessibility/index.ts b/src/lib/accessibility/index.ts
index fc8c7f4904..047279ed5b 100644
--- a/src/lib/accessibility/index.ts
+++ b/src/lib/accessibility/index.ts
@@ -5,6 +5,7 @@ export { default as toggleButtonBehavior } from './Behaviors/Button/toggleButton
export { default as imageBehavior } from './Behaviors/Image/imageBehavior'
export { default as menuBehavior } from './Behaviors/Menu/menuBehavior'
export { default as menuItemBehavior } from './Behaviors/Menu/menuItemBehavior'
+export { default as submenuBehavior } from './Behaviors/Menu/submenuBehavior'
export { default as basicListBehavior } from './Behaviors/List/listBehavior'
export { default as basicListItemBehavior } from './Behaviors/List/basicListItemBehavior'
export { default as listBehavior } from './Behaviors/List/listBehavior'
diff --git a/src/themes/teams/components/Menu/menuStyles.ts b/src/themes/teams/components/Menu/menuStyles.ts
index 46f8f9e0c6..3f9a439656 100644
--- a/src/themes/teams/components/Menu/menuStyles.ts
+++ b/src/themes/teams/components/Menu/menuStyles.ts
@@ -28,7 +28,7 @@ export default {
...solidBorder(variables.primaryBorderColor),
}),
borderRadius: pxToRem(4),
- overflow: 'hidden',
+ // overflow: 'hidden',
}),
...(underlined && {
borderBottom: `2px solid ${variables.primaryUnderlinedBorderColor}`,
diff --git a/test/specs/behaviors/behavior-test.tsx b/test/specs/behaviors/behavior-test.tsx
index bc38e2ddc5..9119d3e3cc 100644
--- a/test/specs/behaviors/behavior-test.tsx
+++ b/test/specs/behaviors/behavior-test.tsx
@@ -14,6 +14,7 @@ import {
inputBehavior,
menuBehavior,
menuItemBehavior,
+ submenuBehavior,
popupBehavior,
popupFocusTrapBehavior,
dialogBehavior,
@@ -44,6 +45,7 @@ testHelper.addBehavior('inputBehavior', inputBehavior)
testHelper.addBehavior('imageBehavior', imageBehavior)
testHelper.addBehavior('menuBehavior', menuBehavior)
testHelper.addBehavior('menuItemBehavior', menuItemBehavior)
+testHelper.addBehavior('submenuBehavior', submenuBehavior)
testHelper.addBehavior('popupBehavior', popupBehavior)
testHelper.addBehavior('popupFocusTrapBehavior', popupFocusTrapBehavior)
testHelper.addBehavior('radioGroupBehavior', radioGroupBehavior)
From 8f678ece86e66330655649026157604eecef90dd Mon Sep 17 00:00:00 2001
From: Juraj Kapsiar
Date: Thu, 29 Nov 2018 07:14:32 +0100
Subject: [PATCH 08/55] left/right arrow handling
---
src/components/Menu/MenuItem.tsx | 29 +++++++++++++++----
.../Behaviors/Menu/menuItemBehavior.ts | 21 +++++++++++---
2 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index e1d7ca86e3..fae70aa22e 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -249,8 +249,9 @@ class MenuItem extends AutoControlledComponent, MenuIt
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
+ openVerticalSubmenu: event => this.openVerticalSubmenu(event),
+ openHorizontalSubmenu: event => this.openHorizontalSubmenu(event),
closeMenu: event => this.closeMenu(event),
- closeSubmenu: event => this.closeSubmenu(event),
}
private handleClick = e => {
@@ -281,11 +282,29 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
}
- private closeSubmenu = e => {
- const { menu } = this.props
+ // private closeSubmenu = e => {
+ // const { menu } = this.props
+ // const { submenuOpen } = this.state
+ // if (menu && submenuOpen) {
+ // this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ // e.stopPropagation()
+ // }
+ // }
+
+ private openVerticalSubmenu = e => {
+ const { menu, vertical } = this.props
const { submenuOpen } = this.state
- if (menu && submenuOpen) {
- this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ if (menu && vertical && !submenuOpen) {
+ this.handleClick(e)
+ e.stopPropagation()
+ }
+ }
+
+ private openHorizontalSubmenu = e => {
+ const { menu, vertical } = this.props
+ const { submenuOpen } = this.state
+ if (menu && !vertical && !submenuOpen) {
+ this.handleClick(e)
e.stopPropagation()
}
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index 8899b1f1f0..1e8db21de0 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -38,13 +38,26 @@ const menuItemBehavior: Accessibility = (props: any) => ({
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
+ openVerticalSubmenu: {
+ keyCombinations: [{ keyCode: keyboardKey.ArrowRight }],
+ },
+ openHorizontalSubmenu: {
+ keyCombinations: [{ keyCode: keyboardKey.ArrowDown }],
+ },
},
wrapper: {
- closeSubmenu: {
- keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
- },
+ // closeSubmenu: {
+ // keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }, { keyCode: keyboardKey.ArrowRight }],
+ // },
+ // closeMenu: {
+ // keyCombinations: [{ keyCode: keyboardKey.Escape }],
+ // },
closeMenu: {
- keyCombinations: [{ keyCode: keyboardKey.Escape }],
+ keyCombinations: [
+ { keyCode: keyboardKey.ArrowLeft },
+ { keyCode: keyboardKey.ArrowRight },
+ { keyCode: keyboardKey.Escape },
+ ],
},
},
},
From 0f4be8bcdc3d5d57eaf134a1e8257124753454c7 Mon Sep 17 00:00:00 2001
From: Juraj Kapsiar
Date: Thu, 29 Nov 2018 08:32:16 +0100
Subject: [PATCH 09/55] simplify keyboard handlers
---
src/components/Menu/MenuItem.tsx | 30 +++++++------------
.../Behaviors/Menu/menuItemBehavior.ts | 25 ++++++----------
2 files changed, 20 insertions(+), 35 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index fae70aa22e..b550b9a224 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -240,7 +240,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
},
render: renderWrapper,
overrideProps: () => ({
- children: [menuItemInner, menu ? '>' : undefined, maybeSubmenu],
+ children: [menuItemInner, maybeSubmenu],
}),
})
}
@@ -249,9 +249,9 @@ class MenuItem extends AutoControlledComponent, MenuIt
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
- openVerticalSubmenu: event => this.openVerticalSubmenu(event),
- openHorizontalSubmenu: event => this.openHorizontalSubmenu(event),
+ openSubmenu: event => this.openSubmenu(event),
closeMenu: event => this.closeMenu(event),
+ closeSubmenu: event => this.closeSubmenu(event),
}
private handleClick = e => {
@@ -282,30 +282,22 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
}
- // private closeSubmenu = e => {
- // const { menu } = this.props
- // const { submenuOpen } = this.state
- // if (menu && submenuOpen) {
- // this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
- // e.stopPropagation()
- // }
- // }
-
- private openVerticalSubmenu = e => {
- const { menu, vertical } = this.props
+ private closeSubmenu = e => {
+ const { menu } = this.props
const { submenuOpen } = this.state
- if (menu && vertical && !submenuOpen) {
- this.handleClick(e)
+ if (menu && submenuOpen) {
+ this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
e.stopPropagation()
}
}
- private openHorizontalSubmenu = e => {
- const { menu, vertical } = this.props
+ private openSubmenu = e => {
+ const { menu } = this.props
const { submenuOpen } = this.state
- if (menu && !vertical && !submenuOpen) {
+ if (menu && !submenuOpen) {
this.handleClick(e)
e.stopPropagation()
+ e.preventDefault()
}
}
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index 1e8db21de0..c3463ced39 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -38,26 +38,19 @@ const menuItemBehavior: Accessibility = (props: any) => ({
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
- openVerticalSubmenu: {
- keyCombinations: [{ keyCode: keyboardKey.ArrowRight }],
- },
- openHorizontalSubmenu: {
- keyCombinations: [{ keyCode: keyboardKey.ArrowDown }],
+
+ openSubmenu: {
+ keyCombinations: [
+ { keyCode: props.vertical ? keyboardKey.ArrowRight : keyboardKey.ArrowDown },
+ ],
},
},
wrapper: {
- // closeSubmenu: {
- // keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }, { keyCode: keyboardKey.ArrowRight }],
- // },
- // closeMenu: {
- // keyCombinations: [{ keyCode: keyboardKey.Escape }],
- // },
closeMenu: {
- keyCombinations: [
- { keyCode: keyboardKey.ArrowLeft },
- { keyCode: keyboardKey.ArrowRight },
- { keyCode: keyboardKey.Escape },
- ],
+ keyCombinations: [{ keyCode: keyboardKey.Escape }, { keyCode: keyboardKey.ArrowRight }],
+ },
+ closeSubmenu: {
+ keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
},
},
},
From 46d52c6e04d4013b09f5581b0cac83a6db9ed7c2 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 30 Nov 2018 11:37:33 +0100
Subject: [PATCH 10/55] -fixed import in the MenuItem
---
src/components/Menu/MenuItem.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index b550b9a224..931b1cb5a7 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -32,7 +32,7 @@ import {
childrenComponentPropTypes,
contentComponentPropsTypes,
} from '../../lib/commonPropTypes'
-import { focusAsync } from 'src/lib/accessibility/FocusZone'
+import { focusAsync } from '../../lib/accessibility/FocusZone'
export interface MenuItemProps
extends UIComponentProps,
From c1ad2da96f0edca8e18979c2ea9f61dc92c85979 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 30 Nov 2018 15:29:14 +0100
Subject: [PATCH 11/55] -added submenuIndicator -small fixes in the styles and
the way the submenu is generated
---
src/components/Menu/MenuItem.tsx | 19 +++++++-----------
.../teams/components/Menu/menuItemStyles.ts | 20 +++++++++++++++++++
.../teams/components/Menu/menuVariables.ts | 6 ++++++
3 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 931b1cb5a7..01924d8010 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -175,7 +175,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
private itemRef = React.createRef()
- renderComponent({ ElementType, classes, accessibility, rest }) {
+ renderComponent({ ElementType, classes, accessibility, rest, styles }) {
const {
children,
content,
@@ -184,8 +184,8 @@ class MenuItem extends AutoControlledComponent, MenuIt
renderWrapper,
wrapper,
menu,
- type,
- vertical,
+ primary,
+ secondary,
active,
} = this.props
@@ -216,17 +216,12 @@ class MenuItem extends AutoControlledComponent, MenuIt
const maybeSubmenu =
menu && active && submenuOpen
? Menu.create(menu, {
- overrideProps: {
+ defaultProps: {
accessibility: submenuBehavior,
vertical: true,
- type,
- styles: {
- // background: 'white',
- // zIndex: '1000',
- position: 'absolute',
- top: vertical ? '0' : '100%',
- left: vertical ? '100%' : '0',
- },
+ primary,
+ secondary,
+ styles: styles.menu,
},
})
: null
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index 2f2263e12e..51ab050629 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -266,8 +266,28 @@ const menuItemStyles: ComponentSlotStylesInput ({
+ // background: 'white',
+ // zIndex: '1000',
+ position: 'absolute',
+ top: vertical ? '0' : '100%',
+ left: vertical ? '100%' : '0',
+ }),
}
export default menuItemStyles
diff --git a/src/themes/teams/components/Menu/menuVariables.ts b/src/themes/teams/components/Menu/menuVariables.ts
index 0aeccf9706..3713639ccc 100644
--- a/src/themes/teams/components/Menu/menuVariables.ts
+++ b/src/themes/teams/components/Menu/menuVariables.ts
@@ -19,6 +19,9 @@ export interface MenuVariables {
iconsMenuItemSize?: string
circularRadius: string
lineHeightBase: string
+
+ submenuIndicatorContent: string
+ submenuIndicatorRotationAngle: number
}
export default (siteVars: any): MenuVariables => {
@@ -41,5 +44,8 @@ export default (siteVars: any): MenuVariables => {
iconsMenuItemSize: pxToRem(32),
circularRadius: pxToRem(999),
lineHeightBase: siteVars.lineHeightBase,
+
+ submenuIndicatorContent: '">"',
+ submenuIndicatorRotationAngle: 90,
}
}
From acee5ee87d095e3100d34255ec6da1ce247afc2f Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 30 Nov 2018 17:08:15 +0100
Subject: [PATCH 12/55] -clicking on leaf element should close the submenus
(the same should be done for enter/space) -applied consistent (left-right)
navigation for horizontal menu and (up-down) navigation for vertical menu
---
src/components/Menu/Menu.tsx | 26 +++++++++++++++++--
src/components/Menu/MenuItem.tsx | 13 ++++++++--
.../Behaviors/Menu/menuBehavior.ts | 3 +++
3 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 30e313d2a7..f4ac341333 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -13,7 +13,12 @@ import { menuBehavior } from '../../lib/accessibility'
import { Accessibility } from '../../lib/accessibility/types'
import { ComponentVariablesObject } from '../../themes/types'
-import { Extendable, ShorthandRenderFunction, ShorthandValue } from '../../../types/utils'
+import {
+ Extendable,
+ ShorthandRenderFunction,
+ ShorthandValue,
+ ComponentEventHandler,
+} from '../../../types/utils'
import { UIComponentProps, ChildrenComponentProps } from '../../lib/commonPropInterfaces'
import { commonUIComponentPropTypes, childrenComponentPropTypes } from '../../lib/commonPropTypes'
@@ -39,6 +44,14 @@ export interface MenuProps extends UIComponentProps, ChildrenComponent
/** Shorthand array of props for Menu. */
items?: ShorthandValue[]
+ /**
+ * Called on click.
+ *
+ * @param {SyntheticEvent} event - React's original SyntheticEvent.
+ * @param {object} data - All props.
+ */
+ onClick?: ComponentEventHandler
+
/** A menu can adjust its appearance to de-emphasize its contents. */
pills?: boolean
@@ -163,11 +176,20 @@ class Menu extends AutoControlledComponent, any> {
renderComponent({ ElementType, classes, accessibility, variables, rest }) {
const { children } = this.props
return (
-
+
{childrenExist(children) ? children : this.renderItems(variables)}
)
}
+
+ private handleClick = (e: React.SyntheticEvent) => {
+ _.invoke(this.props, 'onClick', e, { ...this.props, ...this.state })
+ }
}
Menu.create = createShorthandFactory(Menu, 'items')
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 01924d8010..9861b6f72f 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -10,7 +10,7 @@ import {
AutoControlledComponent,
} from '../../lib'
import Icon from '../Icon/Icon'
-import Menu from '../Menu/Menu'
+import Menu, { MenuProps } from '../Menu/Menu'
import Slot from '../Slot/Slot'
import { menuItemBehavior, submenuBehavior } from '../../lib/accessibility'
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
@@ -223,6 +223,9 @@ class MenuItem extends AutoControlledComponent, MenuIt
secondary,
styles: styles.menu,
},
+ overrideProps: {
+ onClick: this.handleSubmenuClicked,
+ },
})
: null
@@ -252,7 +255,8 @@ class MenuItem extends AutoControlledComponent, MenuIt
private handleClick = e => {
const { active, menu } = this.props
if (menu) {
- this.setState({ submenuOpen: active ? !this.state.submenuOpen : true })
+ this.trySetState({ submenuOpen: active ? !this.state.submenuOpen : true })
+ e.stopPropagation()
}
_.invoke(this.props, 'onClick', e, this.props)
}
@@ -295,6 +299,11 @@ class MenuItem extends AutoControlledComponent, MenuIt
e.preventDefault()
}
}
+
+ private handleSubmenuClicked = (e: React.SyntheticEvent, props: MenuProps) => {
+ this.trySetState({ submenuOpen: false })
+ _.invoke(props, 'onClick', props)
+ }
}
MenuItem.create = createShorthandFactory(MenuItem, 'content')
diff --git a/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
index 377936f75f..e25b3d305e 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
@@ -1,4 +1,5 @@
import { Accessibility, FocusZoneMode } from '../../types'
+import { FocusZoneDirection } from '../../FocusZone'
/**
* @description
@@ -21,6 +22,8 @@ const menuBehavior: Accessibility = (props: any) => ({
isCircularNavigation: true,
preventDefaultWhenHandled: true,
shouldFocusFirstElementWhenReceivedFocus: true,
+ // TODO: check if this should be applied to other behaviors as well
+ direction: props.vertical ? FocusZoneDirection.vertical : FocusZoneDirection.horizontal,
},
},
})
From 5575653335b79d11276d78d998271f743f57e111 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 7 Dec 2018 15:13:03 +0100
Subject: [PATCH 13/55] -implemented outside click to close all menus
-implemented enter key on leaf menu item to close the menu
---
src/components/Menu/MenuItem.tsx | 59 +++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 51176300b2..c746989017 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -2,6 +2,7 @@ import * as _ from 'lodash'
import * as cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
+import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
@@ -12,6 +13,7 @@ import {
ChildrenComponentProps,
ContentComponentProps,
commonPropTypes,
+ EventStack,
} from '../../lib'
import Icon from '../Icon/Icon'
import Menu, { MenuProps } from '../Menu/Menu'
@@ -21,6 +23,7 @@ import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibil
import IsFromKeyboard from '../../lib/isFromKeyboard'
import { ComponentEventHandler, Extendable, ShorthandValue } from '../../../types/utils'
import { focusAsync } from '../../lib/accessibility/FocusZone'
+import { Ref } from '@stardust-ui/react'
export interface MenuItemProps
extends UIComponentProps,
@@ -139,8 +142,23 @@ class MenuItem extends AutoControlledComponent, MenuIt
submenuOpen: false,
}
+ private outsideClickSubscription = EventStack.noSubscription
+
+ private submenuDomElement = null
private itemRef = React.createRef()
+ public componentDidMount() {
+ this.updateOutsideClickSubscription()
+ }
+
+ public componentDidUpdate() {
+ this.updateOutsideClickSubscription()
+ }
+
+ public componentWillUnmount() {
+ this.outsideClickSubscription.unsubscribe()
+ }
+
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
const { children, content, icon, wrapper, menu, primary, secondary, active } = this.props
@@ -179,10 +197,21 @@ class MenuItem extends AutoControlledComponent, MenuIt
},
overrideProps: {
onClick: this.handleSubmenuClicked,
+ onKeyDown: this.handleSubmenuKeyDown,
},
})
: null
+ const maybeSubmenuWithRef = maybeSubmenu ? (
+ [ {
+ this.submenuDomElement = domElement
+ }}
+ >
+ {maybeSubmenu}
+ ]
+ ) : null
+
if (wrapper) {
return Slot.create(wrapper, {
defaultProps: {
@@ -191,7 +220,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
...accessibility.keyHandlers.wrapper,
},
overrideProps: () => ({
- children: [menuItemInner, maybeSubmenu],
+ children: [menuItemInner, maybeSubmenuWithRef],
}),
})
}
@@ -205,6 +234,24 @@ class MenuItem extends AutoControlledComponent, MenuIt
closeSubmenu: event => this.closeSubmenu(event),
}
+ private updateOutsideClickSubscription() {
+ this.outsideClickSubscription.unsubscribe()
+
+ if (this.props.menu && this.state.submenuOpen) {
+ setTimeout(() => {
+ this.outsideClickSubscription = EventStack.subscribe('click', e => {
+ if (
+ this.itemRef &&
+ (!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
+ (!this.submenuDomElement || !this.submenuDomElement.contains(e.target))
+ ) {
+ this.state.submenuOpen && this.trySetState({ submenuOpen: false })
+ }
+ })
+ })
+ }
+ }
+
private handleClick = e => {
const { active, menu } = this.props
if (menu) {
@@ -257,6 +304,16 @@ class MenuItem extends AutoControlledComponent, MenuIt
this.trySetState({ submenuOpen: false })
_.invoke(props, 'onClick', props)
}
+
+ private handleSubmenuKeyDown = (e: React.SyntheticEvent, props: MenuProps) => {
+ if (
+ keyboardKey.getCode(e) === keyboardKey.Enter ||
+ keyboardKey.getCode(e) === keyboardKey.Spacebar
+ ) {
+ this.trySetState({ submenuOpen: false })
+ }
+ _.invoke(props, 'onKeyDown', props)
+ }
}
MenuItem.create = createShorthandFactory(MenuItem, 'content')
From 7a17afb3d0ab3645182254a72945ab8de3e1245d Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 7 Dec 2018 15:36:58 +0100
Subject: [PATCH 14/55] -fixed import
---
src/components/Menu/MenuItem.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index c746989017..a6f7f51552 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -17,13 +17,13 @@ import {
} from '../../lib'
import Icon from '../Icon/Icon'
import Menu, { MenuProps } from '../Menu/Menu'
+import Ref from '../Ref/Ref'
import Slot from '../Slot/Slot'
import { menuItemBehavior, submenuBehavior } from '../../lib/accessibility'
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
import IsFromKeyboard from '../../lib/isFromKeyboard'
import { ComponentEventHandler, Extendable, ShorthandValue } from '../../../types/utils'
import { focusAsync } from '../../lib/accessibility/FocusZone'
-import { Ref } from '@stardust-ui/react'
export interface MenuItemProps
extends UIComponentProps,
From 62620ae937172bf38489b3f3c017ae3bb12a1e7b Mon Sep 17 00:00:00 2001
From: manajdov
Date: Tue, 11 Dec 2018 17:56:09 +0100
Subject: [PATCH 15/55] -refactored MenuItem handlers - fixed issues -removed
onClick handler for the Menu (not necessary for now) -added onKeyDown in the
creation of the MenuItem in the Menu component for handling the action prop
---
src/components/Menu/Menu.tsx | 35 ++++++------
src/components/Menu/MenuItem.tsx | 54 +++++++++----------
.../Behaviors/Menu/menuItemBehavior.ts | 16 +++---
src/lib/getKeyDownHandlers.ts | 3 +-
4 files changed, 49 insertions(+), 59 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index f96c5a656e..6a6d5d33dd 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -1,6 +1,7 @@
import * as _ from 'lodash'
import * as PropTypes from 'prop-types'
import * as React from 'react'
+import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
@@ -16,7 +17,7 @@ import { menuBehavior } from '../../lib/accessibility'
import { Accessibility } from '../../lib/accessibility/types'
import { ComponentVariablesObject } from '../../themes/types'
-import { Extendable, ShorthandValue, ComponentEventHandler } from '../../../types/utils'
+import { Extendable, ShorthandValue } from '../../../types/utils'
export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/**
@@ -40,14 +41,6 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/** Shorthand array of props for Menu. */
items?: ShorthandValue[]
- /**
- * Called on click.
- *
- * @param {SyntheticEvent} event - React's original SyntheticEvent.
- * @param {object} data - All props.
- */
- onClick?: ComponentEventHandler
-
/** A menu can adjust its appearance to de-emphasize its contents. */
pills?: boolean
@@ -113,12 +106,23 @@ class Menu extends AutoControlledComponent, any> {
handleItemOverrides = predefinedProps => ({
onClick: (e, itemProps) => {
- const { index } = itemProps
+ const { index } = predefinedProps
this.trySetState({ activeIndex: index })
_.invoke(predefinedProps, 'onClick', e, itemProps)
},
+ onKeyDown: (e, itemProps) => {
+ if (
+ keyboardKey.getCode(e) === keyboardKey.Enter ||
+ keyboardKey.getCode(e) === keyboardKey.Spacebar
+ ) {
+ // TODO check why itemProps is undefined (should be fixed by the changes added in getKeyDownHandler.ts
+ const { index } = predefinedProps
+ this.trySetState({ activeIndex: index })
+ }
+ _.invoke(predefinedProps, 'onKeyDown', predefinedProps)
+ },
})
renderItems = (variables: ComponentVariablesObject) => {
@@ -160,20 +164,11 @@ class Menu extends AutoControlledComponent, any> {
renderComponent({ ElementType, classes, accessibility, variables, rest }) {
const { children } = this.props
return (
-
+
{childrenExist(children) ? children : this.renderItems(variables)}
)
}
-
- private handleClick = (e: React.SyntheticEvent) => {
- _.invoke(this.props, 'onClick', e, { ...this.props, ...this.state })
- }
}
Menu.create = createShorthandFactory(Menu, 'items')
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index a6f7f51552..7d80e02030 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -2,7 +2,6 @@ import * as _ from 'lodash'
import * as cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
-import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
@@ -16,7 +15,7 @@ import {
EventStack,
} from '../../lib'
import Icon from '../Icon/Icon'
-import Menu, { MenuProps } from '../Menu/Menu'
+import Menu from '../Menu/Menu'
import Ref from '../Ref/Ref'
import Slot from '../Slot/Slot'
import { menuItemBehavior, submenuBehavior } from '../../lib/accessibility'
@@ -59,6 +58,14 @@ export interface MenuItemProps
*/
onClick?: ComponentEventHandler
+ /**
+ * Called on key down pressed.
+ *
+ * @param {SyntheticEvent} event - React's original SyntheticEvent.
+ * @param {object} data - All props.
+ */
+ onKeyDown?: ComponentEventHandler
+
/** A menu can adjust its appearance to de-emphasize its contents. */
pills?: boolean
@@ -169,12 +176,12 @@ class MenuItem extends AutoControlledComponent, MenuIt
) : (
{icon &&
@@ -184,7 +191,6 @@ class MenuItem extends AutoControlledComponent, MenuIt
{content}
)
-
const maybeSubmenu =
menu && active && submenuOpen
? Menu.create(menu, {
@@ -195,10 +201,6 @@ class MenuItem extends AutoControlledComponent, MenuIt
secondary,
styles: styles.menu,
},
- overrideProps: {
- onClick: this.handleSubmenuClicked,
- onKeyDown: this.handleSubmenuKeyDown,
- },
})
: null
@@ -221,6 +223,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
},
overrideProps: () => ({
children: [menuItemInner, maybeSubmenuWithRef],
+ onClick: this.handleClick,
}),
})
}
@@ -228,7 +231,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
protected actionHandlers: AccessibilityActionHandlers = {
- performClick: event => this.handleClick(event),
+ performClick: event => this.performClick(event),
openSubmenu: event => this.openSubmenu(event),
closeMenu: event => this.closeMenu(event),
closeSubmenu: event => this.closeSubmenu(event),
@@ -252,12 +255,22 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
}
- private handleClick = e => {
+ private performClick = e => {
const { active, menu } = this.props
if (menu) {
- this.trySetState({ submenuOpen: active ? !this.state.submenuOpen : true })
- e.stopPropagation()
+ if (this.submenuDomElement && this.submenuDomElement.contains(e.target)) {
+ // submenu was clicked, so we just close it and propagate
+ this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ } else {
+ // the menuItem element was clicked, so just toggle the open/close and stop propagation
+ this.trySetState({ submenuOpen: active ? !this.state.submenuOpen : true })
+ e.stopPropagation()
+ }
}
+ }
+
+ private handleClick = e => {
+ this.performClick(e)
_.invoke(this.props, 'onClick', e, this.props)
}
@@ -294,26 +307,11 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { menu } = this.props
const { submenuOpen } = this.state
if (menu && !submenuOpen) {
- this.handleClick(e)
+ this.setState({ submenuOpen: true })
e.stopPropagation()
e.preventDefault()
}
}
-
- private handleSubmenuClicked = (e: React.SyntheticEvent, props: MenuProps) => {
- this.trySetState({ submenuOpen: false })
- _.invoke(props, 'onClick', props)
- }
-
- private handleSubmenuKeyDown = (e: React.SyntheticEvent, props: MenuProps) => {
- if (
- keyboardKey.getCode(e) === keyboardKey.Enter ||
- keyboardKey.getCode(e) === keyboardKey.Spacebar
- ) {
- this.trySetState({ submenuOpen: false })
- }
- _.invoke(props, 'onKeyDown', props)
- }
}
MenuItem.create = createShorthandFactory(MenuItem, 'content')
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index c3463ced39..30e04658e7 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -34,24 +34,22 @@ const menuItemBehavior: Accessibility = (props: any) => ({
handledProps: ['aria-label', 'aria-labelledby', 'aria-describedby'],
keyActions: {
- anchor: {
+ anchor: {},
+ wrapper: {
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
-
- openSubmenu: {
- keyCombinations: [
- { keyCode: props.vertical ? keyboardKey.ArrowRight : keyboardKey.ArrowDown },
- ],
- },
- },
- wrapper: {
closeMenu: {
keyCombinations: [{ keyCode: keyboardKey.Escape }, { keyCode: keyboardKey.ArrowRight }],
},
closeSubmenu: {
keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
},
+ openSubmenu: {
+ keyCombinations: [
+ { keyCode: props.vertical ? keyboardKey.ArrowRight : keyboardKey.ArrowDown },
+ ],
+ },
},
},
})
diff --git a/src/lib/getKeyDownHandlers.ts b/src/lib/getKeyDownHandlers.ts
index a4dfe9f6e9..f4202ebdb7 100644
--- a/src/lib/getKeyDownHandlers.ts
+++ b/src/lib/getKeyDownHandlers.ts
@@ -41,8 +41,7 @@ const getKeyDownHandlers = (
eventHandler && eventHandler(event)
})
-
- _.invoke(props, 'onKeyDown', event)
+ _.invoke(props, 'onKeyDown', event, props)
},
}
}
From d85b1645c46e6b50cdcd7ca65b6cbde74141e4da Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 12 Dec 2018 16:27:49 +0100
Subject: [PATCH 16/55] -added setActiveIndex callback and removed onKeyDown in
the creation of MenuItems in the Menu component
---
src/components/Menu/Menu.tsx | 13 ++-----------
src/components/Menu/MenuItem.tsx | 4 ++--
.../Behaviors/Menu/menuItemBehavior.ts | 1 -
3 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 6a6d5d33dd..f5f9eb863a 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -1,7 +1,6 @@
import * as _ from 'lodash'
import * as PropTypes from 'prop-types'
import * as React from 'react'
-import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
@@ -112,16 +111,8 @@ class Menu extends AutoControlledComponent, any> {
_.invoke(predefinedProps, 'onClick', e, itemProps)
},
- onKeyDown: (e, itemProps) => {
- if (
- keyboardKey.getCode(e) === keyboardKey.Enter ||
- keyboardKey.getCode(e) === keyboardKey.Spacebar
- ) {
- // TODO check why itemProps is undefined (should be fixed by the changes added in getKeyDownHandler.ts
- const { index } = predefinedProps
- this.trySetState({ activeIndex: index })
- }
- _.invoke(predefinedProps, 'onKeyDown', predefinedProps)
+ setActiveIndex: index => {
+ this.trySetState({ activeIndex: index })
},
})
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 7d80e02030..b4094b656b 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -179,7 +179,6 @@ class MenuItem extends AutoControlledComponent, MenuIt
onBlur={this.handleBlur}
onFocus={this.handleFocus}
{...accessibility.attributes.anchor}
- {...accessibility.keyHandlers.anchor}
{...rest}
{...!wrapper && { onClick: this.handleClick }}
ref={this.itemRef}
@@ -231,7 +230,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
protected actionHandlers: AccessibilityActionHandlers = {
- performClick: event => this.performClick(event),
+ performClick: event => this.handleClick(event),
openSubmenu: event => this.openSubmenu(event),
closeMenu: event => this.closeMenu(event),
closeSubmenu: event => this.closeSubmenu(event),
@@ -308,6 +307,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { submenuOpen } = this.state
if (menu && !submenuOpen) {
this.setState({ submenuOpen: true })
+ _.invoke(this.props, 'setActiveIndex', this.props.index) // or call onClick from the client... => Menu.onClick will change the active index
e.stopPropagation()
e.preventDefault()
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index 30e04658e7..3c267d32af 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -34,7 +34,6 @@ const menuItemBehavior: Accessibility = (props: any) => ({
handledProps: ['aria-label', 'aria-labelledby', 'aria-describedby'],
keyActions: {
- anchor: {},
wrapper: {
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
From e091af20184a8623ce2f60ad3d614aa3b957dd87 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 12 Dec 2018 20:31:01 +0100
Subject: [PATCH 17/55] -right arrow key is closing the submenus and goes to
the next element if the menu is horizontal, or is focusing the first MenuItem
if it is vertical
---
src/components/Menu/Menu.tsx | 5 +++++
src/components/Menu/MenuItem.tsx | 13 +++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index f5f9eb863a..8811d14f69 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -60,6 +60,8 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/** A vertical menu displays elements vertically. */
vertical?: boolean
+
+ parentRef: React.RefObject
}
/**
@@ -88,6 +90,7 @@ class Menu extends AutoControlledComponent, any> {
secondary: customPropTypes.every([customPropTypes.disallow(['primary']), PropTypes.bool]),
underlined: PropTypes.bool,
vertical: PropTypes.bool,
+ parentRef: PropTypes.any,
}
static defaultProps = {
@@ -126,6 +129,7 @@ class Menu extends AutoControlledComponent, any> {
secondary,
underlined,
vertical,
+ parentRef,
} = this.props
const { activeIndex } = this.state
@@ -146,6 +150,7 @@ class Menu extends AutoControlledComponent, any> {
...(active && {
styles: { position: 'relative' },
}),
+ parentRef,
},
overrideProps: this.handleItemOverrides,
})
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index b4094b656b..fc93ba467f 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -98,6 +98,8 @@ export interface MenuItemProps
/** Default submenu open */
defaultSubmenuOpen?: boolean
+
+ parentRef: React.RefObject
}
export interface MenuItemState {
@@ -134,6 +136,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
menu: customPropTypes.itemShorthand,
submenuOpen: PropTypes.bool,
defaultSubmenuOpen: PropTypes.bool,
+ parentRef: PropTypes.any,
}
static defaultProps = {
@@ -199,6 +202,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
primary,
secondary,
styles: styles.menu,
+ parentRef: this.itemRef,
},
})
: null
@@ -286,10 +290,15 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private closeMenu = e => {
- const { menu } = this.props
+ const { menu, parentRef } = this.props
const { submenuOpen } = this.state
if (menu && submenuOpen) {
- this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ this.setState({ submenuOpen: false }, () => {
+ // I this is the first MenuItem and it is vertical
+ if (!parentRef && this.props.vertical) {
+ focusAsync(this.itemRef.current)
+ }
+ })
}
}
From 279c7fa5dbc6d7e10be459372875d29afdac5f73 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 12 Dec 2018 20:43:49 +0100
Subject: [PATCH 18/55] -handled left arrow key
---
src/components/Menu/Menu.tsx | 2 +-
src/components/Menu/MenuItem.tsx | 15 +++++++++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 8811d14f69..7965cda7fe 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -61,7 +61,7 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/** A vertical menu displays elements vertically. */
vertical?: boolean
- parentRef: React.RefObject
+ parentRef?: React.RefObject
}
/**
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index fc93ba467f..b5624a8c4e 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -99,7 +99,7 @@ export interface MenuItemProps
/** Default submenu open */
defaultSubmenuOpen?: boolean
- parentRef: React.RefObject
+ parentRef?: React.RefObject
}
export interface MenuItemState {
@@ -303,11 +303,18 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private closeSubmenu = e => {
- const { menu } = this.props
+ const { menu, parentRef } = this.props
const { submenuOpen } = this.state
+ const shouldStopPropagation = parentRef || this.props.vertical
if (menu && submenuOpen) {
- this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
- e.stopPropagation()
+ this.setState({ submenuOpen: false }, () => {
+ if (shouldStopPropagation) {
+ focusAsync(this.itemRef.current)
+ }
+ })
+ if (shouldStopPropagation) {
+ e.stopPropagation()
+ }
}
}
From 8cc93911936aa10931fa1d06d9ddd56d8723ce6f Mon Sep 17 00:00:00 2001
From: manajdov
Date: Thu, 13 Dec 2018 13:18:30 +0100
Subject: [PATCH 19/55] -changed ref -focus trap wip
---
src/components/Menu/MenuItem.tsx | 49 +++++++++++++++++++++++---------
1 file changed, 36 insertions(+), 13 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index b5624a8c4e..a9b27cc278 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -16,7 +16,6 @@ import {
} from '../../lib'
import Icon from '../Icon/Icon'
import Menu from '../Menu/Menu'
-import Ref from '../Ref/Ref'
import Slot from '../Slot/Slot'
import { menuItemBehavior, submenuBehavior } from '../../lib/accessibility'
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
@@ -99,6 +98,7 @@ export interface MenuItemProps
/** Default submenu open */
defaultSubmenuOpen?: boolean
+ setActiveIndex?: (idx: string | number) => void
parentRef?: React.RefObject
}
@@ -136,6 +136,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
menu: customPropTypes.itemShorthand,
submenuOpen: PropTypes.bool,
defaultSubmenuOpen: PropTypes.bool,
+ setActiveIndex: PropTypes.func,
parentRef: PropTypes.any,
}
@@ -153,20 +154,24 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private outsideClickSubscription = EventStack.noSubscription
+ private outsideFocusSubscription = EventStack.noSubscription
private submenuDomElement = null
private itemRef = React.createRef()
public componentDidMount() {
this.updateOutsideClickSubscription()
+ this.updateOutsideFocusSubscription()
}
public componentDidUpdate() {
this.updateOutsideClickSubscription()
+ this.updateOutsideFocusSubscription()
}
public componentWillUnmount() {
this.outsideClickSubscription.unsubscribe()
+ this.outsideFocusSubscription.unsubscribe()
}
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
@@ -208,13 +213,13 @@ class MenuItem extends AutoControlledComponent, MenuIt
: null
const maybeSubmenuWithRef = maybeSubmenu ? (
- [ {
+ ] {
this.submenuDomElement = domElement
}}
>
{maybeSubmenu}
-
+
) : null
if (wrapper) {
@@ -245,19 +250,37 @@ class MenuItem extends AutoControlledComponent, MenuIt
if (this.props.menu && this.state.submenuOpen) {
setTimeout(() => {
- this.outsideClickSubscription = EventStack.subscribe('click', e => {
- if (
- this.itemRef &&
- (!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
- (!this.submenuDomElement || !this.submenuDomElement.contains(e.target))
- ) {
- this.state.submenuOpen && this.trySetState({ submenuOpen: false })
- }
- })
+ this.outsideClickSubscription = EventStack.subscribe(
+ 'click',
+ this.outsideClickOrFocusHandler,
+ )
})
}
}
+ private updateOutsideFocusSubscription() {
+ this.outsideFocusSubscription.unsubscribe()
+
+ if (this.props.menu && this.state.submenuOpen) {
+ setTimeout(() => {
+ this.outsideFocusSubscription = EventStack.subscribe(
+ 'focus',
+ this.outsideClickOrFocusHandler,
+ )
+ })
+ }
+ }
+
+ private outsideClickOrFocusHandler = e => {
+ if (
+ this.itemRef &&
+ (!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
+ (!this.submenuDomElement || !this.submenuDomElement.contains(e.target))
+ ) {
+ this.state.submenuOpen && this.trySetState({ submenuOpen: false })
+ }
+ }
+
private performClick = e => {
const { active, menu } = this.props
if (menu) {
From 920f2533f706fd07dc619069aafeaf8db09bb664 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Thu, 13 Dec 2018 18:29:55 +0100
Subject: [PATCH 20/55] -added Ref component instead of using the itemRef on
the ElementType -removed subscription for focus
---
src/components/Menu/MenuItem.tsx | 45 +++++++++++---------------------
1 file changed, 15 insertions(+), 30 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index a9b27cc278..9f1588f6e8 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -22,6 +22,7 @@ import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibil
import IsFromKeyboard from '../../lib/isFromKeyboard'
import { ComponentEventHandler, Extendable, ShorthandValue } from '../../../types/utils'
import { focusAsync } from '../../lib/accessibility/FocusZone'
+import Ref from '../Ref/Ref'
export interface MenuItemProps
extends UIComponentProps,
@@ -154,24 +155,20 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private outsideClickSubscription = EventStack.noSubscription
- private outsideFocusSubscription = EventStack.noSubscription
private submenuDomElement = null
private itemRef = React.createRef()
public componentDidMount() {
this.updateOutsideClickSubscription()
- this.updateOutsideFocusSubscription()
}
public componentDidUpdate() {
this.updateOutsideClickSubscription()
- this.updateOutsideFocusSubscription()
}
public componentWillUnmount() {
this.outsideClickSubscription.unsubscribe()
- this.outsideFocusSubscription.unsubscribe()
}
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
@@ -191,11 +188,15 @@ class MenuItem extends AutoControlledComponent, MenuIt
{...!wrapper && { onClick: this.handleClick }}
ref={this.itemRef}
>
- {icon &&
- Icon.create(this.props.icon, {
- defaultProps: { xSpacing: !!content ? 'after' : 'none' },
- })}
- {content}
+ [
+
+ {icon &&
+ Icon.create(this.props.icon, {
+ defaultProps: { xSpacing: !!content ? 'after' : 'none' },
+ })}
+ {content}
+
+ ]
)
const maybeSubmenu =
@@ -213,13 +214,13 @@ class MenuItem extends AutoControlledComponent, MenuIt
: null
const maybeSubmenuWithRef = maybeSubmenu ? (
- {
+ [ {
this.submenuDomElement = domElement
}}
>
{maybeSubmenu}
- ]
+
) : null
if (wrapper) {
@@ -250,28 +251,12 @@ class MenuItem extends AutoControlledComponent, MenuIt
if (this.props.menu && this.state.submenuOpen) {
setTimeout(() => {
- this.outsideClickSubscription = EventStack.subscribe(
- 'click',
- this.outsideClickOrFocusHandler,
- )
+ this.outsideClickSubscription = EventStack.subscribe('click', this.outsideClickHandler)
})
}
}
- private updateOutsideFocusSubscription() {
- this.outsideFocusSubscription.unsubscribe()
-
- if (this.props.menu && this.state.submenuOpen) {
- setTimeout(() => {
- this.outsideFocusSubscription = EventStack.subscribe(
- 'focus',
- this.outsideClickOrFocusHandler,
- )
- })
- }
- }
-
- private outsideClickOrFocusHandler = e => {
+ private outsideClickHandler = e => {
if (
this.itemRef &&
(!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
From 9e3763ebcf21aed5cbf8ae5a8f0e98289fd46ab9 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Thu, 13 Dec 2018 18:53:02 +0100
Subject: [PATCH 21/55] -fixes
---
src/components/Menu/MenuItem.tsx | 7 ++++---
src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts | 2 +-
test/specs/components/Menu/MenuItem-test.tsx | 3 ---
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 342bedc279..d280d14590 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -173,7 +173,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
- const { children, content, icon, wrapper, menu, primary, secondary, active } = this.props
+ const { children, content, icon, wrapper, menu, primary, secondary, active, key } = this.props
const { submenuOpen } = this.state
@@ -226,9 +226,10 @@ class MenuItem extends AutoControlledComponent, MenuIt
if (wrapper) {
return Slot.create(wrapper, {
defaultProps: {
+ key,
className: cx('ui-menu__item__wrapper', classes.wrapper),
- ...accessibility.attributes.wrapper,
- ...accessibility.keyHandlers.wrapper,
+ ...accessibility.attributes.root,
+ ...accessibility.keyHandlers.root,
},
overrideProps: () => ({
children: [menuItemInner, maybeSubmenuWithRef],
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index 3c267d32af..4b3e0e4efd 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -34,7 +34,7 @@ const menuItemBehavior: Accessibility = (props: any) => ({
handledProps: ['aria-label', 'aria-labelledby', 'aria-describedby'],
keyActions: {
- wrapper: {
+ root: {
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
diff --git a/test/specs/components/Menu/MenuItem-test.tsx b/test/specs/components/Menu/MenuItem-test.tsx
index d85082d9e4..820f658bc6 100644
--- a/test/specs/components/Menu/MenuItem-test.tsx
+++ b/test/specs/components/Menu/MenuItem-test.tsx
@@ -7,9 +7,6 @@ import { toolbarButtonBehavior, tabBehavior } from '../../../../src/lib/accessib
describe('MenuItem', () => {
isConformant(MenuItem, {
- eventTargets: {
- onClick: 'a',
- },
usesWrapperSlot: true,
})
From afbdbfaf428c8efe24fa488e85d7422d8d57b5d0 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Thu, 13 Dec 2018 19:21:12 +0100
Subject: [PATCH 22/55] -moved ref //TODO: figure out tests failing
---
src/components/Menu/MenuItem.tsx | 34 +++++++++++++++-----------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index d280d14590..efea58531b 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -180,24 +180,22 @@ class MenuItem extends AutoControlledComponent, MenuIt
const menuItemInner = childrenExist(children) ? (
children
) : (
-
- [
-
- {icon &&
- Icon.create(this.props.icon, {
- defaultProps: { xSpacing: !!content ? 'after' : 'none' },
- })}
- {content}
-
- ]
-
+ [
+
+ {icon &&
+ Icon.create(this.props.icon, {
+ defaultProps: { xSpacing: !!content ? 'after' : 'none' },
+ })}
+ {content}
+
+ ]
)
const maybeSubmenu =
menu && active && submenuOpen
From d3ab05528861ebce2bb66fbedc484cff30ee1417 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 11:26:55 +0100
Subject: [PATCH 23/55] -close menu on outside focus
---
src/components/Menu/MenuItem.tsx | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index efea58531b..90687fbeed 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -232,12 +232,19 @@ class MenuItem extends AutoControlledComponent, MenuIt
overrideProps: () => ({
children: [menuItemInner, maybeSubmenuWithRef],
onClick: this.handleClick,
+ onBlur: this.handleWrapperBlur,
}),
})
}
return menuItemInner
}
+ private handleWrapperBlur = e => {
+ if (!this.props.parentRef && !e.currentTarget.contains(e.relatedTarget)) {
+ this.setState({ submenuOpen: false })
+ }
+ }
+
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
openSubmenu: event => this.openSubmenu(event),
From 5653db39f1967a19b18083576fa1af31483cfce1 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 11:29:44 +0100
Subject: [PATCH 24/55] -improved comments
---
src/components/Menu/MenuItem.tsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 90687fbeed..6ab1a005b1 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -276,10 +276,10 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { active, menu } = this.props
if (menu) {
if (this.submenuDomElement && this.submenuDomElement.contains(e.target)) {
- // submenu was clicked, so we just close it and propagate
+ // submenu was clicked => close it and propagate
this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
} else {
- // the menuItem element was clicked, so just toggle the open/close and stop propagation
+ // the menuItem element was clicked => toggle the open/close and stop propagation
this.trySetState({ submenuOpen: active ? !this.state.submenuOpen : true })
e.stopPropagation()
}
@@ -308,7 +308,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { submenuOpen } = this.state
if (menu && submenuOpen) {
this.setState({ submenuOpen: false }, () => {
- // I this is the first MenuItem and it is vertical
+ // this is the first MenuItem and it is vertical
if (!parentRef && this.props.vertical) {
focusAsync(this.itemRef.current)
}
@@ -337,7 +337,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { submenuOpen } = this.state
if (menu && !submenuOpen) {
this.setState({ submenuOpen: true })
- _.invoke(this.props, 'setActiveIndex', this.props.index) // or call onClick from the client... => Menu.onClick will change the active index
+ _.invoke(this.props, 'setActiveIndex', this.props.index)
e.stopPropagation()
e.preventDefault()
}
From 4bb8ee8023a28b0910bbb60beabf2c8fbd9eca53 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 13:04:21 +0100
Subject: [PATCH 25/55] -fixed escape key not focusing the active element
-changed parentRef to inSubmenu boolean
---
src/components/Menu/Menu.tsx | 9 +++----
src/components/Menu/MenuItem.tsx | 40 ++++++++++++++++++--------------
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index be9ac3f99b..87673a0adc 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -62,7 +62,8 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/** A vertical menu displays elements vertically. */
vertical?: boolean
- parentRef?: React.RefObject
+ /** Indicates whether the element is part of submenu. */
+ inSubmenu?: boolean
}
/**
@@ -91,7 +92,7 @@ class Menu extends AutoControlledComponent, any> {
secondary: customPropTypes.every([customPropTypes.disallow(['primary']), PropTypes.bool]),
underlined: PropTypes.bool,
vertical: PropTypes.bool,
- parentRef: PropTypes.any,
+ inSubmenu: PropTypes.bool,
}
static defaultProps = {
@@ -130,7 +131,7 @@ class Menu extends AutoControlledComponent, any> {
secondary,
underlined,
vertical,
- parentRef,
+ inSubmenu,
} = this.props
const { activeIndex } = this.state
@@ -151,7 +152,7 @@ class Menu extends AutoControlledComponent, any> {
...(active && {
styles: { position: 'relative' },
}),
- parentRef,
+ inSubmenu,
},
overrideProps: this.handleItemOverrides,
})
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 6ab1a005b1..131c20e4c1 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -2,6 +2,7 @@ import * as _ from 'lodash'
import cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
+import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
@@ -100,8 +101,11 @@ export interface MenuItemProps
/** Default submenu open */
defaultSubmenuOpen?: boolean
- setActiveIndex?: (idx: string | number) => void
- parentRef?: React.RefObject
+ /** Callback for setting the current menu item as active element in the menu. */
+ setActiveIndex?: (idx: number) => void
+
+ /** Indicates whether the menu item is part of submenu. */
+ inSubmenu?: boolean
}
export interface MenuItemState {
@@ -139,7 +143,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
submenuOpen: PropTypes.bool,
defaultSubmenuOpen: PropTypes.bool,
setActiveIndex: PropTypes.func,
- parentRef: PropTypes.any,
+ inSubmenu: PropTypes.bool,
}
static defaultProps = {
@@ -173,7 +177,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
- const { children, content, icon, wrapper, menu, primary, secondary, active, key } = this.props
+ const { children, content, icon, wrapper, menu, primary, secondary, active } = this.props
const { submenuOpen } = this.state
@@ -206,7 +210,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
primary,
secondary,
styles: styles.menu,
- parentRef: this.itemRef,
+ inSubmenu: true,
},
})
: null
@@ -224,13 +228,17 @@ class MenuItem extends AutoControlledComponent, MenuIt
if (wrapper) {
return Slot.create(wrapper, {
defaultProps: {
- key,
className: cx('ui-menu__item__wrapper', classes.wrapper),
...accessibility.attributes.root,
...accessibility.keyHandlers.root,
},
overrideProps: () => ({
- children: [menuItemInner, maybeSubmenuWithRef],
+ children: (
+ <>
+ {menuItemInner}
+ {maybeSubmenuWithRef}
+ >
+ ),
onClick: this.handleClick,
onBlur: this.handleWrapperBlur,
}),
@@ -240,7 +248,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private handleWrapperBlur = e => {
- if (!this.props.parentRef && !e.currentTarget.contains(e.relatedTarget)) {
+ if (!this.props.inSubmenu && !e.currentTarget.contains(e.relatedTarget)) {
this.setState({ submenuOpen: false })
}
}
@@ -304,22 +312,20 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private closeMenu = e => {
- const { menu, parentRef } = this.props
+ const { menu, inSubmenu } = this.props
const { submenuOpen } = this.state
if (menu && submenuOpen) {
- this.setState({ submenuOpen: false }, () => {
- // this is the first MenuItem and it is vertical
- if (!parentRef && this.props.vertical) {
- focusAsync(this.itemRef.current)
- }
- })
+ this.setState({ submenuOpen: false })
+ if (!inSubmenu && (keyboardKey.getCode(e) === keyboardKey.Escape || this.props.vertical)) {
+ focusAsync(this.itemRef.current)
+ }
}
}
private closeSubmenu = e => {
- const { menu, parentRef } = this.props
+ const { menu, inSubmenu } = this.props
const { submenuOpen } = this.state
- const shouldStopPropagation = parentRef || this.props.vertical
+ const shouldStopPropagation = inSubmenu || this.props.vertical
if (menu && submenuOpen) {
this.setState({ submenuOpen: false }, () => {
if (shouldStopPropagation) {
From fac416123915f83afb4f5ce52d77450c73f66a5c Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 14:05:08 +0100
Subject: [PATCH 26/55] -fixing key problems -added dependency for generating
id
---
package.json | 3 ++-
src/components/Menu/MenuItem.tsx | 3 ++-
yarn.lock | 5 +++++
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 75c2f3976d..b31e653b04 100644
--- a/package.json
+++ b/package.json
@@ -70,7 +70,8 @@
"prop-types": "^15.6.1",
"react-fela": "^7.2.0",
"react-is": "^16.6.3",
- "react-popper": "^1.0.2"
+ "react-popper": "^1.0.2",
+ "uniqid": "^5.0.3"
},
"devDependencies": {
"@babel/standalone": "^7.1.0",
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 131c20e4c1..fb43331cda 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -3,6 +3,7 @@ import cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
import * as keyboardKey from 'keyboard-key'
+import * as uniqid from 'uniqid'
import {
AutoControlledComponent,
@@ -149,7 +150,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
static defaultProps = {
as: 'a',
accessibility: menuItemBehavior as Accessibility,
- wrapper: { as: 'li' },
+ wrapper: { as: 'li', key: uniqid() },
}
static autoControlledProps = ['submenuOpen']
diff --git a/yarn.lock b/yarn.lock
index 25e665d8cb..43ec421ea6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10479,6 +10479,11 @@ union-value@^1.0.0:
is-extendable "^0.1.1"
set-value "^0.4.3"
+uniqid@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-5.0.3.tgz#917968310edc868d50df6c44f783f32c7d80fac1"
+ integrity sha512-R2qx3X/LYWSdGRaluio4dYrPXAJACTqyUjuyXHoJLBUOIfmMcnYOyY2d6Y4clZcIz5lK6ZaI0Zzmm0cPfsIqzQ==
+
unique-filename@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
From 0e1941da84b769d932d7266dfafc46fbe6140fc8 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 16:16:54 +0100
Subject: [PATCH 27/55] -refactored submenuRef element
---
src/components/Menu/MenuItem.tsx | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index fb43331cda..674a8bada4 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -203,8 +203,13 @@ class MenuItem extends AutoControlledComponent, MenuIt
)
const maybeSubmenu =
- menu && active && submenuOpen
- ? Menu.create(menu, {
+ menu && active && submenuOpen ? (
+ [ {
+ this.submenuDomElement = domElement
+ }}
+ >
+ {Menu.create(menu, {
defaultProps: {
accessibility: submenuBehavior,
vertical: true,
@@ -213,18 +218,9 @@ class MenuItem extends AutoControlledComponent, MenuIt
styles: styles.menu,
inSubmenu: true,
},
- })
- : null
-
- const maybeSubmenuWithRef = maybeSubmenu ? (
- ][ {
- this.submenuDomElement = domElement
- }}
- >
- {maybeSubmenu}
- ]
- ) : null
+ })}
+
+ ) : null
if (wrapper) {
return Slot.create(wrapper, {
@@ -237,7 +233,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
children: (
<>
{menuItemInner}
- {maybeSubmenuWithRef}
+ {maybeSubmenu}
>
),
onClick: this.handleClick,
From 168b16ab0dc83e83eac66a6f8013ce1b7235b469 Mon Sep 17 00:00:00 2001
From: olfedias
Date: Fri, 14 Dec 2018 18:34:38 +0200
Subject: [PATCH 28/55] fix broken tests
---
package.json | 3 +--
src/components/Menu/MenuItem.tsx | 3 +--
test/specs/commonTests/isConformant.tsx | 21 ++++++++++++--------
test/specs/components/Menu/MenuItem-test.tsx | 12 ++++++++++-
yarn.lock | 5 -----
5 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/package.json b/package.json
index b31e653b04..75c2f3976d 100644
--- a/package.json
+++ b/package.json
@@ -70,8 +70,7 @@
"prop-types": "^15.6.1",
"react-fela": "^7.2.0",
"react-is": "^16.6.3",
- "react-popper": "^1.0.2",
- "uniqid": "^5.0.3"
+ "react-popper": "^1.0.2"
},
"devDependencies": {
"@babel/standalone": "^7.1.0",
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 674a8bada4..9c02cc6e87 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -3,7 +3,6 @@ import cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
import * as keyboardKey from 'keyboard-key'
-import * as uniqid from 'uniqid'
import {
AutoControlledComponent,
@@ -150,7 +149,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
static defaultProps = {
as: 'a',
accessibility: menuItemBehavior as Accessibility,
- wrapper: { as: 'li', key: uniqid() },
+ wrapper: { as: 'li' },
}
static autoControlledProps = ['submenuOpen']
diff --git a/test/specs/commonTests/isConformant.tsx b/test/specs/commonTests/isConformant.tsx
index 5836b629b2..055701bcab 100644
--- a/test/specs/commonTests/isConformant.tsx
+++ b/test/specs/commonTests/isConformant.tsx
@@ -19,6 +19,7 @@ import { FOCUSZONE_WRAP_ATTRIBUTE } from 'src/lib/accessibility/FocusZone/focusU
export interface Conformant {
eventTargets?: object
+ nestingLevel?: number
requiredProps?: object
exportedAtTopLevel?: boolean
rendersPortal?: boolean
@@ -39,6 +40,7 @@ export default (Component, options: Conformant = {}) => {
const {
eventTargets = {},
exportedAtTopLevel = true,
+ nestingLevel = 0,
requiredProps = {},
rendersPortal = false,
usesWrapperSlot = false,
@@ -51,9 +53,12 @@ export default (Component, options: Conformant = {}) => {
const getComponent = (wrapper: ReactWrapper) => {
// FelaTheme wrapper and the component itself:
let component = wrapper
- .childAt(0)
- .childAt(0)
- .childAt(0)
+
+ // TODO: Describe magic numbers
+ _.times(nestingLevel + 3, () => {
+ component = component.childAt(0)
+ })
+
if (component.type() === FocusZone) {
// `component` is
component = component.childAt(0) // skip through
@@ -63,10 +68,9 @@ export default (Component, options: Conformant = {}) => {
}
if (usesWrapperSlot) {
- component = component
- .childAt(0)
- .childAt(0)
- .childAt(0)
+ _.times(3, () => {
+ component = component.childAt(0)
+ })
}
return component
@@ -212,7 +216,8 @@ export default (Component, options: Conformant = {}) => {
const wrapper = mount()
const component = getComponent(wrapper)
-
+ // console.log(wrapper.debug())
+ // console.log(component.type())
try {
expect(component.type()).toEqual(MyComponent)
} catch (err) {
diff --git a/test/specs/components/Menu/MenuItem-test.tsx b/test/specs/components/Menu/MenuItem-test.tsx
index 820f658bc6..07c1c86f73 100644
--- a/test/specs/components/Menu/MenuItem-test.tsx
+++ b/test/specs/components/Menu/MenuItem-test.tsx
@@ -7,6 +7,10 @@ import { toolbarButtonBehavior, tabBehavior } from '../../../../src/lib/accessib
describe('MenuItem', () => {
isConformant(MenuItem, {
+ eventTargets: {
+ onClick: '.ui-menu__item__wrapper',
+ },
+ nestingLevel: 2,
usesWrapperSlot: true,
})
@@ -16,7 +20,13 @@ describe('MenuItem', () => {
.hostNodes()
expect(menuItem.is('li')).toBe(true)
- expect(menuItem.childAt(0).is('a')).toBe(true)
+ expect(
+ menuItem
+ .childAt(0)
+ .childAt(0)
+ .childAt(0)
+ .is('a'),
+ ).toBe(true)
expect(menuItem.text()).toBe('Home')
})
diff --git a/yarn.lock b/yarn.lock
index 43ec421ea6..25e665d8cb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10479,11 +10479,6 @@ union-value@^1.0.0:
is-extendable "^0.1.1"
set-value "^0.4.3"
-uniqid@^5.0.3:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-5.0.3.tgz#917968310edc868d50df6c44f783f32c7d80fac1"
- integrity sha512-R2qx3X/LYWSdGRaluio4dYrPXAJACTqyUjuyXHoJLBUOIfmMcnYOyY2d6Y4clZcIz5lK6ZaI0Zzmm0cPfsIqzQ==
-
unique-filename@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
From 2baa1cb1bdf114378268cc83f241a1dfad81aa25 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Fri, 14 Dec 2018 18:20:38 +0100
Subject: [PATCH 29/55] -added comments in the tests -changed the
submenuDomElement so submenuRef
---
src/components/Menu/MenuItem.tsx | 12 ++++--------
test/specs/commonTests/isConformant.tsx | 16 ++++++++++++----
test/specs/components/Menu/MenuItem-test.tsx | 2 ++
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 9c02cc6e87..9f1f36f9f6 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -161,7 +161,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
private outsideClickSubscription = EventStack.noSubscription
- private submenuDomElement = null
+ private submenuRef = React.createRef()
private itemRef = React.createRef()
public componentDidMount() {
@@ -203,11 +203,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
)
const maybeSubmenu =
menu && active && submenuOpen ? (
- [ {
- this.submenuDomElement = domElement
- }}
- >
+ ][
{Menu.create(menu, {
defaultProps: {
accessibility: submenuBehavior,
@@ -270,7 +266,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
if (
this.itemRef &&
(!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
- (!this.submenuDomElement || !this.submenuDomElement.contains(e.target))
+ (!this.submenuRef.current || !this.submenuRef.current.contains(e.target))
) {
this.state.submenuOpen && this.trySetState({ submenuOpen: false })
}
@@ -279,7 +275,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
private performClick = e => {
const { active, menu } = this.props
if (menu) {
- if (this.submenuDomElement && this.submenuDomElement.contains(e.target)) {
+ if (this.submenuRef.current && this.submenuRef.current.contains(e.target)) {
// submenu was clicked => close it and propagate
this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
} else {
diff --git a/test/specs/commonTests/isConformant.tsx b/test/specs/commonTests/isConformant.tsx
index 055701bcab..a024a4391e 100644
--- a/test/specs/commonTests/isConformant.tsx
+++ b/test/specs/commonTests/isConformant.tsx
@@ -54,13 +54,17 @@ export default (Component, options: Conformant = {}) => {
// FelaTheme wrapper and the component itself:
let component = wrapper
- // TODO: Describe magic numbers
+ /**
+ * The wrapper is mounted with Provider, so in total there are three HOC components
+ * that we want to get rid of: ThemeProvider, the actual Component and FelaTheme,
+ * in order to be able to get to the actual rendered result of the component we are testing
+ */
_.times(nestingLevel + 3, () => {
component = component.childAt(0)
})
if (component.type() === FocusZone) {
- // `component` is
+ // another HOC component is added: FocuZone
component = component.childAt(0) // skip through
if (component.prop(FOCUSZONE_WRAP_ATTRIBUTE)) {
component = component.childAt(0) // skip the additional wrap ] of the FocusZone
@@ -68,6 +72,11 @@ export default (Component, options: Conformant = {}) => {
}
if (usesWrapperSlot) {
+ /**
+ * If there is a wrapper slot, then again, we need to get rid of all three HOC components:
+ * ThemeProvider, Wrapper (Slot), and FelaTheme in order to be able to get to the actual
+ * rendered result of the component we are testing
+ */
_.times(3, () => {
component = component.childAt(0)
})
@@ -216,8 +225,7 @@ export default (Component, options: Conformant = {}) => {
const wrapper = mount(
)
const component = getComponent(wrapper)
- // console.log(wrapper.debug())
- // console.log(component.type())
+
try {
expect(component.type()).toEqual(MyComponent)
} catch (err) {
diff --git a/test/specs/components/Menu/MenuItem-test.tsx b/test/specs/components/Menu/MenuItem-test.tsx
index 07c1c86f73..c24ea2547c 100644
--- a/test/specs/components/Menu/MenuItem-test.tsx
+++ b/test/specs/components/Menu/MenuItem-test.tsx
@@ -10,6 +10,7 @@ describe('MenuItem', () => {
eventTargets: {
onClick: '.ui-menu__item__wrapper',
},
+ // The ElementType is wrapped with Ref, which is adding two HOC in total
nestingLevel: 2,
usesWrapperSlot: true,
})
@@ -20,6 +21,7 @@ describe('MenuItem', () => {
.hostNodes()
expect(menuItem.is('li')).toBe(true)
+ // The ElementType is wrapped with Ref, which is adding two HOC in total, that's why we need the three childAt(0) usages
expect(
menuItem
.childAt(0)
From f61533be26a5d5638705c2bb866b7db8129f922a Mon Sep 17 00:00:00 2001
From: manajdov
Date: Sun, 16 Dec 2018 15:11:52 +0100
Subject: [PATCH 30/55] -renamed inSubmenu to submenu prop in the Menu
---
src/components/Menu/Menu.tsx | 10 +++++-----
src/components/Menu/MenuItem.tsx | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 87673a0adc..19131b0c6e 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -62,8 +62,8 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
/** A vertical menu displays elements vertically. */
vertical?: boolean
- /** Indicates whether the element is part of submenu. */
- inSubmenu?: boolean
+ /** Indicates whether the menu is submenu. */
+ submenu?: boolean
}
/**
@@ -92,7 +92,7 @@ class Menu extends AutoControlledComponent, any> {
secondary: customPropTypes.every([customPropTypes.disallow(['primary']), PropTypes.bool]),
underlined: PropTypes.bool,
vertical: PropTypes.bool,
- inSubmenu: PropTypes.bool,
+ submenu: PropTypes.bool,
}
static defaultProps = {
@@ -131,7 +131,7 @@ class Menu extends AutoControlledComponent, any> {
secondary,
underlined,
vertical,
- inSubmenu,
+ submenu,
} = this.props
const { activeIndex } = this.state
@@ -152,7 +152,7 @@ class Menu extends AutoControlledComponent, any> {
...(active && {
styles: { position: 'relative' },
}),
- inSubmenu,
+ inSubmenu: submenu,
},
overrideProps: this.handleItemOverrides,
})
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 9f1f36f9f6..170dbe90a1 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -211,7 +211,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
primary,
secondary,
styles: styles.menu,
- inSubmenu: true,
+ submenu: true,
},
})}
From 636e51daa38f8e14ae1806311159b0c5441e0f68 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Sun, 16 Dec 2018 15:29:58 +0100
Subject: [PATCH 31/55] -fixed with the auto-controlled prop in the Menu
---
src/components/Menu/Menu.tsx | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 19131b0c6e..9baa53c8ae 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -104,13 +104,9 @@ class Menu extends AutoControlledComponent, any> {
static Item = MenuItem
- state = {
- activeIndex: '',
- }
-
handleItemOverrides = predefinedProps => ({
onClick: (e, itemProps) => {
- const { index } = predefinedProps
+ const { index } = itemProps
this.trySetState({ activeIndex: index })
@@ -149,9 +145,6 @@ class Menu extends AutoControlledComponent, any> {
vertical,
index,
active,
- ...(active && {
- styles: { position: 'relative' },
- }),
inSubmenu: submenu,
},
overrideProps: this.handleItemOverrides,
From b97bcce5621ab560be374debf74a7e83af377733 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Sun, 16 Dec 2018 15:53:08 +0100
Subject: [PATCH 32/55] -added state interface in the Menu -improved menu
variables' names
---
src/components/Menu/Menu.tsx | 9 ++++--
src/components/Menu/MenuItem.tsx | 2 +-
.../teams/components/Menu/menuItemStyles.ts | 28 +++++++++----------
.../teams/components/Menu/menuStyles.ts | 3 +-
.../teams/components/Menu/menuVariables.ts | 20 ++++++-------
5 files changed, 33 insertions(+), 29 deletions(-)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 9baa53c8ae..4321fa9e3c 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -66,10 +66,14 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
submenu?: boolean
}
+export interface MenuState {
+ activeIndex?: number | string
+}
+
/**
* A menu displays grouped navigation actions.
*/
-class Menu extends AutoControlledComponent, any> {
+class Menu extends AutoControlledComponent, MenuState> {
static displayName = 'Menu'
static className = 'ui-menu'
@@ -132,7 +136,8 @@ class Menu extends AutoControlledComponent, any> {
const { activeIndex } = this.state
return _.map(items, (item, index) => {
- const active = parseInt(activeIndex, 10) === index
+ const active =
+ typeof activeIndex === 'string' ? parseInt(activeIndex, 10) : activeIndex === index
return MenuItem.create(item, {
defaultProps: {
iconOnly,
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 170dbe90a1..0608c5fc8a 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -102,7 +102,7 @@ export interface MenuItemProps
defaultSubmenuOpen?: boolean
/** Callback for setting the current menu item as active element in the menu. */
- setActiveIndex?: (idx: number) => void
+ setActiveIndex?: (idx: number | string) => void
/** Indicates whether the menu item is part of submenu. */
inSubmenu?: boolean
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index 85a514d0d1..d98017d23b 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -23,7 +23,7 @@ const getActionStyles = ({
(underlined && !isFromKeyboard) || iconOnly
? {
color,
- background: v.defaultBackgroundColor,
+ background: v.backgroundColor,
}
: primary
? {
@@ -32,7 +32,7 @@ const getActionStyles = ({
}
: {
color,
- background: v.defaultActiveBackgroundColor,
+ background: v.activeBackgroundColor,
}
const itemSeparator: ComponentSlotStyleFunction = ({
@@ -52,7 +52,7 @@ const itemSeparator: ComponentSlotStyleFunction {
return {
- defaultColor: siteVars.gray02,
- defaultBackgroundColor: '#FFF',
+ color: siteVars.gray02,
+ backgroundColor: siteVars.white,
- defaultActiveColor: siteVars.black,
- defaultActiveBackgroundColor: siteVars.gray10,
- defaultBorderColor: siteVars.gray08,
+ activeColor: siteVars.black,
+ activeBackgroundColor: siteVars.gray10,
+ borderColor: siteVars.gray08,
primaryActiveColor: siteVars.white,
primaryActiveBackgroundColor: siteVars.brand08,
From eec33c1e882960b5398d36d6b7e68c00a8ec9c46 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Sun, 16 Dec 2018 16:07:17 +0100
Subject: [PATCH 33/55] -fixed variables in examples
---
.../MenuExampleIconOnlyPrimaryInverted.shorthand.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/src/examples/components/Menu/Variations/MenuExampleIconOnlyPrimaryInverted.shorthand.tsx b/docs/src/examples/components/Menu/Variations/MenuExampleIconOnlyPrimaryInverted.shorthand.tsx
index 733f7a7b52..726c14ac9b 100644
--- a/docs/src/examples/components/Menu/Variations/MenuExampleIconOnlyPrimaryInverted.shorthand.tsx
+++ b/docs/src/examples/components/Menu/Variations/MenuExampleIconOnlyPrimaryInverted.shorthand.tsx
@@ -14,9 +14,9 @@ const MenuExampleIconOnlyPrimaryInverted = () => (
items={items}
primary
variables={siteVars => ({
- defaultColor: siteVars.gray06,
- defaultBackgroundColor: siteVars.brand,
- typePrimaryActiveBorderColor: siteVars.white,
+ color: siteVars.gray06,
+ backgroundColor: siteVars.brand,
+ primaryActiveBorderColor: siteVars.white,
})}
/>
)
From 950cd54475f3ae06b24bc8edb7707efe6110e4f3 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Mon, 17 Dec 2018 14:59:34 +0100
Subject: [PATCH 34/55] -remove state initialization in the MenuItem component
---
src/components/Menu/MenuItem.tsx | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 0608c5fc8a..f9a2bc6441 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -154,11 +154,6 @@ class MenuItem extends AutoControlledComponent, MenuIt
static autoControlledProps = ['submenuOpen']
- state = {
- isFromKeyboard: false,
- submenuOpen: false,
- }
-
private outsideClickSubscription = EventStack.noSubscription
private submenuRef = React.createRef()
From 04a531366e6312947a15619ed249c7b5841b94fd Mon Sep 17 00:00:00 2001
From: manajdov
Date: Mon, 17 Dec 2018 17:55:02 +0100
Subject: [PATCH 35/55] -added new handler for escape -changed submenu examples
titles
---
docs/src/examples/components/Menu/Types/index.tsx | 4 ++--
src/components/Menu/MenuItem.tsx | 14 +++++++++++++-
.../Behaviors/Menu/menuItemBehavior.ts | 5 ++++-
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/docs/src/examples/components/Menu/Types/index.tsx b/docs/src/examples/components/Menu/Types/index.tsx
index a589b627a9..221c7fc22a 100644
--- a/docs/src/examples/components/Menu/Types/index.tsx
+++ b/docs/src/examples/components/Menu/Types/index.tsx
@@ -20,12 +20,12 @@ const Types = () => (
examplePath="components/Menu/Types/MenuExampleVertical"
/>
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index f9a2bc6441..e4cc486143 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -244,6 +244,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
performClick: event => this.handleClick(event),
openSubmenu: event => this.openSubmenu(event),
closeMenu: event => this.closeMenu(event),
+ closeMenuAndFocusNextParentItem: event => this.closeMenuAndFocusNextParentItem(event),
closeSubmenu: event => this.closeSubmenu(event),
}
@@ -303,7 +304,18 @@ class MenuItem extends AutoControlledComponent, MenuIt
const { submenuOpen } = this.state
if (menu && submenuOpen) {
this.setState({ submenuOpen: false })
- if (!inSubmenu && (keyboardKey.getCode(e) === keyboardKey.Escape || this.props.vertical)) {
+ if (!inSubmenu) {
+ focusAsync(this.itemRef.current)
+ }
+ }
+ }
+
+ private closeMenuAndFocusNextParentItem = e => {
+ const { menu, inSubmenu } = this.props
+ const { submenuOpen } = this.state
+ if (menu && submenuOpen) {
+ this.setState({ submenuOpen: false })
+ if (!inSubmenu && this.props.vertical) {
focusAsync(this.itemRef.current)
}
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index 4b3e0e4efd..e90bf23376 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -39,7 +39,10 @@ const menuItemBehavior: Accessibility = (props: any) => ({
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
closeMenu: {
- keyCombinations: [{ keyCode: keyboardKey.Escape }, { keyCode: keyboardKey.ArrowRight }],
+ keyCombinations: [{ keyCode: keyboardKey.Escape }],
+ },
+ closeMenuAndFocusNextParentItem: {
+ keyCombinations: [{ keyCode: keyboardKey.ArrowRight }],
},
closeSubmenu: {
keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
From f5e66f0c6658a120d205083fbe331ce46f5d91ab Mon Sep 17 00:00:00 2001
From: manajdov
Date: Mon, 17 Dec 2018 18:27:12 +0100
Subject: [PATCH 36/55] -refactored conditions using doesNodeContainClick
---
src/components/Menu/MenuItem.tsx | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index e4cc486143..520cbdcfca 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -2,13 +2,13 @@ import * as _ from 'lodash'
import cx from 'classnames'
import * as PropTypes from 'prop-types'
import * as React from 'react'
-import * as keyboardKey from 'keyboard-key'
import {
AutoControlledComponent,
childrenExist,
createShorthandFactory,
customPropTypes,
+ doesNodeContainClick,
UIComponentProps,
ChildrenComponentProps,
ContentComponentProps,
@@ -260,9 +260,8 @@ class MenuItem extends AutoControlledComponent, MenuIt
private outsideClickHandler = e => {
if (
- this.itemRef &&
- (!this.itemRef.current || !this.itemRef.current.contains(e.target)) &&
- (!this.submenuRef.current || !this.submenuRef.current.contains(e.target))
+ !doesNodeContainClick(this.itemRef.current, e) &&
+ !doesNodeContainClick(this.submenuRef.current, e)
) {
this.state.submenuOpen && this.trySetState({ submenuOpen: false })
}
@@ -271,7 +270,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
private performClick = e => {
const { active, menu } = this.props
if (menu) {
- if (this.submenuRef.current && this.submenuRef.current.contains(e.target)) {
+ if (doesNodeContainClick(this.submenuRef.current, e)) {
// submenu was clicked => close it and propagate
this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
} else {
From 4c8e2d66c5aaf66de08c7d10a8374ea972c580d4 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Tue, 18 Dec 2018 15:03:45 +0100
Subject: [PATCH 37/55] -renamed submenu* props to menu in the MenuItem
component -changed setActiveIndex with onActiveChanged -introduced different
styles for the hovering vs active elements
---
...nuExampleVerticalWithSubmenu.shorthand.tsx | 18 +----
.../MenuExampleWithSubmenu.shorthand.tsx | 18 +----
src/components/Menu/Menu.tsx | 10 ++-
src/components/Menu/MenuItem.tsx | 80 +++++++++----------
.../Behaviors/Menu/menuItemBehavior.ts | 6 +-
.../teams/components/Menu/menuItemStyles.ts | 38 +++++++--
.../teams/components/Menu/menuVariables.ts | 8 ++
7 files changed, 96 insertions(+), 82 deletions(-)
diff --git a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx
index 1e8dae4410..807b6725a9 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, Provider } from '@stardust-ui/react'
+import { Menu } from '@stardust-ui/react'
const items = [
{
@@ -20,20 +20,6 @@ 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 d2d195cc3b..4899c64fef 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, Provider } from '@stardust-ui/react'
+import { Menu } from '@stardust-ui/react'
const items = [
{
@@ -38,20 +38,6 @@ 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 4321fa9e3c..70c4ca03c4 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -116,8 +116,14 @@ class Menu extends AutoControlledComponent, MenuState> {
_.invoke(predefinedProps, 'onClick', e, itemProps)
},
- setActiveIndex: index => {
- this.trySetState({ activeIndex: index })
+ onActiveChanged: (e, props) => {
+ const { index, active } = props
+ if (active) {
+ this.trySetState({ activeIndex: index })
+ } else if (this.state.activeIndex === index) {
+ this.trySetState({ activeIndex: null })
+ }
+ _.invoke(predefinedProps, 'onActiveChanged', e, props)
},
})
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index 520cbdcfca..ce7d4d2bd3 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -95,14 +95,14 @@ export interface MenuItemProps
/** Shorthand for the submenu. */
menu?: ShorthandValue
- /** Indicates if the submenu is open */
- submenuOpen?: boolean
+ /** Indicates if the menu inside the item is open. */
+ menuOpen?: boolean
- /** Default submenu open */
- defaultSubmenuOpen?: boolean
+ /** Default menu open */
+ defaultMenuOpen?: boolean
/** Callback for setting the current menu item as active element in the menu. */
- setActiveIndex?: (idx: number | string) => void
+ onActiveChanged?: ComponentEventHandler
/** Indicates whether the menu item is part of submenu. */
inSubmenu?: boolean
@@ -110,7 +110,7 @@ export interface MenuItemProps
export interface MenuItemState {
isFromKeyboard: boolean
- submenuOpen: boolean
+ menuOpen: boolean
}
/**
@@ -140,9 +140,9 @@ class MenuItem extends AutoControlledComponent, MenuIt
vertical: PropTypes.bool,
wrapper: PropTypes.oneOfType([PropTypes.node, PropTypes.object]),
menu: customPropTypes.itemShorthand,
- submenuOpen: PropTypes.bool,
- defaultSubmenuOpen: PropTypes.bool,
- setActiveIndex: PropTypes.func,
+ menuOpen: PropTypes.bool,
+ defaultMenuOpen: PropTypes.bool,
+ onActiveChanged: PropTypes.func,
inSubmenu: PropTypes.bool,
}
@@ -152,11 +152,11 @@ class MenuItem extends AutoControlledComponent, MenuIt
wrapper: { as: 'li' },
}
- static autoControlledProps = ['submenuOpen']
+ static autoControlledProps = ['menuOpen']
private outsideClickSubscription = EventStack.noSubscription
- private submenuRef = React.createRef()
+ private menuRef = React.createRef()
private itemRef = React.createRef()
public componentDidMount() {
@@ -174,7 +174,7 @@ class MenuItem extends AutoControlledComponent, MenuIt
renderComponent({ ElementType, classes, accessibility, rest, styles }) {
const { children, content, icon, wrapper, menu, primary, secondary, active } = this.props
- const { submenuOpen } = this.state
+ const { menuOpen } = this.state
const menuItemInner = childrenExist(children) ? (
children
@@ -197,8 +197,8 @@ class MenuItem extends AutoControlledComponent, MenuIt
)
const maybeSubmenu =
- menu && active && submenuOpen ? (
- [
+ menu && active && menuOpen ? (
+ ][
{Menu.create(menu, {
defaultProps: {
accessibility: submenuBehavior,
@@ -236,22 +236,22 @@ class MenuItem extends AutoControlledComponent, MenuIt
private handleWrapperBlur = e => {
if (!this.props.inSubmenu && !e.currentTarget.contains(e.relatedTarget)) {
- this.setState({ submenuOpen: false })
+ this.setState({ menuOpen: false })
}
}
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
- openSubmenu: event => this.openSubmenu(event),
- closeMenu: event => this.closeMenu(event),
+ openMenu: event => this.openMenu(event),
+ closeAllMenus: event => this.closeAllMenus(event),
closeMenuAndFocusNextParentItem: event => this.closeMenuAndFocusNextParentItem(event),
- closeSubmenu: event => this.closeSubmenu(event),
+ closeMenu: event => this.closeMenu(event),
}
private updateOutsideClickSubscription() {
this.outsideClickSubscription.unsubscribe()
- if (this.props.menu && this.state.submenuOpen) {
+ if (this.props.menu && this.state.menuOpen) {
setTimeout(() => {
this.outsideClickSubscription = EventStack.subscribe('click', this.outsideClickHandler)
})
@@ -261,21 +261,21 @@ class MenuItem extends AutoControlledComponent, MenuIt
private outsideClickHandler = e => {
if (
!doesNodeContainClick(this.itemRef.current, e) &&
- !doesNodeContainClick(this.submenuRef.current, e)
+ !doesNodeContainClick(this.menuRef.current, e)
) {
- this.state.submenuOpen && this.trySetState({ submenuOpen: false })
+ this.state.menuOpen && this.trySetState({ menuOpen: false })
}
}
private performClick = e => {
const { active, menu } = this.props
if (menu) {
- if (doesNodeContainClick(this.submenuRef.current, e)) {
+ if (doesNodeContainClick(this.menuRef.current, e)) {
// submenu was clicked => close it and propagate
- this.setState({ submenuOpen: false }, () => focusAsync(this.itemRef.current))
+ this.setState({ menuOpen: false }, () => focusAsync(this.itemRef.current))
} else {
// the menuItem element was clicked => toggle the open/close and stop propagation
- this.trySetState({ submenuOpen: active ? !this.state.submenuOpen : true })
+ this.trySetState({ menuOpen: active ? !this.state.menuOpen : true })
e.stopPropagation()
}
}
@@ -298,11 +298,11 @@ class MenuItem extends AutoControlledComponent, MenuIt
_.invoke(this.props, 'onFocus', e, this.props)
}
- private closeMenu = e => {
+ private closeAllMenus = e => {
const { menu, inSubmenu } = this.props
- const { submenuOpen } = this.state
- if (menu && submenuOpen) {
- this.setState({ submenuOpen: false })
+ const { menuOpen } = this.state
+ if (menu && menuOpen) {
+ this.setState({ menuOpen: false })
if (!inSubmenu) {
focusAsync(this.itemRef.current)
}
@@ -311,21 +311,21 @@ class MenuItem extends AutoControlledComponent, MenuIt
private closeMenuAndFocusNextParentItem = e => {
const { menu, inSubmenu } = this.props
- const { submenuOpen } = this.state
- if (menu && submenuOpen) {
- this.setState({ submenuOpen: false })
+ const { menuOpen } = this.state
+ if (menu && menuOpen) {
+ this.setState({ menuOpen: false })
if (!inSubmenu && this.props.vertical) {
focusAsync(this.itemRef.current)
}
}
}
- private closeSubmenu = e => {
+ private closeMenu = e => {
const { menu, inSubmenu } = this.props
- const { submenuOpen } = this.state
+ const { menuOpen } = this.state
const shouldStopPropagation = inSubmenu || this.props.vertical
- if (menu && submenuOpen) {
- this.setState({ submenuOpen: false }, () => {
+ if (menu && menuOpen) {
+ this.setState({ menuOpen: false }, () => {
if (shouldStopPropagation) {
focusAsync(this.itemRef.current)
}
@@ -336,12 +336,12 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
}
- private openSubmenu = e => {
+ private openMenu = e => {
const { menu } = this.props
- const { submenuOpen } = this.state
- if (menu && !submenuOpen) {
- this.setState({ submenuOpen: true })
- _.invoke(this.props, 'setActiveIndex', this.props.index)
+ const { menuOpen } = this.state
+ if (menu && !menuOpen) {
+ this.setState({ menuOpen: true })
+ _.invoke(this.props, 'onActiveChanged', e, { ...this.props, active: true })
e.stopPropagation()
e.preventDefault()
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index e90bf23376..e2332aa3b4 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -38,16 +38,16 @@ const menuItemBehavior: Accessibility = (props: any) => ({
performClick: {
keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
},
- closeMenu: {
+ closeAllMenus: {
keyCombinations: [{ keyCode: keyboardKey.Escape }],
},
closeMenuAndFocusNextParentItem: {
keyCombinations: [{ keyCode: keyboardKey.ArrowRight }],
},
- closeSubmenu: {
+ closeMenu: {
keyCombinations: [{ keyCode: keyboardKey.ArrowLeft }],
},
- openSubmenu: {
+ openMenu: {
keyCombinations: [
{ keyCode: props.vertical ? keyboardKey.ArrowRight : keyboardKey.ArrowDown },
],
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index d98017d23b..c3dc8c2b19 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -20,7 +20,7 @@ const getActionStyles = ({
variables: MenuVariables
color: string
}): ICSSInJSStyle =>
- (underlined && !isFromKeyboard) || iconOnly
+ underlined || iconOnly
? {
color,
background: v.backgroundColor,
@@ -35,6 +35,35 @@ const getActionStyles = ({
background: v.activeBackgroundColor,
}
+const getFocusedStyles = ({
+ props,
+ variables: v,
+ color,
+}: {
+ props: MenuItemPropsAndState
+ variables: MenuVariables
+ color: string
+}): ICSSInJSStyle => {
+ const { primary, underlined, iconOnly, isFromKeyboard, active } = props
+ if (active) return {}
+ return {
+ ...((underlined && !isFromKeyboard) || iconOnly
+ ? {
+ color,
+ background: v.backgroundColor,
+ }
+ : primary
+ ? {
+ color: v.primaryFocusedColor,
+ background: v.primaryFocusedBackgroundColor,
+ }
+ : {
+ color,
+ background: v.focusedBackgroundColor,
+ }),
+ }
+}
+
const itemSeparator: ComponentSlotStyleFunction = ({
props,
variables: v,
@@ -191,10 +220,10 @@ const menuItemStyles: ComponentSlotStylesInput ({
- // background: 'white',
- // zIndex: '1000',
+ zIndex: '1000',
position: 'absolute',
top: vertical ? '0' : '100%',
left: vertical ? '100%' : '0',
diff --git a/src/themes/teams/components/Menu/menuVariables.ts b/src/themes/teams/components/Menu/menuVariables.ts
index fb778e4455..3558f204b1 100644
--- a/src/themes/teams/components/Menu/menuVariables.ts
+++ b/src/themes/teams/components/Menu/menuVariables.ts
@@ -6,12 +6,16 @@ export interface MenuVariables {
activeColor: string
activeBackgroundColor: string
+ focusedBackgroundColor: string
borderColor: string
primaryActiveColor: string
primaryActiveBackgroundColor: string
primaryActiveBorderColor: string
+ primaryFocusedColor: string
+ primaryFocusedBackgroundColor: string
+
primaryBorderColor: string
primaryHoverBorderColor: string
primaryUnderlinedBorderColor: string
@@ -30,12 +34,16 @@ export default (siteVars: any): MenuVariables => {
activeColor: siteVars.black,
activeBackgroundColor: siteVars.gray10,
+ focusedBackgroundColor: siteVars.gray14,
borderColor: siteVars.gray08,
primaryActiveColor: siteVars.white,
primaryActiveBackgroundColor: siteVars.brand08,
primaryActiveBorderColor: siteVars.brand,
+ primaryFocusedColor: siteVars.white,
+ primaryFocusedBackgroundColor: siteVars.brand12,
+
primaryBorderColor: siteVars.brand08,
primaryHoverBorderColor: siteVars.gray08,
primaryUnderlinedBorderColor: siteVars.gray08,
From 2ece67bff7c01e34dba0f5ad8e8b9450f53c736c Mon Sep 17 00:00:00 2001
From: manajdov
Date: Tue, 18 Dec 2018 18:46:23 +0100
Subject: [PATCH 38/55] -improved example -fixed issue with the condition for
the active prop
---
docs/src/examples/components/Menu/Types/index.tsx | 6 +-----
src/components/Menu/Menu.tsx | 2 +-
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/docs/src/examples/components/Menu/Types/index.tsx b/docs/src/examples/components/Menu/Types/index.tsx
index 221c7fc22a..9eb56ac99b 100644
--- a/docs/src/examples/components/Menu/Types/index.tsx
+++ b/docs/src/examples/components/Menu/Types/index.tsx
@@ -24,11 +24,7 @@ const Types = () => (
description="A menu can have submenus."
examplePath="components/Menu/Types/MenuExampleWithSubmenu"
/>
-
+
)
diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx
index 70c4ca03c4..85bebbdce1 100644
--- a/src/components/Menu/Menu.tsx
+++ b/src/components/Menu/Menu.tsx
@@ -143,7 +143,7 @@ class Menu extends AutoControlledComponent, MenuState> {
return _.map(items, (item, index) => {
const active =
- typeof activeIndex === 'string' ? parseInt(activeIndex, 10) : activeIndex === index
+ (typeof activeIndex === 'string' ? parseInt(activeIndex, 10) : activeIndex) === index
return MenuItem.create(item, {
defaultProps: {
iconOnly,
From a97a2febcd4271a79fb27aa9d519a5e4fce229fa Mon Sep 17 00:00:00 2001
From: manajdov
Date: Tue, 18 Dec 2018 18:51:05 +0100
Subject: [PATCH 39/55] -exported MenuState -added correct typings to the
menuStyles
---
src/index.ts | 2 +-
src/themes/teams/components/Menu/menuStyles.ts | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index af883aeb8b..523f5ae664 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -74,7 +74,7 @@ export { default as Layout, LayoutPropsWithDefaults, LayoutProps } from './compo
export { default as List, ListProps } from './components/List/List'
export { default as ListItem, ListItemState, ListItemProps } from './components/List/ListItem'
-export { default as Menu, MenuProps } from './components/Menu/Menu'
+export { default as Menu, MenuProps, MenuState } from './components/Menu/Menu'
export { default as MenuItem, MenuItemState, MenuItemProps } from './components/Menu/MenuItem'
export { default as Popup, PopupState, PopupProps } from './components/Popup/Popup'
diff --git a/src/themes/teams/components/Menu/menuStyles.ts b/src/themes/teams/components/Menu/menuStyles.ts
index f21091389c..d3fc40d934 100644
--- a/src/themes/teams/components/Menu/menuStyles.ts
+++ b/src/themes/teams/components/Menu/menuStyles.ts
@@ -1,6 +1,9 @@
import { pxToRem } from '../../utils'
import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types'
-import { MenuProps } from '../../../../components/Menu/Menu'
+import { MenuProps, MenuState } from '../../../../components/Menu/Menu'
+import { MenuVariables } from './menuVariables'
+
+type MenuPropsAndState = MenuProps & MenuState
const solidBorder = (color: string) => ({
border: `1px solid ${color}`,
@@ -39,4 +42,4 @@ export default {
listStyleType: 'none',
}
},
-} as ComponentSlotStylesInput
+} as ComponentSlotStylesInput
From c590f3e3b78feaee340fc7db201adb05082c9a82 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Tue, 18 Dec 2018 19:14:23 +0100
Subject: [PATCH 40/55] -fixed underlined active + hovered style
---
src/themes/teams/components/Menu/menuItemStyles.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index c3dc8c2b19..367cf8e404 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -45,7 +45,7 @@ const getFocusedStyles = ({
color: string
}): ICSSInJSStyle => {
const { primary, underlined, iconOnly, isFromKeyboard, active } = props
- if (active) return {}
+ if (active && !underlined) return {}
return {
...((underlined && !isFromKeyboard) || iconOnly
? {
From 77342eaa42be0c5d69be04bdbb90654cce9ca40e Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 19 Dec 2018 11:51:30 +0100
Subject: [PATCH 41/55] -fixed border corner clipped by adding custom styles
for the first child menu items and the last child menu items in vertical menu
---
.../teams/components/Menu/menuItemStyles.ts | 39 +++++++++++++++----
1 file changed, 31 insertions(+), 8 deletions(-)
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index 367cf8e404..36ef09fbf5 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -83,14 +83,6 @@ const itemSeparator: ComponentSlotStyleFunction
Date: Wed, 19 Dec 2018 15:40:03 +0100
Subject: [PATCH 42/55] -addressed comments on PR
---
src/components/Menu/MenuItem.tsx | 29 +++++++------------
.../Behaviors/Menu/menuItemBehavior.ts | 2 +-
2 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index ce7d4d2bd3..143734a42c 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -243,8 +243,8 @@ class MenuItem extends AutoControlledComponent, MenuIt
protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => this.handleClick(event),
openMenu: event => this.openMenu(event),
- closeAllMenus: event => this.closeAllMenus(event),
- closeMenuAndFocusNextParentItem: event => this.closeMenuAndFocusNextParentItem(event),
+ closeAllMenus: event => this.closeAllMenus(event, false),
+ closeAllMenusAndFocusNextParentItem: event => this.closeAllMenus(event, true),
closeMenu: event => this.closeMenu(event),
}
@@ -259,11 +259,12 @@ class MenuItem extends AutoControlledComponent, MenuIt
}
private outsideClickHandler = e => {
+ if (!this.state.menuOpen) return
if (
!doesNodeContainClick(this.itemRef.current, e) &&
!doesNodeContainClick(this.menuRef.current, e)
) {
- this.state.menuOpen && this.trySetState({ menuOpen: false })
+ this.trySetState({ menuOpen: false })
}
}
@@ -298,25 +299,15 @@ class MenuItem extends AutoControlledComponent, MenuIt
_.invoke(this.props, 'onFocus', e, this.props)
}
- private closeAllMenus = e => {
+ private closeAllMenus = (e, focusNextParent: boolean) => {
const { menu, inSubmenu } = this.props
const { menuOpen } = this.state
if (menu && menuOpen) {
- this.setState({ menuOpen: false })
- if (!inSubmenu) {
- focusAsync(this.itemRef.current)
- }
- }
- }
-
- private closeMenuAndFocusNextParentItem = e => {
- const { menu, inSubmenu } = this.props
- const { menuOpen } = this.state
- if (menu && menuOpen) {
- this.setState({ menuOpen: false })
- if (!inSubmenu && this.props.vertical) {
- focusAsync(this.itemRef.current)
- }
+ this.setState({ menuOpen: false }, () => {
+ if (!inSubmenu && (!focusNextParent || this.props.vertical)) {
+ focusAsync(this.itemRef.current)
+ }
+ })
}
}
diff --git a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
index e2332aa3b4..730b783f9c 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuItemBehavior.ts
@@ -41,7 +41,7 @@ const menuItemBehavior: Accessibility = (props: any) => ({
closeAllMenus: {
keyCombinations: [{ keyCode: keyboardKey.Escape }],
},
- closeMenuAndFocusNextParentItem: {
+ closeAllMenusAndFocusNextParentItem: {
keyCombinations: [{ keyCode: keyboardKey.ArrowRight }],
},
closeMenu: {
From 54f0a69083c69bce253995794378e5b3a722a631 Mon Sep 17 00:00:00 2001
From: Miroslav Stastny
Date: Mon, 17 Dec 2018 14:33:49 +0100
Subject: [PATCH 43/55] chore: prepare release 0.15.0 [ci skip]
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a37147e870..6140d7bc1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
+
+## [v0.15.0](https://github.com/stardust-ui/react/tree/v0.15.0) (2018-12-17)
+[Compare changes](https://github.com/stardust-ui/react/compare/v0.14.0...v0.15.0)
+
### BREAKING CHANGES
- `type` prop is replaced with `color` in `Divider` component @layershifter ([#558](https://github.com/stardust-ui/react/pull/558))
- Remove `createColorVariants` and `setColorLightness` utils @layershifter ([#583](https://github.com/stardust-ui/react/pull/583))
From 990948342ac2d492472cb77e401a5c8f58c9acfb Mon Sep 17 00:00:00 2001
From: Miroslav Stastny
Date: Mon, 17 Dec 2018 14:38:53 +0100
Subject: [PATCH 44/55] 0.15.0
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 75c2f3976d..a9d2b60649 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@stardust-ui/react",
- "version": "0.14.0",
+ "version": "0.15.0",
"description": "A themable React component library.",
"jsnext:main": "dist/es/index.js",
"main": "dist/commonjs/index.js",
From fa2e9bd9666bdc5c6ac0488492e03c5dea09b36d Mon Sep 17 00:00:00 2001
From: Sofiya Huts <8460706+sophieH29@users.noreply.github.com>
Date: Mon, 17 Dec 2018 17:22:07 +0100
Subject: [PATCH 45/55] fix(Prototype): Fix Popover prototype after breaking
changes (#623)
---
.../ChatMessageWithPopover.tsx | 6 -----
.../ChatWithPopover.tsx | 25 ++++++++++++++++---
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/docs/src/prototypes/chatMessageWithPopover/ChatMessageWithPopover.tsx b/docs/src/prototypes/chatMessageWithPopover/ChatMessageWithPopover.tsx
index ea3454ed1f..62f17dc69b 100644
--- a/docs/src/prototypes/chatMessageWithPopover/ChatMessageWithPopover.tsx
+++ b/docs/src/prototypes/chatMessageWithPopover/ChatMessageWithPopover.tsx
@@ -4,11 +4,6 @@ import * as React from 'react'
import cx from 'classnames'
import Popover from './Popover'
-const janeAvatar = {
- image: 'public/images/avatar/small/ade.jpg',
- status: { color: 'green', icon: 'check' },
-}
-
interface ChatMessageWithPopoverProps {
className?: string
}
@@ -51,7 +46,6 @@ class ChatMessageWithPopover extends React.Component<
]
),
}}
- avatar={janeAvatar}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
className={cx(this.props.className, this.state.focused ? 'focused' : '')}
diff --git a/docs/src/prototypes/chatMessageWithPopover/ChatWithPopover.tsx b/docs/src/prototypes/chatMessageWithPopover/ChatWithPopover.tsx
index 6796bae0fa..06c4e3b1b5 100644
--- a/docs/src/prototypes/chatMessageWithPopover/ChatWithPopover.tsx
+++ b/docs/src/prototypes/chatMessageWithPopover/ChatWithPopover.tsx
@@ -1,7 +1,12 @@
-import { Chat, Provider } from '@stardust-ui/react'
+import { Chat, Provider, Avatar } from '@stardust-ui/react'
import * as React from 'react'
import ChatMessageWithPopover from './ChatMessageWithPopover'
+const janeAvatar = {
+ image: 'public/images/avatar/small/ade.jpg',
+ status: { color: 'green', icon: 'check' },
+}
+
const ChatWithPopover = () => (
(
>
},
- { key: 'b', content: },
- { key: 'c', content: },
+ {
+ key: 'a',
+ message: { content: },
+ gutter: { content: },
+ },
+ {
+ key: 'b',
+ message: { content: },
+ gutter: { content: },
+ },
+ {
+ key: 'c',
+ message: { content: },
+ gutter: { content: },
+ },
]}
/>
From df0b11a08cf1cfe000fbd34fb050e1097ed064e3 Mon Sep 17 00:00:00 2001
From: kuzhelov
Date: Tue, 18 Dec 2018 12:50:35 +0100
Subject: [PATCH 46/55] chore: cache results of vulnerability scans (#621)
* implement caching strategy
* adjust file name of scan marker
* add yarn lock hash to marker file name
* add change to build config
* fix dir name in build config
* improve caching strategy
* just restore cache
* temporary remove lint and tests
* try
* fix caching strategy
* try
* try
* try
* try epoch
* create file on scan
* return lint and test steps
* introduce comment for the caching approach taken
* remove unnecessary function
* simplify expression for marker file name
---
.circleci/config.yml | 9 +++++
build/gulp/tasks/test-vulns.ts | 65 ++++++++++++++++++++++++++++++++++
gulpfile.ts | 1 +
package.json | 2 +-
4 files changed, 76 insertions(+), 1 deletion(-)
create mode 100644 build/gulp/tasks/test-vulns.ts
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 328faa1561..25f0d08b77 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -49,9 +49,18 @@ jobs:
- run:
name: Report coverage
command: bash <(curl -s https://codecov.io/bash)
+
+ - restore_cache:
+ key: v1-vuln-scans-{{ checksum "yarn.lock" }}
- run:
name: Vulnerability Tests
command: yarn test:vulns
+ # https://discuss.circleci.com/t/add-mechanism-to-update-existing-cache-key/9014/12
+ - save_cache:
+ key: v1-vuln-scans-{{ checksum "yarn.lock" }}-{{ epoch }}
+ paths:
+ - .vuln-scans
+
- run:
name: Visual Tests
command: yarn test:visual
diff --git a/build/gulp/tasks/test-vulns.ts b/build/gulp/tasks/test-vulns.ts
new file mode 100644
index 0000000000..b20351292e
--- /dev/null
+++ b/build/gulp/tasks/test-vulns.ts
@@ -0,0 +1,65 @@
+import * as fs from 'fs'
+import { task } from 'gulp'
+import * as path from 'path'
+import debug from 'debug'
+
+import config from '../../../config'
+import sh from '../sh'
+
+const { paths } = config
+
+const SCAN_RESULTS_DIR_NAME = '.vuln-scans'
+const SCAN_RESULTS_DIR_PATH = paths.base(SCAN_RESULTS_DIR_NAME)
+
+const log = message => debug.log(message)
+log.success = message => debug.log(`✔ ${message}`)
+
+const ensureDirExists = path => {
+ if (!fs.existsSync(path)) {
+ sh(`mkdir -p ${path}`)
+ }
+}
+
+const getTodayScanFilePath = () => {
+ const now = new Date()
+
+ const year = now.getUTCFullYear()
+ const month = now.getUTCMonth() + 1
+ const date = now.getUTCDate()
+
+ const fileName = `snyk-scanned-${year}-${month}-${date}`
+
+ return path.resolve(SCAN_RESULTS_DIR_PATH, fileName)
+}
+
+const recentlyChecked = () => {
+ const recentCheckFilePath = getTodayScanFilePath()
+ return fs.existsSync(recentCheckFilePath)
+}
+
+const registerRecentSucessfulScan = async () => {
+ ensureDirExists(SCAN_RESULTS_DIR_PATH)
+
+ const recentScanFilePath = getTodayScanFilePath()
+ await sh(`touch ${recentScanFilePath}`)
+}
+
+/**
+ * The following strategy is used to perform vulnerabilites scan
+ * - check if there is marker of recent sucessful scan
+ * - if this marker exists, skip checks
+ * - if there is no marker, perform check
+ * - if check is successful, create successful check marker
+ */
+task('test:vulns', async () => {
+ if (recentlyChecked()) {
+ log.success('Vulnerabilities check was already performed recently, skipping..')
+ return
+ }
+
+ log('Scanning dependency packages for vulnerabilities..')
+ await sh(`yarn snyk test`)
+ log.success('Vulnerability scan is successfully passed.')
+
+ registerRecentSucessfulScan()
+})
diff --git a/gulpfile.ts b/gulpfile.ts
index ba23bc51e9..c34184f67a 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -14,6 +14,7 @@ require('./build/gulp/tasks/screener')
require('./build/gulp/tasks/git')
require('./build/gulp/tasks/test-unit')
require('./build/gulp/tasks/test-projects')
+require('./build/gulp/tasks/test-vulns')
// global tasks
task('build', series('dll', parallel('dist', 'build:docs')))
diff --git a/package.json b/package.json
index a9d2b60649..d645944e84 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"pretest": "yarn satisfied",
"test": "gulp test",
"test:watch": "gulp test:watch",
- "test:vulns": "snyk test",
+ "test:vulns": "gulp test:vulns",
"test:visual": "gulp screener",
"test:projects": "gulp test:projects",
"generate:component": "gulp generate:component"
From 1d8e16874eefca1e25606727cf4b849ef7c31881 Mon Sep 17 00:00:00 2001
From: Alexandru Buliga
Date: Tue, 18 Dec 2018 14:16:35 +0200
Subject: [PATCH 47/55] feat(text): color prop (#597)
* feat(text): color prop
* addressed comments
* changelog
* amended changelog
* made text color override other props that change color
---
CHANGELOG.md | 3 ++
.../Variations/TextExampleColor.shorthand.tsx | 18 ++++++++++
.../Text/Variations/TextExampleColor.tsx | 20 +++++++++++
.../components/Text/Variations/index.tsx | 5 +++
src/components/Text/Text.tsx | 9 +++--
src/lib/colorUtils.ts | 11 ++++++
src/lib/index.ts | 1 +
src/themes/teams/colors.ts | 1 -
.../teams/components/Divider/dividerStyles.ts | 35 ++++++++-----------
.../components/Divider/dividerVariables.ts | 14 ++++----
.../teams/components/Text/textStyles.ts | 4 +++
.../teams/components/Text/textVariables.ts | 7 ++++
src/themes/types.ts | 10 ++++++
13 files changed, 109 insertions(+), 29 deletions(-)
create mode 100644 docs/src/examples/components/Text/Variations/TextExampleColor.shorthand.tsx
create mode 100644 docs/src/examples/components/Text/Variations/TextExampleColor.tsx
create mode 100644 src/lib/colorUtils.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6140d7bc1c..e2293b5161 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
+### Features
+- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
+
## [v0.15.0](https://github.com/stardust-ui/react/tree/v0.15.0) (2018-12-17)
[Compare changes](https://github.com/stardust-ui/react/compare/v0.14.0...v0.15.0)
diff --git a/docs/src/examples/components/Text/Variations/TextExampleColor.shorthand.tsx b/docs/src/examples/components/Text/Variations/TextExampleColor.shorthand.tsx
new file mode 100644
index 0000000000..7997c1b1e0
--- /dev/null
+++ b/docs/src/examples/components/Text/Variations/TextExampleColor.shorthand.tsx
@@ -0,0 +1,18 @@
+import React from 'react'
+import _ from 'lodash'
+import { Text, ProviderConsumer } from '@stardust-ui/react'
+
+const TextExampleColor = () => (
+
+ _.keys({ ...emphasisColors, ...naturalColors }).map(color => (
+ <>
+
+
+ >
+ ))
+ }
+ />
+)
+
+export default TextExampleColor
diff --git a/docs/src/examples/components/Text/Variations/TextExampleColor.tsx b/docs/src/examples/components/Text/Variations/TextExampleColor.tsx
new file mode 100644
index 0000000000..79b78d5ae2
--- /dev/null
+++ b/docs/src/examples/components/Text/Variations/TextExampleColor.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import _ from 'lodash'
+import { Text, ProviderConsumer } from '@stardust-ui/react'
+
+const TextExampleColor = () => (
+
+ _.keys({ ...emphasisColors, ...naturalColors }).map(color => (
+ <>
+
+ {_.startCase(color)}
+
+
+ >
+ ))
+ }
+ />
+)
+
+export default TextExampleColor
diff --git a/docs/src/examples/components/Text/Variations/index.tsx b/docs/src/examples/components/Text/Variations/index.tsx
index b2721770bf..73d9b34082 100644
--- a/docs/src/examples/components/Text/Variations/index.tsx
+++ b/docs/src/examples/components/Text/Variations/index.tsx
@@ -4,6 +4,11 @@ import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
const Variations = () => (
+
, any> {
static displayName = 'Text'
static propTypes = {
- ...commonPropTypes.createCommon(),
+ ...commonPropTypes.createCommon({ color: true }),
atMention: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['me'])]),
disabled: PropTypes.bool,
error: PropTypes.bool,
diff --git a/src/lib/colorUtils.ts b/src/lib/colorUtils.ts
new file mode 100644
index 0000000000..471b27aa54
--- /dev/null
+++ b/src/lib/colorUtils.ts
@@ -0,0 +1,11 @@
+import * as _ from 'lodash'
+import { SiteVariablesInput, ColorVariants, ColorValues } from '../themes/types'
+
+export const mapColorsToScheme = (
+ siteVars: SiteVariablesInput,
+ mapper: keyof ColorVariants | ((color: ColorVariants) => T),
+): ColorValues =>
+ _.mapValues(
+ { ...siteVars.emphasisColors, ...siteVars.naturalColors },
+ typeof mapper === 'number' ? String(mapper) : (mapper as any),
+ ) as ColorValues
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 4415498664..7ad00ecc6e 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -3,6 +3,7 @@ import * as commonPropTypes from './commonPropTypes'
export { default as AutoControlledComponent } from './AutoControlledComponent'
export { default as childrenExist } from './childrenExist'
+export { mapColorsToScheme } from './colorUtils'
export { default as UIComponent } from './UIComponent'
export { EventStack } from './eventStack'
export { felaRenderer, felaRtlRenderer } from './felaRenderer'
diff --git a/src/themes/teams/colors.ts b/src/themes/teams/colors.ts
index ab7bec2375..d342ea9d54 100644
--- a/src/themes/teams/colors.ts
+++ b/src/themes/teams/colors.ts
@@ -88,7 +88,6 @@ export const naturalColors: NaturalColors = {
800: '#F9D844',
900: '#F8D22A',
},
-
darkOrange: {
50: '#F9ECEA',
100: '#ECBCB3',
diff --git a/src/themes/teams/components/Divider/dividerStyles.ts b/src/themes/teams/components/Divider/dividerStyles.ts
index b912214933..6cfc94488b 100644
--- a/src/themes/teams/components/Divider/dividerStyles.ts
+++ b/src/themes/teams/components/Divider/dividerStyles.ts
@@ -2,37 +2,32 @@ import * as _ from 'lodash'
import { childrenExist } from '../../../../lib'
import { pxToRem } from '../../utils'
-import { ComponentSlotStylesInput, ICSSInJSStyle, ICSSPseudoElementStyle } from '../../../types'
+import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types'
import { DividerPropsWithDefaults } from '../../../../components/Divider/Divider'
+import { DividerVariables } from './dividerVariables'
-const dividerBorderStyle = (size, color): ICSSInJSStyle => ({
- height: `${size + 1}px`,
- background: color,
-})
-
-const beforeAndAfter = (color, size, type, variables): ICSSPseudoElementStyle => ({
+const beforeAndAfter = (
+ color: string,
+ size: number,
+ variables: DividerVariables,
+): ICSSInJSStyle => ({
content: '""',
flex: 1,
- ...dividerBorderStyle(size, variables.dividerColor),
- ...(color && {
- ...dividerBorderStyle(size, _.get(variables.colors, color)),
- }),
+ height: `${size + 1}px`,
+ background: _.get(variables.colors, color, variables.dividerColor),
})
-const dividerStyles: ComponentSlotStylesInput = {
+const dividerStyles: ComponentSlotStylesInput = {
root: ({ props, variables }): ICSSInJSStyle => {
- const { children, color, fitted, size, type, important, content } = props
+ const { children, color, fitted, size, important, content } = props
return {
- color: variables.textColor,
+ color: _.get(variables.colors, color, variables.textColor),
display: 'flex',
alignItems: 'center',
...(!fitted && {
paddingTop: variables.dividerPadding,
paddingBottom: variables.dividerPadding,
}),
- ...(color && {
- color: _.get(variables.colors, color),
- }),
...(important && {
fontWeight: variables.importantFontWeight,
}),
@@ -42,17 +37,17 @@ const dividerStyles: ComponentSlotStylesInput = {
fontSize: pxToRem(12 + size),
lineHeight: variables.textLineHeight,
'::before': {
- ...beforeAndAfter(color, size, type, variables),
+ ...beforeAndAfter(color, size, variables),
marginRight: pxToRem(20),
},
'::after': {
- ...beforeAndAfter(color, size, type, variables),
+ ...beforeAndAfter(color, size, variables),
marginLeft: pxToRem(20),
},
}
: {
'::before': {
- ...beforeAndAfter(color, size, type, variables),
+ ...beforeAndAfter(color, size, variables),
},
}),
}
diff --git a/src/themes/teams/components/Divider/dividerVariables.ts b/src/themes/teams/components/Divider/dividerVariables.ts
index 915a1a6907..ad11315098 100644
--- a/src/themes/teams/components/Divider/dividerVariables.ts
+++ b/src/themes/teams/components/Divider/dividerVariables.ts
@@ -1,23 +1,25 @@
import * as _ from 'lodash'
-import { pxToRem } from '../../utils'
+import { FontWeightProperty } from 'csstype'
-import { EmphasisColors, NaturalColors } from '../../../types'
+import { pxToRem } from '../../utils'
+import { ColorValues } from '../../../types'
+import { mapColorsToScheme } from '../../../../lib'
export interface DividerVariables {
- colors: Record
+ colors: ColorValues
dividerColor: string
textColor: string
textFontSize: string
textLineHeight: string
- importantFontWeight: string
+ importantFontWeight: FontWeightProperty
dividerPadding: string
}
export default (siteVars: any): DividerVariables => {
- const colorVariant = '500'
+ const colorVariant = 500
return {
- colors: _.mapValues({ ...siteVars.emphasisColors, ...siteVars.naturalColors }, colorVariant),
+ colors: mapColorsToScheme(siteVars, colorVariant),
dividerColor: siteVars.gray09,
textColor: siteVars.gray03,
textFontSize: siteVars.fontSizeSmall,
diff --git a/src/themes/teams/components/Text/textStyles.ts b/src/themes/teams/components/Text/textStyles.ts
index 24861465a2..4c1ac02d67 100644
--- a/src/themes/teams/components/Text/textStyles.ts
+++ b/src/themes/teams/components/Text/textStyles.ts
@@ -1,3 +1,5 @@
+import * as _ from 'lodash'
+
import { ComponentStyleFunctionParam, ICSSInJSStyle } from '../../../types'
import { truncateStyle } from '../../../../styles/customCSS'
import { TextVariables } from './textVariables'
@@ -7,6 +9,7 @@ export default {
root: ({
props: {
atMention,
+ color,
disabled,
error,
size,
@@ -43,6 +46,7 @@ export default {
fontWeight: v.importantWeight,
color: v.importantColor,
}),
+ ...(color && { color: _.get(v.colors, color) }),
...(weight === 'light' && {
fontWeight: v.fontWeightLight,
diff --git a/src/themes/teams/components/Text/textVariables.ts b/src/themes/teams/components/Text/textVariables.ts
index 6330b4b256..75292294de 100644
--- a/src/themes/teams/components/Text/textVariables.ts
+++ b/src/themes/teams/components/Text/textVariables.ts
@@ -1,4 +1,8 @@
+import { ColorValues } from '../../../types'
+import { mapColorsToScheme } from '../../../../lib'
+
export interface TextVariables {
+ colors: ColorValues
atMentionMeColor: string
atMentionMeFontWeight: number
atMentionOtherColor: string
@@ -29,7 +33,10 @@ export interface TextVariables {
}
export default (siteVariables): TextVariables => {
+ const colorVariant = 500
+
return {
+ colors: mapColorsToScheme(siteVariables, colorVariant),
atMentionOtherColor: siteVariables.brand06,
atMentionMeColor: siteVariables.orange04,
atMentionMeFontWeight: siteVariables.fontWeightBold,
diff --git a/src/themes/types.ts b/src/themes/types.ts
index 076b84951f..646d876224 100644
--- a/src/themes/types.ts
+++ b/src/themes/types.ts
@@ -71,6 +71,16 @@ type EmphasisColorsStrict = Partial<{
export type EmphasisColors = Extendable
+/**
+ * A type for extracting the color names.
+ */
+type ColorNames = keyof (EmphasisColorsStrict & NaturalColorsStrict)
+
+/**
+ * A type for an extendable set of ColorNames properties of type T
+ */
+export type ColorValues = Extendable>, T>
+
/**
* A type for a base colors.
*/
From fa78e86780e6c43cb6f478e4f6881a13960d59e0 Mon Sep 17 00:00:00 2001
From: Alexandru Buliga
Date: Tue, 18 Dec 2018 18:03:41 +0200
Subject: [PATCH 48/55] feat(header): header and header description color prop
(#628)
* feat(header): header and header description color prop
* changelog
* fixed examples
* addressed PR comments
---
CHANGELOG.md | 1 +
.../HeaderExampleColor.shorthand.tsx | 21 +++++++++++++++++++
.../Header/Variations/HeaderExampleColor.tsx | 20 ++++++++++++++++++
.../components/Header/Variations/index.tsx | 5 +++++
src/components/Header/Header.tsx | 6 ++++--
src/components/Header/HeaderDescription.tsx | 6 ++++--
.../Header/headerDescriptionStyles.ts | 14 +++++++++----
.../Header/headerDescriptionVariables.ts | 18 +++++++++++++---
.../teams/components/Header/headerStyles.ts | 19 +++++++++++------
.../components/Header/headerVariables.ts | 6 ++++++
10 files changed, 99 insertions(+), 17 deletions(-)
create mode 100644 docs/src/examples/components/Header/Variations/HeaderExampleColor.shorthand.tsx
create mode 100644 docs/src/examples/components/Header/Variations/HeaderExampleColor.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2293b5161..721a2c2f48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
+- Add `color` prop to `Header` and `HeaderDescription` components @Bugaa92 ([#628](https://github.com/stardust-ui/react/pull/628))
## [v0.15.0](https://github.com/stardust-ui/react/tree/v0.15.0) (2018-12-17)
diff --git a/docs/src/examples/components/Header/Variations/HeaderExampleColor.shorthand.tsx b/docs/src/examples/components/Header/Variations/HeaderExampleColor.shorthand.tsx
new file mode 100644
index 0000000000..0ddbdd56f3
--- /dev/null
+++ b/docs/src/examples/components/Header/Variations/HeaderExampleColor.shorthand.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+import _ from 'lodash'
+import { Header, ProviderConsumer } from '@stardust-ui/react'
+
+const HeaderExampleColor = () => (
+
+ _.keys({ ...emphasisColors, ...naturalColors }).map(color => (
+
+ ))
+ }
+ />
+)
+
+export default HeaderExampleColor
diff --git a/docs/src/examples/components/Header/Variations/HeaderExampleColor.tsx b/docs/src/examples/components/Header/Variations/HeaderExampleColor.tsx
new file mode 100644
index 0000000000..9bd112cd00
--- /dev/null
+++ b/docs/src/examples/components/Header/Variations/HeaderExampleColor.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import _ from 'lodash'
+import { Header, ProviderConsumer } from '@stardust-ui/react'
+
+const HeaderExampleColor = () => (
+
+ _.keys({ ...emphasisColors, ...naturalColors }).map(color => (
+
+ {_.startCase(color)}
+
+ {`Description of ${_.lowerCase(color)} color`}
+
+
+ ))
+ }
+ />
+)
+
+export default HeaderExampleColor
diff --git a/docs/src/examples/components/Header/Variations/index.tsx b/docs/src/examples/components/Header/Variations/index.tsx
index ca7a86be9c..346dfbbfc2 100644
--- a/docs/src/examples/components/Header/Variations/index.tsx
+++ b/docs/src/examples/components/Header/Variations/index.tsx
@@ -19,6 +19,11 @@ const Variations = () => (
description="Headers may be aligned to the left, right, center or be justified."
examplePath="components/Header/Variations/HeaderExampleTextAlign"
/>
+
)
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
index d9ade9af52..a563d68fca 100644
--- a/src/components/Header/Header.tsx
+++ b/src/components/Header/Header.tsx
@@ -9,6 +9,7 @@ import {
ChildrenComponentProps,
ContentComponentProps,
commonPropTypes,
+ ColorComponentProps,
} from '../../lib'
import HeaderDescription from './HeaderDescription'
import { Extendable, ShorthandValue } from '../../../types/utils'
@@ -16,7 +17,8 @@ import { Extendable, ShorthandValue } from '../../../types/utils'
export interface HeaderProps
extends UIComponentProps,
ChildrenComponentProps,
- ContentComponentProps {
+ ContentComponentProps,
+ ColorComponentProps {
/** Shorthand for Header.Description. */
description?: ShorthandValue
@@ -40,7 +42,7 @@ class Header extends UIComponent, any> {
static displayName = 'Header'
static propTypes = {
- ...commonPropTypes.createCommon(),
+ ...commonPropTypes.createCommon({ color: true }),
description: customPropTypes.itemShorthand,
textAlign: PropTypes.oneOf(['left', 'center', 'right', 'justified']),
}
diff --git a/src/components/Header/HeaderDescription.tsx b/src/components/Header/HeaderDescription.tsx
index 1943948043..adc25a6209 100644
--- a/src/components/Header/HeaderDescription.tsx
+++ b/src/components/Header/HeaderDescription.tsx
@@ -8,13 +8,15 @@ import {
ChildrenComponentProps,
ContentComponentProps,
commonPropTypes,
+ ColorComponentProps,
} from '../../lib'
import { Extendable } from '../../../types/utils'
export interface HeaderDescriptionProps
extends UIComponentProps,
ChildrenComponentProps,
- ContentComponentProps {}
+ ContentComponentProps,
+ ColorComponentProps {}
/**
* A header's description provides more detailed information.
@@ -27,7 +29,7 @@ class HeaderDescription extends UIComponent,
static displayName = 'HeaderDescription'
static propTypes = {
- ...commonPropTypes.createCommon(),
+ ...commonPropTypes.createCommon({ color: true }),
}
static defaultProps = {
diff --git a/src/themes/teams/components/Header/headerDescriptionStyles.ts b/src/themes/teams/components/Header/headerDescriptionStyles.ts
index b49c56d101..9efcdd2d67 100644
--- a/src/themes/teams/components/Header/headerDescriptionStyles.ts
+++ b/src/themes/teams/components/Header/headerDescriptionStyles.ts
@@ -1,11 +1,17 @@
+import * as _ from 'lodash'
+
import { pxToRem } from '../../utils'
-import { ICSSInJSStyle } from '../../../types'
+import { ICSSInJSStyle, ComponentSlotStylesInput } from '../../../types'
+import { HeaderDescriptionProps } from '../../../../components/Header/HeaderDescription'
+import { HeaderDescriptionVariables } from './headerDescriptionVariables'
-export default {
- root: ({ variables: v }): ICSSInJSStyle => ({
+const headerStyles: ComponentSlotStylesInput = {
+ root: ({ props: p, variables: v }): ICSSInJSStyle => ({
display: 'block',
+ color: _.get(v.colors, p.color, v.color),
fontSize: pxToRem(22),
- color: v.color,
fontWeight: 400,
}),
}
+
+export default headerStyles
diff --git a/src/themes/teams/components/Header/headerDescriptionVariables.ts b/src/themes/teams/components/Header/headerDescriptionVariables.ts
index 3de2fffb02..c41a19513c 100644
--- a/src/themes/teams/components/Header/headerDescriptionVariables.ts
+++ b/src/themes/teams/components/Header/headerDescriptionVariables.ts
@@ -1,3 +1,15 @@
-export default siteVariables => ({
- color: siteVariables.gray04,
-})
+import { ColorValues } from '../../../types'
+import { mapColorsToScheme } from '../../../../lib'
+
+export interface HeaderDescriptionVariables {
+ colors: ColorValues
+ color: string
+}
+
+export default (siteVariables: any): HeaderDescriptionVariables => {
+ const colorVariant = 500
+ return {
+ colors: mapColorsToScheme(siteVariables, colorVariant),
+ color: siteVariables.gray04,
+ }
+}
diff --git a/src/themes/teams/components/Header/headerStyles.ts b/src/themes/teams/components/Header/headerStyles.ts
index 668937a72d..ecf3789006 100644
--- a/src/themes/teams/components/Header/headerStyles.ts
+++ b/src/themes/teams/components/Header/headerStyles.ts
@@ -1,10 +1,17 @@
-import { ICSSInJSStyle } from '../../../types'
+import * as _ from 'lodash'
+import { TextAlignProperty } from 'csstype'
-export default {
- root: ({ props, variables: v }): ICSSInJSStyle => ({
- color: v.color,
- textAlign: props.textAlign,
+import { ICSSInJSStyle, ComponentSlotStylesInput } from '../../../types'
+import { HeaderProps } from '../../../../components/Header/Header'
+import { HeaderVariables } from './headerVariables'
+
+const headerStyles: ComponentSlotStylesInput = {
+ root: ({ props: p, variables: v }): ICSSInJSStyle => ({
display: 'block',
- ...(props.description && { marginBottom: 0 }),
+ color: _.get(v.colors, p.color, v.color),
+ textAlign: p.textAlign as TextAlignProperty,
+ ...(p.description && { marginBottom: 0 }),
}),
}
+
+export default headerStyles
diff --git a/src/themes/teams/components/Header/headerVariables.ts b/src/themes/teams/components/Header/headerVariables.ts
index 2147b544b9..7891afe646 100644
--- a/src/themes/teams/components/Header/headerVariables.ts
+++ b/src/themes/teams/components/Header/headerVariables.ts
@@ -1,10 +1,16 @@
+import { ColorValues } from '../../../types'
+import { mapColorsToScheme } from '../../../../lib'
+
export interface HeaderVariables {
+ colors: ColorValues
color: string
descriptionColor: string
}
export default (siteVars: any): HeaderVariables => {
+ const colorVariant = 500
return {
+ colors: mapColorsToScheme(siteVars, colorVariant),
color: siteVars.black,
descriptionColor: undefined,
}
From 489530107bd22eb4484e4d0a7296615f62c6c21b Mon Sep 17 00:00:00 2001
From: kuzhelov
Date: Tue, 18 Dec 2018 20:11:51 +0100
Subject: [PATCH 49/55] fix(Popup): allow to 'detach' from trigger and RTL
adjustments (#612)
* introduce offset prop
* correct description of supported values
* update changelog
* introduce fix
* ensure RTL is properly applied to complex offset expressions
* rename method to make logic more expressive
* add unit tests
* remove unnecessary grid props from offset example
* update changelog
---
CHANGELOG.md | 3 ++
.../PopupExampleOffset.shorthand.tsx | 45 +++++++----------
src/components/Popup/Popup.tsx | 19 +++----
src/components/Popup/positioningHelper.ts | 29 ++++++++++-
test/specs/components/Popup/Popup-test.tsx | 50 ++++++++++++++++++-
5 files changed, 103 insertions(+), 43 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 721a2c2f48..af197727ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
+### Fixes
+- Ensure `Popup` properly flips values of `offset` prop in RTL @kuzhelov ([#612](https://github.com/stardust-ui/react/pull/612))
+
### Features
- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
- Add `color` prop to `Header` and `HeaderDescription` components @Bugaa92 ([#628](https://github.com/stardust-ui/react/pull/628))
diff --git a/docs/src/examples/components/Popup/Variations/PopupExampleOffset.shorthand.tsx b/docs/src/examples/components/Popup/Variations/PopupExampleOffset.shorthand.tsx
index 8376ee024c..d0901e6e01 100644
--- a/docs/src/examples/components/Popup/Variations/PopupExampleOffset.shorthand.tsx
+++ b/docs/src/examples/components/Popup/Variations/PopupExampleOffset.shorthand.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import { Button, Grid, Popup, Alignment, Position } from '@stardust-ui/react'
+import { Button, Grid, Popup } from '@stardust-ui/react'
const renderButton = rotateArrowUp => (
- ),
- }}
- key={`${position}-${align}`}
- />
- ))}
+
+
+ The popup is rendered at above-start
+
+ corner of the trigger.
+
+ ),
+ }}
+ key="above-start"
+ />
)
diff --git a/src/components/Popup/Popup.tsx b/src/components/Popup/Popup.tsx
index 3876f75dde..812c953743 100644
--- a/src/components/Popup/Popup.tsx
+++ b/src/components/Popup/Popup.tsx
@@ -18,7 +18,7 @@ import {
import { ComponentEventHandler, Extendable, ShorthandValue } from '../../../types/utils'
import Ref from '../Ref/Ref'
-import computePopupPlacement, { Alignment, Position } from './positioningHelper'
+import { getPopupPlacement, applyRtlToOffset, Alignment, Position } from './positioningHelper'
import PopupContent from './PopupContent'
@@ -257,11 +257,14 @@ export default class Popup extends AutoControlledComponent {
* | after | center | right | left
* | after | bottom | right-end | left-end
*/
-export default ({
+export const getPopupPlacement = ({
align,
position,
rtl,
@@ -74,3 +74,30 @@ export default ({
return `${computedPosition}${stringifiedAlignment}` as Placement
}
+
+/////////////////////////////////
+// OFFSET VALUES ADJUSTMENT
+/////////////////////////////////
+
+const flipPlusMinusSigns = (offset: string): string => {
+ return offset
+ .replace(/\-/g, '')
+ .replace(/^(\s*)(?=\d)/, '')
+ .replace(/\+/g, '')
+ .replace(//g, '+')
+ .replace(//g, '-')
+ .trimLeft()
+ .replace(/^\+/, '')
+}
+
+export const applyRtlToOffset = (offset: string, position: Position): string => {
+ if (position === 'above' || position === 'below') {
+ const [horizontal, vertical] = offset.split(',')
+ return [flipPlusMinusSigns(horizontal), vertical]
+ .join(', ')
+ .replace(/, $/, '')
+ .trim()
+ }
+
+ return offset
+}
diff --git a/test/specs/components/Popup/Popup-test.tsx b/test/specs/components/Popup/Popup-test.tsx
index 3470c4be09..8d79103ec5 100644
--- a/test/specs/components/Popup/Popup-test.tsx
+++ b/test/specs/components/Popup/Popup-test.tsx
@@ -1,4 +1,9 @@
-import computePopupPlacement, { Position, Alignment } from 'src/components/Popup/positioningHelper'
+import {
+ getPopupPlacement,
+ applyRtlToOffset,
+ Position,
+ Alignment,
+} from 'src/components/Popup/positioningHelper'
import { Placement } from 'popper.js'
type PositionTestInput = {
@@ -16,7 +21,7 @@ describe('Popup', () => {
rtl = false,
}: PositionTestInput) =>
it(`Popup ${position} position is transformed to ${expectedPlacement} Popper's placement`, () => {
- const actualPlacement = computePopupPlacement({ align, position, rtl })
+ const actualPlacement = getPopupPlacement({ align, position, rtl })
expect(actualPlacement).toEqual(expectedPlacement)
})
@@ -56,4 +61,45 @@ describe('Popup', () => {
testPopupPositionInRtl({ position: 'after', align: 'center', expectedPlacement: 'left' })
testPopupPositionInRtl({ position: 'after', align: 'bottom', expectedPlacement: 'left-end' })
})
+
+ describe('Popup offset transformed correctly in RTL', () => {
+ it("applies transform only for 'above' and 'below' postioning", () => {
+ const originalOffsetValue = '100%'
+
+ expect(applyRtlToOffset(originalOffsetValue, 'above')).not.toBe(originalOffsetValue)
+ expect(applyRtlToOffset(originalOffsetValue, 'below')).not.toBe(originalOffsetValue)
+
+ expect(applyRtlToOffset(originalOffsetValue, 'before')).toBe(originalOffsetValue)
+ expect(applyRtlToOffset(originalOffsetValue, 'after')).toBe(originalOffsetValue)
+ })
+
+ const expectOffsetTransformResult = (originalOffset, resultOffset) => {
+ expect(applyRtlToOffset(originalOffset, 'above')).toBe(resultOffset)
+ }
+
+ it('flips sign of simple expressions', () => {
+ expectOffsetTransformResult('100%', '-100%')
+ expectOffsetTransformResult(' 2000%p ', '-2000%p')
+ expectOffsetTransformResult('100 ', '-100')
+ expectOffsetTransformResult(' - 200vh', '200vh')
+ })
+
+ it('flips sign of complex expressions', () => {
+ expectOffsetTransformResult('100% + 200', '-100% - 200')
+ expectOffsetTransformResult(' - 2000%p - 400 +800vh ', '2000%p + 400 -800vh')
+ })
+
+ it('transforms only horizontal offset value', () => {
+ const xOffset = '-100%'
+ const yOffset = '800vh'
+
+ const offsetValue = [xOffset, yOffset].join(',')
+ const [xOffsetTransformed, yOffsetTransformed] = applyRtlToOffset(offsetValue, 'above').split(
+ ',',
+ )
+
+ expect(xOffsetTransformed.trim()).not.toBe(xOffset)
+ expect(yOffsetTransformed.trim()).toBe(yOffset)
+ })
+ })
})
From 3c6e75e35ff27148bd721d8739476839b142c645 Mon Sep 17 00:00:00 2001
From: Sofiya Huts <8460706+sophieH29@users.noreply.github.com>
Date: Wed, 19 Dec 2018 10:28:56 +0100
Subject: [PATCH 50/55] fix(SelectableList): Items in list should be selectable
(#566)
* Reflect which item is selected in list
* Make list derived from autocontrolled component
* small fix
* Update ListExampleSelection.tsx
* Update ListExampleSelection.shorthand.tsx
* Small improvement
* Rename *ItemIndex -> *Index
* Names refactoring
* Minor improvements
* update changelog
* Add onSelectedIndexChange
* Add some tests
* Small improvements afer CR
* Small improvements afer CR
* Small improvements afer CR
* create focus handler when List is constructed
* fix changelog
* changelog
---
CHANGELOG.md | 1 +
.../Content/ListExampleEndMedia.shorthand.tsx | 2 +-
.../List/Content/ListExampleEndMedia.tsx | 6 +-
.../List/Types/ListExample.shorthand.tsx | 4 +-
.../components/List/Types/ListExample.tsx | 4 +-
...sx => ListExampleSelectable.shorthand.tsx} | 6 +-
...election.tsx => ListExampleSelectable.tsx} | 14 ++-
...tExampleSelectableControlled.shorthand.tsx | 48 ++++++++++
.../List/Types/ListExampleSelection.knobs.tsx | 24 -----
.../examples/components/List/Types/index.tsx | 11 ++-
docs/src/prototypes/SearchPage/SearchPage.tsx | 2 +-
src/components/List/List.tsx | 90 ++++++++++++-------
src/components/List/ListItem.tsx | 51 ++++++-----
src/index.ts | 2 +-
.../Behaviors/List/listBehavior.ts | 4 +-
.../Behaviors/List/listItemBehavior.ts | 4 +-
.../List/selectableListItemBehavior.ts | 15 ++--
.../FocusHandling/FocusContainer.ts | 40 ++++-----
.../teams/components/List/listItemStyles.ts | 20 +++--
.../components/List/listItemVariables.ts | 8 +-
test/specs/behaviors/listBehavior-test.tsx | 6 +-
.../specs/behaviors/listItemBehavior-test.tsx | 6 +-
test/specs/components/List/List-test.ts | 13 ---
test/specs/components/List/List-test.tsx | 76 ++++++++++++++++
.../lib/accessibility/FocusContainer-test.ts | 32 +++----
25 files changed, 316 insertions(+), 173 deletions(-)
rename docs/src/examples/components/List/Types/{ListExampleSelection.shorthand.tsx => ListExampleSelectable.shorthand.tsx} (79%)
rename docs/src/examples/components/List/Types/{ListExampleSelection.tsx => ListExampleSelectable.tsx} (72%)
create mode 100644 docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx
delete mode 100644 docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx
delete mode 100644 test/specs/components/List/List-test.ts
create mode 100644 test/specs/components/List/List-test.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af197727ac..7006d2dd84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Fixes
- Ensure `Popup` properly flips values of `offset` prop in RTL @kuzhelov ([#612](https://github.com/stardust-ui/react/pull/612))
+- Fix `List` - items should be selectable @sophieH29 ([#566](https://github.com/stardust-ui/react/pull/566))
### Features
- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
diff --git a/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx b/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx
index 3955f2f76a..6915275332 100644
--- a/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx
+++ b/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx
@@ -21,6 +21,6 @@ const items = [
},
]
-const ListExample = () =>
+const ListExample = () =>
export default ListExample
diff --git a/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx b/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx
index 6b7a375791..3b99d18770 100644
--- a/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx
+++ b/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx
@@ -8,17 +8,17 @@ const ListExample = () => (
)
diff --git a/docs/src/examples/components/List/Types/ListExample.shorthand.tsx b/docs/src/examples/components/List/Types/ListExample.shorthand.tsx
index 866bfe7a56..10f591fff3 100644
--- a/docs/src/examples/components/List/Types/ListExample.shorthand.tsx
+++ b/docs/src/examples/components/List/Types/ListExample.shorthand.tsx
@@ -25,6 +25,6 @@ const items = [
},
]
-const ListExampleSelection = ({ knobs }) =>
+const ListExampleSelectable = ({ knobs }) =>
-export default ListExampleSelection
+export default ListExampleSelectable
diff --git a/docs/src/examples/components/List/Types/ListExample.tsx b/docs/src/examples/components/List/Types/ListExample.tsx
index da2e3c1f4a..250ab8ef2a 100644
--- a/docs/src/examples/components/List/Types/ListExample.tsx
+++ b/docs/src/examples/components/List/Types/ListExample.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { List, Image } from '@stardust-ui/react'
-const ListExampleSelection = ({ knobs }) => (
+const ListExampleSelectable = ({ knobs }) => (
}
@@ -24,4 +24,4 @@ const ListExampleSelection = ({ knobs }) => (
)
-export default ListExampleSelection
+export default ListExampleSelectable
diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx
similarity index 79%
rename from docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx
rename to docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx
index fe7842da57..49a526710e 100644
--- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx
+++ b/docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx
@@ -25,8 +25,6 @@ const items = [
},
]
-const selection = knobs => (knobs === undefined ? true : knobs.selection)
+const ListExampleSelectable = () =>
-const ListExampleSelection = ({ knobs }) =>
-
-export default ListExampleSelection
+export default ListExampleSelectable
diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.tsx b/docs/src/examples/components/List/Types/ListExampleSelectable.tsx
similarity index 72%
rename from docs/src/examples/components/List/Types/ListExampleSelection.tsx
rename to docs/src/examples/components/List/Types/ListExampleSelectable.tsx
index ff00e6ef0c..55029d1256 100644
--- a/docs/src/examples/components/List/Types/ListExampleSelection.tsx
+++ b/docs/src/examples/components/List/Types/ListExampleSelectable.tsx
@@ -1,32 +1,30 @@
import React from 'react'
import { List, Image } from '@stardust-ui/react'
-const selection = knobs => (knobs === undefined ? true : knobs.selection)
-
-const ListExampleSelection = ({ knobs }) => (
-
+const ListExampleSelectable = () => (
+
}
header="Irving Kuhic"
headerMedia="7:26:56 AM"
content="Program the sensor to the SAS alarm through the haptic SQL card!"
- selection={selection(knobs)}
+ selectable
/>
}
header="Skyler Parks"
headerMedia="11:30:17 PM"
content="Use the online FTP application to input the multi-byte application!"
- selection={selection(knobs)}
+ selectable
/>
}
header="Dante Schneider"
headerMedia="5:22:40 PM"
content="The GB pixel is down, navigate the virtual interface!"
- selection={selection(knobs)}
+ selectable
/>
)
-export default ListExampleSelection
+export default ListExampleSelectable
diff --git a/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx
new file mode 100644
index 0000000000..f493b03105
--- /dev/null
+++ b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx
@@ -0,0 +1,48 @@
+import * as React from 'react'
+import { List, Image } from '@stardust-ui/react'
+
+class SelectableListControlledExample extends React.Component {
+ state = { selectedIndex: -1 }
+
+ items = [
+ {
+ key: 'irving',
+ media: ,
+ header: 'Irving Kuhic',
+ headerMedia: '7:26:56 AM',
+ content: 'Program the sensor to the SAS alarm through the haptic SQL card!',
+ },
+ {
+ key: 'skyler',
+ media: ,
+ header: 'Skyler Parks',
+ headerMedia: '11:30:17 PM',
+ content: 'Use the online FTP application to input the multi-byte application!',
+ },
+ {
+ key: 'dante',
+ media: ,
+ header: 'Dante Schneider',
+ headerMedia: '5:22:40 PM',
+ content: 'The GB pixel is down, navigate the virtual interface!',
+ },
+ ]
+
+ render() {
+ return (
+ {
+ alert(
+ `List is requested to change its selectedIndex state to "${newProps.selectedIndex}"`,
+ )
+ this.setState({ selectedIndex: newProps.selectedIndex })
+ }}
+ items={this.items}
+ />
+ )
+ }
+}
+
+export default SelectableListControlledExample
diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx
deleted file mode 100644
index f8494fc867..0000000000
--- a/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import PropTypes from 'prop-types'
-import React from 'react'
-import Knobs from 'docs/src/components/Knobs/Knobs'
-
-const ListExampleSelectionKnobs: any = props => {
- const { onKnobChange, selection } = props
-
- return (
-
-
-
- )
-}
-
-ListExampleSelectionKnobs.propTypes = {
- onKnobChange: PropTypes.func.isRequired,
- selection: PropTypes.bool,
-}
-
-ListExampleSelectionKnobs.defaultProps = {
- selection: true,
-}
-
-export default ListExampleSelectionKnobs
diff --git a/docs/src/examples/components/List/Types/index.tsx b/docs/src/examples/components/List/Types/index.tsx
index 0d78cbf48a..7483e6ff44 100644
--- a/docs/src/examples/components/List/Types/index.tsx
+++ b/docs/src/examples/components/List/Types/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
@@ -10,9 +10,14 @@ const Types = () => (
examplePath="components/List/Types/ListExample"
/>
+
)
diff --git a/docs/src/prototypes/SearchPage/SearchPage.tsx b/docs/src/prototypes/SearchPage/SearchPage.tsx
index c655a20ad5..fa784abf6c 100644
--- a/docs/src/prototypes/SearchPage/SearchPage.tsx
+++ b/docs/src/prototypes/SearchPage/SearchPage.tsx
@@ -97,7 +97,7 @@ class SearchPage extends React.Component {
Results {results.length} of {DATA_RECORDS.length}
-
+
)}
diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx
index cee47f7d2e..009b69940e 100644
--- a/src/components/List/List.tsx
+++ b/src/components/List/List.tsx
@@ -6,7 +6,7 @@ import * as PropTypes from 'prop-types'
import {
customPropTypes,
childrenExist,
- UIComponent,
+ AutoControlledComponent,
UIComponentProps,
ChildrenComponentProps,
commonPropTypes,
@@ -15,7 +15,7 @@ import ListItem from './ListItem'
import { listBehavior } from '../../lib/accessibility'
import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
import { ContainerFocusHandler } from '../../lib/accessibility/FocusHandling/FocusContainer'
-import { Extendable, ShorthandValue } from '../../../types/utils'
+import { Extendable, ShorthandValue, ComponentEventHandler } from '../../../types/utils'
export interface ListProps extends UIComponentProps, ChildrenComponentProps {
/**
@@ -30,8 +30,21 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps {
/** Shorthand array of props for ListItem. */
items?: ShorthandValue[]
- /** A selection list formats list items as possible choices. */
- selection?: boolean
+ /** A selectable list formats list items as possible choices. */
+ selectable?: boolean
+
+ /** Index of the currently selected item. */
+ selectedIndex?: number
+
+ /** Initial selectedIndex value. */
+ defaultSelectedIndex?: number
+
+ /**
+ * Event for request to change 'selectedIndex' value.
+ * @param {SyntheticEvent} event - React's original SyntheticEvent.
+ * @param {object} data - All props and proposed value.
+ */
+ onSelectedIndexChange?: ComponentEventHandler
/** Truncates content */
truncateContent?: boolean
@@ -41,13 +54,14 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps {
}
export interface ListState {
- selectedItemIndex: number
+ focusedIndex: number
+ selectedIndex?: number
}
/**
* A list displays a group of related content.
*/
-class List extends UIComponent, ListState> {
+class List extends AutoControlledComponent, ListState> {
static displayName = 'List'
static className = 'ui-list'
@@ -59,9 +73,12 @@ class List extends UIComponent, ListState> {
accessibility: PropTypes.func,
debug: PropTypes.bool,
items: customPropTypes.collectionShorthand,
- selection: PropTypes.bool,
+ selectable: PropTypes.bool,
truncateContent: PropTypes.bool,
truncateHeader: PropTypes.bool,
+ selectedIndex: PropTypes.number,
+ defaultSelectedIndex: PropTypes.number,
+ onSelectedIndexChange: PropTypes.func,
}
static defaultProps = {
@@ -69,14 +86,15 @@ class List extends UIComponent, ListState> {
accessibility: listBehavior as Accessibility,
}
+ static autoControlledProps = ['selectedIndex']
+ getInitialAutoControlledState() {
+ return { selectedIndex: -1, focusedIndex: 0 }
+ }
+
static Item = ListItem
// List props that are passed to each individual Item props
- static itemProps = ['debug', 'selection', 'truncateContent', 'truncateHeader', 'variables']
-
- public state = {
- selectedItemIndex: 0,
- }
+ static itemProps = ['debug', 'selectable', 'truncateContent', 'truncateHeader', 'variables']
private focusHandler: ContainerFocusHandler = null
private itemRefs = []
@@ -100,6 +118,22 @@ class List extends UIComponent, ListState> {
},
}
+ constructor(props, context) {
+ super(props, context)
+
+ this.focusHandler = new ContainerFocusHandler(
+ () => this.props.items.length,
+ index => {
+ this.setState({ focusedIndex: index }, () => {
+ const targetComponent = this.itemRefs[index] && this.itemRefs[index].current
+ const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any
+
+ targetDomNode && targetDomNode.focus()
+ })
+ },
+ )
+ }
+
renderComponent({ ElementType, classes, accessibility, rest }) {
const { children } = this.props
@@ -115,36 +149,32 @@ class List extends UIComponent, ListState> {
)
}
- componentDidMount() {
- this.focusHandler = new ContainerFocusHandler(
- () => this.props.items.length,
- index => {
- this.setState({ selectedItemIndex: index }, () => {
- const targetComponent = this.itemRefs[index] && this.itemRefs[index].current
- const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any
-
- targetDomNode && targetDomNode.focus()
- })
- },
- )
- }
-
renderItems() {
const { items } = this.props
- const { selectedItemIndex } = this.state
+ const { focusedIndex, selectedIndex } = this.state
+
+ this.focusHandler.syncFocusedIndex(focusedIndex)
this.itemRefs = []
return _.map(items, (item, idx) => {
const maybeSelectableItemProps = {} as any
- if (this.props.selection) {
+ if (this.props.selectable) {
const ref = React.createRef()
this.itemRefs[idx] = ref
- maybeSelectableItemProps.tabIndex = idx === selectedItemIndex ? 0 : -1
maybeSelectableItemProps.ref = ref
- maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedItemIndex(idx)
+ maybeSelectableItemProps.onFocus = () => this.setState({ focusedIndex: idx })
+ maybeSelectableItemProps.onClick = e => {
+ this.trySetState({ selectedIndex: idx })
+ _.invoke(this.props, 'onSelectedIndexChange', e, {
+ ...this.props,
+ ...{ selectedIndex: idx },
+ })
+ }
+ maybeSelectableItemProps.selected = idx === selectedIndex
+ maybeSelectableItemProps.tabIndex = idx === focusedIndex ? 0 : -1
}
const itemProps = {
diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx
index febdea30d7..b6af86fd62 100644
--- a/src/components/List/ListItem.tsx
+++ b/src/components/List/ListItem.tsx
@@ -1,5 +1,5 @@
import * as React from 'react'
-
+import * as _ from 'lodash'
import * as PropTypes from 'prop-types'
import {
createShorthandFactory,
@@ -10,8 +10,8 @@ import {
} from '../../lib'
import ItemLayout from '../ItemLayout/ItemLayout'
import { listItemBehavior } from '../../lib/accessibility'
-import { Accessibility } from '../../lib/accessibility/types'
-import { Extendable } from '../../../types/utils'
+import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types'
+import { Extendable, ComponentEventHandler } from '../../../types/utils'
export interface ListItemProps extends UIComponentProps, ContentComponentProps {
/**
@@ -20,7 +20,7 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps
}
/**
* A list item contains a single piece of content within a list.
*/
-class ListItem extends UIComponent, ListItemState> {
+class ListItem extends UIComponent> {
static create: Function
static displayName = 'ListItem'
@@ -67,11 +73,14 @@ class ListItem extends UIComponent, ListItemState> {
important: PropTypes.bool,
media: PropTypes.any,
- selection: PropTypes.bool,
+ selectable: PropTypes.bool,
+ selected: PropTypes.bool,
+
truncateContent: PropTypes.bool,
truncateHeader: PropTypes.bool,
accessibility: PropTypes.func,
+ onClick: PropTypes.func,
}
static defaultProps = {
@@ -79,17 +88,18 @@ class ListItem extends UIComponent, ListItemState> {
accessibility: listItemBehavior as Accessibility,
}
- constructor(props: ListItemProps) {
- super(props, null)
-
- this.state = {
- isHovering: false,
- }
+ protected actionHandlers: AccessibilityActionHandlers = {
+ performClick: event => {
+ this.handleClick(event)
+ event.preventDefault()
+ },
}
- private itemRef = React.createRef()
+ handleClick = e => {
+ _.invoke(this.props, 'onClick', e, this.props)
+ }
- renderComponent({ ElementType, classes, accessibility, rest, styles }) {
+ renderComponent({ classes, accessibility, rest, styles }) {
const {
as,
debug,
@@ -121,8 +131,9 @@ class ListItem extends UIComponent, ListItemState> {
headerCSS={styles.header}
headerMediaCSS={styles.headerMedia}
contentCSS={styles.content}
- ref={this.itemRef}
+ onClick={this.handleClick}
{...accessibility.attributes.root}
+ {...accessibility.keyHandlers.root}
{...rest}
/>
)
diff --git a/src/index.ts b/src/index.ts
index 523f5ae664..b324c0fc66 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -72,7 +72,7 @@ export { default as Label, LabelProps } from './components/Label/Label'
export { default as Layout, LayoutPropsWithDefaults, LayoutProps } from './components/Layout/Layout'
export { default as List, ListProps } from './components/List/List'
-export { default as ListItem, ListItemState, ListItemProps } from './components/List/ListItem'
+export { default as ListItem, ListItemProps } from './components/List/ListItem'
export { default as Menu, MenuProps, MenuState } from './components/Menu/Menu'
export { default as MenuItem, MenuItemState, MenuItemProps } from './components/Menu/MenuItem'
diff --git a/src/lib/accessibility/Behaviors/List/listBehavior.ts b/src/lib/accessibility/Behaviors/List/listBehavior.ts
index 360e89f59e..54f91bf632 100644
--- a/src/lib/accessibility/Behaviors/List/listBehavior.ts
+++ b/src/lib/accessibility/Behaviors/List/listBehavior.ts
@@ -4,10 +4,10 @@ import basicListBehavior from './basicListBehavior'
/**
* @description
- * Defines a behavior 'BasicListBehavior' or 'SelectableListBehavior' based on property 'selection'.
+ * Defines a behavior 'BasicListBehavior' or 'SelectableListBehavior' based on property 'selectable'.
*/
const ListBehavior: Accessibility = (props: any) =>
- props.selection ? selectableListBehavior(props) : basicListBehavior(props)
+ props.selectable ? selectableListBehavior(props) : basicListBehavior(props)
export default ListBehavior
diff --git a/src/lib/accessibility/Behaviors/List/listItemBehavior.ts b/src/lib/accessibility/Behaviors/List/listItemBehavior.ts
index 09292f93ff..714bf96c3f 100644
--- a/src/lib/accessibility/Behaviors/List/listItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/List/listItemBehavior.ts
@@ -4,10 +4,10 @@ import { Accessibility } from '../../types'
/**
* @description
- * Defines a behavior "BasicListItemBehavior" or "SelectableListItemBehavior" based on "selection" property.
+ * Defines a behavior "BasicListItemBehavior" or "SelectableListItemBehavior" based on "selectable" property.
*/
const listItemBehavior: Accessibility = (props: any) =>
- props.selection ? selectableListItemBehavior(props) : basicListItemBehavior(props)
+ props.selectable ? selectableListItemBehavior(props) : basicListItemBehavior(props)
export default listItemBehavior
diff --git a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts
index 31f88d0b33..ae946d29a2 100644
--- a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts
+++ b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts
@@ -1,19 +1,24 @@
import { Accessibility } from '../../types'
+import * as keyboardKey from 'keyboard-key'
/**
* @specification
* Adds role='option'. This role is used for a selectable item in a list.
- * Adds attribute 'aria-selected=true' based on the property 'active'. Based on this screen readers will recognize the selected state of the item.
+ * Adds attribute 'aria-selected=true' based on the property 'selected'. Based on this screen readers will recognize the selected state of the item.
*/
const selectableListItemBehavior: Accessibility = (props: any) => ({
attributes: {
root: {
role: 'option',
- 'aria-selected': !!props['active'],
- ...(props.focusableItemProps && {
- tabIndex: props.focusableItemProps.isFocused ? '0' : '-1',
- }),
+ 'aria-selected': !!props['selected'],
+ },
+ },
+ keyActions: {
+ root: {
+ performClick: {
+ keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }],
+ },
},
},
})
diff --git a/src/lib/accessibility/FocusHandling/FocusContainer.ts b/src/lib/accessibility/FocusHandling/FocusContainer.ts
index 14238fdb47..ef2aea39bd 100644
--- a/src/lib/accessibility/FocusHandling/FocusContainer.ts
+++ b/src/lib/accessibility/FocusHandling/FocusContainer.ts
@@ -1,29 +1,29 @@
import * as _ from 'lodash'
export class ContainerFocusHandler {
- private focusedItemIndex = 0
+ private focusedIndex = 0
constructor(private getItemsCount: () => number, private readonly setFocusAt: (number) => void) {}
private noItems = (): boolean => this.getItemsCount() === 0
- private constrainFocusedItemIndex(): void {
- if (this.focusedItemIndex < 0) {
- this.focusedItemIndex = 0
+ private constrainFocusedIndex(): void {
+ if (this.focusedIndex < 0) {
+ this.focusedIndex = 0
}
const itemsCount = this.getItemsCount()
- if (this.focusedItemIndex >= itemsCount) {
- this.focusedItemIndex = itemsCount - 1
+ if (this.focusedIndex >= itemsCount) {
+ this.focusedIndex = itemsCount - 1
}
}
- public getFocusedItemIndex(): number {
- return this.focusedItemIndex
+ public getFocusedIndex(): number {
+ return this.focusedIndex
}
- public syncFocusedItemIndex(withCurrentIndex: number) {
- this.focusedItemIndex = withCurrentIndex
+ public syncFocusedIndex(withCurrentIndex: number) {
+ this.focusedIndex = withCurrentIndex
}
public movePrevious(): void {
@@ -31,10 +31,10 @@ export class ContainerFocusHandler {
return
}
- this.focusedItemIndex -= 1
- this.constrainFocusedItemIndex()
+ this.focusedIndex -= 1
+ this.constrainFocusedIndex()
- this.setFocusAt(this.focusedItemIndex)
+ this.setFocusAt(this.focusedIndex)
}
public moveNext(): void {
@@ -42,10 +42,10 @@ export class ContainerFocusHandler {
return
}
- this.focusedItemIndex += 1
- this.constrainFocusedItemIndex()
+ this.focusedIndex += 1
+ this.constrainFocusedIndex()
- this.setFocusAt(this.focusedItemIndex)
+ this.setFocusAt(this.focusedIndex)
}
public moveFirst(): void {
@@ -53,8 +53,8 @@ export class ContainerFocusHandler {
return
}
- this.focusedItemIndex = 0
- this.setFocusAt(this.focusedItemIndex)
+ this.focusedIndex = 0
+ this.setFocusAt(this.focusedIndex)
}
public moveLast(): void {
@@ -62,7 +62,7 @@ export class ContainerFocusHandler {
return
}
- this.focusedItemIndex = this.getItemsCount() - 1
- this.setFocusAt(this.focusedItemIndex)
+ this.focusedIndex = this.getItemsCount() - 1
+ this.setFocusAt(this.focusedIndex)
}
}
diff --git a/src/themes/teams/components/List/listItemStyles.ts b/src/themes/teams/components/List/listItemStyles.ts
index 4131c9e584..34721950c3 100644
--- a/src/themes/teams/components/List/listItemStyles.ts
+++ b/src/themes/teams/components/List/listItemStyles.ts
@@ -2,9 +2,9 @@ import { pxToRem } from '../../utils'
import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types'
import { ListItemProps } from '../../../../components/List/ListItem'
-const hoverStyle = variables => ({
- background: variables.selectionHoverBackgroundColor,
- color: variables.selectionHoverColor,
+const hoverFocusStyle = variables => ({
+ background: variables.selectableFocusHoverBackgroundColor,
+ color: variables.selectableFocusHoverColor,
cursor: 'pointer',
'& .ui-item-layout__header': { color: 'inherit' },
@@ -18,16 +18,22 @@ const hoverStyle = variables => ({
'& .ui-item-layout__endMedia': { display: 'block', color: 'inherit' },
})
+const selectedStyle = variables => ({
+ background: variables.selectedBackgroundColor,
+ color: variables.selectedColor,
+})
+
const listItemStyles: ComponentSlotStylesInput = {
- root: ({ props: { selection, important }, variables }): ICSSInJSStyle => ({
- ...(selection && {
+ root: ({ props: { selectable, selected, important }, variables }): ICSSInJSStyle => ({
+ ...(selectable && {
position: 'relative',
// hide the end media by default
'& .ui-item-layout__endMedia': { display: 'none' },
- '&:hover': hoverStyle(variables),
- '&:focus': hoverStyle(variables),
+ '&:hover': hoverFocusStyle(variables),
+ '&:focus': hoverFocusStyle(variables),
+ ...(selected && selectedStyle(variables)),
}),
...(important && {
fontWeight: 'bold',
diff --git a/src/themes/teams/components/List/listItemVariables.ts b/src/themes/teams/components/List/listItemVariables.ts
index 5edc096d03..e7a622765c 100644
--- a/src/themes/teams/components/List/listItemVariables.ts
+++ b/src/themes/teams/components/List/listItemVariables.ts
@@ -13,7 +13,9 @@ export default siteVariables => ({
contentFontSize: siteVariables.fontSizes.small,
contentLineHeight: siteVariables.lineHeightSmall,
- // Selection
- selectionHoverColor: siteVariables.white,
- selectionHoverBackgroundColor: siteVariables.brand08,
+ // Selectable
+ selectableFocusHoverColor: siteVariables.white,
+ selectableFocusHoverBackgroundColor: siteVariables.brand08,
+ selectedColor: siteVariables.black,
+ selectedBackgroundColor: siteVariables.gray10,
})
diff --git a/test/specs/behaviors/listBehavior-test.tsx b/test/specs/behaviors/listBehavior-test.tsx
index faed2115ef..eada8135d1 100644
--- a/test/specs/behaviors/listBehavior-test.tsx
+++ b/test/specs/behaviors/listBehavior-test.tsx
@@ -1,15 +1,15 @@
import { listBehavior } from 'src/lib/accessibility'
describe('ListBehavior.ts', () => {
- test('use SelectableListBehavior if selection prop is defined', () => {
+ test('use SelectableListBehavior if selectable prop is defined', () => {
const property = {
- selection: true,
+ selectable: true,
}
const expectedResult = listBehavior(property)
expect(expectedResult.attributes.root.role).toEqual('listbox')
})
- test('use BasicListItemBehavior if selection prop is NOT defined', () => {
+ test('use BasicListItemBehavior if selectable prop is NOT defined', () => {
const property = {}
const expectedResult = listBehavior(property)
expect(expectedResult.attributes.root.role).toEqual('list')
diff --git a/test/specs/behaviors/listItemBehavior-test.tsx b/test/specs/behaviors/listItemBehavior-test.tsx
index 261ba29fe3..752000a625 100644
--- a/test/specs/behaviors/listItemBehavior-test.tsx
+++ b/test/specs/behaviors/listItemBehavior-test.tsx
@@ -1,15 +1,15 @@
import { listItemBehavior } from 'src/lib/accessibility'
describe('ListItemBehavior.ts', () => {
- test('use SelectableListItemBehavior if selection prop is defined', () => {
+ test('use SelectableListItemBehavior if selectable prop is defined', () => {
const property = {
- selection: true,
+ selectable: true,
}
const expectedResult = listItemBehavior(property)
expect(expectedResult.attributes.root.role).toEqual('option')
})
- test('use BasicListBehavior if selection prop is NOT defined', () => {
+ test('use BasicListBehavior if selectable prop is NOT defined', () => {
const property = {}
const expectedResult = listItemBehavior(property)
expect(expectedResult.attributes.root.role).toEqual('listitem')
diff --git a/test/specs/components/List/List-test.ts b/test/specs/components/List/List-test.ts
deleted file mode 100644
index 60f4471d91..0000000000
--- a/test/specs/components/List/List-test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { isConformant, handlesAccessibility } from 'test/specs/commonTests'
-
-import List from 'src/components/List/List'
-import implementsCollectionShorthandProp from '../../commonTests/implementsCollectionShorthandProp'
-import ListItem from 'src/components/List/ListItem'
-
-const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp(List)
-
-describe('List', () => {
- isConformant(List)
- handlesAccessibility(List, { defaultRootRole: 'list' })
- listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' })
-})
diff --git a/test/specs/components/List/List-test.tsx b/test/specs/components/List/List-test.tsx
new file mode 100644
index 0000000000..ae7fe610bf
--- /dev/null
+++ b/test/specs/components/List/List-test.tsx
@@ -0,0 +1,76 @@
+import * as React from 'react'
+
+import { isConformant, handlesAccessibility } from 'test/specs/commonTests'
+import { mountWithProvider } from 'test/utils'
+
+import List from 'src/components/List/List'
+import implementsCollectionShorthandProp from '../../commonTests/implementsCollectionShorthandProp'
+import ListItem from 'src/components/List/ListItem'
+
+const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp(List)
+
+describe('List', () => {
+ isConformant(List)
+ handlesAccessibility(List, { defaultRootRole: 'list' })
+ listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' })
+
+ const getItems = () => [
+ { key: 'irving', content: 'Irving', onClick: jest.fn() },
+ { key: 'skyler', content: 'Skyler' },
+ { key: 'dante', content: 'Dante' },
+ ]
+
+ describe('items', () => {
+ it('renders children', () => {
+ const listItems = mountWithProvider(
).find('ListItem')
+ expect(listItems.length).toBe(3)
+ expect(listItems.first().props().content).toBe('Irving')
+ expect(listItems.last().props().content).toBe('Dante')
+ })
+
+ it('calls onClick handler for item', () => {
+ const items = getItems()
+ const listItems = mountWithProvider(
).find('ListItem')
+
+ listItems
+ .first()
+ .find('li')
+ .first()
+ .simulate('click')
+ expect(items[0].onClick).toHaveBeenCalled()
+ })
+ })
+
+ describe('selectedIndex', () => {
+ it('should not be set by default', () => {
+ const listItems = mountWithProvider(
).find('ListItem')
+ expect(listItems.everyWhere(item => !item.props().selected)).toBe(true)
+ })
+
+ it('can be set a default value', () => {
+ const listItems = mountWithProvider(
+
,
+ ).find('ListItem')
+ expect(listItems.first().props().selected).toBe(true)
+ })
+
+ it('should be set when item is clicked', () => {
+ const wrapper = mountWithProvider(
+
,
+ )
+ const listItems = wrapper.find('ListItem')
+ expect(listItems.at(0).props().selected).toBe(true)
+
+ listItems
+ .at(1)
+ .find('li')
+ .first()
+ .simulate('click')
+
+ const updatedItems = wrapper.find('ListItem')
+
+ expect(updatedItems.at(0).props().selected).toBe(false)
+ expect(updatedItems.at(1).props().selected).toBe(true)
+ })
+ })
+})
diff --git a/test/specs/lib/accessibility/FocusContainer-test.ts b/test/specs/lib/accessibility/FocusContainer-test.ts
index bed2ab14da..58c98d18ff 100644
--- a/test/specs/lib/accessibility/FocusContainer-test.ts
+++ b/test/specs/lib/accessibility/FocusContainer-test.ts
@@ -11,22 +11,22 @@ describe('Focus Container', () => {
const focusContainer = createFocusContainer()
expect(focusContainer).toBeDefined()
- expect(focusContainer.getFocusedItemIndex()).toBe(0)
+ expect(focusContainer.getFocusedIndex()).toBe(0)
})
describe('sync item index', () => {
test('should set focus item index', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(4)
+ focusContainer.syncFocusedIndex(4)
- expect(focusContainer.getFocusedItemIndex()).toBe(4)
+ expect(focusContainer.getFocusedIndex()).toBe(4)
})
test('should not set focus index function', () => {
const setFocusAt = jest.fn()
const focusContainer = createFocusContainer({ itemsCount: 5, setFocusAtFn: setFocusAt })
- focusContainer.syncFocusedItemIndex(4)
+ focusContainer.syncFocusedIndex(4)
expect(setFocusAt).not.toBeCalled()
})
})
@@ -34,10 +34,10 @@ describe('Focus Container', () => {
describe('move previous', () => {
test('should decrement index of focused item', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(4)
+ focusContainer.syncFocusedIndex(4)
focusContainer.movePrevious()
- expect(focusContainer.getFocusedItemIndex()).toBe(3)
+ expect(focusContainer.getFocusedIndex()).toBe(3)
})
test('should call set focus index function if there are any items', () => {
@@ -58,21 +58,21 @@ describe('Focus Container', () => {
test('focused item index should not ever become less than 0', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(0)
+ focusContainer.syncFocusedIndex(0)
focusContainer.movePrevious()
- expect(focusContainer.getFocusedItemIndex()).toBe(0)
+ expect(focusContainer.getFocusedIndex()).toBe(0)
})
})
describe('move next', () => {
test('should increment index of focused item', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(3)
+ focusContainer.syncFocusedIndex(3)
focusContainer.moveNext()
- expect(focusContainer.getFocusedItemIndex()).toBe(4)
+ expect(focusContainer.getFocusedIndex()).toBe(4)
})
test('should call set focus index function if there are any items', () => {
@@ -93,21 +93,21 @@ describe('Focus Container', () => {
test('focused item index should not exceed range of valid indexes', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(4)
+ focusContainer.syncFocusedIndex(4)
focusContainer.moveNext()
- expect(focusContainer.getFocusedItemIndex()).toBe(4)
+ expect(focusContainer.getFocusedIndex()).toBe(4)
})
})
describe('move first', () => {
test('should set focused item index to 0', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(3)
+ focusContainer.syncFocusedIndex(3)
focusContainer.moveFirst()
- expect(focusContainer.getFocusedItemIndex()).toBe(0)
+ expect(focusContainer.getFocusedIndex()).toBe(0)
})
test('should call set focus index function if there are any items', () => {
@@ -130,10 +130,10 @@ describe('Focus Container', () => {
describe('move last', () => {
test('should set focused item index to last index of valid range', () => {
const focusContainer = createFocusContainer({ itemsCount: 5 })
- focusContainer.syncFocusedItemIndex(2)
+ focusContainer.syncFocusedIndex(2)
focusContainer.moveLast()
- expect(focusContainer.getFocusedItemIndex()).toBe(4)
+ expect(focusContainer.getFocusedIndex()).toBe(4)
})
test('should call set focus index function if there are any items', () => {
From d3d60124310285a90b0d13d13bf1424134642b56 Mon Sep 17 00:00:00 2001
From: Oleksandr Fediashov
Date: Wed, 19 Dec 2018 13:53:12 +0100
Subject: [PATCH 51/55] docs(Examples): allow to use TS in examples (#617)
* docs(Examples): allow to use TS in examples
* add jsdoc
* fix typo
* rename file
* add comment
* `createExample` to `createExampleSourceCode`
* rework with `path.relative()`
* remove JSON files on remove tsx
* create getRelativePathToSource function
---
.gitignore | 1 +
build/babel/transform-star-import-plugin.ts | 36 ++++
build/babel/types.ts | 22 +++
build/gulp/plugins/gulp-example-source.ts | 55 ++++++
.../util/getRelativePathToSourceFile.ts | 13 ++
build/gulp/plugins/util/index.ts | 1 +
build/gulp/tasks/docs.ts | 33 +++-
.../ComponentExample/SourceCodeManager.ts | 4 +-
.../components/Ref/Types/RefExample.tsx | 10 +-
.../Ref/Types/RefForwardingExample.tsx | 20 +-
.../States/TextExampleDisabled.shorthand.tsx | 2 +-
.../Text/States/TextExampleDisabled.tsx | 2 +-
.../States/TextExampleError.shorthand.tsx | 2 +-
.../Text/States/TextExampleError.tsx | 2 +-
.../States/TextExampleSuccess.shorthand.tsx | 2 +-
.../Text/States/TextExampleSuccess.tsx | 2 +-
.../States/TextExampleTemporary.shorthand.tsx | 2 +-
.../Text/States/TextExampleTemporary.tsx | 2 +-
.../States/TextExampleTruncated.shorthand.tsx | 2 +-
.../Text/States/TextExampleTruncated.tsx | 2 +-
.../examples/components/Text/States/index.tsx | 2 +-
.../Text/Types/TextSizesExample.shorthand.tsx | 2 +-
.../Text/Types/TextSizesExample.tsx | 2 +-
.../Types/TextWeightsExample.shorthand.tsx | 2 +-
.../Text/Types/TextWeightsExample.tsx | 2 +-
.../examples/components/Text/Types/index.tsx | 2 +-
.../TextExampleAtMention.shorthand.tsx | 2 +-
.../Text/Variations/TextExampleAtMention.tsx | 2 +-
.../TextExampleImportant.shorthand.tsx | 2 +-
.../Text/Variations/TextExampleImportant.tsx | 2 +-
.../TextExampleTimestamp.shorthand.tsx | 2 +-
.../Text/Variations/TextExampleTimestamp.tsx | 2 +-
.../components/Text/Variations/index.tsx | 2 +-
docs/src/examples/components/Text/index.tsx | 2 +-
docs/src/types.ts | 4 +
package.json | 4 +-
yarn.lock | 185 +++++++++++++++++-
37 files changed, 389 insertions(+), 47 deletions(-)
create mode 100644 build/babel/transform-star-import-plugin.ts
create mode 100644 build/babel/types.ts
create mode 100644 build/gulp/plugins/gulp-example-source.ts
create mode 100644 build/gulp/plugins/util/getRelativePathToSourceFile.ts
create mode 100644 docs/src/types.ts
diff --git a/.gitignore b/.gitignore
index ab0e6eaa4f..6428bd2fdb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ docs/src/componentInfo
docs/src/componentMenu.json
docs/src/behaviorMenu.json
docs/src/exampleMenus
+docs/src/exampleSources
docs/dist/
dll/
node_modules/
diff --git a/build/babel/transform-star-import-plugin.ts b/build/babel/transform-star-import-plugin.ts
new file mode 100644
index 0000000000..e59dc3d803
--- /dev/null
+++ b/build/babel/transform-star-import-plugin.ts
@@ -0,0 +1,36 @@
+import * as T from '@babel/types'
+import { BabelPlugin } from './types'
+
+/**
+ * Creates an default import:
+ * - import React from 'react'
+ */
+const createDefaultImportDeclaration = (
+ t: typeof T,
+ declaration: T.ImportDeclaration,
+ specifier: T.ImportNamespaceSpecifier,
+): T.ImportDeclaration =>
+ t.importDeclaration(
+ [t.importDefaultSpecifier(t.identifier(specifier.local.name))],
+ t.stringLiteral(declaration.source.value),
+ )
+
+/**
+ * A plugin for Babel that performs AST transform:
+ * - from: import * as _ from 'lodash'
+ * - to: import _ from 'lodash'
+ */
+const starImportToDefaultPlugin: BabelPlugin = ({ types: t }) => ({
+ visitor: {
+ ImportDeclaration: path => {
+ const { specifiers } = path.node
+ const specifier = specifiers[0]
+
+ if (specifiers.length === 1 && t.isImportNamespaceSpecifier(specifier)) {
+ path.replaceWith(createDefaultImportDeclaration(t, path.node, specifier))
+ }
+ },
+ },
+})
+
+export default starImportToDefaultPlugin
diff --git a/build/babel/types.ts b/build/babel/types.ts
new file mode 100644
index 0000000000..56f6d81505
--- /dev/null
+++ b/build/babel/types.ts
@@ -0,0 +1,22 @@
+import * as T from '@babel/types'
+import { NodePath } from '@babel/traverse'
+
+export type BabelPluginArguments = {
+ types: typeof T
+}
+
+type BabelPluginVisitorFunction = (path: NodePath) => void
+type BabelPluginVisitor =
+ | BabelPluginVisitorFunction
+ | {
+ exit: BabelPluginVisitorFunction
+ }
+
+export type BabelPlugin = (
+ options: BabelPluginArguments,
+) => {
+ visitor: {
+ // This type is extendable, feel to add own visitor types.
+ ImportDeclaration: BabelPluginVisitor
+ }
+}
diff --git a/build/gulp/plugins/gulp-example-source.ts b/build/gulp/plugins/gulp-example-source.ts
new file mode 100644
index 0000000000..d1ca9e7cca
--- /dev/null
+++ b/build/gulp/plugins/gulp-example-source.ts
@@ -0,0 +1,55 @@
+import * as Babel from '@babel/core'
+import * as gutil from 'gulp-util'
+import * as prettier from 'prettier'
+import * as through from 'through2'
+import * as Vinyl from 'vinyl'
+
+import * as prettierConfig from '../../../.prettierrc.json'
+import { ExampleSource } from '../../../docs/src/types'
+import transformStarImportPlugin from '../../babel/transform-star-import-plugin'
+import { getRelativePathToSourceFile } from './util'
+
+const pluginName = 'gulp-example-source'
+
+const createExampleSourceCode = (file: Vinyl): ExampleSource => {
+ const tsSource = file.contents.toString()
+
+ const transformResult = Babel.transform(tsSource, {
+ plugins: [transformStarImportPlugin],
+ presets: [['@babel/preset-typescript', { allExtensions: true, isTSX: true }]],
+ sourceType: 'module',
+ })
+ const jsSource = prettier.format(transformResult.code, {
+ ...prettierConfig,
+ parser: 'babylon',
+ })
+
+ return {
+ js: jsSource,
+ ts: tsSource,
+ }
+}
+
+export default () =>
+ through.obj((file: Vinyl, enc, cb) => {
+ if (file.isNull()) {
+ cb(null, file)
+ return
+ }
+
+ if (file.isStream()) {
+ cb(new gutil.PluginError(pluginName, 'Streaming is not supported'))
+ return
+ }
+
+ const sourcePath = getRelativePathToSourceFile(file.path)
+ const source = createExampleSourceCode(file)
+
+ cb(
+ null,
+ new Vinyl({
+ path: sourcePath,
+ contents: Buffer.from(JSON.stringify(source, null, 2)),
+ }),
+ )
+ })
diff --git a/build/gulp/plugins/util/getRelativePathToSourceFile.ts b/build/gulp/plugins/util/getRelativePathToSourceFile.ts
new file mode 100644
index 0000000000..374646eede
--- /dev/null
+++ b/build/gulp/plugins/util/getRelativePathToSourceFile.ts
@@ -0,0 +1,13 @@
+import * as path from 'path'
+import config from '../../../../config'
+
+const examplesPath = config.paths.docsSrc('examples', 'components')
+
+/**
+ * Generates a relative path to a source file, outputs:
+ * Chat/Types/ChatExample.shorthand.source.json
+ */
+const getRelativePathToSourceFile = (filePath: string): string =>
+ `${path.relative(examplesPath, filePath).replace(/\.tsx$/, '')}.source.json`
+
+export default getRelativePathToSourceFile
diff --git a/build/gulp/plugins/util/index.ts b/build/gulp/plugins/util/index.ts
index 7562fb1087..d6a291e7d3 100644
--- a/build/gulp/plugins/util/index.ts
+++ b/build/gulp/plugins/util/index.ts
@@ -1,5 +1,6 @@
export * from './checksumUtils'
export { default as getComponentInfo } from './getComponentInfo'
+export { default as getRelativePathToSourceFile } from './getRelativePathToSourceFile'
export { default as parseDefaultValue } from './parseDefaultValue'
export { default as parseDocblock } from './parseDocblock'
export { default as parseDocSection } from './parseDocSection'
diff --git a/build/gulp/tasks/docs.ts b/build/gulp/tasks/docs.ts
index 7375a2a93d..a4396eb948 100644
--- a/build/gulp/tasks/docs.ts
+++ b/build/gulp/tasks/docs.ts
@@ -2,6 +2,7 @@ import * as historyApiFallback from 'connect-history-api-fallback'
import * as express from 'express'
import { task, src, dest, lastRun, parallel, series, watch } from 'gulp'
import * as remember from 'gulp-remember'
+import * as fs from 'fs'
import * as path from 'path'
import * as rimraf from 'rimraf'
import * as through2 from 'through2'
@@ -14,7 +15,9 @@ import config from '../../../config'
import gulpComponentMenu from '../plugins/gulp-component-menu'
import gulpComponentMenuBehaviors from '../plugins/gulp-component-menu-behaviors'
import gulpExampleMenu from '../plugins/gulp-example-menu'
+import gulpExampleSource from '../plugins/gulp-example-source'
import gulpReactDocgen from '../plugins/gulp-react-docgen'
+import { getRelativePathToSourceFile } from '../plugins/util'
const { paths } = config
const g = require('gulp-load-plugins')()
@@ -46,6 +49,10 @@ task('clean:docs:example-menus', cb => {
rimraf(paths.docsSrc('exampleMenus'), cb)
})
+task('clean:docs:example-sources', cb => {
+ rimraf(paths.docsSrc('exampleSources'), cb)
+})
+
task(
'clean:docs',
parallel(
@@ -53,6 +60,7 @@ task(
'clean:docs:component-menu-behaviors',
'clean:docs:dist',
'clean:docs:example-menus',
+ 'clean:docs:example-sources',
),
)
@@ -62,7 +70,8 @@ task(
const componentsSrc = [`${paths.posix.src()}/components/*/[A-Z]*.tsx`, '!**/Slot.tsx']
const behaviorSrc = [`${paths.posix.src()}/lib/accessibility/Behaviors/*/[a-z]*.ts`]
-const examplesSrc = `${paths.posix.docsSrc()}/examples/*/*/*/index.tsx`
+const examplesIndexSrc = `${paths.posix.docsSrc()}/examples/*/*/*/index.tsx`
+const examplesSrc = `${paths.posix.docsSrc()}/examples/*/*/*/!(*index|.knobs).tsx`
const markdownSrc = [
'.github/CONTRIBUTING.md',
'.github/setup-local-development.md',
@@ -92,18 +101,25 @@ task('build:docs:component-menu-behaviors', () =>
)
task('build:docs:example-menu', () =>
- src(examplesSrc, { since: lastRun('build:docs:example-menu') })
+ src(examplesIndexSrc, { since: lastRun('build:docs:example-menu') })
.pipe(remember('example-menu')) // FIXME: with watch this unnecessarily processes index files for all examples
.pipe(gulpExampleMenu())
.pipe(dest(paths.docsSrc('exampleMenus'))),
)
+task('build:docs:example-sources', () =>
+ src(examplesSrc, { since: lastRun('build:docs:example-sources') })
+ .pipe(gulpExampleSource())
+ .pipe(dest(paths.docsSrc('exampleSources'))),
+)
+
task(
'build:docs:json',
parallel(
series('build:docs:docgen', 'build:docs:component-menu'),
'build:docs:component-menu-behaviors',
'build:docs:example-menu',
+ 'build:docs:example-sources',
),
)
@@ -218,10 +234,21 @@ task('watch:docs', cb => {
watch(componentsSrc, series('build:docs:docgen')).on('change', handleWatchChange)
// rebuild example menus
- watch(examplesSrc, series('build:docs:example-menu'))
+ watch(examplesIndexSrc, series('build:docs:example-menu'))
.on('change', handleWatchChange)
.on('unlink', path => handleWatchUnlink('example-menu', path))
+ watch(examplesSrc, series('build:docs:example-sources'))
+ .on('change', handleWatchChange)
+ .on('unlink', filePath => {
+ log(`File ${filePath} was deleted, running tasks...`)
+
+ const sourceFilename = getRelativePathToSourceFile(filePath)
+ const sourcePath = config.paths.docsSrc('exampleSources', sourceFilename)
+
+ fs.unlinkSync(sourcePath)
+ })
+
watch(behaviorSrc, series('build:docs:component-menu-behaviors'))
.on('change', handleWatchChange)
.on('unlink', path => handleWatchUnlink('component-menu-behaviors', path))
diff --git a/docs/src/components/ComponentDoc/ComponentExample/SourceCodeManager.ts b/docs/src/components/ComponentDoc/ComponentExample/SourceCodeManager.ts
index fa7f8ae015..3545893e2d 100644
--- a/docs/src/components/ComponentDoc/ComponentExample/SourceCodeManager.ts
+++ b/docs/src/components/ComponentDoc/ComponentExample/SourceCodeManager.ts
@@ -84,7 +84,9 @@ class SourceCodeManager {
private safeRequire = (path: string): string | undefined => {
try {
- return require(`!raw-loader!../../../examples/${path}`)
+ const filename = `${path.replace(/^components\//, '')}.source.json`
+
+ return require(`!docs/src/exampleSources/${filename}`).js
} catch (e) {
return undefined
}
diff --git a/docs/src/examples/components/Ref/Types/RefExample.tsx b/docs/src/examples/components/Ref/Types/RefExample.tsx
index f36982328a..018a4ef88b 100644
--- a/docs/src/examples/components/Ref/Types/RefExample.tsx
+++ b/docs/src/examples/components/Ref/Types/RefExample.tsx
@@ -1,13 +1,17 @@
-import React from 'react'
+import * as React from 'react'
import { Button, Grid, Ref, Segment } from '@stardust-ui/react'
-class RefExample extends React.Component {
+type RefExampleState = {
+ isMounted: boolean
+}
+
+class RefExample extends React.Component<{}, RefExampleState> {
state = { isMounted: false }
createdRef = React.createRef()
functionalRef = null
- handleRef = node => (this.functionalRef = node)
+ handleRef = (node: HTMLButtonElement) => (this.functionalRef = node)
componentDidMount() {
this.setState({ isMounted: true })
diff --git a/docs/src/examples/components/Ref/Types/RefForwardingExample.tsx b/docs/src/examples/components/Ref/Types/RefForwardingExample.tsx
index e0f9bcbd6a..300d577b41 100644
--- a/docs/src/examples/components/Ref/Types/RefForwardingExample.tsx
+++ b/docs/src/examples/components/Ref/Types/RefForwardingExample.tsx
@@ -1,13 +1,19 @@
-import React from 'react'
+import * as React from 'react'
import { Grid, Ref, Segment } from '@stardust-ui/react'
-const ExampleButton = React.forwardRef((props, ref) => (
-
-
-
-))
+type RefForwardingExampleState = {
+ isMounted: boolean
+}
+
+const ExampleButton = React.forwardRef(
+ (props, ref) => (
+
+
+
+ ),
+)
-class RefForwardingExample extends React.Component {
+class RefForwardingExample extends React.Component<{}, RefForwardingExampleState> {
forwardedRef = React.createRef()
state = { isMounted: false }
diff --git a/docs/src/examples/components/Text/States/TextExampleDisabled.shorthand.tsx b/docs/src/examples/components/Text/States/TextExampleDisabled.shorthand.tsx
index 85275c7366..7377e42ca6 100644
--- a/docs/src/examples/components/Text/States/TextExampleDisabled.shorthand.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleDisabled.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleDisabledShorthand = () => (
diff --git a/docs/src/examples/components/Text/States/TextExampleDisabled.tsx b/docs/src/examples/components/Text/States/TextExampleDisabled.tsx
index 97212ded55..9229975e92 100644
--- a/docs/src/examples/components/Text/States/TextExampleDisabled.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleDisabled.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleDisabled = () => This feature has been disabled.
diff --git a/docs/src/examples/components/Text/States/TextExampleError.shorthand.tsx b/docs/src/examples/components/Text/States/TextExampleError.shorthand.tsx
index 10a35dd38d..612f0fa25a 100644
--- a/docs/src/examples/components/Text/States/TextExampleError.shorthand.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleError.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleErrorShorthand = () =>
diff --git a/docs/src/examples/components/Text/States/TextExampleError.tsx b/docs/src/examples/components/Text/States/TextExampleError.tsx
index 73d6654e87..e400914175 100644
--- a/docs/src/examples/components/Text/States/TextExampleError.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleError.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleError = () => There has been an error.
diff --git a/docs/src/examples/components/Text/States/TextExampleSuccess.shorthand.tsx b/docs/src/examples/components/Text/States/TextExampleSuccess.shorthand.tsx
index 046eeeef30..2bf65afc42 100644
--- a/docs/src/examples/components/Text/States/TextExampleSuccess.shorthand.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleSuccess.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleSuccessShorthand = () => (
diff --git a/docs/src/examples/components/Text/States/TextExampleSuccess.tsx b/docs/src/examples/components/Text/States/TextExampleSuccess.tsx
index 2fa1f37b8f..202fb67715 100644
--- a/docs/src/examples/components/Text/States/TextExampleSuccess.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleSuccess.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleSuccess = () => Your action has completed successfully.
diff --git a/docs/src/examples/components/Text/States/TextExampleTemporary.shorthand.tsx b/docs/src/examples/components/Text/States/TextExampleTemporary.shorthand.tsx
index 52ba13efeb..43868a2855 100644
--- a/docs/src/examples/components/Text/States/TextExampleTemporary.shorthand.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleTemporary.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleTemporaryShorthand = () =>
diff --git a/docs/src/examples/components/Text/States/TextExampleTemporary.tsx b/docs/src/examples/components/Text/States/TextExampleTemporary.tsx
index 18aa888caa..0017de5c4d 100644
--- a/docs/src/examples/components/Text/States/TextExampleTemporary.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleTemporary.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleTemporary = () => Lorem ipsum dolor
diff --git a/docs/src/examples/components/Text/States/TextExampleTruncated.shorthand.tsx b/docs/src/examples/components/Text/States/TextExampleTruncated.shorthand.tsx
index f2897c1b05..001a6076a3 100644
--- a/docs/src/examples/components/Text/States/TextExampleTruncated.shorthand.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleTruncated.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const [notTruncatedText, truncatedText] = [
diff --git a/docs/src/examples/components/Text/States/TextExampleTruncated.tsx b/docs/src/examples/components/Text/States/TextExampleTruncated.tsx
index dbb858d4b5..dc4898742a 100644
--- a/docs/src/examples/components/Text/States/TextExampleTruncated.tsx
+++ b/docs/src/examples/components/Text/States/TextExampleTruncated.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const [notTruncatedText, truncatedText] = [
diff --git a/docs/src/examples/components/Text/States/index.tsx b/docs/src/examples/components/Text/States/index.tsx
index be2c9d33ce..9ac7d81498 100644
--- a/docs/src/examples/components/Text/States/index.tsx
+++ b/docs/src/examples/components/Text/States/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
diff --git a/docs/src/examples/components/Text/Types/TextSizesExample.shorthand.tsx b/docs/src/examples/components/Text/Types/TextSizesExample.shorthand.tsx
index 509e393189..4213128740 100644
--- a/docs/src/examples/components/Text/Types/TextSizesExample.shorthand.tsx
+++ b/docs/src/examples/components/Text/Types/TextSizesExample.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import * as _ from 'lodash'
import { Provider, Text } from '@stardust-ui/react'
diff --git a/docs/src/examples/components/Text/Types/TextSizesExample.tsx b/docs/src/examples/components/Text/Types/TextSizesExample.tsx
index 4acd1ec7eb..42447f37b4 100644
--- a/docs/src/examples/components/Text/Types/TextSizesExample.tsx
+++ b/docs/src/examples/components/Text/Types/TextSizesExample.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import * as _ from 'lodash'
import { Provider, Text } from '@stardust-ui/react'
diff --git a/docs/src/examples/components/Text/Types/TextWeightsExample.shorthand.tsx b/docs/src/examples/components/Text/Types/TextWeightsExample.shorthand.tsx
index f0d049762a..d5abc08e76 100644
--- a/docs/src/examples/components/Text/Types/TextWeightsExample.shorthand.tsx
+++ b/docs/src/examples/components/Text/Types/TextWeightsExample.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextWeightsExampleShorthand = () => (
diff --git a/docs/src/examples/components/Text/Types/TextWeightsExample.tsx b/docs/src/examples/components/Text/Types/TextWeightsExample.tsx
index 8059d35902..043ca9523f 100644
--- a/docs/src/examples/components/Text/Types/TextWeightsExample.tsx
+++ b/docs/src/examples/components/Text/Types/TextWeightsExample.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextWeightsExample = () => (
diff --git a/docs/src/examples/components/Text/Types/index.tsx b/docs/src/examples/components/Text/Types/index.tsx
index eed2697185..b8275e86e4 100644
--- a/docs/src/examples/components/Text/Types/index.tsx
+++ b/docs/src/examples/components/Text/Types/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
diff --git a/docs/src/examples/components/Text/Variations/TextExampleAtMention.shorthand.tsx b/docs/src/examples/components/Text/Variations/TextExampleAtMention.shorthand.tsx
index 2db91ee587..bd585647f3 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleAtMention.shorthand.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleAtMention.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleAtMentionShorthand = () => (
diff --git a/docs/src/examples/components/Text/Variations/TextExampleAtMention.tsx b/docs/src/examples/components/Text/Variations/TextExampleAtMention.tsx
index c16ce492f4..d9cfbb7a49 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleAtMention.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleAtMention.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleAtMention = () => (
diff --git a/docs/src/examples/components/Text/Variations/TextExampleImportant.shorthand.tsx b/docs/src/examples/components/Text/Variations/TextExampleImportant.shorthand.tsx
index 4744d6e0ce..0676ec11d5 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleImportant.shorthand.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleImportant.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleImportantShorthand = () =>
diff --git a/docs/src/examples/components/Text/Variations/TextExampleImportant.tsx b/docs/src/examples/components/Text/Variations/TextExampleImportant.tsx
index fc19e0351f..a62558c782 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleImportant.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleImportant.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleImportant = () => This text is important!
diff --git a/docs/src/examples/components/Text/Variations/TextExampleTimestamp.shorthand.tsx b/docs/src/examples/components/Text/Variations/TextExampleTimestamp.shorthand.tsx
index af36508bff..b8967a0680 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleTimestamp.shorthand.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleTimestamp.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleTimestampShorthand = () => (
diff --git a/docs/src/examples/components/Text/Variations/TextExampleTimestamp.tsx b/docs/src/examples/components/Text/Variations/TextExampleTimestamp.tsx
index f4a901a123..d69d52d9b8 100644
--- a/docs/src/examples/components/Text/Variations/TextExampleTimestamp.tsx
+++ b/docs/src/examples/components/Text/Variations/TextExampleTimestamp.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Text } from '@stardust-ui/react'
const TextExampleTimestamp = () => (
diff --git a/docs/src/examples/components/Text/Variations/index.tsx b/docs/src/examples/components/Text/Variations/index.tsx
index 73d9b34082..6f8da3dde3 100644
--- a/docs/src/examples/components/Text/Variations/index.tsx
+++ b/docs/src/examples/components/Text/Variations/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
diff --git a/docs/src/examples/components/Text/index.tsx b/docs/src/examples/components/Text/index.tsx
index 99c54eaa0c..a96a7702b5 100644
--- a/docs/src/examples/components/Text/index.tsx
+++ b/docs/src/examples/components/Text/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import States from './States'
import Types from './Types'
import Variations from './Variations'
diff --git a/docs/src/types.ts b/docs/src/types.ts
new file mode 100644
index 0000000000..cb58586c10
--- /dev/null
+++ b/docs/src/types.ts
@@ -0,0 +1,4 @@
+export type ExampleSource = {
+ js: string
+ ts: string
+}
diff --git a/package.json b/package.json
index d645944e84..0894cd0eb4 100644
--- a/package.json
+++ b/package.json
@@ -73,7 +73,10 @@
"react-popper": "^1.0.2"
},
"devDependencies": {
+ "@babel/core": "^7.2.0",
+ "@babel/preset-typescript": "^7.1.0",
"@babel/standalone": "^7.1.0",
+ "@types/babel__traverse": "^7.0.4",
"@types/color": "^3.0.0",
"@types/enzyme": "^3.1.14",
"@types/faker": "^4.1.3",
@@ -124,7 +127,6 @@
"merge2": "^1.2.2",
"normalize.css": "^8.0.0",
"prettier": "^1.15.3",
- "raw-loader": "^0.5.1",
"react": "^16.0.0",
"react-ace": "^5.1.2",
"react-custom-scrollbars": "^4.2.1",
diff --git a/yarn.lock b/yarn.lock
index 25e665d8cb..1b696d9a7d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,13 @@
# yarn lockfile v1
+"@babel/code-frame@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
+ integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
+ dependencies:
+ "@babel/highlight" "^7.0.0"
+
"@babel/code-frame@^7.0.0-beta.35":
version "7.0.0-beta.52"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.52.tgz#192483bfa0d1e467c101571c21029ccb74af2801"
@@ -9,6 +16,74 @@
dependencies:
"@babel/highlight" "7.0.0-beta.52"
+"@babel/core@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.0.tgz#a4dd3814901998e93340f0086e9867fefa163ada"
+ integrity sha512-7pvAdC4B+iKjFFp9Ztj0QgBndJ++qaMeonT185wAqUnhipw8idm9Rv1UMyBuKtYjfl6ORNkgEgcsYLfHX/GpLw==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/generator" "^7.2.0"
+ "@babel/helpers" "^7.2.0"
+ "@babel/parser" "^7.2.0"
+ "@babel/template" "^7.1.2"
+ "@babel/traverse" "^7.1.6"
+ "@babel/types" "^7.2.0"
+ convert-source-map "^1.1.0"
+ debug "^4.1.0"
+ json5 "^2.1.0"
+ lodash "^4.17.10"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
+"@babel/generator@^7.1.6", "@babel/generator@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.0.tgz#eaf3821fa0301d9d4aef88e63d4bcc19b73ba16c"
+ integrity sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==
+ dependencies:
+ "@babel/types" "^7.2.0"
+ jsesc "^2.5.1"
+ lodash "^4.17.10"
+ source-map "^0.5.0"
+ trim-right "^1.0.1"
+
+"@babel/helper-function-name@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
+ integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.0.0"
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-get-function-arity@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
+ integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-plugin-utils@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
+ integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-split-export-declaration@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
+ integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helpers@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.2.0.tgz#8335f3140f3144270dc63c4732a4f8b0a50b7a21"
+ integrity sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==
+ dependencies:
+ "@babel/template" "^7.1.2"
+ "@babel/traverse" "^7.1.5"
+ "@babel/types" "^7.2.0"
+
"@babel/highlight@7.0.0-beta.52":
version "7.0.0-beta.52"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.52.tgz#ef24931432f06155e7bc39cdb8a6b37b4a28b3d0"
@@ -18,6 +93,35 @@
esutils "^2.0.2"
js-tokens "^3.0.0"
+"@babel/highlight@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
+ integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
+ dependencies:
+ chalk "^2.0.0"
+ esutils "^2.0.2"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.1.2", "@babel/parser@^7.1.6", "@babel/parser@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.0.tgz#02d01dbc330b6cbf36b76ac93c50752c69027065"
+ integrity sha512-M74+GvK4hn1eejD9lZ7967qAwvqTZayQa3g10ag4s9uewgR7TKjeaT0YMyoq+gVfKYABiWZ4MQD701/t5e1Jhg==
+
+"@babel/plugin-syntax-typescript@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.2.0.tgz#55d240536bd314dcbbec70fd949c5cabaed1de29"
+ integrity sha512-WhKr6yu6yGpGcNMVgIBuI9MkredpVc7Y3YR4UzEZmDztHoL6wV56YBHLhWnjO1EvId1B32HrD3DRFc+zSoKI1g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typescript@^7.1.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.2.0.tgz#bce7c06300434de6a860ae8acf6a442ef74a99d1"
+ integrity sha512-EnI7i2/gJ7ZNr2MuyvN2Hu+BHJENlxWte5XygPvfj/MbvtOkWor9zcnHpMMQL2YYaaCcqtIvJUyJ7QVfoGs7ew==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-typescript" "^7.2.0"
+
"@babel/polyfill@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.0.0.tgz#c8ff65c9ec3be6a1ba10113ebd40e8750fb90bff"
@@ -26,6 +130,14 @@
core-js "^2.5.7"
regenerator-runtime "^0.11.1"
+"@babel/preset-typescript@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.1.0.tgz#49ad6e2084ff0bfb5f1f7fb3b5e76c434d442c7f"
+ integrity sha512-LYveByuF9AOM8WrsNne5+N79k1YxjNB6gmpCQsnuSBAcV8QUeB+ZUxQzL7Rz7HksPbahymKkq2qBR+o36ggFZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-transform-typescript" "^7.1.0"
+
"@babel/runtime@^7.0.0-beta.49":
version "7.0.0-beta.52"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.52.tgz#3f3b42b82b92b4e1a283fc78df1bb2fd4ba8d0c7"
@@ -46,6 +158,39 @@
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.1.0.tgz#d79774a17e8df4a53def891864882f5d81c42001"
integrity sha512-3yROQ+PlAAi2uf3Crpdl11hhrYDQ6af+uNmrpLn26WTSdGkOubMXvJKKmHMmiSm2MlOUVrqyotudpQS3hL9UBg==
+"@babel/template@^7.1.0", "@babel/template@^7.1.2":
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644"
+ integrity sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.1.2"
+ "@babel/types" "^7.1.2"
+
+"@babel/traverse@^7.1.5", "@babel/traverse@^7.1.6":
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.6.tgz#c8db9963ab4ce5b894222435482bd8ea854b7b5c"
+ integrity sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/generator" "^7.1.6"
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.0.0"
+ "@babel/parser" "^7.1.6"
+ "@babel/types" "^7.1.6"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.10"
+
+"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.1.6", "@babel/types@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.0.tgz#7941c5b2d8060e06f9601d6be7c223eef906d5d8"
+ integrity sha512-b4v7dyfApuKDvmPb+O488UlGuR1WbwMXFsO/cyqMrnfvRAChZKJAYeeglWTjUO1b9UghKKgepAQM5tsvBJca6A==
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.10"
+ to-fast-properties "^2.0.0"
+
"@fimbul/bifrost@^0.15.0":
version "0.15.0"
resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.15.0.tgz#f3a48dee3046681e926c1f970f0b1a67e29e088e"
@@ -102,6 +247,13 @@
resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.1.0.tgz#8c254dfc7739b2e8513c44c976fc41872d5f6af0"
integrity sha512-mLwF+ccuvRZMS0SxUAxA3dAp8mB3m2FxIsBIUWFTYvzxl+E4XTZb8uFrUqXHbcxhZH1Z8taHohNTbzXZn3M8ag==
+"@types/babel__traverse@^7.0.4":
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.4.tgz#de652399bd8493ab712e4d6b68031b7a2f72ad5f"
+ integrity sha512-2vARZYqR4Flf59WtqMfa9GgbOjK04xLZaN9+CMf7Cs+4cAhxZBP3K9LYRzsWxOQe402VvqX9+7DdXxB72ujEOg==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
"@types/cheerio@*":
version "0.22.9"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.9.tgz#b5990152604c2ada749b7f88cab3476f21f39d7b"
@@ -2034,6 +2186,13 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+convert-source-map@^1.1.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+ integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+ dependencies:
+ safe-buffer "~5.1.1"
+
convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
@@ -2374,7 +2533,7 @@ debug@^3, debug@^3.2.5:
dependencies:
ms "^2.1.1"
-debug@^4.0.1:
+debug@^4.0.1, debug@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87"
integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==
@@ -4045,6 +4204,11 @@ global@^4.3.0:
min-document "^2.19.0"
process "~0.5.1"
+globals@^11.1.0:
+ version "11.9.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249"
+ integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==
+
globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
@@ -5747,7 +5911,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-"js-tokens@^3.0.0 || ^4.0.0":
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
@@ -5802,6 +5966,11 @@ jsesc@^1.3.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
jsome@^2.3.25:
version "2.5.0"
resolved "https://registry.yarnpkg.com/jsome/-/jsome-2.5.0.tgz#5e417eef4341ffeb83ee8bfa9265b36d56fe49ed"
@@ -8182,11 +8351,6 @@ raw-body@^2.2.0:
iconv-lite "0.4.23"
unpipe "1.0.0"
-raw-loader@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
- integrity sha1-DD0L6u2KAclm2Xh793goElKpeao=
-
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@@ -9495,7 +9659,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
-source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1:
+source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -10062,6 +10226,11 @@ to-fast-properties@^1.0.3:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
to-no-case@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a"
From e5aa7b3136c8d15892e0b49c04d69bd4d3d1301b Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 19 Dec 2018 16:12:46 +0100
Subject: [PATCH 52/55] -updated changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7006d2dd84..eade3bb2da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
- Add `color` prop to `Header` and `HeaderDescription` components @Bugaa92 ([#628](https://github.com/stardust-ui/react/pull/628))
+- Add `menu` prop to `MenuItem` @mnajdova ([#539](https://github.com/stardust-ui/react/pull/539))
## [v0.15.0](https://github.com/stardust-ui/react/tree/v0.15.0) (2018-12-17)
From 3c55948788aa9874ff87d3321a3752724b1d1a71 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 19 Dec 2018 16:28:45 +0100
Subject: [PATCH 53/55] -updated changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7006d2dd84..eade3bb2da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597))
- Add `color` prop to `Header` and `HeaderDescription` components @Bugaa92 ([#628](https://github.com/stardust-ui/react/pull/628))
+- Add `menu` prop to `MenuItem` @mnajdova ([#539](https://github.com/stardust-ui/react/pull/539))
## [v0.15.0](https://github.com/stardust-ui/react/tree/v0.15.0) (2018-12-17)
From 6aac80b4da9dca2aad75997c6941ba5f82d33a69 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 19 Dec 2018 16:37:21 +0100
Subject: [PATCH 54/55] -updated changelog with breaking changes -removed TODO
comment
---
CHANGELOG.md | 3 +++
src/lib/accessibility/Behaviors/Menu/menuBehavior.ts | 1 -
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eade3bb2da..866df7de4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
+### BREAKING CHANGES
+- renamed Teams theme menu variables the contains props names as prefixes @mnajdova ([#539](https://github.com/stardust-ui/react/pull/539))
+
### Fixes
- Ensure `Popup` properly flips values of `offset` prop in RTL @kuzhelov ([#612](https://github.com/stardust-ui/react/pull/612))
- Fix `List` - items should be selectable @sophieH29 ([#566](https://github.com/stardust-ui/react/pull/566))
diff --git a/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts b/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
index e25b3d305e..bba1918654 100644
--- a/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
+++ b/src/lib/accessibility/Behaviors/Menu/menuBehavior.ts
@@ -22,7 +22,6 @@ const menuBehavior: Accessibility = (props: any) => ({
isCircularNavigation: true,
preventDefaultWhenHandled: true,
shouldFocusFirstElementWhenReceivedFocus: true,
- // TODO: check if this should be applied to other behaviors as well
direction: props.vertical ? FocusZoneDirection.vertical : FocusZoneDirection.horizontal,
},
},
From e592039a38ae723984f5d2b9ec9fb81f75321ce4 Mon Sep 17 00:00:00 2001
From: manajdov
Date: Wed, 19 Dec 2018 17:33:22 +0100
Subject: [PATCH 55/55] -fixed imports in examples
---
.../Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx | 2 +-
.../components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx
index 807b6725a9..1fc9b1bfde 100644
--- a/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx
+++ b/docs/src/examples/components/Menu/Types/MenuExampleVerticalWithSubmenu.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Menu } from '@stardust-ui/react'
const items = [
diff --git a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
index 4899c64fef..44929b1aa7 100644
--- a/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
+++ b/docs/src/examples/components/Menu/Types/MenuExampleWithSubmenu.shorthand.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import * as React from 'react'
import { Menu } from '@stardust-ui/react'
const items = [