@telus-uds/components-base 0.0.2-prerelease.3 → 0.0.2-prerelease.7

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 (266) hide show
  1. package/.ultra.cache.json +1 -0
  2. package/CHANGELOG.md +55 -0
  3. package/__fixtures__/testTheme.js +528 -42
  4. package/__tests__/Button/ButtonBase.test.jsx +3 -32
  5. package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
  6. package/__tests__/Divider/Divider.test.jsx +26 -5
  7. package/__tests__/Feedback/Feedback.test.jsx +42 -0
  8. package/__tests__/FlexGrid/Col.test.jsx +5 -0
  9. package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
  10. package/__tests__/List/List.test.jsx +60 -0
  11. package/__tests__/Radio/Radio.test.jsx +87 -0
  12. package/__tests__/Select/Select.test.jsx +93 -0
  13. package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
  14. package/__tests__/Spacer/Spacer.test.jsx +63 -0
  15. package/__tests__/StackView/StackView.test.jsx +216 -0
  16. package/__tests__/StackView/StackWrap.test.jsx +47 -0
  17. package/__tests__/StackView/getStackedContent.test.jsx +295 -0
  18. package/__tests__/Tags/Tags.test.jsx +328 -0
  19. package/__tests__/TextInput/TextArea.test.jsx +34 -0
  20. package/__tests__/TextInput/TextInputBase.test.jsx +120 -0
  21. package/__tests__/Tooltip/Tooltip.test.jsx +65 -0
  22. package/__tests__/Tooltip/getTooltipPosition.test.js +79 -0
  23. package/__tests__/utils/useCopy.test.js +31 -0
  24. package/__tests__/utils/useResponsiveProp.test.jsx +202 -0
  25. package/__tests__/utils/{spacing.test.jsx → useSpacingScale.test.jsx} +1 -1
  26. package/__tests__/utils/useUniqueId.test.js +31 -0
  27. package/jest.config.js +8 -2
  28. package/lib/Box/Box.js +7 -2
  29. package/lib/Button/Button.js +10 -3
  30. package/lib/Button/ButtonBase.js +79 -75
  31. package/lib/Button/ButtonGroup.js +24 -49
  32. package/lib/Button/ButtonLink.js +5 -0
  33. package/lib/Checkbox/Checkbox.js +308 -0
  34. package/lib/Checkbox/CheckboxInput.native.js +6 -0
  35. package/lib/Checkbox/CheckboxInput.web.js +57 -0
  36. package/lib/Checkbox/index.js +2 -0
  37. package/lib/Divider/Divider.js +40 -2
  38. package/lib/Feedback/Feedback.js +132 -0
  39. package/lib/Feedback/index.js +2 -0
  40. package/lib/Icon/Icon.js +9 -6
  41. package/lib/Icon/IconText.js +72 -0
  42. package/lib/Icon/index.js +2 -1
  43. package/lib/InputLabel/InputLabel.js +88 -0
  44. package/lib/InputLabel/LabelContent.native.js +8 -0
  45. package/lib/InputLabel/LabelContent.web.js +17 -0
  46. package/lib/InputLabel/index.js +2 -0
  47. package/lib/InputSupports/InputSupports.js +90 -0
  48. package/lib/InputSupports/index.js +2 -0
  49. package/lib/InputSupports/propTypes.js +55 -0
  50. package/lib/Link/ChevronLink.js +35 -10
  51. package/lib/Link/InlinePressable.native.js +78 -0
  52. package/lib/Link/InlinePressable.web.js +32 -0
  53. package/lib/Link/Link.js +11 -10
  54. package/lib/Link/LinkBase.js +69 -124
  55. package/lib/Link/TextButton.js +20 -9
  56. package/lib/Link/index.js +2 -1
  57. package/lib/List/List.js +52 -0
  58. package/lib/List/ListItem.js +207 -0
  59. package/lib/List/index.js +2 -0
  60. package/lib/Pagination/PageButton.js +3 -26
  61. package/lib/Pagination/SideButton.js +32 -42
  62. package/lib/Radio/Radio.js +291 -0
  63. package/lib/Radio/RadioInput.native.js +6 -0
  64. package/lib/Radio/RadioInput.web.js +59 -0
  65. package/lib/Radio/index.js +2 -0
  66. package/lib/Select/Group.native.js +14 -0
  67. package/lib/Select/Group.web.js +18 -0
  68. package/lib/Select/Item.native.js +9 -0
  69. package/lib/Select/Item.web.js +15 -0
  70. package/lib/Select/Picker.native.js +87 -0
  71. package/lib/Select/Picker.web.js +63 -0
  72. package/lib/Select/Select.js +272 -0
  73. package/lib/Select/index.js +6 -0
  74. package/lib/Skeleton/Skeleton.js +119 -0
  75. package/lib/Skeleton/index.js +2 -0
  76. package/lib/Spacer/Spacer.js +98 -0
  77. package/lib/Spacer/index.js +2 -0
  78. package/lib/StackView/StackView.js +107 -0
  79. package/lib/StackView/StackWrap.js +32 -0
  80. package/lib/StackView/StackWrap.native.js +3 -0
  81. package/lib/StackView/StackWrapBox.js +90 -0
  82. package/lib/StackView/StackWrapGap.js +50 -0
  83. package/lib/StackView/common.js +30 -0
  84. package/lib/StackView/getStackedContent.js +111 -0
  85. package/lib/StackView/index.js +5 -0
  86. package/lib/Tags/Tags.js +217 -0
  87. package/lib/Tags/index.js +2 -0
  88. package/lib/TextInput/TextArea.js +82 -0
  89. package/lib/TextInput/TextInput.js +54 -0
  90. package/lib/TextInput/TextInputBase.js +229 -0
  91. package/lib/TextInput/index.js +3 -0
  92. package/lib/TextInput/propTypes.js +31 -0
  93. package/lib/ThemeProvider/useThemeTokens.js +54 -3
  94. package/lib/ToggleSwitch/ToggleSwitch.js +1 -1
  95. package/lib/Tooltip/Backdrop.native.js +35 -0
  96. package/lib/Tooltip/Backdrop.web.js +52 -0
  97. package/lib/Tooltip/Tooltip.js +315 -0
  98. package/lib/Tooltip/dictionary.js +8 -0
  99. package/lib/Tooltip/getTooltipPosition.js +164 -0
  100. package/lib/Tooltip/index.js +2 -0
  101. package/lib/TooltipButton/TooltipButton.js +64 -0
  102. package/lib/TooltipButton/index.js +2 -0
  103. package/lib/Typography/Typography.js +4 -23
  104. package/lib/ViewportProvider/ViewportProvider.js +25 -0
  105. package/lib/ViewportProvider/index.js +2 -43
  106. package/lib/ViewportProvider/useViewport.js +3 -0
  107. package/lib/ViewportProvider/useViewportListener.js +43 -0
  108. package/lib/index.js +15 -1
  109. package/lib/utils/a11y/index.js +1 -0
  110. package/lib/utils/a11y/textSize.js +33 -0
  111. package/lib/utils/index.js +7 -1
  112. package/lib/utils/info/index.js +7 -0
  113. package/lib/utils/info/platform/index.js +11 -0
  114. package/lib/utils/info/platform/platform.android.js +1 -0
  115. package/lib/utils/info/platform/platform.ios.js +1 -0
  116. package/lib/utils/info/platform/platform.native.js +4 -0
  117. package/lib/utils/info/platform/platform.web.js +1 -0
  118. package/lib/utils/info/versions.js +5 -0
  119. package/lib/utils/input.js +3 -1
  120. package/lib/utils/pressability.js +92 -0
  121. package/lib/utils/propTypes.js +77 -8
  122. package/lib/utils/useCopy.js +16 -0
  123. package/lib/utils/useResponsiveProp.js +47 -0
  124. package/lib/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +30 -9
  125. package/lib/utils/useUniqueId.js +12 -0
  126. package/package.json +7 -5
  127. package/release-context.json +4 -4
  128. package/src/Box/Box.jsx +4 -2
  129. package/src/Button/Button.jsx +6 -3
  130. package/src/Button/ButtonBase.jsx +72 -75
  131. package/src/Button/ButtonGroup.jsx +22 -39
  132. package/src/Button/ButtonLink.jsx +11 -2
  133. package/src/Checkbox/Checkbox.jsx +275 -0
  134. package/src/Checkbox/CheckboxInput.native.jsx +6 -0
  135. package/src/Checkbox/CheckboxInput.web.jsx +55 -0
  136. package/src/Checkbox/index.js +3 -0
  137. package/src/Divider/Divider.jsx +38 -3
  138. package/src/Feedback/Feedback.jsx +108 -0
  139. package/src/Feedback/index.js +3 -0
  140. package/src/Icon/Icon.jsx +11 -6
  141. package/src/Icon/IconText.jsx +63 -0
  142. package/src/Icon/index.js +2 -1
  143. package/src/InputLabel/InputLabel.jsx +99 -0
  144. package/src/InputLabel/LabelContent.native.jsx +6 -0
  145. package/src/InputLabel/LabelContent.web.jsx +13 -0
  146. package/src/InputLabel/index.js +3 -0
  147. package/src/InputSupports/InputSupports.jsx +86 -0
  148. package/src/InputSupports/index.js +3 -0
  149. package/src/InputSupports/propTypes.js +44 -0
  150. package/src/Link/ChevronLink.jsx +28 -7
  151. package/src/Link/InlinePressable.native.jsx +73 -0
  152. package/src/Link/InlinePressable.web.jsx +37 -0
  153. package/src/Link/Link.jsx +17 -13
  154. package/src/Link/LinkBase.jsx +62 -139
  155. package/src/Link/TextButton.jsx +25 -11
  156. package/src/Link/index.js +2 -1
  157. package/src/List/List.jsx +47 -0
  158. package/src/List/ListItem.jsx +187 -0
  159. package/src/List/index.js +3 -0
  160. package/src/Pagination/PageButton.jsx +3 -17
  161. package/src/Pagination/SideButton.jsx +27 -38
  162. package/src/Radio/Radio.jsx +270 -0
  163. package/src/Radio/RadioInput.native.jsx +6 -0
  164. package/src/Radio/RadioInput.web.jsx +57 -0
  165. package/src/Radio/index.js +3 -0
  166. package/src/Select/Group.native.jsx +14 -0
  167. package/src/Select/Group.web.jsx +15 -0
  168. package/src/Select/Item.native.jsx +10 -0
  169. package/src/Select/Item.web.jsx +11 -0
  170. package/src/Select/Picker.native.jsx +95 -0
  171. package/src/Select/Picker.web.jsx +67 -0
  172. package/src/Select/Select.jsx +265 -0
  173. package/src/Select/index.js +8 -0
  174. package/src/Skeleton/Skeleton.jsx +101 -0
  175. package/src/Skeleton/index.js +3 -0
  176. package/src/Spacer/Spacer.jsx +91 -0
  177. package/src/Spacer/index.js +3 -0
  178. package/src/StackView/StackView.jsx +104 -0
  179. package/src/StackView/StackWrap.jsx +33 -0
  180. package/src/StackView/StackWrap.native.jsx +4 -0
  181. package/src/StackView/StackWrapBox.jsx +93 -0
  182. package/src/StackView/StackWrapGap.jsx +49 -0
  183. package/src/StackView/common.jsx +28 -0
  184. package/src/StackView/getStackedContent.jsx +106 -0
  185. package/src/StackView/index.js +6 -0
  186. package/src/Tags/Tags.jsx +206 -0
  187. package/src/Tags/index.js +3 -0
  188. package/src/TextInput/TextArea.jsx +78 -0
  189. package/src/TextInput/TextInput.jsx +52 -0
  190. package/src/TextInput/TextInputBase.jsx +220 -0
  191. package/src/TextInput/index.js +4 -0
  192. package/src/TextInput/propTypes.js +29 -0
  193. package/src/ThemeProvider/useThemeTokens.js +54 -3
  194. package/src/ToggleSwitch/ToggleSwitch.jsx +1 -1
  195. package/src/Tooltip/Backdrop.native.jsx +33 -0
  196. package/src/Tooltip/Backdrop.web.jsx +60 -0
  197. package/src/Tooltip/Tooltip.jsx +294 -0
  198. package/src/Tooltip/dictionary.js +8 -0
  199. package/src/Tooltip/getTooltipPosition.js +161 -0
  200. package/src/Tooltip/index.js +3 -0
  201. package/src/TooltipButton/TooltipButton.jsx +53 -0
  202. package/src/TooltipButton/index.js +3 -0
  203. package/src/Typography/Typography.jsx +4 -19
  204. package/src/ViewportProvider/ViewportProvider.jsx +21 -0
  205. package/src/ViewportProvider/index.jsx +2 -41
  206. package/src/ViewportProvider/useViewport.js +5 -0
  207. package/src/ViewportProvider/useViewportListener.js +43 -0
  208. package/src/index.js +15 -1
  209. package/src/utils/a11y/index.js +1 -0
  210. package/src/utils/a11y/textSize.js +30 -0
  211. package/src/utils/index.js +8 -1
  212. package/src/utils/info/index.js +8 -0
  213. package/src/utils/info/platform/index.js +11 -0
  214. package/src/utils/info/platform/platform.android.js +1 -0
  215. package/src/utils/info/platform/platform.ios.js +1 -0
  216. package/src/utils/info/platform/platform.native.js +4 -0
  217. package/src/utils/info/platform/platform.web.js +1 -0
  218. package/src/utils/info/versions.js +6 -0
  219. package/src/utils/input.js +2 -1
  220. package/src/utils/pressability.js +92 -0
  221. package/src/utils/propTypes.js +97 -13
  222. package/src/utils/useCopy.js +13 -0
  223. package/src/utils/useResponsiveProp.js +50 -0
  224. package/src/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +25 -10
  225. package/src/utils/useUniqueId.js +14 -0
  226. package/stories/A11yText/A11yText.stories.jsx +11 -5
  227. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
  228. package/stories/Box/Box.stories.jsx +29 -2
  229. package/stories/Button/Button.stories.jsx +21 -20
  230. package/stories/Button/ButtonGroup.stories.jsx +2 -1
  231. package/stories/Button/ButtonLink.stories.jsx +6 -4
  232. package/stories/Card/Card.stories.jsx +13 -1
  233. package/stories/Checkbox/Checkbox.stories.jsx +71 -0
  234. package/stories/Divider/Divider.stories.jsx +26 -2
  235. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
  236. package/stories/Feedback/Feedback.stories.jsx +96 -0
  237. package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
  238. package/stories/Icon/Icon.stories.jsx +11 -3
  239. package/stories/InputLabel/InputLabel.stories.jsx +42 -0
  240. package/stories/Link/ChevronLink.stories.jsx +20 -4
  241. package/stories/Link/Link.stories.jsx +39 -3
  242. package/stories/Link/TextButton.stories.jsx +24 -2
  243. package/stories/List/List.stories.jsx +117 -0
  244. package/stories/Pagination/Pagination.stories.jsx +28 -14
  245. package/stories/Radio/Radio.stories.jsx +113 -0
  246. package/stories/Select/Select.stories.jsx +55 -0
  247. package/stories/SideNav/SideNav.stories.jsx +17 -2
  248. package/stories/Skeleton/Skeleton.stories.jsx +36 -0
  249. package/stories/Spacer/Spacer.stories.jsx +38 -0
  250. package/stories/StackView/StackView.stories.jsx +75 -0
  251. package/stories/StackView/StackWrap.stories.jsx +64 -0
  252. package/stories/Tags/Tags.stories.jsx +69 -0
  253. package/stories/TextInput/TextArea.stories.jsx +100 -0
  254. package/stories/TextInput/TextInput.stories.jsx +103 -0
  255. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
  256. package/stories/Tooltip/Tooltip.stories.jsx +81 -0
  257. package/stories/TooltipButton/TooltipButton.stories.jsx +11 -0
  258. package/stories/Typography/Typography.stories.jsx +12 -3
  259. package/stories/platform-supports.web.jsx +1 -1
  260. package/stories/supports.jsx +110 -14
  261. package/lib/Pagination/useCopy.js +0 -10
  262. package/lib/utils/spacing/index.js +0 -2
  263. package/lib/utils/spacing/utils.js +0 -32
  264. package/src/Pagination/useCopy.js +0 -7
  265. package/src/utils/spacing/index.js +0 -3
  266. package/src/utils/spacing/utils.js +0 -28
@@ -0,0 +1,43 @@
1
+ import { useLayoutEffect } from 'react';
2
+ import { Dimensions } from 'react-native';
3
+ import { viewports } from '@telus-uds/system-constants'; // Use Dimensions instead of useWindowDimensions because useWindowDimensions forces context
4
+ // to update on every pixel change during window resize; but we only want rerenders to occur
5
+ // when a viewport threshold has been crossed.
6
+
7
+ const lookupViewport = () => viewports.select(Dimensions.get('window').width);
8
+ /**
9
+ * In SSR, React gets spooked if it sees `useLayoutEffect` and fires warnings assuming the
10
+ * developer doesn't realise the effect won't run: https://reactjs.org/link/uselayouteffect-ssr
11
+ *
12
+ * To avoid these warnings while still conforming to the rules of hooks, always use this
13
+ * explicitly no-op hook, instead of the useLayoutEffect that is implicitly no-op on SSR.
14
+ */
15
+
16
+
17
+ const useViewportListenerSSR = () => {};
18
+ /**
19
+ * When client-side rendering, immediately set the viewport to the correct value as a layout effect so
20
+ * if the viewport isn't the smallest, any SSR-rendered components rerender correctly before anything
21
+ * is shown to the user. Then bind events to update the viewport if it changes.
22
+ */
23
+
24
+
25
+ const useViewportListenerCSR = setViewport => {
26
+ useLayoutEffect(() => {
27
+ setViewport(lookupViewport());
28
+
29
+ const onChange = ({
30
+ window
31
+ }) => setViewport(viewports.select(window.width));
32
+
33
+ const listener = Dimensions.addEventListener('change', onChange); // From RN 0.65.0, Dimensions.removeEventListener is deprecated for `remove` on addEventListener return value;
34
+ // however, that is not available in RN <=0.64.X, therefore not in any Expo release as of 2021 (Expo SDK 43).
35
+
36
+ return listener?.remove || (() => Dimensions.removeEventListener('change', onChange)); // setViewport is a function from `useState` so it is stable and won't make the effect re-run
37
+ }, [setViewport]);
38
+ }; // Window is a defined global object in both Web and Native client-side, and undefined in SSR
39
+
40
+
41
+ const isSSR = typeof window === 'undefined';
42
+ const useViewportListener = isSSR ? useViewportListenerSSR : useViewportListenerCSR;
43
+ export default useViewportListener;
package/lib/index.js CHANGED
@@ -1,19 +1,33 @@
1
+ export { default as A11yText } from './A11yText';
1
2
  export { default as ActivityIndicator } from './ActivityIndicator';
2
3
  export { default as Box } from './Box';
3
4
  export * from './Button';
4
5
  export { default as Card } from './Card';
6
+ export { default as Checkbox } from './Checkbox';
5
7
  export { default as Divider } from './Divider';
6
8
  export { default as ExpandCollapse, Accordion } from './ExpandCollapse';
9
+ export { default as Feedback } from './Feedback';
7
10
  export { default as FlexGrid } from './FlexGrid';
8
11
  export { default as Icon } from './Icon';
9
12
  export * from './Icon';
10
13
  export * from './Link';
14
+ export { default as List } from './List';
11
15
  export { default as Pagination } from './Pagination';
16
+ export { default as Radio } from './Radio';
17
+ export { default as Select } from './Select';
12
18
  export { default as SideNav } from './SideNav';
19
+ export { default as Skeleton } from './Skeleton';
20
+ export { default as Spacer } from './Spacer';
21
+ export { default as StackView } from './StackView';
22
+ export * from './StackView';
23
+ export { default as Tags } from './Tags';
24
+ export * from './TextInput';
13
25
  export { default as ToggleSwitch } from './ToggleSwitch';
26
+ export { default as Tooltip } from './Tooltip';
27
+ export { default as TooltipButton } from './TooltipButton';
14
28
  export { default as Typography } from './Typography';
15
29
  export { default as A11yInfoProvider, useA11yInfo } from './A11yInfoProvider';
16
30
  export { default as BaseProvider } from './BaseProvider';
17
31
  export { default as ViewportProvider, useViewport } from './ViewportProvider';
18
- export { default as ThemeProvider, useTheme, useSetTheme } from './ThemeProvider';
32
+ export { default as ThemeProvider, useTheme, useSetTheme, useThemeTokens } from './ThemeProvider';
19
33
  export * from './utils';
@@ -0,0 +1 @@
1
+ export * from './textSize';
@@ -0,0 +1,33 @@
1
+ import { PixelRatio, Platform } from 'react-native';
2
+ /**
3
+ * When a user increases text size using device accessibility settings, some elements
4
+ * should also scale, such as icons alongside text and space between paragraphs.
5
+ *
6
+ * @param {number} [value] - the value to multiply; defaults to system default font size
7
+ * @param {number} [maxScale] - the maximum multiplier to apply; defaults to no maximum
8
+ */
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
+ * Native only, returns `undefined` on Web.
17
+ *
18
+ * Enforces `fontScaleCap` theme tokens as the maximum font size text can become
19
+ * after iOS or Android font scaling, to give consistent accessible maximum sizes
20
+ * that don't make the content unusable.
21
+ *
22
+ * The return value of this function can be used as a React Native `Text` element's
23
+ * `maxFontSizeMultiplier` prop value.
24
+ */
25
+
26
+ export const getMaxFontMultiplier = ({
27
+ fontSize,
28
+ fontScaleCap
29
+ }) => {
30
+ if (Platform.OS === 'web' || !fontScaleCap || !fontSize) return undefined;
31
+ if (fontScaleCap <= fontSize) return 1;
32
+ return fontScaleCap / fontSize;
33
+ };
@@ -1,4 +1,10 @@
1
+ export * from './a11y';
1
2
  export * from './animation';
2
3
  export * from './input';
4
+ export * from './pressability';
3
5
  export * from './propTypes';
4
- export * from './spacing';
6
+ export { default as info } from './info';
7
+ export { default as useSpacingScale } from './useSpacingScale';
8
+ export { default as useResponsiveProp } from './useResponsiveProp';
9
+ export * from './useResponsiveProp';
10
+ export { default as useUniqueId } from './useUniqueId';
@@ -0,0 +1,7 @@
1
+ import platform from './platform';
2
+ import versions from './versions'; // export an object accessed like info.platform.OS, info.version.uds, etc
3
+
4
+ export default {
5
+ platform,
6
+ versions
7
+ };
@@ -0,0 +1,11 @@
1
+ import { Platform } from 'react-native';
2
+ import fileSuffix from './platform'; // Allows consuming libraries that might not have a direct dependency on
3
+ // React Native to check what the current platform is.
4
+ // Particularly useful for validating Jest config: it is possible for Jest to
5
+ // get configured such that Platform.OS returns a different mocked value to the
6
+ // OS being used to select files by platform suffix (e.g. .web, .native).
7
+
8
+ export default {
9
+ OS: Platform.OS,
10
+ fileSuffix
11
+ };
@@ -0,0 +1 @@
1
+ export default '.android';
@@ -0,0 +1 @@
1
+ export default '.ios';
@@ -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 @@
1
+ export default '.web';
@@ -0,0 +1,5 @@
1
+ import { version } from '../../../package.json'; // Add more versions if they are useful e.g. theme schema version
2
+
3
+ export default {
4
+ uds: version
5
+ };
@@ -60,6 +60,7 @@ Consumers of this hook must be one of:
60
60
  * currentValue: any
61
61
  * setValue: (value: any) => void
62
62
  * resetValue: () => void
63
+ * isControlled: bool
63
64
  * }}
64
65
  */
65
66
 
@@ -91,7 +92,8 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
91
92
  return {
92
93
  currentValue,
93
94
  setValue,
94
- resetValue
95
+ resetValue,
96
+ isControlled
95
97
  };
96
98
  };
97
99
  /**
@@ -0,0 +1,92 @@
1
+ import { Platform, StyleSheet } from 'react-native';
2
+ import { pressProps } from './propTypes'; // These roles should result in cursor: pointer but don't in current RNW releases
3
+
4
+ const shouldUseCursor = ['checkbox', 'radio', 'switch'];
5
+ /**
6
+ * React Native Web has some built-in logic for applying cursor styles based on accessibility roles;
7
+ * however, it misses certain cases. This fills in known cases where widely used versions of RNW
8
+ * fail to apply an expected cursor style.
9
+ *
10
+ * @param {object} props
11
+ * @param {boolean} [props.inactive]
12
+ * @param {boolean} [props.disabled]
13
+ * @param {string} [props.accessibilityRole]
14
+ * @returns
15
+ */
16
+
17
+ export const getCursorStyle = ({
18
+ inactive,
19
+ disabled,
20
+ accessibilityRole
21
+ }) => {
22
+ if (Platform.OS !== 'web') return undefined;
23
+ if (inactive || disabled) return staticStyles.notAllowed;
24
+ if (shouldUseCursor.includes(accessibilityRole)) return staticStyles.pointer;
25
+ return undefined; // allows React Native Web's built-in logic to apply
26
+ };
27
+ /**
28
+ * @typedef {{ pressed: boolean, focused: boolean, hovered?: boolean }} PressableState
29
+ */
30
+
31
+ /**
32
+ * Maps the state object given by the React Native `Pressable` component to the set of
33
+ * equivalent appearance names used in UDS.
34
+ *
35
+ * @param {PressableState} pressableState - state object passed by React Native's `<Pressable>` into
36
+ * render functions passed to its `style` or `children` props.
37
+ * @param {object} [additionalState] - properties specific to the current component,
38
+ * such as `inactive`, `selected`, etc.
39
+ * @returns {object}
40
+ */
41
+
42
+ export const resolvePressableState = ({
43
+ pressed = false,
44
+ focused = false,
45
+ hovered = false
46
+ } = {}, additionalState) => ({ ...additionalState,
47
+ focus: focused,
48
+ hover: hovered,
49
+ pressed
50
+ });
51
+ /**
52
+ * Takes a UDS `tokens` prop and, if it is a function, resolves it based on a state
53
+ * object passed from the React Native `Pressable` component and optional extra properties.
54
+ *
55
+ * @param {object|function} tokens - UDS system tokens prop
56
+ * @param {PressableState} pressableState - state object passed by React Native's `<Pressable>`
57
+ * @param {object} [additionalState] - properties specific to the current component
58
+ * @returns {object} - resolved tokens object
59
+ */
60
+
61
+ export const resolvePressableTokens = (tokens, pressableState, additionalState) => {
62
+ if (typeof tokens !== 'function') return tokens;
63
+ const udsPressableState = resolvePressableState(pressableState, additionalState);
64
+ return tokens(udsPressableState);
65
+ };
66
+ const staticStyles = StyleSheet.create(Platform.select({
67
+ web: {
68
+ notAllowed: {
69
+ cursor: 'not-allowed'
70
+ },
71
+ pointer: {
72
+ cursor: 'pointer'
73
+ }
74
+ },
75
+ default: {}
76
+ }));
77
+ /**
78
+ * From an object of props, extracts any platform-appropriate press handler functions and wraps
79
+ * them in a function that passes in some provided args. Allows components containing a Pressable
80
+ * to pass in press handlers that are then called with state or values that is otherwise internal.
81
+ */
82
+
83
+ export const getPressHandlersWithArgs = (pressableProps = {}, args = []) => {
84
+ // Allow handlers to be passed down for blur, hover, focus, pressIn, etc
85
+ const pressHandlers = Object.fromEntries(Object.entries(pressProps.selectHandlers(pressableProps)).map(([key, handler]) => ({
86
+ [key]: (...defaultArgs) => {
87
+ // Pass each handler data on this button and current selection
88
+ handler(...args, ...defaultArgs);
89
+ }
90
+ })));
91
+ return pressHandlers;
92
+ };
@@ -1,6 +1,22 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import { Linking, Platform } from 'react-native';
3
- import { tokenKeys } from '@telus-uds/tools-theme';
3
+ import { components as tokenKeys } from '@telus-uds/system-themes/schema';
4
+ export const paddingProp = {
5
+ propType: PropTypes.shape({
6
+ paddingBottom: PropTypes.number,
7
+ paddingLeft: PropTypes.number,
8
+ paddingRight: PropTypes.number,
9
+ paddingTop: PropTypes.number
10
+ })
11
+ };
12
+ export const rectProp = {
13
+ propType: PropTypes.shape({
14
+ bottom: PropTypes.number,
15
+ left: PropTypes.number,
16
+ right: PropTypes.number,
17
+ top: PropTypes.number
18
+ })
19
+ };
4
20
  /**
5
21
  * @typedef {{[key: string]: string|number|boolean}} AppearanceSet
6
22
  * @typedef {AppearanceSet} VariantProp
@@ -27,7 +43,7 @@ const getTokenNames = componentName => {
27
43
  const componentTokenNames = tokenKeys[componentName];
28
44
 
29
45
  if (!componentTokenNames) {
30
- throw new Error(`No "${componentName}" tokenKeys in @telus-uds/tools-theme`);
46
+ throw new Error(`No "${componentName}" tokenKeys in @telus-uds/system-themes/schema`);
31
47
  }
32
48
 
33
49
  return componentTokenNames;
@@ -57,10 +73,33 @@ export const selectTokens = (componentName, tokens) => {
57
73
  *
58
74
  * This prop is intended to be used as an 'escape hatch' for difficult or exceptional cases
59
75
  * where the main theming system doesn't apply. It is intentionally permissive about values.
76
+ *
77
+ * @param {...string} componentsNames - one or more ComponentName, which tokens keys are accepted
78
+ * @return {function} - a custom PropTypes validator
79
+ *
80
+ * @example
81
+ * Component.propTypes = {
82
+ * // accepts all tokens keys defined in Component schema
83
+ * tokens: getTokensPropType('Component')
84
+ * }
85
+ *
86
+ * Component.propTypes = {
87
+ * // accepts all tokens keys defined in schemas for Component1 and Component2
88
+ * tokens: getTokensPropType('Component1', 'Component2')
89
+ * }
60
90
  */
61
91
 
62
- export const getTokensPropType = componentName => PropTypes.oneOfType([PropTypes.shape(Object.fromEntries(getTokenNames(componentName).map(key => [key, tokenValueType]))), PropTypes.func // function that takes current appearances and returns above shape
63
- ]);
92
+ export const getTokensPropType = (...componentsNames) => (props, propName, componentName) => {
93
+ PropTypes.checkPropTypes({
94
+ [propName]: PropTypes.oneOfType([PropTypes.object, PropTypes.func])
95
+ }, props, propName, componentName);
96
+
97
+ if (typeof props[propName] !== 'function') {
98
+ PropTypes.checkPropTypes({
99
+ [propName]: PropTypes.exact(Object.fromEntries(componentsNames.flatMap(component => getTokenNames(component).map(key => [key, tokenValueType]))))
100
+ }, props, propName, componentName);
101
+ }
102
+ };
64
103
 
65
104
  function getPropSelector(propTypes, regexp) {
66
105
  const keys = Object.keys(propTypes);
@@ -140,7 +179,27 @@ export const a11yProps = {
140
179
  accessibilityElementsHidden: true
141
180
  }
142
181
  })
143
- }
182
+ },
183
+
184
+ /**
185
+ * Generates an object of platform-appropriate a11y props describing an item that has an
186
+ * ordered position in a set. Examples of usage by accessibility tools includes screenreaders
187
+ * saying "Item X of Y" when this item is select.
188
+ *
189
+ * @param {number} itemsCount - the number of items in the set
190
+ * @param {number} index - the current item's index in the set
191
+ * @returns {object} - platform-applicable a11y props describing this position (if available)
192
+ */
193
+ getPositionInSet: (itemsCount, index) => Platform.select({
194
+ web: {
195
+ // accessibilityPosInSet etc exists in React Native Web main branch
196
+ // but not in a release compatible with Expo etc; just use `aria-*`
197
+ 'aria-setsize': itemsCount,
198
+ 'aria-posinset': index + 1
199
+ },
200
+ // No equivalents exist on the native side
201
+ default: {}
202
+ })
144
203
  }; // Props related to HTML <a> anchor link attributes.
145
204
 
146
205
  const targetValues = ['_self', '_blank', '_parent', '_top'];
@@ -194,7 +253,7 @@ export const hrefAttrsProp = {
194
253
 
195
254
  })
196
255
  };
197
- const pressPropTypes = {
256
+ const pressHandlerPropTypes = {
198
257
  onPress: PropTypes.func,
199
258
  onPressIn: PropTypes.func,
200
259
  onPressOut: PropTypes.func,
@@ -205,7 +264,16 @@ const pressPropTypes = {
205
264
  onFocus: PropTypes.func,
206
265
  onBlur: PropTypes.func
207
266
  },
208
- default: {}
267
+ default: {
268
+ onLongPress: PropTypes.func
269
+ }
270
+ })
271
+ };
272
+ const pressPropTypes = { ...pressHandlerPropTypes,
273
+ disabled: PropTypes.bool,
274
+ ...Platform.select({
275
+ hitSlop: PropTypes.number,
276
+ pressRetentionOffset: PropTypes.oneOfType([PropTypes.number, rectProp.propType])
209
277
  })
210
278
  };
211
279
  export const pressProps = {
@@ -217,7 +285,8 @@ export const pressProps = {
217
285
  /**
218
286
  * Filters a props object, returning only the platform-relevant press props defined above
219
287
  */
220
- select: getPropSelector(pressPropTypes)
288
+ select: getPropSelector(pressPropTypes),
289
+ selectHandlers: getPropSelector(pressHandlerPropTypes)
221
290
  };
222
291
  const linkPropTypes = { ...pressPropTypes,
223
292
  href: PropTypes.string,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Extract translations from a keyed dictionary for a given language.
3
+ * Returns a getter.
4
+ *
5
+ * @param {object} dictionary
6
+ * @param {'en'|'fr'} copy - language
7
+ * @return {function(string): string}
8
+ */
9
+ function useCopy({
10
+ dictionary,
11
+ copy
12
+ }) {
13
+ return key => dictionary[copy][key];
14
+ }
15
+
16
+ export default useCopy;
@@ -0,0 +1,47 @@
1
+ import { viewports } from '@telus-uds/system-constants';
2
+ import { useViewport } from '../ViewportProvider';
3
+
4
+ const hasOwn = (objectProp, key) => Object.prototype.hasOwnProperty.call(objectProp, key);
5
+
6
+ const hasResponsiveProperties = objectProp => viewports.keys.some(key => hasOwn(objectProp, key));
7
+ /**
8
+ * Resolves a prop which may be a responsive object with keys for viewports.
9
+ *
10
+ * Used internally in useResponsiveProp - see that for more details.
11
+ *
12
+ * @param {*} prop
13
+ * @param {"xs"|"sm"|"md"|"lg"|"xl"} viewport
14
+ * @param {*} [defaultValue]
15
+ * @returns {*}
16
+ */
17
+
18
+
19
+ export const resolveResponsiveProp = (prop, viewport, defaultValue) => {
20
+ if (prop === undefined) return defaultValue;
21
+ if (!prop || typeof prop !== 'object' || !hasResponsiveProperties(prop)) return prop;
22
+ const value = viewports.keys.includes(viewport) ? // If there's a current viewport, return the closest match at or below it
23
+ viewports.inherit(prop)[viewport] : // If no current viewport is available, default to smallest viewport
24
+ prop[viewports.keys.find(key => hasOwn(prop, key))];
25
+ return value === undefined ? defaultValue : value;
26
+ };
27
+ /**
28
+ * Takes any value and, if that value is a responsive prop (an object with one or more
29
+ * keys matching system viewport names), returns the value corresponding to the largest
30
+ * viewport key smaller than the current screen's viewport.
31
+ *
32
+ * For example, if passed `{ sm: 4, lg: 8 }`, it will return 8 on `xl` and `lg` viewports,
33
+ * 4 on `md` and `sm` viewports, and `undefined` (or a provided `defaultValue`) on `xs` viewports.
34
+ *
35
+ * To generate propTypes for responsive props, see `responsiveProps` in `./utils/propTypes.js`.
36
+ *
37
+ * @param {*} prop - any value which may be an object with viewport keys
38
+ * @param {*} [defaultValue] - default value to provide if no matches found for the current viewport
39
+ * @returns {*}
40
+ */
41
+
42
+ const useResponsiveProp = (prop, defaultValue) => {
43
+ const viewport = useViewport();
44
+ return resolveResponsiveProp(prop, viewport, defaultValue);
45
+ };
46
+
47
+ export default useResponsiveProp;
@@ -1,14 +1,34 @@
1
- import { useViewport } from '../../ViewportProvider';
2
- import { useThemeTokens } from '../../ThemeProvider';
3
- import { resolveSpacingValue, resolveSpacingOptions } from './utils';
1
+ import { useViewport } from '../ViewportProvider';
2
+ import { useThemeTokens } from '../ThemeProvider';
3
+ import { resolveResponsiveProp } from './useResponsiveProp';
4
4
  /**
5
5
  * @typedef {import('@telus-uds/system-constants/viewports').Viewport} Viewport
6
- * @typedef {import('../propTypes.js').SpacingValue} SpacingValue
7
- * @typedef {import('../propTypes.js').SpacingIndex} SpacingIndex
8
- * @typedef {import('../propTypes.js').SpacingObject} SpacingObject
9
- * @typedef {import('../propTypes.js').SpacingOptions} SpacingOptions
6
+ * @typedef {import('./propTypes.js').SpacingValue} SpacingValue
7
+ * @typedef {import('./propTypes.js').SpacingIndex} SpacingIndex
8
+ * @typedef {import('./propTypes.js').SpacingObject} SpacingObject
9
+ * @typedef {import('./propTypes.js').SpacingOptions} SpacingOptions
10
10
  */
11
11
 
12
+ const resolveSpacingOptions = space => {
13
+ if (!space?.options) return {};
14
+ const {
15
+ size,
16
+ variant,
17
+ subtract = 0
18
+ } = space.options;
19
+ const overridden = typeof size === 'number'; // Might need an option that adapts the size value by current user's system font scale, so that
20
+ // meaningful spacing between items doesn't disappear on the highest a11y font scale settings.
21
+ // https://github.com/telus/universal-design-system/issues/583
22
+
23
+ return {
24
+ tokens: {
25
+ size
26
+ },
27
+ variant,
28
+ overridden,
29
+ subtract
30
+ };
31
+ };
12
32
  /**
13
33
  * Pass a {@link SpacingValue}, which is one of:
14
34
  *
@@ -79,6 +99,7 @@ import { resolveSpacingValue, resolveSpacingOptions } from './utils';
79
99
  * @returns {number}
80
100
  */
81
101
 
102
+
82
103
  const useSpacingScale = spaceValue => {
83
104
  // In future, may need to consider window height as well as width, particularly for native apps,
84
105
  // e.g. to ensure designs don't look lost on large, tall, not-so-wide portrait screens.
@@ -89,11 +110,11 @@ const useSpacingScale = spaceValue => {
89
110
  overridden,
90
111
  subtract = 0
91
112
  } = resolveSpacingOptions(spaceValue);
92
- const space = overridden ? null : resolveSpacingValue(spaceValue, viewport);
113
+ const space = !overridden && (spaceValue?.space ?? resolveResponsiveProp(spaceValue, viewport, 0));
93
114
  const {
94
115
  size
95
116
  } = useThemeTokens('spacingScale', tokens, variant, {
96
- space,
117
+ space: typeof space === 'number' ? space : 0,
97
118
  viewport
98
119
  });
99
120
  return Math.max(size - subtract, 0);
@@ -0,0 +1,12 @@
1
+ import { useState } from 'react';
2
+ let id = 0;
3
+
4
+ function useUniqueId(prefix = '') {
5
+ const [uniqueId] = useState(() => {
6
+ id += 1;
7
+ return `${prefix}-${id}`;
8
+ });
9
+ return uniqueId;
10
+ }
11
+
12
+ export default useUniqueId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telus-uds/components-base",
3
- "version": "0.0.2-prerelease.3",
3
+ "version": "0.0.2-prerelease.7",
4
4
  "description": "Base components",
5
5
  "keywords": [
6
6
  "base"
@@ -36,8 +36,9 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": "*",
39
+ "react-dom": "*",
39
40
  "react-native": "*",
40
- "react-native-web": "^0.14.13"
41
+ "react-native-web": "~0.14.13"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@testing-library/jest-native": "^4.0.1",
@@ -46,9 +47,10 @@
46
47
  "react-test-renderer": "^16.3.2"
47
48
  },
48
49
  "dependencies": {
49
- "@telus-uds/system-constants": "^0.0.2-prerelease.1",
50
- "@telus-uds/tools-theme": "^0.0.2-prerelease.2",
50
+ "@telus-uds/system-constants": "^0.0.2-prerelease.2",
51
+ "@telus-uds/system-themes": "^0.0.2-prerelease.0",
51
52
  "lodash.merge": "^4.6.2",
52
- "prop-types": "^15.7.2"
53
+ "prop-types": "^15.7.2",
54
+ "react-native-picker-select": "^8.0.4"
53
55
  }
54
56
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "previousReleaseTag": "@telus-uds/components-base/v0.0.2-prerelease.2",
3
- "changelog": "### [0.0.2-prerelease.3](https://github.com/telus/universal-design-system/compare/@telus-uds/components-base/v0.0.2-prerelease.2...@telus-uds/components-base/v0.0.2-prerelease.3) (2021-10-12)\n\n\n### Features\n\n* **base:** add `Card` component ([#597](https://github.com/telus/universal-design-system/issues/597)) ([9e29d3c](https://github.com/telus/universal-design-system/commit/9e29d3c426c7082ec4d54a0c9d353986c0cc0b01))\n* **base:** add useSpacingScale hook, apply to Box ([#606](https://github.com/telus/universal-design-system/issues/606)) ([5109b4f](https://github.com/telus/universal-design-system/commit/5109b4f7537b8a732b86e34675bef992a54873e5))\n",
4
- "releaseTag": "@telus-uds/components-base/v0.0.2-prerelease.3",
5
- "newVersion": "0.0.2-prerelease.3",
2
+ "previousReleaseTag": "@telus-uds/components-base/v0.0.2-prerelease.6",
3
+ "changelog": "### [0.0.2-prerelease.7](https://github.com/telus/universal-design-system/compare/@telus-uds/components-base/v0.0.2-prerelease.6...@telus-uds/components-base/v0.0.2-prerelease.7) (2021-11-23)\n\n\n### Features\n\n* **base:** add `Radio` button ([#731](https://github.com/telus/universal-design-system/issues/731)) ([ad1613c](https://github.com/telus/universal-design-system/commit/ad1613c547fe3b0cdf1490a92267f32045356133))\n* **base:** add base `Checkbox` component ([#706](https://github.com/telus/universal-design-system/issues/706)) ([a3fa01a](https://github.com/telus/universal-design-system/commit/a3fa01ad0da342be02c15284a103c22d7b315ae9))\n* **base:** add Tags component ([#785](https://github.com/telus/universal-design-system/issues/785)) ([90bbf40](https://github.com/telus/universal-design-system/commit/90bbf4035a0bc0a8cdf319d9b366c8498c0dfb56))\n* **base:** add TextArea ([#720](https://github.com/telus/universal-design-system/issues/720)) ([b18cae3](https://github.com/telus/universal-design-system/commit/b18cae3357375019ecc4e24e78ed9a2345b26139))\n* **base:** add the Select component ([#759](https://github.com/telus/universal-design-system/issues/759)) ([9cfdf84](https://github.com/telus/universal-design-system/commit/9cfdf846b7a273dd140537b60e08f30a70c13a66))\n* **base:** adding list component ([#296](https://github.com/telus/universal-design-system/issues/296)) ([#703](https://github.com/telus/universal-design-system/issues/703)) ([50e474d](https://github.com/telus/universal-design-system/commit/50e474d3d7f1988f5971a10be8416c8ac510626f))\n* **base:** implementing base Skeleton component ([#770](https://github.com/telus/universal-design-system/issues/770)) ([1138f08](https://github.com/telus/universal-design-system/commit/1138f08885f4cf67fc0fb7273758d20cc0a989c1)), closes [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296) [#296](https://github.com/telus/universal-design-system/issues/296)\n\n\n### Bug Fixes\n\n* **base:** adjust event handlers for checkbox / radio inputs on Web ([#820](https://github.com/telus/universal-design-system/issues/820)) ([87624d2](https://github.com/telus/universal-design-system/commit/87624d29166ce85aeaf7dc1db4dc5b60ecd170a5))\n* **base:** fix button text alignment ([#794](https://github.com/telus/universal-design-system/issues/794)) ([9671087](https://github.com/telus/universal-design-system/commit/9671087477eb45b0d3c872b47f1d24cdd43a727f))\n* **base:** various visual fixes for native/app ([#797](https://github.com/telus/universal-design-system/issues/797)) ([a00ab12](https://github.com/telus/universal-design-system/commit/a00ab124c40e1ea46441270acfd64ae0f37b0a68))\n* export a11ytext [prerelease-components-base] ([#830](https://github.com/telus/universal-design-system/issues/830)) ([af627a2](https://github.com/telus/universal-design-system/commit/af627a2b4fa4cbf78983521bbb66b86ab9f87601))\n* **jest:** pick .web files in -allium-web tests ([#765](https://github.com/telus/universal-design-system/issues/765)) ([e0b1bd5](https://github.com/telus/universal-design-system/commit/e0b1bd5edcc52b916685f8c5dcd8d2901d2ecdbd))\n",
4
+ "releaseTag": "@telus-uds/components-base/v0.0.2-prerelease.7",
5
+ "newVersion": "0.0.2-prerelease.7",
6
6
  "packageName": "@telus-uds/components-base"
7
7
  }
package/src/Box/Box.jsx CHANGED
@@ -99,6 +99,7 @@ const Box = ({
99
99
  variant,
100
100
  tokens,
101
101
  scroll,
102
+ testID,
102
103
  ...rest
103
104
  }) => {
104
105
  const a11y = a11yProps.select(rest)
@@ -116,13 +117,13 @@ const Box = ({
116
117
  const scrollProps = typeof scroll === 'object' ? scroll : {}
117
118
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
118
119
  return (
119
- <ScrollView {...scrollProps} {...a11y}>
120
+ <ScrollView {...scrollProps} {...a11y} testID={testID}>
120
121
  {children}
121
122
  </ScrollView>
122
123
  )
123
124
  }
124
125
  return (
125
- <View {...a11y} style={styles}>
126
+ <View {...a11y} style={styles} testID={testID}>
126
127
  {children}
127
128
  </View>
128
129
  )
@@ -142,6 +143,7 @@ Box.propTypes = {
142
143
  ]),
143
144
  variant: variantProp.propType,
144
145
  tokens: getTokensPropType('Box'),
146
+ testID: PropTypes.string,
145
147
  children: PropTypes.node.isRequired
146
148
  }
147
149