@telus-uds/components-base 3.12.2 → 3.14.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 +37 -2
- package/lib/cjs/BaseProvider/index.js +4 -1
- package/lib/cjs/Button/ButtonDropdown.js +105 -12
- package/lib/cjs/Card/Card.js +23 -4
- package/lib/cjs/Card/CardBase.js +170 -19
- package/lib/cjs/Card/PressableCardBase.js +19 -5
- package/lib/cjs/Card/backgroundImageStylesMap.js +197 -0
- package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +1 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
- package/lib/cjs/FlexGrid/FlexGrid.js +71 -6
- package/lib/cjs/Icon/Icon.js +3 -1
- package/lib/cjs/InputLabel/InputLabel.js +1 -1
- package/lib/cjs/InputSupports/InputSupports.js +1 -1
- package/lib/cjs/Notification/Notification.js +27 -8
- package/lib/cjs/Tabs/Tabs.js +34 -2
- package/lib/cjs/Tabs/TabsDropdown.js +252 -0
- package/lib/cjs/Tabs/TabsItem.js +4 -2
- package/lib/cjs/Tabs/dictionary.js +14 -0
- package/lib/cjs/ViewportProvider/ViewportProvider.js +9 -3
- package/lib/cjs/utils/props/inputSupportsProps.js +1 -1
- package/lib/esm/BaseProvider/index.js +4 -1
- package/lib/esm/Button/ButtonDropdown.js +107 -14
- package/lib/esm/Card/Card.js +21 -4
- package/lib/esm/Card/CardBase.js +169 -19
- package/lib/esm/Card/PressableCardBase.js +19 -5
- package/lib/esm/Card/backgroundImageStylesMap.js +190 -0
- package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +2 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
- package/lib/esm/FlexGrid/FlexGrid.js +72 -7
- package/lib/esm/Icon/Icon.js +3 -1
- package/lib/esm/InputLabel/InputLabel.js +1 -1
- package/lib/esm/InputSupports/InputSupports.js +1 -1
- package/lib/esm/Notification/Notification.js +27 -8
- package/lib/esm/Tabs/Tabs.js +35 -3
- package/lib/esm/Tabs/TabsDropdown.js +245 -0
- package/lib/esm/Tabs/TabsItem.js +4 -2
- package/lib/esm/Tabs/dictionary.js +8 -0
- package/lib/esm/ViewportProvider/ViewportProvider.js +9 -3
- package/lib/esm/utils/props/inputSupportsProps.js +1 -1
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/BaseProvider/index.jsx +4 -2
- package/src/Button/ButtonDropdown.jsx +109 -16
- package/src/Card/Card.jsx +27 -3
- package/src/Card/CardBase.jsx +165 -19
- package/src/Card/PressableCardBase.jsx +31 -4
- package/src/Card/backgroundImageStylesMap.js +41 -0
- package/src/ExpandCollapse/ExpandCollapse.jsx +5 -2
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +2 -2
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +39 -9
- package/src/FlexGrid/FlexGrid.jsx +80 -7
- package/src/Icon/Icon.jsx +3 -1
- package/src/InputLabel/InputLabel.jsx +1 -1
- package/src/InputSupports/InputSupports.jsx +1 -1
- package/src/Notification/Notification.jsx +58 -9
- package/src/Tabs/Tabs.jsx +36 -2
- package/src/Tabs/TabsDropdown.jsx +265 -0
- package/src/Tabs/TabsItem.jsx +4 -2
- package/src/Tabs/dictionary.js +8 -0
- package/src/ViewportProvider/ViewportProvider.jsx +8 -3
- package/src/utils/props/inputSupportsProps.js +1 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
|
4
|
+
import { useThemeTokensCallback, applyTextStyles, useTheme } from '../ThemeProvider'
|
|
5
|
+
import {
|
|
6
|
+
a11yProps,
|
|
7
|
+
getTokensPropType,
|
|
8
|
+
resolvePressableTokens,
|
|
9
|
+
selectSystemProps,
|
|
10
|
+
selectTokens,
|
|
11
|
+
useOverlaidPosition,
|
|
12
|
+
useCopy,
|
|
13
|
+
variantProp,
|
|
14
|
+
viewProps,
|
|
15
|
+
withLinkRouter
|
|
16
|
+
} from '../utils'
|
|
17
|
+
import { useViewport } from '../ViewportProvider'
|
|
18
|
+
import Icon from '../Icon'
|
|
19
|
+
import Listbox from '../Listbox'
|
|
20
|
+
import dictionary from './dictionary'
|
|
21
|
+
|
|
22
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
23
|
+
|
|
24
|
+
const selectButtonContentStyles = ({
|
|
25
|
+
backgroundColor,
|
|
26
|
+
borderColor,
|
|
27
|
+
borderWidth,
|
|
28
|
+
borderRadius,
|
|
29
|
+
paddingHorizontal,
|
|
30
|
+
paddingVertical,
|
|
31
|
+
marginHorizontal,
|
|
32
|
+
marginVertical
|
|
33
|
+
}) => ({
|
|
34
|
+
backgroundColor,
|
|
35
|
+
borderColor,
|
|
36
|
+
borderWidth,
|
|
37
|
+
borderRadius,
|
|
38
|
+
paddingHorizontal,
|
|
39
|
+
paddingVertical,
|
|
40
|
+
marginLeft: marginHorizontal,
|
|
41
|
+
marginRight: marginHorizontal,
|
|
42
|
+
marginTop: marginVertical,
|
|
43
|
+
marginBottom: marginVertical
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* TabsDropdown renders a dropdown version of tabs for mobile/tablet viewports.
|
|
48
|
+
* It shows the currently selected tab as a button that opens a dropdown menu
|
|
49
|
+
* containing all available tabs.
|
|
50
|
+
*
|
|
51
|
+
* This is rendered automatically by `Tabs` on mobile viewports and when variant
|
|
52
|
+
* is dropdown and isn't intended to be used directly.
|
|
53
|
+
*/
|
|
54
|
+
const TabsDropdown = React.forwardRef(
|
|
55
|
+
(
|
|
56
|
+
{
|
|
57
|
+
itemTokens,
|
|
58
|
+
variant,
|
|
59
|
+
value,
|
|
60
|
+
onChange,
|
|
61
|
+
items = [],
|
|
62
|
+
LinkRouter,
|
|
63
|
+
linkRouterProps,
|
|
64
|
+
accessibilityRole = 'button',
|
|
65
|
+
copy = 'en',
|
|
66
|
+
dictionary: customDictionary = dictionary,
|
|
67
|
+
...rest
|
|
68
|
+
},
|
|
69
|
+
ref
|
|
70
|
+
) => {
|
|
71
|
+
const { themeOptions } = useTheme()
|
|
72
|
+
const viewport = useViewport()
|
|
73
|
+
const [isOpen, setIsOpen] = React.useState(false)
|
|
74
|
+
|
|
75
|
+
const getTokens = useThemeTokensCallback('TabsItem', itemTokens, { viewport, ...variant })
|
|
76
|
+
|
|
77
|
+
const selectedItem =
|
|
78
|
+
items.find((item) => {
|
|
79
|
+
const itemId = item.id ?? item.label
|
|
80
|
+
return value === itemId
|
|
81
|
+
}) || items[0]
|
|
82
|
+
|
|
83
|
+
const { overlaidPosition, sourceRef, targetRef, onTargetLayout, isReady } = useOverlaidPosition(
|
|
84
|
+
{
|
|
85
|
+
isShown: isOpen,
|
|
86
|
+
offsets: { vertical: 4 },
|
|
87
|
+
align: { top: 'bottom', left: 'left' }
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
const handleToggle = () => setIsOpen((prev) => !prev)
|
|
92
|
+
const handleClose = () => setIsOpen(false)
|
|
93
|
+
|
|
94
|
+
const handleItemSelect = (item, event) => {
|
|
95
|
+
const itemId = item.id ?? item.label
|
|
96
|
+
setIsOpen(false)
|
|
97
|
+
|
|
98
|
+
if (onChange) onChange(itemId, event)
|
|
99
|
+
if (item.onPress) item.onPress(event)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const listboxItems = items.map((item) => ({
|
|
103
|
+
...item,
|
|
104
|
+
onPress: (event) => handleItemSelect(item, event)
|
|
105
|
+
}))
|
|
106
|
+
|
|
107
|
+
const isSelected = Boolean(selectedItem && value)
|
|
108
|
+
const getCopy = useCopy({ dictionary: customDictionary, copy })
|
|
109
|
+
|
|
110
|
+
const selectedProps = selectProps({
|
|
111
|
+
accessibilityRole,
|
|
112
|
+
...rest
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<View ref={ref} style={styles.container}>
|
|
117
|
+
<Pressable
|
|
118
|
+
ref={sourceRef}
|
|
119
|
+
onPress={handleToggle}
|
|
120
|
+
{...selectedProps}
|
|
121
|
+
style={styles.pressable}
|
|
122
|
+
>
|
|
123
|
+
{(pressableState) => {
|
|
124
|
+
// Use resolvePressableTokens like TabBarItem does for proper state handling
|
|
125
|
+
const resolvedTokens = resolvePressableTokens(getTokens, pressableState, {
|
|
126
|
+
viewport,
|
|
127
|
+
expanded: isOpen,
|
|
128
|
+
selected: isSelected
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const textStyles = applyTextStyles({
|
|
132
|
+
...selectTokens('Typography', resolvedTokens),
|
|
133
|
+
themeOptions
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Get dropdown icons from resolved tokens
|
|
137
|
+
const dropdownIcon = isOpen
|
|
138
|
+
? resolvedTokens.dropdownIconExpanded
|
|
139
|
+
: resolvedTokens.dropdownIcon
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<View style={[styles.buttonContent, selectButtonContentStyles(resolvedTokens)]}>
|
|
143
|
+
<Text style={textStyles}>{selectedItem?.label || getCopy('selectTab')}</Text>
|
|
144
|
+
{dropdownIcon && (
|
|
145
|
+
<Icon
|
|
146
|
+
icon={dropdownIcon}
|
|
147
|
+
variant={{ size: 'micro' }}
|
|
148
|
+
tokens={{
|
|
149
|
+
color: textStyles.color
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
</View>
|
|
154
|
+
)
|
|
155
|
+
}}
|
|
156
|
+
</Pressable>
|
|
157
|
+
|
|
158
|
+
{isOpen && (
|
|
159
|
+
<Listbox.Overlay
|
|
160
|
+
overlaidPosition={overlaidPosition}
|
|
161
|
+
maxWidth={400}
|
|
162
|
+
minWidth={200}
|
|
163
|
+
isReady={isReady}
|
|
164
|
+
onLayout={onTargetLayout}
|
|
165
|
+
>
|
|
166
|
+
<Listbox
|
|
167
|
+
items={listboxItems}
|
|
168
|
+
firstItemRef={targetRef}
|
|
169
|
+
parentRef={sourceRef}
|
|
170
|
+
selectedId={value}
|
|
171
|
+
onClose={handleClose}
|
|
172
|
+
LinkRouter={LinkRouter}
|
|
173
|
+
linkRouterProps={linkRouterProps}
|
|
174
|
+
/>
|
|
175
|
+
</Listbox.Overlay>
|
|
176
|
+
)}
|
|
177
|
+
</View>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
TabsDropdown.displayName = 'TabsDropdown'
|
|
183
|
+
|
|
184
|
+
const dictionaryContentShape = PropTypes.shape({
|
|
185
|
+
selectTab: PropTypes.string.isRequired
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
TabsDropdown.propTypes = {
|
|
189
|
+
...selectedSystemPropTypes,
|
|
190
|
+
...withLinkRouter.propTypes,
|
|
191
|
+
/**
|
|
192
|
+
* Array of tab items
|
|
193
|
+
*/
|
|
194
|
+
items: PropTypes.arrayOf(
|
|
195
|
+
PropTypes.shape({
|
|
196
|
+
...withLinkRouter.propTypes,
|
|
197
|
+
/** URL to navigate to when the tab is pressed */
|
|
198
|
+
href: PropTypes.string,
|
|
199
|
+
/** Display text for the tab */
|
|
200
|
+
label: PropTypes.string,
|
|
201
|
+
/** Unique identifier for the tab */
|
|
202
|
+
id: PropTypes.string,
|
|
203
|
+
/** Reference to the tab element */
|
|
204
|
+
ref: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
205
|
+
/** Custom render function for the tab content */
|
|
206
|
+
render: PropTypes.func
|
|
207
|
+
})
|
|
208
|
+
),
|
|
209
|
+
/**
|
|
210
|
+
* Current selected tab id
|
|
211
|
+
*/
|
|
212
|
+
value: PropTypes.string,
|
|
213
|
+
/**
|
|
214
|
+
* Callback for when the selected tab changes
|
|
215
|
+
*/
|
|
216
|
+
onChange: PropTypes.func,
|
|
217
|
+
/**
|
|
218
|
+
* Custom tokens for the main Tabs container
|
|
219
|
+
*/
|
|
220
|
+
tokens: getTokensPropType('Tabs'),
|
|
221
|
+
/**
|
|
222
|
+
* Custom tokens for `TabsItem`
|
|
223
|
+
*/
|
|
224
|
+
itemTokens: getTokensPropType('TabsItem'),
|
|
225
|
+
/**
|
|
226
|
+
* Visual and behavioral variants for the tabs dropdown
|
|
227
|
+
*/
|
|
228
|
+
variant: variantProp.propType,
|
|
229
|
+
/**
|
|
230
|
+
* Select English or French copy for the accessible labels.
|
|
231
|
+
* You may also pass in a custom dictionary object.
|
|
232
|
+
*/
|
|
233
|
+
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
|
|
234
|
+
/**
|
|
235
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
236
|
+
*/
|
|
237
|
+
dictionary: PropTypes.shape({
|
|
238
|
+
en: dictionaryContentShape,
|
|
239
|
+
fr: dictionaryContentShape
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const styles = StyleSheet.create({
|
|
244
|
+
container: {
|
|
245
|
+
position: 'relative',
|
|
246
|
+
width: '100%'
|
|
247
|
+
},
|
|
248
|
+
pressable: {
|
|
249
|
+
outlineWidth: 0,
|
|
250
|
+
outlineStyle: 'none',
|
|
251
|
+
outlineColor: 'transparent'
|
|
252
|
+
},
|
|
253
|
+
buttonContent: {
|
|
254
|
+
display: 'flex',
|
|
255
|
+
flexDirection: 'row',
|
|
256
|
+
alignItems: 'center',
|
|
257
|
+
justifyContent: 'space-between',
|
|
258
|
+
width: '100%',
|
|
259
|
+
minHeight: 44,
|
|
260
|
+
outline: 'none',
|
|
261
|
+
boxSizing: 'border-box'
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
export default TabsDropdown
|
package/src/Tabs/TabsItem.jsx
CHANGED
|
@@ -70,8 +70,10 @@ const selectContainerStyles = ({
|
|
|
70
70
|
borderRadius,
|
|
71
71
|
paddingHorizontal: paddingHorizontal - borderWidth,
|
|
72
72
|
paddingVertical: paddingVertical - borderWidth,
|
|
73
|
-
marginHorizontal,
|
|
74
|
-
|
|
73
|
+
marginLeft: marginHorizontal,
|
|
74
|
+
marginRight: marginHorizontal,
|
|
75
|
+
marginTop: marginVertical,
|
|
76
|
+
marginBottom: marginVertical
|
|
75
77
|
})
|
|
76
78
|
|
|
77
79
|
/**
|
|
@@ -6,16 +6,21 @@ import useViewportListener from './useViewportListener'
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Provides an up-to-date viewport value from system-constants, available via the `useViewport` hook
|
|
9
|
+
*
|
|
10
|
+
* @param {React.ReactNode} children - Child components that will have access to viewport context
|
|
11
|
+
* @param {string} [defaultViewport] - Default viewport to use during server-side rendering.
|
|
12
|
+
* Must be one of the viewport keys from system-constants. If not provided, defaults to the smallest viewport.
|
|
9
13
|
*/
|
|
10
|
-
const ViewportProvider = ({ children }) => {
|
|
14
|
+
const ViewportProvider = ({ children, defaultViewport }) => {
|
|
11
15
|
// Default to the smallest viewport for mobile-first SSR. On client side, this is updated
|
|
12
16
|
// by useViewportListener in a layout effect before anything is shown to the user.
|
|
13
|
-
const [viewport, setViewport] = React.useState(viewports.keys[0])
|
|
17
|
+
const [viewport, setViewport] = React.useState(defaultViewport || viewports.keys[0])
|
|
14
18
|
useViewportListener(setViewport)
|
|
15
19
|
return <ViewportContext.Provider value={viewport}>{children}</ViewportContext.Provider>
|
|
16
20
|
}
|
|
17
21
|
ViewportProvider.propTypes = {
|
|
18
|
-
children: PropTypes.node.isRequired
|
|
22
|
+
children: PropTypes.node.isRequired,
|
|
23
|
+
defaultViewport: PropTypes.oneOf(viewports.keys)
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export default ViewportProvider
|
|
@@ -38,7 +38,7 @@ export default {
|
|
|
38
38
|
* 1. `tooltip` as a string - The content of the tooltip.
|
|
39
39
|
* 2. `tooltip` as an object - Tooltip component props to be passed.
|
|
40
40
|
*/
|
|
41
|
-
tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
|
|
41
|
+
tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
|
|
42
42
|
/**
|
|
43
43
|
* Use to visually mark an input as valid or invalid.
|
|
44
44
|
*/
|