@qoretechnologies/reqraft 0.10.10 → 0.10.12

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 (41) hide show
  1. package/dist/providers/StorageProvider.d.ts.map +1 -1
  2. package/dist/providers/StorageProvider.js +14 -4
  3. package/dist/providers/StorageProvider.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
  6. package/src/components/form/engine/FormEngine.stories.tsx +654 -32
  7. package/src/components/form/engine/FormEngineRemote.stories.tsx +48 -0
  8. package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
  9. package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +40 -0
  10. package/src/components/form/expressions/ExpressionField.stories.tsx +74 -2
  11. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +187 -0
  12. package/src/components/form/fields/Field.stories.tsx +40 -0
  13. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
  14. package/src/components/form/fields/auto/AutoFormField.stories.tsx +136 -0
  15. package/src/components/form/fields/binary/Binary.stories.tsx +32 -0
  16. package/src/components/form/fields/boolean/Boolean.stories.tsx +24 -0
  17. package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
  18. package/src/components/form/fields/color/Color.stories.tsx +30 -0
  19. package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
  20. package/src/components/form/fields/date/Date.stories.tsx +22 -0
  21. package/src/components/form/fields/file/File.stories.tsx +48 -0
  22. package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
  23. package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
  24. package/src/components/form/fields/number/Number.stories.tsx +31 -0
  25. package/src/components/form/fields/object/Object.stories.tsx +88 -0
  26. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
  27. package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
  28. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
  29. package/src/components/form/fields/select/Select.stories.tsx +104 -0
  30. package/src/components/form/fields/string/String.stories.tsx +16 -0
  31. package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
  32. package/src/components/form/fields/url/Url.stories.tsx +24 -0
  33. package/src/components/log/Log.stories.tsx +96 -0
  34. package/src/components/menu/Menu.stories.tsx +54 -0
  35. package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +14 -0
  36. package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
  37. package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
  38. package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
  39. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
  40. package/src/providers/StorageProvider.tsx +12 -4
  41. package/src/stores/currentUser/currentUser.stories.tsx +17 -0
@@ -123,6 +123,14 @@ export const SchemaFromUrl: Story = {
123
123
  name: 'remoteBasic',
124
124
  url: 'sb-remote/basic',
125
125
  },
126
+ parameters: {
127
+ docs: {
128
+ description: {
129
+ story:
130
+ 'Renders FormEngine with a url — the options schema is fetched from options/{url}, three fields mount with the preselected localhost default seeded and onOptionsLoaded fires with the fetched schema.',
131
+ },
132
+ },
133
+ },
126
134
  async beforeEach() {
127
135
  return mockFetchRoutes({ 'options/sb-remote/basic': { body: BASIC_SCHEMA } });
128
136
  },
@@ -153,6 +161,14 @@ export const OperatorsFromUrl: Story = {
153
161
  hostname: { type: 'string', value: 'qore', op: ['like'] },
154
162
  } as any,
155
163
  },
164
+ parameters: {
165
+ docs: {
166
+ description: {
167
+ story:
168
+ 'Renders FormEngine with both url and operatorsUrl — the operator select resolves the "like" op against the fetched operator schema and the WHERE/IS summary tags appear alongside the option.',
169
+ },
170
+ },
171
+ },
156
172
  async beforeEach() {
157
173
  return mockFetchRoutes({
158
174
  'options/sb-remote/search': { body: SEARCH_SCHEMA },
@@ -181,6 +197,14 @@ export const FetchFailure: Story = {
181
197
  name: 'remoteFail',
182
198
  url: 'sb-remote/fail',
183
199
  },
200
+ parameters: {
201
+ docs: {
202
+ description: {
203
+ story:
204
+ 'Renders FormEngine when the schema fetch returns 500 — the skeleton resolves into the "No options available" empty state instead of crashing.',
205
+ },
206
+ },
207
+ },
184
208
  async beforeEach() {
185
209
  return mockFetchRoutes({
186
210
  'options/sb-remote/fail': { status: 500, body: { err: 'boom' } },
@@ -202,6 +226,14 @@ export const UrlChangeResetsValue: Story = {
202
226
  name: 'remoteSwitch',
203
227
  url: 'sb-remote/first',
204
228
  },
229
+ parameters: {
230
+ docs: {
231
+ description: {
232
+ story:
233
+ 'Renders FormEngine wired to url="first". Clicking the switch button re-points the engine to a different schema — the previous value (localhost) is dropped and the new Token field mounts.',
234
+ },
235
+ },
236
+ },
205
237
  render(args) {
206
238
  const [url, setUrl] = useState(args.url);
207
239
  return (
@@ -256,6 +288,14 @@ export const InjectedOptionActions: Story = {
256
288
  },
257
289
  ],
258
290
  },
291
+ parameters: {
292
+ docs: {
293
+ description: {
294
+ story:
295
+ 'Renders FormEngine with an optionActions factory that injects a per-option AI-assist button. Hovering an option reveals the injected action next to it.',
296
+ },
297
+ },
298
+ },
259
299
  async play() {
260
300
  await waitFor(() => expect(optionCount()).toBe(3), { timeout: 10000 });
261
301
 
@@ -278,6 +318,14 @@ export const TemplatesFromContext: Story = {
278
318
  options: BASIC_SCHEMA as any,
279
319
  interfaceContext: 'sb-ctx',
280
320
  },
321
+ parameters: {
322
+ docs: {
323
+ description: {
324
+ story:
325
+ 'Renders FormEngine with an interfaceContext — useTemplates hits system/getContextData so template values are available in the schema fields.',
326
+ },
327
+ },
328
+ },
281
329
  async beforeEach() {
282
330
  return mockFetchRoutes({
283
331
  'system/getContextData': {
@@ -57,6 +57,14 @@ export const WorkflowOrderData: Story = {
57
57
  // Expand deep enough that the parsed-YAML sub-tree is visible.
58
58
  defaultExpandDepth: 3,
59
59
  },
60
+ parameters: {
61
+ docs: {
62
+ description: {
63
+ story:
64
+ 'Renders StructuredDataView over a workflow-order payload — UI-encoded envelopes unwrap to scalars, embedded YAML strings parse into sub-trees and Qorus timestamps format to YYYY-MM-DD HH:mm:ss.SSS in the viewer\'s zone.',
65
+ },
66
+ },
67
+ },
60
68
  play: async () => {
61
69
  await _testsWaitForText('ORD-1002');
62
70
  await _testsWaitForText('order-process');
@@ -59,6 +59,14 @@ type Story = StoryObj<typeof meta>;
59
59
 
60
60
  export const V1CalmTable: Story = {
61
61
  name: '1 · Calm Table',
62
+ parameters: {
63
+ docs: {
64
+ description: {
65
+ story:
66
+ 'Renders the "Calm Table" compact-form variant over the shared basic schema — one of four visual candidates for the read-first FormEngine view.',
67
+ },
68
+ },
69
+ },
62
70
  render: () => (
63
71
  <Frame>
64
72
  <VariantCalmTable options={options} values={values} />
@@ -68,6 +76,14 @@ export const V1CalmTable: Story = {
68
76
 
69
77
  export const V2Cards: Story = {
70
78
  name: '2 · Cards',
79
+ parameters: {
80
+ docs: {
81
+ description: {
82
+ story:
83
+ 'Renders the "Cards" compact-form variant over the shared basic schema — each field group is a card so the presentation reads as chunked panels.',
84
+ },
85
+ },
86
+ },
71
87
  render: () => (
72
88
  <Frame>
73
89
  <VariantCards options={options} values={values} />
@@ -77,6 +93,14 @@ export const V2Cards: Story = {
77
93
 
78
94
  export const V3Focus: Story = {
79
95
  name: '3 · Focus',
96
+ parameters: {
97
+ docs: {
98
+ description: {
99
+ story:
100
+ 'Renders the "Focus" compact-form variant over the richer focus-demo schema (named groups, required-group choices) so the variant is compared under full engine parity.',
101
+ },
102
+ },
103
+ },
80
104
  render: () => (
81
105
  <Frame>
82
106
  <VariantFocus options={focusOptions} values={focusValues} config={focusConfig} />
@@ -86,6 +110,14 @@ export const V3Focus: Story = {
86
110
 
87
111
  export const V4Minimal: Story = {
88
112
  name: '4 · Minimal',
113
+ parameters: {
114
+ docs: {
115
+ description: {
116
+ story:
117
+ 'Renders the "Minimal" compact-form variant over the shared basic schema — the sparest of the four candidates, only labels and values.',
118
+ },
119
+ },
120
+ },
89
121
  render: () => (
90
122
  <Frame>
91
123
  <VariantMinimal options={options} values={values} />
@@ -96,6 +128,14 @@ export const V4Minimal: Story = {
96
128
  /** All four stacked, labelled — for a quick scroll-through comparison. */
97
129
  export const CompareAll: Story = {
98
130
  name: '★ Compare all',
131
+ parameters: {
132
+ docs: {
133
+ description: {
134
+ story:
135
+ 'Renders all four compact-form variants stacked in a single scroll — the Calm Table, Cards, Focus and Minimal layouts sit under labelled panels so they can be compared side by side.',
136
+ },
137
+ },
138
+ },
99
139
  render: () => {
100
140
  const items: [string, React.ReactNode][] = [
101
141
  ['1 · Calm Table', <VariantCalmTable key='v1' options={options} values={values} />],
@@ -50,6 +50,14 @@ export const Default: Story = {
50
50
  args: {
51
51
  value: SAMPLE,
52
52
  },
53
+ parameters: {
54
+ docs: {
55
+ description: {
56
+ story:
57
+ 'Renders ExpressionField holding a "$local:name == John" expression — the Visual builder shows the Logical Equals operator with its two operands and the Visual/Text mode toggle.',
58
+ },
59
+ },
60
+ },
53
61
  async play({ canvasElement }) {
54
62
  const canvas = within(canvasElement);
55
63
 
@@ -73,6 +81,14 @@ export const Empty: Story = {
73
81
  value: { is_expression: true, value: { args: [] } },
74
82
  defaultMode: 'text',
75
83
  },
84
+ parameters: {
85
+ docs: {
86
+ description: {
87
+ story:
88
+ 'Renders ExpressionField in Text mode with an empty expression AST — the "Parsed" preview shows the "(empty)" placeholder for an empty expression.',
89
+ },
90
+ },
91
+ },
76
92
  async beforeEach() {
77
93
  const stop = startDpqlMockLsp();
78
94
  return () => stop();
@@ -102,6 +118,14 @@ export const Empty: Story = {
102
118
  * builder, and the Visual/Text toggle is present.
103
119
  */
104
120
  export const ViaFormEngine: Story = {
121
+ parameters: {
122
+ docs: {
123
+ description: {
124
+ story:
125
+ 'Renders a FormEngine schema with a bool option that carries supports_expressions and a stored expression value — the engine routes through TemplateField and mounts the ExpressionField shell with the Visual builder and the Visual/Text toggle.',
126
+ },
127
+ },
128
+ },
105
129
  render: () => {
106
130
  const [value, setValue] = useState<any>({
107
131
  condition: { type: 'bool', value: SAMPLE.value, is_expression: true },
@@ -150,6 +174,14 @@ export const ViaFormEngine: Story = {
150
174
  * renders the same AST.
151
175
  */
152
176
  export const ViaFormEngineTextMode: Story = {
177
+ parameters: {
178
+ docs: {
179
+ description: {
180
+ story:
181
+ 'Renders the FormEngine expression field, then switches to Text mode — the DPQL editor seeds from the stored AST via the mock LSP\'s dpql/serialize call and the "Parsed" preview mirrors it.',
182
+ },
183
+ },
184
+ },
153
185
  render: ViaFormEngine.render,
154
186
  async beforeEach() {
155
187
  const stop = startDpqlMockLsp();
@@ -200,6 +232,14 @@ export const ViaFormEngineTextMode: Story = {
200
232
  * the seeded direction is covered by `ViaFormEngineTextMode` above.)
201
233
  */
202
234
  export const ViaFormEngineTextTyping: Story = {
235
+ parameters: {
236
+ docs: {
237
+ description: {
238
+ story:
239
+ 'Renders the FormEngine expression field with an empty AST in Text mode. Typing DPQL text triggers the mock LSP\'s dpql/parse and the parsed expression lands in the form value with is_expression set.',
240
+ },
241
+ },
242
+ },
203
243
  render: (args) => {
204
244
  const [value, setValue] = useState<any>({
205
245
  condition: { type: 'bool', value: { args: [] }, is_expression: true },
@@ -277,6 +317,14 @@ export const TextMode: Story = {
277
317
  value: { is_expression: true, value: { args: [] } },
278
318
  defaultMode: 'text',
279
319
  },
320
+ parameters: {
321
+ docs: {
322
+ description: {
323
+ story:
324
+ 'Renders ExpressionField in Text (DPQL) mode over a mock-socket LSP. Typing DPQL text triggers dpql/parse and the "Parsed" preview reflects the resulting AST.',
325
+ },
326
+ },
327
+ },
280
328
  async beforeEach() {
281
329
  const stop = startDpqlMockLsp();
282
330
  return () => stop();
@@ -309,6 +357,14 @@ export const TextMode: Story = {
309
357
  * Driven live for the toggle interaction.
310
358
  */
311
359
  export const ToggleInFormEngine: Story = {
360
+ parameters: {
361
+ docs: {
362
+ description: {
363
+ story:
364
+ 'Renders a FormEngine schema with a plain bool option (supports_expressions, no value). Clicking More then "Use Expression" flips the field to expression mode and the ExpressionField shell with the Visual builder mounts in place of the checkbox.',
365
+ },
366
+ },
367
+ },
312
368
  render: () => {
313
369
  const [value, setValue] = useState<any>({});
314
370
  return (
@@ -380,7 +436,15 @@ export const Live: Story = {
380
436
  // (network/auth), still snapshotted by Chromatic. Mirrors the DpqlEditor
381
437
  // live stories.
382
438
  tags: ['!test'],
383
- parameters: { live: true },
439
+ parameters: {
440
+ live: true,
441
+ docs: {
442
+ description: {
443
+ story:
444
+ 'Renders ExpressionField against a live Qorus instance — the picker fetches the real ~121-function catalogue rather than the mock, and the seeded "contains" expression exercises the schema-driven operators and operands.',
445
+ },
446
+ },
447
+ },
384
448
  args: {
385
449
  type: 'bool',
386
450
  value: {
@@ -423,7 +487,15 @@ export const Live: Story = {
423
487
  */
424
488
  export const LiveExplain: Story = {
425
489
  tags: ['!test'],
426
- parameters: { live: true },
490
+ parameters: {
491
+ live: true,
492
+ docs: {
493
+ description: {
494
+ story:
495
+ 'Renders ExpressionField in Text mode against a live Qorus instance — the "Parsed" preview uses the server-rendered form via dpql/renderExpression rather than the client-side approximation.',
496
+ },
497
+ },
498
+ },
427
499
  args: {
428
500
  value: {
429
501
  is_expression: true,
@@ -113,6 +113,14 @@ export const Default: Story = {
113
113
  args: {
114
114
  returnType: ['string', 'int'],
115
115
  },
116
+ parameters: {
117
+ docs: {
118
+ description: {
119
+ story:
120
+ 'Renders the ExpressionBuilder with no value and a string-or-int return type — a single empty expression card is shown so the operator can pick an operation.',
121
+ },
122
+ },
123
+ },
116
124
  play: async () => {
117
125
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
118
126
  },
@@ -122,6 +130,14 @@ export const DefaultBoolean: Story = {
122
130
  args: {
123
131
  returnType: ['boolean'],
124
132
  },
133
+ parameters: {
134
+ docs: {
135
+ description: {
136
+ story:
137
+ 'Renders the ExpressionBuilder with no value and a boolean return type — a single empty expression card is shown so the operator can pick a boolean-returning operation.',
138
+ },
139
+ },
140
+ },
125
141
  play: async () => {
126
142
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
127
143
  },
@@ -142,6 +158,14 @@ export const WithSimpleValue: Story = {
142
158
  },
143
159
  },
144
160
  },
161
+ parameters: {
162
+ docs: {
163
+ description: {
164
+ story:
165
+ 'Renders the ExpressionBuilder holding a single "contains" expression with three arguments — the card shows the operation and each operand slot.',
166
+ },
167
+ },
168
+ },
145
169
  play: async () => {
146
170
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
147
171
  },
@@ -234,6 +258,14 @@ export const WithComplexValue: Story = {
234
258
  },
235
259
  },
236
260
  },
261
+ parameters: {
262
+ docs: {
263
+ description: {
264
+ story:
265
+ 'Renders the ExpressionBuilder holding a nested &&/|| expression with six leaf expressions — the builder shows the full expression tree with the AND/OR group cards and their operand cards.',
266
+ },
267
+ },
268
+ },
237
269
  play: async () => {
238
270
  await waitFor(() => expect(expressionCount()).toBe(6), { timeout: 10000 });
239
271
  },
@@ -241,6 +273,15 @@ export const WithComplexValue: Story = {
241
273
 
242
274
  export const ShowsExplanation: Story = {
243
275
  ...WithComplexValue,
276
+ parameters: {
277
+ ...WithComplexValue.parameters,
278
+ docs: {
279
+ description: {
280
+ story:
281
+ 'Renders the nested expression tree and clicks Explain — the plain-language explanation of the whole expression is shown alongside the builder.',
282
+ },
283
+ },
284
+ },
244
285
  play: async (args) => {
245
286
  await WithComplexValue.play!(args);
246
287
  await clickButtonByLabel('Explain');
@@ -260,6 +301,15 @@ export const Readonly: Story = {
260
301
  ...WithComplexValue.args,
261
302
  readOnly: true,
262
303
  },
304
+ parameters: {
305
+ ...WithComplexValue.parameters,
306
+ docs: {
307
+ description: {
308
+ story:
309
+ 'Renders the nested expression tree with readOnly enabled — the tree is visible but the AND/OR add-group controls are hidden so the operator cannot mutate it.',
310
+ },
311
+ },
312
+ },
263
313
  play: async (args) => {
264
314
  await WithComplexValue.play!(args);
265
315
  expect(document.querySelector('.expression-and')).toBeNull();
@@ -268,6 +318,15 @@ export const Readonly: Story = {
268
318
 
269
319
  export const FocusedEditing: Story = {
270
320
  ...WithComplexValue,
321
+ parameters: {
322
+ ...WithComplexValue.parameters,
323
+ docs: {
324
+ description: {
325
+ story:
326
+ 'Renders the nested expression tree, hovers a card and clicks its fullscreen action — the focused-editing modal opens over that subtree.',
327
+ },
328
+ },
329
+ },
271
330
  play: async (args) => {
272
331
  await WithComplexValue.play!(args);
273
332
  await userEvent.hover(document.querySelectorAll('.expression')[0]);
@@ -288,6 +347,14 @@ export const WithArgWithAllowedValues: Story = {
288
347
  args: {
289
348
  returnType: ['bool'],
290
349
  },
350
+ parameters: {
351
+ docs: {
352
+ description: {
353
+ story:
354
+ 'Renders an empty ExpressionBuilder and selects "Matches Regular Expression" — the third operand ("Regex Options") carries element_allowed_values and renders the "Select one or more" picker.',
355
+ },
356
+ },
357
+ },
291
358
  play: async (args) => {
292
359
  await Default.play!(args);
293
360
 
@@ -311,6 +378,14 @@ export const WithIntType: Story = {
311
378
  },
312
379
  },
313
380
  },
381
+ parameters: {
382
+ docs: {
383
+ description: {
384
+ story:
385
+ 'Renders the ExpressionBuilder with an int-typed >= expression comparing $local:input to 20 — the operands render as Number fields inside the expression card.',
386
+ },
387
+ },
388
+ },
314
389
  play: async () => {
315
390
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
316
391
  },
@@ -326,6 +401,15 @@ export const WithIntType: Story = {
326
401
  */
327
402
  export const ArgsChangeWhenOperatorChanges: Story = {
328
403
  ...WithSimpleValue,
404
+ parameters: {
405
+ ...WithSimpleValue.parameters,
406
+ docs: {
407
+ description: {
408
+ story:
409
+ 'Renders the seeded "String Contains" expression, then switches the operation to "Matches Regular Expression" — the string operands carry over and the new "Regex Options" allowed-values operand appears.',
410
+ },
411
+ },
412
+ },
329
413
  play: async () => {
330
414
  // Both string operands of the seeded `contains` render as textareas.
331
415
  await waitFor(
@@ -344,6 +428,14 @@ export const NewGroupsCanBeCreated: Story = {
344
428
  ...WithSimpleValue.args,
345
429
  returnType: ['bool'],
346
430
  },
431
+ parameters: {
432
+ docs: {
433
+ description: {
434
+ story:
435
+ 'Renders the seeded "String Contains" expression, then clicks the AND and OR add-group actions — the tree grows from 1 to 3 expression cards as new grouping levels are added.',
436
+ },
437
+ },
438
+ },
347
439
  play: async () => {
348
440
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
349
441
 
@@ -357,6 +449,15 @@ export const NewGroupsCanBeCreated: Story = {
357
449
 
358
450
  export const GroupsCanBeDeleted: Story = {
359
451
  ...WithComplexValue,
452
+ parameters: {
453
+ ...WithComplexValue.parameters,
454
+ docs: {
455
+ description: {
456
+ story:
457
+ 'Renders the nested expression tree (6 cards) and removes one group — the tree shrinks to 5 cards after the delete.',
458
+ },
459
+ },
460
+ },
360
461
  play: async () => {
361
462
  await waitFor(() => expect(expressionCount()).toBe(6), { timeout: 10000 });
362
463
 
@@ -377,6 +478,14 @@ export const GroupsCanBeDeleted: Story = {
377
478
  * inline `AiAssistanceAction({ context: selectedExpression })` captured.
378
479
  */
379
480
  export const WithInjectedExtraActions: Story = {
481
+ parameters: {
482
+ docs: {
483
+ description: {
484
+ story:
485
+ 'Renders the nested expression tree with an extraActions factory that injects an AI-assist button per card. Hovering an expression reveals the injected action alongside the built-in ones.',
486
+ },
487
+ },
488
+ },
380
489
  args: {
381
490
  ...WithComplexValue.args,
382
491
  extraActions: ({ selectedExpression }) => [
@@ -428,6 +537,14 @@ export const ExpressionWithIntReturnType: Story = {
428
537
  is_expression: true,
429
538
  },
430
539
  },
540
+ parameters: {
541
+ docs: {
542
+ description: {
543
+ story:
544
+ 'Renders the ExpressionBuilder with a "+" (Addition) varargs expression holding four int operands — the three "rest" args each expose a remove-argument button.',
545
+ },
546
+ },
547
+ },
431
548
  play: async () => {
432
549
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
433
550
  // Three "rest" args → three remove-argument buttons.
@@ -438,6 +555,15 @@ export const ExpressionWithIntReturnType: Story = {
438
555
  /** Remove one variable argument — the remove-arg count drops from 3 to 2. */
439
556
  export const VariableArgumentsCanBeRemoved: Story = {
440
557
  ...ExpressionWithIntReturnType,
558
+ parameters: {
559
+ ...ExpressionWithIntReturnType.parameters,
560
+ docs: {
561
+ description: {
562
+ story:
563
+ 'Renders the "+" varargs expression and clicks one remove-argument button — one operand disappears and the remove-arg count drops from 3 to 2.',
564
+ },
565
+ },
566
+ },
441
567
  play: async (args) => {
442
568
  await ExpressionWithIntReturnType.play!(args);
443
569
 
@@ -450,6 +576,15 @@ export const VariableArgumentsCanBeRemoved: Story = {
450
576
  /** Remove one then add one back — the count returns to 3. */
451
577
  export const VariableArgumentsCanBeAdded: Story = {
452
578
  ...ExpressionWithIntReturnType,
579
+ parameters: {
580
+ ...ExpressionWithIntReturnType.parameters,
581
+ docs: {
582
+ description: {
583
+ story:
584
+ 'Renders the "+" varargs expression, removes one operand and then adds one back — the remove-arg count moves 3 → 2 → 3.',
585
+ },
586
+ },
587
+ },
453
588
  play: async (args) => {
454
589
  await ExpressionWithIntReturnType.play!(args);
455
590
 
@@ -487,6 +622,14 @@ export const ExpressionCanBeWrapped: Story = {
487
622
  returnType: ['string', 'int'],
488
623
  value: seededConvert,
489
624
  },
625
+ parameters: {
626
+ docs: {
627
+ description: {
628
+ story:
629
+ 'Renders a seeded "Convert to Boolean" expression, hovers the card and picks "Join List Elements" from the Wrap picker — the outer op mounts and the original expression becomes its first operand.',
630
+ },
631
+ },
632
+ },
490
633
  play: async () => {
491
634
  // Seeded base op + its operand render.
492
635
  await waitFor(() => expect(expressionCount()).toBe(1), { timeout: 10000 });
@@ -506,6 +649,15 @@ export const ExpressionCanBeWrapped: Story = {
506
649
  /** Wrap, then unwrap — back to a single expression card. */
507
650
  export const ExpressionCanBeUnwrapped: Story = {
508
651
  ...ExpressionCanBeWrapped,
652
+ parameters: {
653
+ ...ExpressionCanBeWrapped.parameters,
654
+ docs: {
655
+ description: {
656
+ story:
657
+ 'Renders the wrapped expression from ExpressionCanBeWrapped, then hovers and unwraps the outer op — the inner "Convert to Boolean" survives and the outer wrapper is removed.',
658
+ },
659
+ },
660
+ },
509
661
  play: async (args) => {
510
662
  await ExpressionCanBeWrapped.play!(args);
511
663
 
@@ -579,6 +731,14 @@ const SeededMismatchBase: Story = {
579
731
  * exact, so the builder flags it and the confirmation modal mounts.
580
732
  */
581
733
  export const FunctionArgsArePassedToNewFunctionAndNeedConfirmation: Story = {
734
+ parameters: {
735
+ docs: {
736
+ description: {
737
+ story:
738
+ 'Renders a valued "Format Date" expression and switches the operation to "Matches Regular Expression" — the carried date arg is accepted-but-not-exact for the new richtext operand and the mismatched-arguments confirmation modal mounts.',
739
+ },
740
+ },
741
+ },
582
742
  args: {
583
743
  returnType: 'string',
584
744
  value: {
@@ -616,6 +776,15 @@ export const FunctionArgsArePassedToNewFunctionAndNeedConfirmation: Story = {
616
776
  /** "Confirm selection" keeps the (still-selected) mismatched arg, transformed. */
617
777
  export const AcceptIncompatibleFunctionArgsSelection: Story = {
618
778
  ...SeededMismatchBase,
779
+ parameters: {
780
+ ...SeededMismatchBase.parameters,
781
+ docs: {
782
+ description: {
783
+ story:
784
+ 'Renders the seeded mismatch-flagged expression, then clicks "Confirm selection" on the mismatched-arguments modal — the (still-selected) argument is kept and transformed to the new expected type.',
785
+ },
786
+ },
787
+ },
619
788
  play: async (args) => {
620
789
  await SeededMismatchBase.play!(args);
621
790
 
@@ -636,6 +805,15 @@ export const AcceptIncompatibleFunctionArgsSelection: Story = {
636
805
  /** "Accept all" — every mismatched arg is transformed and kept. */
637
806
  export const AcceptAllIncompatibleFunctionArgs: Story = {
638
807
  ...SeededMismatchBase,
808
+ parameters: {
809
+ ...SeededMismatchBase.parameters,
810
+ docs: {
811
+ description: {
812
+ story:
813
+ 'Renders the seeded mismatch-flagged expression, then clicks "Accept all" on the mismatched-arguments modal — every flagged argument is transformed and kept.',
814
+ },
815
+ },
816
+ },
639
817
  play: async (args) => {
640
818
  await SeededMismatchBase.play!(args);
641
819
 
@@ -655,6 +833,15 @@ export const AcceptAllIncompatibleFunctionArgs: Story = {
655
833
  /** "Discard all" — every mismatched arg is cleared. */
656
834
  export const DiscardAllIncompatibleFunctionArgs: Story = {
657
835
  ...SeededMismatchBase,
836
+ parameters: {
837
+ ...SeededMismatchBase.parameters,
838
+ docs: {
839
+ description: {
840
+ story:
841
+ 'Renders the seeded mismatch-flagged expression, then clicks "Discard all" on the mismatched-arguments modal — every flagged argument is cleared.',
842
+ },
843
+ },
844
+ },
658
845
  play: async (args) => {
659
846
  await SeededMismatchBase.play!(args);
660
847