@telus-uds/components-base 0.0.2-prerelease.4 → 0.0.2-prerelease.8

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 (447) hide show
  1. package/.ultra.cache.json +1 -0
  2. package/CHANGELOG.md +76 -0
  3. package/__fixtures__/Accessible.js +33 -0
  4. package/__fixtures__/Accessible.native.js +32 -0
  5. package/__fixtures__/testTheme.js +940 -54
  6. package/__tests__/ActivityIndicator/ActivityIndicator.test.jsx +1 -1
  7. package/__tests__/Button/ButtonBase.test.jsx +3 -32
  8. package/__tests__/Button/ButtonGroup.test.jsx +2 -2
  9. package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
  10. package/__tests__/Checkbox/CheckboxGroup.test.jsx +247 -0
  11. package/__tests__/Divider/Divider.test.jsx +26 -5
  12. package/__tests__/Feedback/Feedback.test.jsx +42 -0
  13. package/__tests__/Icon/Icon.test.jsx +3 -3
  14. package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
  15. package/__tests__/List/List.test.jsx +60 -0
  16. package/__tests__/Modal/Modal.test.jsx +47 -0
  17. package/__tests__/Notification/Notification.test.jsx +20 -0
  18. package/__tests__/Pagination/Pagination.test.jsx +2 -2
  19. package/__tests__/Progress/Progress.test.jsx +79 -0
  20. package/__tests__/Radio/Radio.test.jsx +87 -0
  21. package/__tests__/Radio/RadioGroup.test.jsx +221 -0
  22. package/__tests__/RadioCard/RadioCard.test.jsx +87 -0
  23. package/__tests__/RadioCard/RadioCardGroup.test.jsx +247 -0
  24. package/__tests__/Search/Search.test.jsx +72 -0
  25. package/__tests__/Select/Select.test.jsx +93 -0
  26. package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
  27. package/__tests__/Spacer/Spacer.test.jsx +63 -0
  28. package/__tests__/StackView/StackView.test.jsx +216 -0
  29. package/__tests__/StackView/StackWrap.test.jsx +47 -0
  30. package/__tests__/StackView/getStackedContent.test.jsx +295 -0
  31. package/__tests__/StepTracker/StepTracker.test.jsx +94 -0
  32. package/__tests__/Tabs/Tabs.test.jsx +200 -0
  33. package/__tests__/Tags/Tags.test.jsx +328 -0
  34. package/__tests__/TextInput/TextArea.test.jsx +34 -0
  35. package/__tests__/TextInput/TextInputBase.test.jsx +120 -0
  36. package/__tests__/Tooltip/Tooltip.test.jsx +65 -0
  37. package/__tests__/Tooltip/getTooltipPosition.test.js +79 -0
  38. package/__tests__/utils/input.test.js +58 -0
  39. package/__tests__/utils/useCopy.test.js +42 -0
  40. package/__tests__/utils/useResponsiveProp.test.jsx +202 -0
  41. package/__tests__/utils/{spacing.test.jsx → useSpacingScale.test.jsx} +1 -1
  42. package/__tests__/utils/useUniqueId.test.js +31 -0
  43. package/babel.config.js +20 -0
  44. package/jest.config.js +8 -2
  45. package/lib/A11yInfoProvider/index.js +54 -26
  46. package/lib/A11yText/index.js +37 -14
  47. package/lib/ActivityIndicator/Spinner.js +78 -0
  48. package/lib/ActivityIndicator/Spinner.native.js +121 -87
  49. package/lib/ActivityIndicator/index.js +28 -12
  50. package/lib/ActivityIndicator/shared.js +27 -12
  51. package/lib/BaseProvider/index.js +34 -11
  52. package/lib/Box/Box.js +56 -28
  53. package/lib/Box/index.js +13 -2
  54. package/lib/Button/Button.js +38 -10
  55. package/lib/Button/ButtonBase.js +120 -109
  56. package/lib/Button/ButtonGroup.js +98 -99
  57. package/lib/Button/ButtonLink.js +41 -13
  58. package/lib/Button/index.js +31 -4
  59. package/lib/Button/propTypes.js +32 -9
  60. package/lib/Card/Card.js +36 -41
  61. package/lib/Card/CardBase.js +78 -0
  62. package/lib/Card/PressableCardBase.js +137 -0
  63. package/lib/Card/index.js +40 -2
  64. package/lib/Checkbox/Checkbox.js +344 -0
  65. package/lib/Checkbox/CheckboxGroup.js +231 -0
  66. package/lib/Checkbox/CheckboxInput.js +74 -0
  67. package/lib/Checkbox/CheckboxInput.native.js +14 -0
  68. package/lib/Checkbox/index.js +21 -0
  69. package/lib/Divider/Divider.js +81 -17
  70. package/lib/Divider/index.js +13 -2
  71. package/lib/ExpandCollapse/Accordion.js +20 -7
  72. package/lib/ExpandCollapse/Control.js +50 -27
  73. package/lib/ExpandCollapse/ExpandCollapse.js +41 -24
  74. package/lib/ExpandCollapse/Panel.js +75 -37
  75. package/lib/ExpandCollapse/index.js +25 -7
  76. package/lib/Feedback/Feedback.js +161 -0
  77. package/lib/Feedback/index.js +13 -0
  78. package/lib/Fieldset/Fieldset.js +160 -0
  79. package/lib/Fieldset/FieldsetContainer.js +41 -0
  80. package/lib/Fieldset/FieldsetContainer.native.js +33 -0
  81. package/lib/Fieldset/Legend.js +33 -0
  82. package/lib/Fieldset/Legend.native.js +43 -0
  83. package/lib/Fieldset/cssReset.js +21 -0
  84. package/lib/Fieldset/index.js +13 -0
  85. package/lib/FlexGrid/Col/Col.js +67 -38
  86. package/lib/FlexGrid/Col/index.js +13 -2
  87. package/lib/FlexGrid/FlexGrid.js +70 -45
  88. package/lib/FlexGrid/Row/Row.js +48 -27
  89. package/lib/FlexGrid/Row/index.js +13 -2
  90. package/lib/FlexGrid/helpers/index.js +9 -1
  91. package/lib/FlexGrid/index.js +13 -2
  92. package/lib/FlexGrid/providers/GutterContext.js +15 -3
  93. package/lib/Icon/Icon.js +52 -47
  94. package/lib/Icon/IconText.js +100 -0
  95. package/lib/Icon/index.js +31 -3
  96. package/lib/InputLabel/InputLabel.js +122 -0
  97. package/lib/InputLabel/LabelContent.js +31 -0
  98. package/lib/InputLabel/LabelContent.native.js +16 -0
  99. package/lib/InputLabel/index.js +13 -0
  100. package/lib/InputSupports/InputSupports.js +104 -0
  101. package/lib/InputSupports/index.js +13 -0
  102. package/lib/InputSupports/propTypes.js +66 -0
  103. package/lib/InputSupports/useInputSupports.js +41 -0
  104. package/lib/Link/ChevronLink.js +57 -15
  105. package/lib/Link/InlinePressable.js +50 -0
  106. package/lib/Link/InlinePressable.native.js +101 -0
  107. package/lib/Link/Link.js +30 -13
  108. package/lib/Link/LinkBase.js +121 -146
  109. package/lib/Link/TextButton.js +47 -17
  110. package/lib/Link/index.js +39 -4
  111. package/lib/List/List.js +80 -0
  112. package/lib/List/ListItem.js +237 -0
  113. package/lib/List/index.js +13 -0
  114. package/lib/Modal/Modal.js +226 -0
  115. package/lib/Modal/dictionary.js +16 -0
  116. package/lib/Modal/index.js +13 -0
  117. package/lib/Notification/Notification.js +200 -0
  118. package/lib/Notification/dictionary.js +15 -0
  119. package/lib/Notification/index.js +13 -0
  120. package/lib/Pagination/PageButton.js +45 -46
  121. package/lib/Pagination/Pagination.js +70 -40
  122. package/lib/Pagination/SideButton.js +74 -58
  123. package/lib/Pagination/dictionary.js +9 -2
  124. package/lib/Pagination/index.js +13 -2
  125. package/lib/Pagination/usePagination.js +12 -2
  126. package/lib/Progress/Progress.js +99 -0
  127. package/lib/Progress/ProgressBar.js +146 -0
  128. package/lib/Progress/ProgressBarBackground.js +57 -0
  129. package/lib/Progress/index.js +16 -0
  130. package/lib/Radio/Radio.js +292 -0
  131. package/lib/Radio/RadioButton.js +141 -0
  132. package/lib/Radio/RadioGroup.js +233 -0
  133. package/lib/Radio/RadioInput.js +76 -0
  134. package/lib/Radio/RadioInput.native.js +14 -0
  135. package/lib/Radio/index.js +21 -0
  136. package/lib/RadioCard/RadioCard.js +240 -0
  137. package/lib/RadioCard/RadioCardGroup.js +251 -0
  138. package/lib/RadioCard/index.js +21 -0
  139. package/lib/Search/Search.js +243 -0
  140. package/lib/Search/dictionary.js +19 -0
  141. package/lib/Search/index.js +13 -0
  142. package/lib/Select/Group.js +33 -0
  143. package/lib/Select/Group.native.js +25 -0
  144. package/lib/Select/Item.js +29 -0
  145. package/lib/Select/Item.native.js +19 -0
  146. package/lib/Select/Picker.js +79 -0
  147. package/lib/Select/Picker.native.js +115 -0
  148. package/lib/Select/Select.js +300 -0
  149. package/lib/Select/index.js +19 -0
  150. package/lib/SideNav/Item.js +54 -33
  151. package/lib/SideNav/ItemContent.js +41 -15
  152. package/lib/SideNav/ItemsGroup.js +46 -27
  153. package/lib/SideNav/SideNav.js +92 -69
  154. package/lib/SideNav/index.js +15 -1
  155. package/lib/Skeleton/Skeleton.js +137 -0
  156. package/lib/Skeleton/index.js +13 -0
  157. package/lib/Skeleton/skeleton.constant.js +12 -0
  158. package/lib/Skeleton/skeletonWebAnimation.js +27 -0
  159. package/lib/Skeleton/useSkeletonNativeAnimation.js +37 -0
  160. package/lib/Spacer/Spacer.js +117 -0
  161. package/lib/Spacer/index.js +13 -0
  162. package/lib/StackView/StackView.js +129 -0
  163. package/lib/StackView/StackWrap.js +55 -0
  164. package/lib/StackView/StackWrap.native.js +14 -0
  165. package/lib/StackView/StackWrapBox.js +112 -0
  166. package/lib/StackView/StackWrapGap.js +71 -0
  167. package/lib/StackView/common.js +45 -0
  168. package/lib/StackView/getStackedContent.js +141 -0
  169. package/lib/StackView/index.js +29 -0
  170. package/lib/StepTracker/Step.js +245 -0
  171. package/lib/StepTracker/StepTracker.js +197 -0
  172. package/lib/StepTracker/dictionary.js +17 -0
  173. package/lib/StepTracker/index.js +13 -0
  174. package/lib/Tabs/HorizontalScroll.js +199 -0
  175. package/lib/Tabs/ScrollViewEnd.js +66 -0
  176. package/lib/Tabs/ScrollViewEnd.native.js +41 -0
  177. package/lib/Tabs/Tabs.js +117 -0
  178. package/lib/Tabs/TabsItem.js +234 -0
  179. package/lib/Tabs/TabsScrollButton.js +121 -0
  180. package/lib/Tabs/dictionary.js +18 -0
  181. package/lib/Tabs/index.js +13 -0
  182. package/lib/Tabs/itemPositions.js +128 -0
  183. package/lib/Tags/Tags.js +250 -0
  184. package/lib/Tags/index.js +13 -0
  185. package/lib/TextInput/TextArea.js +109 -0
  186. package/lib/TextInput/TextInput.js +75 -0
  187. package/lib/TextInput/TextInputBase.js +252 -0
  188. package/lib/TextInput/index.js +23 -0
  189. package/lib/TextInput/propTypes.js +42 -0
  190. package/lib/ThemeProvider/ThemeProvider.js +38 -14
  191. package/lib/ThemeProvider/index.js +61 -6
  192. package/lib/ThemeProvider/useSetTheme.js +14 -5
  193. package/lib/ThemeProvider/useTheme.js +13 -4
  194. package/lib/ThemeProvider/useThemeTokens.js +86 -19
  195. package/lib/ThemeProvider/utils/index.js +31 -2
  196. package/lib/ThemeProvider/utils/styles.js +52 -16
  197. package/lib/ThemeProvider/utils/theme-tokens.js +94 -16
  198. package/lib/ToggleSwitch/ToggleSwitch.js +76 -52
  199. package/lib/ToggleSwitch/index.js +13 -2
  200. package/lib/Tooltip/Backdrop.js +56 -0
  201. package/lib/Tooltip/Backdrop.native.js +59 -0
  202. package/lib/Tooltip/Tooltip.js +357 -0
  203. package/lib/Tooltip/dictionary.js +15 -0
  204. package/lib/Tooltip/getTooltipPosition.js +172 -0
  205. package/lib/Tooltip/index.js +13 -0
  206. package/lib/TooltipButton/TooltipButton.js +83 -0
  207. package/lib/TooltipButton/index.js +13 -0
  208. package/lib/Typography/Typography.js +58 -47
  209. package/lib/Typography/index.js +13 -2
  210. package/lib/ViewportProvider/ViewportProvider.js +46 -0
  211. package/lib/ViewportProvider/index.js +22 -38
  212. package/lib/ViewportProvider/useViewport.js +15 -0
  213. package/lib/ViewportProvider/useViewportListener.js +57 -0
  214. package/lib/index.js +509 -19
  215. package/lib/utils/a11y/index.js +18 -0
  216. package/lib/utils/a11y/textSize.js +49 -0
  217. package/lib/utils/animation/index.js +15 -2
  218. package/lib/utils/animation/useVerticalExpandAnimation.js +28 -11
  219. package/lib/utils/children.js +87 -0
  220. package/lib/utils/index.js +163 -4
  221. package/lib/utils/info/index.js +19 -0
  222. package/lib/utils/info/platform/index.js +23 -0
  223. package/lib/utils/info/platform/platform.android.js +8 -0
  224. package/lib/utils/info/platform/platform.ios.js +8 -0
  225. package/lib/utils/info/platform/platform.js +8 -0
  226. package/lib/utils/info/platform/platform.native.js +11 -0
  227. package/lib/utils/info/versions.js +16 -0
  228. package/lib/utils/input.js +54 -34
  229. package/lib/utils/pressability.js +120 -0
  230. package/lib/utils/propTypes.js +269 -108
  231. package/lib/utils/useCopy.js +51 -0
  232. package/lib/utils/useHash.js +48 -0
  233. package/lib/utils/useHash.native.js +15 -0
  234. package/lib/utils/useResponsiveProp.js +59 -0
  235. package/lib/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +45 -12
  236. package/lib/utils/useUniqueId.js +21 -0
  237. package/package.json +11 -8
  238. package/release-context.json +4 -4
  239. package/src/ActivityIndicator/{Spinner.web.jsx → Spinner.jsx} +0 -0
  240. package/src/Box/Box.jsx +13 -4
  241. package/src/Button/Button.jsx +9 -5
  242. package/src/Button/ButtonBase.jsx +74 -86
  243. package/src/Button/ButtonGroup.jsx +24 -41
  244. package/src/Button/ButtonLink.jsx +14 -4
  245. package/src/Button/propTypes.js +12 -2
  246. package/src/Card/Card.jsx +4 -30
  247. package/src/Card/CardBase.jsx +57 -0
  248. package/src/Card/PressableCardBase.jsx +112 -0
  249. package/src/Card/index.js +3 -0
  250. package/src/Checkbox/Checkbox.jsx +274 -0
  251. package/src/Checkbox/CheckboxGroup.jsx +196 -0
  252. package/src/Checkbox/CheckboxInput.jsx +55 -0
  253. package/src/Checkbox/CheckboxInput.native.jsx +6 -0
  254. package/src/Checkbox/index.js +5 -0
  255. package/src/Divider/Divider.jsx +38 -3
  256. package/src/ExpandCollapse/Control.jsx +1 -1
  257. package/src/Feedback/Feedback.jsx +108 -0
  258. package/src/Feedback/index.js +3 -0
  259. package/src/Fieldset/Fieldset.jsx +129 -0
  260. package/src/Fieldset/FieldsetContainer.jsx +22 -0
  261. package/src/Fieldset/FieldsetContainer.native.jsx +16 -0
  262. package/src/Fieldset/Legend.jsx +16 -0
  263. package/src/Fieldset/Legend.native.jsx +22 -0
  264. package/src/Fieldset/cssReset.js +14 -0
  265. package/src/Fieldset/index.js +3 -0
  266. package/src/Icon/Icon.jsx +23 -27
  267. package/src/Icon/IconText.jsx +63 -0
  268. package/src/Icon/index.js +3 -2
  269. package/src/InputLabel/InputLabel.jsx +106 -0
  270. package/src/InputLabel/LabelContent.jsx +13 -0
  271. package/src/InputLabel/LabelContent.native.jsx +6 -0
  272. package/src/InputLabel/index.js +3 -0
  273. package/src/InputSupports/InputSupports.jsx +75 -0
  274. package/src/InputSupports/index.js +3 -0
  275. package/src/InputSupports/propTypes.js +44 -0
  276. package/src/InputSupports/useInputSupports.js +30 -0
  277. package/src/Link/ChevronLink.jsx +28 -7
  278. package/src/Link/InlinePressable.jsx +37 -0
  279. package/src/Link/InlinePressable.native.jsx +73 -0
  280. package/src/Link/Link.jsx +17 -13
  281. package/src/Link/LinkBase.jsx +71 -146
  282. package/src/Link/TextButton.jsx +25 -11
  283. package/src/Link/index.js +2 -1
  284. package/src/List/List.jsx +47 -0
  285. package/src/List/ListItem.jsx +187 -0
  286. package/src/List/index.js +3 -0
  287. package/src/Modal/Modal.jsx +185 -0
  288. package/src/Modal/dictionary.js +9 -0
  289. package/src/Modal/index.js +3 -0
  290. package/src/Notification/Notification.jsx +149 -0
  291. package/src/Notification/dictionary.js +8 -0
  292. package/src/Notification/index.js +3 -0
  293. package/src/Pagination/PageButton.jsx +3 -17
  294. package/src/Pagination/SideButton.jsx +27 -38
  295. package/src/Progress/Progress.jsx +77 -0
  296. package/src/Progress/ProgressBar.jsx +110 -0
  297. package/src/Progress/ProgressBarBackground.jsx +34 -0
  298. package/src/Progress/index.js +6 -0
  299. package/src/Radio/Radio.jsx +233 -0
  300. package/src/Radio/RadioButton.jsx +131 -0
  301. package/src/Radio/RadioGroup.jsx +198 -0
  302. package/src/Radio/RadioInput.jsx +57 -0
  303. package/src/Radio/RadioInput.native.jsx +6 -0
  304. package/src/Radio/index.js +5 -0
  305. package/src/RadioCard/RadioCard.jsx +191 -0
  306. package/src/RadioCard/RadioCardGroup.jsx +211 -0
  307. package/src/RadioCard/index.js +5 -0
  308. package/src/Search/Search.jsx +204 -0
  309. package/src/Search/dictionary.js +12 -0
  310. package/src/Search/index.js +3 -0
  311. package/src/Select/Group.jsx +15 -0
  312. package/src/Select/Group.native.jsx +14 -0
  313. package/src/Select/Item.jsx +11 -0
  314. package/src/Select/Item.native.jsx +10 -0
  315. package/src/Select/Picker.jsx +67 -0
  316. package/src/Select/Picker.native.jsx +95 -0
  317. package/src/Select/Select.jsx +255 -0
  318. package/src/Select/index.js +8 -0
  319. package/src/SideNav/Item.jsx +2 -2
  320. package/src/Skeleton/Skeleton.jsx +98 -0
  321. package/src/Skeleton/index.js +3 -0
  322. package/src/Skeleton/skeleton.constant.js +3 -0
  323. package/src/Skeleton/skeletonWebAnimation.js +13 -0
  324. package/src/Skeleton/useSkeletonNativeAnimation.js +27 -0
  325. package/src/Spacer/Spacer.jsx +91 -0
  326. package/src/Spacer/index.js +3 -0
  327. package/src/StackView/StackView.jsx +111 -0
  328. package/src/StackView/StackWrap.jsx +41 -0
  329. package/src/StackView/StackWrap.native.jsx +4 -0
  330. package/src/StackView/StackWrapBox.jsx +94 -0
  331. package/src/StackView/StackWrapGap.jsx +49 -0
  332. package/src/StackView/common.jsx +28 -0
  333. package/src/StackView/getStackedContent.jsx +112 -0
  334. package/src/StackView/index.js +6 -0
  335. package/src/StepTracker/Step.jsx +202 -0
  336. package/src/StepTracker/StepTracker.jsx +163 -0
  337. package/src/StepTracker/dictionary.js +10 -0
  338. package/src/StepTracker/index.js +3 -0
  339. package/src/Tabs/HorizontalScroll.jsx +165 -0
  340. package/src/Tabs/ScrollViewEnd.jsx +53 -0
  341. package/src/Tabs/ScrollViewEnd.native.jsx +24 -0
  342. package/src/Tabs/Tabs.jsx +89 -0
  343. package/src/Tabs/TabsItem.jsx +204 -0
  344. package/src/Tabs/TabsScrollButton.jsx +100 -0
  345. package/src/Tabs/dictionary.js +11 -0
  346. package/src/Tabs/index.js +3 -0
  347. package/src/Tabs/itemPositions.js +101 -0
  348. package/src/Tags/Tags.jsx +207 -0
  349. package/src/Tags/index.js +3 -0
  350. package/src/TextInput/TextArea.jsx +78 -0
  351. package/src/TextInput/TextInput.jsx +52 -0
  352. package/src/TextInput/TextInputBase.jsx +210 -0
  353. package/src/TextInput/index.js +4 -0
  354. package/src/TextInput/propTypes.js +29 -0
  355. package/src/ThemeProvider/useThemeTokens.js +56 -5
  356. package/src/ThemeProvider/utils/styles.js +18 -5
  357. package/src/ThemeProvider/utils/theme-tokens.js +46 -5
  358. package/src/ToggleSwitch/ToggleSwitch.jsx +3 -4
  359. package/src/Tooltip/Backdrop.jsx +60 -0
  360. package/src/Tooltip/Backdrop.native.jsx +33 -0
  361. package/src/Tooltip/Tooltip.jsx +294 -0
  362. package/src/Tooltip/dictionary.js +8 -0
  363. package/src/Tooltip/getTooltipPosition.js +161 -0
  364. package/src/Tooltip/index.js +3 -0
  365. package/src/TooltipButton/TooltipButton.jsx +49 -0
  366. package/src/TooltipButton/index.js +3 -0
  367. package/src/Typography/Typography.jsx +10 -24
  368. package/src/ViewportProvider/ViewportProvider.jsx +21 -0
  369. package/src/ViewportProvider/index.jsx +2 -41
  370. package/src/ViewportProvider/useViewport.js +5 -0
  371. package/src/ViewportProvider/useViewportListener.js +43 -0
  372. package/src/index.js +34 -2
  373. package/src/utils/a11y/index.js +1 -0
  374. package/src/utils/a11y/textSize.js +30 -0
  375. package/src/utils/children.jsx +66 -0
  376. package/src/utils/index.js +11 -1
  377. package/src/utils/info/index.js +8 -0
  378. package/src/utils/info/platform/index.js +11 -0
  379. package/src/utils/info/platform/platform.android.js +1 -0
  380. package/src/utils/info/platform/platform.ios.js +1 -0
  381. package/src/utils/info/platform/platform.js +1 -0
  382. package/src/utils/info/platform/platform.native.js +4 -0
  383. package/src/utils/info/versions.js +6 -0
  384. package/src/utils/input.js +22 -13
  385. package/src/utils/pressability.js +96 -0
  386. package/src/utils/propTypes.js +195 -47
  387. package/src/utils/useCopy.js +39 -0
  388. package/src/utils/useHash.js +34 -0
  389. package/src/utils/useHash.native.js +6 -0
  390. package/src/utils/useResponsiveProp.js +50 -0
  391. package/src/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +25 -10
  392. package/src/utils/useUniqueId.js +14 -0
  393. package/stories/A11yText/A11yText.stories.jsx +15 -13
  394. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
  395. package/stories/Box/Box.stories.jsx +29 -2
  396. package/stories/Button/Button.stories.jsx +21 -20
  397. package/stories/Button/ButtonGroup.stories.jsx +2 -1
  398. package/stories/Button/ButtonLink.stories.jsx +6 -4
  399. package/stories/Card/Card.stories.jsx +13 -1
  400. package/stories/Checkbox/Checkbox.stories.jsx +94 -0
  401. package/stories/Divider/Divider.stories.jsx +26 -2
  402. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
  403. package/stories/Feedback/Feedback.stories.jsx +96 -0
  404. package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
  405. package/stories/Icon/Icon.stories.jsx +15 -6
  406. package/stories/InputLabel/InputLabel.stories.jsx +42 -0
  407. package/stories/Link/ChevronLink.stories.jsx +20 -4
  408. package/stories/Link/Link.stories.jsx +39 -3
  409. package/stories/Link/TextButton.stories.jsx +24 -2
  410. package/stories/List/List.stories.jsx +117 -0
  411. package/stories/Modal/Modal.stories.jsx +29 -0
  412. package/stories/Notification/Notification.stories.jsx +82 -0
  413. package/stories/Pagination/Pagination.stories.jsx +28 -14
  414. package/stories/Progress/Progress.stories.jsx +93 -0
  415. package/stories/Radio/Radio.stories.jsx +100 -0
  416. package/stories/RadioCard/RadioCard.stories.jsx +98 -0
  417. package/stories/Search/Search.stories.jsx +16 -0
  418. package/stories/Select/Select.stories.jsx +55 -0
  419. package/stories/SideNav/SideNav.stories.jsx +17 -2
  420. package/stories/Skeleton/Skeleton.stories.jsx +36 -0
  421. package/stories/Spacer/Spacer.stories.jsx +38 -0
  422. package/stories/StackView/StackView.stories.jsx +75 -0
  423. package/stories/StackView/StackWrap.stories.jsx +64 -0
  424. package/stories/StepTracker/StepTracker.stories.jsx +71 -0
  425. package/stories/Tabs/Tabs.stories.jsx +97 -0
  426. package/stories/Tags/Tags.stories.jsx +69 -0
  427. package/stories/TextInput/TextArea.stories.jsx +100 -0
  428. package/stories/TextInput/TextInput.stories.jsx +103 -0
  429. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
  430. package/stories/Tooltip/Tooltip.stories.jsx +81 -0
  431. package/stories/TooltipButton/TooltipButton.stories.jsx +11 -0
  432. package/stories/Typography/Typography.stories.jsx +12 -3
  433. package/stories/{platform-supports.web.jsx → platform-supports.jsx} +1 -1
  434. package/stories/supports.jsx +110 -14
  435. package/__fixtures__/accessible.icon.svg +0 -6
  436. package/babel.config.json +0 -8
  437. package/lib/ActivityIndicator/Spinner.web.js +0 -55
  438. package/lib/Pagination/useCopy.js +0 -10
  439. package/lib/config/svgr-icons-web.js +0 -9
  440. package/lib/config/svgr-icons.js +0 -52
  441. package/lib/utils/spacing/index.js +0 -2
  442. package/lib/utils/spacing/utils.js +0 -32
  443. package/src/Pagination/useCopy.js +0 -7
  444. package/src/config/svgr-icons-web.js +0 -11
  445. package/src/config/svgr-icons.js +0 -46
  446. package/src/utils/spacing/index.js +0 -3
  447. package/src/utils/spacing/utils.js +0 -28
@@ -0,0 +1,112 @@
1
+ import React, { Children, Fragment } from 'react'
2
+
3
+ import Box from '../Box'
4
+ import Divider from '../Divider'
5
+ import Spacer from '../Spacer'
6
+
7
+ /**
8
+ * @typedef {import('react').ReactChildren} ReactChildren
9
+ * @typedef {import('react').ReactElement} ReactElement
10
+ * @typedef {import('../utils/propTypes.js').SpacingValue} SpacingValue
11
+ */
12
+
13
+ /**
14
+ * Takes valid React Children and inserts a specified amount of space between each valid (not nullish) top
15
+ * level element, according to the provided `options`.
16
+ *
17
+ * Returns an array of the original elements plus inserted spacing elements.
18
+ * This array of elements is keyed and may be used as a component's `children` without mapping.
19
+ *
20
+ * @param {ReactChildren} children
21
+ * @param {object} [options] -
22
+ * - `space`: amount of space to insert using the theme spacing scale (see `useSpacingScale`)
23
+ * - `direction`: if 'row' or 'row-reverse', applies space horizontally, if 'column' or 'column-reverse', applies space vertically
24
+ * - `box`: if truthy, wraps each valid child in a Box component; if an object, passes it to Box as props
25
+ * - `divider`: if truthy, inserts Divider components; if an object, passes it to Divider as props
26
+ * - `preserveFragments`: if true, adds no space between top-level elements that were passed inside a nested fragment
27
+ * @param {SpacingValue} [options.space]
28
+ * @param {boolean | object} [options.divider]
29
+ * @param {"row" | "column" | "row-reverse" | "column-reverse"} [options.direction]
30
+ * @param {boolean} [options.preserveFragments]
31
+ * @returns {ReactElement[]}
32
+ */
33
+ const getStackedContent = (
34
+ children,
35
+ { divider, space, direction = 'column', box, preserveFragments = false }
36
+ ) => {
37
+ const boxProps = box && typeof box === 'object' ? box : { space }
38
+ const dividerProps = divider && typeof divider === 'object' ? divider : {}
39
+
40
+ // Avoid surprises if children contains comments, nulls, or is a variable wrapped as a fragment
41
+ const topLevelChildren = preserveFragments ? children : unpackFragment(children)
42
+ const validChildren = Children.toArray(topLevelChildren).filter(Boolean)
43
+ const content = validChildren.reduce((newChildren, child, index) => {
44
+ const boxID = `Stack-Box-${index}`
45
+ const item = box ? (
46
+ // If wrapped in Box, that Box needs a key.
47
+ // If possible, use an existing content key; use an index-based key only if necessary.
48
+ <Box {...boxProps} key={child.key || boxID} testID={boxID}>
49
+ {child}
50
+ </Box>
51
+ ) : (
52
+ child
53
+ )
54
+ if (!index || (!space && !divider)) return [...newChildren, item]
55
+
56
+ const testID = `Stack-${divider ? 'Divider' : 'Spacer'}-${index}`
57
+ const commonProps = { testID, key: testID, space }
58
+ const separator = divider ? (
59
+ <Divider vertical={direction.startsWith('row')} {...dividerProps} {...commonProps} />
60
+ ) : (
61
+ <Spacer direction={direction.startsWith('row') ? 'row' : 'column'} {...commonProps} />
62
+ )
63
+ return [...newChildren, separator, item]
64
+ }, [])
65
+
66
+ return content
67
+ }
68
+
69
+ /**
70
+ * Unpacks top-level fragments, so that common compositional patterns such as the following examples
71
+ * can be iterated as flat siblings (as if they were `<Child1 /><Child2 /><Child3 />`):
72
+ *
73
+ * - Setting `children` as a property wrapped in a fragment:
74
+ *
75
+ * ```jsx
76
+ * args.children = <><Child1 /><Child2 /><Child3 /></>
77
+ * ```
78
+ *
79
+ * - Defining `children` as a variable wrapped in a fragment:
80
+ *
81
+ * ```jsx
82
+ * const content = <><Child1 /><Child2 /><Child3 /></>
83
+ * if (someCondition) return <SomeWrapper>{content}</SomeWrapper>
84
+ * ```
85
+ *
86
+ * - Using fragments at the top level of a JSX block for conditional rendering:
87
+ *
88
+ * ```jsx
89
+ * <Child1 />
90
+ * {someCondition && (
91
+ * <>
92
+ * <Child2 />
93
+ * <Child3 />
94
+ * </>
95
+ * )}
96
+ * ```
97
+ *
98
+ * @param {ReactChildren} child
99
+ * @returns {ReactChildren}
100
+ */
101
+ const unpackFragment = (child) => {
102
+ // If this level is a set of top-level siblings rather than one child, check each in turn
103
+ if (Children.count(child) > 1) return Children.map(child, unpackFragment)
104
+
105
+ // When a fragment is found, unpack its children to the top level and check them
106
+ if (child?.type === Fragment) return unpackFragment(child.props?.children)
107
+
108
+ // Stop unpacking as soon as any non-fragment child is found
109
+ return child
110
+ }
111
+
112
+ export default getStackedContent
@@ -0,0 +1,6 @@
1
+ import StackView from './StackView'
2
+ import StackWrap from './StackWrap'
3
+
4
+ export default StackView
5
+ export { StackWrap }
6
+ export { default as getStackedContent } from './getStackedContent'
@@ -0,0 +1,202 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { StyleSheet, Text, View } from 'react-native'
4
+
5
+ import StackView from '../StackView'
6
+ import Icon from '../Icon'
7
+ import { getTokensPropType, variantProp } from '../utils'
8
+ import { applyTextStyles } from '../ThemeProvider'
9
+
10
+ const selectCompletedIconTokens = ({ completedIconColor, completedIconSize }) => ({
11
+ size: completedIconSize,
12
+ color: completedIconColor
13
+ })
14
+ const selectConnectorStyles = (
15
+ {
16
+ connectorColor,
17
+ connectorHeight,
18
+ connectorMinWidth,
19
+ connectorCompletedHeight,
20
+ connectorCompletedColor
21
+ },
22
+ isCompleted
23
+ ) => ({
24
+ backgroundColor: connectorColor,
25
+ height: connectorHeight,
26
+ minWidth: connectorMinWidth,
27
+ ...(isCompleted && { height: connectorCompletedHeight, backgroundColor: connectorCompletedColor })
28
+ })
29
+ const selectCurrentInnerStyles = ({ knobCurrentInnerColor, knobCurrentInnerSize }) => ({
30
+ backgroundColor: knobCurrentInnerColor,
31
+ borderRadius: knobCurrentInnerSize / 2,
32
+ height: knobCurrentInnerSize,
33
+ width: knobCurrentInnerSize
34
+ })
35
+ const selectKnobStyles = (
36
+ {
37
+ knobBackgroundColor,
38
+ knobBorderColor,
39
+ knobBorderWidth,
40
+ knobCompletedBackgroundColor,
41
+ knobCompletedBorderColor,
42
+ knobCompletedPaddingLeft,
43
+ knobCompletedPaddingTop,
44
+ knobCurrentBackgroundColor,
45
+ knobCurrentBorderColor,
46
+ knobCurrentBorderWidth,
47
+ knobCurrentPaddingLeft,
48
+ knobCurrentPaddingTop,
49
+ knobSize
50
+ },
51
+ isCompleted,
52
+ isCurrent
53
+ ) => ({
54
+ backgroundColor: knobBackgroundColor,
55
+ borderColor: knobBorderColor,
56
+ borderRadius: knobSize / 2,
57
+ borderWidth: knobBorderWidth,
58
+ height: knobSize,
59
+ width: knobSize,
60
+ ...(isCompleted && {
61
+ backgroundColor: knobCompletedBackgroundColor,
62
+ borderColor: knobCompletedBorderColor,
63
+ paddingLeft: knobCompletedPaddingLeft,
64
+ paddingTop: knobCompletedPaddingTop
65
+ }),
66
+ ...(isCurrent && {
67
+ backgroundColor: knobCurrentBackgroundColor,
68
+ borderColor: knobCurrentBorderColor,
69
+ borderWidth: knobCurrentBorderWidth,
70
+ paddingLeft: knobCurrentPaddingLeft,
71
+ paddingTop: knobCurrentPaddingTop
72
+ })
73
+ })
74
+ const selectLabelContainerStyles = ({
75
+ labelDirection,
76
+ labelGap,
77
+ labelMarginTop,
78
+ labelPaddingLeft,
79
+ labelPaddingRight
80
+ }) => ({
81
+ marginTop: labelMarginTop,
82
+ paddingLeft: labelPaddingLeft,
83
+ paddingRight: labelPaddingRight,
84
+ flexDirection: labelDirection,
85
+ gap: labelGap
86
+ })
87
+ const selectLabelStyles = (
88
+ {
89
+ labelColor,
90
+ labelCurrentColor,
91
+ labelCurrentFontWeight,
92
+ labelFontSize,
93
+ labelFontWeight,
94
+ labelFontName,
95
+ labelLineHeight
96
+ },
97
+ isCurrent
98
+ ) =>
99
+ applyTextStyles({
100
+ color: isCurrent ? labelCurrentColor : labelColor,
101
+ fontSize: labelFontSize,
102
+ lineHeight: labelLineHeight,
103
+ fontWeight: isCurrent ? labelCurrentFontWeight : labelFontWeight,
104
+ fontName: labelFontName
105
+ })
106
+ const getStepTestID = (isCompleted, isCurrent) => {
107
+ let testID = 'StepTracker-Step'
108
+ if (isCompleted) {
109
+ testID += '-Completed'
110
+ } else if (isCurrent) {
111
+ testID += '-Current'
112
+ }
113
+
114
+ return testID
115
+ }
116
+
117
+ /**
118
+ * A single step of a StepTracker.
119
+ */
120
+ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens }) => {
121
+ const { completedIcon, showStepLabel, showStepName, ...themeTokens } = tokens
122
+ const isFirst = stepIndex === 0
123
+ const isLast = stepIndex === stepCount - 1
124
+ const isCompleted = status > stepIndex
125
+ const isCurrent = status === stepIndex
126
+ const isActive = isCompleted || isCurrent
127
+
128
+ return (
129
+ <StackView
130
+ space={0}
131
+ tokens={{ alignItems: 'stretch', flexGrow: 1 }}
132
+ accessibilityLabel={label}
133
+ accessibilityLevel={2}
134
+ accessibilityCurrent={status === stepIndex ? 'true' : 'false'}
135
+ >
136
+ <StackView direction="row" space={0} tokens={{ alignItems: 'center', flexGrow: 0 }}>
137
+ <View
138
+ style={[staticStyles.connector, !isFirst && selectConnectorStyles(themeTokens, isActive)]}
139
+ />
140
+ <View
141
+ style={[staticStyles.knob, selectKnobStyles(themeTokens, isCompleted, isCurrent)]}
142
+ testID={getStepTestID(isCompleted, isCurrent)}
143
+ >
144
+ {isCompleted && completedIcon && (
145
+ <Icon icon={completedIcon} tokens={selectCompletedIconTokens(themeTokens)} />
146
+ )}
147
+ {isCurrent && <View style={selectCurrentInnerStyles(themeTokens)} />}
148
+ </View>
149
+ <View
150
+ style={[
151
+ staticStyles.connector,
152
+ !isLast && selectConnectorStyles(themeTokens, isCompleted)
153
+ ]}
154
+ />
155
+ </StackView>
156
+ {showStepLabel && (
157
+ <View style={[staticStyles.stepLabelContainer, selectLabelContainerStyles(tokens)]}>
158
+ {showStepName && (
159
+ <Text style={[staticStyles.centeredText, selectLabelStyles(tokens, isCurrent)]}>
160
+ {name}
161
+ </Text>
162
+ )}
163
+ <StackView direction="row">
164
+ <Text
165
+ style={[
166
+ staticStyles.centeredText,
167
+ tokens.labelDirection === 'column' && staticStyles.wrappingLabel,
168
+ selectLabelStyles(tokens, isCurrent)
169
+ ]}
170
+ >
171
+ {label}
172
+ </Text>
173
+ </StackView>
174
+ </View>
175
+ )}
176
+ </StackView>
177
+ )
178
+ }
179
+
180
+ Step.propTypes = {
181
+ label: PropTypes.string.isRequired,
182
+ name: PropTypes.string.isRequired,
183
+ status: PropTypes.number.isRequired,
184
+ stepCount: PropTypes.number.isRequired,
185
+ stepIndex: PropTypes.number.isRequired,
186
+ tokens: getTokensPropType('StepTracker'),
187
+ variant: variantProp.propType
188
+ }
189
+
190
+ export default Step
191
+
192
+ const staticStyles = StyleSheet.create({
193
+ centeredText: { textAlign: 'center' },
194
+ connector: { flex: 1, height: 1 },
195
+ completedKnob: {
196
+ backgroundColor: 'transparent',
197
+ textAlign: 'center'
198
+ },
199
+ knob: { borderWidth: 1 },
200
+ stepLabelContainer: { justifyContent: 'center' },
201
+ wrappingLabel: { width: 0, flex: 1 }
202
+ })
@@ -0,0 +1,163 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { StyleSheet, Text, View } from 'react-native'
4
+
5
+ import StackView from '../StackView'
6
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
7
+ import { getTokensPropType, variantProp } from '../utils'
8
+ import { a11yProps } from '../utils/propTypes'
9
+ import { useViewport } from '../ViewportProvider'
10
+ import useCopy from '../utils/useCopy'
11
+
12
+ import Step from './Step'
13
+ import defaultDictionary from './dictionary'
14
+
15
+ const selectContainerStyles = ({
16
+ containerPaddingBottom,
17
+ containerPaddingLeft,
18
+ containerPaddingRight,
19
+ containerPaddingTop
20
+ }) => ({
21
+ paddingBottom: containerPaddingBottom,
22
+ paddingLeft: containerPaddingLeft,
23
+ paddingRight: containerPaddingRight,
24
+ paddingTop: containerPaddingTop
25
+ })
26
+ const selectStepTrackerLabelContainerStyles = ({ labelMarginTop }) => ({
27
+ marginTop: labelMarginTop
28
+ })
29
+ const selectStepTrackerLabelStyles = ({
30
+ labelColor,
31
+ labelFontSize,
32
+ labelFontWeight,
33
+ labelFontName,
34
+ labelLineHeight
35
+ }) =>
36
+ applyTextStyles({
37
+ color: labelColor,
38
+ fontSize: labelFontSize,
39
+ lineHeight: labelLineHeight,
40
+ fontWeight: labelFontWeight,
41
+ fontName: labelFontName
42
+ })
43
+
44
+ /**
45
+ * StepTracker component shows the current position in a sequence of steps.
46
+ *
47
+ * ## Component API
48
+ *
49
+ * - `current` prop allows to control the current step, 0-based number;
50
+ * - use `copy` and `dictionary` props to internationalize the labels;
51
+ * - `steps` is and array of strings defining step titles;
52
+ * - tokens and variant props define the appearance of the `StepTracker`:
53
+ *
54
+ * ## Usability and A11y guidelines
55
+ *
56
+ * Keep in mind that in its current implementation this is not an interactive
57
+ * component and can’t be used to navigate between steps. The application
58
+ * must provide its own navigation mechanism and state control. That is the
59
+ * main reason the component assumes the `progressbar` role in terms of
60
+ * accessibility. This also makes it extremely important to make sure you
61
+ * set a11y on controls used to modify the state of the step tracker.
62
+ *
63
+ * You can pass standard a11y props to the `StepTracker` component, including
64
+ * the ones that are specific to the `progressbar` role. By default the
65
+ * following props are used:
66
+ *
67
+ * - `accessibilityRole` (`role`): `progressbar`,
68
+ * - `accessibilityLabel` (`aria-label`): `Step I of N: <Current Step Label>`,
69
+ * - `accessibilityLevel` (`aria-level`): 1,
70
+ * - `accessibilityValue.min` (`aria-valuemin`): `0`,
71
+ * - `accessibilityValue.max` (`aria-valuemax`): `steps.length - 1`,
72
+ * - `accessibilityValue.now` (`aria-valuenow`): `current`,
73
+ * - `accessibilityValue.text` (`aria-valuetext`): `<Current Step Label>`,
74
+ *
75
+ */
76
+ const StepTracker = ({
77
+ current = 0,
78
+ copy = 'en',
79
+ dictionary = defaultDictionary,
80
+ steps = [],
81
+ tokens,
82
+ variant,
83
+ ...rest
84
+ }) => {
85
+ const viewport = useViewport()
86
+ const { showStepTrackerLabel, ...themeTokens } = useThemeTokens('StepTracker', tokens, variant, {
87
+ viewport
88
+ })
89
+ const getCopy = useCopy({ dictionary, copy })
90
+ const stepTrackerLabel = getCopy('stepTrackerLabel')
91
+ .replace('%{stepNumber}', current < steps.length ? current + 1 : steps.length)
92
+ .replace('%{stepCount}', steps.length)
93
+ .replace('%{stepLabel}', current < steps.length ? steps[current] : steps[steps.length - 1])
94
+ if (!steps.length) return null
95
+ const accessibilityProps = {
96
+ accessibilityLabel: stepTrackerLabel,
97
+ accessibilityLevel: 1,
98
+ accessibilityRole: 'progressbar',
99
+ accessibilityValue: {
100
+ min: 0,
101
+ max: steps.length - 1,
102
+ now: current,
103
+ text: steps[current]
104
+ },
105
+ ...a11yProps.select(rest)
106
+ }
107
+
108
+ return (
109
+ <View style={selectContainerStyles(themeTokens)} {...accessibilityProps}>
110
+ <StackView space={0}>
111
+ <View style={staticStyles.stepsContainer}>
112
+ {steps.map((label, index) => {
113
+ return (
114
+ <Step
115
+ status={current}
116
+ key={label}
117
+ label={label}
118
+ name={getCopy('stepLabel').replace('%{stepNumber}', index + 1)}
119
+ stepIndex={index}
120
+ stepCount={steps.length}
121
+ tokens={themeTokens}
122
+ />
123
+ )
124
+ })}
125
+ </View>
126
+ {showStepTrackerLabel && (
127
+ <View
128
+ style={[
129
+ staticStyles.stepTrackerLabelContainer,
130
+ selectStepTrackerLabelContainerStyles(themeTokens)
131
+ ]}
132
+ >
133
+ <Text style={selectStepTrackerLabelStyles(themeTokens)}>{stepTrackerLabel}</Text>
134
+ </View>
135
+ )}
136
+ </StackView>
137
+ </View>
138
+ )
139
+ }
140
+
141
+ StepTracker.propTypes = {
142
+ current: PropTypes.number,
143
+ copy: PropTypes.oneOf(['en', 'fr']),
144
+ dictionary: PropTypes.shape({
145
+ en: PropTypes.shape({ stepLabel: PropTypes.string, stepTrackerLabel: PropTypes.string }),
146
+ fr: PropTypes.shape({ stepLabel: PropTypes.string, stepTrackerLabel: PropTypes.string })
147
+ }),
148
+ steps: PropTypes.arrayOf(PropTypes.string),
149
+ tokens: getTokensPropType('StepTracker'),
150
+ variant: variantProp.propType
151
+ }
152
+
153
+ export default StepTracker
154
+
155
+ const staticStyles = StyleSheet.create({
156
+ stepsContainer: {
157
+ alignItems: 'flex-start',
158
+ flexDirection: 'row',
159
+ justifyContent: 'space-evenly',
160
+ flexGrow: 1
161
+ },
162
+ stepTrackerLabelContainer: { textAlign: 'center', flexDirection: 'row', justifyContent: 'center' }
163
+ })
@@ -0,0 +1,10 @@
1
+ export default {
2
+ en: {
3
+ stepLabel: 'Step %{stepNumber}',
4
+ stepTrackerLabel: 'Step %{stepNumber} of %{stepCount}: %{stepLabel}'
5
+ },
6
+ fr: {
7
+ stepLabel: 'Étape %{stepNumber}',
8
+ stepTrackerLabel: 'Étape %{stepNumber} sur %{stepCount}: %{stepLabel}'
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ import StepTracker from './StepTracker'
2
+
3
+ export default StepTracker
@@ -0,0 +1,165 @@
1
+ import React, { useEffect, useRef, useState } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Platform, StyleSheet, View } from 'react-native'
4
+
5
+ import { validateThemeTokens, resolveThemeTokens } from '../ThemeProvider'
6
+ import { getTokensSetPropType, selectTokens, a11yProps } from '../utils'
7
+ import ScrollViewEnd from './ScrollViewEnd'
8
+ import { getItemPositionScrollTarget, itemPositionsPropType } from './itemPositions'
9
+
10
+ const tokenKeys = [
11
+ 'nextIcon',
12
+ 'previousIcon',
13
+ 'gutter',
14
+ 'borderBottomWidth',
15
+ 'borderBottomColor',
16
+ 'buttonClearance'
17
+ ]
18
+ export const selectHorizontalScrollTokens = (themeTokens, prefix) =>
19
+ selectTokens(tokenKeys, themeTokens, prefix)
20
+
21
+ /**
22
+ * Base component that scrolls horizontally and displays left and right buttons if
23
+ * there is content to the left and the right which can be scrolled to.
24
+ *
25
+ * @TODO revist `ScrollButton` after IconButton is stable.
26
+ */
27
+ const HorizontalScroll = ({ ScrollButton, tokens, itemPositions, children, ...rest }) => {
28
+ const {
29
+ nextIcon,
30
+ previousIcon,
31
+ gutter,
32
+ borderBottomWidth,
33
+ borderBottomColor,
34
+ buttonClearance = 0
35
+ } = validateThemeTokens(
36
+ resolveThemeTokens(tokens),
37
+ // Allow missing icons
38
+ getTokensSetPropType(tokenKeys, { partial: true }),
39
+ 'HorizontalScroll'
40
+ )
41
+
42
+ const [containerWidth, setContainerWidth] = useState(0)
43
+ const [contentWidth, setContentWidth] = useState(0)
44
+ const handleContentWidth = (width) => setContentWidth(width)
45
+ const handleContainerLayout = ({
46
+ nativeEvent: {
47
+ layout: { width }
48
+ }
49
+ }) => setContainerWidth(width)
50
+
51
+ // Update the scroll position only when scrolling actions ends to minimally rerender
52
+ // and update which buttons are conditionally rendered.
53
+ const [scrollOffset, setScrollOffset] = useState(0)
54
+ const handleScrollEnd = ({
55
+ nativeEvent: {
56
+ contentOffset: { x }
57
+ }
58
+ }) => {
59
+ setScrollOffset(x)
60
+ }
61
+
62
+ const scrollMax = Math.max(0, contentWidth - containerWidth)
63
+ const showNextButton = scrollOffset < scrollMax
64
+ const showPreviousButton = scrollOffset > 0
65
+
66
+ const scrollRef = useRef(null)
67
+ const scrollTo = (targetX) => {
68
+ if (typeof scrollRef.current?.scrollTo === 'function') {
69
+ const x = getItemPositionScrollTarget(
70
+ targetX,
71
+ scrollMax,
72
+ itemPositions.positions,
73
+ buttonClearance
74
+ )
75
+ scrollRef.current.scrollTo({ x, animated: true })
76
+
77
+ if (Platform.OS === 'android') {
78
+ // React Native calls onScrollMomentumEnd after scrollTo for iOS, but not Android.
79
+ // See https://github.com/facebook/react-native/issues/26661
80
+ // There's also no callback for when scrollTo finishes.
81
+ setTimeout(() => setScrollOffset(x), 300)
82
+ }
83
+ }
84
+ }
85
+ // Make scrollTo and relevant co-ordinates available to any component with an interest in itemPositions.
86
+ // ItemPositions is a mutable ref object intended to have its properties mutated without any rerenders.
87
+ /* eslint-disable-next-line no-param-reassign */
88
+ itemPositions.scrollTo = scrollTo
89
+ /* eslint-disable-next-line no-param-reassign */
90
+ itemPositions.containerWidth = containerWidth
91
+ /* eslint-disable-next-line no-param-reassign */
92
+ itemPositions.contentWidth = contentWidth
93
+ /* eslint-disable-next-line no-param-reassign */
94
+ itemPositions.scrollOffset = scrollOffset
95
+
96
+ const hasWidths = itemPositions.contentWidth > 0 && itemPositions.contentWidth > 0
97
+ useEffect(() => {
98
+ if (hasWidths) itemPositions.setIsReady(true)
99
+ // itemPositions is a ref object so this should occur only when elements' widths are set in state
100
+ }, [hasWidths, itemPositions])
101
+
102
+ // When scrolling, allow clearance around scroll buttons, so e.g. this...
103
+ // (<-) Item 5 Item 6 Item 7 Item 8 It(->)
104
+ // ...doesn't scroll like this with Item 9 never being properly visible:
105
+ // (<-)em 9 Item 10 Item 11 Item 12 Item (->)
106
+ const scrollDistance = containerWidth - buttonClearance * 2
107
+ const scrollNext = () => scrollTo(scrollOffset + scrollDistance)
108
+ const scrollPrevious = () => scrollTo(scrollOffset - scrollDistance)
109
+
110
+ const a11y = a11yProps.select(rest)
111
+
112
+ // On Web, being scrollable in one direction prevents overflow: visible in the other direction.
113
+ // Apply a fixed margin gutter that tab highlight effects can overhang without getting trimmed.
114
+ return (
115
+ <View style={staticStyles.container}>
116
+ <ScrollViewEnd
117
+ onScrollEnd={handleScrollEnd}
118
+ ref={scrollRef}
119
+ horizontal
120
+ onContentSizeChange={handleContentWidth}
121
+ onLayout={handleContainerLayout}
122
+ showsHorizontalScrollIndicator={false}
123
+ contentContainerStyle={[
124
+ staticStyles.scrollContainer,
125
+ { marginBottom: gutter, borderBottomWidth, borderBottomColor }
126
+ ]}
127
+ {...a11y}
128
+ >
129
+ {children}
130
+ </ScrollViewEnd>
131
+ {showPreviousButton && (
132
+ <ScrollButton
133
+ icon={previousIcon}
134
+ onPress={scrollPrevious}
135
+ offset={gutter}
136
+ direction="previous"
137
+ />
138
+ )}
139
+ {showNextButton && (
140
+ <ScrollButton icon={nextIcon} onPress={scrollNext} offset={gutter} direction="next" />
141
+ )}
142
+ </View>
143
+ )
144
+ }
145
+
146
+ const staticStyles = StyleSheet.create({
147
+ scrollContainer: {
148
+ // Gives inner container width from content, allowing it to exceed its container's width
149
+ alignSelf: 'flex-start',
150
+ flexGrow: 1
151
+ },
152
+ container: {
153
+ // Vertically center the ScrollButtons buttons
154
+ justifyContent: 'center'
155
+ }
156
+ })
157
+
158
+ HorizontalScroll.propTypes = {
159
+ itemPositions: itemPositionsPropType,
160
+ ScrollButton: PropTypes.elementType,
161
+ tokens: getTokensSetPropType(tokenKeys, { allowFunction: true }),
162
+ children: PropTypes.node
163
+ }
164
+
165
+ export default HorizontalScroll