@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,4 +1,4 @@
1
- import React, { useState } from 'react'
1
+ import React, { forwardRef, useState } from 'react'
2
2
  import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
@@ -98,90 +98,97 @@ const selectIconContainerStyles = ({ paddingRight, paddingBottom }) => ({
98
98
  paddingBottom
99
99
  })
100
100
 
101
- function TextInputBase({
102
- value,
103
- height,
104
- initialValue,
105
- inactive,
106
- readOnly,
107
- onChange,
108
- onChangeText,
109
- onFocus,
110
- onBlur,
111
- onMouseOver,
112
- onMouseOut,
113
- tokens,
114
- variant = {},
115
- ...remainingProps
116
- }) {
117
- const [isFocused, setIsFocused] = useState(false)
118
- const handleFocus = (event) => {
119
- setIsFocused(true)
120
- if (typeof onFocus === 'function') onFocus(event)
121
- }
122
- const handleBlur = (event) => {
123
- setIsFocused(false)
124
- if (typeof onBlur === 'function') onBlur(event)
125
- }
101
+ const TextInputBase = forwardRef(
102
+ (
103
+ {
104
+ value,
105
+ height,
106
+ initialValue,
107
+ inactive,
108
+ readOnly,
109
+ onChange,
110
+ onChangeText,
111
+ onFocus,
112
+ onBlur,
113
+ onMouseOver,
114
+ onMouseOut,
115
+ tokens,
116
+ variant = {},
117
+ ...remainingProps
118
+ },
119
+ ref
120
+ ) => {
121
+ const [isFocused, setIsFocused] = useState(false)
122
+ const handleFocus = (event) => {
123
+ setIsFocused(true)
124
+ if (typeof onFocus === 'function') onFocus(event)
125
+ }
126
+ const handleBlur = (event) => {
127
+ setIsFocused(false)
128
+ if (typeof onBlur === 'function') onBlur(event)
129
+ }
126
130
 
127
- const [isHovered, setIsHovered] = useState(false)
128
- const handleMouseOver = (event) => {
129
- setIsHovered(true)
130
- if (typeof onMouseOver === 'function') onMouseOver(event)
131
- }
132
- const handleMouseOut = (event) => {
133
- setIsHovered(false)
134
- if (typeof onMouseOut === 'function') onMouseOut(event)
135
- }
131
+ const [isHovered, setIsHovered] = useState(false)
132
+ const handleMouseOver = (event) => {
133
+ setIsHovered(true)
134
+ if (typeof onMouseOver === 'function') onMouseOver(event)
135
+ }
136
+ const handleMouseOut = (event) => {
137
+ setIsHovered(false)
138
+ if (typeof onMouseOut === 'function') onMouseOut(event)
139
+ }
136
140
 
137
- const { currentValue, setValue, isControlled } = useInputValue({
138
- value,
139
- initialValue,
140
- onChange,
141
- readOnly
142
- })
141
+ const { currentValue, setValue, isControlled } = useInputValue({
142
+ value,
143
+ initialValue,
144
+ onChange,
145
+ readOnly
146
+ })
147
+
148
+ const handleChangeText = (event) => {
149
+ const text = event.nativeEvent?.text || event.target?.value
150
+ setValue(text, event)
151
+ if (typeof onChangeText === 'function') onChangeText(text, event)
152
+ }
143
153
 
144
- const handleChangeText = (text) => {
145
- setValue(text)
146
- if (typeof onChangeText === 'function') onChangeText(text)
147
- }
154
+ const states = { focus: isFocused, hover: isHovered, inactive }
148
155
 
149
- const states = { focus: isFocused, hover: isHovered, inactive }
156
+ const themeTokens = useThemeTokens('TextInput', tokens, variant, states)
150
157
 
151
- const themeTokens = useThemeTokens('TextInput', tokens, variant, states)
158
+ const { icon: IconComponent } = themeTokens
152
159
 
153
- const { icon: IconComponent } = themeTokens
160
+ const inputProps = {
161
+ ...remainingProps,
162
+ editable: !inactive,
163
+ onFocus: handleFocus,
164
+ onBlur: handleBlur,
165
+ onMouseOver: handleMouseOver,
166
+ onMouseOut: handleMouseOut,
167
+ onChange: handleChangeText,
168
+ defaultValue: initialValue,
169
+ // currentValue is being updated even if the input is not controlled, passing it down to the
170
+ // Input could lead to changing its state from uncontrolled to controlled
171
+ value: isControlled ? currentValue : undefined
172
+ }
154
173
 
155
- const inputProps = {
156
- ...remainingProps,
157
- editable: !inactive,
158
- onFocus: handleFocus,
159
- onBlur: handleBlur,
160
- onMouseOver: handleMouseOver,
161
- onMouseOut: handleMouseOut,
162
- onChangeText: handleChangeText,
163
- defaultValue: initialValue,
164
- // currentValue is being updated even if the input is not controlled, passing it down to the
165
- // Input could lead to changing its state from uncontrolled to controlled
166
- value: isControlled ? currentValue : undefined
174
+ const nativeInputStyle = selectInputStyles({ ...themeTokens, height }, inactive)
175
+
176
+ return (
177
+ <View style={selectOuterBorderStyles(themeTokens)}>
178
+ <NativeTextInput ref={ref} style={nativeInputStyle} {...inputProps} />
179
+ {IconComponent && (
180
+ <View
181
+ pointerEvents="none" // avoid hijacking input press events
182
+ style={[staticStyles.iconContainer, selectIconContainerStyles(themeTokens)]}
183
+ >
184
+ <IconComponent {...selectIconTokens(themeTokens)} />
185
+ </View>
186
+ )}
187
+ </View>
188
+ )
167
189
  }
168
-
169
- const nativeInputStyle = selectInputStyles({ ...themeTokens, height }, inactive)
170
-
171
- return (
172
- <View style={selectOuterBorderStyles(themeTokens)}>
173
- <NativeTextInput style={nativeInputStyle} {...inputProps} />
174
- {IconComponent && (
175
- <View
176
- pointerEvents="none" // avoid hijacking input press events
177
- style={[staticStyles.iconContainer, selectIconContainerStyles(themeTokens)]}
178
- >
179
- <IconComponent {...selectIconTokens(themeTokens)} />
180
- </View>
181
- )}
182
- </View>
183
- )
184
- }
190
+ )
191
+ TextInputBase.displayName = 'TextInputBase'
185
192
 
186
193
  TextInputBase.propTypes = {
187
194
  value: PropTypes.string,
@@ -1,18 +1,20 @@
1
1
  import React, { createContext, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
+ import { validateThemeTokensVersion } from './utils'
3
4
 
4
5
  export const uninitialisedError = new Error('Theme context used outside of ThemeProvider')
5
6
 
6
7
  export const ThemeContext = createContext(uninitialisedError)
7
8
  export const ThemeSetterContext = createContext(uninitialisedError)
8
9
 
9
- // TODO: develop this as theme metadata is added
10
- // https://github.com/telus/universal-design-system/issues/92
11
- export const themeType = PropTypes.object
12
-
13
10
  const ThemeProvider = ({ children, defaultTheme }) => {
14
11
  const [theme, setTheme] = useState(defaultTheme)
15
12
 
13
+ // Validate the theme tokens version on every render.
14
+ // This will intentionally break the application when attempting to use an invalid theme.
15
+ // This will surface an incompatibility quickly rather than allowing the potential for strange bugs due to missing or incompatible tokens.
16
+ validateThemeTokensVersion(theme)
17
+
16
18
  return (
17
19
  <ThemeSetterContext.Provider value={setTheme}>
18
20
  <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
@@ -21,10 +23,12 @@ const ThemeProvider = ({ children, defaultTheme }) => {
21
23
  }
22
24
 
23
25
  ThemeProvider.propTypes = {
24
- // TODO: implement a custom propType that validates schema version
25
- // https://github.com/telus/universal-design-system/issues/92
26
26
  children: PropTypes.node.isRequired,
27
- defaultTheme: themeType.isRequired
27
+ defaultTheme: PropTypes.shape({
28
+ metadata: PropTypes.shape({
29
+ themeTokensVersion: PropTypes.string.isRequired
30
+ }).isRequired
31
+ }).isRequired
28
32
  }
29
33
 
30
34
  export default ThemeProvider
@@ -4,6 +4,10 @@ import { ThemeSetterContext } from './ThemeProvider'
4
4
  const useSetTheme = () => {
5
5
  // Replace current theme with provided object
6
6
  const setTheme = useContext(ThemeSetterContext)
7
+ // Fail fast if dev uses useSetTheme outside of ThemeProvider
8
+ if (setTheme instanceof Error) {
9
+ throw setTheme
10
+ }
7
11
 
8
12
  // Merge provided object into current theme
9
13
  const editTheme = useCallback(
@@ -1,4 +1,6 @@
1
1
  import PropTypes from 'prop-types'
2
+ import semVerSatisfies from 'semver/functions/satisfies'
3
+ import pkg from '../../../package.json'
2
4
 
3
5
  /**
4
6
  * @typedef {import('../../utils/propTypes.js').AppearanceSet} AppearanceSet
@@ -133,3 +135,29 @@ export const getThemeTokens = (
133
135
  }
134
136
 
135
137
  export const toArray = (strOrArr) => (Array.isArray(strOrArr) ? strOrArr : [strOrArr])
138
+
139
+ /**
140
+ * Throws an error if the theme was built with an incompatible version of @telus-uds/system-theme-tokens
141
+ *
142
+ * This is used internally by the ThemeProvider to force a fast failure when an incompatible version is detected.
143
+ *
144
+ * Version compatibility is calculated with semver.satisfies.
145
+ * Refer to https://github.com/npm/node-semver and https://semver.npmjs.com/ for details about how
146
+ * semver compatibility.
147
+ *
148
+ * @param {object} theme - UDS theme built for react-native
149
+ */
150
+ export const validateThemeTokensVersion = (theme) => {
151
+ const expectedThemeTokensVersion = pkg.dependencies['@telus-uds/system-theme-tokens']
152
+ const actualThemeTokensVersion = theme?.metadata?.themeTokensVersion
153
+
154
+ if (!semVerSatisfies(actualThemeTokensVersion, expectedThemeTokensVersion)) {
155
+ throw new Error(
156
+ `Invalid UDS token schema version detected.
157
+
158
+ The UDS base components ${pkg.name} v${pkg.version} are only compatible with UDS themes that are built with @telus-uds/system-theme-tokens version that is semver compatible with ${expectedThemeTokensVersion}. The current theme was built with @telus-uds/system-theme-tokens v${actualThemeTokensVersion}.
159
+
160
+ If you see this error than most likely you have attempted to install ${pkg.name} and a UDS theme manually because you are building a multi-brand application. If you are building a single brand application, consider installing the brand specific design system package such as @telus-uds/ds-allium. For more information, see https://github.com/telus/universal-design-system/blob/main/packages/docs-uds/docs/multi-brand-usage.md`
161
+ )
162
+ }
163
+ }
@@ -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 { Platform, View, StyleSheet } from 'react-native'
4
4
 
@@ -54,62 +54,61 @@ const selectSwitchStyles = ({
54
54
  })
55
55
  })
56
56
 
57
- const ToggleSwitch = ({
58
- value,
59
- initialValue,
60
- onChange,
61
- inactive,
62
- tokens,
63
- variant,
64
- accessibilityRole = 'switch'
65
- }) => {
66
- const getTokens = useThemeTokensCallback('ToggleSwitch', tokens, variant)
57
+ const ToggleSwitch = forwardRef(
58
+ (
59
+ { value, initialValue, onChange, inactive, tokens, variant, accessibilityRole = 'switch' },
60
+ ref
61
+ ) => {
62
+ const getTokens = useThemeTokensCallback('ToggleSwitch', tokens, variant)
67
63
 
68
- const { currentValue, setValue } = useInputValue({
69
- value,
70
- initialValue,
71
- onChange
72
- })
64
+ const { currentValue, setValue } = useInputValue({
65
+ value,
66
+ initialValue,
67
+ onChange
68
+ })
73
69
 
74
- const handlePress = () => setValue(!currentValue)
70
+ const handlePress = (event) => setValue(!currentValue, event)
75
71
 
76
- const getButtonTokens = (buttonState) => selectButtonTokens(getTokens(buttonState))
72
+ const getButtonTokens = (buttonState) => selectButtonTokens(getTokens(buttonState))
77
73
 
78
- return (
79
- <ButtonBase
80
- selected={currentValue}
81
- inactive={inactive}
82
- tokens={getButtonTokens}
83
- accessibilityRole={accessibilityRole}
84
- accessibilityState={{ checked: currentValue }}
85
- onPress={handlePress}
86
- >
87
- {(buttonState) => {
88
- const themeTokens = getTokens(buttonState)
89
- const IconComponent = themeTokens.icon
90
- const switchStyles = selectSwitchStyles(themeTokens)
91
- const trackStyles = selectTrackStyles(themeTokens)
92
- const iconTokens = selectIconTokens(themeTokens)
74
+ return (
75
+ <ButtonBase
76
+ ref={ref}
77
+ selected={currentValue}
78
+ inactive={inactive}
79
+ tokens={getButtonTokens}
80
+ accessibilityRole={accessibilityRole}
81
+ accessibilityState={{ checked: currentValue }}
82
+ onPress={handlePress}
83
+ >
84
+ {(buttonState) => {
85
+ const themeTokens = getTokens(buttonState)
86
+ const IconComponent = themeTokens.icon
87
+ const switchStyles = selectSwitchStyles(themeTokens)
88
+ const trackStyles = selectTrackStyles(themeTokens)
89
+ const iconTokens = selectIconTokens(themeTokens)
93
90
 
94
- // If drag-slide support is needed, use a PanResponder and apply these to an Animated value.
95
- // Use translate transforms for smoothest non-thread-blocking animations and to allow drag.
96
- const slideStart = 0
97
- const slideEnd =
98
- themeTokens.width - themeTokens.switchSize - themeTokens.trackBorderWidth * 2
99
- const switchOffset = buttonState.selected ? slideEnd : slideStart
100
- const switchPositionStyle = { transform: [{ translateX: switchOffset }] }
91
+ // If drag-slide support is needed, use a PanResponder and apply these to an Animated value.
92
+ // Use translate transforms for smoothest non-thread-blocking animations and to allow drag.
93
+ const slideStart = 0
94
+ const slideEnd =
95
+ themeTokens.width - themeTokens.switchSize - themeTokens.trackBorderWidth * 2
96
+ const switchOffset = buttonState.selected ? slideEnd : slideStart
97
+ const switchPositionStyle = { transform: [{ translateX: switchOffset }] }
101
98
 
102
- return (
103
- <View style={[staticStyles.track, trackStyles]}>
104
- <View style={[staticStyles.switch, switchStyles, switchPositionStyle]}>
105
- {IconComponent && <IconComponent {...iconTokens} />}
99
+ return (
100
+ <View style={[staticStyles.track, trackStyles]}>
101
+ <View style={[staticStyles.switch, switchStyles, switchPositionStyle]}>
102
+ {IconComponent && <IconComponent {...iconTokens} />}
103
+ </View>
106
104
  </View>
107
- </View>
108
- )
109
- }}
110
- </ButtonBase>
111
- )
112
- }
105
+ )
106
+ }}
107
+ </ButtonBase>
108
+ )
109
+ }
110
+ )
111
+ ToggleSwitch.displayName = 'ToggleSwitch'
113
112
 
114
113
  ToggleSwitch.propTypes = {
115
114
  ...a11yProps.propTypes,