@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
@@ -23,23 +23,29 @@ const validateProps = ({
23
23
  const usageError = error => {
24
24
  // Errors inside hooks in React Native get incomplete stack traces pointing at parent component only.
25
25
  // Help devs out by telling them exactly which hook threw the error as well as why.
26
- throw new Error("".concat(hookName, " ").concat(error, ".\n\nConsumers of this hook must be one of:\n1. An \"uncontrolled\" component responding directly to user actions, with an optional `initialValue").concat(s, "` but no `value").concat(s, "` prop,\n2. A \"controlled\" component where an always-defined `value").concat(s, "` prop is managed by an `onChange` handler, with no `initialValue").concat(s, "`,\n3. A \"read-only\" component, with `readOnly` prop set as `true`.\n"));
26
+ throw new Error(`${hookName} ${error}.
27
+
28
+ Consumers of this hook must be one of:
29
+ 1. An "uncontrolled" component responding directly to user actions, with an optional \`initialValue${s}\` but no \`value${s}\` prop,
30
+ 2. A "controlled" component where an always-defined \`value${s}\` prop is managed by an \`onChange\` handler, with no \`initialValue${s}\`,
31
+ 3. A "read-only" component, with \`readOnly\` prop set as \`true\`.
32
+ `);
27
33
  };
28
34
 
29
35
  if (value && !onChange && !readOnly) {
30
- usageError("has `value".concat(s, "` prop without `onChange` or `readOnly`"));
36
+ usageError(`has \`value${s}\` prop without \`onChange\` or \`readOnly\``);
31
37
  }
32
38
 
33
39
  if (initialValue && value) {
34
- usageError("has both `initialValue".concat(s, "` and `value").concat(s, "`"));
40
+ usageError(`has both \`initialValue${s}\` and \`value${s}\``);
35
41
  }
36
42
 
37
43
  if (isControlled && !isCurrentlyControlled) {
38
- usageError("stopped receiving `value".concat(s, "` from parent after delegating state management"));
44
+ usageError(`stopped receiving \`value${s}\` from parent after delegating state management`);
39
45
  }
40
46
 
41
47
  if (!isControlled && isCurrentlyControlled) {
42
- usageError("started receiving `value".concat(s, "` from parent after starting managing own state"));
48
+ usageError(`started receiving \`value${s}\` from parent after starting managing own state`);
43
49
  }
44
50
  };
45
51
  /**
@@ -92,14 +98,14 @@ const useInputValue = (props = {}, hookName = 'useInputValue') => {
92
98
  }); // Make current value accessible inside useCallback without rememoizing every time the value changes
93
99
 
94
100
  valueRef.current.value = currentValue;
95
- const setValue = (0, _react.useCallback)(arg => {
101
+ const setValue = (0, _react.useCallback)((arg, event) => {
96
102
  if (readOnly) return;
97
- const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg; // Only call onChange if the value actually changed
103
+ const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg;
104
+ if (!isControlled) setOwnValue(newValue); // Call onChange handler if there's something for it to handle (event or a changed value)
98
105
 
99
- if (onChange && valueRef.current.value !== newValue) onChange(newValue);
100
- if (!isControlled) setOwnValue(newValue);
106
+ if (onChange && (event || valueRef.current.value !== newValue)) onChange(newValue, event);
101
107
  }, [isControlled, onChange, readOnly]);
102
- const resetValue = (0, _react.useCallback)(() => setValue(valueRef.current.initial), [setValue]);
108
+ const resetValue = (0, _react.useCallback)(event => setValue(valueRef.current.initial, event), [setValue]);
103
109
  return {
104
110
  currentValue,
105
111
  setValue,
@@ -154,7 +160,7 @@ const useMultipleInputValues = ({
154
160
  onChange,
155
161
  value: values,
156
162
  // if we're controlling our own state, always start with an array
157
- initialValue: initialValues !== null && initialValues !== void 0 ? initialValues : values === undefined ? [] : undefined
163
+ initialValue: initialValues ?? (values === undefined ? [] : undefined)
158
164
  }, 'useMultipleInputValues');
159
165
  const enforceMaxValues = (0, _react.useCallback)(newValues => {
160
166
  if (!maxValues) return newValues;
@@ -162,14 +168,18 @@ const useMultipleInputValues = ({
162
168
  return excess > 0 ? newValues.slice(excess) : newValues;
163
169
  }, [maxValues]);
164
170
  const currentValues = enforceMaxValues(currentValue);
165
- const setValues = (0, _react.useCallback)(newValues => setValue(enforceMaxValues(newValues)), [setValue, enforceMaxValues]);
171
+ const setValues = (0, _react.useCallback)((newValues, event) => {
172
+ const validNewValues = enforceMaxValues(newValues);
173
+ setValue(validNewValues, event);
174
+ }, [setValue, enforceMaxValues]);
166
175
  const resetValues = resetValue;
167
- const unsetValues = (0, _react.useCallback)(() => setValues([]), [setValues]);
168
- const toggleOneValue = (0, _react.useCallback)(newValue => {
169
- if (readOnly) return;
170
- setValues( // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
176
+ const unsetValues = (0, _react.useCallback)(event => setValues([], event), [setValues]);
177
+ const toggleOneValue = (0, _react.useCallback)((newValue, event) => {
178
+ if (readOnly) return; // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
171
179
  // a deepEqual() function if there's any use case for toggling stored objects or arrays.
172
- currentValues.includes(newValue) ? currentValues.filter(oldValue => oldValue !== newValue) : [...currentValues, newValue]);
180
+
181
+ const newValues = currentValues.includes(newValue) ? currentValues.filter(oldValue => oldValue !== newValue) : [...currentValues, newValue];
182
+ setValues(newValues, event);
173
183
  }, [currentValues, readOnly, setValues]);
174
184
  return {
175
185
  currentValues,
@@ -11,7 +11,9 @@ var _Linking = _interopRequireDefault(require("react-native-web/dist/cjs/exports
11
11
 
12
12
  var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
13
13
 
14
- var _schema = require("@telus-uds/system-themes/schema");
14
+ var _systemThemeTokens = require("@telus-uds/system-theme-tokens");
15
+
16
+ var _propTypes2 = _interopRequireDefault(require("./a11y/propTypes"));
15
17
 
16
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
19
 
@@ -60,13 +62,13 @@ const tokenValue = _propTypes.default.oneOfType([_propTypes.default.string, _pro
60
62
  const tokenValueType = _propTypes.default.oneOfType([tokenValue, _propTypes.default.objectOf(tokenValue)]);
61
63
 
62
64
  const getTokenNames = componentName => {
63
- const componentTokenNames = _schema.components[componentName];
65
+ const componentTokenSchema = _systemThemeTokens.components[componentName];
64
66
 
65
- if (!componentTokenNames) {
66
- throw new Error("No \"".concat(componentName, "\" tokenKeys in @telus-uds/system-themes/schema"));
67
+ if (!componentTokenSchema) {
68
+ throw new Error(`No "${componentName}" tokenKeys in @telus-uds/system-theme-tokens`);
67
69
  }
68
70
 
69
- return componentTokenNames;
71
+ return Object.keys(componentTokenSchema);
70
72
  };
71
73
  /**
72
74
  * Returns the subset of a set of tokens that may be accepted by the `tokens` prop of a named component
@@ -104,7 +106,7 @@ exports.getTokenNames = getTokenNames;
104
106
  const selectTokens = (specifier, tokens, prefix) => {
105
107
  const tokenNames = typeof specifier === 'string' ? getTokenNames(specifier) : specifier;
106
108
  const filteredTokens = tokenNames.reduce((validTokens, key) => {
107
- const prefixedKey = prefix ? "".concat(prefix).concat(key[0].toUpperCase()).concat(key.slice(1)) : key;
109
+ const prefixedKey = prefix ? `${prefix}${key[0].toUpperCase()}${key.slice(1)}` : key;
108
110
  const token = tokens[prefixedKey];
109
111
  return token !== undefined ? { ...validTokens,
110
112
  [key]: token
@@ -190,49 +192,14 @@ function getPropSelector(propTypes, regexp) {
190
192
  return props => Object.entries(props).reduce((items, [key, value]) => keys.includes(key) || regexp && regexp.test(key) ? { ...items,
191
193
  [key]: value
192
194
  } : items, {});
193
- } // React Native exports a11y prop definitions as TypeScript Interfaces, but no longer exports PropTypes
194
- // so we have to define them ourselves.
195
-
196
-
197
- const a11yPropTypes = {
198
- accessible: _propTypes.default.bool,
199
- focusable: _propTypes.default.bool,
200
- accessibilityElementsHidden: _propTypes.default.bool,
201
- accessibilityHint: _propTypes.default.string,
202
- // react-native-web ignores `accessibilityHint`
203
- accessibilityIgnoresInvertColors: _propTypes.default.bool,
204
- accessibilityLabel: _propTypes.default.string,
205
- accessibilityRole: _propTypes.default.string,
206
- accessibilityActions: _propTypes.default.arrayOf(_propTypes.default.shape({
207
- name: _propTypes.default.string.isRequired,
208
- label: _propTypes.default.string
209
- })),
210
- accessibilityLiveRegion: _propTypes.default.oneOf(['none', 'polite', 'assertive']),
211
- accessibilityState: _propTypes.default.shape({
212
- disabled: _propTypes.default.bool,
213
- selected: _propTypes.default.bool,
214
- checked: _propTypes.default.oneOf([true, false, 'mixed']),
215
- busy: _propTypes.default.bool,
216
- expanded: _propTypes.default.bool
217
- }),
218
- accessibilityValue: _propTypes.default.shape({
219
- min: _propTypes.default.number,
220
- max: _propTypes.default.number,
221
- now: _propTypes.default.number,
222
- text: _propTypes.default.string
223
- }),
224
- accessibilityViewIsModal: _propTypes.default.bool,
225
- importantForAccessibility: _propTypes.default.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
226
- onAccessibilityAction: _propTypes.default.func,
227
- onAccessibilityEscape: _propTypes.default.func,
228
- onAccessibilityTap: _propTypes.default.func
229
- };
195
+ }
196
+
230
197
  const a11yProps = {
231
198
  /**
232
199
  * Proptypes for recognised React Native accessiblity (a11y) props.
233
200
  * Spread this in the propTypes of components that accept React Native a11y props.
234
201
  */
235
- types: a11yPropTypes,
202
+ types: _propTypes2.default,
236
203
 
237
204
  /**
238
205
  * Filters a props object, returning only recognised React Native accessiblity (a11y) props.
@@ -240,7 +207,7 @@ const a11yProps = {
240
207
  * Where components accept React Native a11y props, pass { ...rest } from its props to this,
241
208
  * then spread the returned object into the component's props (usually its outer container).
242
209
  */
243
- select: getPropSelector(a11yPropTypes, /^aria-/),
210
+ select: getPropSelector(_propTypes2.default, /^aria-/),
244
211
 
245
212
  /**
246
213
  * Use this to disable focus for elements which are visually hidden but still rendered.
@@ -332,8 +299,11 @@ const pressHandlerPropTypes = {
332
299
  const pressPropTypes = { ...pressHandlerPropTypes,
333
300
  disabled: _propTypes.default.bool,
334
301
  ..._Platform.default.select({
335
- hitSlop: _propTypes.default.number,
336
- pressRetentionOffset: _propTypes.default.oneOfType([_propTypes.default.number, rectProp.propType])
302
+ web: {},
303
+ default: {
304
+ hitSlop: _propTypes.default.number,
305
+ pressRetentionOffset: _propTypes.default.oneOfType([_propTypes.default.number, rectProp.propType])
306
+ }
337
307
  })
338
308
  };
339
309
  const pressProps = {
@@ -352,7 +322,7 @@ exports.pressProps = pressProps;
352
322
  const linkPropTypes = { ...pressPropTypes,
353
323
  href: _propTypes.default.string,
354
324
  hrefAttrs: _propTypes.default.shape(hrefAttrsProp.types),
355
- ...a11yPropTypes
325
+ ..._propTypes2.default
356
326
  };
357
327
  const linkProps = {
358
328
  /**
@@ -366,7 +336,7 @@ const linkProps = {
366
336
  select: getPropSelector(linkPropTypes),
367
337
 
368
338
  /**
369
- * Turn hrefs into press handlers on Native and throw if not given `onPress` xor `href`.
339
+ * Turn hrefs into press handlers on Native and throw if not given `onPress` or `href`.
370
340
  *
371
341
  * @param {({ onPress?: () => void, href?: string })}
372
342
  * @returns {(() => void)|undefined} Returns a press handler, or undefined on web if href
@@ -376,24 +346,26 @@ const linkProps = {
376
346
  onPress,
377
347
  href
378
348
  }) => {
379
- // TODO: revisit this when integrating routing packages
380
- // https://github.com/telus/universal-design-system/issues/24
381
- if (href && onPress) {
382
- throw new Error("handleHref currently doesn't support both href and onPress");
383
- }
384
-
385
349
  if (!href && !onPress) {
386
350
  throw new Error('handleHref requires either href or onPress');
387
351
  }
388
352
 
389
- return _Platform.default.OS !== 'web' && href ? () => _Linking.default.openURL(href) : onPress;
353
+ return _Platform.default.select({
354
+ web: onPress,
355
+ default: (...args) => {
356
+ if (onPress) onPress(...args);
357
+ if (href) _Linking.default.openURL(href);
358
+ }
359
+ });
390
360
  }
391
361
  };
392
362
  exports.linkProps = linkProps;
393
363
  const viewPropTypes = {
394
364
  pointerEvents: _propTypes.default.oneOf(['all', 'none', 'box-only', 'box-none']),
395
365
  onLayout: _propTypes.default.func,
396
- nativeID: _propTypes.default.string
366
+ nativeID: _propTypes.default.string,
367
+ testID: _propTypes.default.string,
368
+ dataSet: _propTypes.default.object
397
369
  };
398
370
  const viewProps = {
399
371
  /**
@@ -496,23 +468,24 @@ const spacingProps = {
496
468
  }
497
469
  };
498
470
  /**
499
- * Returns a prop type validator which checks whether a prop is either a component or an array of components of a given
500
- * type, based on their `name` property.
471
+ * Returns a prop type validator which checks whether a prop is either a component or an array of
472
+ * components of a given type, based on their `name` or `displayName` properties.
501
473
  * Use an array of strings for `passedName` to accept more than one component type.
502
474
  * For an array the validation fails on the first occurrence of an invalid element.
503
475
  *
504
476
  * @param {string|string[]} passedName
505
- * @param {boolean} [checkDisplayName] - if `true` then `displayName` property on the component will be validated instead of `name`
506
477
  * @return {function}
507
478
  */
508
479
 
509
480
  exports.spacingProps = spacingProps;
510
481
 
511
- const componentPropType = (passedName, checkDisplayName = false) => {
482
+ const componentPropType = passedName => {
512
483
  const passedNames = typeof passedName === 'string' ? [passedName] : passedName;
513
484
 
514
485
  const checkProp = (props, propName, componentName) => {
515
- if (typeof props[propName] === 'undefined' || props[propName] === null) {
486
+ var _props$propName$type, _props$propName$type2;
487
+
488
+ if (props[propName] === undefined || props[propName] === null) {
516
489
  return undefined;
517
490
  }
518
491
 
@@ -522,12 +495,11 @@ const componentPropType = (passedName, checkDisplayName = false) => {
522
495
  return props[propName].map((_, index) => checkProp(props[propName], index, componentName)).find(Boolean);
523
496
  }
524
497
 
525
- const testNameInObject = () => typeof props[propName] === 'object' && (!checkDisplayName && !passedNames.includes(props[propName].type.name) || checkDisplayName && !passedNames.includes(props[propName].type.displayName));
526
-
527
- const testNameInFunction = () => typeof props[propName] === 'function' && (!checkDisplayName && !passedNames.includes(props[propName].name) || checkDisplayName && !passedNames.includes(props[propName].displayName));
498
+ const nameInProp = ((_props$propName$type = props[propName].type) === null || _props$propName$type === void 0 ? void 0 : _props$propName$type.displayName) || ((_props$propName$type2 = props[propName].type) === null || _props$propName$type2 === void 0 ? void 0 : _props$propName$type2.name);
528
499
 
529
- if (props[propName] && typeof props[propName] !== 'object' && typeof props[propName] !== 'function' || testNameInObject() || testNameInFunction()) {
530
- return new Error("".concat(componentName, ": Component passed to `").concat(propName, "` prop should be ").concat(passedNames.join(' or ')));
500
+ if (!nameInProp || !passedNames.includes(nameInProp)) {
501
+ const propDescription = nameInProp ? `Component ${nameInProp}` : typeof props[propName];
502
+ return new Error(`${componentName}: ${propDescription} was passed to \`${propName}\` prop; should be ${passedNames.join(' or ')}`);
531
503
  }
532
504
 
533
505
  return undefined;
@@ -535,7 +507,7 @@ const componentPropType = (passedName, checkDisplayName = false) => {
535
507
 
536
508
  const checkRequired = (props, propName, componentName) => {
537
509
  if (props[propName] === undefined) {
538
- return new Error("The prop `".concat(propName, "` is marked as required in `").concat(componentName, "`, but its value is ").concat(props[propName], "."));
510
+ return new Error(`The prop \`${propName}\` is marked as required in \`${componentName}\`, but its value is ${props[propName]}.`);
539
511
  }
540
512
 
541
513
  return undefined;
@@ -40,7 +40,7 @@ function useCopy({
40
40
  copy = DEFAULT_COPY
41
41
  }) {
42
42
  if (typeof copy === 'string') {
43
- return key => dictionary[copy][key];
43
+ return key => key ? dictionary[copy][key] : dictionary[copy];
44
44
  } // support overriding the entire copy dictionary with an object for a single language
45
45
 
46
46
 
@@ -7,11 +7,15 @@ exports.default = void 0;
7
7
 
8
8
  var _react = require("react");
9
9
 
10
- const doAction = action => {
10
+ const doAction = (action, event) => {
11
11
  var _window, _window$location;
12
12
 
13
- return typeof action === 'function' && action((_window = window) === null || _window === void 0 ? void 0 : (_window$location = _window.location) === null || _window$location === void 0 ? void 0 : _window$location.hash);
13
+ return typeof action === 'function' && action((_window = window) === null || _window === void 0 ? void 0 : (_window$location = _window.location) === null || _window$location === void 0 ? void 0 : _window$location.hash, event);
14
14
  };
15
+ /**
16
+ * @typedef {import('react').SyntheticEvent} SyntheticEvent
17
+ */
18
+
15
19
  /**
16
20
  * Calls a function (passing it the current hash) based on the current hash state on mount,
17
21
  * and binds it to call it again any time the hash changes.
@@ -20,7 +24,7 @@ const doAction = action => {
20
24
  *
21
25
  * On Native, this is replaced with a harmless no-op function as there is no direct equivalent.
22
26
  *
23
- * @param {(hash: string) => void} action - a function to act on the current hash on load and when it changes
27
+ * @param {(hash: string, event?: SyntheticEvent) => void} action - a function to act on the current hash on load and when it changes
24
28
  * @returns
25
29
  */
26
30
 
@@ -37,7 +41,7 @@ const useHash = (action, isReady = true) => {
37
41
  }, [isToDo, action]); // Bind the action for each hash change; do re-bind if the function changes.
38
42
 
39
43
  (0, _react.useEffect)(() => {
40
- const handleChange = () => doAction(action);
44
+ const handleChange = event => doAction(action, event);
41
45
 
42
46
  window.addEventListener('hashchange', handleChange);
43
47
  return () => window.removeEventListener('hashchange', handleChange);
@@ -110,8 +110,6 @@ const resolveSpacingOptions = space => {
110
110
 
111
111
 
112
112
  const useSpacingScale = spaceValue => {
113
- var _spaceValue$space;
114
-
115
113
  // In future, may need to consider window height as well as width, particularly for native apps,
116
114
  // e.g. to ensure designs don't look lost on large, tall, not-so-wide portrait screens.
117
115
  const viewport = (0, _ViewportProvider.useViewport)();
@@ -121,7 +119,7 @@ const useSpacingScale = spaceValue => {
121
119
  overridden,
122
120
  subtract = 0
123
121
  } = resolveSpacingOptions(spaceValue);
124
- const space = !overridden && ((_spaceValue$space = spaceValue === null || spaceValue === void 0 ? void 0 : spaceValue.space) !== null && _spaceValue$space !== void 0 ? _spaceValue$space : (0, _useResponsiveProp.resolveResponsiveProp)(spaceValue, viewport, 0));
122
+ const space = !overridden && ((spaceValue === null || spaceValue === void 0 ? void 0 : spaceValue.space) ?? (0, _useResponsiveProp.resolveResponsiveProp)(spaceValue, viewport, 0));
125
123
  const {
126
124
  size
127
125
  } = (0, _ThemeProvider.useThemeTokens)('spacingScale', tokens, variant, {
@@ -12,7 +12,7 @@ let id = 0;
12
12
  function useUniqueId(prefix = '') {
13
13
  const [uniqueId] = (0, _react.useState)(() => {
14
14
  id += 1;
15
- return "".concat(prefix, "-").concat(id);
15
+ return `${prefix}-${id}`;
16
16
  });
17
17
  return uniqueId;
18
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telus-uds/components-base",
3
- "version": "0.0.2-prerelease.9",
3
+ "version": "1.1.0",
4
4
  "description": "Base components",
5
5
  "keywords": [
6
6
  "base"
@@ -23,8 +23,11 @@
23
23
  "lint": "telus-standard",
24
24
  "lint:fix": "telus-standard --fix",
25
25
  "format": "prettier --write .",
26
- "build": "babel src -d lib",
27
- "dev": "yarn build --watch",
26
+ "build": "yarn build:code && yarn build:docs",
27
+ "build:code": "babel src -d lib",
28
+ "build:docs": "babel-node --plugins=react-docgen-alpha generate-component-docs.js",
29
+ "storybook": "start-storybook",
30
+ "dev": "yarn build:code --watch",
28
31
  "release": "standard-version"
29
32
  },
30
33
  "bugs": {
@@ -33,6 +36,9 @@
33
36
  "standard-engine": {
34
37
  "skip": true
35
38
  },
39
+ "browserslist": [
40
+ "extends @telus-uds/browserslist-config"
41
+ ],
36
42
  "peerDependencies": {
37
43
  "react": "^17.0.2",
38
44
  "react-dom": "*",
@@ -40,6 +46,7 @@
40
46
  "react-native-web": "^0.17.0"
41
47
  },
42
48
  "devDependencies": {
49
+ "@telus-uds/browserslist-config": "^1.0.0",
43
50
  "@testing-library/jest-native": "^4.0.1",
44
51
  "@testing-library/react-hooks": "^7.0.1",
45
52
  "@testing-library/react-native": "^7.2.0",
@@ -47,11 +54,12 @@
47
54
  },
48
55
  "dependencies": {
49
56
  "airbnb-prop-types": "^2.16.0",
50
- "@telus-uds/system-constants": "^0.0.2-prerelease.2",
51
- "@telus-uds/system-themes": "^0.0.2-prerelease.2",
57
+ "@telus-uds/system-constants": "^1.0.1-prerelease.0",
58
+ "@telus-uds/system-theme-tokens": "^1.1.0",
52
59
  "lodash.debounce": "^4.0.8",
53
60
  "lodash.merge": "^4.6.2",
54
61
  "prop-types": "^15.7.2",
55
- "react-native-picker-select": "^8.0.4"
62
+ "react-native-picker-select": "^8.0.4",
63
+ "semver": "^7.3.5"
56
64
  }
57
65
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "previousReleaseTag": "@telus-uds/components-base/v0.0.2-prerelease.8",
3
- "changelog": "### [0.0.2-prerelease.9](https://github.com/telus/universal-design-system/compare/@telus-uds/components-base/v0.0.2-prerelease.8...@telus-uds/components-base/v0.0.2-prerelease.9) (2021-12-29)\n\n\n### Features\n\n* **base:** implement IconButton ([#958](https://github.com/telus/universal-design-system/issues/958)) ([c3ff7dd](https://github.com/telus/universal-design-system/commit/c3ff7dd9fb76123d3ff8409e507c89ad91b7fef2))\n",
4
- "releaseTag": "@telus-uds/components-base/v0.0.2-prerelease.9",
5
- "newVersion": "0.0.2-prerelease.9",
2
+ "previousReleaseTag": "@telus-uds/components-base/v1.0.1",
3
+ "changelog": "## [1.1.0](https://github.com/telus/universal-design-system/compare/@telus-uds/components-base/v1.0.1...@telus-uds/components-base/v1.1.0) (2022-02-28)\n\n\n### Features\n\n* **component-base:** add some fixes to the list component ([#1226](https://github.com/telus/universal-design-system/issues/1226)) ([35a42e0](https://github.com/telus/universal-design-system/commit/35a42e05e23630696286e7c8607e7a1e8da9d6c2))\n* **ds-allium:** add an Allium List ([#1304](https://github.com/telus/universal-design-system/issues/1304)) ([2f91c74](https://github.com/telus/universal-design-system/commit/2f91c74d6c0b30f09089e8f11beb3739e5be1887))\n* generate component docs ([#1292](https://github.com/telus/universal-design-system/issues/1292)) ([e0c03a5](https://github.com/telus/universal-design-system/commit/e0c03a5c4b5186e97d424c5b1c4594b1dda8201f))\n\n\n### Bug Fixes\n\n* **base:** no SSR crash on transpiled global CSS ([#1311](https://github.com/telus/universal-design-system/issues/1311)) ([7bc585c](https://github.com/telus/universal-design-system/commit/7bc585cc76bf7d760b1630a6f90f336f2be2abda))\n",
4
+ "releaseTag": "@telus-uds/components-base/v1.1.0",
5
+ "newVersion": "1.1.0",
6
6
  "packageName": "@telus-uds/components-base"
7
7
  }
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import { Platform, StyleSheet, View } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
4
 
@@ -10,7 +10,7 @@ import { a11yProps } from '../utils/propTypes'
10
10
  * It should be used to add selectable screen-reader-only text to the main document flow,
11
11
  * as a sibling to blocks of text like paragraphs and interactive elements like buttons.
12
12
  */
13
- const A11yText = ({ text, heading, ...rest }) => {
13
+ const A11yText = forwardRef(({ text, heading, ...rest }, ref) => {
14
14
  const a11y = a11yProps.select({
15
15
  // On iOS, needs `accessible` to be true to be focusable without non-a11y content.
16
16
  // On Web, `accessible` causes RNW to set attributes that may make the element be treated as a group.
@@ -21,8 +21,10 @@ const A11yText = ({ text, heading, ...rest }) => {
21
21
  accessibilityRole: heading ? 'header' : 'text',
22
22
  ...rest
23
23
  })
24
- return <View style={styles.invisible} {...a11y} />
25
- }
24
+ return <View style={styles.invisible} ref={ref} {...a11y} />
25
+ })
26
+
27
+ A11yText.displayName = 'A11yText'
26
28
 
27
29
  A11yText.propTypes = {
28
30
  text: PropTypes.string.isRequired,
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import { DURATION, MIN_EMPTY_ANGLE, MIN_STROKE_ANGLE, BEZIER, propTypes } from './shared'
3
3
 
4
4
  const SVG_RADIUS = 20
@@ -20,8 +20,9 @@ const bezierProps = {
20
20
  }
21
21
  // We're using svg rather than css here to define the animation to avoid needing to introduce css injection mechanism
22
22
  // It's possible to replicate this functionality with RNW animations, but it snags on chrome at least, see https://github.com/telus/universal-design-system/pull/477 for details.
23
- const Spinner = ({ size, color, thickness, label }) => (
23
+ const Spinner = forwardRef(({ size, color, thickness, label }, ref) => (
24
24
  <svg
25
+ ref={ref}
25
26
  width={`${size}px`}
26
27
  height={`${size}px`}
27
28
  viewBox="0 0 48 48"
@@ -62,7 +63,8 @@ const Spinner = ({ size, color, thickness, label }) => (
62
63
  </circle>
63
64
  </g>
64
65
  </svg>
65
- )
66
+ ))
67
+ Spinner.displayName = 'Spinner'
66
68
 
67
69
  Spinner.propTypes = propTypes
68
70
 
@@ -1,11 +1,11 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import { Animated, Easing, StyleSheet, View } from 'react-native'
3
3
  import { DURATION, MIN_EMPTY_ANGLE, MIN_STROKE_ANGLE, BEZIER, propTypes } from './shared'
4
4
 
5
5
  const ea = MIN_EMPTY_ANGLE / 2
6
6
  const sa = MIN_STROKE_ANGLE / 2
7
7
 
8
- const Spinner = ({ size, color, thickness, label }) => {
8
+ const Spinner = forwardRef(({ size, color, thickness, label }, ref) => {
9
9
  const { current: timer } = React.useRef(new Animated.Value(0))
10
10
 
11
11
  React.useLayoutEffect(() => {
@@ -31,6 +31,7 @@ const Spinner = ({ size, color, thickness, label }) => {
31
31
  // Credit to https://github.com/n4kz/react-native-indicators and https://github.com/callstack/react-native-paper for this
32
32
  return (
33
33
  <View
34
+ ref={ref}
34
35
  style={[styles.container]}
35
36
  accessible
36
37
  accessibilityLabel={label}
@@ -109,7 +110,8 @@ const Spinner = ({ size, color, thickness, label }) => {
109
110
  </Animated.View>
110
111
  </View>
111
112
  )
112
- }
113
+ })
114
+ Spinner.displayName = 'Spinner'
113
115
 
114
116
  Spinner.propTypes = propTypes
115
117