Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit b4a0246

Browse files
authored
feat(Menu): adding kind prop for the items and MenuDivider component (#682)
* -added menu separator component -added kind prop for the items inside the menu * -removed styles for itemSeparator in the MenuItem styles * -updated changelog * -fixed changelog * -added example for using the kind prop -reverted removed item separator from teams theme * -renamed MenuSeparator to MenuDivider and added test -fixed types of the kind prop -improved styles * -added typings for the kind prop -added service for getting the kind prop -removed kind prop from MenuItem and MenuDivider * -removed unused imports * -renamed MenuItemKindProp to MenuShorthandKinds -renamed Usage examples to Content examples * -changed the order of the content and variations menu examples
1 parent 6cbc10a commit b4a0246

File tree

15 files changed

+161
-6
lines changed

15 files changed

+161
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2020
### Features
2121
- Add `on` and `mouseLeaveDelay` props to `Popup` component @mnajdova ([#622](https://github.com/stardust-ui/react/pull/622))
2222
- Add Dropdown Single Selection variant @silviuavram ([#584](https://github.com/stardust-ui/react/pull/584))
23+
- Add `MenuDivider` component and `kind` prop to the `items` inside of the `Menu` for creating different components @mnajdova ([#682](https://github.com/stardust-ui/react/pull/682))
2324

2425
### Fixes
2526
- Fix unicode arrow characters to be RTL aware ([#690](https://github.com/stardust-ui/react/pull/690))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react'
2+
import { Menu, MenuShorthandKinds } from '@stardust-ui/react'
3+
4+
const items = [
5+
{ key: 'editorials', content: 'Editorials' },
6+
{ key: 'review', content: 'Reviews' },
7+
{ key: 'divider', kind: 'divider' as MenuShorthandKinds },
8+
{ key: 'events', content: 'Upcoming Events' },
9+
]
10+
11+
const MenuExampleKind = () => (
12+
<Menu defaultActiveIndex={0} items={items} vertical pointing="start" />
13+
)
14+
15+
export default MenuExampleKind
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react'
2+
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
3+
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
4+
5+
const Content = () => (
6+
<ExampleSection title="Content">
7+
<ComponentExample
8+
title="Divider"
9+
description="A menu can have divider between some items."
10+
examplePath="components/Menu/Content/MenuExampleDivider"
11+
/>
12+
</ExampleSection>
13+
)
14+
15+
export default Content

docs/src/examples/components/Menu/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import * as React from 'react'
22
import Types from './Types'
33
import Variations from './Variations'
4+
import Content from './Content'
45

56
const MenuExamples = () => (
67
<div>
78
<Types />
9+
<Content />
810
<Variations />
911
</div>
1012
)

src/components/Menu/Menu.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ import {
1010
UIComponentProps,
1111
ChildrenComponentProps,
1212
commonPropTypes,
13+
getKindProp,
1314
} from '../../lib'
1415
import MenuItem from './MenuItem'
1516
import { menuBehavior } from '../../lib/accessibility'
1617
import { Accessibility } from '../../lib/accessibility/types'
1718

1819
import { ComponentVariablesObject } from '../../themes/types'
19-
import { ReactProps, ShorthandValue } from '../../../types/utils'
20+
import { ReactProps, ShorthandCollection } from '../../../types/utils'
21+
import MenuDivider from './MenuDivider'
22+
23+
export type MenuShorthandKinds = 'divider' | 'item'
2024

2125
export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
2226
/**
@@ -39,7 +43,7 @@ export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
3943
iconOnly?: boolean
4044

4145
/** Shorthand array of props for Menu. */
42-
items?: ShorthandValue[]
46+
items?: ShorthandCollection<MenuShorthandKinds>
4347

4448
/** A menu can adjust its appearance to de-emphasize its contents. */
4549
pills?: boolean
@@ -91,7 +95,7 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
9195
defaultActiveIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
9296
fluid: PropTypes.bool,
9397
iconOnly: PropTypes.bool,
94-
items: customPropTypes.collectionShorthand,
98+
items: customPropTypes.collectionShorthandWithKindProp(['divider', 'item']),
9599
pills: PropTypes.bool,
96100
pointing: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['start', 'end'])]),
97101
primary: customPropTypes.every([customPropTypes.disallow(['secondary']), PropTypes.bool]),
@@ -109,6 +113,7 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
109113
static autoControlledProps = ['activeIndex']
110114

111115
static Item = MenuItem
116+
static Divider = MenuDivider
112117

113118
handleItemOverrides = predefinedProps => ({
114119
onClick: (e, itemProps) => {
@@ -146,6 +151,19 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
146151
return _.map(items, (item, index) => {
147152
const active =
148153
(typeof activeIndex === 'string' ? parseInt(activeIndex, 10) : activeIndex) === index
154+
const kind = getKindProp(item, 'item')
155+
156+
if (kind === 'divider') {
157+
return MenuDivider.create(item, {
158+
defaultProps: {
159+
primary,
160+
secondary,
161+
vertical,
162+
variables,
163+
},
164+
})
165+
}
166+
149167
return MenuItem.create(item, {
150168
defaultProps: {
151169
iconOnly,

src/components/Menu/MenuDivider.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as React from 'react'
2+
import * as PropTypes from 'prop-types'
3+
4+
import {
5+
createShorthandFactory,
6+
UIComponent,
7+
UIComponentProps,
8+
ColorComponentProps,
9+
commonPropTypes,
10+
} from '../../lib'
11+
import { ReactProps } from '../../../types/utils'
12+
13+
export interface MenuDividerProps extends UIComponentProps, ColorComponentProps {
14+
vertical?: boolean
15+
primary?: boolean
16+
secondary?: boolean
17+
}
18+
19+
/**
20+
* A menu divider visually segments menu items inside menu.
21+
*/
22+
class MenuDivider extends UIComponent<ReactProps<MenuDividerProps>, any> {
23+
static displayName = 'MenuDivider'
24+
25+
static create: Function
26+
27+
static className = 'ui-menu__divider'
28+
29+
static propTypes = {
30+
...commonPropTypes.createCommon({ content: false, children: false, color: true }),
31+
primary: PropTypes.bool,
32+
secondary: PropTypes.bool,
33+
vertical: PropTypes.bool,
34+
}
35+
36+
renderComponent({ ElementType, classes, rest }) {
37+
return <ElementType {...rest} className={classes.root} />
38+
}
39+
}
40+
41+
MenuDivider.create = createShorthandFactory(MenuDivider, 'color')
42+
43+
export default MenuDivider

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ export { default as Layout, LayoutPropsWithDefaults, LayoutProps } from './compo
7474
export { default as List, ListProps } from './components/List/List'
7575
export { default as ListItem, ListItemProps } from './components/List/ListItem'
7676

77-
export { default as Menu, MenuProps, MenuState } from './components/Menu/Menu'
77+
export { default as Menu, MenuProps, MenuState, MenuShorthandKinds } from './components/Menu/Menu'
7878
export { default as MenuItem, MenuItemState, MenuItemProps } from './components/Menu/MenuItem'
79+
export { default as MenuDivider, MenuDividerProps } from './components/Menu/MenuDivider'
7980

8081
export {
8182
default as Popup,

src/lib/customPropTypes.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,28 @@ export const itemShorthand = every([
380380
disallow(['children']),
381381
PropTypes.oneOfType([PropTypes.node, PropTypes.object]),
382382
])
383+
export const itemShorthandWithKindProp = (kindPropValues: string[]) => {
384+
return every([
385+
disallow(['children']),
386+
PropTypes.oneOfType([
387+
PropTypes.node,
388+
PropTypes.shape({
389+
kind: PropTypes.oneOf(kindPropValues),
390+
}),
391+
]),
392+
])
393+
}
383394

384395
/**
385396
* Collection shorthand ensures a prop is an array of item shorthand.
386397
*/
387398
export const collectionShorthand = every([disallow(['children']), PropTypes.arrayOf(itemShorthand)])
399+
export const collectionShorthandWithKindProp = (kindPropValues: string[]) => {
400+
return every([
401+
disallow(['children']),
402+
PropTypes.arrayOf(itemShorthandWithKindProp(kindPropValues)),
403+
])
404+
}
388405

389406
/**
390407
* Show a deprecated warning for component props with a help message and optional validator.

src/lib/factories.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ function createShorthandFromValue(
188188
props.key = value
189189
}
190190

191+
// Remove the kind prop from the props object
192+
delete props.kind
193+
191194
// ----------------------------------------
192195
// Create Element
193196
// ----------------------------------------

src/lib/getKindProp.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ShorthandValue } from '../../types/utils'
2+
3+
export const getKindProp = (item: ShorthandValue, defaultValue: string) => {
4+
return typeof item === 'object' && (item as any).kind ? (item as any).kind : defaultValue
5+
}

src/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export { pxToRem } from './fontSizeUtility'
3535
export { customPropTypes }
3636
export { default as createAnimationStyles } from './createAnimationStyles'
3737
export { default as createComponent } from './createStardustComponent'
38+
export { getKindProp } from './getKindProp'
3839
export * from './whatInput'
3940

4041
export * from './commonPropInterfaces'

src/themes/teams/componentStyles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export { default as ListItem } from './components/List/listItemStyles'
4444

4545
export { default as Menu } from './components/Menu/menuStyles'
4646
export { default as MenuItem } from './components/Menu/menuItemStyles'
47+
export { default as MenuDivider } from './components/Menu/menuDividerStyles'
4748

4849
export { default as Popup } from './components/Popup/popupStyles'
4950
export { default as PopupContent } from './components/Popup/popupContentStyles'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types'
2+
import { MenuDividerProps } from '../../../../components/Menu/MenuDivider'
3+
import { MenuVariables } from './menuVariables'
4+
5+
const menuDividerStyles: ComponentSlotStylesInput<MenuDividerProps, MenuVariables> = {
6+
root: ({ props: p, variables: v }): ICSSInJSStyle => {
7+
const borderColor = p.primary ? v.primaryBorderColor : v.borderColor
8+
const borderType = p.vertical ? 'borderTop' : 'borderLeft'
9+
10+
return {
11+
[borderType]: `1px solid ${borderColor}`,
12+
}
13+
},
14+
}
15+
16+
export default menuDividerStyles
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { isConformant } from 'test/specs/commonTests'
2+
3+
import MenuDivider from 'src/components/Menu/MenuDivider'
4+
5+
describe('MenuDivider', () => {
6+
isConformant(MenuDivider)
7+
})

types/utils.d.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export type ObjectOrFunc<TResult, TArg = {}> = ((arg: TArg) => TResult) | TResul
2525
// Props
2626
// ========================================================
2727

28-
export type Props = ObjectOf<any>
28+
export type Props<T = {}> = T & ObjectOf<any>
2929
export type ReactChildren = React.ReactNodeArray | React.ReactNode
3030

3131
export type ReactPropsStrict<T> = { [K in keyof T]: NullableIfUndefined<T[K]> }
@@ -60,4 +60,14 @@ export type ShorthandRenderer = (
6060

6161
export type ShorthandRenderCallback = (render: ShorthandRenderer) => React.ReactElement<any>
6262

63-
export type ShorthandValue = React.ReactNode | Props
63+
// The ReactFragment here is replaced from the original typings with ReactNodeArray because of incorrect inheriting of the type when it is defined as {}
64+
type ReactNode =
65+
| React.ReactChild
66+
| React.ReactNodeArray
67+
| React.ReactPortal
68+
| boolean
69+
| null
70+
| undefined
71+
72+
export type ShorthandValue<P = {}> = ReactNode | Props<P>
73+
export type ShorthandCollection<K = []> = ShorthandValue<{ kind?: K }>[]

0 commit comments

Comments
 (0)