@widergy/mobile-ui 1.46.1 → 1.48.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 +14 -0
- package/lib/components/CaptionLabel/README.md +30 -8
- package/lib/components/CaptionLabel/index.js +2 -1
- package/lib/components/CaptionLabel/propTypes.js +1 -0
- package/lib/components/Checkbox/README.md +93 -25
- package/lib/components/Checkbox/index.js +14 -1
- package/lib/components/Checkbox/propTypes.js +1 -0
- package/lib/components/Label/index.js +2 -1
- package/lib/components/Label/propTypes.js +1 -0
- package/lib/components/RadioGroup/components/RadioButton/index.js +29 -18
- package/lib/components/RadioGroup/index.js +19 -5
- package/lib/components/Touchable/index.js +3 -1
- package/lib/components/Touchable/propTypes.js +1 -0
- package/lib/components/UTBadge/index.js +3 -1
- package/lib/components/UTBaseInputField/README.md +41 -19
- package/lib/components/UTBaseInputField/components/ActionAdornment/index.js +10 -3
- package/lib/components/UTBaseInputField/components/BadgeAdornment/index.js +6 -1
- package/lib/components/UTBaseInputField/components/IconAdornment/index.js +8 -1
- package/lib/components/UTBaseInputField/components/PrefixAdornment/index.js +8 -2
- package/lib/components/UTBaseInputField/components/SuffixAdornment/index.js +6 -1
- package/lib/components/UTBaseInputField/components/TooltipAdornment/index.js +16 -3
- package/lib/components/UTBaseInputField/index.js +15 -4
- package/lib/components/UTBottomSheet/README.md +94 -23
- package/lib/components/UTBottomSheet/index.js +27 -4
- package/lib/components/UTButton/index.js +18 -4
- package/lib/components/UTButton/proptypes.js +1 -0
- package/lib/components/UTCheckBox/README.md +47 -0
- package/lib/components/UTCheckBox/index.js +24 -3
- package/lib/components/UTCheckBox/proptypes.js +1 -0
- package/lib/components/UTCheckList/README.MD +63 -0
- package/lib/components/UTCheckList/index.js +25 -2
- package/lib/components/UTCheckList/proptypes.js +1 -0
- package/lib/components/UTDetailDrawer/README.md +60 -10
- package/lib/components/UTDetailDrawer/index.js +11 -1
- package/lib/components/UTDetailDrawer/propTypes.js +1 -0
- package/lib/components/UTFieldLabel/README.md +99 -0
- package/lib/components/UTFieldLabel/index.js +19 -2
- package/lib/components/UTIcon/README.md +25 -2
- package/lib/components/UTIcon/index.js +3 -1
- package/lib/components/UTLabel/README.md +26 -0
- package/lib/components/UTLabel/index.js +2 -0
- package/lib/components/UTLabel/proptypes.js +1 -0
- package/lib/components/UTMenu/README.md +275 -0
- package/lib/components/UTMenu/components/ListView/index.js +5 -3
- package/lib/components/UTMenu/components/ListView/proptypes.js +2 -1
- package/lib/components/UTMenu/components/MenuOption/index.js +5 -3
- package/lib/components/UTMenu/index.js +18 -3
- package/lib/components/UTMenu/proptypes.js +2 -1
- package/lib/components/UTModal/README.md +193 -0
- package/lib/components/UTModal/index.js +22 -2
- package/lib/components/UTModal/proptypes.js +1 -0
- package/lib/components/UTPhoneInput/index.js +25 -2
- package/lib/components/UTRoundView/README.md +158 -0
- package/lib/components/UTRoundView/index.js +12 -1
- package/lib/components/UTRoundView/propTypes.js +4 -2
- package/lib/components/UTSearchField/README.md +64 -14
- package/lib/components/UTSearchField/index.js +3 -1
- package/lib/components/UTSearchField/proptypes.js +2 -1
- package/lib/components/UTSelect/versions/V0/README.md +216 -0
- package/lib/components/UTSelect/versions/V0/componentes/MultipleItem/index.js +4 -2
- package/lib/components/UTSelect/versions/V0/index.js +5 -2
- package/lib/components/UTSelect/versions/V0/proptypes.js +2 -1
- package/lib/components/UTSelect/versions/V1/README.md +94 -0
- package/lib/components/UTSelect/versions/V1/index.js +28 -6
- package/lib/components/UTSelect/versions/V1/proptypes.js +1 -0
- package/lib/components/UTSelectableCard/README.md +85 -0
- package/lib/components/UTSelectableCard/index.js +52 -4
- package/lib/components/UTTabs/README.md +27 -11
- package/lib/components/UTTabs/index.js +139 -24
- package/lib/components/UTTabs/styles.js +104 -58
- package/lib/components/UTTextInput/versions/V0/components/BaseInput/index.js +5 -1
- package/lib/components/UTTextInput/versions/V0/components/InputLabel/index.js +4 -1
- package/lib/components/UTTextInput/versions/V0/flavors/FilledInput/index.js +9 -1
- package/lib/components/UTTextInput/versions/V0/flavors/OutlinedInput/index.js +9 -1
- package/lib/components/UTTextInput/versions/V0/flavors/StandardInput/index.js +9 -1
- package/lib/components/UTTextInput/versions/V1/components/TextInputField/index.js +3 -0
- package/lib/components/UTTextInput/versions/V1/index.js +20 -3
- package/lib/components/UTTooltip/README.md +99 -0
- package/lib/components/UTTooltip/index.js +2 -0
- package/lib/components/UTTooltip/proptypes.js +2 -1
- package/lib/components/UTValidation/index.js +26 -4
- package/lib/constants/testIds.js +44 -0
- package/package.json +1 -1
|
@@ -10,17 +10,31 @@ import { useTheme } from '../../theming';
|
|
|
10
10
|
import UTIcon from '../UTIcon';
|
|
11
11
|
import UTLabel from '../UTLabel';
|
|
12
12
|
import { mergeMultipleStyles } from '../../utils/styleUtils';
|
|
13
|
+
import { TEST_ID_CONSTANTS } from '../../constants/testIds';
|
|
13
14
|
|
|
14
15
|
import { getColorTheme, ownStyles, ICON_SIZE, getAppearanceStyles, ownSizeStyles } from './styles';
|
|
15
16
|
|
|
17
|
+
const {
|
|
18
|
+
icon,
|
|
19
|
+
titleText: titleTextTestId,
|
|
20
|
+
tooltip: tooltipTestId,
|
|
21
|
+
content,
|
|
22
|
+
description: descriptionTestId,
|
|
23
|
+
additionalInfo: additionalInfoTestId,
|
|
24
|
+
title: titleTestId,
|
|
25
|
+
checkIcon: checkIconTestId
|
|
26
|
+
} = TEST_ID_CONSTANTS;
|
|
27
|
+
|
|
16
28
|
const UTSelectableCard = ({
|
|
17
29
|
additionalInfo = {},
|
|
18
30
|
appearance = 'white',
|
|
19
31
|
checkIcon = true,
|
|
20
32
|
children,
|
|
33
|
+
dataTestId,
|
|
21
34
|
description,
|
|
22
35
|
disabled = false,
|
|
23
36
|
Icon,
|
|
37
|
+
iconDataTestId,
|
|
24
38
|
numberOfLines,
|
|
25
39
|
onPress,
|
|
26
40
|
selected = false,
|
|
@@ -43,6 +57,7 @@ const UTSelectableCard = ({
|
|
|
43
57
|
return (
|
|
44
58
|
<Surface style={themedStyles.outerContainer}>
|
|
45
59
|
<Touchable
|
|
60
|
+
dataTestId={dataTestId}
|
|
46
61
|
disabled={disabled}
|
|
47
62
|
onPress={() => onPress?.()}
|
|
48
63
|
style={[
|
|
@@ -56,6 +71,7 @@ const UTSelectableCard = ({
|
|
|
56
71
|
{hasIcon &&
|
|
57
72
|
(Icon.url ? (
|
|
58
73
|
<Image
|
|
74
|
+
testID={dataTestId ? `${dataTestId}.${icon}` : undefined}
|
|
59
75
|
source={{ uri: Icon.url }}
|
|
60
76
|
width={Icon.size || ICON_SIZE}
|
|
61
77
|
height={Icon.size || ICON_SIZE}
|
|
@@ -63,6 +79,7 @@ const UTSelectableCard = ({
|
|
|
63
79
|
/>
|
|
64
80
|
) : Icon.name ? (
|
|
65
81
|
<UTIcon
|
|
82
|
+
dataTestId={dataTestId ? `${dataTestId}.${icon}` : undefined}
|
|
66
83
|
name={Icon.name}
|
|
67
84
|
colorTheme={colorTheme}
|
|
68
85
|
shade={Icon.shade}
|
|
@@ -70,12 +87,16 @@ const UTSelectableCard = ({
|
|
|
70
87
|
style={themedStyles.icon}
|
|
71
88
|
/>
|
|
72
89
|
) : (
|
|
73
|
-
<Icon.Component
|
|
90
|
+
<Icon.Component
|
|
91
|
+
testID={iconDataTestId || (dataTestId ? `${dataTestId}.${icon}` : undefined)}
|
|
92
|
+
style={themedStyles.icon}
|
|
93
|
+
/>
|
|
74
94
|
))}
|
|
75
95
|
<View style={themedStyles.textContainer}>
|
|
76
96
|
<View style={[themedStyles.column, themedStyles.leftColumn]}>
|
|
77
97
|
<View style={[themedStyles.titleAndTooltip, selected && themedStyles.selectedTitleAndTooltip]}>
|
|
78
98
|
<UTLabel
|
|
99
|
+
dataTestId={dataTestId ? `${dataTestId}.${titleTextTestId}` : undefined}
|
|
79
100
|
colorTheme={colorTheme}
|
|
80
101
|
numberOfLines={numberOfLines}
|
|
81
102
|
style={tooltip && themedStyles.titleMargin}
|
|
@@ -85,13 +106,24 @@ const UTSelectableCard = ({
|
|
|
85
106
|
{titleText}
|
|
86
107
|
</UTLabel>
|
|
87
108
|
{tooltip && (
|
|
88
|
-
<UTTooltip
|
|
109
|
+
<UTTooltip
|
|
110
|
+
dataTestId={dataTestId ? `${dataTestId}.${tooltipTestId}` : undefined}
|
|
111
|
+
content={
|
|
112
|
+
<UTLabel
|
|
113
|
+
dataTestId={dataTestId ? `${dataTestId}.${tooltipTestId}.${content}` : undefined}
|
|
114
|
+
>
|
|
115
|
+
{tooltip}
|
|
116
|
+
</UTLabel>
|
|
117
|
+
}
|
|
118
|
+
position="top"
|
|
119
|
+
>
|
|
89
120
|
<UTIcon colorTheme={colorTheme} name="IconHelp" />
|
|
90
121
|
</UTTooltip>
|
|
91
122
|
)}
|
|
92
123
|
</View>
|
|
93
124
|
{description && (
|
|
94
125
|
<UTLabel
|
|
126
|
+
dataTestId={dataTestId ? `${dataTestId}.${descriptionTestId}` : undefined}
|
|
95
127
|
numberOfLines={numberOfLines}
|
|
96
128
|
colorTheme={selected ? 'accent' : 'gray'}
|
|
97
129
|
style={themedStyles.description}
|
|
@@ -103,12 +135,21 @@ const UTSelectableCard = ({
|
|
|
103
135
|
{!isEmpty(additionalInfo) && (
|
|
104
136
|
<View style={themedStyles.column}>
|
|
105
137
|
{!!additionalInfo.title && (
|
|
106
|
-
<UTLabel
|
|
138
|
+
<UTLabel
|
|
139
|
+
dataTestId={
|
|
140
|
+
dataTestId ? `${dataTestId}.${additionalInfoTestId}.${titleTestId}` : undefined
|
|
141
|
+
}
|
|
142
|
+
colorTheme={colorTheme}
|
|
143
|
+
style={themedStyles.additionalInfo}
|
|
144
|
+
>
|
|
107
145
|
{additionalInfo.title}
|
|
108
146
|
</UTLabel>
|
|
109
147
|
)}
|
|
110
148
|
{!!additionalInfo.description && (
|
|
111
149
|
<UTLabel
|
|
150
|
+
dataTestId={
|
|
151
|
+
dataTestId ? `${dataTestId}.${additionalInfoTestId}.${descriptionTestId}` : undefined
|
|
152
|
+
}
|
|
112
153
|
colorTheme={colorTheme}
|
|
113
154
|
style={[themedStyles.description, themedStyles.additionalInfo]}
|
|
114
155
|
>
|
|
@@ -120,7 +161,12 @@ const UTSelectableCard = ({
|
|
|
120
161
|
</View>
|
|
121
162
|
{children}
|
|
122
163
|
{checkIcon && selected && (
|
|
123
|
-
<UTIcon
|
|
164
|
+
<UTIcon
|
|
165
|
+
dataTestId={dataTestId ? `${dataTestId}.${checkIconTestId}` : undefined}
|
|
166
|
+
colorTheme="accent"
|
|
167
|
+
name="IconCheck"
|
|
168
|
+
style={themedStyles.checkIcon}
|
|
169
|
+
/>
|
|
124
170
|
)}
|
|
125
171
|
</View>
|
|
126
172
|
</Touchable>
|
|
@@ -132,9 +178,11 @@ UTSelectableCard.propTypes = {
|
|
|
132
178
|
additionalInfo: shape({ description: string, title: string }),
|
|
133
179
|
appearance: string,
|
|
134
180
|
checkIcon: bool,
|
|
181
|
+
dataTestId: string,
|
|
135
182
|
description: string,
|
|
136
183
|
disabled: bool,
|
|
137
184
|
Icon: shape({ name: string, shade: string, size: number }),
|
|
185
|
+
iconDataTestId: string,
|
|
138
186
|
numberOfLines: number,
|
|
139
187
|
onPress: func,
|
|
140
188
|
selected: bool,
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
# UTTabs
|
|
2
2
|
|
|
3
|
-
Tabs component
|
|
3
|
+
Tabs component with two display modes:
|
|
4
|
+
1. Fixed width (default): Tabs width are adjusted evenly to fill the container width
|
|
5
|
+
2. Scrollable: Each tab has width based on its content and if all tabs don't fit in the container width, the component becomes horizontally scrollable
|
|
4
6
|
|
|
5
7
|
## Props
|
|
6
|
-
Here's a
|
|
8
|
+
Here's a table for the component props:
|
|
7
9
|
|
|
8
|
-
| Name
|
|
9
|
-
|
|
10
|
-
| colorTheme
|
|
11
|
-
| hierarchy
|
|
12
|
-
| onChange
|
|
13
|
-
| style
|
|
14
|
-
| tabs
|
|
15
|
-
| withTabSliding
|
|
10
|
+
| Name | Type | Default | Description |
|
|
11
|
+
|---------------|----------|-----------|------------------------------------------------------------|
|
|
12
|
+
| colorTheme | string | `accent` | Defines the color theme for the component. |
|
|
13
|
+
| hierarchy | string | `primary` | Determines the hierarchy or importance of the tabs. |
|
|
14
|
+
| onChange | func | | Callback function triggered when the selected tab changes. |
|
|
15
|
+
| style | object | | Custom styles for component. |
|
|
16
|
+
| tabs | array | | Array of tab items to be displayed within the component. |
|
|
17
|
+
| withTabSliding| bool | `true` | Defines if tabs can be changed with gesture. |
|
|
18
|
+
| scrollableTabs| bool | `false` | Enables scrollable mode with content-based tab widths. |
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
### colorTheme
|
|
@@ -38,4 +41,17 @@ The value of `hierarchy` must be one of the following:
|
|
|
38
41
|
- `icon`: name of `UTIcon` to render
|
|
39
42
|
- `badge`: not affected by `colorTheme`
|
|
40
43
|
|
|
41
|
-
Besides those, any other prop can be added to a tab to be accessed with the `onChange` callback
|
|
44
|
+
Besides those, any other prop can be added to a tab to be accessed with the `onChange` callback
|
|
45
|
+
|
|
46
|
+
## Behavior
|
|
47
|
+
|
|
48
|
+
### Fixed Width Mode (scrollableTabs=false, default)
|
|
49
|
+
- Each tab has equal width to fill the container space
|
|
50
|
+
- If there are 2 tabs, each takes up 50% of the width
|
|
51
|
+
- The indicator takes the same width as the tab
|
|
52
|
+
|
|
53
|
+
### Scrollable Mode (scrollableTabs=true)
|
|
54
|
+
- Each tab's width is determined by its content (label, icon, badge)
|
|
55
|
+
- When the combined width of all tabs exceeds the container width, the component becomes horizontally scrollable
|
|
56
|
+
- Selected tab is automatically scrolled into view when changed
|
|
57
|
+
- The indicator follows the width of the selected tab
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
import { Animated, View, Pressable, PanResponder, ScrollView } from 'react-native';
|
|
2
3
|
import { array, bool, func, object, string } from 'prop-types';
|
|
3
4
|
import React, { useEffect, useRef, useState } from 'react';
|
|
4
5
|
|
|
@@ -18,24 +19,67 @@ const UTTabs = ({
|
|
|
18
19
|
style,
|
|
19
20
|
tabs,
|
|
20
21
|
theme,
|
|
21
|
-
withTabSliding = true
|
|
22
|
+
withTabSliding = true,
|
|
23
|
+
scrollableTabs = false
|
|
22
24
|
}) => {
|
|
23
25
|
const styles = styleSheet(theme);
|
|
24
26
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
27
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
25
28
|
const position = useRef(new Animated.Value(0)).current;
|
|
29
|
+
const indicatorPos = useRef(new Animated.Value(0)).current;
|
|
30
|
+
const indicatorWidth = useRef(new Animated.Value(0)).current;
|
|
31
|
+
const scrollViewRef = useRef(null);
|
|
32
|
+
const tabRefs = useRef([]);
|
|
26
33
|
|
|
27
34
|
useEffect(() => {
|
|
28
35
|
onChange?.(tabs[selectedTab]);
|
|
29
36
|
}, []);
|
|
30
37
|
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (scrollableTabs) {
|
|
40
|
+
tabRefs.current = Array(tabs.length)
|
|
41
|
+
.fill()
|
|
42
|
+
.map((_, i) => tabRefs.current[i] || React.createRef());
|
|
43
|
+
}
|
|
44
|
+
}, [tabs, scrollableTabs]);
|
|
45
|
+
|
|
31
46
|
useEffect(() => {
|
|
32
47
|
Animated.timing(position, {
|
|
33
48
|
toValue: selectedTab,
|
|
34
|
-
duration: 300
|
|
49
|
+
duration: 300,
|
|
50
|
+
useNativeDriver: false
|
|
35
51
|
}).start();
|
|
52
|
+
|
|
53
|
+
if (scrollableTabs && scrollViewRef.current && tabRefs.current[selectedTab]) {
|
|
54
|
+
tabRefs.current[selectedTab].measureLayout(
|
|
55
|
+
scrollViewRef.current,
|
|
56
|
+
(x, y, width) => {
|
|
57
|
+
scrollViewRef.current?.scrollTo({ x: Math.max(0, x - 50), animated: true });
|
|
58
|
+
|
|
59
|
+
Animated.parallel([
|
|
60
|
+
Animated.timing(indicatorPos, {
|
|
61
|
+
toValue: x,
|
|
62
|
+
duration: 300,
|
|
63
|
+
useNativeDriver: false
|
|
64
|
+
}),
|
|
65
|
+
Animated.timing(indicatorWidth, {
|
|
66
|
+
toValue: width,
|
|
67
|
+
duration: 300,
|
|
68
|
+
useNativeDriver: false
|
|
69
|
+
})
|
|
70
|
+
]).start();
|
|
71
|
+
},
|
|
72
|
+
() => {}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
36
76
|
onChange?.(tabs[selectedTab]);
|
|
37
77
|
}, [selectedTab]);
|
|
38
78
|
|
|
79
|
+
const handleScroll = event => {
|
|
80
|
+
setScrollOffset(event.nativeEvent.contentOffset.x);
|
|
81
|
+
};
|
|
82
|
+
|
|
39
83
|
const panResponder = useRef(
|
|
40
84
|
PanResponder.create({
|
|
41
85
|
onMoveShouldSetPanResponder: (_, gestureState) => Math.abs(gestureState.dx),
|
|
@@ -48,27 +92,97 @@ const UTTabs = ({
|
|
|
48
92
|
})
|
|
49
93
|
).current;
|
|
50
94
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
if (scrollableTabs && tabRefs.current.length !== tabs.length) {
|
|
96
|
+
tabRefs.current = Array(tabs.length)
|
|
97
|
+
.fill()
|
|
98
|
+
.map((_, i) => tabRefs.current[i] || React.createRef());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const calculatedIndicatorLeft = Animated.subtract(indicatorPos, scrollOffset);
|
|
102
|
+
|
|
103
|
+
const indicatorStyle = scrollableTabs
|
|
104
|
+
? [
|
|
105
|
+
styles.indicator(position, tabs.length, hierarchy, colorTheme, true),
|
|
106
|
+
{ width: indicatorWidth, left: calculatedIndicatorLeft }
|
|
107
|
+
]
|
|
108
|
+
: styles.indicator(position, tabs.length, hierarchy, colorTheme, false);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (scrollableTabs && tabRefs.current[selectedTab]) {
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
tabRefs.current[selectedTab]?.measureLayout(
|
|
114
|
+
scrollViewRef.current,
|
|
115
|
+
(x, y, width) => {
|
|
116
|
+
indicatorPos.setValue(x);
|
|
117
|
+
indicatorWidth.setValue(width);
|
|
118
|
+
},
|
|
119
|
+
() => {}
|
|
69
120
|
);
|
|
70
|
-
})
|
|
71
|
-
|
|
121
|
+
}, 100);
|
|
122
|
+
}
|
|
123
|
+
}, [scrollableTabs]);
|
|
124
|
+
|
|
125
|
+
const fixedLabelProps = scrollableTabs
|
|
126
|
+
? labelProps
|
|
127
|
+
: { ...labelProps, numberOfLines: 1, ellipsizeMode: 'tail' };
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<View style={[styles.containerWrapper, style]} {...(withTabSliding ? panResponder.panHandlers : {})}>
|
|
131
|
+
<Animated.View style={indicatorStyle} />
|
|
132
|
+
|
|
133
|
+
{scrollableTabs ? (
|
|
134
|
+
<ScrollView
|
|
135
|
+
ref={scrollViewRef}
|
|
136
|
+
horizontal
|
|
137
|
+
showsHorizontalScrollIndicator={false}
|
|
138
|
+
style={styles.scrollView}
|
|
139
|
+
contentContainerStyle={styles.scrollViewContent}
|
|
140
|
+
onScroll={handleScroll}
|
|
141
|
+
scrollEventThrottle={16}
|
|
142
|
+
>
|
|
143
|
+
{tabs.map(({ badge, label, icon }, index) => {
|
|
144
|
+
const selected = selectedTab === index;
|
|
145
|
+
const colorThemeToUse = selected ? colorTheme : 'gray';
|
|
146
|
+
return (
|
|
147
|
+
<Pressable
|
|
148
|
+
ref={ref => (tabRefs.current[index] = ref)}
|
|
149
|
+
disabled={selected}
|
|
150
|
+
key={`tab-${label || icon}`}
|
|
151
|
+
onPress={() => setSelectedTab(index)}
|
|
152
|
+
style={styles.scrollableTab}
|
|
153
|
+
>
|
|
154
|
+
{icon && <UTIcon colorTheme={colorThemeToUse} name={icon} />}
|
|
155
|
+
<UTLabel colorTheme={colorThemeToUse} shade="04" weight="medium" {...labelProps}>
|
|
156
|
+
{label}
|
|
157
|
+
</UTLabel>
|
|
158
|
+
{badge && <UTBadge colorTheme="accent" />}
|
|
159
|
+
</Pressable>
|
|
160
|
+
);
|
|
161
|
+
})}
|
|
162
|
+
</ScrollView>
|
|
163
|
+
) : (
|
|
164
|
+
<View style={styles.container}>
|
|
165
|
+
{tabs.map(({ badge, label, icon }, index) => {
|
|
166
|
+
const selected = selectedTab === index;
|
|
167
|
+
const colorThemeToUse = selected ? colorTheme : 'gray';
|
|
168
|
+
return (
|
|
169
|
+
<Pressable
|
|
170
|
+
disabled={selected}
|
|
171
|
+
key={`tab-${label || icon}`}
|
|
172
|
+
onPress={() => setSelectedTab(index)}
|
|
173
|
+
style={styles.tab(tabs.length)}
|
|
174
|
+
>
|
|
175
|
+
{icon && <UTIcon colorTheme={colorThemeToUse} name={icon} />}
|
|
176
|
+
<UTLabel colorTheme={colorThemeToUse} shade="04" weight="medium" {...fixedLabelProps}>
|
|
177
|
+
{label}
|
|
178
|
+
</UTLabel>
|
|
179
|
+
{badge && <UTBadge colorTheme="accent" />}
|
|
180
|
+
</Pressable>
|
|
181
|
+
);
|
|
182
|
+
})}
|
|
183
|
+
</View>
|
|
184
|
+
)}
|
|
185
|
+
|
|
72
186
|
<View style={styles.border} />
|
|
73
187
|
</View>
|
|
74
188
|
);
|
|
@@ -82,7 +196,8 @@ UTTabs.propTypes = {
|
|
|
82
196
|
style: object,
|
|
83
197
|
tabs: array,
|
|
84
198
|
theme: object,
|
|
85
|
-
withTabSliding: bool
|
|
199
|
+
withTabSliding: bool,
|
|
200
|
+
scrollableTabs: bool
|
|
86
201
|
};
|
|
87
202
|
|
|
88
203
|
export default withTheme(UTTabs);
|
|
@@ -9,74 +9,120 @@ export default StyleSheet.create(({ Palette: { accent, neutral, negative, light
|
|
|
9
9
|
width: '100%',
|
|
10
10
|
height: 1,
|
|
11
11
|
backgroundColor: light['04'],
|
|
12
|
-
zIndex:
|
|
12
|
+
zIndex: 0
|
|
13
13
|
},
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
14
|
+
containerWrapper: {
|
|
15
|
+
position: 'relative',
|
|
16
|
+
width: '100%'
|
|
17
|
+
},
|
|
18
|
+
container: {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
flexDirection: 'row',
|
|
21
|
+
width: '100%',
|
|
22
|
+
zIndex: 3
|
|
23
|
+
},
|
|
24
|
+
scrollView: {
|
|
25
|
+
flexDirection: 'row',
|
|
26
|
+
width: '100%',
|
|
27
|
+
zIndex: 3
|
|
28
|
+
},
|
|
29
|
+
scrollViewContent: {
|
|
30
|
+
flexDirection: 'row',
|
|
31
|
+
flexGrow: 1
|
|
32
|
+
},
|
|
33
|
+
indicator: (position, length, hierarchy, colorTheme, isScrollable = false) => {
|
|
34
|
+
const baseStyle = {
|
|
35
|
+
position: 'absolute',
|
|
36
|
+
bottom: 0,
|
|
37
|
+
zIndex: 1
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const heightStyle = hierarchy === HIERARCHIES.PRIMARY ? { height: '100%' } : { height: 4 };
|
|
41
|
+
|
|
42
|
+
const backgroundColorStyle = {
|
|
43
|
+
backgroundColor:
|
|
44
|
+
hierarchy === HIERARCHIES.PRIMARY
|
|
45
|
+
? light['01']
|
|
46
|
+
: {
|
|
47
|
+
[COLOR_THEMES.ACCENT]: accent['04'],
|
|
48
|
+
[COLOR_THEMES.NEUTRAL]: neutral['04'],
|
|
49
|
+
[COLOR_THEMES.NEGATIVE]: negative['04']
|
|
50
|
+
}[colorTheme]
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const primaryBorderStyles =
|
|
54
|
+
hierarchy === HIERARCHIES.PRIMARY
|
|
55
|
+
? {
|
|
56
|
+
borderTopWidth: 1,
|
|
57
|
+
borderTopColor: light['04'],
|
|
58
|
+
borderLeftColor: light['04'],
|
|
59
|
+
borderRightColor: light['04'],
|
|
60
|
+
borderBottomWidth: 1,
|
|
61
|
+
borderBottomColor: light['01'],
|
|
62
|
+
borderRightWidth: position.interpolate({
|
|
63
|
+
inputRange: [0, length - 1],
|
|
64
|
+
outputRange: [1, 0]
|
|
65
|
+
}),
|
|
66
|
+
borderTopRightRadius: position.interpolate({
|
|
67
|
+
inputRange: [0, length - 1],
|
|
68
|
+
outputRange: [4, 0]
|
|
69
|
+
}),
|
|
70
|
+
borderLeftWidth: position.interpolate({
|
|
71
|
+
inputRange: [0, 1],
|
|
72
|
+
outputRange: [0, 1],
|
|
73
|
+
extrapolate: 'clamp'
|
|
74
|
+
}),
|
|
75
|
+
borderTopLeftRadius: position.interpolate({
|
|
76
|
+
inputRange: [0, 1],
|
|
77
|
+
outputRange: [0, 4],
|
|
78
|
+
extrapolate: 'clamp'
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
: {};
|
|
82
|
+
|
|
83
|
+
const positioningStyles = isScrollable
|
|
84
|
+
? {}
|
|
85
|
+
: {
|
|
86
|
+
width: `${100 / length}%`,
|
|
87
|
+
left: position.interpolate({
|
|
88
|
+
inputRange: [0, length],
|
|
89
|
+
outputRange: ['0%', '100%']
|
|
90
|
+
})
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...baseStyle,
|
|
95
|
+
...heightStyle,
|
|
96
|
+
...backgroundColorStyle,
|
|
97
|
+
...primaryBorderStyles,
|
|
98
|
+
...positioningStyles
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
// Original fixed-width tab style (for scrollableTabs=false)
|
|
69
102
|
tab:
|
|
70
103
|
tabs =>
|
|
71
104
|
({ pressed }) => ({
|
|
72
105
|
alignItems: 'center',
|
|
73
106
|
display: 'flex',
|
|
74
|
-
flexBasis: `${100 / tabs}%`,
|
|
75
107
|
flexDirection: 'row',
|
|
108
|
+
flexBasis: tabs ? `${100 / tabs}%` : 'auto',
|
|
109
|
+
paddingHorizontal: 16,
|
|
76
110
|
gap: 8,
|
|
77
111
|
height: 48,
|
|
78
112
|
justifyContent: 'center',
|
|
79
113
|
zIndex: 3,
|
|
80
114
|
backgroundColor: pressed ? accent['01'] : null
|
|
81
|
-
})
|
|
115
|
+
}),
|
|
116
|
+
// Scrollable tab style (for scrollableTabs=true)
|
|
117
|
+
scrollableTab: ({ pressed }) => ({
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
display: 'flex',
|
|
120
|
+
flexDirection: 'row',
|
|
121
|
+
paddingHorizontal: 16,
|
|
122
|
+
gap: 8,
|
|
123
|
+
height: 48,
|
|
124
|
+
justifyContent: 'center',
|
|
125
|
+
zIndex: 3,
|
|
126
|
+
backgroundColor: pressed ? accent['01'] : null
|
|
127
|
+
})
|
|
82
128
|
}));
|
|
@@ -23,6 +23,7 @@ const BaseInput = forwardRef(
|
|
|
23
23
|
(
|
|
24
24
|
{
|
|
25
25
|
onBlur,
|
|
26
|
+
dataTestId,
|
|
26
27
|
onFocus,
|
|
27
28
|
onLayout,
|
|
28
29
|
label,
|
|
@@ -80,6 +81,7 @@ const BaseInput = forwardRef(
|
|
|
80
81
|
return (
|
|
81
82
|
<Fragment>
|
|
82
83
|
<TextInput
|
|
84
|
+
testID={dataTestId}
|
|
83
85
|
autoCorrect={false}
|
|
84
86
|
ref={textInputRef}
|
|
85
87
|
onLayout={onLayout}
|
|
@@ -100,6 +102,7 @@ const BaseInput = forwardRef(
|
|
|
100
102
|
{(RightIcon || hiddenInput) && (
|
|
101
103
|
<Touchable
|
|
102
104
|
borderless
|
|
105
|
+
dataTestId={dataTestId ? `${dataTestId}.icon` : undefined}
|
|
103
106
|
onPress={hiddenInput ? toggleShowInput : onRightIconPress}
|
|
104
107
|
style={[
|
|
105
108
|
styles.iconContainer,
|
|
@@ -141,7 +144,8 @@ BaseInput.propTypes = {
|
|
|
141
144
|
outlined: bool,
|
|
142
145
|
InputRef: func,
|
|
143
146
|
variantStyle: ViewPropTypes.style,
|
|
144
|
-
standard: bool
|
|
147
|
+
standard: bool,
|
|
148
|
+
dataTestId: string
|
|
145
149
|
};
|
|
146
150
|
|
|
147
151
|
export default withTheme(BaseInput);
|
|
@@ -10,6 +10,7 @@ const AnimatedView = Animated.View;
|
|
|
10
10
|
const AnimatedText = Animated.Text;
|
|
11
11
|
|
|
12
12
|
const InputLabel = ({
|
|
13
|
+
dataTestId,
|
|
13
14
|
focused,
|
|
14
15
|
error,
|
|
15
16
|
value,
|
|
@@ -109,6 +110,7 @@ const InputLabel = ({
|
|
|
109
110
|
]}
|
|
110
111
|
>
|
|
111
112
|
<AnimatedText
|
|
113
|
+
testID={dataTestId}
|
|
112
114
|
onLayout={handleLayout}
|
|
113
115
|
ellipsizeMode={overflowControl?.ellipsizeMode}
|
|
114
116
|
numberOfLines={overflowControl?.numberOfLines}
|
|
@@ -167,7 +169,8 @@ InputLabel.propTypes = {
|
|
|
167
169
|
initX: number,
|
|
168
170
|
initY: number
|
|
169
171
|
}),
|
|
170
|
-
labelBackgroundColor: string
|
|
172
|
+
labelBackgroundColor: string,
|
|
173
|
+
dataTestId: string
|
|
171
174
|
};
|
|
172
175
|
|
|
173
176
|
export default withTheme(InputLabel);
|
|
@@ -97,6 +97,7 @@ const FilledInput = ({
|
|
|
97
97
|
/>
|
|
98
98
|
{!!label && (
|
|
99
99
|
<InputLabel
|
|
100
|
+
dataTestId={props.dataTestId ? `${props.dataTestId}.inputLabel` : undefined}
|
|
100
101
|
focused={focused}
|
|
101
102
|
error={error}
|
|
102
103
|
value={value}
|
|
@@ -127,8 +128,14 @@ const FilledInput = ({
|
|
|
127
128
|
</View>
|
|
128
129
|
{!!tooltip && (
|
|
129
130
|
<View style={[ownStyles.tooltipContainer, multiline && { height: initialHeight }]}>
|
|
130
|
-
<UTTooltip
|
|
131
|
+
<UTTooltip
|
|
132
|
+
dataTestId={props.dataTestId ? `${props.dataTestId}.tooltip` : undefined}
|
|
133
|
+
content={tooltip}
|
|
134
|
+
position="left"
|
|
135
|
+
{...tooltipProps}
|
|
136
|
+
>
|
|
131
137
|
<Icon
|
|
138
|
+
testID={props.dataTestId ? `${props.dataTestId}.tooltip.icon` : undefined}
|
|
132
139
|
name="questioncircleo"
|
|
133
140
|
type="antdesign"
|
|
134
141
|
color={themeStyles?.tooltip?.color}
|
|
@@ -140,6 +147,7 @@ const FilledInput = ({
|
|
|
140
147
|
)}
|
|
141
148
|
</View>
|
|
142
149
|
<CaptionLabel
|
|
150
|
+
dataTestId={props.dataTestId ? `${props.dataTestId}.captionLabel` : undefined}
|
|
143
151
|
error={error}
|
|
144
152
|
color={theme.colors.primary}
|
|
145
153
|
errorColor={theme.colors.error}
|