@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
@@ -0,0 +1,275 @@
1
+ # UTMenu
2
+
3
+ > **⚠️ AI-Generated Documentation Disclaimer**
4
+ > This documentation has been generated with AI assistance and has not been thoroughly reviewed by a developer. Please verify implementation details and usage examples before relying on them in production code.
5
+
6
+ A flexible dropdown menu component that provides a modal overlay with selectable options. Features customizable positioning, search functionality, Google attribution support, and comprehensive test ID support.
7
+
8
+ ## Props
9
+
10
+ | NAME | TYPE | REQUIRED | DESCRIPTION |
11
+ | ----------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------- |
12
+ | options | array | Yes | Array of option objects. Each option must have `id`, `label`, and optionally `value`, `action` properties. |
13
+ | children | node | Yes | The trigger element that will open the menu when pressed. |
14
+ | onPress | func | No | Callback function called when an option is selected. Receives the selected option. |
15
+ | selectedOption | object | No | Currently selected option object (for single selection) or array of values (for multiple). |
16
+ | onOpen | func | No | Callback function called when the menu opens. |
17
+ | onClose | func | No | Callback function called when the menu closes. |
18
+ | disabled | bool | No | Whether the menu trigger is disabled. |
19
+ | fullWidth | bool | No | Whether the menu should match the width of the trigger element. |
20
+ | verticalOffset | number | No | Vertical offset for menu positioning relative to trigger. Default: 0. |
21
+ | horizontalOffset | number | No | Horizontal offset for menu positioning relative to trigger. Default: 0. |
22
+ | avoidOverlappingAnchor | bool | No | Whether to avoid overlapping the trigger element. Default: true. |
23
+ | withoutOpacity | bool | No | Whether to disable opacity change on trigger press. |
24
+ | isMultiple | bool | No | Enables multiple selection mode. |
25
+ | maxHeight | number | No | Maximum height for the options list. |
26
+ | withAutocomplete | bool | No | Enables search/filter functionality with text input. |
27
+ | autoCompletePlaceholder | string | No | Placeholder text for the search input. |
28
+ | onQueryChange | func | No | Callback function called when search query changes. |
29
+ | filterOptions | bool | No | Whether to filter options based on search query. Default: true. |
30
+ | MenuOptionComponent | component | No | Custom component to render menu options. Default: MenuOption. |
31
+ | ItemSeparatorComponent | component | No | Component to render between menu options. |
32
+ | ListEmptyComponent | component | No | Component to render when no options are available. |
33
+ | withGoogleAttribution | bool | No | Whether to show Google attribution (for location services). Default: false. |
34
+ | styles | object | No | Custom styles for menu components. |
35
+ | dataTestId | string | No | Unique identifier for testing purposes. Creates hierarchical test IDs for child elements. |
36
+
37
+ ## Option Object Structure
38
+
39
+ Each option in the `options` array should have the following structure:
40
+
41
+ ```js
42
+ {
43
+ id: string | number, // Unique identifier for the option
44
+ label: string, // Display text for the option
45
+ value?: any, // Optional value (defaults to id if not provided)
46
+ action?: function // Optional custom action function
47
+ }
48
+ ```
49
+
50
+ ## Usage Examples
51
+
52
+ ### Basic Menu
53
+
54
+ ```jsx
55
+ import React, { useState } from 'react';
56
+ import { Text, View } from 'react-native';
57
+ import UTMenu from '@widergy/mobile-ui/lib/components/UTMenu';
58
+
59
+ const BasicExample = () => {
60
+ const [selectedOption, setSelectedOption] = useState(null);
61
+
62
+ const options = [
63
+ { id: '1', label: 'Option 1' },
64
+ { id: '2', label: 'Option 2' },
65
+ { id: '3', label: 'Option 3' }
66
+ ];
67
+
68
+ return (
69
+ <UTMenu options={options} onPress={setSelectedOption} selectedOption={selectedOption?.id}>
70
+ <View style={{ padding: 10, backgroundColor: '#f0f0f0' }}>
71
+ <Text>{selectedOption?.label || 'Select an option'}</Text>
72
+ </View>
73
+ </UTMenu>
74
+ );
75
+ };
76
+ ```
77
+
78
+ ### Menu with Search
79
+
80
+ ```jsx
81
+ const SearchableMenu = () => {
82
+ const [selected, setSelected] = useState(null);
83
+ const [query, setQuery] = useState('');
84
+
85
+ const options = [
86
+ { id: 'apple', label: 'Apple' },
87
+ { id: 'banana', label: 'Banana' },
88
+ { id: 'cherry', label: 'Cherry' },
89
+ { id: 'date', label: 'Date' },
90
+ { id: 'elderberry', label: 'Elderberry' }
91
+ ];
92
+
93
+ return (
94
+ <UTMenu
95
+ options={options}
96
+ onPress={setSelected}
97
+ selectedOption={selected?.id}
98
+ withAutocomplete
99
+ autoCompletePlaceholder="Search fruits..."
100
+ onQueryChange={setQuery}
101
+ >
102
+ <View style={{ padding: 12, borderWidth: 1, borderColor: '#ccc' }}>
103
+ <Text>{selected?.label || 'Choose a fruit'}</Text>
104
+ </View>
105
+ </UTMenu>
106
+ );
107
+ };
108
+ ```
109
+
110
+ ### Multiple Selection Menu
111
+
112
+ ```jsx
113
+ const MultipleSelectionMenu = () => {
114
+ const [selectedValues, setSelectedValues] = useState([]);
115
+
116
+ const skillOptions = [
117
+ { id: 'react', label: 'React', value: 'react' },
118
+ { id: 'vue', label: 'Vue.js', value: 'vue' },
119
+ { id: 'angular', label: 'Angular', value: 'angular' },
120
+ { id: 'svelte', label: 'Svelte', value: 'svelte' }
121
+ ];
122
+
123
+ return (
124
+ <UTMenu
125
+ options={skillOptions}
126
+ onPress={option => {
127
+ const isSelected = selectedValues.includes(option.value);
128
+ if (isSelected) {
129
+ setSelectedValues(prev => prev.filter(val => val !== option.value));
130
+ } else {
131
+ setSelectedValues(prev => [...prev, option.value]);
132
+ }
133
+ }}
134
+ selectedOption={selectedValues}
135
+ isMultiple
136
+ >
137
+ <View style={{ padding: 12, borderWidth: 1 }}>
138
+ <Text>{selectedValues.length > 0 ? `Selected: ${selectedValues.join(', ')}` : 'Select skills'}</Text>
139
+ </View>
140
+ </UTMenu>
141
+ );
142
+ };
143
+ ```
144
+
145
+ ### Custom Positioning
146
+
147
+ ```jsx
148
+ const CustomPositionMenu = () => {
149
+ return (
150
+ <UTMenu
151
+ options={options}
152
+ onPress={handlePress}
153
+ verticalOffset={-5}
154
+ horizontalOffset={10}
155
+ fullWidth={false}
156
+ avoidOverlappingAnchor={false}
157
+ >
158
+ <YourTriggerComponent />
159
+ </UTMenu>
160
+ );
161
+ };
162
+ ```
163
+
164
+ ## Testing
165
+
166
+ The `UTMenu` component supports comprehensive test ID assignment through the `dataTestId` prop. When provided, it creates a hierarchical structure of test IDs for all interactive and meaningful elements.
167
+
168
+ ### Test ID Structure
169
+
170
+ When you provide `dataTestId="provided.testId"`, the following test IDs are automatically generated:
171
+
172
+ | Element | Test ID | When Available |
173
+ | ------------------- | ------------------------------------------- | ------------------------------------ |
174
+ | Menu anchor/trigger | `provided.testId.anchor` | Always (when dataTestId is provided) |
175
+ | Modal overlay | `provided.testId.modal` | When menu is open |
176
+ | Search input | `provided.testId.searchInput` | When withAutocomplete is true |
177
+ | Options list | `provided.testId.list` | When menu is open |
178
+ | Individual option | `provided.testId.list.option.{index}` | For each option when menu is open |
179
+ | Option label | `provided.testId.list.option.{index}.label` | For each option when menu is open |
180
+ | Attribution text | `provided.testId.attribution.text` | When withGoogleAttribution is true |
181
+ | Attribution logo | `provided.testId.attribution.logo` | When withGoogleAttribution is true |
182
+
183
+ **Note:** The search input test ID (`provided.testId.searchInput`) creates additional hierarchical test IDs based on the UTTextInput component's own test ID structure.
184
+
185
+ ### Test ID Examples
186
+
187
+ ```jsx
188
+ // Basic menu with test IDs
189
+ <UTMenu
190
+ dataTestId="actionMenu"
191
+ options={actionOptions}
192
+ onPress={handleAction}
193
+ >
194
+ <ActionButton />
195
+ </UTMenu>
196
+
197
+ // Creates test IDs:
198
+ // - actionMenu.anchor (trigger button)
199
+ // - actionMenu.modal (when open)
200
+ // - actionMenu.list (options container when open)
201
+ // - actionMenu.list.option.0 (first option when open)
202
+ // - actionMenu.list.option.0.label (first option text when open)
203
+ // - actionMenu.list.option.1 (second option when open)
204
+ // - actionMenu.list.option.1.label (second option text when open)
205
+
206
+ // Searchable menu with test IDs
207
+ <UTMenu
208
+ dataTestId="fruitSelector"
209
+ options={fruitOptions}
210
+ onPress={handleFruitSelection}
211
+ withAutocomplete
212
+ autoCompletePlaceholder="Search fruits..."
213
+ >
214
+ <FruitSelectorTrigger />
215
+ </UTMenu>
216
+
217
+ // Creates test IDs:
218
+ // - fruitSelector.anchor (trigger)
219
+ // - fruitSelector.modal (when open)
220
+ // - fruitSelector.searchInput (search input + UTTextInput sub-elements)
221
+ // - fruitSelector.list (options container)
222
+ // - fruitSelector.list.option.0 (first fruit option)
223
+ // - fruitSelector.list.option.0.label (first fruit option text)
224
+ // - fruitSelector.list.option.1 (second fruit option)
225
+ // - fruitSelector.list.option.1.label (second fruit option text)
226
+
227
+ // Menu with Google attribution and test IDs
228
+ <UTMenu
229
+ dataTestId="locationMenu"
230
+ options={locationOptions}
231
+ onPress={handleLocationSelect}
232
+ withGoogleAttribution
233
+ >
234
+ <LocationTrigger />
235
+ </UTMenu>
236
+
237
+ // Creates test IDs:
238
+ // - locationMenu.anchor (trigger)
239
+ // - locationMenu.modal (when open)
240
+ // - locationMenu.list (options container)
241
+ // - locationMenu.list.option.0 (first location option)
242
+ // - locationMenu.list.option.1 (second location option)
243
+ // - locationMenu.attribution.text (attribution text)
244
+ // - locationMenu.attribution.logo (Google logo)
245
+ ```
246
+
247
+ ## Custom Menu Option Components
248
+
249
+ You can provide a custom `MenuOptionComponent` that will receive the following props:
250
+
251
+ ```jsx
252
+ const CustomMenuOption = ({ onPress, label, selected, item, styles, dataTestId }) => {
253
+ return (
254
+ <TouchableOpacity
255
+ onPress={() => onPress(item)}
256
+ style={[customStyles.option, selected && customStyles.selected]}
257
+ testID={dataTestId}
258
+ >
259
+ <Text testID={dataTestId ? `${dataTestId}.label` : undefined}>{label}</Text>
260
+ </TouchableOpacity>
261
+ );
262
+ };
263
+
264
+ // Usage
265
+ <UTMenu
266
+ MenuOptionComponent={CustomMenuOption}
267
+ options={options}
268
+ onPress={handlePress}
269
+ dataTestId="customMenu"
270
+ >
271
+ <TriggerComponent />
272
+ </UTMenu>;
273
+ ```
274
+
275
+ **Note:** When implementing custom menu option components, make sure to handle the `dataTestId` prop properly to maintain the hierarchical test ID structure.
@@ -31,8 +31,8 @@ class ListView extends PureComponent {
31
31
  return handleOptionPress(item.action || (() => onPress(item)), item?.value);
32
32
  };
33
33
 
34
- renderItem = ({ item }) => {
35
- const { MenuOptionComponent, propStyles } = this.props;
34
+ renderItem = ({ item, index }) => {
35
+ const { MenuOptionComponent, propStyles, dataTestId } = this.props;
36
36
 
37
37
  return (
38
38
  <MenuOptionComponent
@@ -41,12 +41,13 @@ class ListView extends PureComponent {
41
41
  onPress={this.handleOnPress}
42
42
  styles={propStyles}
43
43
  item={item}
44
+ dataTestId={dataTestId ? `${dataTestId}.option.${index}` : undefined}
44
45
  />
45
46
  );
46
47
  };
47
48
 
48
49
  render() {
49
- const { filteredOptions, ItemSeparatorComponent, ListEmptyComponent } = this.props;
50
+ const { filteredOptions, ItemSeparatorComponent, ListEmptyComponent, dataTestId } = this.props;
50
51
  const { style } = this.state;
51
52
 
52
53
  return (
@@ -58,6 +59,7 @@ class ListView extends PureComponent {
58
59
  renderItem={this.renderItem}
59
60
  ItemSeparatorComponent={ItemSeparatorComponent}
60
61
  ListEmptyComponent={ListEmptyComponent}
62
+ testID={dataTestId}
61
63
  />
62
64
  );
63
65
  }
@@ -12,5 +12,6 @@ export default {
12
12
  maxHeight: number,
13
13
  windowHeight: number,
14
14
  usableWindowHeight: number,
15
- ItemSeparatorComponent: any
15
+ ItemSeparatorComponent: any,
16
+ dataTestId: string
16
17
  };
@@ -5,7 +5,7 @@ import { TouchableOpacity, Text } from 'react-native';
5
5
 
6
6
  import styles from './styles';
7
7
 
8
- const MenuOption = ({ onPress, label, selected, styles: propStyles, item }) => (
8
+ const MenuOption = ({ onPress, label, selected, styles: propStyles, item, dataTestId }) => (
9
9
  <TouchableOpacity
10
10
  onPress={() => onPress(item)}
11
11
  style={[
@@ -14,8 +14,9 @@ const MenuOption = ({ onPress, label, selected, styles: propStyles, item }) => (
14
14
  selected && styles.selected,
15
15
  selected && propStyles?.menuOptionSelected
16
16
  ]}
17
+ testID={dataTestId}
17
18
  >
18
- <Text>{label}</Text>
19
+ <Text testID={dataTestId ? `${dataTestId}.label` : undefined}>{label}</Text>
19
20
  </TouchableOpacity>
20
21
  );
21
22
 
@@ -25,7 +26,8 @@ MenuOption.propTypes = {
25
26
  selected: bool,
26
27
  styles: ViewPropTypes.style,
27
28
  // eslint-disable-next-line react/forbid-prop-types
28
- item: shape(any)
29
+ item: shape(any),
30
+ dataTestId: string
29
31
  };
30
32
 
31
33
  export default memo(MenuOption);
@@ -13,6 +13,7 @@ import useKeyboardHeight from '../../hooks/useKeyboardHeight';
13
13
  import UTTextInput from '../UTTextInput';
14
14
  import UTLabel from '../UTLabel';
15
15
  import Surface from '../Surface';
16
+ import { TEST_ID_CONSTANTS } from '../../constants/testIds';
16
17
 
17
18
  import MenuOption from './components/MenuOption';
18
19
  import ListView from './components/ListView';
@@ -21,6 +22,8 @@ import styles from './styles';
21
22
  import GoogleLogo from './assets/google_on_white.png';
22
23
  import { ATTRIBUTION } from './constants';
23
24
 
25
+ const { anchor, modal, searchInput, list, attribution, text, logo } = TEST_ID_CONSTANTS;
26
+
24
27
  const UTMenu = ({
25
28
  autoCompletePlaceholder,
26
29
  avoidOverlappingAnchor = true,
@@ -44,7 +47,8 @@ const UTMenu = ({
44
47
  verticalOffset = 0,
45
48
  withAutocomplete,
46
49
  withoutOpacity,
47
- withGoogleAttribution = false
50
+ withGoogleAttribution = false,
51
+ dataTestId
48
52
  }) => {
49
53
  const [isOpen, setIsOpen] = useState(false);
50
54
  const anchorRef = useRef(null);
@@ -138,6 +142,7 @@ const UTMenu = ({
138
142
  activeOpacity={withoutOpacity && 1}
139
143
  disabled={disabled}
140
144
  onPress={isOpen ? closeMenu : openMenu}
145
+ testID={dataTestId ? `${dataTestId}.${anchor}` : undefined}
141
146
  >
142
147
  <View onLayout={handleAnchorLayout} pointerEvents="box-only" ref={anchorRef}>
143
148
  {children}
@@ -149,6 +154,7 @@ const UTMenu = ({
149
154
  onShow={focusSearchInput}
150
155
  transparent
151
156
  visible={isOpen}
157
+ testID={dataTestId ? `${dataTestId}.${modal}` : undefined}
152
158
  >
153
159
  <TouchableOpacity onPress={closeMenu} style={styles.overlayTouchable}>
154
160
  <View style={styles.overlay} />
@@ -170,6 +176,7 @@ const UTMenu = ({
170
176
  }
171
177
  placeholder={autoCompletePlaceholder}
172
178
  value={query}
179
+ dataTestId={dataTestId ? `${dataTestId}.${searchInput}` : undefined}
173
180
  />
174
181
  </View>
175
182
  )}
@@ -187,13 +194,21 @@ const UTMenu = ({
187
194
  selectedOption={selectedOption}
188
195
  usableWindowHeight={usableWindowHeight}
189
196
  windowHeight={windowHeight}
197
+ dataTestId={dataTestId ? `${dataTestId}.${list}` : undefined}
190
198
  />
191
199
  {withGoogleAttribution && (
192
200
  <View style={styles.attribution}>
193
- <UTLabel colorTheme="gray" variant="small">
201
+ <UTLabel
202
+ colorTheme="gray"
203
+ variant="small"
204
+ dataTestId={dataTestId ? `${dataTestId}.${attribution}.${text}` : undefined}
205
+ >
194
206
  {ATTRIBUTION}
195
207
  </UTLabel>
196
- <Image source={GoogleLogo} />
208
+ <Image
209
+ source={GoogleLogo}
210
+ testID={dataTestId ? `${dataTestId}.${attribution}.${logo}` : undefined}
211
+ />
197
212
  </View>
198
213
  )}
199
214
  </KeyboardAvoidingView>
@@ -17,5 +17,6 @@ export default {
17
17
  horizontalOffset: number,
18
18
  onPress: func,
19
19
  disabled: bool,
20
- withoutOpacity: bool
20
+ withoutOpacity: bool,
21
+ dataTestId: string
21
22
  };
@@ -0,0 +1,193 @@
1
+ # UTModal
2
+
3
+ ## Description
4
+
5
+ > ⚠️ **AI-GENERATED DOCUMENTATION**
6
+ > This documentation was entirely generated by an AI assistant and has NOT been reviewed by human developers. The test ID implementation details, patterns, examples, and usage instructions should be thoroughly verified before use in production. Please validate all functionality and testing approaches with your development team.
7
+
8
+ `UTModal` is a customizable modal component that provides a flexible overlay interface for displaying content, dialogs, and forms. It supports features like background images, loading states, custom buttons, and keyboard handling.
9
+
10
+ ## Props
11
+
12
+ | Name | Type | Default | Description |
13
+ | --------------------- | ------- | ------------ | ------------------------------------------------------------------------- |
14
+ | acceptButton | object | | Configuration for the accept/primary button. See Button Object section. |
15
+ | backgroundImg | number | | Background image source for the modal. |
16
+ | backgroundStyles | object | | Styles to apply to the background image. |
17
+ | cancelButton | object | | Configuration for the cancel/secondary button. See Button Object section. |
18
+ | children | node | | Content to display inside the modal. |
19
+ | closeButtonColorTheme | string | | Color theme for the close button. |
20
+ | dataTestId | string | 'modal' | Test ID for automated testing. Enables hierarchical test ID structure. |
21
+ | disableTouchable | bool | false | If true, disables the touchable overlay that dismisses the keyboard. |
22
+ | hideCloseButton | bool | false | If true, hides the close button. |
23
+ | hideSeparatorBar | bool | false | If true, hides the separator bar above buttons. |
24
+ | imageComponent | element | | Custom image component to display in the modal. |
25
+ | imageStyles | object | | Styles to apply to the image component container. |
26
+ | loading | bool | false | If true, shows a loading overlay. |
27
+ | loadingText | string | | Text to display during loading state. |
28
+ | modalBackgroundColor | string | | Custom background color for the modal. |
29
+ | modalStyles | object | | Custom styles to apply to the modal container. |
30
+ | onRequestClose | func | () => {} | Function called when the modal should be closed. |
31
+ | subtitle | string | | Subtitle text to display below the title. |
32
+ | subtitleProps | object | {} | Additional props to pass to the subtitle UTLabel component. |
33
+ | title | string | | Title text to display in the modal header. |
34
+ | visible | bool | **required** | Controls whether the modal is visible. |
35
+ | style | object | | Custom styles to apply to the modal. |
36
+
37
+ ## Test IDs
38
+
39
+ When `dataTestId` is provided (defaults to 'modal'), the component creates a hierarchical test ID structure:
40
+
41
+ | Element | Test ID | Condition |
42
+ | ------------- | ---------------------------- | ------------------------------------ |
43
+ | Modal | `${dataTestId}` | Always (defaults to 'modal') |
44
+ | Title | `${dataTestId}.title` | When `title` prop is provided |
45
+ | Subtitle | `${dataTestId}.subtitle` | When `subtitle` prop is provided |
46
+ | Close Button | `${dataTestId}.closeButton` | When close button is visible |
47
+ | Cancel Button | `${dataTestId}.cancelButton` | When `cancelButton` prop is provided |
48
+ | Accept Button | `${dataTestId}.acceptButton` | When `acceptButton` prop is provided |
49
+
50
+ ### Test ID Structure Details
51
+
52
+ - **Modal**: The main Modal component that controls visibility and overlay behavior
53
+ - **Title**: The main title text displayed in the modal header (handled by UTLabel)
54
+ - **Subtitle**: The subtitle text displayed below the title (handled by UTLabel)
55
+ - **Close Button**: The X button used to close the modal (handled by UTButton)
56
+ - **Cancel Button**: The secondary/cancel button (handled by UTButton)
57
+ - **Accept Button**: The primary/accept button (handled by UTButton)
58
+
59
+ Each button and label follows their respective component test ID patterns with their own hierarchical structure.
60
+
61
+ **Default Test ID**: Since only one modal should be visible at a time, the `dataTestId` prop defaults to 'modal'. This ensures that every UTModal instance has a consistent, predictable test ID structure available, while still allowing for custom test IDs when multiple modal types need to be distinguished.
62
+
63
+ ### Button Object
64
+
65
+ The `acceptButton` and `cancelButton` props are objects with the following structure:
66
+
67
+ | Name | Type | Description |
68
+ | ---------- | ------ | -------------------------------------------- |
69
+ | colorTheme | string | Color theme for the button. |
70
+ | disabled | bool | Whether the button is disabled. |
71
+ | onPress | func | Function to call when the button is pressed. |
72
+ | text | string | Text to display on the button. |
73
+
74
+ ## Usage
75
+
76
+ ### Basic Example
77
+
78
+ ```jsx
79
+ import React from 'react';
80
+ import { UTModal, UTLabel } from '@widergy/mobile-ui';
81
+
82
+ const Example = () => {
83
+ const [visible, setVisible] = React.useState(false);
84
+
85
+ const handleClose = () => setVisible(false);
86
+ const handleAccept = () => {
87
+ // Perform action
88
+ setVisible(false);
89
+ };
90
+
91
+ return (
92
+ <UTModal
93
+ visible={visible}
94
+ title="Confirm Action"
95
+ subtitle="Are you sure you want to proceed?"
96
+ onRequestClose={handleClose}
97
+ acceptButton={{
98
+ text: 'Yes, Continue',
99
+ onPress: handleAccept
100
+ }}
101
+ cancelButton={{
102
+ text: 'Cancel',
103
+ onPress: handleClose
104
+ }}
105
+ >
106
+ <UTLabel>This action cannot be undone.</UTLabel>
107
+ </UTModal>
108
+ );
109
+ };
110
+
111
+ export default Example;
112
+ ```
113
+
114
+ ### With Custom Test ID
115
+
116
+ ```jsx
117
+ import React from 'react';
118
+ import { UTModal, UTLabel } from '@widergy/mobile-ui';
119
+
120
+ const TestableExample = () => {
121
+ const [visible, setVisible] = React.useState(false);
122
+
123
+ const handleClose = () => setVisible(false);
124
+ const handleDelete = () => {
125
+ // Perform delete action
126
+ setVisible(false);
127
+ };
128
+
129
+ return (
130
+ <UTModal
131
+ dataTestId="deleteConfirmModal"
132
+ visible={visible}
133
+ title="Delete Item"
134
+ subtitle="This action cannot be undone"
135
+ onRequestClose={handleClose}
136
+ acceptButton={{
137
+ text: 'Delete',
138
+ onPress: handleDelete,
139
+ colorTheme: 'danger'
140
+ }}
141
+ cancelButton={{
142
+ text: 'Cancel',
143
+ onPress: handleClose
144
+ }}
145
+ >
146
+ <UTLabel>Are you sure you want to delete this item?</UTLabel>
147
+ </UTModal>
148
+ );
149
+ };
150
+
151
+ // Generated test IDs:
152
+ // deleteConfirmModal (main modal)
153
+ // deleteConfirmModal.title (title text)
154
+ // deleteConfirmModal.subtitle (subtitle text)
155
+ // deleteConfirmModal.closeButton (close X button)
156
+ // deleteConfirmModal.cancelButton (cancel button)
157
+ // deleteConfirmModal.acceptButton (delete button)
158
+
159
+ export default TestableExample;
160
+ ```
161
+
162
+ ### With Default Test ID
163
+
164
+ ```jsx
165
+ import React from 'react';
166
+ import { UTModal, UTLabel } from '@widergy/mobile-ui';
167
+
168
+ const DefaultTestIdExample = () => {
169
+ const [visible, setVisible] = React.useState(false);
170
+
171
+ return (
172
+ <UTModal
173
+ visible={visible}
174
+ title="Default Modal"
175
+ onRequestClose={() => setVisible(false)}
176
+ acceptButton={{
177
+ text: 'OK',
178
+ onPress: () => setVisible(false)
179
+ }}
180
+ >
181
+ <UTLabel>This modal uses the default test ID 'modal'.</UTLabel>
182
+ </UTModal>
183
+ );
184
+ };
185
+
186
+ // Generated test IDs (using default):
187
+ // modal (main modal)
188
+ // modal.title (title text)
189
+ // modal.closeButton (close X button)
190
+ // modal.acceptButton (OK button)
191
+
192
+ export default DefaultTestIdExample;
193
+ ```