@happyvertical/smrt-ui 0.51.39 → 0.53.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 +25 -3
- package/README.md +558 -8
- package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
- package/dist/actions/swipe-dismiss.d.ts +42 -0
- package/dist/actions/swipe-dismiss.d.ts.map +1 -0
- package/dist/actions/swipe-dismiss.js +69 -0
- package/dist/components/calendar/Calendar.svelte +5 -0
- package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
- package/dist/components/calendar/CalendarView.svelte +1250 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
- package/dist/components/calendar/DayView.svelte +5 -0
- package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
- package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
- package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
- package/dist/components/calendar/calendar-model.d.ts +178 -0
- package/dist/components/calendar/calendar-model.d.ts.map +1 -0
- package/dist/components/calendar/calendar-model.js +362 -0
- package/dist/components/calendar/index.d.ts +12 -1
- package/dist/components/calendar/index.d.ts.map +1 -1
- package/dist/components/calendar/index.js +11 -1
- package/dist/components/data/CollectionList.svelte +23 -5
- package/dist/components/data/CollectionList.svelte.d.ts +8 -2
- package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTable.svelte +178 -55
- package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTableController.d.ts +4 -0
- package/dist/components/data/DataTableController.d.ts.map +1 -1
- package/dist/components/data/DataTableController.js +11 -4
- package/dist/components/data/DataTableResponsive.d.ts +4 -0
- package/dist/components/data/DataTableResponsive.d.ts.map +1 -0
- package/dist/components/data/DataTableResponsive.js +22 -0
- package/dist/components/data/ListSortSelect.svelte +121 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
- package/dist/components/data/SortableHeader.svelte +169 -0
- package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
- package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/CollectionList.test.js +37 -0
- package/dist/components/data/__tests__/DataTable.test.js +76 -0
- package/dist/components/data/__tests__/DataTableController.test.js +35 -0
- package/dist/components/data/__tests__/DataTableResponsive.test.js +80 -0
- package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
- package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
- package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
- package/dist/components/data/__tests__/list-sort.test.js +91 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte +29 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +7 -0
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
- package/dist/components/data/data-surface.d.ts +1 -0
- package/dist/components/data/data-surface.d.ts.map +1 -1
- package/dist/components/data/data-surface.js +1 -0
- package/dist/components/data/index.d.ts +6 -0
- package/dist/components/data/index.d.ts.map +1 -1
- package/dist/components/data/index.js +6 -0
- package/dist/components/data/link-surface.d.ts +79 -0
- package/dist/components/data/link-surface.d.ts.map +1 -0
- package/dist/components/data/link-surface.js +155 -0
- package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
- package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
- package/dist/components/data/list-sort-state.svelte.js +47 -0
- package/dist/components/data/list-sort.d.ts +94 -0
- package/dist/components/data/list-sort.d.ts.map +1 -0
- package/dist/components/data/list-sort.js +182 -0
- package/dist/components/data/step-surface.d.ts +73 -0
- package/dist/components/data/step-surface.d.ts.map +1 -0
- package/dist/components/data/step-surface.js +177 -0
- package/dist/components/data/surface-navigation.d.ts +49 -0
- package/dist/components/data/surface-navigation.d.ts.map +1 -0
- package/dist/components/data/surface-navigation.js +100 -0
- package/dist/components/data/types.d.ts +27 -0
- package/dist/components/data/types.d.ts.map +1 -1
- package/dist/components/display/DateDisplay.svelte +76 -14
- package/dist/components/display/DateDisplay.svelte.d.ts +2 -0
- package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -1
- package/dist/components/display/Icon.svelte +3 -16
- package/dist/components/display/Icon.svelte.d.ts +0 -9
- package/dist/components/display/Icon.svelte.d.ts.map +1 -1
- package/dist/components/display/StatusBadge.svelte +29 -2
- package/dist/components/display/StatusBadge.svelte.d.ts +4 -2
- package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -1
- package/dist/components/display/__tests__/DateDisplay.test.js +82 -1
- package/dist/components/display/__tests__/Icon.test.js +132 -2
- package/dist/components/display/__tests__/StatusBadge.test.js +78 -0
- package/dist/components/display/icons.svelte.d.ts +11 -0
- package/dist/components/display/icons.svelte.d.ts.map +1 -0
- package/dist/components/display/icons.svelte.js +60 -0
- package/dist/components/display/index.d.ts +3 -1
- package/dist/components/display/index.d.ts.map +1 -1
- package/dist/components/display/index.js +1 -0
- package/dist/components/display/types.d.ts +2 -0
- package/dist/components/display/types.d.ts.map +1 -1
- package/dist/components/feedback/ConfirmDialog.svelte +59 -48
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts +14 -2
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -1
- package/dist/components/feedback/PhoneSheet.svelte +224 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
- package/dist/components/feedback/ToastViewport.svelte +156 -8
- package/dist/components/feedback/ToastViewport.svelte.d.ts +5 -1
- package/dist/components/feedback/ToastViewport.svelte.d.ts.map +1 -1
- package/dist/components/feedback/WorkingStrip.svelte +426 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +38 -20
- package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
- package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +9 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +19 -0
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/toast.test.js +142 -1
- package/dist/components/feedback/index.d.ts +5 -1
- package/dist/components/feedback/index.d.ts.map +1 -1
- package/dist/components/feedback/index.js +5 -0
- package/dist/components/feedback/toast.d.ts +13 -1
- package/dist/components/feedback/toast.d.ts.map +1 -1
- package/dist/components/feedback/toast.js +51 -10
- package/dist/components/feedback/working-status.d.ts +34 -0
- package/dist/components/feedback/working-status.d.ts.map +1 -0
- package/dist/components/feedback/working-status.js +5 -0
- package/dist/components/forms/CameraCapture.svelte +619 -0
- package/dist/components/forms/CameraCapture.svelte.d.ts +42 -0
- package/dist/components/forms/CameraCapture.svelte.d.ts.map +1 -0
- package/dist/components/forms/Checkbox.svelte +34 -3
- package/dist/components/forms/Checkbox.svelte.d.ts +2 -0
- package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -1
- package/dist/components/forms/Combobox.svelte +108 -12
- package/dist/components/forms/Combobox.svelte.d.ts +6 -0
- package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
- package/dist/components/forms/DictationButton.svelte +128 -0
- package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
- package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
- package/dist/components/forms/DictationStatus.svelte +164 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
- package/dist/components/forms/Fieldset.svelte +5 -1
- package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
- package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
- package/dist/components/forms/Form.svelte +5 -0
- package/dist/components/forms/Form.svelte.d.ts +1 -0
- package/dist/components/forms/Form.svelte.d.ts.map +1 -1
- package/dist/components/forms/FormActionBar.svelte +84 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
- package/dist/components/forms/Input.svelte +17 -1
- package/dist/components/forms/Input.svelte.d.ts +2 -0
- package/dist/components/forms/Input.svelte.d.ts.map +1 -1
- package/dist/components/forms/Listbox.svelte +39 -1
- package/dist/components/forms/Listbox.svelte.d.ts.map +1 -1
- package/dist/components/forms/MultiSelect.svelte +41 -1
- package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -1
- package/dist/components/forms/Radio.svelte +16 -4
- package/dist/components/forms/RadioGroup.svelte +7 -1
- package/dist/components/forms/RadioGroup.svelte.d.ts +2 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -1
- package/dist/components/forms/SearchInput.svelte +207 -0
- package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
- package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/SegmentedControl.svelte +152 -11
- package/dist/components/forms/SegmentedControl.svelte.d.ts +4 -0
- package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -1
- package/dist/components/forms/Select.svelte +17 -1
- package/dist/components/forms/Select.svelte.d.ts +2 -0
- package/dist/components/forms/Select.svelte.d.ts.map +1 -1
- package/dist/components/forms/SignaturePad.svelte +436 -0
- package/dist/components/forms/SignaturePad.svelte.d.ts +32 -0
- package/dist/components/forms/SignaturePad.svelte.d.ts.map +1 -0
- package/dist/components/forms/StagedControlReview.svelte +21 -12
- package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
- package/dist/components/forms/Switch.svelte +18 -5
- package/dist/components/forms/Switch.svelte.d.ts +2 -0
- package/dist/components/forms/Switch.svelte.d.ts.map +1 -1
- package/dist/components/forms/TagsInput.svelte +8 -1
- package/dist/components/forms/Textarea.svelte +16 -1
- package/dist/components/forms/Textarea.svelte.d.ts +2 -0
- package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
- package/dist/components/forms/__tests__/CameraCapture.test.js +504 -0
- package/dist/components/forms/__tests__/Combobox.test.js +354 -0
- package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
- package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
- package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
- package/dist/components/forms/__tests__/Form.test.js +46 -0
- package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
- package/dist/components/forms/__tests__/Listbox.test.js +303 -0
- package/dist/components/forms/__tests__/MultiSelect.test.js +281 -0
- package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
- package/dist/components/forms/__tests__/SignaturePad.test.js +338 -0
- package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
- package/dist/components/forms/__tests__/camera-capture-session.test.js +207 -0
- package/dist/components/forms/__tests__/capture-form-retry.test.js +166 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte +38 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +10 -0
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/capture-test-env.d.ts +70 -0
- package/dist/components/forms/__tests__/capture-test-env.d.ts.map +1 -0
- package/dist/components/forms/__tests__/capture-test-env.js +122 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte +15 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +14 -0
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
- package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
- package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
- package/dist/components/forms/__tests__/dictation.test.js +486 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte +28 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +11 -0
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +31 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-retry-form.test.js +107 -0
- package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +69 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +9 -0
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte +27 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/long-press.test.js +175 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +27 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +13 -0
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/native-file-field.test.js +177 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +28 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/segmented-posting.test.js +92 -0
- package/dist/components/forms/__tests__/signature-pad-logic.test.js +54 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte +34 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/touch-density.test.js +66 -0
- package/dist/components/forms/audio-capture.d.ts +69 -0
- package/dist/components/forms/audio-capture.d.ts.map +1 -0
- package/dist/components/forms/audio-capture.js +193 -0
- package/dist/components/forms/camera-capture-session.d.ts +81 -0
- package/dist/components/forms/camera-capture-session.d.ts.map +1 -0
- package/dist/components/forms/camera-capture-session.js +130 -0
- package/dist/components/forms/control-interaction.d.ts +6 -0
- package/dist/components/forms/control-interaction.d.ts.map +1 -1
- package/dist/components/forms/control-interaction.js +12 -1
- package/dist/components/forms/control-proposals.d.ts +55 -0
- package/dist/components/forms/control-proposals.d.ts.map +1 -0
- package/dist/components/forms/control-proposals.js +188 -0
- package/dist/components/forms/dictation-transcribe.d.ts +36 -0
- package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
- package/dist/components/forms/dictation-transcribe.js +65 -0
- package/dist/components/forms/dictation.svelte.d.ts +200 -0
- package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
- package/dist/components/forms/dictation.svelte.js +686 -0
- package/dist/components/forms/form-retry/__tests__/boundaries.test.js +23 -0
- package/dist/components/forms/form-retry/__tests__/content-digest.test.js +70 -0
- package/dist/components/forms/form-retry/__tests__/controller.test.js +802 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +82 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +1 -0
- package/dist/components/forms/form-retry/__tests__/fake-kit.js +180 -0
- package/dist/components/forms/form-retry/__tests__/form-draft.test.js +169 -0
- package/dist/components/forms/form-retry/__tests__/ssr.test.js +37 -0
- package/dist/components/forms/form-retry/__tests__/storage.d.ts +26 -0
- package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +1 -0
- package/dist/components/forms/form-retry/__tests__/storage.js +63 -0
- package/dist/components/forms/form-retry/__tests__/submission-key.test.js +236 -0
- package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +334 -0
- package/dist/components/forms/form-retry/content-digest.d.ts +54 -0
- package/dist/components/forms/form-retry/content-digest.d.ts.map +1 -0
- package/dist/components/forms/form-retry/content-digest.js +169 -0
- package/dist/components/forms/form-retry/controller.d.ts +214 -0
- package/dist/components/forms/form-retry/controller.d.ts.map +1 -0
- package/dist/components/forms/form-retry/controller.js +404 -0
- package/dist/components/forms/form-retry/form-draft.d.ts +68 -0
- package/dist/components/forms/form-retry/form-draft.d.ts.map +1 -0
- package/dist/components/forms/form-retry/form-draft.js +161 -0
- package/dist/components/forms/form-retry/index.d.ts +19 -0
- package/dist/components/forms/form-retry/index.d.ts.map +1 -0
- package/dist/components/forms/form-retry/index.js +16 -0
- package/dist/components/forms/form-retry/submission-key.d.ts +244 -0
- package/dist/components/forms/form-retry/submission-key.d.ts.map +1 -0
- package/dist/components/forms/form-retry/submission-key.js +342 -0
- package/dist/components/forms/form-retry/submitted-draft.d.ts +112 -0
- package/dist/components/forms/form-retry/submitted-draft.d.ts.map +1 -0
- package/dist/components/forms/form-retry/submitted-draft.js +270 -0
- package/dist/components/forms/form-retry/types.d.ts +84 -0
- package/dist/components/forms/form-retry/types.d.ts.map +1 -0
- package/dist/components/forms/form-retry/types.js +13 -0
- package/dist/components/forms/index.d.ts +21 -2
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +22 -1
- package/dist/components/forms/insert-text.d.ts +12 -0
- package/dist/components/forms/insert-text.d.ts.map +1 -0
- package/dist/components/forms/insert-text.js +38 -0
- package/dist/components/forms/long-press.d.ts +88 -0
- package/dist/components/forms/long-press.d.ts.map +1 -0
- package/dist/components/forms/long-press.js +213 -0
- package/dist/components/forms/native-file-field.d.ts +94 -0
- package/dist/components/forms/native-file-field.d.ts.map +1 -0
- package/dist/components/forms/native-file-field.js +147 -0
- package/dist/components/forms/ready-beep.d.ts +24 -0
- package/dist/components/forms/ready-beep.d.ts.map +1 -0
- package/dist/components/forms/ready-beep.js +82 -0
- package/dist/components/forms/signature-pad-logic.d.ts +50 -0
- package/dist/components/forms/signature-pad-logic.d.ts.map +1 -0
- package/dist/components/forms/signature-pad-logic.js +48 -0
- package/dist/components/forms/types.d.ts +41 -0
- package/dist/components/forms/types.d.ts.map +1 -1
- package/dist/components/layout/PageHeader.svelte +151 -17
- package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
- package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
- package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/layout/page-header-context.d.ts +25 -0
- package/dist/components/layout/page-header-context.d.ts.map +1 -0
- package/dist/components/layout/page-header-context.js +16 -0
- package/dist/components/nav/FilterChips.svelte +8 -1
- package/dist/components/nav/FilterChips.svelte.d.ts +2 -0
- package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -1
- package/dist/components/nav/Tabs.svelte +241 -0
- package/dist/components/nav/Tabs.svelte.d.ts +15 -0
- package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
- package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
- package/dist/components/nav/index.d.ts +1 -0
- package/dist/components/nav/index.d.ts.map +1 -1
- package/dist/components/nav/index.js +1 -0
- package/dist/components/nav/tabs-overflow.d.ts +12 -0
- package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
- package/dist/components/nav/tabs-overflow.js +17 -0
- package/dist/components/nav/types.d.ts +8 -0
- package/dist/components/nav/types.d.ts.map +1 -1
- package/dist/components/ui/Button.svelte +16 -2
- package/dist/components/ui/Button.svelte.d.ts +2 -0
- package/dist/components/ui/Button.svelte.d.ts.map +1 -1
- package/dist/components/ui/Card.svelte +14 -5
- package/dist/components/ui/Dropdown.svelte +55 -1
- package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
- package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
- package/dist/components/ui/IconToggle.svelte +139 -0
- package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
- package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
- package/dist/components/ui/IconToggleGroup.svelte +56 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
- package/dist/components/ui/Pagination.svelte +1 -1
- package/dist/components/ui/Popover.svelte +17 -1
- package/dist/components/ui/Popover.svelte.d.ts +8 -0
- package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
- package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
- package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
- package/dist/components/ui/__tests__/Popover.test.js +44 -0
- package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
- package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +2 -0
- package/dist/i18n/strings.ui.d.ts +65 -0
- package/dist/i18n/strings.ui.d.ts.map +1 -1
- package/dist/i18n/strings.ui.js +72 -0
- package/dist/styles/tokens.css +2 -0
- package/dist/svelte/__tests__/playground.test.js +3 -1
- package/dist/svelte/playground/CalendarPreview.svelte +140 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground/CapturePreview.svelte +56 -0
- package/dist/svelte/playground/CapturePreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/CapturePreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +20 -0
- package/dist/themes/ThemeProvider.svelte +9 -0
- package/dist/themes/ThemeProvider.svelte.d.ts +2 -0
- package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/themes/css-generator.d.ts.map +1 -1
- package/dist/themes/css-generator.js +3 -0
- package/dist/themes/styles/glass.css +4 -0
- package/dist/themes/styles/happyvertical.css +4 -0
- package/dist/themes/styles/material.css +4 -0
- package/dist/themes/styles/smrt.css +4 -0
- package/dist/themes/styles/studio.css +4 -0
- package/dist/types-generic.d.ts +2 -0
- package/dist/types-generic.d.ts.map +1 -1
- package/package.json +9 -3
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/svelte';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
5
|
+
import SearchInput from '../SearchInput.svelte';
|
|
6
|
+
describe('SearchInput', () => {
|
|
7
|
+
beforeEach(() => vi.useFakeTimers({ shouldAdvanceTime: true }));
|
|
8
|
+
afterEach(() => vi.useRealTimers());
|
|
9
|
+
it('is a labelled search field in a search landmark', () => {
|
|
10
|
+
render(SearchInput, {
|
|
11
|
+
props: { value: '', onsearch: vi.fn(), placeholder: 'Search by title' },
|
|
12
|
+
});
|
|
13
|
+
expect(screen.getByRole('search')).toBeInTheDocument();
|
|
14
|
+
expect(screen.getByRole('searchbox', { name: 'Search' })).toHaveAttribute('placeholder', 'Search by title');
|
|
15
|
+
});
|
|
16
|
+
it('searches once, trimmed and single-line, after the last key', async () => {
|
|
17
|
+
const onsearch = vi.fn();
|
|
18
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
19
|
+
render(SearchInput, { props: { value: '', onsearch, debounceMs: 300 } });
|
|
20
|
+
await user.type(screen.getByRole('searchbox'), ' council hall ');
|
|
21
|
+
expect(onsearch).not.toHaveBeenCalled();
|
|
22
|
+
vi.advanceTimersByTime(300);
|
|
23
|
+
expect(onsearch).toHaveBeenCalledTimes(1);
|
|
24
|
+
expect(onsearch).toHaveBeenCalledWith('council hall');
|
|
25
|
+
});
|
|
26
|
+
it('searches at once on Enter', async () => {
|
|
27
|
+
const onsearch = vi.fn();
|
|
28
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
29
|
+
render(SearchInput, { props: { value: '', onsearch } });
|
|
30
|
+
await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
|
|
31
|
+
expect(onsearch).toHaveBeenCalledWith('roads');
|
|
32
|
+
});
|
|
33
|
+
it('does not search for the search it is already showing', async () => {
|
|
34
|
+
const onsearch = vi.fn();
|
|
35
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
36
|
+
render(SearchInput, { props: { value: 'roads', onsearch } });
|
|
37
|
+
await user.type(screen.getByRole('searchbox'), '{Enter}');
|
|
38
|
+
expect(onsearch).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
it('clears with the x and focuses the field', async () => {
|
|
41
|
+
const onsearch = vi.fn();
|
|
42
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
43
|
+
render(SearchInput, { props: { value: 'roads', onsearch } });
|
|
44
|
+
const field = screen.getByRole('searchbox');
|
|
45
|
+
expect(field).toHaveValue('roads');
|
|
46
|
+
await user.click(screen.getByRole('button', { name: 'Clear search' }));
|
|
47
|
+
expect(onsearch).toHaveBeenCalledWith('');
|
|
48
|
+
expect(field).toHaveValue('');
|
|
49
|
+
expect(field).toHaveFocus();
|
|
50
|
+
expect(screen.queryByRole('button', { name: 'Clear search' })).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it('follows the applied search when it changes from outside', async () => {
|
|
53
|
+
const { rerender } = render(SearchInput, {
|
|
54
|
+
props: { value: 'roads', onsearch: vi.fn() },
|
|
55
|
+
});
|
|
56
|
+
await rerender({ value: 'parks', onsearch: vi.fn() });
|
|
57
|
+
expect(screen.getByRole('searchbox')).toHaveValue('parks');
|
|
58
|
+
});
|
|
59
|
+
it('inside a host form, Enter searches and never submits the host form', async () => {
|
|
60
|
+
const onsearch = vi.fn();
|
|
61
|
+
const onsubmit = vi.fn((event) => event.preventDefault());
|
|
62
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
63
|
+
const host = document.createElement('form');
|
|
64
|
+
host.addEventListener('submit', onsubmit);
|
|
65
|
+
document.body.append(host);
|
|
66
|
+
render(SearchInput, { target: host, props: { value: '', onsearch } });
|
|
67
|
+
expect(host.querySelector('form')).toBeNull();
|
|
68
|
+
await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
|
|
69
|
+
expect(onsearch).toHaveBeenCalledWith('roads');
|
|
70
|
+
expect(onsubmit).not.toHaveBeenCalled();
|
|
71
|
+
host.remove();
|
|
72
|
+
});
|
|
73
|
+
it('a value the host normalized differently does not swallow a later one', async () => {
|
|
74
|
+
const onsearch = vi.fn();
|
|
75
|
+
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
76
|
+
const view = render(SearchInput, { props: { value: '', onsearch } });
|
|
77
|
+
await user.type(screen.getByRole('searchbox'), 'Roads{Enter}');
|
|
78
|
+
expect(onsearch).toHaveBeenCalledWith('Roads');
|
|
79
|
+
// The host applies its own normalization, then later navigates to the
|
|
80
|
+
// exact text the box once sent: the box must follow both.
|
|
81
|
+
await view.rerender({ value: 'roads', onsearch });
|
|
82
|
+
expect(screen.getByRole('searchbox')).toHaveValue('roads');
|
|
83
|
+
await view.rerender({ value: 'Roads', onsearch });
|
|
84
|
+
expect(screen.getByRole('searchbox')).toHaveValue('Roads');
|
|
85
|
+
});
|
|
86
|
+
it('is axe-clean', async () => {
|
|
87
|
+
const { container } = render(SearchInput, {
|
|
88
|
+
props: { value: 'roads', onsearch: vi.fn() },
|
|
89
|
+
});
|
|
90
|
+
await expectNoA11yViolations(container);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SignaturePad rendered states, pointer gating, PNG output, and native form
|
|
3
|
+
* posting (smrt#3290).
|
|
4
|
+
*/
|
|
5
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { expectNoA11yViolations } from '../../../test-support/a11y.js';
|
|
8
|
+
import SignaturePad from '../SignaturePad.svelte';
|
|
9
|
+
import { SIGNATURE_INK_COLOR, SIGNATURE_PAPER_COLOR, } from '../signature-pad-logic.js';
|
|
10
|
+
import CaptureFormFixture from './capture-form.fixture.svelte';
|
|
11
|
+
import { stubCanvas, submittedEntries, useFileFieldStrategy, } from './capture-test-env.js';
|
|
12
|
+
let context;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
context = stubCanvas();
|
|
15
|
+
useFileFieldStrategy('data-transfer');
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.restoreAllMocks();
|
|
19
|
+
vi.unstubAllGlobals();
|
|
20
|
+
});
|
|
21
|
+
function canvas(container) {
|
|
22
|
+
return container.querySelector('canvas');
|
|
23
|
+
}
|
|
24
|
+
function root(container) {
|
|
25
|
+
return container.querySelector('.signature-pad');
|
|
26
|
+
}
|
|
27
|
+
async function stroke(target, pointerType = 'mouse', pointerId = 1) {
|
|
28
|
+
await fireEvent.pointerDown(target, {
|
|
29
|
+
pointerType,
|
|
30
|
+
pointerId,
|
|
31
|
+
clientX: 10,
|
|
32
|
+
clientY: 10,
|
|
33
|
+
});
|
|
34
|
+
await fireEvent.pointerMove(target, {
|
|
35
|
+
pointerType,
|
|
36
|
+
pointerId,
|
|
37
|
+
clientX: 60,
|
|
38
|
+
clientY: 40,
|
|
39
|
+
});
|
|
40
|
+
await fireEvent.pointerUp(target, { pointerType, pointerId });
|
|
41
|
+
}
|
|
42
|
+
const useButton = () => screen.getByRole('button', { name: 'Use signature' });
|
|
43
|
+
describe('SignaturePad states', () => {
|
|
44
|
+
it('starts empty on white paper with Use signature disabled', async () => {
|
|
45
|
+
const { container } = render(SignaturePad);
|
|
46
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
47
|
+
expect(useButton()).toBeDisabled();
|
|
48
|
+
expect(screen.getByRole('button', { name: 'Clear' })).toBeEnabled();
|
|
49
|
+
expect(canvas(container)).toHaveAttribute('aria-label', 'Signature area, not yet signed');
|
|
50
|
+
expect(screen.getByText('Sign with a stylus, your finger, or the mouse.')).toBeInTheDocument();
|
|
51
|
+
// Paper is painted into the bitmap and shown behind it regardless of theme.
|
|
52
|
+
expect(context.fillStyle).toBe(SIGNATURE_PAPER_COLOR);
|
|
53
|
+
expect(context.fillRect).toHaveBeenCalledWith(0, 0, 600, 240);
|
|
54
|
+
expect(canvas(container).style.backgroundColor).toBe('rgb(255, 255, 255)');
|
|
55
|
+
await expectNoA11yViolations(container);
|
|
56
|
+
});
|
|
57
|
+
it('enables Use signature once an accepted stroke exists, inked dark', async () => {
|
|
58
|
+
const { container } = render(SignaturePad);
|
|
59
|
+
await stroke(canvas(container));
|
|
60
|
+
expect(root(container)).toHaveAttribute('data-state', 'signed');
|
|
61
|
+
expect(canvas(container)).toHaveAttribute('aria-label', 'Signature area, signed');
|
|
62
|
+
expect(useButton()).toBeEnabled();
|
|
63
|
+
expect(context.stroke).toHaveBeenCalled();
|
|
64
|
+
expect(context.strokeStyle).toBe(SIGNATURE_INK_COLOR);
|
|
65
|
+
await expectNoA11yViolations(container);
|
|
66
|
+
});
|
|
67
|
+
it('ignores finger and mouse under stylusOnly, and accepts the pen', async () => {
|
|
68
|
+
const { container } = render(SignaturePad, { props: { stylusOnly: true } });
|
|
69
|
+
expect(screen.getByText('Sign with the stylus.')).toBeInTheDocument();
|
|
70
|
+
await stroke(canvas(container), 'touch');
|
|
71
|
+
await stroke(canvas(container), 'mouse', 2);
|
|
72
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
73
|
+
expect(useButton()).toBeDisabled();
|
|
74
|
+
expect(context.stroke).not.toHaveBeenCalled();
|
|
75
|
+
await stroke(canvas(container), 'pen', 3);
|
|
76
|
+
expect(useButton()).toBeEnabled();
|
|
77
|
+
});
|
|
78
|
+
it('ignores a second pointer that did not start the stroke', async () => {
|
|
79
|
+
const { container } = render(SignaturePad);
|
|
80
|
+
await fireEvent.pointerMove(canvas(container), {
|
|
81
|
+
pointerType: 'mouse',
|
|
82
|
+
pointerId: 9,
|
|
83
|
+
});
|
|
84
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
85
|
+
});
|
|
86
|
+
it('is inert when disabled', async () => {
|
|
87
|
+
const { container } = render(SignaturePad, { props: { disabled: true } });
|
|
88
|
+
await stroke(canvas(container));
|
|
89
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
90
|
+
expect(useButton()).toBeDisabled();
|
|
91
|
+
expect(screen.getByRole('button', { name: 'Clear' })).toBeDisabled();
|
|
92
|
+
});
|
|
93
|
+
it('returns a PNG through onCapture and locks the pad until cleared', async () => {
|
|
94
|
+
const onCapture = vi.fn();
|
|
95
|
+
const onClear = vi.fn();
|
|
96
|
+
const { container } = render(SignaturePad, {
|
|
97
|
+
props: { onCapture, onClear },
|
|
98
|
+
});
|
|
99
|
+
await stroke(canvas(container));
|
|
100
|
+
await fireEvent.click(useButton());
|
|
101
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
102
|
+
const [{ blob, dataUrl }] = onCapture.mock.calls[0];
|
|
103
|
+
expect(blob.type).toBe('image/png');
|
|
104
|
+
expect(dataUrl).toMatch(/^data:image\/png/);
|
|
105
|
+
expect(screen.getByRole('status')).toHaveTextContent('Signature attached.');
|
|
106
|
+
expect(useButton()).toBeDisabled();
|
|
107
|
+
context.stroke.mockClear();
|
|
108
|
+
await stroke(canvas(container));
|
|
109
|
+
expect(context.stroke).not.toHaveBeenCalled();
|
|
110
|
+
await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
|
|
111
|
+
expect(onClear).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
113
|
+
});
|
|
114
|
+
it('locks the pad while the PNG encodes so it commits once and matches the canvas', async () => {
|
|
115
|
+
let finish;
|
|
116
|
+
vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
|
|
117
|
+
finish = () => callback(new Blob(['pixels'], { type }));
|
|
118
|
+
});
|
|
119
|
+
const onCapture = vi.fn();
|
|
120
|
+
const { container } = render(SignaturePad, { props: { onCapture } });
|
|
121
|
+
await stroke(canvas(container));
|
|
122
|
+
await fireEvent.click(useButton());
|
|
123
|
+
expect(useButton()).toBeDisabled();
|
|
124
|
+
await fireEvent.click(useButton());
|
|
125
|
+
context.stroke.mockClear();
|
|
126
|
+
await stroke(canvas(container), 'mouse', 2);
|
|
127
|
+
expect(context.stroke).not.toHaveBeenCalled();
|
|
128
|
+
finish();
|
|
129
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
130
|
+
expect(onCapture).toHaveBeenCalledTimes(1);
|
|
131
|
+
});
|
|
132
|
+
it('discards an encoding superseded by Clear', async () => {
|
|
133
|
+
let finish;
|
|
134
|
+
vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
|
|
135
|
+
finish = () => callback(new Blob(['pixels'], { type }));
|
|
136
|
+
});
|
|
137
|
+
const onCapture = vi.fn();
|
|
138
|
+
const { container } = render(SignaturePad, { props: { onCapture } });
|
|
139
|
+
await stroke(canvas(container));
|
|
140
|
+
await fireEvent.click(useButton());
|
|
141
|
+
await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
|
|
142
|
+
finish();
|
|
143
|
+
await Promise.resolve();
|
|
144
|
+
expect(onCapture).not.toHaveBeenCalled();
|
|
145
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
146
|
+
});
|
|
147
|
+
it('keeps a committed signature across prop updates and resets only on a size change', async () => {
|
|
148
|
+
const onClear = vi.fn();
|
|
149
|
+
const props = { name: 'signature', onClear };
|
|
150
|
+
const { container, rerender } = render(SignaturePad, { props });
|
|
151
|
+
await stroke(canvas(container));
|
|
152
|
+
await fireEvent.click(useButton());
|
|
153
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
154
|
+
// The harness replaces the whole props object, as a parent re-spreading a
|
|
155
|
+
// fresh object does; width and height are unchanged.
|
|
156
|
+
await rerender({ ...props, disabled: true });
|
|
157
|
+
await rerender({ ...props, disabled: false });
|
|
158
|
+
expect(root(container)).toHaveAttribute('data-state', 'committed');
|
|
159
|
+
expect(onClear).not.toHaveBeenCalled();
|
|
160
|
+
await rerender({ ...props, width: 300 });
|
|
161
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
162
|
+
expect(onClear).toHaveBeenCalledTimes(1);
|
|
163
|
+
});
|
|
164
|
+
it('accepts label overrides', () => {
|
|
165
|
+
render(SignaturePad, {
|
|
166
|
+
props: { labels: { useSignature: 'Signer', clear: 'Effacer' } },
|
|
167
|
+
});
|
|
168
|
+
expect(screen.getByRole('button', { name: 'Signer' })).toBeInTheDocument();
|
|
169
|
+
expect(screen.getByRole('button', { name: 'Effacer' })).toBeInTheDocument();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('SignaturePad native form post', () => {
|
|
173
|
+
it('posts the committed signature as signature.png and removes it on Clear', async () => {
|
|
174
|
+
const { container } = render(CaptureFormFixture, {
|
|
175
|
+
props: { kind: 'signature', name: 'signature' },
|
|
176
|
+
});
|
|
177
|
+
const form = container.querySelector('form');
|
|
178
|
+
expect(root(container)).toHaveAttribute('data-smrt-file-field', 'data-transfer');
|
|
179
|
+
expect(submittedEntries(form).get('signature').size).toBe(0);
|
|
180
|
+
await stroke(canvas(container));
|
|
181
|
+
await fireEvent.click(useButton());
|
|
182
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
183
|
+
const posted = submittedEntries(form).getAll('signature');
|
|
184
|
+
expect(posted).toHaveLength(1);
|
|
185
|
+
expect(posted[0].name).toBe('signature.png');
|
|
186
|
+
expect(posted[0].type).toBe('image/png');
|
|
187
|
+
expect(submittedEntries(form).get('note')).toBe('hazard');
|
|
188
|
+
await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
|
|
189
|
+
expect(submittedEntries(form).get('signature').size).toBe(0);
|
|
190
|
+
});
|
|
191
|
+
it('falls back to the formdata event when DataTransfer is unavailable', async () => {
|
|
192
|
+
useFileFieldStrategy('formdata-event');
|
|
193
|
+
const { container } = render(CaptureFormFixture, {
|
|
194
|
+
props: { kind: 'signature', name: 'sig', fileName: 'flha-signature.png' },
|
|
195
|
+
});
|
|
196
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', 'formdata-event'));
|
|
197
|
+
await stroke(canvas(container));
|
|
198
|
+
await fireEvent.click(useButton());
|
|
199
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
200
|
+
const posted = submittedEntries(container.querySelector('form')).getAll('sig');
|
|
201
|
+
expect(posted).toHaveLength(1);
|
|
202
|
+
expect(posted[0].name).toBe('flha-signature.png');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('SignaturePad in a disabled fieldset', () => {
|
|
206
|
+
it.each([
|
|
207
|
+
'data-transfer',
|
|
208
|
+
'formdata-event',
|
|
209
|
+
])('posts nothing, committed or empty, like a disabled native control (%s)', async (strategy) => {
|
|
210
|
+
useFileFieldStrategy(strategy);
|
|
211
|
+
const { container } = render(CaptureFormFixture, {
|
|
212
|
+
props: { kind: 'signature', name: 'signature', inFieldset: true },
|
|
213
|
+
});
|
|
214
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', strategy));
|
|
215
|
+
const form = container.querySelector('form');
|
|
216
|
+
const fieldset = form.querySelector('fieldset');
|
|
217
|
+
fieldset.disabled = true;
|
|
218
|
+
expect(submittedEntries(form).has('signature')).toBe(false);
|
|
219
|
+
fieldset.disabled = false;
|
|
220
|
+
await stroke(canvas(container));
|
|
221
|
+
await fireEvent.click(useButton());
|
|
222
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
223
|
+
expect(submittedEntries(form).getAll('signature')).toHaveLength(1);
|
|
224
|
+
fieldset.disabled = true;
|
|
225
|
+
expect(submittedEntries(form).has('signature')).toBe(false);
|
|
226
|
+
expect(submittedEntries(form).get('note')).toBe('hazard');
|
|
227
|
+
fieldset.disabled = false;
|
|
228
|
+
expect(submittedEntries(form).getAll('signature')).toHaveLength(1);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
/**
|
|
232
|
+
* A reset of the owning form (smrt#3290): SvelteKit `enhance`'s `update()`
|
|
233
|
+
* after a success, `createFormRetry()`'s conditional reset, a reset button and
|
|
234
|
+
* `form.reset()` all empty a native file input, so the pad must empty with it.
|
|
235
|
+
* jsdom's reset does not empty file inputs (browsers do), so these assertions
|
|
236
|
+
* prove the component empties its own field.
|
|
237
|
+
*/
|
|
238
|
+
describe('SignaturePad form reset', () => {
|
|
239
|
+
function form(container) {
|
|
240
|
+
return container.querySelector('form');
|
|
241
|
+
}
|
|
242
|
+
async function commit(container) {
|
|
243
|
+
await stroke(canvas(container));
|
|
244
|
+
await fireEvent.click(useButton());
|
|
245
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
|
|
246
|
+
}
|
|
247
|
+
function postedSignatures(container) {
|
|
248
|
+
return submittedEntries(form(container)).getAll('signature');
|
|
249
|
+
}
|
|
250
|
+
it.each([
|
|
251
|
+
'data-transfer',
|
|
252
|
+
'formdata-event',
|
|
253
|
+
])('discards a committed signature like Clear (%s)', async (strategy) => {
|
|
254
|
+
useFileFieldStrategy(strategy);
|
|
255
|
+
const onClear = vi.fn();
|
|
256
|
+
const { container } = render(CaptureFormFixture, {
|
|
257
|
+
props: { kind: 'signature', name: 'signature', onClear },
|
|
258
|
+
});
|
|
259
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', strategy));
|
|
260
|
+
await commit(container);
|
|
261
|
+
expect(postedSignatures(container)[0].name).toBe('signature.png');
|
|
262
|
+
context.fillRect.mockClear();
|
|
263
|
+
form(container).reset();
|
|
264
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'empty'));
|
|
265
|
+
expect(onClear).toHaveBeenCalledTimes(1);
|
|
266
|
+
expect(context.fillRect).toHaveBeenCalled();
|
|
267
|
+
expect(context.fillStyle).toBe(SIGNATURE_PAPER_COLOR);
|
|
268
|
+
expect(screen.getByRole('status')).toHaveTextContent('');
|
|
269
|
+
const posted = postedSignatures(container);
|
|
270
|
+
expect(posted).toHaveLength(1);
|
|
271
|
+
expect(posted[0].size).toBe(0);
|
|
272
|
+
expect(posted[0].name).toBe('');
|
|
273
|
+
// Unlocked: the next entry can be signed and committed.
|
|
274
|
+
await commit(container);
|
|
275
|
+
expect(postedSignatures(container)[0].name).toBe('signature.png');
|
|
276
|
+
});
|
|
277
|
+
it('a reset button discards ink not yet committed, without onClear', async () => {
|
|
278
|
+
const onClear = vi.fn();
|
|
279
|
+
const { container } = render(CaptureFormFixture, {
|
|
280
|
+
props: { kind: 'signature', name: 'signature', onClear },
|
|
281
|
+
});
|
|
282
|
+
await stroke(canvas(container));
|
|
283
|
+
expect(useButton()).toBeEnabled();
|
|
284
|
+
await fireEvent.click(screen.getByRole('button', { name: 'Reset form' }));
|
|
285
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
286
|
+
expect(useButton()).toBeDisabled();
|
|
287
|
+
expect(onClear).not.toHaveBeenCalled();
|
|
288
|
+
});
|
|
289
|
+
it('clears a committed signature even while disabled', async () => {
|
|
290
|
+
const onClear = vi.fn();
|
|
291
|
+
const { container, rerender } = render(CaptureFormFixture, {
|
|
292
|
+
props: { kind: 'signature', name: 'signature', onClear },
|
|
293
|
+
});
|
|
294
|
+
await commit(container);
|
|
295
|
+
await rerender({
|
|
296
|
+
kind: 'signature',
|
|
297
|
+
name: 'signature',
|
|
298
|
+
onClear,
|
|
299
|
+
disabled: true,
|
|
300
|
+
});
|
|
301
|
+
form(container).reset();
|
|
302
|
+
await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'empty'));
|
|
303
|
+
expect(onClear).toHaveBeenCalledTimes(1);
|
|
304
|
+
expect(postedSignatures(container)[0].size).toBe(0);
|
|
305
|
+
});
|
|
306
|
+
it('supersedes a signature still encoding', async () => {
|
|
307
|
+
let finish;
|
|
308
|
+
vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
|
|
309
|
+
finish = () => callback(new Blob(['pixels'], { type }));
|
|
310
|
+
});
|
|
311
|
+
const onCapture = vi.fn();
|
|
312
|
+
const { container } = render(CaptureFormFixture, {
|
|
313
|
+
props: { kind: 'signature', name: 'signature', onCapture },
|
|
314
|
+
});
|
|
315
|
+
await stroke(canvas(container));
|
|
316
|
+
await fireEvent.click(useButton());
|
|
317
|
+
form(container).reset();
|
|
318
|
+
finish();
|
|
319
|
+
await Promise.resolve();
|
|
320
|
+
expect(onCapture).not.toHaveBeenCalled();
|
|
321
|
+
expect(root(container)).toHaveAttribute('data-state', 'empty');
|
|
322
|
+
expect(postedSignatures(container)[0].size).toBe(0);
|
|
323
|
+
});
|
|
324
|
+
it('ignores the reset of another form', async () => {
|
|
325
|
+
const onClear = vi.fn();
|
|
326
|
+
const { container } = render(CaptureFormFixture, {
|
|
327
|
+
props: { kind: 'signature', name: 'signature', onClear },
|
|
328
|
+
});
|
|
329
|
+
await commit(container);
|
|
330
|
+
const other = document.createElement('form');
|
|
331
|
+
document.body.append(other);
|
|
332
|
+
other.reset();
|
|
333
|
+
expect(root(container)).toHaveAttribute('data-state', 'committed');
|
|
334
|
+
expect(onClear).not.toHaveBeenCalled();
|
|
335
|
+
expect(postedSignatures(container)[0].name).toBe('signature.png');
|
|
336
|
+
other.remove();
|
|
337
|
+
});
|
|
338
|
+
});
|
|
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
5
5
|
import { createControlInteractionRegistry, executeLocalControlBatch, executeLocalControlCommand, } from '../control-interaction.js';
|
|
6
|
+
import StagedControlReview from '../StagedControlReview.svelte';
|
|
6
7
|
import CompositeUserEditFixture from './composite-user-edit.fixture.svelte';
|
|
7
8
|
import SelectFixture from './select-interaction.fixture.svelte';
|
|
8
9
|
import Fixture from './staged-review.fixture.svelte';
|
|
@@ -77,7 +78,8 @@ describe('StagedControlReview', () => {
|
|
|
77
78
|
await waitFor(() => expect(field).toHaveAttribute('data-smrt-staged', 'true'));
|
|
78
79
|
expect(screen.getByRole('region', { name: 'Review proposed changes' })).toBeInTheDocument();
|
|
79
80
|
expect(screen.getByText(/Proposed by assistant/)).toBeInTheDocument();
|
|
80
|
-
|
|
81
|
+
// Plain field labels only: registry addressing never shows (#forms-qa).
|
|
82
|
+
expect(screen.queryByText(/profile\/display-name/)).not.toBeInTheDocument();
|
|
81
83
|
const proposal = screen.getByRole('textbox', {
|
|
82
84
|
name: 'Edit proposed value for Display name',
|
|
83
85
|
});
|
|
@@ -93,6 +95,38 @@ describe('StagedControlReview', () => {
|
|
|
93
95
|
expect(field).toHaveFocus();
|
|
94
96
|
await expectNoA11yViolations(container);
|
|
95
97
|
});
|
|
98
|
+
it('shows plain field labels, never registry ids, for unlabeled controls', async () => {
|
|
99
|
+
const registry = createReviewRegistry();
|
|
100
|
+
let town = '';
|
|
101
|
+
registry.register({
|
|
102
|
+
identity: {
|
|
103
|
+
formId: 'setup-network',
|
|
104
|
+
controlId: 'setup-network/town_name',
|
|
105
|
+
subject: { type: 'site', id: 'site-1', label: 'Lacombe News' },
|
|
106
|
+
},
|
|
107
|
+
metadata: { kind: 'text' },
|
|
108
|
+
getValue: () => town,
|
|
109
|
+
setValue: (next) => {
|
|
110
|
+
town = String(next);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
render(StagedControlReview, {
|
|
114
|
+
props: { registry, formId: 'setup-network' },
|
|
115
|
+
});
|
|
116
|
+
await registry.execute({
|
|
117
|
+
action: 'stage',
|
|
118
|
+
identity: {
|
|
119
|
+
formId: 'setup-network',
|
|
120
|
+
controlId: 'setup-network/town_name',
|
|
121
|
+
subject: { type: 'site', id: 'site-1' },
|
|
122
|
+
},
|
|
123
|
+
value: 'Lacombe',
|
|
124
|
+
}, { source: 'agent' });
|
|
125
|
+
await waitFor(() => expect(screen.getByText('Town name')).toBeVisible());
|
|
126
|
+
expect(screen.getByText('Lacombe News')).toBeVisible();
|
|
127
|
+
expect(screen.queryByText(/setup-network/)).not.toBeInTheDocument();
|
|
128
|
+
expect(screen.queryByText(/site-1/)).not.toBeInTheDocument();
|
|
129
|
+
});
|
|
96
130
|
it('keeps Enter in the proposal editor from submitting the form', async () => {
|
|
97
131
|
const registry = createReviewRegistry();
|
|
98
132
|
render(Fixture, { props: { registry } });
|
|
@@ -594,8 +628,8 @@ describe('StagedControlReview', () => {
|
|
|
594
628
|
value: 'Grace',
|
|
595
629
|
}, { source: 'agent' });
|
|
596
630
|
await waitFor(() => expect(renderedFirst).toHaveAttribute('data-smrt-staged', 'true'));
|
|
597
|
-
expect(screen.
|
|
598
|
-
expect(screen.
|
|
631
|
+
expect(screen.queryByText(/profile\/display-name/)).not.toBeInTheDocument();
|
|
632
|
+
expect(screen.queryByText(/record:one/)).not.toBeInTheDocument();
|
|
599
633
|
for (const subjectId of ['one', 'two']) {
|
|
600
634
|
const actionName = `Display name record:${subjectId} [identity:["profile","display-name","record","${subjectId}"]]`;
|
|
601
635
|
expect(screen.getByRole('textbox', {
|