@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.
Files changed (83) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/components/CaptionLabel/README.md +30 -8
  3. package/lib/components/CaptionLabel/index.js +2 -1
  4. package/lib/components/CaptionLabel/propTypes.js +1 -0
  5. package/lib/components/Checkbox/README.md +93 -25
  6. package/lib/components/Checkbox/index.js +14 -1
  7. package/lib/components/Checkbox/propTypes.js +1 -0
  8. package/lib/components/Label/index.js +2 -1
  9. package/lib/components/Label/propTypes.js +1 -0
  10. package/lib/components/RadioGroup/components/RadioButton/index.js +29 -18
  11. package/lib/components/RadioGroup/index.js +19 -5
  12. package/lib/components/Touchable/index.js +3 -1
  13. package/lib/components/Touchable/propTypes.js +1 -0
  14. package/lib/components/UTBadge/index.js +3 -1
  15. package/lib/components/UTBaseInputField/README.md +41 -19
  16. package/lib/components/UTBaseInputField/components/ActionAdornment/index.js +10 -3
  17. package/lib/components/UTBaseInputField/components/BadgeAdornment/index.js +6 -1
  18. package/lib/components/UTBaseInputField/components/IconAdornment/index.js +8 -1
  19. package/lib/components/UTBaseInputField/components/PrefixAdornment/index.js +8 -2
  20. package/lib/components/UTBaseInputField/components/SuffixAdornment/index.js +6 -1
  21. package/lib/components/UTBaseInputField/components/TooltipAdornment/index.js +16 -3
  22. package/lib/components/UTBaseInputField/index.js +15 -4
  23. package/lib/components/UTBottomSheet/README.md +94 -23
  24. package/lib/components/UTBottomSheet/index.js +27 -4
  25. package/lib/components/UTButton/index.js +18 -4
  26. package/lib/components/UTButton/proptypes.js +1 -0
  27. package/lib/components/UTCheckBox/README.md +47 -0
  28. package/lib/components/UTCheckBox/index.js +24 -3
  29. package/lib/components/UTCheckBox/proptypes.js +1 -0
  30. package/lib/components/UTCheckList/README.MD +63 -0
  31. package/lib/components/UTCheckList/index.js +25 -2
  32. package/lib/components/UTCheckList/proptypes.js +1 -0
  33. package/lib/components/UTDetailDrawer/README.md +60 -10
  34. package/lib/components/UTDetailDrawer/index.js +11 -1
  35. package/lib/components/UTDetailDrawer/propTypes.js +1 -0
  36. package/lib/components/UTFieldLabel/README.md +99 -0
  37. package/lib/components/UTFieldLabel/index.js +19 -2
  38. package/lib/components/UTIcon/README.md +25 -2
  39. package/lib/components/UTIcon/index.js +3 -1
  40. package/lib/components/UTLabel/README.md +26 -0
  41. package/lib/components/UTLabel/index.js +2 -0
  42. package/lib/components/UTLabel/proptypes.js +1 -0
  43. package/lib/components/UTMenu/README.md +275 -0
  44. package/lib/components/UTMenu/components/ListView/index.js +5 -3
  45. package/lib/components/UTMenu/components/ListView/proptypes.js +2 -1
  46. package/lib/components/UTMenu/components/MenuOption/index.js +5 -3
  47. package/lib/components/UTMenu/index.js +18 -3
  48. package/lib/components/UTMenu/proptypes.js +2 -1
  49. package/lib/components/UTModal/README.md +193 -0
  50. package/lib/components/UTModal/index.js +22 -2
  51. package/lib/components/UTModal/proptypes.js +1 -0
  52. package/lib/components/UTPhoneInput/index.js +25 -2
  53. package/lib/components/UTRoundView/README.md +158 -0
  54. package/lib/components/UTRoundView/index.js +12 -1
  55. package/lib/components/UTRoundView/propTypes.js +4 -2
  56. package/lib/components/UTSearchField/README.md +64 -14
  57. package/lib/components/UTSearchField/index.js +3 -1
  58. package/lib/components/UTSearchField/proptypes.js +2 -1
  59. package/lib/components/UTSelect/versions/V0/README.md +216 -0
  60. package/lib/components/UTSelect/versions/V0/componentes/MultipleItem/index.js +4 -2
  61. package/lib/components/UTSelect/versions/V0/index.js +5 -2
  62. package/lib/components/UTSelect/versions/V0/proptypes.js +2 -1
  63. package/lib/components/UTSelect/versions/V1/README.md +94 -0
  64. package/lib/components/UTSelect/versions/V1/index.js +28 -6
  65. package/lib/components/UTSelect/versions/V1/proptypes.js +1 -0
  66. package/lib/components/UTSelectableCard/README.md +85 -0
  67. package/lib/components/UTSelectableCard/index.js +52 -4
  68. package/lib/components/UTTabs/README.md +27 -11
  69. package/lib/components/UTTabs/index.js +139 -24
  70. package/lib/components/UTTabs/styles.js +104 -58
  71. package/lib/components/UTTextInput/versions/V0/components/BaseInput/index.js +5 -1
  72. package/lib/components/UTTextInput/versions/V0/components/InputLabel/index.js +4 -1
  73. package/lib/components/UTTextInput/versions/V0/flavors/FilledInput/index.js +9 -1
  74. package/lib/components/UTTextInput/versions/V0/flavors/OutlinedInput/index.js +9 -1
  75. package/lib/components/UTTextInput/versions/V0/flavors/StandardInput/index.js +9 -1
  76. package/lib/components/UTTextInput/versions/V1/components/TextInputField/index.js +3 -0
  77. package/lib/components/UTTextInput/versions/V1/index.js +20 -3
  78. package/lib/components/UTTooltip/README.md +99 -0
  79. package/lib/components/UTTooltip/index.js +2 -0
  80. package/lib/components/UTTooltip/proptypes.js +2 -1
  81. package/lib/components/UTValidation/index.js +26 -4
  82. package/lib/constants/testIds.js +44 -0
  83. 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 style={themedStyles.icon} />
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 content={<UTLabel>{tooltip}</UTLabel>} position="top">
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 colorTheme={colorTheme} style={themedStyles.additionalInfo}>
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 colorTheme="accent" name="IconCheck" style={themedStyles.checkIcon} />
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 new table for the additional props:
8
+ Here's a table for the component props:
7
9
 
8
- | Name | Type | Default | Description |
9
- |------------|---------|------------|-----------------------------------------------------------|
10
- | colorTheme | string | `accent` | Defines the color theme for the component. |
11
- | hierarchy | string | `primary` | Determines the hierarchy or importance of the tabs. |
12
- | onChange | func | | Callback function triggered when the selected tab changes.|
13
- | style | object | | Custom styles for component. |
14
- | tabs | array | | Array of tab items to be displayed within the component. |
15
- | withTabSliding | bool | `true` | Defines if tabs can be changed with gesture. |
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
- import { Animated, View, Pressable, PanResponder } from 'react-native';
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
- return (
52
- <View style={[styles.container, style]} {...(withTabSliding ? panResponder.panHandlers : {})}>
53
- {tabs.map(({ badge, label, icon }, index) => {
54
- const selected = selectedTab === index;
55
- const colorThemeToUse = selected ? colorTheme : 'gray';
56
- return (
57
- <Pressable
58
- disabled={selected}
59
- key={`tab-${label || icon}`}
60
- onPress={() => setSelectedTab(index)}
61
- style={styles.tab(tabs.length)}
62
- >
63
- <UTIcon colorTheme={colorThemeToUse} name={icon} />
64
- <UTLabel colorTheme={colorThemeToUse} shade="04" weight="medium" {...labelProps}>
65
- {label}
66
- </UTLabel>
67
- {badge && <UTBadge colorTheme="accent" />}
68
- </Pressable>
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
- <Animated.View style={styles.indicator(position, tabs.length, hierarchy, colorTheme)} />
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: 1
12
+ zIndex: 0
13
13
  },
14
- container: { display: 'flex', flexDirection: 'row', width: '100%' },
15
- indicator: (position, length, hierarchy, colorTheme) =>
16
- ({
17
- [HIERARCHIES.PRIMARY]: {
18
- position: 'absolute',
19
- bottom: 0,
20
- width: `${100 / length}%`,
21
- height: '100%',
22
- borderRightWidth: position.interpolate({
23
- inputRange: [0, length - 1],
24
- outputRange: [1, 0]
25
- }),
26
- borderTopRightRadius: position.interpolate({
27
- inputRange: [0, length - 1],
28
- outputRange: [4, 0]
29
- }),
30
- borderLeftWidth: position.interpolate({
31
- inputRange: [0, 1],
32
- outputRange: [0, 1],
33
- extrapolate: 'clamp'
34
- }),
35
- borderTopLeftRadius: position.interpolate({
36
- inputRange: [0, 1],
37
- outputRange: [0, 4],
38
- extrapolate: 'clamp'
39
- }),
40
- backgroundColor: light['01'],
41
- borderTopWidth: 1,
42
- borderTopColor: light['04'],
43
- borderLeftColor: light['04'],
44
- borderRightColor: light['04'],
45
- borderBottomWidth: 1,
46
- borderBottomColor: light['01'],
47
- left: position.interpolate({
48
- inputRange: [0, length],
49
- outputRange: ['0%', '100%']
50
- }),
51
- zIndex: 2
52
- },
53
- [HIERARCHIES.SECONDARY]: {
54
- position: 'absolute',
55
- bottom: 0,
56
- width: `${100 / length}%`,
57
- height: 4,
58
- backgroundColor: {
59
- [COLOR_THEMES.ACCENT]: accent['04'],
60
- [COLOR_THEMES.NEUTRAL]: neutral['04'],
61
- [COLOR_THEMES.NEGATIVE]: negative['04']
62
- }[colorTheme],
63
- left: position.interpolate({
64
- inputRange: [0, length],
65
- outputRange: ['0%', '100%']
66
- })
67
- }
68
- })[hierarchy],
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 content={tooltip} position="left" {...tooltipProps}>
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}