@pure-ds/storybook 0.4.17 → 0.4.19

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 (35) hide show
  1. package/.storybook/addons/html-preview/Panel.jsx +21 -21
  2. package/.storybook/addons/html-preview/preview.js +4 -5
  3. package/.storybook/manager.js +337 -49
  4. package/.storybook/preview-head.html +2 -2
  5. package/.storybook/preview.js +2 -2
  6. package/README.md +2 -2
  7. package/dist/pds-reference.json +1915 -261
  8. package/package.json +2 -2
  9. package/public/assets/css/app.css +2 -2
  10. package/public/assets/js/app.js +41 -22
  11. package/public/assets/js/pds.js +60 -41
  12. package/public/assets/pds/components/{pds-jsonform.js → pds-form.js} +536 -45
  13. package/public/assets/pds/custom-elements.json +8 -8
  14. package/public/assets/pds/vscode-custom-data.json +63 -63
  15. package/scripts/build-pds-reference.mjs +112 -38
  16. package/scripts/generate-stories.js +2 -2
  17. package/src/js/common/ask.js +48 -21
  18. package/src/js/pds-configurator/pds-config-form.js +9 -9
  19. package/src/js/pds-configurator/pds-demo.js +2 -2
  20. package/src/js/pds-core/pds-config.js +14 -14
  21. package/src/js/pds-core/pds-generator.js +25 -12
  22. package/src/js/pds-core/pds-ontology.js +5 -5
  23. package/src/js/pds.d.ts +2 -2
  24. package/stories/GettingStarted.stories.js +3 -0
  25. package/stories/WhatIsPDS.stories.js +3 -0
  26. package/stories/components/PdsForm.stories.js +4356 -0
  27. package/stories/components/{PdsJsonformUiSchema.md → PdsFormUiSchema.md} +2 -2
  28. package/stories/foundations/Spacing.stories.js +5 -5
  29. package/stories/layout/LayoutOverview.stories.js +13 -11
  30. package/stories/primitives/{Forms.stories.js → FormElements.stories.js} +3 -3
  31. package/stories/primitives/HtmlFormElements.stories.js +128 -0
  32. package/stories/primitives/{FormGroups.stories.js → HtmlFormGroups.stories.js} +70 -21
  33. package/stories/utils/PdsAsk.stories.js +14 -13
  34. package/stories/components/PdsJsonform.stories.js +0 -2007
  35. /package/src/{pds-core → node-api}/pds-api.js +0 -0
@@ -1,2007 +0,0 @@
1
- import { html } from 'lit';
2
- import { createComponentDocsPage } from '../reference/reference-docs.js';
3
- import showdown from 'showdown';
4
-
5
- const markdownConverter = new showdown.Converter({ tables: true });
6
-
7
- const uiSchemaReferenceMarkdown = `
8
- # uiSchema Reference
9
-
10
- Complete reference for all **uiSchema** configuration options in \`pds-jsonform\`.
11
-
12
- The uiSchema controls *how* fields are rendered while JSON Schema defines *what* data to collect.
13
-
14
- ---
15
-
16
- ## Path Syntax
17
-
18
- Use **slash notation** (JSON Pointer) to target fields:
19
-
20
- | Target | Path |
21
- |--------|------|
22
- | Root form | \`"/"\` or root-level \`ui:*\` keys |
23
- | Top-level field | \`"/fieldName"\` or \`"fieldName"\` |
24
- | Nested field | \`"/parent/child"\` or \`"parent/child"\` |
25
-
26
- ---
27
-
28
- ## Root-Level Layout
29
-
30
- Apply layout to the **entire form** without modifying your JSON Schema:
31
-
32
- \`\`\`javascript
33
- const uiSchema = {
34
- 'ui:layout': 'grid',
35
- 'ui:layoutOptions': {
36
- columns: 2,
37
- gap: 'md'
38
- }
39
- };
40
- \`\`\`
41
-
42
- You can combine root-level layout with field-specific options:
43
-
44
- \`\`\`javascript
45
- const uiSchema = {
46
- // Root form layout
47
- 'ui:layout': 'grid',
48
- 'ui:layoutOptions': { columns: 2, gap: 'md' },
49
-
50
- // Field-specific options
51
- '/email': { 'ui:icon': 'envelope' },
52
- '/bio': { 'ui:widget': 'textarea' }
53
- };
54
- \`\`\`
55
-
56
- ---
57
-
58
- ## Widget Selection
59
-
60
- ### \`ui:widget\`
61
-
62
- Override the automatically selected widget for a field.
63
-
64
- | Widget | Use For | Notes |
65
- |--------|---------|-------|
66
- | \`input-text\` | String | Default for strings |
67
- | \`textarea\` | String | Multi-line text |
68
- | \`password\` | String | Masked input |
69
- | \`input-email\` | String | Email with validation |
70
- | \`input-url\` | String | URL with validation |
71
- | \`input-number\` | Number | Default for numbers |
72
- | \`input-range\` | Number | Slider control |
73
- | \`input-date\` | String | Date picker |
74
- | \`input-time\` | String | Time picker |
75
- | \`input-datetime\` | String | Date + time picker |
76
- | \`input-color\` | String | Color picker |
77
- | \`checkbox\` | Boolean | Default for booleans |
78
- | \`toggle\` | Boolean | Toggle switch |
79
- | \`select\` | String (enum) | Default for enums |
80
- | \`radio\` | String (enum) | Radio button group |
81
- | \`checkbox-group\` | Array (enum items) | Multi-select checkboxes |
82
- | \`upload\` | String | File upload (pds-upload) |
83
- | \`richtext\` | String | Rich text editor (pds-richtext) |
84
- | \`const\` | Any | Read-only display |
85
-
86
- ---
87
-
88
- ## Widget Options
89
-
90
- ### \`ui:options\`
91
-
92
- Widget-specific configuration object.
93
-
94
- #### Textarea Options
95
- | Option | Type | Description |
96
- |--------|------|-------------|
97
- | \`rows\` | number | Number of visible rows |
98
- | \`cols\` | number | Number of visible columns |
99
-
100
- #### Range Slider Options
101
- | Option | Type | Description |
102
- |--------|------|-------------|
103
- | \`min\` | number | Minimum value |
104
- | \`max\` | number | Maximum value |
105
- | \`step\` | number | Step increment |
106
-
107
- #### Upload Options (pds-upload)
108
- | Option | Type | Description |
109
- |--------|------|-------------|
110
- | \`accept\` | string | MIME types (e.g., \`"image/*"\`) |
111
- | \`maxSize\` | number | Max file size in bytes |
112
- | \`multiple\` | boolean | Allow multiple files |
113
- | \`label\` | string | Upload button label |
114
-
115
- #### Rich Text Options (pds-richtext)
116
- | Option | Type | Description |
117
- |--------|------|-------------|
118
- | \`toolbar\` | string | \`"minimal"\`, \`"standard"\`, \`"full"\` |
119
- | \`spellcheck\` | boolean | Enable spellcheck |
120
- | \`format\` | string | \`"html"\` or \`"markdown"\` for output |
121
- | \`submitOnEnter\` | boolean | Submit form on Enter |
122
-
123
- ---
124
-
125
- ## Layout Options
126
-
127
- ### \`ui:layout\`
128
-
129
- Control how nested object fields are arranged.
130
-
131
- | Value | Description |
132
- |-------|-------------|
133
- | \`default\` | Standard vertical fieldset |
134
- | \`flex\` | Flexbox row layout |
135
- | \`grid\` | CSS Grid layout |
136
- | \`accordion\` | Collapsible \`<details>\` sections |
137
- | \`tabs\` | Tabbed interface (pds-tabstrip) |
138
-
139
- ### \`ui:layoutOptions\`
140
-
141
- Layout-specific configuration.
142
-
143
- #### Flex Layout Options
144
- | Option | Type | Description |
145
- |--------|------|-------------|
146
- | \`wrap\` | boolean | Allow wrapping |
147
- | \`gap\` | string | Gap size: \`"sm"\`, \`"md"\`, \`"lg"\` |
148
- | \`direction\` | string | \`"row"\` or \`"column"\` |
149
-
150
- #### Grid Layout Options
151
- | Option | Type | Description |
152
- |--------|------|-------------|
153
- | \`columns\` | number | Number of columns |
154
- | \`gap\` | string | Gap size: \`"sm"\`, \`"md"\`, \`"lg"\` |
155
-
156
- #### Accordion Layout Options
157
- | Option | Type | Description |
158
- |--------|------|-------------|
159
- | \`openFirst\` | boolean | First section open by default |
160
-
161
- ---
162
-
163
- ## Field Customization
164
-
165
- ### Display Options
166
-
167
- | Option | Type | Description |
168
- |--------|------|-------------|
169
- | \`ui:help\` | string | Help text below field (overrides \`description\`) |
170
- | \`ui:placeholder\` | string | Placeholder text (overrides \`examples\`) |
171
- | \`ui:class\` | string | Additional CSS classes |
172
- | \`ui:order\` | string[] | Field order within object |
173
-
174
- ### State Options
175
-
176
- | Option | Type | Description |
177
- |--------|------|-------------|
178
- | \`ui:hidden\` | boolean | Hide field from UI (still in data) |
179
- | \`ui:readonly\` | boolean | Make field read-only |
180
- | \`ui:disabled\` | boolean | Disable field |
181
-
182
- ---
183
-
184
- ## Icon Enhancement
185
-
186
- ### \`ui:icon\`
187
-
188
- Add an icon to the input field. Requires \`pds-icon\` component.
189
-
190
- | Option | Type | Description |
191
- |--------|------|-------------|
192
- | \`ui:icon\` | string | Icon name from sprite |
193
- | \`ui:iconPosition\` | string | \`"start"\` or \`"end"\` |
194
-
195
- ---
196
-
197
- ## Surface Wrapping
198
-
199
- ### \`ui:surface\`
200
-
201
- Wrap a fieldset in a styled container.
202
-
203
- | Value | Description |
204
- |-------|-------------|
205
- | \`card\` | Card surface |
206
- | \`elevated\` | Elevated surface with shadow |
207
- | \`sunken\` | Sunken/inset surface |
208
-
209
- ---
210
-
211
- ## Dialog Forms
212
-
213
- ### \`ui:dialog\`
214
-
215
- Collect nested object data in a modal dialog instead of inline.
216
-
217
- | Option | Type | Description |
218
- |--------|------|-------------|
219
- | \`ui:dialog\` | boolean | Enable dialog mode |
220
-
221
- ### \`ui:dialogOptions\`
222
-
223
- | Option | Type | Description |
224
- |--------|------|-------------|
225
- | \`buttonLabel\` | string | Trigger button label |
226
- | \`dialogTitle\` | string | Dialog title |
227
- | \`icon\` | string | Icon for trigger button |
228
-
229
- ---
230
-
231
- ## Autocomplete
232
-
233
- ### \`ui:datalist\`
234
-
235
- Provide autocomplete suggestions using native \`<datalist>\`.
236
-
237
- | Option | Type | Description |
238
- |--------|------|-------------|
239
- | \`ui:datalist\` | string[] | Array of suggestion values |
240
-
241
- ### \`ui:autocomplete\`
242
-
243
- HTML autocomplete attribute hint.
244
-
245
- | Option | Type | Description |
246
- |--------|------|-------------|
247
- | \`ui:autocomplete\` | string | e.g., \`"email"\`, \`"new-password"\`, \`"tel"\` |
248
-
249
- ---
250
-
251
- ## Enhanced Select
252
-
253
- ### \`ui:dropdown\`
254
-
255
- Use enhanced dropdown menu instead of native select.
256
-
257
- | Option | Type | Description |
258
- |--------|------|-------------|
259
- | \`ui:dropdown\` | boolean | Enable enhanced dropdown |
260
- `;
261
-
262
- const docsParameters = {
263
- description: {
264
- component: `**⭐ Recommended for modern applications** - Automatically generate complete forms from JSON Schema definitions.
265
-
266
- ### Key Features
267
- - 🎯 **Zero boilerplate** - Define form structure in JSON, get a working form with validation
268
- - ✅ **Built-in validation** - Automatic validation based on schema rules (required, min/max, patterns, etc.)
269
- - 🔄 **Data binding** - Two-way data binding with form state management
270
- - 🎨 **PDS styled** - Uses all PDS design tokens automatically
271
- - 📱 **Responsive** - Mobile-friendly layouts out of the box
272
- - 🧩 **Conditional logic** - Show/hide fields based on other field values
273
- - 🌐 **Nested objects** - Support for complex nested data structures
274
- - 🔧 **Extensible** - Custom field types and validators
275
-
276
- ### Why JSON Schema Forms?
277
- Instead of manually writing HTML for every form field, validation rule, and error message, you define your data schema once and get:
278
- - Form UI generation
279
- - Client-side validation
280
- - Server-side validation (same schema)
281
- - API documentation
282
- - Type definitions
283
- - Database schemas
284
-
285
- See the examples below to get started, or check the [primitive forms](/story/primitives-forms--default) for manual form building.
286
- `
287
- },
288
- page: createComponentDocsPage('pds-jsonform', {
289
- hideStories: true,
290
- additionalContent: markdownConverter.makeHtml(uiSchemaReferenceMarkdown)
291
- }),
292
- toc: true
293
- };
294
-
295
- export default {
296
- title: 'Components/Pds Jsonform',
297
- tags: ['autodocs', 'form', 'forms', 'jsonform', 'json-schema', 'validation', 'input'],
298
- parameters: {
299
- pds: {
300
- tags: ['form', 'forms', 'jsonform', 'json-schema', 'validation', 'input', 'pds-jsonform', 'interaction']
301
- },
302
- docs: docsParameters
303
- }
304
- };
305
-
306
- const simpleSchema = {
307
- type: 'object',
308
- properties: {
309
- name: {
310
- type: 'string',
311
- title: 'Full Name',
312
- examples: ['John Doe']
313
- },
314
- email: {
315
- type: 'string',
316
- format: 'email',
317
- title: 'Email Address',
318
- examples: ['john.doe@example.com']
319
- },
320
- age: {
321
- type: 'number',
322
- title: 'Age',
323
- minimum: 18,
324
- examples: [25]
325
- },
326
- newsletter: {
327
- type: 'boolean',
328
- title: 'Subscribe to newsletter'
329
- }
330
- },
331
- required: ['name', 'email']
332
- };
333
-
334
- const complexSchema = {
335
- type: 'object',
336
- properties: {
337
- personalInfo: {
338
- type: 'object',
339
- title: 'Personal Information',
340
- properties: {
341
- firstName: { type: 'string', title: 'First Name', examples: ['John'] },
342
- lastName: { type: 'string', title: 'Last Name', examples: ['Doe'] },
343
- dateOfBirth: { type: 'string', format: 'date', title: 'Date of Birth' }
344
- }
345
- },
346
- address: {
347
- type: 'object',
348
- title: 'Address',
349
- properties: {
350
- street: { type: 'string', title: 'Street', examples: ['123 Main Street'] },
351
- city: { type: 'string', title: 'City', examples: ['New York'] },
352
- country: {
353
- type: 'string',
354
- title: 'Country',
355
- enum: ['USA', 'UK', 'Canada', 'Australia']
356
- }
357
- }
358
- },
359
- preferences: {
360
- type: 'array',
361
- title: 'Interests',
362
- items: {
363
- type: 'string',
364
- enum: ['Technology', 'Sports', 'Music', 'Travel', 'Reading']
365
- }
366
- }
367
- }
368
- };
369
-
370
- export const SimpleForm = {
371
- render: () => {
372
- return html`
373
- <pds-jsonform data-required
374
- .jsonSchema=${simpleSchema}
375
- @pw:submit=${(e) => toastFormData(e.detail)}
376
- ></pds-jsonform>
377
- `;
378
- }
379
- };
380
-
381
- export const ComplexForm = {
382
- render: () => {
383
- return html`
384
- <pds-jsonform
385
- .jsonSchema=${complexSchema}
386
- @pw:submit=${(e) => toastFormData(e.detail)}
387
- ></pds-jsonform>
388
- `;
389
- }
390
- };
391
-
392
- export const WithInitialData = {
393
- render: () => {
394
- const initialValues = {
395
- name: 'John Doe',
396
- email: 'john@example.com',
397
- age: 25,
398
- newsletter: true
399
- };
400
-
401
- return html`
402
- <pds-jsonform
403
- .jsonSchema=${simpleSchema}
404
- .values=${initialValues}
405
- @pw:value-change=${(e) => console.log('🔄 Value changed:', e.detail)}
406
- @pw:submit=${(e) => toastFormData(e.detail)}
407
- ></pds-jsonform>
408
- `;
409
- }
410
- };
411
-
412
- export const WithTogglesSwitches = {
413
- name: 'Toggles & Switches',
414
- render: () => {
415
- const schema = {
416
- type: 'object',
417
- properties: {
418
- toggles: {
419
- type: 'object',
420
- title: 'Preferences',
421
- properties: {
422
- emailNotifications: {
423
- type: 'boolean',
424
- title: 'Email Notifications',
425
- description: 'Receive notifications via email'
426
- },
427
- pushNotifications: {
428
- type: 'boolean',
429
- title: 'Push Notifications',
430
- description: 'Receive push notifications on your device'
431
- },
432
- darkMode: {
433
- type: 'boolean',
434
- title: 'Dark Mode',
435
- description: 'Enable dark theme'
436
- },
437
- autoSave: {
438
- type: 'boolean',
439
- title: 'Auto-save',
440
- description: 'Automatically save changes'
441
- }
442
- }
443
- }
444
- }
445
- };
446
-
447
- const uiSchema = {
448
- toggles: {
449
- 'ui:layout': 'flex',
450
- 'ui:layoutOptions': {
451
- direction: 'column',
452
- gap: 'md'
453
- }
454
- }
455
- };
456
-
457
- const options = {
458
- widgets: {
459
- booleans: 'toggle' // Use toggle switches instead of checkboxes
460
- }
461
- };
462
-
463
- return html`
464
- <pds-jsonform
465
- .jsonSchema=${schema}
466
- .uiSchema=${uiSchema}
467
- .options=${options}
468
- @pw:submit=${(e) => toastFormData(e.detail)}
469
- ></pds-jsonform>
470
- `;
471
- }
472
- };
473
-
474
- export const WithRangeSliders = {
475
- name: 'Range Sliders with Output',
476
- render: () => {
477
- const schema = {
478
- type: 'object',
479
- properties: {
480
- volume: {
481
- type: 'number',
482
- title: 'Volume',
483
- minimum: 0,
484
- maximum: 100,
485
- default: 50
486
- },
487
- brightness: {
488
- type: 'number',
489
- title: 'Brightness',
490
- minimum: 0,
491
- maximum: 100,
492
- default: 75
493
- },
494
- fontSize: {
495
- type: 'number',
496
- title: 'Font Size',
497
- minimum: 10,
498
- maximum: 24,
499
- default: 16
500
- },
501
- quality: {
502
- type: 'integer',
503
- title: 'Quality',
504
- minimum: 1,
505
- maximum: 10,
506
- default: 7
507
- }
508
- }
509
- };
510
-
511
- const uiSchema = {
512
- volume: { 'ui:widget': 'input-range' },
513
- brightness: { 'ui:widget': 'input-range' },
514
- fontSize: { 'ui:widget': 'input-range' },
515
- quality: { 'ui:widget': 'input-range' }
516
- };
517
-
518
- const options = {
519
- enhancements: {
520
- rangeOutput: true // Add live value display
521
- }
522
- };
523
-
524
- return html`
525
- <pds-jsonform
526
- .jsonSchema=${schema}
527
- .uiSchema=${uiSchema}
528
- .options=${options}
529
- @pw:value-change=${(e) => console.log('🎚️ Value changed:', e.detail)}
530
- @pw:submit=${(e) => toastFormData(e.detail)}
531
- ></pds-jsonform>
532
- `;
533
- }
534
- };
535
-
536
- export const WithIcons = {
537
- name: 'Icon-Enhanced Inputs',
538
- render: () => {
539
- const schema = {
540
- type: 'object',
541
- properties: {
542
- username: {
543
- type: 'string',
544
- title: 'Username',
545
- examples: ['Enter your username']
546
- },
547
- email: {
548
- type: 'string',
549
- format: 'email',
550
- title: 'Email',
551
- examples: ['your.email@example.com']
552
- },
553
- password: {
554
- type: 'string',
555
- title: 'Password',
556
- examples: ['••••••••']
557
- },
558
- website: {
559
- type: 'string',
560
- format: 'uri',
561
- title: 'Website',
562
- examples: ['https://yourwebsite.com']
563
- },
564
- phone: {
565
- type: 'string',
566
- title: 'Phone',
567
- examples: ['+1 (555) 123-4567']
568
- },
569
- location: {
570
- type: 'string',
571
- title: 'Location',
572
- examples: ['City, Country']
573
- }
574
- },
575
- required: ['username', 'email', 'password']
576
- };
577
-
578
- const uiSchema = {
579
- username: {
580
- 'ui:icon': 'user',
581
- 'ui:iconPosition': 'start'
582
- },
583
- email: {
584
- 'ui:icon': 'envelope',
585
- 'ui:iconPosition': 'start'
586
- },
587
- password: {
588
- 'ui:icon': 'lock',
589
- 'ui:iconPosition': 'start',
590
- 'ui:widget': 'password'
591
- },
592
- website: {
593
- 'ui:icon': 'globe',
594
- 'ui:iconPosition': 'start'
595
- },
596
- phone: {
597
- 'ui:icon': 'phone',
598
- 'ui:iconPosition': 'start'
599
- },
600
- location: {
601
- 'ui:icon': 'map-pin',
602
- 'ui:iconPosition': 'start'
603
- }
604
- };
605
-
606
- return html`
607
- <pds-jsonform
608
- .jsonSchema=${schema}
609
- .uiSchema=${uiSchema}
610
- @pw:submit=${(e) => toastFormData(e.detail)}
611
- ></pds-jsonform>
612
- `;
613
- }
614
- };
615
-
616
- export const WithPdsUpload = {
617
- name: 'File Upload (pds-upload)',
618
- render: () => {
619
- const schema = {
620
- type: 'object',
621
- properties: {
622
- profilePicture: {
623
- type: 'string',
624
- title: 'Profile Picture',
625
- description: 'Upload your profile photo (JPG, PNG)',
626
- contentMediaType: 'image/*',
627
- contentEncoding: 'base64'
628
- },
629
- resume: {
630
- type: 'string',
631
- title: 'Resume',
632
- description: 'Upload your resume (PDF)',
633
- contentMediaType: 'application/pdf',
634
- contentEncoding: 'base64'
635
- },
636
- portfolio: {
637
- type: 'string',
638
- title: 'Portfolio Files',
639
- description: 'Upload multiple portfolio items',
640
- contentMediaType: 'image/*,application/pdf',
641
- contentEncoding: 'base64'
642
- }
643
- }
644
- };
645
-
646
- const uiSchema = {
647
- profilePicture: {
648
- 'ui:options': {
649
- accept: 'image/jpeg,image/png',
650
- maxSize: 5242880, // 5MB
651
- label: 'Choose photo'
652
- }
653
- },
654
- resume: {
655
- 'ui:options': {
656
- accept: 'application/pdf',
657
- maxSize: 10485760, // 10MB
658
- label: 'Choose PDF'
659
- }
660
- },
661
- portfolio: {
662
- 'ui:options': {
663
- multiple: true,
664
- accept: 'image/*,application/pdf',
665
- label: 'Choose files'
666
- }
667
- }
668
- };
669
-
670
- return html`
671
- <pds-jsonform
672
- .jsonSchema=${schema}
673
- .uiSchema=${uiSchema}
674
- @pw:submit=${(e) => toastFormData(e.detail)}
675
- ></pds-jsonform>
676
- `;
677
- }
678
- };
679
-
680
- export const WithPdsRichtext = {
681
- name: 'Rich Text Editor (pds-richtext)',
682
- render: () => {
683
- const schema = {
684
- type: 'object',
685
- properties: {
686
- bio: {
687
- type: 'string',
688
- title: 'Biography',
689
- description: 'Tell us about yourself',
690
- examples: ['Write your biography...']
691
- },
692
- coverLetter: {
693
- type: 'string',
694
- title: 'Cover Letter',
695
- description: 'Write your cover letter',
696
- examples: ['Write your cover letter...']
697
- },
698
- jobDescription: {
699
- type: 'string',
700
- title: 'Job Description',
701
- examples: ['Describe the position...']
702
- }
703
- }
704
- };
705
-
706
- const uiSchema = {
707
- bio: {
708
- 'ui:widget': 'richtext',
709
- 'ui:options': {
710
- toolbar: 'minimal'
711
- }
712
- },
713
- coverLetter: {
714
- 'ui:widget': 'richtext',
715
- 'ui:options': {
716
- toolbar: 'standard'
717
- }
718
- },
719
- jobDescription: {
720
- 'ui:widget': 'richtext',
721
- 'ui:options': {
722
- toolbar: 'full'
723
- }
724
- }
725
- };
726
-
727
- return html`
728
- <pds-jsonform
729
- .jsonSchema=${schema}
730
- .uiSchema=${uiSchema}
731
- @pw:submit=${(e) => toastFormData(e.detail)}
732
- ></pds-jsonform>
733
- `;
734
- }
735
- };
736
-
737
- export const WithFlexLayout = {
738
- name: 'Flex Layout',
739
- render: () => {
740
- const schema = {
741
- type: 'object',
742
- properties: {
743
- contactInfo: {
744
- type: 'object',
745
- title: 'Contact Information',
746
- properties: {
747
- firstName: { type: 'string', title: 'First Name', examples: ['Jane'] },
748
- lastName: { type: 'string', title: 'Last Name', examples: ['Smith'] },
749
- email: { type: 'string', format: 'email', title: 'Email', examples: ['jane.smith@example.com'] },
750
- phone: { type: 'string', title: 'Phone', examples: ['+1 (555) 987-6543'] }
751
- }
752
- }
753
- }
754
- };
755
-
756
- const uiSchema = {
757
- contactInfo: {
758
- 'ui:layout': 'flex',
759
- 'ui:layoutOptions': {
760
- gap: 'md',
761
- wrap: true,
762
- direction: 'row'
763
- }
764
- }
765
- };
766
-
767
- return html`
768
- <pds-jsonform
769
- .jsonSchema=${schema}
770
- .uiSchema=${uiSchema}
771
- @pw:submit=${(e) => toastFormData(e.detail)}
772
- ></pds-jsonform>
773
- `;
774
- }
775
- };
776
-
777
- export const WithGridLayout = {
778
- name: 'Grid Layout',
779
- render: () => {
780
- const schema = {
781
- type: 'object',
782
- properties: {
783
- productInfo: {
784
- type: 'object',
785
- title: 'Product Information',
786
- properties: {
787
- name: { type: 'string', title: 'Product Name', examples: ['Wireless Headphones'] },
788
- sku: { type: 'string', title: 'SKU', examples: ['WH-1000XM4'] },
789
- price: { type: 'number', title: 'Price', examples: [299.99] },
790
- quantity: { type: 'integer', title: 'Quantity', examples: [50] },
791
- category: { type: 'string', title: 'Category', enum: ['Electronics', 'Clothing', 'Books', 'Home', 'Sports', 'Garden'] },
792
- brand: { type: 'string', title: 'Brand', examples: ['Sony'] },
793
- weight: { type: 'number', title: 'Weight (kg)', examples: [0.25] },
794
- dimensions: { type: 'string', title: 'Dimensions', examples: ['20 x 18 x 8 cm'] }
795
- }
796
- }
797
- }
798
- };
799
-
800
- const uiSchema = {
801
- productInfo: {
802
- 'ui:layout': 'grid',
803
- 'ui:layoutOptions': {
804
- columns: 3,
805
- gap: 'md'
806
- }
807
- }
808
- };
809
-
810
- return html`
811
- <pds-jsonform
812
- .jsonSchema=${schema}
813
- .uiSchema=${uiSchema}
814
- @pw:submit=${(e) => toastFormData(e.detail)}
815
- ></pds-jsonform>
816
- `;
817
- }
818
- };
819
-
820
- export const WithAccordionLayout = {
821
- name: 'Accordion Layout',
822
- render: () => {
823
- const schema = {
824
- type: 'object',
825
- properties: {
826
- name: {
827
- type: 'string',
828
- title: 'Full Name',
829
- examples: ['Alex Rodriguez']
830
- },
831
- email: {
832
- type: 'string',
833
- format: 'email',
834
- title: 'Email',
835
- examples: ['alex.rodriguez@example.com']
836
- },
837
- settings: {
838
- type: 'object',
839
- title: 'Settings',
840
- properties: {
841
- displaySettings: {
842
- type: 'object',
843
- title: 'Display Settings',
844
- properties: {
845
- theme: { type: 'string', title: 'Theme', enum: ['Light', 'Dark', 'Auto'], default: 'Auto' },
846
- fontSize: { type: 'number', title: 'Font Size (px)', minimum: 12, maximum: 24, default: 16 },
847
- density: { type: 'string', title: 'Density', enum: ['Compact', 'Comfortable', 'Spacious'], default: 'Comfortable' },
848
- animations: { type: 'boolean', title: 'Enable Animations', default: true }
849
- }
850
- },
851
- notificationSettings: {
852
- type: 'object',
853
- title: 'Notification Settings',
854
- properties: {
855
- email: { type: 'boolean', title: 'Email Notifications', default: true },
856
- push: { type: 'boolean', title: 'Push Notifications', default: false },
857
- sms: { type: 'boolean', title: 'SMS Notifications', default: false },
858
- frequency: { type: 'string', title: 'Frequency', enum: ['Real-time', 'Daily', 'Weekly'], default: 'Daily' }
859
- }
860
- },
861
- privacySettings: {
862
- type: 'object',
863
- title: 'Privacy Settings',
864
- properties: {
865
- profileVisibility: { type: 'string', title: 'Profile Visibility', enum: ['Public', 'Friends', 'Private'], default: 'Friends' },
866
- showEmail: { type: 'boolean', title: 'Show Email', default: false },
867
- showActivity: { type: 'boolean', title: 'Show Activity', default: true },
868
- allowMessages: { type: 'boolean', title: 'Allow Messages', default: true }
869
- }
870
- }
871
- }
872
- }
873
- },
874
- required: ['name', 'email']
875
- };
876
-
877
- const uiSchema = {
878
- settings: { 'ui:layout': 'accordion' }
879
- };
880
-
881
- return html`
882
- <pds-jsonform
883
- .jsonSchema=${schema}
884
- .uiSchema=${uiSchema}
885
- @pw:submit=${(e) => toastFormData(e.detail)}
886
- ></pds-jsonform>
887
- `;
888
- }
889
- };
890
-
891
- export const WithTabsLayout = {
892
- name: 'Tabs Layout (pds-tabstrip)',
893
- render: () => {
894
- const schema = {
895
- type: 'object',
896
- properties: {
897
- userSettings: {
898
- type: 'object',
899
- title: 'User Settings',
900
- properties: {
901
- account: {
902
- type: 'object',
903
- title: 'Account',
904
- properties: {
905
- username: { type: 'string', title: 'Username', examples: ['coolguy123'] },
906
- email: { type: 'string', format: 'email', title: 'Email', examples: ['user@example.com'] },
907
- password: { type: 'string', title: 'New Password', examples: ['••••••••'] }
908
- }
909
- },
910
- profile: {
911
- type: 'object',
912
- title: 'Profile',
913
- properties: {
914
- displayName: { type: 'string', title: 'Display Name', examples: ['Cool Guy'] },
915
- bio: { type: 'string', title: 'Bio', examples: ['Tell us about yourself...'] },
916
- location: { type: 'string', title: 'Location', examples: ['San Francisco, CA'] },
917
- website: { type: 'string', format: 'uri', title: 'Website', examples: ['https://mywebsite.com'] }
918
- }
919
- },
920
- privacy: {
921
- type: 'object',
922
- title: 'Privacy',
923
- properties: {
924
- publicProfile: { type: 'boolean', title: 'Public Profile' },
925
- showEmail: { type: 'boolean', title: 'Show Email' },
926
- allowMessages: { type: 'boolean', title: 'Allow Messages' },
927
- searchable: { type: 'boolean', title: 'Searchable' }
928
- }
929
- }
930
- }
931
- }
932
- }
933
- };
934
-
935
- const uiSchema = {
936
- userSettings: { 'ui:layout': 'tabs' }
937
- };
938
-
939
- return html`
940
- <pds-jsonform
941
- .jsonSchema=${schema}
942
- .uiSchema=${uiSchema}
943
- @pw:submit=${(e) => toastFormData(e.detail)}
944
- ></pds-jsonform>
945
- `;
946
- }
947
- };
948
-
949
- export const WithSurfaces = {
950
- name: 'Surface Wrapping (Cards)',
951
- render: () => {
952
- const schema = {
953
- type: 'object',
954
- properties: {
955
- cardGroups: {
956
- type: 'object',
957
- title: 'Product Catalog',
958
- properties: {
959
- product1: {
960
- type: 'object',
961
- title: 'Premium Membership',
962
- properties: {
963
- name: { type: 'string', title: 'Product Name', default: 'Premium Plan', examples: ['Premium Plan'] },
964
- price: { type: 'number', title: 'Price (USD)', default: 29.99, minimum: 0, examples: [29.99] },
965
- billing: { type: 'string', title: 'Billing Cycle', enum: ['Monthly', 'Quarterly', 'Yearly'], default: 'Monthly' },
966
- autoRenew: { type: 'boolean', title: 'Auto-Renew', default: true }
967
- }
968
- },
969
- product2: {
970
- type: 'object',
971
- title: 'Enterprise Solution',
972
- properties: {
973
- name: { type: 'string', title: 'Product Name', default: 'Enterprise', examples: ['Enterprise'] },
974
- seats: { type: 'integer', title: 'Number of Seats', default: 10, minimum: 1, examples: [10] },
975
- support: { type: 'string', title: 'Support Level', enum: ['Standard', 'Priority', '24/7'], default: 'Priority' },
976
- sla: { type: 'boolean', title: 'SLA Agreement', default: true }
977
- }
978
- },
979
- product3: {
980
- type: 'object',
981
- title: 'Developer Tools',
982
- properties: {
983
- name: { type: 'string', title: 'Product Name', default: 'Dev Tools Pro', examples: ['Dev Tools Pro'] },
984
- apiCalls: { type: 'integer', title: 'API Calls/Month', default: 100000, minimum: 1000, examples: [100000] },
985
- environments: { type: 'integer', title: 'Environments', default: 3, minimum: 1, maximum: 10, examples: [3] },
986
- monitoring: { type: 'boolean', title: 'Performance Monitoring', default: true }
987
- }
988
- },
989
- product4: {
990
- type: 'object',
991
- title: 'Storage Package',
992
- properties: {
993
- name: { type: 'string', title: 'Product Name', default: 'Cloud Storage+', examples: ['Cloud Storage+'] },
994
- storage: { type: 'number', title: 'Storage (TB)', default: 5, minimum: 1, maximum: 100, examples: [5] },
995
- bandwidth: { type: 'number', title: 'Bandwidth (TB)', default: 10, minimum: 1, examples: [10] },
996
- backup: { type: 'boolean', title: 'Automated Backup', default: true }
997
- }
998
- }
999
- }
1000
- }
1001
- }
1002
- };
1003
-
1004
- const uiSchema = {
1005
- cardGroups: {
1006
- 'ui:layout': 'grid',
1007
- 'ui:layoutOptions': {
1008
- columns: 'auto',
1009
- autoSize: 'md',
1010
- gap: 'md'
1011
- }
1012
- },
1013
- 'cardGroups/product1': {
1014
- 'ui:surface': 'surface-sunken'
1015
- },
1016
- 'cardGroups/product2': {
1017
- 'ui:surface': 'surface-inverse'
1018
- },
1019
- 'cardGroups/product3': {
1020
- 'ui:surface': 'card'
1021
- },
1022
- 'cardGroups/product4': {
1023
- 'ui:surface': 'elevated'
1024
- }
1025
- };
1026
-
1027
- return html`
1028
- <pds-jsonform
1029
- .jsonSchema=${schema}
1030
- .uiSchema=${uiSchema}
1031
- @pw:submit=${(e) => toastFormData(e.detail)}
1032
- ></pds-jsonform>
1033
- `;
1034
- }
1035
- };
1036
-
1037
- export const WithDialogForms = {
1038
- name: 'Dialog-Based Nested Forms',
1039
- parameters: {
1040
- docs: {
1041
- description: {
1042
- story: 'Dialog-based forms use `ui:dialog` to edit nested objects in modal dialogs. State is transferred via FormData when using `PDS.ask()` with `useForm: true`. Click "Edit" buttons to modify nested data, then submit the main form to see all changes preserved.'
1043
- }
1044
- }
1045
- },
1046
- render: () => {
1047
- const schema = {
1048
- type: 'object',
1049
- properties: {
1050
- projectName: {
1051
- type: 'string',
1052
- title: 'Project Name',
1053
- examples: ['Digital Transformation Initiative']
1054
- },
1055
- teamLead: {
1056
- type: 'object',
1057
- title: 'Team Lead',
1058
- properties: {
1059
- name: { type: 'string', title: 'Full Name', examples: ['Sarah Johnson'] },
1060
- email: { type: 'string', format: 'email', title: 'Email Address', examples: ['sarah.johnson@company.com'] },
1061
- phone: { type: 'string', title: 'Phone Number', examples: ['+1-555-0123'] },
1062
- department: { type: 'string', title: 'Department', examples: ['Engineering'] },
1063
- location: { type: 'string', title: 'Office Location', examples: ['New York Office'] }
1064
- }
1065
- },
1066
- budget: {
1067
- type: 'object',
1068
- title: 'Budget Details',
1069
- properties: {
1070
- amount: { type: 'number', title: 'Budget Amount', examples: [250000] },
1071
- currency: { type: 'string', title: 'Currency', enum: ['USD', 'EUR', 'GBP', 'JPY', 'AUD'] },
1072
- fiscalYear: { type: 'string', title: 'Fiscal Year', examples: ['2025'] },
1073
- department: { type: 'string', title: 'Cost Center', examples: ['IT-001'] },
1074
- approved: { type: 'boolean', title: 'Budget Approved' }
1075
- }
1076
- },
1077
- timeline: {
1078
- type: 'object',
1079
- title: 'Project Timeline',
1080
- properties: {
1081
- startDate: { type: 'string', format: 'date', title: 'Start Date' },
1082
- endDate: { type: 'string', format: 'date', title: 'End Date' },
1083
- milestones: { type: 'integer', title: 'Number of Milestones', minimum: 1, maximum: 20, examples: [8] },
1084
- status: { type: 'string', title: 'Status', enum: ['Planning', 'In Progress', 'On Hold', 'Completed'], default: 'Planning' }
1085
- }
1086
- }
1087
- }
1088
- };
1089
-
1090
- // Initial values to test state persistence
1091
- const initialValues = {
1092
- projectName: 'Digital Transformation Initiative',
1093
- teamLead: {
1094
- name: 'Sarah Johnson',
1095
- email: 'sarah.johnson@company.com',
1096
- phone: '+1-555-0123',
1097
- department: 'Engineering',
1098
- location: 'New York Office'
1099
- },
1100
- budget: {
1101
- amount: 250000,
1102
- currency: 'USD',
1103
- fiscalYear: '2025',
1104
- department: 'IT-001',
1105
- approved: true
1106
- },
1107
- timeline: {
1108
- startDate: '2025-01-15',
1109
- endDate: '2025-12-31',
1110
- milestones: 8,
1111
- status: 'In Progress'
1112
- }
1113
- };
1114
-
1115
- const uiSchema = {
1116
- projectName: {
1117
- 'ui:icon': 'folder',
1118
- 'ui:iconPosition': 'start'
1119
- },
1120
- teamLead: {
1121
- 'ui:dialog': true,
1122
- 'ui:dialogOptions': {
1123
- buttonLabel: 'Edit Team Lead',
1124
- dialogTitle: 'Team Lead Information',
1125
- icon: 'user-gear'
1126
- },
1127
- name: { 'ui:icon': 'user', 'ui:iconPosition': 'start' },
1128
- email: { 'ui:icon': 'envelope', 'ui:iconPosition': 'start' },
1129
- phone: { 'ui:icon': 'phone', 'ui:iconPosition': 'start' },
1130
- department: { 'ui:icon': 'building', 'ui:iconPosition': 'start' },
1131
- location: { 'ui:icon': 'map-pin', 'ui:iconPosition': 'start' }
1132
- },
1133
- budget: {
1134
- 'ui:dialog': true,
1135
- 'ui:dialogOptions': {
1136
- buttonLabel: 'Edit Budget',
1137
- dialogTitle: 'Budget Details',
1138
- icon: 'currency-dollar'
1139
- },
1140
- amount: { 'ui:icon': 'dollar-sign', 'ui:iconPosition': 'start' },
1141
- currency: { 'ui:icon': 'coins', 'ui:iconPosition': 'start' },
1142
- fiscalYear: { 'ui:icon': 'calendar', 'ui:iconPosition': 'start' },
1143
- department: { 'ui:icon': 'building', 'ui:iconPosition': 'start' }
1144
- },
1145
- timeline: {
1146
- 'ui:dialog': true,
1147
- 'ui:dialogOptions': {
1148
- buttonLabel: 'Edit Timeline',
1149
- dialogTitle: 'Project Timeline',
1150
- icon: 'calendar'
1151
- },
1152
- startDate: { 'ui:icon': 'calendar-check', 'ui:iconPosition': 'start' },
1153
- endDate: { 'ui:icon': 'calendar-xmark', 'ui:iconPosition': 'start' },
1154
- milestones: { 'ui:icon': 'flag', 'ui:iconPosition': 'start' },
1155
- status: { 'ui:icon': 'list-check', 'ui:iconPosition': 'start' }
1156
- }
1157
- };
1158
-
1159
- return html`
1160
- <pds-jsonform
1161
- .jsonSchema=${schema}
1162
- .uiSchema=${uiSchema}
1163
- .values=${initialValues}
1164
- @pw:submit=${(e) => toastFormData(e.detail)}
1165
- @pw:dialog-submit=${(e) => console.log('📝 Dialog saved:', e.detail)}
1166
- ></pds-jsonform>
1167
- `;
1168
- }
1169
- };
1170
-
1171
- export const WithRadioGroupOpen = {
1172
- name: 'Radio Group Open (Single Selection)',
1173
- parameters: {
1174
- docs: {
1175
- description: {
1176
- story: `When an array has \`maxItems: 1\`, it renders as a Radio Group Open, allowing single selection with the ability to add custom options.
1177
-
1178
- This is perfect for scenarios where users can choose one option from predefined choices or add their own custom value. The \`data-open\` enhancement automatically provides an input field to add new options dynamically.
1179
-
1180
- ### Key Features:
1181
- - **Single selection** - Only one option can be selected at a time (radio buttons)
1182
- - **Add custom options** - Users can type new options in the input field
1183
- - **Remove options** - Click the × button to remove options
1184
- - **Pre-populated** - Start with default options from the schema
1185
-
1186
- This pattern is ideal for fields like "Priority", "Status", "Category", or any single-choice field where users might need custom values.`
1187
- }
1188
- }
1189
- },
1190
- render: () => {
1191
- const schema = {
1192
- type: 'object',
1193
- properties: {
1194
- priority: {
1195
- type: 'array',
1196
- title: 'Project Priority',
1197
- description: 'Select one priority level or add your own',
1198
- items: {
1199
- type: 'string',
1200
- examples: ['High', 'Medium', 'Low']
1201
- },
1202
- default: ['High', 'Medium', 'Low'],
1203
- uniqueItems: true,
1204
- maxItems: 1
1205
- },
1206
- status: {
1207
- type: 'array',
1208
- title: 'Current Status',
1209
- description: 'Choose the current project status',
1210
- items: {
1211
- type: 'string',
1212
- examples: ['Planning', 'In Progress', 'Review', 'Completed']
1213
- },
1214
- default: ['Planning', 'In Progress', 'Review', 'Completed'],
1215
- uniqueItems: true,
1216
- maxItems: 1
1217
- },
1218
- department: {
1219
- type: 'array',
1220
- title: 'Department',
1221
- description: 'Select your department',
1222
- items: {
1223
- type: 'string',
1224
- examples: ['Engineering', 'Design', 'Marketing', 'Sales']
1225
- },
1226
- default: ['Engineering', 'Design', 'Marketing', 'Sales'],
1227
- uniqueItems: true,
1228
- maxItems: 1
1229
- }
1230
- },
1231
- required: ['priority', 'status']
1232
- };
1233
-
1234
- const initialValues = {
1235
- priority: ['High', 'Medium', 'Low'],
1236
- status: ['Planning', 'In Progress', 'Review', 'Completed'],
1237
- department: ['Engineering', 'Design', 'Marketing', 'Sales']
1238
- };
1239
-
1240
- return html`
1241
- <pds-jsonform
1242
- .jsonSchema=${schema}
1243
- .values=${initialValues}
1244
- @pw:value-change=${(e) => console.log('🔄 Value changed:', e.detail)}
1245
- @pw:submit=${(e) => toastFormData(e.detail)}
1246
- ></pds-jsonform>
1247
- `;
1248
- }
1249
- };
1250
-
1251
- export const WithDatalistAutocomplete = {
1252
- name: 'Datalist Autocomplete',
1253
- render: () => {
1254
- const schema = {
1255
- type: 'object',
1256
- properties: {
1257
- country: {
1258
- type: 'string',
1259
- title: 'Country',
1260
- examples: ['United States']
1261
- },
1262
- city: {
1263
- type: 'string',
1264
- title: 'City',
1265
- examples: ['New York']
1266
- },
1267
- skillset: {
1268
- type: 'string',
1269
- title: 'Primary Skill',
1270
- examples: ['JavaScript']
1271
- },
1272
- company: {
1273
- type: 'string',
1274
- title: 'Company',
1275
- examples: ['Microsoft']
1276
- }
1277
- }
1278
- };
1279
-
1280
- const uiSchema = {
1281
- country: {
1282
- 'ui:datalist': ['United States', 'United Kingdom', 'Canada', 'Australia', 'Germany', 'France', 'Spain', 'Italy', 'Japan', 'China', 'India', 'Brazil']
1283
- },
1284
- city: {
1285
- 'ui:datalist': ['New York', 'London', 'Tokyo', 'Paris', 'Berlin', 'Sydney', 'Toronto', 'Amsterdam', 'Singapore', 'Dubai']
1286
- },
1287
- skillset: {
1288
- 'ui:datalist': ['JavaScript', 'Python', 'Java', 'C++', 'Go', 'Rust', 'TypeScript', 'Ruby', 'PHP', 'Swift', 'Kotlin']
1289
- },
1290
- company: {
1291
- 'ui:datalist': ['Microsoft', 'Google', 'Apple', 'Amazon', 'Meta', 'Tesla', 'Netflix', 'Adobe', 'Salesforce', 'Oracle']
1292
- }
1293
- };
1294
-
1295
- return html`
1296
- <pds-jsonform
1297
- .jsonSchema=${schema}
1298
- .uiSchema=${uiSchema}
1299
- @pw:submit=${(e) => toastFormData(e.detail)}
1300
- ></pds-jsonform>
1301
- `;
1302
- }
1303
- };
1304
-
1305
- export const WithArrayFields = {
1306
- name: 'Dynamic Arrays (Add/Remove)',
1307
- parameters: {
1308
- docs: {
1309
- description: {
1310
- story: `Arrays in JSON Schema forms allow users to dynamically add and remove items. This is perfect for managing lists like team members, tasks, or any collection that can grow or shrink.
1311
-
1312
- ### Features:
1313
- - **Add items** - Click "Add" button to create new entries
1314
- - **Remove items** - Delete individual items with the "Remove" button
1315
- - **Reorder items** - Use up/down arrows to change order
1316
- - **Nested objects** - Each array item can contain complex nested data
1317
- - **Initial values** - Pre-populate with default items
1318
- - **Radio Group Open** - Arrays with \`maxItems: 1\` render as radio buttons for single selection`
1319
- }
1320
- }
1321
- },
1322
- render: () => {
1323
- const schema = {
1324
- type: 'object',
1325
- properties: {
1326
- projectName: {
1327
- type: 'string',
1328
- title: 'Project Name',
1329
- examples: ['Website Redesign Project']
1330
- },
1331
- priority: {
1332
- type: 'array',
1333
- title: 'Project Priority',
1334
- items: {
1335
- type: 'string',
1336
- examples: ['High', 'Medium', 'Low']
1337
- },
1338
- default: ['High', 'Medium', 'Low'],
1339
- uniqueItems: true,
1340
- maxItems: 1
1341
- },
1342
- tags: {
1343
- type: 'array',
1344
- title: 'Project Tags',
1345
- items: {
1346
- type: 'string'
1347
- },
1348
- default: ['web', 'design', 'frontend']
1349
- },
1350
- teamMembers: {
1351
- type: 'array',
1352
- title: 'Team Members',
1353
- items: {
1354
- type: 'object',
1355
- properties: {
1356
- name: {
1357
- type: 'string',
1358
- title: 'Full Name',
1359
- examples: ['Alice Johnson']
1360
- },
1361
- role: {
1362
- type: 'string',
1363
- title: 'Role',
1364
- enum: ['Developer', 'Designer', 'Project Manager', 'QA Engineer', 'DevOps'],
1365
- default: 'Developer'
1366
- },
1367
- email: {
1368
- type: 'string',
1369
- format: 'email',
1370
- title: 'Email',
1371
- examples: ['alice.johnson@company.com']
1372
- },
1373
- hours: {
1374
- type: 'number',
1375
- title: 'Hours/Week',
1376
- minimum: 1,
1377
- maximum: 40,
1378
- default: 40
1379
- }
1380
- },
1381
- required: ['name', 'role', 'email']
1382
- },
1383
- minItems: 1
1384
- },
1385
- milestones: {
1386
- type: 'array',
1387
- title: 'Project Milestones',
1388
- items: {
1389
- type: 'object',
1390
- properties: {
1391
- title: {
1392
- type: 'string',
1393
- title: 'Milestone Title',
1394
- examples: ['MVP Launch']
1395
- },
1396
- dueDate: {
1397
- type: 'string',
1398
- format: 'date',
1399
- title: 'Due Date'
1400
- },
1401
- completed: {
1402
- type: 'boolean',
1403
- title: 'Completed',
1404
- default: false
1405
- }
1406
- },
1407
- required: ['title', 'dueDate']
1408
- }
1409
- }
1410
-
1411
- },
1412
- required: ['projectName', 'teamMembers']
1413
- };
1414
-
1415
- // Initial values to demonstrate pre-populated arrays
1416
- const initialValues = {
1417
- projectName: 'Website Redesign Project',
1418
- priority: ['High', 'Medium', 'Low'],
1419
- teamMembers: [
1420
- {
1421
- name: 'Alice Johnson',
1422
- role: 'Project Manager',
1423
- email: 'alice.johnson@company.com',
1424
- hours: 40
1425
- },
1426
- {
1427
- name: 'Bob Smith',
1428
- role: 'Developer',
1429
- email: 'bob.smith@company.com',
1430
- hours: 35
1431
- }
1432
- ],
1433
- milestones: [
1434
- {
1435
- title: 'Design Phase Complete',
1436
- dueDate: '2025-02-01',
1437
- completed: true
1438
- },
1439
- {
1440
- title: 'MVP Launch',
1441
- dueDate: '2025-04-15',
1442
- completed: false
1443
- }
1444
- ],
1445
- tags: ['web', 'design', 'frontend', 'responsive']
1446
- };
1447
-
1448
- const uiSchema = {
1449
- teamMembers: {
1450
- 'ui:layout': 'default',
1451
- role: {
1452
- 'ui:widget': 'select'
1453
- }
1454
- }
1455
- };
1456
-
1457
- return html`
1458
- <pds-jsonform
1459
- .jsonSchema=${schema}
1460
- .uiSchema=${uiSchema}
1461
- .values=${initialValues}
1462
- @pw:array-add=${(e) => console.log('➕ Item added to:', e.detail.path)}
1463
- @pw:array-remove=${(e) => console.log('➖ Item removed from:', e.detail.path, 'at index:', e.detail.index)}
1464
- @pw:array-reorder=${(e) => console.log('🔄 Item moved from', e.detail.from, 'to', e.detail.to, 'in:', e.detail.path)}
1465
- @pw:value-change=${(e) => console.log('🔄 Value changed:', e.detail)}
1466
- @pw:submit=${(e) => toastFormData(e.detail)}
1467
- ></pds-jsonform>
1468
- `;
1469
- }
1470
- };
1471
-
1472
- export const ComprehensiveExample = {
1473
- name: 'All Features Combined',
1474
- render: () => {
1475
- const schema = {
1476
- type: 'object',
1477
- properties: {
1478
- userProfile: {
1479
- type: 'object',
1480
- title: 'User Profile',
1481
- properties: {
1482
- personalInfo: {
1483
- type: 'object',
1484
- title: 'Personal Information',
1485
- properties: {
1486
- firstName: { type: 'string', title: 'First Name', examples: ['John'] },
1487
- lastName: { type: 'string', title: 'Last Name', examples: ['Doe'] },
1488
- email: { type: 'string', format: 'email', title: 'Email', examples: ['john.doe@example.com'] },
1489
- phone: { type: 'string', title: 'Phone', examples: ['+1 (555) 123-4567'] },
1490
- dateOfBirth: { type: 'string', format: 'date', title: 'Date of Birth' }
1491
- },
1492
- required: ['firstName', 'lastName', 'email']
1493
- },
1494
- settings: {
1495
- type: 'object',
1496
- title: 'Settings',
1497
- properties: {
1498
- accountSettings: {
1499
- type: 'object',
1500
- title: 'Account Settings',
1501
- properties: {
1502
- notifications: { type: 'boolean', title: 'Email Notifications' },
1503
- newsletter: { type: 'boolean', title: 'Newsletter Subscription' },
1504
- twoFactor: { type: 'boolean', title: 'Two-Factor Authentication' }
1505
- }
1506
- },
1507
- preferences: {
1508
- type: 'object',
1509
- title: 'Preferences',
1510
- properties: {
1511
- theme: { type: 'string', title: 'Theme', enum: ['Light', 'Dark', 'Auto'] },
1512
- language: { type: 'string', title: 'Language', enum: ['English', 'Spanish', 'French', 'German', 'Chinese', 'Japanese'] },
1513
- fontSize: { type: 'number', title: 'Font Size', minimum: 12, maximum: 20, default: 14 },
1514
- lineHeight: { type: 'number', title: 'Line Height', minimum: 1.0, maximum: 2.0, default: 1.5 }
1515
- }
1516
- }
1517
- }
1518
- }
1519
- }
1520
- },
1521
- profile: {
1522
- type: 'object',
1523
- title: 'Profile',
1524
- properties: {
1525
- avatar: {
1526
- type: 'string',
1527
- title: 'Profile Picture',
1528
- contentMediaType: 'image/*',
1529
- contentEncoding: 'base64'
1530
- },
1531
- bio: { type: 'string', title: 'Biography', examples: ['Tell us about yourself...'] },
1532
- website: { type: 'string', format: 'uri', title: 'Website', examples: ['https://yourwebsite.com'] }
1533
- }
1534
- }
1535
- }
1536
- };
1537
-
1538
- const uiSchema = {
1539
- userProfile: {
1540
- 'ui:layout': 'accordion',
1541
- 'ui:layoutOptions': { openFirst: true },
1542
- 'ui:surface': 'elevated',
1543
- personalInfo: {
1544
- 'ui:layout': 'grid',
1545
- 'ui:layoutOptions': { columns: 2, gap: 'md' },
1546
- 'ui:surface': 'sunken',
1547
- email: { 'ui:icon': 'envelope', 'ui:iconPosition': 'start' },
1548
- phone: { 'ui:icon': 'phone', 'ui:iconPosition': 'start' }
1549
- },
1550
- settings: {
1551
- accountSettings: {
1552
- 'ui:surface': 'sunken'
1553
- },
1554
- preferences: {
1555
- 'ui:surface': 'sunken',
1556
- theme: { 'ui:class': 'buttons' },
1557
- fontSize: { 'ui:widget': 'input-range' },
1558
- lineHeight: { 'ui:widget': 'input-range' }
1559
- }
1560
- }
1561
- },
1562
- profile: {
1563
- 'ui:dialog': true,
1564
- 'ui:dialogOptions': {
1565
- buttonLabel: 'Edit Profile',
1566
- dialogTitle: 'Your Profile Information'
1567
- },
1568
- avatar: {
1569
- 'ui:options': {
1570
- accept: 'image/*',
1571
- maxSize: 5242880,
1572
- label: 'Upload Avatar'
1573
- }
1574
- },
1575
- bio: {
1576
- 'ui:widget': 'richtext',
1577
- 'ui:options': {
1578
- toolbar: 'standard'
1579
- }
1580
- },
1581
- website: { 'ui:icon': 'globe', 'ui:iconPosition': 'start' }
1582
- }
1583
- };
1584
-
1585
- const options = {
1586
- widgets: {
1587
- booleans: 'toggle'
1588
- },
1589
- enhancements: {
1590
- rangeOutput: true
1591
- }
1592
- };
1593
-
1594
- return html`
1595
- <pds-jsonform
1596
- .jsonSchema=${schema}
1597
- .uiSchema=${uiSchema}
1598
- .options=${options}
1599
- @pw:value-change=${(e) => console.log('🔄 Changed:', e.detail)}
1600
- @pw:submit=${(e) => toastFormData(e.detail)}
1601
- ></pds-jsonform>
1602
- `;
1603
- }
1604
- };
1605
-
1606
- export const CustomFormActions = {
1607
- name: 'Custom Form Actions',
1608
- parameters: {
1609
- docs: {
1610
- description: {
1611
- story: `Demonstrates using \`hide-actions\` to provide custom form submission buttons and handling.
1612
-
1613
- When \`hide-actions\` is set, the default Submit and Reset buttons are hidden, allowing you to create custom action buttons in the \`actions\` slot. You can then handle form submission programmatically using the form's \`submit()\` method or by manually triggering the form element.`
1614
- }
1615
- }
1616
- },
1617
- render: () => {
1618
- const schema = {
1619
- type: 'object',
1620
- properties: {
1621
- username: {
1622
- type: 'string',
1623
- title: 'Username',
1624
- minLength: 3,
1625
- examples: ['johndoe']
1626
- },
1627
- email: {
1628
- type: 'string',
1629
- format: 'email',
1630
- title: 'Email',
1631
- examples: ['john@example.com']
1632
- },
1633
- password: {
1634
- type: 'string',
1635
- format: 'password',
1636
- title: 'Password',
1637
- minLength: 8
1638
- },
1639
- terms: {
1640
- type: 'boolean',
1641
- title: 'I agree to the terms and conditions'
1642
- }
1643
- },
1644
- required: ['username', 'email', 'password', 'terms']
1645
- };
1646
-
1647
- const handleSaveDraft = (e) => {
1648
- const form = e.target.closest('pds-jsonform');
1649
- const data = form.serialize();
1650
- console.log('💾 Saving draft:', data.json);
1651
- };
1652
-
1653
- return html`
1654
- <pds-jsonform
1655
- .jsonSchema=${schema}
1656
- hide-actions
1657
- @pw:value-change=${(e) => console.log('🔄 Field changed:', e.detail)}
1658
- @pw:submit=${(e) => toastFormData(e.detail)}
1659
- >
1660
- <div slot="actions" class="flex gap-sm items-center">
1661
- <button
1662
- type="submit"
1663
- class="btn btn-primary"
1664
- >
1665
- <pds-icon icon="check"></pds-icon>
1666
- Create Account
1667
- </button>
1668
- <button
1669
- type="button"
1670
- class="btn"
1671
- @click=${handleSaveDraft}
1672
- >
1673
- <pds-icon icon="file"></pds-icon>
1674
- Save Draft
1675
- </button>
1676
- <button
1677
- type="button"
1678
- class="btn btn-secondary btn-outline grow text-right"
1679
- @click=${() => console.log('Registration cancelled')}
1680
- >
1681
- Cancel
1682
- </button>
1683
- </div>
1684
- </pds-jsonform>
1685
- `;
1686
- }
1687
- };
1688
-
1689
- // =============================================================================
1690
- // Root-Level Layout Stories
1691
- // =============================================================================
1692
-
1693
- export const RootGridLayout = {
1694
- name: 'Root-Level Grid Layout',
1695
- parameters: {
1696
- docs: {
1697
- description: {
1698
- story: `Apply a grid layout to the entire form using root-level \`ui:layout\` and \`ui:layoutOptions\`.
1699
-
1700
- This allows you to control the form layout **without modifying your JSON Schema** — keeping data structure separate from presentation.
1701
-
1702
- \`\`\`javascript
1703
- const uiSchema = {
1704
- 'ui:layout': 'grid',
1705
- 'ui:layoutOptions': {
1706
- columns: 2,
1707
- gap: 'md'
1708
- }
1709
- };
1710
- \`\`\``
1711
- }
1712
- }
1713
- },
1714
- render: () => {
1715
- const schema = {
1716
- type: 'object',
1717
- title: 'Contact Form',
1718
- properties: {
1719
- firstName: { type: 'string', title: 'First Name', examples: ['John'] },
1720
- lastName: { type: 'string', title: 'Last Name', examples: ['Doe'] },
1721
- email: { type: 'string', format: 'email', title: 'Email', examples: ['john.doe@example.com'] },
1722
- phone: { type: 'string', title: 'Phone', examples: ['+1 (555) 123-4567'] },
1723
- company: { type: 'string', title: 'Company', examples: ['Acme Inc.'] },
1724
- role: { type: 'string', title: 'Role', examples: ['Developer'] }
1725
- },
1726
- required: ['firstName', 'lastName', 'email']
1727
- };
1728
-
1729
- const uiSchema = {
1730
- 'ui:layout': 'grid',
1731
- 'ui:layoutOptions': {
1732
- columns: 2,
1733
- gap: 'md'
1734
- }
1735
- };
1736
-
1737
- return html`
1738
- <pds-jsonform
1739
- .jsonSchema=${schema}
1740
- .uiSchema=${uiSchema}
1741
- @pw:submit=${(e) => toastFormData(e.detail)}
1742
- ></pds-jsonform>
1743
- `;
1744
- }
1745
- };
1746
-
1747
- export const RootFlexLayout = {
1748
- name: 'Root-Level Flex Layout',
1749
- parameters: {
1750
- docs: {
1751
- description: {
1752
- story: `Apply a flex layout to the entire form using root-level \`ui:layout\` and \`ui:layoutOptions\`.
1753
-
1754
- \`\`\`javascript
1755
- const uiSchema = {
1756
- 'ui:layout': 'flex',
1757
- 'ui:layoutOptions': {
1758
- gap: 'lg',
1759
- wrap: true
1760
- }
1761
- };
1762
- \`\`\``
1763
- }
1764
- }
1765
- },
1766
- render: () => {
1767
- const schema = {
1768
- type: 'object',
1769
- title: 'Quick Settings',
1770
- properties: {
1771
- theme: {
1772
- type: 'string',
1773
- enum: ['light', 'dark', 'system'],
1774
- title: 'Theme',
1775
- default: 'system'
1776
- },
1777
- notifications: {
1778
- type: 'boolean',
1779
- title: 'Notifications',
1780
- default: true
1781
- },
1782
- language: {
1783
- type: 'string',
1784
- enum: ['en', 'es', 'fr', 'de'],
1785
- title: 'Language',
1786
- default: 'en'
1787
- }
1788
- }
1789
- };
1790
-
1791
- const uiSchema = {
1792
- 'ui:layout': 'flex',
1793
- 'ui:layoutOptions': {
1794
- gap: 'lg',
1795
- wrap: true
1796
- }
1797
- };
1798
-
1799
- return html`
1800
- <pds-jsonform
1801
- .jsonSchema=${schema}
1802
- .uiSchema=${uiSchema}
1803
- @pw:submit=${(e) => toastFormData(e.detail)}
1804
- ></pds-jsonform>
1805
- `;
1806
- }
1807
- };
1808
-
1809
- export const RootLayoutWithFieldOptions = {
1810
- name: 'Root Layout + Field Options',
1811
- parameters: {
1812
- docs: {
1813
- description: {
1814
- story: `Combine root-level layout with field-specific UI options.
1815
-
1816
- Root-level \`ui:*\` properties control the overall form layout, while path-keyed entries (like \`'/email'\`) customize individual fields.
1817
-
1818
- \`\`\`javascript
1819
- const uiSchema = {
1820
- // Root form layout
1821
- 'ui:layout': 'grid',
1822
- 'ui:layoutOptions': { columns: 2, gap: 'md' },
1823
-
1824
- // Field-specific options
1825
- '/email': { 'ui:icon': 'envelope' },
1826
- '/bio': { 'ui:widget': 'textarea', 'ui:options': { rows: 4 } }
1827
- };
1828
- \`\`\``
1829
- }
1830
- }
1831
- },
1832
- render: () => {
1833
- const schema = {
1834
- type: 'object',
1835
- title: 'User Profile',
1836
- properties: {
1837
- username: { type: 'string', title: 'Username', minLength: 3, examples: ['johndoe'] },
1838
- email: { type: 'string', format: 'email', title: 'Email', examples: ['john@example.com'] },
1839
- bio: { type: 'string', title: 'Bio', maxLength: 500, examples: ['Tell us about yourself...'] },
1840
- website: { type: 'string', format: 'uri', title: 'Website', examples: ['https://yoursite.com'] }
1841
- },
1842
- required: ['username', 'email']
1843
- };
1844
-
1845
- const uiSchema = {
1846
- // Root form layout
1847
- 'ui:layout': 'grid',
1848
- 'ui:layoutOptions': {
1849
- columns: 2,
1850
- gap: 'md'
1851
- },
1852
- // Field-specific options
1853
- '/username': {
1854
- 'ui:icon': 'user'
1855
- },
1856
- '/email': {
1857
- 'ui:icon': 'envelope',
1858
- 'ui:help': 'We will never share your email'
1859
- },
1860
- '/bio': {
1861
- 'ui:widget': 'textarea',
1862
- 'ui:options': { rows: 4 },
1863
- 'ui:class': 'grid-col-span-2'
1864
- },
1865
- '/website': {
1866
- 'ui:icon': 'globe',
1867
- 'ui:class': 'grid-col-span-2'
1868
- }
1869
- };
1870
-
1871
- return html`
1872
- <pds-jsonform
1873
- .jsonSchema=${schema}
1874
- .uiSchema=${uiSchema}
1875
- @pw:submit=${(e) => toastFormData(e.detail)}
1876
- ></pds-jsonform>
1877
- `;
1878
- }
1879
- };
1880
-
1881
- export const RootThreeColumnGrid = {
1882
- name: 'Root 3-Column Grid',
1883
- parameters: {
1884
- docs: {
1885
- description: {
1886
- story: `A more complex form using a 3-column root grid layout with various field types.`
1887
- }
1888
- }
1889
- },
1890
- render: () => {
1891
- const schema = {
1892
- type: 'object',
1893
- title: 'Product Registration',
1894
- properties: {
1895
- productName: { type: 'string', title: 'Product Name', examples: ['Widget Pro'] },
1896
- serialNumber: { type: 'string', title: 'Serial Number', examples: ['SN-12345'] },
1897
- purchaseDate: { type: 'string', format: 'date', title: 'Purchase Date' },
1898
- retailer: { type: 'string', title: 'Retailer', examples: ['Amazon'] },
1899
- price: { type: 'number', title: 'Price', minimum: 0, examples: [99.99] },
1900
- currency: { type: 'string', enum: ['USD', 'EUR', 'GBP'], title: 'Currency', default: 'USD' },
1901
- condition: { type: 'string', enum: ['new', 'refurbished', 'used'], title: 'Condition', default: 'new' },
1902
- extendedWarranty: { type: 'boolean', title: 'Extended Warranty' },
1903
- newsletter: { type: 'boolean', title: 'Subscribe to Newsletter' }
1904
- },
1905
- required: ['productName', 'serialNumber', 'purchaseDate']
1906
- };
1907
-
1908
- const uiSchema = {
1909
- 'ui:layout': 'grid',
1910
- 'ui:layoutOptions': {
1911
- columns: 3,
1912
- gap: 'md'
1913
- }
1914
- };
1915
-
1916
- const options = {
1917
- widgets: { booleans: 'toggle' }
1918
- };
1919
-
1920
- return html`
1921
- <pds-jsonform
1922
- .jsonSchema=${schema}
1923
- .uiSchema=${uiSchema}
1924
- .options=${options}
1925
- @pw:submit=${(e) => toastFormData(e.detail)}
1926
- ></pds-jsonform>
1927
- `;
1928
- }
1929
- };
1930
-
1931
- export const EnumWithEnumNames = {
1932
- name: 'Enum with enumNames',
1933
- parameters: {
1934
- docs: {
1935
- description: {
1936
- story: `Use \`enumNames\` to provide human-friendly display labels for enum values.
1937
-
1938
- This is useful when you want to store technical values (like language codes or IDs) while showing user-friendly labels.
1939
-
1940
- \`\`\`javascript
1941
- const schema = {
1942
- properties: {
1943
- language: {
1944
- type: 'string',
1945
- title: 'Language',
1946
- enum: ['en', 'es', 'fr', 'de', 'zh', 'ja'], // Values stored
1947
- enumNames: ['English', 'Spanish', 'French', 'German', 'Chinese', 'Japanese'] // Labels shown
1948
- }
1949
- }
1950
- };
1951
- \`\`\`
1952
-
1953
- The \`enumNames\` array must match the length and order of the \`enum\` array. Works with select dropdowns, radio buttons, and checkbox groups.`
1954
- }
1955
- }
1956
- },
1957
- render: () => {
1958
- const schema = {
1959
- type: 'object',
1960
- title: 'Preferences',
1961
- properties: {
1962
- language: {
1963
- type: 'string',
1964
- title: 'Language',
1965
- enum: ['en', 'es', 'fr', 'de', 'zh', 'ja'],
1966
- enumNames: ['English', 'Spanish', 'French', 'German', 'Chinese', 'Japanese'],
1967
- default: 'en'
1968
- },
1969
- country: {
1970
- type: 'string',
1971
- title: 'Country',
1972
- enum: ['US', 'GB', 'CA', 'AU', 'DE', 'FR'],
1973
- enumNames: ['United States', 'United Kingdom', 'Canada', 'Australia', 'Germany', 'France']
1974
- },
1975
- priority: {
1976
- type: 'string',
1977
- title: 'Priority Level',
1978
- enum: ['p1', 'p2', 'p3', 'p4'],
1979
- enumNames: ['🔴 Critical', '🟠 High', '🟡 Medium', '🟢 Low'],
1980
- default: 'p3'
1981
- },
1982
- interests: {
1983
- type: 'array',
1984
- title: 'Interests (checkbox group)',
1985
- items: {
1986
- type: 'string',
1987
- enum: ['dev', 'design', 'pm', 'qa', 'devops'],
1988
- enumNames: ['Development', 'Design', 'Project Management', 'Quality Assurance', 'DevOps']
1989
- },
1990
- uniqueItems: true
1991
- }
1992
- }
1993
- };
1994
-
1995
- const uiSchema = {
1996
- '/priority': { 'ui:widget': 'radio' }
1997
- };
1998
-
1999
- return html`
2000
- <pds-jsonform
2001
- .jsonSchema=${schema}
2002
- .uiSchema=${uiSchema}
2003
- @pw:submit=${(e) => toastFormData(e.detail)}
2004
- ></pds-jsonform>
2005
- `;
2006
- }
2007
- };