@telus-uds/components-base 1.18.1 → 1.20.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 +42 -2
- package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +2 -1
- package/component-docs.json +1035 -231
- package/jest.config-android.js +17 -0
- package/jest.config-ios.js +18 -0
- package/jest.config-web.js +31 -0
- package/lib/BaseProvider/index.js +2 -1
- package/lib/Box/Box.js +14 -1
- package/lib/Button/ButtonBase.js +6 -2
- package/lib/Button/ButtonDropdown.js +207 -0
- package/lib/Button/index.js +8 -0
- package/lib/Carousel/Carousel.js +34 -6
- package/lib/Carousel/CarouselItem/CarouselItem.js +7 -1
- package/lib/Carousel/CarouselTabs/CarouselTabsPanel.js +22 -14
- package/lib/FlexGrid/Col/Col.js +1 -3
- package/lib/FlexGrid/FlexGrid.js +3 -5
- package/lib/FlexGrid/Row/Row.js +3 -3
- package/lib/IconButton/IconButton.js +12 -4
- package/lib/MultiSelectFilter/MultiSelectFilter.js +276 -0
- package/lib/MultiSelectFilter/dictionary.js +19 -0
- package/lib/MultiSelectFilter/index.js +13 -0
- package/lib/Pagination/SideButton.js +6 -4
- package/lib/Responsive/Responsive.js +58 -0
- package/lib/Responsive/index.js +13 -0
- package/lib/Search/Search.js +33 -63
- package/lib/Select/Picker.native.js +16 -13
- package/lib/Select/Select.js +7 -1
- package/lib/Select/constants.js +15 -0
- package/lib/StepTracker/Step.js +2 -1
- package/lib/Tags/Tags.js +10 -4
- package/lib/TextInput/TextInput.js +9 -2
- package/lib/TextInput/TextInputBase.js +98 -20
- package/lib/TextInput/dictionary.js +15 -0
- package/lib/ThemeProvider/ThemeProvider.js +6 -1
- package/lib/index.js +18 -0
- package/lib/utils/BaseView/BaseView.js +64 -0
- package/lib/utils/BaseView/BaseView.native.js +16 -0
- package/lib/utils/BaseView/index.js +13 -0
- package/lib/utils/index.js +10 -1
- package/lib/utils/input.js +11 -3
- package/lib/utils/props/handlerProps.js +5 -0
- package/lib-module/BaseProvider/index.js +2 -1
- package/lib-module/Box/Box.js +14 -1
- package/lib-module/Button/ButtonBase.js +6 -2
- package/lib-module/Button/ButtonDropdown.js +181 -0
- package/lib-module/Button/index.js +2 -1
- package/lib-module/Carousel/Carousel.js +34 -6
- package/lib-module/Carousel/CarouselItem/CarouselItem.js +8 -2
- package/lib-module/Carousel/CarouselTabs/CarouselTabsPanel.js +24 -16
- package/lib-module/FlexGrid/Col/Col.js +2 -3
- package/lib-module/FlexGrid/FlexGrid.js +2 -3
- package/lib-module/FlexGrid/Row/Row.js +2 -2
- package/lib-module/IconButton/IconButton.js +14 -4
- package/lib-module/MultiSelectFilter/MultiSelectFilter.js +248 -0
- package/lib-module/MultiSelectFilter/dictionary.js +12 -0
- package/lib-module/MultiSelectFilter/index.js +2 -0
- package/lib-module/Pagination/SideButton.js +6 -4
- package/lib-module/Responsive/Responsive.js +45 -0
- package/lib-module/Responsive/index.js +2 -0
- package/lib-module/Search/Search.js +33 -61
- package/lib-module/Select/Picker.native.js +15 -13
- package/lib-module/Select/Select.js +6 -1
- package/lib-module/Select/constants.js +5 -0
- package/lib-module/StepTracker/Step.js +2 -1
- package/lib-module/Tags/Tags.js +10 -4
- package/lib-module/TextInput/TextInput.js +6 -0
- package/lib-module/TextInput/TextInputBase.js +96 -21
- package/lib-module/TextInput/dictionary.js +8 -0
- package/lib-module/ThemeProvider/ThemeProvider.js +6 -1
- package/lib-module/index.js +2 -0
- package/lib-module/utils/BaseView/BaseView.js +43 -0
- package/lib-module/utils/BaseView/BaseView.native.js +6 -0
- package/lib-module/utils/BaseView/index.js +2 -0
- package/lib-module/utils/index.js +2 -1
- package/lib-module/utils/input.js +11 -3
- package/lib-module/utils/props/handlerProps.js +5 -0
- package/package.json +6 -3
- package/src/BaseProvider/index.jsx +4 -1
- package/src/Box/Box.jsx +14 -1
- package/src/Button/ButtonBase.jsx +4 -2
- package/src/Button/ButtonDropdown.jsx +179 -0
- package/src/Button/index.js +2 -1
- package/src/Carousel/Carousel.jsx +48 -13
- package/src/Carousel/CarouselItem/CarouselItem.jsx +9 -2
- package/src/Carousel/CarouselTabs/CarouselTabsPanel.jsx +19 -15
- package/src/FlexGrid/Col/Col.jsx +4 -4
- package/src/FlexGrid/FlexGrid.jsx +11 -10
- package/src/FlexGrid/Row/Row.jsx +4 -3
- package/src/IconButton/IconButton.jsx +3 -1
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +227 -0
- package/src/MultiSelectFilter/dictionary.js +12 -0
- package/src/MultiSelectFilter/index.js +3 -0
- package/src/Pagination/SideButton.jsx +5 -5
- package/src/Responsive/Responsive.jsx +33 -0
- package/src/Responsive/index.js +3 -0
- package/src/Search/Search.jsx +19 -33
- package/src/Select/Picker.native.jsx +29 -14
- package/src/Select/Select.jsx +7 -1
- package/src/Select/constants.js +5 -0
- package/src/StepTracker/Step.jsx +5 -1
- package/src/Tags/Tags.jsx +46 -33
- package/src/TextInput/TextInput.jsx +5 -0
- package/src/TextInput/TextInputBase.jsx +85 -20
- package/src/TextInput/dictionary.js +8 -0
- package/src/ThemeProvider/ThemeProvider.jsx +5 -1
- package/src/index.js +2 -0
- package/src/utils/BaseView/BaseView.jsx +38 -0
- package/src/utils/BaseView/BaseView.native.jsx +6 -0
- package/src/utils/BaseView/index.js +3 -0
- package/src/utils/index.js +1 -0
- package/src/utils/input.js +9 -4
- package/src/utils/props/handlerProps.js +4 -0
package/src/Search/Search.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react'
|
|
2
|
-
import { View
|
|
2
|
+
import { View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
import PropTypes from 'prop-types'
|
|
5
5
|
import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider'
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
} from '../utils'
|
|
18
18
|
import TextInputBase from '../TextInput/TextInputBase'
|
|
19
19
|
import ButtonBase from '../Button/ButtonBase'
|
|
20
|
-
import StackView from '../StackView'
|
|
21
20
|
import useCopy from '../utils/useCopy'
|
|
22
21
|
import dictionary from './dictionary'
|
|
23
22
|
|
|
@@ -42,7 +41,6 @@ const selectInputTokens = ({ searchTokens, buttonTokens, buttonsGapSize }) => {
|
|
|
42
41
|
}
|
|
43
42
|
const selectButtonTokens = (tokens) => selectTokens('Button', tokens)
|
|
44
43
|
|
|
45
|
-
const selectIconsContainerStyle = ({ paddingRight }) => ({ paddingRight })
|
|
46
44
|
const selectIconTokens = ({ iconSize, iconColor }) => ({ color: iconColor, size: iconSize })
|
|
47
45
|
|
|
48
46
|
/**
|
|
@@ -110,7 +108,8 @@ const Search = forwardRef(
|
|
|
110
108
|
|
|
111
109
|
const handleClear = (event) => {
|
|
112
110
|
setValue('', event)
|
|
113
|
-
|
|
111
|
+
onClear?.('', event)
|
|
112
|
+
ref?.current?.focus()
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
const handleFocus = (event) => {
|
|
@@ -126,7 +125,7 @@ const Search = forwardRef(
|
|
|
126
125
|
const { nativeID, testID, ...containerProps } = selectContainerProps(rest)
|
|
127
126
|
|
|
128
127
|
return (
|
|
129
|
-
<View
|
|
128
|
+
<View {...containerProps}>
|
|
130
129
|
<TextInputBase
|
|
131
130
|
nativeID={nativeID}
|
|
132
131
|
testID={testID}
|
|
@@ -150,28 +149,28 @@ const Search = forwardRef(
|
|
|
150
149
|
onSubmitEditing={handleSubmit}
|
|
151
150
|
onFocus={handleFocus}
|
|
152
151
|
accessibilityLabel={a11yLabelText}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<StackView direction="row" space={buttonsGap}>
|
|
156
|
-
{ClearButtonIcon && !isEmpty && (
|
|
152
|
+
buttons={[
|
|
153
|
+
ClearButtonIcon && !isEmpty && (
|
|
157
154
|
<ButtonBase
|
|
158
|
-
onPress={handleClear}
|
|
159
|
-
inactive={inactive}
|
|
160
|
-
accessibilityRole="button"
|
|
161
155
|
accessibilityLabel={getCopy('clearButtonAccessibilityLabel')}
|
|
156
|
+
accessibilityRole="button"
|
|
157
|
+
inactive={inactive}
|
|
158
|
+
key="clear"
|
|
159
|
+
onPress={handleClear}
|
|
162
160
|
tokens={(appearances) => selectButtonTokens(getButtonTokens(appearances))}
|
|
163
161
|
>
|
|
164
162
|
{(buttonState) => (
|
|
165
163
|
<ClearButtonIcon {...selectIconTokens(getButtonTokens(buttonState))} />
|
|
166
164
|
)}
|
|
167
165
|
</ButtonBase>
|
|
168
|
-
)
|
|
169
|
-
|
|
166
|
+
),
|
|
167
|
+
SubmitButtonIcon && (
|
|
170
168
|
<ButtonBase
|
|
171
|
-
onPress={handleSubmit}
|
|
172
|
-
inactive={inactive}
|
|
173
|
-
accessibilityRole="button"
|
|
174
169
|
accessibilityLabel={getCopy('submitButtonAccessibilityLabel')}
|
|
170
|
+
accessibilityRole="button"
|
|
171
|
+
inactive={inactive}
|
|
172
|
+
key="submit"
|
|
173
|
+
onPress={handleSubmit}
|
|
175
174
|
tokens={(buttonState) =>
|
|
176
175
|
selectButtonTokens(getButtonTokens({ ...buttonState, priority: 'high' }))
|
|
177
176
|
}
|
|
@@ -182,9 +181,9 @@ const Search = forwardRef(
|
|
|
182
181
|
/>
|
|
183
182
|
)}
|
|
184
183
|
</ButtonBase>
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
)
|
|
185
|
+
]}
|
|
186
|
+
/>
|
|
188
187
|
</View>
|
|
189
188
|
)
|
|
190
189
|
}
|
|
@@ -248,16 +247,3 @@ Search.propTypes = {
|
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
export default Search
|
|
251
|
-
|
|
252
|
-
const staticStyles = StyleSheet.create({
|
|
253
|
-
container: {
|
|
254
|
-
// No styles needed here except the View defaults (position: relative etc)
|
|
255
|
-
},
|
|
256
|
-
iconsContainer: {
|
|
257
|
-
position: 'absolute',
|
|
258
|
-
right: 0,
|
|
259
|
-
top: 0,
|
|
260
|
-
bottom: 0,
|
|
261
|
-
justifyContent: 'center'
|
|
262
|
-
}
|
|
263
|
-
})
|
|
@@ -6,28 +6,43 @@ import NativePicker from 'react-native-picker-select'
|
|
|
6
6
|
import { a11yProps, componentPropType } from '../utils'
|
|
7
7
|
import Group from './Group'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}) => ({
|
|
17
|
-
height
|
|
9
|
+
import {
|
|
10
|
+
ANDROID_HEIGHT_OFFSET,
|
|
11
|
+
ANDROID_HORIZONTAL_PADDING_OFFSET,
|
|
12
|
+
ANDROID_DEFAULT_PADDING
|
|
13
|
+
} from './constants'
|
|
14
|
+
|
|
15
|
+
// Styling of the native input is very limited, most of the styles have to be applied to an additional View
|
|
16
|
+
const selectAndroidInputStyles = ({ height = 0, color }) => ({
|
|
17
|
+
height,
|
|
18
|
+
paddingBottom: 0,
|
|
19
|
+
paddingTop: 0,
|
|
18
20
|
color
|
|
19
21
|
})
|
|
20
22
|
|
|
21
|
-
//
|
|
22
|
-
const selectAndroidContainerStyles = ({
|
|
23
|
-
paddingLeft
|
|
24
|
-
paddingRight
|
|
23
|
+
// The native input has a side padding of 8px, which can't be adjusted, so we have to account for that in the container
|
|
24
|
+
const selectAndroidContainerStyles = ({
|
|
25
|
+
paddingLeft = ANDROID_DEFAULT_PADDING,
|
|
26
|
+
paddingRight = ANDROID_DEFAULT_PADDING,
|
|
25
27
|
...rest
|
|
28
|
+
}) => ({
|
|
29
|
+
...rest,
|
|
30
|
+
paddingLeft:
|
|
31
|
+
paddingLeft > ANDROID_HORIZONTAL_PADDING_OFFSET
|
|
32
|
+
? paddingLeft - ANDROID_HORIZONTAL_PADDING_OFFSET
|
|
33
|
+
: ANDROID_DEFAULT_PADDING,
|
|
34
|
+
paddingRight:
|
|
35
|
+
paddingRight > ANDROID_HORIZONTAL_PADDING_OFFSET
|
|
36
|
+
? paddingRight - ANDROID_HORIZONTAL_PADDING_OFFSET
|
|
37
|
+
: ANDROID_DEFAULT_PADDING,
|
|
38
|
+
paddingBottom: ANDROID_DEFAULT_PADDING,
|
|
39
|
+
paddingTop: ANDROID_DEFAULT_PADDING,
|
|
40
|
+
height: rest.height + ANDROID_HEIGHT_OFFSET
|
|
26
41
|
})
|
|
27
42
|
|
|
28
43
|
const Picker = forwardRef(
|
|
29
44
|
({ value, onChange, onFocus, onBlur, style, inactive, children, placeholder, ...rest }, ref) => {
|
|
30
|
-
//
|
|
45
|
+
// Ungroup items, since there's no way to support groups on native
|
|
31
46
|
const flatChildren = Children.toArray(children).flatMap((child) => {
|
|
32
47
|
if (child.type === Group) {
|
|
33
48
|
return child.props.children
|
package/src/Select/Select.jsx
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
import Picker from './Picker'
|
|
17
17
|
import InputSupports from '../InputSupports'
|
|
18
18
|
|
|
19
|
+
import { ANDROID_VALIDATION_ICON_CONTAINER_OFFSET } from './constants'
|
|
20
|
+
|
|
19
21
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
20
22
|
a11yProps,
|
|
21
23
|
inputSupportsProps,
|
|
@@ -133,7 +135,11 @@ const selectValidationIconContainerStyles = ({
|
|
|
133
135
|
paddingBottom
|
|
134
136
|
}) => ({
|
|
135
137
|
paddingRight: icon ? paddingRight + iconSize : paddingRight,
|
|
136
|
-
|
|
138
|
+
...(Platform.OS === 'android'
|
|
139
|
+
? {
|
|
140
|
+
paddingBottom: paddingBottom + ANDROID_VALIDATION_ICON_CONTAINER_OFFSET
|
|
141
|
+
}
|
|
142
|
+
: { paddingBottom })
|
|
137
143
|
})
|
|
138
144
|
|
|
139
145
|
/**
|
package/src/StepTracker/Step.jsx
CHANGED
|
@@ -138,7 +138,11 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
|
|
|
138
138
|
accessibilityCurrent={status === stepIndex}
|
|
139
139
|
{...selectProps(rest)}
|
|
140
140
|
>
|
|
141
|
-
<StackView
|
|
141
|
+
<StackView
|
|
142
|
+
direction="row"
|
|
143
|
+
space={0}
|
|
144
|
+
tokens={{ alignItems: 'center', flexGrow: 0, justifyContent: 'center' }}
|
|
145
|
+
>
|
|
142
146
|
<View
|
|
143
147
|
style={[staticStyles.connector, !isFirst && selectConnectorStyles(themeTokens, isActive)]}
|
|
144
148
|
/>
|
package/src/Tags/Tags.jsx
CHANGED
|
@@ -30,39 +30,49 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
30
30
|
viewProps
|
|
31
31
|
])
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
iconPosition,
|
|
48
|
-
iconSpace,
|
|
49
|
-
iconWrapperStyle: {
|
|
50
|
-
backgroundColor: iconBackground,
|
|
51
|
-
borderRadius: iconBorderRadius,
|
|
52
|
-
alignSelf: iconAlignSelf,
|
|
53
|
-
padding: iconPadding,
|
|
54
|
-
...Platform.select({
|
|
55
|
-
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
56
|
-
web: { transition: 'color 200ms, background 200ms' }
|
|
57
|
-
})
|
|
33
|
+
const separateIconTextTokens = (
|
|
34
|
+
{
|
|
35
|
+
icon,
|
|
36
|
+
iconPosition,
|
|
37
|
+
iconSpace,
|
|
38
|
+
iconSize,
|
|
39
|
+
iconColor,
|
|
40
|
+
iconBackground,
|
|
41
|
+
iconBorderRadius,
|
|
42
|
+
iconAlignSelf,
|
|
43
|
+
iconPadding,
|
|
44
|
+
iconTranslateX,
|
|
45
|
+
iconTranslateY,
|
|
46
|
+
...rest
|
|
58
47
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
returnRest
|
|
49
|
+
) =>
|
|
50
|
+
returnRest
|
|
51
|
+
? rest
|
|
52
|
+
: {
|
|
53
|
+
icon,
|
|
54
|
+
iconPosition,
|
|
55
|
+
iconSpace,
|
|
56
|
+
iconWrapperStyle: {
|
|
57
|
+
backgroundColor: iconBackground,
|
|
58
|
+
borderRadius: iconBorderRadius,
|
|
59
|
+
alignSelf: iconAlignSelf,
|
|
60
|
+
padding: iconPadding,
|
|
61
|
+
...Platform.select({
|
|
62
|
+
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
63
|
+
web: { transition: 'color 200ms, background 200ms' }
|
|
64
|
+
})
|
|
65
|
+
},
|
|
66
|
+
iconTokens: {
|
|
67
|
+
size: iconSize,
|
|
68
|
+
color: iconColor,
|
|
69
|
+
translateX: iconTranslateX,
|
|
70
|
+
translateY: iconTranslateY
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const selectIconTextTokens = (tokens) => separateIconTextTokens(tokens, false)
|
|
75
|
+
const selectNonIconTextTokens = (tokens) => separateIconTextTokens(tokens, true)
|
|
66
76
|
|
|
67
77
|
const Tags = forwardRef(
|
|
68
78
|
(
|
|
@@ -87,7 +97,10 @@ const Tags = forwardRef(
|
|
|
87
97
|
const { direction, space } = themeTokens
|
|
88
98
|
|
|
89
99
|
const getItemTokens = useThemeTokensCallback('TagsItem', tokens, variant)
|
|
90
|
-
|
|
100
|
+
|
|
101
|
+
const getButtonTokens = (buttonState) =>
|
|
102
|
+
// Remove icon-text-related tokens, since we want to handle them ourselves, not use ButtonBase's handling
|
|
103
|
+
selectTokens('Button', selectNonIconTextTokens(getItemTokens(buttonState)))
|
|
91
104
|
|
|
92
105
|
const { currentValues, toggleOneValue } = useMultipleInputValues({
|
|
93
106
|
initialValues,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
2
3
|
import {
|
|
3
4
|
a11yProps,
|
|
4
5
|
focusHandlerProps,
|
|
@@ -63,6 +64,10 @@ TextInput.displayName = 'TextInput'
|
|
|
63
64
|
TextInput.propTypes = {
|
|
64
65
|
...selectedSystemPropTypes,
|
|
65
66
|
...textInputPropTypes,
|
|
67
|
+
/**
|
|
68
|
+
* A callback which if provided will get a clear button rendered and will be called whenever that button gets pressed.
|
|
69
|
+
*/
|
|
70
|
+
onClear: PropTypes.func,
|
|
66
71
|
tokens: getTokensPropType('TextInput'),
|
|
67
72
|
variant: variantProp.propType
|
|
68
73
|
}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import React, { forwardRef, useEffect, useState } from 'react'
|
|
2
|
-
import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-native'
|
|
3
|
-
|
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState } from 'react'
|
|
4
2
|
import PropTypes from 'prop-types'
|
|
3
|
+
import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-native'
|
|
5
4
|
import { applyTextStyles, useTheme, useThemeTokens, applyOuterBorder } from '../ThemeProvider'
|
|
5
|
+
import StackView from '../StackView'
|
|
6
|
+
import IconButton from '../IconButton'
|
|
6
7
|
import {
|
|
7
8
|
a11yProps,
|
|
8
9
|
getTokensPropType,
|
|
9
10
|
selectSystemProps,
|
|
10
11
|
textInputHandlerProps,
|
|
11
12
|
textInputProps,
|
|
13
|
+
useCopy,
|
|
12
14
|
useInputValue,
|
|
15
|
+
useSpacingScale,
|
|
13
16
|
variantProp,
|
|
14
17
|
viewProps
|
|
15
18
|
} from '../utils'
|
|
19
|
+
import dictionary from './dictionary'
|
|
16
20
|
|
|
17
21
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
18
22
|
a11yProps,
|
|
@@ -116,27 +120,37 @@ const selectIconTokens = ({ iconSize, iconColor }) => ({
|
|
|
116
120
|
color: iconColor
|
|
117
121
|
})
|
|
118
122
|
|
|
119
|
-
const selectIconContainerStyles = (
|
|
120
|
-
paddingRight,
|
|
123
|
+
const selectIconContainerStyles = (
|
|
124
|
+
{ buttonSize, buttonsGapSize, paddingRight, paddingBottom },
|
|
125
|
+
buttonCount
|
|
126
|
+
) => ({
|
|
127
|
+
paddingRight: paddingRight + buttonCount * (buttonSize + buttonsGapSize),
|
|
121
128
|
paddingBottom
|
|
122
129
|
})
|
|
123
130
|
|
|
131
|
+
const selectButtonsContainerStyle = ({ buttonsPaddingRight }) => ({
|
|
132
|
+
paddingRight: buttonsPaddingRight
|
|
133
|
+
})
|
|
134
|
+
|
|
124
135
|
const TextInputBase = forwardRef(
|
|
125
136
|
(
|
|
126
137
|
{
|
|
127
|
-
|
|
138
|
+
buttons = [],
|
|
139
|
+
copy = 'en',
|
|
128
140
|
height,
|
|
129
|
-
initialValue,
|
|
130
141
|
inactive,
|
|
131
|
-
|
|
142
|
+
initialValue,
|
|
143
|
+
onBlur,
|
|
132
144
|
onChange,
|
|
133
145
|
onChangeText,
|
|
146
|
+
onClear,
|
|
134
147
|
onFocus,
|
|
135
|
-
onBlur,
|
|
136
|
-
onMouseOver,
|
|
137
148
|
onMouseOut,
|
|
149
|
+
onMouseOver,
|
|
138
150
|
pattern,
|
|
151
|
+
readOnly,
|
|
139
152
|
tokens,
|
|
153
|
+
value,
|
|
140
154
|
variant = {},
|
|
141
155
|
...rest
|
|
142
156
|
},
|
|
@@ -162,14 +176,18 @@ const TextInputBase = forwardRef(
|
|
|
162
176
|
if (typeof onMouseOut === 'function') onMouseOut(event)
|
|
163
177
|
}
|
|
164
178
|
|
|
165
|
-
const
|
|
179
|
+
const defaultRef = useRef()
|
|
180
|
+
const inputRef = ref ?? defaultRef
|
|
181
|
+
|
|
182
|
+
const { currentValue, resetValue, setValue, isControlled, isDirty } = useInputValue({
|
|
166
183
|
value,
|
|
167
184
|
initialValue,
|
|
185
|
+
inputRef,
|
|
168
186
|
onChange,
|
|
169
187
|
readOnly
|
|
170
188
|
})
|
|
171
189
|
|
|
172
|
-
const element =
|
|
190
|
+
const element = inputRef?.current
|
|
173
191
|
useEffect(() => {
|
|
174
192
|
if (Platform.OS === 'web' && pattern && element) {
|
|
175
193
|
// React Native Web doesn't support `pattern`, so we have to attach it via a ref,
|
|
@@ -188,7 +206,25 @@ const TextInputBase = forwardRef(
|
|
|
188
206
|
|
|
189
207
|
const themeTokens = useThemeTokens('TextInput', tokens, variant, states)
|
|
190
208
|
|
|
191
|
-
const { icon: IconComponent } = themeTokens
|
|
209
|
+
const { buttonsGap, clearButtonIcon: ClearButtonIcon, icon: IconComponent } = themeTokens
|
|
210
|
+
const buttonsGapSize = useSpacingScale(buttonsGap)
|
|
211
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
212
|
+
if (onClear && isDirty) {
|
|
213
|
+
const handleClear = (event) => {
|
|
214
|
+
onClear?.(event)
|
|
215
|
+
resetValue(event)
|
|
216
|
+
inputRef?.current?.focus()
|
|
217
|
+
}
|
|
218
|
+
buttons?.unshift(
|
|
219
|
+
<IconButton
|
|
220
|
+
accessibilityLabel={getCopy('clearButtonAccessibilityLabel')}
|
|
221
|
+
icon={ClearButtonIcon}
|
|
222
|
+
key="clear"
|
|
223
|
+
onPress={handleClear}
|
|
224
|
+
variant={{ compact: true }}
|
|
225
|
+
/>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
192
228
|
|
|
193
229
|
const inputProps = {
|
|
194
230
|
...selectProps(rest),
|
|
@@ -209,15 +245,25 @@ const TextInputBase = forwardRef(
|
|
|
209
245
|
|
|
210
246
|
return (
|
|
211
247
|
<View style={selectOuterBorderStyles(themeTokens)}>
|
|
212
|
-
<NativeTextInput ref={
|
|
248
|
+
<NativeTextInput ref={inputRef} style={nativeInputStyle} {...inputProps} />
|
|
213
249
|
{IconComponent && (
|
|
214
250
|
<View
|
|
215
251
|
pointerEvents="none" // avoid hijacking input press events
|
|
216
|
-
style={[
|
|
252
|
+
style={[
|
|
253
|
+
staticStyles.iconContainer,
|
|
254
|
+
selectIconContainerStyles({ ...themeTokens, buttonsGapSize }, buttons?.length)
|
|
255
|
+
]}
|
|
217
256
|
>
|
|
218
257
|
<IconComponent {...selectIconTokens(themeTokens)} />
|
|
219
258
|
</View>
|
|
220
259
|
)}
|
|
260
|
+
{buttons?.length > 0 && (
|
|
261
|
+
<View style={[staticStyles.buttonsContainer, selectButtonsContainerStyle(themeTokens)]}>
|
|
262
|
+
<StackView direction="row" space={buttonsGap}>
|
|
263
|
+
{buttons}
|
|
264
|
+
</StackView>
|
|
265
|
+
</View>
|
|
266
|
+
)}
|
|
221
267
|
</View>
|
|
222
268
|
)
|
|
223
269
|
}
|
|
@@ -226,24 +272,43 @@ TextInputBase.displayName = 'TextInputBase'
|
|
|
226
272
|
|
|
227
273
|
TextInputBase.propTypes = {
|
|
228
274
|
...selectedSystemPropTypes,
|
|
229
|
-
|
|
275
|
+
buttons: PropTypes.arrayOf(PropTypes.node),
|
|
276
|
+
/**
|
|
277
|
+
* Select English or French copy for the accessible labels.
|
|
278
|
+
* You may also pass in a custom dictionary object.
|
|
279
|
+
*/
|
|
280
|
+
copy: PropTypes.oneOfType([
|
|
281
|
+
PropTypes.oneOf(['en', 'fr']),
|
|
282
|
+
PropTypes.shape({
|
|
283
|
+
clearButtonAccessibilityLabel: PropTypes.string
|
|
284
|
+
})
|
|
285
|
+
]),
|
|
230
286
|
height: PropTypes.number,
|
|
231
|
-
initialValue: PropTypes.string,
|
|
232
287
|
inactive: PropTypes.bool,
|
|
233
|
-
|
|
288
|
+
initialValue: PropTypes.string,
|
|
289
|
+
onBlur: PropTypes.func,
|
|
234
290
|
onChange: PropTypes.func,
|
|
235
291
|
onChangeText: PropTypes.func,
|
|
292
|
+
onClear: PropTypes.func,
|
|
236
293
|
onFocus: PropTypes.func,
|
|
237
|
-
onBlur: PropTypes.func,
|
|
238
|
-
onMouseOver: PropTypes.func,
|
|
239
294
|
onMouseOut: PropTypes.func,
|
|
295
|
+
onMouseOver: PropTypes.func,
|
|
296
|
+
readOnly: PropTypes.bool,
|
|
240
297
|
tokens: getTokensPropType('TextInput', 'TextArea'),
|
|
298
|
+
value: PropTypes.string,
|
|
241
299
|
variant: variantProp.propType
|
|
242
300
|
}
|
|
243
301
|
|
|
244
302
|
export default TextInputBase
|
|
245
303
|
|
|
246
304
|
const staticStyles = StyleSheet.create({
|
|
305
|
+
buttonsContainer: {
|
|
306
|
+
position: 'absolute',
|
|
307
|
+
right: 0,
|
|
308
|
+
top: 0,
|
|
309
|
+
bottom: 0,
|
|
310
|
+
justifyContent: 'center'
|
|
311
|
+
},
|
|
247
312
|
iconContainer: {
|
|
248
313
|
position: 'absolute',
|
|
249
314
|
right: 0,
|
|
@@ -12,7 +12,8 @@ const ThemeProvider = ({
|
|
|
12
12
|
children,
|
|
13
13
|
defaultTheme,
|
|
14
14
|
// TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
|
|
15
|
-
|
|
15
|
+
// TODO: switch `forceZIndex` to be false by default in the next major version
|
|
16
|
+
themeOptions = { forceAbsoluteFontSizing: true, forceZIndex: true }
|
|
16
17
|
}) => {
|
|
17
18
|
const [theme, setTheme] = useState(defaultTheme)
|
|
18
19
|
|
|
@@ -43,9 +44,12 @@ ThemeProvider.propTypes = {
|
|
|
43
44
|
* relative sizing (in `rem`, scales depending on the browser settings)
|
|
44
45
|
* - `contentMaxWidth`: allows configuration of the content max width to be used in components
|
|
45
46
|
* such as Footnote and Notification to avoid content to stretch width more then the page's width
|
|
47
|
+
* - `forceZIndex`: available on web only, when set to false, sets zIndex on `View` to be `auto`
|
|
48
|
+
* and when true, sets zIndex to be `0` (the default from `react-native-web`)
|
|
46
49
|
*/
|
|
47
50
|
themeOptions: PropTypes.shape({
|
|
48
51
|
forceAbsoluteFontSizing: PropTypes.bool,
|
|
52
|
+
forceZIndex: PropTypes.bool,
|
|
49
53
|
contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number)
|
|
50
54
|
})
|
|
51
55
|
}
|
package/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ export { default as InputSupports } from './InputSupports'
|
|
|
21
21
|
export * from './Link'
|
|
22
22
|
export { default as List, ListItem, ListBase } from './List'
|
|
23
23
|
export { default as Modal } from './Modal'
|
|
24
|
+
export { default as MultiSelectFilter } from './MultiSelectFilter'
|
|
24
25
|
export { default as Notification } from './Notification'
|
|
25
26
|
export { default as Pagination } from './Pagination'
|
|
26
27
|
export { default as Progress } from './Progress'
|
|
@@ -29,6 +30,7 @@ export { default as Radio } from './Radio'
|
|
|
29
30
|
export * from './Radio'
|
|
30
31
|
export { default as RadioCard } from './RadioCard'
|
|
31
32
|
export * from './RadioCard'
|
|
33
|
+
export { default as Responsive } from './Responsive'
|
|
32
34
|
export { default as Search } from './Search'
|
|
33
35
|
export { default as Select } from './Select'
|
|
34
36
|
export { default as SideNav } from './SideNav'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import { View as NativeView, StyleSheet } from 'react-native'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
import { useTheme } from '../../ThemeProvider'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Identical to React Native's View and supporting all the same props, but with:
|
|
8
|
+
* - a zIndex: 'auto' style added to prevent unexpectedly causing children to overlap other elements from other stacking contexts
|
|
9
|
+
*/
|
|
10
|
+
const BaseView = forwardRef(({ children, style, ...rest }, ref) => {
|
|
11
|
+
const { themeOptions } = useTheme()
|
|
12
|
+
const styleProp = Array.isArray(style) ? [...style] : [style]
|
|
13
|
+
|
|
14
|
+
if (!themeOptions.forceZIndex) {
|
|
15
|
+
styleProp.unshift(styles.resetZIndex)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<NativeView {...rest} style={styleProp} ref={ref}>
|
|
20
|
+
{children}
|
|
21
|
+
</NativeView>
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
BaseView.displayName = 'BaseView'
|
|
26
|
+
|
|
27
|
+
const styles = StyleSheet.create({
|
|
28
|
+
resetZIndex: {
|
|
29
|
+
zIndex: 'auto'
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
BaseView.propTypes = {
|
|
34
|
+
children: PropTypes.node,
|
|
35
|
+
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default BaseView
|
package/src/utils/index.js
CHANGED
package/src/utils/input.js
CHANGED
|
@@ -66,7 +66,7 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
|
|
|
66
66
|
const [isControlled] = useState(isCurrentlyControlled)
|
|
67
67
|
validateProps(props, { isControlled, isCurrentlyControlled }, hookName)
|
|
68
68
|
|
|
69
|
-
const { value, initialValue, onChange, readOnly = false } = props
|
|
69
|
+
const { value, initialValue, inputRef, onChange, readOnly = false } = props
|
|
70
70
|
const [ownValue, setOwnValue] = useState(!isControlled && initialValue)
|
|
71
71
|
const currentValue = isControlled ? value : ownValue
|
|
72
72
|
|
|
@@ -76,19 +76,24 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
|
|
|
76
76
|
// Make current value accessible inside useCallback without rememoizing every time the value changes
|
|
77
77
|
valueRef.current.value = currentValue
|
|
78
78
|
|
|
79
|
+
const isDirty = currentValue !== valueRef.current.initial
|
|
80
|
+
|
|
79
81
|
const setValue = useCallback(
|
|
80
82
|
(arg, event) => {
|
|
81
83
|
if (readOnly) return
|
|
82
84
|
const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg
|
|
83
|
-
if (!isControlled)
|
|
85
|
+
if (!isControlled) {
|
|
86
|
+
setOwnValue(newValue)
|
|
87
|
+
if (inputRef?.current) inputRef.current.value = newValue ?? ''
|
|
88
|
+
}
|
|
84
89
|
// Call onChange handler if there's something for it to handle (event or a changed value)
|
|
85
90
|
if (onChange && (event || valueRef.current.value !== newValue)) onChange(newValue, event)
|
|
86
91
|
},
|
|
87
|
-
[isControlled, onChange, readOnly]
|
|
92
|
+
[inputRef, isControlled, onChange, readOnly]
|
|
88
93
|
)
|
|
89
94
|
const resetValue = useCallback((event) => setValue(valueRef.current.initial, event), [setValue])
|
|
90
95
|
|
|
91
|
-
return { currentValue, setValue, resetValue, isControlled }
|
|
96
|
+
return { currentValue, setValue, resetValue, isControlled, isDirty }
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/**
|