@telus-uds/components-base 0.0.2-prerelease.9 → 1.1.0

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 (293) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.storybook/main.js +4 -0
  3. package/.storybook/preview.js +37 -0
  4. package/.ultra.cache.json +1 -1
  5. package/CHANGELOG.md +50 -0
  6. package/README.md +4 -2
  7. package/__fixtures__/test-utils.js +25 -0
  8. package/__fixtures__/testTheme.js +4 -2
  9. package/__tests__/Button/ButtonGroup.test.jsx +4 -5
  10. package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
  11. package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
  12. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  13. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  14. package/__tests__/Link/LinkBase.test.jsx +0 -14
  15. package/__tests__/Radio/Radio.test.jsx +2 -2
  16. package/__tests__/Radio/RadioGroup.test.jsx +4 -5
  17. package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
  18. package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
  19. package/__tests__/Search/Search.test.jsx +9 -8
  20. package/__tests__/Select/Select.test.jsx +3 -2
  21. package/__tests__/Tabs/Tabs.test.jsx +1 -161
  22. package/__tests__/Tags/Tags.test.jsx +4 -5
  23. package/__tests__/TextInput/TextArea.test.jsx +3 -2
  24. package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
  25. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  26. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  27. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  28. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  29. package/__tests__/utils/children.test.jsx +128 -0
  30. package/__tests__/utils/input.test.js +1 -1
  31. package/__tests__/utils/semantics.test.jsx +43 -0
  32. package/babel.config.js +9 -16
  33. package/component-docs.json +10313 -0
  34. package/generate-component-docs.js +56 -0
  35. package/lib/A11yText/index.js +10 -5
  36. package/lib/ActivityIndicator/Spinner.js +16 -13
  37. package/lib/ActivityIndicator/Spinner.native.js +12 -8
  38. package/lib/Box/Box.js +103 -8
  39. package/lib/Button/Button.js +9 -8
  40. package/lib/Button/ButtonBase.js +14 -7
  41. package/lib/Button/ButtonGroup.js +25 -10
  42. package/lib/Button/ButtonLink.js +10 -7
  43. package/lib/Card/Card.js +2 -0
  44. package/lib/Card/CardBase.js +13 -5
  45. package/lib/Card/PressableCardBase.js +12 -8
  46. package/lib/Checkbox/Checkbox.js +25 -14
  47. package/lib/Checkbox/CheckboxGroup.js +22 -12
  48. package/lib/Divider/Divider.js +12 -7
  49. package/lib/ExpandCollapse/Accordion.js +10 -4
  50. package/lib/ExpandCollapse/Control.js +12 -6
  51. package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
  52. package/lib/ExpandCollapse/Panel.js +8 -7
  53. package/lib/Feedback/Feedback.js +10 -5
  54. package/lib/Fieldset/Fieldset.js +10 -5
  55. package/lib/Fieldset/FieldsetContainer.js +10 -5
  56. package/lib/Fieldset/FieldsetContainer.native.js +10 -5
  57. package/lib/Fieldset/Legend.js +10 -5
  58. package/lib/Fieldset/Legend.native.js +10 -5
  59. package/lib/FlexGrid/Col/Col.js +8 -5
  60. package/lib/FlexGrid/FlexGrid.js +31 -6
  61. package/lib/FlexGrid/Row/Row.js +12 -5
  62. package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
  63. package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
  64. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
  65. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
  66. package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  67. package/lib/HorizontalScroll/index.js +35 -0
  68. package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  69. package/lib/Icon/Icon.js +16 -9
  70. package/lib/Icon/IconText.js +8 -7
  71. package/lib/IconButton/IconButton.js +10 -5
  72. package/lib/InputLabel/InputLabel.js +33 -5
  73. package/lib/InputLabel/LabelContent.js +22 -12
  74. package/lib/InputLabel/LabelContent.native.js +23 -5
  75. package/lib/InputSupports/InputSupports.js +10 -5
  76. package/lib/Link/ChevronLink.js +12 -5
  77. package/lib/Link/InlinePressable.js +10 -4
  78. package/lib/Link/InlinePressable.native.js +5 -4
  79. package/lib/Link/Link.js +12 -5
  80. package/lib/Link/LinkBase.js +12 -5
  81. package/lib/Link/TextButton.js +10 -5
  82. package/lib/List/List.js +6 -6
  83. package/lib/List/ListItem.js +28 -33
  84. package/lib/List/index.js +15 -0
  85. package/lib/Modal/Modal.js +10 -5
  86. package/lib/Notification/Notification.js +21 -5
  87. package/lib/Pagination/PageButton.js +16 -11
  88. package/lib/Pagination/Pagination.js +12 -7
  89. package/lib/Pagination/SideButton.js +12 -7
  90. package/lib/Pagination/usePagination.js +2 -2
  91. package/lib/Progress/Progress.js +10 -5
  92. package/lib/Progress/ProgressBar.js +21 -10
  93. package/lib/Progress/ProgressBarBackground.js +12 -8
  94. package/lib/Radio/Radio.js +14 -13
  95. package/lib/Radio/RadioButton.js +20 -9
  96. package/lib/Radio/RadioGroup.js +24 -13
  97. package/lib/RadioCard/RadioCard.js +14 -10
  98. package/lib/RadioCard/RadioCardGroup.js +13 -12
  99. package/lib/Search/Search.js +29 -18
  100. package/lib/Select/Picker.js +11 -6
  101. package/lib/Select/Picker.native.js +21 -6
  102. package/lib/Select/Select.js +46 -4
  103. package/lib/SideNav/Item.js +10 -5
  104. package/lib/SideNav/ItemsGroup.js +10 -5
  105. package/lib/SideNav/SideNav.js +11 -7
  106. package/lib/Skeleton/Skeleton.js +15 -15
  107. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  108. package/lib/Spacer/Spacer.js +19 -7
  109. package/lib/StackView/StackView.js +26 -7
  110. package/lib/StackView/StackWrap.js +24 -13
  111. package/lib/StackView/StackWrapBox.js +34 -8
  112. package/lib/StackView/StackWrapGap.js +16 -7
  113. package/lib/StackView/common.js +4 -2
  114. package/lib/StackView/getStackedContent.js +2 -2
  115. package/lib/StepTracker/StepTracker.js +10 -5
  116. package/lib/Tabs/Tabs.js +26 -19
  117. package/lib/Tabs/TabsItem.js +16 -12
  118. package/lib/Tags/Tags.js +27 -11
  119. package/lib/TextInput/TextArea.js +7 -5
  120. package/lib/TextInput/TextInput.js +12 -6
  121. package/lib/TextInput/TextInputBase.js +12 -8
  122. package/lib/ThemeProvider/ThemeProvider.js +14 -10
  123. package/lib/ThemeProvider/useSetTheme.js +6 -1
  124. package/lib/ThemeProvider/utils/styles.js +2 -2
  125. package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
  126. package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
  127. package/lib/Tooltip/Backdrop.js +10 -2
  128. package/lib/Tooltip/Tooltip.js +5 -4
  129. package/lib/Typography/Typography.js +40 -24
  130. package/lib/index.js +36 -1
  131. package/lib/utils/a11y/index.js +13 -0
  132. package/lib/utils/a11y/propTypes.js +61 -0
  133. package/lib/utils/a11y/propTypes.native.js +47 -0
  134. package/lib/utils/a11y/semantics.js +173 -0
  135. package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
  136. package/lib/utils/children.js +55 -8
  137. package/lib/utils/input.js +27 -17
  138. package/lib/utils/propTypes.js +40 -68
  139. package/lib/utils/useCopy.js +1 -1
  140. package/lib/utils/useHash.js +8 -4
  141. package/lib/utils/useSpacingScale.js +1 -3
  142. package/lib/utils/useUniqueId.js +1 -1
  143. package/package.json +14 -6
  144. package/release-context.json +4 -4
  145. package/src/A11yText/index.jsx +6 -4
  146. package/src/ActivityIndicator/Spinner.jsx +5 -3
  147. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  148. package/src/Box/Box.jsx +125 -39
  149. package/src/Button/Button.jsx +7 -4
  150. package/src/Button/ButtonBase.jsx +86 -77
  151. package/src/Button/ButtonGroup.jsx +81 -69
  152. package/src/Button/ButtonLink.jsx +18 -13
  153. package/src/Card/Card.jsx +2 -2
  154. package/src/Card/CardBase.jsx +6 -4
  155. package/src/Card/PressableCardBase.jsx +71 -64
  156. package/src/Checkbox/Checkbox.jsx +118 -108
  157. package/src/Checkbox/CheckboxGroup.jsx +72 -62
  158. package/src/Divider/Divider.jsx +7 -4
  159. package/src/ExpandCollapse/Accordion.jsx +3 -2
  160. package/src/ExpandCollapse/Control.jsx +40 -43
  161. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  162. package/src/ExpandCollapse/Panel.jsx +69 -63
  163. package/src/Feedback/Feedback.jsx +36 -33
  164. package/src/Fieldset/Fieldset.jsx +63 -56
  165. package/src/Fieldset/FieldsetContainer.jsx +14 -5
  166. package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
  167. package/src/Fieldset/Legend.jsx +7 -2
  168. package/src/Fieldset/Legend.native.jsx +7 -2
  169. package/src/FlexGrid/Col/Col.jsx +139 -132
  170. package/src/FlexGrid/FlexGrid.jsx +79 -51
  171. package/src/FlexGrid/Row/Row.jsx +55 -48
  172. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  173. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  174. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
  175. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
  176. package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  177. package/src/HorizontalScroll/index.js +17 -0
  178. package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  179. package/src/Icon/Icon.jsx +37 -35
  180. package/src/Icon/IconText.jsx +22 -17
  181. package/src/IconButton/IconButton.jsx +49 -42
  182. package/src/InputLabel/InputLabel.jsx +53 -38
  183. package/src/InputLabel/LabelContent.jsx +14 -6
  184. package/src/InputLabel/LabelContent.native.jsx +11 -2
  185. package/src/InputSupports/InputSupports.jsx +29 -34
  186. package/src/Link/ChevronLink.jsx +26 -16
  187. package/src/Link/InlinePressable.jsx +5 -3
  188. package/src/Link/InlinePressable.native.jsx +5 -3
  189. package/src/Link/Link.jsx +22 -16
  190. package/src/Link/LinkBase.jsx +67 -58
  191. package/src/Link/TextButton.jsx +30 -23
  192. package/src/List/List.jsx +6 -7
  193. package/src/List/ListItem.jsx +70 -90
  194. package/src/List/index.js +5 -0
  195. package/src/Modal/Modal.jsx +9 -4
  196. package/src/Notification/Notification.jsx +58 -43
  197. package/src/Pagination/PageButton.jsx +37 -34
  198. package/src/Pagination/Pagination.jsx +88 -92
  199. package/src/Pagination/SideButton.jsx +44 -41
  200. package/src/Progress/Progress.jsx +5 -4
  201. package/src/Progress/ProgressBar.jsx +42 -29
  202. package/src/Progress/ProgressBarBackground.jsx +5 -3
  203. package/src/Radio/Radio.jsx +85 -78
  204. package/src/Radio/RadioButton.jsx +54 -43
  205. package/src/Radio/RadioGroup.jsx +74 -63
  206. package/src/RadioCard/RadioCard.jsx +75 -68
  207. package/src/RadioCard/RadioCardGroup.jsx +82 -75
  208. package/src/Search/Search.jsx +127 -106
  209. package/src/Select/Picker.jsx +49 -42
  210. package/src/Select/Picker.native.jsx +56 -49
  211. package/src/Select/Select.jsx +115 -72
  212. package/src/SideNav/Item.jsx +53 -46
  213. package/src/SideNav/ItemsGroup.jsx +50 -43
  214. package/src/SideNav/SideNav.jsx +68 -60
  215. package/src/Skeleton/Skeleton.jsx +9 -13
  216. package/src/Spacer/Spacer.jsx +11 -4
  217. package/src/StackView/StackView.jsx +47 -23
  218. package/src/StackView/StackWrap.jsx +14 -12
  219. package/src/StackView/StackWrapBox.jsx +62 -28
  220. package/src/StackView/StackWrapGap.jsx +46 -24
  221. package/src/StackView/common.jsx +3 -2
  222. package/src/StepTracker/StepTracker.jsx +73 -62
  223. package/src/Tabs/Tabs.jsx +70 -62
  224. package/src/Tabs/TabsItem.jsx +111 -103
  225. package/src/Tags/Tags.jsx +114 -102
  226. package/src/TextInput/TextArea.jsx +5 -4
  227. package/src/TextInput/TextInput.jsx +5 -4
  228. package/src/TextInput/TextInputBase.jsx +84 -77
  229. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  230. package/src/ThemeProvider/useSetTheme.js +4 -0
  231. package/src/ThemeProvider/utils/theme-tokens.js +28 -0
  232. package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
  233. package/src/Tooltip/Tooltip.jsx +134 -130
  234. package/src/Typography/Typography.jsx +67 -44
  235. package/src/index.js +3 -1
  236. package/src/utils/a11y/index.js +1 -0
  237. package/src/utils/a11y/propTypes.js +61 -0
  238. package/src/utils/a11y/propTypes.native.js +39 -0
  239. package/src/utils/a11y/semantics.js +162 -0
  240. package/src/utils/children.jsx +60 -7
  241. package/src/utils/input.js +20 -17
  242. package/src/utils/propTypes.js +30 -76
  243. package/src/utils/useCopy.js +1 -1
  244. package/src/utils/useHash.js +8 -3
  245. package/stories/A11yText/A11yText.stories.jsx +3 -3
  246. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +2 -2
  247. package/stories/Box/Box.stories.jsx +2 -2
  248. package/stories/Button/Button.stories.jsx +3 -3
  249. package/stories/Button/ButtonGroup.stories.jsx +2 -2
  250. package/stories/Button/ButtonLink.stories.jsx +2 -2
  251. package/stories/Card/Card.stories.jsx +2 -2
  252. package/stories/Checkbox/Checkbox.stories.jsx +2 -2
  253. package/stories/Divider/Divider.stories.jsx +2 -2
  254. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +3 -3
  255. package/stories/Feedback/Feedback.stories.jsx +2 -2
  256. package/stories/FlexGrid/01 FlexGrid.stories.jsx +2 -2
  257. package/stories/FlexGrid/02 Row.stories.jsx +2 -2
  258. package/stories/FlexGrid/03 Col.stories.jsx +2 -2
  259. package/stories/Icon/Icon.stories.jsx +2 -2
  260. package/stories/IconButton/IconButton.stories.jsx +2 -2
  261. package/stories/InputLabel/InputLabel.stories.jsx +2 -2
  262. package/stories/Link/ChevronLink.stories.jsx +2 -2
  263. package/stories/Link/Link.stories.jsx +2 -2
  264. package/stories/Link/TextButton.stories.jsx +2 -2
  265. package/stories/List/List.stories.jsx +2 -2
  266. package/stories/Modal/Modal.stories.jsx +2 -2
  267. package/stories/Notification/Notification.stories.jsx +2 -2
  268. package/stories/Pagination/Pagination.stories.jsx +2 -2
  269. package/stories/Progress/Progress.stories.jsx +2 -2
  270. package/stories/Radio/Radio.stories.jsx +2 -2
  271. package/stories/RadioCard/RadioCard.stories.jsx +2 -2
  272. package/stories/Search/Search.stories.jsx +2 -2
  273. package/stories/Select/Select.stories.jsx +2 -2
  274. package/stories/SideNav/SideNav.stories.jsx +2 -2
  275. package/stories/SideNav/SideNavItem.stories.jsx +2 -2
  276. package/stories/SideNav/SideNavItemsGroup.stories.jsx +2 -2
  277. package/stories/Skeleton/Skeleton.stories.jsx +3 -3
  278. package/stories/Spacer/Spacer.stories.jsx +2 -2
  279. package/stories/StackView/StackView.stories.jsx +2 -2
  280. package/stories/StackView/StackWrap.stories.jsx +2 -2
  281. package/stories/StepTracker/StepTracker.stories.jsx +2 -2
  282. package/stories/Tabs/Tabs.stories.jsx +2 -2
  283. package/stories/Tags/Tags.stories.jsx +2 -2
  284. package/stories/TextInput/TextArea.stories.jsx +2 -2
  285. package/stories/TextInput/TextInput.stories.jsx +2 -2
  286. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +2 -2
  287. package/stories/Tooltip/Tooltip.stories.jsx +2 -2
  288. package/stories/TooltipButton/TooltipButton.stories.jsx +2 -2
  289. package/stories/Typography/Typography.stories.jsx +2 -2
  290. package/stories/platform-supports.jsx +1 -1
  291. package/stories/supports.jsx +4 -5
  292. package/src/Tabs/HorizontalScroll.jsx +0 -165
  293. package/src/Tabs/TabsScrollButton.jsx +0 -100
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react'
1
+ import React, { forwardRef, useState } from 'react'
2
2
  import { View, StyleSheet } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
@@ -41,117 +41,124 @@ const selectIconTokens = ({ iconSize, iconColor }) => ({ color: iconColor, size:
41
41
  * Use the following props to supply additional accessibility labels for the input - `accessibilityLabel`,
42
42
  * clear button - `clearButtonAccessibilityLabel`, and submit button - `submitButtonAccessibilityLabel`.
43
43
  */
44
- const Search = ({
45
- initialValue = '',
46
- placeholder = 'Search',
47
- inactive,
48
- onChange,
49
- onSubmit,
50
- onClear,
51
- accessibilityLabel,
52
- copy = 'en',
53
- tokens,
54
- variant
55
- }) => {
56
- const [value, setValue] = useState(initialValue)
57
- const themeTokens = useThemeTokens('Search', tokens, variant)
58
- const buttonTokens = useThemeTokens('SearchButton', tokens, variant)
59
-
60
- const getThemeTokens = useThemeTokensCallback('Search', tokens, variant)
61
- const getButtonTokens = useThemeTokensCallback('SearchButton', tokens, variant)
62
-
63
- // TODO: support overriding the a11y props using a custom dictionary
64
- const getCopy = useCopy({ dictionary, copy })
65
-
66
- const {
67
- placeholderColor,
68
- buttonsGap,
69
- clearButtonIcon: ClearButtonIcon,
70
- submitButtonIcon: SubmitButtonIcon
71
- } = themeTokens
72
-
73
- // get the actual gap value for the current viewport
74
- const buttonsGapSize = useSpacingScale(buttonsGap)
75
-
76
- const handleSubmit = () => {
77
- if (onSubmit !== undefined) {
78
- onSubmit(value)
44
+ const Search = forwardRef(
45
+ (
46
+ {
47
+ initialValue = '',
48
+ placeholder = 'Search',
49
+ inactive,
50
+ onChange,
51
+ onSubmit,
52
+ onClear,
53
+ accessibilityLabel,
54
+ copy = 'en',
55
+ tokens,
56
+ variant
57
+ },
58
+ ref
59
+ ) => {
60
+ const [value, setValue] = useState(initialValue)
61
+ const themeTokens = useThemeTokens('Search', tokens, variant)
62
+ const buttonTokens = useThemeTokens('SearchButton', tokens, variant)
63
+
64
+ const getThemeTokens = useThemeTokensCallback('Search', tokens, variant)
65
+ const getButtonTokens = useThemeTokensCallback('SearchButton', tokens, variant)
66
+
67
+ // TODO: support overriding the a11y props using a custom dictionary
68
+ const getCopy = useCopy({ dictionary, copy })
69
+
70
+ const {
71
+ placeholderColor,
72
+ buttonsGap,
73
+ clearButtonIcon: ClearButtonIcon,
74
+ submitButtonIcon: SubmitButtonIcon
75
+ } = themeTokens
76
+
77
+ // get the actual gap value for the current viewport
78
+ const buttonsGapSize = useSpacingScale(buttonsGap)
79
+
80
+ const handleSubmit = (event) => {
81
+ if (onSubmit !== undefined) {
82
+ onSubmit(value, event)
83
+ }
79
84
  }
80
- }
81
85
 
82
- const handleChange = (currentValue) => {
83
- setValue(currentValue)
86
+ const handleChange = (currentValue, event) => {
87
+ setValue(currentValue, event)
84
88
 
85
- if (onChange !== undefined) onChange(currentValue)
86
- }
89
+ if (onChange !== undefined) onChange(currentValue, event)
90
+ }
87
91
 
88
- const handleClear = () => {
89
- setValue('')
92
+ const handleClear = (event) => {
93
+ setValue('', event)
90
94
 
91
- if (onClear !== undefined) onClear()
92
- if (onChange !== undefined) onChange('')
93
- }
95
+ if (onClear !== undefined) onClear('', event)
96
+ if (onChange !== undefined) onChange('', event)
97
+ }
94
98
 
95
- const isEmpty = value === ''
96
-
97
- return (
98
- <View>
99
- <TextInputBase
100
- tokens={(appearances) =>
101
- selectInputTokens({
102
- searchTokens: getThemeTokens(appearances),
103
- buttonTokens,
104
- buttonsGapSize,
105
- isEmpty
106
- })
107
- }
108
- placeholder={placeholder}
109
- placeholderTextColor={placeholderColor}
110
- inactive={inactive}
111
- enablesReturnKeyAutomatically
112
- returnKeyType="search"
113
- value={value}
114
- onChange={handleChange}
115
- onSubmitEditing={handleSubmit}
116
- accessibilityLabel={accessibilityLabel || getCopy('accessibilityLabel')}
117
- />
118
- <View style={[staticStyles.iconsContainer, selectIconsContainerStyle(themeTokens)]}>
119
- <StackView direction="row" space={buttonsGap}>
120
- {ClearButtonIcon && !isEmpty && (
121
- <ButtonBase
122
- onPress={handleClear}
123
- inactive={inactive}
124
- accessibilityRole="button"
125
- accessibilityLabel={getCopy('clearButtonAccessibilityLabel')}
126
- tokens={(appearances) => selectButtonTokens(getButtonTokens(appearances))}
127
- >
128
- {(buttonState) => (
129
- <ClearButtonIcon tokens={selectIconTokens(getButtonTokens(buttonState))} />
130
- )}
131
- </ButtonBase>
132
- )}
133
- {SubmitButtonIcon && (
134
- <ButtonBase
135
- onPress={handleSubmit}
136
- inactive={inactive}
137
- accessibilityRole="button"
138
- accessibilityLabel={getCopy('submitButtonAccessibilityLabel')}
139
- tokens={(buttonState) =>
140
- selectButtonTokens(getButtonTokens({ ...buttonState, priority: 'high' }))
141
- }
142
- >
143
- {(buttonState) => (
144
- <SubmitButtonIcon
145
- tokens={selectIconTokens(getButtonTokens({ ...buttonState, priority: 'high' }))}
146
- />
147
- )}
148
- </ButtonBase>
149
- )}
150
- </StackView>
99
+ const isEmpty = value === ''
100
+
101
+ return (
102
+ <View style={staticStyles.container}>
103
+ <TextInputBase
104
+ ref={ref}
105
+ tokens={(appearances) =>
106
+ selectInputTokens({
107
+ searchTokens: getThemeTokens(appearances),
108
+ buttonTokens,
109
+ buttonsGapSize,
110
+ isEmpty
111
+ })
112
+ }
113
+ placeholder={placeholder}
114
+ placeholderTextColor={placeholderColor}
115
+ inactive={inactive}
116
+ enablesReturnKeyAutomatically
117
+ returnKeyType="search"
118
+ value={value}
119
+ onChange={handleChange}
120
+ onSubmitEditing={handleSubmit}
121
+ accessibilityLabel={accessibilityLabel || getCopy('accessibilityLabel')}
122
+ />
123
+ <View style={[staticStyles.iconsContainer, selectIconsContainerStyle(themeTokens)]}>
124
+ <StackView direction="row" space={buttonsGap}>
125
+ {ClearButtonIcon && !isEmpty && (
126
+ <ButtonBase
127
+ onPress={handleClear}
128
+ inactive={inactive}
129
+ accessibilityRole="button"
130
+ accessibilityLabel={getCopy('clearButtonAccessibilityLabel')}
131
+ tokens={(appearances) => selectButtonTokens(getButtonTokens(appearances))}
132
+ >
133
+ {(buttonState) => (
134
+ <ClearButtonIcon {...selectIconTokens(getButtonTokens(buttonState))} />
135
+ )}
136
+ </ButtonBase>
137
+ )}
138
+ {SubmitButtonIcon && (
139
+ <ButtonBase
140
+ onPress={handleSubmit}
141
+ inactive={inactive}
142
+ accessibilityRole="button"
143
+ accessibilityLabel={getCopy('submitButtonAccessibilityLabel')}
144
+ tokens={(buttonState) =>
145
+ selectButtonTokens(getButtonTokens({ ...buttonState, priority: 'high' }))
146
+ }
147
+ >
148
+ {(buttonState) => (
149
+ <SubmitButtonIcon
150
+ {...selectIconTokens(getButtonTokens({ ...buttonState, priority: 'high' }))}
151
+ />
152
+ )}
153
+ </ButtonBase>
154
+ )}
155
+ </StackView>
156
+ </View>
151
157
  </View>
152
- </View>
153
- )
154
- }
158
+ )
159
+ }
160
+ )
161
+ Search.displayName = 'Search'
155
162
 
156
163
  Search.propTypes = {
157
164
  /**
@@ -186,7 +193,18 @@ Search.propTypes = {
186
193
  * Use to provide an accessible label for the input (visually hidden).
187
194
  */
188
195
  accessibilityLabel: PropTypes.string,
189
- copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), PropTypes.object]),
196
+ /**
197
+ * Select english or french copy for the accessible labels.
198
+ * You may also pass in a custom dictionary object.
199
+ */
200
+ copy: PropTypes.oneOfType([
201
+ PropTypes.oneOf(['en', 'fr']),
202
+ PropTypes.shape({
203
+ accessibilityLabel: PropTypes.string,
204
+ clearButtonAccessibilityLabel: PropTypes.string,
205
+ submitButtonAccessibilityLabel: PropTypes.string
206
+ })
207
+ ]),
190
208
  tokens: getTokensPropType('Search'),
191
209
  variant: variantProp.propType
192
210
  }
@@ -194,6 +212,9 @@ Search.propTypes = {
194
212
  export default Search
195
213
 
196
214
  const staticStyles = StyleSheet.create({
215
+ container: {
216
+ // No styles needed here except the View defaults (position: relative etc)
217
+ },
197
218
  iconsContainer: {
198
219
  position: 'absolute',
199
220
  right: 0,
@@ -1,50 +1,57 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
 
3
3
  import PropTypes from 'prop-types'
4
4
  import { componentPropType } from '../utils'
5
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
6
+ const Picker = forwardRef(
7
+ (
8
+ {
9
+ value,
10
+ onChange,
11
+ onFocus,
12
+ onBlur,
13
+ onMouseOver,
14
+ onMouseOut,
15
+ style,
16
+ inactive,
17
+ children,
18
+ placeholder,
19
+ nativeID,
20
+ testID,
21
+ ...rest
22
+ },
23
+ ref
24
+ ) => {
25
+ const { accessibilityLabel, accessibilityDescribedBy, accessibilityInvalid } = rest
22
26
 
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
- }
27
+ return (
28
+ <select
29
+ ref={ref}
30
+ style={style}
31
+ onMouseOver={onMouseOver}
32
+ onMouseOut={onMouseOut}
33
+ onFocus={onFocus}
34
+ onBlur={onBlur}
35
+ disabled={inactive}
36
+ value={value || (placeholder !== undefined ? placeholder.value : undefined)}
37
+ onChange={(event) => onChange(event.target.value, event)}
38
+ id={nativeID}
39
+ aria-label={accessibilityLabel}
40
+ aria-describedby={accessibilityDescribedBy}
41
+ aria-invalid={accessibilityInvalid}
42
+ data-testid={testID}
43
+ >
44
+ {placeholder !== undefined && (
45
+ <option value={placeholder.value} disabled hidden>
46
+ {placeholder.label}
47
+ </option>
48
+ )}
49
+ {children}
50
+ </select>
51
+ )
52
+ }
53
+ )
54
+ Picker.displayName = 'Picker'
48
55
 
49
56
  export default Picker
50
57
 
@@ -1,4 +1,4 @@
1
- import React, { Children } from 'react'
1
+ import React, { forwardRef, Children } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View, Platform } from 'react-native'
4
4
  import NativePicker from 'react-native-picker-select'
@@ -25,58 +25,65 @@ const selectAndroidContainerStyles = ({ paddingLeft = 0, paddingRight = 0, ...re
25
25
  ...rest
26
26
  })
27
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
- }
28
+ const Picker = forwardRef(
29
+ ({ value, onChange, onFocus, onBlur, style, inactive, children, placeholder, ...rest }, ref) => {
30
+ // ungroup items, since there's no way to support groups on native
31
+ const flatChildren = Children.toArray(children).flatMap((child) => {
32
+ if (child.type === Group) {
33
+ return child.props.children
34
+ }
44
35
 
45
- return child
46
- })
36
+ return child
37
+ })
47
38
 
48
- const items = flatChildren.map(({ props }) => ({
49
- label: props.children,
50
- value: props.value
51
- }))
39
+ const items = flatChildren.map(({ props }) => ({
40
+ label: props.children,
41
+ value: props.value
42
+ }))
52
43
 
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
- )
44
+ // Unlike other input components, react-native-picker-select doesn't pass an event to its change fn.
45
+ const handleChange = (newValue, index) => {
46
+ if (onChange)
47
+ onChange(newValue, {
48
+ // TODO: see if it's possible to pass a real React SyntheticEvent here. For now, just pass an
49
+ // object with a shape where if an onChange deconstructs `event.nativeEvent`, it won't crash.
50
+ nativeEvent: {
51
+ index,
52
+ value: newValue
53
+ }
54
+ })
55
+ }
69
56
 
70
- return (
71
- <>
72
- {Platform.OS === 'android' ? (
73
- <View style={selectAndroidContainerStyles(style)}>{picker}</View>
74
- ) : (
75
- picker
76
- )}
77
- </>
78
- )
79
- }
57
+ const picker = (
58
+ <NativePicker
59
+ pickerProps={{ ref }}
60
+ touchableWrapperProps={a11yProps.select(rest)}
61
+ onOpen={onFocus}
62
+ onClose={onBlur}
63
+ disabled={inactive}
64
+ items={items}
65
+ value={value}
66
+ onValueChange={handleChange}
67
+ style={{
68
+ inputIOS: style,
69
+ inputAndroid: selectAndroidInputStyles(style)
70
+ }}
71
+ placeholder={placeholder !== undefined ? placeholder : {}}
72
+ />
73
+ )
74
+
75
+ return (
76
+ <>
77
+ {Platform.OS === 'android' ? (
78
+ <View style={selectAndroidContainerStyles(style)}>{picker}</View>
79
+ ) : (
80
+ picker
81
+ )}
82
+ </>
83
+ )
84
+ }
85
+ )
86
+ Picker.displayName = 'Picker'
80
87
 
81
88
  export default Picker
82
89