@instructure/ui-options 8.19.1-snapshot.8 → 8.20.1-snapshot.18

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/es/Options/Item/__examples__/Item.examples.js +5 -0
  3. package/es/Options/Item/index.js +16 -9
  4. package/es/Options/Item/props.js +3 -2
  5. package/es/Options/Item/styles.js +18 -1
  6. package/es/Options/Item/theme.js +1 -1
  7. package/es/Options/index.js +2 -2
  8. package/es/Options/theme.js +3 -5
  9. package/es/index.js +1 -1
  10. package/lib/Options/Item/__examples__/Item.examples.js +5 -0
  11. package/lib/Options/Item/index.js +15 -8
  12. package/lib/Options/Item/props.js +3 -2
  13. package/lib/Options/Item/styles.js +18 -1
  14. package/lib/Options/Item/theme.js +1 -1
  15. package/lib/Options/index.js +2 -2
  16. package/lib/Options/theme.js +3 -7
  17. package/lib/index.js +0 -6
  18. package/package.json +14 -14
  19. package/src/Options/Item/__examples__/Item.examples.ts +10 -0
  20. package/src/Options/Item/index.tsx +31 -4
  21. package/src/Options/Item/props.ts +22 -3
  22. package/src/Options/Item/styles.ts +17 -1
  23. package/src/Options/Item/theme.ts +1 -1
  24. package/src/Options/README.md +61 -0
  25. package/src/Options/index.tsx +8 -2
  26. package/src/Options/theme.ts +1 -10
  27. package/src/index.ts +1 -1
  28. package/tsconfig.build.tsbuildinfo +1 -1
  29. package/types/Options/Item/__examples__/Item.examples.d.ts.map +1 -1
  30. package/types/Options/Item/index.d.ts +6 -4
  31. package/types/Options/Item/index.d.ts.map +1 -1
  32. package/types/Options/Item/props.d.ts +8 -3
  33. package/types/Options/Item/props.d.ts.map +1 -1
  34. package/types/Options/Item/styles.d.ts.map +1 -1
  35. package/types/Options/index.d.ts.map +1 -1
  36. package/types/Options/theme.d.ts +1 -2
  37. package/types/Options/theme.d.ts.map +1 -1
  38. package/types/index.d.ts +1 -1
  39. package/types/index.d.ts.map +1 -1
@@ -31,18 +31,24 @@ import type {
31
31
  OptionsItemTheme,
32
32
  OtherHTMLAttributes
33
33
  } from '@instructure/shared-types'
34
+ import type { WithDeterministicIdProps } from '@instructure/ui-react-utils'
34
35
  import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
35
36
 
36
37
  type ItemProps = {
37
38
  children?: React.ReactNode | (() => React.ReactNode)
38
39
  /**
39
- * Element type to render as
40
+ * Element type to render as. Will be set to `<a>` if href is provided.
40
41
  */
41
42
  as?: AsElementType
42
43
  /**
43
44
  * The style variant of the item
44
45
  */
45
- variant?: 'default' | 'highlighted' | 'selected' | 'disabled'
46
+ variant?:
47
+ | 'default'
48
+ | 'highlighted'
49
+ | 'selected'
50
+ | 'disabled'
51
+ | 'highlighted-disabled'
46
52
  /**
47
53
  * The ARIA role of the element
48
54
  */
@@ -76,6 +82,10 @@ type OptionsItemOwnProps = ItemProps & {
76
82
  * The ARIA role of the description element
77
83
  */
78
84
  descriptionRole?: string
85
+ /**
86
+ * Providing href will render the option as `<a>`.
87
+ */
88
+ href?: string
79
89
  /**
80
90
  * provides a reference to the underlying html root element
81
91
  */
@@ -88,6 +98,7 @@ type AllowedPropKeys = Readonly<Array<PropKeys>>
88
98
 
89
99
  type OptionsItemProps = OptionsItemOwnProps &
90
100
  WithStyleProps<OptionsItemTheme, OptionsItemStyle> &
101
+ WithDeterministicIdProps &
91
102
  OtherHTMLAttributes<OptionsItemOwnProps>
92
103
 
93
104
  type OptionsItemStyle = ComponentStyle<
@@ -101,7 +112,13 @@ type OptionsItemStyle = ComponentStyle<
101
112
 
102
113
  const propTypes: PropValidators<PropKeys> = {
103
114
  as: PropTypes.elementType,
104
- variant: PropTypes.oneOf(['default', 'highlighted', 'selected', 'disabled']),
115
+ variant: PropTypes.oneOf([
116
+ 'default',
117
+ 'highlighted',
118
+ 'selected',
119
+ 'disabled',
120
+ 'highlighted-disabled'
121
+ ]),
105
122
  role: PropTypes.string,
106
123
  renderBeforeLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
107
124
  renderAfterLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
@@ -109,6 +126,7 @@ const propTypes: PropValidators<PropKeys> = {
109
126
  afterLabelContentVAlign: PropTypes.oneOf(['start', 'center', 'end']),
110
127
  description: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
111
128
  descriptionRole: PropTypes.string,
129
+ href: PropTypes.string,
112
130
  elementRef: PropTypes.func,
113
131
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
114
132
  }
@@ -123,6 +141,7 @@ const allowedProps: AllowedPropKeys = [
123
141
  'afterLabelContentVAlign',
124
142
  'description',
125
143
  'descriptionRole',
144
+ 'href',
126
145
  'elementRef',
127
146
  'children'
128
147
  ]
@@ -63,6 +63,12 @@ const generateStyle = (
63
63
  color: componentTheme.highlightedLabelColor
64
64
  },
65
65
  disabled: { cursor: 'not-allowed', opacity: 0.5 },
66
+ ['highlighted-disabled']: {
67
+ background: componentTheme.highlightedBackground,
68
+ color: componentTheme.highlightedLabelColor,
69
+ cursor: 'not-allowed',
70
+ opacity: 0.5
71
+ },
66
72
  default: {}
67
73
  }
68
74
 
@@ -92,6 +98,8 @@ const generateStyle = (
92
98
  }[vAlign!]
93
99
  }
94
100
 
101
+ const linkStyles = { textDecoration: 'none', color: 'currentColor' }
102
+
95
103
  const transition = 'background 200ms'
96
104
 
97
105
  return {
@@ -143,7 +151,15 @@ const generateStyle = (
143
151
  hasContentAfterLabel && {
144
152
  paddingInlineEnd: `calc(${componentTheme.iconPadding} * 2 + 1em)`,
145
153
  paddingInlineStart: `calc(${componentTheme.iconPadding} * 2 + 1em)`
146
- })
154
+ }),
155
+
156
+ // NOTE: needs separate groups for `:is()` and `:-webkit-any()` because of css selector group validation (see https://www.w3.org/TR/selectors-3/#grouping)
157
+ '&:is(a)': {
158
+ '&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
159
+ },
160
+ '&:-webkit-any(a)': {
161
+ '&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
162
+ }
147
163
  },
148
164
  content: {
149
165
  label: 'optionItem__content',
@@ -55,7 +55,7 @@ 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?.small,
59
59
 
60
60
  beforeLabelContentVOffset: '0.625rem',
61
61
  afterLabelContentVOffset: '0.625rem',
@@ -24,6 +24,9 @@ example: true
24
24
  <Options.Item variant="disabled">
25
25
  Disabled option
26
26
  </Options.Item>
27
+ <Options.Item variant="highlighted-disabled">
28
+ Highlighted disabled option
29
+ </Options.Item>
27
30
  </Options>
28
31
  </View>
29
32
  ```
@@ -372,3 +375,61 @@ class Example extends React.Component {
372
375
 
373
376
  render(<Example />)
374
377
  ```
378
+
379
+ Providing a `href` prop will render the option as `<a>` link element.
380
+
381
+ **WARNING!** Since `Options` is a view-only component, you have to make sure it is accessible, and if set the variant to disabled, disable the links as well!
382
+
383
+ ```js
384
+ ---
385
+ render: false
386
+ example: true
387
+ ---
388
+ class Example extends React.Component {
389
+ constructor(props) {
390
+ super(props)
391
+
392
+ this.state = {
393
+ highlighted: -1
394
+ }
395
+ }
396
+
397
+ handleMouseOver = (index) => {
398
+ this.setState({ highlighted: index })
399
+ }
400
+
401
+ render() {
402
+ return (
403
+ <View display="block" width="300px">
404
+ <Options onMouseOut={(e) => this.setState({ highlighted: -1 })}>
405
+ <Options.Item
406
+ onMouseOver={(e) => this.handleMouseOver(1)}
407
+ variant={this.state.highlighted === 1 ? 'highlighted' : 'default'}
408
+ href="/"
409
+ >
410
+ Link one
411
+ </Options.Item>
412
+ <Options.Item
413
+ onMouseOver={(e) => this.handleMouseOver(2)}
414
+ variant={this.state.highlighted === 2 ? 'highlighted' : 'default'}
415
+ href="/"
416
+ >
417
+ Link two
418
+ </Options.Item>
419
+ <Options.Item
420
+ onMouseOver={(e) => this.handleMouseOver(3)}
421
+ variant={this.state.highlighted === 3 ? 'highlighted' : 'default'}
422
+ variant="disabled"
423
+ onClick={(e) => { e.preventDefault() }}
424
+ href="/"
425
+ >
426
+ Link three
427
+ </Options.Item>
428
+ </Options>
429
+ </View>
430
+ )
431
+ }
432
+ }
433
+
434
+ render(<Example />)
435
+ ```
@@ -134,7 +134,7 @@ class Options extends Component<OptionsProps> {
134
134
  return this.renderSubList(child)
135
135
  }
136
136
  if (matchComponentTypes(child, ['Item', 'Separator'])) {
137
- return safeCloneElement(child, { as: this.childAs })
137
+ return safeCloneElement(child, { as: this.childAs || child.props.as })
138
138
  }
139
139
  return undefined
140
140
  })
@@ -161,7 +161,13 @@ class Options extends Component<OptionsProps> {
161
161
  margin="none"
162
162
  padding="none"
163
163
  background="primary"
164
- aria-labelledby={renderLabel ? this._labelId : undefined}
164
+ aria-labelledby={
165
+ renderLabel
166
+ ? this._labelId
167
+ : this.props['aria-labelledby']
168
+ ? this.props['aria-labelledby']
169
+ : undefined
170
+ }
165
171
  >
166
172
  {this.renderChildren()}
167
173
  </View>
@@ -25,14 +25,6 @@
25
25
  import type { Theme } from '@instructure/ui-themes'
26
26
  import type { OptionsTheme } from '@instructure/shared-types'
27
27
 
28
- const optionsThemeKeys: (keyof OptionsTheme)[] = [
29
- 'labelFontWeight',
30
- 'background',
31
- 'labelColor',
32
- 'labelPadding',
33
- 'nestedLabelPadding'
34
- ]
35
-
36
28
  /**
37
29
  * Generates the theme object for the component from the theme and provided additional information
38
30
  * @param {Object} theme The actual theme object.
@@ -41,7 +33,6 @@ const optionsThemeKeys: (keyof OptionsTheme)[] = [
41
33
  const generateComponentTheme = (theme: Theme): OptionsTheme => {
42
34
  const { colors, typography, spacing } = theme
43
35
 
44
- // TODO: if updated, update optionsThemeKeys array too
45
36
  const componentVariables: OptionsTheme = {
46
37
  labelFontWeight: typography?.fontWeightBold,
47
38
 
@@ -57,5 +48,5 @@ const generateComponentTheme = (theme: Theme): OptionsTheme => {
57
48
  }
58
49
  }
59
50
 
60
- export { generateComponentTheme as optionsThemeGenerator, optionsThemeKeys }
51
+ export { generateComponentTheme as optionsThemeGenerator }
61
52
  export default generateComponentTheme
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  export { Options } from './Options'
26
26
 
27
- export { optionsThemeGenerator, optionsThemeKeys } from './Options/theme'
27
+ export { optionsThemeGenerator } from './Options/theme'
28
28
  export { optionsItemThemeGenerator } from './Options/Item/theme'
29
29
  export { optionsSeparatorThemeGenerator } from './Options/Separator/theme'
30
30