@telus-uds/components-base 1.82.0 → 1.84.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 +33 -2
- package/lib/Button/ButtonGroup.js +9 -0
- package/lib/Carousel/Carousel.js +314 -145
- package/lib/ExpandCollapse/ExpandCollapse.js +18 -9
- package/lib/ExpandCollapse/Panel.js +12 -0
- package/lib/ExpandCollapse/dictionary.js +17 -0
- package/lib/Icon/IconText.js +3 -3
- package/lib/Listbox/Listbox.js +7 -5
- package/lib/Modal/WebModal.js +5 -3
- package/lib/Notification/Notification.js +1 -1
- package/lib/Radio/Radio.js +1 -1
- package/lib-module/Button/ButtonGroup.js +9 -0
- package/lib-module/Carousel/Carousel.js +312 -145
- package/lib-module/ExpandCollapse/ExpandCollapse.js +18 -9
- package/lib-module/ExpandCollapse/Panel.js +13 -1
- package/lib-module/ExpandCollapse/dictionary.js +10 -0
- package/lib-module/Icon/IconText.js +3 -3
- package/lib-module/Listbox/Listbox.js +8 -6
- package/lib-module/Modal/WebModal.js +5 -3
- package/lib-module/Notification/Notification.js +1 -1
- package/lib-module/Radio/Radio.js +1 -1
- package/package.json +2 -2
- package/src/Button/ButtonGroup.jsx +9 -0
- package/src/Carousel/Carousel.jsx +338 -133
- package/src/ExpandCollapse/ExpandCollapse.jsx +13 -5
- package/src/ExpandCollapse/Panel.jsx +27 -5
- package/src/ExpandCollapse/dictionary.js +10 -0
- package/src/Icon/IconText.jsx +5 -3
- package/src/Listbox/Listbox.jsx +112 -105
- package/src/Modal/WebModal.jsx +8 -4
- package/src/Notification/Notification.jsx +1 -1
- package/src/Radio/Radio.jsx +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react'
|
|
2
|
-
import { View } from 'react-native'
|
|
2
|
+
import { View, StyleSheet } from 'react-native'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
|
|
5
5
|
import { useThemeTokens } from '../ThemeProvider'
|
|
@@ -46,10 +46,12 @@ const ExpandCollapse = forwardRef(
|
|
|
46
46
|
const themeTokens = useThemeTokens('ExpandCollapse', tokens, variant)
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<View
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
<View style={staticStyles.container} ref={ref} {...selectProps(rest)}>
|
|
50
|
+
<View style={selectBorderStyles(themeTokens)}>
|
|
51
|
+
{typeof children === 'function'
|
|
52
|
+
? children({ openIds, onToggle, resetValues, setValues, unsetValues })
|
|
53
|
+
: children}
|
|
54
|
+
</View>
|
|
53
55
|
</View>
|
|
54
56
|
)
|
|
55
57
|
}
|
|
@@ -86,3 +88,9 @@ ExpandCollapse.propTypes = {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
export default ExpandCollapse
|
|
91
|
+
|
|
92
|
+
const staticStyles = StyleSheet.create({
|
|
93
|
+
container: {
|
|
94
|
+
display: 'flex'
|
|
95
|
+
}
|
|
96
|
+
})
|
|
@@ -11,9 +11,12 @@ import {
|
|
|
11
11
|
selectSystemProps,
|
|
12
12
|
useVerticalExpandAnimation,
|
|
13
13
|
variantProp,
|
|
14
|
-
viewProps
|
|
14
|
+
viewProps,
|
|
15
|
+
useCopy
|
|
15
16
|
} from '../utils'
|
|
16
17
|
|
|
18
|
+
import dictionary from './dictionary'
|
|
19
|
+
|
|
17
20
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
18
21
|
|
|
19
22
|
// just void functions for now, since there are no style tokens for a panel or control at this point
|
|
@@ -92,12 +95,14 @@ const ExpandCollapsePanel = forwardRef(
|
|
|
92
95
|
variant,
|
|
93
96
|
controlRef,
|
|
94
97
|
content,
|
|
98
|
+
copy = 'en',
|
|
95
99
|
...rest
|
|
96
100
|
},
|
|
97
101
|
ref
|
|
98
102
|
) => {
|
|
99
103
|
const [containerHeight, setContainerHeight] = useState(null)
|
|
100
104
|
const isExpanded = openIds.includes(panelId)
|
|
105
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
101
106
|
|
|
102
107
|
const selectedProps = selectProps({
|
|
103
108
|
...rest,
|
|
@@ -130,16 +135,28 @@ const ExpandCollapsePanel = forwardRef(
|
|
|
130
135
|
})
|
|
131
136
|
|
|
132
137
|
const focusabilityProps = isExpanded ? {} : a11yProps.nonFocusableProps
|
|
138
|
+
|
|
139
|
+
const panelAccessibilityLabel = `${panelId} ${getCopy('panel')}`
|
|
140
|
+
const subPanelAccessibilityLabel = `${panelId} ${getCopy('subPanel')}`
|
|
141
|
+
|
|
133
142
|
return content ? (
|
|
134
|
-
<View
|
|
143
|
+
<View
|
|
144
|
+
style={selectContentPanelStyles(themeTokens)}
|
|
145
|
+
accessibilityLabel={panelAccessibilityLabel}
|
|
146
|
+
>
|
|
135
147
|
{typeof children === 'string' ? (
|
|
136
|
-
<Text
|
|
148
|
+
<Text
|
|
149
|
+
style={selectTextStyles(themeTokens)}
|
|
150
|
+
accessibilityLabel={subPanelAccessibilityLabel}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</Text>
|
|
137
154
|
) : (
|
|
138
155
|
children
|
|
139
156
|
)}
|
|
140
157
|
</View>
|
|
141
158
|
) : (
|
|
142
|
-
<View ref={ref} style={themeTokens}>
|
|
159
|
+
<View ref={ref} style={themeTokens} accessibilityLabel={panelAccessibilityLabel}>
|
|
143
160
|
<View style={selectControlPanelStyles(themeTokens)}>
|
|
144
161
|
<ExpandCollapseControl
|
|
145
162
|
{...selectedProps}
|
|
@@ -174,7 +191,12 @@ const ExpandCollapsePanel = forwardRef(
|
|
|
174
191
|
})
|
|
175
192
|
}}
|
|
176
193
|
>
|
|
177
|
-
<View
|
|
194
|
+
<View
|
|
195
|
+
style={selectContainerStyles(themeTokens)}
|
|
196
|
+
accessibilityLabel={subPanelAccessibilityLabel}
|
|
197
|
+
>
|
|
198
|
+
{children}
|
|
199
|
+
</View>
|
|
178
200
|
</View>
|
|
179
201
|
</Animated.View>
|
|
180
202
|
</View>
|
package/src/Icon/IconText.jsx
CHANGED
|
@@ -31,8 +31,10 @@ const IconText = forwardRef(
|
|
|
31
31
|
*/
|
|
32
32
|
const resultY = valueTranslateY ? Math.floor(-1 * (valueTranslateY - 4)) : 0
|
|
33
33
|
|
|
34
|
-
const
|
|
35
|
-
<View style={{ transform: [{ translateY: size * 0.2 }] }}>
|
|
34
|
+
const iconAdjustedAndroid = (
|
|
35
|
+
<View style={{ transform: [{ translateY: valueTranslateY ? size * 0.2 : size * 0.01 }] }}>
|
|
36
|
+
{iconContent}
|
|
37
|
+
</View>
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
const iconAdjustedIOS = (
|
|
@@ -41,7 +43,7 @@ const IconText = forwardRef(
|
|
|
41
43
|
</View>
|
|
42
44
|
)
|
|
43
45
|
|
|
44
|
-
const mobile = Platform.OS === 'android' ?
|
|
46
|
+
const mobile = Platform.OS === 'android' ? iconAdjustedAndroid : iconAdjustedIOS
|
|
45
47
|
const adjustedContainer = Platform.OS === 'web' ? iconContent : mobile
|
|
46
48
|
|
|
47
49
|
return getStackedContent(
|
package/src/Listbox/Listbox.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
1
|
+
import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { View, StyleSheet, Platform } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
@@ -25,119 +25,126 @@ const getInitialOpen = (items, selectedId) =>
|
|
|
25
25
|
)
|
|
26
26
|
.map((item) => item.id ?? item.label)
|
|
27
27
|
|
|
28
|
-
const Listbox = (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
const Listbox = forwardRef(
|
|
29
|
+
(
|
|
30
|
+
{
|
|
31
|
+
items = [],
|
|
32
|
+
firstItemRef = null, // focus will be moved to this one once within the menu
|
|
33
|
+
parentRef = null, // to return focus to after leaving the last menu item
|
|
34
|
+
selectedId: defaultSelectedId,
|
|
35
|
+
LinkRouter,
|
|
36
|
+
itemRouterProps,
|
|
37
|
+
onClose,
|
|
38
|
+
variant,
|
|
39
|
+
tokens
|
|
40
|
+
},
|
|
41
|
+
ref
|
|
42
|
+
) => {
|
|
43
|
+
const initialOpen = getInitialOpen(items, defaultSelectedId)
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
const [selectedId, setSelectedId] = useState(defaultSelectedId)
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
const { minHeight, minWidth } = useThemeTokens('Listbox', variant, tokens)
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// We need to keep track of each item's ref in order to be able to
|
|
50
|
+
// focus on a specific item via keyboard navigation
|
|
51
|
+
const itemRefs = useRef([])
|
|
52
|
+
if (firstItemRef?.current) itemRefs.current[0] = firstItemRef.current
|
|
53
|
+
const [focusedIndex, setFocusedIndex] = useState(0)
|
|
54
|
+
const handleKeydown = useCallback(
|
|
55
|
+
(event) => {
|
|
56
|
+
const nextItemRef = itemRefs.current[focusedIndex + 1]
|
|
57
|
+
const prevItemRef = itemRefs.current[focusedIndex - 1]
|
|
58
|
+
if (event.key === 'ArrowUp' || (event.shiftKey && event.key === 'Tab')) {
|
|
59
|
+
// Move the focus to the previous item or to the parent one if on the first
|
|
60
|
+
if (prevItemRef) {
|
|
61
|
+
event.preventDefault()
|
|
62
|
+
prevItemRef.focus()
|
|
63
|
+
} else if (parentRef) parentRef.current?.focus()
|
|
64
|
+
setFocusedIndex(focusedIndex - 1)
|
|
65
|
+
} else if ((event.key === 'ArrowDown' || event.key === 'Tab') && nextItemRef) {
|
|
57
66
|
event.preventDefault()
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
firstItemRef.current?.focus()
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
[focusedIndex, onClose, parentRef, firstItemRef]
|
|
79
|
-
)
|
|
67
|
+
setFocusedIndex(focusedIndex + 1)
|
|
68
|
+
nextItemRef.focus()
|
|
69
|
+
} else if (event.key === 'Escape') {
|
|
70
|
+
// Close the dropdown
|
|
71
|
+
parentRef?.current?.click()
|
|
72
|
+
// Return focus to the dropdown control after leaving the last item
|
|
73
|
+
parentRef?.current?.focus()
|
|
74
|
+
if (onClose) onClose(event)
|
|
75
|
+
} else if (!nextItemRef && firstItemRef) {
|
|
76
|
+
// If the last item is focused, move the focus to the first one
|
|
77
|
+
event.preventDefault()
|
|
78
|
+
setFocusedIndex(0)
|
|
79
|
+
firstItemRef.current?.focus()
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[focusedIndex, onClose, parentRef, firstItemRef]
|
|
83
|
+
)
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
// Add listeners for mouse clicks outside and for key presses
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (Platform.OS === 'web') {
|
|
88
|
+
window.addEventListener('click', onClose)
|
|
89
|
+
window.addEventListener('keydown', handleKeydown)
|
|
90
|
+
window.addEventListener('touchstart', onClose)
|
|
91
|
+
return () => {
|
|
92
|
+
window.removeEventListener('click', onClose)
|
|
93
|
+
window.removeEventListener('keydown', handleKeydown)
|
|
94
|
+
window.removeEventListener('touchstart', onClose)
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}, [onClose, handleKeydown])
|
|
97
|
+
return () => {}
|
|
98
|
+
}, [onClose, handleKeydown])
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
return (
|
|
101
|
+
<ListboxContext.Provider value={{ selectedId, setSelectedId }}>
|
|
102
|
+
<ExpandCollapse initialOpen={initialOpen} maxOpen={1} ref={ref}>
|
|
103
|
+
{(expandProps) => (
|
|
104
|
+
<View style={[styles.list, { minHeight, minWidth }]} role="listbox">
|
|
105
|
+
{items.map((item, index) => {
|
|
106
|
+
const { id, label, items: nestedItems } = item
|
|
107
|
+
const itemId = id ?? label
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
// Give the list of refs.
|
|
110
|
+
const itemRef = (currentItemRef) => {
|
|
111
|
+
itemRefs.current[index] = currentItemRef
|
|
112
|
+
return currentItemRef
|
|
113
|
+
}
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
115
|
+
return nestedItems ? (
|
|
116
|
+
<ListboxGroup
|
|
117
|
+
{...item}
|
|
118
|
+
expandProps={expandProps}
|
|
119
|
+
LinkRouter={LinkRouter}
|
|
120
|
+
itemRouterProps={itemRouterProps}
|
|
121
|
+
prevItemRef={itemRefs.current[index - 1] ?? null}
|
|
122
|
+
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
123
|
+
ref={index === 0 ? firstItemRef : itemRef}
|
|
124
|
+
key={itemId}
|
|
125
|
+
/>
|
|
126
|
+
) : (
|
|
127
|
+
<ListboxItem
|
|
128
|
+
{...item}
|
|
129
|
+
key={itemId}
|
|
130
|
+
id={itemId}
|
|
131
|
+
LinkRouter={LinkRouter}
|
|
132
|
+
itemRouterProps={itemRouterProps}
|
|
133
|
+
prevItemRef={itemRefs.current[index - 1] ?? null}
|
|
134
|
+
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
135
|
+
ref={index === 0 ? firstItemRef : itemRef}
|
|
136
|
+
/>
|
|
137
|
+
)
|
|
138
|
+
})}
|
|
139
|
+
</View>
|
|
140
|
+
)}
|
|
141
|
+
</ExpandCollapse>
|
|
142
|
+
</ListboxContext.Provider>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
Listbox.displayName = 'Listbox'
|
|
141
148
|
|
|
142
149
|
Listbox.propTypes = {
|
|
143
150
|
...withLinkRouter.propTypes,
|
package/src/Modal/WebModal.jsx
CHANGED
|
@@ -16,7 +16,10 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
16
16
|
const WebModal = ({ children, ...rest }) => {
|
|
17
17
|
return (
|
|
18
18
|
<View style={staticStyles.container} {...selectProps(rest)}>
|
|
19
|
-
|
|
19
|
+
{/* eslint-disable-next-line react-native-a11y/has-valid-accessibility-role */}
|
|
20
|
+
<View style={staticStyles.content} accessibilityRole="dialog">
|
|
21
|
+
{children}
|
|
22
|
+
</View>
|
|
20
23
|
</View>
|
|
21
24
|
)
|
|
22
25
|
}
|
|
@@ -46,14 +49,15 @@ const staticStyles = StyleSheet.create({
|
|
|
46
49
|
minHeight: 0,
|
|
47
50
|
minWidth: 0,
|
|
48
51
|
padding: 0,
|
|
49
|
-
|
|
50
|
-
zIndex:
|
|
52
|
+
textDecorationLine: 'none',
|
|
53
|
+
zIndex: 1000
|
|
51
54
|
},
|
|
52
55
|
content: {
|
|
53
56
|
flex: 1,
|
|
54
57
|
flexGrow: 1,
|
|
55
58
|
flexShrink: 1,
|
|
56
|
-
flexBasis: 0
|
|
59
|
+
flexBasis: 0,
|
|
60
|
+
zIndex: 1000
|
|
57
61
|
}
|
|
58
62
|
})
|
|
59
63
|
|
|
@@ -65,7 +65,7 @@ const selectDismissButtonContainerStyles = ({ dismissButtonGap }) => ({
|
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
const selectContentContainerStyle = (maxWidth) => ({
|
|
68
|
-
|
|
68
|
+
maxWidth: maxWidth || '100%'
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, dismissible) => {
|
package/src/Radio/Radio.jsx
CHANGED