@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
@@ -1,36 +1,30 @@
1
1
  import React from 'react'
2
- import { StyleSheet, View, Text, PixelRatio } from 'react-native'
2
+ import { Text } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
5
5
 
6
6
  import ButtonBase from '../Button/ButtonBase'
7
- import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
7
+ import { IconText } from '../Icon'
8
+ import { useThemeTokensCallback } from '../ThemeProvider'
8
9
  import { useViewport } from '../ViewportProvider'
9
10
  import { copyPropTypes, hrefAttrsProp, linkProps, selectTokens } from '../utils'
10
11
 
11
12
  import dictionary from './dictionary'
12
- import useCopy from './useCopy'
13
-
14
- const selectTextStyles = ({ color, fontName, fontSize, fontWeight, lineHeight }) =>
15
- applyTextStyles({
16
- color,
17
- fontName,
18
- fontSize,
19
- fontWeight,
20
- lineHeight
21
- })
22
-
23
- const selectIconTokens = ({ color, iconSize, iconTranslateX }) => {
24
- // Scale icon with text, but with a cap so text isn't squashed at large scales
25
- const iconScale = Math.min(PixelRatio.getFontScale(), 2)
13
+ import useCopy from '../utils/useCopy'
26
14
 
15
+ const selectIconTokens = ({ color, iconSize, iconDisplace }, direction) => {
27
16
  return {
28
17
  color,
29
- size: iconSize * iconScale,
30
- translateX: iconTranslateX
18
+ size: iconSize,
19
+ translateX: iconDisplace * (direction === 'previous' ? -1 : 1)
31
20
  }
32
21
  }
33
22
 
23
+ const directionToSide = {
24
+ previous: 'left',
25
+ next: 'right'
26
+ }
27
+
34
28
  function SideButton({ direction = 'previous', onPress, href, copy, variant, tokens, ...props }) {
35
29
  const viewport = useViewport()
36
30
  const buttonVariant = { direction, viewport, ...variant }
@@ -38,11 +32,10 @@ function SideButton({ direction = 'previous', onPress, href, copy, variant, toke
38
32
 
39
33
  const getCopy = useCopy({ dictionary, copy })
40
34
 
41
- const { icon: IconComponent } = getTokens(tokens, buttonVariant)
35
+ const { icon } = getTokens(tokens, buttonVariant)
42
36
 
43
37
  const getButtonTokens = (buttonState) => selectTokens('Button', getTokens(buttonState))
44
- const getIconTokens = (buttonState) => selectIconTokens(getTokens(buttonState))
45
- const getTextStyles = (buttonState) => selectTextStyles(getTokens(buttonState))
38
+ const getIconTokens = (buttonState) => selectIconTokens(getTokens(buttonState), direction)
46
39
 
47
40
  const label = direction === 'previous' ? getCopy('previousText') : getCopy('nextText')
48
41
  const showLabel = viewport !== 'sm' && viewport !== 'xs'
@@ -63,17 +56,19 @@ function SideButton({ direction = 'previous', onPress, href, copy, variant, toke
63
56
 
64
57
  return (
65
58
  <ButtonBase {...buttonProps} tokens={getButtonTokens}>
66
- {(buttonState) => (
67
- <View style={staticStyles.contentContainer}>
68
- {direction === 'previous' && IconComponent && (
69
- <IconComponent tokens={getIconTokens(buttonState)} />
70
- )}
71
- {showLabel && <Text style={getTextStyles(buttonState)}>{label}</Text>}
72
- {direction === 'next' && IconComponent && (
73
- <IconComponent tokens={getIconTokens(buttonState)} />
74
- )}
75
- </View>
76
- )}
59
+ {({ textStyles, ...buttonState }) => {
60
+ const iconProps = { tokens: getIconTokens(buttonState) }
61
+ return (
62
+ <IconText
63
+ icon={icon}
64
+ space={1}
65
+ iconPosition={directionToSide[direction]}
66
+ iconProps={iconProps}
67
+ >
68
+ {showLabel && <Text style={textStyles}>{label}</Text>}
69
+ </IconText>
70
+ )
71
+ }}
77
72
  </ButtonBase>
78
73
  )
79
74
  }
@@ -85,9 +80,3 @@ SideButton.propTypes = {
85
80
  }
86
81
 
87
82
  export default SideButton
88
-
89
- const staticStyles = StyleSheet.create({
90
- contentContainer: {
91
- flexDirection: 'row'
92
- }
93
- })
@@ -0,0 +1,270 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Pressable, StyleSheet, Text, View } from 'react-native'
4
+
5
+ import RadioInput from './RadioInput'
6
+ // @todo move `LabelContent` outside of the `InputLabel` and fix
7
+ // the issue with the cursor not being pointer on Web
8
+ import RadioLabel from '../InputLabel/LabelContent'
9
+ import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
10
+ import { getTokensPropType, useInputValue, useUniqueId, variantProp } from '../utils'
11
+ import { a11yProps } from '../utils/propTypes'
12
+ import StackView from '../StackView'
13
+
14
+ const selectCheckedStyles = ({ checkedBackgroundColor, checkedSize }) => ({
15
+ backgroundColor: checkedBackgroundColor,
16
+ borderRadius: checkedSize / 2,
17
+ height: checkedSize,
18
+ width: checkedSize
19
+ })
20
+ const selectContainerStyles = ({
21
+ containerBackgroundColor,
22
+ containerBorderRadius,
23
+ containerMarginBottom,
24
+ containerOpacity,
25
+ containerPaddingBottom,
26
+ containerPaddingLeft,
27
+ containerPaddingRight,
28
+ containerPaddingTop,
29
+ containerShadow
30
+ }) => ({
31
+ backgroundColor: containerBackgroundColor,
32
+ borderRadius: containerBorderRadius,
33
+ marginBottom: containerMarginBottom,
34
+ opacity: containerOpacity,
35
+ paddingBottom: containerPaddingBottom,
36
+ paddingLeft: containerPaddingLeft,
37
+ paddingRight: containerPaddingRight,
38
+ paddingTop: containerPaddingTop,
39
+ ...applyShadowToken(containerShadow)
40
+ })
41
+ const selectDescriptionStyles = ({
42
+ containerPaddingLeft = 0,
43
+ descriptionFontSize,
44
+ descriptionLineHeight,
45
+ descriptionMarginLeft,
46
+ inputSize,
47
+ labelMarginLeft = 0
48
+ }) => ({
49
+ marginLeft: descriptionMarginLeft ?? containerPaddingLeft + inputSize + labelMarginLeft,
50
+ ...applyTextStyles({ fontSize: descriptionFontSize, lineHeight: descriptionLineHeight })
51
+ })
52
+ const selectInputStyles = ({
53
+ inputBackgroundColor,
54
+ inputBorderColor,
55
+ inputBorderWidth,
56
+ inputOutlineColor,
57
+ inputOutlineWidth,
58
+ inputSize
59
+ }) => ({
60
+ borderColor: inputBorderColor,
61
+ borderRadius: inputSize / 2,
62
+ borderWidth: inputBorderWidth,
63
+ backgroundColor: inputBackgroundColor,
64
+ height: inputSize,
65
+ outlineStyle: 'solid',
66
+ outlineColor: inputOutlineColor,
67
+ outlineWidth: inputOutlineWidth,
68
+ width: inputSize
69
+ })
70
+ const selectLabelStyles = ({
71
+ labelColor,
72
+ labelFontName,
73
+ labelFontSize,
74
+ labelFontWeight,
75
+ labelMarginLeft,
76
+ labelLineHeight
77
+ }) =>
78
+ applyTextStyles({
79
+ color: labelColor,
80
+ fontName: labelFontName,
81
+ fontWeight: labelFontWeight,
82
+ fontSize: labelFontSize,
83
+ lineHeight: labelLineHeight,
84
+ marginLeft: labelMarginLeft
85
+ })
86
+
87
+ /**
88
+ * Basic Radio component.
89
+ *
90
+ * ## Component API
91
+ *
92
+ * Use `label` prop to provide a label for the radio button. For a disabled `Radio` set the `inactive` prop to `true`.
93
+ *
94
+ * ### Controlled version
95
+ *
96
+ * If the radio button is controlled from outside, it needs to receive `checked` and `onChange` props.
97
+ *
98
+ * ### Uncontrolled version
99
+ *
100
+ * In case of uncontrolled radio button you can use `defaultChecked` prop to provide the initial value.
101
+ * Whenever the radio button gets toggled, it calls the `onChange` callback with the new value (boolean).
102
+ *
103
+ * ### Using within forms
104
+ *
105
+ * You can pass `name` and `value` props if you need a particular radio button to be a part of a radio group
106
+ * on a form.
107
+ *
108
+ * ### Validation
109
+ *
110
+ * You can mark a radio button as failing validation by setting the `error` prop to `true`.
111
+ *
112
+ * ## A11y guidelines
113
+ *
114
+ * Radio component accepts all the common accessibility props, but also sets some defaults, including
115
+ * accessibility role `'radio'` and accessibility state that depends on the other props (`checked`, `inactive`)
116
+ * or the internal state in case of uncontrolled radio button.
117
+ *
118
+ */
119
+ const Radio = ({
120
+ checked,
121
+ defaultChecked,
122
+ description,
123
+ error = false,
124
+ id,
125
+ inactive,
126
+ label,
127
+ name,
128
+ onChange,
129
+ tokens,
130
+ value,
131
+ variant,
132
+ ...rest
133
+ }) => {
134
+ const { currentValue: isChecked, setValue: setIsChecked, isControlled } = useInputValue(
135
+ {
136
+ value: checked,
137
+ initialValue: defaultChecked,
138
+ onChange
139
+ },
140
+ 'useRadioValue'
141
+ )
142
+ const getTokens = useThemeTokensCallback('Radio', tokens, {
143
+ checked: isChecked,
144
+ inactive,
145
+ error,
146
+ ...variant
147
+ })
148
+ const handleChange = () => {
149
+ if (!inactive && !isChecked) {
150
+ setIsChecked(true)
151
+ }
152
+ }
153
+ const accessibilityProps = a11yProps.select(rest)
154
+ const uniqueId = useUniqueId('radio')
155
+ const inputId = id ?? uniqueId
156
+
157
+ // @todo our current version of React Native Web doesn't include
158
+ // keyboard support on `Pressable` (which starts with 0.15.3), so
159
+ // the complete accessibility of the `Radio` component on Web
160
+ // (namely, change on key pressed) is pending RNW upgrade
161
+ // (see https://github.com/necolas/react-native-web/issues/1950)
162
+ return (
163
+ <Pressable
164
+ disabled={inactive}
165
+ onPress={handleChange}
166
+ accessibilityRole="radio"
167
+ accessibilityState={{ checked: isChecked, disabled: inactive }}
168
+ {...accessibilityProps}
169
+ >
170
+ {({ focused: focus, hovered: hover, pressed }) => {
171
+ const stateTokens = getTokens({ focus, hover, pressed })
172
+
173
+ return (
174
+ <StackView space={0}>
175
+ <View style={[staticStyles.container, selectContainerStyles(stateTokens)]}>
176
+ <View
177
+ style={StyleSheet.flatten([
178
+ staticStyles.defaultInputStyles,
179
+ selectInputStyles(stateTokens, isChecked)
180
+ ])}
181
+ testID="Radio-Input"
182
+ >
183
+ {/* Add a real input on Web, skip on native platforms */}
184
+ <RadioInput
185
+ checked={isChecked}
186
+ defaultChecked={defaultChecked}
187
+ disabled={inactive}
188
+ id={inputId}
189
+ isControlled={isControlled}
190
+ name={name}
191
+ value={value}
192
+ />
193
+ {isChecked && (
194
+ <View style={selectCheckedStyles(stateTokens)} testID="Radio-Checked" />
195
+ )}
196
+ </View>
197
+ {Boolean(label) && (
198
+ <Text style={selectLabelStyles(stateTokens)}>
199
+ <RadioLabel forId={inputId}>{label}</RadioLabel>
200
+ </Text>
201
+ )}
202
+ </View>
203
+ {Boolean(description) && (
204
+ <Text style={selectDescriptionStyles(getTokens())}>{description}</Text>
205
+ )}
206
+ </StackView>
207
+ )
208
+ }}
209
+ </Pressable>
210
+ )
211
+ }
212
+
213
+ Radio.propTypes = {
214
+ ...a11yProps.propTypes,
215
+ /**
216
+ * Use `checked` for controlled Radio. For uncontrolled Radio, use the `defaultChecked` prop.
217
+ */
218
+ checked: PropTypes.bool,
219
+ /**
220
+ * Use `defaultChecked` to provide the initial value for an uncontrolled Radio.
221
+ */
222
+ defaultChecked: PropTypes.bool,
223
+ /**
224
+ * An optional radio button description.
225
+ */
226
+ description: PropTypes.string,
227
+ /**
228
+ * Radio button ID.
229
+ */
230
+ id: PropTypes.string,
231
+ /**
232
+ * Whether the corresponding input is disabled or active.
233
+ */
234
+ inactive: PropTypes.bool,
235
+ /**
236
+ * The label.
237
+ */
238
+ label: PropTypes.string,
239
+ /**
240
+ * Associate this radio button with a group (set as the name attribute).
241
+ */
242
+ name: PropTypes.string,
243
+ /**
244
+ * Whether the underlying input triggered a validation error or not.
245
+ */
246
+ error: PropTypes.bool,
247
+ /**
248
+ * The value. Must be unique within the group.
249
+ */
250
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
251
+ /**
252
+ * Callback called when a controlled radio button gets interacted with.
253
+ */
254
+ onChange: PropTypes.func,
255
+ /**
256
+ * Radio tokens.
257
+ */
258
+ tokens: getTokensPropType('Radio'),
259
+ /**
260
+ * Radio variant.
261
+ */
262
+ variant: variantProp.propType
263
+ }
264
+
265
+ export default Radio
266
+
267
+ const staticStyles = StyleSheet.create({
268
+ container: { flexDirection: 'row', alignItems: 'center' },
269
+ defaultInputStyles: { alignItems: 'center', justifyContent: 'center' }
270
+ })
@@ -0,0 +1,6 @@
1
+ /**
2
+ * There's no radio button input on native platforms, so this is a noop.
3
+ */
4
+ const RadioInput = () => null
5
+
6
+ export default RadioInput
@@ -0,0 +1,57 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ /**
5
+ * On Web we need to include an actual input but hide it.
6
+ */
7
+ const RadioInput = forwardRef(
8
+ ({ checked, defaultChecked, disabled, id, isControlled, name, onChange, value }, ref) => {
9
+ const handleClick = (event) => {
10
+ // Cancel the click dispatched via the label tag, since it's already wrapped
11
+ // in <Pressable>
12
+ event.preventDefault()
13
+ event.stopPropagation()
14
+ }
15
+
16
+ return (
17
+ <input
18
+ checked={isControlled ? checked : undefined}
19
+ defaultChecked={isControlled ? undefined : defaultChecked}
20
+ disabled={disabled}
21
+ hidden
22
+ id={id}
23
+ name={name}
24
+ onChange={onChange}
25
+ onClick={handleClick}
26
+ ref={ref}
27
+ type="radio"
28
+ value={value}
29
+ />
30
+ )
31
+ }
32
+ )
33
+ RadioInput.displayName = 'Radio'
34
+
35
+ RadioInput.propTypes = {
36
+ checked: PropTypes.bool,
37
+ defaultChecked: PropTypes.bool,
38
+ disabled: PropTypes.bool,
39
+ id: PropTypes.string,
40
+ isControlled: PropTypes.bool,
41
+ name: PropTypes.string,
42
+ onChange: PropTypes.func,
43
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])
44
+ }
45
+
46
+ RadioInput.defaultProps = {
47
+ checked: undefined,
48
+ defaultChecked: undefined,
49
+ disabled: false,
50
+ id: null,
51
+ isControlled: false,
52
+ name: undefined,
53
+ onChange: () => {},
54
+ value: undefined
55
+ }
56
+
57
+ export default RadioInput
@@ -0,0 +1,3 @@
1
+ import Radio from './Radio'
2
+
3
+ export default Radio
@@ -0,0 +1,14 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ import { componentPropType } from '../utils'
4
+
5
+ const Group = ({ children }) => {
6
+ return children
7
+ }
8
+
9
+ export default Group
10
+
11
+ Group.propTypes = {
12
+ children: componentPropType('Item'),
13
+ label: PropTypes.string.isRequired
14
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { componentPropType } from '../utils'
5
+
6
+ const Group = ({ children, label }) => {
7
+ return <optgroup label={label}>{children}</optgroup>
8
+ }
9
+
10
+ export default Group
11
+
12
+ Group.propTypes = {
13
+ children: componentPropType('Item'),
14
+ label: PropTypes.string.isRequired
15
+ }
@@ -0,0 +1,10 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ const Item = () => null
4
+
5
+ export default Item
6
+
7
+ Item.propTypes = {
8
+ children: PropTypes.string.isRequired,
9
+ value: PropTypes.string.isRequired
10
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ const Item = ({ children, value }) => <option value={value}>{children}</option>
5
+
6
+ export default Item
7
+
8
+ Item.propTypes = {
9
+ children: PropTypes.string.isRequired,
10
+ value: PropTypes.string.isRequired
11
+ }
@@ -0,0 +1,95 @@
1
+ import React, { Children } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { View, Platform } from 'react-native'
4
+ import NativePicker from 'react-native-picker-select'
5
+
6
+ import { a11yProps, componentPropType } from '../utils'
7
+ import Group from './Group'
8
+
9
+ // styling of the native input is very limited, most of the styles have to be applied to an additional View
10
+ const selectAndroidInputStyles = ({
11
+ height = 0,
12
+ paddingBottom = 0,
13
+ paddingTop = 0,
14
+ borderWidth = 0,
15
+ color
16
+ }) => ({
17
+ height: height - paddingTop - paddingBottom - 2 * borderWidth,
18
+ color
19
+ })
20
+
21
+ // the native input has a side padding of 8px, which can't be adjusted, so we have to account for that in the container
22
+ const selectAndroidContainerStyles = ({ paddingLeft = 0, paddingRight = 0, ...rest }) => ({
23
+ paddingLeft: paddingLeft > 8 ? paddingLeft - 8 : 0,
24
+ paddingRight: paddingRight > 8 ? paddingRight - 8 : 0,
25
+ ...rest
26
+ })
27
+
28
+ const Picker = ({
29
+ value,
30
+ onChange,
31
+ onFocus,
32
+ onBlur,
33
+ style,
34
+ inactive,
35
+ children,
36
+ placeholder,
37
+ ...rest
38
+ }) => {
39
+ // ungroup items, since there's no way to support groups on native
40
+ const flatChildren = Children.toArray(children).flatMap((child) => {
41
+ if (child.type === Group) {
42
+ return child.props.children
43
+ }
44
+
45
+ return child
46
+ })
47
+
48
+ const items = flatChildren.map(({ props }) => ({
49
+ label: props.children,
50
+ value: props.value
51
+ }))
52
+
53
+ const picker = (
54
+ <NativePicker
55
+ touchableWrapperProps={a11yProps.select(rest)}
56
+ onOpen={onFocus}
57
+ onClose={onBlur}
58
+ disabled={inactive}
59
+ items={items}
60
+ value={value}
61
+ onValueChange={onChange}
62
+ style={{
63
+ inputIOS: style,
64
+ inputAndroid: selectAndroidInputStyles(style)
65
+ }}
66
+ placeholder={placeholder !== undefined ? placeholder : {}}
67
+ />
68
+ )
69
+
70
+ return (
71
+ <>
72
+ {Platform.OS === 'android' ? (
73
+ <View style={selectAndroidContainerStyles(style)}>{picker}</View>
74
+ ) : (
75
+ picker
76
+ )}
77
+ </>
78
+ )
79
+ }
80
+
81
+ export default Picker
82
+
83
+ Picker.propTypes = {
84
+ value: PropTypes.string,
85
+ onChange: PropTypes.func,
86
+ onFocus: PropTypes.func,
87
+ onBlur: PropTypes.func,
88
+ style: PropTypes.object,
89
+ inactive: PropTypes.bool,
90
+ children: componentPropType(['Item', 'Group']),
91
+ placeholder: PropTypes.shape({
92
+ value: PropTypes.string,
93
+ label: PropTypes.string
94
+ })
95
+ }
@@ -0,0 +1,67 @@
1
+ import React from 'react'
2
+
3
+ import PropTypes from 'prop-types'
4
+ import { componentPropType } from '../utils'
5
+
6
+ const Picker = ({
7
+ value,
8
+ onChange,
9
+ onFocus,
10
+ onBlur,
11
+ onMouseOver,
12
+ onMouseOut,
13
+ style,
14
+ inactive,
15
+ children,
16
+ placeholder,
17
+ nativeID,
18
+ testID,
19
+ ...rest
20
+ }) => {
21
+ const { accessibilityLabel, accessibilityDescribedBy, accessibilityInvalid } = rest
22
+
23
+ return (
24
+ <select
25
+ style={style}
26
+ onMouseOver={onMouseOver}
27
+ onMouseOut={onMouseOut}
28
+ onFocus={onFocus}
29
+ onBlur={onBlur}
30
+ disabled={inactive}
31
+ value={value || (placeholder !== undefined ? placeholder.value : undefined)}
32
+ onChange={(event) => onChange(event.target.value)}
33
+ id={nativeID}
34
+ aria-label={accessibilityLabel}
35
+ aria-describedby={accessibilityDescribedBy}
36
+ aria-invalid={accessibilityInvalid}
37
+ data-testid={testID}
38
+ >
39
+ {placeholder !== undefined && (
40
+ <option value={placeholder.value} disabled hidden>
41
+ {placeholder.label}
42
+ </option>
43
+ )}
44
+ {children}
45
+ </select>
46
+ )
47
+ }
48
+
49
+ export default Picker
50
+
51
+ Picker.propTypes = {
52
+ value: PropTypes.string,
53
+ onChange: PropTypes.func,
54
+ onFocus: PropTypes.func,
55
+ onBlur: PropTypes.func,
56
+ onMouseOver: PropTypes.func,
57
+ onMouseOut: PropTypes.func,
58
+ style: PropTypes.object,
59
+ inactive: PropTypes.bool,
60
+ children: componentPropType(['Item', 'Group']),
61
+ placeholder: PropTypes.shape({
62
+ value: PropTypes.string,
63
+ label: PropTypes.string
64
+ }),
65
+ nativeID: PropTypes.string,
66
+ testID: PropTypes.string
67
+ }