@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
@@ -93,6 +93,14 @@ export default meta;
93
93
  export type Story = StoryObj<typeof meta>;
94
94
 
95
95
  export const Basic: Story = {
96
+ parameters: {
97
+ docs: {
98
+ description: {
99
+ story:
100
+ 'Renders ReqraftLog wired to a mock WebSocket that emits a stream of test log lines — the messages accumulate in the log panel as they arrive.',
101
+ },
102
+ },
103
+ },
96
104
  play: async () => {
97
105
  await testsWaitForText('This is a message with a link: https://www.qoretechnologies.com');
98
106
  },
@@ -103,6 +111,14 @@ export const Filterable: Story = {
103
111
  args: {
104
112
  filterable: true,
105
113
  },
114
+ parameters: {
115
+ docs: {
116
+ description: {
117
+ story:
118
+ 'Renders ReqraftLog with the filter input enabled. Typing "error" narrows the visible list to the single ERROR message.',
119
+ },
120
+ },
121
+ },
106
122
  play: async (args) => {
107
123
  await Basic.play(args);
108
124
  await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'error' } });
@@ -115,6 +131,14 @@ export const WithTimestamps: Story = {
115
131
  args: {
116
132
  showTimestamps: true,
117
133
  },
134
+ parameters: {
135
+ docs: {
136
+ description: {
137
+ story:
138
+ 'Renders ReqraftLog with per-message timestamps enabled — each streamed line is prefixed with its arrival time.',
139
+ },
140
+ },
141
+ },
118
142
  };
119
143
 
120
144
  export const WithDefaultMessages: Story = {
@@ -126,6 +150,14 @@ export const WithDefaultMessages: Story = {
126
150
  { message: 'This is another default message', timestamp: '08:00:00.000' },
127
151
  ],
128
152
  },
153
+ parameters: {
154
+ docs: {
155
+ description: {
156
+ story:
157
+ 'Renders ReqraftLog seeded with two default messages — those lines appear before any messages arrive from the socket.',
158
+ },
159
+ },
160
+ },
129
161
  };
130
162
 
131
163
  export const WithCopyableMessages: Story = {
@@ -133,6 +165,14 @@ export const WithCopyableMessages: Story = {
133
165
  args: {
134
166
  allowMessageCopy: true,
135
167
  },
168
+ parameters: {
169
+ docs: {
170
+ description: {
171
+ story:
172
+ 'Renders ReqraftLog with allowMessageCopy — each message row exposes a copy action next to its content.',
173
+ },
174
+ },
175
+ },
136
176
  play: async (args) => {
137
177
  await Basic.play(args);
138
178
  },
@@ -144,6 +184,14 @@ export const WithDeletableMessages: Story = {
144
184
  allowMessageCopy: true,
145
185
  allowMessageDeletion: true,
146
186
  },
187
+ parameters: {
188
+ docs: {
189
+ description: {
190
+ story:
191
+ 'Renders ReqraftLog with copy and delete actions on every message. Clicking a message\'s delete action removes just that row from the log.',
192
+ },
193
+ },
194
+ },
147
195
  play: async (args) => {
148
196
  await Basic.play(args);
149
197
  await testsClickButton({ selector: '.reqraft-log-delete-message', nth: 4 });
@@ -155,6 +203,14 @@ export const Pause: Story = {
155
203
  args: {
156
204
  allowMessageDeletion: true,
157
205
  },
206
+ parameters: {
207
+ docs: {
208
+ description: {
209
+ story:
210
+ 'Renders ReqraftLog while messages stream in. Clicking the pause/resume action halts appending new messages until it is clicked again.',
211
+ },
212
+ },
213
+ },
158
214
  play: async () => {
159
215
  await testsWaitForText('This is another message');
160
216
  await testsClickButton({ selector: '.reqraft-log-pause-resume' });
@@ -166,6 +222,14 @@ export const Clear: Story = {
166
222
  args: {
167
223
  allowMessageDeletion: true,
168
224
  },
225
+ parameters: {
226
+ docs: {
227
+ description: {
228
+ story:
229
+ 'Renders ReqraftLog with messages streamed in. Clicking the clear action empties the log and the "No messages" empty state is shown.',
230
+ },
231
+ },
232
+ },
169
233
  play: async (args) => {
170
234
  await Basic.play(args);
171
235
  await testsClickButton({ selector: '.reqraft-log-clear' });
@@ -192,6 +256,14 @@ export const FormattedMessages: Story = {
192
256
  return { message, ...rest };
193
257
  },
194
258
  },
259
+ parameters: {
260
+ docs: {
261
+ description: {
262
+ story:
263
+ 'Renders ReqraftLog with a messageFormatter that assigns intents by content — WARNING lines colour warning, ERROR lines danger and ALL-CAPS lines info.',
264
+ },
265
+ },
266
+ },
195
267
  play: async (args) => {
196
268
  await Basic.play(args);
197
269
  },
@@ -203,6 +275,14 @@ export const CanSendMessages: Story = {
203
275
  canSendMessages: true,
204
276
  showTimestamps: true,
205
277
  },
278
+ parameters: {
279
+ docs: {
280
+ description: {
281
+ story:
282
+ 'Renders ReqraftLog with canSendMessages — an input at the bottom lets the operator send a message back through the socket alongside the incoming stream.',
283
+ },
284
+ },
285
+ },
206
286
  play: async (args) => {
207
287
  await Basic.play(args);
208
288
  },
@@ -214,6 +294,14 @@ export const WithAutoScroll: Story = {
214
294
  autoScroll: true,
215
295
  style: { height: '200px' },
216
296
  },
297
+ parameters: {
298
+ docs: {
299
+ description: {
300
+ story:
301
+ 'Renders ReqraftLog with autoScroll in a fixed 200px viewport — the log stays pinned to the newest message as new lines arrive.',
302
+ },
303
+ },
304
+ },
217
305
  play: async (args) => {
218
306
  await Basic.play(args);
219
307
  },
@@ -225,6 +313,14 @@ export const WithAutoScrollAndManualScroll: Story = {
225
313
  autoScroll: true,
226
314
  style: { height: '200px' },
227
315
  },
316
+ parameters: {
317
+ docs: {
318
+ description: {
319
+ story:
320
+ 'Renders ReqraftLog with autoScroll while the operator scrolls the panel manually — auto-scroll pauses so the operator can inspect older messages without being yanked back to the tail.',
321
+ },
322
+ },
323
+ },
228
324
  play: async (args) => {
229
325
  await WithAutoScroll.play(args);
230
326
  await sleep(3000);
@@ -27,6 +27,14 @@ export const Basic: Story = {
27
27
  args: {
28
28
  menu: typedMenu,
29
29
  },
30
+ parameters: {
31
+ docs: {
32
+ description: {
33
+ story:
34
+ 'Renders ReqraftMenu with the mock Qorus navigation — sections and items appear in the sidebar with no active path highlighted.',
35
+ },
36
+ },
37
+ },
30
38
  play: async () => {
31
39
  await testsWaitForText('Interfaces & More');
32
40
  },
@@ -36,6 +44,14 @@ export const ActivePath: Story = {
36
44
  path: '/Interfaces/mapper',
37
45
  menu: typedMenu,
38
46
  },
47
+ parameters: {
48
+ docs: {
49
+ description: {
50
+ story:
51
+ 'Renders ReqraftMenu with the /Interfaces/mapper path — the matching menu entry is highlighted as active.',
52
+ },
53
+ },
54
+ },
39
55
  play: async () => {
40
56
  await testsWaitForText('Interfaces & More');
41
57
  },
@@ -47,6 +63,14 @@ export const ActiveMenuItemIntent: Story = {
47
63
  menu: typedMenu,
48
64
  activeItemIntent: 'success',
49
65
  },
66
+ parameters: {
67
+ docs: {
68
+ description: {
69
+ story:
70
+ 'Renders ReqraftMenu with the /Interfaces/mapper path and a "success" activeItemIntent — the active entry uses the success intent for its highlight colour.',
71
+ },
72
+ },
73
+ },
50
74
  play: async () => {
51
75
  await testsWaitForText('Interfaces & More');
52
76
  },
@@ -57,6 +81,14 @@ export const WithDefaultQuery: Story = {
57
81
  menu: typedMenu,
58
82
  defaultQuery: 'mapper',
59
83
  },
84
+ parameters: {
85
+ docs: {
86
+ description: {
87
+ story:
88
+ 'Renders ReqraftMenu with a defaultQuery of "mapper" — the search input is pre-filled and the visible items are narrowed to those matching the query.',
89
+ },
90
+ },
91
+ },
60
92
  play: async () => {
61
93
  await testsWaitForText('Interfaces & More');
62
94
  await expect(document.querySelector('.reqore-input')).toHaveValue('mapper');
@@ -69,6 +101,14 @@ export const Filtered: Story = {
69
101
  menu: typedMenu,
70
102
  onQueryChange: fn(),
71
103
  },
104
+ parameters: {
105
+ docs: {
106
+ description: {
107
+ story:
108
+ 'Renders ReqraftMenu with an onQueryChange handler. Typing "step" into the search filter narrows the visible items to just the matching entries.',
109
+ },
110
+ },
111
+ },
72
112
  play: async () => {
73
113
  await testsWaitForText('Interfaces & More');
74
114
  await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'step' } });
@@ -82,6 +122,12 @@ export const Filtered: Story = {
82
122
  export const WidthFromStorage: Story = {
83
123
  ...ActivePath,
84
124
  parameters: {
125
+ docs: {
126
+ description: {
127
+ story:
128
+ 'Renders ReqraftMenu backed by user storage that already holds a persisted width — the menu mounts at the stored width rather than the default.',
129
+ },
130
+ },
85
131
  mockData: [...storiesStorageMock],
86
132
  },
87
133
  };
@@ -93,6 +139,14 @@ export const WithCustomChildren: Story = {
93
139
  topChildren: <div style={{ padding: 10 }}>Top Custom Child</div>,
94
140
  bottomChildren: <div style={{ padding: 10 }}>Bottom Custom Child</div>,
95
141
  },
142
+ parameters: {
143
+ docs: {
144
+ description: {
145
+ story:
146
+ 'Renders ReqraftMenu with topChildren and bottomChildren slots — custom nodes appear above and below the menu list.',
147
+ },
148
+ },
149
+ },
96
150
  play: async () => {
97
151
  await testsWaitForText('Interfaces & More');
98
152
  await testsWaitForText('Top Custom Child');
@@ -398,6 +398,14 @@ export const BasicMock: Story = {
398
398
  args: {
399
399
  initialValue: '/list services ',
400
400
  },
401
+ parameters: {
402
+ docs: {
403
+ description: {
404
+ story:
405
+ 'Renders the QonsoleSmartInput against a mocked Qonsole LSP. Typing "-" after "/list services " opens the flag dropdown with --desc, --limit and --search; pressing Enter accepts the first item without producing a stray double dash.',
406
+ },
407
+ },
408
+ },
401
409
  async beforeEach() {
402
410
  const server = setupBasicQonsoleMockServer();
403
411
  return () => server.close();
@@ -509,6 +517,12 @@ export const LiveQonsoleWithContext: Story = {
509
517
  },
510
518
  parameters: {
511
519
  chromatic: { disable: true },
520
+ docs: {
521
+ description: {
522
+ story:
523
+ 'Renders the QonsoleSmartInput against the real Qonsole LSP with a pre-bound "/use services" context — predictive text is scoped to the services resource via the qonsole/setContext call.',
524
+ },
525
+ },
512
526
  },
513
527
  };
514
528
 
@@ -72,6 +72,14 @@ export const BasicMock: Story = {
72
72
  initialValue: '',
73
73
  triggerCharacters: new Set([' ', '.']),
74
74
  },
75
+ parameters: {
76
+ docs: {
77
+ description: {
78
+ story:
79
+ 'Renders the SmartEditor against a mocked LSP server. Typing the "." trigger character sends a completion request and the dropdown mounts with the apple and banana suggestions.',
80
+ },
81
+ },
82
+ },
75
83
  async beforeEach() {
76
84
  const lsp = createMockLspServer(MOCK_LSP_URL, {
77
85
  capabilities: {},
@@ -295,6 +303,14 @@ export const WithDiagnostics: Story = {
295
303
  languageId: 'demo',
296
304
  initialValue: 'list services in pricing',
297
305
  },
306
+ parameters: {
307
+ docs: {
308
+ description: {
309
+ story:
310
+ 'Renders the SmartEditor against a mocked LSP server that pushes diagnostics on didOpen — the text is decorated with wavy underlines and the stacked message panel below the editor lists both diagnostics.',
311
+ },
312
+ },
313
+ },
298
314
  async beforeEach() {
299
315
  const lsp = createMockLspServer(MOCK_LSP_URL, {
300
316
  capabilities: {},
@@ -342,6 +358,14 @@ export const ReadOnly: Story = {
342
358
  initialValue: 'frozen content',
343
359
  readOnly: true,
344
360
  },
361
+ parameters: {
362
+ docs: {
363
+ description: {
364
+ story:
365
+ 'Renders the SmartEditor in read-only mode over an initial value — the Slate editor mounts as contenteditable=false so the content shows but cannot be edited.',
366
+ },
367
+ },
368
+ },
345
369
  async beforeEach() {
346
370
  const lsp = createMockLspServer(MOCK_LSP_URL, { capabilities: {} });
347
371
  return () => lsp.close();
@@ -367,6 +391,14 @@ export const LoadingOverlay: Story = {
367
391
  languageId: 'demo',
368
392
  initialValue: 'visible under the overlay',
369
393
  },
394
+ parameters: {
395
+ docs: {
396
+ description: {
397
+ story:
398
+ 'Renders the SmartEditor against a mock LSP that never finishes initialize — the "Connecting to language server" overlay stays over the editor for the whole session.',
399
+ },
400
+ },
401
+ },
370
402
  async beforeEach() {
371
403
  const lsp = createMockLspServer(MOCK_LSP_URL, {
372
404
  capabilities: {},
@@ -35,6 +35,12 @@ export const Get: Story = {
35
35
  method: 'GET',
36
36
  },
37
37
  parameters: {
38
+ docs: {
39
+ description: {
40
+ story:
41
+ 'Renders a useFetch demo that issues a GET against /public/info on mount and displays the returned Qorus system info as a tree.',
42
+ },
43
+ },
38
44
  mockData: [
39
45
  {
40
46
  // An array of mock objects which will add in every story
@@ -66,6 +72,12 @@ export const Put: Story = {
66
72
  method: 'PUT',
67
73
  },
68
74
  parameters: {
75
+ docs: {
76
+ description: {
77
+ story:
78
+ 'Renders a useFetch demo that issues a PUT against /public/info on mount and displays the returned success status.',
79
+ },
80
+ },
69
81
  mockData: [
70
82
  {
71
83
  // An array of mock objects which will add in every story
@@ -88,6 +100,12 @@ export const Post: Story = {
88
100
  method: 'POST',
89
101
  },
90
102
  parameters: {
103
+ docs: {
104
+ description: {
105
+ story:
106
+ 'Renders a useFetch demo that issues a POST against /public/info on mount and displays the returned success status.',
107
+ },
108
+ },
91
109
  mockData: [
92
110
  {
93
111
  // An array of mock objects which will add in every story
@@ -110,6 +128,12 @@ export const Del: Story = {
110
128
  method: 'DELETE',
111
129
  },
112
130
  parameters: {
131
+ docs: {
132
+ description: {
133
+ story:
134
+ 'Renders a useFetch demo that issues a DELETE against /public/info on mount and displays the returned success status.',
135
+ },
136
+ },
113
137
  mockData: [
114
138
  {
115
139
  // An array of mock objects which will add in every story
@@ -132,6 +156,12 @@ export const Error409: Story = {
132
156
  method: 'POST',
133
157
  },
134
158
  parameters: {
159
+ docs: {
160
+ description: {
161
+ story:
162
+ 'Renders a useFetch demo that issues a POST returning a 409 error — the hook exposes errorData and the Qorus stack trace is rendered in place of the response.',
163
+ },
164
+ },
135
165
  mockData: [
136
166
  {
137
167
  // An array of mock objects which will add in every story
@@ -35,6 +35,12 @@ export const DefaultValue: Story = {
35
35
  method: 'GET',
36
36
  },
37
37
  parameters: {
38
+ docs: {
39
+ description: {
40
+ story:
41
+ 'Renders a useReqraftStorage demo where the server returns no stored value — the hook falls back to the default and displays it.',
42
+ },
43
+ },
38
44
  mockData: [
39
45
  {
40
46
  url: 'https://hq.qoretechnologies.com:8092/api/latest/users?action=current',
@@ -51,6 +57,12 @@ export const DefaultValue: Story = {
51
57
 
52
58
  export const StorageValue: Story = {
53
59
  parameters: {
60
+ docs: {
61
+ description: {
62
+ story:
63
+ 'Renders a useReqraftStorage demo with a value already persisted on the server — the hook loads it and displays it in place of the default.',
64
+ },
65
+ },
54
66
  mockData: [...storiesStorageMock],
55
67
  },
56
68
  play: async () => {
@@ -60,6 +72,15 @@ export const StorageValue: Story = {
60
72
 
61
73
  export const ValueCanBeUpdated: Story = {
62
74
  ...StorageValue,
75
+ parameters: {
76
+ ...StorageValue.parameters,
77
+ docs: {
78
+ description: {
79
+ story:
80
+ 'Renders the useReqraftStorage demo with a stored value. When "Update storage" is clicked, the setter writes a new value and the display refreshes to match.',
81
+ },
82
+ },
83
+ },
63
84
  play: async ({ canvasElement }) => {
64
85
  const canvas = within(canvasElement);
65
86
 
@@ -74,6 +95,15 @@ export const ValueCanBeUpdated: Story = {
74
95
 
75
96
  export const ValueCanBeRemoved: Story = {
76
97
  ...StorageValue,
98
+ parameters: {
99
+ ...StorageValue.parameters,
100
+ docs: {
101
+ description: {
102
+ story:
103
+ 'Renders the useReqraftStorage demo with a stored value. When "Remove value" is clicked, the remover clears storage and the hook falls back to the default value.',
104
+ },
105
+ },
106
+ },
77
107
  play: async ({ canvasElement }) => {
78
108
  const canvas = within(canvasElement);
79
109
 
@@ -106,8 +106,25 @@ const meta = {
106
106
  export default meta;
107
107
  export type Story = StoryObj<typeof meta>;
108
108
 
109
- export const Default: Story = {};
109
+ export const Default: Story = {
110
+ parameters: {
111
+ docs: {
112
+ description: {
113
+ story:
114
+ 'Renders the useReqraftWebSocket demo with no lifecycle options — the socket stays CLOSED until the operator clicks Connect.',
115
+ },
116
+ },
117
+ },
118
+ };
110
119
  export const OpenManually: Story = {
120
+ parameters: {
121
+ docs: {
122
+ description: {
123
+ story:
124
+ 'Renders the useReqraftWebSocket demo and clicks the Connect action — the socket opens and the onOpen callback fires.',
125
+ },
126
+ },
127
+ },
111
128
  play: async ({ args }) => {
112
129
  await testsClickButton({ label: 'Connect' });
113
130
  await testsWaitForText('Websocket Status: OPEN');
@@ -118,6 +135,14 @@ export const OpenOnMount: Story = {
118
135
  args: {
119
136
  openOnMount: true,
120
137
  },
138
+ parameters: {
139
+ docs: {
140
+ description: {
141
+ story:
142
+ 'Renders the useReqraftWebSocket demo with openOnMount enabled — the socket opens immediately after mount without any user action.',
143
+ },
144
+ },
145
+ },
121
146
  play: async ({ args }) => {
122
147
  await testsWaitForText('Websocket Status: OPEN');
123
148
  await expect(args.onOpen).toHaveBeenCalled();
@@ -126,6 +151,15 @@ export const OpenOnMount: Story = {
126
151
 
127
152
  export const CloseManually: Story = {
128
153
  ...OpenOnMount,
154
+ parameters: {
155
+ ...OpenOnMount.parameters,
156
+ docs: {
157
+ description: {
158
+ story:
159
+ 'Renders the useReqraftWebSocket demo opened on mount. When Disconnect is clicked, the socket transitions to CLOSED and the onClose callback fires.',
160
+ },
161
+ },
162
+ },
129
163
  play: async ({ args, ...rest }) => {
130
164
  await OpenOnMount.play({ args, ...rest });
131
165
  await testsClickButton({ label: 'Disconnect' });
@@ -141,6 +175,14 @@ export const Reconnects: Story = {
141
175
  maxReconnectTries: 5,
142
176
  openOnMount: true,
143
177
  },
178
+ parameters: {
179
+ docs: {
180
+ description: {
181
+ story:
182
+ 'Renders the useReqraftWebSocket demo with reconnect enabled. When the server is killed, the socket transitions to CONNECTING and then back to OPEN once the server returns.',
183
+ },
184
+ },
185
+ },
144
186
  play: async ({ args, ...rest }) => {
145
187
  await OpenOnMount.play({ args, ...rest });
146
188
  await testsClickButton({ label: 'Kill' });
@@ -159,6 +201,12 @@ export const ReconnectFails: Story = {
159
201
  reconnectInterval: 500,
160
202
  },
161
203
  parameters: {
204
+ docs: {
205
+ description: {
206
+ story:
207
+ 'Renders the useReqraftWebSocket demo with a low maxReconnectTries. When the server never returns, the socket exhausts its reconnect attempts and fires onReconnectFailed.',
208
+ },
209
+ },
162
210
  jest: {
163
211
  timeout: 60000,
164
212
  },
@@ -178,6 +226,14 @@ export const SendMessage: Story = {
178
226
  includeSentMessagesInState: true,
179
227
  useState: true,
180
228
  },
229
+ parameters: {
230
+ docs: {
231
+ description: {
232
+ story:
233
+ 'Renders the useReqraftWebSocket demo with useState enabled. When Send is clicked, the message is dispatched and the echoed reply appears in the accumulated message list.',
234
+ },
235
+ },
236
+ },
181
237
  play: async ({ args, ...rest }) => {
182
238
  await OpenOnMount.play({ args, ...rest });
183
239
  await testsClickButton({ label: 'Send' });
@@ -197,6 +253,14 @@ export const WithLogs: Story = {
197
253
  useState: true,
198
254
  reconnectInterval: 1500,
199
255
  },
256
+ parameters: {
257
+ docs: {
258
+ description: {
259
+ story:
260
+ 'Renders the useReqraftWebSocket demo with includeLogMessagesInState. Connect / disconnect / reconnect lifecycle events are captured as log entries alongside regular messages.',
261
+ },
262
+ },
263
+ },
200
264
  play: async ({ args, ...rest }) => {
201
265
  await Reconnects.play({ args, ...rest });
202
266
 
@@ -206,6 +270,15 @@ export const WithLogs: Story = {
206
270
 
207
271
  export const ClearsMessages: Story = {
208
272
  ...SendMessage,
273
+ parameters: {
274
+ ...SendMessage.parameters,
275
+ docs: {
276
+ description: {
277
+ story:
278
+ 'Renders the useReqraftWebSocket demo after sending a message. When Clear is clicked, the accumulated message list is emptied.',
279
+ },
280
+ },
281
+ },
209
282
  play: async ({ args, ...rest }) => {
210
283
  await SendMessage.play({ args, ...rest });
211
284
  await testsClickButton({ label: 'Clear' });
@@ -353,6 +426,14 @@ export const MultipleConnections: Story = {
353
426
  includeLogMessagesInState: true,
354
427
  useState: true,
355
428
  },
429
+ parameters: {
430
+ docs: {
431
+ description: {
432
+ story:
433
+ 'Renders three panels that each call useReqraftWebSocket against the same URL — proves the connections share one pooled underlying WebSocket while keeping independent message state.',
434
+ },
435
+ },
436
+ },
356
437
  // @ts-expect-error customprops
357
438
  render: (args: IUseReqraftWebSocketOptions) => {
358
439
  const [conectionStatus, setConnectionStatus] = useState<string>('CLOSED');
@@ -405,6 +486,15 @@ export const MultipleConnectionsOpenOnMount: Story = {
405
486
  ...MultipleConnections.args,
406
487
  openOnMount: true,
407
488
  },
489
+ parameters: {
490
+ ...MultipleConnections.parameters,
491
+ docs: {
492
+ description: {
493
+ story:
494
+ 'Renders three pooled useReqraftWebSocket panels with openOnMount — all three transition to OPEN over the shared underlying socket without any user action.',
495
+ },
496
+ },
497
+ },
408
498
  play: async ({ args }) => {
409
499
  await testsWaitForText('First Connection Status: OPEN');
410
500
  await testsWaitForText('Second Connection Status: OPEN');
@@ -420,6 +510,15 @@ export const MultipleConnectionsClosedAtOnce: Story = {
420
510
  ...MultipleConnections.args,
421
511
  openOnMount: true,
422
512
  },
513
+ parameters: {
514
+ ...MultipleConnections.parameters,
515
+ docs: {
516
+ description: {
517
+ story:
518
+ 'Renders three pooled useReqraftWebSocket panels. When Close All closes the shared socket, every consumer transitions to CLOSED at once and each panel logs its close handler.',
519
+ },
520
+ },
521
+ },
423
522
  play: async (args) => {
424
523
  await MultipleConnectionsOpenOnMount.play(args);
425
524
 
@@ -436,6 +535,15 @@ export const ConnectionIsClosedWhenAllUsersAreClosed: Story = {
436
535
  ...MultipleConnections.args,
437
536
  openOnMount: true,
438
537
  },
538
+ parameters: {
539
+ ...MultipleConnections.parameters,
540
+ docs: {
541
+ description: {
542
+ story:
543
+ 'Renders three pooled useReqraftWebSocket panels. Closing them one by one keeps the shared socket OPEN until the last consumer unmounts, then it closes.',
544
+ },
545
+ },
546
+ },
439
547
  play: async (args) => {
440
548
  await MultipleConnectionsOpenOnMount.play(args);
441
549
 
@@ -454,6 +562,15 @@ export const MultipleConnectionsHaveCustomHandlers: Story = {
454
562
  ...MultipleConnections.args,
455
563
  openOnMount: true,
456
564
  },
565
+ parameters: {
566
+ ...MultipleConnections.parameters,
567
+ docs: {
568
+ description: {
569
+ story:
570
+ 'Renders three pooled useReqraftWebSocket panels. Each panel attaches its own message handler, and sending a message triggers the handlers of only the connections still open.',
571
+ },
572
+ },
573
+ },
457
574
  play: async (args) => {
458
575
  const canvas = within(args.canvasElement);
459
576
 
@@ -480,6 +597,15 @@ export const MultipleConnectionsHaveCustomHandlers: Story = {
480
597
 
481
598
  export const MultipleConnectionsCanBeDisconnectedAndReconnected: Story = {
482
599
  ...MultipleConnectionsHaveCustomHandlers,
600
+ parameters: {
601
+ ...MultipleConnectionsHaveCustomHandlers.parameters,
602
+ docs: {
603
+ description: {
604
+ story:
605
+ 'Renders three pooled useReqraftWebSocket panels after custom handlers have fired. Disconnecting one and reconnecting another proves individual consumers can toggle without disturbing peers.',
606
+ },
607
+ },
608
+ },
483
609
  play: async (args) => {
484
610
  await MultipleConnectionsHaveCustomHandlers.play(args);
485
611