playbook_ui 18.2.0 → 18.3.0.pre.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_form/docs/_description_react.md +3 -0
  3. data/app/pb_kits/playbook/pb_form/docs/_form_default_values.jsx +898 -0
  4. data/app/pb_kits/playbook/pb_form/docs/_form_default_values_react.md +3 -0
  5. data/app/pb_kits/playbook/pb_form/docs/_form_defaults.jsx +879 -0
  6. data/app/pb_kits/playbook/pb_form/docs/_form_defaults_react.md +29 -0
  7. data/app/pb_kits/playbook/pb_form/docs/_form_loading.jsx +75 -0
  8. data/app/pb_kits/playbook/pb_form/docs/{_form_loading.md → _form_loading_rails.md} +1 -0
  9. data/app/pb_kits/playbook/pb_form/docs/_form_loading_react.md +1 -0
  10. data/app/pb_kits/playbook/pb_form/docs/_form_model_values.jsx +147 -0
  11. data/app/pb_kits/playbook/pb_form/docs/_form_model_values_react.md +16 -0
  12. data/app/pb_kits/playbook/pb_form/docs/_form_multi_column.jsx +164 -0
  13. data/app/pb_kits/playbook/pb_form/docs/_form_multi_column_react.md +3 -0
  14. data/app/pb_kits/playbook/pb_form/docs/_form_validation.jsx +1057 -0
  15. data/app/pb_kits/playbook/pb_form/docs/_form_validation_react.md +26 -0
  16. data/app/pb_kits/playbook/pb_form/docs/example.yml +7 -0
  17. data/app/pb_kits/playbook/pb_legend/_legend.tsx +3 -1
  18. data/app/pb_kits/playbook/pb_legend/kit.schema.json +1 -0
  19. data/app/pb_kits/playbook/pb_progress_step/_progress_step_item.tsx +3 -1
  20. data/app/pb_kits/playbook/pb_timeline/_item.tsx +3 -1
  21. data/app/pb_kits/playbook/utilities/test/globalProps/onClick.test.js +90 -0
  22. data/dist/ai/all-schemas.json +1 -0
  23. data/dist/ai/forms.json +1 -1
  24. data/dist/ai/index.json +2 -2
  25. data/dist/ai/kits/legend.schema.json +1 -0
  26. data/dist/chunks/vendor.js +1 -1
  27. data/lib/playbook/version.rb +2 -2
  28. metadata +26 -10
  29. /data/app/pb_kits/playbook/pb_form/docs/{_description.md → _description_rails.md} +0 -0
  30. /data/app/pb_kits/playbook/pb_form/docs/{_form_default_values.md → _form_default_values_rails.md} +0 -0
  31. /data/app/pb_kits/playbook/pb_form/docs/{_form_defaults.md → _form_defaults_rails.md} +0 -0
  32. /data/app/pb_kits/playbook/pb_form/docs/{_form_model_values.md → _form_model_values_rails.md} +0 -0
  33. /data/app/pb_kits/playbook/pb_form/docs/{_form_multi_column.md → _form_multi_column_rails.md} +0 -0
  34. /data/app/pb_kits/playbook/pb_form/docs/{_form_validation.md → _form_validation_rails.md} +0 -0
@@ -0,0 +1,898 @@
1
+ import React, { useEffect, useState } from "react"
2
+ import { Controller, useForm } from "react-hook-form"
3
+
4
+ import Body from "../../pb_body/_body"
5
+ import Button from "../../pb_button/_button"
6
+ import Caption from "../../pb_caption/_caption"
7
+ import Card from "../../pb_card/_card"
8
+ import Checkbox from "../../pb_checkbox/_checkbox"
9
+ import DatePicker from "../../pb_date_picker/_date_picker"
10
+ import Dropdown from "../../pb_dropdown/_dropdown"
11
+ import Flex from "../../pb_flex/_flex"
12
+ import MultiLevelSelect from "../../pb_multi_level_select/_multi_level_select"
13
+ import PhoneNumberInput from "../../pb_phone_number_input/_phone_number_input"
14
+ import Pill from "../../pb_pill/_pill"
15
+ import Select from "../../pb_select/_select"
16
+ import StarRating from "../../pb_star_rating/_star_rating"
17
+ import Textarea from "../../pb_textarea/_textarea"
18
+ import TextInput from "../../pb_text_input/_text_input"
19
+ import TimePicker from "../../pb_time_picker/_time_picker"
20
+ import Typeahead from "../../pb_typeahead/_typeahead"
21
+
22
+ const INPUT_TYPES = [
23
+ { key: "checkbox", label: "Checkbox" },
24
+ { key: "date_picker", label: "Date Picker" },
25
+ { key: "dropdown", label: "Dropdown: Default" },
26
+ { key: "multi_select", label: "Dropdown: Multi Select" },
27
+ { key: "multi_level_select", label: "Multi Level Select" },
28
+ { key: "phone_number", label: "Phone Number" },
29
+ { key: "select", label: "Select: Default" },
30
+ { key: "star_rating", label: "Star Rating" },
31
+ { key: "text_field", label: "Text Input: Default" },
32
+ { key: "email_field", label: "Text Input: Email" },
33
+ { key: "number_field", label: "Text Input: Number" },
34
+ { key: "password_field", label: "Text Input: Password" },
35
+ { key: "search_field", label: "Text Input: Search" },
36
+ { key: "url_field", label: "Text Input: URL" },
37
+ { key: "textarea", label: "Textarea" },
38
+ { key: "time_picker", label: "Time Picker" },
39
+ { key: "typeahead_default", label: "Typeahead: Default" },
40
+ { key: "typeahead", label: "Typeahead: Multi" },
41
+ ]
42
+
43
+ const DEFAULT_SELECTED_KEY = "text_field"
44
+
45
+ const COLOR_SELECT_OPTIONS = [
46
+ { text: "Red", value: "red" },
47
+ { text: "Blue", value: "blue" },
48
+ { text: "Green", value: "green" },
49
+ ]
50
+
51
+ const COUNTRY_OPTIONS = [
52
+ { id: "us-dv", label: "United States", value: "United States" },
53
+ { id: "ca-dv", label: "Canada", value: "Canada" },
54
+ { id: "pk-dv", label: "Pakistan", value: "Pakistan" },
55
+ ]
56
+
57
+ const CANADA = COUNTRY_OPTIONS[1]
58
+
59
+ const TYPEAHEAD_COLOR_OPTIONS = [
60
+ { label: "Orange", value: "#FFA500" },
61
+ { label: "Red", value: "#FF0000" },
62
+ { label: "Green", value: "#00FF00" },
63
+ { label: "Blue", value: "#0000FF" },
64
+ ]
65
+
66
+ const BLUE = TYPEAHEAD_COLOR_OPTIONS[3]
67
+
68
+ const DEPARTMENT_TREE_DATA = [
69
+ {
70
+ children: [
71
+ {
72
+ children: [
73
+ { id: "default-values-102", label: "Talent Acquisition", value: "talentAcquisition" },
74
+ { id: "default-values-103", label: "People Experience", value: "peopleExperience" },
75
+ ],
76
+ id: "default-values-101",
77
+ label: "People",
78
+ value: "people",
79
+ },
80
+ {
81
+ children: [
82
+ { id: "default-values-105", label: "Customer Service", value: "customerService" },
83
+ { id: "default-values-106", label: "Energy", value: "energy" },
84
+ ],
85
+ id: "default-values-104",
86
+ label: "Contact Center",
87
+ value: "contactCenter",
88
+ },
89
+ ],
90
+ expanded: true,
91
+ id: "default-values-100",
92
+ label: "Power Home Remodeling",
93
+ value: "powerHomeRemodeling",
94
+ },
95
+ ]
96
+
97
+ // Recursively locate a node by its `id` so onSubmit can translate stored ids
98
+ // back into the human-readable `value`s (matches FormDefaults behavior).
99
+ const findNodeById = (nodes, id) => {
100
+ for (const node of nodes) {
101
+ if (node.id === id) return node
102
+ if (node.children) {
103
+ const found = findNodeById(node.children, id)
104
+ if (found) return found
105
+ }
106
+ }
107
+ return null
108
+ }
109
+
110
+ // NOTE: `rating` is intentionally omitted. StarRating does not forward `rating`
111
+ // into StarRatingInteractive (which keeps its own useState(0)), so a prefilled
112
+ // rating would submit a value the control never displays. Re-add once the kit
113
+ // forwards the initial rating.
114
+ const DEFAULT_VALUES = {
115
+ bio: "Loves building with Playbook.",
116
+ countries: [CANADA],
117
+ country: CANADA,
118
+ department: ["default-values-102"],
119
+ email: "jane@example.com",
120
+ favoriteColor: "blue",
121
+ favoriteColorSearch: BLUE,
122
+ favoriteColors: [BLUE],
123
+ fullName: "John Doe",
124
+ newsletter: true,
125
+ password: "secret",
126
+ phone: "5551234567",
127
+ projectNumber: "33-12345",
128
+ quantity: 3,
129
+ startDate: "06/15/2026",
130
+ startTime: "09:30",
131
+ website: "https://example.com",
132
+ }
133
+
134
+ // Map each field name to its toggle key so onSubmit can filter out fields
135
+ // whose pill has been deselected.
136
+ const FIELD_TO_INPUT_KEY = {
137
+ newsletter: "checkbox",
138
+ startDate: "date_picker",
139
+ country: "dropdown",
140
+ countries: "multi_select",
141
+ department: "multi_level_select",
142
+ phone: "phone_number",
143
+ favoriteColor: "select",
144
+ rating: "star_rating",
145
+ fullName: "text_field",
146
+ email: "email_field",
147
+ quantity: "number_field",
148
+ password: "password_field",
149
+ projectNumber: "search_field",
150
+ website: "url_field",
151
+ bio: "textarea",
152
+ startTime: "time_picker",
153
+ favoriteColorSearch: "typeahead_default",
154
+ favoriteColors: "typeahead",
155
+ }
156
+
157
+ // Only interactive StarRating needs the reset: it keeps its own useState and
158
+ // ignores `rating`, so a stored rating would submit while the control looks
159
+ // empty after remount. Other toggle-remounted kits (TimePicker, DatePicker,
160
+ // Dropdown, Typeahead) rehydrate from their `defaultX` / `value` props.
161
+ const RESET_ON_DESELECT = {
162
+ star_rating: "rating",
163
+ }
164
+
165
+ const CODE_SNIPPETS = {
166
+ checkbox: `<Controller
167
+ control={control}
168
+ name="newsletter"
169
+ render={({ field }) => (
170
+ <Checkbox
171
+ checked={!!field.value}
172
+ onChange={(event) => field.onChange(event.target.checked)}
173
+ text="Subscribe to newsletter"
174
+ />
175
+ )}
176
+ />`,
177
+ date_picker: `<Controller
178
+ control={control}
179
+ name="startDate"
180
+ render={({ field }) => (
181
+ <DatePicker
182
+ allowInput
183
+ defaultDate={field.value}
184
+ initializeOnce
185
+ key={resetKey}
186
+ label="Start Date"
187
+ onChange={(dateStr) => field.onChange(dateStr)}
188
+ pickerId="form-default-values-start-date"
189
+ />
190
+ )}
191
+ />`,
192
+ dropdown: `<Controller
193
+ control={control}
194
+ name="country"
195
+ render={({ field }) => (
196
+ <Dropdown
197
+ key={resetKey}
198
+ label="Country"
199
+ onChange={(event) => field.onChange(event.target.value)}
200
+ options={countryOptions}
201
+ value={field.value}
202
+ />
203
+ )}
204
+ />`,
205
+ email_field: `<Controller
206
+ control={control}
207
+ name="email"
208
+ render={({ field }) => (
209
+ <TextInput
210
+ label="Email"
211
+ onChange={(event) => field.onChange(event.target.value)}
212
+ type="email"
213
+ value={field.value}
214
+ />
215
+ )}
216
+ />`,
217
+ multi_level_select: `<Controller
218
+ control={control}
219
+ name="department"
220
+ render={({ field }) => (
221
+ <MultiLevelSelect
222
+ id="mls-default-values"
223
+ label="Department"
224
+ onChange={(event) => field.onChange(event.target.value.map((item) => item.id))}
225
+ selectedIds={field.value}
226
+ treeData={departmentTreeData}
227
+ />
228
+ )}
229
+ />`,
230
+ multi_select: `<Controller
231
+ control={control}
232
+ name="countries"
233
+ render={({ field }) => (
234
+ <Dropdown
235
+ key={resetKey}
236
+ label="Countries"
237
+ multiSelect
238
+ onChange={(event) => field.onChange(event.target.value)}
239
+ options={countryOptions}
240
+ value={field.value}
241
+ />
242
+ )}
243
+ />`,
244
+ number_field: `<Controller
245
+ control={control}
246
+ name="quantity"
247
+ render={({ field }) => (
248
+ <TextInput
249
+ label="Quantity"
250
+ onChange={(event) => field.onChange(event.target.value)}
251
+ type="number"
252
+ value={field.value}
253
+ />
254
+ )}
255
+ />`,
256
+ password_field: `<Controller
257
+ control={control}
258
+ name="password"
259
+ render={({ field }) => (
260
+ <TextInput
261
+ label="Password"
262
+ onChange={(event) => field.onChange(event.target.value)}
263
+ type="password"
264
+ value={field.value}
265
+ />
266
+ )}
267
+ />`,
268
+ phone_number: `<Controller
269
+ control={control}
270
+ name="phone"
271
+ render={({ field }) => (
272
+ <PhoneNumberInput
273
+ initialCountry="us"
274
+ key={resetKey}
275
+ label="Phone Number"
276
+ onChange={(data) => field.onChange(data?.number || "")}
277
+ value={field.value}
278
+ />
279
+ )}
280
+ />`,
281
+ search_field: `<Controller
282
+ control={control}
283
+ name="projectNumber"
284
+ render={({ field }) => (
285
+ <TextInput
286
+ label="Project Number"
287
+ onChange={(event) => field.onChange(event.target.value)}
288
+ type="search"
289
+ value={field.value}
290
+ />
291
+ )}
292
+ />`,
293
+ select: `<Controller
294
+ control={control}
295
+ name="favoriteColor"
296
+ render={({ field }) => (
297
+ <Select
298
+ blankSelection="Select one..."
299
+ key={resetKey}
300
+ label="Favorite Color"
301
+ onChange={(event) => field.onChange(event.target.value)}
302
+ options={colorSelectOptions}
303
+ value={field.value}
304
+ />
305
+ )}
306
+ />`,
307
+ star_rating: `// NOTE: resetField("rating") runs from an effect when this pill is
308
+ // deselected, because interactive StarRating keeps its own state and ignores
309
+ // the rating prop on remount.
310
+ <Controller
311
+ control={control}
312
+ name="rating"
313
+ render={({ field }) => (
314
+ <StarRating
315
+ key={resetKey}
316
+ label="Rating"
317
+ onClick={(value) => field.onChange(value)}
318
+ rating={field.value || 0}
319
+ variant="interactive"
320
+ />
321
+ )}
322
+ />`,
323
+ text_field: `<Controller
324
+ control={control}
325
+ name="fullName"
326
+ render={({ field }) => (
327
+ <TextInput
328
+ label="Full Name"
329
+ onChange={(event) => field.onChange(event.target.value)}
330
+ value={field.value}
331
+ />
332
+ )}
333
+ />`,
334
+ textarea: `<Controller
335
+ control={control}
336
+ name="bio"
337
+ render={({ field }) => (
338
+ <Textarea
339
+ label="Bio"
340
+ onChange={(event) => field.onChange(event.target.value)}
341
+ value={field.value}
342
+ />
343
+ )}
344
+ />`,
345
+ time_picker: `// NOTE: TimePicker rehydrates from defaultTime={field.value} on remount, so
346
+ // no reset is needed when the pill is turned off and back on.
347
+ <Controller
348
+ control={control}
349
+ name="startTime"
350
+ render={({ field }) => (
351
+ <TimePicker
352
+ defaultTime={field.value}
353
+ key={resetKey}
354
+ label="Start Time"
355
+ onChange={(time) => field.onChange(time)}
356
+ />
357
+ )}
358
+ />`,
359
+ typeahead: `<Controller
360
+ control={control}
361
+ name="favoriteColors"
362
+ render={({ field: { onChange: handleChange, value } }) => (
363
+ <Typeahead
364
+ isMulti
365
+ key={resetKey}
366
+ label="Favorite Color"
367
+ onChange={handleChange}
368
+ options={typeaheadColorOptions}
369
+ pills
370
+ placeholder="Search for a color"
371
+ value={value}
372
+ />
373
+ )}
374
+ />`,
375
+ typeahead_default: `<Controller
376
+ control={control}
377
+ name="favoriteColorSearch"
378
+ render={({ field: { onChange: handleChange, value } }) => (
379
+ <Typeahead
380
+ key={resetKey}
381
+ label="Favorite Color"
382
+ onChange={handleChange}
383
+ options={typeaheadColorOptions}
384
+ placeholder="Search for a color"
385
+ value={value}
386
+ />
387
+ )}
388
+ />`,
389
+ url_field: `<Controller
390
+ control={control}
391
+ name="website"
392
+ render={({ field }) => (
393
+ <TextInput
394
+ label="Website"
395
+ onChange={(event) => field.onChange(event.target.value)}
396
+ type="url"
397
+ value={field.value}
398
+ />
399
+ )}
400
+ />`,
401
+ }
402
+
403
+ const FORM_OPEN = `const { control, handleSubmit } = useForm({
404
+ defaultValues: {
405
+ fullName: "John Doe",
406
+ email: "jane@example.com",
407
+ quantity: 3,
408
+ newsletter: true,
409
+ startDate: "06/15/2026",
410
+ },
411
+ })
412
+
413
+ <form onSubmit={handleSubmit(onSubmit)}>`
414
+ const FORM_CLOSE = ` <Button htmlType="submit" text="Submit" />
415
+ <Button htmlType="button" onClick={onReset} text="Cancel" variant="secondary" />
416
+ </form>`
417
+
418
+ const FormDefaultValues = () => {
419
+ const [selectedKeys, setSelectedKeys] = useState([DEFAULT_SELECTED_KEY])
420
+ const [submittedData, setSubmittedData] = useState(null)
421
+ const [resetKey, setResetKey] = useState(0)
422
+ const { control, handleSubmit, reset, resetField, getValues } = useForm({
423
+ defaultValues: DEFAULT_VALUES,
424
+ })
425
+
426
+ const isSelected = (key) => selectedKeys.includes(key)
427
+
428
+ // When a pill is turned off for a control that can't rehydrate on remount,
429
+ // clear its stored value so the next mount and the submitted payload agree.
430
+ useEffect(() => {
431
+ Object.entries(RESET_ON_DESELECT).forEach(([inputKey, fieldName]) => {
432
+ const stillSelected = selectedKeys.includes(inputKey)
433
+ const hasValue = getValues(fieldName) !== undefined && getValues(fieldName) !== null
434
+ if (!stillSelected && hasValue) resetField(fieldName)
435
+ })
436
+ }, [selectedKeys, getValues, resetField])
437
+
438
+ const toggleKey = (key) => {
439
+ setSelectedKeys((current) => {
440
+ if (!current.includes(key)) return [...current, key]
441
+ return current.length > 1 ? current.filter((selected) => selected !== key) : current
442
+ })
443
+ }
444
+
445
+ const handlePillKeyDown = (event, key) => {
446
+ if (event.key !== "Enter" && event.key !== " ") return
447
+ event.preventDefault()
448
+ toggleKey(key)
449
+ }
450
+
451
+ const onSubmit = (data) => {
452
+ // Dropdown stores the selected option node (or array of nodes for multi),
453
+ // and MultiLevelSelect stores selected ids. Translate each back into the
454
+ // human-readable `value`s so the submitted payload is primitives, not
455
+ // kit-internal shapes.
456
+ const normalized = {
457
+ ...data,
458
+ country:
459
+ data.country && typeof data.country === "object"
460
+ ? data.country.value ?? data.country.label
461
+ : data.country,
462
+ countries: Array.isArray(data.countries)
463
+ ? data.countries.map((node) =>
464
+ node && typeof node === "object" ? node.value ?? node.label : node
465
+ )
466
+ : data.countries,
467
+ department: Array.isArray(data.department)
468
+ ? data.department.map((id) => findNodeById(DEPARTMENT_TREE_DATA, id)?.value ?? id)
469
+ : data.department,
470
+ }
471
+
472
+ // Fields that are deselected stay mounted (we removed shouldUnregister)
473
+ // so we filter them out here rather than letting RHF drop their values.
474
+ const filtered = Object.fromEntries(
475
+ Object.entries(normalized).filter(([fieldName]) => {
476
+ const inputKey = FIELD_TO_INPUT_KEY[fieldName]
477
+ return inputKey ? isSelected(inputKey) : true
478
+ })
479
+ )
480
+ setSubmittedData(filtered)
481
+ }
482
+
483
+ const onReset = () => {
484
+ reset()
485
+ setResetKey((current) => current + 1)
486
+ setSubmittedData(null)
487
+ }
488
+
489
+ const codePreview = [FORM_OPEN]
490
+ .concat(INPUT_TYPES.filter((inputType) => isSelected(inputType.key)).map((inputType) => CODE_SNIPPETS[inputType.key]))
491
+ .concat([FORM_CLOSE])
492
+ .join("\n\n")
493
+
494
+ return (
495
+ <div>
496
+ <Body color="light"
497
+ marginBottom="xs"
498
+ text="Select one or more inputs to add them to the form."
499
+ />
500
+ <Flex gap="xs"
501
+ marginBottom="md"
502
+ wrap
503
+ >
504
+ {INPUT_TYPES.map((inputType) => (
505
+ <Pill
506
+ htmlOptions={{
507
+ onClick: () => toggleKey(inputType.key),
508
+ onKeyDown: (event) => handlePillKeyDown(event, inputType.key),
509
+ role: "button",
510
+ style: { cursor: "pointer" },
511
+ tabIndex: 0,
512
+ }}
513
+ key={inputType.key}
514
+ text={inputType.label}
515
+ textTransform="none"
516
+ variant={isSelected(inputType.key) ? "primary" : "neutral"}
517
+ />
518
+ ))}
519
+ </Flex>
520
+
521
+ <Flex align="stretch"
522
+ gap="md"
523
+ wrap
524
+ >
525
+ <Flex
526
+ align="stretch"
527
+ htmlOptions={{ style: { flex: "2 1" } }}
528
+ orientation="column"
529
+ >
530
+ <Card
531
+ htmlOptions={{ style: { minWidth: 0 } }}
532
+ padding="md"
533
+ shadow="deep"
534
+ >
535
+ <form onSubmit={handleSubmit(onSubmit)}>
536
+ {isSelected("checkbox") && (
537
+ <Controller
538
+ control={control}
539
+ name="newsletter"
540
+ render={({ field }) => (
541
+ <Checkbox
542
+ checked={!!field.value}
543
+ marginBottom="sm"
544
+ onChange={(event) => field.onChange(event.target.checked)}
545
+ text="Subscribe to newsletter"
546
+ />
547
+ )}
548
+ />
549
+ )}
550
+
551
+ {isSelected("date_picker") && (
552
+ <Controller
553
+ control={control}
554
+ name="startDate"
555
+ render={({ field }) => (
556
+ <DatePicker
557
+ allowInput
558
+ defaultDate={field.value}
559
+ initializeOnce
560
+ key={resetKey}
561
+ label="Start Date"
562
+ marginBottom="sm"
563
+ onChange={(dateStr) => field.onChange(dateStr)}
564
+ pickerId="form-default-values-start-date"
565
+ />
566
+ )}
567
+ />
568
+ )}
569
+
570
+ {isSelected("dropdown") && (
571
+ <Controller
572
+ control={control}
573
+ name="country"
574
+ render={({ field }) => (
575
+ <Dropdown
576
+ key={`dropdown-${resetKey}`}
577
+ label="Country"
578
+ marginBottom="sm"
579
+ onChange={(event) => field.onChange(event.target.value)}
580
+ options={COUNTRY_OPTIONS}
581
+ value={field.value}
582
+ />
583
+ )}
584
+ />
585
+ )}
586
+
587
+ {isSelected("multi_select") && (
588
+ <Controller
589
+ control={control}
590
+ name="countries"
591
+ render={({ field }) => (
592
+ <Dropdown
593
+ key={`multi-select-${resetKey}`}
594
+ label="Countries"
595
+ marginBottom="sm"
596
+ multiSelect
597
+ onChange={(event) => field.onChange(event.target.value)}
598
+ options={COUNTRY_OPTIONS}
599
+ value={field.value}
600
+ />
601
+ )}
602
+ />
603
+ )}
604
+
605
+ {isSelected("multi_level_select") && (
606
+ <Controller
607
+ control={control}
608
+ name="department"
609
+ render={({ field }) => (
610
+ <MultiLevelSelect
611
+ id="mls-default-values"
612
+ label="Department"
613
+ marginBottom="sm"
614
+ onChange={(event) => field.onChange(event.target.value.map((item) => item.id))}
615
+ selectedIds={field.value}
616
+ treeData={DEPARTMENT_TREE_DATA}
617
+ />
618
+ )}
619
+ />
620
+ )}
621
+
622
+ {isSelected("phone_number") && (
623
+ <Controller
624
+ control={control}
625
+ name="phone"
626
+ render={({ field }) => (
627
+ <PhoneNumberInput
628
+ initialCountry="us"
629
+ key={`phone-${resetKey}`}
630
+ label="Phone Number"
631
+ marginBottom="sm"
632
+ onChange={(data) => field.onChange(data?.number || "")}
633
+ value={field.value}
634
+ />
635
+ )}
636
+ />
637
+ )}
638
+
639
+ {isSelected("select") && (
640
+ <Controller
641
+ control={control}
642
+ name="favoriteColor"
643
+ render={({ field }) => (
644
+ <Select
645
+ blankSelection="Select one..."
646
+ key={`select-${resetKey}`}
647
+ label="Favorite Color"
648
+ marginBottom="sm"
649
+ onChange={(event) => field.onChange(event.target.value)}
650
+ options={COLOR_SELECT_OPTIONS}
651
+ value={field.value}
652
+ />
653
+ )}
654
+ />
655
+ )}
656
+
657
+ {isSelected("star_rating") && (
658
+ <Controller
659
+ control={control}
660
+ name="rating"
661
+ render={({ field }) => (
662
+ <StarRating
663
+ key={`rating-${resetKey}`}
664
+ label="Rating"
665
+ marginBottom="sm"
666
+ onClick={(value) => field.onChange(value)}
667
+ rating={field.value || 0}
668
+ variant="interactive"
669
+ />
670
+ )}
671
+ />
672
+ )}
673
+
674
+ {isSelected("text_field") && (
675
+ <Controller
676
+ control={control}
677
+ name="fullName"
678
+ render={({ field }) => (
679
+ <TextInput
680
+ label="Full Name"
681
+ marginBottom="sm"
682
+ onChange={(event) => field.onChange(event.target.value)}
683
+ value={field.value}
684
+ />
685
+ )}
686
+ />
687
+ )}
688
+
689
+ {isSelected("email_field") && (
690
+ <Controller
691
+ control={control}
692
+ name="email"
693
+ render={({ field }) => (
694
+ <TextInput
695
+ label="Email"
696
+ marginBottom="sm"
697
+ onChange={(event) => field.onChange(event.target.value)}
698
+ type="email"
699
+ value={field.value}
700
+ />
701
+ )}
702
+ />
703
+ )}
704
+
705
+ {isSelected("number_field") && (
706
+ <Controller
707
+ control={control}
708
+ name="quantity"
709
+ render={({ field }) => (
710
+ <TextInput
711
+ label="Quantity"
712
+ marginBottom="sm"
713
+ onChange={(event) => field.onChange(event.target.value)}
714
+ type="number"
715
+ value={field.value}
716
+ />
717
+ )}
718
+ />
719
+ )}
720
+
721
+ {isSelected("password_field") && (
722
+ <Controller
723
+ control={control}
724
+ name="password"
725
+ render={({ field }) => (
726
+ <TextInput
727
+ label="Password"
728
+ marginBottom="sm"
729
+ onChange={(event) => field.onChange(event.target.value)}
730
+ type="password"
731
+ value={field.value}
732
+ />
733
+ )}
734
+ />
735
+ )}
736
+
737
+ {isSelected("search_field") && (
738
+ <Controller
739
+ control={control}
740
+ name="projectNumber"
741
+ render={({ field }) => (
742
+ <TextInput
743
+ label="Project Number"
744
+ marginBottom="sm"
745
+ onChange={(event) => field.onChange(event.target.value)}
746
+ type="search"
747
+ value={field.value}
748
+ />
749
+ )}
750
+ />
751
+ )}
752
+
753
+ {isSelected("url_field") && (
754
+ <Controller
755
+ control={control}
756
+ name="website"
757
+ render={({ field }) => (
758
+ <TextInput
759
+ label="Website"
760
+ marginBottom="sm"
761
+ onChange={(event) => field.onChange(event.target.value)}
762
+ type="url"
763
+ value={field.value}
764
+ />
765
+ )}
766
+ />
767
+ )}
768
+
769
+ {isSelected("textarea") && (
770
+ <Controller
771
+ control={control}
772
+ name="bio"
773
+ render={({ field }) => (
774
+ <Textarea
775
+ label="Bio"
776
+ marginBottom="sm"
777
+ onChange={(event) => field.onChange(event.target.value)}
778
+ value={field.value}
779
+ />
780
+ )}
781
+ />
782
+ )}
783
+
784
+ {isSelected("time_picker") && (
785
+ <Controller
786
+ control={control}
787
+ name="startTime"
788
+ render={({ field }) => (
789
+ <TimePicker
790
+ defaultTime={field.value}
791
+ key={`time-${resetKey}`}
792
+ label="Start Time"
793
+ marginBottom="sm"
794
+ onChange={(time) => field.onChange(time)}
795
+ />
796
+ )}
797
+ />
798
+ )}
799
+
800
+ {isSelected("typeahead_default") && (
801
+ <Controller
802
+ control={control}
803
+ name="favoriteColorSearch"
804
+ render={({ field: { onChange: handleChange, value } }) => (
805
+ <Typeahead
806
+ key={`typeahead-default-${resetKey}`}
807
+ label="Favorite Color"
808
+ marginBottom="sm"
809
+ onChange={handleChange}
810
+ options={TYPEAHEAD_COLOR_OPTIONS}
811
+ placeholder="Search for a color"
812
+ value={value}
813
+ />
814
+ )}
815
+ />
816
+ )}
817
+
818
+ {isSelected("typeahead") && (
819
+ <Controller
820
+ control={control}
821
+ name="favoriteColors"
822
+ render={({ field: { onChange: handleChange, value } }) => (
823
+ <Typeahead
824
+ isMulti
825
+ key={`typeahead-multi-${resetKey}`}
826
+ label="Favorite Color"
827
+ marginBottom="sm"
828
+ onChange={handleChange}
829
+ options={TYPEAHEAD_COLOR_OPTIONS}
830
+ pills
831
+ placeholder="Search for a color"
832
+ value={value}
833
+ />
834
+ )}
835
+ />
836
+ )}
837
+
838
+ <Flex gap="xs"
839
+ marginTop="sm"
840
+ >
841
+ <Button htmlType="submit"
842
+ text="Submit"
843
+ />
844
+ <Button
845
+ htmlType="button"
846
+ onClick={onReset}
847
+ text="Cancel"
848
+ variant="secondary"
849
+ />
850
+ </Flex>
851
+ </form>
852
+ </Card>
853
+ </Flex>
854
+
855
+ <Flex
856
+ align="stretch"
857
+ htmlOptions={{ style: { flex: "1 1" } }}
858
+ orientation="column"
859
+ >
860
+ <Caption marginBottom="xs"
861
+ text="Submitted Form Data"
862
+ />
863
+ <Card background="light"
864
+ padding="md"
865
+ >
866
+ {submittedData ? (
867
+ <pre style={{ fontSize: "12px", lineHeight: 1.5, margin: 0, overflow: "auto", whiteSpace: "pre-wrap" }}>
868
+ {JSON.stringify(submittedData, null, 2)}
869
+ </pre>
870
+ ) : (
871
+ <Body color="light"
872
+ text="No data submitted yet."
873
+ />
874
+ )}
875
+ </Card>
876
+ </Flex>
877
+ </Flex>
878
+
879
+ <Body
880
+ color="light"
881
+ marginBottom="sm"
882
+ marginTop="sm"
883
+ text="Submit the form to see the data on the right."
884
+ />
885
+
886
+ <Card background="dark"
887
+ dark
888
+ padding="none"
889
+ >
890
+ <pre style={{ fontSize: "12px", lineHeight: 1.5, margin: 0, overflow: "auto", padding: "16px", whiteSpace: "pre" }}>
891
+ {codePreview}
892
+ </pre>
893
+ </Card>
894
+ </div>
895
+ )
896
+ }
897
+
898
+ export default FormDefaultValues