@qoretechnologies/reqraft 0.10.9 → 0.10.11

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 (38) hide show
  1. package/.claude/CLAUDE.md +39 -18
  2. package/package.json +1 -1
  3. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
  4. package/src/components/form/engine/FormEngine.stories.tsx +654 -32
  5. package/src/components/form/engine/FormEngineRemote.stories.tsx +48 -0
  6. package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
  7. package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +40 -0
  8. package/src/components/form/expressions/ExpressionField.stories.tsx +74 -2
  9. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +187 -0
  10. package/src/components/form/fields/Field.stories.tsx +40 -0
  11. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
  12. package/src/components/form/fields/auto/AutoFormField.stories.tsx +136 -0
  13. package/src/components/form/fields/binary/Binary.stories.tsx +32 -0
  14. package/src/components/form/fields/boolean/Boolean.stories.tsx +24 -0
  15. package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
  16. package/src/components/form/fields/color/Color.stories.tsx +30 -0
  17. package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
  18. package/src/components/form/fields/date/Date.stories.tsx +22 -0
  19. package/src/components/form/fields/file/File.stories.tsx +48 -0
  20. package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
  21. package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
  22. package/src/components/form/fields/number/Number.stories.tsx +31 -0
  23. package/src/components/form/fields/object/Object.stories.tsx +88 -0
  24. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
  25. package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
  26. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
  27. package/src/components/form/fields/select/Select.stories.tsx +104 -0
  28. package/src/components/form/fields/string/String.stories.tsx +16 -0
  29. package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
  30. package/src/components/form/fields/url/Url.stories.tsx +24 -0
  31. package/src/components/log/Log.stories.tsx +96 -0
  32. package/src/components/menu/Menu.stories.tsx +54 -0
  33. package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +14 -0
  34. package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
  35. package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
  36. package/src/hooks/useStorage/useStorage.stories.tsx +30 -0
  37. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
  38. package/src/stores/currentUser/currentUser.stories.tsx +17 -0
@@ -30,6 +30,14 @@ export default meta;
30
30
  type Story = StoryObj<typeof meta>;
31
31
 
32
32
  export const Empty: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story:
37
+ 'Renders the File field with no value — the "Click or drop files here to upload" drop zone is visible.',
38
+ },
39
+ },
40
+ },
33
41
  async play({ canvasElement }) {
34
42
  const canvas = within(canvasElement);
35
43
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -44,6 +52,14 @@ export const WithValue: Story = {
44
52
  size: 28736,
45
53
  },
46
54
  },
55
+ parameters: {
56
+ docs: {
57
+ description: {
58
+ story:
59
+ 'Renders the File field pre-populated with a PDF value — the file name is displayed in place of the drop zone.',
60
+ },
61
+ },
62
+ },
47
63
  async play({ canvasElement }) {
48
64
  const canvas = within(canvasElement);
49
65
  await expect(canvas.getByText('report.pdf')).toBeInTheDocument();
@@ -58,6 +74,14 @@ export const WithImageValue: Story = {
58
74
  size: 1024,
59
75
  },
60
76
  },
77
+ parameters: {
78
+ docs: {
79
+ description: {
80
+ story:
81
+ 'Renders the File field pre-populated with a PNG image value — the file name and an inline preview thumbnail are shown.',
82
+ },
83
+ },
84
+ },
61
85
  async play({ canvasElement }) {
62
86
  const canvas = within(canvasElement);
63
87
  await expect(canvas.getByText('photo.png')).toBeInTheDocument();
@@ -75,6 +99,14 @@ export const WithAcceptedExtensions: Story = {
75
99
  },
76
100
  },
77
101
  },
102
+ parameters: {
103
+ docs: {
104
+ description: {
105
+ story:
106
+ 'Renders the File field with an accept option limiting uploads to PNG, JPEG and PDF — the allowed extensions are listed below the drop zone.',
107
+ },
108
+ },
109
+ },
78
110
  async play({ canvasElement }) {
79
111
  const canvas = within(canvasElement);
80
112
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -86,6 +118,14 @@ export const Multiple: Story = {
86
118
  args: {
87
119
  multiple: true,
88
120
  },
121
+ parameters: {
122
+ docs: {
123
+ description: {
124
+ story:
125
+ 'Renders the File field with multiple-selection enabled — the underlying input carries the multiple attribute and the drop zone stays visible after files are added.',
126
+ },
127
+ },
128
+ },
89
129
  async play({ canvasElement }) {
90
130
  const canvas = within(canvasElement);
91
131
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -105,6 +145,14 @@ export const WithBuildTab: Story = {
105
145
  content: { type: 'string', ui_type: 'string', display_name: 'Content', required: true },
106
146
  },
107
147
  },
148
+ parameters: {
149
+ docs: {
150
+ description: {
151
+ story:
152
+ 'Renders the File field with an argSchema — a "Build a File" tab appears alongside the "Upload a File" drop zone so the user can synthesize a file from schema fields.',
153
+ },
154
+ },
155
+ },
108
156
  async play({ canvasElement }) {
109
157
  const canvas = within(canvasElement);
110
158
  await expect(canvas.getByText('Upload a File')).toBeInTheDocument();
@@ -39,6 +39,14 @@ export const WithValue: Story = {
39
39
  args: {
40
40
  value: 'Hello world',
41
41
  },
42
+ parameters: {
43
+ docs: {
44
+ description: {
45
+ story:
46
+ 'Renders the LongString field pre-populated with "Hello world". Clicking clear empties the textarea and fires onChange; typing new content updates the value after the debounce.',
47
+ },
48
+ },
49
+ },
42
50
  async play({ canvasElement, args, step }) {
43
51
  const canvas = within(canvasElement);
44
52
  const textarea = canvas.getByLabelText('LongString');
@@ -66,6 +74,14 @@ export const WithValue: Story = {
66
74
  };
67
75
 
68
76
  export const Empty: Story = {
77
+ parameters: {
78
+ docs: {
79
+ description: {
80
+ story:
81
+ 'Renders the LongString field with no value. Typing into the empty textarea updates the value and fires onChange after the debounce.',
82
+ },
83
+ },
84
+ },
69
85
  async play({ canvasElement, args }) {
70
86
  const canvas = within(canvasElement);
71
87
  const textarea = canvas.getByLabelText('LongString');
@@ -86,6 +102,14 @@ export const Disabled: Story = {
86
102
  value: 'Cannot edit this',
87
103
  disabled: true,
88
104
  },
105
+ parameters: {
106
+ docs: {
107
+ description: {
108
+ story:
109
+ 'Renders the LongString field with a value but disabled — the textarea shows the value and rejects further edits.',
110
+ },
111
+ },
112
+ },
89
113
  async play({ canvasElement }) {
90
114
  const canvas = within(canvasElement);
91
115
  const textarea = canvas.getByLabelText('LongString');
@@ -35,6 +35,14 @@ export const Default: Story = {
35
35
  args: {
36
36
  'aria-label': 'MarkdownEditor',
37
37
  },
38
+ parameters: {
39
+ docs: {
40
+ description: {
41
+ story:
42
+ 'Renders the Markdown field pre-populated with the sample markdown fixture — the editor and the live preview render side by side.',
43
+ },
44
+ },
45
+ },
38
46
  async play({ canvasElement, args }) {
39
47
  const canvas = within(canvasElement);
40
48
  const editor = canvas.getByLabelText('MarkdownEditor');
@@ -38,6 +38,14 @@ export const Integer: Story = {
38
38
  value: 42,
39
39
  type: 'int',
40
40
  },
41
+ parameters: {
42
+ docs: {
43
+ description: {
44
+ story:
45
+ 'Renders the Number field in integer mode with 42 pre-populated. Typing a new integer updates the value and fires onChange after the debounce.',
46
+ },
47
+ },
48
+ },
41
49
  async play({ args, canvasElement }) {
42
50
  const canvas = within(canvasElement);
43
51
  const input = canvas.getByLabelText('Number');
@@ -58,6 +66,14 @@ export const Float: Story = {
58
66
  value: 3.14,
59
67
  type: 'float',
60
68
  },
69
+ parameters: {
70
+ docs: {
71
+ description: {
72
+ story:
73
+ 'Renders the Number field in float mode with 3.14 pre-populated — the input uses a 0.1 step and accepts decimals.',
74
+ },
75
+ },
76
+ },
61
77
  async play({ args, canvasElement }) {
62
78
  const canvas = within(canvasElement);
63
79
  const input = canvas.getByLabelText('Number');
@@ -76,6 +92,13 @@ export const Empty: Story = {
76
92
  args: {
77
93
  type: 'int',
78
94
  },
95
+ parameters: {
96
+ docs: {
97
+ description: {
98
+ story: 'Renders the Number field in integer mode with no value — the input mounts empty.',
99
+ },
100
+ },
101
+ },
79
102
  async play({ canvasElement }) {
80
103
  const canvas = within(canvasElement);
81
104
  const input = canvas.getByLabelText('Number');
@@ -91,6 +114,14 @@ export const Disabled: Story = {
91
114
  type: 'int',
92
115
  disabled: true,
93
116
  },
117
+ parameters: {
118
+ docs: {
119
+ description: {
120
+ story:
121
+ 'Renders the Number field with a value but disabled — the input shows the value and rejects further edits.',
122
+ },
123
+ },
124
+ },
94
125
  async play({ canvasElement }) {
95
126
  const canvas = within(canvasElement);
96
127
  const input = canvas.getByLabelText('Number');
@@ -50,6 +50,14 @@ export const Object: Story = {
50
50
  dataType: 'json',
51
51
  resultDataType: 'json',
52
52
  },
53
+ parameters: {
54
+ docs: {
55
+ description: {
56
+ story:
57
+ 'Renders the Object field in JSON object mode with no value. Clicking "New Object" seeds an empty JSON object in the editor.',
58
+ },
59
+ },
60
+ },
53
61
  async play() {
54
62
  await testsWaitForText('New Object');
55
63
  await testsClickButton({ label: 'New Object' });
@@ -64,6 +72,14 @@ export const List: Story = {
64
72
  dataType: 'yaml',
65
73
  resultDataType: 'yaml',
66
74
  },
75
+ parameters: {
76
+ docs: {
77
+ description: {
78
+ story:
79
+ 'Renders the Object field in YAML list mode with no value. Clicking "New List" seeds an empty list in the editor.',
80
+ },
81
+ },
82
+ },
67
83
  async play() {
68
84
  await testsWaitForText('New List');
69
85
  await testsClickButton({ label: 'New List' });
@@ -79,6 +95,14 @@ export const Disabled: Story = {
79
95
  resultDataType: 'native',
80
96
  disabled: true,
81
97
  },
98
+ parameters: {
99
+ docs: {
100
+ description: {
101
+ story:
102
+ 'Renders the Object field in native-array mode but disabled — the field is visible and non-interactive.',
103
+ },
104
+ },
105
+ },
82
106
  };
83
107
 
84
108
  export const Small: Story = {
@@ -96,6 +120,14 @@ export const Small: Story = {
96
120
  false,
97
121
  ],
98
122
  },
123
+ parameters: {
124
+ docs: {
125
+ description: {
126
+ story:
127
+ 'Renders the Object field in native-array mode at "small" size with mixed item types — object, string, number and boolean.',
128
+ },
129
+ },
130
+ },
99
131
  };
100
132
 
101
133
  export const DisabledWithValue: Story = {
@@ -113,6 +145,14 @@ export const DisabledWithValue: Story = {
113
145
  ],
114
146
  disabled: true,
115
147
  },
148
+ parameters: {
149
+ docs: {
150
+ description: {
151
+ story:
152
+ 'Renders the Object field in native-array mode with a populated value but disabled — the items are visible in read-only form.',
153
+ },
154
+ },
155
+ },
116
156
  };
117
157
 
118
158
  export const NativeOnly: Story = {
@@ -129,6 +169,14 @@ export const NativeOnly: Story = {
129
169
  false,
130
170
  ],
131
171
  },
172
+ parameters: {
173
+ docs: {
174
+ description: {
175
+ story:
176
+ 'Renders the Object field in native-array mode with mixed item types — the native editor shows the items without a YAML/JSON textarea alternative.',
177
+ },
178
+ },
179
+ },
132
180
  async play() {
133
181
  await testsWaitForText('"value"');
134
182
  },
@@ -148,6 +196,14 @@ export const ValueCanBeRemoved: Story = {
148
196
  false,
149
197
  ],
150
198
  },
199
+ parameters: {
200
+ docs: {
201
+ description: {
202
+ story:
203
+ 'Renders the Object field with a populated native array. Clicking Remove clears the value and the field falls back to the "New List" placeholder.',
204
+ },
205
+ },
206
+ },
151
207
  async play() {
152
208
  await testsWaitForText('"value"');
153
209
  await testsClickButton({ label: 'Remove' });
@@ -166,6 +222,14 @@ export const Json: Story = {
166
222
  }),
167
223
  resultDataType: 'json',
168
224
  },
225
+ parameters: {
226
+ docs: {
227
+ description: {
228
+ story:
229
+ 'Renders the Object field with a JSON string value. Switching to Text mode, editing the JSON and clicking Save fires onChange with the new JSON serialization.',
230
+ },
231
+ },
232
+ },
169
233
  async play({ args }) {
170
234
  await testsWaitForText('"value"');
171
235
  await testsClickButton({ label: 'Text' });
@@ -213,6 +277,14 @@ export const Yaml: Story = {
213
277
  }),
214
278
  resultDataType: 'yaml',
215
279
  },
280
+ parameters: {
281
+ docs: {
282
+ description: {
283
+ story:
284
+ 'Renders the Object field with a YAML string value. Switching to Text mode, editing the YAML and clicking Save fires onChange with the new YAML serialization.',
285
+ },
286
+ },
287
+ },
216
288
  async play({ args }) {
217
289
  await testsWaitForText('"value"');
218
290
  await testsClickButton({ label: 'Text' });
@@ -247,6 +319,14 @@ export const YamlToJson: Story = {
247
319
  }),
248
320
  resultDataType: 'json',
249
321
  },
322
+ parameters: {
323
+ docs: {
324
+ description: {
325
+ story:
326
+ 'Renders the Object field with a YAML input value and JSON output — editing the YAML in Text mode and saving fires onChange with the equivalent JSON string.',
327
+ },
328
+ },
329
+ },
250
330
  async play({ args }) {
251
331
  await testsWaitForText('"value"');
252
332
  await testsClickButton({ label: 'Text' });
@@ -281,6 +361,14 @@ export const NativeToYaml: Story = {
281
361
  },
282
362
  resultDataType: 'yaml',
283
363
  },
364
+ parameters: {
365
+ docs: {
366
+ description: {
367
+ story:
368
+ 'Renders the Object field with a native object value and YAML output — editing in Text mode and saving fires onChange with the YAML serialization.',
369
+ },
370
+ },
371
+ },
284
372
  async play({ args }) {
285
373
  await testsWaitForText('"value"');
286
374
  await testsClickButton({ label: 'Text' });
@@ -42,6 +42,14 @@ export const Default: Story = {
42
42
  { label: 'Python', value: 'Python', 'aria-label': 'Python' },
43
43
  ],
44
44
  },
45
+ parameters: {
46
+ docs: {
47
+ description: {
48
+ story:
49
+ 'Renders the RadioGroup field with three language options and "Qore" pre-selected. Clicking a different option fires onChange with the new value.',
50
+ },
51
+ },
52
+ },
45
53
  async play({ canvasElement, args }) {
46
54
  const canvas = within(canvasElement);
47
55
  const java = canvas.getByLabelText('Java');
@@ -59,6 +67,14 @@ export const WithImages: Story = {
59
67
  { label: 'Python', value: 'Python', image: python },
60
68
  ],
61
69
  },
70
+ parameters: {
71
+ docs: {
72
+ description: {
73
+ story:
74
+ 'Renders the RadioGroup field with three language options — each option displays its language logo alongside the label.',
75
+ },
76
+ },
77
+ },
62
78
  async play({ canvasElement }) {
63
79
  await expect(canvasElement.querySelector(`img[src="${qore}"]`)).toBeInTheDocument();
64
80
  await expect(canvasElement.querySelector(`img[src="${java}"]`)).toBeInTheDocument();
@@ -68,4 +84,12 @@ export const WithImages: Story = {
68
84
 
69
85
  export const Disabled: Story = {
70
86
  args: { ...Default.args, disabled: true },
87
+ parameters: {
88
+ docs: {
89
+ description: {
90
+ story:
91
+ 'Renders the RadioGroup field with three language options but disabled — the group is visible and non-interactive.',
92
+ },
93
+ },
94
+ },
71
95
  };
@@ -33,6 +33,14 @@ export const WithValue: Story = {
33
33
  args: {
34
34
  value: 'Hello world',
35
35
  },
36
+ parameters: {
37
+ docs: {
38
+ description: {
39
+ story:
40
+ 'Renders the RichText field pre-populated with the plain-string value "Hello world" — the editor mounts with the text rendered as a paragraph.',
41
+ },
42
+ },
43
+ },
36
44
  async play({ canvasElement }) {
37
45
  const canvas = within(canvasElement);
38
46
  // Only assert on render — typing assertions would need waitFor due to debounce
@@ -42,6 +50,13 @@ export const WithValue: Story = {
42
50
  };
43
51
 
44
52
  export const Empty: Story = {
53
+ parameters: {
54
+ docs: {
55
+ description: {
56
+ story: 'Renders the RichText field with no value — the editor mounts empty.',
57
+ },
58
+ },
59
+ },
45
60
  async play({ canvasElement }) {
46
61
  const canvas = within(canvasElement);
47
62
  await expect(canvas.getByRole('textbox')).toBeInTheDocument();
@@ -52,6 +67,14 @@ export const WithSlateValue: Story = {
52
67
  args: {
53
68
  value: [{ type: 'paragraph', children: [{ text: 'Rich text with Slate format' }] }],
54
69
  },
70
+ parameters: {
71
+ docs: {
72
+ description: {
73
+ story:
74
+ 'Renders the RichText field pre-populated with a Slate document rather than a plain string — the editor mounts with the paragraph node rendered as text.',
75
+ },
76
+ },
77
+ },
55
78
  async play({ canvasElement }) {
56
79
  const canvas = within(canvasElement);
57
80
  await expect(canvas.getByRole('textbox')).toBeInTheDocument();
@@ -40,6 +40,14 @@ type Story = StoryObj<typeof meta>;
40
40
  /** Empty definition — the catalogue drives the full tab set. */
41
41
  export const Empty: Story = {
42
42
  args: {},
43
+ parameters: {
44
+ docs: {
45
+ description: {
46
+ story:
47
+ 'Renders the SchemaDefinition editor with no existing value — the Schema, Tables, Sequences and Migrations tabs mount straight from the catalogue.',
48
+ },
49
+ },
50
+ },
43
51
  async play({ canvasElement }) {
44
52
  const canvas = within(canvasElement);
45
53
  // Tabs come straight from the catalogue's `definition` sections.
@@ -59,6 +67,14 @@ export const Starter: Story = {
59
67
  args: {
60
68
  value: starterDefinition,
61
69
  },
70
+ parameters: {
71
+ docs: {
72
+ description: {
73
+ story:
74
+ 'Renders the SchemaDefinition editor seeded with the "example_schema" starter template — the Schema tab shows the single scaffolded table.',
75
+ },
76
+ },
77
+ },
62
78
  };
63
79
 
64
80
  /** A realistic populated schema — two tables, sequence, reference data, migrations. */
@@ -66,6 +82,14 @@ export const Populated: Story = {
66
82
  args: {
67
83
  value: mockPopulatedDefinition,
68
84
  },
85
+ parameters: {
86
+ docs: {
87
+ description: {
88
+ story:
89
+ 'Renders the SchemaDefinition editor with the populated fixture. Opening the Tables tab shows the customers and addresses tables.',
90
+ },
91
+ },
92
+ },
69
93
  async play({ canvasElement }) {
70
94
  const canvas = within(canvasElement);
71
95
  // Open the Tables tab → the two table panels render from the value.
@@ -81,6 +105,14 @@ export const ReadOnly: Story = {
81
105
  value: mockPopulatedDefinition,
82
106
  readOnly: true,
83
107
  },
108
+ parameters: {
109
+ docs: {
110
+ description: {
111
+ story:
112
+ 'Renders the SchemaDefinition editor with readOnly enabled — every input becomes a typeset display row and no inputs, textareas or selects are rendered.',
113
+ },
114
+ },
115
+ },
84
116
  async play({ canvasElement }) {
85
117
  const canvas = within(canvasElement);
86
118
  await expect(await canvas.findByText('Schema')).toBeInTheDocument();
@@ -94,6 +126,14 @@ export const CatalogError: Story = {
94
126
  args: {
95
127
  catalogError: 'Schema options are unavailable on this server.',
96
128
  },
129
+ parameters: {
130
+ docs: {
131
+ description: {
132
+ story:
133
+ 'Renders the SchemaDefinition editor when the catalogue fetch fails — a "Could not load schema options" callout replaces the form.',
134
+ },
135
+ },
136
+ },
97
137
  async play({ canvasElement }) {
98
138
  const canvas = within(canvasElement);
99
139
  await expect(await canvas.findByText('Could not load schema options')).toBeInTheDocument();
@@ -106,6 +146,14 @@ export const CatalogError: Story = {
106
146
  * wired. `catalogOverride` rides in via `fieldProps`.
107
147
  */
108
148
  export const ViaFormField: Story = {
149
+ parameters: {
150
+ docs: {
151
+ description: {
152
+ story:
153
+ 'Renders the SchemaDefinition editor through the FormField dispatcher (type="schema-definition"). The catalogue is injected via fieldProps and the Schema and Tables tabs mount as usual.',
154
+ },
155
+ },
156
+ },
109
157
  render: () => {
110
158
  const [value, setValue] = useState<IDataSchemaDefinition | undefined>(mockPopulatedDefinition);
111
159
  return (
@@ -132,6 +180,14 @@ export const ViaFormField: Story = {
132
180
  * → FormField → SchemaDefinitionEditor).
133
181
  */
134
182
  export const ViaFormEngineUiType: Story = {
183
+ parameters: {
184
+ docs: {
185
+ description: {
186
+ story:
187
+ 'Renders a FormEngine schema whose "definition" option declares ui_type: schema-definition — FormEngine flows through TemplateField and FormField and mounts the schema editor with the injected catalogue.',
188
+ },
189
+ },
190
+ },
135
191
  render: () => {
136
192
  const [value, setValue] = useState<any>({
137
193
  definition: { type: 'hash', value: mockPopulatedDefinition },
@@ -177,6 +233,14 @@ export const Sandboxed: Story = {
177
233
  wrapperName: 'example_customer_addresses',
178
234
  value: mockPopulatedDefinition,
179
235
  },
236
+ parameters: {
237
+ docs: {
238
+ description: {
239
+ story:
240
+ 'Renders the SchemaDefinition editor for an untrusted (sandboxed) caller using the sandboxed catalogue — the Advanced tab and every trusted-only section are hidden.',
241
+ },
242
+ },
243
+ },
180
244
  async play({ canvasElement }) {
181
245
  const canvas = within(canvasElement);
182
246
  await expect(await canvas.findByText('Migrations')).toBeInTheDocument();
@@ -195,6 +259,14 @@ export const WithValidationBanner: Story = {
195
259
  wrapperName: 'renamed_schema',
196
260
  value: mockPopulatedDefinition,
197
261
  },
262
+ parameters: {
263
+ docs: {
264
+ description: {
265
+ story:
266
+ 'Renders the SchemaDefinition editor when the wrapper interface name and the DataSchema name differ — the "name does not match" banner is shown, and clicking "Sync name" resolves the mismatch.',
267
+ },
268
+ },
269
+ },
198
270
  async play({ canvasElement }) {
199
271
  const canvas = within(canvasElement);
200
272
  // The banner renders the mismatch both as its title and its body text.