@telus-uds/components-base 1.8.3 → 1.9.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 (284) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/component-docs.json +17 -1
  3. package/lib/ActivityIndicator/Spinner.js +7 -7
  4. package/lib/ActivityIndicator/Spinner.native.js +2 -2
  5. package/lib/Button/ButtonBase.js +2 -2
  6. package/lib/Button/ButtonGroup.js +1 -1
  7. package/lib/Card/Card.js +9 -4
  8. package/lib/Checkbox/Checkbox.js +5 -3
  9. package/lib/Checkbox/CheckboxGroup.js +2 -2
  10. package/lib/Divider/Divider.js +2 -2
  11. package/lib/ExpandCollapse/Panel.js +12 -18
  12. package/lib/FlexGrid/Col/Col.js +1 -1
  13. package/lib/Icon/Icon.js +1 -1
  14. package/lib/Link/InlinePressable.js +5 -1
  15. package/lib/Link/LinkBase.js +5 -2
  16. package/lib/List/List.js +8 -2
  17. package/lib/Pagination/PageButton.js +2 -2
  18. package/lib/Pagination/Pagination.js +5 -3
  19. package/lib/Pagination/usePagination.js +2 -2
  20. package/lib/Progress/ProgressBar.js +3 -3
  21. package/lib/Progress/ProgressBarBackground.js +3 -3
  22. package/lib/Radio/Radio.js +2 -2
  23. package/lib/Radio/RadioGroup.js +2 -2
  24. package/lib/RadioCard/RadioCard.js +1 -1
  25. package/lib/RadioCard/RadioCardGroup.js +2 -2
  26. package/lib/Search/Search.js +1 -1
  27. package/lib/SideNav/SideNav.js +2 -2
  28. package/lib/Skeleton/Skeleton.js +1 -1
  29. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  30. package/lib/StackView/StackWrap.js +3 -1
  31. package/lib/StackView/getStackedContent.js +2 -2
  32. package/lib/Tabs/Tabs.js +28 -3
  33. package/lib/Tabs/TabsItem.js +5 -6
  34. package/lib/Tags/Tags.js +1 -1
  35. package/lib/TextInput/TextInput.js +0 -11
  36. package/lib/TextInput/TextInputBase.js +9 -0
  37. package/lib/TextInput/propTypes.js +3 -8
  38. package/lib/ThemeProvider/utils/styles.js +2 -2
  39. package/lib/ThemeProvider/utils/theme-tokens.js +7 -9
  40. package/lib/ToggleSwitch/ToggleSwitch.js +1 -1
  41. package/lib/ToggleSwitch/ToggleSwitchGroup.js +1 -1
  42. package/lib/Tooltip/Backdrop.js +2 -10
  43. package/lib/utils/animation/useVerticalExpandAnimation.js +40 -14
  44. package/lib/utils/children.js +2 -2
  45. package/lib/utils/input.js +6 -12
  46. package/lib/utils/props/componentPropType.js +3 -3
  47. package/lib/utils/props/selectSystemProps.js +2 -2
  48. package/lib/utils/props/textInputProps.js +8 -1
  49. package/lib/utils/props/tokens.js +2 -2
  50. package/lib/utils/useSpacingScale.js +3 -1
  51. package/lib/utils/useUniqueId.js +1 -1
  52. package/lib-module/A11yInfoProvider/index.js +63 -0
  53. package/lib-module/A11yText/index.js +56 -0
  54. package/lib-module/ActivityIndicator/Spinner.js +77 -0
  55. package/lib-module/ActivityIndicator/Spinner.native.js +144 -0
  56. package/lib-module/ActivityIndicator/index.js +41 -0
  57. package/lib-module/ActivityIndicator/shared.js +12 -0
  58. package/lib-module/BaseProvider/index.js +29 -0
  59. package/lib-module/Box/Box.js +244 -0
  60. package/lib-module/Box/index.js +2 -0
  61. package/lib-module/Button/Button.js +26 -0
  62. package/lib-module/Button/ButtonBase.js +271 -0
  63. package/lib-module/Button/ButtonGroup.js +247 -0
  64. package/lib-module/Button/ButtonLink.js +40 -0
  65. package/lib-module/Button/index.js +4 -0
  66. package/lib-module/Button/propTypes.js +36 -0
  67. package/lib-module/Card/Card.js +85 -0
  68. package/lib-module/Card/CardBase.js +66 -0
  69. package/lib-module/Card/PressableCardBase.js +114 -0
  70. package/lib-module/Card/index.js +4 -0
  71. package/lib-module/Checkbox/Checkbox.js +348 -0
  72. package/lib-module/Checkbox/CheckboxGroup.js +233 -0
  73. package/lib-module/Checkbox/CheckboxInput.js +60 -0
  74. package/lib-module/Checkbox/CheckboxInput.native.js +6 -0
  75. package/lib-module/Checkbox/index.js +3 -0
  76. package/lib-module/Divider/Divider.js +124 -0
  77. package/lib-module/Divider/index.js +2 -0
  78. package/lib-module/ExpandCollapse/Accordion.js +15 -0
  79. package/lib-module/ExpandCollapse/Control.js +136 -0
  80. package/lib-module/ExpandCollapse/ExpandCollapse.js +95 -0
  81. package/lib-module/ExpandCollapse/Panel.js +159 -0
  82. package/lib-module/ExpandCollapse/index.js +7 -0
  83. package/lib-module/Feedback/Feedback.js +157 -0
  84. package/lib-module/Feedback/index.js +2 -0
  85. package/lib-module/Fieldset/Fieldset.js +153 -0
  86. package/lib-module/Fieldset/FieldsetContainer.js +32 -0
  87. package/lib-module/Fieldset/FieldsetContainer.native.js +23 -0
  88. package/lib-module/Fieldset/Legend.js +24 -0
  89. package/lib-module/Fieldset/Legend.native.js +31 -0
  90. package/lib-module/Fieldset/cssReset.js +14 -0
  91. package/lib-module/Fieldset/index.js +2 -0
  92. package/lib-module/FlexGrid/Col/Col.js +276 -0
  93. package/lib-module/FlexGrid/Col/index.js +2 -0
  94. package/lib-module/FlexGrid/FlexGrid.js +148 -0
  95. package/lib-module/FlexGrid/Row/Row.js +184 -0
  96. package/lib-module/FlexGrid/Row/index.js +2 -0
  97. package/lib-module/FlexGrid/helpers/index.js +18 -0
  98. package/lib-module/FlexGrid/index.js +2 -0
  99. package/lib-module/FlexGrid/providers/GutterContext.js +3 -0
  100. package/lib-module/HorizontalScroll/HorizontalScroll.js +175 -0
  101. package/lib-module/HorizontalScroll/HorizontalScrollButton.js +81 -0
  102. package/lib-module/HorizontalScroll/ScrollViewEnd.js +48 -0
  103. package/lib-module/HorizontalScroll/ScrollViewEnd.native.js +27 -0
  104. package/lib-module/HorizontalScroll/dictionary.js +11 -0
  105. package/lib-module/HorizontalScroll/index.js +11 -0
  106. package/lib-module/HorizontalScroll/itemPositions.js +112 -0
  107. package/lib-module/Icon/Icon.js +62 -0
  108. package/lib-module/Icon/IconText.js +83 -0
  109. package/lib-module/Icon/index.js +4 -0
  110. package/lib-module/IconButton/IconButton.js +122 -0
  111. package/lib-module/IconButton/index.js +2 -0
  112. package/lib-module/InputLabel/InputLabel.js +148 -0
  113. package/lib-module/InputLabel/LabelContent.js +27 -0
  114. package/lib-module/InputLabel/LabelContent.native.js +19 -0
  115. package/lib-module/InputLabel/index.js +2 -0
  116. package/lib-module/InputSupports/InputSupports.js +96 -0
  117. package/lib-module/InputSupports/index.js +2 -0
  118. package/lib-module/InputSupports/useInputSupports.js +32 -0
  119. package/lib-module/Link/ChevronLink.js +52 -0
  120. package/lib-module/Link/InlinePressable.js +44 -0
  121. package/lib-module/Link/InlinePressable.native.js +89 -0
  122. package/lib-module/Link/Link.js +28 -0
  123. package/lib-module/Link/LinkBase.js +243 -0
  124. package/lib-module/Link/TextButton.js +35 -0
  125. package/lib-module/Link/index.js +5 -0
  126. package/lib-module/List/List.js +62 -0
  127. package/lib-module/List/ListItem.js +248 -0
  128. package/lib-module/List/index.js +5 -0
  129. package/lib-module/Modal/Modal.js +222 -0
  130. package/lib-module/Modal/dictionary.js +9 -0
  131. package/lib-module/Modal/index.js +2 -0
  132. package/lib-module/Notification/Notification.js +209 -0
  133. package/lib-module/Notification/dictionary.js +8 -0
  134. package/lib-module/Notification/index.js +2 -0
  135. package/lib-module/Pagination/PageButton.js +66 -0
  136. package/lib-module/Pagination/Pagination.js +153 -0
  137. package/lib-module/Pagination/SideButton.js +106 -0
  138. package/lib-module/Pagination/dictionary.js +18 -0
  139. package/lib-module/Pagination/index.js +2 -0
  140. package/lib-module/Pagination/usePagination.js +73 -0
  141. package/lib-module/Progress/Progress.js +89 -0
  142. package/lib-module/Progress/ProgressBar.js +138 -0
  143. package/lib-module/Progress/ProgressBarBackground.js +42 -0
  144. package/lib-module/Progress/index.js +4 -0
  145. package/lib-module/Radio/Radio.js +285 -0
  146. package/lib-module/Radio/RadioButton.js +138 -0
  147. package/lib-module/Radio/RadioGroup.js +243 -0
  148. package/lib-module/Radio/RadioInput.js +62 -0
  149. package/lib-module/Radio/RadioInput.native.js +6 -0
  150. package/lib-module/Radio/index.js +3 -0
  151. package/lib-module/RadioCard/RadioCard.js +219 -0
  152. package/lib-module/RadioCard/RadioCardGroup.js +250 -0
  153. package/lib-module/RadioCard/index.js +3 -0
  154. package/lib-module/Search/Search.js +261 -0
  155. package/lib-module/Search/dictionary.js +12 -0
  156. package/lib-module/Search/index.js +2 -0
  157. package/lib-module/Select/Group.js +21 -0
  158. package/lib-module/Select/Group.native.js +15 -0
  159. package/lib-module/Select/Item.js +20 -0
  160. package/lib-module/Select/Item.native.js +3 -0
  161. package/lib-module/Select/Picker.js +68 -0
  162. package/lib-module/Select/Picker.native.js +120 -0
  163. package/lib-module/Select/Select.js +337 -0
  164. package/lib-module/Select/index.js +6 -0
  165. package/lib-module/SideNav/Item.js +145 -0
  166. package/lib-module/SideNav/ItemContent.js +48 -0
  167. package/lib-module/SideNav/ItemsGroup.js +117 -0
  168. package/lib-module/SideNav/SideNav.js +136 -0
  169. package/lib-module/SideNav/index.js +1 -0
  170. package/lib-module/Skeleton/Skeleton.js +179 -0
  171. package/lib-module/Skeleton/index.js +2 -0
  172. package/lib-module/Skeleton/skeleton.constant.js +3 -0
  173. package/lib-module/Skeleton/skeletonWebAnimation.js +18 -0
  174. package/lib-module/Skeleton/useSkeletonNativeAnimation.js +24 -0
  175. package/lib-module/Spacer/Spacer.js +98 -0
  176. package/lib-module/Spacer/index.js +2 -0
  177. package/lib-module/StackView/StackView.js +125 -0
  178. package/lib-module/StackView/StackWrap.js +50 -0
  179. package/lib-module/StackView/StackWrap.native.js +3 -0
  180. package/lib-module/StackView/StackWrapBox.js +115 -0
  181. package/lib-module/StackView/StackWrapGap.js +59 -0
  182. package/lib-module/StackView/common.js +35 -0
  183. package/lib-module/StackView/getStackedContent.js +124 -0
  184. package/lib-module/StackView/index.js +5 -0
  185. package/lib-module/StepTracker/Step.js +248 -0
  186. package/lib-module/StepTracker/StepTracker.js +185 -0
  187. package/lib-module/StepTracker/dictionary.js +10 -0
  188. package/lib-module/StepTracker/index.js +2 -0
  189. package/lib-module/Tabs/Tabs.js +143 -0
  190. package/lib-module/Tabs/TabsItem.js +224 -0
  191. package/lib-module/Tabs/index.js +2 -0
  192. package/lib-module/Tags/Tags.js +251 -0
  193. package/lib-module/Tags/index.js +2 -0
  194. package/lib-module/TextInput/TextArea.js +94 -0
  195. package/lib-module/TextInput/TextInput.js +64 -0
  196. package/lib-module/TextInput/TextInputBase.js +254 -0
  197. package/lib-module/TextInput/index.js +3 -0
  198. package/lib-module/TextInput/propTypes.js +33 -0
  199. package/lib-module/ThemeProvider/ThemeProvider.js +36 -0
  200. package/lib-module/ThemeProvider/index.js +6 -0
  201. package/lib-module/ThemeProvider/useSetTheme.js +25 -0
  202. package/lib-module/ThemeProvider/useTheme.js +14 -0
  203. package/lib-module/ThemeProvider/useThemeTokens.js +110 -0
  204. package/lib-module/ThemeProvider/utils/index.js +2 -0
  205. package/lib-module/ThemeProvider/utils/styles.js +181 -0
  206. package/lib-module/ThemeProvider/utils/theme-tokens.js +163 -0
  207. package/lib-module/ToggleSwitch/ToggleSwitch.js +250 -0
  208. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +220 -0
  209. package/lib-module/ToggleSwitch/index.js +3 -0
  210. package/lib-module/Tooltip/Backdrop.js +45 -0
  211. package/lib-module/Tooltip/Backdrop.native.js +44 -0
  212. package/lib-module/Tooltip/Tooltip.js +348 -0
  213. package/lib-module/Tooltip/dictionary.js +8 -0
  214. package/lib-module/Tooltip/getTooltipPosition.js +175 -0
  215. package/lib-module/Tooltip/index.js +2 -0
  216. package/lib-module/TooltipButton/TooltipButton.js +78 -0
  217. package/lib-module/TooltipButton/index.js +2 -0
  218. package/lib-module/Typography/Typography.js +124 -0
  219. package/lib-module/Typography/index.js +2 -0
  220. package/lib-module/ViewportProvider/ViewportProvider.js +29 -0
  221. package/lib-module/ViewportProvider/index.js +3 -0
  222. package/lib-module/ViewportProvider/useViewport.js +3 -0
  223. package/lib-module/ViewportProvider/useViewportListener.js +46 -0
  224. package/lib-module/index.js +48 -0
  225. package/lib-module/utils/a11y/index.js +2 -0
  226. package/lib-module/utils/a11y/semantics.js +157 -0
  227. package/lib-module/utils/a11y/textSize.js +36 -0
  228. package/lib-module/utils/animation/index.js +2 -0
  229. package/lib-module/utils/animation/useVerticalExpandAnimation.js +88 -0
  230. package/lib-module/utils/children.js +119 -0
  231. package/lib-module/utils/containUniqueFields.js +26 -0
  232. package/lib-module/utils/index.js +16 -0
  233. package/lib-module/utils/info/index.js +7 -0
  234. package/lib-module/utils/info/platform/index.js +11 -0
  235. package/lib-module/utils/info/platform/platform.android.js +1 -0
  236. package/lib-module/utils/info/platform/platform.ios.js +1 -0
  237. package/lib-module/utils/info/platform/platform.js +1 -0
  238. package/lib-module/utils/info/platform/platform.native.js +4 -0
  239. package/lib-module/utils/info/versions.js +5 -0
  240. package/lib-module/utils/input.js +179 -0
  241. package/lib-module/utils/pressability.js +111 -0
  242. package/lib-module/utils/props/a11yProps.js +140 -0
  243. package/lib-module/utils/props/clickProps.js +26 -0
  244. package/lib-module/utils/props/componentPropType.js +63 -0
  245. package/lib-module/utils/props/copyPropTypes.js +2 -0
  246. package/lib-module/utils/props/getPropSelector.js +9 -0
  247. package/lib-module/utils/props/handlerProps.js +65 -0
  248. package/lib-module/utils/props/hrefAttrsProp.js +33 -0
  249. package/lib-module/utils/props/index.js +19 -0
  250. package/lib-module/utils/props/inputSupportsProps.js +62 -0
  251. package/lib-module/utils/props/linkProps.js +44 -0
  252. package/lib-module/utils/props/paddingProp.js +9 -0
  253. package/lib-module/utils/props/pressProps.js +42 -0
  254. package/lib-module/utils/props/rectProp.js +9 -0
  255. package/lib-module/utils/props/responsiveProps.js +30 -0
  256. package/lib-module/utils/props/selectSystemProps.js +24 -0
  257. package/lib-module/utils/props/spacingProps.js +56 -0
  258. package/lib-module/utils/props/textInputProps.js +201 -0
  259. package/lib-module/utils/props/textProps.js +59 -0
  260. package/lib-module/utils/props/tokens.js +133 -0
  261. package/lib-module/utils/props/variantProp.js +18 -0
  262. package/lib-module/utils/props/viewProps.js +22 -0
  263. package/lib-module/utils/ssr.js +38 -0
  264. package/lib-module/utils/useCopy.js +44 -0
  265. package/lib-module/utils/useHash.js +45 -0
  266. package/lib-module/utils/useHash.native.js +7 -0
  267. package/lib-module/utils/useResponsiveProp.js +47 -0
  268. package/lib-module/utils/useSpacingScale.js +125 -0
  269. package/lib-module/utils/useUniqueId.js +13 -0
  270. package/lib-module/utils/withLinkRouter.js +83 -0
  271. package/package.json +3 -3
  272. package/src/Card/Card.jsx +6 -4
  273. package/src/Checkbox/Checkbox.jsx +3 -1
  274. package/src/ExpandCollapse/Panel.jsx +10 -20
  275. package/src/Link/InlinePressable.jsx +5 -2
  276. package/src/Link/LinkBase.jsx +4 -1
  277. package/src/List/List.jsx +6 -2
  278. package/src/Tabs/Tabs.jsx +24 -2
  279. package/src/Tabs/TabsItem.jsx +6 -5
  280. package/src/TextInput/TextInput.jsx +1 -8
  281. package/src/TextInput/TextInputBase.jsx +11 -1
  282. package/src/TextInput/propTypes.js +3 -7
  283. package/src/utils/animation/useVerticalExpandAnimation.js +47 -13
  284. package/src/utils/props/textInputProps.js +7 -1
@@ -1,6 +1,7 @@
1
1
  import React, { forwardRef, useState } from 'react'
2
- import { Animated, Platform, StyleSheet, View } from 'react-native'
2
+ import { Animated, Platform, View } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
+ import ABBPropTypes from 'airbnb-prop-types'
4
5
 
5
6
  import ExpandCollapseControl from './Control'
6
7
  import { useThemeTokens } from '../ThemeProvider'
@@ -51,6 +52,7 @@ const ExpandCollapsePanel = forwardRef(
51
52
  children,
52
53
  tokens,
53
54
  variant,
55
+ controlRef,
54
56
  ...rest
55
57
  },
56
58
  ref
@@ -86,13 +88,6 @@ const ExpandCollapsePanel = forwardRef(
86
88
  tokens: themeTokens
87
89
  })
88
90
 
89
- // on web we can hide the contents until we have the container measured and avoid occasional jitter
90
- // this won't work on native platforms
91
- const overflowContainerStyles =
92
- containerHeight === null && Platform.OS === 'web'
93
- ? { height: 0, visibility: 'hidden' }
94
- : undefined
95
-
96
91
  const focusabilityProps = isExpanded ? {} : a11yProps.nonFocusableProps
97
92
 
98
93
  return (
@@ -102,14 +97,11 @@ const ExpandCollapsePanel = forwardRef(
102
97
  isExpanded={isExpanded}
103
98
  tokens={controlTokens}
104
99
  onPress={handleControlPress}
100
+ ref={controlRef}
105
101
  >
106
102
  {control}
107
103
  </ExpandCollapseControl>
108
- <Animated.View
109
- ref={animatedRef}
110
- style={[overflowContainerStyles, animatedStyles, staticStyles.itemsContainer]}
111
- {...focusabilityProps}
112
- >
104
+ <Animated.View ref={animatedRef} style={animatedStyles} {...focusabilityProps}>
113
105
  <View onLayout={onContainerLayout}>
114
106
  <View style={selectContainerStyles(themeTokens)}>{children}</View>
115
107
  </View>
@@ -153,13 +145,11 @@ ExpandCollapsePanel.propTypes = {
153
145
  /**
154
146
  * Optional theme token overrides that may be passed to the ExpandCollapseControl element.
155
147
  */
156
- controlTokens: getTokensPropType('ExpandCollapseControl')
148
+ controlTokens: getTokensPropType('ExpandCollapseControl'),
149
+ /**
150
+ * An optional ref to be attached to the control
151
+ */
152
+ controlRef: ABBPropTypes.ref()
157
153
  }
158
154
 
159
- const staticStyles = StyleSheet.create({
160
- itemsContainer: {
161
- overflow: 'hidden'
162
- }
163
- })
164
-
165
155
  export default ExpandCollapsePanel
@@ -15,11 +15,11 @@ import { Pressable, StyleSheet } from 'react-native'
15
15
  */
16
16
  // React Native exports prop Types but not propTypes, use JSDoc types here rather than duplicate RN
17
17
  // eslint-disable-next-line react/prop-types
18
- const InlinePressable = forwardRef(({ children, style, ...props }, ref) => (
18
+ const InlinePressable = forwardRef(({ children, style, inline = false, ...props }, ref) => (
19
19
  <Pressable
20
20
  ref={ref}
21
21
  style={(pressState) => [
22
- staticStyles.inline,
22
+ staticStyles[inline ? 'inline' : 'inlineFlex'],
23
23
  typeof style === 'function' ? style(pressState) : style
24
24
  ]}
25
25
  {...props}
@@ -32,6 +32,9 @@ InlinePressable.displayName = 'InlinePressable'
32
32
  const staticStyles = StyleSheet.create({
33
33
  inline: {
34
34
  // Stop Pressable defaulting to (block) flex
35
+ display: 'inline'
36
+ },
37
+ inlineFlex: {
35
38
  display: 'inline-flex'
36
39
  }
37
40
  })
@@ -132,18 +132,21 @@ const LinkBase = forwardRef(
132
132
  const resolveLinkTokens = (pressState) =>
133
133
  resolvePressableTokens(tokens, pressState, { iconPosition })
134
134
 
135
+ const defaultThemeTokens = resolveLinkTokens({})
136
+ const hasIcon = Boolean(icon || defaultThemeTokens.icon)
137
+
135
138
  // On web, this makes focus rings wrap only the link, not the entire block
136
139
  const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft
137
140
 
138
141
  return (
139
142
  <InlinePressable
140
143
  {...selectedProps}
144
+ inline={hasIcon} // assuming links without icons should be inline (even if they are long)
141
145
  ref={ref}
142
146
  style={(linkState) => {
143
147
  const themeTokens = resolveLinkTokens(linkState)
144
148
  const outerBorderStyles = selectOuterBorderStyles(themeTokens)
145
149
  const decorationStyles = selectDecorationStyles(themeTokens)
146
- const hasIcon = Boolean(icon || themeTokens.icon)
147
150
  return [
148
151
  outerBorderStyles,
149
152
  blockLeftStyle,
package/src/List/List.jsx CHANGED
@@ -30,12 +30,16 @@ const List = forwardRef(
30
30
  ref
31
31
  ) => {
32
32
  const items = Children.map(children, (child, index) => {
33
- if (child.type.name === ListItem.name) {
33
+ // Pass ListItem-specific props to children (by name so teams can add their own ListItems)
34
+ const isListItem = (componentName) => Boolean(componentName === ListItem.displayName)
35
+
36
+ if (isListItem(child?.type?.displayName) || isListItem(child?.type?.name)) {
34
37
  return cloneElement(child, {
35
38
  showDivider,
36
39
  isLastItem: index + 1 === Children.count(children),
37
40
  tokens,
38
- variant
41
+ variant,
42
+ ...child.props
39
43
  })
40
44
  }
41
45
  return child
package/src/Tabs/Tabs.jsx CHANGED
@@ -23,9 +23,24 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
23
23
 
24
24
  const { selectHorizontalScrollTokens, useItemPositions } = horizontalScrollUtils
25
25
 
26
+ const getDefaultTabItemAccessibilityRole = (parentRole) => {
27
+ switch (parentRole) {
28
+ case 'tablist':
29
+ return 'tab'
30
+ case 'navigation':
31
+ return 'link'
32
+ default:
33
+ return undefined
34
+ }
35
+ }
36
+
26
37
  /**
27
38
  * Tabs renders a horizontally-scrolling menu of selectable buttons which may link
28
39
  * to a page or control what content is displayed on this page.
40
+ *
41
+ * If you are using Tabs to navigate to a new page (web-only) you should pass
42
+ * `navigation`as the `accessibilityRole` to te Tabs component, this will cause
43
+ * TabItems to default to a role of link and obtain aria-current behaviour.
29
44
  */
30
45
  const Tabs = forwardRef(
31
46
  (
@@ -61,6 +76,11 @@ const Tabs = forwardRef(
61
76
  isPositioningReady
62
77
  )
63
78
 
79
+ const restProps = selectProps(rest)
80
+ const parentAccessibilityRole = restProps.accessibilityRole ?? 'tablist'
81
+ const defaultTabItemAccessibiltyRole =
82
+ getDefaultTabItemAccessibilityRole(parentAccessibilityRole)
83
+
64
84
  return (
65
85
  <HorizontalScroll
66
86
  ref={ref}
@@ -68,8 +88,8 @@ const Tabs = forwardRef(
68
88
  itemPositions={itemPositions}
69
89
  tokens={selectHorizontalScrollTokens(themeTokens)}
70
90
  scrollButtonTokens={scrollButtonTokens}
71
- accessibilityRole="tablist"
72
- {...selectProps(rest)}
91
+ accessibilityRole={parentAccessibilityRole}
92
+ {...restProps}
73
93
  >
74
94
  <StackView space={space} direction="row">
75
95
  {items.map(
@@ -78,6 +98,7 @@ const Tabs = forwardRef(
78
98
  href,
79
99
  label,
80
100
  id,
101
+ accessibilityRole = defaultTabItemAccessibiltyRole,
81
102
  ref: itemRef,
82
103
  LinkRouter: ItemLinkRouter = LinkRouter,
83
104
  linkRouterProps: itemLinkRouterProps
@@ -98,6 +119,7 @@ const Tabs = forwardRef(
98
119
  selected={isSelected}
99
120
  itemPositions={itemPositions}
100
121
  index={index}
122
+ accessibilityRole={accessibilityRole}
101
123
  LinkRouter={ItemLinkRouter}
102
124
  linkRouterProps={{ ...linkRouterProps, ...itemLinkRouterProps }}
103
125
  >
@@ -86,11 +86,11 @@ const TabsItem = forwardRef(
86
86
  itemPositions,
87
87
  index,
88
88
  children,
89
- accessibilityRole = href ? 'link' : 'tab',
90
- accessibilityState = Platform.OS === 'web' && accessibilityRole === 'link'
91
- ? // Web links can't be aria-selected but can be aria-current
92
- { current: selected ? 'page' : false }
93
- : { selected },
89
+ accessibilityRole,
90
+ accessibilityCurrent = Platform.OS === 'web' && accessibilityRole === 'link' && selected
91
+ ? 'page'
92
+ : undefined,
93
+ accessibilityState = accessibilityRole === 'tab' ? { selected } : undefined,
94
94
  ...rawRest
95
95
  },
96
96
  ref
@@ -121,6 +121,7 @@ const TabsItem = forwardRef(
121
121
 
122
122
  const selectedProps = selectProps({
123
123
  accessibilityRole,
124
+ accessibilityCurrent,
124
125
  accessibilityState,
125
126
  ...rest
126
127
  })
@@ -1,5 +1,4 @@
1
1
  import React, { forwardRef } from 'react'
2
- import { Platform } from 'react-native'
3
2
  import {
4
3
  a11yProps,
5
4
  focusHandlerProps,
@@ -42,13 +41,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
42
41
  * supported props and <a href="https://reactnative.dev/docs/textinput" target="_blank">React Native Web documentation</a> for
43
42
  * their implementation on the web.
44
43
  */
45
- const TextInput = forwardRef(({ tokens, variant = {}, pattern, ...rest }, ref) => {
46
- React.useEffect(() => {
47
- if (Platform.OS === 'web' && pattern && ref.current) {
48
- // eslint-disable-next-line no-param-reassign
49
- ref.current.pattern = pattern
50
- }
51
- }, [ref, pattern])
44
+ const TextInput = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
52
45
  const { supportsProps, ...selectedProps } = selectProps(rest)
53
46
 
54
47
  const inputProps = {
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useState } from 'react'
1
+ import React, { forwardRef, useEffect, useState } from 'react'
2
2
  import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
@@ -128,6 +128,7 @@ const TextInputBase = forwardRef(
128
128
  onBlur,
129
129
  onMouseOver,
130
130
  onMouseOut,
131
+ pattern,
131
132
  tokens,
132
133
  variant = {},
133
134
  ...rest
@@ -161,6 +162,15 @@ const TextInputBase = forwardRef(
161
162
  readOnly
162
163
  })
163
164
 
165
+ const element = ref?.current
166
+ useEffect(() => {
167
+ if (Platform.OS === 'web' && pattern && element) {
168
+ // React Native Web doesn't support `pattern`, so we have to attach it via a ref,
169
+ // which a `pattern` user must provide anyway to call .checkValidity() on the element.
170
+ element.pattern = pattern
171
+ }
172
+ }, [element, pattern])
173
+
164
174
  const handleChangeText = (event) => {
165
175
  const text = event.nativeEvent?.text || event.target?.value
166
176
  setValue(text, event)
@@ -1,6 +1,7 @@
1
1
  import PropTypes from 'prop-types'
2
- import { Platform } from 'react-native'
3
2
 
3
+ // These are prop types specific to UDS TextInput; see also ../utils/props/textInputProps
4
+ // for generic React Native props and HTML input attrs that are passed through.
4
5
  const textInputPropTypes = {
5
6
  /**
6
7
  * If the input's state is to be controlled by a parent component, use this prop
@@ -24,12 +25,7 @@ const textInputPropTypes = {
24
25
  * Use to react upon input's value changes. Required when the `value` prop is set.
25
26
  * Will receive the input's value as an argument.
26
27
  */
27
- onChange: PropTypes.func,
28
- ...Platform.select({
29
- web: {
30
- pattern: PropTypes.string
31
- }
32
- })
28
+ onChange: PropTypes.func
33
29
  }
34
30
 
35
31
  export default textInputPropTypes
@@ -1,21 +1,27 @@
1
- import { useEffect, useRef, useState } from 'react'
1
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react'
2
2
  import { Animated, Easing, Platform } from 'react-native'
3
3
 
4
4
  // TODO: systematise animations
5
5
  // https://github.com/telus/universal-design-system/issues/487
6
6
  function useVerticalExpandAnimation({ containerHeight, isExpanded, tokens }) {
7
7
  const [isAnimating, setIsAnimating] = useState(false)
8
+ const [wasExpanded, setWasExpanded] = useState(isExpanded)
9
+ const expandStateChanged = isExpanded !== wasExpanded
8
10
 
9
11
  const expandAnimatedValue = useRef(new Animated.Value(0)).current
10
12
  const elementRef = useRef(null)
11
13
 
12
14
  const { expandDuration, collapseDuration } = tokens
13
15
 
14
- // Treat as animating from when expanded state changes, until animation completes
15
- useEffect(() => setIsAnimating(true), [isExpanded])
16
+ useLayoutEffect(() => {
17
+ if (expandStateChanged) {
18
+ setIsAnimating(true)
19
+ setWasExpanded(isExpanded)
20
+ }
21
+ }, [expandStateChanged, isExpanded])
16
22
 
17
23
  useEffect(() => {
18
- const onComplete = () => !isExpanded && setIsAnimating(false)
24
+ const onComplete = () => setIsAnimating(false)
19
25
 
20
26
  if (Platform.OS === 'web') {
21
27
  if (!elementRef.current) return () => {}
@@ -39,19 +45,47 @@ function useVerticalExpandAnimation({ containerHeight, isExpanded, tokens }) {
39
45
  return () => animation.stop()
40
46
  }, [isExpanded, expandAnimatedValue, containerHeight, expandDuration, collapseDuration])
41
47
 
42
- // Without `visibility: 'hidden', descendents are focusable on web even when collapsed
43
- const containerStyles = !isExpanded && !isAnimating ? { visibility: 'hidden' } : {}
48
+ const containerStyles = {}
44
49
 
45
- // don't visually collapse the container until we have it measured
46
- if (containerHeight !== null) {
47
- if (Platform.OS === 'web') {
48
- const transitionDuration = isExpanded ? expandDuration : collapseDuration
49
- containerStyles.transition = `height ${transitionDuration}ms ease-in-out`
50
+ if (isAnimating || expandStateChanged) containerStyles.overflow = 'hidden'
50
51
 
51
- containerStyles.height = isExpanded ? containerHeight : 0
52
+ if (!isExpanded && !isAnimating && !expandStateChanged) {
53
+ if (Platform.OS === 'web') {
54
+ // Without `visibility: 'hidden', descendents are focusable on web even when collapsed.
55
+ containerStyles.visibility = 'hidden'
52
56
  } else {
53
- containerStyles.height = expandAnimatedValue
57
+ // There's no `visibility: hidden` in React Native, and display: none causes a flicker on expand.
58
+ // Without some form of hiding, some children leak through even when closed e.g. `List.Item` bullets.
59
+ containerStyles.opacity = 0
60
+ }
61
+ }
62
+
63
+ // don't visually collapse the container until we have it measured
64
+ if (containerHeight === null) {
65
+ if (Platform.OS === 'web') {
66
+ // on web we can hide the contents until we have the container measured and avoid occasional jitter
67
+ // this won't work on native platforms
68
+ containerStyles.height = 0
69
+ containerStyles.visibility = 'hidden'
54
70
  }
71
+ } else if (Platform.OS === 'web') {
72
+ const transitionDuration = isExpanded ? expandDuration : collapseDuration
73
+ containerStyles.transition = `height ${transitionDuration}ms ease-in-out`
74
+
75
+ containerStyles.height = isExpanded ? containerHeight : 0
76
+ } else if (
77
+ Platform.OS === 'ios' &&
78
+ isExpanded &&
79
+ !isAnimating &&
80
+ !expandStateChanged &&
81
+ typeof containerHeight === 'number'
82
+ ) {
83
+ // iOS reflows text while the height animation is in progress. Sometimes, it hits a timing bug where
84
+ // it fails to reflow properly after the last animation frame, leaving the last line of text not visible.
85
+ // Force reflow after animation completes with a static height non-identical to the last animated height.
86
+ containerStyles.height = containerHeight + 1
87
+ } else {
88
+ containerStyles.height = expandAnimatedValue
55
89
  }
56
90
 
57
91
  return [containerStyles, elementRef]
@@ -121,7 +121,13 @@ const crossPlatform = {
121
121
  const webOnly = {
122
122
  disabled: PropTypes.bool,
123
123
  dir: PropTypes.oneOf(['auto', 'ltr', 'rtl']),
124
- lang: PropTypes.string
124
+ lang: PropTypes.string,
125
+ /**
126
+ * Sets the HTML input `pattern` attr. Not supported by React Native Web, but is supported by UDS.
127
+ * Must also pass in a ref and check validity by calling the HTML element's checkValidity method:
128
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/checkValidity
129
+ */
130
+ pattern: PropTypes.string
125
131
  }
126
132
 
127
133
  /**