@happyvertical/smrt-svelte 0.30.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.
- package/AGENTS.md +317 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +185 -0
- package/dist/Provider.svelte +204 -0
- package/dist/Provider.svelte.d.ts +73 -0
- package/dist/Provider.svelte.d.ts.map +1 -0
- package/dist/__tests__/app-state.test.js +156 -0
- package/dist/__tests__/warm-clients.test.js +186 -0
- package/dist/browser-ai/adapters/llm/factory.d.ts +38 -0
- package/dist/browser-ai/adapters/llm/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/factory.js +91 -0
- package/dist/browser-ai/adapters/llm/index.d.ts +7 -0
- package/dist/browser-ai/adapters/llm/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/index.js +6 -0
- package/dist/browser-ai/adapters/llm/types.d.ts +182 -0
- package/dist/browser-ai/adapters/llm/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/types.js +43 -0
- package/dist/browser-ai/adapters/llm/webllm.d.ts +33 -0
- package/dist/browser-ai/adapters/llm/webllm.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/webllm.js +225 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts +31 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/browser-speech.js +217 -0
- package/dist/browser-ai/adapters/stt/factory.d.ts +49 -0
- package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/factory.js +110 -0
- package/dist/browser-ai/adapters/stt/index.d.ts +9 -0
- package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/index.js +8 -0
- package/dist/browser-ai/adapters/stt/types.d.ts +154 -0
- package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/types.js +4 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts +46 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.js +348 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +51 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.js +380 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts +42 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.js +235 -0
- package/dist/browser-ai/adapters/tts/factory.d.ts +53 -0
- package/dist/browser-ai/adapters/tts/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/factory.js +92 -0
- package/dist/browser-ai/adapters/tts/index.d.ts +7 -0
- package/dist/browser-ai/adapters/tts/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/index.js +6 -0
- package/dist/browser-ai/adapters/tts/types.d.ts +140 -0
- package/dist/browser-ai/adapters/tts/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/types.js +4 -0
- package/dist/browser-ai/capabilities/detector.d.ts +38 -0
- package/dist/browser-ai/capabilities/detector.d.ts.map +1 -0
- package/dist/browser-ai/capabilities/detector.js +211 -0
- package/dist/browser-ai/core/errors.d.ts +62 -0
- package/dist/browser-ai/core/errors.d.ts.map +1 -0
- package/dist/browser-ai/core/errors.js +92 -0
- package/dist/browser-ai/core/index.d.ts +6 -0
- package/dist/browser-ai/core/index.d.ts.map +1 -0
- package/dist/browser-ai/core/index.js +5 -0
- package/dist/browser-ai/core/types.d.ts +115 -0
- package/dist/browser-ai/core/types.d.ts.map +1 -0
- package/dist/browser-ai/core/types.js +34 -0
- package/dist/browser-ai/index.d.ts +12 -0
- package/dist/browser-ai/index.d.ts.map +1 -0
- package/dist/browser-ai/index.js +16 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte +77 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts +16 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte +57 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts +15 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte +141 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts +15 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte +379 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts +9 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte +200 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts +16 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/index.d.ts +15 -0
- package/dist/browser-ai/svelte/index.d.ts.map +1 -0
- package/dist/browser-ai/svelte/index.js +28 -0
- package/dist/browser-ai/ui.d.ts +16 -0
- package/dist/browser-ai/ui.d.ts.map +1 -0
- package/dist/browser-ai/ui.js +67 -0
- package/dist/components/admin/AgentAdminPanel.svelte +111 -0
- package/dist/components/admin/AgentAdminPanel.svelte.d.ts +25 -0
- package/dist/components/admin/AgentAdminPanel.svelte.d.ts.map +1 -0
- package/dist/components/admin/AgentAdminTabs.svelte +280 -0
- package/dist/components/admin/AgentAdminTabs.svelte.d.ts +23 -0
- package/dist/components/admin/AgentAdminTabs.svelte.d.ts.map +1 -0
- package/dist/components/admin/AgentSettingsShell.svelte +257 -0
- package/dist/components/admin/AgentSettingsShell.svelte.d.ts +33 -0
- package/dist/components/admin/AgentSettingsShell.svelte.d.ts.map +1 -0
- package/dist/components/admin/index.d.ts +5 -0
- package/dist/components/admin/index.d.ts.map +1 -0
- package/dist/components/admin/index.js +6 -0
- package/dist/components/forms/AddressInput.svelte +500 -0
- package/dist/components/forms/AddressInput.svelte.d.ts +36 -0
- package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/CheckboxInput.svelte +208 -0
- package/dist/components/forms/CheckboxInput.svelte.d.ts +20 -0
- package/dist/components/forms/CheckboxInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/DateRangeInput.svelte +628 -0
- package/dist/components/forms/DateRangeInput.svelte.d.ts +33 -0
- package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/DateTimeInput.svelte +521 -0
- package/dist/components/forms/DateTimeInput.svelte.d.ts +24 -0
- package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/FileUpload.svelte +358 -0
- package/dist/components/forms/FileUpload.svelte.d.ts +22 -0
- package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -0
- package/dist/components/forms/Form.svelte +771 -0
- package/dist/components/forms/Form.svelte.d.ts +26 -0
- package/dist/components/forms/Form.svelte.d.ts.map +1 -0
- package/dist/components/forms/FormGroup.svelte +86 -0
- package/dist/components/forms/FormGroup.svelte.d.ts +13 -0
- package/dist/components/forms/FormGroup.svelte.d.ts.map +1 -0
- package/dist/components/forms/FormMicButton.svelte +179 -0
- package/dist/components/forms/FormMicButton.svelte.d.ts +10 -0
- package/dist/components/forms/FormMicButton.svelte.d.ts.map +1 -0
- package/dist/components/forms/Input.svelte +83 -0
- package/dist/components/forms/Input.svelte.d.ts +9 -0
- package/dist/components/forms/Input.svelte.d.ts.map +1 -0
- package/dist/components/forms/MeasurementInput.svelte +505 -0
- package/dist/components/forms/MeasurementInput.svelte.d.ts +35 -0
- package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/MoneyInput.svelte +412 -0
- package/dist/components/forms/MoneyInput.svelte.d.ts +30 -0
- package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/NumberInput.svelte +310 -0
- package/dist/components/forms/NumberInput.svelte.d.ts +28 -0
- package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/PhoneInput.svelte +530 -0
- package/dist/components/forms/PhoneInput.svelte.d.ts +22 -0
- package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/SearchInput.svelte +358 -0
- package/dist/components/forms/SearchInput.svelte.d.ts +33 -0
- package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Select.svelte +83 -0
- package/dist/components/forms/Select.svelte.d.ts +11 -0
- package/dist/components/forms/Select.svelte.d.ts.map +1 -0
- package/dist/components/forms/SelectInput.svelte +254 -0
- package/dist/components/forms/SelectInput.svelte.d.ts +25 -0
- package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/TextInput.svelte +415 -0
- package/dist/components/forms/TextInput.svelte.d.ts +26 -0
- package/dist/components/forms/TextInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Textarea.svelte +85 -0
- package/dist/components/forms/Textarea.svelte.d.ts +10 -0
- package/dist/components/forms/Textarea.svelte.d.ts.map +1 -0
- package/dist/components/forms/TextareaInput.svelte +386 -0
- package/dist/components/forms/TextareaInput.svelte.d.ts +26 -0
- package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Toggle.svelte +217 -0
- package/dist/components/forms/Toggle.svelte.d.ts +37 -0
- package/dist/components/forms/Toggle.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/AddressInput.behavior.test.js +122 -0
- package/dist/components/forms/__tests__/CheckboxInput.test.js +92 -0
- package/dist/components/forms/__tests__/DateRangeInput.behavior.test.js +135 -0
- package/dist/components/forms/__tests__/DateTimeInput.behavior.test.js +103 -0
- package/dist/components/forms/__tests__/FileUpload.test.js +90 -0
- package/dist/components/forms/__tests__/Form.behavior.test.js +137 -0
- package/dist/components/forms/__tests__/Form.test.js +58 -0
- package/dist/components/forms/__tests__/FormGroup.test.js +48 -0
- package/dist/components/forms/__tests__/FormMicButton.test.js +86 -0
- package/dist/components/forms/__tests__/Input.test.js +49 -0
- package/dist/components/forms/__tests__/MeasurementInput.behavior.test.js +129 -0
- package/dist/components/forms/__tests__/MoneyInput.behavior.test.js +124 -0
- package/dist/components/forms/__tests__/NumberInput.behavior.test.js +141 -0
- package/dist/components/forms/__tests__/PhoneInput.behavior.test.js +96 -0
- package/dist/components/forms/__tests__/SearchInput.test.js +79 -0
- package/dist/components/forms/__tests__/Select.test.js +37 -0
- package/dist/components/forms/__tests__/SelectInput.behavior.test.js +132 -0
- package/dist/components/forms/__tests__/TextInput.behavior.test.js +131 -0
- package/dist/components/forms/__tests__/Textarea.test.js +39 -0
- package/dist/components/forms/__tests__/TextareaInput.behavior.test.js +96 -0
- package/dist/components/forms/__tests__/Toggle.test.js +87 -0
- package/dist/components/forms/__tests__/composite-inputs-a11y.test.js +69 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte +16 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +9 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte +33 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/rich-inputs-a11y.test.js +87 -0
- package/dist/components/forms/index.d.ts +25 -0
- package/dist/components/forms/index.d.ts.map +1 -0
- package/dist/components/forms/index.js +25 -0
- package/dist/components/forms/types.d.ts +33 -0
- package/dist/components/forms/types.d.ts.map +1 -0
- package/dist/components/forms/types.js +4 -0
- package/dist/components/module/ModulePanel.svelte +134 -0
- package/dist/components/module/ModulePanel.svelte.d.ts +22 -0
- package/dist/components/module/ModulePanel.svelte.d.ts.map +1 -0
- package/dist/components/module/index.d.ts +5 -0
- package/dist/components/module/index.d.ts.map +1 -0
- package/dist/components/module/index.js +4 -0
- package/dist/components/workspace/Breadcrumbs.svelte +141 -0
- package/dist/components/workspace/Breadcrumbs.svelte.d.ts +21 -0
- package/dist/components/workspace/Breadcrumbs.svelte.d.ts.map +1 -0
- package/dist/components/workspace/NavTree.svelte +354 -0
- package/dist/components/workspace/NavTree.svelte.d.ts +45 -0
- package/dist/components/workspace/NavTree.svelte.d.ts.map +1 -0
- package/dist/components/workspace/README.md +34 -0
- package/dist/components/workspace/RoleShell.svelte +309 -0
- package/dist/components/workspace/RoleShell.svelte.d.ts +91 -0
- package/dist/components/workspace/RoleShell.svelte.d.ts.map +1 -0
- package/dist/components/workspace/WorkspaceShell.svelte +951 -0
- package/dist/components/workspace/WorkspaceShell.svelte.d.ts +112 -0
- package/dist/components/workspace/WorkspaceShell.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/RoleShell.test.js +772 -0
- package/dist/components/workspace/__tests__/WorkspaceShell.test.js +630 -0
- package/dist/components/workspace/__tests__/breadcrumbs-helpers.test.js +141 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte +45 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts +21 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/define-tools-dock.test.js +1010 -0
- package/dist/components/workspace/__tests__/harness.svelte +25 -0
- package/dist/components/workspace/__tests__/harness.svelte.d.ts +14 -0
- package/dist/components/workspace/__tests__/harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/index.test.js +37 -0
- package/dist/components/workspace/__tests__/manifest-nav-helpers.test.js +24 -0
- package/dist/components/workspace/__tests__/manifest-nav.test.js +599 -0
- package/dist/components/workspace/__tests__/nav-helpers.test.js +95 -0
- package/dist/components/workspace/__tests__/render-harness.svelte +66 -0
- package/dist/components/workspace/__tests__/render-harness.svelte.d.ts +32 -0
- package/dist/components/workspace/__tests__/render-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/render-tools-dock.test.js +243 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte +58 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts +16 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte +41 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts +13 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/test-icon.svelte +17 -0
- package/dist/components/workspace/__tests__/test-icon.svelte.d.ts +19 -0
- package/dist/components/workspace/__tests__/test-icon.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte +38 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts +22 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts +65 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.js +115 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts +15 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.js +7 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture.test.js +115 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte +9 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts +19 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/use-harness.svelte +23 -0
- package/dist/components/workspace/__tests__/use-harness.svelte.d.ts +8 -0
- package/dist/components/workspace/__tests__/use-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/use-tools-dock.test.js +33 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte +43 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts +11 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/breadcrumbs-helpers.d.ts +44 -0
- package/dist/components/workspace/breadcrumbs-helpers.d.ts.map +1 -0
- package/dist/components/workspace/breadcrumbs-helpers.js +88 -0
- package/dist/components/workspace/index.d.ts +16 -0
- package/dist/components/workspace/index.d.ts.map +1 -0
- package/dist/components/workspace/index.js +14 -0
- package/dist/components/workspace/manifest-nav.d.ts +200 -0
- package/dist/components/workspace/manifest-nav.d.ts.map +1 -0
- package/dist/components/workspace/manifest-nav.js +408 -0
- package/dist/components/workspace/nav-helpers.d.ts +36 -0
- package/dist/components/workspace/nav-helpers.d.ts.map +1 -0
- package/dist/components/workspace/nav-helpers.js +60 -0
- package/dist/components/workspace/server/__tests__/compose-availability.test.js +383 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts +78 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts.map +1 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.js +104 -0
- package/dist/components/workspace/server/compose-availability.d.ts +73 -0
- package/dist/components/workspace/server/compose-availability.d.ts.map +1 -0
- package/dist/components/workspace/server/compose-availability.js +114 -0
- package/dist/components/workspace/server/index.d.ts +13 -0
- package/dist/components/workspace/server/index.d.ts.map +1 -0
- package/dist/components/workspace/server/index.js +11 -0
- package/dist/components/workspace/server/types.d.ts +108 -0
- package/dist/components/workspace/server/types.d.ts.map +1 -0
- package/dist/components/workspace/server/types.js +11 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte +565 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts +14 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts +143 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.js +487 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.d.ts +41 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.js +50 -0
- package/dist/components/workspace/types.d.ts +372 -0
- package/dist/components/workspace/types.d.ts.map +1 -0
- package/dist/components/workspace/types.js +6 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +10 -0
- package/dist/hooks/useAppState.svelte.d.ts +46 -0
- package/dist/hooks/useAppState.svelte.d.ts.map +1 -0
- package/dist/hooks/useAppState.svelte.js +59 -0
- package/dist/hooks/useAuth.svelte.d.ts +41 -0
- package/dist/hooks/useAuth.svelte.d.ts.map +1 -0
- package/dist/hooks/useAuth.svelte.js +43 -0
- package/dist/hooks/useLLM.svelte.d.ts +69 -0
- package/dist/hooks/useLLM.svelte.d.ts.map +1 -0
- package/dist/hooks/useLLM.svelte.js +85 -0
- package/dist/hooks/useSTT.svelte.d.ts +68 -0
- package/dist/hooks/useSTT.svelte.d.ts.map +1 -0
- package/dist/hooks/useSTT.svelte.js +97 -0
- package/dist/hooks/useSocket.svelte.d.ts +45 -0
- package/dist/hooks/useSocket.svelte.d.ts.map +1 -0
- package/dist/hooks/useSocket.svelte.js +54 -0
- package/dist/hooks/useTTS.svelte.d.ts +65 -0
- package/dist/hooks/useTTS.svelte.d.ts.map +1 -0
- package/dist/hooks/useTTS.svelte.js +93 -0
- package/dist/hooks/useTheme.d.ts +13 -0
- package/dist/hooks/useTheme.d.ts.map +1 -0
- package/dist/hooks/useTheme.js +16 -0
- package/dist/i18n/__tests__/server.spec.js +50 -0
- package/dist/i18n/server.d.ts +47 -0
- package/dist/i18n/server.d.ts.map +1 -0
- package/dist/i18n/server.js +58 -0
- package/dist/i18n/strings.forms.d.ts +33 -0
- package/dist/i18n/strings.forms.d.ts.map +1 -0
- package/dist/i18n/strings.forms.js +54 -0
- package/dist/i18n/strings.workspace.d.ts +34 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -0
- package/dist/i18n/strings.workspace.js +40 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/state/__tests__/warm-clients.test.js +40 -0
- package/dist/state/app-state.d.ts +308 -0
- package/dist/state/app-state.d.ts.map +1 -0
- package/dist/state/app-state.js +64 -0
- package/dist/state/app-state.svelte.d.ts +196 -0
- package/dist/state/app-state.svelte.d.ts.map +1 -0
- package/dist/state/app-state.svelte.js +774 -0
- package/dist/state/context.d.ts +23 -0
- package/dist/state/context.d.ts.map +1 -0
- package/dist/state/context.js +32 -0
- package/dist/state/form-context.d.ts +59 -0
- package/dist/state/form-context.d.ts.map +1 -0
- package/dist/state/form-context.js +31 -0
- package/dist/state/form-group-context.d.ts +13 -0
- package/dist/state/form-group-context.d.ts.map +1 -0
- package/dist/state/form-group-context.js +28 -0
- package/dist/state/index.d.ts +9 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +8 -0
- package/dist/state/warm-clients.d.ts +136 -0
- package/dist/state/warm-clients.d.ts.map +1 -0
- package/dist/state/warm-clients.js +231 -0
- package/package.json +137 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep behavior tests for SelectInput (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* The existing a11y suite only asserts labelling + describedby + axe. This
|
|
5
|
+
* suite drives the real selection pipeline: choosing an option → handleChange →
|
|
6
|
+
* updateValue → onchange + bindable value; the placeholder option (disabled +
|
|
7
|
+
* selected when empty); option rendering from the options prop; and the
|
|
8
|
+
* required/disabled wiring.
|
|
9
|
+
*
|
|
10
|
+
* SelectInput calls useAppState (it throws outside a <Provider>), so it is
|
|
11
|
+
* mocked to a non-listening stub — the reference pattern from
|
|
12
|
+
* rich-inputs-a11y.test.ts. (SelectInput does not use useSTT.)
|
|
13
|
+
*/
|
|
14
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
15
|
+
import { render, screen } from '@testing-library/svelte';
|
|
16
|
+
import userEvent from '@testing-library/user-event';
|
|
17
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
18
|
+
vi.mock('../../../hooks/useAppState.svelte.js', () => ({
|
|
19
|
+
useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
|
|
20
|
+
}));
|
|
21
|
+
import SelectInput from '../SelectInput.svelte';
|
|
22
|
+
const COUNTRIES = [
|
|
23
|
+
{ value: 'us', label: 'United States' },
|
|
24
|
+
{ value: 'ca', label: 'Canada' },
|
|
25
|
+
{ value: 'mx', label: 'Mexico' },
|
|
26
|
+
];
|
|
27
|
+
describe('SelectInput — selection', () => {
|
|
28
|
+
it('fires onchange with the chosen option value', async () => {
|
|
29
|
+
const onchange = vi.fn();
|
|
30
|
+
render(SelectInput, {
|
|
31
|
+
props: {
|
|
32
|
+
name: 'country',
|
|
33
|
+
label: 'Country',
|
|
34
|
+
options: COUNTRIES,
|
|
35
|
+
onchange,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
await userEvent.selectOptions(screen.getByLabelText('Country'), 'ca');
|
|
39
|
+
expect(onchange).toHaveBeenCalledWith('ca');
|
|
40
|
+
});
|
|
41
|
+
it('reflects the selected value on the <select>', async () => {
|
|
42
|
+
render(SelectInput, {
|
|
43
|
+
props: { name: 'country', label: 'Country', options: COUNTRIES },
|
|
44
|
+
});
|
|
45
|
+
const select = screen.getByLabelText('Country');
|
|
46
|
+
await userEvent.selectOptions(select, 'mx');
|
|
47
|
+
expect(select).toHaveValue('mx');
|
|
48
|
+
});
|
|
49
|
+
it('renders the initial value prop as the selected option', () => {
|
|
50
|
+
render(SelectInput, {
|
|
51
|
+
props: {
|
|
52
|
+
name: 'country',
|
|
53
|
+
label: 'Country',
|
|
54
|
+
options: COUNTRIES,
|
|
55
|
+
value: 'us',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
expect(screen.getByLabelText('Country')).toHaveValue('us');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('SelectInput — options & placeholder', () => {
|
|
62
|
+
it('renders one <option> per provided option plus the placeholder', () => {
|
|
63
|
+
render(SelectInput, {
|
|
64
|
+
props: {
|
|
65
|
+
name: 'country',
|
|
66
|
+
label: 'Country',
|
|
67
|
+
options: COUNTRIES,
|
|
68
|
+
placeholder: 'Choose a country…',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
// 3 countries + 1 placeholder option.
|
|
72
|
+
expect(screen.getAllByRole('option')).toHaveLength(4);
|
|
73
|
+
expect(screen.getByRole('option', { name: 'United States' })).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
it('renders the placeholder as a disabled option, selected when value is empty', () => {
|
|
76
|
+
render(SelectInput, {
|
|
77
|
+
props: {
|
|
78
|
+
name: 'country',
|
|
79
|
+
label: 'Country',
|
|
80
|
+
options: COUNTRIES,
|
|
81
|
+
placeholder: 'Choose a country…',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
const placeholder = screen.getByRole('option', {
|
|
85
|
+
name: 'Choose a country…',
|
|
86
|
+
});
|
|
87
|
+
expect(placeholder).toBeDisabled();
|
|
88
|
+
expect(placeholder.value).toBe('');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('SelectInput — states & attributes', () => {
|
|
92
|
+
it('reflects required onto the select', () => {
|
|
93
|
+
// NOTE: with required, the "*" lives inside the <label>, so the select's
|
|
94
|
+
// accessible name becomes "Country *" — query by id rather than label.
|
|
95
|
+
const { container } = render(SelectInput, {
|
|
96
|
+
props: {
|
|
97
|
+
name: 'country',
|
|
98
|
+
label: 'Country',
|
|
99
|
+
options: COUNTRIES,
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
expect(container.querySelector('#country')).toBeRequired();
|
|
104
|
+
});
|
|
105
|
+
it('disables the select and ignores selection when disabled', async () => {
|
|
106
|
+
const onchange = vi.fn();
|
|
107
|
+
render(SelectInput, {
|
|
108
|
+
props: {
|
|
109
|
+
name: 'country',
|
|
110
|
+
label: 'Country',
|
|
111
|
+
options: COUNTRIES,
|
|
112
|
+
disabled: true,
|
|
113
|
+
onchange,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const select = screen.getByLabelText('Country');
|
|
117
|
+
expect(select).toBeDisabled();
|
|
118
|
+
await userEvent.selectOptions(select, 'ca').catch(() => { });
|
|
119
|
+
expect(onchange).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
it('is axe-clean when labelled with a placeholder', async () => {
|
|
122
|
+
const { container } = render(SelectInput, {
|
|
123
|
+
props: {
|
|
124
|
+
name: 'country',
|
|
125
|
+
label: 'Country',
|
|
126
|
+
placeholder: 'Choose…',
|
|
127
|
+
options: COUNTRIES,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
await expectNoA11yViolations(container);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep behavior tests for TextInput (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* The existing a11y suite only asserts labelling + describedby + axe. This
|
|
5
|
+
* suite exercises the real value pipeline: typing → handleInput → updateValue →
|
|
6
|
+
* onchange + bindable value, the email-validation derived state (aria-invalid,
|
|
7
|
+
* the invalid-email supporting message), disabled/required wiring, and the
|
|
8
|
+
* placeholder-on-focus behavior.
|
|
9
|
+
*
|
|
10
|
+
* TextInput calls useAppState/useSTT (they throw outside a <Provider>), so both
|
|
11
|
+
* are mocked to non-listening stubs — the reference pattern from
|
|
12
|
+
* rich-inputs-a11y.test.ts.
|
|
13
|
+
*/
|
|
14
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
15
|
+
import { render, screen } from '@testing-library/svelte';
|
|
16
|
+
import userEvent from '@testing-library/user-event';
|
|
17
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
18
|
+
vi.mock('../../../hooks/useAppState.svelte.js', () => ({
|
|
19
|
+
useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
|
|
20
|
+
}));
|
|
21
|
+
vi.mock('../../../hooks/useSTT.svelte.js', () => ({
|
|
22
|
+
useSTT: () => ({
|
|
23
|
+
isListening: false,
|
|
24
|
+
isInitializing: false,
|
|
25
|
+
isReady: false,
|
|
26
|
+
adapterType: null,
|
|
27
|
+
downloadProgress: 0,
|
|
28
|
+
lastResult: '',
|
|
29
|
+
initialize: vi.fn(),
|
|
30
|
+
start: vi.fn(),
|
|
31
|
+
stop: vi.fn(),
|
|
32
|
+
}),
|
|
33
|
+
}));
|
|
34
|
+
import TextInput from '../TextInput.svelte';
|
|
35
|
+
describe('TextInput — typing & value', () => {
|
|
36
|
+
it('fires onchange with the current value on each keystroke', async () => {
|
|
37
|
+
const onchange = vi.fn();
|
|
38
|
+
render(TextInput, {
|
|
39
|
+
props: { name: 'fullname', label: 'Full name', onchange },
|
|
40
|
+
});
|
|
41
|
+
await userEvent.type(screen.getByLabelText('Full name'), 'Ada');
|
|
42
|
+
expect(onchange).toHaveBeenLastCalledWith('Ada');
|
|
43
|
+
});
|
|
44
|
+
it('renders the initial value prop into the input', () => {
|
|
45
|
+
render(TextInput, {
|
|
46
|
+
props: { name: 'fullname', label: 'Full name', value: 'Seed' },
|
|
47
|
+
});
|
|
48
|
+
expect(screen.getByLabelText('Full name')).toHaveValue('Seed');
|
|
49
|
+
});
|
|
50
|
+
it('updates the input value as the user types', async () => {
|
|
51
|
+
render(TextInput, { props: { name: 'fullname', label: 'Full name' } });
|
|
52
|
+
const input = screen.getByLabelText('Full name');
|
|
53
|
+
await userEvent.type(input, 'Lovelace');
|
|
54
|
+
expect(input).toHaveValue('Lovelace');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('TextInput — email validation', () => {
|
|
58
|
+
it('marks aria-invalid when an email value is malformed', () => {
|
|
59
|
+
render(TextInput, {
|
|
60
|
+
props: {
|
|
61
|
+
name: 'email',
|
|
62
|
+
label: 'Email',
|
|
63
|
+
type: 'email',
|
|
64
|
+
value: 'not-an-email',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
expect(screen.getByLabelText('Email')).toHaveAttribute('aria-invalid', 'true');
|
|
68
|
+
});
|
|
69
|
+
it('shows the invalid-email supporting message for a bad address', () => {
|
|
70
|
+
render(TextInput, {
|
|
71
|
+
props: { name: 'email', label: 'Email', type: 'email', value: 'nope' },
|
|
72
|
+
});
|
|
73
|
+
expect(screen.getByText('Invalid email address')).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
it('is valid (no aria-invalid) for a well-formed email', () => {
|
|
76
|
+
render(TextInput, {
|
|
77
|
+
props: {
|
|
78
|
+
name: 'email',
|
|
79
|
+
label: 'Email',
|
|
80
|
+
type: 'email',
|
|
81
|
+
value: 'ada@example.com',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
expect(screen.getByLabelText('Email')).not.toHaveAttribute('aria-invalid', 'true');
|
|
85
|
+
});
|
|
86
|
+
it('treats an empty email as valid (no error until the user types)', () => {
|
|
87
|
+
render(TextInput, {
|
|
88
|
+
props: { name: 'email', label: 'Email', type: 'email' },
|
|
89
|
+
});
|
|
90
|
+
expect(screen.getByLabelText('Email')).not.toHaveAttribute('aria-invalid', 'true');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('TextInput — states & attributes', () => {
|
|
94
|
+
it('reflects required onto the input', () => {
|
|
95
|
+
// NOTE: with required, the "*" lives inside the <label>, so the input's
|
|
96
|
+
// accessible name becomes "Full name *" — query by id rather than label.
|
|
97
|
+
const { container } = render(TextInput, {
|
|
98
|
+
props: { name: 'fullname', label: 'Full name', required: true },
|
|
99
|
+
});
|
|
100
|
+
expect(container.querySelector('#fullname')).toBeRequired();
|
|
101
|
+
});
|
|
102
|
+
it('disables the input and ignores typing when disabled', async () => {
|
|
103
|
+
const onchange = vi.fn();
|
|
104
|
+
render(TextInput, {
|
|
105
|
+
props: { name: 'fullname', label: 'Full name', disabled: true, onchange },
|
|
106
|
+
});
|
|
107
|
+
const input = screen.getByLabelText('Full name');
|
|
108
|
+
expect(input).toBeDisabled();
|
|
109
|
+
await userEvent.type(input, 'ignored');
|
|
110
|
+
expect(onchange).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
it('reveals the placeholder only while focused', async () => {
|
|
113
|
+
render(TextInput, {
|
|
114
|
+
props: {
|
|
115
|
+
name: 'fullname',
|
|
116
|
+
label: 'Full name',
|
|
117
|
+
placeholder: 'Enter name',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
const input = screen.getByLabelText('Full name');
|
|
121
|
+
expect(input).toHaveAttribute('placeholder', '');
|
|
122
|
+
await userEvent.click(input);
|
|
123
|
+
expect(input).toHaveAttribute('placeholder', 'Enter name');
|
|
124
|
+
});
|
|
125
|
+
it('is axe-clean when labelled', async () => {
|
|
126
|
+
const { container } = render(TextInput, {
|
|
127
|
+
props: { name: 'fullname', label: 'Full name' },
|
|
128
|
+
});
|
|
129
|
+
await expectNoA11yViolations(container);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden test for Textarea (Sweep L1, #1420).
|
|
3
|
+
*
|
|
4
|
+
* Textarea forwards aria attributes and inherits id / describedby / invalid from
|
|
5
|
+
* a wrapping FormGroup via context.
|
|
6
|
+
*/
|
|
7
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
8
|
+
import { render, screen } from '@testing-library/svelte';
|
|
9
|
+
import userEvent from '@testing-library/user-event';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import Textarea from '../Textarea.svelte';
|
|
12
|
+
describe('Textarea', () => {
|
|
13
|
+
it('forwards aria-label / aria-describedby / aria-invalid', () => {
|
|
14
|
+
render(Textarea, {
|
|
15
|
+
props: {
|
|
16
|
+
'aria-label': 'Notes',
|
|
17
|
+
'aria-describedby': 'notes-hint',
|
|
18
|
+
'aria-invalid': 'true',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const textarea = screen.getByRole('textbox', { name: 'Notes' });
|
|
22
|
+
expect(textarea).toHaveAttribute('aria-describedby', 'notes-hint');
|
|
23
|
+
expect(textarea).toHaveAttribute('aria-invalid', 'true');
|
|
24
|
+
});
|
|
25
|
+
it('accepts typed input', async () => {
|
|
26
|
+
render(Textarea, { props: { 'aria-label': 'Notes' } });
|
|
27
|
+
const textarea = screen.getByRole('textbox', {
|
|
28
|
+
name: 'Notes',
|
|
29
|
+
});
|
|
30
|
+
await userEvent.type(textarea, 'hello');
|
|
31
|
+
expect(textarea).toHaveValue('hello');
|
|
32
|
+
});
|
|
33
|
+
it('is axe-clean when labelled', async () => {
|
|
34
|
+
const { container } = render(Textarea, {
|
|
35
|
+
props: { 'aria-label': 'Notes' },
|
|
36
|
+
});
|
|
37
|
+
await expectNoA11yViolations(container);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep behavior tests for TextareaInput (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* The existing a11y suite only asserts labelling + describedby + axe. This
|
|
5
|
+
* suite drives the real value pipeline: typing → handleInput → updateValue →
|
|
6
|
+
* onchange + bindable value, plus the rows/required/disabled wiring and the
|
|
7
|
+
* focus-gated placeholder.
|
|
8
|
+
*
|
|
9
|
+
* TextareaInput calls useAppState/useSTT (they throw outside a <Provider>), so
|
|
10
|
+
* both are mocked to non-listening stubs — the reference pattern from
|
|
11
|
+
* rich-inputs-a11y.test.ts.
|
|
12
|
+
*/
|
|
13
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
14
|
+
import { render, screen } from '@testing-library/svelte';
|
|
15
|
+
import userEvent from '@testing-library/user-event';
|
|
16
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
17
|
+
vi.mock('../../../hooks/useAppState.svelte.js', () => ({
|
|
18
|
+
useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../../../hooks/useSTT.svelte.js', () => ({
|
|
21
|
+
useSTT: () => ({
|
|
22
|
+
isListening: false,
|
|
23
|
+
isInitializing: false,
|
|
24
|
+
isReady: false,
|
|
25
|
+
adapterType: null,
|
|
26
|
+
downloadProgress: 0,
|
|
27
|
+
lastResult: '',
|
|
28
|
+
initialize: vi.fn(),
|
|
29
|
+
start: vi.fn(),
|
|
30
|
+
stop: vi.fn(),
|
|
31
|
+
}),
|
|
32
|
+
}));
|
|
33
|
+
import TextareaInput from '../TextareaInput.svelte';
|
|
34
|
+
describe('TextareaInput — typing & value', () => {
|
|
35
|
+
it('fires onchange with the current value as the user types', async () => {
|
|
36
|
+
const onchange = vi.fn();
|
|
37
|
+
render(TextareaInput, { props: { name: 'bio', label: 'Bio', onchange } });
|
|
38
|
+
await userEvent.type(screen.getByLabelText('Bio'), 'Hello');
|
|
39
|
+
expect(onchange).toHaveBeenLastCalledWith('Hello');
|
|
40
|
+
});
|
|
41
|
+
it('renders the initial value prop', () => {
|
|
42
|
+
render(TextareaInput, {
|
|
43
|
+
props: { name: 'bio', label: 'Bio', value: 'Seeded text' },
|
|
44
|
+
});
|
|
45
|
+
expect(screen.getByLabelText('Bio')).toHaveValue('Seeded text');
|
|
46
|
+
});
|
|
47
|
+
it('captures multi-line input', async () => {
|
|
48
|
+
render(TextareaInput, { props: { name: 'bio', label: 'Bio' } });
|
|
49
|
+
const textarea = screen.getByLabelText('Bio');
|
|
50
|
+
await userEvent.type(textarea, 'line one{enter}line two');
|
|
51
|
+
expect(textarea).toHaveValue('line one\nline two');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('TextareaInput — states & attributes', () => {
|
|
55
|
+
it('forwards the rows prop onto the textarea', () => {
|
|
56
|
+
render(TextareaInput, { props: { name: 'bio', label: 'Bio', rows: 8 } });
|
|
57
|
+
expect(screen.getByLabelText('Bio')).toHaveAttribute('rows', '8');
|
|
58
|
+
});
|
|
59
|
+
it('defaults to 4 rows when not specified', () => {
|
|
60
|
+
render(TextareaInput, { props: { name: 'bio', label: 'Bio' } });
|
|
61
|
+
expect(screen.getByLabelText('Bio')).toHaveAttribute('rows', '4');
|
|
62
|
+
});
|
|
63
|
+
it('reflects required onto the textarea', () => {
|
|
64
|
+
// NOTE: with required, the "*" lives inside the <label>, so the textarea's
|
|
65
|
+
// accessible name becomes "Bio *" — query by id rather than label.
|
|
66
|
+
const { container } = render(TextareaInput, {
|
|
67
|
+
props: { name: 'bio', label: 'Bio', required: true },
|
|
68
|
+
});
|
|
69
|
+
expect(container.querySelector('#bio')).toBeRequired();
|
|
70
|
+
});
|
|
71
|
+
it('disables the textarea and ignores typing when disabled', async () => {
|
|
72
|
+
const onchange = vi.fn();
|
|
73
|
+
render(TextareaInput, {
|
|
74
|
+
props: { name: 'bio', label: 'Bio', disabled: true, onchange },
|
|
75
|
+
});
|
|
76
|
+
const textarea = screen.getByLabelText('Bio');
|
|
77
|
+
expect(textarea).toBeDisabled();
|
|
78
|
+
await userEvent.type(textarea, 'ignored');
|
|
79
|
+
expect(onchange).not.toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
it('reveals the placeholder only while focused', async () => {
|
|
82
|
+
render(TextareaInput, {
|
|
83
|
+
props: { name: 'bio', label: 'Bio', placeholder: 'Tell us about you' },
|
|
84
|
+
});
|
|
85
|
+
const textarea = screen.getByLabelText('Bio');
|
|
86
|
+
expect(textarea).toHaveAttribute('placeholder', '');
|
|
87
|
+
await userEvent.click(textarea);
|
|
88
|
+
expect(textarea).toHaveAttribute('placeholder', 'Tell us about you');
|
|
89
|
+
});
|
|
90
|
+
it('is axe-clean when labelled', async () => {
|
|
91
|
+
const { container } = render(TextareaInput, {
|
|
92
|
+
props: { name: 'bio', label: 'Bio' },
|
|
93
|
+
});
|
|
94
|
+
await expectNoA11yViolations(container);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component test for Toggle (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* Follows the golden pattern (src/components/ui/__tests__/Button.test.ts):
|
|
5
|
+
* render → assert role/name/state → drive with user-event → prove axe-clean.
|
|
6
|
+
* Toggle renders native checkbox semantics, so it surfaces as a `checkbox` role.
|
|
7
|
+
*/
|
|
8
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
9
|
+
import { render, screen } from '@testing-library/svelte';
|
|
10
|
+
import userEvent from '@testing-library/user-event';
|
|
11
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
+
import Toggle from '../Toggle.svelte';
|
|
13
|
+
describe('Toggle', () => {
|
|
14
|
+
it('renders a checkbox with its label as the accessible name', () => {
|
|
15
|
+
render(Toggle, { props: { label: 'Email notifications' } });
|
|
16
|
+
const toggle = screen.getByRole('checkbox', {
|
|
17
|
+
name: 'Email notifications',
|
|
18
|
+
});
|
|
19
|
+
expect(toggle).toBeInTheDocument();
|
|
20
|
+
expect(toggle).not.toBeChecked();
|
|
21
|
+
});
|
|
22
|
+
it('prefers ariaLabel over label for the accessible name', () => {
|
|
23
|
+
render(Toggle, { props: { label: 'Visible', ariaLabel: 'Wifi' } });
|
|
24
|
+
expect(screen.getByRole('checkbox', { name: 'Wifi' })).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
it('reflects the checked prop', () => {
|
|
27
|
+
render(Toggle, { props: { ariaLabel: 'Active', checked: true } });
|
|
28
|
+
expect(screen.getByRole('checkbox', { name: 'Active' })).toBeChecked();
|
|
29
|
+
});
|
|
30
|
+
it('reflects the disabled prop', () => {
|
|
31
|
+
render(Toggle, { props: { ariaLabel: 'Active', disabled: true } });
|
|
32
|
+
expect(screen.getByRole('checkbox', { name: 'Active' })).toBeDisabled();
|
|
33
|
+
});
|
|
34
|
+
it('forwards name, value, and id to the input for form submission', () => {
|
|
35
|
+
render(Toggle, {
|
|
36
|
+
props: { ariaLabel: 'Sub', name: 'sub', value: 'on', id: 'sub-toggle' },
|
|
37
|
+
});
|
|
38
|
+
const toggle = screen.getByRole('checkbox', {
|
|
39
|
+
name: 'Sub',
|
|
40
|
+
});
|
|
41
|
+
expect(toggle).toHaveAttribute('name', 'sub');
|
|
42
|
+
expect(toggle).toHaveAttribute('id', 'sub-toggle');
|
|
43
|
+
// Svelte binds `value` as the DOM property on checkboxes (not a reflected attribute).
|
|
44
|
+
expect(toggle.value).toBe('on');
|
|
45
|
+
});
|
|
46
|
+
it('toggles checked and fires onchange when clicked', async () => {
|
|
47
|
+
const onchange = vi.fn();
|
|
48
|
+
render(Toggle, { props: { ariaLabel: 'Active', onchange } });
|
|
49
|
+
const toggle = screen.getByRole('checkbox', { name: 'Active' });
|
|
50
|
+
await userEvent.click(toggle);
|
|
51
|
+
expect(toggle).toBeChecked();
|
|
52
|
+
expect(onchange).toHaveBeenCalledWith(true);
|
|
53
|
+
await userEvent.click(toggle);
|
|
54
|
+
expect(toggle).not.toBeChecked();
|
|
55
|
+
expect(onchange).toHaveBeenLastCalledWith(false);
|
|
56
|
+
});
|
|
57
|
+
it('is keyboard-activatable with Space when focused', async () => {
|
|
58
|
+
const onchange = vi.fn();
|
|
59
|
+
render(Toggle, { props: { ariaLabel: 'Active', onchange } });
|
|
60
|
+
const toggle = screen.getByRole('checkbox', { name: 'Active' });
|
|
61
|
+
toggle.focus();
|
|
62
|
+
expect(toggle).toHaveFocus();
|
|
63
|
+
await userEvent.keyboard(' ');
|
|
64
|
+
expect(toggle).toBeChecked();
|
|
65
|
+
expect(onchange).toHaveBeenCalledWith(true);
|
|
66
|
+
});
|
|
67
|
+
it('does not fire onchange when disabled', async () => {
|
|
68
|
+
const onchange = vi.fn();
|
|
69
|
+
render(Toggle, {
|
|
70
|
+
props: { ariaLabel: 'Active', disabled: true, onchange },
|
|
71
|
+
});
|
|
72
|
+
await userEvent.click(screen.getByRole('checkbox', { name: 'Active' }));
|
|
73
|
+
expect(onchange).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
it('is axe-clean with a label', async () => {
|
|
76
|
+
const { container } = render(Toggle, {
|
|
77
|
+
props: { label: 'Accessible toggle' },
|
|
78
|
+
});
|
|
79
|
+
await expectNoA11yViolations(container);
|
|
80
|
+
});
|
|
81
|
+
it('is axe-clean in the checked + disabled state', async () => {
|
|
82
|
+
const { container } = render(Toggle, {
|
|
83
|
+
props: { label: 'Locked on', checked: true, disabled: true },
|
|
84
|
+
});
|
|
85
|
+
await expectNoA11yViolations(container);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility golden tests for the multi-field composite form inputs
|
|
3
|
+
* (Sweep L1, #1420). Each sub-field is programmatically labelled and the
|
|
4
|
+
* shared/validation error is associated via aria-describedby + role="alert".
|
|
5
|
+
* Hooks are mocked (Provider-backed, throw otherwise).
|
|
6
|
+
*/
|
|
7
|
+
import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
|
|
8
|
+
import { render, screen } from '@testing-library/svelte';
|
|
9
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
10
|
+
vi.mock('../../../hooks/useAppState.svelte.js', () => ({
|
|
11
|
+
useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
|
|
12
|
+
}));
|
|
13
|
+
vi.mock('../../../hooks/useSTT.svelte.js', () => ({
|
|
14
|
+
useSTT: () => ({
|
|
15
|
+
isListening: false,
|
|
16
|
+
isInitializing: false,
|
|
17
|
+
isReady: false,
|
|
18
|
+
adapterType: null,
|
|
19
|
+
downloadProgress: 0,
|
|
20
|
+
lastResult: '',
|
|
21
|
+
initialize: vi.fn(),
|
|
22
|
+
start: vi.fn(),
|
|
23
|
+
stop: vi.fn(),
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
import AddressInput from '../AddressInput.svelte';
|
|
27
|
+
import DateRangeInput from '../DateRangeInput.svelte';
|
|
28
|
+
import DateTimeInput from '../DateTimeInput.svelte';
|
|
29
|
+
import MeasurementInput from '../MeasurementInput.svelte';
|
|
30
|
+
import MoneyInput from '../MoneyInput.svelte';
|
|
31
|
+
describe('composite form inputs — accessibility', () => {
|
|
32
|
+
it('MoneyInput: labelled amount + axe-clean', async () => {
|
|
33
|
+
const { container } = render(MoneyInput, {
|
|
34
|
+
props: { name: 'price', label: 'Price' },
|
|
35
|
+
});
|
|
36
|
+
expect(screen.getByLabelText('Price')).toBeInTheDocument();
|
|
37
|
+
await expectNoA11yViolations(container);
|
|
38
|
+
});
|
|
39
|
+
it('MeasurementInput: labelled value + labelled unit select + axe-clean', async () => {
|
|
40
|
+
const { container } = render(MeasurementInput, {
|
|
41
|
+
props: { name: 'weight', label: 'Weight' },
|
|
42
|
+
});
|
|
43
|
+
expect(screen.getByLabelText('Weight')).toBeInTheDocument();
|
|
44
|
+
expect(screen.getByLabelText('Weight unit')).toBeInTheDocument();
|
|
45
|
+
await expectNoA11yViolations(container);
|
|
46
|
+
});
|
|
47
|
+
it('DateRangeInput: labelled start/end + axe-clean', async () => {
|
|
48
|
+
const { container } = render(DateRangeInput, {
|
|
49
|
+
props: { name: 'range', label: 'Dates' },
|
|
50
|
+
});
|
|
51
|
+
expect(screen.getByLabelText('Start')).toBeInTheDocument();
|
|
52
|
+
expect(screen.getByLabelText('End')).toBeInTheDocument();
|
|
53
|
+
await expectNoA11yViolations(container);
|
|
54
|
+
});
|
|
55
|
+
it('AddressInput: all fields labelled + axe-clean', async () => {
|
|
56
|
+
const { container } = render(AddressInput, {
|
|
57
|
+
props: { name: 'addr', label: 'Address' },
|
|
58
|
+
});
|
|
59
|
+
expect(screen.getAllByRole('textbox').length).toBeGreaterThan(0);
|
|
60
|
+
await expectNoA11yViolations(container);
|
|
61
|
+
});
|
|
62
|
+
it('DateTimeInput: labelled + axe-clean', async () => {
|
|
63
|
+
const { container } = render(DateTimeInput, {
|
|
64
|
+
props: { name: 'when', label: 'When' },
|
|
65
|
+
});
|
|
66
|
+
expect(screen.getByLabelText('When')).toBeInTheDocument();
|
|
67
|
+
await expectNoA11yViolations(container);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Test fixture (L1 #1420): an Input wrapped in a FormGroup, to exercise the
|
|
3
|
+
* FormGroup → base-input accessibility auto-wiring. */
|
|
4
|
+
import FormGroup from '../FormGroup.svelte';
|
|
5
|
+
import Input from '../Input.svelte';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
error = undefined,
|
|
9
|
+
hint = undefined,
|
|
10
|
+
required = false,
|
|
11
|
+
}: { error?: string; hint?: string; required?: boolean } = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<FormGroup label="Email" {error} {hint} {required}>
|
|
15
|
+
<Input type="email" name="email" />
|
|
16
|
+
</FormGroup>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
error?: string;
|
|
3
|
+
hint?: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const FormGroupInput: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type FormGroupInput = ReturnType<typeof FormGroupInput>;
|
|
8
|
+
export default FormGroupInput;
|
|
9
|
+
//# sourceMappingURL=form-group-input.fixture.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-group-input.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/form-group-input.fixture.svelte.ts"],"names":[],"mappings":"AAQC,KAAK,gBAAgB,GAAI;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAmBhF,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** Test fixture (S11 #1416): a Form wrapping a TextInput + NumberInput so the
|
|
3
|
+
* Form's field-registration → value-collection → onsubmit contract can be
|
|
4
|
+
* exercised end-to-end (the submit handler reads each registered field's
|
|
5
|
+
* getValue()). */
|
|
6
|
+
import Form from '../Form.svelte';
|
|
7
|
+
import NumberInput from '../NumberInput.svelte';
|
|
8
|
+
import TextInput from '../TextInput.svelte';
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
onsubmit = undefined,
|
|
12
|
+
method = undefined,
|
|
13
|
+
action = undefined,
|
|
14
|
+
textValue = '',
|
|
15
|
+
numberValue = null,
|
|
16
|
+
showAge = true,
|
|
17
|
+
}: {
|
|
18
|
+
onsubmit?: (data: Record<string, unknown>) => void;
|
|
19
|
+
method?: 'GET' | 'POST';
|
|
20
|
+
action?: string;
|
|
21
|
+
textValue?: string;
|
|
22
|
+
numberValue?: number | null;
|
|
23
|
+
showAge?: boolean;
|
|
24
|
+
} = $props();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<Form {onsubmit} {method} {action}>
|
|
28
|
+
<TextInput name="fullname" label="Full name" bind:value={textValue} />
|
|
29
|
+
{#if showAge}
|
|
30
|
+
<NumberInput name="age" label="Age" bind:value={numberValue} />
|
|
31
|
+
{/if}
|
|
32
|
+
<button type="submit">Submit</button>
|
|
33
|
+
</Form>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
onsubmit?: (data: Record<string, unknown>) => void;
|
|
3
|
+
method?: 'GET' | 'POST';
|
|
4
|
+
action?: string;
|
|
5
|
+
textValue?: string;
|
|
6
|
+
numberValue?: number | null;
|
|
7
|
+
showAge?: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare const FormWithFields: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type FormWithFields = ReturnType<typeof FormWithFields>;
|
|
11
|
+
export default FormWithFields;
|
|
12
|
+
//# sourceMappingURL=form-with-fields.fixture.svelte.d.ts.map
|
|
@@ -0,0 +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;CACnB,CAAC;AA2BF,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|