@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.
Files changed (50) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +534 -0
  3. package/dist/cjs/components/DataForm/ButtonContainer/index.js +158 -0
  4. package/dist/cjs/components/DataForm/ButtonContainer/index.js.map +1 -0
  5. package/dist/cjs/components/DataForm/FieldBuilder/index.js.map +1 -1
  6. package/dist/cjs/components/DataForm/FormBuilder/index.js +178 -65
  7. package/dist/cjs/components/DataForm/FormBuilder/index.js.map +1 -1
  8. package/dist/cjs/components/DataForm/FormBuilder/types.js.map +1 -1
  9. package/dist/cjs/components/DataForm/index.js +6 -3
  10. package/dist/cjs/components/DataForm/index.js.map +1 -1
  11. package/dist/cjs/components/DataTable/GridBuilder/index.js.map +1 -1
  12. package/dist/cjs/components/DataTable/SearchBar/index.js +184 -0
  13. package/dist/cjs/components/DataTable/SearchBar/index.js.map +1 -0
  14. package/dist/cjs/components/DataTable/index.js +47 -31
  15. package/dist/cjs/components/DataTable/index.js.map +1 -1
  16. package/dist/cjs/components/DataTable/types.js.map +1 -1
  17. package/dist/esm/components/DataForm/ButtonContainer/index.js +154 -0
  18. package/dist/esm/components/DataForm/ButtonContainer/index.js.map +1 -0
  19. package/dist/esm/components/DataForm/FieldBuilder/index.js.map +1 -1
  20. package/dist/esm/components/DataForm/FormBuilder/index.js +180 -67
  21. package/dist/esm/components/DataForm/FormBuilder/index.js.map +1 -1
  22. package/dist/esm/components/DataForm/FormBuilder/types.js.map +1 -1
  23. package/dist/esm/components/DataForm/index.js +7 -4
  24. package/dist/esm/components/DataForm/index.js.map +1 -1
  25. package/dist/esm/components/DataTable/GridBuilder/index.js.map +1 -1
  26. package/dist/esm/components/DataTable/SearchBar/index.js +180 -0
  27. package/dist/esm/components/DataTable/SearchBar/index.js.map +1 -0
  28. package/dist/esm/components/DataTable/index.js +45 -29
  29. package/dist/esm/components/DataTable/index.js.map +1 -1
  30. package/dist/esm/components/DataTable/types.js.map +1 -1
  31. package/dist/esm/types/components/DataForm/ButtonContainer/index.d.ts +12 -0
  32. package/dist/esm/types/components/DataForm/ButtonContainer/index.d.ts.map +1 -0
  33. package/dist/esm/types/components/DataForm/ButtonContainer/types.d.ts +82 -0
  34. package/dist/esm/types/components/DataForm/ButtonContainer/types.d.ts.map +1 -0
  35. package/dist/esm/types/components/DataForm/FieldBuilder/index.d.ts.map +1 -1
  36. package/dist/esm/types/components/DataForm/FormBuilder/index.d.ts.map +1 -1
  37. package/dist/esm/types/components/DataForm/FormBuilder/types.d.ts +40 -0
  38. package/dist/esm/types/components/DataForm/FormBuilder/types.d.ts.map +1 -1
  39. package/dist/esm/types/components/DataForm/index.d.ts.map +1 -1
  40. package/dist/esm/types/components/DataForm/types.d.ts +2 -0
  41. package/dist/esm/types/components/DataForm/types.d.ts.map +1 -1
  42. package/dist/esm/types/components/DataTable/SearchBar/index.d.ts +11 -0
  43. package/dist/esm/types/components/DataTable/SearchBar/index.d.ts.map +1 -0
  44. package/dist/esm/types/components/DataTable/index.d.ts +1 -1
  45. package/dist/esm/types/components/DataTable/index.d.ts.map +1 -1
  46. package/dist/esm/types/components/DataTable/types.d.ts +37 -0
  47. package/dist/esm/types/components/DataTable/types.d.ts.map +1 -1
  48. package/dist/esm/types/components/index.d.ts +1 -0
  49. package/dist/esm/types/components/index.d.ts.map +1 -1
  50. 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