@telus-uds/components-base 3.22.0 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/lib/cjs/Button/Button.js +2 -0
- package/lib/cjs/Button/ButtonBase.js +10 -5
- package/lib/cjs/Button/ButtonDropdown.js +2 -0
- package/lib/cjs/Button/ButtonGroup.js +45 -38
- package/lib/cjs/Button/propTypes.js +6 -0
- package/lib/cjs/Card/CardBase.js +97 -17
- package/lib/cjs/Card/PressableCardBase.js +12 -8
- package/lib/cjs/Carousel/Carousel.js +52 -19
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
- package/lib/cjs/Icon/Icon.js +11 -11
- package/lib/cjs/Icon/IconText.js +0 -1
- package/lib/cjs/Listbox/GroupControl.js +44 -44
- package/lib/cjs/Listbox/Listbox.js +63 -20
- package/lib/cjs/Listbox/ListboxGroup.js +141 -9
- package/lib/cjs/Listbox/ListboxOverlay.js +13 -5
- package/lib/cjs/Listbox/PressableItem.js +8 -4
- package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
- package/lib/cjs/Listbox/dictionary.js +14 -0
- package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
- package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
- package/lib/cjs/Shortcuts/index.js +16 -0
- package/lib/cjs/TextInput/TextInputBase.js +5 -1
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
- package/lib/cjs/Validator/Validator.js +171 -135
- package/lib/cjs/index.js +15 -0
- package/lib/esm/Button/Button.js +2 -0
- package/lib/esm/Button/ButtonBase.js +10 -5
- package/lib/esm/Button/ButtonDropdown.js +2 -0
- package/lib/esm/Button/ButtonGroup.js +44 -39
- package/lib/esm/Button/propTypes.js +6 -0
- package/lib/esm/Card/CardBase.js +97 -17
- package/lib/esm/Card/PressableCardBase.js +10 -8
- package/lib/esm/Carousel/Carousel.js +52 -19
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
- package/lib/esm/Icon/Icon.js +11 -11
- package/lib/esm/Icon/IconText.js +0 -1
- package/lib/esm/Listbox/GroupControl.js +44 -44
- package/lib/esm/Listbox/Listbox.js +64 -21
- package/lib/esm/Listbox/ListboxGroup.js +143 -11
- package/lib/esm/Listbox/ListboxOverlay.js +13 -5
- package/lib/esm/Listbox/PressableItem.js +8 -4
- package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
- package/lib/esm/Listbox/dictionary.js +8 -0
- package/lib/esm/Shortcuts/Shortcuts.js +160 -0
- package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
- package/lib/esm/Shortcuts/index.js +3 -0
- package/lib/esm/TextInput/TextInputBase.js +5 -1
- package/lib/esm/Tooltip/Tooltip.native.js +2 -0
- package/lib/esm/Validator/Validator.js +171 -135
- package/lib/esm/index.js +1 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/Button.jsx +2 -1
- package/src/Button/ButtonBase.jsx +18 -12
- package/src/Button/ButtonDropdown.jsx +2 -0
- package/src/Button/ButtonGroup.jsx +62 -45
- package/src/Button/propTypes.js +6 -0
- package/src/Card/CardBase.jsx +113 -14
- package/src/Card/PressableCardBase.jsx +17 -5
- package/src/Carousel/Carousel.jsx +58 -5
- package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
- package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
- package/src/Icon/Icon.jsx +14 -14
- package/src/Icon/IconText.jsx +0 -1
- package/src/Listbox/GroupControl.jsx +72 -70
- package/src/Listbox/Listbox.jsx +67 -11
- package/src/Listbox/ListboxGroup.jsx +160 -27
- package/src/Listbox/ListboxOverlay.jsx +23 -5
- package/src/Listbox/PressableItem.jsx +8 -4
- package/src/Listbox/SecondLevelHeader.jsx +182 -0
- package/src/Listbox/dictionary.js +8 -0
- package/src/Shortcuts/Shortcuts.jsx +174 -0
- package/src/Shortcuts/ShortcutsItem.jsx +297 -0
- package/src/Shortcuts/index.js +4 -0
- package/src/TextInput/TextInputBase.jsx +5 -1
- package/src/Tooltip/Tooltip.native.jsx +2 -1
- package/src/Validator/Validator.jsx +180 -159
- package/src/index.js +1 -0
- package/types/Listbox.d.ts +24 -0
- package/types/Shortcuts.d.ts +136 -0
- package/types/index.d.ts +12 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, Text, StyleSheet } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
5
|
import Icon from '../Icon'
|
|
6
6
|
import Spacer from '../Spacer'
|
|
7
7
|
import { useListboxContext } from './ListboxContext'
|
|
8
|
+
import { variantProp } from '../utils'
|
|
8
9
|
|
|
9
10
|
const styles = StyleSheet.create({
|
|
10
|
-
|
|
11
|
+
container: {
|
|
11
12
|
width: '100%',
|
|
12
13
|
flex: 1,
|
|
13
14
|
alignItems: 'center',
|
|
@@ -17,76 +18,76 @@ const styles = StyleSheet.create({
|
|
|
17
18
|
}
|
|
18
19
|
})
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
expanded,
|
|
28
|
-
pressed,
|
|
29
|
-
hover,
|
|
30
|
-
current: selectedId === id && id !== undefined,
|
|
31
|
-
focus
|
|
32
|
-
}
|
|
33
|
-
)
|
|
34
|
-
const {
|
|
35
|
-
groupFontName,
|
|
36
|
-
groupFontWeight,
|
|
37
|
-
groupFontSize,
|
|
38
|
-
groupColor,
|
|
39
|
-
groupBackgroundColor,
|
|
40
|
-
groupBorderColor,
|
|
41
|
-
groupBorderWidth,
|
|
42
|
-
groupBorderRadius,
|
|
43
|
-
groupPaddingLeft,
|
|
44
|
-
groupPaddingRight,
|
|
45
|
-
groupPaddingTop,
|
|
46
|
-
groupPaddingBottom,
|
|
47
|
-
itemTextDecoration,
|
|
48
|
-
itemOutline,
|
|
49
|
-
groupHeight
|
|
50
|
-
} = tokens
|
|
51
|
-
|
|
52
|
-
const getTextStyles = () => ({
|
|
53
|
-
color: groupColor
|
|
54
|
-
})
|
|
21
|
+
const selectTextStyles = (tokens) => ({
|
|
22
|
+
color: tokens.groupColor,
|
|
23
|
+
fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
|
|
24
|
+
fontSize: tokens.groupFontSize,
|
|
25
|
+
fontWeight: tokens.groupFontWeight
|
|
26
|
+
})
|
|
55
27
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
ref={ref}
|
|
78
|
-
>
|
|
79
|
-
<Text style={getTextStyles()}>{label}</Text>
|
|
80
|
-
<Spacer space={1} direction="row" />
|
|
81
|
-
<Icon
|
|
82
|
-
icon={tokens.groupIcon}
|
|
83
|
-
tokens={{ color: tokens.groupColor }}
|
|
84
|
-
variant={{ size: 'micro' }}
|
|
85
|
-
/>
|
|
86
|
-
</View>
|
|
87
|
-
)
|
|
28
|
+
const selectContainerStyles = (tokens) => ({
|
|
29
|
+
fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
|
|
30
|
+
fontSize: tokens.groupFontSize,
|
|
31
|
+
color: tokens.groupColor,
|
|
32
|
+
textDecoration: tokens.itemTextDecoration,
|
|
33
|
+
backgroundColor: tokens.groupBackgroundColor,
|
|
34
|
+
outline: tokens.itemOutline,
|
|
35
|
+
minHeight: tokens.groupHeight,
|
|
36
|
+
borderRadius: tokens.groupBorderRadius,
|
|
37
|
+
paddingLeft: tokens.groupPaddingLeft - tokens.groupBorderLeftWidth,
|
|
38
|
+
paddingRight: tokens.groupPaddingRight - tokens.groupBorderRightWidth,
|
|
39
|
+
paddingTop: tokens.groupPaddingTop - tokens.groupBorderTopWidth,
|
|
40
|
+
paddingBottom: tokens.groupPaddingBottom - tokens.groupBorderBottomWidth,
|
|
41
|
+
borderLeftWidth: tokens.groupBorderLeftWidth,
|
|
42
|
+
borderLeftColor: tokens.groupBorderLeftColor,
|
|
43
|
+
borderRightWidth: tokens.groupBorderRightWidth,
|
|
44
|
+
borderRightColor: tokens.groupBorderRightColor,
|
|
45
|
+
borderTopWidth: tokens.groupBorderTopWidth,
|
|
46
|
+
borderTopColor: tokens.groupBorderTopColor,
|
|
47
|
+
borderBottomWidth: tokens.groupBorderBottomWidth,
|
|
48
|
+
borderBottomColor: tokens.groupBorderBottomColor
|
|
88
49
|
})
|
|
89
50
|
|
|
51
|
+
const GroupControl = React.forwardRef(
|
|
52
|
+
({ expanded, pressed, hover, focus, label, id, variant = {} }, ref) => {
|
|
53
|
+
const { selectedId, setSelectedId } = useListboxContext()
|
|
54
|
+
const isSecondLevel = variant?.secondLevel === true
|
|
55
|
+
const tokens = useThemeTokens(
|
|
56
|
+
'Listbox',
|
|
57
|
+
variant,
|
|
58
|
+
{},
|
|
59
|
+
{
|
|
60
|
+
expanded,
|
|
61
|
+
pressed,
|
|
62
|
+
hover,
|
|
63
|
+
current: selectedId === id && id !== undefined,
|
|
64
|
+
focus,
|
|
65
|
+
secondLevel: isSecondLevel
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const displayIcon = isSecondLevel ? tokens.secondLevelParentIcon : tokens.groupIcon
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<View
|
|
73
|
+
onPress={() => setSelectedId(id)}
|
|
74
|
+
style={[styles.container, selectContainerStyles(tokens)]}
|
|
75
|
+
ref={ref}
|
|
76
|
+
>
|
|
77
|
+
<Text style={selectTextStyles(tokens)}>{label}</Text>
|
|
78
|
+
<Spacer space={1} direction="row" />
|
|
79
|
+
{displayIcon && (
|
|
80
|
+
<Icon
|
|
81
|
+
icon={displayIcon}
|
|
82
|
+
tokens={{ color: tokens.groupColor }}
|
|
83
|
+
variant={{ size: 'micro' }}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</View>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
90
91
|
GroupControl.displayName = 'GroupControl'
|
|
91
92
|
|
|
92
93
|
GroupControl.propTypes = {
|
|
@@ -95,7 +96,8 @@ GroupControl.propTypes = {
|
|
|
95
96
|
pressed: PropTypes.bool,
|
|
96
97
|
hover: PropTypes.bool,
|
|
97
98
|
focus: PropTypes.bool,
|
|
98
|
-
label: PropTypes.string
|
|
99
|
+
label: PropTypes.string,
|
|
100
|
+
variant: variantProp.propType
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
export default GroupControl
|
package/src/Listbox/Listbox.jsx
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, Platform, StyleSheet } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
|
-
import { withLinkRouter, getTokensPropType } from '../utils'
|
|
5
|
+
import { withLinkRouter, getTokensPropType, variantProp } from '../utils'
|
|
6
6
|
import ExpandCollapse from '../ExpandCollapse'
|
|
7
7
|
import ListboxGroup from './ListboxGroup'
|
|
8
8
|
import ListboxItem from './ListboxItem'
|
|
9
9
|
import { ListboxContext } from './ListboxContext'
|
|
10
10
|
import DropdownOverlay from './ListboxOverlay'
|
|
11
|
+
import defaultDictionary from './dictionary'
|
|
11
12
|
|
|
12
13
|
const styles = StyleSheet.create({
|
|
13
|
-
|
|
14
|
+
container: {
|
|
14
15
|
padding: 0,
|
|
15
|
-
margin: 0
|
|
16
|
+
margin: 0,
|
|
17
|
+
position: 'relative',
|
|
18
|
+
overflow: 'visible'
|
|
16
19
|
}
|
|
17
20
|
})
|
|
18
21
|
|
|
22
|
+
const selectContainerStyles = (tokens) => ({
|
|
23
|
+
minHeight: tokens.minHeight,
|
|
24
|
+
minWidth: tokens.minWidth,
|
|
25
|
+
backgroundColor: tokens.containerBackgroundColor
|
|
26
|
+
})
|
|
27
|
+
|
|
19
28
|
const getInitialOpen = (items, selectedId) =>
|
|
20
29
|
items
|
|
21
30
|
.filter(
|
|
@@ -35,16 +44,18 @@ const Listbox = React.forwardRef(
|
|
|
35
44
|
LinkRouter,
|
|
36
45
|
itemRouterProps,
|
|
37
46
|
onClose,
|
|
47
|
+
copy = 'en',
|
|
48
|
+
dictionary = defaultDictionary,
|
|
38
49
|
variant,
|
|
39
|
-
tokens
|
|
50
|
+
tokens,
|
|
51
|
+
testID
|
|
40
52
|
},
|
|
41
53
|
ref
|
|
42
54
|
) => {
|
|
43
55
|
const initialOpen = getInitialOpen(items, defaultSelectedId)
|
|
44
|
-
|
|
45
56
|
const [selectedId, setSelectedId] = React.useState(defaultSelectedId)
|
|
46
|
-
|
|
47
|
-
const
|
|
57
|
+
const [activeSecondLevelGroup, setActiveSecondLevelGroup] = React.useState(null)
|
|
58
|
+
const listboxTokens = useThemeTokens('Listbox', tokens, variant)
|
|
48
59
|
|
|
49
60
|
// We need to keep track of each item's ref in order to be able to
|
|
50
61
|
// focus on a specific item via keyboard navigation
|
|
@@ -97,11 +108,22 @@ const Listbox = React.forwardRef(
|
|
|
97
108
|
return () => {}
|
|
98
109
|
}, [onClose, handleKeydown])
|
|
99
110
|
|
|
111
|
+
const contextValue = {
|
|
112
|
+
selectedId,
|
|
113
|
+
setSelectedId,
|
|
114
|
+
activeSecondLevelGroup,
|
|
115
|
+
setActiveSecondLevelGroup
|
|
116
|
+
}
|
|
117
|
+
|
|
100
118
|
return (
|
|
101
|
-
<ListboxContext.Provider value={
|
|
119
|
+
<ListboxContext.Provider value={contextValue}>
|
|
102
120
|
<ExpandCollapse initialOpen={initialOpen} maxOpen={1} ref={ref}>
|
|
103
121
|
{(expandProps) => (
|
|
104
|
-
<View
|
|
122
|
+
<View
|
|
123
|
+
style={[styles.container, selectContainerStyles(listboxTokens)]}
|
|
124
|
+
accessibilityRole="combobox"
|
|
125
|
+
testID={testID}
|
|
126
|
+
>
|
|
105
127
|
{items.map((item, index) => {
|
|
106
128
|
const { id, label, items: nestedItems } = item
|
|
107
129
|
const itemId = id ?? label
|
|
@@ -112,6 +134,10 @@ const Listbox = React.forwardRef(
|
|
|
112
134
|
return currentItemRef
|
|
113
135
|
}
|
|
114
136
|
|
|
137
|
+
if (!nestedItems && activeSecondLevelGroup) {
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
115
141
|
return nestedItems ? (
|
|
116
142
|
<ListboxGroup
|
|
117
143
|
{...item}
|
|
@@ -122,6 +148,11 @@ const Listbox = React.forwardRef(
|
|
|
122
148
|
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
123
149
|
ref={index === 0 ? firstItemRef : itemRef}
|
|
124
150
|
key={itemId}
|
|
151
|
+
copy={copy}
|
|
152
|
+
dictionary={dictionary}
|
|
153
|
+
variant={variant}
|
|
154
|
+
tokens={tokens}
|
|
155
|
+
onClose={onClose}
|
|
125
156
|
/>
|
|
126
157
|
) : (
|
|
127
158
|
<ListboxItem
|
|
@@ -133,6 +164,8 @@ const Listbox = React.forwardRef(
|
|
|
133
164
|
prevItemRef={itemRefs.current[index - 1] ?? null}
|
|
134
165
|
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
135
166
|
ref={index === 0 ? firstItemRef : itemRef}
|
|
167
|
+
variant={variant}
|
|
168
|
+
tokens={tokens}
|
|
136
169
|
/>
|
|
137
170
|
)
|
|
138
171
|
})}
|
|
@@ -169,7 +202,30 @@ Listbox.propTypes = {
|
|
|
169
202
|
/**
|
|
170
203
|
* onClose event
|
|
171
204
|
*/
|
|
172
|
-
onClose: PropTypes.func
|
|
205
|
+
onClose: PropTypes.func,
|
|
206
|
+
/**
|
|
207
|
+
* Test ID for testing
|
|
208
|
+
*/
|
|
209
|
+
testID: PropTypes.string,
|
|
210
|
+
/**
|
|
211
|
+
* Select English or French copy
|
|
212
|
+
*/
|
|
213
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
214
|
+
/**
|
|
215
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
216
|
+
*/
|
|
217
|
+
dictionary: PropTypes.shape({
|
|
218
|
+
en: PropTypes.shape({
|
|
219
|
+
closeMenu: PropTypes.string.isRequired
|
|
220
|
+
}),
|
|
221
|
+
fr: PropTypes.shape({
|
|
222
|
+
closeMenu: PropTypes.string.isRequired
|
|
223
|
+
})
|
|
224
|
+
}),
|
|
225
|
+
/**
|
|
226
|
+
* Listbox variant
|
|
227
|
+
*/
|
|
228
|
+
variant: variantProp.propType
|
|
173
229
|
}
|
|
174
230
|
|
|
175
231
|
Listbox.Overlay = DropdownOverlay
|
|
@@ -2,21 +2,36 @@
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
import { View, StyleSheet, Platform } from 'react-native'
|
|
5
|
-
import { withLinkRouter } from '../utils'
|
|
5
|
+
import { withLinkRouter, variantProp, copyPropTypes } from '../utils'
|
|
6
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
6
7
|
import ExpandCollapse from '../ExpandCollapse'
|
|
7
8
|
import ListboxItem from './ListboxItem'
|
|
8
9
|
import { useListboxContext } from './ListboxContext'
|
|
9
10
|
import GroupControl from './GroupControl'
|
|
11
|
+
import SecondLevelHeader from './SecondLevelHeader'
|
|
12
|
+
import defaultDictionary from './dictionary'
|
|
10
13
|
|
|
11
14
|
const styles = StyleSheet.create({
|
|
12
15
|
groupWrapper: {
|
|
13
16
|
margin: 0,
|
|
14
17
|
padding: 0,
|
|
15
|
-
overflow: '
|
|
18
|
+
overflow: 'visible'
|
|
16
19
|
},
|
|
17
20
|
list: {
|
|
18
21
|
margin: 0,
|
|
19
22
|
padding: 0
|
|
23
|
+
},
|
|
24
|
+
secondLevelContainer: {
|
|
25
|
+
margin: 0,
|
|
26
|
+
padding: 0,
|
|
27
|
+
width: '100%',
|
|
28
|
+
display: 'flex',
|
|
29
|
+
flexDirection: 'column'
|
|
30
|
+
},
|
|
31
|
+
secondLevelList: {
|
|
32
|
+
margin: 0,
|
|
33
|
+
padding: 0,
|
|
34
|
+
width: '100%'
|
|
20
35
|
}
|
|
21
36
|
})
|
|
22
37
|
|
|
@@ -27,6 +42,10 @@ const getAccessibilityRole = () =>
|
|
|
27
42
|
web: 'listitem'
|
|
28
43
|
})
|
|
29
44
|
|
|
45
|
+
const selectSecondLevelContainerStyles = ({ secondLevelHeaderBackgroundColor }) => ({
|
|
46
|
+
backgroundColor: secondLevelHeaderBackgroundColor
|
|
47
|
+
})
|
|
48
|
+
|
|
30
49
|
const ListboxGroup = React.forwardRef(
|
|
31
50
|
(
|
|
32
51
|
{
|
|
@@ -38,11 +57,86 @@ const ListboxGroup = React.forwardRef(
|
|
|
38
57
|
expandProps,
|
|
39
58
|
onLastItemBlur,
|
|
40
59
|
nextItemRef,
|
|
41
|
-
prevItemRef
|
|
60
|
+
prevItemRef,
|
|
61
|
+
copy = 'en',
|
|
62
|
+
dictionary = defaultDictionary,
|
|
63
|
+
variant = {},
|
|
64
|
+
tokens = {},
|
|
65
|
+
onClose
|
|
42
66
|
},
|
|
43
67
|
ref
|
|
44
68
|
) => {
|
|
45
|
-
const { selectedId } = useListboxContext()
|
|
69
|
+
const { selectedId, activeSecondLevelGroup, setActiveSecondLevelGroup } = useListboxContext()
|
|
70
|
+
const [secondLevelOpen, setSecondLevelOpen] = React.useState(false)
|
|
71
|
+
const isSecondLevel = variant?.secondLevel === true
|
|
72
|
+
const listboxTokens = useThemeTokens('Listbox', variant, tokens)
|
|
73
|
+
const groupId = id ?? label
|
|
74
|
+
|
|
75
|
+
const handleGroupClick = React.useCallback(() => {
|
|
76
|
+
if (isSecondLevel) {
|
|
77
|
+
setSecondLevelOpen(true)
|
|
78
|
+
setActiveSecondLevelGroup(groupId)
|
|
79
|
+
}
|
|
80
|
+
}, [isSecondLevel, groupId, setActiveSecondLevelGroup])
|
|
81
|
+
|
|
82
|
+
const handleBackClick = React.useCallback(() => {
|
|
83
|
+
setSecondLevelOpen(false)
|
|
84
|
+
setActiveSecondLevelGroup(null)
|
|
85
|
+
}, [setActiveSecondLevelGroup])
|
|
86
|
+
|
|
87
|
+
const handleCloseClick = React.useCallback(() => {
|
|
88
|
+
setSecondLevelOpen(false)
|
|
89
|
+
setActiveSecondLevelGroup(null)
|
|
90
|
+
if (onClose) {
|
|
91
|
+
onClose()
|
|
92
|
+
}
|
|
93
|
+
}, [setActiveSecondLevelGroup, onClose])
|
|
94
|
+
|
|
95
|
+
if (isSecondLevel && activeSecondLevelGroup && activeSecondLevelGroup !== groupId) {
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (isSecondLevel && secondLevelOpen) {
|
|
100
|
+
return (
|
|
101
|
+
<View
|
|
102
|
+
style={[styles.secondLevelContainer, selectSecondLevelContainerStyles(listboxTokens)]}
|
|
103
|
+
>
|
|
104
|
+
<SecondLevelHeader
|
|
105
|
+
label={label}
|
|
106
|
+
onBack={handleBackClick}
|
|
107
|
+
onClose={handleCloseClick}
|
|
108
|
+
copy={copy}
|
|
109
|
+
dictionary={dictionary}
|
|
110
|
+
variant={variant}
|
|
111
|
+
tokens={tokens}
|
|
112
|
+
/>
|
|
113
|
+
<View style={styles.secondLevelList}>
|
|
114
|
+
{items &&
|
|
115
|
+
items.map((item, index) => {
|
|
116
|
+
return (
|
|
117
|
+
<ListboxItem
|
|
118
|
+
key={item.label}
|
|
119
|
+
id={item.id ?? item.label}
|
|
120
|
+
{...item}
|
|
121
|
+
selected={
|
|
122
|
+
(item.id && item.id === selectedId) ||
|
|
123
|
+
(item.label && item.label === selectedId)
|
|
124
|
+
}
|
|
125
|
+
isChild={false}
|
|
126
|
+
LinkRouter={LinkRouter}
|
|
127
|
+
linkRouterProps={linkRouterProps}
|
|
128
|
+
variant={variant}
|
|
129
|
+
tokens={tokens}
|
|
130
|
+
{...(index === 0 && { prevItemRef })}
|
|
131
|
+
{...(index === items.length - 1 && { nextItemRef })}
|
|
132
|
+
{...(index === items.length - 1 && { onBlur: onLastItemBlur })}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
})}
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
46
140
|
|
|
47
141
|
// TODO: implement keyboard navigation via refs for grouped items separately here
|
|
48
142
|
return (
|
|
@@ -63,9 +157,11 @@ const ListboxGroup = React.forwardRef(
|
|
|
63
157
|
// TODO refactor
|
|
64
158
|
// eslint-disable-next-line react/no-unstable-nested-components
|
|
65
159
|
control={(controlProps) => (
|
|
66
|
-
<GroupControl id={id ?? label} {...controlProps} label={label} />
|
|
160
|
+
<GroupControl id={id ?? label} {...controlProps} label={label} variant={variant} />
|
|
67
161
|
)}
|
|
68
162
|
{...expandProps}
|
|
163
|
+
{...(isSecondLevel && { open: false })}
|
|
164
|
+
{...(isSecondLevel && { onPress: handleGroupClick })}
|
|
69
165
|
tokens={{
|
|
70
166
|
contentPaddingLeft: 0,
|
|
71
167
|
contentPaddingRight: 0,
|
|
@@ -74,30 +170,36 @@ const ListboxGroup = React.forwardRef(
|
|
|
74
170
|
borderColor: 'transparent',
|
|
75
171
|
borderRadius: 0,
|
|
76
172
|
borderWidth: 0,
|
|
77
|
-
marginBottom: 0
|
|
173
|
+
marginBottom: 0,
|
|
174
|
+
contentPanelBackgroundColor: 'transparent'
|
|
78
175
|
}}
|
|
79
176
|
controlRef={ref}
|
|
80
177
|
>
|
|
81
|
-
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
178
|
+
{!isSecondLevel && (
|
|
179
|
+
<View style={styles.list}>
|
|
180
|
+
{items.map((item, index) => {
|
|
181
|
+
return (
|
|
182
|
+
<ListboxItem
|
|
183
|
+
key={item.label}
|
|
184
|
+
id={item.id ?? item.label}
|
|
185
|
+
{...item}
|
|
186
|
+
selected={
|
|
187
|
+
(item.id && item.id === selectedId) ||
|
|
188
|
+
(item.label && item.label === selectedId)
|
|
189
|
+
}
|
|
190
|
+
isChild
|
|
191
|
+
LinkRouter={LinkRouter}
|
|
192
|
+
linkRouterProps={linkRouterProps}
|
|
193
|
+
variant={variant}
|
|
194
|
+
tokens={tokens}
|
|
195
|
+
{...(index === 0 && { prevItemRef })}
|
|
196
|
+
{...(index === items.length - 1 && { nextItemRef })}
|
|
197
|
+
{...(index === items.length - 1 && { onBlur: onLastItemBlur })}
|
|
198
|
+
/>
|
|
199
|
+
)
|
|
200
|
+
})}
|
|
201
|
+
</View>
|
|
202
|
+
)}
|
|
101
203
|
</ExpandCollapse.Panel>
|
|
102
204
|
</View>
|
|
103
205
|
)
|
|
@@ -107,6 +209,10 @@ ListboxGroup.displayName = 'ListboxGroup'
|
|
|
107
209
|
|
|
108
210
|
ListboxGroup.propTypes = {
|
|
109
211
|
...withLinkRouter.propTypes,
|
|
212
|
+
/**
|
|
213
|
+
* Unique identifier for the group
|
|
214
|
+
*/
|
|
215
|
+
id: PropTypes.string,
|
|
110
216
|
label: PropTypes.string,
|
|
111
217
|
items: PropTypes.arrayOf(
|
|
112
218
|
PropTypes.shape({
|
|
@@ -121,7 +227,34 @@ ListboxGroup.propTypes = {
|
|
|
121
227
|
/**
|
|
122
228
|
* Use this callback to redirect the focus after it leaves the last item of the group.
|
|
123
229
|
*/
|
|
124
|
-
onLastItemBlur: PropTypes.func
|
|
230
|
+
onLastItemBlur: PropTypes.func,
|
|
231
|
+
/**
|
|
232
|
+
* Select English or French copy
|
|
233
|
+
*/
|
|
234
|
+
copy: copyPropTypes,
|
|
235
|
+
/**
|
|
236
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
237
|
+
*/
|
|
238
|
+
dictionary: PropTypes.shape({
|
|
239
|
+
en: PropTypes.shape({
|
|
240
|
+
closeMenu: PropTypes.string.isRequired
|
|
241
|
+
}),
|
|
242
|
+
fr: PropTypes.shape({
|
|
243
|
+
closeMenu: PropTypes.string.isRequired
|
|
244
|
+
})
|
|
245
|
+
}),
|
|
246
|
+
/**
|
|
247
|
+
* Variant configuration for secondLevel behavior
|
|
248
|
+
*/
|
|
249
|
+
variant: variantProp.propType,
|
|
250
|
+
/**
|
|
251
|
+
* Custom tokens
|
|
252
|
+
*/
|
|
253
|
+
tokens: PropTypes.object,
|
|
254
|
+
/**
|
|
255
|
+
* Callback when the menu is closed
|
|
256
|
+
*/
|
|
257
|
+
onClose: PropTypes.func
|
|
125
258
|
}
|
|
126
259
|
|
|
127
260
|
export default ListboxGroup
|
|
@@ -22,13 +22,28 @@ const paddingVertical = 0
|
|
|
22
22
|
const paddingHorizontal = 0
|
|
23
23
|
|
|
24
24
|
const DropdownOverlay = React.forwardRef(
|
|
25
|
-
(
|
|
26
|
-
|
|
25
|
+
(
|
|
26
|
+
{
|
|
27
|
+
children,
|
|
28
|
+
isReady = false,
|
|
29
|
+
overlaidPosition,
|
|
30
|
+
maxWidth,
|
|
31
|
+
minWidth,
|
|
32
|
+
onLayout,
|
|
33
|
+
tokens,
|
|
34
|
+
testID,
|
|
35
|
+
variant
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
ref
|
|
39
|
+
) => {
|
|
40
|
+
const systemTokens = useThemeTokens('Listbox', variant, tokens)
|
|
27
41
|
|
|
28
42
|
return (
|
|
29
43
|
<View
|
|
30
44
|
ref={ref}
|
|
31
45
|
onLayout={onLayout}
|
|
46
|
+
testID={testID}
|
|
32
47
|
style={[
|
|
33
48
|
overlaidPosition,
|
|
34
49
|
{ maxWidth, minWidth },
|
|
@@ -39,11 +54,12 @@ const DropdownOverlay = React.forwardRef(
|
|
|
39
54
|
<Card
|
|
40
55
|
tokens={{
|
|
41
56
|
shadow: systemTokens.shadow,
|
|
57
|
+
borderRadius: systemTokens.borderRadius,
|
|
58
|
+
...(Platform.OS === 'web' && { overflowY: 'hidden' }),
|
|
42
59
|
paddingBottom: paddingVertical,
|
|
43
60
|
paddingTop: paddingVertical,
|
|
44
61
|
paddingLeft: paddingHorizontal,
|
|
45
|
-
paddingRight: paddingHorizontal
|
|
46
|
-
...tokens
|
|
62
|
+
paddingRight: paddingHorizontal
|
|
47
63
|
}}
|
|
48
64
|
>
|
|
49
65
|
{children}
|
|
@@ -75,7 +91,9 @@ DropdownOverlay.propTypes = {
|
|
|
75
91
|
maxWidth: PropTypes.number,
|
|
76
92
|
minWidth: PropTypes.number,
|
|
77
93
|
onLayout: PropTypes.func,
|
|
78
|
-
tokens: PropTypes.object
|
|
94
|
+
tokens: PropTypes.object,
|
|
95
|
+
testID: PropTypes.string,
|
|
96
|
+
variant: PropTypes.object
|
|
79
97
|
}
|
|
80
98
|
|
|
81
99
|
export default Platform.OS === 'web' ? withPortal(DropdownOverlay) : DropdownOverlay
|
|
@@ -39,10 +39,14 @@ const getItemStyles = ({
|
|
|
39
39
|
color: itemColor,
|
|
40
40
|
outline: itemOutline,
|
|
41
41
|
textDecoration: itemTextDecoration,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
borderLeftWidth: itemBorderLeftWidth,
|
|
43
|
+
borderLeftColor: itemBorderLeftColor,
|
|
44
|
+
borderRightWidth: itemBorderRightWidth,
|
|
45
|
+
borderRightColor: itemBorderRightColor,
|
|
46
|
+
borderTopWidth: itemBorderTopWidth,
|
|
47
|
+
borderTopColor: itemBorderTopColor,
|
|
48
|
+
borderBottomWidth: itemBorderBottomWidth,
|
|
49
|
+
borderBottomColor: itemBorderBottomColor,
|
|
46
50
|
borderRadius: itemBorderRadius,
|
|
47
51
|
justifyContent: 'center'
|
|
48
52
|
})
|