@happyvertical/smrt-svelte 0.43.4 → 0.43.6

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 (92) hide show
  1. package/AGENTS.md +23 -0
  2. package/README.md +23 -1
  3. package/dist/components/forms/AddressInput.svelte +155 -42
  4. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
  5. package/dist/components/forms/CheckboxInput.svelte +40 -13
  6. package/dist/components/forms/CheckboxInput.svelte.d.ts.map +1 -1
  7. package/dist/components/forms/DateRangeInput.svelte +132 -16
  8. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
  9. package/dist/components/forms/DateTimeInput.svelte +48 -14
  10. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
  11. package/dist/components/forms/Form.svelte +599 -70
  12. package/dist/components/forms/Form.svelte.d.ts +6 -2
  13. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  14. package/dist/components/forms/MeasurementInput.svelte +137 -31
  15. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
  16. package/dist/components/forms/MoneyInput.svelte +42 -23
  17. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
  18. package/dist/components/forms/NumberInput.svelte +59 -27
  19. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
  20. package/dist/components/forms/PhoneInput.svelte +51 -22
  21. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
  22. package/dist/components/forms/SelectInput.svelte +38 -22
  23. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
  24. package/dist/components/forms/TextInput.svelte +32 -18
  25. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
  26. package/dist/components/forms/TextareaInput.svelte +25 -18
  27. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
  28. package/dist/components/forms/__tests__/AddressInput.behavior.test.js +94 -0
  29. package/dist/components/forms/__tests__/Form.behavior.test.js +1 -1
  30. package/dist/components/forms/__tests__/Form.extraction.test.js +68 -0
  31. package/dist/components/forms/__tests__/Form.test.js +28 -0
  32. package/dist/components/forms/__tests__/Form.webmcp.test.js +2123 -114
  33. package/dist/components/forms/__tests__/FormMicButton.test.js +0 -1
  34. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte +30 -0
  35. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte.d.ts +7 -0
  36. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte.d.ts.map +1 -0
  37. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte +19 -0
  38. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte.d.ts +9 -0
  39. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte.d.ts.map +1 -0
  40. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte +21 -0
  41. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte.d.ts +8 -0
  42. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte.d.ts.map +1 -0
  43. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte +27 -0
  44. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte.d.ts +9 -0
  45. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte.d.ts.map +1 -0
  46. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte +48 -0
  47. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte.d.ts +10 -0
  48. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte.d.ts.map +1 -0
  49. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte +26 -0
  50. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte.d.ts +13 -0
  51. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte.d.ts.map +1 -0
  52. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte +52 -0
  53. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte.d.ts +16 -0
  54. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte.d.ts.map +1 -0
  55. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte +108 -5
  56. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts +34 -0
  57. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts.map +1 -1
  58. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte +24 -0
  59. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte.d.ts +11 -0
  60. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte.d.ts.map +1 -0
  61. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte +69 -4
  62. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte.d.ts +32 -0
  63. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte.d.ts.map +1 -1
  64. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte +44 -0
  65. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte.d.ts +9 -0
  66. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte.d.ts.map +1 -0
  67. package/dist/components/forms/__tests__/policy-field.fixture.svelte +35 -0
  68. package/dist/components/forms/__tests__/policy-field.fixture.svelte.d.ts +8 -0
  69. package/dist/components/forms/__tests__/policy-field.fixture.svelte.d.ts.map +1 -0
  70. package/dist/components/forms/prepare-field-value.d.ts +5 -0
  71. package/dist/components/forms/prepare-field-value.d.ts.map +1 -0
  72. package/dist/components/forms/prepare-field-value.js +15 -0
  73. package/dist/state/form-context.d.ts +28 -6
  74. package/dist/state/form-context.d.ts.map +1 -1
  75. package/dist/state/form-context.js +75 -7
  76. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte +62 -0
  77. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte.d.ts +8 -0
  78. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte.d.ts.map +1 -0
  79. package/dist/web/__tests__/webmcp-composed.fixture.svelte +52 -0
  80. package/dist/web/__tests__/webmcp-composed.fixture.svelte.d.ts +12 -0
  81. package/dist/web/__tests__/webmcp-composed.fixture.svelte.d.ts.map +1 -0
  82. package/dist/web/__tests__/webmcp-composed.integration.svelte.test.js +247 -0
  83. package/dist/web/__tests__/webmcp-ssr.fixture.svelte +13 -0
  84. package/dist/web/__tests__/webmcp-ssr.fixture.svelte.d.ts +19 -0
  85. package/dist/web/__tests__/webmcp-ssr.fixture.svelte.d.ts.map +1 -0
  86. package/dist/web/__tests__/webmcp-ui.test.js +209 -33
  87. package/dist/web/__tests__/webmcp.test.js +20 -0
  88. package/dist/web/webmcp-ui.d.ts.map +1 -1
  89. package/dist/web/webmcp-ui.js +50 -31
  90. package/dist/web/webmcp.svelte.d.ts.map +1 -1
  91. package/dist/web/webmcp.svelte.js +6 -1
  92. package/package.json +8 -6
@@ -38,7 +38,6 @@ vi.mock('../../../state/form-context.js', () => ({
38
38
  },
39
39
  toggleListening,
40
40
  registerField: vi.fn(),
41
- unregisterField: vi.fn(),
42
41
  getFieldSchema: () => [],
43
42
  }),
44
43
  }));
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import {
4
+ type FieldDefinition,
5
+ tryGetFormContext,
6
+ } from '../../../state/form-context.js';
7
+
8
+ let {
9
+ validate,
10
+ }: {
11
+ validate: () => boolean | Promise<boolean>;
12
+ } = $props();
13
+
14
+ const formContext = tryGetFormContext();
15
+ const label = 'Async invalid field';
16
+
17
+ onMount(() => {
18
+ const field: FieldDefinition = {
19
+ name: 'async-invalid',
20
+ type: 'text',
21
+ label,
22
+ getValue: () => 'proposal',
23
+ setValue: () => {},
24
+ validate,
25
+ };
26
+ return formContext?.registerField(field);
27
+ });
28
+ </script>
29
+
30
+ <input aria-label={label} value="proposal" />
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ validate: () => boolean | Promise<boolean>;
3
+ };
4
+ declare const AsyncInvalidField: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type AsyncInvalidField = ReturnType<typeof AsyncInvalidField>;
6
+ export default AsyncInvalidField;
7
+ //# sourceMappingURL=async-invalid-field.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-invalid-field.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/async-invalid-field.fixture.svelte.ts"],"names":[],"mappings":"AASC,KAAK,gBAAgB,GAAI;IACxB,QAAQ,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5C,CAAC;AA6BF,QAAA,MAAM,iBAAiB,sDAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import Form from '../Form.svelte';
3
+ import AsyncInvalidField from './async-invalid-field.fixture.svelte';
4
+
5
+ let {
6
+ onsubmit,
7
+ validate,
8
+ webmcp = true,
9
+ }: {
10
+ onsubmit: (data: Record<string, unknown>) => void | Promise<void>;
11
+ validate: () => boolean | Promise<boolean>;
12
+ webmcp?: boolean;
13
+ } = $props();
14
+ </script>
15
+
16
+ <Form {onsubmit} {webmcp}>
17
+ <AsyncInvalidField {validate} />
18
+ <button type="submit">Submit</button>
19
+ </Form>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ onsubmit: (data: Record<string, unknown>) => void | Promise<void>;
3
+ validate: () => boolean | Promise<boolean>;
4
+ webmcp?: boolean;
5
+ };
6
+ declare const AsyncValidationForm: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type AsyncValidationForm = ReturnType<typeof AsyncValidationForm>;
8
+ export default AsyncValidationForm;
9
+ //# sourceMappingURL=async-validation-form.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-validation-form.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/async-validation-form.fixture.svelte.ts"],"names":[],"mappings":"AAMC,KAAK,gBAAgB,GAAI;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAmBF,QAAA,MAAM,mBAAmB,sDAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import Form from '../Form.svelte';
3
+ import TextInput from '../TextInput.svelte';
4
+ import AsyncInvalidField from './async-invalid-field.fixture.svelte';
5
+
6
+ let {
7
+ onsubmit,
8
+ validate,
9
+ }: {
10
+ onsubmit: (data: Record<string, unknown>) => void | Promise<void>;
11
+ validate: () => boolean | Promise<boolean>;
12
+ } = $props();
13
+
14
+ let laterValue = $state('before');
15
+ </script>
16
+
17
+ <Form {onsubmit} webmcp>
18
+ <AsyncInvalidField {validate} />
19
+ <TextInput name="later" label="Later value" bind:value={laterValue} />
20
+ <button type="submit">Submit</button>
21
+ </Form>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ onsubmit: (data: Record<string, unknown>) => void | Promise<void>;
3
+ validate: () => boolean | Promise<boolean>;
4
+ };
5
+ declare const AsyncValidationSnapshotForm: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type AsyncValidationSnapshotForm = ReturnType<typeof AsyncValidationSnapshotForm>;
7
+ export default AsyncValidationSnapshotForm;
8
+ //# sourceMappingURL=async-validation-snapshot-form.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-validation-snapshot-form.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte.ts"],"names":[],"mappings":"AAOC,KAAK,gBAAgB,GAAI;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5C,CAAC;AAsBF,QAAA,MAAM,2BAA2B,sDAAwC,CAAC;AAC1E,KAAK,2BAA2B,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAClF,eAAe,2BAA2B,CAAC"}
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import { getFormContext } from '../../../state/form-context.js';
3
+ import CustomRegistrationField from './custom-registration-field.fixture.svelte';
4
+
5
+ let {
6
+ showFirst,
7
+ showReplacement,
8
+ legacyCleanup,
9
+ }: {
10
+ showFirst: boolean;
11
+ showReplacement: boolean;
12
+ legacyCleanup: boolean;
13
+ } = $props();
14
+
15
+ // Compound fields often read the context for their own coordination before
16
+ // mounting nested custom fields. Their descendants must still own cleanup.
17
+ const formId = getFormContext().formId;
18
+ </script>
19
+
20
+ <div data-compound-form={formId}>
21
+ {#if showFirst}
22
+ <CustomRegistrationField name="custom-shared" label="Custom first" {legacyCleanup} />
23
+ {/if}
24
+ {#if showReplacement}
25
+ <CustomRegistrationField name="custom-shared" label="Custom replacement" {legacyCleanup} />
26
+ {/if}
27
+ </div>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ showFirst: boolean;
3
+ showReplacement: boolean;
4
+ legacyCleanup: boolean;
5
+ };
6
+ declare const CompoundRegistrationWrapper: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type CompoundRegistrationWrapper = ReturnType<typeof CompoundRegistrationWrapper>;
8
+ export default CompoundRegistrationWrapper;
9
+ //# sourceMappingURL=compound-registration-wrapper.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compound-registration-wrapper.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/compound-registration-wrapper.fixture.svelte.ts"],"names":[],"mappings":"AAMC,KAAK,gBAAgB,GAAI;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AA2BF,QAAA,MAAM,2BAA2B,sDAAwC,CAAC;AAC1E,KAAK,2BAA2B,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAClF,eAAe,2BAA2B,CAAC"}
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import {
4
+ type FieldDefinition,
5
+ getFormContext,
6
+ tryGetFormContext,
7
+ } from '../../../state/form-context.js';
8
+
9
+ let {
10
+ name,
11
+ label,
12
+ legacyCleanup = false,
13
+ separateLegacyCleanupContext = false,
14
+ }: {
15
+ name: string;
16
+ label: string;
17
+ legacyCleanup?: boolean;
18
+ separateLegacyCleanupContext?: boolean;
19
+ } = $props();
20
+
21
+ let value = $state('');
22
+ const formContext = tryGetFormContext();
23
+ // Legacy helpers sometimes obtain the context again for teardown. The accessors
24
+ // must retain this component's ownership boundary across both calls.
25
+ // svelte-ignore state_referenced_locally
26
+ const cleanupContext = separateLegacyCleanupContext
27
+ ? getFormContext()
28
+ : formContext;
29
+
30
+ onMount(() => {
31
+ const field: FieldDefinition = {
32
+ name,
33
+ type: 'text',
34
+ label,
35
+ setValue: (next) => {
36
+ value = String(next ?? '');
37
+ },
38
+ getValue: () => value,
39
+ };
40
+ const dispose = formContext?.registerField(field);
41
+ if (!formContext) return;
42
+ return legacyCleanup
43
+ ? () => cleanupContext?.unregisterField(field.name)
44
+ : dispose;
45
+ });
46
+ </script>
47
+
48
+ <input {name} aria-label={label} bind:value />
@@ -0,0 +1,10 @@
1
+ type $$ComponentProps = {
2
+ name: string;
3
+ label: string;
4
+ legacyCleanup?: boolean;
5
+ separateLegacyCleanupContext?: boolean;
6
+ };
7
+ declare const CustomRegistrationField: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type CustomRegistrationField = ReturnType<typeof CustomRegistrationField>;
9
+ export default CustomRegistrationField;
10
+ //# sourceMappingURL=custom-registration-field.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-registration-field.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/custom-registration-field.fixture.svelte.ts"],"names":[],"mappings":"AAUC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC,CAAC;AA2CF,QAAA,MAAM,uBAAuB,sDAAwC,CAAC;AACtE,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC1E,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import DateRangeInput from '../DateRangeInput.svelte';
3
+ import Form from '../Form.svelte';
4
+ import NumberInput from '../NumberInput.svelte';
5
+ import TextInput from '../TextInput.svelte';
6
+
7
+ let {
8
+ onagechange,
9
+ ondateschange,
10
+ ontextchange,
11
+ showDateRange = false,
12
+ }: {
13
+ onagechange?: (value: number | null) => void;
14
+ ondateschange?: (value: { startDate: string; endDate: string }) => void;
15
+ ontextchange?: (value: string) => void;
16
+ showDateRange?: boolean;
17
+ } = $props();
18
+ </script>
19
+
20
+ <Form showFormListen sttAdapter="browser-speech">
21
+ <NumberInput name="age" label="Age" onchange={onagechange} />
22
+ {#if showDateRange}
23
+ <DateRangeInput name="dates" label="Dates" onchange={ondateschange} />
24
+ {/if}
25
+ <TextInput name="fullname" label="Full name" onchange={ontextchange} />
26
+ </Form>
@@ -0,0 +1,13 @@
1
+ type $$ComponentProps = {
2
+ onagechange?: (value: number | null) => void;
3
+ ondateschange?: (value: {
4
+ startDate: string;
5
+ endDate: string;
6
+ }) => void;
7
+ ontextchange?: (value: string) => void;
8
+ showDateRange?: boolean;
9
+ };
10
+ declare const FormExtractionValues: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type FormExtractionValues = ReturnType<typeof FormExtractionValues>;
12
+ export default FormExtractionValues;
13
+ //# sourceMappingURL=form-extraction-values.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-extraction-values.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-extraction-values.fixture.svelte.ts"],"names":[],"mappings":"AAQC,KAAK,gBAAgB,GAAI;IACxB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAyBF,QAAA,MAAM,oBAAoB,sDAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import type { ControlInteractionRegistry } from '@happyvertical/smrt-ui/forms';
3
+ import Form from '../Form.svelte';
4
+ import TextInput from '../TextInput.svelte';
5
+ import CompoundRegistrationWrapper from './compound-registration-wrapper.fixture.svelte';
6
+ import CustomRegistrationField from './custom-registration-field.fixture.svelte';
7
+
8
+ let {
9
+ interactionRegistry,
10
+ firstName = 'first',
11
+ showFirst = true,
12
+ showReplacement = false,
13
+ showCustomFirst = false,
14
+ showCustomReplacement = false,
15
+ legacyCustomCleanup = false,
16
+ separateLegacyCleanupContext = false,
17
+ compoundLegacyCustom = false,
18
+ }: {
19
+ interactionRegistry: ControlInteractionRegistry;
20
+ firstName?: string;
21
+ showFirst?: boolean;
22
+ showReplacement?: boolean;
23
+ showCustomFirst?: boolean;
24
+ showCustomReplacement?: boolean;
25
+ legacyCustomCleanup?: boolean;
26
+ separateLegacyCleanupContext?: boolean;
27
+ compoundLegacyCustom?: boolean;
28
+ } = $props();
29
+ </script>
30
+
31
+ <Form {interactionRegistry} formId="registration-lifecycle">
32
+ {#if showFirst}
33
+ <TextInput name={firstName} label="First field" />
34
+ {/if}
35
+ {#if showReplacement}
36
+ <TextInput name="shared" label="Replacement field" />
37
+ {/if}
38
+ {#if compoundLegacyCustom}
39
+ <CompoundRegistrationWrapper
40
+ showFirst={showCustomFirst}
41
+ showReplacement={showCustomReplacement}
42
+ legacyCleanup={legacyCustomCleanup}
43
+ />
44
+ {:else}
45
+ {#if showCustomFirst}
46
+ <CustomRegistrationField name="custom-shared" label="Custom first" legacyCleanup={legacyCustomCleanup} {separateLegacyCleanupContext} />
47
+ {/if}
48
+ {#if showCustomReplacement}
49
+ <CustomRegistrationField name="custom-shared" label="Custom replacement" legacyCleanup={legacyCustomCleanup} {separateLegacyCleanupContext} />
50
+ {/if}
51
+ {/if}
52
+ </Form>
@@ -0,0 +1,16 @@
1
+ import type { ControlInteractionRegistry } from '@happyvertical/smrt-ui/forms';
2
+ type $$ComponentProps = {
3
+ interactionRegistry: ControlInteractionRegistry;
4
+ firstName?: string;
5
+ showFirst?: boolean;
6
+ showReplacement?: boolean;
7
+ showCustomFirst?: boolean;
8
+ showCustomReplacement?: boolean;
9
+ legacyCustomCleanup?: boolean;
10
+ separateLegacyCleanupContext?: boolean;
11
+ compoundLegacyCustom?: boolean;
12
+ };
13
+ declare const FormRegistrationLifecycle: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type FormRegistrationLifecycle = ReturnType<typeof FormRegistrationLifecycle>;
15
+ export default FormRegistrationLifecycle;
16
+ //# sourceMappingURL=form-registration-lifecycle.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-registration-lifecycle.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-registration-lifecycle.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAM9E,KAAK,gBAAgB,GAAI;IACxB,mBAAmB,EAAE,0BAA0B,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AA0CF,QAAA,MAAM,yBAAyB,sDAAwC,CAAC;AACxE,KAAK,yBAAyB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC9E,eAAe,yBAAyB,CAAC"}
@@ -3,8 +3,15 @@
3
3
  * Form's field-registration → value-collection → onsubmit contract can be
4
4
  * exercised end-to-end (the submit handler reads each registered field's
5
5
  * getValue()). */
6
+ import type { ControlInteractionRegistry } from '@happyvertical/smrt-ui/forms';
7
+ import CheckboxInput from '../CheckboxInput.svelte';
8
+ import DateTimeInput from '../DateTimeInput.svelte';
6
9
  import Form from '../Form.svelte';
10
+ import MoneyInput from '../MoneyInput.svelte';
7
11
  import NumberInput from '../NumberInput.svelte';
12
+ import PhoneInput from '../PhoneInput.svelte';
13
+ import SelectInput from '../SelectInput.svelte';
14
+ import TextareaInput from '../TextareaInput.svelte';
8
15
  import TextInput from '../TextInput.svelte';
9
16
 
10
17
  let {
@@ -14,11 +21,36 @@ let {
14
21
  textValue = '',
15
22
  numberValue = null,
16
23
  showAge = true,
24
+ showMoney = false,
25
+ showClearFields = false,
26
+ showScalarFields = false,
27
+ checkboxValue = true,
28
+ checkboxRequired = false,
29
+ selectValue = 'second',
30
+ selectRequired = false,
31
+ dateValue = '',
32
+ phoneValue = '',
33
+ notesValue = '',
34
+ notesAppendMode = false,
35
+ notesRequired = false,
36
+ moneyMin = undefined,
37
+ moneyMax = undefined,
17
38
  webmcp = false,
18
39
  textRequired = false,
40
+ textDisabled = false,
41
+ fieldsetDisabled = false,
42
+ formSubject = undefined,
19
43
  ageRequired = false,
44
+ ageLabel = 'Age',
20
45
  ageMin = undefined,
21
46
  ageMax = undefined,
47
+ ageStep = undefined,
48
+ interactionRegistry = undefined,
49
+ ontextchange = undefined,
50
+ oncheckboxchange = undefined,
51
+ onselectchange = undefined,
52
+ onnoteschange = undefined,
53
+ onnumberchange = undefined,
22
54
  }: {
23
55
  onsubmit?: (data: Record<string, unknown>) => void;
24
56
  method?: 'GET' | 'POST';
@@ -26,30 +58,101 @@ let {
26
58
  textValue?: string;
27
59
  numberValue?: number | null;
28
60
  showAge?: boolean;
61
+ showMoney?: boolean;
62
+ showClearFields?: boolean;
63
+ showScalarFields?: boolean;
64
+ checkboxValue?: boolean;
65
+ checkboxRequired?: boolean;
66
+ selectValue?: string;
67
+ selectRequired?: boolean;
68
+ dateValue?: string;
69
+ phoneValue?: string;
70
+ notesValue?: string;
71
+ notesAppendMode?: boolean;
72
+ notesRequired?: boolean;
73
+ moneyMin?: number;
74
+ moneyMax?: number;
29
75
  webmcp?: boolean;
30
76
  textRequired?: boolean;
77
+ textDisabled?: boolean;
78
+ fieldsetDisabled?: boolean;
79
+ formSubject?: { type: string; id: string; label?: string };
31
80
  ageRequired?: boolean;
81
+ ageLabel?: string;
32
82
  ageMin?: number;
33
83
  ageMax?: number;
84
+ ageStep?: number;
85
+ interactionRegistry?: ControlInteractionRegistry;
86
+ ontextchange?: (value: string) => void;
87
+ oncheckboxchange?: (value: boolean) => void;
88
+ onselectchange?: (value: string) => void;
89
+ onnoteschange?: (value: string) => void;
90
+ onnumberchange?: (value: number | null) => void;
34
91
  } = $props();
92
+ let lastSubject: { type: string; id: string; label?: string } | undefined;
93
+ let mutableSubject = $state<
94
+ { type: string; id: string; label?: string } | undefined
95
+ >(undefined);
96
+ const mutateSubjectLabel = 'Mutate subject';
97
+ $effect(() => {
98
+ const nextSubject = formSubject;
99
+ if (nextSubject !== lastSubject) {
100
+ lastSubject = nextSubject;
101
+ mutableSubject = nextSubject ? { ...nextSubject } : undefined;
102
+ }
103
+ });
104
+ function mutateSubject() {
105
+ if (mutableSubject) mutableSubject.id = 'person-mutated';
106
+ }
35
107
  </script>
36
108
 
37
- <Form {onsubmit} {method} {action} {webmcp}>
109
+ <Form {onsubmit} {method} {action} {webmcp} {interactionRegistry} subject={mutableSubject}>
110
+ <fieldset disabled={fieldsetDisabled}>
38
111
  <TextInput
39
112
  name="fullname"
40
113
  label="Full name"
41
- required={textRequired}
114
+ required={textRequired}
115
+ disabled={textDisabled}
116
+ onchange={ontextchange}
42
117
  bind:value={textValue}
43
118
  />
44
119
  {#if showAge}
45
- <NumberInput
46
- name="age"
47
- label="Age"
120
+ <NumberInput
121
+ name="age"
122
+ label={ageLabel}
48
123
  required={ageRequired}
49
124
  min={ageMin}
50
125
  max={ageMax}
126
+ step={ageStep}
127
+ onchange={onnumberchange}
51
128
  bind:value={numberValue}
52
129
  />
53
130
  {/if}
131
+ {#if showMoney}
132
+ <MoneyInput name="budget" label="Budget" min={moneyMin} max={moneyMax} />
133
+ {/if}
134
+ {#if showClearFields}
135
+ <CheckboxInput name="enabled" label="Enabled" required={checkboxRequired} onchange={oncheckboxchange} bind:checked={checkboxValue} />
136
+ <SelectInput
137
+ name="choice"
138
+ label="Choice"
139
+ required={selectRequired}
140
+ onchange={onselectchange}
141
+ options={[
142
+ { value: 'first', label: 'First' },
143
+ { value: 'second', label: 'Second' },
144
+ ]}
145
+ bind:value={selectValue}
146
+ />
147
+ {/if}
148
+ {#if showScalarFields}
149
+ <DateTimeInput name="appointment" label="Appointment" includeTime={false} bind:value={dateValue} />
150
+ <PhoneInput name="phone" label="Phone" bind:value={phoneValue} />
151
+ <TextareaInput name="notes" label="Notes" required={notesRequired} appendMode={notesAppendMode} onchange={onnoteschange} bind:value={notesValue} />
152
+ {/if}
153
+ </fieldset>
154
+ {#if mutableSubject}
155
+ <button type="button" onclick={mutateSubject}>{mutateSubjectLabel}</button>
156
+ {/if}
54
157
  <button type="submit">Submit</button>
55
158
  </Form>
@@ -1,3 +1,8 @@
1
+ /** Test fixture (S11 #1416): a Form wrapping a TextInput + NumberInput so the
2
+ * Form's field-registration → value-collection → onsubmit contract can be
3
+ * exercised end-to-end (the submit handler reads each registered field's
4
+ * getValue()). */
5
+ import type { ControlInteractionRegistry } from '@happyvertical/smrt-ui/forms';
1
6
  type $$ComponentProps = {
2
7
  onsubmit?: (data: Record<string, unknown>) => void;
3
8
  method?: 'GET' | 'POST';
@@ -5,11 +10,40 @@ type $$ComponentProps = {
5
10
  textValue?: string;
6
11
  numberValue?: number | null;
7
12
  showAge?: boolean;
13
+ showMoney?: boolean;
14
+ showClearFields?: boolean;
15
+ showScalarFields?: boolean;
16
+ checkboxValue?: boolean;
17
+ checkboxRequired?: boolean;
18
+ selectValue?: string;
19
+ selectRequired?: boolean;
20
+ dateValue?: string;
21
+ phoneValue?: string;
22
+ notesValue?: string;
23
+ notesAppendMode?: boolean;
24
+ notesRequired?: boolean;
25
+ moneyMin?: number;
26
+ moneyMax?: number;
8
27
  webmcp?: boolean;
9
28
  textRequired?: boolean;
29
+ textDisabled?: boolean;
30
+ fieldsetDisabled?: boolean;
31
+ formSubject?: {
32
+ type: string;
33
+ id: string;
34
+ label?: string;
35
+ };
10
36
  ageRequired?: boolean;
37
+ ageLabel?: string;
11
38
  ageMin?: number;
12
39
  ageMax?: number;
40
+ ageStep?: number;
41
+ interactionRegistry?: ControlInteractionRegistry;
42
+ ontextchange?: (value: string) => void;
43
+ oncheckboxchange?: (value: boolean) => void;
44
+ onselectchange?: (value: string) => void;
45
+ onnoteschange?: (value: string) => void;
46
+ onnumberchange?: (value: number | null) => void;
13
47
  };
14
48
  declare const FormWithFields: import("svelte").Component<$$ComponentProps, {}, "">;
15
49
  type FormWithFields = ReturnType<typeof FormWithFields>;
@@ -1 +1 @@
1
- {"version":3,"file":"form-with-fields.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-with-fields.fixture.svelte.ts"],"names":[],"mappings":"AAWC,KAAK,gBAAgB,GAAI;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnD,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAgCF,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"form-with-fields.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-with-fields.fixture.svelte.ts"],"names":[],"mappings":"AAGA;;;mBAGmB;AACnB,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAW9E,KAAK,gBAAgB,GAAI;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnD,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CACjD,CAAC;AAmGF,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import type {
3
+ ControlInteractionRegistry,
4
+ ControlSensitivity,
5
+ } from '@happyvertical/smrt-ui/forms';
6
+ import Form from '../Form.svelte';
7
+ import PolicyField from './policy-field.fixture.svelte';
8
+
9
+ let {
10
+ webmcp = false,
11
+ interactionRegistry,
12
+ sensitivity = 'public',
13
+ writable = true,
14
+ }: {
15
+ webmcp?: boolean;
16
+ interactionRegistry?: ControlInteractionRegistry;
17
+ sensitivity?: ControlSensitivity;
18
+ writable?: boolean;
19
+ } = $props();
20
+ </script>
21
+
22
+ <Form {webmcp} {interactionRegistry} formId="policy-form">
23
+ <PolicyField sensitivity={sensitivity === 'secret' ? 'secret' : 'public'} {writable} />
24
+ </Form>
@@ -0,0 +1,11 @@
1
+ import type { ControlInteractionRegistry, ControlSensitivity } from '@happyvertical/smrt-ui/forms';
2
+ type $$ComponentProps = {
3
+ webmcp?: boolean;
4
+ interactionRegistry?: ControlInteractionRegistry;
5
+ sensitivity?: ControlSensitivity;
6
+ writable?: boolean;
7
+ };
8
+ declare const FormWithPolicyField: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type FormWithPolicyField = ReturnType<typeof FormWithPolicyField>;
10
+ export default FormWithPolicyField;
11
+ //# sourceMappingURL=form-with-policy-field.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-with-policy-field.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-with-policy-field.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,8BAA8B,CAAC;AAIrC,KAAK,gBAAgB,GAAI;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAoBF,QAAA,MAAM,mBAAmB,sDAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}