@telus-uds/components-base 1.6.1 → 1.8.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/.storybook/main.js +7 -0
- package/.turbo/turbo-build.log +3 -3
- package/.turbo/turbo-lint.log +3 -13
- package/CHANGELOG.json +112 -1
- package/CHANGELOG.md +41 -2
- package/__fixtures__/Accessible.js +1 -2
- package/__fixtures__/Accessible.native.js +1 -2
- package/__tests__/FlexGrid/Col.test.jsx +5 -0
- package/__tests__/InputLabel/InputLabel.test.jsx +28 -0
- package/__tests__/InputLabel/__snapshots__/InputLabel.test.jsx.snap +3 -0
- package/__tests__/InputSupports/InputSupports.test.jsx +10 -0
- package/component-docs.json +278 -40
- package/lib/Button/ButtonGroup.js +118 -45
- package/lib/Checkbox/CheckboxGroup.js +3 -3
- package/lib/ExpandCollapse/Panel.js +2 -1
- package/lib/Fieldset/Fieldset.js +7 -0
- package/lib/InputLabel/InputLabel.js +8 -1
- package/lib/InputSupports/InputSupports.js +7 -0
- package/lib/List/ListItem.js +22 -12
- package/lib/Notification/Notification.js +1 -1
- package/lib/Radio/RadioGroup.js +12 -5
- package/lib/RadioCard/RadioCardGroup.js +7 -0
- package/lib/Search/Search.js +28 -20
- package/lib/Skeleton/Skeleton.js +48 -2
- package/lib/TextInput/TextArea.js +1 -1
- package/lib/TextInput/TextInput.js +1 -1
- package/lib/TextInput/TextInputBase.js +1 -1
- package/lib/ToggleSwitch/ToggleSwitch.js +7 -0
- package/lib/ToggleSwitch/ToggleSwitchGroup.js +7 -0
- package/lib/Tooltip/Tooltip.js +1 -1
- package/lib/Typography/Typography.js +12 -10
- package/lib/index.js +22 -1
- package/lib/utils/animation/useVerticalExpandAnimation.js +26 -13
- package/lib/utils/input.js +5 -6
- package/lib/utils/props/index.js +18 -0
- package/lib/utils/props/inputSupportsProps.js +7 -0
- package/lib/utils/props/textInputProps.js +207 -0
- package/lib/utils/props/textProps.js +72 -0
- package/lib-module/Button/ButtonGroup.js +117 -45
- package/lib-module/Checkbox/CheckboxGroup.js +3 -3
- package/lib-module/ExpandCollapse/Panel.js +2 -1
- package/lib-module/Fieldset/Fieldset.js +7 -0
- package/lib-module/InputLabel/InputLabel.js +8 -1
- package/lib-module/InputSupports/InputSupports.js +7 -0
- package/lib-module/List/ListItem.js +22 -12
- package/lib-module/Notification/Notification.js +1 -1
- package/lib-module/Radio/RadioGroup.js +12 -5
- package/lib-module/RadioCard/RadioCardGroup.js +7 -0
- package/lib-module/Search/Search.js +30 -22
- package/lib-module/Skeleton/Skeleton.js +49 -3
- package/lib-module/TextInput/TextArea.js +2 -2
- package/lib-module/TextInput/TextInput.js +2 -2
- package/lib-module/TextInput/TextInputBase.js +2 -2
- package/lib-module/ToggleSwitch/ToggleSwitch.js +7 -0
- package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +7 -0
- package/lib-module/Tooltip/Tooltip.js +1 -1
- package/lib-module/Typography/Typography.js +13 -11
- package/lib-module/index.js +1 -1
- package/lib-module/utils/animation/useVerticalExpandAnimation.js +26 -14
- package/lib-module/utils/input.js +6 -6
- package/lib-module/utils/props/index.js +2 -0
- package/lib-module/utils/props/inputSupportsProps.js +7 -0
- package/lib-module/utils/props/textInputProps.js +194 -0
- package/lib-module/utils/props/textProps.js +59 -0
- package/package.json +9 -4
- package/src/Button/ButtonGroup.jsx +106 -41
- package/src/Checkbox/Checkbox.jsx +7 -4
- package/src/Checkbox/CheckboxGroup.jsx +3 -3
- package/src/ExpandCollapse/Panel.jsx +3 -1
- package/src/Fieldset/Fieldset.jsx +6 -0
- package/src/InputLabel/InputLabel.jsx +17 -2
- package/src/InputSupports/InputSupports.jsx +9 -1
- package/src/List/ListItem.jsx +17 -9
- package/src/Notification/Notification.jsx +1 -1
- package/src/Radio/Radio.jsx +5 -1
- package/src/Radio/RadioGroup.jsx +11 -5
- package/src/RadioCard/RadioCard.jsx +5 -1
- package/src/RadioCard/RadioCardGroup.jsx +6 -0
- package/src/Search/Search.jsx +34 -22
- package/src/Skeleton/Skeleton.jsx +56 -3
- package/src/TextInput/TextArea.jsx +2 -0
- package/src/TextInput/TextInput.jsx +2 -0
- package/src/TextInput/TextInputBase.jsx +2 -0
- package/src/ToggleSwitch/ToggleSwitch.jsx +6 -0
- package/src/ToggleSwitch/ToggleSwitchGroup.jsx +6 -0
- package/src/Tooltip/Tooltip.jsx +1 -1
- package/src/Typography/Typography.jsx +13 -9
- package/src/index.js +4 -1
- package/src/utils/animation/useVerticalExpandAnimation.js +25 -12
- package/src/utils/input.js +5 -7
- package/src/utils/props/index.js +2 -0
- package/src/utils/props/inputSupportsProps.js +6 -1
- package/src/utils/props/textInputProps.js +178 -0
- package/src/utils/props/textProps.js +58 -0
- package/src/utils/props/tokens.js +21 -19
- package/stories/Search/Search.stories.jsx +49 -2
- package/stories/Tabs/Tabs.stories.jsx +4 -3
package/src/List/ListItem.jsx
CHANGED
|
@@ -22,8 +22,7 @@ const selectBulletStyles = ({ itemBulletWidth, itemBulletHeight, itemBulletColor
|
|
|
22
22
|
|
|
23
23
|
const selectBulletContainerStyles = ({ itemBulletContainerWidth, itemBulletContainerAlign }) => ({
|
|
24
24
|
width: itemBulletContainerWidth,
|
|
25
|
-
alignItems: itemBulletContainerAlign
|
|
26
|
-
justifyContent: itemBulletContainerAlign
|
|
25
|
+
alignItems: itemBulletContainerAlign
|
|
27
26
|
})
|
|
28
27
|
|
|
29
28
|
const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
|
|
@@ -31,12 +30,15 @@ const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
|
|
|
31
30
|
color: itemIconColor
|
|
32
31
|
})
|
|
33
32
|
|
|
34
|
-
const
|
|
35
|
-
marginTop: iconMarginTop
|
|
33
|
+
const selectSideItemContainerStyles = ({ listGutter, iconMarginTop }) => ({
|
|
34
|
+
marginTop: iconMarginTop,
|
|
35
|
+
marginRight: listGutter
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// Align bullets with the top line of text the same way icons are aligned
|
|
39
|
+
const selectBulletPositioningStyles = ({ itemIconSize }) => ({
|
|
40
|
+
width: itemIconSize,
|
|
41
|
+
height: itemIconSize
|
|
40
42
|
})
|
|
41
43
|
|
|
42
44
|
const selectItemStyles = ({ itemFontWeight, itemFontSize, itemLineHeight, itemFontName }) =>
|
|
@@ -73,8 +75,8 @@ const ListItem = forwardRef(
|
|
|
73
75
|
const dividerStyles = selectDividerStyles(themeTokens)
|
|
74
76
|
const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
|
|
75
77
|
const itemBulletStyles = selectBulletStyles(themeTokens)
|
|
78
|
+
const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens)
|
|
76
79
|
const iconTokens = selectItemIconTokens(themeTokens)
|
|
77
|
-
const commonIconStyles = selectCommonIconStyles(themeTokens)
|
|
78
80
|
const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
|
|
79
81
|
const accessibilityRole = Platform.select({ web: 'listitem', default: 'item' })
|
|
80
82
|
|
|
@@ -109,7 +111,7 @@ const ListItem = forwardRef(
|
|
|
109
111
|
|
|
110
112
|
if (icon) {
|
|
111
113
|
return (
|
|
112
|
-
<View style={
|
|
114
|
+
<View style={sideItemContainerStyles}>
|
|
113
115
|
<IconComponent
|
|
114
116
|
size={iconSize || iconTokens.size}
|
|
115
117
|
color={iconColor || iconTokens.color}
|
|
@@ -120,7 +122,9 @@ const ListItem = forwardRef(
|
|
|
120
122
|
|
|
121
123
|
return (
|
|
122
124
|
<View style={[sideItemContainerStyles, itemBulletContainerStyles]}>
|
|
123
|
-
<View style={
|
|
125
|
+
<View style={[staticStyles.bulletPositioning, itemBulletPositioningStyles]}>
|
|
126
|
+
<View style={itemBulletStyles} testID="unordered-item-bullet" />
|
|
127
|
+
</View>
|
|
124
128
|
</View>
|
|
125
129
|
)
|
|
126
130
|
}
|
|
@@ -146,6 +150,10 @@ const staticStyles = StyleSheet.create({
|
|
|
146
150
|
},
|
|
147
151
|
wrap: {
|
|
148
152
|
flex: 1
|
|
153
|
+
},
|
|
154
|
+
bulletPositioning: {
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
justifyContent: 'center'
|
|
149
157
|
}
|
|
150
158
|
})
|
|
151
159
|
|
|
@@ -159,7 +159,7 @@ Notification.propTypes = {
|
|
|
159
159
|
*/
|
|
160
160
|
dismissible: PropTypes.bool,
|
|
161
161
|
/**
|
|
162
|
-
* Select
|
|
162
|
+
* Select English or French copy for the accessible label of the dismiss button.
|
|
163
163
|
*/
|
|
164
164
|
copy: PropTypes.oneOfType([
|
|
165
165
|
PropTypes.oneOf(['en', 'fr']),
|
package/src/Radio/Radio.jsx
CHANGED
|
@@ -121,7 +121,11 @@ const Radio = forwardRef(
|
|
|
121
121
|
},
|
|
122
122
|
ref
|
|
123
123
|
) => {
|
|
124
|
-
const {
|
|
124
|
+
const {
|
|
125
|
+
currentValue: isChecked,
|
|
126
|
+
setValue: setIsChecked,
|
|
127
|
+
isControlled
|
|
128
|
+
} = useInputValue(
|
|
125
129
|
{
|
|
126
130
|
value: checked,
|
|
127
131
|
initialValue: defaultChecked,
|
package/src/Radio/RadioGroup.jsx
CHANGED
|
@@ -44,7 +44,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
44
44
|
* ### Uncontrolled version
|
|
45
45
|
*
|
|
46
46
|
* If the RadioGroup manages its own state, you can use `initialCheckedId` prop to provide the initial value.
|
|
47
|
-
* Whenever the radio
|
|
47
|
+
* Whenever the radio gets toggled, it calls the `onChange` callback with the new value (string).
|
|
48
48
|
*
|
|
49
49
|
* ### Use in forms
|
|
50
50
|
*
|
|
@@ -76,6 +76,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
76
76
|
const RadioGroup = forwardRef(
|
|
77
77
|
(
|
|
78
78
|
{
|
|
79
|
+
copy = 'en',
|
|
79
80
|
tokens,
|
|
80
81
|
radioTokens,
|
|
81
82
|
variant,
|
|
@@ -142,6 +143,7 @@ const RadioGroup = forwardRef(
|
|
|
142
143
|
|
|
143
144
|
return (
|
|
144
145
|
<Fieldset
|
|
146
|
+
copy={copy}
|
|
145
147
|
ref={ref}
|
|
146
148
|
name={inputGroupName}
|
|
147
149
|
legend={legend}
|
|
@@ -163,6 +165,10 @@ RadioGroup.displayName = 'RadioGroup'
|
|
|
163
165
|
|
|
164
166
|
RadioGroup.propTypes = {
|
|
165
167
|
...selectedSystemPropTypes,
|
|
168
|
+
/**
|
|
169
|
+
* Whether the English or French copy will be used (e.g. for accessibility labels).
|
|
170
|
+
*/
|
|
171
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
166
172
|
/**
|
|
167
173
|
* Optional theme token overrides for the outer RadioGroup component
|
|
168
174
|
*/
|
|
@@ -208,11 +214,11 @@ RadioGroup.propTypes = {
|
|
|
208
214
|
*/
|
|
209
215
|
feedback: PropTypes.string,
|
|
210
216
|
/**
|
|
211
|
-
* If provided, the radio
|
|
217
|
+
* If provided, the radio with this id is selected on first render.
|
|
212
218
|
*/
|
|
213
219
|
initialCheckedId: PropTypes.string,
|
|
214
220
|
/**
|
|
215
|
-
* If not undefined, the radio
|
|
221
|
+
* If not undefined, the radio with this id is selected (or none is selected if `null`), and the
|
|
216
222
|
* element's selection state will be controlled by its parent using the `onChange` function.
|
|
217
223
|
*/
|
|
218
224
|
checkedId: PropTypes.string,
|
|
@@ -222,11 +228,11 @@ RadioGroup.propTypes = {
|
|
|
222
228
|
*/
|
|
223
229
|
onChange: PropTypes.func,
|
|
224
230
|
/**
|
|
225
|
-
* If true, the
|
|
231
|
+
* If true, the radios cannot be selected by the user and simply show their current state.
|
|
226
232
|
*/
|
|
227
233
|
readOnly: PropTypes.bool,
|
|
228
234
|
/**
|
|
229
|
-
* If true, the
|
|
235
|
+
* If true, the radios cannot be interacted with, elements are set as `disabled` and if the
|
|
230
236
|
* theme supports `inactive` appearances rules, these are applied.
|
|
231
237
|
*/
|
|
232
238
|
inactive: PropTypes.bool,
|
|
@@ -77,7 +77,11 @@ const RadioCard = forwardRef(
|
|
|
77
77
|
},
|
|
78
78
|
ref
|
|
79
79
|
) => {
|
|
80
|
-
const {
|
|
80
|
+
const {
|
|
81
|
+
currentValue: isChecked,
|
|
82
|
+
setValue: setIsChecked,
|
|
83
|
+
isControlled
|
|
84
|
+
} = useInputValue({
|
|
81
85
|
value: checked,
|
|
82
86
|
initialValue: defaultChecked,
|
|
83
87
|
onChange
|
|
@@ -77,6 +77,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
77
77
|
const RadioCardGroup = forwardRef(
|
|
78
78
|
(
|
|
79
79
|
{
|
|
80
|
+
copy = 'en',
|
|
80
81
|
tokens,
|
|
81
82
|
radioCardTokens,
|
|
82
83
|
variant,
|
|
@@ -120,6 +121,7 @@ const RadioCardGroup = forwardRef(
|
|
|
120
121
|
|
|
121
122
|
return (
|
|
122
123
|
<Fieldset
|
|
124
|
+
copy={copy}
|
|
123
125
|
ref={ref}
|
|
124
126
|
name={inputGroupName}
|
|
125
127
|
legend={legend}
|
|
@@ -170,6 +172,10 @@ RadioCardGroup.displayName = 'RadioCardGroup'
|
|
|
170
172
|
|
|
171
173
|
RadioCardGroup.propTypes = {
|
|
172
174
|
...selectedSystemPropTypes,
|
|
175
|
+
/**
|
|
176
|
+
* Whether the English or French copy will be used (e.g. for accessibility labels).
|
|
177
|
+
*/
|
|
178
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
173
179
|
/**
|
|
174
180
|
* Optional theme token overrides for the outer RadioCardGroup component
|
|
175
181
|
*/
|
package/src/Search/Search.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
2
|
import { View, StyleSheet } from 'react-native'
|
|
3
3
|
|
|
4
4
|
import PropTypes from 'prop-types'
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
getTokensPropType,
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
selectTokens,
|
|
11
|
+
useInputValue,
|
|
11
12
|
useSpacingScale,
|
|
13
|
+
textInputHandlerProps,
|
|
14
|
+
textInputProps,
|
|
12
15
|
variantProp,
|
|
13
16
|
viewProps
|
|
14
17
|
} from '../utils'
|
|
@@ -18,7 +21,11 @@ import StackView from '../StackView'
|
|
|
18
21
|
import useCopy from '../utils/useCopy'
|
|
19
22
|
import dictionary from './dictionary'
|
|
20
23
|
|
|
21
|
-
const [
|
|
24
|
+
const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
25
|
+
const [selectInputProps, selectedInputPropTypes] = selectSystemProps([
|
|
26
|
+
textInputHandlerProps,
|
|
27
|
+
textInputProps
|
|
28
|
+
])
|
|
22
29
|
|
|
23
30
|
const selectInputTokens = ({ searchTokens, buttonTokens, buttonsGapSize }) => {
|
|
24
31
|
const { paddingRight: inputPaddingRight, clearButtonIcon, submitButtonIcon } = searchTokens
|
|
@@ -54,8 +61,9 @@ const selectIconTokens = ({ iconSize, iconColor }) => ({ color: iconColor, size:
|
|
|
54
61
|
const Search = forwardRef(
|
|
55
62
|
(
|
|
56
63
|
{
|
|
57
|
-
initialValue
|
|
58
|
-
|
|
64
|
+
initialValue,
|
|
65
|
+
value,
|
|
66
|
+
placeholder,
|
|
59
67
|
inactive,
|
|
60
68
|
onChange,
|
|
61
69
|
onSubmit,
|
|
@@ -68,7 +76,12 @@ const Search = forwardRef(
|
|
|
68
76
|
},
|
|
69
77
|
ref
|
|
70
78
|
) => {
|
|
71
|
-
const
|
|
79
|
+
const { currentValue = '', setValue } = useInputValue({
|
|
80
|
+
value,
|
|
81
|
+
initialValue,
|
|
82
|
+
onChange
|
|
83
|
+
})
|
|
84
|
+
|
|
72
85
|
const themeTokens = useThemeTokens('Search', tokens, variant)
|
|
73
86
|
const buttonTokens = useThemeTokens('SearchButton', tokens, variant)
|
|
74
87
|
|
|
@@ -90,28 +103,26 @@ const Search = forwardRef(
|
|
|
90
103
|
|
|
91
104
|
const handleSubmit = (event) => {
|
|
92
105
|
if (onSubmit !== undefined) {
|
|
93
|
-
onSubmit(
|
|
106
|
+
onSubmit(currentValue, event)
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
|
|
97
|
-
const handleChange = (currentValue, event) => {
|
|
98
|
-
setValue(currentValue, event)
|
|
99
|
-
|
|
100
|
-
if (onChange !== undefined) onChange(currentValue, event)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
110
|
const handleClear = (event) => {
|
|
104
111
|
setValue('', event)
|
|
105
|
-
|
|
106
112
|
if (onClear !== undefined) onClear('', event)
|
|
107
|
-
if (onChange !== undefined) onChange('', event)
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
const isEmpty =
|
|
115
|
+
const isEmpty = currentValue === ''
|
|
116
|
+
|
|
117
|
+
// Accessibility label should always be present and correctly localised
|
|
118
|
+
const a11yLabelText = accessibilityLabel || getCopy('accessibilityLabel')
|
|
119
|
+
// Placeholder is optional and may be unset by passing an empty string
|
|
120
|
+
const placeholderText = placeholder ?? a11yLabelText
|
|
111
121
|
|
|
112
122
|
return (
|
|
113
|
-
<View style={staticStyles.container} {...
|
|
123
|
+
<View style={staticStyles.container} {...selectContainerProps(rest)}>
|
|
114
124
|
<TextInputBase
|
|
125
|
+
{...selectInputProps(rest)}
|
|
115
126
|
ref={ref}
|
|
116
127
|
tokens={(appearances) =>
|
|
117
128
|
selectInputTokens({
|
|
@@ -121,15 +132,15 @@ const Search = forwardRef(
|
|
|
121
132
|
isEmpty
|
|
122
133
|
})
|
|
123
134
|
}
|
|
124
|
-
placeholder={
|
|
135
|
+
placeholder={placeholderText}
|
|
125
136
|
placeholderTextColor={placeholderColor}
|
|
126
137
|
inactive={inactive}
|
|
127
138
|
enablesReturnKeyAutomatically
|
|
128
139
|
returnKeyType="search"
|
|
129
|
-
value={
|
|
130
|
-
onChange={
|
|
140
|
+
value={currentValue}
|
|
141
|
+
onChange={setValue}
|
|
131
142
|
onSubmitEditing={handleSubmit}
|
|
132
|
-
accessibilityLabel={
|
|
143
|
+
accessibilityLabel={a11yLabelText}
|
|
133
144
|
/>
|
|
134
145
|
<View style={[staticStyles.iconsContainer, selectIconsContainerStyle(themeTokens)]}>
|
|
135
146
|
<StackView direction="row" space={buttonsGap}>
|
|
@@ -172,7 +183,8 @@ const Search = forwardRef(
|
|
|
172
183
|
Search.displayName = 'Search'
|
|
173
184
|
|
|
174
185
|
Search.propTypes = {
|
|
175
|
-
...
|
|
186
|
+
...selectedContainerPropTypes,
|
|
187
|
+
...selectedInputPropTypes,
|
|
176
188
|
/**
|
|
177
189
|
* Use this to set the initial value of the search input.
|
|
178
190
|
* Updating `initialValue` will **not** update the actual value.
|
|
@@ -206,7 +218,7 @@ Search.propTypes = {
|
|
|
206
218
|
*/
|
|
207
219
|
accessibilityLabel: PropTypes.string,
|
|
208
220
|
/**
|
|
209
|
-
* Select
|
|
221
|
+
* Select English or French copy for the accessible labels.
|
|
210
222
|
* You may also pass in a custom dictionary object.
|
|
211
223
|
*/
|
|
212
224
|
copy: PropTypes.oneOfType([
|
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
a11yProps,
|
|
8
8
|
getTokensPropType,
|
|
9
9
|
selectSystemProps,
|
|
10
|
+
responsiveProps,
|
|
11
|
+
useResponsiveProp,
|
|
10
12
|
useSpacingScale,
|
|
13
|
+
spacingProps,
|
|
11
14
|
variantProp,
|
|
12
15
|
viewProps
|
|
13
16
|
} from '../utils'
|
|
@@ -37,9 +40,29 @@ const selectSquareStyles = ({ radius }) => ({
|
|
|
37
40
|
})
|
|
38
41
|
|
|
39
42
|
const Skeleton = forwardRef(
|
|
40
|
-
(
|
|
43
|
+
(
|
|
44
|
+
{
|
|
45
|
+
tokens,
|
|
46
|
+
variant,
|
|
47
|
+
size,
|
|
48
|
+
sizeIndex = size,
|
|
49
|
+
sizePixels,
|
|
50
|
+
characters,
|
|
51
|
+
lines,
|
|
52
|
+
shape = 'line',
|
|
53
|
+
...rest
|
|
54
|
+
},
|
|
55
|
+
ref
|
|
56
|
+
) => {
|
|
41
57
|
const themeTokens = useThemeTokens('Skeleton', tokens, variant)
|
|
42
|
-
const
|
|
58
|
+
const pixels = useResponsiveProp(sizePixels)
|
|
59
|
+
const spacingScaleValue =
|
|
60
|
+
typeof pixels === 'number'
|
|
61
|
+
? // Size by an exact number of pixels
|
|
62
|
+
{ options: { size: pixels } }
|
|
63
|
+
: // Size by an index on the spacing scale (getting default index from theme if none provided)
|
|
64
|
+
sizeIndex || themeTokens.size
|
|
65
|
+
const skeletonHeight = useSpacingScale(spacingScaleValue)
|
|
43
66
|
const nativeAnimation = useSkeletonNativeAnimation()
|
|
44
67
|
|
|
45
68
|
const getAnimationBaseOnPlatform = () => {
|
|
@@ -97,9 +120,39 @@ Skeleton.propTypes = {
|
|
|
97
120
|
...selectedSystemPropTypes,
|
|
98
121
|
tokens: getTokensPropType('Skeleton'),
|
|
99
122
|
variant: variantProp.propType,
|
|
100
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Sets the size of Skeleton lines or shape according to the theme's spacing scale. For example, size={1} gives the smallest non-zero theme-defined spacing size.
|
|
125
|
+
*
|
|
126
|
+
* May also accept an object with responsive viewport keys or spacing scale options - see `useSpacingScale` for details.
|
|
127
|
+
*/
|
|
128
|
+
sizeIndex: spacingProps.types.spacingValue,
|
|
129
|
+
/**
|
|
130
|
+
* @deprecated alias for `sizeIndex`
|
|
131
|
+
*/
|
|
132
|
+
size: spacingProps.types.spacingValue,
|
|
133
|
+
/**
|
|
134
|
+
* Sets the size of Skeleton lines or shape to an exact number of pixels. Use when it's necessary to exactly match sizes of images or other boxes.
|
|
135
|
+
*
|
|
136
|
+
* Accepts a number or an object with responsive viewport keys, e.g. { xs: 32, lg: 64 } would be 32px at xs, sm and md and 64 at lg and xl viewports.
|
|
137
|
+
*/
|
|
138
|
+
sizePixels: responsiveProps.getTypeOptionallyByViewport(propTypes.number),
|
|
139
|
+
/**
|
|
140
|
+
* Determines the width of simulated lines of text if the Skeleton's shape is 'line' (the default shape).
|
|
141
|
+
*
|
|
142
|
+
* Only has any affect if shape is line (the default). If unset, takes a default value from the theme.
|
|
143
|
+
*/
|
|
101
144
|
characters: propTypes.number,
|
|
145
|
+
/**
|
|
146
|
+
* Determines how many Skeleton items are rendered (default 1).
|
|
147
|
+
*
|
|
148
|
+
* Recommended usage is to simulate paragraphs of text when Skeleton's shape is 'line' (the default shape).
|
|
149
|
+
*
|
|
150
|
+
* The amount of spacing between multiple lines is controlled by theme tokens.
|
|
151
|
+
*/
|
|
102
152
|
lines: propTypes.number,
|
|
153
|
+
/**
|
|
154
|
+
* Determines if the skeleton should resemble lines of text (default), a circle, or a square box with themed rounded corners.
|
|
155
|
+
*/
|
|
103
156
|
shape: propTypes.oneOf(['line', 'circle', 'box'])
|
|
104
157
|
}
|
|
105
158
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
inputSupportsProps,
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
textInputHandlerProps,
|
|
11
|
+
textInputProps,
|
|
11
12
|
variantProp,
|
|
12
13
|
viewProps
|
|
13
14
|
} from '../utils'
|
|
@@ -21,6 +22,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
|
21
22
|
focusHandlerProps,
|
|
22
23
|
inputSupportsProps,
|
|
23
24
|
textInputHandlerProps,
|
|
25
|
+
textInputProps,
|
|
24
26
|
viewProps
|
|
25
27
|
])
|
|
26
28
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
inputSupportsProps,
|
|
8
8
|
selectSystemProps,
|
|
9
9
|
textInputHandlerProps,
|
|
10
|
+
textInputProps,
|
|
10
11
|
variantProp,
|
|
11
12
|
viewProps
|
|
12
13
|
} from '../utils'
|
|
@@ -19,6 +20,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
|
19
20
|
focusHandlerProps,
|
|
20
21
|
inputSupportsProps,
|
|
21
22
|
textInputHandlerProps,
|
|
23
|
+
textInputProps,
|
|
22
24
|
viewProps
|
|
23
25
|
])
|
|
24
26
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getTokensPropType,
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
textInputHandlerProps,
|
|
11
|
+
textInputProps,
|
|
11
12
|
useInputValue,
|
|
12
13
|
variantProp,
|
|
13
14
|
viewProps
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
17
18
|
a11yProps,
|
|
18
19
|
textInputHandlerProps,
|
|
20
|
+
textInputProps,
|
|
19
21
|
viewProps
|
|
20
22
|
])
|
|
21
23
|
|
|
@@ -85,6 +85,7 @@ const selectLabelTokens = ({
|
|
|
85
85
|
const ToggleSwitch = forwardRef(
|
|
86
86
|
(
|
|
87
87
|
{
|
|
88
|
+
copy = 'en',
|
|
88
89
|
value,
|
|
89
90
|
initialValue,
|
|
90
91
|
onChange,
|
|
@@ -119,6 +120,7 @@ const ToggleSwitch = forwardRef(
|
|
|
119
120
|
{Boolean(label) && (
|
|
120
121
|
<View style={[selectLabelStyles(themeTokens), staticStyles.containText]}>
|
|
121
122
|
<InputLabel
|
|
123
|
+
copy={copy}
|
|
122
124
|
forId={inputId}
|
|
123
125
|
label={label}
|
|
124
126
|
tokens={selectLabelTokens(themeTokens)}
|
|
@@ -170,6 +172,10 @@ ToggleSwitch.displayName = 'ToggleSwitch'
|
|
|
170
172
|
|
|
171
173
|
ToggleSwitch.propTypes = {
|
|
172
174
|
...selectedSystemPropTypes,
|
|
175
|
+
/**
|
|
176
|
+
* Whether the English or French copy will be used (e.g. for accessibility labels).
|
|
177
|
+
*/
|
|
178
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
173
179
|
tokens: getTokensPropType('ToggleSwitch'),
|
|
174
180
|
variant: variantProp.propType,
|
|
175
181
|
/**
|
|
@@ -28,6 +28,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
28
28
|
const ToggleSwitchGroup = forwardRef(
|
|
29
29
|
(
|
|
30
30
|
{
|
|
31
|
+
copy = 'en',
|
|
31
32
|
variant,
|
|
32
33
|
tokens,
|
|
33
34
|
items = [],
|
|
@@ -105,6 +106,7 @@ const ToggleSwitchGroup = forwardRef(
|
|
|
105
106
|
|
|
106
107
|
return (
|
|
107
108
|
<ToggleSwitch
|
|
109
|
+
copy={copy}
|
|
108
110
|
id={id}
|
|
109
111
|
ref={itemRef}
|
|
110
112
|
key={id}
|
|
@@ -143,6 +145,10 @@ ToggleSwitchGroup.displayName = 'ToggleSwitchGroup'
|
|
|
143
145
|
|
|
144
146
|
ToggleSwitchGroup.propTypes = {
|
|
145
147
|
...selectedSystemPropTypes,
|
|
148
|
+
/**
|
|
149
|
+
* Whether the English or French copy will be used (e.g. for accessibility labels).
|
|
150
|
+
*/
|
|
151
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
146
152
|
tokens: getTokensPropType('ToggleSwitchGroup'),
|
|
147
153
|
variant: variantProp.propType,
|
|
148
154
|
/**
|
package/src/Tooltip/Tooltip.jsx
CHANGED
|
@@ -276,7 +276,7 @@ Tooltip.propTypes = {
|
|
|
276
276
|
*/
|
|
277
277
|
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
278
278
|
/**
|
|
279
|
-
* Select
|
|
279
|
+
* Select English or French copy for the accessible label.
|
|
280
280
|
*/
|
|
281
281
|
copy: PropTypes.oneOf(['en', 'fr']),
|
|
282
282
|
/**
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
headingTags,
|
|
14
14
|
selectSystemProps,
|
|
15
15
|
textTags,
|
|
16
|
+
textProps,
|
|
16
17
|
viewProps,
|
|
17
18
|
getA11yPropsFromHtmlTag
|
|
18
19
|
} from '../utils'
|
|
@@ -20,7 +21,8 @@ import {
|
|
|
20
21
|
* @typedef {import('../utils/a11y/semantics').TextTag} TextTag
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
|
-
const [
|
|
24
|
+
const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
25
|
+
const [selectTextProps, selectedTextPropTypes] = selectSystemProps([textProps])
|
|
24
26
|
|
|
25
27
|
const selectTextStyles = ({
|
|
26
28
|
fontWeight,
|
|
@@ -62,23 +64,24 @@ const Typography = forwardRef(
|
|
|
62
64
|
) => {
|
|
63
65
|
const viewport = useViewport()
|
|
64
66
|
const themeTokens = useThemeTokens('Typography', tokens, variant, { viewport })
|
|
65
|
-
const
|
|
67
|
+
const resolvedTextProps = {
|
|
68
|
+
...selectTextProps(rest),
|
|
66
69
|
style: selectTextStyles(align ? { ...themeTokens, textAlign: align } : themeTokens),
|
|
67
70
|
dataSet,
|
|
68
71
|
maxFontSizeMultiplier: getMaxFontMultiplier(themeTokens)
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
const
|
|
74
|
+
const containerProps = {
|
|
72
75
|
...getA11yPropsFromHtmlTag(tag, accessibilityRole),
|
|
73
|
-
...rest
|
|
74
|
-
}
|
|
76
|
+
...selectContainerProps(rest)
|
|
77
|
+
}
|
|
75
78
|
|
|
76
79
|
return block ? (
|
|
77
|
-
<View ref={ref} {...
|
|
78
|
-
<Text {...
|
|
80
|
+
<View ref={ref} {...containerProps}>
|
|
81
|
+
<Text {...resolvedTextProps}>{children}</Text>
|
|
79
82
|
</View>
|
|
80
83
|
) : (
|
|
81
|
-
<Text ref={ref} {...
|
|
84
|
+
<Text ref={ref} {...containerProps} {...resolvedTextProps}>
|
|
82
85
|
{children}
|
|
83
86
|
</Text>
|
|
84
87
|
)
|
|
@@ -87,7 +90,8 @@ const Typography = forwardRef(
|
|
|
87
90
|
Typography.displayName = 'Typography'
|
|
88
91
|
|
|
89
92
|
Typography.propTypes = {
|
|
90
|
-
...
|
|
93
|
+
...selectedContainerPropTypes,
|
|
94
|
+
...selectedTextPropTypes,
|
|
91
95
|
tokens: getTokensPropType('Typography'),
|
|
92
96
|
variant: variantProp.propType,
|
|
93
97
|
/**
|
package/src/index.js
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
1
|
+
import { useEffect, 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
|
+
const [isAnimating, setIsAnimating] = useState(false)
|
|
8
|
+
|
|
7
9
|
const expandAnimatedValue = useRef(new Animated.Value(0)).current
|
|
10
|
+
const elementRef = useRef(null)
|
|
8
11
|
|
|
9
12
|
const { expandDuration, collapseDuration } = tokens
|
|
10
13
|
|
|
14
|
+
// Treat as animating from when expanded state changes, until animation completes
|
|
15
|
+
useEffect(() => setIsAnimating(true), [isExpanded])
|
|
16
|
+
|
|
11
17
|
useEffect(() => {
|
|
18
|
+
const onComplete = () => !isExpanded && setIsAnimating(false)
|
|
19
|
+
|
|
12
20
|
if (Platform.OS === 'web') {
|
|
13
|
-
return
|
|
21
|
+
if (!elementRef.current) return () => {}
|
|
22
|
+
|
|
23
|
+
// React Native Web does not pass `onTransitionEnd` through, must attach manually.
|
|
24
|
+
// https://github.com/necolas/react-native-web/pull/1713
|
|
25
|
+
const element = elementRef.current
|
|
26
|
+
element.addEventListener('transitionend', onComplete)
|
|
27
|
+
return () => element.removeEventListener('transitionend', onComplete)
|
|
14
28
|
}
|
|
15
29
|
|
|
16
30
|
const animationConfig = {
|
|
@@ -20,28 +34,27 @@ function useVerticalExpandAnimation({ containerHeight, isExpanded, tokens }) {
|
|
|
20
34
|
useNativeDriver: false
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
Animated.timing(expandAnimatedValue, animationConfig)
|
|
37
|
+
const animation = Animated.timing(expandAnimatedValue, animationConfig)
|
|
38
|
+
animation.start(onComplete)
|
|
39
|
+
return () => animation.stop()
|
|
24
40
|
}, [isExpanded, expandAnimatedValue, containerHeight, expandDuration, collapseDuration])
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
// Without `visibility: 'hidden', descendents are focusable on web even when collapsed
|
|
43
|
+
const containerStyles = !isExpanded && !isAnimating ? { visibility: 'hidden' } : {}
|
|
27
44
|
|
|
28
45
|
// don't visually collapse the container until we have it measured
|
|
29
46
|
if (containerHeight !== null) {
|
|
30
47
|
if (Platform.OS === 'web') {
|
|
31
48
|
const transitionDuration = isExpanded ? expandDuration : collapseDuration
|
|
49
|
+
containerStyles.transition = `height ${transitionDuration}ms ease-in-out`
|
|
32
50
|
|
|
33
|
-
containerStyles =
|
|
34
|
-
transition: `height ${transitionDuration}ms ease-in-out`,
|
|
35
|
-
height: isExpanded ? containerHeight : 0
|
|
36
|
-
}
|
|
51
|
+
containerStyles.height = isExpanded ? containerHeight : 0
|
|
37
52
|
} else {
|
|
38
|
-
containerStyles =
|
|
39
|
-
height: expandAnimatedValue
|
|
40
|
-
}
|
|
53
|
+
containerStyles.height = expandAnimatedValue
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
return containerStyles
|
|
57
|
+
return [containerStyles, elementRef]
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
export default useVerticalExpandAnimation
|