@telus-uds/components-base 1.5.0 → 1.7.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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +13 -0
- package/CHANGELOG.json +154 -1
- package/CHANGELOG.md +47 -2
- package/__tests__/FlexGrid/Row.test.jsx +100 -25
- package/__tests__/utils/containUniqueFields.test.js +25 -0
- package/component-docs.json +78 -26
- package/generate-component-docs.js +20 -7
- package/lib/Button/ButtonBase.js +1 -1
- package/lib/Button/ButtonGroup.js +20 -12
- package/lib/Card/PressableCardBase.js +1 -1
- package/lib/Checkbox/Checkbox.js +27 -16
- package/lib/Checkbox/CheckboxGroup.js +19 -5
- package/lib/ExpandCollapse/Panel.js +10 -10
- package/lib/FlexGrid/Col/Col.js +13 -3
- package/lib/FlexGrid/Row/Row.js +8 -2
- package/lib/InputLabel/InputLabel.js +27 -25
- package/lib/Link/LinkBase.js +19 -6
- package/lib/Link/TextButton.js +1 -10
- package/lib/List/ListItem.js +22 -12
- package/lib/Modal/Modal.js +18 -18
- package/lib/Radio/Radio.js +23 -12
- package/lib/Radio/RadioGroup.js +12 -3
- package/lib/RadioCard/RadioCard.js +1 -1
- package/lib/RadioCard/RadioCardGroup.js +11 -2
- package/lib/Search/Search.js +27 -19
- package/lib/Select/Select.js +2 -3
- package/lib/Tags/Tags.js +23 -17
- package/lib/TextInput/TextArea.js +2 -2
- package/lib/TextInput/TextInput.js +12 -2
- package/lib/TextInput/TextInputBase.js +1 -1
- package/lib/TextInput/propTypes.js +8 -1
- package/lib/ToggleSwitch/ToggleSwitch.js +5 -2
- package/lib/ToggleSwitch/ToggleSwitchGroup.js +20 -12
- package/lib/Typography/Typography.js +12 -10
- package/lib/index.js +22 -1
- package/lib/utils/containUniqueFields.js +34 -0
- package/lib/utils/index.js +10 -1
- package/lib/utils/input.js +5 -6
- package/lib/utils/props/handlerProps.js +72 -0
- package/lib/utils/props/index.js +32 -0
- package/lib/utils/props/inputSupportsProps.js +3 -5
- package/lib/utils/props/linkProps.js +3 -7
- package/lib/utils/props/textInputProps.js +206 -0
- package/lib/utils/props/textProps.js +72 -0
- package/lib-module/Button/ButtonBase.js +2 -2
- package/lib-module/Button/ButtonGroup.js +15 -6
- package/lib-module/Card/PressableCardBase.js +2 -2
- package/lib-module/Checkbox/Checkbox.js +28 -17
- package/lib-module/Checkbox/CheckboxGroup.js +20 -7
- package/lib-module/ExpandCollapse/Panel.js +10 -10
- package/lib-module/FlexGrid/Col/Col.js +13 -3
- package/lib-module/FlexGrid/Row/Row.js +8 -2
- package/lib-module/InputLabel/InputLabel.js +28 -25
- package/lib-module/Link/LinkBase.js +19 -6
- package/lib-module/Link/TextButton.js +1 -10
- package/lib-module/List/ListItem.js +22 -12
- package/lib-module/Modal/Modal.js +19 -19
- package/lib-module/Radio/Radio.js +24 -13
- package/lib-module/Radio/RadioGroup.js +13 -4
- package/lib-module/RadioCard/RadioCard.js +2 -2
- package/lib-module/RadioCard/RadioCardGroup.js +12 -3
- package/lib-module/Search/Search.js +29 -21
- package/lib-module/Select/Select.js +2 -3
- package/lib-module/Tags/Tags.js +18 -11
- package/lib-module/TextInput/TextArea.js +3 -3
- package/lib-module/TextInput/TextInput.js +11 -3
- package/lib-module/TextInput/TextInputBase.js +2 -2
- package/lib-module/TextInput/propTypes.js +7 -1
- package/lib-module/ToggleSwitch/ToggleSwitch.js +6 -3
- package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +15 -6
- package/lib-module/Typography/Typography.js +13 -11
- package/lib-module/index.js +1 -1
- package/lib-module/utils/containUniqueFields.js +26 -0
- package/lib-module/utils/index.js +2 -1
- package/lib-module/utils/input.js +6 -6
- package/lib-module/utils/props/handlerProps.js +59 -0
- package/lib-module/utils/props/index.js +3 -0
- package/lib-module/utils/props/inputSupportsProps.js +3 -5
- package/lib-module/utils/props/linkProps.js +3 -7
- package/lib-module/utils/props/textInputProps.js +193 -0
- package/lib-module/utils/props/textProps.js +59 -0
- package/package.json +5 -5
- package/src/Button/ButtonBase.jsx +8 -2
- package/src/Button/ButtonGroup.jsx +51 -34
- package/src/Card/PressableCardBase.jsx +6 -1
- package/src/Checkbox/Checkbox.jsx +35 -23
- package/src/Checkbox/CheckboxGroup.jsx +52 -22
- package/src/ExpandCollapse/Panel.jsx +9 -9
- package/src/FlexGrid/Col/Col.jsx +11 -2
- package/src/FlexGrid/Row/Row.jsx +8 -2
- package/src/InputLabel/InputLabel.jsx +36 -27
- package/src/Link/LinkBase.jsx +20 -4
- package/src/Link/TextButton.jsx +1 -19
- package/src/List/ListItem.jsx +17 -9
- package/src/Modal/Modal.jsx +30 -26
- package/src/Radio/Radio.jsx +26 -14
- package/src/Radio/RadioGroup.jsx +39 -21
- package/src/RadioCard/RadioCard.jsx +6 -1
- package/src/RadioCard/RadioCardGroup.jsx +17 -1
- package/src/Search/Search.jsx +33 -21
- package/src/Select/Select.jsx +2 -2
- package/src/Tags/Tags.jsx +23 -9
- package/src/TextInput/TextArea.jsx +7 -1
- package/src/TextInput/TextInput.jsx +15 -3
- package/src/TextInput/TextInputBase.jsx +8 -1
- package/src/TextInput/propTypes.js +7 -1
- package/src/ToggleSwitch/ToggleSwitch.jsx +11 -2
- package/src/ToggleSwitch/ToggleSwitchGroup.jsx +19 -6
- package/src/Typography/Typography.jsx +13 -9
- package/src/index.js +4 -1
- package/src/utils/containUniqueFields.js +32 -0
- package/src/utils/index.js +1 -0
- package/src/utils/input.js +5 -7
- package/src/utils/props/handlerProps.js +47 -0
- package/src/utils/props/index.js +3 -0
- package/src/utils/props/inputSupportsProps.js +3 -4
- package/src/utils/props/linkProps.js +3 -6
- package/src/utils/props/textInputProps.js +177 -0
- package/src/utils/props/textProps.js +58 -0
- package/stories/InputLabel/InputLabel.stories.jsx +25 -28
- package/stories/Modal/Modal.stories.jsx +25 -0
- package/stories/Search/Search.stories.jsx +53 -3
- package/stories/TextInput/TextInput.stories.jsx +40 -2
- package/__tests__/Link/LinkBase.test.jsx +0 -22
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
|
|
package/src/Modal/Modal.jsx
CHANGED
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
selectSystemProps,
|
|
17
17
|
useCopy,
|
|
18
18
|
variantProp,
|
|
19
|
-
viewProps
|
|
19
|
+
viewProps,
|
|
20
|
+
selectTokens
|
|
20
21
|
} from '../utils'
|
|
21
22
|
import { useViewport } from '../ViewportProvider'
|
|
22
|
-
import
|
|
23
|
+
import IconButton from '../IconButton'
|
|
23
24
|
import dictionary from './dictionary'
|
|
24
25
|
|
|
25
26
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
@@ -70,11 +71,6 @@ const selectCloseButtonContainerStyles = ({ paddingRight, paddingTop }) => ({
|
|
|
70
71
|
paddingTop
|
|
71
72
|
})
|
|
72
73
|
|
|
73
|
-
const selectCloseIconProps = ({ closeIconSize, closeIconColor }) => ({
|
|
74
|
-
size: closeIconSize,
|
|
75
|
-
color: closeIconColor
|
|
76
|
-
})
|
|
77
|
-
|
|
78
74
|
/**
|
|
79
75
|
* A modal window is a secondary window that opens on top of the main one.
|
|
80
76
|
* Users have to interact with it before they can carry out their task and return to the main window.
|
|
@@ -90,7 +86,7 @@ const selectCloseIconProps = ({ closeIconSize, closeIconColor }) => ({
|
|
|
90
86
|
* - Don’t use modals consecutively
|
|
91
87
|
*/
|
|
92
88
|
const Modal = forwardRef(
|
|
93
|
-
({ children, isOpen, onClose, maxWidth, tokens, variant, copy, ...rest }, ref) => {
|
|
89
|
+
({ children, isOpen, onClose, maxWidth, tokens, variant, copy, closeButton, ...rest }, ref) => {
|
|
94
90
|
const viewport = useViewport()
|
|
95
91
|
const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth })
|
|
96
92
|
|
|
@@ -107,11 +103,14 @@ const Modal = forwardRef(
|
|
|
107
103
|
if (event.key === 'Escape') onClose()
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
// Show the custom react node passed to `closedButton` or the default close button if `closeButton` is `undefined`.
|
|
107
|
+
// Hide the close button if `closeButton` is `null`.
|
|
108
|
+
const showCloseButton = closeButton !== null
|
|
109
|
+
|
|
110
110
|
if (!isOpen) {
|
|
111
111
|
return null
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// TODO: replace the close button with IconButton when implemented (https://github.com/telus/universal-design-system/issues/281)
|
|
115
114
|
return (
|
|
116
115
|
<NativeModal transparent {...selectProps(rest)}>
|
|
117
116
|
<View style={[staticStyles.positioningContainer]}>
|
|
@@ -124,23 +123,24 @@ const Modal = forwardRef(
|
|
|
124
123
|
style={[staticStyles.modal, selectModalStyles(themeTokens)]}
|
|
125
124
|
onKeyUp={handleKeyUp}
|
|
126
125
|
>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<ButtonBase
|
|
134
|
-
onPress={handleClose}
|
|
135
|
-
accessibilityRole="button"
|
|
136
|
-
accessibilityLabel={closeLabel}
|
|
126
|
+
{showCloseButton && (
|
|
127
|
+
<View
|
|
128
|
+
style={[
|
|
129
|
+
staticStyles.closeButtonContainer,
|
|
130
|
+
selectCloseButtonContainerStyles(themeTokens)
|
|
131
|
+
]}
|
|
137
132
|
>
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
{closeButton || (
|
|
134
|
+
<IconButton
|
|
135
|
+
onPress={handleClose}
|
|
136
|
+
icon={CloseIconComponent}
|
|
137
|
+
accessibilityRole="button"
|
|
138
|
+
accessibilityLabel={closeLabel}
|
|
139
|
+
tokens={selectTokens('IconButton', themeTokens, 'close')}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
142
|
+
</View>
|
|
143
|
+
)}
|
|
144
144
|
{children}
|
|
145
145
|
</View>
|
|
146
146
|
</View>
|
|
@@ -164,7 +164,11 @@ Modal.propTypes = {
|
|
|
164
164
|
onClose: PropTypes.func,
|
|
165
165
|
maxWidth: PropTypes.bool,
|
|
166
166
|
tokens: getTokensPropType('Modal'),
|
|
167
|
-
variant: variantProp.propType
|
|
167
|
+
variant: variantProp.propType,
|
|
168
|
+
/**
|
|
169
|
+
* Pass a react node to override the default close button or pass `null` to hide the close button.
|
|
170
|
+
*/
|
|
171
|
+
closeButton: PropTypes.node
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
export default Modal
|
package/src/Radio/Radio.jsx
CHANGED
|
@@ -7,6 +7,7 @@ import RadioButton, { selectRadioButtonTokens } from './RadioButton'
|
|
|
7
7
|
import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
|
|
8
8
|
import {
|
|
9
9
|
a11yProps,
|
|
10
|
+
focusHandlerProps,
|
|
10
11
|
getTokensPropType,
|
|
11
12
|
selectSystemProps,
|
|
12
13
|
useInputValue,
|
|
@@ -16,7 +17,11 @@ import {
|
|
|
16
17
|
} from '../utils'
|
|
17
18
|
import StackView from '../StackView'
|
|
18
19
|
|
|
19
|
-
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
20
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
21
|
+
a11yProps,
|
|
22
|
+
focusHandlerProps,
|
|
23
|
+
viewProps
|
|
24
|
+
])
|
|
20
25
|
|
|
21
26
|
const selectContainerStyles = ({
|
|
22
27
|
containerBackgroundColor,
|
|
@@ -154,23 +159,29 @@ const Radio = forwardRef(
|
|
|
154
159
|
>
|
|
155
160
|
{({ focused: focus, hovered: hover, pressed }) => {
|
|
156
161
|
const stateTokens = getTokens({ focus, hover, pressed })
|
|
162
|
+
const labelStyles = selectLabelStyles(stateTokens)
|
|
163
|
+
const alignWithLabel = label
|
|
164
|
+
? [staticStyles.alignWithLabel, { height: labelStyles.lineHeight }]
|
|
165
|
+
: null
|
|
157
166
|
|
|
158
167
|
return (
|
|
159
168
|
<StackView space={0}>
|
|
160
169
|
<View style={[staticStyles.container, selectContainerStyles(stateTokens)]}>
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
<View style={alignWithLabel}>
|
|
171
|
+
<RadioButton
|
|
172
|
+
tokens={selectRadioButtonTokens(stateTokens)}
|
|
173
|
+
isControlled={isControlled}
|
|
174
|
+
isChecked={isChecked}
|
|
175
|
+
inactive={inactive}
|
|
176
|
+
defaultChecked={defaultChecked}
|
|
177
|
+
inputId={inputId}
|
|
178
|
+
handleChange={handleChange}
|
|
179
|
+
name={inputName}
|
|
180
|
+
value={value}
|
|
181
|
+
/>
|
|
182
|
+
</View>
|
|
172
183
|
{Boolean(label) && (
|
|
173
|
-
<Text style={
|
|
184
|
+
<Text style={labelStyles}>
|
|
174
185
|
<RadioLabel forId={inputId}>{label}</RadioLabel>
|
|
175
186
|
</Text>
|
|
176
187
|
)}
|
|
@@ -242,5 +253,6 @@ Radio.propTypes = {
|
|
|
242
253
|
export default Radio
|
|
243
254
|
|
|
244
255
|
const staticStyles = StyleSheet.create({
|
|
245
|
-
container: { flexDirection: 'row', alignItems: 'center' }
|
|
256
|
+
container: { flexDirection: 'row', alignItems: 'center' },
|
|
257
|
+
alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' }
|
|
246
258
|
})
|
package/src/Radio/RadioGroup.jsx
CHANGED
|
@@ -6,6 +6,8 @@ import { useViewport } from '../ViewportProvider'
|
|
|
6
6
|
import { useThemeTokens } from '../ThemeProvider'
|
|
7
7
|
import {
|
|
8
8
|
a11yProps,
|
|
9
|
+
containUniqueFields,
|
|
10
|
+
focusHandlerProps,
|
|
9
11
|
getTokensPropType,
|
|
10
12
|
selectSystemProps,
|
|
11
13
|
useInputValue,
|
|
@@ -17,6 +19,11 @@ import Radio from './Radio'
|
|
|
17
19
|
import Fieldset from '../Fieldset'
|
|
18
20
|
|
|
19
21
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
22
|
+
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
23
|
+
a11yProps,
|
|
24
|
+
focusHandlerProps,
|
|
25
|
+
viewProps
|
|
26
|
+
])
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
29
|
* A group of Radios that behave as a radio group. Use when users select a single choice from mutually
|
|
@@ -100,28 +107,38 @@ const RadioGroup = forwardRef(
|
|
|
100
107
|
onChange,
|
|
101
108
|
readOnly: readOnly || inactive
|
|
102
109
|
})
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
|
|
111
|
+
const uniqueFields = ['id', 'label']
|
|
112
|
+
if (!containUniqueFields(items, uniqueFields)) {
|
|
113
|
+
throw new Error(`RadioGroup items must have unique ${uniqueFields.join(', ')}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const radios = items.map(
|
|
117
|
+
({ label, id, onChange: itemOnChange, ref: itemRef, ...itemRest }, index) => {
|
|
118
|
+
const radioId = id || `Radio[${index}]`
|
|
119
|
+
const isChecked = currentValue === radioId
|
|
120
|
+
const handleChange = (newCheckedState, event) => {
|
|
121
|
+
if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
|
|
122
|
+
if (newCheckedState) setValue(radioId, event)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Radio
|
|
127
|
+
ref={itemRef}
|
|
128
|
+
key={radioId}
|
|
129
|
+
id={radioId}
|
|
130
|
+
checked={isChecked}
|
|
131
|
+
onChange={handleChange}
|
|
132
|
+
inactive={inactive}
|
|
133
|
+
label={label}
|
|
134
|
+
name={inputGroupName}
|
|
135
|
+
tokens={radioTokens}
|
|
136
|
+
variant={variant}
|
|
137
|
+
{...selectItemProps(itemRest)}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
109
140
|
}
|
|
110
|
-
|
|
111
|
-
<Radio
|
|
112
|
-
ref={itemRef}
|
|
113
|
-
key={radioId}
|
|
114
|
-
id={radioId}
|
|
115
|
-
checked={isChecked}
|
|
116
|
-
onChange={handleChange}
|
|
117
|
-
inactive={inactive}
|
|
118
|
-
label={label}
|
|
119
|
-
name={inputGroupName}
|
|
120
|
-
tokens={radioTokens}
|
|
121
|
-
variant={variant}
|
|
122
|
-
/>
|
|
123
|
-
)
|
|
124
|
-
})
|
|
141
|
+
)
|
|
125
142
|
|
|
126
143
|
return (
|
|
127
144
|
<Fieldset
|
|
@@ -163,6 +180,7 @@ RadioGroup.propTypes = {
|
|
|
163
180
|
*/
|
|
164
181
|
items: PropTypes.arrayOf(
|
|
165
182
|
PropTypes.exact({
|
|
183
|
+
...selectedItemPropTypes,
|
|
166
184
|
label: PropTypes.string,
|
|
167
185
|
id: PropTypes.string,
|
|
168
186
|
onChange: PropTypes.func,
|
|
@@ -5,6 +5,7 @@ import { StyleSheet, Text, View } from 'react-native'
|
|
|
5
5
|
import { useThemeTokensCallback, applyTextStyles } from '../ThemeProvider'
|
|
6
6
|
import {
|
|
7
7
|
a11yProps,
|
|
8
|
+
focusHandlerProps,
|
|
8
9
|
getTokensPropType,
|
|
9
10
|
selectSystemProps,
|
|
10
11
|
selectTokens,
|
|
@@ -17,7 +18,11 @@ import { PressableCardBase, selectPressableCardTokens } from '../Card'
|
|
|
17
18
|
import StackView from '../StackView'
|
|
18
19
|
import RadioButton, { selectRadioButtonTokens } from '../Radio/RadioButton'
|
|
19
20
|
|
|
20
|
-
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
21
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
22
|
+
a11yProps,
|
|
23
|
+
focusHandlerProps,
|
|
24
|
+
viewProps
|
|
25
|
+
])
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* A Card that behaves like a radio button. Use when users select a single choice from mutually exclusive options
|
|
@@ -5,6 +5,8 @@ import { useViewport } from '../ViewportProvider'
|
|
|
5
5
|
import { useThemeTokens } from '../ThemeProvider'
|
|
6
6
|
import {
|
|
7
7
|
a11yProps,
|
|
8
|
+
containUniqueFields,
|
|
9
|
+
focusHandlerProps,
|
|
8
10
|
getTokensPropType,
|
|
9
11
|
selectSystemProps,
|
|
10
12
|
useInputValue,
|
|
@@ -16,6 +18,11 @@ import RadioCard from './RadioCard'
|
|
|
16
18
|
import Fieldset from '../Fieldset'
|
|
17
19
|
|
|
18
20
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
21
|
+
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
22
|
+
a11yProps,
|
|
23
|
+
focusHandlerProps,
|
|
24
|
+
viewProps
|
|
25
|
+
])
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* A group of Cards that behave as a radio button group. Use when users select a single choice from mutually
|
|
@@ -105,6 +112,12 @@ const RadioCardGroup = forwardRef(
|
|
|
105
112
|
// Needs 'radiogroup' role on direct parent of radios for MacOS Voiceover's numbering to work,
|
|
106
113
|
// and also needs 'radiogroup' role on fieldset for correct description on focusing the set.
|
|
107
114
|
// TODO: test this on more web screen readers.
|
|
115
|
+
|
|
116
|
+
const uniqueFields = ['id']
|
|
117
|
+
if (!containUniqueFields(items, uniqueFields)) {
|
|
118
|
+
throw new Error(`RadioCardGroup items must have unique ${uniqueFields.join(', ')}`)
|
|
119
|
+
}
|
|
120
|
+
|
|
108
121
|
return (
|
|
109
122
|
<Fieldset
|
|
110
123
|
ref={ref}
|
|
@@ -121,12 +134,13 @@ const RadioCardGroup = forwardRef(
|
|
|
121
134
|
>
|
|
122
135
|
{(props) => (
|
|
123
136
|
<StackContainer space={space} accessibilityRole="radiogroup">
|
|
124
|
-
{items.map(({ title, content, id, onChange: itemOnChange }, index) => {
|
|
137
|
+
{items.map(({ title, content, id, onChange: itemOnChange, ...itemRest }, index) => {
|
|
125
138
|
const cardId = id || `RadioCard[${index}]`
|
|
126
139
|
const handleChange = (newCheckedState, event) => {
|
|
127
140
|
if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
|
|
128
141
|
if (newCheckedState) setValue(cardId, event)
|
|
129
142
|
}
|
|
143
|
+
|
|
130
144
|
return (
|
|
131
145
|
<RadioCard
|
|
132
146
|
key={cardId}
|
|
@@ -139,6 +153,7 @@ const RadioCardGroup = forwardRef(
|
|
|
139
153
|
tokens={radioCardTokens}
|
|
140
154
|
variant={variant}
|
|
141
155
|
readOnly={readOnly}
|
|
156
|
+
{...selectItemProps(itemRest)}
|
|
142
157
|
{...props}
|
|
143
158
|
>
|
|
144
159
|
{content}
|
|
@@ -172,6 +187,7 @@ RadioCardGroup.propTypes = {
|
|
|
172
187
|
*/
|
|
173
188
|
items: PropTypes.arrayOf(
|
|
174
189
|
PropTypes.exact({
|
|
190
|
+
...selectedItemPropTypes,
|
|
175
191
|
title: PropTypes.string,
|
|
176
192
|
content: PropTypes.node,
|
|
177
193
|
id: PropTypes.string,
|
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.
|
package/src/Select/Select.jsx
CHANGED
|
@@ -194,7 +194,7 @@ const Select = forwardRef(
|
|
|
194
194
|
const handleMouseOver = () => setIsHovered(true)
|
|
195
195
|
const handleMouseOut = () => setIsHovered(false)
|
|
196
196
|
|
|
197
|
-
const {
|
|
197
|
+
const { supportsProps, ...selectedProps } = selectProps(rest)
|
|
198
198
|
|
|
199
199
|
const themeTokens = useThemeTokens('Select', tokens, variant, {
|
|
200
200
|
focus: isFocused,
|
|
@@ -236,7 +236,7 @@ const Select = forwardRef(
|
|
|
236
236
|
selectValidationIconContainerStyles(themeTokens)
|
|
237
237
|
]}
|
|
238
238
|
>
|
|
239
|
-
<ValidationIconComponent
|
|
239
|
+
<ValidationIconComponent {...selectValidationIconTokens(themeTokens)} />
|
|
240
240
|
</View>
|
|
241
241
|
)}
|
|
242
242
|
{IconComponent &&
|
package/src/Tags/Tags.jsx
CHANGED
|
@@ -10,17 +10,25 @@ import { useViewport } from '../ViewportProvider'
|
|
|
10
10
|
import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider'
|
|
11
11
|
import {
|
|
12
12
|
a11yProps,
|
|
13
|
+
containUniqueFields,
|
|
14
|
+
focusHandlerProps,
|
|
13
15
|
getTokensPropType,
|
|
14
16
|
pressProps,
|
|
15
17
|
selectSystemProps,
|
|
16
18
|
selectTokens,
|
|
19
|
+
useMultipleInputValues,
|
|
17
20
|
variantProp,
|
|
18
21
|
viewProps
|
|
19
|
-
} from '../utils
|
|
20
|
-
import { useMultipleInputValues } from '../utils/input'
|
|
22
|
+
} from '../utils'
|
|
21
23
|
import { getPressHandlersWithArgs } from '../utils/pressability'
|
|
22
24
|
|
|
23
|
-
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps,
|
|
25
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
26
|
+
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
27
|
+
a11yProps,
|
|
28
|
+
focusHandlerProps,
|
|
29
|
+
pressProps,
|
|
30
|
+
viewProps
|
|
31
|
+
])
|
|
24
32
|
|
|
25
33
|
const selectIconTextTokens = ({
|
|
26
34
|
icon,
|
|
@@ -95,6 +103,11 @@ const Tags = forwardRef(
|
|
|
95
103
|
})
|
|
96
104
|
const itemA11yRole = 'checkbox'
|
|
97
105
|
|
|
106
|
+
const uniqueFields = ['id', 'label']
|
|
107
|
+
if (!containUniqueFields(items, uniqueFields)) {
|
|
108
|
+
throw new Error(`Tags items must have unique ${uniqueFields.join(', ')}`)
|
|
109
|
+
}
|
|
110
|
+
|
|
98
111
|
return (
|
|
99
112
|
<StackWrap
|
|
100
113
|
ref={ref}
|
|
@@ -103,22 +116,22 @@ const Tags = forwardRef(
|
|
|
103
116
|
direction={direction}
|
|
104
117
|
tokens={stackTokens}
|
|
105
118
|
>
|
|
106
|
-
{items.map(({ label, id = label,
|
|
119
|
+
{items.map(({ label, id = label, ref: itemRef, ...itemRest }, index) => {
|
|
107
120
|
const isSelected = currentValues.includes(id)
|
|
108
121
|
|
|
109
122
|
// Pass an object of relevant component state as first argument for any passed-in press handlers
|
|
110
123
|
const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
|
|
111
124
|
|
|
112
125
|
const handlePress = (event) => {
|
|
113
|
-
if (pressHandlers.onPress) pressHandlers.onPress()
|
|
126
|
+
if (pressHandlers.onPress) pressHandlers.onPress(event)
|
|
114
127
|
toggleOneValue(id, event)
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
const
|
|
130
|
+
const itemProps = {
|
|
118
131
|
accessibilityState: { checked: isSelected },
|
|
119
132
|
accessibilityRole: itemA11yRole,
|
|
120
|
-
|
|
121
|
-
...
|
|
133
|
+
...a11yProps.getPositionInSet(items.length, index),
|
|
134
|
+
...selectItemProps(itemRest)
|
|
122
135
|
}
|
|
123
136
|
|
|
124
137
|
return (
|
|
@@ -130,7 +143,7 @@ const Tags = forwardRef(
|
|
|
130
143
|
tokens={getButtonTokens}
|
|
131
144
|
selected={isSelected}
|
|
132
145
|
inactive={inactive}
|
|
133
|
-
{...
|
|
146
|
+
{...itemProps}
|
|
134
147
|
>
|
|
135
148
|
{({ textStyles, ...buttonState }) => {
|
|
136
149
|
// TODO: once Icon/IconButton designs are stable, see if this sort of styling around
|
|
@@ -183,6 +196,7 @@ Tags.propTypes = {
|
|
|
183
196
|
*/
|
|
184
197
|
items: PropTypes.arrayOf(
|
|
185
198
|
PropTypes.shape({
|
|
199
|
+
...selectedItemPropTypes,
|
|
186
200
|
/**
|
|
187
201
|
* The text displayed to the user in the button, describing this option,
|
|
188
202
|
* passed to the button as its child.
|
|
@@ -3,9 +3,12 @@ import React, { forwardRef, useState } from 'react'
|
|
|
3
3
|
import { Platform } from 'react-native'
|
|
4
4
|
import {
|
|
5
5
|
a11yProps,
|
|
6
|
+
focusHandlerProps,
|
|
6
7
|
getTokensPropType,
|
|
7
8
|
inputSupportsProps,
|
|
8
9
|
selectSystemProps,
|
|
10
|
+
textInputHandlerProps,
|
|
11
|
+
textInputProps,
|
|
9
12
|
variantProp,
|
|
10
13
|
viewProps
|
|
11
14
|
} from '../utils'
|
|
@@ -16,7 +19,10 @@ import textInputPropTypes from './propTypes'
|
|
|
16
19
|
|
|
17
20
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
18
21
|
a11yProps,
|
|
22
|
+
focusHandlerProps,
|
|
19
23
|
inputSupportsProps,
|
|
24
|
+
textInputHandlerProps,
|
|
25
|
+
textInputProps,
|
|
20
26
|
viewProps
|
|
21
27
|
])
|
|
22
28
|
|
|
@@ -60,7 +66,7 @@ const TextArea = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
|
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
const {
|
|
69
|
+
const { supportsProps, ...selectedProps } = selectProps(rest)
|
|
64
70
|
|
|
65
71
|
const inputProps = {
|
|
66
72
|
...selectedProps,
|