@telus-uds/components-base 0.0.2-prerelease.9 → 1.0.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 (282) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.ultra.cache.json +1 -1
  3. package/CHANGELOG.md +32 -0
  4. package/README.md +4 -2
  5. package/__fixtures__/test-utils.js +25 -0
  6. package/__fixtures__/testTheme.js +4 -2
  7. package/__tests__/Button/ButtonGroup.test.jsx +4 -5
  8. package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
  9. package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
  10. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  11. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  12. package/__tests__/Link/LinkBase.test.jsx +0 -14
  13. package/__tests__/Radio/Radio.test.jsx +2 -2
  14. package/__tests__/Radio/RadioGroup.test.jsx +4 -5
  15. package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
  16. package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
  17. package/__tests__/Search/Search.test.jsx +9 -8
  18. package/__tests__/Select/Select.test.jsx +3 -2
  19. package/__tests__/Tabs/Tabs.test.jsx +1 -161
  20. package/__tests__/Tags/Tags.test.jsx +4 -5
  21. package/__tests__/TextInput/TextArea.test.jsx +3 -2
  22. package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
  23. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  24. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  25. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  26. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  27. package/__tests__/utils/children.test.jsx +128 -0
  28. package/__tests__/utils/input.test.js +1 -1
  29. package/__tests__/utils/semantics.test.jsx +43 -0
  30. package/lib/A11yText/index.js +10 -5
  31. package/lib/ActivityIndicator/Spinner.js +16 -13
  32. package/lib/ActivityIndicator/Spinner.native.js +12 -8
  33. package/lib/Box/Box.js +102 -8
  34. package/lib/Button/Button.js +9 -8
  35. package/lib/Button/ButtonBase.js +14 -7
  36. package/lib/Button/ButtonGroup.js +25 -10
  37. package/lib/Button/ButtonLink.js +10 -7
  38. package/lib/Card/Card.js +2 -0
  39. package/lib/Card/CardBase.js +12 -5
  40. package/lib/Card/PressableCardBase.js +12 -8
  41. package/lib/Checkbox/Checkbox.js +25 -14
  42. package/lib/Checkbox/CheckboxGroup.js +22 -12
  43. package/lib/Divider/Divider.js +12 -7
  44. package/lib/ExpandCollapse/Accordion.js +10 -4
  45. package/lib/ExpandCollapse/Control.js +12 -6
  46. package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
  47. package/lib/ExpandCollapse/Panel.js +8 -7
  48. package/lib/Feedback/Feedback.js +10 -5
  49. package/lib/Fieldset/Fieldset.js +10 -5
  50. package/lib/Fieldset/FieldsetContainer.js +10 -5
  51. package/lib/Fieldset/FieldsetContainer.native.js +10 -5
  52. package/lib/Fieldset/Legend.js +10 -5
  53. package/lib/Fieldset/Legend.native.js +10 -5
  54. package/lib/FlexGrid/Col/Col.js +8 -5
  55. package/lib/FlexGrid/FlexGrid.js +31 -6
  56. package/lib/FlexGrid/Row/Row.js +12 -5
  57. package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
  58. package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
  59. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
  60. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
  61. package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  62. package/lib/HorizontalScroll/index.js +35 -0
  63. package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  64. package/lib/Icon/Icon.js +16 -9
  65. package/lib/Icon/IconText.js +8 -7
  66. package/lib/IconButton/IconButton.js +10 -5
  67. package/lib/InputLabel/InputLabel.js +33 -5
  68. package/lib/InputLabel/LabelContent.js +22 -12
  69. package/lib/InputLabel/LabelContent.native.js +23 -5
  70. package/lib/InputSupports/InputSupports.js +10 -5
  71. package/lib/Link/ChevronLink.js +12 -5
  72. package/lib/Link/InlinePressable.js +10 -4
  73. package/lib/Link/InlinePressable.native.js +5 -4
  74. package/lib/Link/Link.js +12 -5
  75. package/lib/Link/LinkBase.js +12 -5
  76. package/lib/Link/TextButton.js +10 -5
  77. package/lib/List/List.js +5 -4
  78. package/lib/List/ListItem.js +16 -8
  79. package/lib/Modal/Modal.js +10 -5
  80. package/lib/Notification/Notification.js +21 -5
  81. package/lib/Pagination/PageButton.js +21 -10
  82. package/lib/Pagination/Pagination.js +12 -7
  83. package/lib/Pagination/SideButton.js +12 -7
  84. package/lib/Pagination/usePagination.js +2 -2
  85. package/lib/Progress/Progress.js +10 -5
  86. package/lib/Progress/ProgressBar.js +21 -10
  87. package/lib/Progress/ProgressBarBackground.js +12 -8
  88. package/lib/Radio/Radio.js +14 -13
  89. package/lib/Radio/RadioButton.js +20 -9
  90. package/lib/Radio/RadioGroup.js +24 -13
  91. package/lib/RadioCard/RadioCard.js +14 -10
  92. package/lib/RadioCard/RadioCardGroup.js +13 -12
  93. package/lib/Search/Search.js +29 -18
  94. package/lib/Select/Picker.js +11 -6
  95. package/lib/Select/Picker.native.js +21 -6
  96. package/lib/Select/Select.js +46 -4
  97. package/lib/SideNav/Item.js +10 -5
  98. package/lib/SideNav/ItemsGroup.js +10 -5
  99. package/lib/SideNav/SideNav.js +11 -7
  100. package/lib/Skeleton/Skeleton.js +15 -15
  101. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  102. package/lib/Spacer/Spacer.js +19 -7
  103. package/lib/StackView/StackView.js +25 -7
  104. package/lib/StackView/StackWrap.js +16 -9
  105. package/lib/StackView/StackWrapBox.js +33 -8
  106. package/lib/StackView/StackWrapGap.js +16 -7
  107. package/lib/StackView/common.js +4 -2
  108. package/lib/StackView/getStackedContent.js +2 -2
  109. package/lib/StepTracker/StepTracker.js +10 -5
  110. package/lib/Tabs/Tabs.js +26 -19
  111. package/lib/Tabs/TabsItem.js +16 -12
  112. package/lib/Tags/Tags.js +27 -11
  113. package/lib/TextInput/TextArea.js +7 -5
  114. package/lib/TextInput/TextInput.js +12 -6
  115. package/lib/TextInput/TextInputBase.js +12 -8
  116. package/lib/ThemeProvider/ThemeProvider.js +14 -10
  117. package/lib/ThemeProvider/useSetTheme.js +6 -1
  118. package/lib/ThemeProvider/utils/styles.js +2 -2
  119. package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
  120. package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
  121. package/lib/Tooltip/Backdrop.js +10 -2
  122. package/lib/Tooltip/Tooltip.js +5 -4
  123. package/lib/Typography/Typography.js +40 -24
  124. package/lib/index.js +21 -0
  125. package/lib/utils/a11y/index.js +13 -0
  126. package/lib/utils/a11y/semantics.js +173 -0
  127. package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
  128. package/lib/utils/children.js +55 -8
  129. package/lib/utils/input.js +27 -17
  130. package/lib/utils/propTypes.js +82 -29
  131. package/lib/utils/useCopy.js +1 -1
  132. package/lib/utils/useHash.js +8 -4
  133. package/lib/utils/useSpacingScale.js +1 -3
  134. package/lib/utils/useUniqueId.js +1 -1
  135. package/package.json +9 -5
  136. package/release-context.json +4 -4
  137. package/src/A11yText/index.jsx +6 -4
  138. package/src/ActivityIndicator/Spinner.jsx +5 -3
  139. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  140. package/src/Box/Box.jsx +124 -39
  141. package/src/Button/Button.jsx +7 -4
  142. package/src/Button/ButtonBase.jsx +86 -77
  143. package/src/Button/ButtonGroup.jsx +81 -69
  144. package/src/Button/ButtonLink.jsx +18 -13
  145. package/src/Card/Card.jsx +2 -2
  146. package/src/Card/CardBase.jsx +5 -4
  147. package/src/Card/PressableCardBase.jsx +71 -64
  148. package/src/Checkbox/Checkbox.jsx +118 -108
  149. package/src/Checkbox/CheckboxGroup.jsx +72 -62
  150. package/src/Divider/Divider.jsx +7 -4
  151. package/src/ExpandCollapse/Accordion.jsx +3 -2
  152. package/src/ExpandCollapse/Control.jsx +40 -43
  153. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  154. package/src/ExpandCollapse/Panel.jsx +69 -63
  155. package/src/Feedback/Feedback.jsx +36 -33
  156. package/src/Fieldset/Fieldset.jsx +63 -56
  157. package/src/Fieldset/FieldsetContainer.jsx +14 -5
  158. package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
  159. package/src/Fieldset/Legend.jsx +7 -2
  160. package/src/Fieldset/Legend.native.jsx +7 -2
  161. package/src/FlexGrid/Col/Col.jsx +139 -132
  162. package/src/FlexGrid/FlexGrid.jsx +79 -51
  163. package/src/FlexGrid/Row/Row.jsx +55 -48
  164. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  165. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  166. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
  167. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
  168. package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  169. package/src/HorizontalScroll/index.js +17 -0
  170. package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  171. package/src/Icon/Icon.jsx +37 -35
  172. package/src/Icon/IconText.jsx +22 -17
  173. package/src/IconButton/IconButton.jsx +49 -42
  174. package/src/InputLabel/InputLabel.jsx +53 -38
  175. package/src/InputLabel/LabelContent.jsx +14 -6
  176. package/src/InputLabel/LabelContent.native.jsx +11 -2
  177. package/src/InputSupports/InputSupports.jsx +29 -34
  178. package/src/Link/ChevronLink.jsx +26 -16
  179. package/src/Link/InlinePressable.jsx +5 -3
  180. package/src/Link/InlinePressable.native.jsx +5 -3
  181. package/src/Link/Link.jsx +22 -16
  182. package/src/Link/LinkBase.jsx +67 -58
  183. package/src/Link/TextButton.jsx +30 -23
  184. package/src/List/List.jsx +5 -4
  185. package/src/List/ListItem.jsx +77 -82
  186. package/src/Modal/Modal.jsx +9 -4
  187. package/src/Notification/Notification.jsx +58 -43
  188. package/src/Pagination/PageButton.jsx +42 -35
  189. package/src/Pagination/Pagination.jsx +88 -92
  190. package/src/Pagination/SideButton.jsx +44 -41
  191. package/src/Progress/Progress.jsx +5 -4
  192. package/src/Progress/ProgressBar.jsx +42 -29
  193. package/src/Progress/ProgressBarBackground.jsx +5 -3
  194. package/src/Radio/Radio.jsx +85 -78
  195. package/src/Radio/RadioButton.jsx +54 -43
  196. package/src/Radio/RadioGroup.jsx +74 -63
  197. package/src/RadioCard/RadioCard.jsx +75 -68
  198. package/src/RadioCard/RadioCardGroup.jsx +82 -75
  199. package/src/Search/Search.jsx +127 -106
  200. package/src/Select/Picker.jsx +49 -42
  201. package/src/Select/Picker.native.jsx +56 -49
  202. package/src/Select/Select.jsx +115 -72
  203. package/src/SideNav/Item.jsx +53 -46
  204. package/src/SideNav/ItemsGroup.jsx +50 -43
  205. package/src/SideNav/SideNav.jsx +68 -60
  206. package/src/Skeleton/Skeleton.jsx +9 -13
  207. package/src/Spacer/Spacer.jsx +11 -4
  208. package/src/StackView/StackView.jsx +46 -23
  209. package/src/StackView/StackWrap.jsx +7 -6
  210. package/src/StackView/StackWrapBox.jsx +61 -28
  211. package/src/StackView/StackWrapGap.jsx +46 -24
  212. package/src/StackView/common.jsx +3 -2
  213. package/src/StepTracker/StepTracker.jsx +73 -62
  214. package/src/Tabs/Tabs.jsx +70 -62
  215. package/src/Tabs/TabsItem.jsx +111 -103
  216. package/src/Tags/Tags.jsx +114 -102
  217. package/src/TextInput/TextArea.jsx +5 -4
  218. package/src/TextInput/TextInput.jsx +5 -4
  219. package/src/TextInput/TextInputBase.jsx +84 -77
  220. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  221. package/src/ThemeProvider/useSetTheme.js +4 -0
  222. package/src/ThemeProvider/utils/theme-tokens.js +28 -0
  223. package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
  224. package/src/Tooltip/Tooltip.jsx +134 -130
  225. package/src/Typography/Typography.jsx +67 -44
  226. package/src/index.js +2 -0
  227. package/src/utils/a11y/index.js +1 -0
  228. package/src/utils/a11y/semantics.js +162 -0
  229. package/src/utils/children.jsx +60 -7
  230. package/src/utils/input.js +20 -17
  231. package/src/utils/propTypes.js +101 -39
  232. package/src/utils/useCopy.js +1 -1
  233. package/src/utils/useHash.js +8 -3
  234. package/stories/A11yText/A11yText.stories.jsx +2 -2
  235. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +1 -1
  236. package/stories/Box/Box.stories.jsx +1 -1
  237. package/stories/Button/Button.stories.jsx +2 -2
  238. package/stories/Button/ButtonGroup.stories.jsx +1 -1
  239. package/stories/Button/ButtonLink.stories.jsx +1 -1
  240. package/stories/Card/Card.stories.jsx +1 -1
  241. package/stories/Checkbox/Checkbox.stories.jsx +1 -1
  242. package/stories/Divider/Divider.stories.jsx +1 -1
  243. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +2 -2
  244. package/stories/Feedback/Feedback.stories.jsx +1 -1
  245. package/stories/FlexGrid/01 FlexGrid.stories.jsx +1 -1
  246. package/stories/FlexGrid/02 Row.stories.jsx +1 -1
  247. package/stories/FlexGrid/03 Col.stories.jsx +1 -1
  248. package/stories/Icon/Icon.stories.jsx +1 -1
  249. package/stories/IconButton/IconButton.stories.jsx +1 -1
  250. package/stories/InputLabel/InputLabel.stories.jsx +1 -1
  251. package/stories/Link/ChevronLink.stories.jsx +1 -1
  252. package/stories/Link/Link.stories.jsx +1 -1
  253. package/stories/Link/TextButton.stories.jsx +1 -1
  254. package/stories/List/List.stories.jsx +1 -1
  255. package/stories/Modal/Modal.stories.jsx +1 -1
  256. package/stories/Notification/Notification.stories.jsx +1 -1
  257. package/stories/Pagination/Pagination.stories.jsx +1 -1
  258. package/stories/Progress/Progress.stories.jsx +1 -1
  259. package/stories/Radio/Radio.stories.jsx +1 -1
  260. package/stories/RadioCard/RadioCard.stories.jsx +1 -1
  261. package/stories/Search/Search.stories.jsx +1 -1
  262. package/stories/Select/Select.stories.jsx +1 -1
  263. package/stories/SideNav/SideNav.stories.jsx +1 -1
  264. package/stories/SideNav/SideNavItem.stories.jsx +1 -1
  265. package/stories/SideNav/SideNavItemsGroup.stories.jsx +1 -1
  266. package/stories/Skeleton/Skeleton.stories.jsx +2 -2
  267. package/stories/Spacer/Spacer.stories.jsx +1 -1
  268. package/stories/StackView/StackView.stories.jsx +1 -1
  269. package/stories/StackView/StackWrap.stories.jsx +1 -1
  270. package/stories/StepTracker/StepTracker.stories.jsx +1 -1
  271. package/stories/Tabs/Tabs.stories.jsx +1 -1
  272. package/stories/Tags/Tags.stories.jsx +1 -1
  273. package/stories/TextInput/TextArea.stories.jsx +1 -1
  274. package/stories/TextInput/TextInput.stories.jsx +1 -1
  275. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +1 -1
  276. package/stories/Tooltip/Tooltip.stories.jsx +1 -1
  277. package/stories/TooltipButton/TooltipButton.stories.jsx +1 -1
  278. package/stories/Typography/Typography.stories.jsx +1 -1
  279. package/stories/platform-supports.jsx +1 -1
  280. package/stories/supports.jsx +2 -2
  281. package/src/Tabs/HorizontalScroll.jsx +0 -165
  282. package/src/Tabs/TabsScrollButton.jsx +0 -100
@@ -1,5 +1,6 @@
1
1
  import React, { Children, Fragment } from 'react'
2
2
  import { Text } from 'react-native'
3
+ import A11yText from '../A11yText'
3
4
 
4
5
  /**
5
6
  * Unpacks top-level fragments, so that common compositional patterns such as the following examples
@@ -44,23 +45,75 @@ export const unpackFragment = (child) => {
44
45
  return child
45
46
  }
46
47
 
48
+ const isStringOrNumber = (child) => typeof child === 'string' || typeof child === 'number'
49
+ // Wrap an A11yText with neighouring text strings so it doesn't split them into multiple <Text>s
50
+ const isWrapable = (child) => isStringOrNumber(child) || child.type === A11yText
51
+ const combineKeys = (childrenArray) =>
52
+ childrenArray.reduce((newKey, child) => `${newKey}${child.key || ''}`, '')
53
+
54
+ // Group wrappable children for one `<Text>` parent, merging adjacent text nodes
55
+ const wrapChild = (child, wrappedText) => {
56
+ const lastIndex = wrappedText.length - 1
57
+ // If possible, simplify content by combining text nodes into one string.
58
+ // jest-native's `.toHaveTextContent` is buggy about array of text nodes in <Text> elements.
59
+ if (lastIndex >= 0 && isStringOrNumber(child) && isStringOrNumber(wrappedText[lastIndex])) {
60
+ /* eslint-disable-next-line no-param-reassign */
61
+ wrappedText[lastIndex] = `${wrappedText[lastIndex]}${child}`
62
+ } else {
63
+ wrappedText.push(child)
64
+ }
65
+ }
66
+
47
67
  /**
48
68
  * React Native on Native crashes if text content is rendered outside `<Text>`, and on web,
49
- * text style inheritance isn't as expected.
69
+ * text style inheritance behaves differently with text compared to regular HTML.
50
70
  *
51
71
  * Call this function on children that may contain text (strings or numbers) at the top level,
52
72
  * and any that are found will be wrapped in a React Native `<Text>` element with supplied props.
53
73
  *
54
- * Note that this does not wrap strings that are nested children of the top level children:
74
+ * Note that this does not wrap strings that are nested children of rendered top level children:
55
75
  * `wrapStringsInText(<View>Some text</View>)` will not wrap the inner text and will still crash,
56
- * but `wrapStringsInText(<>{someString}{anotherString}</>)` will wrap the strings in the fragment.
76
+ * but `wrapStringsInText(<>{someString}{anotherString}</>)` wraps the strings inside the fragment.
57
77
  *
58
78
  * @param {ReactChildren} children
59
79
  * @param {TextProps} props
60
80
  * @returns {ReactChildren}
61
81
  */
62
- export const wrapStringsInText = (children, props = {}) => {
63
- return Children.map(unpackFragment(children), (child) =>
64
- typeof child === 'string' || typeof child === 'number' ? <Text {...props}>{child}</Text> : child
65
- )
82
+ export const wrapStringsInText = (children, textProps = {}) => {
83
+ const childrenArray = unpackFragment(Children.toArray(children))
84
+
85
+ // Group adjacent wrapable children together in one Text element to create as few Text elements
86
+ // as possible, e.g. give <X>Text {someString}</X> one Text, same as <X>{`Text ${someString}`}</X>
87
+ const wrapables = [[]]
88
+ let wrapablesIndex = 0
89
+ childrenArray.forEach((child) => {
90
+ if (isWrapable(child)) {
91
+ // Make this child a child of the current `Text`
92
+ wrapChild(child, wrapables[wrapablesIndex])
93
+ } else {
94
+ // Close current `Text` and start a new one after this child
95
+ wrapables.push(child, [])
96
+ wrapablesIndex += 2
97
+ }
98
+ })
99
+
100
+ const items = wrapables.reduce((flatChildren, group, index) => {
101
+ // Skip nullish children and empty arrays
102
+ if (!group || (Array.isArray(group) && !group.length)) return flatChildren
103
+
104
+ return [
105
+ ...flatChildren,
106
+ Array.isArray(group) && group.some((child) => isStringOrNumber(child)) ? (
107
+ // Wrap text nodes and their wrappable neighbours in Text with as stable a key as possible.
108
+ // Avoid one-item arrays because jest-native's `.toHaveTextContent` is buggy
109
+ // and sometimes fails to match text content in arrays.
110
+ <Text key={combineKeys(group) || index} {...textProps}>
111
+ {group.length === 1 ? group[0] : group}
112
+ </Text>
113
+ ) : (
114
+ group
115
+ )
116
+ ]
117
+ }, [])
118
+ return items.length === 1 ? items[0] : items
66
119
  }
@@ -77,16 +77,16 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
77
77
  valueRef.current.value = currentValue
78
78
 
79
79
  const setValue = useCallback(
80
- (arg) => {
80
+ (arg, event) => {
81
81
  if (readOnly) return
82
82
  const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg
83
- // Only call onChange if the value actually changed
84
- if (onChange && valueRef.current.value !== newValue) onChange(newValue)
85
83
  if (!isControlled) setOwnValue(newValue)
84
+ // Call onChange handler if there's something for it to handle (event or a changed value)
85
+ if (onChange && (event || valueRef.current.value !== newValue)) onChange(newValue, event)
86
86
  },
87
87
  [isControlled, onChange, readOnly]
88
88
  )
89
- const resetValue = useCallback(() => setValue(valueRef.current.initial), [setValue])
89
+ const resetValue = useCallback((event) => setValue(valueRef.current.initial, event), [setValue])
90
90
 
91
91
  return { currentValue, setValue, resetValue, isControlled }
92
92
  }
@@ -146,23 +146,26 @@ export const useMultipleInputValues = ({
146
146
  )
147
147
  const currentValues = enforceMaxValues(currentValue)
148
148
 
149
- const setValues = useCallback((newValues) => setValue(enforceMaxValues(newValues)), [
150
- setValue,
151
- enforceMaxValues
152
- ])
149
+ const setValues = useCallback(
150
+ (newValues, event) => {
151
+ const validNewValues = enforceMaxValues(newValues)
152
+ setValue(validNewValues, event)
153
+ },
154
+ [setValue, enforceMaxValues]
155
+ )
153
156
 
154
157
  const resetValues = resetValue
155
- const unsetValues = useCallback(() => setValues([]), [setValues])
158
+ const unsetValues = useCallback((event) => setValues([], event), [setValues])
156
159
  const toggleOneValue = useCallback(
157
- (newValue) => {
160
+ (newValue, event) => {
158
161
  if (readOnly) return
159
- setValues(
160
- // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
161
- // a deepEqual() function if there's any use case for toggling stored objects or arrays.
162
- currentValues.includes(newValue)
163
- ? currentValues.filter((oldValue) => oldValue !== newValue)
164
- : [...currentValues, newValue]
165
- )
162
+ // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
163
+ // a deepEqual() function if there's any use case for toggling stored objects or arrays.
164
+ const newValues = currentValues.includes(newValue)
165
+ ? currentValues.filter((oldValue) => oldValue !== newValue)
166
+ : [...currentValues, newValue]
167
+
168
+ setValues(newValues, event)
166
169
  },
167
170
  [currentValues, readOnly, setValues]
168
171
  )
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types'
2
2
  import { Linking, Platform } from 'react-native'
3
- import { components as tokenKeys } from '@telus-uds/system-themes/schema'
3
+ import { components as tokenKeys } from '@telus-uds/system-theme-tokens'
4
4
 
5
5
  export const paddingProp = {
6
6
  propType: PropTypes.shape({
@@ -51,11 +51,11 @@ const tokenValue = PropTypes.oneOfType([
51
51
  const tokenValueType = PropTypes.oneOfType([tokenValue, PropTypes.objectOf(tokenValue)])
52
52
 
53
53
  export const getTokenNames = (componentName) => {
54
- const componentTokenNames = tokenKeys[componentName]
55
- if (!componentTokenNames) {
56
- throw new Error(`No "${componentName}" tokenKeys in @telus-uds/system-themes/schema`)
54
+ const componentTokenSchema = tokenKeys[componentName]
55
+ if (!componentTokenSchema) {
56
+ throw new Error(`No "${componentName}" tokenKeys in @telus-uds/system-theme-tokens`)
57
57
  }
58
- return componentTokenNames
58
+ return Object.keys(componentTokenSchema)
59
59
  }
60
60
 
61
61
  /**
@@ -237,7 +237,79 @@ const a11yPropTypes = {
237
237
  importantForAccessibility: PropTypes.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
238
238
  onAccessibilityAction: PropTypes.func,
239
239
  onAccessibilityEscape: PropTypes.func,
240
- onAccessibilityTap: PropTypes.func
240
+ onAccessibilityTap: PropTypes.func,
241
+ ...Platform.select({
242
+ web: {
243
+ // React Native Web adds many a11y props that alias aria-* attributes
244
+ // Types based on https://necolas.github.io/react-native-web/docs/accessibility/
245
+ accessibilityActiveDescendant: PropTypes.string,
246
+ accessibilityAtomic: PropTypes.bool,
247
+ accessibilityAutoComplete: PropTypes.string,
248
+ accessibilityBusy: PropTypes.bool,
249
+ accessibilityChecked: PropTypes.oneOf([true, false, 'mixed']),
250
+ accessibilityColumnCount: PropTypes.number,
251
+ accessibilityColumnIndex: PropTypes.number,
252
+ accessibilityColumnSpan: PropTypes.number,
253
+ accessibilityControls: PropTypes.oneOfType([
254
+ PropTypes.string,
255
+ PropTypes.arrayOf(PropTypes.string)
256
+ ]),
257
+ accessibilityCurrent: PropTypes.oneOf([
258
+ true,
259
+ false,
260
+ 'page',
261
+ 'step',
262
+ 'location',
263
+ 'date',
264
+ 'time'
265
+ ]),
266
+ accessibilityDescribedBy: PropTypes.oneOfType([
267
+ PropTypes.string,
268
+ PropTypes.arrayOf(PropTypes.string)
269
+ ]),
270
+ accessibilityDetails: PropTypes.string,
271
+ accessibilityDisabled: PropTypes.bool,
272
+ accessibilityErrorMessage: PropTypes.string,
273
+ accessibilityExpanded: PropTypes.bool,
274
+ accessibilityFlowTo: PropTypes.oneOfType([
275
+ PropTypes.string,
276
+ PropTypes.arrayOf(PropTypes.string)
277
+ ]),
278
+ accessibilityHasPopup: PropTypes.string,
279
+ accessibilityHidden: PropTypes.bool,
280
+ accessibilityInvalid: PropTypes.bool,
281
+ accessibilityKeyShortcuts: PropTypes.string,
282
+ accessibilityLabelledBy: PropTypes.oneOfType([
283
+ PropTypes.string,
284
+ PropTypes.arrayOf(PropTypes.string)
285
+ ]),
286
+ accessibilityLevel: PropTypes.number,
287
+ accessibilityModal: PropTypes.bool,
288
+ accessibilityMultiline: PropTypes.bool,
289
+ accessibilityMultiSelectable: PropTypes.bool,
290
+ accessibilityOrientation: PropTypes.oneOf(['horizontal', 'vertical']),
291
+ accessibilityOwns: PropTypes.oneOfType([
292
+ PropTypes.string,
293
+ PropTypes.arrayOf(PropTypes.string)
294
+ ]),
295
+ accessibilityPlaceholder: PropTypes.string,
296
+ accessibilityPosInSet: PropTypes.number,
297
+ accessibilityPressed: PropTypes.bool,
298
+ accessibilityReadOnly: PropTypes.bool,
299
+ accessibilityRequired: PropTypes.bool,
300
+ accessibilityRoleDescription: PropTypes.string,
301
+ accessibilityRowCount: PropTypes.number,
302
+ accessibilityRowIndex: PropTypes.number,
303
+ accessibilityRowSpan: PropTypes.number,
304
+ accessibilitySelected: PropTypes.bool,
305
+ accessibilitySetSize: PropTypes.number,
306
+ accessibilitySort: PropTypes.oneOf(['ascending', 'descending', 'none', 'other']),
307
+ accessibilityValueMax: PropTypes.number,
308
+ accessibilityValueMin: PropTypes.number,
309
+ accessibilityValueNow: PropTypes.number,
310
+ accessibilityValueText: PropTypes.string
311
+ }
312
+ })
241
313
  }
242
314
 
243
315
  export const a11yProps = {
@@ -337,8 +409,11 @@ const pressPropTypes = {
337
409
  ...pressHandlerPropTypes,
338
410
  disabled: PropTypes.bool,
339
411
  ...Platform.select({
340
- hitSlop: PropTypes.number,
341
- pressRetentionOffset: PropTypes.oneOfType([PropTypes.number, rectProp.propType])
412
+ web: {},
413
+ default: {
414
+ hitSlop: PropTypes.number,
415
+ pressRetentionOffset: PropTypes.oneOfType([PropTypes.number, rectProp.propType])
416
+ }
342
417
  })
343
418
  }
344
419
 
@@ -371,29 +446,31 @@ export const linkProps = {
371
446
  */
372
447
  select: getPropSelector(linkPropTypes),
373
448
  /**
374
- * Turn hrefs into press handlers on Native and throw if not given `onPress` xor `href`.
449
+ * Turn hrefs into press handlers on Native and throw if not given `onPress` or `href`.
375
450
  *
376
451
  * @param {({ onPress?: () => void, href?: string })}
377
452
  * @returns {(() => void)|undefined} Returns a press handler, or undefined on web if href
378
453
  * string is provided. Expects calling component to use href string on web (e.g. in `<a>`).
379
454
  */
380
455
  handleHref: ({ onPress, href }) => {
381
- // TODO: revisit this when integrating routing packages
382
- // https://github.com/telus/universal-design-system/issues/24
383
- if (href && onPress) {
384
- throw new Error("handleHref currently doesn't support both href and onPress")
385
- }
386
456
  if (!href && !onPress) {
387
457
  throw new Error('handleHref requires either href or onPress')
388
458
  }
389
- return Platform.OS !== 'web' && href ? () => Linking.openURL(href) : onPress
459
+ return Platform.select({
460
+ web: onPress,
461
+ default: (...args) => {
462
+ if (onPress) onPress(...args)
463
+ if (href) Linking.openURL(href)
464
+ }
465
+ })
390
466
  }
391
467
  }
392
468
 
393
469
  const viewPropTypes = {
394
470
  pointerEvents: PropTypes.oneOf(['all', 'none', 'box-only', 'box-none']),
395
471
  onLayout: PropTypes.func,
396
- nativeID: PropTypes.string
472
+ nativeID: PropTypes.string,
473
+ testID: PropTypes.string
397
474
  }
398
475
 
399
476
  export const viewProps = {
@@ -492,20 +569,19 @@ export const spacingProps = {
492
569
  }
493
570
 
494
571
  /**
495
- * Returns a prop type validator which checks whether a prop is either a component or an array of components of a given
496
- * type, based on their `name` property.
572
+ * Returns a prop type validator which checks whether a prop is either a component or an array of
573
+ * components of a given type, based on their `name` or `displayName` properties.
497
574
  * Use an array of strings for `passedName` to accept more than one component type.
498
575
  * For an array the validation fails on the first occurrence of an invalid element.
499
576
  *
500
577
  * @param {string|string[]} passedName
501
- * @param {boolean} [checkDisplayName] - if `true` then `displayName` property on the component will be validated instead of `name`
502
578
  * @return {function}
503
579
  */
504
- export const componentPropType = (passedName, checkDisplayName = false) => {
580
+ export const componentPropType = (passedName) => {
505
581
  const passedNames = typeof passedName === 'string' ? [passedName] : passedName
506
582
 
507
583
  const checkProp = (props, propName, componentName) => {
508
- if (typeof props[propName] === 'undefined' || props[propName] === null) {
584
+ if (props[propName] === undefined || props[propName] === null) {
509
585
  return undefined
510
586
  }
511
587
 
@@ -517,25 +593,11 @@ export const componentPropType = (passedName, checkDisplayName = false) => {
517
593
  .find(Boolean)
518
594
  }
519
595
 
520
- const testNameInObject = () =>
521
- typeof props[propName] === 'object' &&
522
- ((!checkDisplayName && !passedNames.includes(props[propName].type.name)) ||
523
- (checkDisplayName && !passedNames.includes(props[propName].type.displayName)))
524
-
525
- const testNameInFunction = () =>
526
- typeof props[propName] === 'function' &&
527
- ((!checkDisplayName && !passedNames.includes(props[propName].name)) ||
528
- (checkDisplayName && !passedNames.includes(props[propName].displayName)))
529
-
530
- if (
531
- (props[propName] &&
532
- typeof props[propName] !== 'object' &&
533
- typeof props[propName] !== 'function') ||
534
- testNameInObject() ||
535
- testNameInFunction()
536
- ) {
596
+ const nameInProp = props[propName].type?.displayName || props[propName].type?.name
597
+ if (!nameInProp || !passedNames.includes(nameInProp)) {
598
+ const propDescription = nameInProp ? `Component ${nameInProp}` : typeof props[propName]
537
599
  return new Error(
538
- `${componentName}: Component passed to \`${propName}\` prop should be ${passedNames.join(
600
+ `${componentName}: ${propDescription} was passed to \`${propName}\` prop; should be ${passedNames.join(
539
601
  ' or '
540
602
  )}`
541
603
  )
@@ -29,7 +29,7 @@ export const DEFAULT_COPY = 'en'
29
29
  */
30
30
  function useCopy({ dictionary, copy = DEFAULT_COPY }) {
31
31
  if (typeof copy === 'string') {
32
- return (key) => dictionary[copy][key]
32
+ return (key) => (key ? dictionary[copy][key] : dictionary[copy])
33
33
  }
34
34
 
35
35
  // support overriding the entire copy dictionary with an object for a single language
@@ -1,6 +1,11 @@
1
1
  import { useEffect, useState } from 'react'
2
2
 
3
- const doAction = (action) => typeof action === 'function' && action(window?.location?.hash)
3
+ const doAction = (action, event) =>
4
+ typeof action === 'function' && action(window?.location?.hash, event)
5
+
6
+ /**
7
+ * @typedef {import('react').SyntheticEvent} SyntheticEvent
8
+ */
4
9
 
5
10
  /**
6
11
  * Calls a function (passing it the current hash) based on the current hash state on mount,
@@ -10,7 +15,7 @@ const doAction = (action) => typeof action === 'function' && action(window?.loca
10
15
  *
11
16
  * On Native, this is replaced with a harmless no-op function as there is no direct equivalent.
12
17
  *
13
- * @param {(hash: string) => void} action - a function to act on the current hash on load and when it changes
18
+ * @param {(hash: string, event?: SyntheticEvent) => void} action - a function to act on the current hash on load and when it changes
14
19
  * @returns
15
20
  */
16
21
  const useHash = (action, isReady = true) => {
@@ -26,7 +31,7 @@ const useHash = (action, isReady = true) => {
26
31
 
27
32
  // Bind the action for each hash change; do re-bind if the function changes.
28
33
  useEffect(() => {
29
- const handleChange = () => doAction(action)
34
+ const handleChange = (event) => doAction(action, event)
30
35
  window.addEventListener('hashchange', handleChange)
31
36
  return () => window.removeEventListener('hashchange', handleChange)
32
37
  }, [action])
@@ -2,8 +2,8 @@
2
2
  import React from 'react'
3
3
  import { StyleSheet, View } from 'react-native'
4
4
 
5
- import A11yText from '../../lib/A11yText'
6
- import { Button, Typography } from '../../lib'
5
+ import A11yText from '../../src/A11yText'
6
+ import { Button, Typography } from '../../src'
7
7
  import { EachParentType, parentTypesParams } from '../supports'
8
8
 
9
9
  const defaultArgs = {
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
- import { ActivityIndicator } from '../../lib'
3
+ import { ActivityIndicator } from '../../src'
4
4
  import { EachParentType, parentTypesParams } from '../supports'
5
5
 
6
6
  export default {
@@ -2,7 +2,7 @@
2
2
  import React from 'react'
3
3
 
4
4
  import { View, StyleSheet } from 'react-native'
5
- import { Box, Typography } from '../../lib'
5
+ import { Box, Typography } from '../../src'
6
6
  import {
7
7
  spacingObjectArg,
8
8
  Container,
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
- import { Button, useTheme } from '../../lib'
3
+ import { Button, useTheme } from '../../src'
4
4
  import { Container, useVariants, EachParentType, parentTypesParams } from '../supports'
5
- import { getComponentTheme } from '../../lib/ThemeProvider/utils'
5
+ import { getComponentTheme } from '../../src/ThemeProvider/utils'
6
6
 
7
7
  export const Default = (args) => <Button {...args} />
8
8
  Default.storyName = 'Button'
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React, { useState } from 'react'
3
3
  import { View } from 'react-native'
4
- import { ButtonGroup, Typography } from '../../lib'
4
+ import { ButtonGroup, Typography } from '../../src'
5
5
  import { Container } from '../supports'
6
6
 
7
7
  const defaultArgs = {
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { ButtonLink } from '../../lib'
2
+ import { ButtonLink } from '../../src'
3
3
  import { Container, useVariants } from '../supports'
4
4
 
5
5
  export default {
@@ -2,7 +2,7 @@
2
2
  import React from 'react'
3
3
  import { StyleSheet, Text, View } from 'react-native'
4
4
 
5
- import { Card } from '../../lib'
5
+ import { Card } from '../../src'
6
6
  import { Container, useVariants, EachParentType, parentTypesParams } from '../supports'
7
7
 
8
8
  export default {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
 
4
- import { Checkbox, CheckboxGroup, Typography } from '../../lib'
4
+ import { Checkbox, CheckboxGroup, Typography } from '../../src'
5
5
  import { Container, EachParentType, parentTypesParams } from '../supports'
6
6
 
7
7
  export default {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
  import { View, StyleSheet } from 'react-native'
4
- import { Divider, Typography } from '../../lib'
4
+ import { Divider, Typography } from '../../src'
5
5
  import { EachParentType, parentTypesParams } from '../supports'
6
6
 
7
7
  export default {
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
  import PropTypes from 'prop-types'
4
- import { Accordion, ButtonGroup, ExpandCollapse, StackView, Typography } from '../../lib'
4
+ import { Accordion, ButtonGroup, ExpandCollapse, StackView, Typography } from '../../src'
5
5
  import { Container, EachParentType, parentTypesParams } from '../supports'
6
- import { useMultipleInputValues } from '../../lib/utils'
6
+ import { useMultipleInputValues } from '../../src/utils'
7
7
 
8
8
  export default {
9
9
  title: 'Base/ExpandCollapse',
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react'
2
2
  import { Text } from 'react-native'
3
3
 
4
- import { Feedback, Typography, TextInput } from '../../lib'
4
+ import { Feedback, Typography, TextInput } from '../../src'
5
5
  import { Container } from '../supports'
6
6
 
7
7
  export default {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
 
4
- import { FlexGrid } from '../../lib'
4
+ import { FlexGrid } from '../../src'
5
5
  import { Placeholder, EachParentType, parentTypesParams } from '../supports'
6
6
 
7
7
  const { Row, Col } = FlexGrid
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
 
3
- import { FlexGrid } from '../../lib'
3
+ import { FlexGrid } from '../../src'
4
4
  import { Placeholder } from '../supports'
5
5
 
6
6
  const { Row, Col } = FlexGrid
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
 
3
- import { FlexGrid } from '../../lib'
3
+ import { FlexGrid } from '../../src'
4
4
  import { Placeholder } from '../supports'
5
5
 
6
6
  const { Row, Col } = FlexGrid
@@ -4,7 +4,7 @@ import { StyleSheet, Text, View } from 'react-native'
4
4
 
5
5
  import AccessibleIcon from '../../__fixtures__/Accessible'
6
6
  import { Container, useVariants, EachIcon, EachParentType, parentTypesParams } from '../supports'
7
- import { Icon, StackWrap, Tooltip } from '../../lib'
7
+ import { Icon, StackWrap, Tooltip } from '../../src'
8
8
 
9
9
  export default {
10
10
  title: 'Base/Icon',
@@ -4,7 +4,7 @@ import React from 'react'
4
4
  /* eslint-disable-next-line import/no-extraneous-dependencies */
5
5
  import { Add } from '@telus-uds/palette-allium/build/rn/icons'
6
6
 
7
- import { Box, IconButton, StackView } from '../../lib'
7
+ import { Box, IconButton, StackView } from '../../src'
8
8
  import { useVariants, EachParentType, parentTypesParams } from '../supports'
9
9
 
10
10
  export default {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
 
4
- import InputLabel from '../../lib/InputLabel/InputLabel'
4
+ import InputLabel from '../../src/InputLabel/InputLabel'
5
5
 
6
6
  export default {
7
7
  title: 'Base/InputLabel',
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-multi-comp */
2
2
  import React from 'react'
3
3
 
4
- import { ChevronLink } from '../../lib'
4
+ import { ChevronLink } from '../../src'
5
5
  import { Container, EachParentType, parentTypesParams, useVariants } from '../supports'
6
6
 
7
7
  export default {
@@ -4,7 +4,7 @@ import { View } from 'react-native'
4
4
 
5
5
  import AccessibleIcon from '../../__fixtures__/Accessible'
6
6
 
7
- import { Link, Typography, variantProp } from '../../lib'
7
+ import { Link, Typography, variantProp } from '../../src'
8
8
  import { Container, EachIcon, EachParentType, parentTypesParams, useVariants } from '../supports'
9
9
 
10
10
  export default {
@@ -6,7 +6,7 @@ import { View } from 'react-native'
6
6
  // eslint-disable-next-line import/no-extraneous-dependencies
7
7
  import * as Icons from '@telus-uds/palette-allium/build/rn/icons'
8
8
 
9
- import { TextButton, variantProp } from '../../lib'
9
+ import { TextButton, variantProp } from '../../src'
10
10
  import { Container, EachParentType, useVariants, parentTypesParams } from '../supports'
11
11
 
12
12
  export default {
@@ -6,7 +6,7 @@ import { StyleSheet, Text } from 'react-native'
6
6
  import { Checkmark, ThumbsUp, Baby } from '@telus-uds/palette-allium/build/rn/icons'
7
7
  import { useVariants, Container } from '../supports'
8
8
 
9
- import { List, Typography } from '../../lib'
9
+ import { List, Typography } from '../../src'
10
10
 
11
11
  export default {
12
12
  title: 'Base/List',
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
- import { Button, Modal, Spacer, Typography } from '../../lib'
3
+ import { Button, Modal, Spacer, Typography } from '../../src'
4
4
 
5
5
  export default {
6
6
  title: 'Base/Modal',
@@ -2,7 +2,7 @@
2
2
  import React from 'react'
3
3
  import { Text } from 'react-native'
4
4
 
5
- import { Link, Notification, Typography } from '../../lib'
5
+ import { Link, Notification, Typography } from '../../src'
6
6
  import { Container } from '../supports'
7
7
 
8
8
  export default {
@@ -2,7 +2,7 @@
2
2
  import React, { useState } from 'react'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
- import { Pagination, Typography } from '../../lib'
5
+ import { Pagination, Typography } from '../../src'
6
6
  import { Container, EachParentType, parentTypesParams } from '../supports'
7
7
 
8
8
  export default {