@wix/auto-patterns 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/AutoPatternsCollectionPageContent/AutoPatternsCollectionPageContent.js +16 -4
- package/dist/cjs/components/AutoPatternsCollectionPageContent/AutoPatternsCollectionPageContent.js.map +1 -1
- package/dist/cjs/components/AutoPatternsCollectionPageContent/SkeletonCollection.js +34 -0
- package/dist/cjs/components/AutoPatternsCollectionPageContent/SkeletonCollection.js.map +1 -0
- package/dist/cjs/components/AutoPatternsEntityPage/AutoPatternsEntityPage.js +1 -0
- package/dist/cjs/components/AutoPatternsEntityPage/AutoPatternsEntityPage.js.map +1 -1
- package/dist/cjs/components/AutoPatternsEntityPage/SkeletonEntity.js +198 -0
- package/dist/cjs/components/AutoPatternsEntityPage/SkeletonEntity.js.map +1 -0
- package/dist/cjs/components/AutoPatternsRoute/AutoPatternsPage.js +13 -3
- package/dist/cjs/components/AutoPatternsRoute/AutoPatternsPage.js.map +1 -1
- package/dist/cjs/hooks/useBaseTableFeatures.js +7 -2
- package/dist/cjs/hooks/useBaseTableFeatures.js.map +1 -1
- package/dist/cjs/hooks/useCollectionPageOnRowClickActions.js +49 -0
- package/dist/cjs/hooks/useCollectionPageOnRowClickActions.js.map +1 -0
- package/dist/cjs/providers/PatternsWizardOverridesContext.js +1 -1
- package/dist/cjs/providers/PatternsWizardOverridesContext.js.map +1 -1
- package/dist/cjs/providers/SchemaContext.js +4 -3
- package/dist/cjs/providers/SchemaContext.js.map +1 -1
- package/dist/cjs/types/CollectionPageConfig.js.map +1 -1
- package/dist/cjs/types/actions/collectionPageActions.js.map +1 -1
- package/dist/docs/action_cell.md +214 -0
- package/dist/docs/app_config_structure.md +355 -0
- package/dist/docs/auto-patterns-guide.md +2447 -0
- package/dist/docs/bulk_actions.md +266 -0
- package/dist/docs/collection_page.md +54 -0
- package/dist/docs/collection_page_actions.md +343 -0
- package/dist/docs/custom_overrides.md +511 -0
- package/dist/docs/entity_page.md +104 -0
- package/dist/docs/entity_page_actions.md +92 -0
- package/dist/docs/index.md +76 -0
- package/dist/docs/installation.md +55 -0
- package/dist/docs/introduction.md +74 -0
- package/dist/docs/pages_configuration.md +129 -0
- package/dist/docs/recipe-bulk-operations.md +1352 -0
- package/dist/docs/recipe-crud-operations.md +786 -0
- package/dist/docs/recipe-customization.md +1702 -0
- package/dist/docs/recipe-first-dashboard.md +794 -0
- package/dist/docs/sdk_and_schema.md +215 -0
- package/dist/esm/components/AutoPatternsCollectionPageContent/AutoPatternsCollectionPageContent.js +7 -1
- package/dist/esm/components/AutoPatternsCollectionPageContent/AutoPatternsCollectionPageContent.js.map +1 -1
- package/dist/esm/components/AutoPatternsCollectionPageContent/SkeletonCollection.js +21 -0
- package/dist/esm/components/AutoPatternsCollectionPageContent/SkeletonCollection.js.map +1 -0
- package/dist/esm/components/AutoPatternsEntityPage/AutoPatternsEntityPage.js +3 -1
- package/dist/esm/components/AutoPatternsEntityPage/AutoPatternsEntityPage.js.map +1 -1
- package/dist/esm/components/AutoPatternsEntityPage/SkeletonEntity.js +91 -0
- package/dist/esm/components/AutoPatternsEntityPage/SkeletonEntity.js.map +1 -0
- package/dist/esm/components/AutoPatternsRoute/AutoPatternsPage.js +5 -1
- package/dist/esm/components/AutoPatternsRoute/AutoPatternsPage.js.map +1 -1
- package/dist/esm/hooks/useBaseTableFeatures.js +6 -1
- package/dist/esm/hooks/useBaseTableFeatures.js.map +1 -1
- package/dist/esm/hooks/useCollectionPageOnRowClickActions.js +45 -0
- package/dist/esm/hooks/useCollectionPageOnRowClickActions.js.map +1 -0
- package/dist/esm/providers/PatternsWizardOverridesContext.js.map +1 -1
- package/dist/esm/providers/SchemaContext.js +3 -2
- package/dist/esm/providers/SchemaContext.js.map +1 -1
- package/dist/esm/types/CollectionPageConfig.js.map +1 -1
- package/dist/esm/types/actions/collectionPageActions.js.map +1 -1
- package/dist/types/components/AutoPatternsCollectionPageContent/AutoPatternsCollectionPageContent.d.ts.map +1 -1
- package/dist/types/components/AutoPatternsCollectionPageContent/SkeletonCollection.d.ts +5 -0
- package/dist/types/components/AutoPatternsCollectionPageContent/SkeletonCollection.d.ts.map +1 -0
- package/dist/types/components/AutoPatternsEntityPage/SkeletonEntity.d.ts +7 -0
- package/dist/types/components/AutoPatternsEntityPage/SkeletonEntity.d.ts.map +1 -0
- package/dist/types/components/AutoPatternsRoute/AutoPatternsPage.d.ts.map +1 -1
- package/dist/types/hooks/useBaseTableFeatures.d.ts +1 -1
- package/dist/types/hooks/useBaseTableFeatures.d.ts.map +1 -1
- package/dist/types/hooks/useCollectionPageOnRowClickActions.d.ts +7 -0
- package/dist/types/hooks/useCollectionPageOnRowClickActions.d.ts.map +1 -0
- package/dist/types/hooks/useTableFeatures.d.ts +1 -1
- package/dist/types/hooks/useTableGridSwitchFeatures.d.ts +1 -1
- package/dist/types/providers/PatternsWizardOverridesContext.d.ts +2 -2
- package/dist/types/providers/PatternsWizardOverridesContext.d.ts.map +1 -1
- package/dist/types/providers/SchemaContext.d.ts +2 -1
- package/dist/types/providers/SchemaContext.d.ts.map +1 -1
- package/dist/types/types/CollectionPageConfig.d.ts +2 -1
- package/dist/types/types/CollectionPageConfig.d.ts.map +1 -1
- package/dist/types/types/actions/collectionPageActions.d.ts +8 -0
- package/dist/types/types/actions/collectionPageActions.d.ts.map +1 -1
- package/package.json +21 -16
|
@@ -0,0 +1,1352 @@
|
|
|
1
|
+
# Recipe 3: Implementing Bulk Operations
|
|
2
|
+
|
|
3
|
+
**Use Case**: "I need users to select multiple items and perform bulk actions (delete, export, archive, etc.)"
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Bulk Action Toolbar Configuration
|
|
8
|
+
|
|
9
|
+
The Bulk Action Toolbar feature enables users to perform operations on multiple selected entities simultaneously in collection tables or grids. When configured, it adds checkboxes to each row and displays a toolbar with bulk actions when items are selected.
|
|
10
|
+
|
|
11
|
+
### Placement and Structure
|
|
12
|
+
|
|
13
|
+
The Bulk Action Toolbar is configured within the table / grid / table-grid switch configuration using the `bulkActionToolbar` property. It has a two-level structure:
|
|
14
|
+
* `primaryActions`: Actions shown directly in the bulk action toolbar
|
|
15
|
+
* `secondaryActions`: Additional actions organized in sections, typically shown in a dropdown menu
|
|
16
|
+
|
|
17
|
+
Both properties are optional, but at least one should be provided for the bulk action toolbar to be functional.
|
|
18
|
+
|
|
19
|
+
### Bulk Action Types Reference
|
|
20
|
+
|
|
21
|
+
1. **Bulk Delete Action** (`type: "bulkDelete"`):
|
|
22
|
+
- ✓ Use when: Removing multiple entities with confirmation
|
|
23
|
+
- ✓ Common scenarios:
|
|
24
|
+
- Mass deletion of records
|
|
25
|
+
- Bulk cleanup operations
|
|
26
|
+
- ✓ Built-in functionality: No custom implementation needed
|
|
27
|
+
|
|
28
|
+
2. **Custom Action** (`type: "custom"`):
|
|
29
|
+
- ✓ Use when: Executing custom JavaScript for bulk operations
|
|
30
|
+
- ✓ Common scenarios:
|
|
31
|
+
- Bulk API calls
|
|
32
|
+
- Bulk exports/downloads
|
|
33
|
+
- Complex bulk operations without UI
|
|
34
|
+
- ⚠️ Requires implementation: Must register action in overrides
|
|
35
|
+
|
|
36
|
+
### Type Selection Decision Tree
|
|
37
|
+
|
|
38
|
+
When choosing a bulk action type, follow this decision process:
|
|
39
|
+
|
|
40
|
+
1. IF removing multiple entities:
|
|
41
|
+
→ Use `type: "bulkDelete"`
|
|
42
|
+
|
|
43
|
+
2. IF executing custom logic for bulk operations without UI:
|
|
44
|
+
→ Use `type: "custom"`
|
|
45
|
+
- MUST implement action handler
|
|
46
|
+
- MUST register with `actions` override
|
|
47
|
+
|
|
48
|
+
### Bulk Delete Action Configuration
|
|
49
|
+
|
|
50
|
+
Bulk delete actions remove multiple entities with a confirmation modal.
|
|
51
|
+
|
|
52
|
+
#### Validation Rules:
|
|
53
|
+
|
|
54
|
+
1. `bulkDelete.mode` must be `"modal"` (currently only modal is supported)
|
|
55
|
+
2. `bulkDelete.modal` object must exist
|
|
56
|
+
3. The modal properties (title, description, actions, feedback) are all optional
|
|
57
|
+
|
|
58
|
+
### Custom Bulk Action Configuration
|
|
59
|
+
|
|
60
|
+
Custom bulk actions execute JavaScript code that you define for bulk operations. These actions receive parameters that give them access to selected entities data and utilities. Here's how to implement a custom bulk action:
|
|
61
|
+
|
|
62
|
+
1. First, create the actions folder structure in your page folder:
|
|
63
|
+
```
|
|
64
|
+
your-page/
|
|
65
|
+
├── page.tsx
|
|
66
|
+
└── components/
|
|
67
|
+
└── actions/
|
|
68
|
+
├── index.tsx // Exports all actions
|
|
69
|
+
└── bulkExportPets.tsx // Your custom bulk action
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2. Create your bulk action handler in `bulkExportPets.tsx`:
|
|
73
|
+
```typescript
|
|
74
|
+
import { CustomBulkActionsActionResolver } from '@wix/auto-patterns';
|
|
75
|
+
import { Download } from '@wix/wix-ui-icons-common';
|
|
76
|
+
import React from 'react';
|
|
77
|
+
|
|
78
|
+
// IMPORTANT: Function name MUST match the action id in your configuration
|
|
79
|
+
export const bulkExportPets: CustomBulkActionsActionResolver = (params) => {
|
|
80
|
+
const { actionParams, sdk } = params;
|
|
81
|
+
const { selectedValues, total } = actionParams;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
label: 'Export Selected',
|
|
85
|
+
icon: <Download />,
|
|
86
|
+
onClick: () => {
|
|
87
|
+
// sdk is provided to custom action resolvers (see SDK Utilities section)
|
|
88
|
+
const optimisticActions = sdk.getOptimisticActions(sdk.collectionId);
|
|
89
|
+
const schema = sdk.getSchema(sdk.collectionId);
|
|
90
|
+
|
|
91
|
+
// Example: Mark pets as exported
|
|
92
|
+
const updatedItems = selectedValues.map(item => ({
|
|
93
|
+
...item,
|
|
94
|
+
lastExported: new Date()
|
|
95
|
+
}));
|
|
96
|
+
optimisticActions.updateMany(updatedItems, {
|
|
97
|
+
submit: async (items) => {
|
|
98
|
+
// Your export logic here + update server
|
|
99
|
+
const exportData = items.map(pet => ({
|
|
100
|
+
name: pet.name,
|
|
101
|
+
age: pet.age,
|
|
102
|
+
owner: pet.owner
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
// Create and download CSV
|
|
106
|
+
const csv = exportData.map(row => Object.values(row).join(',')).join('\n');
|
|
107
|
+
const blob = new Blob([csv], { type: 'text/csv' });
|
|
108
|
+
const url = URL.createObjectURL(blob);
|
|
109
|
+
const a = document.createElement('a');
|
|
110
|
+
a.href = url;
|
|
111
|
+
a.download = 'pets-export.csv';
|
|
112
|
+
a.click();
|
|
113
|
+
|
|
114
|
+
// Update server with export timestamp
|
|
115
|
+
return await schema.actions.update(items);
|
|
116
|
+
},
|
|
117
|
+
successToast: `${selectedValues.length} pets exported`,
|
|
118
|
+
errorToast: (err, {retry}) => ({
|
|
119
|
+
text: 'Export failed',
|
|
120
|
+
action: { text: 'Retry', onClick: retry }
|
|
121
|
+
})
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. Export your action in `actions/index.tsx`:
|
|
129
|
+
```typescript
|
|
130
|
+
export * from './bulkExportPets';
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
4. Configure the action in your JSON configuration:
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"id": "bulkExportPets", // MUST match the function name exactly
|
|
137
|
+
"type": "custom", // REQUIRED: Must be exactly "custom"
|
|
138
|
+
"label": "Export Selected", // Optional: Displayed text
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
5. Register your action in the `PatternsWizardOverridesProvider`:
|
|
143
|
+
```typescript
|
|
144
|
+
import * as actions from './components/actions';
|
|
145
|
+
|
|
146
|
+
<PatternsWizardOverridesProvider value={{ actions }}>
|
|
147
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
148
|
+
</PatternsWizardOverridesProvider>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Key Points for Custom Bulk Actions:
|
|
152
|
+
- The action `id` in the configuration MUST exactly match the function name exported from your actions folder
|
|
153
|
+
- The function name and file name should follow a consistent naming convention (e.g., camelCase)
|
|
154
|
+
- The implementation must be exported as a named export (not default export)
|
|
155
|
+
- The implementation must use the `CustomBulkActionsActionResolver` type
|
|
156
|
+
- Access selected entities through `actionParams.selectedValues`
|
|
157
|
+
- Access total count through `actionParams.total`
|
|
158
|
+
|
|
159
|
+
#### Validation Rules for Custom Bulk Actions:
|
|
160
|
+
|
|
161
|
+
1. `id` must:
|
|
162
|
+
- Match exactly the function name of the custom bulk action implementation
|
|
163
|
+
- Be registered in the `actions` property of your `PatternsWizardOverridesProvider`
|
|
164
|
+
- Follow JavaScript identifier naming rules (camelCase recommended)
|
|
165
|
+
|
|
166
|
+
2. The implementation must return an object with:
|
|
167
|
+
- `label`: Text displayed for the action
|
|
168
|
+
- `icon`: An Icon component from "@wix/wix-ui-icons-common"
|
|
169
|
+
- `onClick`: Handler function for the bulk action
|
|
170
|
+
|
|
171
|
+
3. The implementation must:
|
|
172
|
+
- Use the correct type: `CustomBulkActionsActionResolver`
|
|
173
|
+
- Be exported as a named export
|
|
174
|
+
- Have a filename matching the function name
|
|
175
|
+
|
|
176
|
+
### Bulk Action Toolbar Structure
|
|
177
|
+
|
|
178
|
+
The bulk action toolbar uses a specific structure for organizing actions:
|
|
179
|
+
|
|
180
|
+
#### Primary Actions Structure:
|
|
181
|
+
```json
|
|
182
|
+
"bulkActionToolbar": {
|
|
183
|
+
"primaryActions": [
|
|
184
|
+
{
|
|
185
|
+
"type": "action",
|
|
186
|
+
"action": {
|
|
187
|
+
"item": {
|
|
188
|
+
"id": "bulkCustom",
|
|
189
|
+
"type": "custom",
|
|
190
|
+
"label": "Custom Bulk",
|
|
191
|
+
"custom": {
|
|
192
|
+
"id": "MyCustomBulk"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"type": "menu",
|
|
199
|
+
"menu": {
|
|
200
|
+
"label": "More actions",
|
|
201
|
+
"items": [
|
|
202
|
+
{
|
|
203
|
+
"id": "exportItems",
|
|
204
|
+
"type": "custom",
|
|
205
|
+
"label": "Export Items"
|
|
206
|
+
},
|
|
207
|
+
{ "type": "divider" },
|
|
208
|
+
{
|
|
209
|
+
"id": "downloadItems",
|
|
210
|
+
"type": "custom",
|
|
211
|
+
"label": "Download Items"
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### Secondary Actions Structure:
|
|
221
|
+
```json
|
|
222
|
+
"bulkActionToolbar": {
|
|
223
|
+
"secondaryActions": [
|
|
224
|
+
{
|
|
225
|
+
"id": "bulkDeleteWithModal",
|
|
226
|
+
"label": "Bulk Delete",
|
|
227
|
+
"type": "bulkDelete",
|
|
228
|
+
"bulkDelete": {
|
|
229
|
+
"mode": "modal",
|
|
230
|
+
"modal": {
|
|
231
|
+
"title": { "text": "Delete items" },
|
|
232
|
+
"description": { "text": "Are you sure?" }
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"id": "bulkExportPets",
|
|
238
|
+
"type": "custom",
|
|
239
|
+
"label": "Export Selected"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Key Implementation Decisions
|
|
246
|
+
|
|
247
|
+
Follow this decision process when implementing Bulk Action Toolbar:
|
|
248
|
+
|
|
249
|
+
1. **Basic Decision**: Decide which bulk operations users need to perform:
|
|
250
|
+
- Delete multiple entities? → Use `bulkDelete` actions
|
|
251
|
+
- Custom bulk operations? → Use `custom` actions
|
|
252
|
+
|
|
253
|
+
2. **Primary vs Secondary**: Choose the most common/important bulk action as primary:
|
|
254
|
+
- Most common bulk operation → Place in `primaryActions.items` array
|
|
255
|
+
- Less common bulk operations → Place in `secondaryActions` array
|
|
256
|
+
|
|
257
|
+
3. **Custom Implementation**:
|
|
258
|
+
- For `custom` bulk actions, you must provide implementations in your code and register them with `PatternsWizardOverridesProvider`
|
|
259
|
+
|
|
260
|
+
### Bulk Action Toolbar Validation Checklist
|
|
261
|
+
|
|
262
|
+
AI agents should verify these requirements before generating Bulk Action Toolbar configurations:
|
|
263
|
+
|
|
264
|
+
✓ Bulk Action Toolbar is placed directly inside `table` or `grid` configuration as `bulkActionToolbar`
|
|
265
|
+
✓ Each bulk action has a unique `id` and correct `type` value
|
|
266
|
+
✓ Each action type only includes its required field(s)
|
|
267
|
+
✓ Bulk delete actions have modal configuration
|
|
268
|
+
✓ Custom bulk actions match implementations in overrides
|
|
269
|
+
✓ At least one of `primaryActions` or `secondaryActions` is provided
|
|
270
|
+
✓ Primary actions use `action`/`menu` structure with proper `action` or `menu` properties
|
|
271
|
+
✓ Secondary actions are an array that can include dividers
|
|
272
|
+
✓ Menu items within primary actions is array that can include dividers
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
# Collection Page Actions
|
|
277
|
+
|
|
278
|
+
## ⚠️ Required Actions
|
|
279
|
+
|
|
280
|
+
- **Every collection page must include a create action that navigates to the entity page for adding new entities** - this is essential for user workflow
|
|
281
|
+
|
|
282
|
+
The `actions` property is an optional object within the `collectionPage` configuration, but it is strongly recommended to always include primaryActions with a create action for better user experience.
|
|
283
|
+
|
|
284
|
+
## `primaryActions` and `secondaryActions` Structure
|
|
285
|
+
|
|
286
|
+
Both `primaryActions` and `secondaryActions` are optional and share the same underlying structure for defining how actions are displayed. They can be configured in one of two ways:
|
|
287
|
+
|
|
288
|
+
### A. Action Layout (`type: "action"`)
|
|
289
|
+
* **Description**: This layout is used to display a single, prominent page-level action. For example, a "Create New Item" button.
|
|
290
|
+
* **`action.item`**: Contains the configuration for the single action.
|
|
291
|
+
|
|
292
|
+
### B. Action Menu Layout (`type: "menu"`)
|
|
293
|
+
* **Description**: This layout groups several page-level actions, often rendered as a dropdown menu or a set of related buttons under a common label.
|
|
294
|
+
* **`menu.label`**: A string that serves as the title or accessible label for the group of actions.
|
|
295
|
+
* **`menu.items`**: A flat array of action configurations, which can include divider objects for visual separation.
|
|
296
|
+
|
|
297
|
+
## Individual Action Configuration
|
|
298
|
+
|
|
299
|
+
Each individual action, whether standalone in an `action` layout or part of an `items` array in a `menu` layout, is defined by the action item structure (see `AppConfig Structure`).
|
|
300
|
+
|
|
301
|
+
In addition to these common properties, each action item must specify a `type` which determines the action's behavior and additional required configuration.
|
|
302
|
+
|
|
303
|
+
### 1. `type: "create"`
|
|
304
|
+
* **Purpose**: Navigates to an entity page, allowing the user to create a new item in the specified collection.
|
|
305
|
+
* **Details**:
|
|
306
|
+
* `create.mode`: Must be `'page'`.
|
|
307
|
+
* `create.page.id`: Must be the `id` of an existing `entityPage` in your `AppConfig`. This entity page should be set up to handle the creation of new entities for the `collection.collectionId`.
|
|
308
|
+
|
|
309
|
+
### 2. `type: "custom"`
|
|
310
|
+
* **Purpose**: Executes custom JavaScript logic defined in your application's overrides.
|
|
311
|
+
* **Details**:
|
|
312
|
+
* The `custom` object in the configuration is typically empty. The functionality is determined by a custom action resolver function that you implement and register in the `actions` section of your `PatternsWizardOverridesProvider`. The `id` of this action item must exactly match the name (key) of the registered custom action resolver. The resolver will receive parameters including `collectionId`.
|
|
313
|
+
|
|
314
|
+
### 3. `type: "divider"`
|
|
315
|
+
* **Purpose**: Creates a visual separator between action groups in menus and lists.
|
|
316
|
+
* **Details**:
|
|
317
|
+
* Divider actions require no additional configuration beyond `{ "type": "divider" }`.
|
|
318
|
+
* Used within flat arrays to create logical groupings.
|
|
319
|
+
|
|
320
|
+
## Note on `secondaryActions`
|
|
321
|
+
|
|
322
|
+
`secondaryActions` follow the exact same structural rules (`type: "action"` or `type: "menu"`) and use the same action item options as `primaryActions`. They are typically used for less prominent or less frequently used page-level actions, often rendered in a secondary position or within a "more options" style menu.
|
|
323
|
+
|
|
324
|
+
## Custom Collection Page Action Configuration
|
|
325
|
+
|
|
326
|
+
Custom collection page actions execute JavaScript code that you define for collection-level operations. These actions receive parameters that give them access to collection context and utilities. Here's how to implement a custom collection page action:
|
|
327
|
+
|
|
328
|
+
1. First, create the actions folder structure in your page folder:
|
|
329
|
+
```
|
|
330
|
+
your-page/
|
|
331
|
+
├── page.tsx
|
|
332
|
+
└── components/
|
|
333
|
+
└── actions/
|
|
334
|
+
├── index.tsx // Exports all actions
|
|
335
|
+
└── exportCollection.tsx // Your custom collection action
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
2. Create your collection action handler in `exportCollection.tsx`:
|
|
339
|
+
```typescript
|
|
340
|
+
import { CustomActionCollectionPageActionResolver } from '@wix/auto-patterns';
|
|
341
|
+
import React from 'react';
|
|
342
|
+
import { Download } from '@wix/ui-icons-common';
|
|
343
|
+
|
|
344
|
+
// IMPORTANT: Function name MUST match the action id in your configuration
|
|
345
|
+
export const exportCollection: CustomActionCollectionPageActionResolver = (params) => {
|
|
346
|
+
const { actionParams, sdk } = params;
|
|
347
|
+
const { collectionId } = actionParams;
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
label: 'Export Collection',
|
|
351
|
+
icon: <Download />,
|
|
352
|
+
onClick: () => {
|
|
353
|
+
// sdk is provided to custom action resolvers (see SDK Utilities section)
|
|
354
|
+
const optimisticActions = sdk.getOptimisticActions(collectionId);
|
|
355
|
+
const schema = sdk.getSchema(collectionId);
|
|
356
|
+
|
|
357
|
+
// Example: Mark entire collection as exported
|
|
358
|
+
optimisticActions.updateAll(
|
|
359
|
+
(item) => ({ lastExported: new Date() }),
|
|
360
|
+
{
|
|
361
|
+
submit: async () => {
|
|
362
|
+
// Your collection export logic here
|
|
363
|
+
console.log(`Exporting collection: ${collectionId}`);
|
|
364
|
+
// Export and update all items on server
|
|
365
|
+
return await schema.actions.bulkUpdate({ lastExported: new Date() });
|
|
366
|
+
},
|
|
367
|
+
successToast: 'Collection exported successfully',
|
|
368
|
+
errorToast: (err, {retry}) => ({
|
|
369
|
+
text: 'Export failed',
|
|
370
|
+
action: { text: 'Retry', onClick: retry }
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
3. Export your action in `actions/index.tsx`:
|
|
380
|
+
```typescript
|
|
381
|
+
export * from './exportCollection';
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
4. Configure the action in your JSON configuration:
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"id": "exportCollection", // MUST match the function name exactly
|
|
388
|
+
"type": "custom", // REQUIRED: Must be exactly "custom"
|
|
389
|
+
"label": "Export Collection", // Optional: Displayed text
|
|
390
|
+
"collection": {
|
|
391
|
+
"collectionId": "WixPets",
|
|
392
|
+
"entityTypeSource": "cms"
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
5. Register your action in the `PatternsWizardOverridesProvider`:
|
|
398
|
+
```typescript
|
|
399
|
+
import * as actions from './components/actions';
|
|
400
|
+
|
|
401
|
+
<PatternsWizardOverridesProvider value={{ actions }}>
|
|
402
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
403
|
+
</PatternsWizardOverridesProvider>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Key Points for Custom Collection Page Actions:
|
|
407
|
+
- The action `id` in the configuration MUST exactly match the function name exported from your actions folder
|
|
408
|
+
- The function name and file name should follow a consistent naming convention (e.g., camelCase)
|
|
409
|
+
- The implementation must be exported as a named export (not default export)
|
|
410
|
+
- The implementation must use the `CustomActionCollectionPageActionResolver` type
|
|
411
|
+
- Access collection context through `actionParams.collectionId`
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Custom Row Click Actions
|
|
416
|
+
|
|
417
|
+
In addition to page-level actions, you can also customize what happens when users click on individual rows in your collection table. By default, clicking a row navigates to the entity page, but you can override this behavior with custom logic.
|
|
418
|
+
|
|
419
|
+
**Before You Start:**
|
|
420
|
+
- Only configure `onRowClick` if you need custom behavior (e.g., opening modals, side panels, custom actions)
|
|
421
|
+
- If you just want navigation to entity page, don't configure `onRowClick` - it's the default behavior
|
|
422
|
+
- Once you configure `onRowClick`, you must provide a complete working implementation
|
|
423
|
+
|
|
424
|
+
### Configuration
|
|
425
|
+
|
|
426
|
+
Row click actions are configured at the table level using the `onRowClick` property:
|
|
427
|
+
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"type": "Table",
|
|
431
|
+
"table": {
|
|
432
|
+
"columns": [...],
|
|
433
|
+
"onRowClick": {
|
|
434
|
+
"id": "handleRowClick", // MUST match the function name exactly
|
|
435
|
+
"type": "custom", // REQUIRED: Must be exactly "custom"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Implementation Requirements
|
|
442
|
+
|
|
443
|
+
⚠️ **CRITICAL**: When you configure `onRowClick` in your JSON, you MUST provide a complete working implementation. The Auto Patterns framework cannot function without it.
|
|
444
|
+
|
|
445
|
+
Custom row click actions use the `CustomActionCollectionPageActionOnRowClickResolver` type and MUST return a `ResolvedAction` object with all required properties:
|
|
446
|
+
|
|
447
|
+
#### Required Return Object Structure:
|
|
448
|
+
```typescript
|
|
449
|
+
return {
|
|
450
|
+
label: string, // REQUIRED: Action label
|
|
451
|
+
icon: ReactElement, // REQUIRED: Icon component
|
|
452
|
+
onClick: () => void // REQUIRED: Click handler function
|
|
453
|
+
};
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
#### Complete Implementation Example:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
import { CustomActionCollectionPageActionOnRowClickResolver } from '@wix/auto-patterns';
|
|
460
|
+
import React from 'react';
|
|
461
|
+
import { More } from '@wix/wix-ui-icons-common';
|
|
462
|
+
|
|
463
|
+
// IMPORTANT: Function name MUST match the action id in your configuration
|
|
464
|
+
export const handleRowClick: CustomActionCollectionPageActionOnRowClickResolver = (params) => {
|
|
465
|
+
const { actionParams, sdk } = params;
|
|
466
|
+
const { item } = actionParams; // The clicked row's data
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
label: 'View Details', // REQUIRED
|
|
470
|
+
icon: <More />, // REQUIRED
|
|
471
|
+
onClick: () => { // REQUIRED
|
|
472
|
+
// Your custom row click logic here
|
|
473
|
+
console.log('Row clicked:', item);
|
|
474
|
+
|
|
475
|
+
// Example: Show a custom modal, perform an action, etc.
|
|
476
|
+
// You can access all SDK utilities here (see SDK Utilities section)
|
|
477
|
+
const optimisticActions = sdk.getOptimisticActions(sdk.collectionId);
|
|
478
|
+
const schema = sdk.getSchema(sdk.collectionId);
|
|
479
|
+
|
|
480
|
+
// Your custom logic...
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
};
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Common Use Cases and Complete Examples
|
|
487
|
+
|
|
488
|
+
#### 1. Opening a Side Panel Modal
|
|
489
|
+
|
|
490
|
+
This is a complete working example for opening a side panel when clicking a row:
|
|
491
|
+
|
|
492
|
+
**Step 1: Create the row click action** (`components/actions/openSidePanel.tsx`):
|
|
493
|
+
```typescript
|
|
494
|
+
import { CustomActionCollectionPageActionOnRowClickResolver } from '@wix/auto-patterns';
|
|
495
|
+
import React from 'react';
|
|
496
|
+
import { More } from '@wix/wix-ui-icons-common';
|
|
497
|
+
|
|
498
|
+
export const openSidePanel: CustomActionCollectionPageActionOnRowClickResolver = (params) => {
|
|
499
|
+
const { actionParams, sdk } = params;
|
|
500
|
+
const { item } = actionParams;
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
label: 'View Details',
|
|
504
|
+
icon: <More />,
|
|
505
|
+
onClick: () => {
|
|
506
|
+
// Open a custom modal with the item data
|
|
507
|
+
// You need to implement the modal opening mechanism
|
|
508
|
+
// This could be through a modal context, state management, etc.
|
|
509
|
+
console.log('Opening side panel for:', item);
|
|
510
|
+
|
|
511
|
+
// Example: Using a global modal state (you need to implement this)
|
|
512
|
+
// window.dispatchEvent(new CustomEvent('openSidePanel', { detail: item }));
|
|
513
|
+
|
|
514
|
+
// Or use a modal service/context that you've set up
|
|
515
|
+
// modalService.openSidePanel(item);
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
};
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Step 2: Configure in JSON**:
|
|
522
|
+
```json
|
|
523
|
+
{
|
|
524
|
+
"type": "Table",
|
|
525
|
+
"table": {
|
|
526
|
+
"onRowClick": {
|
|
527
|
+
"id": "openSidePanel",
|
|
528
|
+
"type": "custom"
|
|
529
|
+
},
|
|
530
|
+
"columns": [...]
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Step 3: Export and Register**:
|
|
536
|
+
```typescript
|
|
537
|
+
// components/actions/index.tsx
|
|
538
|
+
export * from './openSidePanel';
|
|
539
|
+
|
|
540
|
+
// page.tsx
|
|
541
|
+
import * as actions from './components/actions';
|
|
542
|
+
|
|
543
|
+
<PatternsWizardOverridesProvider value={{ actions }}>
|
|
544
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
545
|
+
</PatternsWizardOverridesProvider>
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
#### 2. Direct Data Manipulation
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
export const quickToggle: CustomActionCollectionPageActionOnRowClickResolver = (params) => {
|
|
552
|
+
const { actionParams, sdk } = params;
|
|
553
|
+
const { item } = actionParams;
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
label: 'Quick Toggle',
|
|
557
|
+
icon: <Toggle />,
|
|
558
|
+
onClick: () => {
|
|
559
|
+
const optimisticActions = sdk.getOptimisticActions(sdk.collectionId);
|
|
560
|
+
const schema = sdk.getSchema(sdk.collectionId);
|
|
561
|
+
|
|
562
|
+
// Example: Toggle a boolean field
|
|
563
|
+
const updatedItem = { ...item, isActive: !item.isActive };
|
|
564
|
+
|
|
565
|
+
optimisticActions.updateOne(updatedItem, {
|
|
566
|
+
submit: async (items) => schema.actions.update(items[0]),
|
|
567
|
+
successToast: `${item.name} toggled successfully`,
|
|
568
|
+
errorToast: (err, {retry}) => ({
|
|
569
|
+
text: 'Toggle failed',
|
|
570
|
+
action: { text: 'Retry', onClick: retry }
|
|
571
|
+
})
|
|
572
|
+
});
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Default vs Custom Behavior
|
|
579
|
+
|
|
580
|
+
**Default Behavior (when `onRowClick` is not configured):**
|
|
581
|
+
- Clicking a row automatically navigates to the entity page
|
|
582
|
+
- Uses the `entityPageId` configuration to determine the target page
|
|
583
|
+
- Passes the selected item's data to the entity page
|
|
584
|
+
|
|
585
|
+
**Custom Behavior (when `onRowClick` is configured):**
|
|
586
|
+
- Default navigation is **disabled**
|
|
587
|
+
- Your custom action function is executed instead
|
|
588
|
+
- You have complete control over the row click behavior
|
|
589
|
+
- You can still navigate to the entity page programmatically if needed using the SDK navigation utilities
|
|
590
|
+
|
|
591
|
+
### Key Points for Custom Row Click Actions:
|
|
592
|
+
- **MANDATORY IMPLEMENTATION**: If you configure `onRowClick` in JSON, you MUST provide a complete working implementation - the framework cannot function without it
|
|
593
|
+
- The action `id` in the configuration MUST exactly match the function name exported from your actions folder
|
|
594
|
+
- The implementation must use the `CustomActionCollectionPageActionOnRowClickResolver` type
|
|
595
|
+
- **Required Return Object**: Must return an object with `label`, `icon`, and `onClick` properties - all are required
|
|
596
|
+
- Access the clicked item's data through `actionParams.item`
|
|
597
|
+
- The implementation must be exported as a named export and registered in your `PatternsWizardOverridesProvider`
|
|
598
|
+
- When `onRowClick` is configured, the default navigation to entity page is completely disabled
|
|
599
|
+
- **Complete Setup Required**: You need to create the action file, export it in the index, and register it in the provider - missing any step will cause errors
|
|
600
|
+
|
|
601
|
+
## Validation Checklist for Collection Page Actions
|
|
602
|
+
|
|
603
|
+
✓ Every collection page must include a create action.
|
|
604
|
+
✓ `actions` is an optional property of `collectionPage`.
|
|
605
|
+
✓ `primaryActions` and `secondaryActions` (if defined) have a valid `type` ("action" or "menu").
|
|
606
|
+
✓ If `type: "action"`, `action.item` is a valid action item configuration.
|
|
607
|
+
✓ If `type: "menu"`, `menu.items` is an array of valid action item configurations that can include dividers.
|
|
608
|
+
✓ Each action item contains a unique `id`, and the full `collection` object (`collectionId`, `entityTypeSource: 'cms'`).
|
|
609
|
+
✓ Each action item has a supported `type` (`create`, `custom`) and its corresponding configuration block (e.g., `create` block for `type: "create"`).
|
|
610
|
+
✓ `create` actions specify a `create.page.id` that matches an existing `entityPage` ID in the configuration.
|
|
611
|
+
✓ `custom` actions (identified by their main `id`) correspond to an action resolver function name registered in the `actions` override.
|
|
612
|
+
✓ Divider actions use `{ "type": "divider" }` format and require no additional properties.
|
|
613
|
+
✓ If `onRowClick` is configured in table layout, it must have a valid `id` and `type: "custom"`.
|
|
614
|
+
✓ **CRITICAL**: Custom row click actions must have corresponding implementations registered in the `actions` override - configuration without implementation will cause errors.
|
|
615
|
+
✓ Custom row click action implementations must return an object with `label`, `icon`, and `onClick` properties - all are required.
|
|
616
|
+
✓ Custom row click action implementations must be exported as named exports and included in the actions index file.
|
|
617
|
+
✓ `onRowClick` is optional - when not configured, rows navigate to entity page by default.
|
|
618
|
+
✓ **IMPORTANT**: Configuring `onRowClick` completely disables default navigation - you must handle all row click logic in your custom implementation.
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## SDK Utilities
|
|
623
|
+
|
|
624
|
+
The `sdk` parameter provides access to Auto Patterns utilities and context. Available in custom actions across all action types (ActionCell, BulkActions, CollectionPage actions, and EntityPage Actions).
|
|
625
|
+
|
|
626
|
+
### Key SDK Utilities
|
|
627
|
+
The only functions exist in sdk are:
|
|
628
|
+
|
|
629
|
+
• **closeModal** - `closeModal(): void`
|
|
630
|
+
- Closes the currently open modal
|
|
631
|
+
- Example: `sdk.closeModal()` after saving or canceling
|
|
632
|
+
|
|
633
|
+
• **getOptimisticActions** - `getOptimisticActions(collectionId): OptimisticActions`
|
|
634
|
+
- Provides optimistic UI updates for immediate user feedback
|
|
635
|
+
- Supports create, update, delete operations with automatic rollback on failure
|
|
636
|
+
- Example: `sdk.getOptimisticActions(sdk.collectionId).updateOne(item, { ... })`
|
|
637
|
+
|
|
638
|
+
• **getSchema** - `getSchema(collectionId): SchemaConfig | undefined`
|
|
639
|
+
- Access to collection schema information (fields, types, validation)
|
|
640
|
+
- Useful for dynamic operations based on collection structure
|
|
641
|
+
- Example: `const schema = sdk.getSchema(sdk.collectionId)`
|
|
642
|
+
|
|
643
|
+
• **collectionId** - `string`
|
|
644
|
+
- Current collection context identifier
|
|
645
|
+
- Available in all action contexts for referencing the active collection
|
|
646
|
+
- Example: `sdk.collectionId` to get the current collection ID
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## OptimisticActions
|
|
651
|
+
|
|
652
|
+
Provides immediate UI updates with automatic server synchronization and error recovery.
|
|
653
|
+
|
|
654
|
+
### Usage Rules
|
|
655
|
+
|
|
656
|
+
**Use OptimisticActions for:**
|
|
657
|
+
- Data modification operations (create, update, delete)
|
|
658
|
+
- Operations requiring immediate visual feedback
|
|
659
|
+
|
|
660
|
+
**Do NOT use for:**
|
|
661
|
+
- Read-only operations
|
|
662
|
+
- Operations requiring server confirmation first
|
|
663
|
+
|
|
664
|
+
### Core Pattern
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
// Get instances from SDK (see SDK Utilities section)
|
|
668
|
+
const optimisticActions = sdk.getOptimisticActions(sdk.collectionId);
|
|
669
|
+
const schema = sdk.getSchema(sdk.collectionId);
|
|
670
|
+
|
|
671
|
+
optimisticActions.operation(items, {
|
|
672
|
+
submit: async (items) => schema.actions.serverMethod(items),
|
|
673
|
+
successToast: 'Success message',
|
|
674
|
+
errorToast: (err, {retry}) => ({ text: 'Error message', action: { text: 'Retry', onClick: retry }})
|
|
675
|
+
});
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Available Operations
|
|
679
|
+
|
|
680
|
+
#### Create Operations
|
|
681
|
+
- `createOne(item: T, params: OptimisticParams<T>): void`
|
|
682
|
+
- `createMany(items: T[], params: OptimisticParams<T>): void`
|
|
683
|
+
|
|
684
|
+
#### Update Operations
|
|
685
|
+
- `updateOne(item: T, params: OptimisticParams<T>): void`
|
|
686
|
+
- `updateMany(items: T[], params: OptimisticParams<T>): void`
|
|
687
|
+
- `updateAll(transformFn: (item: T) => Partial<T>, params: OptimisticParams<T>): void`
|
|
688
|
+
|
|
689
|
+
#### Delete Operations
|
|
690
|
+
- `deleteOne(item: T, params: OptimisticParams<T> & { showUndoToast: true }): void`
|
|
691
|
+
- `deleteMany(items: T[], params: OptimisticParams<T> & { showUndoToast: true }): void`
|
|
692
|
+
- `deleteAll(params: OptimisticParams<T> & { showUndoToast: true }): void`
|
|
693
|
+
|
|
694
|
+
### Type Definitions
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
interface OptimisticParams<T> {
|
|
698
|
+
submit: (items: T[]) => Promise<any>;
|
|
699
|
+
successToast: string | ToastConfig;
|
|
700
|
+
errorToast: (error: Error, actions: { retry: () => void }) => ToastConfig | string;
|
|
701
|
+
showUndoToast?: boolean; // Required: true for delete operations
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
interface ToastConfig {
|
|
705
|
+
text: string;
|
|
706
|
+
action?: { text: string; onClick: () => void };
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Validation Requirements
|
|
711
|
+
|
|
712
|
+
**Before using optimistic actions:**
|
|
713
|
+
- Verify `sdk.getOptimisticActions(collectionId)` returns valid instance
|
|
714
|
+
- Verify `sdk.getSchema(collectionId)` returns valid schema
|
|
715
|
+
- For delete operations: `showUndoToast: true` is mandatory
|
|
716
|
+
- All `submit` functions must return a Promise
|
|
717
|
+
|
|
718
|
+
**SDK Parameter:** Available in custom actions and modals. See SDK Utilities section for complete interface.
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## SchemaConfig Usage
|
|
723
|
+
|
|
724
|
+
SchemaConfig provides complete collection metadata and server actions. Essential for dynamic operations and accessing collection structure information.
|
|
725
|
+
|
|
726
|
+
### Key Properties
|
|
727
|
+
|
|
728
|
+
• **id** - `string`
|
|
729
|
+
- Collection identifier (e.g., "WixPets")
|
|
730
|
+
- Example: `schema.id === "WixPets"`
|
|
731
|
+
|
|
732
|
+
• **idField** - `string`
|
|
733
|
+
- Primary key field name (usually "_id")
|
|
734
|
+
- Required for all update/delete operations
|
|
735
|
+
- Example: `const id = item[schema.idField]`
|
|
736
|
+
|
|
737
|
+
• **displayField** - `string`
|
|
738
|
+
- Main field for displaying items (name, title, etc.)
|
|
739
|
+
- Used in UI components for item identification
|
|
740
|
+
- Example: `const label = item[schema.displayField]`
|
|
741
|
+
|
|
742
|
+
• **fields** - `Record<string, Field | undefined>`
|
|
743
|
+
- Complete field definitions with types and metadata
|
|
744
|
+
- Useful for dynamic form generation or validation
|
|
745
|
+
- Example: `schema.fields.name.type === 'TEXT'`
|
|
746
|
+
|
|
747
|
+
• **actions** - Server operation functions
|
|
748
|
+
- Pre-configured API calls for CRUD operations
|
|
749
|
+
- Use with optimistic actions for best UX
|
|
750
|
+
- Example: `await schema.actions.update(item)`
|
|
751
|
+
|
|
752
|
+
### Available Schema Actions
|
|
753
|
+
|
|
754
|
+
- schema.actions.create(item) // Create single item
|
|
755
|
+
- schema.actions.update(item) // Update single item
|
|
756
|
+
- schema.actions.delete(itemId) // Delete by ID
|
|
757
|
+
- schema.actions.bulkUpdate(updates) // Update multiple items
|
|
758
|
+
- schema.actions.bulkDelete(itemIds) // Delete multiple items
|
|
759
|
+
|
|
760
|
+
### Schema Validation Checklist
|
|
761
|
+
|
|
762
|
+
Before using schema in operations:
|
|
763
|
+
|
|
764
|
+
✓ Check if schema exists: `if (!schema) return;`
|
|
765
|
+
✓ Verify required fields exist on items
|
|
766
|
+
✓ Use `schema.idField` for ID operations
|
|
767
|
+
✓ Use `schema.displayField` for UI display
|
|
768
|
+
✓ Use `schema.actions` for server operations
|
|
769
|
+
|
|
770
|
+
### Common Usage Patterns
|
|
771
|
+
|
|
772
|
+
- **ActionCell**: Use `schema.actions.update()` or `schema.actions.delete()` for single item operations
|
|
773
|
+
- **BulkActions**: Use `schema.actions.bulkUpdate()` or `schema.actions.bulkDelete()` for multiple items
|
|
774
|
+
- **Dynamic UI**: Use `schema.fields` to build forms or validate data
|
|
775
|
+
- **Error Messages**: Use `schema.displayField` to create meaningful user feedback
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
## Filters Configuration Notes
|
|
780
|
+
|
|
781
|
+
To configure filters in a `collectionPage`, add a `filters` property inside the page's component configuration object. Each filter must reference a valid field by its `fieldId`, and the supported types are:
|
|
782
|
+
|
|
783
|
+
* `numberConfig`: used with fields of type `NUMBER`
|
|
784
|
+
* `dateConfig`: used with fields of type `DATETIME`
|
|
785
|
+
* `booleanConfig`: used with fields of type `BOOLEAN`
|
|
786
|
+
* `enumConfig`: used with fields of type `ARRAY` or `ARRAY_STRING`
|
|
787
|
+
|
|
788
|
+
### Enum Configuration Implementation
|
|
789
|
+
|
|
790
|
+
When implementing enum filters, you must ask the user to provide the possible option values. Never invent or assume enum values. Here's how to properly handle enumConfig:
|
|
791
|
+
|
|
792
|
+
#### Example: User-Provided Enum Implementation
|
|
793
|
+
|
|
794
|
+
1. First, collect the possible values from the user:
|
|
795
|
+
```
|
|
796
|
+
User requests: "I need a filter for pet types."
|
|
797
|
+
You ask: "What are the possible values for pet types that should be available in the filter?"
|
|
798
|
+
User responds: "dog, cat, bird, rabbit, fish"
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
2. Then, create the `enumConfig` structure:
|
|
802
|
+
```json
|
|
803
|
+
"enumConfig": {
|
|
804
|
+
"options": [
|
|
805
|
+
{ "value": "dog", "label": "Dog" },
|
|
806
|
+
{ "value": "cat", "label": "Cat" },
|
|
807
|
+
{ "value": "bird", "label": "Bird" },
|
|
808
|
+
{ "value": "rabbit", "label": "Rabbit" },
|
|
809
|
+
{ "value": "fish", "label": "Fish" }
|
|
810
|
+
],
|
|
811
|
+
"selectionMode": "multiple",
|
|
812
|
+
"optionType": "checkbox"
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
Notice how the `label` is derived from the `value` by capitalizing the first letter. The user's exact values become the `value` property.
|
|
817
|
+
|
|
818
|
+
### Grouping Filters with Section Title
|
|
819
|
+
|
|
820
|
+
* Filters can be grouped by sections using the `sectionTitle` property.
|
|
821
|
+
* If multiple filter items share the same `sectionTitle`, they will be displayed together in a grouped section in the UI.
|
|
822
|
+
* Filters without a `sectionTitle` will appear in a default section or be displayed individually.
|
|
823
|
+
* Grouping helps maintain clarity, especially when dealing with multiple filter options.
|
|
824
|
+
|
|
825
|
+
### Key Guidelines
|
|
826
|
+
|
|
827
|
+
* **openByDefault**: Automatically expands the filter accordion when the filters panel is opened.
|
|
828
|
+
* **tagLabel**: Specifies the label displayed in a Tag component on the table or grid once the filter is active. For example, if the tagLabel is "Age", the filter display might show: `Age: 7`.
|
|
829
|
+
* **maxInlineFilters**: Limits the number of filters shown inline in the table toolbar. Others are accessible via the panel. Default is 0.
|
|
830
|
+
* **dateConfig.mode**:
|
|
831
|
+
|
|
832
|
+
* `ONLY_PREDEFINED`: user can select only from preset options
|
|
833
|
+
* `ONLY_CUSTOM`: user must select a custom date range manually (no presets)
|
|
834
|
+
* `COMBINE`: both options available
|
|
835
|
+
* **dateConfig.presets** must be omitted if mode is `ONLY_CUSTOM`.
|
|
836
|
+
* **dateConfig.includeTime**: Controls whether time selection is also enabled alongside date (default is `true`).
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
# Custom Overrides
|
|
841
|
+
|
|
842
|
+
## ⚠️ Override Rules
|
|
843
|
+
|
|
844
|
+
- **Custom overrides are restricted to the defined areas only** - attempting to override or modify any other aspect of `AutoPatternsApp` is prohibited and can cause unexpected behavior
|
|
845
|
+
- **Always verify override implementation** - when implementing custom overrides, you MUST ensure they are correctly imported and passed to the `PatternsWizardOverridesProvider`
|
|
846
|
+
|
|
847
|
+
The `PatternsWizardOverridesProvider` allows you to inject custom code to override default behaviors or add additional functionality. Below are the areas where overrides can be applied:
|
|
848
|
+
|
|
849
|
+
> **Note:** These are the only areas where overrides are supported. Avoid attempting to override or modify other parts of the system, as this is not supported and may lead to unexpected behavior.
|
|
850
|
+
|
|
851
|
+
## Folder Structure Organization
|
|
852
|
+
|
|
853
|
+
All custom overrides (components, modals, actions, columns, and other customizations) should be created in a `components` folder inside your page directory, not in a global `/src/components` folder. This keeps page-specific customizations organized alongside their respective pages.
|
|
854
|
+
|
|
855
|
+
### Recommended Structure:
|
|
856
|
+
|
|
857
|
+
```
|
|
858
|
+
your-page/
|
|
859
|
+
├── page.tsx // Your main page component
|
|
860
|
+
├── MyCollectionConfig.patterns.json // Configuration file
|
|
861
|
+
└── components/ // Page-specific components folder
|
|
862
|
+
├── index.tsx // Exports all overrides for easy importing
|
|
863
|
+
├── actions/ // Custom actions
|
|
864
|
+
│ ├── index.tsx
|
|
865
|
+
│ └── myCustomAction.tsx
|
|
866
|
+
├── columns/ // Column overrides
|
|
867
|
+
│ ├── index.tsx
|
|
868
|
+
│ ├── name.ts
|
|
869
|
+
│ └── date.ts
|
|
870
|
+
└── customComponents/ // Custom entity page components
|
|
871
|
+
├── index.tsx
|
|
872
|
+
├── CustomNameField.tsx
|
|
873
|
+
└── InfoCard.tsx
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Importing Overrides in Your Page
|
|
877
|
+
|
|
878
|
+
In your page component, import from the local components folder:
|
|
879
|
+
|
|
880
|
+
```tsx
|
|
881
|
+
import * as modals from './components/modals';
|
|
882
|
+
import * as actions from './components/actions';
|
|
883
|
+
import * as columns from './components/columns';
|
|
884
|
+
import * as components from './components/customComponents';
|
|
885
|
+
|
|
886
|
+
<PatternsWizardOverridesProvider value={{ modals, actions, columns, components }}>
|
|
887
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
888
|
+
</PatternsWizardOverridesProvider>
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
### Important: Updating Index Files
|
|
892
|
+
|
|
893
|
+
**When adding any new implementation (action, modal, column, or component), you MUST update the corresponding `index.tsx` file to export your new implementation.** The main page component imports from these index files, so they serve as the central export point for each type of override.
|
|
894
|
+
|
|
895
|
+
For example:
|
|
896
|
+
- Adding a new action → Update `./components/actions/index.tsx`
|
|
897
|
+
- Adding a new modal → Update `./components/modals/index.tsx`
|
|
898
|
+
- Adding a new column override → Update `./components/columns/index.tsx`
|
|
899
|
+
- Adding a new custom component → Update `./components/customComponents/index.tsx`
|
|
900
|
+
|
|
901
|
+
Without updating the index files, your implementations won't be available to the `PatternsWizardOverridesProvider`.
|
|
902
|
+
|
|
903
|
+
## ⚠️ Common Override Mistakes to Avoid
|
|
904
|
+
|
|
905
|
+
- Attempting to override unsupported areas
|
|
906
|
+
- Invalid column rendering functions
|
|
907
|
+
- Missing index file exports for new implementations
|
|
908
|
+
- Incorrect import paths or naming mismatches
|
|
909
|
+
|
|
910
|
+
## Columns
|
|
911
|
+
|
|
912
|
+
Each column in the table has a default rendering based on its field type. You can override this rendering by providing a custom function for the `column.id`. This allows you to customize how specific columns are displayed.
|
|
913
|
+
|
|
914
|
+
**Enhanced Column Overrides**: Column override can receive both the individual column `value` and the entire `row` data, enabling you to create complex columns that combine multiple field values from the same row.
|
|
915
|
+
|
|
916
|
+
### Function Signature
|
|
917
|
+
|
|
918
|
+
```typescript
|
|
919
|
+
function columnOverride({ value, row }) {
|
|
920
|
+
// value: The individual column value
|
|
921
|
+
// row: The entire row object containing all field values
|
|
922
|
+
return <YourCustomRendering />;
|
|
923
|
+
}
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### Understanding Row Data
|
|
927
|
+
|
|
928
|
+
**Important**: The `row` object contains all field values from the entity, where each property corresponds to a **field ID** from the collection schema. To access specific field values, use the exact field ID as defined in your collection schema.
|
|
929
|
+
|
|
930
|
+
For example, if your collection schema has these fields:
|
|
931
|
+
```json
|
|
932
|
+
{
|
|
933
|
+
"fields": [
|
|
934
|
+
{ "key": "name", "displayName": "Pet Name", "type": "TEXT" },
|
|
935
|
+
{ "key": "age", "displayName": "Age", "type": "NUMBER" },
|
|
936
|
+
{ "key": "isVaccinated", "displayName": "Vaccinated", "type": "BOOLEAN" },
|
|
937
|
+
{ "key": "lastActivity", "displayName": "Last Activity", "type": "DATETIME" }
|
|
938
|
+
]
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Then in your column override, you access these values using the field IDs:
|
|
943
|
+
```typescript
|
|
944
|
+
export function myColumn({ value, row }) {
|
|
945
|
+
// Access field values using their schema field IDs
|
|
946
|
+
const petName = row.name; // "name" field ID
|
|
947
|
+
const petAge = row.age; // "age" field ID
|
|
948
|
+
const isVaccinated = row.isVaccinated; // "isVaccinated" field ID
|
|
949
|
+
const lastActivity = row.lastActivity; // "lastActivity" field ID
|
|
950
|
+
|
|
951
|
+
return <YourCustomRendering />;
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### Use Cases for Row Data Access
|
|
956
|
+
|
|
957
|
+
1. **Complex Display Columns**: Combine multiple fields into a single display (e.g., "Name (Age)" combining name and age fields)
|
|
958
|
+
2. **Conditional Rendering**: Show different content based on other field values in the same row
|
|
959
|
+
3. **Calculated Columns**: Create computed values using multiple row fields
|
|
960
|
+
4. **Cross-Field Validation Display**: Show validation status based on relationships between fields
|
|
961
|
+
|
|
962
|
+
### Example: Defining and Using Column Overrides
|
|
963
|
+
|
|
964
|
+
In `components/columns/name.tsx`:
|
|
965
|
+
|
|
966
|
+
```ts
|
|
967
|
+
import React from 'react';
|
|
968
|
+
|
|
969
|
+
export function name({ value, row }) {
|
|
970
|
+
// Simple value formatting
|
|
971
|
+
return <strong>{value}</strong>;
|
|
972
|
+
}
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
In `components/columns/petInfo.tsx`:
|
|
976
|
+
|
|
977
|
+
```ts
|
|
978
|
+
import React from 'react';
|
|
979
|
+
import { Box, Text } from '@wix/design-system';
|
|
980
|
+
|
|
981
|
+
export function petInfo({ value, row }) {
|
|
982
|
+
// Complex column combining multiple row values
|
|
983
|
+
return (
|
|
984
|
+
<Box direction="vertical" gap={1}>
|
|
985
|
+
<Text weight="bold">{row.name}</Text>
|
|
986
|
+
<Text size="small" skin="disabled">
|
|
987
|
+
{row.age} years old • {row.type}
|
|
988
|
+
</Text>
|
|
989
|
+
{row.isVaccinated && (
|
|
990
|
+
<Text size="tiny" skin="success">✓ Vaccinated</Text>
|
|
991
|
+
)}
|
|
992
|
+
</Box>
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
In `components/columns/status.tsx`:
|
|
998
|
+
|
|
999
|
+
```ts
|
|
1000
|
+
import React from 'react';
|
|
1001
|
+
import { Badge } from '@wix/design-system';
|
|
1002
|
+
|
|
1003
|
+
export function status({ value, row }) {
|
|
1004
|
+
// Conditional rendering based on multiple row fields
|
|
1005
|
+
if (row.isVaccinated && row.age > 1) {
|
|
1006
|
+
return <Badge skin="success">Ready for Adoption</Badge>;
|
|
1007
|
+
} else if (!row.isVaccinated) {
|
|
1008
|
+
return <Badge skin="warning">Needs Vaccination</Badge>;
|
|
1009
|
+
} else {
|
|
1010
|
+
return <Badge skin="neutral">Too Young</Badge>;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
In `components/columns/fullName.tsx`:
|
|
1016
|
+
|
|
1017
|
+
```ts
|
|
1018
|
+
import React from 'react';
|
|
1019
|
+
|
|
1020
|
+
export function fullName({ value, row }) {
|
|
1021
|
+
// Calculated column using multiple fields
|
|
1022
|
+
return `${row.name} (owned by ${row.owner})`;
|
|
1023
|
+
}
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
In `components/columns/date.tsx`:
|
|
1027
|
+
|
|
1028
|
+
```ts
|
|
1029
|
+
import React from 'react';
|
|
1030
|
+
|
|
1031
|
+
export function date({ value, row }) {
|
|
1032
|
+
// Access to other row data for enhanced date formatting
|
|
1033
|
+
const isRecent = row.lastActivity && new Date(row.lastActivity) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
1034
|
+
|
|
1035
|
+
return (
|
|
1036
|
+
<span style={{ color: isRecent ? 'green' : 'inherit' }}>
|
|
1037
|
+
<em>{new Date(value).toLocaleDateString()}</em>
|
|
1038
|
+
{isRecent && ' (Recent)'}
|
|
1039
|
+
</span>
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
In `components/columns/index.tsx`:
|
|
1045
|
+
|
|
1046
|
+
```ts
|
|
1047
|
+
export * from './name';
|
|
1048
|
+
export * from './petInfo';
|
|
1049
|
+
export * from './status';
|
|
1050
|
+
export * from './fullName';
|
|
1051
|
+
export * from './date';
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
**Important:** Every time you add a new column override file, you must add a corresponding export line to this `index.tsx` file. For example, if you create `price.tsx`, you must add `export * from './price';` to the index file.
|
|
1055
|
+
|
|
1056
|
+
In the `PatternsWizardOverridesProvider`:
|
|
1057
|
+
|
|
1058
|
+
```tsx
|
|
1059
|
+
import * as columns from './components/columns';
|
|
1060
|
+
|
|
1061
|
+
<PatternsWizardOverridesProvider value={{ columns }}>
|
|
1062
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
1063
|
+
</PatternsWizardOverridesProvider>
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
### Visual Representation
|
|
1067
|
+
|
|
1068
|
+
```
|
|
1069
|
+
your-page/
|
|
1070
|
+
└── components/
|
|
1071
|
+
└── columns/
|
|
1072
|
+
├── index.tsx // Exports all column overrides
|
|
1073
|
+
├── name.tsx // Simple value formatting
|
|
1074
|
+
├── petInfo.tsx // Complex multi-field column
|
|
1075
|
+
├── status.tsx // Conditional rendering column
|
|
1076
|
+
├── fullName.tsx // Calculated column
|
|
1077
|
+
└── date.tsx // Enhanced formatting with row context
|
|
1078
|
+
|
|
1079
|
+
PatternsWizardOverridesProvider
|
|
1080
|
+
└── value.columns
|
|
1081
|
+
├── name
|
|
1082
|
+
├── petInfo
|
|
1083
|
+
├── status
|
|
1084
|
+
├── fullName
|
|
1085
|
+
└── date
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
### Key Benefits of Row Data Access
|
|
1089
|
+
|
|
1090
|
+
1. **Reduced Configuration Complexity**: Instead of adding multiple columns, create one complex column that shows related information
|
|
1091
|
+
2. **Better User Experience**: Present related data together in a logical, readable format
|
|
1092
|
+
3. **Dynamic Content**: Show different content based on the state of other fields
|
|
1093
|
+
4. **Data Relationships**: Highlight relationships between different field values in the same entity
|
|
1094
|
+
|
|
1095
|
+
### Important Guidelines
|
|
1096
|
+
|
|
1097
|
+
- **Performance**: Remember that column functions are called for every row, so keep calculations lightweight
|
|
1098
|
+
- **Consistency**: When using row data, ensure the column header accurately represents what's displayed
|
|
1099
|
+
- **Accessibility**: Maintain proper semantic structure when combining multiple values
|
|
1100
|
+
|
|
1101
|
+
## Components
|
|
1102
|
+
|
|
1103
|
+
Components allow you to create custom rendering for specific elements in the entity page. Each component has a unique `componentId` that corresponds to the ID specified in the layout configuration.
|
|
1104
|
+
|
|
1105
|
+
The custom components receive two essential props:
|
|
1106
|
+
|
|
1107
|
+
1. **form**: An instance of `UseFormReturn` from react-hook-form (re-exported through `@wix/auto-patterns/form`), giving you access to the form control, methods, and state.
|
|
1108
|
+
2. **entity**: A key-value object where keys are field IDs from the collection schema and values are the current field values, providing access to the entity's data.
|
|
1109
|
+
|
|
1110
|
+
Custom components can serve two main purposes:
|
|
1111
|
+
|
|
1112
|
+
### 1. Standalone Custom Components
|
|
1113
|
+
|
|
1114
|
+
These components can display custom UI elements like notifications, information cards, or any other custom content that isn't directly tied to specific fields. These are useful for adding unique UI elements that enhance the entity page experience.
|
|
1115
|
+
|
|
1116
|
+
### 2. Field Rendering Overrides
|
|
1117
|
+
|
|
1118
|
+
You can use custom components to override the default rendering of one or more fields. This allows you to:
|
|
1119
|
+
- Apply custom validation logic
|
|
1120
|
+
- Create custom input components
|
|
1121
|
+
- Combine multiple fields into a single UI component
|
|
1122
|
+
- Add field-specific functionality not available in the default renderers
|
|
1123
|
+
|
|
1124
|
+
### Using the useController Hook for Field Overrides
|
|
1125
|
+
|
|
1126
|
+
When creating field overrides, use the `useController` hook from `@wix/auto-patterns/form` (a re-export of react-hook-form's hook) to connect your custom component to the form state:
|
|
1127
|
+
|
|
1128
|
+
```tsx
|
|
1129
|
+
import { useController } from '@wix/auto-patterns/form'; // Always import from this path, not react-hook-form
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
The hook requires:
|
|
1133
|
+
- **name**: The field name you want to edit (should match the schema field ID)
|
|
1134
|
+
- **control**: Retrieved from `form.control`
|
|
1135
|
+
- **defaultValue**: Set from `entity?.[fieldId]` when it exists
|
|
1136
|
+
|
|
1137
|
+
### Example: Defining a Custom Component
|
|
1138
|
+
|
|
1139
|
+
Here's an example of a custom component that overrides the rendering of the "name" field:
|
|
1140
|
+
|
|
1141
|
+
```tsx
|
|
1142
|
+
import React, { FC } from 'react';
|
|
1143
|
+
import { Box, Card, FormField, Input, Text } from '@wix/design-system';
|
|
1144
|
+
import { useController } from '@wix/auto-patterns/form';
|
|
1145
|
+
import { CustomComponentProps } from '@wix/auto-patterns/types';
|
|
1146
|
+
|
|
1147
|
+
export const customNameField: FC<CustomComponentProps> = ({ form, entity }) => {
|
|
1148
|
+
// Create a controller for the name field
|
|
1149
|
+
const controller = useController({
|
|
1150
|
+
name: 'name', // Field ID from the schema
|
|
1151
|
+
control: form.control, // Form control
|
|
1152
|
+
defaultValue: entity?.name, // Default value from entity
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
return (
|
|
1156
|
+
<FormField
|
|
1157
|
+
label="Name"
|
|
1158
|
+
required={true}
|
|
1159
|
+
charCount={100}
|
|
1160
|
+
// Connect field state to UI
|
|
1161
|
+
status={controller.fieldState.invalid ? 'error' : undefined}
|
|
1162
|
+
statusMessage={controller.fieldState.error?.message}
|
|
1163
|
+
dataHook={`short-text-${controller.field.name}`}
|
|
1164
|
+
>
|
|
1165
|
+
<Input
|
|
1166
|
+
// Connect field value and onChange
|
|
1167
|
+
value={controller.field.value}
|
|
1168
|
+
onChange={(e) => controller.field.onChange(e.target.value)}
|
|
1169
|
+
dataHook={`short-text-${controller.field.name}`}
|
|
1170
|
+
/>
|
|
1171
|
+
</FormField>
|
|
1172
|
+
);
|
|
1173
|
+
};
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
### Example: Standalone Component (Not Field-Specific)
|
|
1178
|
+
|
|
1179
|
+
Custom components can also be used to add UI elements not tied to specific fields:
|
|
1180
|
+
|
|
1181
|
+
```tsx
|
|
1182
|
+
import React, { FC } from 'react';
|
|
1183
|
+
import { Box, Card, Text, Button } from '@wix/design-system';
|
|
1184
|
+
import { CustomComponentProps } from '@wix/auto-patterns/types';
|
|
1185
|
+
|
|
1186
|
+
export const infoCard: FC<CustomComponentProps> = ({ entity }) => {
|
|
1187
|
+
return (
|
|
1188
|
+
<Card>
|
|
1189
|
+
<Card.Content>
|
|
1190
|
+
<Box direction="vertical" gap={2}>
|
|
1191
|
+
<Text weight="bold">Important Information</Text>
|
|
1192
|
+
<Text>
|
|
1193
|
+
This custom component can display additional information or functionality
|
|
1194
|
+
that isn't directly tied to a specific field.
|
|
1195
|
+
</Text>
|
|
1196
|
+
{entity?.isVaccinated ? (
|
|
1197
|
+
<Text skin="success">This pet is vaccinated</Text>
|
|
1198
|
+
) : (
|
|
1199
|
+
<Text skin="warning">This pet needs vaccination</Text>
|
|
1200
|
+
)}
|
|
1201
|
+
</Box>
|
|
1202
|
+
</Card.Content>
|
|
1203
|
+
</Card>
|
|
1204
|
+
);
|
|
1205
|
+
};
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
### Connecting Components in the Provider
|
|
1209
|
+
|
|
1210
|
+
In your main page file, import and provide these components via the `PatternsWizardOverridesProvider`:
|
|
1211
|
+
|
|
1212
|
+
```tsx
|
|
1213
|
+
import * as components from './components';
|
|
1214
|
+
|
|
1215
|
+
<PatternsWizardOverridesProvider value={{ components }}>
|
|
1216
|
+
<AutoPatternsApp configuration={config as AppConfig} />
|
|
1217
|
+
</PatternsWizardOverridesProvider>
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
### Important Guidelines for Custom Components
|
|
1221
|
+
|
|
1222
|
+
1. **Always import from `@wix/auto-patterns/form`** instead of directly from `react-hook-form`
|
|
1223
|
+
2. **Follow react-hook-form best practices** - the underlying infrastructure is built on react-hook-form
|
|
1224
|
+
3. **Handle form state properly**:
|
|
1225
|
+
- Use `controller.fieldState.invalid` for error state
|
|
1226
|
+
- Use `controller.fieldState.error?.message` for error messages
|
|
1227
|
+
- Connect `controller.field.value` to input values
|
|
1228
|
+
- Use `controller.field.onChange` for change handlers
|
|
1229
|
+
4. **Component rendering**:
|
|
1230
|
+
- Choose appropriate design-system components based on the field type
|
|
1231
|
+
- For text fields: `Input`
|
|
1232
|
+
- For multi-line text: `InputArea`
|
|
1233
|
+
- For checkboxes: `Checkbox`
|
|
1234
|
+
- For dates: `DatePicker`
|
|
1235
|
+
- For dropdowns: `Dropdown`
|
|
1236
|
+
|
|
1237
|
+
**Important:** Every time you create a new custom component, you must add a corresponding export line to the `./components/customComponents/index.tsx` file. For example, if you create `StatusIndicator.tsx`, you must add `export * from './StatusIndicator';` to the index file.
|
|
1238
|
+
|
|
1239
|
+
### Understanding Reactivity in Custom Components
|
|
1240
|
+
|
|
1241
|
+
5. **Reactivity and field value changes (IMPORTANT)**:
|
|
1242
|
+
- **NEVER rely on the `entity` object for reactive UI** - it is not reactive to form changes
|
|
1243
|
+
- For any reactive UI that needs to respond to field value changes in real-time:
|
|
1244
|
+
- Use `form.watch('fieldName')` to observe field changes reactively
|
|
1245
|
+
- Use `useController` hook when you need both read and write access to a field
|
|
1246
|
+
|
|
1247
|
+
#### Common Reactivity Issues and Solutions
|
|
1248
|
+
|
|
1249
|
+
##### Example: Conditional Display Based on Field Value
|
|
1250
|
+
|
|
1251
|
+
```tsx
|
|
1252
|
+
// ❌ INCORRECT APPROACH (Non-reactive)
|
|
1253
|
+
const CustomComponent: FC<CustomComponentProps> = ({ form, entity }) => {
|
|
1254
|
+
// This won't update when the user changes the name in the form
|
|
1255
|
+
const showSpecialMessage = entity?.name === 'special';
|
|
1256
|
+
|
|
1257
|
+
return (
|
|
1258
|
+
<Box>
|
|
1259
|
+
<Input
|
|
1260
|
+
value={form.getValues('name')}
|
|
1261
|
+
onChange={(e) => form.setValue('name', e.target.value)}
|
|
1262
|
+
/>
|
|
1263
|
+
|
|
1264
|
+
{showSpecialMessage && (
|
|
1265
|
+
<Text>Special message for special name!</Text>
|
|
1266
|
+
)}
|
|
1267
|
+
</Box>
|
|
1268
|
+
);
|
|
1269
|
+
};
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
```tsx
|
|
1273
|
+
// ✅ CORRECT APPROACH (Reactive)
|
|
1274
|
+
const CustomComponent: FC<CustomComponentProps> = ({ form, entity }) => {
|
|
1275
|
+
// This WILL update whenever the name field changes
|
|
1276
|
+
const nameValue = form.watch('name');
|
|
1277
|
+
const showSpecialMessage = nameValue === 'special';
|
|
1278
|
+
|
|
1279
|
+
return (
|
|
1280
|
+
<Box>
|
|
1281
|
+
<Input
|
|
1282
|
+
value={nameValue}
|
|
1283
|
+
onChange={(e) => form.setValue('name', e.target.value)}
|
|
1284
|
+
/>
|
|
1285
|
+
|
|
1286
|
+
{showSpecialMessage && (
|
|
1287
|
+
<Text>Special message for special name!</Text>
|
|
1288
|
+
)}
|
|
1289
|
+
</Box>
|
|
1290
|
+
);
|
|
1291
|
+
};
|
|
1292
|
+
```
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
##### When to Use the Entity Object
|
|
1296
|
+
|
|
1297
|
+
The `entity` object is useful for:
|
|
1298
|
+
- Setting initial values
|
|
1299
|
+
- Accessing read-only data that doesn't change
|
|
1300
|
+
- Comparing form state with original values (e.g., detecting if changes were made)
|
|
1301
|
+
- Initializing form fields with useController's defaultValue parameter
|
|
1302
|
+
|
|
1303
|
+
```tsx
|
|
1304
|
+
// Example: Proper use of entity object with useController
|
|
1305
|
+
const CustomComponent: FC<CustomComponentProps> = ({ form, entity }) => {
|
|
1306
|
+
// Use entity for initialization via defaultValue
|
|
1307
|
+
const controller = useController({
|
|
1308
|
+
name: 'name', // Field ID from the schema
|
|
1309
|
+
control: form.control,
|
|
1310
|
+
defaultValue: entity?.name // Initialize from entity
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
// Use watch for reactive updates
|
|
1314
|
+
const currentName = controller.field.value;
|
|
1315
|
+
const hasChanges = entity?.name !== currentName;
|
|
1316
|
+
|
|
1317
|
+
return (
|
|
1318
|
+
<Box>
|
|
1319
|
+
<FormField label="Name">
|
|
1320
|
+
<Input
|
|
1321
|
+
value={currentName}
|
|
1322
|
+
onChange={(e) => controller.field.onChange(e.target.value)}
|
|
1323
|
+
/>
|
|
1324
|
+
</FormField>
|
|
1325
|
+
{hasChanges && (
|
|
1326
|
+
<Text size="small">Original value: {entity?.name}</Text>
|
|
1327
|
+
)}
|
|
1328
|
+
</Box>
|
|
1329
|
+
);
|
|
1330
|
+
};
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
### Visual Representation
|
|
1334
|
+
|
|
1335
|
+
```
|
|
1336
|
+
your-page/
|
|
1337
|
+
└── components/
|
|
1338
|
+
├── index.tsx // Exports all component overrides
|
|
1339
|
+
├── customNameField.tsx // Field override component
|
|
1340
|
+
├── combinedNameFields.tsx // Multiple fields override
|
|
1341
|
+
└── infoCard.tsx // Standalone component
|
|
1342
|
+
|
|
1343
|
+
PatternsWizardOverridesProvider
|
|
1344
|
+
└── value.components
|
|
1345
|
+
├── customNameField
|
|
1346
|
+
├── combinedNameFields
|
|
1347
|
+
└── infoCard
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
By using these component overrides, you can tailor the behavior and appearance of your `AutoPatternsApp` to meet specific requirements beyond what the default rendering provides.
|
|
1351
|
+
|
|
1352
|
+
---
|