@telus-uds/components-base 0.0.2-prerelease.6 → 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 (458) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.ultra.cache.json +1 -1
  3. package/CHANGELOG.md +85 -0
  4. package/README.md +4 -2
  5. package/__fixtures__/Accessible.js +33 -0
  6. package/__fixtures__/Accessible.native.js +32 -0
  7. package/__fixtures__/test-utils.js +25 -0
  8. package/__fixtures__/testTheme.js +851 -57
  9. package/__tests__/ActivityIndicator/ActivityIndicator.test.jsx +1 -1
  10. package/__tests__/Button/ButtonBase.test.jsx +2 -31
  11. package/__tests__/Button/ButtonGroup.test.jsx +6 -7
  12. package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
  13. package/__tests__/Checkbox/CheckboxGroup.test.jsx +246 -0
  14. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  15. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  16. package/__tests__/Icon/Icon.test.jsx +3 -3
  17. package/__tests__/IconButton/IconButton.test.jsx +52 -0
  18. package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
  19. package/__tests__/Link/LinkBase.test.jsx +0 -14
  20. package/__tests__/List/List.test.jsx +60 -0
  21. package/__tests__/Modal/Modal.test.jsx +47 -0
  22. package/__tests__/Notification/Notification.test.jsx +20 -0
  23. package/__tests__/Pagination/Pagination.test.jsx +2 -2
  24. package/__tests__/Progress/Progress.test.jsx +79 -0
  25. package/__tests__/Radio/Radio.test.jsx +87 -0
  26. package/__tests__/Radio/RadioGroup.test.jsx +220 -0
  27. package/__tests__/RadioCard/RadioCard.test.jsx +87 -0
  28. package/__tests__/RadioCard/RadioCardGroup.test.jsx +246 -0
  29. package/__tests__/Search/Search.test.jsx +73 -0
  30. package/__tests__/Select/Select.test.jsx +94 -0
  31. package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
  32. package/__tests__/StepTracker/StepTracker.test.jsx +94 -0
  33. package/__tests__/Tabs/Tabs.test.jsx +40 -0
  34. package/__tests__/Tags/Tags.test.jsx +327 -0
  35. package/__tests__/TextInput/TextArea.test.jsx +35 -0
  36. package/__tests__/TextInput/{TextInput.test.jsx → TextInputBase.test.jsx} +29 -50
  37. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  38. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  39. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  40. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  41. package/__tests__/utils/children.test.jsx +128 -0
  42. package/__tests__/utils/input.test.js +59 -1
  43. package/__tests__/utils/semantics.test.jsx +43 -0
  44. package/__tests__/utils/useCopy.test.js +14 -3
  45. package/babel.config.js +20 -0
  46. package/jest.config.js +8 -3
  47. package/lib/A11yInfoProvider/index.js +54 -26
  48. package/lib/A11yText/index.js +45 -17
  49. package/lib/ActivityIndicator/Spinner.js +81 -0
  50. package/lib/ActivityIndicator/Spinner.native.js +129 -91
  51. package/lib/ActivityIndicator/index.js +28 -12
  52. package/lib/ActivityIndicator/shared.js +27 -12
  53. package/lib/BaseProvider/index.js +34 -11
  54. package/lib/Box/Box.js +152 -35
  55. package/lib/Box/index.js +13 -2
  56. package/lib/Button/Button.js +41 -12
  57. package/lib/Button/ButtonBase.js +125 -97
  58. package/lib/Button/ButtonGroup.js +112 -89
  59. package/lib/Button/ButtonLink.js +48 -17
  60. package/lib/Button/index.js +31 -4
  61. package/lib/Button/propTypes.js +32 -9
  62. package/lib/Card/Card.js +38 -41
  63. package/lib/Card/CardBase.js +85 -0
  64. package/lib/Card/PressableCardBase.js +141 -0
  65. package/lib/Card/index.js +40 -2
  66. package/lib/Checkbox/Checkbox.js +355 -0
  67. package/lib/Checkbox/CheckboxGroup.js +241 -0
  68. package/lib/Checkbox/CheckboxInput.js +74 -0
  69. package/lib/Checkbox/CheckboxInput.native.js +14 -0
  70. package/lib/Checkbox/index.js +21 -0
  71. package/lib/Divider/Divider.js +59 -28
  72. package/lib/Divider/index.js +13 -2
  73. package/lib/ExpandCollapse/Accordion.js +26 -7
  74. package/lib/ExpandCollapse/Control.js +60 -31
  75. package/lib/ExpandCollapse/ExpandCollapse.js +50 -28
  76. package/lib/ExpandCollapse/Panel.js +83 -44
  77. package/lib/ExpandCollapse/index.js +25 -7
  78. package/lib/Feedback/Feedback.js +90 -39
  79. package/lib/Feedback/index.js +13 -2
  80. package/lib/Fieldset/Fieldset.js +165 -0
  81. package/lib/Fieldset/FieldsetContainer.js +46 -0
  82. package/lib/Fieldset/FieldsetContainer.native.js +38 -0
  83. package/lib/Fieldset/Legend.js +38 -0
  84. package/lib/Fieldset/Legend.native.js +48 -0
  85. package/lib/Fieldset/cssReset.js +21 -0
  86. package/lib/Fieldset/index.js +13 -0
  87. package/lib/FlexGrid/Col/Col.js +73 -41
  88. package/lib/FlexGrid/Col/index.js +13 -2
  89. package/lib/FlexGrid/FlexGrid.js +99 -49
  90. package/lib/FlexGrid/Row/Row.js +58 -30
  91. package/lib/FlexGrid/Row/index.js +13 -2
  92. package/lib/FlexGrid/helpers/index.js +9 -1
  93. package/lib/FlexGrid/index.js +13 -2
  94. package/lib/FlexGrid/providers/GutterContext.js +15 -3
  95. package/lib/HorizontalScroll/HorizontalScroll.js +200 -0
  96. package/lib/HorizontalScroll/HorizontalScrollButton.js +127 -0
  97. package/lib/HorizontalScroll/ScrollViewEnd.js +66 -0
  98. package/lib/HorizontalScroll/ScrollViewEnd.native.js +41 -0
  99. package/lib/HorizontalScroll/dictionary.js +18 -0
  100. package/lib/HorizontalScroll/index.js +35 -0
  101. package/lib/HorizontalScroll/itemPositions.js +128 -0
  102. package/lib/Icon/Icon.js +62 -50
  103. package/lib/Icon/IconText.js +101 -0
  104. package/lib/Icon/index.js +31 -3
  105. package/lib/IconButton/IconButton.js +140 -0
  106. package/lib/IconButton/index.js +13 -0
  107. package/lib/InputLabel/InputLabel.js +102 -40
  108. package/lib/InputLabel/LabelContent.js +41 -0
  109. package/lib/InputLabel/LabelContent.native.js +32 -6
  110. package/lib/InputLabel/index.js +13 -2
  111. package/lib/InputSupports/InputSupports.js +109 -0
  112. package/lib/InputSupports/index.js +13 -0
  113. package/lib/InputSupports/propTypes.js +66 -0
  114. package/lib/InputSupports/useInputSupports.js +41 -0
  115. package/lib/Link/ChevronLink.js +58 -31
  116. package/lib/Link/InlinePressable.js +56 -0
  117. package/lib/Link/InlinePressable.native.js +102 -0
  118. package/lib/Link/Link.js +39 -15
  119. package/lib/Link/LinkBase.js +126 -150
  120. package/lib/Link/TextButton.js +53 -18
  121. package/lib/Link/index.js +39 -4
  122. package/lib/List/List.js +81 -0
  123. package/lib/List/ListItem.js +245 -0
  124. package/lib/List/index.js +13 -0
  125. package/lib/Modal/Modal.js +231 -0
  126. package/lib/Modal/dictionary.js +16 -0
  127. package/lib/Modal/index.js +13 -0
  128. package/lib/Notification/Notification.js +216 -0
  129. package/lib/Notification/dictionary.js +15 -0
  130. package/lib/Notification/index.js +13 -0
  131. package/lib/Pagination/PageButton.js +58 -48
  132. package/lib/Pagination/Pagination.js +78 -43
  133. package/lib/Pagination/SideButton.js +79 -58
  134. package/lib/Pagination/dictionary.js +9 -2
  135. package/lib/Pagination/index.js +13 -2
  136. package/lib/Pagination/usePagination.js +12 -2
  137. package/lib/Progress/Progress.js +104 -0
  138. package/lib/Progress/ProgressBar.js +157 -0
  139. package/lib/Progress/ProgressBarBackground.js +61 -0
  140. package/lib/Progress/index.js +16 -0
  141. package/lib/Radio/Radio.js +293 -0
  142. package/lib/Radio/RadioButton.js +152 -0
  143. package/lib/Radio/RadioGroup.js +244 -0
  144. package/lib/Radio/RadioInput.js +76 -0
  145. package/lib/Radio/RadioInput.native.js +14 -0
  146. package/lib/Radio/index.js +21 -0
  147. package/lib/RadioCard/RadioCard.js +244 -0
  148. package/lib/RadioCard/RadioCardGroup.js +252 -0
  149. package/lib/RadioCard/index.js +21 -0
  150. package/lib/Search/Search.js +254 -0
  151. package/lib/Search/dictionary.js +19 -0
  152. package/lib/Search/index.js +13 -0
  153. package/lib/Select/Group.js +33 -0
  154. package/lib/Select/Group.native.js +25 -0
  155. package/lib/Select/Item.js +29 -0
  156. package/lib/Select/Item.native.js +19 -0
  157. package/lib/Select/Picker.js +84 -0
  158. package/lib/Select/Picker.native.js +130 -0
  159. package/lib/Select/Select.js +342 -0
  160. package/lib/Select/index.js +19 -0
  161. package/lib/SideNav/Item.js +63 -37
  162. package/lib/SideNav/ItemContent.js +41 -15
  163. package/lib/SideNav/ItemsGroup.js +55 -31
  164. package/lib/SideNav/SideNav.js +100 -73
  165. package/lib/SideNav/index.js +15 -1
  166. package/lib/Skeleton/Skeleton.js +137 -0
  167. package/lib/Skeleton/index.js +13 -0
  168. package/lib/Skeleton/skeleton.constant.js +12 -0
  169. package/lib/Skeleton/skeletonWebAnimation.js +27 -0
  170. package/lib/Skeleton/useSkeletonNativeAnimation.js +37 -0
  171. package/lib/Spacer/Spacer.js +49 -18
  172. package/lib/Spacer/index.js +13 -2
  173. package/lib/StackView/StackView.js +71 -31
  174. package/lib/StackView/StackWrap.js +43 -13
  175. package/lib/StackView/StackWrap.native.js +13 -2
  176. package/lib/StackView/StackWrapBox.js +76 -29
  177. package/lib/StackView/StackWrapGap.js +56 -26
  178. package/lib/StackView/common.js +23 -6
  179. package/lib/StackView/getStackedContent.js +47 -17
  180. package/lib/StackView/index.js +29 -5
  181. package/lib/StepTracker/Step.js +245 -0
  182. package/lib/StepTracker/StepTracker.js +202 -0
  183. package/lib/StepTracker/dictionary.js +17 -0
  184. package/lib/StepTracker/index.js +13 -0
  185. package/lib/Tabs/Tabs.js +124 -0
  186. package/lib/Tabs/TabsItem.js +238 -0
  187. package/lib/Tabs/index.js +13 -0
  188. package/lib/Tags/Tags.js +266 -0
  189. package/lib/Tags/index.js +13 -0
  190. package/lib/TextInput/TextArea.js +111 -0
  191. package/lib/TextInput/TextInput.js +50 -304
  192. package/lib/TextInput/TextInputBase.js +256 -0
  193. package/lib/TextInput/index.js +23 -2
  194. package/lib/TextInput/propTypes.js +42 -0
  195. package/lib/ThemeProvider/ThemeProvider.js +46 -18
  196. package/lib/ThemeProvider/index.js +61 -6
  197. package/lib/ThemeProvider/useSetTheme.js +19 -5
  198. package/lib/ThemeProvider/useTheme.js +13 -4
  199. package/lib/ThemeProvider/useThemeTokens.js +86 -19
  200. package/lib/ThemeProvider/utils/index.js +31 -2
  201. package/lib/ThemeProvider/utils/styles.js +50 -14
  202. package/lib/ThemeProvider/utils/theme-tokens.js +121 -12
  203. package/lib/ToggleSwitch/ToggleSwitch.js +86 -57
  204. package/lib/ToggleSwitch/index.js +13 -2
  205. package/lib/Tooltip/{Backdrop.web.js → Backdrop.js} +20 -8
  206. package/lib/Tooltip/Backdrop.native.js +39 -15
  207. package/lib/Tooltip/Tooltip.js +117 -74
  208. package/lib/Tooltip/dictionary.js +9 -2
  209. package/lib/Tooltip/getTooltipPosition.js +9 -1
  210. package/lib/Tooltip/index.js +13 -2
  211. package/lib/TooltipButton/TooltipButton.js +57 -38
  212. package/lib/TooltipButton/index.js +13 -2
  213. package/lib/Typography/Typography.js +86 -55
  214. package/lib/Typography/index.js +13 -2
  215. package/lib/ViewportProvider/ViewportProvider.js +34 -13
  216. package/lib/ViewportProvider/index.js +28 -3
  217. package/lib/ViewportProvider/useViewport.js +15 -3
  218. package/lib/ViewportProvider/useViewportListener.js +24 -10
  219. package/lib/index.js +539 -26
  220. package/lib/utils/a11y/index.js +31 -0
  221. package/lib/utils/a11y/semantics.js +173 -0
  222. package/lib/utils/a11y/textSize.js +49 -0
  223. package/lib/utils/animation/index.js +15 -2
  224. package/lib/utils/animation/useVerticalExpandAnimation.js +27 -10
  225. package/lib/utils/children.js +134 -0
  226. package/lib/utils/index.js +163 -7
  227. package/lib/utils/info/index.js +19 -0
  228. package/lib/utils/info/platform/index.js +23 -0
  229. package/lib/utils/info/platform/platform.android.js +8 -0
  230. package/lib/utils/info/platform/platform.ios.js +8 -0
  231. package/lib/utils/info/platform/platform.js +8 -0
  232. package/lib/utils/info/platform/platform.native.js +11 -0
  233. package/lib/utils/info/versions.js +16 -0
  234. package/lib/utils/input.js +53 -25
  235. package/lib/utils/pressability.js +120 -0
  236. package/lib/utils/propTypes.js +338 -132
  237. package/lib/utils/useCopy.js +40 -5
  238. package/lib/utils/useHash.js +52 -0
  239. package/lib/utils/useHash.native.js +15 -0
  240. package/lib/utils/useResponsiveProp.js +21 -9
  241. package/lib/utils/useSpacingScale.js +19 -9
  242. package/lib/utils/useUniqueId.js +12 -3
  243. package/package.json +16 -10
  244. package/release-context.json +4 -4
  245. package/src/A11yText/index.jsx +6 -4
  246. package/src/ActivityIndicator/{Spinner.web.jsx → Spinner.jsx} +5 -3
  247. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  248. package/src/Box/Box.jsx +131 -39
  249. package/src/Button/Button.jsx +13 -6
  250. package/src/Button/ButtonBase.jsx +134 -125
  251. package/src/Button/ButtonGroup.jsx +85 -86
  252. package/src/Button/ButtonLink.jsx +22 -7
  253. package/src/Button/propTypes.js +12 -2
  254. package/src/Card/Card.jsx +5 -31
  255. package/src/Card/CardBase.jsx +58 -0
  256. package/src/Card/PressableCardBase.jsx +119 -0
  257. package/src/Card/index.js +3 -0
  258. package/src/Checkbox/Checkbox.jsx +284 -0
  259. package/src/Checkbox/CheckboxGroup.jsx +206 -0
  260. package/src/Checkbox/CheckboxInput.jsx +55 -0
  261. package/src/Checkbox/CheckboxInput.native.jsx +6 -0
  262. package/src/Checkbox/index.js +5 -0
  263. package/src/Divider/Divider.jsx +7 -4
  264. package/src/ExpandCollapse/Accordion.jsx +3 -2
  265. package/src/ExpandCollapse/Control.jsx +40 -43
  266. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  267. package/src/ExpandCollapse/Panel.jsx +69 -63
  268. package/src/Feedback/Feedback.jsx +42 -30
  269. package/src/Fieldset/Fieldset.jsx +136 -0
  270. package/src/Fieldset/FieldsetContainer.jsx +31 -0
  271. package/src/Fieldset/FieldsetContainer.native.jsx +19 -0
  272. package/src/Fieldset/Legend.jsx +21 -0
  273. package/src/Fieldset/Legend.native.jsx +27 -0
  274. package/src/Fieldset/cssReset.js +14 -0
  275. package/src/Fieldset/index.js +3 -0
  276. package/src/FlexGrid/Col/Col.jsx +139 -132
  277. package/src/FlexGrid/FlexGrid.jsx +79 -51
  278. package/src/FlexGrid/Row/Row.jsx +55 -48
  279. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  280. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  281. package/src/HorizontalScroll/ScrollViewEnd.jsx +53 -0
  282. package/src/HorizontalScroll/ScrollViewEnd.native.jsx +24 -0
  283. package/src/HorizontalScroll/dictionary.js +11 -0
  284. package/src/HorizontalScroll/index.js +17 -0
  285. package/src/HorizontalScroll/itemPositions.js +101 -0
  286. package/src/Icon/Icon.jsx +46 -49
  287. package/src/Icon/IconText.jsx +68 -0
  288. package/src/Icon/index.js +3 -2
  289. package/src/IconButton/IconButton.jsx +114 -0
  290. package/src/IconButton/index.js +3 -0
  291. package/src/InputLabel/InputLabel.jsx +57 -35
  292. package/src/InputLabel/LabelContent.jsx +21 -0
  293. package/src/InputLabel/LabelContent.native.jsx +11 -2
  294. package/src/InputSupports/InputSupports.jsx +70 -0
  295. package/src/InputSupports/index.js +3 -0
  296. package/src/InputSupports/propTypes.js +44 -0
  297. package/src/InputSupports/useInputSupports.js +30 -0
  298. package/src/Link/ChevronLink.jsx +34 -21
  299. package/src/Link/InlinePressable.jsx +39 -0
  300. package/src/Link/InlinePressable.native.jsx +75 -0
  301. package/src/Link/Link.jsx +23 -13
  302. package/src/Link/LinkBase.jsx +98 -170
  303. package/src/Link/TextButton.jsx +37 -16
  304. package/src/Link/index.js +2 -1
  305. package/src/List/List.jsx +48 -0
  306. package/src/List/ListItem.jsx +182 -0
  307. package/src/List/index.js +3 -0
  308. package/src/Modal/Modal.jsx +190 -0
  309. package/src/Modal/dictionary.js +9 -0
  310. package/src/Modal/index.js +3 -0
  311. package/src/Notification/Notification.jsx +164 -0
  312. package/src/Notification/dictionary.js +8 -0
  313. package/src/Notification/index.js +3 -0
  314. package/src/Pagination/PageButton.jsx +42 -49
  315. package/src/Pagination/Pagination.jsx +88 -92
  316. package/src/Pagination/SideButton.jsx +58 -66
  317. package/src/Progress/Progress.jsx +78 -0
  318. package/src/Progress/ProgressBar.jsx +123 -0
  319. package/src/Progress/ProgressBarBackground.jsx +36 -0
  320. package/src/Progress/index.js +6 -0
  321. package/src/Radio/Radio.jsx +240 -0
  322. package/src/Radio/RadioButton.jsx +142 -0
  323. package/src/Radio/RadioGroup.jsx +209 -0
  324. package/src/Radio/RadioInput.jsx +57 -0
  325. package/src/Radio/RadioInput.native.jsx +6 -0
  326. package/src/Radio/index.js +5 -0
  327. package/src/RadioCard/RadioCard.jsx +198 -0
  328. package/src/RadioCard/RadioCardGroup.jsx +218 -0
  329. package/src/RadioCard/index.js +5 -0
  330. package/src/Search/Search.jsx +225 -0
  331. package/src/Search/dictionary.js +12 -0
  332. package/src/Search/index.js +3 -0
  333. package/src/Select/Group.jsx +15 -0
  334. package/src/Select/Group.native.jsx +14 -0
  335. package/src/Select/Item.jsx +11 -0
  336. package/src/Select/Item.native.jsx +10 -0
  337. package/src/Select/Picker.jsx +74 -0
  338. package/src/Select/Picker.native.jsx +102 -0
  339. package/src/Select/Select.jsx +298 -0
  340. package/src/Select/index.js +8 -0
  341. package/src/SideNav/Item.jsx +54 -47
  342. package/src/SideNav/ItemsGroup.jsx +50 -43
  343. package/src/SideNav/SideNav.jsx +68 -60
  344. package/src/Skeleton/Skeleton.jsx +94 -0
  345. package/src/Skeleton/index.js +3 -0
  346. package/src/Skeleton/skeleton.constant.js +3 -0
  347. package/src/Skeleton/skeletonWebAnimation.js +13 -0
  348. package/src/Skeleton/useSkeletonNativeAnimation.js +27 -0
  349. package/src/Spacer/Spacer.jsx +11 -4
  350. package/src/StackView/StackView.jsx +53 -23
  351. package/src/StackView/StackWrap.jsx +16 -7
  352. package/src/StackView/StackWrapBox.jsx +62 -28
  353. package/src/StackView/StackWrapGap.jsx +46 -24
  354. package/src/StackView/common.jsx +3 -2
  355. package/src/StackView/getStackedContent.jsx +8 -2
  356. package/src/StepTracker/Step.jsx +202 -0
  357. package/src/StepTracker/StepTracker.jsx +174 -0
  358. package/src/StepTracker/dictionary.js +10 -0
  359. package/src/StepTracker/index.js +3 -0
  360. package/src/Tabs/Tabs.jsx +97 -0
  361. package/src/Tabs/TabsItem.jsx +212 -0
  362. package/src/Tabs/index.js +3 -0
  363. package/src/Tags/Tags.jsx +219 -0
  364. package/src/Tags/index.js +3 -0
  365. package/src/TextInput/TextArea.jsx +79 -0
  366. package/src/TextInput/TextInput.jsx +18 -284
  367. package/src/TextInput/TextInputBase.jsx +217 -0
  368. package/src/TextInput/index.js +2 -1
  369. package/src/TextInput/propTypes.js +29 -0
  370. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  371. package/src/ThemeProvider/useSetTheme.js +4 -0
  372. package/src/ThemeProvider/useThemeTokens.js +56 -5
  373. package/src/ThemeProvider/utils/styles.js +18 -5
  374. package/src/ThemeProvider/utils/theme-tokens.js +74 -5
  375. package/src/ToggleSwitch/ToggleSwitch.jsx +51 -53
  376. package/src/Tooltip/{Backdrop.web.jsx → Backdrop.jsx} +0 -0
  377. package/src/Tooltip/Tooltip.jsx +135 -131
  378. package/src/TooltipButton/TooltipButton.jsx +23 -27
  379. package/src/Typography/Typography.jsx +72 -59
  380. package/src/index.js +31 -3
  381. package/src/utils/a11y/index.js +2 -0
  382. package/src/utils/a11y/semantics.js +162 -0
  383. package/src/utils/a11y/textSize.js +30 -0
  384. package/src/utils/children.jsx +119 -0
  385. package/src/utils/index.js +6 -0
  386. package/src/utils/info/index.js +8 -0
  387. package/src/utils/info/platform/index.js +11 -0
  388. package/src/utils/info/platform/platform.android.js +1 -0
  389. package/src/utils/info/platform/platform.ios.js +1 -0
  390. package/src/utils/info/platform/platform.js +1 -0
  391. package/src/utils/info/platform/platform.native.js +4 -0
  392. package/src/utils/info/versions.js +6 -0
  393. package/src/utils/input.js +36 -25
  394. package/src/utils/pressability.js +96 -0
  395. package/src/utils/propTypes.js +291 -90
  396. package/src/utils/useCopy.js +30 -4
  397. package/src/utils/useHash.js +39 -0
  398. package/src/utils/useHash.native.js +6 -0
  399. package/stories/A11yText/A11yText.stories.jsx +6 -10
  400. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +1 -1
  401. package/stories/Box/Box.stories.jsx +1 -1
  402. package/stories/Button/Button.stories.jsx +7 -2
  403. package/stories/Button/ButtonGroup.stories.jsx +1 -1
  404. package/stories/Button/ButtonLink.stories.jsx +1 -1
  405. package/stories/Card/Card.stories.jsx +1 -1
  406. package/stories/Checkbox/Checkbox.stories.jsx +94 -0
  407. package/stories/Divider/Divider.stories.jsx +1 -1
  408. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +2 -2
  409. package/stories/Feedback/Feedback.stories.jsx +5 -6
  410. package/stories/FlexGrid/01 FlexGrid.stories.jsx +1 -1
  411. package/stories/FlexGrid/02 Row.stories.jsx +1 -1
  412. package/stories/FlexGrid/03 Col.stories.jsx +1 -1
  413. package/stories/Icon/Icon.stories.jsx +27 -7
  414. package/stories/IconButton/IconButton.stories.jsx +50 -0
  415. package/stories/InputLabel/InputLabel.stories.jsx +1 -1
  416. package/stories/Link/ChevronLink.stories.jsx +1 -1
  417. package/stories/Link/Link.stories.jsx +28 -18
  418. package/stories/Link/TextButton.stories.jsx +1 -1
  419. package/stories/List/List.stories.jsx +117 -0
  420. package/stories/Modal/Modal.stories.jsx +29 -0
  421. package/stories/Notification/Notification.stories.jsx +82 -0
  422. package/stories/Pagination/Pagination.stories.jsx +1 -1
  423. package/stories/Progress/Progress.stories.jsx +93 -0
  424. package/stories/Radio/Radio.stories.jsx +100 -0
  425. package/stories/RadioCard/RadioCard.stories.jsx +98 -0
  426. package/stories/Search/Search.stories.jsx +16 -0
  427. package/stories/Select/Select.stories.jsx +55 -0
  428. package/stories/SideNav/SideNav.stories.jsx +1 -1
  429. package/stories/SideNav/SideNavItem.stories.jsx +1 -1
  430. package/stories/SideNav/SideNavItemsGroup.stories.jsx +1 -1
  431. package/stories/Skeleton/Skeleton.stories.jsx +36 -0
  432. package/stories/Spacer/Spacer.stories.jsx +1 -1
  433. package/stories/StackView/StackView.stories.jsx +1 -1
  434. package/stories/StackView/StackWrap.stories.jsx +1 -1
  435. package/stories/StepTracker/StepTracker.stories.jsx +71 -0
  436. package/stories/Tabs/Tabs.stories.jsx +97 -0
  437. package/stories/Tags/Tags.stories.jsx +69 -0
  438. package/stories/TextInput/TextArea.stories.jsx +100 -0
  439. package/stories/TextInput/TextInput.stories.jsx +1 -1
  440. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +1 -1
  441. package/stories/Tooltip/Tooltip.stories.jsx +1 -1
  442. package/stories/TooltipButton/TooltipButton.stories.jsx +1 -1
  443. package/stories/Typography/Typography.stories.jsx +1 -1
  444. package/stories/{platform-supports.web.jsx → platform-supports.jsx} +1 -1
  445. package/stories/supports.jsx +38 -4
  446. package/__fixtures__/accessible.icon.svg +0 -6
  447. package/babel.config.json +0 -8
  448. package/docs/Contributing.stories.mdx +0 -9
  449. package/docs/Fonts.stories.mdx +0 -104
  450. package/docs/Icons.stories.mdx +0 -144
  451. package/docs/Introduction.stories.mdx +0 -9
  452. package/lib/ActivityIndicator/Spinner.web.js +0 -55
  453. package/lib/InputLabel/LabelContent.web.js +0 -17
  454. package/lib/config/svgr-icons-web.js +0 -9
  455. package/lib/config/svgr-icons.js +0 -52
  456. package/src/InputLabel/LabelContent.web.jsx +0 -13
  457. package/src/config/svgr-icons-web.js +0 -11
  458. package/src/config/svgr-icons.js +0 -46
@@ -0,0 +1,162 @@
1
+ import { Platform } from 'react-native'
2
+ /**
3
+ * @typedef {import('react-native').AccessibilityRole} AccessibilityRole
4
+ */
5
+
6
+ /**
7
+ * This is based on the role-to-tag mapping that React Native Web uses to set HTML tags.
8
+ * It's not exported in any way from RNW, so we need to keep this up-to-date manually.
9
+ * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
10
+ *
11
+ * Note: every role in this list is a web-only aria-role. There is no overlap between _these_ web tags
12
+ * or roles and native accessibilityRoles. Only h1, h2, h3 etc map to an RN equivalent ("heading").
13
+ *
14
+ * - RN "summary" native role maps to "region" aria-role, but setting `<section>`/"region" does not
15
+ * set React Native's "summary" role, which has a much narrower use case.
16
+ * - `<Header>`/"Banner" also do not map to RN's "heading". Only h1 / h2 etc map to RN "heading".
17
+ *
18
+ * Therefore, all of these tags / roles default to no accessibilityRole in native apps. This is not wrong:
19
+ * in general, RN accessibilityRoles tend to be more about interaction and less about semantics than web roles.
20
+ *
21
+ * RNW's one-way mapping of React Native accessibilityRoles to web aria-roles:
22
+ * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/modules/AccessibilityUtil/propsToAriaRole.js
23
+ */
24
+ // Adding `/** @type {const} */ ({...})` denotes object content as `readonly` in many IDEs
25
+ // eslint-disable-next-line prettier/prettier
26
+ const rolesToTags = /** @type {const} */ ({
27
+ article: 'article',
28
+ banner: 'header',
29
+ blockquote: 'blockquote',
30
+ code: 'code',
31
+ complementary: 'aside',
32
+ contentinfo: 'footer',
33
+ deletion: 'del',
34
+ emphasis: 'em',
35
+ figure: 'figure',
36
+ insertion: 'ins',
37
+ form: 'form',
38
+ list: 'ul',
39
+ listitem: 'li',
40
+ main: 'main',
41
+ navigation: 'nav',
42
+ region: 'section',
43
+ strong: 'strong',
44
+
45
+ // Add special cases that are in RNW's function logic but not in its mapping object
46
+ label: 'label'
47
+ // eslint-disable-next-line prettier/prettier
48
+ })
49
+
50
+ // Invert React Native Web's mapping, so a tag gets the role that gets that tag
51
+ export const tagsToRoles = Object.fromEntries(
52
+ Object.entries(rolesToTags).map(([key, value]) => [value, key])
53
+ )
54
+
55
+ /**
56
+ * Heading HTML tags map to the "heading" accessibilityRole in native apps, which is similar
57
+ * to headings on web but without the expectation of a hierarchy of headings within one screen.
58
+ */
59
+ // eslint-disable-next-line prettier/prettier
60
+ export const headingTags = /** @type {const} */ (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
61
+ /**
62
+ * @typedef {typeof headingTags[number]} HeadingTag
63
+ */
64
+
65
+ /**
66
+ * All HTML tags that may be set via RNW accesibility props alone and therefore may be set using
67
+ * the getA11yPropsFromHtmlTag function without changing other behaviour.
68
+ *
69
+ * Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
70
+ */
71
+ export const supportedTags = [...Object.keys(tagsToRoles), ...headingTags]
72
+ /**
73
+ * Uses readonly mapping keys/values to generate static types for IDEs that support TS in JSDoc.
74
+ * @typedef {keyof rolesToTags} RoleWithTag
75
+ * @typedef {typeof rolesToTags[RoleWithTag] | typeof headingTags[number]} SupportedTag
76
+ */
77
+
78
+ /**
79
+ * Subset of supported HTML tags that may be used with layout containers like Box.
80
+ *
81
+ * Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
82
+ */
83
+ // eslint-disable-next-line prettier/prettier
84
+ export const layoutTags = /** @type {const} */ ([
85
+ ...headingTags,
86
+ 'article',
87
+ 'aside',
88
+ 'blockquote',
89
+ 'footer',
90
+ 'figure',
91
+ 'form',
92
+ 'header',
93
+ 'ul',
94
+ 'li',
95
+ 'main',
96
+ 'nav',
97
+ 'section',
98
+ 'label'
99
+ // eslint-disable-next-line prettier/prettier
100
+ ])
101
+ /**
102
+ * @typedef {typeof layoutTags[number]} LayoutTag
103
+ */
104
+
105
+ /**
106
+ * Subset of supported HTML tags that may be used with text elements like Typography.
107
+ *
108
+ * Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
109
+ */
110
+ // eslint-disable-next-line prettier/prettier
111
+ export const textTags = /** @type {const} */ ([
112
+ ...headingTags,
113
+ 'blockquote',
114
+ 'code',
115
+ 'del',
116
+ 'em',
117
+ 'ins',
118
+ 'li',
119
+ 'strong',
120
+ 'label'
121
+ // eslint-disable-next-line prettier/prettier
122
+ ])
123
+ /**
124
+ * @typedef {typeof layoutTags[number]} TextTag
125
+ */
126
+
127
+ /**
128
+ * If passed a heading tag string like 'h1', 'h2' etc, returns the heading number as a number
129
+ * ready for use in `accessibilityLevel` props and similar.
130
+ *
131
+ * @param {string} [tag] - HTML tag string; returns undefined if not a {@link HeadingTag}
132
+ * @returns {'1' | '2' | '3' | '4' | '5' | '6' | undefined}
133
+ */
134
+ export const getHeadingLevel = (tag) => (headingTags.includes(tag) ? Number(tag[1]) : undefined)
135
+
136
+ /**
137
+ * Takes a supported HTML tag, and returns the accessibility props that, on web, make React Native Web
138
+ * render that tag.
139
+ *
140
+ * For cross-platform apps, a second argument may be passed with an [accessibilityRole](https://reactnative.dev/docs/accessibility#accessibilityrole)
141
+ * to use in native apps. Heading tags (h1, h2, ...h5, h6) map to "heading" role by default;
142
+ * no other supported semantic HTML tags have an equivalent native accessibilityRole.
143
+ *
144
+ * @param {SupportedTag} tag - HTML tag to use on web
145
+ * @param {AccessibilityRole | null} [nativeRole] - optional accessibilityRole for native apps
146
+ * @returns {{ accessibilityRole: string, accessibilityLevel?: string } | undefined}
147
+ */
148
+ export const getA11yPropsFromHtmlTag = (tag, nativeRole) => {
149
+ // Allow cross-platform apps to set accessibilityRoles alongside a web tag without conflict
150
+ if (nativeRole !== undefined && Platform.OS !== 'web') return { accessibilityRole: nativeRole }
151
+
152
+ if (tag) {
153
+ const accessibilityRole = tagsToRoles[tag]
154
+ if (accessibilityRole) return { accessibilityRole }
155
+
156
+ const accessibilityLevel = getHeadingLevel(tag)
157
+ if (accessibilityLevel) return { accessibilityRole: 'header', accessibilityLevel }
158
+ }
159
+ // If nothing matches or no tag supplied, return undefined and let component decide how to fall back.
160
+ // Note that return value may always be spread in objects (it is safe to spread undefined like { ...undefined })
161
+ return undefined
162
+ }
@@ -0,0 +1,30 @@
1
+ import { PixelRatio, Platform } from 'react-native'
2
+
3
+ /**
4
+ * When a user increases text size using device accessibility settings, some elements
5
+ * should also scale, such as icons alongside text and space between paragraphs.
6
+ *
7
+ * @param {number} [value] - the value to multiply; defaults to system default font size
8
+ * @param {number} [maxScale] - the maximum multiplier to apply; defaults to no maximum
9
+ */
10
+ export const scaleWithText = (value, maxScale = Infinity) => {
11
+ if (!value) return value
12
+ const scale = Math.min(PixelRatio.getFontScale(), maxScale) || 1
13
+ return value * scale
14
+ }
15
+
16
+ /**
17
+ * Native only, returns `undefined` on Web.
18
+ *
19
+ * Enforces `fontScaleCap` theme tokens as the maximum font size text can become
20
+ * after iOS or Android font scaling, to give consistent accessible maximum sizes
21
+ * that don't make the content unusable.
22
+ *
23
+ * The return value of this function can be used as a React Native `Text` element's
24
+ * `maxFontSizeMultiplier` prop value.
25
+ */
26
+ export const getMaxFontMultiplier = ({ fontSize, fontScaleCap }) => {
27
+ if (Platform.OS === 'web' || !fontScaleCap || !fontSize) return undefined
28
+ if (fontScaleCap <= fontSize) return 1
29
+ return fontScaleCap / fontSize
30
+ }
@@ -0,0 +1,119 @@
1
+ import React, { Children, Fragment } from 'react'
2
+ import { Text } from 'react-native'
3
+ import A11yText from '../A11yText'
4
+
5
+ /**
6
+ * Unpacks top-level fragments, so that common compositional patterns such as the following examples
7
+ * can be iterated as flat siblings (as if they were `<Child1 /><Child2 /><Child3 />`):
8
+ *
9
+ * - Setting `children` as a property wrapped in a fragment:
10
+ *
11
+ * ```jsx
12
+ * args.children = <><Child1 /><Child2 /><Child3 /></>
13
+ * ```
14
+ *
15
+ * - Defining `children` as a variable wrapped in a fragment:
16
+ *
17
+ * ```jsx
18
+ * const content = <><Child1 /><Child2 /><Child3 /></>
19
+ * if (someCondition) return <SomeWrapper>{content}</SomeWrapper>
20
+ * ```
21
+ *
22
+ * - Using fragments at the top level of a JSX block for conditional rendering:
23
+ *
24
+ * ```jsx
25
+ * <Child1 />
26
+ * {someCondition && (
27
+ * <>
28
+ * <Child2 />
29
+ * <Child3 />
30
+ * </>
31
+ * )}
32
+ * ```
33
+ *
34
+ * @param {ReactChildren} child
35
+ * @returns {ReactChildren}
36
+ */
37
+ export const unpackFragment = (child) => {
38
+ // If this level is a set of top-level siblings rather than one child, check each in turn
39
+ if (Children.count(child) > 1) return Children.map(child, unpackFragment)
40
+
41
+ // When a fragment is found, unpack its children to the top level and check them
42
+ if (child?.type === Fragment) return unpackFragment(child.props?.children)
43
+
44
+ // Stop unpacking as soon as any non-fragment child is found
45
+ return child
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
+
67
+ /**
68
+ * React Native on Native crashes if text content is rendered outside `<Text>`, and on web,
69
+ * text style inheritance behaves differently with text compared to regular HTML.
70
+ *
71
+ * Call this function on children that may contain text (strings or numbers) at the top level,
72
+ * and any that are found will be wrapped in a React Native `<Text>` element with supplied props.
73
+ *
74
+ * Note that this does not wrap strings that are nested children of rendered top level children:
75
+ * `wrapStringsInText(<View>Some text</View>)` will not wrap the inner text and will still crash,
76
+ * but `wrapStringsInText(<>{someString}{anotherString}</>)` wraps the strings inside the fragment.
77
+ *
78
+ * @param {ReactChildren} children
79
+ * @param {TextProps} props
80
+ * @returns {ReactChildren}
81
+ */
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
119
+ }
@@ -1,7 +1,13 @@
1
+ export * from './a11y'
1
2
  export * from './animation'
3
+ export * from './children'
2
4
  export * from './input'
5
+ export * from './pressability'
3
6
  export * from './propTypes'
4
7
 
8
+ export { default as info } from './info'
9
+ export { default as useCopy } from './useCopy'
10
+ export { default as useHash } from './useHash'
5
11
  export { default as useSpacingScale } from './useSpacingScale'
6
12
  export { default as useResponsiveProp } from './useResponsiveProp'
7
13
  export * from './useResponsiveProp'
@@ -0,0 +1,8 @@
1
+ import platform from './platform'
2
+ import versions from './versions'
3
+
4
+ // export an object accessed like info.platform.OS, info.version.uds, etc
5
+ export default {
6
+ platform,
7
+ versions
8
+ }
@@ -0,0 +1,11 @@
1
+ import { Platform } from 'react-native'
2
+ import fileSuffix from './platform'
3
+
4
+ // Allows consuming libraries that might not have a direct dependency on
5
+ // React Native to check what the current platform is.
6
+
7
+ // Particularly useful for validating Jest config: it is possible for Jest to
8
+ // get configured such that Platform.OS returns a different mocked value to the
9
+ // OS being used to select files by platform suffix (e.g. .web, .native).
10
+
11
+ export default { OS: Platform.OS, fileSuffix }
@@ -0,0 +1 @@
1
+ export default '.android'
@@ -0,0 +1 @@
1
+ export default '.ios'
@@ -0,0 +1 @@
1
+ export default '.web (default)'
@@ -0,0 +1,4 @@
1
+ // This shouldn't ever be chosen, as the more specific '.ios' and '.android' are available.
2
+ // If this gets picked, either an unsupported React Native platform is being used somehow
3
+ // (e.g. a native Windows app), or there's a serious config problem somewhere.
4
+ export default '.native'
@@ -0,0 +1,6 @@
1
+ import pkgJson from '../../../package.json'
2
+
3
+ // Add more versions if they are useful e.g. theme schema version
4
+ export default {
5
+ uds: pkgJson.version
6
+ }
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from 'react'
1
+ import { useCallback, useRef, useState } from 'react'
2
2
 
3
3
  const pluralHooks = ['useMultipleInputValues']
4
4
 
@@ -44,16 +44,18 @@ Consumers of this hook must be one of:
44
44
  * management tools such as Formik and React Hooks Form.
45
45
  *
46
46
  * @param {object} props
47
- * @param {any} [props.value] - for a controlled input, the active values as set by a parent
48
- * @param {any} [props.initialValue] - for an uncontrolled input, the default active values
47
+ * @param {string|number|null} [props.value] - for a controlled input, the active values as set by a parent
48
+ * @param {string|number} [props.initialValue] - for an uncontrolled input, the default active values
49
49
  * @param {function} [props.onChange] - function called when values change (required for controlled inputs)
50
50
  * @param {boolean} [props.readOnly] - if true, stops the input values from changing
51
51
  *
52
52
  * @param {string} hookName - optional, used for tailoring error messages
53
53
  *
54
+ * @typedef {(oldValue: string|number|null) => string|number|null} UpdaterFunction - `setValue` takes a value or
55
+ * a function returning a new value from the old value
54
56
  * @returns {{
55
- * currentValue: any
56
- * setValue: (value: any) => void
57
+ * currentValue: string|number|null
58
+ * setValue: (newValue: string|number|null|UpdaterFunction) => void
57
59
  * resetValue: () => void
58
60
  * isControlled: bool
59
61
  * }}
@@ -70,17 +72,21 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
70
72
 
71
73
  // Store the first valid value, for resetting input to default. If behaviour like Formik's `enableReinitialize`
72
74
  // is needed, add useEffect calling a set fn if !deepEqual(initializedValues, initialValues)
73
- const [initializedValue] = useState(currentValue)
75
+ const valueRef = useRef({ initial: currentValue, value: currentValue })
76
+ // Make current value accessible inside useCallback without rememoizing every time the value changes
77
+ valueRef.current.value = currentValue
74
78
 
75
79
  const setValue = useCallback(
76
- (newValue) => {
80
+ (arg, event) => {
77
81
  if (readOnly) return
78
- if (onChange) onChange(newValue)
82
+ const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg
79
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)
80
86
  },
81
87
  [isControlled, onChange, readOnly]
82
88
  )
83
- const resetValue = useCallback(() => setValue(initializedValue), [initializedValue, setValue])
89
+ const resetValue = useCallback((event) => setValue(valueRef.current.initial, event), [setValue])
84
90
 
85
91
  return { currentValue, setValue, resetValue, isControlled }
86
92
  }
@@ -94,18 +100,20 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
94
100
  * management tools such as Formik and React Hooks Form.
95
101
  *
96
102
  * @param {object} props
97
- * @param {any[]} [props.values] - for a controlled input, the active values as set by a parent
98
- * @param {any[]} [props.initialValues] - for an uncontrolled input, the default active values
103
+ * @param {string[]|number[]} [props.values] - for a controlled input, the active values as set by a parent
104
+ * @param {string[]|number[]} [props.initialValues] - for an uncontrolled input, the default active values
99
105
  * @param {number?} [props.maxValues] - maximum number of values that may be selected at once (no limit if null)
100
106
  * @param {function} [props.onChange] - function called when values change (required for controlled inputs)
101
107
  * @param {boolean} [props.readOnly] - if true, stops the input values from changing
102
108
  *
103
109
  * @param {string} componentName - optional, used in error messages
104
110
  *
111
+ * @typedef {(oldValues: string[]|number[]) => string[]|number[]} UpdaterFunction - `setValues` takes values or
112
+ * a function returning new values from old values
105
113
  * @returns {{
106
114
  * currentValues: any
107
115
  * resetValues: () => void
108
- * setValues: (newValues: any[]) => void
116
+ * setValues: (newValues: string[]|number[]|UpdaterFunction) => void
109
117
  * toggleOneValue: (value: string|number) => void
110
118
  * unsetValues: () => void
111
119
  * }}
@@ -138,23 +146,26 @@ export const useMultipleInputValues = ({
138
146
  )
139
147
  const currentValues = enforceMaxValues(currentValue)
140
148
 
141
- const setValues = useCallback((newValues) => setValue(enforceMaxValues(newValues)), [
142
- setValue,
143
- enforceMaxValues
144
- ])
149
+ const setValues = useCallback(
150
+ (newValues, event) => {
151
+ const validNewValues = enforceMaxValues(newValues)
152
+ setValue(validNewValues, event)
153
+ },
154
+ [setValue, enforceMaxValues]
155
+ )
145
156
 
146
157
  const resetValues = resetValue
147
- const unsetValues = useCallback(() => setValues([]), [setValues])
158
+ const unsetValues = useCallback((event) => setValues([], event), [setValues])
148
159
  const toggleOneValue = useCallback(
149
- (newValue) => {
160
+ (newValue, event) => {
150
161
  if (readOnly) return
151
- setValues(
152
- // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
153
- // a deepEqual() function if there's any use case for toggling stored objects or arrays.
154
- currentValues.includes(newValue)
155
- ? currentValues.filter((oldValue) => oldValue !== newValue)
156
- : [...currentValues, newValue]
157
- )
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)
158
169
  },
159
170
  [currentValues, readOnly, setValues]
160
171
  )
@@ -0,0 +1,96 @@
1
+ import { Platform, StyleSheet } from 'react-native'
2
+ import { pressProps } from './propTypes'
3
+
4
+ /**
5
+ * @typedef {import('react').ReactNode} ReactNode
6
+ */
7
+
8
+ // These roles should result in cursor: pointer but don't in current RNW releases
9
+ const shouldUseCursor = ['checkbox', 'radio', 'switch']
10
+
11
+ /**
12
+ * React Native Web has some built-in logic for applying cursor styles based on accessibility roles;
13
+ * however, it misses certain cases. This fills in known cases where widely used versions of RNW
14
+ * fail to apply an expected cursor style.
15
+ *
16
+ * @param {object} props
17
+ * @param {boolean} [props.inactive]
18
+ * @param {boolean} [props.disabled]
19
+ * @param {string} [props.accessibilityRole]
20
+ * @returns
21
+ */
22
+ export const getCursorStyle = ({ inactive, disabled, accessibilityRole }) => {
23
+ if (Platform.OS !== 'web') return undefined
24
+
25
+ if (inactive || disabled) return staticStyles.notAllowed
26
+ if (shouldUseCursor.includes(accessibilityRole)) return staticStyles.pointer
27
+
28
+ return undefined // allows React Native Web's built-in logic to apply
29
+ }
30
+
31
+ /**
32
+ * @typedef {{ pressed: boolean, focused: boolean, hovered?: boolean }} PressableState
33
+ */
34
+
35
+ /**
36
+ * Maps the state object given by the React Native `Pressable` component to the set of
37
+ * equivalent appearance names used in UDS.
38
+ *
39
+ * @param {PressableState} pressableState - state object passed by React Native's `<Pressable>` into
40
+ * render functions passed to its `style` or `children` props.
41
+ * @param {object} [additionalState] - properties specific to the current component,
42
+ * such as `inactive`, `selected`, etc.
43
+ * @returns {object}
44
+ */
45
+ export const resolvePressableState = (
46
+ { pressed = false, focused = false, hovered = false } = {},
47
+ additionalState
48
+ ) => ({
49
+ ...additionalState,
50
+ focus: focused,
51
+ hover: hovered,
52
+ pressed
53
+ })
54
+
55
+ /**
56
+ * Takes a UDS `tokens` prop and, if it is a function, resolves it based on a state
57
+ * object passed from the React Native `Pressable` component and optional extra properties.
58
+ *
59
+ * @param {object|function} tokens - UDS system tokens prop
60
+ * @param {PressableState} pressableState - state object passed by React Native's `<Pressable>`
61
+ * @param {object} [additionalState] - properties specific to the current component
62
+ * @returns {object} - resolved tokens object
63
+ */
64
+ export const resolvePressableTokens = (tokens, pressableState, additionalState) => {
65
+ if (typeof tokens !== 'function') return tokens
66
+ const udsPressableState = resolvePressableState(pressableState, additionalState)
67
+ return tokens(udsPressableState)
68
+ }
69
+
70
+ const staticStyles = StyleSheet.create(
71
+ Platform.select({
72
+ web: {
73
+ notAllowed: { cursor: 'not-allowed' },
74
+ pointer: { cursor: 'pointer' }
75
+ },
76
+ default: {}
77
+ })
78
+ )
79
+
80
+ /**
81
+ * From an object of props, extracts any platform-appropriate press handler functions and wraps
82
+ * them in a function that passes in some provided args. Allows components containing a Pressable
83
+ * to pass in press handlers that are then called with state or values that is otherwise internal.
84
+ */
85
+ export const getPressHandlersWithArgs = (pressableProps = {}, args = []) => {
86
+ // Allow handlers to be passed down for blur, hover, focus, pressIn, etc
87
+ const pressHandlers = Object.fromEntries(
88
+ Object.entries(pressProps.selectHandlers(pressableProps)).map(([key, handler]) => ({
89
+ [key]: (...defaultArgs) => {
90
+ // Pass each handler data on this button and current selection
91
+ handler(...args, ...defaultArgs)
92
+ }
93
+ }))
94
+ )
95
+ return pressHandlers
96
+ }