@adobe-commerce/aio-experience-kit 1.0.2 → 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 +117 -0
- package/README.md +590 -2
- 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 +3 -2
- 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 +72 -58
- 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 +53 -33
- package/dist/cjs/components/DataTable/index.js.map +1 -1
- package/dist/cjs/components/DataTable/types.js.map +1 -1
- package/dist/cjs/components/FileUpload/index.js +12 -2
- package/dist/cjs/components/FileUpload/index.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 +3 -2
- 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 +74 -60
- 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 +53 -33
- package/dist/esm/components/DataTable/index.js.map +1 -1
- package/dist/esm/components/DataTable/types.js.map +1 -1
- package/dist/esm/components/FileUpload/index.js +13 -3
- package/dist/esm/components/FileUpload/index.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/GridBuilder/index.d.ts +1 -1
- package/dist/esm/types/components/DataTable/GridBuilder/index.d.ts.map +1 -1
- package/dist/esm/types/components/DataTable/GridBuilder/types.d.ts +1 -0
- package/dist/esm/types/components/DataTable/GridBuilder/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 +38 -0
- package/dist/esm/types/components/DataTable/types.d.ts.map +1 -1
- package/dist/esm/types/components/FileUpload/index.d.ts +2 -3
- package/dist/esm/types/components/FileUpload/index.d.ts.map +1 -1
- package/dist/esm/types/components/FileUpload/types.d.ts +14 -0
- package/dist/esm/types/components/FileUpload/types.d.ts.map +1 -1
- package/dist/esm/types/components/index.d.ts +2 -1
- package/dist/esm/types/components/index.d.ts.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -181,13 +181,167 @@ class EntityModel {
|
|
|
181
181
|
}
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
**Vertical Scrolling for Large Datasets (v1.0.3+):**
|
|
185
|
+
|
|
186
|
+
Control table height with automatic scrolling:
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
<DataTable
|
|
190
|
+
columns={columns}
|
|
191
|
+
data={largeDataset}
|
|
192
|
+
maxHeight="size-6000" // Spectrum token (480px)
|
|
193
|
+
// or maxHeight="500px" // CSS value
|
|
194
|
+
// or maxHeight="60vh" // Viewport height
|
|
195
|
+
onGridLoad={async () => {
|
|
196
|
+
// Load large dataset
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
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
|
+
|
|
184
336
|
**Features:**
|
|
185
337
|
- **Advanced Data Management**: Sortable columns with intelligent data type handling and custom sort functions
|
|
186
338
|
- **Flexible Selection**: Support for single-row selection or multi-row selection with visual feedback
|
|
187
339
|
- **Bulk Operations**: Mass action system for performing operations on multiple selected items
|
|
188
340
|
- **Contextual Actions**: Per-row actions with customizable button layouts, links, or dropdown menus
|
|
189
341
|
- **State Management**: Built-in loading states, empty state handling, and error boundary support
|
|
190
|
-
- **Performance Optimized**: Virtual scrolling for large datasets
|
|
342
|
+
- **Performance Optimized**: Virtual scrolling for large datasets with efficient re-rendering
|
|
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
|
|
191
345
|
- **User Experience**: Intuitive interactions with hover states, keyboard navigation, and clear visual hierarchy
|
|
192
346
|
|
|
193
347
|
#### `DataForm`
|
|
@@ -398,9 +552,202 @@ The DataForm component supports various field types for different input scenario
|
|
|
398
552
|
}
|
|
399
553
|
```
|
|
400
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
|
+
|
|
401
747
|
**Features:**
|
|
402
748
|
- **Dynamic Form Generation**: Create complex forms from simple configuration objects without manual field creation
|
|
403
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
|
|
404
751
|
- **Multi-Selection Support**: MULTISELECT field type with React Spectrum CheckboxGroup for selecting multiple options with scrollable overflow and accessibility support
|
|
405
752
|
- **Toggle Controls**: TOGGLE field type using React Spectrum Switch component with '1'/'0' value handling and automatic default value initialization
|
|
406
753
|
- **Environment Variable Support**: Built-in support for environment variable checkboxes on compatible field types (TEXT, SELECT, MULTISELECT, TOGGLE)
|
|
@@ -408,6 +755,211 @@ The DataForm component supports various field types for different input scenario
|
|
|
408
755
|
- **Workflow Integration**: Loading states, processing indicators, and navigation controls for multi-step workflows
|
|
409
756
|
- **Organized Layout**: Logical field grouping with collapsible sections and intuitive information hierarchy
|
|
410
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
|
|
411
963
|
|
|
412
964
|
#### `ConfirmationDialog`
|
|
413
965
|
|
|
@@ -528,12 +1080,47 @@ const DocumentUploader = () => {
|
|
|
528
1080
|
};
|
|
529
1081
|
```
|
|
530
1082
|
|
|
1083
|
+
**Programmatic Control with Refs (v1.0.3+):**
|
|
1084
|
+
|
|
1085
|
+
Reset the component programmatically from parent components:
|
|
1086
|
+
|
|
1087
|
+
```javascript
|
|
1088
|
+
import React, { useRef } from 'react';
|
|
1089
|
+
import { FileUpload, FileUploadHandle } from '@adobe-commerce/aio-experience-kit';
|
|
1090
|
+
|
|
1091
|
+
const MyForm = () => {
|
|
1092
|
+
const fileUploadRef = useRef<FileUploadHandle>(null);
|
|
1093
|
+
|
|
1094
|
+
const handleReset = () => {
|
|
1095
|
+
fileUploadRef.current?.reset(); // Clear all files
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
const handleGetFiles = () => {
|
|
1099
|
+
const files = fileUploadRef.current?.getFiles(); // Get current files
|
|
1100
|
+
console.log('Current files:', files);
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
return (
|
|
1104
|
+
<>
|
|
1105
|
+
<FileUpload
|
|
1106
|
+
ref={fileUploadRef}
|
|
1107
|
+
label="Upload Files"
|
|
1108
|
+
onSelect={(files) => console.log('Selected:', files)}
|
|
1109
|
+
/>
|
|
1110
|
+
<Button onPress={handleReset}>Reset</Button>
|
|
1111
|
+
<Button onPress={handleGetFiles}>Get Files</Button>
|
|
1112
|
+
</>
|
|
1113
|
+
);
|
|
1114
|
+
};
|
|
1115
|
+
```
|
|
1116
|
+
|
|
531
1117
|
**Features:**
|
|
532
1118
|
- **File Selection**: Click to browse files with support for single or multiple file selection
|
|
533
1119
|
- **Automatic Content Reading**: Automatically reads file content as text or base64 based on file type
|
|
534
1120
|
- **File Type Validation**: Built-in validation for accepted MIME types and file extensions
|
|
535
1121
|
- **Built-in Display**: Automatically displays selected files with name, size, and type information
|
|
536
|
-
- **Clear Functionality**: Built-in "Clear Files" link to remove selected files
|
|
1122
|
+
- **Clear Functionality**: Built-in "Clear Files" link to remove selected files and programmatic reset via ref
|
|
1123
|
+
- **Imperative Handle**: Access reset() and getFiles() methods via React refs for parent control
|
|
537
1124
|
- **Loading States**: Shows processing indicator while reading file contents
|
|
538
1125
|
- **Error Handling**: Comprehensive error messaging for file reading failures
|
|
539
1126
|
- **Accessibility**: Full keyboard navigation, screen reader support, and ARIA labels
|
|
@@ -631,6 +1218,7 @@ import type {
|
|
|
631
1218
|
DataFormProps,
|
|
632
1219
|
ConfirmationDialogProps,
|
|
633
1220
|
FileUploadProps,
|
|
1221
|
+
FileUploadHandle,
|
|
634
1222
|
FieldType,
|
|
635
1223
|
AdminUiSdkInterface,
|
|
636
1224
|
AdminUiSdkCredentials
|