@instructure/ui-options 8.14.1-snapshot.7 → 8.15.1-snapshot.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/es/Options/Item/__examples__/Item.examples.js +28 -2
- package/es/Options/Item/index.js +11 -4
- package/es/Options/Item/props.js +5 -1
- package/es/Options/Item/styles.js +46 -6
- package/es/Options/Item/theme.js +8 -1
- package/es/Options/index.js +4 -4
- package/lib/Options/Item/__examples__/Item.examples.js +28 -2
- package/lib/Options/Item/index.js +11 -4
- package/lib/Options/Item/props.js +5 -1
- package/lib/Options/Item/styles.js +46 -6
- package/lib/Options/Item/theme.js +8 -1
- package/lib/Options/index.js +5 -3
- package/package.json +15 -15
- package/src/Options/Item/__examples__/Item.examples.ts +44 -5
- package/src/Options/Item/index.tsx +14 -4
- package/src/Options/Item/props.ts +31 -3
- package/src/Options/Item/styles.ts +51 -5
- package/src/Options/Item/theme.ts +10 -1
- package/src/Options/README.md +78 -2
- package/src/Options/index.tsx +14 -6
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Options/Item/OptionsItemLocator.d.ts +4 -4
- package/types/Options/Item/__examples__/Item.examples.d.ts +3 -26
- package/types/Options/Item/__examples__/Item.examples.d.ts.map +1 -1
- package/types/Options/Item/index.d.ts +5 -5
- package/types/Options/Item/index.d.ts.map +1 -1
- package/types/Options/Item/props.d.ts +18 -3
- package/types/Options/Item/props.d.ts.map +1 -1
- package/types/Options/Item/styles.d.ts.map +1 -1
- package/types/Options/Item/theme.d.ts.map +1 -1
- package/types/Options/OptionsLocator.d.ts +14 -14
- package/types/Options/index.d.ts +8 -7
- package/types/Options/index.d.ts.map +1 -1
|
@@ -58,9 +58,9 @@ class Item extends Component<OptionsItemProps> {
|
|
|
58
58
|
as: 'span',
|
|
59
59
|
variant: 'default',
|
|
60
60
|
role: 'listitem',
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
descriptionRole: 'note',
|
|
62
|
+
beforeLabelContentVAlign: 'center',
|
|
63
|
+
afterLabelContentVAlign: 'center'
|
|
64
64
|
} as const
|
|
65
65
|
|
|
66
66
|
ref: Element | null = null
|
|
@@ -104,6 +104,8 @@ class Item extends Component<OptionsItemProps> {
|
|
|
104
104
|
as,
|
|
105
105
|
role,
|
|
106
106
|
styles,
|
|
107
|
+
description,
|
|
108
|
+
descriptionRole,
|
|
107
109
|
renderBeforeLabel,
|
|
108
110
|
renderAfterLabel,
|
|
109
111
|
children
|
|
@@ -112,6 +114,9 @@ class Item extends Component<OptionsItemProps> {
|
|
|
112
114
|
const ElementType = getElementType(Item, this.props, () => as!)
|
|
113
115
|
const passthroughProps = omitProps(this.props, Item.allowedProps)
|
|
114
116
|
|
|
117
|
+
const childrenContent = callRenderProp(children)
|
|
118
|
+
const descriptionContent = callRenderProp(description)
|
|
119
|
+
|
|
115
120
|
return (
|
|
116
121
|
<ElementType
|
|
117
122
|
role="none"
|
|
@@ -122,7 +127,12 @@ class Item extends Component<OptionsItemProps> {
|
|
|
122
127
|
}}
|
|
123
128
|
>
|
|
124
129
|
<span {...passthroughProps} css={styles?.container} role={role}>
|
|
125
|
-
{
|
|
130
|
+
{childrenContent}
|
|
131
|
+
{descriptionContent && (
|
|
132
|
+
<span css={styles?.description} role={descriptionRole}>
|
|
133
|
+
{descriptionContent}
|
|
134
|
+
</span>
|
|
135
|
+
)}
|
|
126
136
|
</span>
|
|
127
137
|
{renderBeforeLabel &&
|
|
128
138
|
this.renderContent(renderBeforeLabel, styles?.contentBefore)}
|
|
@@ -44,7 +44,7 @@ type ItemProps = {
|
|
|
44
44
|
*/
|
|
45
45
|
variant?: 'default' | 'highlighted' | 'selected' | 'disabled'
|
|
46
46
|
/**
|
|
47
|
-
* The
|
|
47
|
+
* The ARIA role of the element
|
|
48
48
|
*/
|
|
49
49
|
role?: string
|
|
50
50
|
}
|
|
@@ -60,7 +60,22 @@ type OptionsItemOwnProps = ItemProps & {
|
|
|
60
60
|
* (if you pass a function, it has the `props` as its parameter)
|
|
61
61
|
*/
|
|
62
62
|
renderAfterLabel?: React.ReactNode | ((props: ItemProps) => React.ReactNode)
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Sets the vAlign of renderBeforeLabel content
|
|
65
|
+
*/
|
|
66
|
+
beforeLabelContentVAlign?: 'start' | 'center' | 'end'
|
|
67
|
+
/**
|
|
68
|
+
* Sets the vAlign of renderAfterLabel content
|
|
69
|
+
*/
|
|
70
|
+
afterLabelContentVAlign?: 'start' | 'center' | 'end'
|
|
71
|
+
/**
|
|
72
|
+
* Additional "secondary" description text
|
|
73
|
+
*/
|
|
74
|
+
description?: React.ReactNode | (() => React.ReactNode)
|
|
75
|
+
/**
|
|
76
|
+
* The ARIA role of the description element
|
|
77
|
+
*/
|
|
78
|
+
descriptionRole?: string
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
type PropKeys = keyof OptionsItemOwnProps
|
|
@@ -72,7 +87,12 @@ type OptionsItemProps = OptionsItemOwnProps &
|
|
|
72
87
|
OtherHTMLAttributes<OptionsItemOwnProps>
|
|
73
88
|
|
|
74
89
|
type OptionsItemStyle = ComponentStyle<
|
|
75
|
-
|
|
90
|
+
| 'item'
|
|
91
|
+
| 'container'
|
|
92
|
+
| 'content'
|
|
93
|
+
| 'contentBefore'
|
|
94
|
+
| 'contentAfter'
|
|
95
|
+
| 'description'
|
|
76
96
|
>
|
|
77
97
|
|
|
78
98
|
const propTypes: PropValidators<PropKeys> = {
|
|
@@ -81,6 +101,10 @@ const propTypes: PropValidators<PropKeys> = {
|
|
|
81
101
|
role: PropTypes.string,
|
|
82
102
|
renderBeforeLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
83
103
|
renderAfterLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
104
|
+
beforeLabelContentVAlign: PropTypes.oneOf(['start', 'center', 'end']),
|
|
105
|
+
afterLabelContentVAlign: PropTypes.oneOf(['start', 'center', 'end']),
|
|
106
|
+
description: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
107
|
+
descriptionRole: PropTypes.string,
|
|
84
108
|
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
|
|
85
109
|
}
|
|
86
110
|
|
|
@@ -90,6 +114,10 @@ const allowedProps: AllowedPropKeys = [
|
|
|
90
114
|
'role',
|
|
91
115
|
'renderBeforeLabel',
|
|
92
116
|
'renderAfterLabel',
|
|
117
|
+
'beforeLabelContentVAlign',
|
|
118
|
+
'afterLabelContentVAlign',
|
|
119
|
+
'description',
|
|
120
|
+
'descriptionRole',
|
|
93
121
|
'children'
|
|
94
122
|
]
|
|
95
123
|
|
|
@@ -45,11 +45,14 @@ const generateStyle = (
|
|
|
45
45
|
variant,
|
|
46
46
|
children,
|
|
47
47
|
renderBeforeLabel: hasContentBeforeLabel,
|
|
48
|
-
renderAfterLabel: hasContentAfterLabel
|
|
48
|
+
renderAfterLabel: hasContentAfterLabel,
|
|
49
|
+
beforeLabelContentVAlign,
|
|
50
|
+
afterLabelContentVAlign
|
|
49
51
|
} = props
|
|
50
52
|
|
|
51
53
|
const containsList = matchComponentTypes(children, ['Options'])
|
|
52
54
|
|
|
55
|
+
// used for label and description too
|
|
53
56
|
const variantVariants = {
|
|
54
57
|
highlighted: {
|
|
55
58
|
background: componentTheme.highlightedBackground,
|
|
@@ -63,6 +66,34 @@ const generateStyle = (
|
|
|
63
66
|
default: {}
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
const getContentVAlign = (type: 'before' | 'after') => {
|
|
70
|
+
const vAlign =
|
|
71
|
+
type === 'before' ? beforeLabelContentVAlign : afterLabelContentVAlign
|
|
72
|
+
|
|
73
|
+
const vOffset =
|
|
74
|
+
type === 'before'
|
|
75
|
+
? componentTheme.beforeLabelContentVOffset
|
|
76
|
+
: componentTheme.afterLabelContentVOffset
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
start: {
|
|
80
|
+
alignItems: 'flex-start',
|
|
81
|
+
paddingBlockStart: vOffset
|
|
82
|
+
},
|
|
83
|
+
center: {
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
paddingBlockStart: vOffset,
|
|
86
|
+
paddingBlockEnd: vOffset
|
|
87
|
+
},
|
|
88
|
+
end: {
|
|
89
|
+
alignItems: 'flex-end',
|
|
90
|
+
paddingBlockEnd: vOffset
|
|
91
|
+
}
|
|
92
|
+
}[vAlign!]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const transition = 'background 200ms'
|
|
96
|
+
|
|
66
97
|
return {
|
|
67
98
|
item: {
|
|
68
99
|
label: 'optionItem',
|
|
@@ -76,7 +107,7 @@ const generateStyle = (
|
|
|
76
107
|
lineHeight: componentTheme.lineHeight,
|
|
77
108
|
outline: 'none',
|
|
78
109
|
position: 'relative',
|
|
79
|
-
transition
|
|
110
|
+
transition,
|
|
80
111
|
userSelect: 'none',
|
|
81
112
|
...variantVariants[variant!],
|
|
82
113
|
...(containsList && { cursor: 'default' }),
|
|
@@ -115,9 +146,9 @@ const generateStyle = (
|
|
|
115
146
|
},
|
|
116
147
|
content: {
|
|
117
148
|
label: 'optionItem__content',
|
|
118
|
-
alignItems: 'center',
|
|
119
149
|
display: 'flex',
|
|
120
150
|
height: '100%',
|
|
151
|
+
boxSizing: 'border-box',
|
|
121
152
|
pointerEvents: 'none',
|
|
122
153
|
position: 'absolute',
|
|
123
154
|
top: '0'
|
|
@@ -125,12 +156,27 @@ const generateStyle = (
|
|
|
125
156
|
contentBefore: {
|
|
126
157
|
label: 'optionItem__content--before',
|
|
127
158
|
insetInlineEnd: 'auto',
|
|
128
|
-
insetInlineStart: componentTheme.iconPadding
|
|
159
|
+
insetInlineStart: componentTheme.iconPadding,
|
|
160
|
+
...getContentVAlign('before')
|
|
129
161
|
},
|
|
130
162
|
contentAfter: {
|
|
131
163
|
label: 'optionItem__content--after',
|
|
132
164
|
insetInlineEnd: componentTheme.iconPadding,
|
|
133
|
-
insetInlineStart: 'auto'
|
|
165
|
+
insetInlineStart: 'auto',
|
|
166
|
+
...getContentVAlign('after')
|
|
167
|
+
},
|
|
168
|
+
description: {
|
|
169
|
+
label: 'optionItem__description',
|
|
170
|
+
display: 'block',
|
|
171
|
+
transition,
|
|
172
|
+
paddingBlockStart: componentTheme.descriptionPaddingStart,
|
|
173
|
+
fontWeight: componentTheme.descriptionFontWeight,
|
|
174
|
+
fontSize: componentTheme.descriptionFontSize,
|
|
175
|
+
lineHeight: componentTheme.descriptionLineHeight,
|
|
176
|
+
color: componentTheme.descriptionColor,
|
|
177
|
+
|
|
178
|
+
...variantVariants[variant!],
|
|
179
|
+
background: 'none' // needed to clear variant background
|
|
134
180
|
}
|
|
135
181
|
}
|
|
136
182
|
}
|
|
@@ -55,7 +55,16 @@ const generateComponentTheme = (theme: Theme): OptionsItemTheme => {
|
|
|
55
55
|
|
|
56
56
|
padding: `${spacing?.xSmall} ${spacing?.small}`,
|
|
57
57
|
iconPadding: spacing?.small,
|
|
58
|
-
nestedPadding: spacing?.medium
|
|
58
|
+
nestedPadding: spacing?.medium,
|
|
59
|
+
|
|
60
|
+
beforeLabelContentVOffset: '0.625rem',
|
|
61
|
+
afterLabelContentVOffset: '0.625rem',
|
|
62
|
+
|
|
63
|
+
descriptionFontSize: typography.fontSizeSmall,
|
|
64
|
+
descriptionFontWeight: typography.fontWeightNormal,
|
|
65
|
+
descriptionLineHeight: typography.lineHeight,
|
|
66
|
+
descriptionPaddingStart: '0.25em',
|
|
67
|
+
descriptionColor: colors.textDark
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
return {
|
package/src/Options/README.md
CHANGED
|
@@ -130,7 +130,11 @@ class Example extends React.Component {
|
|
|
130
130
|
width="300px"
|
|
131
131
|
shadow="above"
|
|
132
132
|
>
|
|
133
|
-
<Options
|
|
133
|
+
<Options
|
|
134
|
+
onKeyDown={this.handleKeyDown}
|
|
135
|
+
onMouseOut={(e) => this.setState({ highlighted: -1 })}
|
|
136
|
+
tabIndex="0"
|
|
137
|
+
>
|
|
134
138
|
{this.props.options.map((option, index) => {
|
|
135
139
|
let variant = 'default'
|
|
136
140
|
if (this.state.highlighted === index) {
|
|
@@ -225,7 +229,11 @@ class Example extends React.Component {
|
|
|
225
229
|
width="300px"
|
|
226
230
|
shadow="above"
|
|
227
231
|
>
|
|
228
|
-
<Options
|
|
232
|
+
<Options
|
|
233
|
+
onKeyDown={this.handleKeyDown}
|
|
234
|
+
onMouseOut={(e) => this.setState({ highlighted: -1 })}
|
|
235
|
+
tabIndex="0"
|
|
236
|
+
>
|
|
229
237
|
{this.props.options.map((option, index) => {
|
|
230
238
|
let variant = 'default'
|
|
231
239
|
if (this.state.highlighted === index) {
|
|
@@ -296,3 +304,71 @@ render(
|
|
|
296
304
|
]} />
|
|
297
305
|
)
|
|
298
306
|
```
|
|
307
|
+
|
|
308
|
+
Additional/secondary text can be added via the `description` prop, and the ARIA role of it can be set with the `descriptionRole` prop.
|
|
309
|
+
|
|
310
|
+
For longer, multi-line options the problem of the vertical alignment comes up. The content of the `renderBeforeLabel` and `renderAfterLabel` props are vertically centered by default. This can be changed with the `beforeLabelContentVAlign` and `afterLabelContentVAlign` props.
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
---
|
|
314
|
+
render: false
|
|
315
|
+
example: true
|
|
316
|
+
---
|
|
317
|
+
class Example extends React.Component {
|
|
318
|
+
constructor(props) {
|
|
319
|
+
super(props)
|
|
320
|
+
|
|
321
|
+
this.state = {
|
|
322
|
+
highlighted: -1
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
handleMouseOver = (index) => {
|
|
327
|
+
this.setState({ highlighted: index })
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
render() {
|
|
331
|
+
return (
|
|
332
|
+
<View display="block" width="300px">
|
|
333
|
+
<Options onMouseOut={(e) => this.setState({ highlighted: -1 })}>
|
|
334
|
+
<Options.Item
|
|
335
|
+
onMouseOver={(e) => this.handleMouseOver(1)}
|
|
336
|
+
variant={this.state.highlighted === 1 ? 'highlighted' : 'default'}
|
|
337
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
338
|
+
renderBeforeLabel={IconCheckSolid}
|
|
339
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
340
|
+
beforeLabelContentVAlign="start"
|
|
341
|
+
afterLabelContentVAlign="start"
|
|
342
|
+
>
|
|
343
|
+
Option one
|
|
344
|
+
</Options.Item>
|
|
345
|
+
<Options.Item
|
|
346
|
+
onMouseOver={(e) => this.handleMouseOver(2)}
|
|
347
|
+
variant={this.state.highlighted === 2 ? 'highlighted' : 'default'}
|
|
348
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
349
|
+
renderBeforeLabel={IconCheckSolid}
|
|
350
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
351
|
+
beforeLabelContentVAlign="center"
|
|
352
|
+
afterLabelContentVAlign="center"
|
|
353
|
+
>
|
|
354
|
+
Option two
|
|
355
|
+
</Options.Item>
|
|
356
|
+
<Options.Item
|
|
357
|
+
onMouseOver={(e) => this.handleMouseOver(3)}
|
|
358
|
+
variant={this.state.highlighted === 3 ? 'highlighted' : 'default'}
|
|
359
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
360
|
+
renderBeforeLabel={IconCheckSolid}
|
|
361
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
362
|
+
beforeLabelContentVAlign="end"
|
|
363
|
+
afterLabelContentVAlign="end"
|
|
364
|
+
>
|
|
365
|
+
Option three
|
|
366
|
+
</Options.Item>
|
|
367
|
+
</Options>
|
|
368
|
+
</View>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
render(<Example />)
|
|
374
|
+
```
|
package/src/Options/index.tsx
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/** @jsx jsx */
|
|
26
|
-
import { Component, Children
|
|
26
|
+
import React, { Component, Children } from 'react'
|
|
27
27
|
|
|
28
28
|
import {
|
|
29
29
|
omitProps,
|
|
@@ -42,10 +42,18 @@ import generateStyles from './styles'
|
|
|
42
42
|
import generateComponentTheme from './theme'
|
|
43
43
|
|
|
44
44
|
import { Item } from './Item'
|
|
45
|
+
import type { OptionsItemProps } from './Item/props'
|
|
45
46
|
import { Separator } from './Separator'
|
|
47
|
+
import type { OptionsSeparatorProps } from './Separator/props'
|
|
48
|
+
|
|
46
49
|
import type { OptionsProps } from './props'
|
|
47
50
|
import { allowedProps, propTypes } from './props'
|
|
48
51
|
|
|
52
|
+
type ItemChild = React.ComponentElement<OptionsItemProps, Item>
|
|
53
|
+
type SeparatorChild = React.ComponentElement<OptionsSeparatorProps, Separator>
|
|
54
|
+
type OptionsChild = React.ComponentElement<OptionsProps, Options>
|
|
55
|
+
type OptionsChildren = (ItemChild | SeparatorChild | OptionsChild)[]
|
|
56
|
+
|
|
49
57
|
/**
|
|
50
58
|
---
|
|
51
59
|
category: components
|
|
@@ -109,11 +117,11 @@ class Options extends Component<OptionsProps> {
|
|
|
109
117
|
)
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
renderSubList(
|
|
120
|
+
renderSubList(subOptions: OptionsChild) {
|
|
113
121
|
const { styles } = this.props
|
|
114
122
|
return (
|
|
115
123
|
<Item as={this.childAs} role="presentation" css={styles?.label}>
|
|
116
|
-
{
|
|
124
|
+
{subOptions}
|
|
117
125
|
</Item>
|
|
118
126
|
)
|
|
119
127
|
}
|
|
@@ -121,12 +129,12 @@ class Options extends Component<OptionsProps> {
|
|
|
121
129
|
renderChildren() {
|
|
122
130
|
const { children } = this.props
|
|
123
131
|
|
|
124
|
-
return Children.map(children, (child) => {
|
|
125
|
-
if (matchComponentTypes(child, ['Options'])) {
|
|
132
|
+
return Children.map(children as OptionsChildren, (child) => {
|
|
133
|
+
if (matchComponentTypes<OptionsChild>(child, ['Options'])) {
|
|
126
134
|
return this.renderSubList(child)
|
|
127
135
|
}
|
|
128
136
|
if (matchComponentTypes(child, ['Item', 'Separator'])) {
|
|
129
|
-
return safeCloneElement(child
|
|
137
|
+
return safeCloneElement(child, { as: this.childAs })
|
|
130
138
|
}
|
|
131
139
|
return undefined
|
|
132
140
|
})
|