@adobe-commerce/aio-experience-kit 1.0.3 → 1.0.4
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 +91 -0
- package/README.md +534 -0
- package/dist/cjs/components/DataForm/ButtonContainer/index.js +158 -0
- package/dist/cjs/components/DataForm/ButtonContainer/index.js.map +1 -0
- package/dist/cjs/components/DataForm/FieldBuilder/index.js.map +1 -1
- package/dist/cjs/components/DataForm/FormBuilder/index.js +178 -65
- package/dist/cjs/components/DataForm/FormBuilder/index.js.map +1 -1
- package/dist/cjs/components/DataForm/FormBuilder/types.js.map +1 -1
- package/dist/cjs/components/DataForm/index.js +6 -3
- package/dist/cjs/components/DataForm/index.js.map +1 -1
- package/dist/cjs/components/DataTable/GridBuilder/index.js.map +1 -1
- package/dist/cjs/components/DataTable/SearchBar/index.js +184 -0
- package/dist/cjs/components/DataTable/SearchBar/index.js.map +1 -0
- package/dist/cjs/components/DataTable/index.js +47 -31
- package/dist/cjs/components/DataTable/index.js.map +1 -1
- package/dist/cjs/components/DataTable/types.js.map +1 -1
- package/dist/esm/components/DataForm/ButtonContainer/index.js +154 -0
- package/dist/esm/components/DataForm/ButtonContainer/index.js.map +1 -0
- package/dist/esm/components/DataForm/FieldBuilder/index.js.map +1 -1
- package/dist/esm/components/DataForm/FormBuilder/index.js +180 -67
- package/dist/esm/components/DataForm/FormBuilder/index.js.map +1 -1
- package/dist/esm/components/DataForm/FormBuilder/types.js.map +1 -1
- package/dist/esm/components/DataForm/index.js +7 -4
- package/dist/esm/components/DataForm/index.js.map +1 -1
- package/dist/esm/components/DataTable/GridBuilder/index.js.map +1 -1
- package/dist/esm/components/DataTable/SearchBar/index.js +180 -0
- package/dist/esm/components/DataTable/SearchBar/index.js.map +1 -0
- package/dist/esm/components/DataTable/index.js +45 -29
- package/dist/esm/components/DataTable/index.js.map +1 -1
- package/dist/esm/components/DataTable/types.js.map +1 -1
- package/dist/esm/types/components/DataForm/ButtonContainer/index.d.ts +12 -0
- package/dist/esm/types/components/DataForm/ButtonContainer/index.d.ts.map +1 -0
- package/dist/esm/types/components/DataForm/ButtonContainer/types.d.ts +82 -0
- package/dist/esm/types/components/DataForm/ButtonContainer/types.d.ts.map +1 -0
- package/dist/esm/types/components/DataForm/FieldBuilder/index.d.ts.map +1 -1
- package/dist/esm/types/components/DataForm/FormBuilder/index.d.ts.map +1 -1
- package/dist/esm/types/components/DataForm/FormBuilder/types.d.ts +40 -0
- package/dist/esm/types/components/DataForm/FormBuilder/types.d.ts.map +1 -1
- package/dist/esm/types/components/DataForm/index.d.ts.map +1 -1
- package/dist/esm/types/components/DataForm/types.d.ts +2 -0
- package/dist/esm/types/components/DataForm/types.d.ts.map +1 -1
- package/dist/esm/types/components/DataTable/SearchBar/index.d.ts +11 -0
- package/dist/esm/types/components/DataTable/SearchBar/index.d.ts.map +1 -0
- package/dist/esm/types/components/DataTable/index.d.ts +1 -1
- package/dist/esm/types/components/DataTable/index.d.ts.map +1 -1
- package/dist/esm/types/components/DataTable/types.d.ts +37 -0
- package/dist/esm/types/components/DataTable/types.d.ts.map +1 -1
- package/dist/esm/types/components/index.d.ts +1 -0
- package/dist/esm/types/components/index.d.ts.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,97 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.4] - 2025-01-01
|
|
9
|
+
|
|
10
|
+
### ✨ Added
|
|
11
|
+
|
|
12
|
+
- **[DataTable - SearchBar](./src/components/DataTable/SearchBar/index.tsx)** - Comprehensive search functionality with filtering capabilities (#33)
|
|
13
|
+
- Added configurable search bar component integrated into DataTable header
|
|
14
|
+
- **Two Search Modes**:
|
|
15
|
+
- Button mode: Search triggered only on button click or Enter key press
|
|
16
|
+
- Type mode: Real-time search-as-you-type with configurable debouncing
|
|
17
|
+
- **Search Configuration** via `DataTableSearchConfig` interface with 14 properties:
|
|
18
|
+
- `enabled` - Enable/disable search bar
|
|
19
|
+
- `placeholder` - Customizable search input placeholder
|
|
20
|
+
- `searchButtonLabel` - Button label text (displayed with icon)
|
|
21
|
+
- `searchOnType` - Toggle between button and type modes
|
|
22
|
+
- `debounceMs` - Configurable debounce delay (default: 300ms)
|
|
23
|
+
- `minCharacters` - Minimum character validation
|
|
24
|
+
- `maxLength` - Maximum input length constraint
|
|
25
|
+
- `searchableColumns` - Column-specific filtering
|
|
26
|
+
- `showClearButton` - Clear button visibility
|
|
27
|
+
- `isSearching` - Loading state with progress indicator
|
|
28
|
+
- `caseSensitive` - Case sensitivity control
|
|
29
|
+
- `showResultsCount` - Results count display
|
|
30
|
+
- `ariaLabel` - Accessibility label customization
|
|
31
|
+
- **Keyboard Shortcuts**:
|
|
32
|
+
- Enter: Trigger search (bypasses debounce in type mode)
|
|
33
|
+
- Escape: Clear search (when clear button enabled)
|
|
34
|
+
- **Layout**: Right-aligned in header with `justifyContent="space-between"`, maintaining ActionsBuilder and ProgressCircle left alignment
|
|
35
|
+
- **Accessibility**: Full ARIA label support, keyboard navigation, screen reader compatible, high contrast mode support
|
|
36
|
+
- **Debouncing**: Smart debounce implementation with timer cleanup on unmount and clear operations
|
|
37
|
+
- **Use Cases**: Product catalog search, customer lookup, order management, inventory filtering, log/audit trail search, multi-column search
|
|
38
|
+
- **API Design**: Uncontrolled component with single `onSearch` callback for both button and type modes
|
|
39
|
+
- **Test Coverage**: 32 unit tests + 12 integration tests, 98.03% statement coverage, 100% function coverage
|
|
40
|
+
- **Documentation**: Comprehensive README with 5 usage examples, configuration table, keyboard shortcuts, and accessibility features
|
|
41
|
+
|
|
42
|
+
- **[DataForm - FieldDependency](./src/components/DataForm/FormBuilder/types.ts)** - Conditional field visibility based on other field values (#32)
|
|
43
|
+
- Added `FieldDependency` interface with field reference and condition configuration
|
|
44
|
+
- Added `DependencyCondition` type union with 9 condition types:
|
|
45
|
+
- `equals` - Field value equals specified value
|
|
46
|
+
- `notEquals` - Field value does not equal specified value
|
|
47
|
+
- `in` - Field value exists in array of values
|
|
48
|
+
- `notIn` - Field value not in array of values
|
|
49
|
+
- `greaterThan` - Numeric field value greater than threshold
|
|
50
|
+
- `lessThan` - Numeric field value less than threshold
|
|
51
|
+
- `truthy` - Field value is truthy (for toggle/boolean fields)
|
|
52
|
+
- `falsy` - Field value is falsy
|
|
53
|
+
- `custom` - Custom validator function for complex conditions
|
|
54
|
+
- **Features**:
|
|
55
|
+
- Dynamic show/hide fields based on dependency conditions
|
|
56
|
+
- Automatic value clearing for hidden fields (optional via `clearValueOnHide`)
|
|
57
|
+
- Excluded hidden fields from form validation and submission
|
|
58
|
+
- Performance optimized with React memoization
|
|
59
|
+
- Multiple fields can depend on same source field
|
|
60
|
+
- **Use Cases**: Progressive disclosure, conditional required fields, multi-step forms, dynamic field groups
|
|
61
|
+
- **Test Coverage**: 18 comprehensive unit tests covering all condition types and edge cases
|
|
62
|
+
- **Code Coverage**: 98.85% for FormBuilder component
|
|
63
|
+
- **Documentation**: README updated with examples and configuration options
|
|
64
|
+
|
|
65
|
+
- **[DataForm - ButtonContainer](./src/components/DataForm/ButtonContainer/index.tsx)** - Configurable form buttons with custom layout control (#34)
|
|
66
|
+
- Added `ButtonContainer` component for flexible form action button customization
|
|
67
|
+
- Added `FormButtonConfig` interface with comprehensive button configuration
|
|
68
|
+
- **Customizable Submit Button**:
|
|
69
|
+
- Custom label, icon, visibility, and ARIA labels
|
|
70
|
+
- Default label: "Save" with SaveFloppy icon
|
|
71
|
+
- **Customizable Back Button**:
|
|
72
|
+
- Custom label, icon, visibility, and ARIA labels
|
|
73
|
+
- Default label: "Back" with Back icon
|
|
74
|
+
- **Secondary Buttons**:
|
|
75
|
+
- Add multiple custom action buttons
|
|
76
|
+
- Support async handlers with independent loading states
|
|
77
|
+
- Button variants: primary, secondary, negative
|
|
78
|
+
- Custom labels, icons, and ARIA labels
|
|
79
|
+
- **Layout Configuration**:
|
|
80
|
+
- Button positioning: before or after form content
|
|
81
|
+
- Button alignment: start, center, end
|
|
82
|
+
- Button gap: small (8px), medium (16px), large (24px)
|
|
83
|
+
- **Features**:
|
|
84
|
+
- Independent loading states for each secondary button
|
|
85
|
+
- Automatic disabled state during form submission
|
|
86
|
+
- Full accessibility with ARIA labels and keyboard navigation
|
|
87
|
+
- 100% backward compatible (defaults match original behavior)
|
|
88
|
+
- **Test Coverage**: 37 comprehensive unit tests covering all customization scenarios
|
|
89
|
+
- **Documentation**: README updated with 10 usage examples for different configurations
|
|
90
|
+
|
|
91
|
+
### 🐛 Fixed
|
|
92
|
+
|
|
93
|
+
- **[DataForm - FieldDependency Test](./test/components/DataForm/FieldDependency.test.tsx)** - TypeScript type errors
|
|
94
|
+
- Added `FormBuilderComponents` import for proper type handling
|
|
95
|
+
- Used type assertion for intentional invalid test case testing
|
|
96
|
+
- Removed unused `@ts-expect-error` directive
|
|
97
|
+
- All 583 tests passing with proper TypeScript compilation
|
|
98
|
+
|
|
8
99
|
## [1.0.3] - 2024-11-19
|
|
9
100
|
|
|
10
101
|
### ✨ Added
|
package/README.md
CHANGED
|
@@ -198,6 +198,141 @@ Control table height with automatic scrolling:
|
|
|
198
198
|
/>
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
+
**Search and Filtering Capabilities:**
|
|
202
|
+
|
|
203
|
+
Add built-in search functionality to enable users to quickly find and filter records within your DataTable. The search bar is configurable and supports both button-triggered and search-as-you-type modes.
|
|
204
|
+
|
|
205
|
+
*Example 1: Basic Search (Button Click Mode)*
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
<DataTable
|
|
209
|
+
columns={columns}
|
|
210
|
+
data={data}
|
|
211
|
+
searchConfig={{
|
|
212
|
+
enabled: true,
|
|
213
|
+
placeholder: "Search products...",
|
|
214
|
+
searchOnType: false, // Only search when button clicked or Enter pressed
|
|
215
|
+
}}
|
|
216
|
+
onSearch={async (searchText) => {
|
|
217
|
+
// Triggered only on button click or Enter key
|
|
218
|
+
await fetchFilteredData(searchText);
|
|
219
|
+
}}
|
|
220
|
+
/>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
*Example 2: Search-as-you-type with Debounce (Recommended for Client-Side Filtering)*
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
const [allData, setAllData] = useState(products);
|
|
227
|
+
const [filteredData, setFilteredData] = useState(products);
|
|
228
|
+
|
|
229
|
+
<DataTable
|
|
230
|
+
columns={columns}
|
|
231
|
+
data={filteredData} // Show filtered results
|
|
232
|
+
searchConfig={{
|
|
233
|
+
enabled: true,
|
|
234
|
+
searchOnType: true, // Search while typing
|
|
235
|
+
debounceMs: 300, // Wait 300ms after user stops typing
|
|
236
|
+
showClearButton: true,
|
|
237
|
+
minCharacters: 2, // Only search if 2+ characters entered
|
|
238
|
+
}}
|
|
239
|
+
onSearch={async (searchText) => {
|
|
240
|
+
if (!searchText) {
|
|
241
|
+
setFilteredData(allData); // Show all if search is empty
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Client-side filtering
|
|
246
|
+
const filtered = allData.filter(item =>
|
|
247
|
+
Object.values(item).some(val =>
|
|
248
|
+
String(val).toLowerCase().includes(searchText.toLowerCase())
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
setFilteredData(filtered);
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
*Example 3: Server-Side Search with Loading State*
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
const [isSearching, setSearching] = useState(false);
|
|
260
|
+
|
|
261
|
+
<DataTable
|
|
262
|
+
columns={columns}
|
|
263
|
+
data={data}
|
|
264
|
+
searchConfig={{
|
|
265
|
+
enabled: true,
|
|
266
|
+
isSearching: isSearching, // Shows spinner in search button
|
|
267
|
+
placeholder: "Search across 10,000+ records...",
|
|
268
|
+
searchOnType: true,
|
|
269
|
+
debounceMs: 800, // Longer debounce for server calls
|
|
270
|
+
}}
|
|
271
|
+
onSearch={async (searchText) => {
|
|
272
|
+
setSearching(true);
|
|
273
|
+
try {
|
|
274
|
+
const results = await apiSearchCall(searchText);
|
|
275
|
+
setData(results);
|
|
276
|
+
} finally {
|
|
277
|
+
setSearching(false);
|
|
278
|
+
}
|
|
279
|
+
}}
|
|
280
|
+
/>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
*Example 4: Column-Specific Search*
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
<DataTable
|
|
287
|
+
columns={columns}
|
|
288
|
+
data={data}
|
|
289
|
+
searchConfig={{
|
|
290
|
+
enabled: true,
|
|
291
|
+
searchableColumns: ['name', 'sku', 'email'], // Only search these columns
|
|
292
|
+
caseSensitive: false,
|
|
293
|
+
}}
|
|
294
|
+
onSearch={async (searchText, searchColumns) => {
|
|
295
|
+
// searchColumns will be ['name', 'sku', 'email']
|
|
296
|
+
await searchInColumns(searchText, searchColumns);
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Search Configuration Options:**
|
|
302
|
+
|
|
303
|
+
| Property | Type | Default | Description |
|
|
304
|
+
|----------|------|---------|-------------|
|
|
305
|
+
| `enabled` | boolean | `false` | Enable/disable search bar |
|
|
306
|
+
| `placeholder` | string | `"Search..."` | Placeholder text for search input |
|
|
307
|
+
| `searchButtonLabel` | string | `"Search"` | Label text displayed on search button (shown with icon) |
|
|
308
|
+
| `clearButtonLabel` | string | `"Clear search"` | Accessible label for clear button |
|
|
309
|
+
| `showClearButton` | boolean | `true` | Show clear button when text exists |
|
|
310
|
+
| `searchOnType` | boolean | `false` | Search while typing vs. button click only |
|
|
311
|
+
| `debounceMs` | number | `300` | Debounce time for search-as-you-type (ms) |
|
|
312
|
+
| `caseSensitive` | boolean | `false` | Case-sensitive search |
|
|
313
|
+
| `searchableColumns` | string[] | `[]` | Specific columns to search (empty = all columns) |
|
|
314
|
+
| `minCharacters` | number | `1` | Minimum characters before search triggers |
|
|
315
|
+
| `maxLength` | number | `100` | Maximum length of search input |
|
|
316
|
+
| `showResultsCount` | boolean | `false` | Show count of filtered results |
|
|
317
|
+
| `isSearching` | boolean | `false` | Show loading state in search button |
|
|
318
|
+
| `ariaLabel` | string | `"Search table"` | ARIA label for search input |
|
|
319
|
+
|
|
320
|
+
**Search Behavior Modes:**
|
|
321
|
+
|
|
322
|
+
- **Button Mode** (`searchOnType: false`): Search only triggers when the search button is clicked or Enter key is pressed. Ideal for server-side searches with expensive operations.
|
|
323
|
+
|
|
324
|
+
- **Type Mode** (`searchOnType: true`): Search triggers as the user types (with debouncing). Perfect for client-side filtering or real-time search experiences.
|
|
325
|
+
|
|
326
|
+
**Keyboard Shortcuts:**
|
|
327
|
+
- **Enter**: Triggers search in both modes (bypasses debounce in type mode)
|
|
328
|
+
- **Escape**: Clears search when `showClearButton` is enabled
|
|
329
|
+
|
|
330
|
+
**Accessibility Features:**
|
|
331
|
+
- Full keyboard navigation support
|
|
332
|
+
- Customizable ARIA labels for screen readers
|
|
333
|
+
- Loading state announcements
|
|
334
|
+
- High contrast mode support
|
|
335
|
+
|
|
201
336
|
**Features:**
|
|
202
337
|
- **Advanced Data Management**: Sortable columns with intelligent data type handling and custom sort functions
|
|
203
338
|
- **Flexible Selection**: Support for single-row selection or multi-row selection with visual feedback
|
|
@@ -206,6 +341,7 @@ Control table height with automatic scrolling:
|
|
|
206
341
|
- **State Management**: Built-in loading states, empty state handling, and error boundary support
|
|
207
342
|
- **Performance Optimized**: Virtual scrolling for large datasets with efficient re-rendering
|
|
208
343
|
- **Scrollable Tables**: Optional maxHeight prop for vertical scrolling with large datasets (v1.0.3+)
|
|
344
|
+
- **Search & Filter**: Built-in search bar with button and type modes, column-specific filtering
|
|
209
345
|
- **User Experience**: Intuitive interactions with hover states, keyboard navigation, and clear visual hierarchy
|
|
210
346
|
|
|
211
347
|
#### `DataForm`
|
|
@@ -416,9 +552,202 @@ The DataForm component supports various field types for different input scenario
|
|
|
416
552
|
}
|
|
417
553
|
```
|
|
418
554
|
|
|
555
|
+
##### Dependent Field Visibility
|
|
556
|
+
|
|
557
|
+
The DataForm component supports conditional field visibility based on the values of other fields. This allows you to create dynamic forms that show or hide fields based on user selections.
|
|
558
|
+
|
|
559
|
+
**Basic Example:**
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
{
|
|
563
|
+
groups: [
|
|
564
|
+
{
|
|
565
|
+
code: 'settings',
|
|
566
|
+
label: 'Settings',
|
|
567
|
+
fields: [
|
|
568
|
+
{
|
|
569
|
+
label: 'Enable Feature',
|
|
570
|
+
code: 'feature_enabled',
|
|
571
|
+
db_field: 'feature_enabled',
|
|
572
|
+
type: FieldType.TOGGLE,
|
|
573
|
+
required: false
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
label: 'Feature Configuration',
|
|
577
|
+
code: 'feature_config',
|
|
578
|
+
db_field: 'feature_config',
|
|
579
|
+
type: FieldType.TEXT,
|
|
580
|
+
required: false,
|
|
581
|
+
// This field only shows when feature_enabled is truthy
|
|
582
|
+
dependsOn: {
|
|
583
|
+
field: 'feature_enabled',
|
|
584
|
+
condition: { type: 'truthy' }
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
]
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Dependency Condition Types:**
|
|
594
|
+
|
|
595
|
+
```javascript
|
|
596
|
+
// Equals - Show field when value matches exactly
|
|
597
|
+
dependsOn: {
|
|
598
|
+
field: 'status',
|
|
599
|
+
condition: { type: 'equals', value: 'active' }
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Not Equals - Show field when value doesn't match
|
|
603
|
+
dependsOn: {
|
|
604
|
+
field: 'status',
|
|
605
|
+
condition: { type: 'notEquals', value: 'disabled' }
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// In - Show field when value is in array
|
|
609
|
+
dependsOn: {
|
|
610
|
+
field: 'status',
|
|
611
|
+
condition: { type: 'in', values: ['pending', 'processing'] }
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Not In - Show field when value is not in array
|
|
615
|
+
dependsOn: {
|
|
616
|
+
field: 'status',
|
|
617
|
+
condition: { type: 'notIn', values: ['cancelled', 'completed'] }
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Greater Than - Show field when numeric value is greater
|
|
621
|
+
dependsOn: {
|
|
622
|
+
field: 'quantity',
|
|
623
|
+
condition: { type: 'greaterThan', value: 10 }
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Less Than - Show field when numeric value is less
|
|
627
|
+
dependsOn: {
|
|
628
|
+
field: 'stock_level',
|
|
629
|
+
condition: { type: 'lessThan', value: 5 }
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Truthy - Show field when value is truthy (handles '0' and 'false' as falsy)
|
|
633
|
+
dependsOn: {
|
|
634
|
+
field: 'enable_feature',
|
|
635
|
+
condition: { type: 'truthy' }
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Falsy - Show field when value is falsy
|
|
639
|
+
dependsOn: {
|
|
640
|
+
field: 'enable_feature',
|
|
641
|
+
condition: { type: 'falsy' }
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Custom - Show field based on custom validation function
|
|
645
|
+
dependsOn: {
|
|
646
|
+
field: 'username',
|
|
647
|
+
condition: {
|
|
648
|
+
type: 'custom',
|
|
649
|
+
validator: (value, allFormValues) => {
|
|
650
|
+
return value && value.length > 3;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**Advanced Example - Multi-Step Configuration:**
|
|
657
|
+
|
|
658
|
+
```javascript
|
|
659
|
+
{
|
|
660
|
+
groups: [
|
|
661
|
+
{
|
|
662
|
+
code: 'shipping',
|
|
663
|
+
label: 'Shipping Configuration',
|
|
664
|
+
fields: [
|
|
665
|
+
{
|
|
666
|
+
label: 'Shipping Method',
|
|
667
|
+
code: 'shipping_method',
|
|
668
|
+
db_field: 'shipping_method',
|
|
669
|
+
type: FieldType.SELECT,
|
|
670
|
+
required: true,
|
|
671
|
+
options: [
|
|
672
|
+
{ value: 'standard', label: 'Standard Shipping' },
|
|
673
|
+
{ value: 'express', label: 'Express Shipping' },
|
|
674
|
+
{ value: 'pickup', label: 'Store Pickup' }
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
label: 'Carrier',
|
|
679
|
+
code: 'carrier',
|
|
680
|
+
db_field: 'carrier',
|
|
681
|
+
type: FieldType.SELECT,
|
|
682
|
+
required: true,
|
|
683
|
+
options: [
|
|
684
|
+
{ value: 'ups', label: 'UPS' },
|
|
685
|
+
{ value: 'fedex', label: 'FedEx' },
|
|
686
|
+
{ value: 'usps', label: 'USPS' }
|
|
687
|
+
],
|
|
688
|
+
// Only show carrier selection for standard and express shipping
|
|
689
|
+
dependsOn: {
|
|
690
|
+
field: 'shipping_method',
|
|
691
|
+
condition: { type: 'in', values: ['standard', 'express'] }
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
label: 'Tracking Number',
|
|
696
|
+
code: 'tracking_number',
|
|
697
|
+
db_field: 'tracking_number',
|
|
698
|
+
type: FieldType.TEXT,
|
|
699
|
+
required: false,
|
|
700
|
+
// Only show tracking for shipped methods
|
|
701
|
+
dependsOn: {
|
|
702
|
+
field: 'shipping_method',
|
|
703
|
+
condition: { type: 'notEquals', value: 'pickup' }
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
label: 'Store Location',
|
|
708
|
+
code: 'store_location',
|
|
709
|
+
db_field: 'store_location',
|
|
710
|
+
type: FieldType.SELECT,
|
|
711
|
+
required: true,
|
|
712
|
+
options: [
|
|
713
|
+
{ value: 'store1', label: 'Main Street Store' },
|
|
714
|
+
{ value: 'store2', label: 'Downtown Store' }
|
|
715
|
+
],
|
|
716
|
+
// Only show store location for pickup
|
|
717
|
+
dependsOn: {
|
|
718
|
+
field: 'shipping_method',
|
|
719
|
+
condition: { type: 'equals', value: 'pickup' }
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Dependency Configuration:**
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
interface FieldDependency {
|
|
732
|
+
field: string; // The code of the field this depends on
|
|
733
|
+
condition: DependencyCondition; // The condition to evaluate
|
|
734
|
+
clearValueOnHide?: boolean; // Whether to clear field value when hidden (default: false)
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**Key Features:**
|
|
739
|
+
- **Dynamic Visibility**: Fields automatically show/hide based on dependency conditions
|
|
740
|
+
- **Multiple Conditions**: Support for equals, notEquals, in, notIn, greaterThan, lessThan, truthy, falsy, and custom validators
|
|
741
|
+
- **Validation Integration**: Hidden fields are excluded from form validation
|
|
742
|
+
- **Value Management**: Optional automatic clearing of hidden field values
|
|
743
|
+
- **Performance Optimized**: Uses React memoization to prevent unnecessary re-renders
|
|
744
|
+
- **Chain Dependencies**: Fields can depend on other dependent fields
|
|
745
|
+
- **Form Submission**: Hidden field values are automatically excluded from submission
|
|
746
|
+
|
|
419
747
|
**Features:**
|
|
420
748
|
- **Dynamic Form Generation**: Create complex forms from simple configuration objects without manual field creation
|
|
421
749
|
- **Rich Field Types**: Support for text, email, password, number, select, multiselect, toggle, and specialized input types
|
|
750
|
+
- **Conditional Field Visibility**: Show/hide fields dynamically based on other field values with multiple condition types
|
|
422
751
|
- **Multi-Selection Support**: MULTISELECT field type with React Spectrum CheckboxGroup for selecting multiple options with scrollable overflow and accessibility support
|
|
423
752
|
- **Toggle Controls**: TOGGLE field type using React Spectrum Switch component with '1'/'0' value handling and automatic default value initialization
|
|
424
753
|
- **Environment Variable Support**: Built-in support for environment variable checkboxes on compatible field types (TEXT, SELECT, MULTISELECT, TOGGLE)
|
|
@@ -426,6 +755,211 @@ The DataForm component supports various field types for different input scenario
|
|
|
426
755
|
- **Workflow Integration**: Loading states, processing indicators, and navigation controls for multi-step workflows
|
|
427
756
|
- **Organized Layout**: Logical field grouping with collapsible sections and intuitive information hierarchy
|
|
428
757
|
- **Developer Friendly**: Event-driven architecture with lifecycle hooks for custom business logic integration
|
|
758
|
+
- **Configurable Buttons**: Full control over form action buttons with custom labels, icons, visibility, positioning, and alignment
|
|
759
|
+
|
|
760
|
+
##### Button Configuration
|
|
761
|
+
|
|
762
|
+
The DataForm component provides extensive button customization through the `buttonConfig` prop:
|
|
763
|
+
|
|
764
|
+
**Basic Usage:**
|
|
765
|
+
|
|
766
|
+
```javascript
|
|
767
|
+
<DataForm
|
|
768
|
+
components={formFields}
|
|
769
|
+
editItem={editItem}
|
|
770
|
+
onFormLoad={onFormLoad}
|
|
771
|
+
onFormSubmit={onFormSubmit}
|
|
772
|
+
onBackPress={onBackPress}
|
|
773
|
+
buttonConfig={{
|
|
774
|
+
submitButton: { label: 'Create Product' },
|
|
775
|
+
backButton: { label: 'Cancel' },
|
|
776
|
+
}}
|
|
777
|
+
/>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Button Configuration Options:**
|
|
781
|
+
|
|
782
|
+
```typescript
|
|
783
|
+
interface FormButtonConfig {
|
|
784
|
+
submitButton?: {
|
|
785
|
+
label?: string; // Button text (default: "Save")
|
|
786
|
+
icon?: ReactElement; // Custom icon (default: SaveFloppy)
|
|
787
|
+
isHidden?: boolean; // Hide button (default: false)
|
|
788
|
+
ariaLabel?: string; // Custom ARIA label
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
backButton?: {
|
|
792
|
+
label?: string; // Button text (default: "Back")
|
|
793
|
+
icon?: ReactElement; // Custom icon (default: Back)
|
|
794
|
+
isHidden?: boolean; // Hide button (default: false)
|
|
795
|
+
ariaLabel?: string; // Custom ARIA label
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
secondaryButtons?: Array<{
|
|
799
|
+
key: string; // Unique identifier
|
|
800
|
+
label: string; // Button text
|
|
801
|
+
icon?: ReactElement; // Optional icon
|
|
802
|
+
onPress: () => Promise<void>; // Click handler
|
|
803
|
+
variant?: 'primary' | 'secondary' | 'negative'; // Style variant
|
|
804
|
+
isDisabled?: boolean; // Disable state
|
|
805
|
+
type?: 'button' | 'submit'; // Button type (default: 'button')
|
|
806
|
+
ariaLabel?: string; // Custom ARIA label
|
|
807
|
+
}>;
|
|
808
|
+
|
|
809
|
+
position?: 'before' | 'after'; // Button position relative to form (default: 'after')
|
|
810
|
+
alignment?: 'start' | 'center' | 'end'; // Horizontal alignment (default: 'start')
|
|
811
|
+
gap?: 'small' | 'medium' | 'large'; // Spacing between buttons (default: 'small')
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
**Common Use Cases:**
|
|
816
|
+
|
|
817
|
+
```javascript
|
|
818
|
+
// Example 1: Create vs Edit Forms
|
|
819
|
+
<DataForm
|
|
820
|
+
buttonConfig={{
|
|
821
|
+
submitButton: {
|
|
822
|
+
label: isEditMode ? 'Update Product' : 'Create Product'
|
|
823
|
+
},
|
|
824
|
+
}}
|
|
825
|
+
/>
|
|
826
|
+
|
|
827
|
+
// Example 2: Modal Forms (hide back button)
|
|
828
|
+
<DataForm
|
|
829
|
+
buttonConfig={{
|
|
830
|
+
submitButton: { label: 'Save Changes' },
|
|
831
|
+
backButton: { isHidden: true },
|
|
832
|
+
}}
|
|
833
|
+
/>
|
|
834
|
+
|
|
835
|
+
// Example 3: Wizard Forms with Navigation
|
|
836
|
+
<DataForm
|
|
837
|
+
buttonConfig={{
|
|
838
|
+
submitButton: { label: 'Next Step' },
|
|
839
|
+
backButton: { label: 'Previous Step' },
|
|
840
|
+
}}
|
|
841
|
+
/>
|
|
842
|
+
|
|
843
|
+
// Example 4: Draft Workflow with Secondary Action
|
|
844
|
+
<DataForm
|
|
845
|
+
buttonConfig={{
|
|
846
|
+
submitButton: { label: 'Save Draft' },
|
|
847
|
+
secondaryButtons: [
|
|
848
|
+
{
|
|
849
|
+
key: 'publish',
|
|
850
|
+
label: 'Publish',
|
|
851
|
+
variant: 'primary',
|
|
852
|
+
onPress: async () => {
|
|
853
|
+
await handlePublish();
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
],
|
|
857
|
+
}}
|
|
858
|
+
/>
|
|
859
|
+
|
|
860
|
+
// Example 5: Edit Form with Delete Action
|
|
861
|
+
<DataForm
|
|
862
|
+
buttonConfig={{
|
|
863
|
+
submitButton: { label: 'Update' },
|
|
864
|
+
secondaryButtons: [
|
|
865
|
+
{
|
|
866
|
+
key: 'delete',
|
|
867
|
+
label: 'Delete',
|
|
868
|
+
variant: 'negative',
|
|
869
|
+
onPress: async () => {
|
|
870
|
+
const confirmed = await showConfirmDialog();
|
|
871
|
+
if (confirmed) {
|
|
872
|
+
await handleDelete();
|
|
873
|
+
navigate('/products');
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
}}
|
|
879
|
+
/>
|
|
880
|
+
|
|
881
|
+
// Example 6: Top-Aligned Buttons for Long Forms
|
|
882
|
+
<DataForm
|
|
883
|
+
buttonConfig={{
|
|
884
|
+
submitButton: { label: 'Save' },
|
|
885
|
+
position: 'before',
|
|
886
|
+
alignment: 'end',
|
|
887
|
+
}}
|
|
888
|
+
/>
|
|
889
|
+
|
|
890
|
+
// Example 7: Right-Aligned Buttons (Dialog Pattern)
|
|
891
|
+
<DataForm
|
|
892
|
+
buttonConfig={{
|
|
893
|
+
submitButton: { label: 'Confirm' },
|
|
894
|
+
backButton: { label: 'Cancel' },
|
|
895
|
+
alignment: 'end',
|
|
896
|
+
}}
|
|
897
|
+
/>
|
|
898
|
+
|
|
899
|
+
// Example 8: Multi-Action Workflow
|
|
900
|
+
<DataForm
|
|
901
|
+
buttonConfig={{
|
|
902
|
+
submitButton: { label: 'Save' },
|
|
903
|
+
secondaryButtons: [
|
|
904
|
+
{
|
|
905
|
+
key: 'save-continue',
|
|
906
|
+
label: 'Save & Continue',
|
|
907
|
+
onPress: async () => {
|
|
908
|
+
await handleSave();
|
|
909
|
+
navigateToNext();
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
key: 'save-new',
|
|
914
|
+
label: 'Save & Add New',
|
|
915
|
+
onPress: async () => {
|
|
916
|
+
await handleSave();
|
|
917
|
+
resetForm();
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
],
|
|
921
|
+
}}
|
|
922
|
+
/>
|
|
923
|
+
|
|
924
|
+
// Example 9: Custom Icons and ARIA Labels
|
|
925
|
+
import SaveFloppy from '@spectrum-icons/workflow/SaveFloppy';
|
|
926
|
+
import CheckmarkCircle from '@spectrum-icons/workflow/CheckmarkCircle';
|
|
927
|
+
|
|
928
|
+
<DataForm
|
|
929
|
+
buttonConfig={{
|
|
930
|
+
submitButton: {
|
|
931
|
+
label: 'Approve',
|
|
932
|
+
icon: <CheckmarkCircle />,
|
|
933
|
+
ariaLabel: 'Approve and save changes',
|
|
934
|
+
},
|
|
935
|
+
}}
|
|
936
|
+
/>
|
|
937
|
+
|
|
938
|
+
// Example 10: Centered Buttons with Custom Gap
|
|
939
|
+
<DataForm
|
|
940
|
+
buttonConfig={{
|
|
941
|
+
submitButton: { label: 'Submit' },
|
|
942
|
+
backButton: { label: 'Cancel' },
|
|
943
|
+
alignment: 'center',
|
|
944
|
+
gap: 'large',
|
|
945
|
+
}}
|
|
946
|
+
/>
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Button Order and States:**
|
|
950
|
+
|
|
951
|
+
- **Button Order**: ProgressCircle → Submit → Back → Secondary buttons (in order)
|
|
952
|
+
- **Disabled States**: All buttons are automatically disabled during form submission or when `isProcessing` is true
|
|
953
|
+
- **Loading States**: ProgressCircle shows automatically during submission, secondary buttons can show individual loading states
|
|
954
|
+
- **Accessibility**: All buttons have proper ARIA labels and keyboard navigation support
|
|
955
|
+
|
|
956
|
+
**Migration Notes:**
|
|
957
|
+
|
|
958
|
+
For existing implementations without `buttonConfig`, the default behavior is preserved:
|
|
959
|
+
- Submit button labeled "Save" with SaveFloppy icon
|
|
960
|
+
- Back button labeled "Back" with Back icon
|
|
961
|
+
- Buttons positioned after the form with start (left) alignment
|
|
962
|
+
- No breaking changes to existing code
|
|
429
963
|
|
|
430
964
|
#### `ConfirmationDialog`
|
|
431
965
|
|