@telus-uds/components-base 3.28.2 → 3.29.1
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.
- package/CHANGELOG.md +27 -1
- package/jest.config.cjs +1 -0
- package/lib/cjs/ActionCard/ActionCard.js +4 -4
- package/lib/cjs/ActivityIndicator/Dots.native.js +1 -2
- package/lib/cjs/ActivityIndicator/Spinner.native.js +1 -2
- package/lib/cjs/Box/Box.js +1 -2
- package/lib/cjs/Button/Button.js +1 -2
- package/lib/cjs/Button/ButtonBase.js +8 -8
- package/lib/cjs/Button/ButtonDropdown.js +1 -2
- package/lib/cjs/Button/ButtonGroup.js +1 -2
- package/lib/cjs/Button/ButtonLink.js +1 -2
- package/lib/cjs/Card/Card.js +1 -2
- package/lib/cjs/Card/CardBase.js +12 -0
- package/lib/cjs/Card/PressableCardBase.js +1 -2
- package/lib/cjs/Card/index.js +1 -2
- package/lib/cjs/CardGroup/CardGroup.js +1 -2
- package/lib/cjs/Carousel/Carousel.js +6 -6
- package/lib/cjs/CheckboxCard/CheckboxCard.js +1 -2
- package/lib/cjs/CheckboxCardGroup/CheckboxCardGroup.js +1 -2
- package/lib/cjs/ColourToggle/ColourBubble.js +17 -3
- package/lib/cjs/ColourToggle/ColourToggle.js +8 -2
- package/lib/cjs/ExpandCollapse/Control.js +17 -3
- package/lib/cjs/ExpandCollapse/Panel.js +6 -0
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
- package/lib/cjs/HorizontalScroll/index.js +1 -2
- package/lib/cjs/Icon/IconText.js +1 -2
- package/lib/cjs/Icon/index.js +1 -2
- package/lib/cjs/InputSupports/InputSupports.js +1 -2
- package/lib/cjs/InputSupports/useInputSupports.js +1 -3
- package/lib/cjs/Link/ChevronLink.js +1 -0
- package/lib/cjs/Link/LinkBase.js +29 -13
- package/lib/cjs/Link/MobileIconTextContent.js +155 -0
- package/lib/cjs/Listbox/Listbox.js +1 -2
- package/lib/cjs/Modal/Modal.js +1 -1
- package/lib/cjs/Pagination/PageButton.js +1 -2
- package/lib/cjs/Pagination/Pagination.js +1 -2
- package/lib/cjs/QuickLinks/QuickLinks.js +7 -0
- package/lib/cjs/Radio/Radio.js +1 -2
- package/lib/cjs/RadioCard/RadioCard.js +1 -2
- package/lib/cjs/RadioCard/RadioCardGroup.js +1 -2
- package/lib/cjs/Shortcuts/Shortcuts.js +1 -2
- package/lib/cjs/SideNav/SideNav.js +1 -2
- package/lib/cjs/StackView/StackWrapBox.js +9 -1
- package/lib/cjs/StackView/StackWrapGap.js +3 -1
- package/lib/cjs/StackView/getStackedContent.js +21 -12
- package/lib/cjs/TabBar/TabBar.js +7 -2
- package/lib/cjs/Tabs/Tabs.js +1 -2
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -2
- package/lib/cjs/index.js +1 -2
- package/lib/cjs/utils/index.js +1 -2
- package/lib/esm/ActionCard/ActionCard.js +4 -4
- package/lib/esm/Button/ButtonBase.js +8 -8
- package/lib/esm/Card/CardBase.js +12 -0
- package/lib/esm/Carousel/Carousel.js +6 -6
- package/lib/esm/ColourToggle/ColourBubble.js +17 -3
- package/lib/esm/ColourToggle/ColourToggle.js +8 -2
- package/lib/esm/ExpandCollapse/Control.js +17 -3
- package/lib/esm/ExpandCollapse/Panel.js +6 -0
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
- package/lib/esm/InputSupports/InputSupports.js +1 -2
- package/lib/esm/InputSupports/useInputSupports.js +1 -3
- package/lib/esm/Link/ChevronLink.js +1 -0
- package/lib/esm/Link/LinkBase.js +29 -13
- package/lib/esm/Link/MobileIconTextContent.js +147 -0
- package/lib/esm/Modal/Modal.js +1 -1
- package/lib/esm/QuickLinks/QuickLinks.js +7 -0
- package/lib/esm/StackView/StackWrapBox.js +9 -1
- package/lib/esm/StackView/StackWrapGap.js +3 -1
- package/lib/esm/StackView/getStackedContent.js +20 -10
- package/lib/esm/TabBar/TabBar.js +7 -2
- package/lib/esm/Tooltip/Tooltip.native.js +2 -2
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/src/Card/CardBase.jsx +12 -0
- package/src/ColourToggle/ColourBubble.jsx +18 -3
- package/src/ColourToggle/ColourToggle.jsx +7 -2
- package/src/ExpandCollapse/Control.jsx +24 -4
- package/src/ExpandCollapse/Panel.jsx +6 -0
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +23 -3
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +14 -2
- package/src/InputSupports/InputSupports.jsx +1 -6
- package/src/InputSupports/useInputSupports.js +1 -1
- package/src/Link/ChevronLink.jsx +1 -0
- package/src/Link/LinkBase.jsx +47 -20
- package/src/Link/MobileIconTextContent.jsx +129 -0
- package/src/Modal/Modal.jsx +1 -1
- package/src/QuickLinks/QuickLinks.jsx +8 -0
- package/src/StackView/StackWrapBox.jsx +13 -1
- package/src/StackView/StackWrapGap.jsx +2 -1
- package/src/StackView/getStackedContent.jsx +22 -8
- package/src/TabBar/TabBar.jsx +21 -4
package/src/Link/ChevronLink.jsx
CHANGED
package/src/Link/LinkBase.jsx
CHANGED
|
@@ -16,6 +16,7 @@ import { resolvePressableTokens } from '../utils/pressability'
|
|
|
16
16
|
import { withLinkRouter } from '../utils'
|
|
17
17
|
|
|
18
18
|
import InlinePressable from './InlinePressable'
|
|
19
|
+
import MobileIconTextContent from './MobileIconTextContent'
|
|
19
20
|
import { applyTextStyles, applyOuterBorder, useTheme } from '../ThemeProvider'
|
|
20
21
|
import { IconText, iconComponentPropTypes } from '../Icon'
|
|
21
22
|
|
|
@@ -133,6 +134,7 @@ const LinkBase = React.forwardRef(
|
|
|
133
134
|
tokens = {},
|
|
134
135
|
children,
|
|
135
136
|
dataSet,
|
|
137
|
+
useMeasuredMobileIconLayout = false,
|
|
136
138
|
accessibilityRole = 'link',
|
|
137
139
|
...rawRest
|
|
138
140
|
},
|
|
@@ -171,12 +173,14 @@ const LinkBase = React.forwardRef(
|
|
|
171
173
|
const themeTokens = resolveLinkTokens(linkState)
|
|
172
174
|
const outerBorderStyles = selectOuterBorderStyles(themeTokens)
|
|
173
175
|
const decorationStyles = selectDecorationStyles(themeTokens)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
const shouldUseMeasuredMobileContent =
|
|
177
|
+
Platform.OS !== 'web' && useMeasuredMobileIconLayout
|
|
176
178
|
|
|
177
179
|
return [
|
|
178
180
|
outerBorderStyles,
|
|
179
|
-
|
|
181
|
+
shouldUseMeasuredMobileContent
|
|
182
|
+
? staticStyles.measuredMobileOuterBorderCompensation
|
|
183
|
+
: null,
|
|
180
184
|
blockLeftStyle,
|
|
181
185
|
decorationStyles,
|
|
182
186
|
hasIcon && staticStyles.rowContainer
|
|
@@ -196,28 +200,50 @@ const LinkBase = React.forwardRef(
|
|
|
196
200
|
|
|
197
201
|
const isTextOnlyLink = !IconComponent && !icon && accessibilityRole === 'link'
|
|
198
202
|
const adjustedIconSpace = Platform.OS !== 'web' && isTextOnlyLink ? 0 : iconSpace
|
|
203
|
+
const shouldUseMeasuredMobileContent =
|
|
204
|
+
Platform.OS !== 'web' && useMeasuredMobileIconLayout
|
|
205
|
+
const textBaselineStyle = shouldUseMeasuredMobileContent ? null : staticStyles.baseline
|
|
206
|
+
|
|
207
|
+
const linkTextContent = (
|
|
208
|
+
<Text
|
|
209
|
+
style={[
|
|
210
|
+
textStyles,
|
|
211
|
+
blockTextStyles,
|
|
212
|
+
textBaselineStyle,
|
|
213
|
+
staticStyles.bubblePointerEvents
|
|
214
|
+
]}
|
|
215
|
+
>
|
|
216
|
+
{typeof children === 'function' ? children(linkState) : children}
|
|
217
|
+
</Text>
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
const sharedIconProps = {
|
|
221
|
+
...iconProps,
|
|
222
|
+
tokens: iconTokens,
|
|
223
|
+
style: staticStyles.bubblePointerEvents
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (shouldUseMeasuredMobileContent) {
|
|
227
|
+
return (
|
|
228
|
+
<MobileIconTextContent
|
|
229
|
+
icon={IconComponent}
|
|
230
|
+
iconPosition={iconPosition}
|
|
231
|
+
space={adjustedIconSpace}
|
|
232
|
+
iconProps={sharedIconProps}
|
|
233
|
+
>
|
|
234
|
+
{linkTextContent}
|
|
235
|
+
</MobileIconTextContent>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
199
238
|
|
|
200
239
|
return (
|
|
201
240
|
<IconText
|
|
202
241
|
icon={IconComponent}
|
|
203
242
|
iconPosition={iconPosition}
|
|
204
243
|
space={adjustedIconSpace}
|
|
205
|
-
iconProps={
|
|
206
|
-
...iconProps,
|
|
207
|
-
tokens: iconTokens,
|
|
208
|
-
style: staticStyles.bubblePointerEvents
|
|
209
|
-
}}
|
|
244
|
+
iconProps={sharedIconProps}
|
|
210
245
|
>
|
|
211
|
-
|
|
212
|
-
style={[
|
|
213
|
-
textStyles,
|
|
214
|
-
blockTextStyles,
|
|
215
|
-
staticStyles.baseline,
|
|
216
|
-
staticStyles.bubblePointerEvents
|
|
217
|
-
]}
|
|
218
|
-
>
|
|
219
|
-
{typeof children === 'function' ? children(linkState) : children}
|
|
220
|
-
</Text>
|
|
246
|
+
{linkTextContent}
|
|
221
247
|
</IconText>
|
|
222
248
|
)
|
|
223
249
|
}}
|
|
@@ -280,11 +306,12 @@ const staticStyles = StyleSheet.create({
|
|
|
280
306
|
}
|
|
281
307
|
})
|
|
282
308
|
},
|
|
283
|
-
|
|
309
|
+
measuredMobileOuterBorderCompensation: {
|
|
284
310
|
...(Platform.OS !== 'web' && {
|
|
285
311
|
marginHorizontal: 2,
|
|
312
|
+
marginVertical: 2,
|
|
286
313
|
paddingHorizontal: Platform.OS === 'android' ? 2 : 0,
|
|
287
|
-
|
|
314
|
+
paddingVertical: Platform.OS === 'android' ? 2 : 0
|
|
288
315
|
})
|
|
289
316
|
}
|
|
290
317
|
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { Text, View, StyleSheet } from 'react-native'
|
|
4
|
+
|
|
5
|
+
import Icon, { iconComponentPropTypes } from '../Icon/Icon'
|
|
6
|
+
import { spacingProps } from '../utils'
|
|
7
|
+
|
|
8
|
+
const MobileIconTextContent = React.forwardRef(
|
|
9
|
+
({ space = 0, iconPosition = 'left', icon: IconComponent, iconProps = {}, children }, ref) => {
|
|
10
|
+
const [translateY, setTranslateY] = React.useState(0)
|
|
11
|
+
const latestTranslateYRef = React.useRef(0)
|
|
12
|
+
const layoutsRef = React.useRef({
|
|
13
|
+
container: null,
|
|
14
|
+
text: null,
|
|
15
|
+
icon: null
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const applyAlignment = React.useCallback(() => {
|
|
19
|
+
const { container, text, icon } = layoutsRef.current
|
|
20
|
+
|
|
21
|
+
if (!container || !icon || !icon.height) return
|
|
22
|
+
|
|
23
|
+
const targetY = text ? text.y + text.height / 2 : container.height / 2
|
|
24
|
+
const iconY = icon.y + icon.height / 2
|
|
25
|
+
const nextTranslateY = Math.round((targetY - iconY) * 100) / 100
|
|
26
|
+
|
|
27
|
+
if (!Number.isFinite(nextTranslateY)) return
|
|
28
|
+
if (Math.abs(nextTranslateY - latestTranslateYRef.current) < 0.5) return
|
|
29
|
+
|
|
30
|
+
latestTranslateYRef.current = nextTranslateY
|
|
31
|
+
setTranslateY(nextTranslateY)
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
const handleContainerLayout = React.useCallback(
|
|
35
|
+
({ nativeEvent: { layout } }) => {
|
|
36
|
+
layoutsRef.current.container = layout
|
|
37
|
+
applyAlignment()
|
|
38
|
+
},
|
|
39
|
+
[applyAlignment]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const handleTextLayout = React.useCallback(
|
|
43
|
+
({ nativeEvent: { layout } }) => {
|
|
44
|
+
layoutsRef.current.text = layout
|
|
45
|
+
applyAlignment()
|
|
46
|
+
},
|
|
47
|
+
[applyAlignment]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const handleIconLayout = React.useCallback(
|
|
51
|
+
({ nativeEvent: { layout } }) => {
|
|
52
|
+
layoutsRef.current.icon = layout
|
|
53
|
+
applyAlignment()
|
|
54
|
+
},
|
|
55
|
+
[applyAlignment]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const iconContent = IconComponent ? (
|
|
59
|
+
<Icon ref={ref} icon={IconComponent} scalesWithText {...iconProps} />
|
|
60
|
+
) : null
|
|
61
|
+
|
|
62
|
+
const iconWrapper = IconComponent ? (
|
|
63
|
+
<View
|
|
64
|
+
onLayout={handleIconLayout}
|
|
65
|
+
style={[staticStyles.iconContainer, { transform: [{ translateY }] }]}
|
|
66
|
+
>
|
|
67
|
+
{iconContent}
|
|
68
|
+
</View>
|
|
69
|
+
) : null
|
|
70
|
+
|
|
71
|
+
if (iconPosition === 'inline') {
|
|
72
|
+
return (
|
|
73
|
+
<Text onLayout={handleContainerLayout}>
|
|
74
|
+
<Text onLayout={handleTextLayout}>{children}</Text>{' '}
|
|
75
|
+
<View style={staticStyles.inlineIconContainer}>{iconWrapper}</View>
|
|
76
|
+
</Text>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const iconSpaceStyle = iconPosition === 'left' ? { marginRight: space } : { marginLeft: space }
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<View onLayout={handleContainerLayout} style={staticStyles.rowContainer}>
|
|
84
|
+
{iconPosition === 'left' && <View style={iconSpaceStyle}>{iconWrapper}</View>}
|
|
85
|
+
<View onLayout={handleTextLayout}>{children}</View>
|
|
86
|
+
{iconPosition === 'right' && <View style={iconSpaceStyle}>{iconWrapper}</View>}
|
|
87
|
+
</View>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
MobileIconTextContent.displayName = 'MobileIconTextContent'
|
|
93
|
+
|
|
94
|
+
MobileIconTextContent.propTypes = {
|
|
95
|
+
/**
|
|
96
|
+
* Amount of space between text and icon. Uses the theme spacing scale.
|
|
97
|
+
*/
|
|
98
|
+
space: spacingProps.types.spacingValue,
|
|
99
|
+
/**
|
|
100
|
+
* Position of the icon relative to text.
|
|
101
|
+
*/
|
|
102
|
+
iconPosition: PropTypes.oneOf(['left', 'right', 'inline']),
|
|
103
|
+
/**
|
|
104
|
+
* A valid UDS icon component imported from a UDS palette.
|
|
105
|
+
*/
|
|
106
|
+
icon: PropTypes.elementType,
|
|
107
|
+
/**
|
|
108
|
+
* Props passed to the icon component.
|
|
109
|
+
*/
|
|
110
|
+
iconProps: PropTypes.exact(iconComponentPropTypes),
|
|
111
|
+
/**
|
|
112
|
+
* Content rendered alongside the icon.
|
|
113
|
+
*/
|
|
114
|
+
children: PropTypes.node
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const staticStyles = StyleSheet.create({
|
|
118
|
+
rowContainer: {
|
|
119
|
+
flexDirection: 'row'
|
|
120
|
+
},
|
|
121
|
+
iconContainer: {
|
|
122
|
+
alignSelf: 'flex-start'
|
|
123
|
+
},
|
|
124
|
+
inlineIconContainer: {
|
|
125
|
+
position: 'absolute'
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
export default MobileIconTextContent
|
package/src/Modal/Modal.jsx
CHANGED
|
@@ -262,7 +262,7 @@ const Modal = React.forwardRef(
|
|
|
262
262
|
</View>
|
|
263
263
|
{/* when a modal becomes open its first focusable element is being automatically focused */}
|
|
264
264
|
{/* and we prefer the close button over backdrop */}
|
|
265
|
-
<TouchableWithoutFeedback onPress={isBackdropClickable
|
|
265
|
+
<TouchableWithoutFeedback onPress={isBackdropClickable ? handleClose : undefined}>
|
|
266
266
|
<View style={[staticStyles.backdrop, selectBackdropStyles(themeTokens)]} />
|
|
267
267
|
</TouchableWithoutFeedback>
|
|
268
268
|
</ScrollView>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
|
+
import { Platform } from 'react-native'
|
|
4
5
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
6
|
import { useViewport } from '../ViewportProvider'
|
|
6
7
|
import { getTokensPropType, variantProp } from '../utils'
|
|
8
|
+
import { tagsToRoles } from '../utils/a11y/semantics'
|
|
7
9
|
|
|
8
10
|
import List from '../List'
|
|
9
11
|
import StackWrap from '../StackView/StackWrap'
|
|
@@ -27,6 +29,11 @@ const QuickLinks = React.forwardRef(
|
|
|
27
29
|
}
|
|
28
30
|
)
|
|
29
31
|
|
|
32
|
+
const itemAccessibilityRole = Platform.select({
|
|
33
|
+
web: ['ul', 'ol'].includes(tag) ? tagsToRoles.li : undefined,
|
|
34
|
+
default: undefined
|
|
35
|
+
})
|
|
36
|
+
|
|
30
37
|
const content = (list && (
|
|
31
38
|
<List ref={ref} tokens={listTokens} showDivider={dividers} tag={tag} {...rest}>
|
|
32
39
|
{children}
|
|
@@ -37,6 +44,7 @@ const QuickLinks = React.forwardRef(
|
|
|
37
44
|
gap={stackGap}
|
|
38
45
|
tokens={{ justifyContent: stackJustify }}
|
|
39
46
|
tag={tag}
|
|
47
|
+
itemAccessibilityRole={itemAccessibilityRole}
|
|
40
48
|
{...rest}
|
|
41
49
|
>
|
|
42
50
|
{children}
|
|
@@ -58,6 +58,7 @@ const StackWrapBox = React.forwardRef(
|
|
|
58
58
|
variant,
|
|
59
59
|
tag,
|
|
60
60
|
accessibilityRole,
|
|
61
|
+
itemAccessibilityRole,
|
|
61
62
|
...rest
|
|
62
63
|
},
|
|
63
64
|
ref
|
|
@@ -76,7 +77,12 @@ const StackWrapBox = React.forwardRef(
|
|
|
76
77
|
const gapSize = useSpacingScale(gap)
|
|
77
78
|
const offsetStyle = { [offsetSides[direction]]: -1 * gapSize }
|
|
78
79
|
const boxProps = { [gapSides[direction]]: gap, [spaceSides[direction]]: space }
|
|
79
|
-
const content = getStackedContent(children, {
|
|
80
|
+
const content = getStackedContent(children, {
|
|
81
|
+
direction,
|
|
82
|
+
space: 0,
|
|
83
|
+
box: boxProps,
|
|
84
|
+
itemAccessibilityRole
|
|
85
|
+
})
|
|
80
86
|
|
|
81
87
|
return (
|
|
82
88
|
<View
|
|
@@ -119,6 +125,12 @@ StackWrapBox.propTypes = {
|
|
|
119
125
|
* is not defined, the accessibilityRole will default to "heading".
|
|
120
126
|
*/
|
|
121
127
|
tag: PropTypes.oneOf(layoutTags),
|
|
128
|
+
/**
|
|
129
|
+
* Optional accessibility role to apply to each item in the stack.
|
|
130
|
+
* On web, items are wrapped (or cloned) with this role, enabling correct list semantics
|
|
131
|
+
* when the container tag is "ul" or "ol".
|
|
132
|
+
*/
|
|
133
|
+
itemAccessibilityRole: PropTypes.string,
|
|
122
134
|
/**
|
|
123
135
|
* A StackWrap may take any children, but will have no effect if it is only passed one child or is passed children
|
|
124
136
|
* wrapped in a component. If necessary, children may be wrapped in one React Fragment.
|
|
@@ -37,6 +37,7 @@ const StackWrapGap = React.forwardRef(
|
|
|
37
37
|
children,
|
|
38
38
|
tag,
|
|
39
39
|
accessibilityRole,
|
|
40
|
+
itemAccessibilityRole,
|
|
40
41
|
...rest
|
|
41
42
|
},
|
|
42
43
|
ref
|
|
@@ -54,7 +55,7 @@ const StackWrapGap = React.forwardRef(
|
|
|
54
55
|
const size = useSpacingScale(space)
|
|
55
56
|
const gapStyle = { gap: size }
|
|
56
57
|
|
|
57
|
-
const content = getStackedContent(children, { direction, space: 0 })
|
|
58
|
+
const content = getStackedContent(children, { direction, space: 0, itemAccessibilityRole })
|
|
58
59
|
|
|
59
60
|
return (
|
|
60
61
|
<View
|
|
@@ -32,7 +32,7 @@ import Spacer from '../Spacer'
|
|
|
32
32
|
*/
|
|
33
33
|
const getStackedContent = (
|
|
34
34
|
children,
|
|
35
|
-
{ divider, space, direction = 'column', box, preserveFragments = false }
|
|
35
|
+
{ divider, space, direction = 'column', box, preserveFragments = false, itemAccessibilityRole }
|
|
36
36
|
) => {
|
|
37
37
|
const boxProps = box && typeof box === 'object' ? box : { space }
|
|
38
38
|
const dividerProps = divider && typeof divider === 'object' ? divider : {}
|
|
@@ -42,15 +42,29 @@ const getStackedContent = (
|
|
|
42
42
|
const validChildren = React.Children.toArray(topLevelChildren).filter(Boolean)
|
|
43
43
|
const content = validChildren.reduce((newChildren, child, index) => {
|
|
44
44
|
const boxID = `Stack-Box-${index}`
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
let item
|
|
47
|
+
if (box) {
|
|
46
48
|
// If wrapped in Box, that Box needs a key.
|
|
47
49
|
// If possible, use an existing content key; use an index-based key only if necessary.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
item = (
|
|
51
|
+
<Box
|
|
52
|
+
{...boxProps}
|
|
53
|
+
accessibilityRole={itemAccessibilityRole}
|
|
54
|
+
key={child.key || boxID}
|
|
55
|
+
testID={boxID}
|
|
56
|
+
>
|
|
57
|
+
{child}
|
|
58
|
+
</Box>
|
|
59
|
+
)
|
|
60
|
+
} else if (itemAccessibilityRole) {
|
|
61
|
+
item = React.cloneElement(child, {
|
|
62
|
+
accessibilityRole: itemAccessibilityRole,
|
|
63
|
+
key: child.key || boxID
|
|
64
|
+
})
|
|
65
|
+
} else {
|
|
66
|
+
item = child
|
|
67
|
+
}
|
|
54
68
|
if (!index || (!space && !divider)) return [...newChildren, item]
|
|
55
69
|
|
|
56
70
|
const testID = `Stack-${divider ? 'Divider' : 'Spacer'}-${index}`
|
package/src/TabBar/TabBar.jsx
CHANGED
|
@@ -42,12 +42,24 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
42
42
|
* items={items}
|
|
43
43
|
* initiallySelectedItem="1"
|
|
44
44
|
* onChange={(itemId) => console.log(itemId)}
|
|
45
|
+
* accessibilityLabel="Main navigation"
|
|
45
46
|
* />
|
|
46
47
|
* )
|
|
47
48
|
*/
|
|
48
49
|
|
|
49
50
|
const TabBar = React.forwardRef(
|
|
50
|
-
(
|
|
51
|
+
(
|
|
52
|
+
{
|
|
53
|
+
items = [],
|
|
54
|
+
initiallySelectedItem = '0',
|
|
55
|
+
onChange,
|
|
56
|
+
variant,
|
|
57
|
+
tokens,
|
|
58
|
+
accessibilityLabel,
|
|
59
|
+
...rest
|
|
60
|
+
},
|
|
61
|
+
ref
|
|
62
|
+
) => {
|
|
51
63
|
const [isSelected, setIsSelected] = React.useState(initiallySelectedItem)
|
|
52
64
|
const themeTokens = useThemeTokens('TabBar', tokens, variant)
|
|
53
65
|
|
|
@@ -62,7 +74,11 @@ const TabBar = React.forwardRef(
|
|
|
62
74
|
style={[styles.tabBar, selectTabBarContainerStyles(themeTokens)]}
|
|
63
75
|
{...selectProps(rest)}
|
|
64
76
|
>
|
|
65
|
-
<View
|
|
77
|
+
<View
|
|
78
|
+
style={[styles.tabBarItem, selectTabBarItemContainerStyles(themeTokens)]}
|
|
79
|
+
accessibilityRole="tablist"
|
|
80
|
+
accessibilityLabel={accessibilityLabel}
|
|
81
|
+
>
|
|
66
82
|
{items.map((item, index) => (
|
|
67
83
|
<TabBarItem
|
|
68
84
|
key={item.id}
|
|
@@ -73,7 +89,6 @@ const TabBar = React.forwardRef(
|
|
|
73
89
|
iconActive={item.iconActive}
|
|
74
90
|
onPress={() => handlePress(item.id)}
|
|
75
91
|
id={`tab-item-${index}`}
|
|
76
|
-
accessibilityRole="tablist"
|
|
77
92
|
tokens={item.tokens}
|
|
78
93
|
/>
|
|
79
94
|
))}
|
|
@@ -105,7 +120,9 @@ TabBar.propTypes = {
|
|
|
105
120
|
/** Variant of TabBar for styling purposes. */
|
|
106
121
|
variant: variantProp.propType,
|
|
107
122
|
/** Tokens for theming and styling. */
|
|
108
|
-
tokens: getTokensPropType('TabBar')
|
|
123
|
+
tokens: getTokensPropType('TabBar'),
|
|
124
|
+
/** Accessible label for the tab bar navigation region, used by screen readers to identify the tablist. */
|
|
125
|
+
accessibilityLabel: PropTypes.string
|
|
109
126
|
}
|
|
110
127
|
|
|
111
128
|
const styles = StyleSheet.create({
|