@telus-uds/components-base 1.0.0 → 1.2.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 +4 -0
- package/.storybook/preview.js +37 -0
- package/.ultra.cache.json +1 -1
- package/CHANGELOG.md +26 -0
- package/README.md +1 -1
- package/__fixtures__/Accessible.js +4 -2
- package/__fixtures__/Accessible.native.js +5 -2
- package/__fixtures__/testTheme.js +9 -0
- package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +1 -0
- package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +10 -0
- package/__tests__/ToggleSwitch/ToggleSwitchGroup.test.jsx +192 -0
- package/babel.config.js +9 -16
- package/component-docs.json +10131 -0
- package/generate-component-docs.js +56 -0
- package/lib/Box/Box.js +1 -0
- package/lib/Button/ButtonBase.js +20 -6
- package/lib/Card/CardBase.js +1 -0
- package/lib/Card/PressableCardBase.js +9 -3
- package/lib/Checkbox/Checkbox.js +0 -2
- package/lib/FlexGrid/FlexGrid.js +1 -1
- package/lib/IconButton/IconButton.js +8 -3
- package/lib/Link/LinkBase.js +10 -3
- package/lib/List/List.js +1 -2
- package/lib/List/ListItem.js +14 -27
- package/lib/List/index.js +15 -0
- package/lib/Pagination/PageButton.js +4 -8
- package/lib/Pagination/Pagination.js +16 -4
- package/lib/Pagination/SideButton.js +3 -1
- package/lib/Radio/Radio.js +0 -2
- package/lib/StackView/StackView.js +1 -0
- package/lib/StackView/StackWrap.js +9 -5
- package/lib/StackView/StackWrapBox.js +1 -0
- package/lib/Tabs/Tabs.js +12 -4
- package/lib/Tabs/TabsItem.js +12 -6
- package/lib/ToggleSwitch/ToggleSwitch.js +99 -37
- package/lib/ToggleSwitch/ToggleSwitchGroup.js +230 -0
- package/lib/ToggleSwitch/index.js +14 -4
- package/lib/index.js +28 -9
- package/lib/utils/a11y/propTypes.js +61 -0
- package/lib/utils/a11y/propTypes.native.js +47 -0
- package/lib/utils/index.js +10 -1
- package/lib/utils/propTypes.js +35 -91
- package/lib/utils/withLinkRouter.js +98 -0
- package/package.json +10 -6
- package/release-context.json +4 -4
- package/src/Box/Box.jsx +1 -0
- package/src/Button/ButtonBase.jsx +11 -4
- package/src/Card/CardBase.jsx +1 -0
- package/src/Card/PressableCardBase.jsx +6 -4
- package/src/Checkbox/Checkbox.jsx +0 -2
- package/src/FlexGrid/FlexGrid.jsx +1 -1
- package/src/IconButton/IconButton.jsx +6 -4
- package/src/Link/LinkBase.jsx +6 -4
- package/src/List/List.jsx +1 -3
- package/src/List/ListItem.jsx +11 -26
- package/src/List/index.js +5 -0
- package/src/Pagination/PageButton.jsx +5 -8
- package/src/Pagination/Pagination.jsx +29 -2
- package/src/Pagination/SideButton.jsx +2 -2
- package/src/Radio/Radio.jsx +0 -2
- package/src/StackView/StackView.jsx +1 -0
- package/src/StackView/StackWrap.jsx +7 -6
- package/src/StackView/StackWrapBox.jsx +1 -0
- package/src/Tabs/Tabs.jsx +49 -22
- package/src/Tabs/TabsItem.jsx +11 -7
- package/src/ToggleSwitch/ToggleSwitch.jsx +92 -34
- package/src/ToggleSwitch/ToggleSwitchGroup.jsx +203 -0
- package/src/ToggleSwitch/index.js +2 -1
- package/src/index.js +2 -2
- package/src/utils/a11y/propTypes.js +61 -0
- package/src/utils/a11y/propTypes.native.js +39 -0
- package/src/utils/index.js +1 -0
- package/src/utils/propTypes.js +33 -111
- package/src/utils/withLinkRouter.jsx +68 -0
- package/stories/A11yText/A11yText.stories.jsx +1 -1
- package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +1 -1
- package/stories/Box/Box.stories.jsx +1 -1
- package/stories/Button/Button.stories.jsx +1 -1
- package/stories/Button/ButtonGroup.stories.jsx +1 -1
- package/stories/Button/ButtonLink.stories.jsx +1 -1
- package/stories/Card/Card.stories.jsx +1 -1
- package/stories/Checkbox/Checkbox.stories.jsx +1 -1
- package/stories/Divider/Divider.stories.jsx +1 -1
- package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +1 -1
- package/stories/Feedback/Feedback.stories.jsx +1 -1
- package/stories/FlexGrid/01 FlexGrid.stories.jsx +1 -1
- package/stories/FlexGrid/02 Row.stories.jsx +1 -1
- package/stories/FlexGrid/03 Col.stories.jsx +1 -1
- package/stories/Icon/Icon.stories.jsx +1 -1
- package/stories/IconButton/IconButton.stories.jsx +1 -1
- package/stories/InputLabel/InputLabel.stories.jsx +1 -1
- package/stories/Link/ChevronLink.stories.jsx +1 -1
- package/stories/Link/Link.stories.jsx +1 -1
- package/stories/Link/TextButton.stories.jsx +1 -1
- package/stories/List/List.stories.jsx +1 -1
- package/stories/Modal/Modal.stories.jsx +1 -1
- package/stories/Notification/Notification.stories.jsx +1 -1
- package/stories/Pagination/Pagination.stories.jsx +1 -1
- package/stories/Progress/Progress.stories.jsx +1 -1
- package/stories/Radio/Radio.stories.jsx +1 -1
- package/stories/RadioCard/RadioCard.stories.jsx +1 -1
- package/stories/Search/Search.stories.jsx +1 -1
- package/stories/Select/Select.stories.jsx +1 -1
- package/stories/SideNav/SideNav.stories.jsx +1 -1
- package/stories/SideNav/SideNavItem.stories.jsx +1 -1
- package/stories/SideNav/SideNavItemsGroup.stories.jsx +1 -1
- package/stories/Skeleton/Skeleton.stories.jsx +1 -1
- package/stories/Spacer/Spacer.stories.jsx +1 -1
- package/stories/StackView/StackView.stories.jsx +1 -1
- package/stories/StackView/StackWrap.stories.jsx +1 -1
- package/stories/StepTracker/StepTracker.stories.jsx +1 -1
- package/stories/Tabs/Tabs.stories.jsx +1 -1
- package/stories/Tags/Tags.stories.jsx +1 -1
- package/stories/TextInput/TextArea.stories.jsx +2 -1
- package/stories/TextInput/TextInput.stories.jsx +1 -1
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +6 -2
- package/stories/ToggleSwitch/ToggleSwitchGroup.stories.jsx +81 -0
- package/stories/Tooltip/Tooltip.stories.jsx +1 -1
- package/stories/TooltipButton/TooltipButton.stories.jsx +1 -1
- package/stories/Typography/Typography.stories.jsx +1 -1
- package/stories/supports.jsx +2 -3
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import ABBPropTypes from 'airbnb-prop-types'
|
|
4
|
+
import { Platform } from 'react-native'
|
|
5
|
+
|
|
6
|
+
import ToggleSwitch from './ToggleSwitch'
|
|
7
|
+
import Fieldset from '../Fieldset'
|
|
8
|
+
import { getStackedContent } from '../StackView'
|
|
9
|
+
import { useViewport } from '../ViewportProvider'
|
|
10
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
11
|
+
import { a11yProps, pressProps, variantProp, getTokensPropType } from '../utils/propTypes'
|
|
12
|
+
import { useMultipleInputValues } from '../utils/input'
|
|
13
|
+
|
|
14
|
+
const ToggleSwitchGroup = forwardRef(
|
|
15
|
+
(
|
|
16
|
+
{
|
|
17
|
+
variant,
|
|
18
|
+
tokens,
|
|
19
|
+
items = [],
|
|
20
|
+
values,
|
|
21
|
+
initialValues,
|
|
22
|
+
maxValues = 1,
|
|
23
|
+
onChange,
|
|
24
|
+
readOnly = false,
|
|
25
|
+
inactive = false,
|
|
26
|
+
feedback,
|
|
27
|
+
hint,
|
|
28
|
+
tooltip,
|
|
29
|
+
legend,
|
|
30
|
+
name: inputGroupName,
|
|
31
|
+
accessibilityRole = maxValues === 1
|
|
32
|
+
? 'radiogroup' // radiogroup is cross-platform; only web aria has generic groups
|
|
33
|
+
: Platform.select({ web: 'group', default: 'none' }),
|
|
34
|
+
toggleSwitchTokens,
|
|
35
|
+
validation,
|
|
36
|
+
...rest
|
|
37
|
+
},
|
|
38
|
+
ref
|
|
39
|
+
) => {
|
|
40
|
+
const viewport = useViewport()
|
|
41
|
+
|
|
42
|
+
const { space, fieldSpace } = useThemeTokens('ToggleSwitchGroup', tokens, variant, {
|
|
43
|
+
viewport
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const { currentValues, toggleOneValue } = useMultipleInputValues({
|
|
47
|
+
initialValues,
|
|
48
|
+
values,
|
|
49
|
+
maxValues,
|
|
50
|
+
onChange,
|
|
51
|
+
readOnly
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const a11y = a11yProps.select({
|
|
55
|
+
accessibilityRole,
|
|
56
|
+
...rest
|
|
57
|
+
})
|
|
58
|
+
const itemA11yRole = a11y.accessibilityRole === 'radiogroup' ? 'radio' : 'switch'
|
|
59
|
+
|
|
60
|
+
const toggleSwitches = items.map(
|
|
61
|
+
(
|
|
62
|
+
{
|
|
63
|
+
label,
|
|
64
|
+
id = label,
|
|
65
|
+
accessibilityLabel = label,
|
|
66
|
+
onChange: itemOnChange,
|
|
67
|
+
ref: itemRef,
|
|
68
|
+
tooltip: itemTooltip
|
|
69
|
+
},
|
|
70
|
+
index
|
|
71
|
+
) => {
|
|
72
|
+
const isSelected = currentValues.includes(id)
|
|
73
|
+
|
|
74
|
+
const handleChange = (newCheckedState, event) => {
|
|
75
|
+
if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
|
|
76
|
+
toggleOneValue(id, event)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const itemA11y = {
|
|
80
|
+
accessibilityState: { checked: isSelected },
|
|
81
|
+
accessibilityRole: itemA11yRole,
|
|
82
|
+
accessibilityLabel,
|
|
83
|
+
...a11yProps.getPositionInSet(items.length, index)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<ToggleSwitch
|
|
88
|
+
id={id}
|
|
89
|
+
ref={itemRef}
|
|
90
|
+
key={id}
|
|
91
|
+
onChange={handleChange}
|
|
92
|
+
tokens={toggleSwitchTokens}
|
|
93
|
+
value={isSelected}
|
|
94
|
+
inactive={inactive}
|
|
95
|
+
label={label}
|
|
96
|
+
tooltip={itemTooltip}
|
|
97
|
+
{...itemA11y}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Fieldset
|
|
105
|
+
ref={ref}
|
|
106
|
+
name={inputGroupName}
|
|
107
|
+
legend={legend}
|
|
108
|
+
tooltip={tooltip}
|
|
109
|
+
hint={hint}
|
|
110
|
+
space={fieldSpace}
|
|
111
|
+
feedback={feedback}
|
|
112
|
+
inactive={inactive}
|
|
113
|
+
validation={validation}
|
|
114
|
+
{...a11y}
|
|
115
|
+
>
|
|
116
|
+
{getStackedContent(toggleSwitches, { space, direction: 'column' })}
|
|
117
|
+
</Fieldset>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
ToggleSwitchGroup.displayName = 'ToggleSwitchGroup'
|
|
122
|
+
|
|
123
|
+
ToggleSwitchGroup.propTypes = {
|
|
124
|
+
...a11yProps.propTypes,
|
|
125
|
+
...pressProps.propTypes,
|
|
126
|
+
tokens: getTokensPropType('ToggleSwitchGroup'),
|
|
127
|
+
variant: variantProp.propType,
|
|
128
|
+
/**
|
|
129
|
+
* The maximum number of items a user may select at once. Defaults to 1 and behaves
|
|
130
|
+
* like radio buttons. To have no limit and allow any number of selections, pass `null`.
|
|
131
|
+
*/
|
|
132
|
+
maxValues: PropTypes.number,
|
|
133
|
+
/**
|
|
134
|
+
* The options a user may select
|
|
135
|
+
*/
|
|
136
|
+
items: PropTypes.arrayOf(
|
|
137
|
+
PropTypes.shape({
|
|
138
|
+
/**
|
|
139
|
+
* The text displayed to the user on the label.
|
|
140
|
+
*/
|
|
141
|
+
label: PropTypes.string.isRequired,
|
|
142
|
+
/**
|
|
143
|
+
* An optional accessibility label may be passed to each ToggleSwitch
|
|
144
|
+
* and will be applied as normal for a React Native accessibilityLabel prop.
|
|
145
|
+
*/
|
|
146
|
+
accessibilityLabel: PropTypes.string,
|
|
147
|
+
/**
|
|
148
|
+
* An optional unique string may be provided to identify this option,
|
|
149
|
+
* which will be used in code and passed to any onChange function.
|
|
150
|
+
* If not provided, the label is used.
|
|
151
|
+
*/
|
|
152
|
+
id: PropTypes.string,
|
|
153
|
+
/**
|
|
154
|
+
* An optional ref for one individual ToggleSwitch in the ToggleSwitchGroup
|
|
155
|
+
*/
|
|
156
|
+
ref: ABBPropTypes.ref()
|
|
157
|
+
})
|
|
158
|
+
),
|
|
159
|
+
/**
|
|
160
|
+
* If provided, this function is called when the current selection is changed
|
|
161
|
+
* and is passed an array of the `id`s of all currently selected `items`.
|
|
162
|
+
*/
|
|
163
|
+
onChange: PropTypes.func,
|
|
164
|
+
/**
|
|
165
|
+
* If the selected item(s) in the toggle switch group are to be controlled externally by
|
|
166
|
+
* a parent component, pass an array of strings as well as an `onChange` handler.
|
|
167
|
+
* Passing an array for "values" makes the ToggleSwitchGroup a "controlled" component that
|
|
168
|
+
* expects its state to be handled via `onChange` and so doesn't handle it itself.
|
|
169
|
+
*/
|
|
170
|
+
values: PropTypes.arrayOf(PropTypes.string),
|
|
171
|
+
/**
|
|
172
|
+
* If `values` is not passed, making the ToggleSwitchGroup an "uncontrolled" component
|
|
173
|
+
* managing its own selected state, a default set of selections may be provided.
|
|
174
|
+
* Changing the `initialValues` does not change the user's selections.
|
|
175
|
+
*/
|
|
176
|
+
initialValues: PropTypes.arrayOf(PropTypes.string),
|
|
177
|
+
/**
|
|
178
|
+
* Optional additional text giving more detail to help a user make a choice.
|
|
179
|
+
*/
|
|
180
|
+
hint: PropTypes.string,
|
|
181
|
+
/**
|
|
182
|
+
* Optional tooltip text content to include alongside the legend and hint.
|
|
183
|
+
*/
|
|
184
|
+
tooltip: PropTypes.string,
|
|
185
|
+
/**
|
|
186
|
+
* If provided, a Feedback element is rendered containing this text.
|
|
187
|
+
*/
|
|
188
|
+
feedback: PropTypes.string,
|
|
189
|
+
/**
|
|
190
|
+
* Main text used to describe this group, used in Fieldset's Legend element.
|
|
191
|
+
*/
|
|
192
|
+
legend: PropTypes.string,
|
|
193
|
+
/**
|
|
194
|
+
* Toggle switch token overrides.
|
|
195
|
+
*/
|
|
196
|
+
toggleSwitchTokens: getTokensPropType('ToggleSwitch'),
|
|
197
|
+
/**
|
|
198
|
+
* Current validation status of the group, passed to the feedback element if there is one.
|
|
199
|
+
*/
|
|
200
|
+
validation: PropTypes.oneOf(['error', 'success'])
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default ToggleSwitchGroup
|
package/src/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export * from './Icon'
|
|
|
17
17
|
export { default as IconButton } from './IconButton'
|
|
18
18
|
export { default as InputLabel } from './InputLabel'
|
|
19
19
|
export * from './Link'
|
|
20
|
-
export { default as List } from './List'
|
|
20
|
+
export { default as List, ListItem, ListBase } from './List'
|
|
21
21
|
export { default as Modal } from './Modal'
|
|
22
22
|
export { default as Notification } from './Notification'
|
|
23
23
|
export { default as Pagination } from './Pagination'
|
|
@@ -37,7 +37,7 @@ export { default as StepTracker } from './StepTracker'
|
|
|
37
37
|
export { default as Tabs } from './Tabs'
|
|
38
38
|
export { default as Tags } from './Tags'
|
|
39
39
|
export * from './TextInput'
|
|
40
|
-
export
|
|
40
|
+
export * from './ToggleSwitch'
|
|
41
41
|
export { default as Tooltip } from './Tooltip'
|
|
42
42
|
export { default as TooltipButton } from './TooltipButton'
|
|
43
43
|
export { default as Typography } from './Typography'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
|
|
3
|
+
import nativePropTypes from './propTypes.native'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
...nativePropTypes,
|
|
7
|
+
// React Native Web adds many a11y props that alias aria-* attributes
|
|
8
|
+
// Types based on https://necolas.github.io/react-native-web/docs/accessibility/
|
|
9
|
+
accessibilityActiveDescendant: PropTypes.string,
|
|
10
|
+
accessibilityAtomic: PropTypes.bool,
|
|
11
|
+
accessibilityAutoComplete: PropTypes.string,
|
|
12
|
+
accessibilityBusy: PropTypes.bool,
|
|
13
|
+
accessibilityChecked: PropTypes.oneOf([true, false, 'mixed']),
|
|
14
|
+
accessibilityColumnCount: PropTypes.number,
|
|
15
|
+
accessibilityColumnIndex: PropTypes.number,
|
|
16
|
+
accessibilityColumnSpan: PropTypes.number,
|
|
17
|
+
accessibilityControls: PropTypes.oneOfType([
|
|
18
|
+
PropTypes.string,
|
|
19
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
20
|
+
]),
|
|
21
|
+
accessibilityCurrent: PropTypes.oneOf([true, false, 'page', 'step', 'location', 'date', 'time']),
|
|
22
|
+
accessibilityDescribedBy: PropTypes.oneOfType([
|
|
23
|
+
PropTypes.string,
|
|
24
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
25
|
+
]),
|
|
26
|
+
accessibilityDetails: PropTypes.string,
|
|
27
|
+
accessibilityDisabled: PropTypes.bool,
|
|
28
|
+
accessibilityErrorMessage: PropTypes.string,
|
|
29
|
+
accessibilityExpanded: PropTypes.bool,
|
|
30
|
+
accessibilityFlowTo: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
31
|
+
accessibilityHasPopup: PropTypes.string,
|
|
32
|
+
accessibilityHidden: PropTypes.bool,
|
|
33
|
+
accessibilityInvalid: PropTypes.bool,
|
|
34
|
+
accessibilityKeyShortcuts: PropTypes.string,
|
|
35
|
+
accessibilityLabelledBy: PropTypes.oneOfType([
|
|
36
|
+
PropTypes.string,
|
|
37
|
+
PropTypes.arrayOf(PropTypes.string)
|
|
38
|
+
]),
|
|
39
|
+
accessibilityLevel: PropTypes.number,
|
|
40
|
+
accessibilityModal: PropTypes.bool,
|
|
41
|
+
accessibilityMultiline: PropTypes.bool,
|
|
42
|
+
accessibilityMultiSelectable: PropTypes.bool,
|
|
43
|
+
accessibilityOrientation: PropTypes.oneOf(['horizontal', 'vertical']),
|
|
44
|
+
accessibilityOwns: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
45
|
+
accessibilityPlaceholder: PropTypes.string,
|
|
46
|
+
accessibilityPosInSet: PropTypes.number,
|
|
47
|
+
accessibilityPressed: PropTypes.bool,
|
|
48
|
+
accessibilityReadOnly: PropTypes.bool,
|
|
49
|
+
accessibilityRequired: PropTypes.bool,
|
|
50
|
+
accessibilityRoleDescription: PropTypes.string,
|
|
51
|
+
accessibilityRowCount: PropTypes.number,
|
|
52
|
+
accessibilityRowIndex: PropTypes.number,
|
|
53
|
+
accessibilityRowSpan: PropTypes.number,
|
|
54
|
+
accessibilitySelected: PropTypes.bool,
|
|
55
|
+
accessibilitySetSize: PropTypes.number,
|
|
56
|
+
accessibilitySort: PropTypes.oneOf(['ascending', 'descending', 'none', 'other']),
|
|
57
|
+
accessibilityValueMax: PropTypes.number,
|
|
58
|
+
accessibilityValueMin: PropTypes.number,
|
|
59
|
+
accessibilityValueNow: PropTypes.number,
|
|
60
|
+
accessibilityValueText: PropTypes.string
|
|
61
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
|
|
3
|
+
// React Native exports a11y prop definitions as TypeScript Interfaces, but no longer exports PropTypes
|
|
4
|
+
// so we have to define them ourselves.
|
|
5
|
+
export default {
|
|
6
|
+
accessible: PropTypes.bool,
|
|
7
|
+
focusable: PropTypes.bool,
|
|
8
|
+
|
|
9
|
+
accessibilityElementsHidden: PropTypes.bool,
|
|
10
|
+
accessibilityHint: PropTypes.string, // react-native-web ignores `accessibilityHint`
|
|
11
|
+
accessibilityIgnoresInvertColors: PropTypes.bool,
|
|
12
|
+
accessibilityLabel: PropTypes.string,
|
|
13
|
+
accessibilityRole: PropTypes.string,
|
|
14
|
+
accessibilityActions: PropTypes.arrayOf(
|
|
15
|
+
PropTypes.shape({
|
|
16
|
+
name: PropTypes.string.isRequired,
|
|
17
|
+
label: PropTypes.string
|
|
18
|
+
})
|
|
19
|
+
),
|
|
20
|
+
accessibilityLiveRegion: PropTypes.oneOf(['none', 'polite', 'assertive']),
|
|
21
|
+
accessibilityState: PropTypes.shape({
|
|
22
|
+
disabled: PropTypes.bool,
|
|
23
|
+
selected: PropTypes.bool,
|
|
24
|
+
checked: PropTypes.oneOf([true, false, 'mixed']),
|
|
25
|
+
busy: PropTypes.bool,
|
|
26
|
+
expanded: PropTypes.bool
|
|
27
|
+
}),
|
|
28
|
+
accessibilityValue: PropTypes.shape({
|
|
29
|
+
min: PropTypes.number,
|
|
30
|
+
max: PropTypes.number,
|
|
31
|
+
now: PropTypes.number,
|
|
32
|
+
text: PropTypes.string
|
|
33
|
+
}),
|
|
34
|
+
accessibilityViewIsModal: PropTypes.bool,
|
|
35
|
+
importantForAccessibility: PropTypes.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
|
|
36
|
+
onAccessibilityAction: PropTypes.func,
|
|
37
|
+
onAccessibilityEscape: PropTypes.func,
|
|
38
|
+
onAccessibilityTap: PropTypes.func
|
|
39
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -12,3 +12,4 @@ export { default as useSpacingScale } from './useSpacingScale'
|
|
|
12
12
|
export { default as useResponsiveProp } from './useResponsiveProp'
|
|
13
13
|
export * from './useResponsiveProp'
|
|
14
14
|
export { default as useUniqueId } from './useUniqueId'
|
|
15
|
+
export { default as withLinkRouter } from './withLinkRouter'
|
package/src/utils/propTypes.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import { Linking, Platform } from 'react-native'
|
|
3
3
|
import { components as tokenKeys } from '@telus-uds/system-theme-tokens'
|
|
4
|
+
import a11yPropTypes from './a11y/propTypes'
|
|
4
5
|
|
|
5
6
|
export const paddingProp = {
|
|
6
7
|
propType: PropTypes.shape({
|
|
@@ -202,116 +203,6 @@ function getPropSelector(propTypes, regexp) {
|
|
|
202
203
|
)
|
|
203
204
|
}
|
|
204
205
|
|
|
205
|
-
// React Native exports a11y prop definitions as TypeScript Interfaces, but no longer exports PropTypes
|
|
206
|
-
// so we have to define them ourselves.
|
|
207
|
-
const a11yPropTypes = {
|
|
208
|
-
accessible: PropTypes.bool,
|
|
209
|
-
focusable: PropTypes.bool,
|
|
210
|
-
|
|
211
|
-
accessibilityElementsHidden: PropTypes.bool,
|
|
212
|
-
accessibilityHint: PropTypes.string, // react-native-web ignores `accessibilityHint`
|
|
213
|
-
accessibilityIgnoresInvertColors: PropTypes.bool,
|
|
214
|
-
accessibilityLabel: PropTypes.string,
|
|
215
|
-
accessibilityRole: PropTypes.string,
|
|
216
|
-
accessibilityActions: PropTypes.arrayOf(
|
|
217
|
-
PropTypes.shape({
|
|
218
|
-
name: PropTypes.string.isRequired,
|
|
219
|
-
label: PropTypes.string
|
|
220
|
-
})
|
|
221
|
-
),
|
|
222
|
-
accessibilityLiveRegion: PropTypes.oneOf(['none', 'polite', 'assertive']),
|
|
223
|
-
accessibilityState: PropTypes.shape({
|
|
224
|
-
disabled: PropTypes.bool,
|
|
225
|
-
selected: PropTypes.bool,
|
|
226
|
-
checked: PropTypes.oneOf([true, false, 'mixed']),
|
|
227
|
-
busy: PropTypes.bool,
|
|
228
|
-
expanded: PropTypes.bool
|
|
229
|
-
}),
|
|
230
|
-
accessibilityValue: PropTypes.shape({
|
|
231
|
-
min: PropTypes.number,
|
|
232
|
-
max: PropTypes.number,
|
|
233
|
-
now: PropTypes.number,
|
|
234
|
-
text: PropTypes.string
|
|
235
|
-
}),
|
|
236
|
-
accessibilityViewIsModal: PropTypes.bool,
|
|
237
|
-
importantForAccessibility: PropTypes.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
|
|
238
|
-
onAccessibilityAction: PropTypes.func,
|
|
239
|
-
onAccessibilityEscape: PropTypes.func,
|
|
240
|
-
onAccessibilityTap: PropTypes.func,
|
|
241
|
-
...Platform.select({
|
|
242
|
-
web: {
|
|
243
|
-
// React Native Web adds many a11y props that alias aria-* attributes
|
|
244
|
-
// Types based on https://necolas.github.io/react-native-web/docs/accessibility/
|
|
245
|
-
accessibilityActiveDescendant: PropTypes.string,
|
|
246
|
-
accessibilityAtomic: PropTypes.bool,
|
|
247
|
-
accessibilityAutoComplete: PropTypes.string,
|
|
248
|
-
accessibilityBusy: PropTypes.bool,
|
|
249
|
-
accessibilityChecked: PropTypes.oneOf([true, false, 'mixed']),
|
|
250
|
-
accessibilityColumnCount: PropTypes.number,
|
|
251
|
-
accessibilityColumnIndex: PropTypes.number,
|
|
252
|
-
accessibilityColumnSpan: PropTypes.number,
|
|
253
|
-
accessibilityControls: PropTypes.oneOfType([
|
|
254
|
-
PropTypes.string,
|
|
255
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
256
|
-
]),
|
|
257
|
-
accessibilityCurrent: PropTypes.oneOf([
|
|
258
|
-
true,
|
|
259
|
-
false,
|
|
260
|
-
'page',
|
|
261
|
-
'step',
|
|
262
|
-
'location',
|
|
263
|
-
'date',
|
|
264
|
-
'time'
|
|
265
|
-
]),
|
|
266
|
-
accessibilityDescribedBy: PropTypes.oneOfType([
|
|
267
|
-
PropTypes.string,
|
|
268
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
269
|
-
]),
|
|
270
|
-
accessibilityDetails: PropTypes.string,
|
|
271
|
-
accessibilityDisabled: PropTypes.bool,
|
|
272
|
-
accessibilityErrorMessage: PropTypes.string,
|
|
273
|
-
accessibilityExpanded: PropTypes.bool,
|
|
274
|
-
accessibilityFlowTo: PropTypes.oneOfType([
|
|
275
|
-
PropTypes.string,
|
|
276
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
277
|
-
]),
|
|
278
|
-
accessibilityHasPopup: PropTypes.string,
|
|
279
|
-
accessibilityHidden: PropTypes.bool,
|
|
280
|
-
accessibilityInvalid: PropTypes.bool,
|
|
281
|
-
accessibilityKeyShortcuts: PropTypes.string,
|
|
282
|
-
accessibilityLabelledBy: PropTypes.oneOfType([
|
|
283
|
-
PropTypes.string,
|
|
284
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
285
|
-
]),
|
|
286
|
-
accessibilityLevel: PropTypes.number,
|
|
287
|
-
accessibilityModal: PropTypes.bool,
|
|
288
|
-
accessibilityMultiline: PropTypes.bool,
|
|
289
|
-
accessibilityMultiSelectable: PropTypes.bool,
|
|
290
|
-
accessibilityOrientation: PropTypes.oneOf(['horizontal', 'vertical']),
|
|
291
|
-
accessibilityOwns: PropTypes.oneOfType([
|
|
292
|
-
PropTypes.string,
|
|
293
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
294
|
-
]),
|
|
295
|
-
accessibilityPlaceholder: PropTypes.string,
|
|
296
|
-
accessibilityPosInSet: PropTypes.number,
|
|
297
|
-
accessibilityPressed: PropTypes.bool,
|
|
298
|
-
accessibilityReadOnly: PropTypes.bool,
|
|
299
|
-
accessibilityRequired: PropTypes.bool,
|
|
300
|
-
accessibilityRoleDescription: PropTypes.string,
|
|
301
|
-
accessibilityRowCount: PropTypes.number,
|
|
302
|
-
accessibilityRowIndex: PropTypes.number,
|
|
303
|
-
accessibilityRowSpan: PropTypes.number,
|
|
304
|
-
accessibilitySelected: PropTypes.bool,
|
|
305
|
-
accessibilitySetSize: PropTypes.number,
|
|
306
|
-
accessibilitySort: PropTypes.oneOf(['ascending', 'descending', 'none', 'other']),
|
|
307
|
-
accessibilityValueMax: PropTypes.number,
|
|
308
|
-
accessibilityValueMin: PropTypes.number,
|
|
309
|
-
accessibilityValueNow: PropTypes.number,
|
|
310
|
-
accessibilityValueText: PropTypes.string
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
|
|
315
206
|
export const a11yProps = {
|
|
316
207
|
/**
|
|
317
208
|
* Proptypes for recognised React Native accessiblity (a11y) props.
|
|
@@ -429,6 +320,36 @@ export const pressProps = {
|
|
|
429
320
|
selectHandlers: getPropSelector(pressHandlerPropTypes)
|
|
430
321
|
}
|
|
431
322
|
|
|
323
|
+
const clickHandlerMapping = {
|
|
324
|
+
onClick: 'onPress',
|
|
325
|
+
mouseDown: 'onPressIn',
|
|
326
|
+
mouseUp: 'onPressOut'
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export const clickProps = {
|
|
330
|
+
/**
|
|
331
|
+
* Web-oriented HTML click handlers that may be mapped to React Native press handlers
|
|
332
|
+
*/
|
|
333
|
+
types: Object.fromEntries(
|
|
334
|
+
Object.keys(clickHandlerMapping).map((mouseName) => [mouseName, PropTypes.func])
|
|
335
|
+
),
|
|
336
|
+
/**
|
|
337
|
+
* Takes a set of props and converts HTML mouse click oriented event handlers to closest
|
|
338
|
+
* equivalent React Native press event handler.
|
|
339
|
+
*
|
|
340
|
+
* Use this when a component that expects press-oriented props may need to support third-party
|
|
341
|
+
* web-oriented tooling that injects web-oriented event handlers directly. For example, for
|
|
342
|
+
* to support use with NextJS's 'next/link' component, which injects `onClick` prop into its child.
|
|
343
|
+
*/
|
|
344
|
+
toPressProps: (props) =>
|
|
345
|
+
Object.fromEntries(
|
|
346
|
+
Object.entries(props).map(([originalName, value]) => {
|
|
347
|
+
const translatedName = clickHandlerMapping[originalName]
|
|
348
|
+
return translatedName ? [translatedName, value] : [originalName, value]
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
432
353
|
const linkPropTypes = {
|
|
433
354
|
...pressPropTypes,
|
|
434
355
|
href: PropTypes.string,
|
|
@@ -470,7 +391,8 @@ const viewPropTypes = {
|
|
|
470
391
|
pointerEvents: PropTypes.oneOf(['all', 'none', 'box-only', 'box-none']),
|
|
471
392
|
onLayout: PropTypes.func,
|
|
472
393
|
nativeID: PropTypes.string,
|
|
473
|
-
testID: PropTypes.string
|
|
394
|
+
testID: PropTypes.string,
|
|
395
|
+
dataSet: PropTypes.object
|
|
474
396
|
}
|
|
475
397
|
|
|
476
398
|
export const viewProps = {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
// Prototype-safe alternative to (linter-forbidden) someObject.hasOwnProperty()
|
|
5
|
+
const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop)
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Higher-order component that has no effect unless an additional prop `LinkRouter` is passed.
|
|
9
|
+
* This may be used to provide custom wrappers for integrations with third party libraries.
|
|
10
|
+
*
|
|
11
|
+
* If LinkRouter is passed, LinkRouter is rendered in place of the main component and is passed:
|
|
12
|
+
*
|
|
13
|
+
* - `linkRouterProps`: an optional object passed alongside LinkRouter, for props needed by the wrapper
|
|
14
|
+
* that are not valid props for the wrapped component.
|
|
15
|
+
* - `Component`: automatically provided, the original component to render inside the wrapper.
|
|
16
|
+
* - `ref`: forwarded `ref` passed down to `Component`
|
|
17
|
+
* - All other props passed to the outer component
|
|
18
|
+
*
|
|
19
|
+
* @example A LinkRouter component to be used with link-like components might look like:
|
|
20
|
+
*
|
|
21
|
+
* ```jsx
|
|
22
|
+
* const LinkLinkRouter = forwardRef(({ Component, linkRouterProps: { to, options }, href, ...rest }, ref) => {
|
|
23
|
+
* const { href: resolvedHref, onClick } = useSomeRouterHook({ to, href, options })
|
|
24
|
+
* return <Component href={resolvedHref} onPress={onClick} {...rest} />
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Any component that takes href and onPress props may then use this wrapper:
|
|
29
|
+
*
|
|
30
|
+
* ```jsx
|
|
31
|
+
* <Link href={href} LinkRouter={LinkLinkRouter} linkRouterProps={{ to, options }}>Some link</Link>
|
|
32
|
+
* <IconButton icon={SomeIcon} LinkRouter={LinkLinkRouter} linkRouterProps={{ to, options }} ref={iconRef} />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const withLinkRouter = (Component) => {
|
|
36
|
+
const wrappedComponent = forwardRef(({ LinkRouter, linkRouterProps, ...props }, ref) => {
|
|
37
|
+
if (!LinkRouter) return <Component {...props} ref={ref} />
|
|
38
|
+
return (
|
|
39
|
+
<LinkRouter linkRouterProps={linkRouterProps} Component={Component} ref={ref} {...props} />
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Ensure the returned component has appropriate outer properties set:
|
|
44
|
+
/* eslint-disable-next-line react/forbid-foreign-prop-types */
|
|
45
|
+
const { displayName, name, propTypes, ...otherProperties } = Component
|
|
46
|
+
|
|
47
|
+
// Apply unique component name as a displayName
|
|
48
|
+
wrappedComponent.displayName = Component.displayName || Component.name
|
|
49
|
+
|
|
50
|
+
// Apply proptypes including wrapper props - is safely { ...undefined, ...undefined } in prod
|
|
51
|
+
wrappedComponent.propTypes = { ...Component.propTypes, ...withLinkRouter.propTypes }
|
|
52
|
+
|
|
53
|
+
// Forward any other properties explicitly set e.g. Component.SubComponent
|
|
54
|
+
Object.keys(otherProperties).forEach((key) => {
|
|
55
|
+
// Skip internal React properties from wrappedComponent's forwardRef (render, $$typeof, etc)
|
|
56
|
+
if (hasOwnProperty(Component, key) && !hasOwnProperty(wrappedComponent, key)) {
|
|
57
|
+
wrappedComponent[key] = Component[key]
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
return wrappedComponent
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
withLinkRouter.propTypes = {
|
|
64
|
+
LinkRouter: PropTypes.elementType,
|
|
65
|
+
linkRouterProps: PropTypes.object
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default withLinkRouter
|
|
@@ -6,7 +6,7 @@ import { Container, EachParentType, parentTypesParams } from '../supports'
|
|
|
6
6
|
import { useMultipleInputValues } from '../../src/utils'
|
|
7
7
|
|
|
8
8
|
export default {
|
|
9
|
-
title: '
|
|
9
|
+
title: 'ExpandCollapse',
|
|
10
10
|
component: ExpandCollapse,
|
|
11
11
|
args: {
|
|
12
12
|
children: () => {}
|