@telus-uds/components-base 1.64.0 → 1.66.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 +22 -2
- package/lib/Checkbox/CheckboxButton.js +200 -0
- package/lib/CheckboxCard/CheckboxCard.js +225 -0
- package/lib/CheckboxCard/index.js +13 -0
- package/lib/StackView/StackView.js +4 -1
- package/lib/Tabs/Tabs.js +15 -0
- package/lib/TextInput/TextInputBase.js +102 -19
- package/lib/TextInput/propTypes.js +5 -0
- package/lib/index.js +9 -0
- package/lib/utils/props/textInputProps.js +1 -0
- package/lib-module/Checkbox/CheckboxButton.js +174 -0
- package/lib-module/CheckboxCard/CheckboxCard.js +200 -0
- package/lib-module/CheckboxCard/index.js +2 -0
- package/lib-module/StackView/StackView.js +4 -1
- package/lib-module/Tabs/Tabs.js +14 -0
- package/lib-module/TextInput/TextInputBase.js +101 -19
- package/lib-module/TextInput/propTypes.js +5 -0
- package/lib-module/index.js +1 -0
- package/lib-module/utils/props/textInputProps.js +1 -0
- package/package.json +2 -2
- package/src/Checkbox/CheckboxButton.jsx +180 -0
- package/src/CheckboxCard/CheckboxCard.jsx +190 -0
- package/src/CheckboxCard/index.js +3 -0
- package/src/StackView/StackView.jsx +2 -1
- package/src/Tabs/Tabs.jsx +14 -1
- package/src/TextInput/TextInputBase.jsx +67 -12
- package/src/TextInput/propTypes.js +4 -0
- package/src/index.js +1 -0
- package/src/utils/props/textInputProps.js +1 -0
package/src/Tabs/Tabs.jsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback } from 'react'
|
|
2
|
+
import { Platform } from 'react-native'
|
|
2
3
|
import PropTypes from 'prop-types'
|
|
3
4
|
import ABBPropTypes from 'airbnb-prop-types'
|
|
4
5
|
import { useThemeTokens } from '../ThemeProvider'
|
|
@@ -42,6 +43,17 @@ const getDefaultTabItemAccessibilityRole = (parentRole) => {
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
const getStackViewTokens = (variant) => {
|
|
47
|
+
const equalWidth = variant?.equalWidth
|
|
48
|
+
return Platform.select({
|
|
49
|
+
web: {
|
|
50
|
+
justifyContent: equalWidth ? 'space-evenly' : 'flex-start',
|
|
51
|
+
width: equalWidth ? '100%' : 'auto'
|
|
52
|
+
},
|
|
53
|
+
default: {}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
/**
|
|
46
58
|
* Tabs renders a horizontally-scrolling menu of selectable buttons which may link
|
|
47
59
|
* to a page or control what content is displayed on this page.
|
|
@@ -88,6 +100,7 @@ const Tabs = forwardRef(
|
|
|
88
100
|
const parentAccessibilityRole = restProps.accessibilityRole ?? 'tablist'
|
|
89
101
|
const defaultTabItemAccessibiltyRole =
|
|
90
102
|
getDefaultTabItemAccessibilityRole(parentAccessibilityRole)
|
|
103
|
+
const stackViewTokens = getStackViewTokens(variant)
|
|
91
104
|
|
|
92
105
|
return (
|
|
93
106
|
<HorizontalScroll
|
|
@@ -99,7 +112,7 @@ const Tabs = forwardRef(
|
|
|
99
112
|
accessibilityRole={parentAccessibilityRole}
|
|
100
113
|
{...restProps}
|
|
101
114
|
>
|
|
102
|
-
<StackView space={space} direction="row">
|
|
115
|
+
<StackView space={space} direction="row" tokens={stackViewTokens}>
|
|
103
116
|
{items.map(
|
|
104
117
|
(
|
|
105
118
|
{
|
|
@@ -4,6 +4,7 @@ import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-
|
|
|
4
4
|
import { applyTextStyles, useTheme, useThemeTokens, applyOuterBorder } from '../ThemeProvider'
|
|
5
5
|
import StackView from '../StackView'
|
|
6
6
|
import IconButton from '../IconButton'
|
|
7
|
+
import Icon from '../Icon'
|
|
7
8
|
import {
|
|
8
9
|
a11yProps,
|
|
9
10
|
getTokensPropType,
|
|
@@ -48,7 +49,8 @@ const selectInputStyles = (
|
|
|
48
49
|
height
|
|
49
50
|
},
|
|
50
51
|
themeOptions,
|
|
51
|
-
inactive
|
|
52
|
+
inactive,
|
|
53
|
+
type
|
|
52
54
|
) => {
|
|
53
55
|
// Subtract border width from padding so overall input width/height doesn't
|
|
54
56
|
// jump around if the border width changes (avoiding NaN and negative padding)
|
|
@@ -88,7 +90,7 @@ const selectInputStyles = (
|
|
|
88
90
|
borderWidth,
|
|
89
91
|
borderColor,
|
|
90
92
|
borderRadius,
|
|
91
|
-
paddingLeft: offsetBorder(paddingLeft),
|
|
93
|
+
paddingLeft: type === 'card' ? offsetBorder(paddingLeft + 34) : offsetBorder(paddingLeft),
|
|
92
94
|
paddingRight: icon ? offsetBorder(paddingWithIcon) : offsetBorder(paddingRight),
|
|
93
95
|
paddingTop: offsetBorder(paddingTop),
|
|
94
96
|
paddingBottom: offsetBorder(paddingBottom),
|
|
@@ -128,10 +130,29 @@ const selectIconContainerStyles = (
|
|
|
128
130
|
paddingBottom
|
|
129
131
|
})
|
|
130
132
|
|
|
133
|
+
const selectLeftIconContainerStyles = ({ leftIconPaddingBottom }) => ({
|
|
134
|
+
// not tokenizing paddingLeft as it remains same across brands for now
|
|
135
|
+
paddingLeft: 10,
|
|
136
|
+
paddingBottom: leftIconPaddingBottom
|
|
137
|
+
})
|
|
138
|
+
|
|
131
139
|
const selectButtonsContainerStyle = ({ buttonsPaddingRight }) => ({
|
|
132
140
|
paddingRight: buttonsPaddingRight
|
|
133
141
|
})
|
|
134
142
|
|
|
143
|
+
const getIcon = (cardNumber = '', { defaultCreditIcon, amexIcon, visaIcon, masterCardIcon }) => {
|
|
144
|
+
const cardType = {
|
|
145
|
+
1: { icon: visaIcon, testID: 'visa' },
|
|
146
|
+
2: { icon: amexIcon, testID: 'amex' },
|
|
147
|
+
4: { icon: masterCardIcon, testID: 'mastercard' }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const firstDigit = cardNumber ? cardNumber[0] : ''
|
|
151
|
+
const defaultIcon = { icon: defaultCreditIcon, testID: 'default' }
|
|
152
|
+
const selectedIcon = cardNumber.length > 4 ? cardType[firstDigit] || defaultIcon : defaultIcon
|
|
153
|
+
return <Icon icon={selectedIcon.icon} variant={{ size: 'large' }} testID={selectedIcon.testID} />
|
|
154
|
+
}
|
|
155
|
+
|
|
135
156
|
const TextInputBase = forwardRef(
|
|
136
157
|
(
|
|
137
158
|
{
|
|
@@ -152,6 +173,7 @@ const TextInputBase = forwardRef(
|
|
|
152
173
|
tokens,
|
|
153
174
|
value,
|
|
154
175
|
variant = {},
|
|
176
|
+
type,
|
|
155
177
|
...rest
|
|
156
178
|
},
|
|
157
179
|
ref
|
|
@@ -187,6 +209,9 @@ const TextInputBase = forwardRef(
|
|
|
187
209
|
onChange,
|
|
188
210
|
readOnly
|
|
189
211
|
})
|
|
212
|
+
const { password, numeric } = variant
|
|
213
|
+
const isNumeric = numeric || type === 'card' || type === 'number'
|
|
214
|
+
const isPassword = password || type === 'password'
|
|
190
215
|
|
|
191
216
|
const element = inputRef?.current
|
|
192
217
|
useEffect(() => {
|
|
@@ -198,9 +223,14 @@ const TextInputBase = forwardRef(
|
|
|
198
223
|
}, [element, pattern])
|
|
199
224
|
|
|
200
225
|
const handleChangeText = (event) => {
|
|
201
|
-
const { numeric } = variant
|
|
202
226
|
const text = event.nativeEvent?.text || event.target?.value
|
|
203
|
-
|
|
227
|
+
let filteredText = isNumeric ? text?.replace(/[^\d]/g, '') : text
|
|
228
|
+
if (type === 'card' && filteredText) {
|
|
229
|
+
const formattedValue = filteredText.replace(/[^a-zA-Z0-9]/g, '')
|
|
230
|
+
const regex = new RegExp(`([a-zA-Z0-9]{4})(?=[a-zA-Z0-9])`, 'g')
|
|
231
|
+
// Add a space every 4 digits starting from the 5th position
|
|
232
|
+
filteredText = formattedValue.replace(regex, '$1 ').trim()
|
|
233
|
+
}
|
|
204
234
|
setValue(filteredText, event)
|
|
205
235
|
if (typeof onChangeText === 'function') onChangeText(filteredText, event)
|
|
206
236
|
}
|
|
@@ -224,7 +254,11 @@ const TextInputBase = forwardRef(
|
|
|
224
254
|
clearButtonIcon: ClearButtonIcon,
|
|
225
255
|
icon: IconComponent,
|
|
226
256
|
passwordShowButtonIcon,
|
|
227
|
-
passwordHideButtonIcon
|
|
257
|
+
passwordHideButtonIcon,
|
|
258
|
+
defaultCreditIcon,
|
|
259
|
+
amexIcon,
|
|
260
|
+
visaIcon,
|
|
261
|
+
masterCardIcon
|
|
228
262
|
} = themeTokens
|
|
229
263
|
const buttonsGapSize = useSpacingScale(buttonsGap)
|
|
230
264
|
const getCopy = useCopy({ dictionary, copy })
|
|
@@ -242,7 +276,7 @@ const TextInputBase = forwardRef(
|
|
|
242
276
|
)
|
|
243
277
|
}
|
|
244
278
|
|
|
245
|
-
if (
|
|
279
|
+
if (isPassword) {
|
|
246
280
|
textInputButtons?.unshift(
|
|
247
281
|
<IconButton
|
|
248
282
|
accessibilityLabel={
|
|
@@ -268,27 +302,41 @@ const TextInputBase = forwardRef(
|
|
|
268
302
|
onMouseOut: handleMouseOut,
|
|
269
303
|
onChange: handleChangeText,
|
|
270
304
|
defaultValue: initialValue,
|
|
305
|
+
maxLength: type === 'card' ? 19 : undefined,
|
|
271
306
|
value: isControlled ? currentValue : undefined
|
|
272
307
|
}
|
|
273
308
|
|
|
274
309
|
const { themeOptions } = useTheme()
|
|
275
|
-
const nativeInputStyle = selectInputStyles(
|
|
310
|
+
const nativeInputStyle = selectInputStyles(
|
|
311
|
+
{ ...themeTokens, height },
|
|
312
|
+
themeOptions,
|
|
313
|
+
inactive,
|
|
314
|
+
type
|
|
315
|
+
)
|
|
276
316
|
|
|
277
317
|
return (
|
|
278
318
|
<View style={selectOuterBorderStyles(themeTokens)}>
|
|
319
|
+
{type === 'card' && (
|
|
320
|
+
<View
|
|
321
|
+
pointerEvents="none"
|
|
322
|
+
style={[staticStyles.leftIconContainer, selectLeftIconContainerStyles(themeTokens)]}
|
|
323
|
+
>
|
|
324
|
+
{getIcon(currentValue, { defaultCreditIcon, amexIcon, visaIcon, masterCardIcon })}
|
|
325
|
+
</View>
|
|
326
|
+
)}
|
|
279
327
|
<NativeTextInput
|
|
280
328
|
ref={inputRef}
|
|
281
|
-
keyboardType={
|
|
282
|
-
inputMode={
|
|
329
|
+
keyboardType={isNumeric && 'numeric'}
|
|
330
|
+
inputMode={isNumeric && 'numeric'}
|
|
283
331
|
style={nativeInputStyle}
|
|
284
|
-
secureTextEntry={
|
|
332
|
+
secureTextEntry={isPassword && !showPassword}
|
|
285
333
|
{...inputProps}
|
|
286
334
|
/>
|
|
287
335
|
{IconComponent && (
|
|
288
336
|
<View
|
|
289
337
|
pointerEvents="none" // avoid hijacking input press events
|
|
290
338
|
style={[
|
|
291
|
-
staticStyles.
|
|
339
|
+
staticStyles.rightIconContainer,
|
|
292
340
|
selectIconContainerStyles({ ...themeTokens, buttonsGapSize }, buttons?.length)
|
|
293
341
|
]}
|
|
294
342
|
>
|
|
@@ -321,6 +369,7 @@ TextInputBase.propTypes = {
|
|
|
321
369
|
clearButtonAccessibilityLabel: PropTypes.string
|
|
322
370
|
})
|
|
323
371
|
]),
|
|
372
|
+
type: PropTypes.oneOfType([PropTypes.oneOf(['password', 'card', 'number'])]),
|
|
324
373
|
height: PropTypes.number,
|
|
325
374
|
inactive: PropTypes.bool,
|
|
326
375
|
initialValue: PropTypes.string,
|
|
@@ -349,9 +398,15 @@ const staticStyles = StyleSheet.create({
|
|
|
349
398
|
bottom: 0,
|
|
350
399
|
justifyContent: 'center'
|
|
351
400
|
},
|
|
352
|
-
|
|
401
|
+
rightIconContainer: {
|
|
353
402
|
position: 'absolute',
|
|
354
403
|
right: 0,
|
|
355
404
|
bottom: 0
|
|
405
|
+
},
|
|
406
|
+
leftIconContainer: {
|
|
407
|
+
position: 'absolute',
|
|
408
|
+
left: 0,
|
|
409
|
+
bottom: 0,
|
|
410
|
+
zIndex: 1
|
|
356
411
|
}
|
|
357
412
|
})
|
|
@@ -8,6 +8,10 @@ const textInputPropTypes = {
|
|
|
8
8
|
* together with the `onChange` to pass down and update the lifted state.
|
|
9
9
|
*/
|
|
10
10
|
value: PropTypes.string,
|
|
11
|
+
/**
|
|
12
|
+
* Use this to set the type of the input. Defaults to `text`.
|
|
13
|
+
*/
|
|
14
|
+
type: PropTypes.string,
|
|
11
15
|
/**
|
|
12
16
|
* Use this to set the initial value of an uncontrolled input.
|
|
13
17
|
* Updating `initialValue` will **not** update the actual value.
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './Carousel'
|
|
|
8
8
|
export { default as Listbox } from './Listbox'
|
|
9
9
|
export { default as Checkbox } from './Checkbox'
|
|
10
10
|
export * from './Checkbox'
|
|
11
|
+
export { default as CheckboxCard } from './CheckboxCard'
|
|
11
12
|
export { default as Divider } from './Divider'
|
|
12
13
|
export { default as ExpandCollapse, Accordion } from './ExpandCollapse'
|
|
13
14
|
export { default as Feedback } from './Feedback'
|