@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,32 @@
1
+ import React from 'react';
2
+ import { Pressable, StyleSheet } from 'react-native';
3
+ /**
4
+ * @typedef {import('react-native').PressableProps} PressableProps
5
+ */
6
+
7
+ /**
8
+ * InlinePressable is an alternative to React Native's Pressable that works better when nested
9
+ * inline inside Text. It accepts the same props as React Native's Pressable.
10
+ *
11
+ * On Web it simply passes its props to Pressable and defaults to `inline-flex` instead of `flex`.
12
+ *
13
+ * @param {PressableProps} PressableProps
14
+ */
15
+ // React Native exports prop Types but not propTypes, use JSDoc types here rather than duplicate RN
16
+ // eslint-disable-next-line react/prop-types
17
+
18
+ const InlinePressable = ({
19
+ children,
20
+ style,
21
+ ...props
22
+ }) => /*#__PURE__*/React.createElement(Pressable, Object.assign({
23
+ style: pressState => [staticStyles.inline, typeof style === 'function' ? style(pressState) : style]
24
+ }, props), pressState => typeof children === 'function' ? children(pressState) : children);
25
+
26
+ const staticStyles = StyleSheet.create({
27
+ inline: {
28
+ // Stop Pressable defaulting to (block) flex
29
+ display: 'inline-flex'
30
+ }
31
+ });
32
+ export default InlinePressable;
package/lib/Link/Link.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { useThemeTokensCallback } from '../ThemeProvider';
2
3
  import LinkBase from './LinkBase';
3
4
 
4
5
  const Link = ({
@@ -6,16 +7,16 @@ const Link = ({
6
7
  children,
7
8
  accessibilityRole = 'link',
8
9
  variant = {},
10
+ tokens,
9
11
  ...linkProps
10
- }) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
11
- href: href,
12
- accessibilityRole: accessibilityRole,
13
- variant: {
14
- component: 'Link',
15
- ...variant
16
- }
17
- }, linkProps), children);
18
-
19
- Link.propTypes = { ...LinkBase.propTypes
12
+ }) => {
13
+ const getTokens = useThemeTokensCallback('Link', tokens, variant);
14
+ return /*#__PURE__*/React.createElement(LinkBase, Object.assign({
15
+ href: href,
16
+ accessibilityRole: accessibilityRole,
17
+ tokens: getTokens
18
+ }, linkProps), children);
20
19
  };
20
+
21
+ Link.propTypes = LinkBase.propTypes;
21
22
  export default Link;
@@ -1,14 +1,11 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Text, Platform, StyleSheet, Pressable, PixelRatio, View, TouchableWithoutFeedback } from 'react-native';
3
+ import { Text, Platform, StyleSheet } from 'react-native';
4
4
  import { a11yProps, hrefAttrsProp, variantProp, linkProps, getTokensPropType } from '../utils/propTypes';
5
- import { useThemeTokens, applyTextStyles } from '../ThemeProvider';
6
-
7
- const selectContentStyles = ({
8
- color
9
- }) => ({
10
- color
11
- });
5
+ import { resolvePressableTokens } from '../utils/pressability';
6
+ import InlinePressable from './InlinePressable';
7
+ import { applyTextStyles } from '../ThemeProvider';
8
+ import { IconText, iconComponentPropTypes } from '../Icon';
12
9
 
13
10
  const selectOuterBorderStyles = ({
14
11
  outerBorderColor,
@@ -25,15 +22,26 @@ Platform.OS === 'web' ? {
25
22
  outline: outerBorderOutline,
26
23
  borderWidth: outerBorderWidth,
27
24
  borderColor: outerBorderColor,
28
- borderRadius: outerBorderRadius
25
+ borderRadius: outerBorderRadius,
26
+ // Stops focus ring stretching horizontally if parent has display: block
27
+ // width: fit-content isn't supported on Firefox; can't cascade props like CSS `width: fit-content; width: --moz-fit-content;`
28
+ display: 'inline-flex'
29
29
  } : {};
30
30
 
31
31
  const selectTextStyles = ({
32
+ color,
32
33
  textLine,
33
34
  textLineStyle
34
35
  }) => ({
36
+ color,
35
37
  textDecorationLine: textLine,
36
- textDecorationStyle: textLineStyle
38
+ textDecorationStyle: textLineStyle,
39
+ ...Platform.select({
40
+ web: {
41
+ // TODO: https://github.com/telus/universal-design-system/issues/487
42
+ transition: 'color 200ms'
43
+ }
44
+ })
37
45
  });
38
46
 
39
47
  const selectBlockStyles = ({
@@ -48,18 +56,16 @@ const selectBlockStyles = ({
48
56
  fontName: blockFontName
49
57
  });
50
58
 
51
- const selectIconStyles = ({
59
+ const selectIconTokens = ({
60
+ color,
52
61
  iconSize,
53
- iconGapBefore,
54
- iconGapAfter,
55
- iconScale,
56
- iconTranslateX
62
+ iconTranslateX,
63
+ iconTranslateY
57
64
  }) => ({
58
- scale: iconScale,
65
+ color,
59
66
  translateX: iconTranslateX,
60
- size: iconSize,
61
- gapBefore: iconGapBefore,
62
- gapAfter: iconGapAfter
67
+ translateY: iconTranslateY,
68
+ size: iconSize
63
69
  });
64
70
  /**
65
71
  * Renders a pressable text link, with optional icon. This is rendered as a block element
@@ -90,35 +96,13 @@ const LinkBase = ({
90
96
  onPress,
91
97
  icon,
92
98
  iconPosition = icon ? 'left' : undefined,
93
- iconVariant,
99
+ iconProps,
94
100
  variant,
95
- tokens,
101
+ tokens = {},
96
102
  children,
97
103
  accessibilityRole = 'link',
98
104
  ...props
99
105
  }) => {
100
- const handlePress = linkProps.handleHref({
101
- href,
102
- onPress
103
- });
104
- const [isFocused, setIsFocused] = useState(false);
105
-
106
- const handleFocus = () => setIsFocused(true);
107
-
108
- const handleBlur = () => setIsFocused(false);
109
-
110
- const [isHovered, setIsHovered] = useState(false);
111
-
112
- const handleHoverIn = () => setIsHovered(true);
113
-
114
- const handleHoverOut = () => setIsHovered(false);
115
-
116
- const [isPressed, setIsPressed] = useState(false);
117
-
118
- const handlePressIn = () => setIsPressed(true);
119
-
120
- const handlePressOut = () => setIsPressed(false);
121
-
122
106
  const {
123
107
  hrefAttrs,
124
108
  rest
@@ -126,91 +110,49 @@ const LinkBase = ({
126
110
  const linkPropSet = linkProps.select({
127
111
  accessibilityRole,
128
112
  href,
129
- onPress: handlePress,
130
- onPressIn: handlePressIn,
131
- onPressOut: handlePressOut,
132
- onFocus: handleFocus,
133
- onBlur: handleBlur,
134
- onMouseEnter: handleHoverIn,
135
- onMouseLeave: handleHoverOut,
113
+ onPress: linkProps.handleHref({
114
+ href,
115
+ onPress
116
+ }),
136
117
  ...hrefAttrsProp.spread(hrefAttrs),
137
118
  ...rest
138
119
  });
139
- const themeTokens = useThemeTokens('Link', tokens, variant, {
140
- focus: isFocused,
141
- hover: isHovered,
142
- pressed: isPressed,
143
- iconPosition
144
- });
145
- const IconComponent = icon || themeTokens.icon;
146
- const hasIcon = !!IconComponent;
147
- const textStyles = selectTextStyles(themeTokens);
148
- const outerBorderStyles = selectOuterBorderStyles(themeTokens);
149
- const contentStyles = selectContentStyles(themeTokens);
150
- const blockStyles = selectBlockStyles(themeTokens);
151
- const iconStyles = selectIconStyles(themeTokens); // TODO: re-apply support for inline links
152
-
153
- const isNested = false; // On web, this makes focus rings wrap only the link, not the entire block
154
120
 
155
- const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft; // Apply typographic text styles if not inheriting, and block positioning if a block
156
-
157
- const blockTextStyles = !isNested && blockStyles;
158
-
159
- if (!hasIcon) {
160
- // onPressIn / onPressOut only work if on TouchableWithoutFeedback, onFocus only works if on Text
121
+ const resolveTokens = pressState => resolvePressableTokens(tokens, pressState, {
122
+ iconPosition
123
+ }); // On web, this makes focus rings wrap only the link, not the entire block
124
+
125
+
126
+ const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft;
127
+ return /*#__PURE__*/React.createElement(InlinePressable, Object.assign({}, linkPropSet, {
128
+ style: linkState => {
129
+ const themeTokens = resolveTokens(linkState);
130
+ const outerBorderStyles = selectOuterBorderStyles(themeTokens);
131
+ const hasIcon = Boolean(icon || themeTokens.icon);
132
+ return [outerBorderStyles, blockLeftStyle, hasIcon && staticStyles.rowContainer];
133
+ }
134
+ }), linkState => {
135
+ const themeTokens = resolveTokens(linkState);
136
+ const textStyles = selectTextStyles(themeTokens);
137
+ const iconTokens = selectIconTokens(themeTokens); // TODO: may need to apply some smarter text inheritance here if inline to avoid native
138
+ // issues like double-application of line heights breaking align-items: baseline
139
+
140
+ const blockTextStyles = selectBlockStyles(themeTokens);
141
+ const IconComponent = icon || themeTokens.icon;
161
142
  const {
162
- onPress: _onPress,
163
- onPressIn,
164
- onPressOut,
165
- ...textProps
166
- } = linkPropSet;
167
- return (
168
- /*#__PURE__*/
169
- // TouchableWithoutFeedback modifies its child so this stack is all (touchable) Text: no Views
170
- React.createElement(TouchableWithoutFeedback, {
171
- onPressIn: onPressIn,
172
- onPressOut: onPressOut,
173
- onPress: _onPress
174
- }, /*#__PURE__*/React.createElement(Text, Object.assign({}, textProps, {
175
- style: [outerBorderStyles, blockLeftStyle, contentStyles, blockTextStyles, textStyles]
176
- }), children))
177
- );
178
- } // Scale icon with text, but with a cap so text isn't squashed at large scales
179
-
180
-
181
- const iconScale = Math.min(PixelRatio.getFontScale(), 2);
182
- const iconTokens = {
183
- size: iconStyles.size ? iconStyles.size * iconScale : undefined,
184
- color: contentStyles.color ?? undefined,
185
- scale: iconStyles.scale ?? undefined,
186
- translateX: iconStyles.translateX ?? undefined
187
- };
188
- const iconContent = /*#__PURE__*/React.createElement(IconComponent, {
189
- tokens: iconTokens,
190
- variant: iconVariant
191
- });
192
- const iconBlock = isNested ? // TODO: when reimplementing inline support, insert an appropriate space character
193
- // based on the value of gapBefore and gapAfter on native (no inline margins/padding)
194
- iconContent : /*#__PURE__*/React.createElement(View, {
195
- style: [iconPosition === 'left' && {
196
- marginRight: iconStyles.gapAfter * iconScale
197
- }, iconPosition === 'right' && {
198
- marginLeft: iconStyles.gapBefore * iconScale
199
- }]
200
- }, iconContent); // TODO: this doesn't work well when nested inline but does allow block-links to have icons align
201
- // properly on Android which purely inline links can't do. Add an isNested case that is similar to
202
- // https://github.com/telus/universal-design-system/pull/233
203
-
204
- return /*#__PURE__*/React.createElement(Pressable, Object.assign({}, linkPropSet, {
205
- style: [outerBorderStyles, blockLeftStyle, staticStyles.rowContainer]
206
- }), iconPosition === 'left' && iconBlock, /*#__PURE__*/React.createElement(Text, {
207
- style: [contentStyles, blockTextStyles, textStyles, Platform.select({
208
- web: {
209
- // TODO: https://github.com/telus/universal-design-system/issues/487
210
- transition: 'color 200ms'
143
+ iconSpace
144
+ } = themeTokens;
145
+ return /*#__PURE__*/React.createElement(IconText, {
146
+ icon: IconComponent,
147
+ iconPosition: iconPosition,
148
+ space: iconSpace,
149
+ iconProps: { ...iconProps,
150
+ tokens: iconTokens
211
151
  }
212
- })]
213
- }, children), iconPosition === 'right' && iconBlock);
152
+ }, /*#__PURE__*/React.createElement(Text, {
153
+ style: [textStyles, blockTextStyles, staticStyles.baseline]
154
+ }, children));
155
+ });
214
156
  };
215
157
 
216
158
  LinkBase.propTypes = { ...a11yProps.types,
@@ -221,7 +163,7 @@ LinkBase.propTypes = { ...a11yProps.types,
221
163
  /**
222
164
  * Optional variant that may be passed down to the link's icon if there is one
223
165
  */
224
- iconVariant: variantProp.propType,
166
+ iconProps: PropTypes.exact(iconComponentPropTypes),
225
167
 
226
168
  /**
227
169
  * A function component for an SVG icon to render inside the link. Inherits size and color from
@@ -248,6 +190,9 @@ const staticStyles = StyleSheet.create({
248
190
  flexDirection: 'row',
249
191
  alignItems: 'center',
250
192
  justifyContent: 'flex-start'
193
+ },
194
+ baseline: {
195
+ alignSelf: 'baseline'
251
196
  }
252
197
  });
253
198
  export default LinkBase;
@@ -1,25 +1,36 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { useThemeTokensCallback } from '../ThemeProvider';
3
4
  import LinkBase from './LinkBase';
5
+ /**
6
+ * `TextButton` is a button that looks like a Link. It uses the same theming and variants as
7
+ * Link but has the accessibility role of a `Button`. It should be used for actions that
8
+ * take place on the current page, or for navigation within an app.
9
+ */
4
10
 
5
11
  const TextButton = ({
6
12
  onPress,
7
13
  children,
8
- variant = {},
14
+ variant,
15
+ tokens,
16
+ // TODO: this may need to use `link` role on Web in the case of being passed both `href` and
17
+ // `onPress` in an omniplatform app that uses React Navigation's useLinkProps for internal nav.
9
18
  accessibilityRole = 'button',
10
19
  ...linkProps
11
- }) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
12
- onPress: onPress,
13
- accessibilityRole: accessibilityRole,
14
- variant: {
15
- component: 'TextButton',
16
- ...variant
17
- }
18
- }, linkProps), children);
20
+ }) => {
21
+ const getTokens = useThemeTokensCallback('Link', tokens, variant);
22
+ return /*#__PURE__*/React.createElement(LinkBase, Object.assign({
23
+ onPress: onPress,
24
+ accessibilityRole: accessibilityRole,
25
+ tokens: getTokens
26
+ }, linkProps), children);
27
+ };
19
28
 
20
29
  TextButton.propTypes = { ...LinkBase.propTypes,
21
30
  onPress: PropTypes.func.isRequired
22
31
  }; // Remove incompatible Link prop (if this build includes propTypes)
32
+ // TODO: test if this works with web navigation in omniplatform React Navigation
33
+ // https://github.com/telus/universal-design-system/issues/665
23
34
  // eslint-disable-next-line react/forbid-foreign-prop-types
24
35
 
25
36
  if (TextButton.propTypes?.href) delete TextButton.propTypes.href;
package/lib/Link/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import ChevronLink from './ChevronLink';
2
2
  import Link from './Link';
3
+ import LinkBase from './LinkBase';
3
4
  import TextButton from './TextButton';
4
- export { ChevronLink, Link, TextButton };
5
+ export { ChevronLink, Link, LinkBase, TextButton };
@@ -0,0 +1,52 @@
1
+ import React, { cloneElement, Children } from 'react';
2
+ import { View, Platform } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import { getTokensPropType, variantProp, componentPropType } from '../utils';
5
+ import { a11yProps } from '../utils/propTypes';
6
+ import ListItem from './ListItem';
7
+ /**
8
+ * A Unordered List component has a child a ListItem that
9
+ * allows icon, dividers and customized typography
10
+ */
11
+
12
+ const List = ({
13
+ children,
14
+ showDivider,
15
+ tokens,
16
+ variant,
17
+ ...rest
18
+ }) => {
19
+ const accessibilityRole = Platform.select({
20
+ web: 'list',
21
+ default: 'none'
22
+ });
23
+ const a11y = a11yProps.select(rest);
24
+ const items = Children.map(children, (child, index) => {
25
+ if (child.type.name === ListItem.name) {
26
+ return /*#__PURE__*/cloneElement(child, {
27
+ showDivider,
28
+ isLastItem: index + 1 === Children.count(children),
29
+ tokens,
30
+ variant
31
+ });
32
+ }
33
+
34
+ return child;
35
+ });
36
+ return /*#__PURE__*/React.createElement(View, Object.assign({
37
+ accessibilityRole: accessibilityRole
38
+ }, a11y), items);
39
+ };
40
+
41
+ List.Item = ListItem;
42
+ List.propTypes = { ...a11yProps.types,
43
+ tokens: getTokensPropType('List'),
44
+ variant: variantProp.propType,
45
+ children: componentPropType('ListItem'),
46
+
47
+ /**
48
+ * In case it is not the last item allow display divider
49
+ */
50
+ showDivider: PropTypes.bool
51
+ };
52
+ export default List;
@@ -0,0 +1,207 @@
1
+ import React from 'react';
2
+ import { View, Platform, Text, StyleSheet } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import { useThemeTokens, applyTextStyles } from '../ThemeProvider';
5
+ import { getTokensPropType, variantProp } from '../utils';
6
+
7
+ const selectBulletStyles = ({
8
+ itemBulletWidth,
9
+ itemBulletHeight,
10
+ itemBulletColor
11
+ }) => ({
12
+ width: itemBulletWidth,
13
+ height: itemBulletHeight,
14
+ borderRadius: itemBulletHeight / 2,
15
+ backgroundColor: itemBulletColor
16
+ });
17
+
18
+ const selectBulletContainerStyles = ({
19
+ itemBulletContainerWidth,
20
+ itemBulletContainerAlign
21
+ }) => ({
22
+ width: itemBulletContainerWidth,
23
+ alignItems: itemBulletContainerAlign,
24
+ justifyContent: itemBulletContainerAlign
25
+ });
26
+
27
+ const selectItemIconTokens = ({
28
+ itemIconSize,
29
+ itemIconColor
30
+ }) => ({
31
+ size: itemIconSize,
32
+ color: itemIconColor
33
+ });
34
+
35
+ const selectSideItemContainerStyles = ({
36
+ listGutter
37
+ }) => ({
38
+ marginRight: listGutter
39
+ });
40
+
41
+ const selectItemStyles = ({
42
+ itemFontWeight,
43
+ itemFontSize,
44
+ itemLineHeight,
45
+ itemFontName
46
+ }) => applyTextStyles({
47
+ fontWeight: itemFontWeight,
48
+ fontSize: itemFontSize,
49
+ lineHeight: itemLineHeight,
50
+ fontName: itemFontName
51
+ });
52
+
53
+ const selectItemBlockStyles = ({
54
+ interItemMargin
55
+ }) => ({
56
+ marginBottom: interItemMargin
57
+ });
58
+
59
+ const selectDividerStyles = ({
60
+ dividerColor,
61
+ dividerSize,
62
+ interItemMarginWithDivider
63
+ }) => ({
64
+ borderBottomWidth: dividerSize,
65
+ borderColor: dividerColor,
66
+ marginBottom: interItemMarginWithDivider,
67
+ paddingBottom: interItemMarginWithDivider
68
+ });
69
+ /**
70
+ * ListItem is responsible for rendering icon or a bullet as side item
71
+ */
72
+
73
+
74
+ const ListItem = ({
75
+ tokens,
76
+ variant,
77
+ icon,
78
+ iconColor,
79
+ iconSize,
80
+ showDivider,
81
+ children,
82
+ isLastItem
83
+ }) => {
84
+ const themeTokens = useThemeTokens('List', tokens, variant);
85
+ const itemStyles = selectItemStyles(themeTokens);
86
+ const itemBlockStyles = selectItemBlockStyles(themeTokens);
87
+ const dividerStyles = selectDividerStyles(themeTokens);
88
+ const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens);
89
+ const itemBulletStyles = selectBulletStyles(themeTokens);
90
+ const iconTokens = selectItemIconTokens(themeTokens);
91
+ const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens);
92
+ const accessibilityRole = Platform.select({
93
+ web: 'listitem',
94
+ default: 'item'
95
+ });
96
+
97
+ const areChildrenStrings = () => {
98
+ if (Array.isArray(children)) {
99
+ return children.every(child => typeof child === 'string');
100
+ }
101
+
102
+ return typeof children === 'string';
103
+ };
104
+
105
+ const renderItem = () => {
106
+ if (areChildrenStrings()) {
107
+ return /*#__PURE__*/React.createElement(Text, {
108
+ style: itemStyles
109
+ }, children);
110
+ }
111
+
112
+ return /*#__PURE__*/React.createElement(View, null, children);
113
+ };
114
+ /**
115
+ * Function responsible returning styling, in case the item is the last shouldn't
116
+ * add extra margin on the bottom, if "showDivider" is true it should add a divider
117
+ * and custom margin and padding, otherwise just adds a margin to the bottom
118
+ */
119
+
120
+
121
+ const getContainerStyle = () => {
122
+ if (isLastItem) {
123
+ return undefined;
124
+ }
125
+
126
+ if (showDivider) {
127
+ return dividerStyles;
128
+ }
129
+
130
+ return itemBlockStyles;
131
+ };
132
+ /**
133
+ * Renders item bullet or Icon in case it's defined
134
+ * in case children are string the icon is centered otherwise
135
+ * it will align itself at start of the container
136
+ */
137
+
138
+
139
+ const renderMarker = () => {
140
+ const IconComponent = icon || /*#__PURE__*/React.createElement(React.Fragment, null);
141
+
142
+ if (icon) {
143
+ return /*#__PURE__*/React.createElement(View, {
144
+ style: [sideItemContainerStyles, areChildrenStrings() ? staticStyles.centeredIcons : undefined]
145
+ }, /*#__PURE__*/React.createElement(IconComponent, {
146
+ tokens: { ...iconTokens,
147
+ size: iconSize || iconTokens.size,
148
+ color: iconColor || iconTokens.color
149
+ }
150
+ }));
151
+ }
152
+
153
+ return /*#__PURE__*/React.createElement(View, {
154
+ style: [sideItemContainerStyles, itemBulletContainerStyles]
155
+ }, /*#__PURE__*/React.createElement(View, {
156
+ style: itemBulletStyles,
157
+ testID: "unordered-item-bullet"
158
+ }));
159
+ };
160
+
161
+ return /*#__PURE__*/React.createElement(View, {
162
+ style: [staticStyles.itemContainer, getContainerStyle()],
163
+ accessibilityRole: accessibilityRole
164
+ }, renderMarker(), renderItem());
165
+ };
166
+
167
+ const staticStyles = StyleSheet.create({
168
+ itemContainer: {
169
+ flexDirection: 'row'
170
+ },
171
+ centeredIcons: {
172
+ justifyContent: 'center'
173
+ }
174
+ });
175
+ ListItem.propTypes = {
176
+ tokens: getTokensPropType('List'),
177
+ variant: variantProp.propType,
178
+ children: PropTypes.node.isRequired,
179
+
180
+ /**
181
+ * Renders side item icon
182
+ */
183
+ icon: PropTypes.func,
184
+
185
+ /**
186
+ * Will set display icon color
187
+ */
188
+ iconColor: PropTypes.string,
189
+
190
+ /**
191
+ * Allow the user define the icon size if not defined the theme's file
192
+ */
193
+ iconSize: PropTypes.number,
194
+
195
+ /**
196
+ * @ignore
197
+ * Defined by parent if it's last item on the list
198
+ */
199
+ isLastItem: PropTypes.bool,
200
+
201
+ /**
202
+ * @ignore
203
+ * In case it is not the last item allow display divider
204
+ */
205
+ showDivider: PropTypes.bool
206
+ };
207
+ export default ListItem;
@@ -0,0 +1,2 @@
1
+ import List from './List';
2
+ export default List;
@@ -1,28 +1,11 @@
1
1
  import React from 'react';
2
- import { Text } from 'react-native';
3
2
  import PropTypes from 'prop-types';
4
3
  import ButtonBase from '../Button/ButtonBase';
5
- import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
4
+ import { useThemeTokensCallback } from '../ThemeProvider';
6
5
  import { a11yProps, copyPropTypes, getTokensPropType, hrefAttrsProp, linkProps, selectTokens, variantProp } from '../utils';
7
- import useCopy from './useCopy';
6
+ import useCopy from '../utils/useCopy';
8
7
  import dictionary from './dictionary';
9
8
 
10
- const selectTextStyles = ({
11
- color,
12
- fontName,
13
- fontSize,
14
- fontWeight,
15
- lineHeight,
16
- textLine
17
- }) => applyTextStyles({
18
- color,
19
- fontName,
20
- fontSize,
21
- fontWeight,
22
- lineHeight,
23
- textDecorationLine: textLine
24
- });
25
-
26
9
  function PageButton({
27
10
  label,
28
11
  onPress,
@@ -41,8 +24,6 @@ function PageButton({
41
24
 
42
25
  const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
43
26
 
44
- const getTextStyles = buttonState => selectTextStyles(getTokens(buttonState));
45
-
46
27
  const activeProps = isActive ? {
47
28
  selected: true,
48
29
  ...a11yProps.nonFocusableProps,
@@ -67,11 +48,7 @@ function PageButton({
67
48
  };
68
49
  return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, buttonProps, {
69
50
  tokens: getButtonTokens
70
- }, activeProps), buttonState => {
71
- return /*#__PURE__*/React.createElement(Text, {
72
- style: getTextStyles(buttonState)
73
- }, label);
74
- });
51
+ }, activeProps), label);
75
52
  }
76
53
 
77
54
  PageButton.propTypes = {