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

Commit 995bcbe

Browse files
committed
feat(menu): color prop and complex scheme
1 parent 8435577 commit 995bcbe

35 files changed

+560
-275
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as PropTypes from 'prop-types'
2+
import * as React from 'react'
3+
import * as _ from 'lodash'
4+
5+
import KnobsField from './KnobsField'
6+
import KnobsLabel from './KnobsLabel'
7+
import KnobsControl from './KnobsControl'
8+
import { ObjectOf } from 'types/utils'
9+
10+
export type KnobsSelectItem = { name: string; value: string }
11+
12+
export interface KnobsSelectProps {
13+
onChange?: (data: KnobsSelectProps) => void
14+
name?: string
15+
items?: KnobsSelectItem[]
16+
selectedItem?: KnobsSelectItem
17+
}
18+
19+
class KnobsSelect extends React.Component<KnobsSelectProps, {}> {
20+
private itemsMap: ObjectOf<KnobsSelectItem> = {}
21+
22+
public static propTypes = {
23+
onChange: PropTypes.func,
24+
name: PropTypes.string.isRequired,
25+
items: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string, value: PropTypes.string })),
26+
selectedItem: PropTypes.shape({ name: PropTypes.string, value: PropTypes.string }),
27+
}
28+
29+
componentDidMount() {
30+
this.props.items.forEach(item => {
31+
this.itemsMap[item.value] = item
32+
})
33+
}
34+
35+
render() {
36+
const { name, items } = this.props
37+
if (!items || !items.length) {
38+
return null
39+
}
40+
41+
const selectedItem = this.props.selectedItem || this.props.items[0]
42+
43+
return (
44+
<KnobsField>
45+
<KnobsControl>
46+
<select value={selectedItem.value} onChange={this.handleChange}>
47+
{items.map(({ name, value }) => (
48+
<option value={value}>{name}</option>
49+
))}
50+
</select>
51+
</KnobsControl>
52+
<KnobsLabel value={selectedItem.value} name={name} />
53+
</KnobsField>
54+
)
55+
}
56+
57+
private handleChange = (e: React.SyntheticEvent<HTMLElement>) => {
58+
this.props.onChange({ selectedItem: this.itemsMap[_.get(e, 'target.value')] })
59+
}
60+
}
61+
62+
export default KnobsSelect
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as React from 'react'
2+
import * as _ from 'lodash'
3+
import { Menu, ProviderConsumer, Grid, Text } from '@stardust-ui/react'
4+
5+
const items = [
6+
{ key: 'editorials', content: 'Editorials' },
7+
{ key: 'review', content: 'Reviews' },
8+
{ key: 'events', content: 'Upcoming Events' },
9+
]
10+
11+
const iconItems = [
12+
{ key: 'home', content: 'Home', icon: 'home' },
13+
{ key: 'users', content: 'Users', icon: 'users' },
14+
{ key: 'search', icon: 'search' },
15+
]
16+
17+
const MenuExampleColor = () => (
18+
<ProviderConsumer
19+
render={({ siteVariables: { colorScheme } }) => {
20+
const colorsArr = _.keys(colorScheme)
21+
const colors = _.times(7, num => colorsArr[num % colorsArr.length])
22+
23+
return (
24+
<Grid
25+
columns="repeat(2, auto)"
26+
styles={{ justifyContent: 'left', justifyItems: 'left', alignItems: 'center' }}
27+
variables={{ gridGap: '10px' }}
28+
>
29+
<Text content={`${_.upperCase(colors[0])} DEFAULT MENU:`} weight="bold" />
30+
<Menu defaultActiveIndex={0} color={colors[0]} items={items} />
31+
<Text content={`${_.upperCase(colors[1])} PILLS MENU:`} weight="bold" />
32+
<Menu defaultActiveIndex={0} color={colors[1]} pills items={items} />
33+
<Text content={`${_.upperCase(colors[2])} POINTING MENU:`} weight="bold" />
34+
<Menu defaultActiveIndex={0} color={colors[2]} pointing items={items} />
35+
<Text content={`${_.upperCase(colors[3])} VERTICAL POINTING MENU:`} weight="bold" />
36+
<Menu defaultActiveIndex={0} color={colors[3]} vertical pointing items={items} />
37+
<Text content={`${_.upperCase(colors[4])} UNDERLINED MENU:`} weight="bold" />
38+
<Menu defaultActiveIndex={0} color={colors[4]} underlined items={items} />
39+
<Text content={`${_.upperCase(colors[5])} ICON MENU:`} weight="bold" />
40+
<Menu defaultActiveIndex={0} color={colors[5]} items={iconItems} />
41+
<Text content={`${_.upperCase(colors[6])} ICON ONLY MENU:`} weight="bold" />
42+
<Menu
43+
defaultActiveIndex={0}
44+
color={colors[6]}
45+
iconOnly
46+
items={iconItems.map(item => _.pick(item, ['key', 'icon']))}
47+
/>
48+
<Text content={`UNDERLINED MENU (mutiple colors):`} weight="bold" />
49+
<Menu
50+
defaultActiveIndex={0}
51+
underlined
52+
color={{ foreground: colors[4], background: colors[5], border: colors[1] }}
53+
items={items}
54+
/>
55+
</Grid>
56+
)
57+
}}
58+
/>
59+
)
60+
61+
export default MenuExampleColor

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ const Variations = () => (
1919
description="A vertical menu can be fluid which takes up the full space of its container. A horizontal menu does this by default."
2020
examplePath="components/Menu/Variations/MenuExampleFluid"
2121
/>
22+
<ComponentExample
23+
title="Colored Menu"
24+
description="A Menu can have different colors."
25+
examplePath="components/Menu/Variations/MenuExampleColor"
26+
/>
2227
</ExampleSection>
2328
)
2429

src/components/Label/Label.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ import {
1313
commonPropTypes,
1414
ColorComponentProps,
1515
rtlTextContainer,
16+
ComplexColorPropType,
1617
} from '../../lib'
1718

1819
import Icon from '../Icon/Icon'
1920
import Image from '../Image/Image'
2021
import Layout from '../Layout/Layout'
2122
import { Accessibility } from '../../lib/accessibility/types'
2223
import { ReactProps, ShorthandValue } from '../../../types/utils'
23-
import { ComplexColorPropType } from '../../lib/commonPropInterfaces'
2424

2525
export interface LabelProps
2626
extends UIComponentProps,

src/components/Menu/Menu.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
commonPropTypes,
1313
getKindProp,
1414
rtlTextContainer,
15+
ColorComponentProps,
16+
ComplexColorPropType,
1517
} from '../../lib'
1618
import MenuItem from './MenuItem'
1719
import { menuBehavior } from '../../lib/accessibility'
@@ -23,7 +25,10 @@ import MenuDivider from './MenuDivider'
2325

2426
export type MenuShorthandKinds = 'divider' | 'item'
2527

26-
export interface MenuProps extends UIComponentProps, ChildrenComponentProps {
28+
export interface MenuProps
29+
extends UIComponentProps,
30+
ChildrenComponentProps,
31+
ColorComponentProps<ComplexColorPropType> {
2732
/**
2833
* Accessibility behavior if overridden by the user.
2934
* @default menuBehavior
@@ -93,6 +98,7 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
9398
static propTypes = {
9499
...commonPropTypes.createCommon({
95100
content: false,
101+
color: 'complex',
96102
}),
97103
accessibility: PropTypes.func,
98104
activeIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -141,6 +147,7 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
141147

142148
renderItems = (variables: ComponentVariablesObject) => {
143149
const {
150+
color,
144151
iconOnly,
145152
items,
146153
pills,
@@ -172,6 +179,7 @@ class Menu extends AutoControlledComponent<ReactProps<MenuProps>, MenuState> {
172179

173180
return MenuItem.create(item, {
174181
defaultProps: {
182+
color,
175183
iconOnly,
176184
pills,
177185
pointing,

src/components/Menu/MenuItem.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
isFromKeyboard,
1717
EventStack,
1818
rtlTextContainer,
19+
ColorComponentProps,
20+
ComplexColorPropType,
1921
} from '../../lib'
2022
import Icon from '../Icon/Icon'
2123
import Menu from '../Menu/Menu'
@@ -30,7 +32,8 @@ import Indicator from '../Indicator/Indicator'
3032
export interface MenuItemProps
3133
extends UIComponentProps,
3234
ChildrenComponentProps,
33-
ContentComponentProps {
35+
ContentComponentProps,
36+
ColorComponentProps<ComplexColorPropType> {
3437
/**
3538
* Accessibility behavior if overridden by the user.
3639
* @default menuItemBehavior
@@ -128,7 +131,7 @@ class MenuItem extends AutoControlledComponent<ReactProps<MenuItemProps>, MenuIt
128131
static create: Function
129132

130133
static propTypes = {
131-
...commonPropTypes.createCommon(),
134+
...commonPropTypes.createCommon({ color: 'complex' }),
132135
accessibility: PropTypes.func,
133136
active: PropTypes.bool,
134137
disabled: PropTypes.bool,
@@ -180,6 +183,7 @@ class MenuItem extends AutoControlledComponent<ReactProps<MenuItemProps>, MenuIt
180183
renderComponent({ ElementType, classes, accessibility, unhandledProps, styles }) {
181184
const {
182185
children,
186+
color,
183187
content,
184188
icon,
185189
wrapper,
@@ -230,6 +234,7 @@ class MenuItem extends AutoControlledComponent<ReactProps<MenuItemProps>, MenuIt
230234
{Menu.create(menu, {
231235
defaultProps: {
232236
accessibility: submenuBehavior,
237+
color,
233238
vertical: true,
234239
primary,
235240
secondary,

src/lib/colorUtils.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import {
55
ColorValues,
66
ColorSchemeMapping,
77
ColorScheme,
8+
ColorSchemeStates,
89
} from '../themes/types'
9-
import { Partial } from '../../types/utils'
1010
import { ComplexColorPropType } from './commonPropInterfaces'
1111

12+
const mapColorToInitialScheme = (color: string): ColorSchemeStates => ({ initial: color })
13+
1214
export const mapColorsToScheme = <T>(
1315
siteVars: SiteVariablesInput,
1416
mapper: keyof ColorVariants | ((color: ColorVariants) => T),
@@ -18,24 +20,27 @@ export const mapColorsToScheme = <T>(
1820
typeof mapper === 'number' ? String(mapper) : (mapper as any),
1921
) as ColorValues<T>
2022

21-
export const getColorSchemeFn = <T>(colorProp: string, colorScheme: ColorValues<T>) => {
22-
const colors = _.get(colorScheme, colorProp)
23-
return (area: keyof T, defaultColor: string) => (colors ? colors[area] : defaultColor)
24-
}
23+
export const getColorFromScheme = <T extends {}>(
24+
colorScheme: T,
25+
area: keyof T,
26+
defaultColor: string,
27+
): ColorSchemeStates => _.get(colorScheme, area, mapColorToInitialScheme(defaultColor))
2528

2629
export const getColorSchemeFromObject = (
27-
colorScheme: ColorValues<Partial<ColorScheme>>,
30+
colorScheme: ColorValues<ColorScheme>,
2831
colors: ComplexColorPropType,
29-
): Partial<ColorScheme> =>
32+
): ColorScheme =>
3033
_.mapValues(colors, (color, colorName) => {
3134
// if the color scheme contains the color, then get the value from it, otherwise return the color provided
3235
const colorSchemeValue = _.get(colorScheme, color, colorScheme.default[color])
33-
return colorSchemeValue ? colorSchemeValue[colorName] : colors[colorName]
36+
return colorSchemeValue
37+
? colorSchemeValue[colorName]
38+
: mapColorToInitialScheme(colors[colorName])
3439
})
3540

3641
export const getColorSchemeWithCustomDefaults = (
3742
colorScheme: ColorSchemeMapping,
38-
customDefaultValues: Partial<ColorScheme>,
43+
customDefaultValues: ColorScheme,
3944
) => {
4045
const mergedDefaultValues = {
4146
...colorScheme.default,
@@ -49,8 +54,8 @@ export const getColorSchemeWithCustomDefaults = (
4954

5055
export const generateColorScheme = (
5156
colorProp: ComplexColorPropType,
52-
colorScheme: ColorValues<Partial<ColorScheme>>,
53-
): Partial<ColorScheme> => {
57+
colorScheme: ColorValues<ColorScheme>,
58+
): ColorScheme => {
5459
// if both color prop and color scheme are defined, we are merging them
5560
if (colorProp && colorScheme) {
5661
return typeof colorProp === 'string'
@@ -61,14 +66,14 @@ export const generateColorScheme = (
6166
// if the color prop is not defined, but the the color scheme is defined, then we are returning
6267
// the defaults from the color scheme if they exists
6368
if (colorScheme) {
64-
return colorScheme && colorScheme.default ? colorScheme.default : {}
69+
return colorScheme.default || {}
6570
}
6671

6772
// if the color scheme is not defined, then if the color prop is a scheme object we are
6873
// returning it, otherwise we return an empty object, as it means that the component is
6974
// implementing the simple color prop
7075
if (colorProp) {
71-
return typeof colorProp === 'string' ? {} : colorProp
76+
return typeof colorProp === 'string' ? {} : _.mapValues(colorProp, mapColorToInitialScheme)
7277
}
7378

7479
// if neither the color prop, nor the color scheme are defined, we are returning empty object

src/lib/commonPropInterfaces.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,8 @@ export type ColorValue =
4040
| string
4141

4242
export type ComplexColorPropType =
43-
| {
44-
foreground?: ColorValue
45-
background?: ColorValue
46-
border?: ColorValue
47-
shadow?: ColorValue
48-
}
4943
| ColorValue
44+
| Partial<Record<'foreground' | 'background' | 'border' | 'shadow', ColorValue>>
5045

5146
export interface ColorComponentProps<TColor = ColorValue> {
5247
/** A component can have a color. */

0 commit comments

Comments
 (0)