@telus-uds/components-base 1.14.3 → 1.15.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.
- package/CHANGELOG.md +15 -2
- package/__tests17__/A11yText/A11yText.test.jsx +34 -0
- package/__tests17__/ActivityIndicator/ActivityIndicator.test.jsx +68 -0
- package/__tests17__/ActivityIndicator/__snapshots__/ActivityIndicator.test.jsx.snap +299 -0
- package/__tests17__/Box/Box.test.jsx +111 -0
- package/__tests17__/Button/Button.test.jsx +86 -0
- package/__tests17__/Button/ButtonBase.test.jsx +82 -0
- package/__tests17__/Button/ButtonGroup.test.jsx +347 -0
- package/__tests17__/Button/ButtonLink.test.jsx +61 -0
- package/__tests17__/Card/Card.test.jsx +63 -0
- package/__tests17__/Carousel/Carousel.test.jsx +128 -0
- package/__tests17__/Carousel/CarouselTabs.test.jsx +142 -0
- package/__tests17__/Checkbox/Checkbox.test.jsx +94 -0
- package/__tests17__/Checkbox/CheckboxGroup.test.jsx +246 -0
- package/__tests17__/Divider/Divider.test.jsx +91 -0
- package/__tests17__/ExpandCollapse/ExpandCollapse.test.jsx +109 -0
- package/__tests17__/Feedback/Feedback.test.jsx +42 -0
- package/__tests17__/FlexGrid/Col.test.jsx +261 -0
- package/__tests17__/FlexGrid/FlexGrid.test.jsx +136 -0
- package/__tests17__/FlexGrid/Row.test.jsx +273 -0
- package/__tests17__/HorizontalScroll/HorizontalScroll.test.jsx +165 -0
- package/__tests17__/Icon/Icon.test.jsx +61 -0
- package/__tests17__/IconButton/IconButton.test.jsx +52 -0
- package/__tests17__/InputLabel/InputLabel.test.jsx +28 -0
- package/__tests17__/InputLabel/__snapshots__/InputLabel.test.jsx.snap +3 -0
- package/__tests17__/InputSupports/InputSupports.test.jsx +60 -0
- package/__tests17__/Link/Link.test.jsx +63 -0
- package/__tests17__/Link/TextButton.test.jsx +35 -0
- package/__tests17__/List/List.test.jsx +82 -0
- package/__tests17__/Modal/Modal.test.jsx +47 -0
- package/__tests17__/Notification/Notification.test.jsx +20 -0
- package/__tests17__/Pagination/Pagination.test.jsx +160 -0
- package/__tests17__/Progress/Progress.test.jsx +79 -0
- package/__tests17__/Radio/Radio.test.jsx +87 -0
- package/__tests17__/Radio/RadioGroup.test.jsx +220 -0
- package/__tests17__/RadioCard/RadioCard.test.jsx +87 -0
- package/__tests17__/RadioCard/RadioCardGroup.test.jsx +246 -0
- package/__tests17__/Search/Search.test.jsx +87 -0
- package/__tests17__/Select/Select.test.jsx +94 -0
- package/__tests17__/SideNav/SideNav.test.jsx +110 -0
- package/__tests17__/Skeleton/Skeleton.test.jsx +61 -0
- package/__tests17__/SkipLink/SkipLink.test.jsx +61 -0
- package/__tests17__/Spacer/Spacer.test.jsx +63 -0
- package/__tests17__/StackView/StackView.test.jsx +211 -0
- package/__tests17__/StackView/StackWrap.test.jsx +47 -0
- package/__tests17__/StackView/getStackedContent.test.jsx +295 -0
- package/__tests17__/StepTracker/StepTracker.test.jsx +108 -0
- package/__tests17__/Tabs/Tabs.test.jsx +49 -0
- package/__tests17__/Tags/Tags.test.jsx +327 -0
- package/__tests17__/TextInput/TextArea.test.jsx +35 -0
- package/__tests17__/TextInput/TextInputBase.test.jsx +125 -0
- package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +80 -0
- package/__tests17__/ThemeProvider/useThemeTokens.test.jsx +514 -0
- package/__tests17__/ThemeProvider/utils/theme-tokens.test.js +41 -0
- package/__tests17__/ToggleSwitch/ToggleSwitch.test.jsx +82 -0
- package/__tests17__/ToggleSwitch/ToggleSwitchGroup.test.jsx +192 -0
- package/__tests17__/Tooltip/Tooltip.test.jsx +65 -0
- package/__tests17__/Tooltip/getTooltipPosition.test.js +79 -0
- package/__tests17__/Typography/typography.test.jsx +90 -0
- package/__tests17__/utils/children.test.jsx +128 -0
- package/__tests17__/utils/containUniqueFields.test.js +25 -0
- package/__tests17__/utils/input.test.js +375 -0
- package/__tests17__/utils/props.test.js +36 -0
- package/__tests17__/utils/semantics.test.jsx +34 -0
- package/__tests17__/utils/useCopy.test.js +42 -0
- package/__tests17__/utils/useResponsiveProp.test.jsx +202 -0
- package/__tests17__/utils/useSpacingScale.test.jsx +273 -0
- package/__tests17__/utils/useUniqueId.test.js +31 -0
- package/component-docs.json +85 -438
- package/lib/A11yInfoProvider/index.js +14 -5
- package/lib/Button/ButtonGroup.js +3 -2
- package/lib/Checkbox/Checkbox.js +9 -6
- package/lib/ExpandCollapse/Control.js +6 -5
- package/lib/ExpandCollapse/Panel.js +5 -4
- package/lib/List/ListItem.js +10 -236
- package/lib/List/ListItemBase.js +162 -0
- package/lib/List/ListItemContent.js +85 -0
- package/lib/List/ListItemMark.js +158 -0
- package/lib/List/PressableListItemBase.js +147 -0
- package/lib/Notification/Notification.js +2 -1
- package/lib/Pagination/Pagination.js +4 -3
- package/lib/Radio/Radio.js +9 -6
- package/lib/RadioCard/RadioCard.js +9 -6
- package/lib/Tabs/Tabs.js +12 -3
- package/lib/Tags/Tags.js +3 -3
- package/lib/TextInput/TextInput.js +5 -4
- package/lib/ViewportProvider/useViewportListener.js +11 -5
- package/lib/utils/hasOwnProperty.js +18 -0
- package/lib/utils/props/a11yProps.js +212 -45
- package/lib/utils/props/getPropSelector.js +47 -5
- package/lib/utils/useResponsiveProp.js +5 -3
- package/lib/utils/withLinkRouter.js +3 -5
- package/lib-module/A11yInfoProvider/index.js +14 -4
- package/lib-module/Button/ButtonGroup.js +3 -2
- package/lib-module/Checkbox/Checkbox.js +9 -6
- package/lib-module/ExpandCollapse/Control.js +6 -5
- package/lib-module/ExpandCollapse/Panel.js +5 -4
- package/lib-module/List/ListItem.js +13 -235
- package/lib-module/List/ListItemBase.js +139 -0
- package/lib-module/List/ListItemContent.js +66 -0
- package/lib-module/List/ListItemMark.js +143 -0
- package/lib-module/List/PressableListItemBase.js +117 -0
- package/lib-module/Notification/Notification.js +2 -1
- package/lib-module/Pagination/Pagination.js +5 -3
- package/lib-module/Radio/Radio.js +9 -6
- package/lib-module/RadioCard/RadioCard.js +9 -6
- package/lib-module/Tabs/Tabs.js +13 -4
- package/lib-module/Tags/Tags.js +3 -3
- package/lib-module/TextInput/TextInput.js +5 -4
- package/lib-module/ViewportProvider/useViewportListener.js +10 -4
- package/lib-module/utils/hasOwnProperty.js +11 -0
- package/lib-module/utils/props/a11yProps.js +210 -45
- package/lib-module/utils/props/getPropSelector.js +44 -5
- package/lib-module/utils/useResponsiveProp.js +3 -4
- package/lib-module/utils/withLinkRouter.js +3 -5
- package/package.json +11 -16
- package/src/A11yInfoProvider/index.jsx +20 -4
- package/src/Button/ButtonGroup.jsx +4 -2
- package/src/Checkbox/Checkbox.jsx +7 -3
- package/src/ExpandCollapse/Control.jsx +8 -5
- package/src/ExpandCollapse/Panel.jsx +7 -5
- package/src/List/ListItem.jsx +12 -191
- package/src/List/ListItemBase.jsx +118 -0
- package/src/List/ListItemContent.jsx +52 -0
- package/src/List/ListItemMark.jsx +99 -0
- package/src/List/PressableListItemBase.jsx +102 -0
- package/src/Notification/Notification.jsx +1 -1
- package/src/Pagination/Pagination.jsx +6 -1
- package/src/Radio/Radio.jsx +7 -3
- package/src/RadioCard/RadioCard.jsx +7 -3
- package/src/Tabs/Tabs.jsx +19 -2
- package/src/Tags/Tags.jsx +3 -3
- package/src/TextInput/TextInput.jsx +4 -4
- package/src/ViewportProvider/useViewportListener.js +10 -5
- package/src/utils/hasOwnProperty.js +11 -0
- package/src/utils/props/a11yProps.js +168 -55
- package/src/utils/props/getPropSelector.js +45 -4
- package/src/utils/useResponsiveProp.js +3 -3
- package/src/utils/withLinkRouter.jsx +1 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
import { View, StyleSheet } from 'react-native'
|
|
5
|
+
|
|
6
|
+
export const tokenTypes = {
|
|
7
|
+
itemIconSize: PropTypes.number.isRequired,
|
|
8
|
+
itemIconColor: PropTypes.string.isRequired,
|
|
9
|
+
listGutter: PropTypes.number.isRequired,
|
|
10
|
+
iconMarginTop: PropTypes.number.isRequired
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
|
|
14
|
+
size: itemIconSize,
|
|
15
|
+
color: itemIconColor
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const selectSideItemContainerStyles = ({ listGutter, iconMarginTop }) => ({
|
|
19
|
+
marginTop: iconMarginTop,
|
|
20
|
+
marginRight: listGutter
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// Align bullets with the top line of text the same way icons are aligned
|
|
24
|
+
const selectBulletPositioningStyles = ({ itemIconSize }) => ({
|
|
25
|
+
width: itemIconSize,
|
|
26
|
+
height: itemIconSize
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const selectBulletStyles = ({ itemBulletWidth, itemBulletHeight, itemBulletColor }) => ({
|
|
30
|
+
width: itemBulletWidth,
|
|
31
|
+
height: itemBulletHeight,
|
|
32
|
+
borderRadius: itemBulletHeight / 2,
|
|
33
|
+
backgroundColor: itemBulletColor
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const selectBulletContainerStyles = ({ itemBulletContainerWidth, itemBulletContainerAlign }) => ({
|
|
37
|
+
width: itemBulletContainerWidth,
|
|
38
|
+
alignItems: itemBulletContainerAlign
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Subcomponent used within ListItem and similar components for rendering bullets or icons
|
|
43
|
+
* that sit alongside a ListIconContent in a { flexDirection: row } container.
|
|
44
|
+
*
|
|
45
|
+
* It's the responsibility of themes to make sure that the supplied tokens align the
|
|
46
|
+
* icon or bullet nicely against the first line of text in a ListIconContent.
|
|
47
|
+
*/
|
|
48
|
+
const ListItemMark = ({ icon, iconColor, iconSize, tokens = {} }) => {
|
|
49
|
+
const IconComponent = icon || <></>
|
|
50
|
+
|
|
51
|
+
const themeTokens = typeof tokens === 'function' ? tokens() : tokens
|
|
52
|
+
|
|
53
|
+
const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
|
|
54
|
+
|
|
55
|
+
if (icon) {
|
|
56
|
+
const iconTokens = selectItemIconTokens(themeTokens)
|
|
57
|
+
return (
|
|
58
|
+
<View style={sideItemContainerStyles}>
|
|
59
|
+
<IconComponent size={iconSize || iconTokens.size} color={iconColor || iconTokens.color} />
|
|
60
|
+
</View>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
|
|
65
|
+
const itemBulletStyles = selectBulletStyles(themeTokens)
|
|
66
|
+
const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens)
|
|
67
|
+
return (
|
|
68
|
+
<View style={[sideItemContainerStyles, itemBulletContainerStyles]}>
|
|
69
|
+
<View style={[staticStyles.bulletPositioning, itemBulletPositioningStyles]}>
|
|
70
|
+
<View style={itemBulletStyles} testID="unordered-item-bullet" />
|
|
71
|
+
</View>
|
|
72
|
+
</View>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ListItemMark.propTypes = {
|
|
77
|
+
tokens: PropTypes.shape(tokenTypes).isRequired,
|
|
78
|
+
/**
|
|
79
|
+
* Renders side item icon
|
|
80
|
+
*/
|
|
81
|
+
icon: PropTypes.elementType,
|
|
82
|
+
/**
|
|
83
|
+
* Will set display icon color
|
|
84
|
+
*/
|
|
85
|
+
iconColor: PropTypes.string,
|
|
86
|
+
/**
|
|
87
|
+
* Allow the user define the icon size
|
|
88
|
+
*/
|
|
89
|
+
iconSize: PropTypes.number
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const staticStyles = StyleSheet.create({
|
|
93
|
+
bulletPositioning: {
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
justifyContent: 'center'
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
export default ListItemMark
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import ABBPropTypes from 'airbnb-prop-types'
|
|
4
|
+
|
|
5
|
+
import { Pressable, StyleSheet, Platform } from 'react-native'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
resolvePressableTokens,
|
|
9
|
+
clickProps,
|
|
10
|
+
linkProps,
|
|
11
|
+
hrefAttrsProp,
|
|
12
|
+
withLinkRouter
|
|
13
|
+
} from '../utils'
|
|
14
|
+
|
|
15
|
+
import ListItemBase from './ListItemBase'
|
|
16
|
+
import ListItemContent, { tokenTypes as contentTokenTypes } from './ListItemContent'
|
|
17
|
+
import ListItemMark, { tokenTypes as markTokenTypes } from './ListItemMark'
|
|
18
|
+
|
|
19
|
+
const selectPressableStyles = ({
|
|
20
|
+
backgroundColor,
|
|
21
|
+
paddingLeft,
|
|
22
|
+
paddingRight,
|
|
23
|
+
paddingTop,
|
|
24
|
+
paddingBottom,
|
|
25
|
+
interItemMargin
|
|
26
|
+
}) => ({
|
|
27
|
+
backgroundColor,
|
|
28
|
+
paddingLeft,
|
|
29
|
+
paddingRight,
|
|
30
|
+
paddingTop,
|
|
31
|
+
paddingBottom,
|
|
32
|
+
marginBottom: interItemMargin,
|
|
33
|
+
...Platform.select({ web: { outline: 'none' } })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const PressableListItemBase = forwardRef(
|
|
37
|
+
({ href, tokens, icon, children, listItemRef, ...rest }, ref) => {
|
|
38
|
+
const { onPress, ...props } = clickProps.toPressProps(rest)
|
|
39
|
+
const { hrefAttrs, rest: listItemProps } = hrefAttrsProp.bundle(props)
|
|
40
|
+
const handlePress = linkProps.handleHref({ href, onPress })
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ListItemBase ref={listItemRef} tokens={tokens()} {...listItemProps}>
|
|
44
|
+
{({ isLastItem }) => {
|
|
45
|
+
const getTokens = (pressableState) =>
|
|
46
|
+
resolvePressableTokens(tokens, pressableState, { last: isLastItem })
|
|
47
|
+
const getPressableStyle = (pressableState) => [
|
|
48
|
+
staticStyles.itemContainer,
|
|
49
|
+
selectPressableStyles(getTokens(pressableState))
|
|
50
|
+
]
|
|
51
|
+
return (
|
|
52
|
+
<Pressable
|
|
53
|
+
onPress={handlePress}
|
|
54
|
+
href={href}
|
|
55
|
+
hrefAttrs={hrefAttrs}
|
|
56
|
+
style={getPressableStyle}
|
|
57
|
+
ref={ref}
|
|
58
|
+
>
|
|
59
|
+
{(pressableState) => {
|
|
60
|
+
const themeTokens = getTokens(pressableState)
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<ListItemMark icon={icon} tokens={themeTokens} />
|
|
64
|
+
<ListItemContent tokens={themeTokens}>
|
|
65
|
+
{typeof children === 'function' ? children(pressableState) : children}
|
|
66
|
+
</ListItemContent>
|
|
67
|
+
</>
|
|
68
|
+
)
|
|
69
|
+
}}
|
|
70
|
+
</Pressable>
|
|
71
|
+
)
|
|
72
|
+
}}
|
|
73
|
+
</ListItemBase>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
PressableListItemBase.displayName = 'PressableListItemBase'
|
|
79
|
+
|
|
80
|
+
const staticStyles = StyleSheet.create({
|
|
81
|
+
itemContainer: {
|
|
82
|
+
flexDirection: 'row',
|
|
83
|
+
flex: 1
|
|
84
|
+
},
|
|
85
|
+
tokens: {
|
|
86
|
+
...contentTokenTypes,
|
|
87
|
+
...markTokenTypes
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
PressableListItemBase.propTypes = {
|
|
92
|
+
href: PropTypes.string,
|
|
93
|
+
onPress: PropTypes.func,
|
|
94
|
+
// TODO - type this better, maybe import the subcomponent token types and run it through util
|
|
95
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
96
|
+
tokens: PropTypes.any,
|
|
97
|
+
icon: PropTypes.elementType,
|
|
98
|
+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
99
|
+
listItemRef: ABBPropTypes.ref()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default withLinkRouter(PressableListItemBase)
|
|
@@ -112,7 +112,7 @@ const Notification = forwardRef(
|
|
|
112
112
|
const textStyles = selectTextStyles(themeTokens, themeOptions)
|
|
113
113
|
|
|
114
114
|
const content = wrapStringsInText(
|
|
115
|
-
typeof children === 'function' ? children({ textStyles }) : children,
|
|
115
|
+
typeof children === 'function' ? children({ textStyles, variant }) : children,
|
|
116
116
|
{ style: textStyles }
|
|
117
117
|
)
|
|
118
118
|
|
|
@@ -103,12 +103,17 @@ const Pagination = forwardRef(
|
|
|
103
103
|
label={buttonLabel}
|
|
104
104
|
copy={copy}
|
|
105
105
|
isActive={isItemActive(itemIndex)}
|
|
106
|
+
key={buttonLabel}
|
|
106
107
|
/>
|
|
107
108
|
)
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
if (shouldRenderEllipsis(itemIndex)) {
|
|
111
|
-
return
|
|
112
|
+
return (
|
|
113
|
+
<Text key="..." style={ellipsisTextStyles}>
|
|
114
|
+
...
|
|
115
|
+
</Text>
|
|
116
|
+
)
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
return null
|
package/src/Radio/Radio.jsx
CHANGED
|
@@ -151,15 +151,19 @@ const Radio = forwardRef(
|
|
|
151
151
|
const uniqueId = useUniqueId('radio')
|
|
152
152
|
const inputId = id ?? uniqueId
|
|
153
153
|
|
|
154
|
+
const selectedProps = selectProps({
|
|
155
|
+
accessibilityRole: 'radio',
|
|
156
|
+
accessibilityState: { checked: isChecked, disabled: inactive },
|
|
157
|
+
...rest
|
|
158
|
+
})
|
|
159
|
+
|
|
154
160
|
return (
|
|
155
161
|
<Pressable
|
|
156
162
|
ref={ref}
|
|
157
163
|
disabled={inactive}
|
|
158
164
|
onKeyDown={handleKeyDown}
|
|
159
165
|
onPress={handleChange}
|
|
160
|
-
|
|
161
|
-
accessibilityState={{ checked: isChecked, disabled: inactive }}
|
|
162
|
-
{...selectProps(rest)}
|
|
166
|
+
{...selectedProps}
|
|
163
167
|
>
|
|
164
168
|
{({ focused: focus, hovered: hover, pressed }) => {
|
|
165
169
|
const stateTokens = getTokens({ focus, hover, pressed })
|
|
@@ -98,6 +98,12 @@ const RadioCard = forwardRef(
|
|
|
98
98
|
const getCardTokens = (cardState) => selectPressableCardTokens(getTokens(cardState))
|
|
99
99
|
const { themeOptions } = useTheme()
|
|
100
100
|
|
|
101
|
+
const selectedProps = selectProps({
|
|
102
|
+
accessibilityRole: 'radio',
|
|
103
|
+
accessibilityState: { checked: isChecked, disabled: inactive },
|
|
104
|
+
...rest
|
|
105
|
+
})
|
|
106
|
+
|
|
101
107
|
return (
|
|
102
108
|
<PressableCardBase
|
|
103
109
|
ref={ref}
|
|
@@ -105,9 +111,7 @@ const RadioCard = forwardRef(
|
|
|
105
111
|
checked={isChecked}
|
|
106
112
|
tokens={getCardTokens}
|
|
107
113
|
onPress={handleChange}
|
|
108
|
-
|
|
109
|
-
accessibilityState={{ checked: isChecked, disabled: inactive }}
|
|
110
|
-
{...selectProps(rest)}
|
|
114
|
+
{...selectedProps}
|
|
111
115
|
>
|
|
112
116
|
{(cardState) => {
|
|
113
117
|
const { radioSpace, contentSpace, ...themeTokens } = getTokens(cardState)
|
package/src/Tabs/Tabs.jsx
CHANGED
|
@@ -6,6 +6,8 @@ import StackView from '../StackView'
|
|
|
6
6
|
import {
|
|
7
7
|
a11yProps,
|
|
8
8
|
getTokensPropType,
|
|
9
|
+
focusHandlerProps,
|
|
10
|
+
pressProps,
|
|
9
11
|
selectSystemProps,
|
|
10
12
|
useHash,
|
|
11
13
|
useInputValue,
|
|
@@ -20,6 +22,12 @@ import HorizontalScroll, {
|
|
|
20
22
|
import TabsItem from './TabsItem'
|
|
21
23
|
|
|
22
24
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
25
|
+
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
26
|
+
a11yProps,
|
|
27
|
+
focusHandlerProps,
|
|
28
|
+
pressProps,
|
|
29
|
+
viewProps
|
|
30
|
+
])
|
|
23
31
|
|
|
24
32
|
const { selectHorizontalScrollTokens, useItemPositions } = horizontalScrollUtils
|
|
25
33
|
|
|
@@ -99,15 +107,22 @@ const Tabs = forwardRef(
|
|
|
99
107
|
label,
|
|
100
108
|
id,
|
|
101
109
|
accessibilityRole = defaultTabItemAccessibiltyRole,
|
|
110
|
+
onPress,
|
|
102
111
|
ref: itemRef,
|
|
103
112
|
LinkRouter: ItemLinkRouter = LinkRouter,
|
|
104
|
-
linkRouterProps: itemLinkRouterProps
|
|
113
|
+
linkRouterProps: itemLinkRouterProps,
|
|
114
|
+
...itemRest
|
|
105
115
|
},
|
|
106
116
|
index
|
|
107
117
|
) => {
|
|
108
118
|
const itemId = id ?? label
|
|
109
119
|
const isSelected = Boolean(currentValue && currentValue === itemId)
|
|
110
|
-
const handlePress = (event) =>
|
|
120
|
+
const handlePress = (event) => {
|
|
121
|
+
if (typeof onPress === 'function') onPress(event)
|
|
122
|
+
setValue(itemId, event)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const itemProps = selectItemProps(itemRest)
|
|
111
126
|
return (
|
|
112
127
|
<TabsItem
|
|
113
128
|
ref={itemRef}
|
|
@@ -122,6 +137,7 @@ const Tabs = forwardRef(
|
|
|
122
137
|
accessibilityRole={accessibilityRole}
|
|
123
138
|
LinkRouter={ItemLinkRouter}
|
|
124
139
|
linkRouterProps={{ ...linkRouterProps, ...itemLinkRouterProps }}
|
|
140
|
+
{...itemProps}
|
|
125
141
|
>
|
|
126
142
|
{label}
|
|
127
143
|
</TabsItem>
|
|
@@ -140,6 +156,7 @@ Tabs.propTypes = {
|
|
|
140
156
|
...withLinkRouter.PropTypes,
|
|
141
157
|
items: PropTypes.arrayOf(
|
|
142
158
|
PropTypes.shape({
|
|
159
|
+
...selectedItemPropTypes,
|
|
143
160
|
...withLinkRouter.PropTypes,
|
|
144
161
|
href: PropTypes.string,
|
|
145
162
|
label: PropTypes.string,
|
package/src/Tags/Tags.jsx
CHANGED
|
@@ -127,12 +127,12 @@ const Tags = forwardRef(
|
|
|
127
127
|
toggleOneValue(id, event)
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
const itemProps = {
|
|
130
|
+
const itemProps = selectItemProps({
|
|
131
131
|
accessibilityState: { checked: isSelected },
|
|
132
132
|
accessibilityRole: itemA11yRole,
|
|
133
133
|
...a11yProps.getPositionInSet(items.length, index),
|
|
134
|
-
...
|
|
135
|
-
}
|
|
134
|
+
...itemRest
|
|
135
|
+
})
|
|
136
136
|
|
|
137
137
|
return (
|
|
138
138
|
<ButtonBase
|
|
@@ -41,7 +41,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
|
41
41
|
* supported props and <a href="https://reactnative.dev/docs/textinput" target="_blank">React Native Web documentation</a> for
|
|
42
42
|
* their implementation on the web.
|
|
43
43
|
*/
|
|
44
|
-
const TextInput = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
|
|
44
|
+
const TextInput = forwardRef(({ tokens, nativeID, variant = {}, ...rest }, ref) => {
|
|
45
45
|
const { supportsProps, ...selectedProps } = selectProps(rest)
|
|
46
46
|
|
|
47
47
|
const inputProps = {
|
|
@@ -51,9 +51,9 @@ const TextInput = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return (
|
|
54
|
-
<InputSupports nativeID={
|
|
55
|
-
{({ inputId, ...
|
|
56
|
-
<TextInputBase ref={ref}
|
|
54
|
+
<InputSupports nativeID={nativeID} {...supportsProps}>
|
|
55
|
+
{({ inputId, ...propsFromInputSupports }) => (
|
|
56
|
+
<TextInputBase ref={ref} nativeID={inputId} {...propsFromInputSupports} {...inputProps} />
|
|
57
57
|
)}
|
|
58
58
|
</InputSupports>
|
|
59
59
|
)
|
|
@@ -28,11 +28,16 @@ const useViewportListenerCSR = (setViewport) => {
|
|
|
28
28
|
const onChange = ({ window }) => setViewport(viewports.select(window.width))
|
|
29
29
|
const listener = Dimensions.addEventListener('change', onChange)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
return () => {
|
|
32
|
+
if (typeof listener?.remove === 'function') {
|
|
33
|
+
// Can't just return listener.remove because listener.emitter disappears, causing an internal error.
|
|
34
|
+
// See https://github.com/facebook/react-native/issues/34508
|
|
35
|
+
listener.remove()
|
|
36
|
+
// From RN 0.65.0, Dimensions.removeEventListener is deprecated for `remove` on addEventListener return value
|
|
37
|
+
} else if (typeof Dimensions.removeEventListener === 'function') {
|
|
38
|
+
Dimensions.removeEventListener('change', onChange)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
36
41
|
}, [setViewport])
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linter disallows object instance prototype methods like someObject.hasOwnProperty(key),
|
|
3
|
+
* but we can use this instead.
|
|
4
|
+
*
|
|
5
|
+
* @param {object} object
|
|
6
|
+
* @param {String} key
|
|
7
|
+
* @returns {Boolean}
|
|
8
|
+
*/
|
|
9
|
+
export default function hasOwnProperty(object, key) {
|
|
10
|
+
return Object.prototype.hasOwnProperty.call(object, key)
|
|
11
|
+
}
|
|
@@ -40,62 +40,164 @@ const nativeA11yPropTypes = {
|
|
|
40
40
|
onAccessibilityTap: PropTypes.func
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const a11yPropTypes = {
|
|
44
|
-
...nativeA11yPropTypes,
|
|
43
|
+
const a11yPropTypes = Platform.select({
|
|
45
44
|
// React Native Web adds many a11y props that alias aria-* attributes
|
|
46
45
|
// Types based on https://necolas.github.io/react-native-web/docs/accessibility/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
PropTypes.
|
|
57
|
-
PropTypes.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
PropTypes.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
PropTypes.string,
|
|
75
|
-
PropTypes.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
46
|
+
web: {
|
|
47
|
+
...nativeA11yPropTypes,
|
|
48
|
+
accessibilityActiveDescendant: PropTypes.string,
|
|
49
|
+
accessibilityAtomic: PropTypes.bool,
|
|
50
|
+
accessibilityAutoComplete: PropTypes.string,
|
|
51
|
+
accessibilityBusy: PropTypes.bool,
|
|
52
|
+
accessibilityChecked: PropTypes.oneOf([true, false, 'mixed']),
|
|
53
|
+
accessibilityColumnCount: PropTypes.number,
|
|
54
|
+
accessibilityColumnIndex: PropTypes.number,
|
|
55
|
+
accessibilityColumnSpan: PropTypes.number,
|
|
56
|
+
accessibilityControls: PropTypes.oneOfType([
|
|
57
|
+
PropTypes.string,
|
|
58
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
59
|
+
]),
|
|
60
|
+
accessibilityCurrent: PropTypes.oneOf([
|
|
61
|
+
true,
|
|
62
|
+
false,
|
|
63
|
+
'page',
|
|
64
|
+
'step',
|
|
65
|
+
'location',
|
|
66
|
+
'date',
|
|
67
|
+
'time'
|
|
68
|
+
]),
|
|
69
|
+
accessibilityDescribedBy: PropTypes.oneOfType([
|
|
70
|
+
PropTypes.string,
|
|
71
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
72
|
+
]),
|
|
73
|
+
accessibilityDetails: PropTypes.string,
|
|
74
|
+
accessibilityDisabled: PropTypes.bool,
|
|
75
|
+
accessibilityErrorMessage: PropTypes.string,
|
|
76
|
+
accessibilityExpanded: PropTypes.bool,
|
|
77
|
+
accessibilityFlowTo: PropTypes.oneOfType([
|
|
78
|
+
PropTypes.string,
|
|
79
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
80
|
+
]),
|
|
81
|
+
accessibilityHasPopup: PropTypes.string,
|
|
82
|
+
accessibilityHidden: PropTypes.bool,
|
|
83
|
+
accessibilityInvalid: PropTypes.bool,
|
|
84
|
+
accessibilityKeyShortcuts: PropTypes.string,
|
|
85
|
+
accessibilityLabelledBy: PropTypes.oneOfType([
|
|
86
|
+
PropTypes.string,
|
|
87
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
88
|
+
]),
|
|
89
|
+
accessibilityLevel: PropTypes.number,
|
|
90
|
+
accessibilityModal: PropTypes.bool,
|
|
91
|
+
accessibilityMultiline: PropTypes.bool,
|
|
92
|
+
accessibilityMultiSelectable: PropTypes.bool,
|
|
93
|
+
accessibilityOrientation: PropTypes.oneOf(['horizontal', 'vertical']),
|
|
94
|
+
accessibilityOwns: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
95
|
+
accessibilityPlaceholder: PropTypes.string,
|
|
96
|
+
accessibilityPosInSet: PropTypes.number,
|
|
97
|
+
accessibilityPressed: PropTypes.bool,
|
|
98
|
+
accessibilityReadOnly: PropTypes.bool,
|
|
99
|
+
accessibilityRequired: PropTypes.bool,
|
|
100
|
+
accessibilityRoleDescription: PropTypes.string,
|
|
101
|
+
accessibilityRowCount: PropTypes.number,
|
|
102
|
+
accessibilityRowIndex: PropTypes.number,
|
|
103
|
+
accessibilityRowSpan: PropTypes.number,
|
|
104
|
+
accessibilitySelected: PropTypes.bool,
|
|
105
|
+
accessibilitySetSize: PropTypes.number,
|
|
106
|
+
accessibilitySort: PropTypes.oneOf(['ascending', 'descending', 'none', 'other']),
|
|
107
|
+
accessibilityValueMax: PropTypes.number,
|
|
108
|
+
accessibilityValueMin: PropTypes.number,
|
|
109
|
+
accessibilityValueNow: PropTypes.number,
|
|
110
|
+
accessibilityValueText: PropTypes.string
|
|
111
|
+
},
|
|
112
|
+
// Ignore web-only props in native builds
|
|
113
|
+
default: nativeA11yPropTypes
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// These RNW-only props only exist in RNW >=0.18. Catch them and map them according to platform
|
|
117
|
+
// so all props work on RN, RNW >=0.18 and RNW <=0.18, regardless of which they were written for:
|
|
118
|
+
// - On native, bundle them into objects, like `accessibilityValue: { max: 100 }`
|
|
119
|
+
// - On web, split them into both of:
|
|
120
|
+
// - The appropriate aria-* attr, like `aria-valuenow`, which will work regardless of RNW version
|
|
121
|
+
// - The corresponding RNW >=0.18 prop, like `accessibilityValueNow`, which in some cases does more
|
|
122
|
+
// than just add the aria-* (e.g. `accessibilityDisabled` adds `disabled` if element supports it,
|
|
123
|
+
// and future releases might add more features here).
|
|
124
|
+
const rwnPropMappings = {
|
|
125
|
+
// Former accessibilityValue props.
|
|
126
|
+
accessibilityValueMax: (value) =>
|
|
127
|
+
Platform.select({
|
|
128
|
+
web: { 'aria-valuemax': value, accessibilityValueMax: value },
|
|
129
|
+
default: { accessibilityValue: { max: value } }
|
|
130
|
+
}),
|
|
131
|
+
accessibilityValueMin: (value) =>
|
|
132
|
+
Platform.select({
|
|
133
|
+
web: { 'aria-valuemin': value, accessibilityValueMin: value },
|
|
134
|
+
default: { accessibilityValue: { min: value } }
|
|
135
|
+
}),
|
|
136
|
+
accessibilityValueNow: (value) =>
|
|
137
|
+
Platform.select({
|
|
138
|
+
web: { 'aria-valuenow': value, accessibilityValueNow: value },
|
|
139
|
+
default: { accessibilityValue: { now: value } }
|
|
140
|
+
}),
|
|
141
|
+
accessibilityValueText: (value) =>
|
|
142
|
+
Platform.select({
|
|
143
|
+
web: { 'aria-valuetext': value, accessibilityValueText: value },
|
|
144
|
+
default: { accessibilityValue: { text: value } }
|
|
145
|
+
}),
|
|
146
|
+
|
|
147
|
+
// Former accessibilityState props
|
|
148
|
+
accessibilityBusy: (value) =>
|
|
149
|
+
Platform.select({
|
|
150
|
+
web: { 'aria-busy': value, accessibilityBusy: value },
|
|
151
|
+
default: { accessibilityState: { busy: value } }
|
|
152
|
+
}),
|
|
153
|
+
accessibilityChecked: (value) =>
|
|
154
|
+
Platform.select({
|
|
155
|
+
web: { 'aria-checked': value, accessibilityChecked: value },
|
|
156
|
+
default: { accessibilityState: { checked: value } }
|
|
157
|
+
}),
|
|
158
|
+
accessibilityDisabled: (value) =>
|
|
159
|
+
Platform.select({
|
|
160
|
+
web: {
|
|
161
|
+
'aria-disabled': value,
|
|
162
|
+
// RNW >= 0.18 maps `accessibilityDisabled` to `disabled` attr if element supports it
|
|
163
|
+
accessibilityDisabled: value,
|
|
164
|
+
// As of RNW 0.18.9, Pressable doesn't support `accessibilityDisabled`, only `disabled`,
|
|
165
|
+
// but everything else supports `accessibilityDisabled` but not `disabled`.
|
|
166
|
+
disabled: value
|
|
167
|
+
},
|
|
168
|
+
default: { accessibilityState: { disabled: value } }
|
|
169
|
+
}),
|
|
170
|
+
accessibilityExpanded: (value) =>
|
|
171
|
+
Platform.select({
|
|
172
|
+
web: { 'aria-expanded': value, accessibilityExpanded: value },
|
|
173
|
+
default: { accessibilityState: { expanded: value } }
|
|
174
|
+
}),
|
|
175
|
+
accessibilitySelected: (value) =>
|
|
176
|
+
Platform.select({
|
|
177
|
+
web: { 'aria-selected': value, accessibilitySelected: value },
|
|
178
|
+
default: { accessibilityState: { selected: value } }
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
if (Platform.OS === 'web') {
|
|
182
|
+
const mapIfDefined = (value, fn) => (value === undefined ? undefined : fn(value))
|
|
183
|
+
|
|
184
|
+
// On Web only, these React Native object props need manual mapping in RNW >=0.18
|
|
185
|
+
// which dropped support for the React Native shape of these props.
|
|
186
|
+
// Re-use our RNW 0.18 prop mappings to support both RNW <0.18 (aria-*) and
|
|
187
|
+
// new features added in >=0.18 (e.g. for accessibilityDisabled).
|
|
188
|
+
rwnPropMappings.accessibilityValue = ({ max, min, now, text } = {}) => ({
|
|
189
|
+
...mapIfDefined(max, rwnPropMappings.accessibilityValueMax),
|
|
190
|
+
...mapIfDefined(min, rwnPropMappings.accessibilityValueMin),
|
|
191
|
+
...mapIfDefined(now, rwnPropMappings.accessibilityValueNow),
|
|
192
|
+
...mapIfDefined(text, rwnPropMappings.accessibilityValueText)
|
|
193
|
+
})
|
|
194
|
+
rwnPropMappings.accessibilityState = ({ busy, checked, disabled, expanded, selected } = {}) => ({
|
|
195
|
+
...mapIfDefined(busy, rwnPropMappings.accessibilityBusy),
|
|
196
|
+
...mapIfDefined(checked, rwnPropMappings.accessibilityChecked),
|
|
197
|
+
...mapIfDefined(disabled, rwnPropMappings.accessibilityDisabled),
|
|
198
|
+
...mapIfDefined(expanded, rwnPropMappings.accessibilityExpanded),
|
|
199
|
+
...mapIfDefined(selected, rwnPropMappings.accessibilitySelected)
|
|
200
|
+
})
|
|
99
201
|
}
|
|
100
202
|
|
|
101
203
|
export default {
|
|
@@ -110,7 +212,18 @@ export default {
|
|
|
110
212
|
* Where components accept React Native a11y props, pass { ...rest } from its props to this,
|
|
111
213
|
* then spread the returned object into the component's props (usually its outer container).
|
|
112
214
|
*/
|
|
113
|
-
select: getPropSelector(
|
|
215
|
+
select: getPropSelector(
|
|
216
|
+
// Allow all React Native accessibility props
|
|
217
|
+
a11yPropTypes,
|
|
218
|
+
// Allow any `aria-*` attribute on web; ignore them on native
|
|
219
|
+
Platform.OS === 'web' && /^aria-/,
|
|
220
|
+
// For the props added and deprecated in React Native Web 0.18, convert them to
|
|
221
|
+
// a form that is platform-appropriate and RNW-version safe
|
|
222
|
+
(key, value) => {
|
|
223
|
+
const rnwPropMapper = rwnPropMappings[key]
|
|
224
|
+
return rnwPropMapper ? rnwPropMapper(value) : undefined
|
|
225
|
+
}
|
|
226
|
+
),
|
|
114
227
|
/**
|
|
115
228
|
* Use this to disable focus for elements which are visually hidden but still rendered.
|
|
116
229
|
*/
|