@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,132 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
5
+ import { a11yProps, getTokensPropType, selectTokens, variantProp } from '../utils';
6
+ import StackView from '../StackView';
7
+
8
+ const selectStyles = tokens => selectTokens('Feedback', tokens);
9
+
10
+ const selectTitleTextStyles = ({
11
+ titleFontSize,
12
+ ...tokens
13
+ }) => applyTextStyles(selectTokens('Typography', { ...tokens,
14
+ fontSize: titleFontSize
15
+ }));
16
+
17
+ const selectContentTextStyles = ({
18
+ contentFontSize,
19
+ ...tokens
20
+ }) => applyTextStyles(selectTokens('Typography', { ...tokens,
21
+ fontSize: contentFontSize
22
+ }));
23
+
24
+ const selectIconTokens = ({
25
+ iconSize,
26
+ iconColor
27
+ }) => ({
28
+ size: iconSize,
29
+ color: iconColor
30
+ });
31
+
32
+ const selectIconContainerStyles = ({
33
+ iconGap
34
+ }) => ({
35
+ paddingRight: iconGap
36
+ });
37
+ /**
38
+ * A feedback box commonly used with form fields.
39
+ *
40
+ * ### Standalone usage
41
+ * While its primary use is to facilitate feedback states for other form components such as `TextInput`,
42
+ * you may use it standalone.
43
+ *
44
+ * ### Complex content
45
+ * You may pass any React tree as the children of this component, bear in mind that a render function
46
+ * is better suited for styling children based on Feedback's variant.
47
+ *
48
+ * ### Using a render function
49
+ * When a function is passed for rendering content, it will receive the feedback text styles and
50
+ * variant as arguments.
51
+ *
52
+ * ### Accessibility
53
+ * All accessibility props set on this component will be applied to the outer container.
54
+ */
55
+
56
+
57
+ const Feedback = ({
58
+ title,
59
+ children,
60
+ id,
61
+ validation,
62
+ tokens,
63
+ variant,
64
+ ...rest
65
+ }) => {
66
+ const themeTokens = useThemeTokens('Feedback', tokens, { ...variant,
67
+ validation
68
+ });
69
+ const {
70
+ space
71
+ } = themeTokens;
72
+ const {
73
+ icon: IconComponent
74
+ } = themeTokens;
75
+ const titleTextStyles = selectTitleTextStyles(themeTokens);
76
+ const contentTextStyles = selectContentTextStyles(themeTokens);
77
+ const content = typeof children === 'string' ? /*#__PURE__*/React.createElement(Text, {
78
+ style: contentTextStyles
79
+ }, children) : children;
80
+ const accessibilityProps = a11yProps.select({
81
+ accessibilityRole: validation === 'error' ? 'alert' : undefined,
82
+ ...rest
83
+ });
84
+ return /*#__PURE__*/React.createElement(View, Object.assign({
85
+ style: selectStyles(themeTokens)
86
+ }, accessibilityProps, {
87
+ nativeID: id
88
+ }), /*#__PURE__*/React.createElement(StackView, {
89
+ space: space
90
+ }, title !== undefined && /*#__PURE__*/React.createElement(View, {
91
+ style: staticStyles.title
92
+ }, IconComponent && /*#__PURE__*/React.createElement(View, {
93
+ style: selectIconContainerStyles(themeTokens)
94
+ }, /*#__PURE__*/React.createElement(IconComponent, {
95
+ tokens: selectIconTokens(themeTokens)
96
+ })), /*#__PURE__*/React.createElement(Text, {
97
+ style: titleTextStyles
98
+ }, title)), content && typeof content === 'function' ? content({
99
+ textStyles: contentTextStyles,
100
+ variant
101
+ }) : content));
102
+ };
103
+
104
+ Feedback.propTypes = {
105
+ /**
106
+ * Emphasized summary of the feedback. If an icon is set, it is rendered next to the title.
107
+ */
108
+ title: PropTypes.string,
109
+
110
+ /**
111
+ * Feedback content rendered below the title. A render function `({textStyles, variant}) => {}` is supported.
112
+ */
113
+ children: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.func]),
114
+
115
+ /**
116
+ * Use to visually mark the Feedback as valid or invalid.
117
+ */
118
+ validation: PropTypes.oneOf(['error', 'success']),
119
+
120
+ /** @ignore */
121
+ id: PropTypes.string,
122
+ tokens: getTokensPropType('Feedback'),
123
+ variant: variantProp.propType
124
+ };
125
+ export default Feedback;
126
+ const staticStyles = StyleSheet.create({
127
+ title: {
128
+ display: 'flex',
129
+ flexDirection: 'row',
130
+ alignItems: 'center'
131
+ }
132
+ });
@@ -0,0 +1,2 @@
1
+ import Feedback from './Feedback';
2
+ export default Feedback;
package/lib/Icon/Icon.js CHANGED
@@ -2,28 +2,31 @@ import React from 'react';
2
2
  import { Platform, View } from 'react-native';
3
3
  import PropTypes from 'prop-types';
4
4
  import { useThemeTokens } from '../ThemeProvider';
5
- import { getTokensPropType, variantProp } from '../utils/propTypes';
5
+ import { getTokensPropType, scaleWithText, variantProp } from '../utils';
6
6
 
7
7
  const Icon = ({
8
8
  IconSvg,
9
9
  variant,
10
10
  label,
11
11
  titleId,
12
- tokens = {}
12
+ tokens,
13
+ scalesWithText = false
13
14
  }) => {
14
15
  const themeTokens = useThemeTokens('Icon', tokens, variant);
16
+ const size = scalesWithText ? scaleWithText(themeTokens.size) : themeTokens.size;
15
17
  const iconContent = /*#__PURE__*/React.createElement(IconSvg, {
16
18
  title: label,
17
19
  titleId: titleId,
18
- size: themeTokens.size,
20
+ size: size,
19
21
  color: themeTokens.color
20
22
  });
21
23
  return Platform.OS === 'web' ? /*#__PURE__*/React.createElement(View // eslint-disable-next-line react-native/no-inline-styles
22
24
  , {
23
25
  style: {
24
- // TODO: https://github.com/telus/universal-design-system/issues/487
25
- transition: 'transform 200ms',
26
- transform: [themeTokens.scale ? `scale(${themeTokens.scale})` : '', themeTokens.translateX ? `translateX(${themeTokens.translateX}px)` : ''].filter(exists => exists).join(' ')
26
+ // TODO: systematise animations.
27
+ // https://github.com/telus/universal-design-system/issues/487
28
+ transition: 'transform 200ms, color 200ms',
29
+ transform: [themeTokens.scale ? `scale(${themeTokens.scale})` : '', themeTokens.translateX ? `translateX(${themeTokens.translateX}px)` : '', themeTokens.translateY ? `translateY(${themeTokens.translateY}px)` : ''].filter(exists => exists).join(' ')
27
30
  }
28
31
  }, iconContent) : iconContent;
29
32
  }; // Auto-generated SVG icon components wrap Icon and pass through these props:
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Platform, View } from 'react-native';
4
+ import { iconComponentPropTypes } from './Icon';
5
+ import { getStackedContent } from '../StackView';
6
+ import { spacingProps } from '../utils';
7
+ /**
8
+ * Returns an icon and some text with a sized gap between them. This is a utility component
9
+ * intended for use when creating components, not intended for use in applications on its own.
10
+ *
11
+ * IconText does not wrap its children, so should be used either:
12
+ * - inline within a <Text> element
13
+ * - within a container with `flexDirection: 'row'`
14
+ */
15
+
16
+ const IconText = ({
17
+ space,
18
+ iconPosition = 'left',
19
+ icon: IconComponent,
20
+ iconProps,
21
+ children
22
+ }) => {
23
+ const iconContent = IconComponent && /*#__PURE__*/React.createElement(IconComponent, Object.assign({
24
+ scalesWithText: true
25
+ }, iconProps)); // Inline images on Android are always baseline-aligned which makes them look misaligned - offset it.
26
+ // See abandoned issue https://github.com/facebook/react-native/issues/6529
27
+
28
+ const size = iconProps?.tokens?.size;
29
+ const iconAdjusted = Platform.OS === 'android' && iconContent && size ? /*#__PURE__*/React.createElement(View, {
30
+ style: {
31
+ transform: [{
32
+ translateY: size * 0.2
33
+ }]
34
+ }
35
+ }, iconContent) : iconContent;
36
+ return getStackedContent(iconPosition === 'left' ? [iconAdjusted, children] : [children, iconAdjusted], {
37
+ space,
38
+ direction: 'row'
39
+ });
40
+ };
41
+
42
+ IconText.propTypes = {
43
+ /**
44
+ * Amount of space to separate the text content and icon. Uses the themes's spacing scale
45
+ * (see useSpacingScale for more info).
46
+ */
47
+ space: spacingProps.types.spacingValue,
48
+
49
+ /**
50
+ * Whether the icon should be to the left of the content or the right of the content.
51
+ */
52
+ iconPosition: PropTypes.oneOf(['left', 'right']),
53
+
54
+ /**
55
+ * A valid UDS icon component imported from a UDS palette.
56
+ */
57
+ icon: PropTypes.func,
58
+
59
+ /**
60
+ * Props that will be passed to the icon component. By default the icon's `scalesWithText`
61
+ * prop will be set as "true" so that the icon continues to match the size of the text
62
+ * if users use accessibility settings to increase text size.
63
+ */
64
+ iconProps: PropTypes.exact(iconComponentPropTypes),
65
+
66
+ /**
67
+ * Content to be rendered alongside the Icon component. Usually this should be a
68
+ * `<Typography>` component, or a component that renders `<Text>`.
69
+ */
70
+ children: PropTypes.node
71
+ };
72
+ export default IconText;
package/lib/Icon/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import Icon, { iconComponentPropTypes, iconSvgPropTypes } from './Icon';
2
+ import IconText from './IconText';
2
3
  export default Icon;
3
- export { iconComponentPropTypes, iconSvgPropTypes };
4
+ export { iconComponentPropTypes, iconSvgPropTypes, IconText };
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
5
+ import { getTokensPropType, selectTokens, variantProp } from '../utils';
6
+ import LabelContent from './LabelContent';
7
+ import Tooltip from '../Tooltip';
8
+
9
+ const selectLabelStyles = tokens => applyTextStyles(selectTokens('Typography', tokens));
10
+
11
+ const selectHintStyles = ({
12
+ hintColor,
13
+ hintFontName,
14
+ hintFontSize,
15
+ hintFontWeight,
16
+ hintLineHeight
17
+ }) => applyTextStyles({
18
+ color: hintColor,
19
+ fontName: hintFontName,
20
+ fontSize: hintFontSize,
21
+ fontWeight: hintFontWeight,
22
+ lineHeight: hintLineHeight
23
+ });
24
+
25
+ const selectGapStyles = ({
26
+ gap
27
+ }) => ({
28
+ marginRight: gap
29
+ });
30
+
31
+ function InputLabel({
32
+ label,
33
+ forId,
34
+ hint,
35
+ hintPosition = 'inline',
36
+ hintId,
37
+ tooltip,
38
+ tokens,
39
+ variant
40
+ }) {
41
+ const themeTokens = useThemeTokens('InputLabel', tokens, variant);
42
+ const hasTooltip = tooltip !== undefined;
43
+ const isHintInline = hintPosition === 'inline';
44
+ return /*#__PURE__*/React.createElement(View, {
45
+ style: [staticStyles.container, !isHintInline && staticStyles.containerWithHintBelow]
46
+ }, /*#__PURE__*/React.createElement(Text, {
47
+ style: [selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label]
48
+ }, /*#__PURE__*/React.createElement(LabelContent, {
49
+ forId: forId
50
+ }, label)), hint && isHintInline && /*#__PURE__*/React.createElement(Text, {
51
+ style: [selectHintStyles(themeTokens), hasTooltip && selectGapStyles(themeTokens)],
52
+ nativeID: hintId
53
+ }, hint), hasTooltip && /*#__PURE__*/React.createElement(Tooltip, {
54
+ content: tooltip
55
+ }), hint && !isHintInline && /*#__PURE__*/React.createElement(Text, {
56
+ style: [selectHintStyles(themeTokens), staticStyles.hintBelow],
57
+ nativeID: hintId
58
+ }, hint));
59
+ }
60
+
61
+ InputLabel.propTypes = {
62
+ label: PropTypes.string.isRequired,
63
+ forId: PropTypes.string,
64
+ hint: PropTypes.string,
65
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
66
+ hintId: PropTypes.string,
67
+ tooltip: PropTypes.string,
68
+ tokens: getTokensPropType('InputLabel'),
69
+ variant: variantProp.propType
70
+ };
71
+ export default InputLabel;
72
+ const staticStyles = StyleSheet.create({
73
+ container: {
74
+ display: 'flex',
75
+ flexDirection: 'row',
76
+ alignItems: 'center'
77
+ },
78
+ containerWithHintBelow: {
79
+ flexWrap: 'wrap'
80
+ },
81
+ label: {
82
+ flexShrink: 0
83
+ },
84
+ hintBelow: {
85
+ flexBasis: '100%',
86
+ flexShrink: 0
87
+ }
88
+ });
@@ -0,0 +1,8 @@
1
+ // Since there's no equivalent for web's <label> element we're simply rendering whatever is passed to this component.
2
+ function LabelContent({
3
+ children
4
+ }) {
5
+ return children;
6
+ }
7
+
8
+ export default LabelContent;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ function LabelContent({
5
+ children,
6
+ forId
7
+ }) {
8
+ return /*#__PURE__*/React.createElement("label", {
9
+ htmlFor: forId
10
+ }, children);
11
+ }
12
+
13
+ export default LabelContent;
14
+ LabelContent.propTypes = {
15
+ children: PropTypes.string,
16
+ forId: PropTypes.string
17
+ };
@@ -0,0 +1,2 @@
1
+ import InputLabel from './InputLabel';
2
+ export default InputLabel;
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import InputLabel from '../InputLabel';
4
+ import Feedback from '../Feedback';
5
+ import StackView from '../StackView';
6
+ import { useThemeTokens } from '../ThemeProvider';
7
+ import useUniqueId from '../utils/useUniqueId';
8
+
9
+ const joinDefined = array => array.filter(item => item !== undefined).join(' ');
10
+
11
+ function InputSupports({
12
+ children,
13
+ label,
14
+ hint,
15
+ hintPosition = 'inline',
16
+ feedback,
17
+ tooltip,
18
+ validation
19
+ }) {
20
+ const {
21
+ space
22
+ } = useThemeTokens('InputSupports');
23
+ const hasValidationError = validation === 'error';
24
+ const inputId = useUniqueId('input');
25
+ const hintId = useUniqueId('input-hint');
26
+ const feedbackId = useUniqueId('input-feedback');
27
+ const a11yProps = {
28
+ accessibilityLabel: label,
29
+ accessibilityHint: joinDefined([!hasValidationError && feedback, hint]),
30
+ // native only -> replaced with describedBy on web
31
+ accessibilityDescribedBy: joinDefined([!hasValidationError && feedback && feedbackId, // feedback receives a11yRole=alert on error, so there's no need to include it here
32
+ hint && hintId]),
33
+ // introduced in RNW 0.15.0
34
+ accessibilityInvalid: hasValidationError // introduced in RNW 0.15.0
35
+
36
+ };
37
+ return /*#__PURE__*/React.createElement(StackView, {
38
+ space: space
39
+ }, label && /*#__PURE__*/React.createElement(InputLabel, {
40
+ label: label,
41
+ hint: hint,
42
+ hintPosition: hintPosition,
43
+ hintId: hintId,
44
+ tooltip: tooltip,
45
+ forId: inputId
46
+ }), typeof children === 'function' ? children({
47
+ a11yProps,
48
+ inputId
49
+ }) : children, feedback && /*#__PURE__*/React.createElement(Feedback, {
50
+ id: feedbackId,
51
+ title: feedback,
52
+ validation: validation
53
+ }));
54
+ }
55
+
56
+ InputSupports.propTypes = {
57
+ children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
58
+
59
+ /**
60
+ * The input label.
61
+ */
62
+ label: PropTypes.string,
63
+
64
+ /**
65
+ * A short description of the expected input.
66
+ */
67
+ hint: PropTypes.string,
68
+
69
+ /**
70
+ * Position of the hint relative to label.
71
+ */
72
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
73
+
74
+ /**
75
+ * A detailed description of validation error/success or additional instructions.
76
+ * Visual variant is determined based on the `validation` prop.
77
+ */
78
+ feedback: PropTypes.string,
79
+
80
+ /**
81
+ * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
82
+ */
83
+ tooltip: PropTypes.string,
84
+
85
+ /**
86
+ * Use to visually mark an input as valid or invalid.
87
+ */
88
+ validation: PropTypes.oneOf(['error', 'success'])
89
+ };
90
+ export default InputSupports;
@@ -0,0 +1,2 @@
1
+ import InputSupports from './InputSupports';
2
+ export default InputSupports;
@@ -0,0 +1,55 @@
1
+ import PropTypes from 'prop-types';
2
+ const inputSupportProps = {
3
+ types: {
4
+ /**
5
+ * The input label.
6
+ */
7
+ label: PropTypes.string,
8
+
9
+ /**
10
+ * A short description of the expected input.
11
+ */
12
+ hint: PropTypes.string,
13
+
14
+ /**
15
+ * Position of the hint relative to label. Use `below` to display a larger hint below the label.
16
+ */
17
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
18
+
19
+ /**
20
+ * A detailed description of validation error/success or additional instructions.
21
+ * Visual variant is determined based on the `validation` prop.
22
+ */
23
+ feedback: PropTypes.string,
24
+
25
+ /**
26
+ * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
27
+ */
28
+ tooltip: PropTypes.string,
29
+
30
+ /**
31
+ * Use to visually mark an input as valid or invalid.
32
+ */
33
+ validation: PropTypes.oneOf(['error', 'success'])
34
+ },
35
+ select: ({
36
+ label,
37
+ hint,
38
+ hintPosition,
39
+ feedback,
40
+ tooltip,
41
+ validation,
42
+ ...rest
43
+ }) => ({
44
+ props: {
45
+ label,
46
+ hint,
47
+ hintPosition,
48
+ feedback,
49
+ tooltip,
50
+ validation
51
+ },
52
+ rest
53
+ })
54
+ };
55
+ export default inputSupportProps;
@@ -1,21 +1,46 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import Link from './Link';
3
+ import { useThemeTokensCallback } from '../ThemeProvider';
4
+ import { selectTokens, getTokensPropType } from '../utils';
5
+ import LinkBase from './LinkBase';
6
+ /**
7
+ * `ChevronLink` is a convenience wrapper around the `Link` component to enable "directional" links.
8
+ * It effectively pre-binds left and right icons, and a directional translation of the icon on hover.
9
+ *
10
+ * ChevronLink is not intended to be deeply themable; variants passed to ChevronLink are forwarded to Link.
11
+ */
4
12
 
5
13
  const ChevronLink = ({
6
14
  direction = 'right',
7
15
  children,
8
- variant = {},
16
+ tokens,
17
+ variant,
9
18
  ...linkProps
10
- }) => /*#__PURE__*/React.createElement(Link, Object.assign({
11
- variant: {
12
- component: 'ChevronLink',
13
- ...variant
14
- },
15
- iconPosition: direction
16
- }, linkProps), children);
19
+ }) => {
20
+ const getChevronTokens = useThemeTokensCallback('ChevronLink', tokens, variant);
17
21
 
18
- ChevronLink.propTypes = { ...Link.propTypes,
22
+ const applyChevronTokens = linkState => {
23
+ const {
24
+ leftIcon,
25
+ rightIcon,
26
+ iconDisplace,
27
+ ...otherTokens
28
+ } = getChevronTokens(linkState);
29
+ return { ...selectTokens('Link', otherTokens),
30
+ icon: direction === 'right' ? rightIcon : leftIcon,
31
+ iconTranslateX: iconDisplace * (direction === 'right' ? 1 : -1) || 0
32
+ };
33
+ };
34
+
35
+ const getTokens = useThemeTokensCallback('Link', applyChevronTokens, variant);
36
+ return /*#__PURE__*/React.createElement(LinkBase, Object.assign({}, linkProps, {
37
+ iconPosition: direction,
38
+ tokens: getTokens
39
+ }), children);
40
+ };
41
+
42
+ ChevronLink.propTypes = { ...LinkBase.propTypes,
43
+ tokens: getTokensPropType('ChevronLink', 'Link'),
19
44
  direction: PropTypes.oneOf(['left', 'right'])
20
45
  };
21
46
  export default ChevronLink;
@@ -0,0 +1,78 @@
1
+ /* eslint-disable camelcase */
2
+ import React, { useState } from 'react';
3
+ import { Text, TouchableWithoutFeedback } from 'react-native';
4
+ /**
5
+ * @typedef {import('react-native').PressableProps} PressableProps
6
+ */
7
+ // TouchableWithoutFeedback and Pressable have similar but not identical props APIs
8
+
9
+ const pressablePropsToTouchable = ({
10
+ unstable_pressDelay,
11
+ android_disableSound,
12
+ android_ripple,
13
+ // Unsupported, discard it
14
+ ...props
15
+ }) => ({ ...props,
16
+ touchSoundDisabled: android_disableSound,
17
+ delayPressIn: unstable_pressDelay
18
+ });
19
+ /**
20
+ * InlinePressable is an alternative to React Native's Pressable that works better when nested
21
+ * inline inside Text. It accepts the same props as React Native's Pressable.
22
+ *
23
+ * There are a lot of React Native bugs around Views/Pressables nested in Text, e.g.:
24
+ *
25
+ * - https://github.com/facebook/react-native/issues/23601#issuecomment-468069822
26
+ * - https://github.com/facebook/react-native/issues/30375
27
+ * - https://github.com/facebook/react-native/issues/31955
28
+ *
29
+ * On Native, these can be avoided using a `Text` wrapped in a `TouchableWithoutFeedback`; the latter
30
+ * injects additional handlers such as onPressIn to `Text`, resulting in a tree that is purely `Text`.
31
+ *
32
+ * Note that this should only be used on Native, not Web, because React Navigation's Web navigation
33
+ * functions don't work in Touchables (but do work in Pressable) due to different event handling.
34
+ *
35
+ * @param {PressableProps} PressableProps
36
+ */
37
+ // React Native exports prop Types but not propTypes, use JSDoc types here rather than duplicate RN
38
+ // eslint-disable-next-line react/prop-types
39
+
40
+
41
+ const InlinePressable = ({
42
+ onPress,
43
+ children,
44
+ style,
45
+ ...rest
46
+ }) => {
47
+ const [isFocused, setIsFocused] = useState(false);
48
+
49
+ const handleFocus = () => setIsFocused(true);
50
+
51
+ const handleBlur = () => setIsFocused(false);
52
+
53
+ const [isPressed, setIsPressed] = useState(false);
54
+
55
+ const handlePressIn = () => setIsPressed(true);
56
+
57
+ const handlePressOut = () => setIsPressed(false);
58
+
59
+ const pressState = {
60
+ pressed: isPressed,
61
+ focus: isFocused,
62
+ // limited support on native
63
+ hover: false // not yet supported on native
64
+
65
+ };
66
+ const currentStyle = typeof style === 'function' ? style(pressState) : style;
67
+ return /*#__PURE__*/React.createElement(TouchableWithoutFeedback, Object.assign({
68
+ onPress: onPress,
69
+ onPressIn: handlePressIn,
70
+ onPressOut: handlePressOut,
71
+ onFocus: handleFocus,
72
+ onBlur: handleBlur
73
+ }, pressablePropsToTouchable(rest)), /*#__PURE__*/React.createElement(Text, {
74
+ style: currentStyle
75
+ }, typeof children === 'function' ? children(pressState) : children));
76
+ };
77
+
78
+ export default InlinePressable;