@telus-uds/components-base 0.0.2-prerelease.9 → 1.1.0

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 (293) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.storybook/main.js +4 -0
  3. package/.storybook/preview.js +37 -0
  4. package/.ultra.cache.json +1 -1
  5. package/CHANGELOG.md +50 -0
  6. package/README.md +4 -2
  7. package/__fixtures__/test-utils.js +25 -0
  8. package/__fixtures__/testTheme.js +4 -2
  9. package/__tests__/Button/ButtonGroup.test.jsx +4 -5
  10. package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
  11. package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
  12. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  13. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  14. package/__tests__/Link/LinkBase.test.jsx +0 -14
  15. package/__tests__/Radio/Radio.test.jsx +2 -2
  16. package/__tests__/Radio/RadioGroup.test.jsx +4 -5
  17. package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
  18. package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
  19. package/__tests__/Search/Search.test.jsx +9 -8
  20. package/__tests__/Select/Select.test.jsx +3 -2
  21. package/__tests__/Tabs/Tabs.test.jsx +1 -161
  22. package/__tests__/Tags/Tags.test.jsx +4 -5
  23. package/__tests__/TextInput/TextArea.test.jsx +3 -2
  24. package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
  25. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  26. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  27. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  28. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  29. package/__tests__/utils/children.test.jsx +128 -0
  30. package/__tests__/utils/input.test.js +1 -1
  31. package/__tests__/utils/semantics.test.jsx +43 -0
  32. package/babel.config.js +9 -16
  33. package/component-docs.json +10313 -0
  34. package/generate-component-docs.js +56 -0
  35. package/lib/A11yText/index.js +10 -5
  36. package/lib/ActivityIndicator/Spinner.js +16 -13
  37. package/lib/ActivityIndicator/Spinner.native.js +12 -8
  38. package/lib/Box/Box.js +103 -8
  39. package/lib/Button/Button.js +9 -8
  40. package/lib/Button/ButtonBase.js +14 -7
  41. package/lib/Button/ButtonGroup.js +25 -10
  42. package/lib/Button/ButtonLink.js +10 -7
  43. package/lib/Card/Card.js +2 -0
  44. package/lib/Card/CardBase.js +13 -5
  45. package/lib/Card/PressableCardBase.js +12 -8
  46. package/lib/Checkbox/Checkbox.js +25 -14
  47. package/lib/Checkbox/CheckboxGroup.js +22 -12
  48. package/lib/Divider/Divider.js +12 -7
  49. package/lib/ExpandCollapse/Accordion.js +10 -4
  50. package/lib/ExpandCollapse/Control.js +12 -6
  51. package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
  52. package/lib/ExpandCollapse/Panel.js +8 -7
  53. package/lib/Feedback/Feedback.js +10 -5
  54. package/lib/Fieldset/Fieldset.js +10 -5
  55. package/lib/Fieldset/FieldsetContainer.js +10 -5
  56. package/lib/Fieldset/FieldsetContainer.native.js +10 -5
  57. package/lib/Fieldset/Legend.js +10 -5
  58. package/lib/Fieldset/Legend.native.js +10 -5
  59. package/lib/FlexGrid/Col/Col.js +8 -5
  60. package/lib/FlexGrid/FlexGrid.js +31 -6
  61. package/lib/FlexGrid/Row/Row.js +12 -5
  62. package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
  63. package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
  64. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
  65. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
  66. package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  67. package/lib/HorizontalScroll/index.js +35 -0
  68. package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  69. package/lib/Icon/Icon.js +16 -9
  70. package/lib/Icon/IconText.js +8 -7
  71. package/lib/IconButton/IconButton.js +10 -5
  72. package/lib/InputLabel/InputLabel.js +33 -5
  73. package/lib/InputLabel/LabelContent.js +22 -12
  74. package/lib/InputLabel/LabelContent.native.js +23 -5
  75. package/lib/InputSupports/InputSupports.js +10 -5
  76. package/lib/Link/ChevronLink.js +12 -5
  77. package/lib/Link/InlinePressable.js +10 -4
  78. package/lib/Link/InlinePressable.native.js +5 -4
  79. package/lib/Link/Link.js +12 -5
  80. package/lib/Link/LinkBase.js +12 -5
  81. package/lib/Link/TextButton.js +10 -5
  82. package/lib/List/List.js +6 -6
  83. package/lib/List/ListItem.js +28 -33
  84. package/lib/List/index.js +15 -0
  85. package/lib/Modal/Modal.js +10 -5
  86. package/lib/Notification/Notification.js +21 -5
  87. package/lib/Pagination/PageButton.js +16 -11
  88. package/lib/Pagination/Pagination.js +12 -7
  89. package/lib/Pagination/SideButton.js +12 -7
  90. package/lib/Pagination/usePagination.js +2 -2
  91. package/lib/Progress/Progress.js +10 -5
  92. package/lib/Progress/ProgressBar.js +21 -10
  93. package/lib/Progress/ProgressBarBackground.js +12 -8
  94. package/lib/Radio/Radio.js +14 -13
  95. package/lib/Radio/RadioButton.js +20 -9
  96. package/lib/Radio/RadioGroup.js +24 -13
  97. package/lib/RadioCard/RadioCard.js +14 -10
  98. package/lib/RadioCard/RadioCardGroup.js +13 -12
  99. package/lib/Search/Search.js +29 -18
  100. package/lib/Select/Picker.js +11 -6
  101. package/lib/Select/Picker.native.js +21 -6
  102. package/lib/Select/Select.js +46 -4
  103. package/lib/SideNav/Item.js +10 -5
  104. package/lib/SideNav/ItemsGroup.js +10 -5
  105. package/lib/SideNav/SideNav.js +11 -7
  106. package/lib/Skeleton/Skeleton.js +15 -15
  107. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  108. package/lib/Spacer/Spacer.js +19 -7
  109. package/lib/StackView/StackView.js +26 -7
  110. package/lib/StackView/StackWrap.js +24 -13
  111. package/lib/StackView/StackWrapBox.js +34 -8
  112. package/lib/StackView/StackWrapGap.js +16 -7
  113. package/lib/StackView/common.js +4 -2
  114. package/lib/StackView/getStackedContent.js +2 -2
  115. package/lib/StepTracker/StepTracker.js +10 -5
  116. package/lib/Tabs/Tabs.js +26 -19
  117. package/lib/Tabs/TabsItem.js +16 -12
  118. package/lib/Tags/Tags.js +27 -11
  119. package/lib/TextInput/TextArea.js +7 -5
  120. package/lib/TextInput/TextInput.js +12 -6
  121. package/lib/TextInput/TextInputBase.js +12 -8
  122. package/lib/ThemeProvider/ThemeProvider.js +14 -10
  123. package/lib/ThemeProvider/useSetTheme.js +6 -1
  124. package/lib/ThemeProvider/utils/styles.js +2 -2
  125. package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
  126. package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
  127. package/lib/Tooltip/Backdrop.js +10 -2
  128. package/lib/Tooltip/Tooltip.js +5 -4
  129. package/lib/Typography/Typography.js +40 -24
  130. package/lib/index.js +36 -1
  131. package/lib/utils/a11y/index.js +13 -0
  132. package/lib/utils/a11y/propTypes.js +61 -0
  133. package/lib/utils/a11y/propTypes.native.js +47 -0
  134. package/lib/utils/a11y/semantics.js +173 -0
  135. package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
  136. package/lib/utils/children.js +55 -8
  137. package/lib/utils/input.js +27 -17
  138. package/lib/utils/propTypes.js +40 -68
  139. package/lib/utils/useCopy.js +1 -1
  140. package/lib/utils/useHash.js +8 -4
  141. package/lib/utils/useSpacingScale.js +1 -3
  142. package/lib/utils/useUniqueId.js +1 -1
  143. package/package.json +14 -6
  144. package/release-context.json +4 -4
  145. package/src/A11yText/index.jsx +6 -4
  146. package/src/ActivityIndicator/Spinner.jsx +5 -3
  147. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  148. package/src/Box/Box.jsx +125 -39
  149. package/src/Button/Button.jsx +7 -4
  150. package/src/Button/ButtonBase.jsx +86 -77
  151. package/src/Button/ButtonGroup.jsx +81 -69
  152. package/src/Button/ButtonLink.jsx +18 -13
  153. package/src/Card/Card.jsx +2 -2
  154. package/src/Card/CardBase.jsx +6 -4
  155. package/src/Card/PressableCardBase.jsx +71 -64
  156. package/src/Checkbox/Checkbox.jsx +118 -108
  157. package/src/Checkbox/CheckboxGroup.jsx +72 -62
  158. package/src/Divider/Divider.jsx +7 -4
  159. package/src/ExpandCollapse/Accordion.jsx +3 -2
  160. package/src/ExpandCollapse/Control.jsx +40 -43
  161. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  162. package/src/ExpandCollapse/Panel.jsx +69 -63
  163. package/src/Feedback/Feedback.jsx +36 -33
  164. package/src/Fieldset/Fieldset.jsx +63 -56
  165. package/src/Fieldset/FieldsetContainer.jsx +14 -5
  166. package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
  167. package/src/Fieldset/Legend.jsx +7 -2
  168. package/src/Fieldset/Legend.native.jsx +7 -2
  169. package/src/FlexGrid/Col/Col.jsx +139 -132
  170. package/src/FlexGrid/FlexGrid.jsx +79 -51
  171. package/src/FlexGrid/Row/Row.jsx +55 -48
  172. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  173. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  174. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
  175. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
  176. package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  177. package/src/HorizontalScroll/index.js +17 -0
  178. package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  179. package/src/Icon/Icon.jsx +37 -35
  180. package/src/Icon/IconText.jsx +22 -17
  181. package/src/IconButton/IconButton.jsx +49 -42
  182. package/src/InputLabel/InputLabel.jsx +53 -38
  183. package/src/InputLabel/LabelContent.jsx +14 -6
  184. package/src/InputLabel/LabelContent.native.jsx +11 -2
  185. package/src/InputSupports/InputSupports.jsx +29 -34
  186. package/src/Link/ChevronLink.jsx +26 -16
  187. package/src/Link/InlinePressable.jsx +5 -3
  188. package/src/Link/InlinePressable.native.jsx +5 -3
  189. package/src/Link/Link.jsx +22 -16
  190. package/src/Link/LinkBase.jsx +67 -58
  191. package/src/Link/TextButton.jsx +30 -23
  192. package/src/List/List.jsx +6 -7
  193. package/src/List/ListItem.jsx +70 -90
  194. package/src/List/index.js +5 -0
  195. package/src/Modal/Modal.jsx +9 -4
  196. package/src/Notification/Notification.jsx +58 -43
  197. package/src/Pagination/PageButton.jsx +37 -34
  198. package/src/Pagination/Pagination.jsx +88 -92
  199. package/src/Pagination/SideButton.jsx +44 -41
  200. package/src/Progress/Progress.jsx +5 -4
  201. package/src/Progress/ProgressBar.jsx +42 -29
  202. package/src/Progress/ProgressBarBackground.jsx +5 -3
  203. package/src/Radio/Radio.jsx +85 -78
  204. package/src/Radio/RadioButton.jsx +54 -43
  205. package/src/Radio/RadioGroup.jsx +74 -63
  206. package/src/RadioCard/RadioCard.jsx +75 -68
  207. package/src/RadioCard/RadioCardGroup.jsx +82 -75
  208. package/src/Search/Search.jsx +127 -106
  209. package/src/Select/Picker.jsx +49 -42
  210. package/src/Select/Picker.native.jsx +56 -49
  211. package/src/Select/Select.jsx +115 -72
  212. package/src/SideNav/Item.jsx +53 -46
  213. package/src/SideNav/ItemsGroup.jsx +50 -43
  214. package/src/SideNav/SideNav.jsx +68 -60
  215. package/src/Skeleton/Skeleton.jsx +9 -13
  216. package/src/Spacer/Spacer.jsx +11 -4
  217. package/src/StackView/StackView.jsx +47 -23
  218. package/src/StackView/StackWrap.jsx +14 -12
  219. package/src/StackView/StackWrapBox.jsx +62 -28
  220. package/src/StackView/StackWrapGap.jsx +46 -24
  221. package/src/StackView/common.jsx +3 -2
  222. package/src/StepTracker/StepTracker.jsx +73 -62
  223. package/src/Tabs/Tabs.jsx +70 -62
  224. package/src/Tabs/TabsItem.jsx +111 -103
  225. package/src/Tags/Tags.jsx +114 -102
  226. package/src/TextInput/TextArea.jsx +5 -4
  227. package/src/TextInput/TextInput.jsx +5 -4
  228. package/src/TextInput/TextInputBase.jsx +84 -77
  229. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  230. package/src/ThemeProvider/useSetTheme.js +4 -0
  231. package/src/ThemeProvider/utils/theme-tokens.js +28 -0
  232. package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
  233. package/src/Tooltip/Tooltip.jsx +134 -130
  234. package/src/Typography/Typography.jsx +67 -44
  235. package/src/index.js +3 -1
  236. package/src/utils/a11y/index.js +1 -0
  237. package/src/utils/a11y/propTypes.js +61 -0
  238. package/src/utils/a11y/propTypes.native.js +39 -0
  239. package/src/utils/a11y/semantics.js +162 -0
  240. package/src/utils/children.jsx +60 -7
  241. package/src/utils/input.js +20 -17
  242. package/src/utils/propTypes.js +30 -76
  243. package/src/utils/useCopy.js +1 -1
  244. package/src/utils/useHash.js +8 -3
  245. package/stories/A11yText/A11yText.stories.jsx +3 -3
  246. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +2 -2
  247. package/stories/Box/Box.stories.jsx +2 -2
  248. package/stories/Button/Button.stories.jsx +3 -3
  249. package/stories/Button/ButtonGroup.stories.jsx +2 -2
  250. package/stories/Button/ButtonLink.stories.jsx +2 -2
  251. package/stories/Card/Card.stories.jsx +2 -2
  252. package/stories/Checkbox/Checkbox.stories.jsx +2 -2
  253. package/stories/Divider/Divider.stories.jsx +2 -2
  254. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +3 -3
  255. package/stories/Feedback/Feedback.stories.jsx +2 -2
  256. package/stories/FlexGrid/01 FlexGrid.stories.jsx +2 -2
  257. package/stories/FlexGrid/02 Row.stories.jsx +2 -2
  258. package/stories/FlexGrid/03 Col.stories.jsx +2 -2
  259. package/stories/Icon/Icon.stories.jsx +2 -2
  260. package/stories/IconButton/IconButton.stories.jsx +2 -2
  261. package/stories/InputLabel/InputLabel.stories.jsx +2 -2
  262. package/stories/Link/ChevronLink.stories.jsx +2 -2
  263. package/stories/Link/Link.stories.jsx +2 -2
  264. package/stories/Link/TextButton.stories.jsx +2 -2
  265. package/stories/List/List.stories.jsx +2 -2
  266. package/stories/Modal/Modal.stories.jsx +2 -2
  267. package/stories/Notification/Notification.stories.jsx +2 -2
  268. package/stories/Pagination/Pagination.stories.jsx +2 -2
  269. package/stories/Progress/Progress.stories.jsx +2 -2
  270. package/stories/Radio/Radio.stories.jsx +2 -2
  271. package/stories/RadioCard/RadioCard.stories.jsx +2 -2
  272. package/stories/Search/Search.stories.jsx +2 -2
  273. package/stories/Select/Select.stories.jsx +2 -2
  274. package/stories/SideNav/SideNav.stories.jsx +2 -2
  275. package/stories/SideNav/SideNavItem.stories.jsx +2 -2
  276. package/stories/SideNav/SideNavItemsGroup.stories.jsx +2 -2
  277. package/stories/Skeleton/Skeleton.stories.jsx +3 -3
  278. package/stories/Spacer/Spacer.stories.jsx +2 -2
  279. package/stories/StackView/StackView.stories.jsx +2 -2
  280. package/stories/StackView/StackWrap.stories.jsx +2 -2
  281. package/stories/StepTracker/StepTracker.stories.jsx +2 -2
  282. package/stories/Tabs/Tabs.stories.jsx +2 -2
  283. package/stories/Tags/Tags.stories.jsx +2 -2
  284. package/stories/TextInput/TextArea.stories.jsx +2 -2
  285. package/stories/TextInput/TextInput.stories.jsx +2 -2
  286. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +2 -2
  287. package/stories/Tooltip/Tooltip.stories.jsx +2 -2
  288. package/stories/TooltipButton/TooltipButton.stories.jsx +2 -2
  289. package/stories/Typography/Typography.stories.jsx +2 -2
  290. package/stories/platform-supports.jsx +1 -1
  291. package/stories/supports.jsx +4 -5
  292. package/src/Tabs/HorizontalScroll.jsx +0 -165
  293. package/src/Tabs/TabsScrollButton.jsx +0 -100
package/src/Box/Box.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View, ScrollView } from 'react-native'
4
4
  import { useThemeTokens } from '../ThemeProvider'
@@ -8,7 +8,9 @@ import {
8
8
  variantProp,
9
9
  getTokensPropType,
10
10
  useSpacingScale,
11
- viewProps
11
+ viewProps,
12
+ getA11yPropsFromHtmlTag,
13
+ layoutTags
12
14
  } from '../utils'
13
15
  /**
14
16
  * @typedef {import('../utils/propTypes.js').SpacingValue} SpacingValue
@@ -94,63 +96,147 @@ const selectBoxStyles = (tokens) => {
94
96
  * text content is inside a scrollable box, as screens are not scrollable by default and even very
95
97
  * short text will require scrolling on small devices at the highest accessibility text scaling settings.
96
98
  */
97
- const Box = ({
98
- space,
99
- horizontal = space,
100
- vertical = space,
101
- top = vertical,
102
- bottom = vertical,
103
- left = horizontal,
104
- right = horizontal,
105
- children,
106
- variant,
107
- tokens,
108
- scroll,
109
- testID,
110
- ...rest
111
- }) => {
112
- const props = { ...a11yProps.select(rest), ...viewProps.select(rest) }
99
+ const Box = forwardRef(
100
+ (
101
+ {
102
+ space,
103
+ horizontal = space,
104
+ vertical = space,
105
+ top = vertical,
106
+ bottom = vertical,
107
+ left = horizontal,
108
+ right = horizontal,
109
+ flex,
110
+ children,
111
+ variant,
112
+ tokens,
113
+ scroll,
114
+ tag,
115
+ accessibilityRole,
116
+ testID,
117
+ dataSet,
118
+ ...rest
119
+ },
120
+ ref
121
+ ) => {
122
+ const props = {
123
+ ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
124
+ ...a11yProps.select(rest),
125
+ ...viewProps.select(rest)
126
+ }
113
127
 
114
- const themeTokens = useThemeTokens('Box', tokens, variant)
115
- const styles = {
116
- paddingLeft: useSpacingScale(left),
117
- paddingRight: useSpacingScale(right),
118
- paddingTop: useSpacingScale(top),
119
- paddingBottom: useSpacingScale(bottom),
120
- ...selectBoxStyles(themeTokens)
121
- }
128
+ const themeTokens = useThemeTokens('Box', tokens, variant)
129
+ const styles = {
130
+ flex,
131
+ paddingLeft: useSpacingScale(left),
132
+ paddingRight: useSpacingScale(right),
133
+ paddingTop: useSpacingScale(top),
134
+ paddingBottom: useSpacingScale(bottom),
135
+ ...selectBoxStyles(themeTokens)
136
+ }
122
137
 
123
- if (scroll) {
124
- const scrollProps = typeof scroll === 'object' ? scroll : {}
125
- scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
138
+ if (scroll) {
139
+ const scrollProps = typeof scroll === 'object' ? scroll : {}
140
+ scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
141
+ return (
142
+ <ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSet} ref={ref}>
143
+ {children}
144
+ </ScrollView>
145
+ )
146
+ }
126
147
  return (
127
- <ScrollView {...scrollProps} {...props} testID={testID}>
148
+ <View {...props} style={styles} testID={testID} dataSet={dataSet} ref={ref}>
128
149
  {children}
129
- </ScrollView>
150
+ </View>
130
151
  )
131
152
  }
132
- return (
133
- <View {...props} style={styles} testID={testID}>
134
- {children}
135
- </View>
136
- )
137
- }
153
+ )
154
+ Box.displayName = 'Box'
138
155
 
139
156
  Box.propTypes = {
157
+ ...a11yProps.propTypes,
158
+ ...viewProps.types,
159
+ variant: variantProp.propType,
160
+ tokens: getTokensPropType('Box'),
161
+ /**
162
+ * Sets default padding on all sides of the box using the theme's spacing scale.
163
+ *
164
+ * @see {@link SpacingValue}
165
+ */
140
166
  space: spacingProps.types.spacingValue,
167
+ /**
168
+ * Sets top and bottom padding using the theme's spacing scale.
169
+ *
170
+ * @see {@link SpacingValue}
171
+ */
141
172
  vertical: spacingProps.types.spacingValue,
173
+ /**
174
+ * Sets left and right padding using the theme's spacing scale.
175
+ *
176
+ * @see {@link SpacingValue}
177
+ */
142
178
  horizontal: spacingProps.types.spacingValue,
179
+ /**
180
+ * Sets bottom padding using the theme's spacing scale.
181
+ *
182
+ * @see {@link SpacingValue}
183
+ */
143
184
  bottom: spacingProps.types.spacingValue,
185
+ /**
186
+ * Sets left padding using the theme's spacing scale.
187
+ *
188
+ * @see {@link SpacingValue}
189
+ */
144
190
  left: spacingProps.types.spacingValue,
191
+ /**
192
+ * Sets right padding using the theme's spacing scale.
193
+ *
194
+ * @see {@link SpacingValue}
195
+ */
145
196
  right: spacingProps.types.spacingValue,
197
+ /**
198
+ * Sets top padding using the theme's spacing scale.
199
+ *
200
+ * @see {@link SpacingValue}
201
+ */
146
202
  top: spacingProps.types.spacingValue,
203
+ /**
204
+ * Sets the `flex` style, which controls `flexGrow`, `flexShrink` and `flexBasis` styles.
205
+ *
206
+ * Set as 1 for the box to stretch and grow to fit in the available space, or another number to share
207
+ * space disproportionately with siblings. See https://reactnative.dev/docs/flexbox#flex for details.
208
+ *
209
+ * With the default `0`, the box takes its minimum width from its content.
210
+ */
211
+ flex: PropTypes.number,
212
+ /**
213
+ * Renders a scrollable ScrollView instead of an unscrollable View.
214
+ *
215
+ * May take an object of ScrollView props which are passed to the rendered ScrollView.
216
+ */
147
217
  scroll: PropTypes.oneOfType([
148
218
  PropTypes.bool,
149
219
  ScrollView.propTypes ? PropTypes.shape(ScrollView.propTypes) : PropTypes.object
150
220
  ]),
151
- variant: variantProp.propType,
152
- tokens: getTokensPropType('Box'),
221
+ /**
222
+ * Optional semantic HTML tag, to apply to the container on web. Does not change styling.
223
+ *
224
+ * In native apps, if a header tag is provided ("h1", "h2" etc) and accessibilityRole prop
225
+ * is not defined, the accessibilityRole will default to "heading".
226
+ */
227
+ tag: PropTypes.oneOf(layoutTags),
228
+ /**
229
+ * Use in tests if the box itself needs to be targetted and no other way of selecting the
230
+ * box is available (e.g. it cannot be targetted using an appropriate `accessibilityRole`).
231
+ */
153
232
  testID: PropTypes.string,
233
+ /**
234
+ * @ignore
235
+ */
236
+ dataSet: PropTypes.object,
237
+ /**
238
+ * Box accepts any content as children.
239
+ */
154
240
  children: PropTypes.node.isRequired
155
241
  }
156
242
 
@@ -1,14 +1,17 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
 
3
3
  import ButtonBase from './ButtonBase'
4
4
  import buttonPropTypes, { textAndA11yText } from './propTypes'
5
5
  import { useThemeTokensCallback } from '../ThemeProvider'
6
6
  import { a11yProps } from '../utils/propTypes'
7
7
 
8
- const Button = ({ accessibilityRole = 'button', tokens, variant, ...props }) => {
8
+ const Button = forwardRef(({ accessibilityRole = 'button', tokens, variant, ...props }, ref) => {
9
9
  const getTokens = useThemeTokensCallback('Button', tokens, variant)
10
- return <ButtonBase {...props} tokens={getTokens} accessibilityRole={accessibilityRole} />
11
- }
10
+ return (
11
+ <ButtonBase {...props} tokens={getTokens} accessibilityRole={accessibilityRole} ref={ref} />
12
+ )
13
+ })
14
+ Button.displayName = 'Button'
12
15
 
13
16
  Button.propTypes = {
14
17
  ...a11yProps.types,
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import { Pressable, View, StyleSheet, Platform } from 'react-native'
3
3
 
4
4
  import { applyTextStyles, applyShadowToken, applyOuterBorder } from '../ThemeProvider/utils'
@@ -130,83 +130,92 @@ const selectWebOnlyStyles = (inactive, themeTokens, { accessibilityRole }) => {
130
130
  })
131
131
  }
132
132
 
133
- const ButtonBase = ({
134
- href,
135
- hrefAttrs,
136
- children,
137
- onPress,
138
- tokens = {},
139
- disabled = false, // alias for inactive
140
- inactive = disabled,
141
- selected = false,
142
- ...rest
143
- }) => {
144
- const extraButtonState = { inactive, selected }
145
- const resolveButtonTokens = (pressableState) =>
146
- resolvePressableTokens(tokens, pressableState, extraButtonState)
147
-
148
- const a11y = a11yProps.select(rest)
149
-
150
- const getPressableStyle = (pressableState) => {
151
- const themeTokens = resolveButtonTokens(pressableState)
152
- return [
153
- staticStyles.row,
154
- selectWebOnlyStyles(inactive, themeTokens, a11y),
155
- selectOuterContainerStyles(themeTokens),
156
- selectOuterWidthStyles(themeTokens)
157
- ]
158
- }
133
+ const ButtonBase = forwardRef(
134
+ (
135
+ {
136
+ href,
137
+ hrefAttrs,
138
+ children,
139
+ onPress,
140
+ tokens = {},
141
+ disabled = false, // alias for inactive
142
+ inactive = disabled,
143
+ selected = false,
144
+ dataSet,
145
+ ...rest
146
+ },
147
+ ref
148
+ ) => {
149
+ const extraButtonState = { inactive, selected }
150
+ const resolveButtonTokens = (pressableState) =>
151
+ resolvePressableTokens(tokens, pressableState, extraButtonState)
152
+
153
+ const a11y = a11yProps.select(rest)
154
+
155
+ const getPressableStyle = (pressableState) => {
156
+ const themeTokens = resolveButtonTokens(pressableState)
157
+ return [
158
+ staticStyles.row,
159
+ selectWebOnlyStyles(inactive, themeTokens, a11y),
160
+ selectOuterContainerStyles(themeTokens),
161
+ selectOuterWidthStyles(themeTokens)
162
+ ]
163
+ }
159
164
 
160
- return (
161
- <Pressable
162
- href={href}
163
- onPress={linkProps.handleHref({ href, onPress })}
164
- style={getPressableStyle}
165
- disabled={inactive}
166
- hrefAttrs={hrefAttrs}
167
- {...a11y}
168
- >
169
- {(pressableState) => {
170
- const themeTokens = resolveButtonTokens(pressableState)
171
- const containerStyles = selectInnerContainerStyles(themeTokens)
172
- const borderStyles = selectBorderStyles(themeTokens)
173
- const textStyles = [selectTextStyles(themeTokens), staticStyles.text]
174
-
175
- // If the container has a width set, fill it instead of sizing from content.
176
- // If in future we support text alignments other than center, add here.
177
- const stretchStyles = themeTokens.width ? staticStyles.stretch : staticStyles.align
178
-
179
- return (
180
- <View
181
- style={[
182
- containerStyles,
183
- borderStyles,
184
- stretchStyles,
185
- staticStyles.row,
186
- Platform.select({
187
- web: {
188
- maxWidth: '100%', // ensure overflowing content wraps
189
- // TODO: https://github.com/telus/universal-design-system/issues/487
190
- transition: 'background-color 200ms, border-color 200ms'
191
- }
192
- })
193
- ]}
194
- >
195
- {wrapStringsInText(
196
- typeof children === 'function'
197
- ? children({
198
- ...resolvePressableState(pressableState, extraButtonState),
199
- textStyles
200
- })
201
- : children,
202
- { style: textStyles }
203
- )}
204
- </View>
205
- )
206
- }}
207
- </Pressable>
208
- )
209
- }
165
+ return (
166
+ <Pressable
167
+ ref={ref}
168
+ href={href}
169
+ onPress={linkProps.handleHref({ href, onPress })}
170
+ style={getPressableStyle}
171
+ disabled={inactive}
172
+ dataSet={dataSet}
173
+ hrefAttrs={hrefAttrs}
174
+ {...a11y}
175
+ >
176
+ {(pressableState) => {
177
+ const themeTokens = resolveButtonTokens(pressableState)
178
+ const containerStyles = selectInnerContainerStyles(themeTokens)
179
+ const borderStyles = selectBorderStyles(themeTokens)
180
+ const textStyles = [selectTextStyles(themeTokens), staticStyles.text]
181
+
182
+ // If the container has a width set, fill it instead of sizing from content.
183
+ // If in future we support text alignments other than center, add here.
184
+ const stretchStyles = themeTokens.width ? staticStyles.stretch : staticStyles.align
185
+
186
+ return (
187
+ <View
188
+ style={[
189
+ containerStyles,
190
+ borderStyles,
191
+ stretchStyles,
192
+ staticStyles.row,
193
+ Platform.select({
194
+ web: {
195
+ maxWidth: '100%', // ensure overflowing content wraps
196
+ // TODO: https://github.com/telus/universal-design-system/issues/487
197
+ transition: 'background-color 200ms, border-color 200ms'
198
+ }
199
+ })
200
+ ]}
201
+ >
202
+ {wrapStringsInText(
203
+ typeof children === 'function'
204
+ ? children({
205
+ ...resolvePressableState(pressableState, extraButtonState),
206
+ textStyles
207
+ })
208
+ : children,
209
+ { style: textStyles }
210
+ )}
211
+ </View>
212
+ )
213
+ }}
214
+ </Pressable>
215
+ )
216
+ }
217
+ )
218
+ ButtonBase.displayName = 'ButtonBase'
210
219
 
211
220
  ButtonBase.propTypes = {
212
221
  ...a11yProps.types,
@@ -1,5 +1,6 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
+ import ABBPropTypes from 'airbnb-prop-types'
3
4
  import { Platform } from 'react-native'
4
5
 
5
6
  import ButtonBase from './ButtonBase'
@@ -16,81 +17,88 @@ import {
16
17
  import { useMultipleInputValues } from '../utils/input'
17
18
  import { getPressHandlersWithArgs } from '../utils/pressability'
18
19
 
19
- const ButtonGroup = ({
20
- variant,
21
- tokens,
22
- items = [],
23
- values,
24
- initialValues,
25
- maxValues = 1,
26
- onChange,
27
- readOnly = false,
28
- inactive = false,
29
- accessibilityRole = maxValues === 1
30
- ? 'radiogroup' // radiogroup is cross-platform; only web aria has generic groups
31
- : Platform.select({ web: 'group', default: 'none' }),
32
- ...rest
33
- }) => {
34
- const viewport = useViewport()
35
- const themeTokens = useThemeTokens('ButtonGroup', tokens, variant, { viewport })
36
- const stackTokens = selectTokens('StackView', themeTokens)
37
- const { direction, space } = themeTokens
20
+ const ButtonGroup = forwardRef(
21
+ (
22
+ {
23
+ variant,
24
+ tokens,
25
+ items = [],
26
+ values,
27
+ initialValues,
28
+ maxValues = 1,
29
+ onChange,
30
+ readOnly = false,
31
+ inactive = false,
32
+ accessibilityRole = maxValues === 1
33
+ ? 'radiogroup' // radiogroup is cross-platform; only web aria has generic groups
34
+ : Platform.select({ web: 'group', default: 'none' }),
35
+ ...rest
36
+ },
37
+ ref
38
+ ) => {
39
+ const viewport = useViewport()
40
+ const themeTokens = useThemeTokens('ButtonGroup', tokens, variant, { viewport })
41
+ const stackTokens = selectTokens('StackView', themeTokens)
42
+ const { direction, space } = themeTokens
38
43
 
39
- const getButtonTokens = useThemeTokensCallback('ButtonGroupItem', tokens, variant)
44
+ const getButtonTokens = useThemeTokensCallback('ButtonGroupItem', tokens, variant)
40
45
 
41
- const { currentValues, toggleOneValue } = useMultipleInputValues({
42
- initialValues,
43
- values,
44
- maxValues,
45
- onChange,
46
- readOnly
47
- })
46
+ const { currentValues, toggleOneValue } = useMultipleInputValues({
47
+ initialValues,
48
+ values,
49
+ maxValues,
50
+ onChange,
51
+ readOnly
52
+ })
48
53
 
49
- const a11y = a11yProps.select({
50
- accessibilityRole,
51
- ...rest
52
- })
53
- const itemA11yRole = a11y.accessibilityRole === 'radiogroup' ? 'radio' : 'checkbox'
54
+ const a11y = a11yProps.select({
55
+ accessibilityRole,
56
+ ...rest
57
+ })
58
+ const itemA11yRole = a11y.accessibilityRole === 'radiogroup' ? 'radio' : 'checkbox'
54
59
 
55
- return (
56
- <StackWrap {...a11y} space={space} direction={direction} tokens={stackTokens}>
57
- {items.map(({ label, id = label, accessibilityLabel }, index) => {
58
- const isSelected = currentValues.includes(id)
60
+ return (
61
+ <StackWrap {...a11y} space={space} direction={direction} tokens={stackTokens} ref={ref}>
62
+ {items.map(({ label, id = label, accessibilityLabel, ref: itemRef }, index) => {
63
+ const isSelected = currentValues.includes(id)
59
64
 
60
- // Pass an object of relevant component state as first argument for any passed-in press handlers
61
- const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
65
+ // Pass an object of relevant component state as first argument for any passed-in press handlers
66
+ const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
62
67
 
63
- const handlePress = () => {
64
- if (pressHandlers.onPress) pressHandlers.onPress()
65
- toggleOneValue(id)
66
- }
68
+ const handlePress = (event) => {
69
+ if (pressHandlers.onPress) pressHandlers.onPress()
70
+ toggleOneValue(id, event)
71
+ }
67
72
 
68
- const itemA11y = {
69
- accessibilityState: { checked: isSelected },
70
- accessibilityRole: itemA11yRole,
71
- accessibilityLabel,
72
- ...a11yProps.getPositionInSet(items.length, index)
73
- }
73
+ const itemA11y = {
74
+ accessibilityState: { checked: isSelected },
75
+ accessibilityRole: itemA11yRole,
76
+ accessibilityLabel,
77
+ ...a11yProps.getPositionInSet(items.length, index)
78
+ }
74
79
 
75
- // Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
76
- // "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
77
- return (
78
- <ButtonBase
79
- key={id}
80
- {...pressHandlers}
81
- onPress={handlePress}
82
- tokens={getButtonTokens}
83
- selected={isSelected}
84
- inactive={inactive}
85
- {...itemA11y}
86
- >
87
- {label}
88
- </ButtonBase>
89
- )
90
- })}
91
- </StackWrap>
92
- )
93
- }
80
+ // Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
81
+ // "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
82
+ return (
83
+ <ButtonBase
84
+ ref={itemRef}
85
+ key={id}
86
+ {...pressHandlers}
87
+ onPress={handlePress}
88
+ tokens={getButtonTokens}
89
+ selected={isSelected}
90
+ inactive={inactive}
91
+ {...itemA11y}
92
+ >
93
+ {label}
94
+ </ButtonBase>
95
+ )
96
+ })}
97
+ </StackWrap>
98
+ )
99
+ }
100
+ )
101
+ ButtonGroup.displayName = 'ButtonGroup'
94
102
 
95
103
  ButtonGroup.propTypes = {
96
104
  ...a11yProps.propTypes,
@@ -122,7 +130,11 @@ ButtonGroup.propTypes = {
122
130
  * which will be used in code and passed to any onChange function.
123
131
  * If not provided, the label is used.
124
132
  */
125
- id: PropTypes.string
133
+ id: PropTypes.string,
134
+ /**
135
+ * An optional ref for one individual button in the ButtonGroup
136
+ */
137
+ ref: ABBPropTypes.ref()
126
138
  })
127
139
  ),
128
140
  /**
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import ButtonBase from './ButtonBase'
3
3
  import buttonPropTypes, { textAndA11yText } from './propTypes'
4
4
  import { a11yProps, hrefAttrsProp, linkProps } from '../utils/propTypes'
@@ -8,18 +8,23 @@ import { useThemeTokensCallback } from '../ThemeProvider'
8
8
  * `ButtonLink` is a component with the semantics and behaviour of a link, but with the visual appearance of a button.
9
9
  * ButtonLink is a block-level component and should not be used inline.
10
10
  */
11
- const ButtonLink = ({ accessibilityRole = 'link', tokens, variant, ...props }) => {
12
- const { hrefAttrs, rest } = hrefAttrsProp.bundle(props)
13
- const getTokens = useThemeTokensCallback('Button', tokens, variant)
14
- return (
15
- <ButtonBase
16
- accessibilityRole={accessibilityRole}
17
- tokens={getTokens}
18
- hrefAttrs={hrefAttrs}
19
- {...rest}
20
- />
21
- )
22
- }
11
+ const ButtonLink = forwardRef(
12
+ ({ accessibilityRole = 'link', tokens, variant, dataSet, ...props }, ref) => {
13
+ const { hrefAttrs, rest } = hrefAttrsProp.bundle(props)
14
+ const getTokens = useThemeTokensCallback('Button', tokens, variant)
15
+ return (
16
+ <ButtonBase
17
+ ref={ref}
18
+ accessibilityRole={accessibilityRole}
19
+ tokens={getTokens}
20
+ hrefAttrs={hrefAttrs}
21
+ dataSet={dataSet}
22
+ {...rest}
23
+ />
24
+ )
25
+ }
26
+ )
27
+ ButtonLink.displayName = 'ButtonLink'
23
28
 
24
29
  ButtonLink.propTypes = {
25
30
  ...a11yProps.types,
package/src/Card/Card.jsx CHANGED
@@ -55,11 +55,11 @@ import CardBase from './CardBase'
55
55
  * you automatically make inaccessible its children, which may or may not be appropriate
56
56
  * depending on what you are trying to achieve.
57
57
  */
58
- const Card = ({ children, tokens, variant, ...rest }) => {
58
+ const Card = ({ children, tokens, variant, dataSet, ...rest }) => {
59
59
  const viewport = useViewport()
60
60
  const themeTokens = useThemeTokens('Card', tokens, variant, { viewport })
61
61
  return (
62
- <CardBase tokens={themeTokens} {...rest}>
62
+ <CardBase tokens={themeTokens} dataSet={dataSet} {...rest}>
63
63
  {children}
64
64
  </CardBase>
65
65
  )
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View } from 'react-native'
4
4
 
@@ -37,20 +37,22 @@ const selectStyles = ({
37
37
  * A themeless base component for Card which components can apply theme tokens to. Not
38
38
  * intended to be used in apps or sites directly: build themed components on top of this.
39
39
  */
40
- const CardBase = ({ children, tokens, ...rest }) => {
40
+ const CardBase = forwardRef(({ children, tokens, dataSet, ...rest }, ref) => {
41
41
  const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens)
42
42
  const props = { ...a11yProps.select(rest), ...viewProps.select(rest) }
43
43
 
44
44
  return (
45
- <View style={cardStyle} {...props}>
45
+ <View style={cardStyle} dataSet={dataSet} ref={ref} {...props}>
46
46
  {children}
47
47
  </View>
48
48
  )
49
- }
49
+ })
50
+ CardBase.displayName = 'CardBase'
50
51
 
51
52
  CardBase.propTypes = {
52
53
  children: PropTypes.node,
53
54
  tokens: getTokensPropType('Card'),
55
+ ...viewProps.types,
54
56
  ...a11yProps.types
55
57
  }
56
58