@webiny/app-admin 0.0.0-unstable.78f581c1d2 → 0.0.0-unstable.7be00a75a9
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/README.md +7 -17
- package/assets/icons/add-18px.js +20 -0
- package/assets/icons/add-18px.js.map +1 -0
- package/assets/icons/add-18px.svg +1 -1
- package/assets/icons/arrow_drop_down-24px.js +20 -0
- package/assets/icons/arrow_drop_down-24px.js.map +1 -0
- package/assets/icons/arrow_drop_down-24px.svg +1 -1
- package/assets/icons/attach_file_black_24dp.js +20 -0
- package/assets/icons/attach_file_black_24dp.js.map +1 -0
- package/assets/icons/baseline-menu-24px.js +20 -0
- package/assets/icons/baseline-menu-24px.js.map +1 -0
- package/assets/icons/baseline-notification_important-24px.js +19 -0
- package/assets/icons/baseline-notification_important-24px.js.map +1 -0
- package/assets/icons/baseline-security-24px.js +19 -0
- package/assets/icons/baseline-security-24px.js.map +1 -0
- package/assets/icons/file_download.js +19 -0
- package/assets/icons/file_download.js.map +1 -0
- package/assets/icons/file_download.svg +1 -0
- package/assets/icons/file_upload.js +19 -0
- package/assets/icons/file_upload.js.map +1 -0
- package/assets/icons/file_upload.svg +1 -0
- package/assets/icons/filter-24px.js +23 -0
- package/assets/icons/filter-24px.js.map +1 -0
- package/assets/icons/filter-24px.svg +1 -1
- package/assets/icons/github-brands.js +19 -0
- package/assets/icons/github-brands.js.map +1 -0
- package/assets/icons/highlight-24px.js +19 -0
- package/assets/icons/highlight-24px.js.map +1 -0
- package/assets/icons/highlight-24px.svg +1 -1
- package/assets/icons/icon-community.js +18 -0
- package/assets/icons/icon-community.js.map +1 -0
- package/assets/icons/icon-documentation.js +18 -0
- package/assets/icons/icon-documentation.js.map +1 -0
- package/assets/icons/info.js +19 -0
- package/assets/icons/info.js.map +1 -0
- package/assets/icons/info.svg +1 -1
- package/assets/icons/insert_drive_file-24px.js +19 -0
- package/assets/icons/insert_drive_file-24px.js.map +1 -0
- package/assets/icons/insert_drive_file-24px.svg +1 -1
- package/assets/icons/insert_photo-24px.js +19 -0
- package/assets/icons/insert_photo-24px.js.map +1 -0
- package/assets/icons/insert_photo-24px.svg +1 -1
- package/assets/icons/label-24px.js +19 -0
- package/assets/icons/label-24px.js.map +1 -0
- package/assets/icons/label-24px.svg +1 -1
- package/assets/icons/round-account_circle-24px.js +19 -0
- package/assets/icons/round-account_circle-24px.js.map +1 -0
- package/assets/icons/round-account_circle-24px.svg +1 -1
- package/assets/icons/round-add-24px.js +20 -0
- package/assets/icons/round-add-24px.js.map +1 -0
- package/assets/icons/round-arrow_drop_down-24px.js +19 -0
- package/assets/icons/round-arrow_drop_down-24px.js.map +1 -0
- package/assets/icons/round-arrow_drop_down-24px.svg +1 -1
- package/assets/icons/round-chevron_right-24px.js +20 -0
- package/assets/icons/round-chevron_right-24px.js.map +1 -0
- package/assets/icons/round-feedback-24px.js +20 -0
- package/assets/icons/round-feedback-24px.js.map +1 -0
- package/assets/icons/round-help-24px.js +19 -0
- package/assets/icons/round-help-24px.js.map +1 -0
- package/assets/icons/round-help-24px.svg +1 -1
- package/assets/icons/round-invert_colors-24px.js +20 -0
- package/assets/icons/round-invert_colors-24px.js.map +1 -0
- package/assets/icons/round-keyboard_arrow_down-24px.js +17 -0
- package/assets/icons/round-keyboard_arrow_down-24px.js.map +1 -0
- package/assets/icons/round-keyboard_arrow_up-24px.js +17 -0
- package/assets/icons/round-keyboard_arrow_up-24px.js.map +1 -0
- package/assets/icons/round-lock_open-24px.js +22 -0
- package/assets/icons/round-lock_open-24px.js.map +1 -0
- package/assets/icons/round-more_vert-24px.js +20 -0
- package/assets/icons/round-more_vert-24px.js.map +1 -0
- package/assets/icons/round-open_in_new-24px.js +20 -0
- package/assets/icons/round-open_in_new-24px.js.map +1 -0
- package/assets/icons/round-settings-24px.js +20 -0
- package/assets/icons/round-settings-24px.js.map +1 -0
- package/assets/icons/round-settings-24px.svg +1 -1
- package/assets/icons/search-24px.js +21 -0
- package/assets/icons/search-24px.js.map +1 -0
- package/assets/icons/slack-logo.js +14 -0
- package/assets/icons/slack-logo.js.map +1 -0
- package/assets/icons/today-24px.js +19 -0
- package/assets/icons/today-24px.js.map +1 -0
- package/assets/icons/today-24px.svg +1 -1
- package/assets/icons/touch_app.js +20 -0
- package/assets/icons/touch_app.js.map +1 -0
- package/assets/icons/touch_app.svg +1 -1
- package/assets/images/webiny-logo.js +34 -0
- package/assets/images/webiny-logo.js.map +1 -0
- package/assets/images/webiny-orange-logo.js +34 -0
- package/assets/images/webiny-orange-logo.js.map +1 -0
- package/base/Admin.d.ts +5 -2
- package/base/Admin.js +57 -53
- package/base/Admin.js.map +1 -1
- package/base/Base/DefaultFieldRenderers.d.ts +2 -0
- package/base/Base/DefaultFieldRenderers.js +102 -0
- package/base/Base/DefaultFieldRenderers.js.map +1 -0
- package/base/Base/DefaultLayoutRenderers.d.ts +2 -0
- package/base/Base/DefaultLayoutRenderers.js +14 -0
- package/base/Base/DefaultLayoutRenderers.js.map +1 -0
- package/base/Base/FieldRenderers/CheckboxesRenderer.d.ts +13 -0
- package/base/Base/FieldRenderers/CheckboxesRenderer.js +27 -0
- package/base/Base/FieldRenderers/CheckboxesRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/CodeEditorRenderer.d.ts +15 -0
- package/base/Base/FieldRenderers/CodeEditorRenderer.js +14 -0
- package/base/Base/FieldRenderers/CodeEditorRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/DateTimeInputsRenderer.d.ts +17 -0
- package/base/Base/FieldRenderers/DateTimeInputsRenderer.js +66 -0
- package/base/Base/FieldRenderers/DateTimeInputsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/DateTimeRenderer.d.ts +21 -0
- package/base/Base/FieldRenderers/DateTimeRenderer.js +45 -0
- package/base/Base/FieldRenderers/DateTimeRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/FilePickerRenderer.d.ts +13 -0
- package/base/Base/FieldRenderers/FilePickerRenderer.js +44 -0
- package/base/Base/FieldRenderers/FilePickerRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/FileUrlPickerRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/FileUrlPickerRenderer.js +20 -0
- package/base/Base/FieldRenderers/FileUrlPickerRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/HiddenRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/HiddenRenderer.js +5 -0
- package/base/Base/FieldRenderers/HiddenRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/HorizontalTabsRenderer.d.ts +5 -0
- package/base/Base/FieldRenderers/HorizontalTabsRenderer.js +26 -0
- package/base/Base/FieldRenderers/HorizontalTabsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/InputRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/InputRenderer.js +19 -0
- package/base/Base/FieldRenderers/InputRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/LexicalRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/LexicalRenderer.js +48 -0
- package/base/Base/FieldRenderers/LexicalRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/MultiFilePickerRenderer.d.ts +13 -0
- package/base/Base/FieldRenderers/MultiFilePickerRenderer.js +70 -0
- package/base/Base/FieldRenderers/MultiFilePickerRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/NumberInputRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/NumberInputRenderer.js +20 -0
- package/base/Base/FieldRenderers/NumberInputRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/NumberInputsRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/NumberInputsRenderer.js +50 -0
- package/base/Base/FieldRenderers/NumberInputsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js +17 -0
- package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js +59 -0
- package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.d.ts +10 -0
- package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.js +97 -0
- package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.d.ts +17 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js +48 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.d.ts +28 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js +69 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js +17 -0
- package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.d.ts +10 -0
- package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.js +58 -0
- package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.d.ts +10 -0
- package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.js +68 -0
- package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.js.map +1 -0
- package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.d.ts +4 -0
- package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js +19 -0
- package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js.map +1 -0
- package/base/Base/FieldRenderers/PassthroughRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/PassthroughRenderer.js +12 -0
- package/base/Base/FieldRenderers/PassthroughRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/RadioButtonsRenderer.d.ts +13 -0
- package/base/Base/FieldRenderers/RadioButtonsRenderer.js +26 -0
- package/base/Base/FieldRenderers/RadioButtonsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/SelectRenderer.d.ts +13 -0
- package/base/Base/FieldRenderers/SelectRenderer.js +28 -0
- package/base/Base/FieldRenderers/SelectRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/SwitchRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/SwitchRenderer.js +16 -0
- package/base/Base/FieldRenderers/SwitchRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/TagsRenderer.d.ts +12 -0
- package/base/Base/FieldRenderers/TagsRenderer.js +20 -0
- package/base/Base/FieldRenderers/TagsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/TextInputsRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/TextInputsRenderer.js +49 -0
- package/base/Base/FieldRenderers/TextInputsRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/TextareaRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/TextareaRenderer.js +20 -0
- package/base/Base/FieldRenderers/TextareaRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/TextareasRenderer.d.ts +14 -0
- package/base/Base/FieldRenderers/TextareasRenderer.js +52 -0
- package/base/Base/FieldRenderers/TextareasRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/VerticalTabsRenderer.d.ts +5 -0
- package/base/Base/FieldRenderers/VerticalTabsRenderer.js +40 -0
- package/base/Base/FieldRenderers/VerticalTabsRenderer.js.map +1 -0
- package/base/Base/LexicalPreset.d.ts +2 -0
- package/base/Base/LexicalPreset.js +63 -0
- package/base/Base/LexicalPreset.js.map +1 -0
- package/base/Base/Menus/SupportMenuItems.d.ts +2 -0
- package/base/Base/Menus/SupportMenuItems.js +14 -0
- package/base/Base/Menus/SupportMenuItems.js.map +1 -0
- package/base/Base/Menus/WebinyVersion.d.ts +2 -0
- package/base/Base/Menus/WebinyVersion.js +23 -0
- package/base/Base/Menus/WebinyVersion.js.map +1 -0
- package/base/Base/Menus.d.ts +2 -0
- package/base/Base/Menus.js +58 -0
- package/base/Base/Menus.js.map +1 -0
- package/base/Base/RoutesConfig.d.ts +2 -0
- package/base/Base/RoutesConfig.js +28 -0
- package/base/Base/RoutesConfig.js.map +1 -0
- package/base/Base/Tenant/wby-horizontal.js +33 -0
- package/base/Base/Tenant/wby-horizontal.js.map +1 -0
- package/base/Base/Tenant/wby-horizontal.svg +23 -0
- package/base/Base/Tenant/wby-square.js +20 -0
- package/base/Base/Tenant/wby-square.js.map +1 -0
- package/base/Base/Tenant/wby-square.svg +3 -0
- package/base/Base/Tenant.d.ts +2 -0
- package/base/Base/Tenant.js +22 -0
- package/base/Base/Tenant.js.map +1 -0
- package/base/Base/UserMenu/ExitTenant.d.ts +20 -0
- package/base/Base/UserMenu/ExitTenant.js +26 -0
- package/base/Base/UserMenu/ExitTenant.js.map +1 -0
- package/base/Base/UserMenu/SignOut.d.ts +2 -0
- package/base/Base/UserMenu/SignOut.js +18 -0
- package/base/Base/UserMenu/SignOut.js.map +1 -0
- package/base/Base/UserMenu/UserInfo.d.ts +6 -0
- package/base/Base/UserMenu/UserInfo.js +30 -0
- package/base/Base/UserMenu/UserInfo.js.map +1 -0
- package/base/Base/UserMenu.d.ts +2 -0
- package/base/Base/UserMenu.js +20 -0
- package/base/Base/UserMenu.js.map +1 -0
- package/base/Base.d.ts +1 -1
- package/base/Base.js +14 -110
- package/base/Base.js.map +1 -1
- package/base/TelemetryAdminAppStart.d.ts +1 -0
- package/base/TelemetryAdminAppStart.js +16 -0
- package/base/TelemetryAdminAppStart.js.map +1 -0
- package/base/WebinyVersion.d.ts +2 -0
- package/base/WebinyVersion.js +23 -0
- package/base/WebinyVersion.js.map +1 -0
- package/base/createRootContainer.d.ts +2 -0
- package/base/createRootContainer.js +63 -0
- package/base/createRootContainer.js.map +1 -0
- package/base/plugins/AddGraphQLQuerySelection.d.ts +2 -3
- package/base/plugins/AddGraphQLQuerySelection.js +17 -29
- package/base/plugins/AddGraphQLQuerySelection.js.map +1 -1
- package/base/providers/AdminUiStateProvider.d.ts +1 -0
- package/base/providers/AdminUiStateProvider.js +9 -0
- package/base/providers/AdminUiStateProvider.js.map +1 -0
- package/base/providers/ApolloProvider.d.ts +5 -2
- package/base/providers/ApolloProvider.js +10 -26
- package/base/providers/ApolloProvider.js.map +1 -1
- package/base/providers/UiProviders.d.ts +1 -0
- package/base/providers/UiProviders.js +29 -0
- package/base/providers/UiProviders.js.map +1 -0
- package/base/providers/UiStateProvider.d.ts +1 -2
- package/base/providers/UiStateProvider.js +9 -23
- package/base/providers/UiStateProvider.js.map +1 -1
- package/base/ui/Brand.d.ts +39 -2
- package/base/ui/Brand.js +6 -18
- package/base/ui/Brand.js.map +1 -1
- package/base/ui/CenteredView.d.ts +21 -1
- package/base/ui/CenteredView.js +11 -40
- package/base/ui/CenteredView.js.map +1 -1
- package/base/ui/Dashboard.d.ts +39 -2
- package/base/ui/Dashboard.js +6 -18
- package/base/ui/Dashboard.js.map +1 -1
- package/base/ui/FileManager.d.ts +75 -0
- package/base/ui/FileManager.js +47 -0
- package/base/ui/FileManager.js.map +1 -0
- package/base/ui/Layout.d.ts +40 -2
- package/base/ui/Layout.js +6 -23
- package/base/ui/Layout.js.map +1 -1
- package/base/ui/LoginScreen.d.ts +40 -2
- package/base/ui/LoginScreen.js +19 -25
- package/base/ui/LoginScreen.js.map +1 -1
- package/base/ui/Logo.d.ts +38 -7
- package/base/ui/Logo.js +6 -35
- package/base/ui/Logo.js.map +1 -1
- package/base/ui/Navigation.d.ts +39 -23
- package/base/ui/Navigation.js +13 -181
- package/base/ui/Navigation.js.map +1 -1
- package/base/ui/NotFound.d.ts +39 -2
- package/base/ui/NotFound.js +6 -18
- package/base/ui/NotFound.js.map +1 -1
- package/base/ui/Tags.d.ts +2 -2
- package/base/ui/Tags.js +13 -26
- package/base/ui/Tags.js.map +1 -1
- package/base/ui/TenantSelector.d.ts +39 -0
- package/base/ui/TenantSelector.js +7 -0
- package/base/ui/TenantSelector.js.map +1 -0
- package/base/ui/UserMenu/UserMenu.d.ts +39 -0
- package/base/ui/UserMenu/UserMenu.js +7 -0
- package/base/ui/UserMenu/UserMenu.js.map +1 -0
- package/base/ui/UserMenu/UserMenuHandle.d.ts +39 -0
- package/base/ui/UserMenu/UserMenuHandle.js +7 -0
- package/base/ui/UserMenu/UserMenuHandle.js.map +1 -0
- package/base/ui/UserMenu/UserMenuItem.d.ts +81 -0
- package/base/ui/UserMenu/UserMenuItem.js +9 -0
- package/base/ui/UserMenu/UserMenuItem.js.map +1 -0
- package/base/ui/UserMenu/UserMenuLink.d.ts +81 -0
- package/base/ui/UserMenu/UserMenuLink.js +9 -0
- package/base/ui/UserMenu/UserMenuLink.js.map +1 -0
- package/base/ui/UserMenu/UserMenuSeparator.d.ts +42 -0
- package/base/ui/UserMenu/UserMenuSeparator.js +7 -0
- package/base/ui/UserMenu/UserMenuSeparator.js.map +1 -0
- package/base/ui/UserMenu.d.ts +79 -29
- package/base/ui/UserMenu.js +12 -137
- package/base/ui/UserMenu.js.map +1 -1
- package/components/AdminLayout.d.ts +2 -1
- package/components/AdminLayout.js +8 -22
- package/components/AdminLayout.js.map +1 -1
- package/components/BulkActions/Worker.d.ts +82 -0
- package/components/BulkActions/Worker.js +63 -0
- package/components/BulkActions/Worker.js.map +1 -0
- package/components/BulkActions/index.d.ts +2 -0
- package/components/BulkActions/index.js +2 -0
- package/components/BulkActions/useDialogWithReport/DialogMessage.d.ts +5 -0
- package/components/BulkActions/useDialogWithReport/DialogMessage.js +19 -0
- package/components/BulkActions/useDialogWithReport/DialogMessage.js.map +1 -0
- package/components/BulkActions/useDialogWithReport/index.d.ts +1 -0
- package/components/BulkActions/useDialogWithReport/index.js +1 -0
- package/components/BulkActions/useDialogWithReport/useDialogWithReport.d.ts +19 -0
- package/components/BulkActions/useDialogWithReport/useDialogWithReport.js +40 -0
- package/components/BulkActions/useDialogWithReport/useDialogWithReport.js.map +1 -0
- package/components/Buttons/Buttons.d.ts +24 -0
- package/components/Buttons/Buttons.js +50 -0
- package/components/Buttons/Buttons.js.map +1 -0
- package/components/Buttons/Buttons.styles.d.ts +4 -0
- package/components/Buttons/Buttons.styles.js +11 -0
- package/components/Buttons/Buttons.styles.js.map +1 -0
- package/components/Buttons/index.d.ts +2 -0
- package/components/Buttons/index.js +2 -0
- package/components/Buttons/useButtons.d.ts +14 -0
- package/components/Buttons/useButtons.js +19 -0
- package/components/Buttons/useButtons.js.map +1 -0
- package/components/DeveloperMode/DeveloperMode.d.ts +11 -0
- package/components/DeveloperMode/DeveloperMode.js +8 -0
- package/components/DeveloperMode/DeveloperMode.js.map +1 -0
- package/components/DeveloperMode/index.d.ts +2 -0
- package/components/DeveloperMode/index.js +1 -0
- package/components/Dialogs/Dialog.d.ts +22 -0
- package/components/Dialogs/Dialog.js +50 -0
- package/components/Dialogs/Dialog.js.map +1 -0
- package/components/Dialogs/DialogParamsContext.d.ts +6 -0
- package/components/Dialogs/DialogParamsContext.js +10 -0
- package/components/Dialogs/DialogParamsContext.js.map +1 -0
- package/components/Dialogs/DialogsContext.d.ts +41 -0
- package/components/Dialogs/DialogsContext.js +142 -0
- package/components/Dialogs/DialogsContext.js.map +1 -0
- package/components/Dialogs/useDialogs.d.ts +2 -0
- package/components/Dialogs/useDialogs.js +10 -0
- package/components/Dialogs/useDialogs.js.map +1 -0
- package/components/EmptyView.d.ts +4 -4
- package/components/EmptyView.js +29 -70
- package/components/EmptyView.js.map +1 -1
- package/components/Filters/Filters.d.ts +16 -0
- package/components/Filters/Filters.js +20 -0
- package/components/Filters/Filters.js.map +1 -0
- package/components/Filters/FiltersToggle.d.ts +7 -0
- package/components/Filters/FiltersToggle.js +21 -0
- package/components/Filters/FiltersToggle.js.map +1 -0
- package/components/Filters/index.d.ts +2 -0
- package/components/Filters/index.js +2 -0
- package/components/FloatingActionButton.d.ts +4 -1
- package/components/FloatingActionButton.js +15 -28
- package/components/FloatingActionButton.js.map +1 -1
- package/components/IconPicker/IconPicker.d.ts +13 -0
- package/components/IconPicker/IconPicker.js +37 -0
- package/components/IconPicker/IconPicker.js.map +1 -0
- package/components/IconPicker/IconPickerComponent.d.ts +17 -0
- package/components/IconPicker/IconPickerComponent.js +65 -0
- package/components/IconPicker/IconPickerComponent.js.map +1 -0
- package/components/IconPicker/IconPickerPresenter.d.ts +53 -0
- package/components/IconPicker/IconPickerPresenter.js +71 -0
- package/components/IconPicker/IconPickerPresenter.js.map +1 -0
- package/components/IconPicker/IconPickerPresenter.test.d.ts +1 -0
- package/components/IconPicker/IconPickerPresenter.test.js +84 -0
- package/components/IconPicker/IconPickerPresenter.test.js.map +1 -0
- package/components/IconPicker/IconPickerPresenterProvider.d.ts +9 -0
- package/components/IconPicker/IconPickerPresenterProvider.js +13 -0
- package/components/IconPicker/IconPickerPresenterProvider.js.map +1 -0
- package/components/IconPicker/IconPickerTab.d.ts +33 -0
- package/components/IconPicker/IconPickerTab.js +116 -0
- package/components/IconPicker/IconPickerTab.js.map +1 -0
- package/components/IconPicker/IconRenderer.d.ts +34 -0
- package/components/IconPicker/IconRenderer.js +19 -0
- package/components/IconPicker/IconRenderer.js.map +1 -0
- package/components/IconPicker/IconRepository.d.ts +19 -0
- package/components/IconPicker/IconRepository.js +48 -0
- package/components/IconPicker/IconRepository.js.map +1 -0
- package/components/IconPicker/IconRepository.test.d.ts +1 -0
- package/components/IconPicker/IconRepository.test.js +60 -0
- package/components/IconPicker/IconRepository.test.js.map +1 -0
- package/components/IconPicker/IconRepositoryFactory.d.ts +9 -0
- package/components/IconPicker/IconRepositoryFactory.js +21 -0
- package/components/IconPicker/IconRepositoryFactory.js.map +1 -0
- package/components/IconPicker/Loading.d.ts +14 -0
- package/components/IconPicker/Loading.js +51 -0
- package/components/IconPicker/Loading.js.map +1 -0
- package/components/IconPicker/components/IconPickerCell.d.ts +12 -0
- package/components/IconPicker/components/IconPickerCell.js +25 -0
- package/components/IconPicker/components/IconPickerCell.js.map +1 -0
- package/components/IconPicker/components/IconPickerContent.d.ts +11 -0
- package/components/IconPicker/components/IconPickerContent.js +33 -0
- package/components/IconPicker/components/IconPickerContent.js.map +1 -0
- package/components/IconPicker/components/IconPickerRow.d.ts +4 -0
- package/components/IconPicker/components/IconPickerRow.js +9 -0
- package/components/IconPicker/components/IconPickerRow.js.map +1 -0
- package/components/IconPicker/components/IconPickerTrigger.d.ts +13 -0
- package/components/IconPicker/components/IconPickerTrigger.js +50 -0
- package/components/IconPicker/components/IconPickerTrigger.js.map +1 -0
- package/components/IconPicker/components/index.d.ts +4 -0
- package/components/IconPicker/components/index.js +4 -0
- package/components/IconPicker/config/Emojis.d.ts +2 -0
- package/components/IconPicker/config/Emojis.js +22 -0
- package/components/IconPicker/config/Emojis.js.map +1 -0
- package/components/IconPicker/config/FontAwesomeIcons.d.ts +2 -0
- package/components/IconPicker/config/FontAwesomeIcons.js +57 -0
- package/components/IconPicker/config/FontAwesomeIcons.js.map +1 -0
- package/components/IconPicker/config/IconPackProvider.d.ts +7 -0
- package/components/IconPicker/config/IconPackProvider.js +21 -0
- package/components/IconPicker/config/IconPackProvider.js.map +1 -0
- package/components/IconPicker/config/IconType.d.ts +32 -0
- package/components/IconPicker/config/IconType.js +57 -0
- package/components/IconPicker/config/IconType.js.map +1 -0
- package/components/IconPicker/config/index.d.ts +31 -0
- package/components/IconPicker/config/index.js +42 -0
- package/components/IconPicker/config/index.js.map +1 -0
- package/components/IconPicker/defaultIcon.d.ts +6 -0
- package/components/IconPicker/defaultIcon.js +9 -0
- package/components/IconPicker/defaultIcon.js.map +1 -0
- package/components/IconPicker/index.d.ts +7 -0
- package/components/IconPicker/index.js +6 -0
- package/components/IconPicker/plugins/customPlugin.d.ts +2 -0
- package/components/IconPicker/plugins/customPlugin.js +104 -0
- package/components/IconPicker/plugins/customPlugin.js.map +1 -0
- package/components/IconPicker/plugins/emojisPlugin.d.ts +2 -0
- package/components/IconPicker/plugins/emojisPlugin.js +102 -0
- package/components/IconPicker/plugins/emojisPlugin.js.map +1 -0
- package/components/IconPicker/plugins/graphql.d.ts +20 -0
- package/components/IconPicker/plugins/graphql.js +22 -0
- package/components/IconPicker/plugins/graphql.js.map +1 -0
- package/components/IconPicker/plugins/iconsPlugin.d.ts +2 -0
- package/components/IconPicker/plugins/iconsPlugin.js +84 -0
- package/components/IconPicker/plugins/iconsPlugin.js.map +1 -0
- package/components/IconPicker/types.d.ts +22 -0
- package/components/IconPicker/types.js +7 -0
- package/components/IconPicker/types.js.map +1 -0
- package/components/LexicalEditor/DefaultLexicalEditorConfig.d.ts +2 -0
- package/components/LexicalEditor/DefaultLexicalEditorConfig.js +81 -0
- package/components/LexicalEditor/DefaultLexicalEditorConfig.js.map +1 -0
- package/components/LexicalEditor/LexicalEditor.d.ts +12 -0
- package/components/LexicalEditor/LexicalEditor.js +48 -0
- package/components/LexicalEditor/LexicalEditor.js.map +1 -0
- package/components/LexicalEditor/LexicalLinkForm.d.ts +6 -0
- package/components/LexicalEditor/LexicalLinkForm.js +88 -0
- package/components/LexicalEditor/LexicalLinkForm.js.map +1 -0
- package/components/LexicalEditor/TypographyDropDown.d.ts +8 -0
- package/components/LexicalEditor/TypographyDropDown.js +60 -0
- package/components/LexicalEditor/TypographyDropDown.js.map +1 -0
- package/components/LexicalEditor/index.d.ts +3 -0
- package/components/LexicalEditor/index.js +3 -0
- package/components/LexicalEditor/lexicalValueFromHtml.d.ts +7 -0
- package/components/LexicalEditor/lexicalValueFromHtml.js +28 -0
- package/components/LexicalEditor/lexicalValueFromHtml.js.map +1 -0
- package/components/LexicalEditor/lexicalValueWithHtml.d.ts +7 -0
- package/components/LexicalEditor/lexicalValueWithHtml.js +30 -0
- package/components/LexicalEditor/lexicalValueWithHtml.js.map +1 -0
- package/components/MultiImageUpload.d.ts +1 -1
- package/components/MultiImageUpload.js +6 -42
- package/components/MultiImageUpload.js.map +1 -1
- package/components/NotAuthorizedError/NotAuthorizedError.d.ts +2 -0
- package/components/NotAuthorizedError/NotAuthorizedError.js +19 -0
- package/components/NotAuthorizedError/NotAuthorizedError.js.map +1 -0
- package/components/NotAuthorizedError/SecureRouteError.js +297 -0
- package/components/NotAuthorizedError/SecureRouteError.js.map +1 -0
- package/components/NotAuthorizedError/SecureRouteError.svg +1 -0
- package/components/NotAuthorizedError/index.d.ts +1 -0
- package/components/NotAuthorizedError/index.js +1 -0
- package/components/OptionsMenu/OptionsMenu.d.ts +16 -0
- package/components/OptionsMenu/OptionsMenu.js +24 -0
- package/components/OptionsMenu/OptionsMenu.js.map +1 -0
- package/components/OptionsMenu/OptionsMenuItem.d.ts +10 -0
- package/components/OptionsMenu/OptionsMenuItem.js +18 -0
- package/components/OptionsMenu/OptionsMenuItem.js.map +1 -0
- package/components/OptionsMenu/OptionsMenuLink.d.ts +12 -0
- package/components/OptionsMenu/OptionsMenuLink.js +16 -0
- package/components/OptionsMenu/OptionsMenuLink.js.map +1 -0
- package/components/OptionsMenu/index.d.ts +4 -0
- package/components/OptionsMenu/index.js +4 -0
- package/components/OptionsMenu/useOptionsMenuItem.d.ts +13 -0
- package/components/OptionsMenu/useOptionsMenuItem.js +18 -0
- package/components/OptionsMenu/useOptionsMenuItem.js.map +1 -0
- package/components/OverlayLayout/OverlayLayout.d.ts +6 -17
- package/components/OverlayLayout/OverlayLayout.js +39 -182
- package/components/OverlayLayout/OverlayLayout.js.map +1 -1
- package/components/OverlayLayout/components/OverlayBackdrop.d.ts +7 -0
- package/components/OverlayLayout/components/OverlayBackdrop.js +10 -0
- package/components/OverlayLayout/components/OverlayBackdrop.js.map +1 -0
- package/components/OverlayLayout/components/OverlayContent.d.ts +6 -0
- package/components/OverlayLayout/components/OverlayContent.js +22 -0
- package/components/OverlayLayout/components/OverlayContent.js.map +1 -0
- package/components/OverlayLayout/components/OverlayHeader.d.ts +13 -0
- package/components/OverlayLayout/components/OverlayHeader.js +37 -0
- package/components/OverlayLayout/components/OverlayHeader.js.map +1 -0
- package/components/OverlayLayout/components/OverlayRoot.d.ts +9 -0
- package/components/OverlayLayout/components/OverlayRoot.js +18 -0
- package/components/OverlayLayout/components/OverlayRoot.js.map +1 -0
- package/components/OverlayLayout/components/index.d.ts +4 -0
- package/components/OverlayLayout/components/index.js +4 -0
- package/components/OverlayLayout/index.d.ts +1 -1
- package/components/OverlayLayout/index.js +1 -18
- package/components/Permissions/CannotUseAaclAlert.d.ts +2 -0
- package/components/Permissions/CannotUseAaclAlert.js +13 -0
- package/components/Permissions/CannotUseAaclAlert.js.map +1 -0
- package/components/Permissions/Permissions.d.ts +2 -2
- package/components/Permissions/Permissions.js +37 -52
- package/components/Permissions/Permissions.js.map +1 -1
- package/components/Permissions/PermissionsGroup.d.ts +6 -0
- package/components/Permissions/PermissionsGroup.js +12 -0
- package/components/Permissions/PermissionsGroup.js.map +1 -0
- package/components/Permissions/StyledComponents.d.ts +2 -3
- package/components/Permissions/StyledComponents.js +13 -35
- package/components/Permissions/StyledComponents.js.map +1 -1
- package/components/Permissions/index.d.ts +4 -2
- package/components/Permissions/index.js +4 -30
- package/components/RegisterFeature.d.ts +11 -0
- package/components/RegisterFeature.js +19 -0
- package/components/RegisterFeature.js.map +1 -0
- package/components/ResizablePanels/index.d.ts +1 -0
- package/components/ResizablePanels/index.js +1 -0
- package/components/RoleAutocomplete/graphql.d.ts +1 -0
- package/components/RoleAutocomplete/graphql.js +19 -0
- package/components/RoleAutocomplete/graphql.js.map +1 -0
- package/components/RoleAutocomplete/index.d.ts +7 -0
- package/components/RoleAutocomplete/index.js +26 -0
- package/components/RoleAutocomplete/index.js.map +1 -0
- package/components/RolesMultiAutocomplete/graphql.d.ts +1 -0
- package/components/RolesMultiAutocomplete/graphql.js +24 -0
- package/components/RolesMultiAutocomplete/graphql.js.map +1 -0
- package/components/RolesMultiAutocomplete/index.d.ts +7 -0
- package/components/RolesMultiAutocomplete/index.js +26 -0
- package/components/RolesMultiAutocomplete/index.js.map +1 -0
- package/components/SearchUI.d.ts +3 -2
- package/components/SearchUI.js +21 -73
- package/components/SearchUI.js.map +1 -1
- package/components/SimpleForm/SimpleForm.d.ts +19 -8
- package/components/SimpleForm/SimpleForm.js +60 -104
- package/components/SimpleForm/SimpleForm.js.map +1 -1
- package/components/SimpleForm/index.d.ts +1 -1
- package/components/SimpleForm/index.js +1 -31
- package/components/SimpleUI/InputField.d.ts +3 -1
- package/components/SimpleUI/InputField.js +76 -113
- package/components/SimpleUI/InputField.js.map +1 -1
- package/components/SingleImageUpload.d.ts +21 -8
- package/components/SingleImageUpload.js +39 -112
- package/components/SingleImageUpload.js.map +1 -1
- package/components/SplitView/SplitView.d.ts +11 -9
- package/components/SplitView/SplitView.js +51 -91
- package/components/SplitView/SplitView.js.map +1 -1
- package/components/SplitView/index.d.ts +1 -1
- package/components/SplitView/index.js +1 -25
- package/components/TeamAutocomplete/graphql.d.ts +1 -0
- package/components/TeamAutocomplete/graphql.js +19 -0
- package/components/TeamAutocomplete/graphql.js.map +1 -0
- package/components/TeamAutocomplete/index.d.ts +7 -0
- package/components/TeamAutocomplete/index.js +26 -0
- package/components/TeamAutocomplete/index.js.map +1 -0
- package/components/TeamsMultiAutocomplete/graphql.d.ts +1 -0
- package/components/TeamsMultiAutocomplete/graphql.js +24 -0
- package/components/TeamsMultiAutocomplete/graphql.js.map +1 -0
- package/components/TeamsMultiAutocomplete/index.d.ts +7 -0
- package/components/TeamsMultiAutocomplete/index.js +26 -0
- package/components/TeamsMultiAutocomplete/index.js.map +1 -0
- package/components/Wcp.d.ts +19 -0
- package/components/Wcp.js +37 -0
- package/components/Wcp.js.map +1 -0
- package/components/index.d.ts +27 -1
- package/components/index.js +25 -13
- package/config/AdminConfig/Dashboard.d.ts +3 -0
- package/config/AdminConfig/Dashboard.js +7 -0
- package/config/AdminConfig/Dashboard.js.map +1 -0
- package/config/AdminConfig/Dialog.d.ts +10 -0
- package/config/AdminConfig/Dialog.js +19 -0
- package/config/AdminConfig/Dialog.js.map +1 -0
- package/config/AdminConfig/FieldRenderer.d.ts +11 -0
- package/config/AdminConfig/FieldRenderer.js +19 -0
- package/config/AdminConfig/FieldRenderer.js.map +1 -0
- package/config/AdminConfig/Form.d.ts +4 -0
- package/config/AdminConfig/Form.js +9 -0
- package/config/AdminConfig/Form.js.map +1 -0
- package/config/AdminConfig/LayoutRenderer.d.ts +10 -0
- package/config/AdminConfig/LayoutRenderer.js +19 -0
- package/config/AdminConfig/LayoutRenderer.js.map +1 -0
- package/config/AdminConfig/LexicalTheme/Color.d.ts +11 -0
- package/config/AdminConfig/LexicalTheme/Color.js +38 -0
- package/config/AdminConfig/LexicalTheme/Color.js.map +1 -0
- package/config/AdminConfig/LexicalTheme/Typography.d.ts +30 -0
- package/config/AdminConfig/LexicalTheme/Typography.js +66 -0
- package/config/AdminConfig/LexicalTheme/Typography.js.map +1 -0
- package/config/AdminConfig/LexicalTheme.d.ts +9 -0
- package/config/AdminConfig/LexicalTheme.js +9 -0
- package/config/AdminConfig/LexicalTheme.js.map +1 -0
- package/config/AdminConfig/Logo.d.ts +24 -0
- package/config/AdminConfig/Logo.js +19 -0
- package/config/AdminConfig/Logo.js.map +1 -0
- package/config/AdminConfig/Menu/MenuGroup.d.ts +62 -0
- package/config/AdminConfig/Menu/MenuGroup.js +12 -0
- package/config/AdminConfig/Menu/MenuGroup.js.map +1 -0
- package/config/AdminConfig/Menu/MenuItem.d.ts +43 -0
- package/config/AdminConfig/Menu/MenuItem.js +12 -0
- package/config/AdminConfig/Menu/MenuItem.js.map +1 -0
- package/config/AdminConfig/Menu/MenuLink.d.ts +43 -0
- package/config/AdminConfig/Menu/MenuLink.js +21 -0
- package/config/AdminConfig/Menu/MenuLink.js.map +1 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.d.ts +43 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.js +11 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.js.map +1 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.d.ts +24 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.js +11 -0
- package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.js.map +1 -0
- package/config/AdminConfig/Menu/SupportMenu.d.ts +72 -0
- package/config/AdminConfig/Menu/SupportMenu.js +38 -0
- package/config/AdminConfig/Menu/SupportMenu.js.map +1 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuItem.d.ts +40 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuItem.js +7 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuItem.js.map +1 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuLink.d.ts +40 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuLink.js +7 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuLink.js.map +1 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuSeparator.d.ts +1 -0
- package/config/AdminConfig/Menu/UserMenu/UserMenuSeparator.js +1 -0
- package/config/AdminConfig/Menu/UserMenu/types.d.ts +7 -0
- package/config/AdminConfig/Menu/UserMenu/types.js +0 -0
- package/config/AdminConfig/Menu/UserMenu.d.ts +129 -0
- package/config/AdminConfig/Menu/UserMenu.js +48 -0
- package/config/AdminConfig/Menu/UserMenu.js.map +1 -0
- package/config/AdminConfig/Menu/types.d.ts +7 -0
- package/config/AdminConfig/Menu/types.js +0 -0
- package/config/AdminConfig/Menu.d.ts +352 -0
- package/config/AdminConfig/Menu.js +69 -0
- package/config/AdminConfig/Menu.js.map +1 -0
- package/config/AdminConfig/Route.d.ts +3 -0
- package/config/AdminConfig/Route.js +7 -0
- package/config/AdminConfig/Route.js.map +1 -0
- package/config/AdminConfig/Security.d.ts +3 -0
- package/config/AdminConfig/Security.js +7 -0
- package/config/AdminConfig/Security.js.map +1 -0
- package/config/AdminConfig/SecurityPermissions.d.ts +17 -0
- package/config/AdminConfig/SecurityPermissions.js +24 -0
- package/config/AdminConfig/SecurityPermissions.js.map +1 -0
- package/config/AdminConfig/Tenant/TenantLogo.d.ts +24 -0
- package/config/AdminConfig/Tenant/TenantLogo.js +22 -0
- package/config/AdminConfig/Tenant/TenantLogo.js.map +1 -0
- package/config/AdminConfig/Tenant/TenantName.d.ts +23 -0
- package/config/AdminConfig/Tenant/TenantName.js +18 -0
- package/config/AdminConfig/Tenant/TenantName.js.map +1 -0
- package/config/AdminConfig/Tenant.d.ts +49 -0
- package/config/AdminConfig/Tenant.js +11 -0
- package/config/AdminConfig/Tenant.js.map +1 -0
- package/config/AdminConfig/Theme/assignColor.d.ts +2 -0
- package/config/AdminConfig/Theme/assignColor.js +55 -0
- package/config/AdminConfig/Theme/assignColor.js.map +1 -0
- package/config/AdminConfig/Theme/consts.d.ts +2 -0
- package/config/AdminConfig/Theme/consts.js +22 -0
- package/config/AdminConfig/Theme/consts.js.map +1 -0
- package/config/AdminConfig/Theme/types.d.ts +3 -0
- package/config/AdminConfig/Theme/types.js +0 -0
- package/config/AdminConfig/Theme.d.ts +16 -0
- package/config/AdminConfig/Theme.js +15 -0
- package/config/AdminConfig/Theme.js.map +1 -0
- package/config/AdminConfig/Title.d.ts +23 -0
- package/config/AdminConfig/Title.js +15 -0
- package/config/AdminConfig/Title.js.map +1 -0
- package/config/AdminConfig/Widget.d.ts +15 -0
- package/config/AdminConfig/Widget.js +29 -0
- package/config/AdminConfig/Widget.js.map +1 -0
- package/config/AdminConfig.d.ts +500 -0
- package/config/AdminConfig.js +69 -0
- package/config/AdminConfig.js.map +1 -0
- package/config/createAdminConfig.d.ts +57 -0
- package/config/createAdminConfig.js +63 -0
- package/config/createAdminConfig.js.map +1 -0
- package/css/tokens.css +379 -0
- package/css/typography.css +70 -0
- package/domain/Identity.d.ts +59 -0
- package/domain/Identity.js +86 -0
- package/domain/Identity.js.map +1 -0
- package/errors/ErrorOverlayNetworkErrorHandler.d.ts +11 -0
- package/errors/ErrorOverlayNetworkErrorHandler.js +51 -0
- package/errors/ErrorOverlayNetworkErrorHandler.js.map +1 -0
- package/errors/TenantIsDisabled.d.ts +2 -0
- package/errors/TenantIsDisabled.js +22 -0
- package/errors/TenantIsDisabled.js.map +1 -0
- package/exports/admin/build-params.d.ts +4 -0
- package/exports/admin/build-params.js +2 -0
- package/exports/admin/configs.d.ts +1 -0
- package/exports/admin/configs.js +1 -0
- package/exports/admin/form.d.ts +16 -0
- package/exports/admin/form.js +5 -0
- package/exports/admin/security.d.ts +14 -0
- package/exports/admin/security.js +9 -0
- package/exports/admin/tenancy.d.ts +2 -0
- package/exports/admin/tenancy.js +2 -0
- package/exports/admin/ui/file-manager.d.ts +3 -0
- package/exports/admin/ui/file-manager.js +2 -0
- package/exports/admin/ui/lexical.d.ts +1 -0
- package/exports/admin/ui/lexical.js +1 -0
- package/exports/admin/ui.d.ts +7 -0
- package/exports/admin/ui.js +6 -0
- package/exports/admin.d.ts +9 -0
- package/exports/admin.js +8 -0
- package/features/apolloClient/abstraction.d.ts +5 -0
- package/features/apolloClient/abstraction.js +5 -0
- package/features/apolloClient/abstraction.js.map +1 -0
- package/features/apolloClient/feature.d.ts +2 -0
- package/features/apolloClient/feature.js +31 -0
- package/features/apolloClient/feature.js.map +1 -0
- package/features/buildParams/BuildParams.d.ts +9 -0
- package/features/buildParams/BuildParams.js +24 -0
- package/features/buildParams/BuildParams.js.map +1 -0
- package/features/buildParams/abstractions.d.ts +15 -0
- package/features/buildParams/abstractions.js +6 -0
- package/features/buildParams/abstractions.js.map +1 -0
- package/features/buildParams/feature.d.ts +1 -0
- package/features/buildParams/feature.js +15 -0
- package/features/buildParams/feature.js.map +1 -0
- package/features/buildParams/index.d.ts +2 -0
- package/features/buildParams/index.js +2 -0
- package/features/fileUrlFormatter/abstractions.d.ts +6 -0
- package/features/fileUrlFormatter/abstractions.js +5 -0
- package/features/fileUrlFormatter/abstractions.js.map +1 -0
- package/features/formModel/ConditionRuleEvaluator.d.ts +9 -0
- package/features/formModel/ConditionRuleEvaluator.js +49 -0
- package/features/formModel/ConditionRuleEvaluator.js.map +1 -0
- package/features/formModel/Field.d.ts +99 -0
- package/features/formModel/Field.js +347 -0
- package/features/formModel/Field.js.map +1 -0
- package/features/formModel/FieldBuilder.d.ts +44 -0
- package/features/formModel/FieldBuilder.js +152 -0
- package/features/formModel/FieldBuilder.js.map +1 -0
- package/features/formModel/FieldBuilder.test.d.ts +1 -0
- package/features/formModel/FieldBuilder.test.js +236 -0
- package/features/formModel/FieldBuilder.test.js.map +1 -0
- package/features/formModel/FieldBuilderRegistry.d.ts +4 -0
- package/features/formModel/FieldBuilderRegistry.js +29 -0
- package/features/formModel/FieldBuilderRegistry.js.map +1 -0
- package/features/formModel/FocusManager.d.ts +14 -0
- package/features/formModel/FocusManager.js +83 -0
- package/features/formModel/FocusManager.js.map +1 -0
- package/features/formModel/FormErrors.d.ts +9 -0
- package/features/formModel/FormErrors.js +35 -0
- package/features/formModel/FormErrors.js.map +1 -0
- package/features/formModel/FormModel.d.ts +59 -0
- package/features/formModel/FormModel.js +410 -0
- package/features/formModel/FormModel.js.map +1 -0
- package/features/formModel/FormModel.test.d.ts +1 -0
- package/features/formModel/FormModel.test.js +3811 -0
- package/features/formModel/FormModel.test.js.map +1 -0
- package/features/formModel/FormModelFactory.d.ts +11 -0
- package/features/formModel/FormModelFactory.js +33 -0
- package/features/formModel/FormModelFactory.js.map +1 -0
- package/features/formModel/FormView.d.ts +46 -0
- package/features/formModel/FormView.js +187 -0
- package/features/formModel/FormView.js.map +1 -0
- package/features/formModel/LayoutBuilderFactory.d.ts +61 -0
- package/features/formModel/LayoutBuilderFactory.js +316 -0
- package/features/formModel/LayoutBuilderFactory.js.map +1 -0
- package/features/formModel/LayoutMutator.d.ts +11 -0
- package/features/formModel/LayoutMutator.js +129 -0
- package/features/formModel/LayoutMutator.js.map +1 -0
- package/features/formModel/LayoutResolver.d.ts +26 -0
- package/features/formModel/LayoutResolver.js +199 -0
- package/features/formModel/LayoutResolver.js.map +1 -0
- package/features/formModel/ObjectField.d.ts +97 -0
- package/features/formModel/ObjectField.js +563 -0
- package/features/formModel/ObjectField.js.map +1 -0
- package/features/formModel/PresenterErrors.d.ts +9 -0
- package/features/formModel/PresenterErrors.js +32 -0
- package/features/formModel/PresenterErrors.js.map +1 -0
- package/features/formModel/Rules.test.d.ts +1 -0
- package/features/formModel/Rules.test.js +324 -0
- package/features/formModel/Rules.test.js.map +1 -0
- package/features/formModel/abstractions.d.ts +715 -0
- package/features/formModel/abstractions.js +8 -0
- package/features/formModel/abstractions.js.map +1 -0
- package/features/formModel/createFieldRenderer.d.ts +20 -0
- package/features/formModel/createFieldRenderer.js +38 -0
- package/features/formModel/createFieldRenderer.js.map +1 -0
- package/features/formModel/demo/FieldRenderersDemoPresenter.d.ts +18 -0
- package/features/formModel/demo/FieldRenderersDemoPresenter.js +299 -0
- package/features/formModel/demo/FieldRenderersDemoPresenter.js.map +1 -0
- package/features/formModel/demo/FormModelDemo.d.ts +4 -0
- package/features/formModel/demo/FormModelDemo.js +206 -0
- package/features/formModel/demo/FormModelDemo.js.map +1 -0
- package/features/formModel/demo/FormModelDemoPresenter.d.ts +22 -0
- package/features/formModel/demo/FormModelDemoPresenter.js +140 -0
- package/features/formModel/demo/FormModelDemoPresenter.js.map +1 -0
- package/features/formModel/demo/FormModelPhase11Presenter.d.ts +25 -0
- package/features/formModel/demo/FormModelPhase11Presenter.js +107 -0
- package/features/formModel/demo/FormModelPhase11Presenter.js.map +1 -0
- package/features/formModel/demo/FormModelPhase8c1Presenter.d.ts +23 -0
- package/features/formModel/demo/FormModelPhase8c1Presenter.js +69 -0
- package/features/formModel/demo/FormModelPhase8c1Presenter.js.map +1 -0
- package/features/formModel/feature.d.ts +3 -0
- package/features/formModel/feature.js +30 -0
- package/features/formModel/feature.js.map +1 -0
- package/features/formModel/fieldTypes/BooleanFieldType.d.ts +19 -0
- package/features/formModel/fieldTypes/BooleanFieldType.js +26 -0
- package/features/formModel/fieldTypes/BooleanFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +173 -0
- package/features/formModel/fieldTypes/DateTimeFieldType.js +246 -0
- package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/FileFieldType.d.ts +29 -0
- package/features/formModel/fieldTypes/FileFieldType.js +56 -0
- package/features/formModel/fieldTypes/FileFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/FileUrlFieldType.d.ts +18 -0
- package/features/formModel/fieldTypes/FileUrlFieldType.js +23 -0
- package/features/formModel/fieldTypes/FileUrlFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/LexicalFieldType.d.ts +19 -0
- package/features/formModel/fieldTypes/LexicalFieldType.js +23 -0
- package/features/formModel/fieldTypes/LexicalFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/NumberFieldType.d.ts +19 -0
- package/features/formModel/fieldTypes/NumberFieldType.js +28 -0
- package/features/formModel/fieldTypes/NumberFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/ObjectFieldType.d.ts +34 -0
- package/features/formModel/fieldTypes/ObjectFieldType.js +95 -0
- package/features/formModel/fieldTypes/ObjectFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/TextFieldType.d.ts +18 -0
- package/features/formModel/fieldTypes/TextFieldType.js +23 -0
- package/features/formModel/fieldTypes/TextFieldType.js.map +1 -0
- package/features/formModel/fieldTypes/index.d.ts +8 -0
- package/features/formModel/fieldTypes/index.js +8 -0
- package/features/formModel/index.d.ts +24 -0
- package/features/formModel/index.js +22 -0
- package/features/formModel/renderers.d.ts +21 -0
- package/features/formModel/renderers.js +21 -0
- package/features/formModel/useFieldRenderers.d.ts +2 -0
- package/features/formModel/useFieldRenderers.js +15 -0
- package/features/formModel/useFieldRenderers.js.map +1 -0
- package/features/formModel/useLayoutRenderers.d.ts +2 -0
- package/features/formModel/useLayoutRenderers.js +15 -0
- package/features/formModel/useLayoutRenderers.js.map +1 -0
- package/features/listCache/ListCache.d.ts +28 -0
- package/features/listCache/ListCache.js +48 -0
- package/features/listCache/ListCache.js.map +1 -0
- package/features/listCache/index.d.ts +2 -0
- package/features/listCache/index.js +1 -0
- package/features/security/AuthenticationContext/AuthenticationContext.d.ts +15 -0
- package/features/security/AuthenticationContext/AuthenticationContext.js +32 -0
- package/features/security/AuthenticationContext/AuthenticationContext.js.map +1 -0
- package/features/security/AuthenticationContext/GraphQLClientDecorator.d.ts +12 -0
- package/features/security/AuthenticationContext/GraphQLClientDecorator.js +31 -0
- package/features/security/AuthenticationContext/GraphQLClientDecorator.js.map +1 -0
- package/features/security/AuthenticationContext/InternalIdTokenProvider.d.ts +10 -0
- package/features/security/AuthenticationContext/InternalIdTokenProvider.js +20 -0
- package/features/security/AuthenticationContext/InternalIdTokenProvider.js.map +1 -0
- package/features/security/AuthenticationContext/abstractions.d.ts +26 -0
- package/features/security/AuthenticationContext/abstractions.js +6 -0
- package/features/security/AuthenticationContext/abstractions.js.map +1 -0
- package/features/security/AuthenticationContext/feature.d.ts +3 -0
- package/features/security/AuthenticationContext/feature.js +21 -0
- package/features/security/AuthenticationContext/feature.js.map +1 -0
- package/features/security/AuthenticationContext/index.d.ts +1 -0
- package/features/security/AuthenticationContext/index.js +1 -0
- package/features/security/AuthenticationContext/types.d.ts +11 -0
- package/features/security/AuthenticationContext/types.js +0 -0
- package/features/security/IdentityContext/IdentityContext.d.ts +13 -0
- package/features/security/IdentityContext/IdentityContext.js +25 -0
- package/features/security/IdentityContext/IdentityContext.js.map +1 -0
- package/features/security/IdentityContext/abstractions.d.ts +10 -0
- package/features/security/IdentityContext/abstractions.js +5 -0
- package/features/security/IdentityContext/abstractions.js.map +1 -0
- package/features/security/IdentityContext/feature.d.ts +3 -0
- package/features/security/IdentityContext/feature.js +17 -0
- package/features/security/IdentityContext/feature.js.map +1 -0
- package/features/security/IdentityContext/index.d.ts +2 -0
- package/features/security/IdentityContext/index.js +2 -0
- package/features/security/LogIn/IdentityMapper.d.ts +9 -0
- package/features/security/LogIn/IdentityMapper.js +14 -0
- package/features/security/LogIn/IdentityMapper.js.map +1 -0
- package/features/security/LogIn/LogInGateway.d.ts +11 -0
- package/features/security/LogIn/LogInGateway.js +25 -0
- package/features/security/LogIn/LogInGateway.js.map +1 -0
- package/features/security/LogIn/LogInRepository.d.ts +14 -0
- package/features/security/LogIn/LogInRepository.js +21 -0
- package/features/security/LogIn/LogInRepository.js.map +1 -0
- package/features/security/LogIn/LogInUseCase.d.ts +15 -0
- package/features/security/LogIn/LogInUseCase.js +32 -0
- package/features/security/LogIn/LogInUseCase.js.map +1 -0
- package/features/security/LogIn/abstractions.d.ts +48 -0
- package/features/security/LogIn/abstractions.js +8 -0
- package/features/security/LogIn/abstractions.js.map +1 -0
- package/features/security/LogIn/createLoginMutation.d.ts +1 -0
- package/features/security/LogIn/createLoginMutation.js +48 -0
- package/features/security/LogIn/createLoginMutation.js.map +1 -0
- package/features/security/LogIn/feature.d.ts +3 -0
- package/features/security/LogIn/feature.js +23 -0
- package/features/security/LogIn/feature.js.map +1 -0
- package/features/security/LogIn/index.d.ts +1 -0
- package/features/security/LogIn/index.js +1 -0
- package/features/security/LogOut/LogOutUseCase.d.ts +13 -0
- package/features/security/LogOut/LogOutUseCase.js +25 -0
- package/features/security/LogOut/LogOutUseCase.js.map +1 -0
- package/features/security/LogOut/abstractions.d.ts +7 -0
- package/features/security/LogOut/abstractions.js +5 -0
- package/features/security/LogOut/abstractions.js.map +1 -0
- package/features/security/LogOut/feature.d.ts +3 -0
- package/features/security/LogOut/feature.js +17 -0
- package/features/security/LogOut/feature.js.map +1 -0
- package/features/security/LogOut/index.d.ts +2 -0
- package/features/security/LogOut/index.js +2 -0
- package/features/security/SecurityFeature.d.ts +1 -0
- package/features/security/SecurityFeature.js +17 -0
- package/features/security/SecurityFeature.js.map +1 -0
- package/features/telemetry/TelemetryService.d.ts +6 -0
- package/features/telemetry/TelemetryService.js +19 -0
- package/features/telemetry/TelemetryService.js.map +1 -0
- package/features/telemetry/abstractions.d.ts +7 -0
- package/features/telemetry/abstractions.js +5 -0
- package/features/telemetry/abstractions.js.map +1 -0
- package/features/telemetry/feature.d.ts +3 -0
- package/features/telemetry/feature.js +17 -0
- package/features/telemetry/feature.js.map +1 -0
- package/features/telemetry/index.d.ts +2 -0
- package/features/telemetry/index.js +2 -0
- package/features/tenancy/GraphQLClientDecorator.d.ts +12 -0
- package/features/tenancy/GraphQLClientDecorator.js +30 -0
- package/features/tenancy/GraphQLClientDecorator.js.map +1 -0
- package/features/tenancy/TenantContext.d.ts +17 -0
- package/features/tenancy/TenantContext.js +57 -0
- package/features/tenancy/TenantContext.js.map +1 -0
- package/features/tenancy/abstractions.d.ts +14 -0
- package/features/tenancy/abstractions.js +5 -0
- package/features/tenancy/abstractions.js.map +1 -0
- package/features/tenancy/feature.d.ts +5 -0
- package/features/tenancy/feature.js +31 -0
- package/features/tenancy/feature.js.map +1 -0
- package/features/tenancy/types.d.ts +4 -0
- package/features/tenancy/types.js +0 -0
- package/features/tools/LexicalContext/LexicalContext.d.ts +14 -0
- package/features/tools/LexicalContext/LexicalContext.js +25 -0
- package/features/tools/LexicalContext/LexicalContext.js.map +1 -0
- package/features/tools/LexicalContext/abstractions.d.ts +11 -0
- package/features/tools/LexicalContext/abstractions.js +5 -0
- package/features/tools/LexicalContext/abstractions.js.map +1 -0
- package/features/tools/LexicalContext/index.d.ts +2 -0
- package/features/tools/LexicalContext/index.js +1 -0
- package/features/tools/ToolPipelineRunner.d.ts +10 -0
- package/features/tools/ToolPipelineRunner.js +35 -0
- package/features/tools/ToolPipelineRunner.js.map +1 -0
- package/features/tools/ToolRegistry.d.ts +12 -0
- package/features/tools/ToolRegistry.js +38 -0
- package/features/tools/ToolRegistry.js.map +1 -0
- package/features/tools/abstractions.d.ts +28 -0
- package/features/tools/abstractions.js +7 -0
- package/features/tools/abstractions.js.map +1 -0
- package/features/tools/feature.d.ts +4 -0
- package/features/tools/feature.js +22 -0
- package/features/tools/feature.js.map +1 -0
- package/features/tools/index.d.ts +3 -0
- package/features/tools/index.js +3 -0
- package/features/wcp/ReactLicense.d.ts +22 -0
- package/features/wcp/ReactLicense.js +56 -0
- package/features/wcp/ReactLicense.js.map +1 -0
- package/features/wcp/WcpGateway.d.ts +10 -0
- package/features/wcp/WcpGateway.js +78 -0
- package/features/wcp/WcpGateway.js.map +1 -0
- package/features/wcp/WcpService.d.ts +20 -0
- package/features/wcp/WcpService.js +74 -0
- package/features/wcp/WcpService.js.map +1 -0
- package/features/wcp/abstractions.d.ts +19 -0
- package/features/wcp/abstractions.js +6 -0
- package/features/wcp/abstractions.js.map +1 -0
- package/features/wcp/feature.d.ts +3 -0
- package/features/wcp/feature.js +19 -0
- package/features/wcp/feature.js.map +1 -0
- package/features/wcp/types.d.ts +21 -0
- package/features/wcp/types.js +0 -0
- package/features/webinySdk/WebinySdk.d.ts +11 -0
- package/features/webinySdk/WebinySdk.js +28 -0
- package/features/webinySdk/WebinySdk.js.map +1 -0
- package/features/webinySdk/abstractions.d.ts +6 -0
- package/features/webinySdk/abstractions.js +5 -0
- package/features/webinySdk/abstractions.js.map +1 -0
- package/features/webinySdk/feature.d.ts +3 -0
- package/features/webinySdk/feature.js +17 -0
- package/features/webinySdk/feature.js.map +1 -0
- package/features/webinySdk/index.d.ts +2 -0
- package/features/webinySdk/index.js +2 -0
- package/hooks/index.d.ts +13 -0
- package/hooks/index.js +13 -0
- package/hooks/useConfirmationDialog.d.ts +7 -3
- package/hooks/useConfirmationDialog.js +36 -57
- package/hooks/useConfirmationDialog.js.map +1 -1
- package/hooks/useDialog.d.ts +9 -10
- package/hooks/useDialog.js +18 -36
- package/hooks/useDialog.js.map +1 -1
- package/hooks/useHotkeys.d.ts +9 -0
- package/hooks/useHotkeys.js +66 -0
- package/hooks/useHotkeys.js.map +1 -0
- package/hooks/useIsMounted.d.ts +3 -0
- package/hooks/useIsMounted.js +16 -0
- package/hooks/useIsMounted.js.map +1 -0
- package/hooks/useKeyHandler.d.ts +8 -0
- package/hooks/useKeyHandler.js +56 -0
- package/hooks/useKeyHandler.js.map +1 -0
- package/hooks/useModKey.d.ts +1 -0
- package/hooks/useModKey.js +24 -0
- package/hooks/useModKey.js.map +1 -0
- package/hooks/useOpenDialog.d.ts +7 -0
- package/hooks/useOpenDialog.js +18 -0
- package/hooks/useOpenDialog.js.map +1 -0
- package/hooks/useShiftKey.d.ts +5 -0
- package/hooks/useShiftKey.js +42 -0
- package/hooks/useShiftKey.js.map +1 -0
- package/hooks/useSnackbar.d.ts +4 -0
- package/hooks/useSnackbar.js +28 -36
- package/hooks/useSnackbar.js.map +1 -1
- package/hooks/useStateIfMounted.d.ts +1 -0
- package/hooks/useStateIfMounted.js +18 -0
- package/hooks/useStateIfMounted.js.map +1 -0
- package/hooks/useStateWithCallback.d.ts +5 -0
- package/hooks/useStateWithCallback.js +22 -0
- package/hooks/useStateWithCallback.js.map +1 -0
- package/hooks/useToggler.d.ts +7 -0
- package/hooks/useToggler.js +16 -0
- package/hooks/useToggler.js.map +1 -0
- package/index.d.ts +58 -35
- package/index.js +47 -373
- package/lexical.css +2 -0
- package/package.json +78 -68
- package/permissions/PermissionRenderer.d.ts +6 -0
- package/permissions/PermissionRenderer.js +205 -0
- package/permissions/PermissionRenderer.js.map +1 -0
- package/permissions/PermissionValueContext.d.ts +11 -0
- package/permissions/PermissionValueContext.js +16 -0
- package/permissions/PermissionValueContext.js.map +1 -0
- package/permissions/createHasPermission.d.ts +4 -0
- package/permissions/createHasPermission.js +39 -0
- package/permissions/createHasPermission.js.map +1 -0
- package/permissions/createPermissionSchema.d.ts +2 -0
- package/permissions/createPermissionSchema.js +9 -0
- package/permissions/createPermissionSchema.js.map +1 -0
- package/permissions/createPermissions.d.ts +6 -0
- package/permissions/createPermissions.js +141 -0
- package/permissions/createPermissions.js.map +1 -0
- package/permissions/createPermissions.test.d.ts +1 -0
- package/permissions/createPermissions.test.js +209 -0
- package/permissions/createPermissions.test.js.map +1 -0
- package/permissions/index.d.ts +9 -0
- package/permissions/index.js +7 -0
- package/permissions/types.d.ts +267 -0
- package/permissions/types.js +0 -0
- package/permissions/usePermissionForm.d.ts +17 -0
- package/permissions/usePermissionForm.js +157 -0
- package/permissions/usePermissionForm.js.map +1 -0
- package/permissions/usePermissions.d.ts +4 -0
- package/permissions/usePermissions.js +10 -0
- package/permissions/usePermissions.js.map +1 -0
- package/plugins/MenuPlugin.d.ts +1 -1
- package/plugins/MenuPlugin.js +17 -55
- package/plugins/MenuPlugin.js.map +1 -1
- package/presentation/browserFilePicker/BrowserFilePicker.d.ts +3 -0
- package/presentation/browserFilePicker/BrowserFilePicker.js +88 -0
- package/presentation/browserFilePicker/BrowserFilePicker.js.map +1 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.d.ts +10 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.js +84 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.js.map +1 -0
- package/presentation/browserFilePicker/index.d.ts +3 -0
- package/presentation/browserFilePicker/index.js +2 -0
- package/presentation/browserFilePicker/types.d.ts +53 -0
- package/presentation/browserFilePicker/types.js +0 -0
- package/presentation/browserFilePicker/utils.d.ts +2 -0
- package/presentation/browserFilePicker/utils.js +16 -0
- package/presentation/browserFilePicker/utils.js.map +1 -0
- package/presentation/buildParams/useBuildParams.d.ts +1 -0
- package/presentation/buildParams/useBuildParams.js +6 -0
- package/presentation/buildParams/useBuildParams.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/SystemInstaller.d.ts +8 -0
- package/presentation/installation/components/SystemInstaller/SystemInstaller.js +63 -0
- package/presentation/installation/components/SystemInstaller/SystemInstaller.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.d.ts +2 -0
- package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.js +12 -0
- package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/index.d.ts +1 -0
- package/presentation/installation/components/SystemInstaller/index.js +1 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.d.ts +8 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.js +17 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.d.ts +1 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.js +16 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.d.ts +7 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.js +69 -0
- package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.d.ts +7 -0
- package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.js +85 -0
- package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/Center.d.ts +4 -0
- package/presentation/installation/components/SystemInstaller/steps/Center.js +7 -0
- package/presentation/installation/components/SystemInstaller/steps/Center.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/Container.d.ts +8 -0
- package/presentation/installation/components/SystemInstaller/steps/Container.js +22 -0
- package/presentation/installation/components/SystemInstaller/steps/Container.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/FinishSetup.d.ts +11 -0
- package/presentation/installation/components/SystemInstaller/steps/FinishSetup.js +53 -0
- package/presentation/installation/components/SystemInstaller/steps/FinishSetup.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.d.ts +7 -0
- package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.js +18 -0
- package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/introduction.js +723 -0
- package/presentation/installation/components/SystemInstaller/steps/introduction.js.map +1 -0
- package/presentation/installation/components/SystemInstaller/steps/introduction.svg +154 -0
- package/presentation/installation/components/SystemInstaller/steps/referralSources.d.ts +1 -0
- package/presentation/installation/components/SystemInstaller/steps/referralSources.js +16 -0
- package/presentation/installation/components/SystemInstaller/steps/referralSources.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.d.ts +10 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.js +62 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.d.ts +24 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.js +131 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.d.ts +11 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.js +32 -0
- package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/abstractions.d.ts +59 -0
- package/presentation/installation/presenters/SystemInstaller/abstractions.js +7 -0
- package/presentation/installation/presenters/SystemInstaller/abstractions.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/feature.d.ts +3 -0
- package/presentation/installation/presenters/SystemInstaller/feature.js +21 -0
- package/presentation/installation/presenters/SystemInstaller/feature.js.map +1 -0
- package/presentation/installation/presenters/SystemInstaller/index.d.ts +3 -0
- package/presentation/installation/presenters/SystemInstaller/index.js +2 -0
- package/presentation/lexicalContext/useLexicalContext.d.ts +3 -0
- package/presentation/lexicalContext/useLexicalContext.js +17 -0
- package/presentation/lexicalContext/useLexicalContext.js.map +1 -0
- package/presentation/listPresenter/ListPresenter.d.ts +30 -0
- package/presentation/listPresenter/ListPresenter.js +216 -0
- package/presentation/listPresenter/ListPresenter.js.map +1 -0
- package/presentation/listPresenter/ListPresenter.test.d.ts +1 -0
- package/presentation/listPresenter/ListPresenter.test.js +816 -0
- package/presentation/listPresenter/ListPresenter.test.js.map +1 -0
- package/presentation/listPresenter/SelectionController.d.ts +21 -0
- package/presentation/listPresenter/SelectionController.js +74 -0
- package/presentation/listPresenter/SelectionController.js.map +1 -0
- package/presentation/listPresenter/abstractions.d.ts +107 -0
- package/presentation/listPresenter/abstractions.js +5 -0
- package/presentation/listPresenter/abstractions.js.map +1 -0
- package/presentation/listPresenter/feature.d.ts +3 -0
- package/presentation/listPresenter/feature.js +17 -0
- package/presentation/listPresenter/feature.js.map +1 -0
- package/presentation/listPresenter/index.d.ts +3 -0
- package/presentation/listPresenter/index.js +2 -0
- package/presentation/security/components/HasPermission.d.ts +10 -0
- package/presentation/security/components/HasPermission.js +21 -0
- package/presentation/security/components/HasPermission.js.map +1 -0
- package/presentation/security/components/SecureRoute.d.ts +7 -0
- package/presentation/security/components/SecureRoute.js +10 -0
- package/presentation/security/components/SecureRoute.js.map +1 -0
- package/presentation/security/hooks/useAuthentication.d.ts +9 -0
- package/presentation/security/hooks/useAuthentication.js +18 -0
- package/presentation/security/hooks/useAuthentication.js.map +1 -0
- package/presentation/security/hooks/useIdentity.d.ts +6 -0
- package/presentation/security/hooks/useIdentity.js +26 -0
- package/presentation/security/hooks/useIdentity.js.map +1 -0
- package/presentation/security/hooks/useSecurity.d.ts +10 -0
- package/presentation/security/hooks/useSecurity.js +18 -0
- package/presentation/security/hooks/useSecurity.js.map +1 -0
- package/presentation/tenancy/TenancyProvider.d.ts +6 -0
- package/presentation/tenancy/TenancyProvider.js +30 -0
- package/presentation/tenancy/TenancyProvider.js.map +1 -0
- package/presentation/tenancy/createTenancyProvider.d.ts +1 -0
- package/presentation/tenancy/createTenancyProvider.js +9 -0
- package/presentation/tenancy/createTenancyProvider.js.map +1 -0
- package/presentation/tenancy/useTenantContext.d.ts +5 -0
- package/presentation/tenancy/useTenantContext.js +23 -0
- package/presentation/tenancy/useTenantContext.js.map +1 -0
- package/presentation/textToLexicalTool/TextToLexicalTool.d.ts +30 -0
- package/presentation/textToLexicalTool/TextToLexicalTool.js +31 -0
- package/presentation/textToLexicalTool/TextToLexicalTool.js.map +1 -0
- package/presentation/textToLexicalTool/feature.d.ts +1 -0
- package/presentation/textToLexicalTool/feature.js +11 -0
- package/presentation/textToLexicalTool/feature.js.map +1 -0
- package/presentation/textToLexicalTool/textToLexicalState.d.ts +6 -0
- package/presentation/textToLexicalTool/textToLexicalState.js +31 -0
- package/presentation/textToLexicalTool/textToLexicalState.js.map +1 -0
- package/presentation/wcp/WcpProvider.d.ts +7 -0
- package/presentation/wcp/WcpProvider.js +23 -0
- package/presentation/wcp/WcpProvider.js.map +1 -0
- package/presentation/wcp/useWcp.d.ts +2 -0
- package/presentation/wcp/useWcp.js +17 -0
- package/presentation/wcp/useWcp.js.map +1 -0
- package/routes.d.ts +6 -0
- package/routes.js +18 -0
- package/routes.js.map +1 -0
- package/static/svg/SecureRouteError.e8b15981.svg +1 -0
- package/static/svg/add-18px.b3062af6.svg +3 -0
- package/static/svg/arrow_drop_down-24px.da74d713.svg +4 -0
- package/static/svg/attach_file_black_24dp.7fb13a7e.svg +1 -0
- package/static/svg/baseline-menu-24px.071b179d.svg +4 -0
- package/static/svg/baseline-notification_important-24px.29277e73.svg +4 -0
- package/static/svg/baseline-security-24px.12e519cc.svg +4 -0
- package/static/svg/file_download.afe3fbec.svg +1 -0
- package/static/svg/file_upload.6865d820.svg +1 -0
- package/static/svg/filter-24px.886d9ff0.svg +8 -0
- package/static/svg/github-brands.6311f0fa.svg +1 -0
- package/static/svg/highlight-24px.959a97f7.svg +1 -0
- package/static/svg/icon-community.cd087355.svg +16 -0
- package/static/svg/icon-documentation.a189d24c.svg +16 -0
- package/static/svg/info.fe810b72.svg +1 -0
- package/static/svg/insert_drive_file-24px.39d490eb.svg +1 -0
- package/static/svg/insert_photo-24px.81a5f945.svg +1 -0
- package/static/svg/introduction.108720aa.svg +154 -0
- package/static/svg/label-24px.9deafa4f.svg +1 -0
- package/static/svg/round-account_circle-24px.f0b48cb7.svg +1 -0
- package/static/svg/round-add-24px.4a5219b4.svg +16 -0
- package/static/svg/round-arrow_drop_down-24px.5fdf92d1.svg +1 -0
- package/static/svg/round-chevron_right-24px.34936511.svg +12 -0
- package/static/svg/round-feedback-24px.4dae7231.svg +54 -0
- package/static/svg/round-help-24px.a61b1d66.svg +1 -0
- package/static/svg/round-invert_colors-24px.a229a1dd.svg +52 -0
- package/static/svg/round-keyboard_arrow_down-24px.6febe804.svg +16 -0
- package/static/svg/round-keyboard_arrow_up-24px.b1f7db34.svg +16 -0
- package/static/svg/round-lock_open-24px.d527f6a5.svg +60 -0
- package/static/svg/round-more_vert-24px.68d41b07.svg +12 -0
- package/static/svg/round-open_in_new-24px.be5b4ddd.svg +44 -0
- package/static/svg/round-settings-24px.c0b3056f.svg +4 -0
- package/static/svg/search-24px.3c0f88dd.svg +20 -0
- package/static/svg/slack-logo.7ee35e7f.svg +1 -0
- package/static/svg/today-24px.df206362.svg +1 -0
- package/static/svg/touch_app.debf3744.svg +1 -0
- package/static/svg/wby-horizontal.03327bf6.svg +23 -0
- package/static/svg/wby-square.ff8f47c3.svg +3 -0
- package/static/svg/webiny-logo.ef56725c.svg +20 -0
- package/static/svg/webiny-orange-logo.4eccf0f2.svg +20 -0
- package/styles.scss +7 -2
- package/types.d.ts +8 -92
- package/types.js +0 -5
- package/assets/images/icons.png +0 -0
- package/assets/images/icons_retina.png +0 -0
- package/assets/images/swich.png +0 -0
- package/base/providers/TelemetryProvider.d.ts +0 -2
- package/base/providers/TelemetryProvider.js +0 -37
- package/base/providers/TelemetryProvider.js.map +0 -1
- package/base/providers/ViewCompositionProvider.d.ts +0 -17
- package/base/providers/ViewCompositionProvider.js +0 -70
- package/base/providers/ViewCompositionProvider.js.map +0 -1
- package/base/ui/LocaleSelector.d.ts +0 -2
- package/base/ui/LocaleSelector.js +0 -19
- package/base/ui/LocaleSelector.js.map +0 -1
- package/base/ui/Menu.d.ts +0 -31
- package/base/ui/Menu.js +0 -166
- package/base/ui/Menu.js.map +0 -1
- package/base/ui/Search.d.ts +0 -19
- package/base/ui/Search.js +0 -81
- package/base/ui/Search.js.map +0 -1
- package/components/AppInstaller/AppInstaller.d.ts +0 -7
- package/components/AppInstaller/AppInstaller.js +0 -183
- package/components/AppInstaller/AppInstaller.js.map +0 -1
- package/components/AppInstaller/Sidebar.d.ts +0 -9
- package/components/AppInstaller/Sidebar.js +0 -178
- package/components/AppInstaller/Sidebar.js.map +0 -1
- package/components/AppInstaller/assets/sign-in-divider.svg +0 -19
- package/components/AppInstaller/index.d.ts +0 -2
- package/components/AppInstaller/index.js +0 -29
- package/components/AppInstaller/index.js.map +0 -1
- package/components/AppInstaller/styled.d.ts +0 -9
- package/components/AppInstaller/styled.js +0 -91
- package/components/AppInstaller/styled.js.map +0 -1
- package/components/AppInstaller/useInstaller.d.ts +0 -31
- package/components/AppInstaller/useInstaller.js +0 -321
- package/components/AppInstaller/useInstaller.js.map +0 -1
- package/components/FileManager/BottomInfoBar/SupportedFileTypes.d.ts +0 -6
- package/components/FileManager/BottomInfoBar/SupportedFileTypes.js +0 -55
- package/components/FileManager/BottomInfoBar/SupportedFileTypes.js.map +0 -1
- package/components/FileManager/BottomInfoBar/UploadStatus.d.ts +0 -6
- package/components/FileManager/BottomInfoBar/UploadStatus.js +0 -60
- package/components/FileManager/BottomInfoBar/UploadStatus.js.map +0 -1
- package/components/FileManager/BottomInfoBar.d.ts +0 -5
- package/components/FileManager/BottomInfoBar.js +0 -60
- package/components/FileManager/BottomInfoBar.js.map +0 -1
- package/components/FileManager/DropFilesHere.d.ts +0 -10
- package/components/FileManager/DropFilesHere.js +0 -76
- package/components/FileManager/DropFilesHere.js.map +0 -1
- package/components/FileManager/File.d.ts +0 -17
- package/components/FileManager/File.js +0 -155
- package/components/FileManager/File.js.map +0 -1
- package/components/FileManager/FileDetails/Name.d.ts +0 -8
- package/components/FileManager/FileDetails/Name.js +0 -138
- package/components/FileManager/FileDetails/Name.js.map +0 -1
- package/components/FileManager/FileDetails/Tags.d.ts +0 -10
- package/components/FileManager/FileDetails/Tags.js +0 -339
- package/components/FileManager/FileDetails/Tags.js.map +0 -1
- package/components/FileManager/FileDetails.d.ts +0 -24
- package/components/FileManager/FileDetails.js +0 -474
- package/components/FileManager/FileDetails.js.map +0 -1
- package/components/FileManager/FileManagerContext.d.ts +0 -42
- package/components/FileManager/FileManagerContext.js +0 -224
- package/components/FileManager/FileManagerContext.js.map +0 -1
- package/components/FileManager/FileManagerView.d.ts +0 -18
- package/components/FileManager/FileManagerView.js +0 -728
- package/components/FileManager/FileManagerView.js.map +0 -1
- package/components/FileManager/LeftSidebar.d.ts +0 -10
- package/components/FileManager/LeftSidebar.js +0 -127
- package/components/FileManager/LeftSidebar.js.map +0 -1
- package/components/FileManager/NoPermissionView.d.ts +0 -3
- package/components/FileManager/NoPermissionView.js +0 -87
- package/components/FileManager/NoPermissionView.js.map +0 -1
- package/components/FileManager/NoResults.d.ts +0 -3
- package/components/FileManager/NoResults.js +0 -28
- package/components/FileManager/NoResults.js.map +0 -1
- package/components/FileManager/getFileTypePlugin.d.ts +0 -4
- package/components/FileManager/getFileTypePlugin.js +0 -57
- package/components/FileManager/getFileTypePlugin.js.map +0 -1
- package/components/FileManager/getFileUploader.d.ts +0 -3
- package/components/FileManager/getFileUploader.js +0 -20
- package/components/FileManager/getFileUploader.js.map +0 -1
- package/components/FileManager/graphql.d.ts +0 -96
- package/components/FileManager/graphql.js +0 -58
- package/components/FileManager/graphql.js.map +0 -1
- package/components/FileManager/icons/content_copy-black-24px.svg +0 -1
- package/components/FileManager/icons/delete.svg +0 -12
- package/components/FileManager/icons/privacy_tip-24px.svg +0 -10
- package/components/FileManager/icons/round-check_box-24px.svg +0 -4
- package/components/FileManager/icons/round-check_box_outline_blank-24px.svg +0 -4
- package/components/FileManager/icons/round-cloud_download-24px.svg +0 -4
- package/components/FileManager/icons/round-cloud_upload-24px.svg +0 -4
- package/components/FileManager/icons/round-description-24px.svg +0 -4
- package/components/FileManager/icons/round-edit-24px.svg +0 -4
- package/components/FileManager/icons/round-info-24px.svg +0 -4
- package/components/FileManager/icons/round-label-24px.svg +0 -4
- package/components/FileManager/icons/round-more_vert-24px.svg +0 -4
- package/components/FileManager/icons/round-search-24px.svg +0 -20
- package/components/FileManager/outputFileSelectionError.d.ts +0 -7
- package/components/FileManager/outputFileSelectionError.js +0 -54
- package/components/FileManager/outputFileSelectionError.js.map +0 -1
- package/components/FileManager/types.d.ts +0 -21
- package/components/FileManager/types.js +0 -5
- package/components/FileManager/types.js.map +0 -1
- package/components/FileManager.d.ts +0 -67
- package/components/FileManager.js +0 -144
- package/components/FileManager.js.map +0 -1
- package/components/OverlayLayout/icons/close.svg +0 -13
- package/components/OverlayLayout/icons/navigate_before.svg +0 -16
- package/components/OverlayLayout/index.js.map +0 -1
- package/components/Permissions/index.js.map +0 -1
- package/components/RichTextEditor/RichTextEditor.d.ts +0 -3
- package/components/RichTextEditor/RichTextEditor.js +0 -30
- package/components/RichTextEditor/RichTextEditor.js.map +0 -1
- package/components/RichTextEditor/index.d.ts +0 -2
- package/components/RichTextEditor/index.js +0 -21
- package/components/RichTextEditor/index.js.map +0 -1
- package/components/RichTextEditor/styles.scss +0 -96
- package/components/RichTextEditor/tools/header/index.d.ts +0 -229
- package/components/RichTextEditor/tools/header/index.js +0 -731
- package/components/RichTextEditor/tools/header/index.js.map +0 -1
- package/components/RichTextEditor/tools/header/styles.scss +0 -48
- package/components/RichTextEditor/tools/image/index.d.ts +0 -124
- package/components/RichTextEditor/tools/image/index.js +0 -265
- package/components/RichTextEditor/tools/image/index.js.map +0 -1
- package/components/RichTextEditor/tools/image/styles.scss +0 -90
- package/components/RichTextEditor/tools/image/svgs.d.ts +0 -6
- package/components/RichTextEditor/tools/image/svgs.js +0 -12
- package/components/RichTextEditor/tools/image/svgs.js.map +0 -1
- package/components/RichTextEditor/tools/image/tunes.d.ts +0 -57
- package/components/RichTextEditor/tools/image/tunes.js +0 -146
- package/components/RichTextEditor/tools/image/tunes.js.map +0 -1
- package/components/RichTextEditor/tools/image/types.d.ts +0 -29
- package/components/RichTextEditor/tools/image/types.js +0 -5
- package/components/RichTextEditor/tools/image/types.js.map +0 -1
- package/components/RichTextEditor/tools/image/ui.d.ts +0 -122
- package/components/RichTextEditor/tools/image/ui.js +0 -301
- package/components/RichTextEditor/tools/image/ui.js.map +0 -1
- package/components/RichTextEditor/tools/paragraph/index.d.ts +0 -209
- package/components/RichTextEditor/tools/paragraph/index.js +0 -514
- package/components/RichTextEditor/tools/paragraph/index.js.map +0 -1
- package/components/RichTextEditor/tools/paragraph/styles.scss +0 -29
- package/components/RichTextEditor/tools/textColor/index.d.ts +0 -64
- package/components/RichTextEditor/tools/textColor/index.js +0 -271
- package/components/RichTextEditor/tools/textColor/index.js.map +0 -1
- package/components/RichTextEditor/tools/textColor/styles.scss +0 -21
- package/components/RichTextEditor/tools/utils.d.ts +0 -19
- package/components/RichTextEditor/tools/utils.js +0 -33
- package/components/RichTextEditor/tools/utils.js.map +0 -1
- package/components/Routes.d.ts +0 -6
- package/components/Routes.js +0 -49
- package/components/Routes.js.map +0 -1
- package/components/SimpleForm/index.js.map +0 -1
- package/components/SplitView/index.js.map +0 -1
- package/components/index.js.map +0 -1
- package/index.js.map +0 -1
- package/plugins/FileManagerFileTypePlugin.d.ts +0 -33
- package/plugins/FileManagerFileTypePlugin.js +0 -63
- package/plugins/FileManagerFileTypePlugin.js.map +0 -1
- package/plugins/PermissionRendererPlugin.d.ts +0 -22
- package/plugins/PermissionRendererPlugin.js +0 -58
- package/plugins/PermissionRendererPlugin.js.map +0 -1
- package/plugins/fileManager/fileDefault.d.ts +0 -2
- package/plugins/fileManager/fileDefault.js +0 -34
- package/plugins/fileManager/fileDefault.js.map +0 -1
- package/plugins/fileManager/fileImage/DeleteAction.d.ts +0 -7
- package/plugins/fileManager/fileImage/DeleteAction.js +0 -83
- package/plugins/fileManager/fileImage/DeleteAction.js.map +0 -1
- package/plugins/fileManager/fileImage/EditAction.d.ts +0 -10
- package/plugins/fileManager/fileImage/EditAction.js +0 -179
- package/plugins/fileManager/fileImage/EditAction.js.map +0 -1
- package/plugins/fileManager/fileImage/index.d.ts +0 -2
- package/plugins/fileManager/fileImage/index.js +0 -46
- package/plugins/fileManager/fileImage/index.js.map +0 -1
- package/plugins/fileManager/icons/edit.svg +0 -17
- package/plugins/fileManager/icons/round-description-24px.svg +0 -1
- package/plugins/fileManager/index.d.ts +0 -2
- package/plugins/fileManager/index.js +0 -21
- package/plugins/fileManager/index.js.map +0 -1
- package/plugins/globalSearch/SearchBar.d.ts +0 -15
- package/plugins/globalSearch/SearchBar.js +0 -273
- package/plugins/globalSearch/SearchBar.js.map +0 -1
- package/plugins/globalSearch/SearchBarDropdown.d.ts +0 -23
- package/plugins/globalSearch/SearchBarDropdown.js +0 -101
- package/plugins/globalSearch/SearchBarDropdown.js.map +0 -1
- package/plugins/globalSearch/icons/round-search-24px.svg +0 -20
- package/plugins/globalSearch/index.d.ts +0 -7
- package/plugins/globalSearch/index.js +0 -16
- package/plugins/globalSearch/index.js.map +0 -1
- package/plugins/globalSearch/styled.d.ts +0 -11
- package/plugins/globalSearch/styled.js +0 -135
- package/plugins/globalSearch/styled.js.map +0 -1
- package/plugins/uiLayoutRenderer/index.d.ts +0 -2
- package/plugins/uiLayoutRenderer/index.js +0 -75
- package/plugins/uiLayoutRenderer/index.js.map +0 -1
- package/styles/material-theme-assignments.scss +0 -346
- package/styles/material.scss +0 -43
- package/styles/theme.scss +0 -46
- package/types.js.map +0 -1
- package/ui/UIElement.d.ts +0 -2
- package/ui/UIElement.js +0 -19
- package/ui/UIElement.js.map +0 -1
- package/ui/UILayout.d.ts +0 -1
- package/ui/UILayout.js +0 -19
- package/ui/UILayout.js.map +0 -1
- package/ui/UIRenderer.d.ts +0 -2
- package/ui/UIRenderer.js +0 -13
- package/ui/UIRenderer.js.map +0 -1
- package/ui/UIView.d.ts +0 -2
- package/ui/UIView.js +0 -25
- package/ui/UIView.js.map +0 -1
- package/ui/elements/AccordionElement.d.ts +0 -27
- package/ui/elements/AccordionElement.js +0 -110
- package/ui/elements/AccordionElement.js.map +0 -1
- package/ui/elements/ButtonElement.d.ts +0 -24
- package/ui/elements/ButtonElement.js +0 -105
- package/ui/elements/ButtonElement.js.map +0 -1
- package/ui/elements/ButtonGroupElement.d.ts +0 -6
- package/ui/elements/ButtonGroupElement.js +0 -67
- package/ui/elements/ButtonGroupElement.js.map +0 -1
- package/ui/elements/GenericElement.d.ts +0 -1
- package/ui/elements/GenericElement.js +0 -18
- package/ui/elements/GenericElement.js.map +0 -1
- package/ui/elements/LabelElement.d.ts +0 -16
- package/ui/elements/LabelElement.js +0 -60
- package/ui/elements/LabelElement.js.map +0 -1
- package/ui/elements/NavigationMenuElement.d.ts +0 -33
- package/ui/elements/NavigationMenuElement.js +0 -177
- package/ui/elements/NavigationMenuElement.js.map +0 -1
- package/ui/elements/PanelElement.d.ts +0 -3
- package/ui/elements/PanelElement.js +0 -33
- package/ui/elements/PanelElement.js.map +0 -1
- package/ui/elements/PlaceholderElement.d.ts +0 -6
- package/ui/elements/PlaceholderElement.js +0 -44
- package/ui/elements/PlaceholderElement.js.map +0 -1
- package/ui/elements/SmallButtonElement.d.ts +0 -6
- package/ui/elements/SmallButtonElement.js +0 -64
- package/ui/elements/SmallButtonElement.js.map +0 -1
- package/ui/elements/TypographyElement.d.ts +0 -13
- package/ui/elements/TypographyElement.js +0 -67
- package/ui/elements/TypographyElement.js.map +0 -1
- package/ui/elements/ViewElement.d.ts +0 -1
- package/ui/elements/ViewElement.js +0 -18
- package/ui/elements/ViewElement.js.map +0 -1
- package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.d.ts +0 -6
- package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.js +0 -60
- package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.js.map +0 -1
- package/ui/elements/form/DynamicFieldsetElement.d.ts +0 -43
- package/ui/elements/form/DynamicFieldsetElement.js +0 -158
- package/ui/elements/form/DynamicFieldsetElement.js.map +0 -1
- package/ui/elements/form/FileManagerElement/EmptyStateElement.d.ts +0 -4
- package/ui/elements/form/FileManagerElement/EmptyStateElement.js +0 -41
- package/ui/elements/form/FileManagerElement/EmptyStateElement.js.map +0 -1
- package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.d.ts +0 -7
- package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.js +0 -95
- package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.js.map +0 -1
- package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.d.ts +0 -13
- package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.js +0 -123
- package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.js.map +0 -1
- package/ui/elements/form/FileManagerElement/styled.d.ts +0 -20
- package/ui/elements/form/FileManagerElement/styled.js +0 -120
- package/ui/elements/form/FileManagerElement/styled.js.map +0 -1
- package/ui/elements/form/FileManagerElement.d.ts +0 -24
- package/ui/elements/form/FileManagerElement.js +0 -108
- package/ui/elements/form/FileManagerElement.js.map +0 -1
- package/ui/elements/form/FormElement.d.ts +0 -21
- package/ui/elements/form/FormElement.js +0 -66
- package/ui/elements/form/FormElement.js.map +0 -1
- package/ui/elements/form/FormFieldElement.d.ts +0 -56
- package/ui/elements/form/FormFieldElement.js +0 -206
- package/ui/elements/form/FormFieldElement.js.map +0 -1
- package/ui/elements/form/HiddenElement.d.ts +0 -6
- package/ui/elements/form/HiddenElement.js +0 -64
- package/ui/elements/form/HiddenElement.js.map +0 -1
- package/ui/elements/form/InputElement.d.ts +0 -7
- package/ui/elements/form/InputElement.js +0 -72
- package/ui/elements/form/InputElement.js.map +0 -1
- package/ui/elements/form/PasswordElement.d.ts +0 -6
- package/ui/elements/form/PasswordElement.js +0 -66
- package/ui/elements/form/PasswordElement.js.map +0 -1
- package/ui/elements/form/README.md +0 -2
- package/ui/elements/form/SelectElement.d.ts +0 -15
- package/ui/elements/form/SelectElement.js +0 -87
- package/ui/elements/form/SelectElement.js.map +0 -1
- package/ui/elements/form/TextareaElement.d.ts +0 -15
- package/ui/elements/form/TextareaElement.js +0 -72
- package/ui/elements/form/TextareaElement.js.map +0 -1
- package/ui/views/AdminView/ContentElement.d.ts +0 -4
- package/ui/views/AdminView/ContentElement.js +0 -78
- package/ui/views/AdminView/ContentElement.js.map +0 -1
- package/ui/views/AdminView/HeaderElement.d.ts +0 -13
- package/ui/views/AdminView/HeaderElement.js +0 -144
- package/ui/views/AdminView/HeaderElement.js.map +0 -1
- package/ui/views/AdminView/HeaderSectionCenterElement.d.ts +0 -6
- package/ui/views/AdminView/HeaderSectionCenterElement.js +0 -62
- package/ui/views/AdminView/HeaderSectionCenterElement.js.map +0 -1
- package/ui/views/AdminView/HeaderSectionLeftElement.d.ts +0 -6
- package/ui/views/AdminView/HeaderSectionLeftElement.js +0 -62
- package/ui/views/AdminView/HeaderSectionLeftElement.js.map +0 -1
- package/ui/views/AdminView/HeaderSectionRightElement.d.ts +0 -6
- package/ui/views/AdminView/HeaderSectionRightElement.js +0 -62
- package/ui/views/AdminView/HeaderSectionRightElement.js.map +0 -1
- package/ui/views/AdminView/components/Dialog.d.ts +0 -2
- package/ui/views/AdminView/components/Dialog.js +0 -57
- package/ui/views/AdminView/components/Dialog.js.map +0 -1
- package/ui/views/AdminView/components/Hamburger.d.ts +0 -3
- package/ui/views/AdminView/components/Hamburger.js +0 -39
- package/ui/views/AdminView/components/Hamburger.js.map +0 -1
- package/ui/views/AdminView/components/Snackbar.d.ts +0 -3
- package/ui/views/AdminView/components/Snackbar.js +0 -41
- package/ui/views/AdminView/components/Snackbar.js.map +0 -1
- package/ui/views/FormView/FormContainerElement.d.ts +0 -12
- package/ui/views/FormView/FormContainerElement.js +0 -71
- package/ui/views/FormView/FormContainerElement.js.map +0 -1
- package/ui/views/FormView/FormContentElement.d.ts +0 -3
- package/ui/views/FormView/FormContentElement.js +0 -33
- package/ui/views/FormView/FormContentElement.js.map +0 -1
- package/ui/views/FormView/FormFooterElement.d.ts +0 -6
- package/ui/views/FormView/FormFooterElement.js +0 -68
- package/ui/views/FormView/FormFooterElement.js.map +0 -1
- package/ui/views/FormView/FormHeaderElement.d.ts +0 -14
- package/ui/views/FormView/FormHeaderElement.js +0 -103
- package/ui/views/FormView/FormHeaderElement.js.map +0 -1
- package/ui/views/FormView.d.ts +0 -40
- package/ui/views/FormView.js +0 -208
- package/ui/views/FormView.js.map +0 -1
- package/ui/views/OverlayView/ContentElement.d.ts +0 -6
- package/ui/views/OverlayView/ContentElement.js +0 -61
- package/ui/views/OverlayView/ContentElement.js.map +0 -1
- package/ui/views/OverlayView/HeaderElement.d.ts +0 -24
- package/ui/views/OverlayView/HeaderElement.js +0 -134
- package/ui/views/OverlayView/HeaderElement.js.map +0 -1
- package/ui/views/OverlayView/HeaderTitleElement.d.ts +0 -13
- package/ui/views/OverlayView/HeaderTitleElement.js +0 -73
- package/ui/views/OverlayView/HeaderTitleElement.js.map +0 -1
- package/ui/views/OverlayView/useOverlayView.d.ts +0 -5
- package/ui/views/OverlayView/useOverlayView.js +0 -54
- package/ui/views/OverlayView/useOverlayView.js.map +0 -1
- package/ui/views/OverlayView.d.ts +0 -29
- package/ui/views/OverlayView.js +0 -199
- package/ui/views/OverlayView.js.map +0 -1
- package/ui/views/SplitView/SplitViewPanelElement.d.ts +0 -11
- package/ui/views/SplitView/SplitViewPanelElement.js +0 -91
- package/ui/views/SplitView/SplitViewPanelElement.js.map +0 -1
- package/ui/views/SplitView.d.ts +0 -21
- package/ui/views/SplitView.js +0 -162
- package/ui/views/SplitView.js.map +0 -1
|
@@ -0,0 +1,3811 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { Container } from "@webiny/di";
|
|
4
|
+
import { FormModelFeature } from "./feature.js";
|
|
5
|
+
import { FormModelFactory } from "./abstractions.js";
|
|
6
|
+
function createForm(config) {
|
|
7
|
+
const container = new Container();
|
|
8
|
+
FormModelFeature.register(container);
|
|
9
|
+
return container.resolve(FormModelFactory).create(config);
|
|
10
|
+
}
|
|
11
|
+
function asRow(node) {
|
|
12
|
+
if ("row" !== node.type) throw new Error(`Expected row node, got "${node.type}"`);
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
function createBasicForm() {
|
|
16
|
+
return createForm({
|
|
17
|
+
fields: (fields)=>({
|
|
18
|
+
title: fields.text().label("Title").required("Title is required"),
|
|
19
|
+
path: fields.text().label("Path").required("Path is required")
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
describe("FormModel", ()=>{
|
|
24
|
+
describe("field creation", ()=>{
|
|
25
|
+
it("should create fields from builder definitions", ()=>{
|
|
26
|
+
const form = createBasicForm();
|
|
27
|
+
expect(form.field("title")).toBeDefined();
|
|
28
|
+
expect(form.field("path")).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
it("should throw on unknown field access", ()=>{
|
|
31
|
+
const form = createBasicForm();
|
|
32
|
+
expect(()=>form.field("unknown")).toThrow('Field "unknown" not found.');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("setValue / getValue", ()=>{
|
|
36
|
+
it("should set and get field values", ()=>{
|
|
37
|
+
const form = createBasicForm();
|
|
38
|
+
form.field("title").setValue("Hello");
|
|
39
|
+
expect(form.field("title").getValue()).toBe("Hello");
|
|
40
|
+
});
|
|
41
|
+
it("should default to null when no defaultValue is set", ()=>{
|
|
42
|
+
const form = createBasicForm();
|
|
43
|
+
expect(form.field("title").getValue()).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
it("should use defaultValue when provided", ()=>{
|
|
46
|
+
const form = createForm({
|
|
47
|
+
fields: (fields)=>({
|
|
48
|
+
status: fields.text().defaultValue("draft")
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
expect(form.field("status").getValue()).toBe("draft");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("getData / setData", ()=>{
|
|
55
|
+
it("should return all field values including hidden", ()=>{
|
|
56
|
+
const form = createForm({
|
|
57
|
+
fields: (fields)=>({
|
|
58
|
+
title: fields.text().label("Title"),
|
|
59
|
+
pageType: fields.text().hidden().defaultValue("static")
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
form.field("title").setValue("Hello");
|
|
63
|
+
const data = form.getData();
|
|
64
|
+
expect(data).toEqual({
|
|
65
|
+
title: "Hello",
|
|
66
|
+
pageType: "static"
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it("should hydrate fields from data object", ()=>{
|
|
70
|
+
const form = createBasicForm();
|
|
71
|
+
form.setData({
|
|
72
|
+
title: "My Page",
|
|
73
|
+
path: "/my-page"
|
|
74
|
+
});
|
|
75
|
+
expect(form.field("title").getValue()).toBe("My Page");
|
|
76
|
+
expect(form.field("path").getValue()).toBe("/my-page");
|
|
77
|
+
});
|
|
78
|
+
it("should ignore unknown fields in setData", ()=>{
|
|
79
|
+
const form = createBasicForm();
|
|
80
|
+
form.setData({
|
|
81
|
+
title: "Test",
|
|
82
|
+
unknown: "value"
|
|
83
|
+
});
|
|
84
|
+
expect(form.field("title").getValue()).toBe("Test");
|
|
85
|
+
expect(()=>form.field("unknown")).toThrow();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("isDirty", ()=>{
|
|
89
|
+
it("should not be dirty initially", ()=>{
|
|
90
|
+
const form = createBasicForm();
|
|
91
|
+
expect(form.isDirty).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it("should be dirty after setValue", ()=>{
|
|
94
|
+
const form = createBasicForm();
|
|
95
|
+
form.field("title").setValue("Changed");
|
|
96
|
+
expect(form.isDirty).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
it("should not be dirty after setData", ()=>{
|
|
99
|
+
const form = createBasicForm();
|
|
100
|
+
form.setData({
|
|
101
|
+
title: "Loaded",
|
|
102
|
+
path: "/loaded"
|
|
103
|
+
});
|
|
104
|
+
expect(form.isDirty).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it("should not be dirty after reverting to baseline value", ()=>{
|
|
107
|
+
const form = createBasicForm();
|
|
108
|
+
form.setData({
|
|
109
|
+
title: "Original",
|
|
110
|
+
path: "/original"
|
|
111
|
+
});
|
|
112
|
+
form.field("title").setValue("Changed");
|
|
113
|
+
expect(form.isDirty).toBe(true);
|
|
114
|
+
form.field("title").setValue("Original");
|
|
115
|
+
expect(form.isDirty).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe("reset", ()=>{
|
|
119
|
+
it("should revert values to setData baseline", ()=>{
|
|
120
|
+
const form = createBasicForm();
|
|
121
|
+
form.setData({
|
|
122
|
+
title: "Original",
|
|
123
|
+
path: "/original"
|
|
124
|
+
});
|
|
125
|
+
form.field("title").setValue("Changed");
|
|
126
|
+
form.reset();
|
|
127
|
+
expect(form.field("title").getValue()).toBe("Original");
|
|
128
|
+
expect(form.isDirty).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
it("should clear validation state on reset", async ()=>{
|
|
131
|
+
const form = createBasicForm();
|
|
132
|
+
await form.validate();
|
|
133
|
+
expect(form.errors.length).toBeGreaterThan(0);
|
|
134
|
+
form.reset();
|
|
135
|
+
expect(form.errors).toEqual([]);
|
|
136
|
+
expect(form.isValid).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe("validation", ()=>{
|
|
140
|
+
it("should fail validation for empty required fields", async ()=>{
|
|
141
|
+
const form = createBasicForm();
|
|
142
|
+
const valid = await form.validate();
|
|
143
|
+
expect(valid).toBe(false);
|
|
144
|
+
expect(form.isValid).toBe(false);
|
|
145
|
+
expect(form.errors).toHaveLength(2);
|
|
146
|
+
expect(form.errors[0].path).toBe("title");
|
|
147
|
+
expect(form.errors[0].message).toBe("Title is required");
|
|
148
|
+
expect(form.errors[1].path).toBe("path");
|
|
149
|
+
});
|
|
150
|
+
it("should pass validation when required fields have values", async ()=>{
|
|
151
|
+
const form = createBasicForm();
|
|
152
|
+
form.field("title").setValue("My Page");
|
|
153
|
+
form.field("path").setValue("/my-page");
|
|
154
|
+
const valid = await form.validate();
|
|
155
|
+
expect(valid).toBe(true);
|
|
156
|
+
expect(form.isValid).toBe(true);
|
|
157
|
+
expect(form.errors).toHaveLength(0);
|
|
158
|
+
});
|
|
159
|
+
it("should validate zod schemas", async ()=>{
|
|
160
|
+
const form = createForm({
|
|
161
|
+
fields: (fields)=>({
|
|
162
|
+
email: fields.text().label("Email").schema(z.string().email("Invalid email"))
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
form.field("email").setValue("not-an-email");
|
|
166
|
+
const valid = await form.validate();
|
|
167
|
+
expect(valid).toBe(false);
|
|
168
|
+
expect(form.errors[0].message).toBe("Invalid email");
|
|
169
|
+
});
|
|
170
|
+
it("should run required check before zod schema", async ()=>{
|
|
171
|
+
const form = createForm({
|
|
172
|
+
fields: (fields)=>({
|
|
173
|
+
email: fields.text().label("Email").required("Email is required").schema(z.string().email("Invalid email"))
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
const valid = await form.validate();
|
|
177
|
+
expect(valid).toBe(false);
|
|
178
|
+
expect(form.errors[0].message).toBe("Email is required");
|
|
179
|
+
});
|
|
180
|
+
it("should expose field-level validation in field.vm", async ()=>{
|
|
181
|
+
const form = createBasicForm();
|
|
182
|
+
await form.validate();
|
|
183
|
+
const titleVm = form.field("title").vm;
|
|
184
|
+
expect(titleVm.validation.isValid).toBe(false);
|
|
185
|
+
expect(titleVm.validation.message).toBe("Title is required");
|
|
186
|
+
});
|
|
187
|
+
it("isValid should be null before first validation", ()=>{
|
|
188
|
+
const form = createBasicForm();
|
|
189
|
+
expect(form.isValid).toBeNull();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe("submit", ()=>{
|
|
193
|
+
it("should return data when valid", async ()=>{
|
|
194
|
+
const form = createBasicForm();
|
|
195
|
+
form.field("title").setValue("My Page");
|
|
196
|
+
form.field("path").setValue("/my-page");
|
|
197
|
+
const result = await form.submit();
|
|
198
|
+
expect(result).toEqual({
|
|
199
|
+
title: "My Page",
|
|
200
|
+
path: "/my-page"
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
it("should return false when invalid", async ()=>{
|
|
204
|
+
const form = createBasicForm();
|
|
205
|
+
const result = await form.submit();
|
|
206
|
+
expect(result).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe("vm", ()=>{
|
|
210
|
+
it("should expose layout with field VMs", ()=>{
|
|
211
|
+
const form = createBasicForm();
|
|
212
|
+
const vm = form.vm;
|
|
213
|
+
expect(vm.layout).toHaveLength(2);
|
|
214
|
+
expect(vm.layout[0].type).toBe("row");
|
|
215
|
+
expect(asRow(vm.layout[0]).fields[0].name).toBe("title");
|
|
216
|
+
expect(asRow(vm.layout[1]).fields[0].name).toBe("path");
|
|
217
|
+
});
|
|
218
|
+
it("should exclude hidden fields from layout", ()=>{
|
|
219
|
+
const form = createForm({
|
|
220
|
+
fields: (fields)=>({
|
|
221
|
+
title: fields.text().label("Title"),
|
|
222
|
+
pageType: fields.text().hidden().defaultValue("static")
|
|
223
|
+
})
|
|
224
|
+
});
|
|
225
|
+
const vm = form.vm;
|
|
226
|
+
expect(vm.layout).toHaveLength(1);
|
|
227
|
+
expect(asRow(vm.layout[0]).fields[0].name).toBe("title");
|
|
228
|
+
});
|
|
229
|
+
it("should expose isDirty and isValid", ()=>{
|
|
230
|
+
const form = createBasicForm();
|
|
231
|
+
expect(form.vm.isDirty).toBe(false);
|
|
232
|
+
expect(form.vm.isValid).toBeNull();
|
|
233
|
+
});
|
|
234
|
+
it("should expose field onChange that calls setValue", ()=>{
|
|
235
|
+
const form = createBasicForm();
|
|
236
|
+
const fieldVM = asRow(form.vm.layout[0]).fields[0];
|
|
237
|
+
fieldVM.onChange("New Value");
|
|
238
|
+
expect(form.field("title").getValue()).toBe("New Value");
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe("layout", ()=>{
|
|
242
|
+
it("should generate default layout (one row per non-hidden field)", ()=>{
|
|
243
|
+
const form = createBasicForm();
|
|
244
|
+
expect(form.vm.layout).toHaveLength(2);
|
|
245
|
+
});
|
|
246
|
+
it("should use explicit layout when provided", ()=>{
|
|
247
|
+
const form = createForm({
|
|
248
|
+
fields: (fields)=>({
|
|
249
|
+
title: fields.text().label("Title"),
|
|
250
|
+
path: fields.text().label("Path")
|
|
251
|
+
}),
|
|
252
|
+
layout: (layout)=>[
|
|
253
|
+
layout.row("title", "path")
|
|
254
|
+
]
|
|
255
|
+
});
|
|
256
|
+
expect(form.vm.layout).toHaveLength(1);
|
|
257
|
+
expect(asRow(form.vm.layout[0]).fields).toHaveLength(2);
|
|
258
|
+
});
|
|
259
|
+
it("should warn about orphan fields in explicit layout", ()=>{
|
|
260
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
|
|
261
|
+
createForm({
|
|
262
|
+
fields: (fields)=>({
|
|
263
|
+
title: fields.text().label("Title"),
|
|
264
|
+
path: fields.text().label("Path")
|
|
265
|
+
}),
|
|
266
|
+
layout: (layout)=>[
|
|
267
|
+
layout.row("title")
|
|
268
|
+
]
|
|
269
|
+
});
|
|
270
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Field "path" is not in the layout'));
|
|
271
|
+
warnSpy.mockRestore();
|
|
272
|
+
});
|
|
273
|
+
it("should not warn about hidden orphan fields", ()=>{
|
|
274
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
|
|
275
|
+
createForm({
|
|
276
|
+
fields: (fields)=>({
|
|
277
|
+
title: fields.text().label("Title"),
|
|
278
|
+
pageType: fields.text().hidden().defaultValue("static")
|
|
279
|
+
}),
|
|
280
|
+
layout: (layout)=>[
|
|
281
|
+
layout.row("title")
|
|
282
|
+
]
|
|
283
|
+
});
|
|
284
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
285
|
+
warnSpy.mockRestore();
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe("beforeChange / afterChange", ()=>{
|
|
289
|
+
it("should run beforeChange pipeline in order, transforming value", ()=>{
|
|
290
|
+
const form = createForm({
|
|
291
|
+
fields: (fields)=>({
|
|
292
|
+
path: fields.text().label("Path").beforeChange((value)=>String(value).trim()).beforeChange((value)=>String(value).toLowerCase())
|
|
293
|
+
})
|
|
294
|
+
});
|
|
295
|
+
form.field("path").setValue(" Hello World ");
|
|
296
|
+
expect(form.field("path").getValue()).toBe("hello world");
|
|
297
|
+
});
|
|
298
|
+
it("should run afterChange after value is stored", ()=>{
|
|
299
|
+
const received = [];
|
|
300
|
+
const form = createForm({
|
|
301
|
+
fields: (fields)=>({
|
|
302
|
+
title: fields.text().label("Title").afterChange((value)=>{
|
|
303
|
+
received.push(value);
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
form.field("title").setValue("Hello");
|
|
308
|
+
expect(received).toEqual([
|
|
309
|
+
"Hello"
|
|
310
|
+
]);
|
|
311
|
+
});
|
|
312
|
+
it("should pass transformed value to afterChange", ()=>{
|
|
313
|
+
const received = [];
|
|
314
|
+
const form = createForm({
|
|
315
|
+
fields: (fields)=>({
|
|
316
|
+
path: fields.text().label("Path").beforeChange((value)=>String(value).toLowerCase()).afterChange((value)=>{
|
|
317
|
+
received.push(value);
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
form.field("path").setValue("HELLO");
|
|
322
|
+
expect(form.field("path").getValue()).toBe("hello");
|
|
323
|
+
expect(received).toEqual([
|
|
324
|
+
"hello"
|
|
325
|
+
]);
|
|
326
|
+
});
|
|
327
|
+
it("should not fire afterChange when value does not change (recursion guard)", ()=>{
|
|
328
|
+
const calls = [];
|
|
329
|
+
const form = createForm({
|
|
330
|
+
fields: (fields)=>({
|
|
331
|
+
title: fields.text().label("Title").beforeChange(()=>"constant").afterChange(()=>{
|
|
332
|
+
calls.push("afterChange");
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
});
|
|
336
|
+
form.field("title").setValue("anything");
|
|
337
|
+
expect(calls).toEqual([
|
|
338
|
+
"afterChange"
|
|
339
|
+
]);
|
|
340
|
+
expect(form.field("title").getValue()).toBe("constant");
|
|
341
|
+
form.field("title").setValue("something else");
|
|
342
|
+
expect(calls).toEqual([
|
|
343
|
+
"afterChange"
|
|
344
|
+
]);
|
|
345
|
+
});
|
|
346
|
+
it("should not trigger beforeChange or afterChange on setData", ()=>{
|
|
347
|
+
const calls = [];
|
|
348
|
+
const form = createForm({
|
|
349
|
+
fields: (fields)=>({
|
|
350
|
+
title: fields.text().label("Title").beforeChange((value)=>{
|
|
351
|
+
calls.push("before");
|
|
352
|
+
return value;
|
|
353
|
+
}).afterChange(()=>{
|
|
354
|
+
calls.push("after");
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
});
|
|
358
|
+
form.setData({
|
|
359
|
+
title: "Loaded"
|
|
360
|
+
});
|
|
361
|
+
expect(calls).toEqual([]);
|
|
362
|
+
expect(form.field("title").getValue()).toBe("Loaded");
|
|
363
|
+
});
|
|
364
|
+
it("should support cross-field afterChange triggering target field pipeline", ()=>{
|
|
365
|
+
const form = createForm({
|
|
366
|
+
fields: (fields)=>({
|
|
367
|
+
title: fields.text().label("Title").afterChange((value, f)=>{
|
|
368
|
+
const path = "/" + String(value).toLowerCase().replace(/\s+/g, "-");
|
|
369
|
+
f.field("path").setValue(path);
|
|
370
|
+
}),
|
|
371
|
+
path: fields.text().label("Path").beforeChange((value)=>{
|
|
372
|
+
const str = String(value);
|
|
373
|
+
return str.startsWith("/") ? str : "/" + str;
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
});
|
|
377
|
+
form.field("title").setValue("Hello World");
|
|
378
|
+
expect(form.field("path").getValue()).toBe("/hello-world");
|
|
379
|
+
});
|
|
380
|
+
it("should allow appending callbacks to existing fields at runtime", ()=>{
|
|
381
|
+
const form = createForm({
|
|
382
|
+
fields: (fields)=>({
|
|
383
|
+
title: fields.text().label("Title")
|
|
384
|
+
})
|
|
385
|
+
});
|
|
386
|
+
const field = form.field("title");
|
|
387
|
+
field.addBeforeChange((value)=>String(value).toUpperCase());
|
|
388
|
+
field.setValue("hello");
|
|
389
|
+
expect(field.getValue()).toBe("HELLO");
|
|
390
|
+
});
|
|
391
|
+
it("should chain builder callbacks with runtime-appended callbacks", ()=>{
|
|
392
|
+
const form = createForm({
|
|
393
|
+
fields: (fields)=>({
|
|
394
|
+
path: fields.text().label("Path").beforeChange((value)=>String(value).trim())
|
|
395
|
+
})
|
|
396
|
+
});
|
|
397
|
+
form.field("path").addBeforeChange((value)=>String(value).toLowerCase());
|
|
398
|
+
form.field("path").setValue(" HELLO ");
|
|
399
|
+
expect(form.field("path").getValue()).toBe("hello");
|
|
400
|
+
});
|
|
401
|
+
it("should demonstrate title→path with path-dirty tracking", ()=>{
|
|
402
|
+
const form = createForm({
|
|
403
|
+
fields: (fields)=>({
|
|
404
|
+
title: fields.text().label("Title").required("Title is required").afterChange((value, f)=>{
|
|
405
|
+
if (f.field("path").getValue()) return;
|
|
406
|
+
const slug = String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
407
|
+
f.field("path").setValue("/" + slug);
|
|
408
|
+
}),
|
|
409
|
+
path: fields.text().label("Path").required("Path is required").beforeChange((value)=>{
|
|
410
|
+
const str = String(value);
|
|
411
|
+
return "/" + str.replace(/^\//, "").toLowerCase().replace(/[^a-z0-9/-]+/g, "-").replace(/^-|-$/g, "");
|
|
412
|
+
})
|
|
413
|
+
}),
|
|
414
|
+
layout: (layout)=>[
|
|
415
|
+
layout.row("title"),
|
|
416
|
+
layout.row("path")
|
|
417
|
+
]
|
|
418
|
+
});
|
|
419
|
+
form.field("title").setValue("Hello World");
|
|
420
|
+
expect(form.field("path").getValue()).toBe("/hello-world");
|
|
421
|
+
form.field("path").setValue("/custom-path");
|
|
422
|
+
expect(form.field("path").getValue()).toBe("/custom-path");
|
|
423
|
+
form.field("title").setValue("New Title");
|
|
424
|
+
expect(form.field("path").getValue()).toBe("/custom-path");
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
describe("field with options", ()=>{
|
|
428
|
+
it("should resolve static options in field VM", ()=>{
|
|
429
|
+
const form = createForm({
|
|
430
|
+
fields: (fields)=>({
|
|
431
|
+
lang: fields.text().label("Language").options([
|
|
432
|
+
{
|
|
433
|
+
label: "English",
|
|
434
|
+
value: "en"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
label: "German",
|
|
438
|
+
value: "de"
|
|
439
|
+
}
|
|
440
|
+
])
|
|
441
|
+
})
|
|
442
|
+
});
|
|
443
|
+
const fieldVM = asRow(form.vm.layout[0]).fields[0];
|
|
444
|
+
expect(fieldVM.options).toEqual([
|
|
445
|
+
{
|
|
446
|
+
label: "English",
|
|
447
|
+
value: "en"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
label: "German",
|
|
451
|
+
value: "de"
|
|
452
|
+
}
|
|
453
|
+
]);
|
|
454
|
+
});
|
|
455
|
+
it("should resolve reactive options function in field VM", ()=>{
|
|
456
|
+
const form = createForm({
|
|
457
|
+
fields: (fields)=>({
|
|
458
|
+
lang: fields.text().label("Language").options(()=>[
|
|
459
|
+
{
|
|
460
|
+
label: "Dynamic",
|
|
461
|
+
value: "dynamic"
|
|
462
|
+
}
|
|
463
|
+
])
|
|
464
|
+
})
|
|
465
|
+
});
|
|
466
|
+
const fieldVM = asRow(form.vm.layout[0]).fields[0];
|
|
467
|
+
expect(fieldVM.options).toEqual([
|
|
468
|
+
{
|
|
469
|
+
label: "Dynamic",
|
|
470
|
+
value: "dynamic"
|
|
471
|
+
}
|
|
472
|
+
]);
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
describe("modifiers (Phase 3)", ()=>{
|
|
476
|
+
describe("form.fields() — add / replace / remove", ()=>{
|
|
477
|
+
it("should add a new field via form.fields()", ()=>{
|
|
478
|
+
const form = createBasicForm();
|
|
479
|
+
form.fields((fields)=>({
|
|
480
|
+
language: fields.text().label("Language").options([
|
|
481
|
+
{
|
|
482
|
+
label: "English",
|
|
483
|
+
value: "en"
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
label: "German",
|
|
487
|
+
value: "de"
|
|
488
|
+
}
|
|
489
|
+
])
|
|
490
|
+
}));
|
|
491
|
+
expect(form.field("language")).toBeDefined();
|
|
492
|
+
expect(form.field("language").type).toBe("text");
|
|
493
|
+
expect(form.getData()).toHaveProperty("language");
|
|
494
|
+
});
|
|
495
|
+
it("should add a field that appears in getData but not layout until positioned", ()=>{
|
|
496
|
+
const form = createForm({
|
|
497
|
+
fields: (fields)=>({
|
|
498
|
+
title: fields.text().label("Title")
|
|
499
|
+
})
|
|
500
|
+
});
|
|
501
|
+
form.fields((fields)=>({
|
|
502
|
+
description: fields.text().label("Description")
|
|
503
|
+
}));
|
|
504
|
+
expect(form.getData()).toHaveProperty("description");
|
|
505
|
+
const fieldNames = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
506
|
+
expect(fieldNames).not.toContain("description");
|
|
507
|
+
form.layout((layout)=>[
|
|
508
|
+
layout.row("description").after("title")
|
|
509
|
+
]);
|
|
510
|
+
const updatedNames = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
511
|
+
expect(updatedNames).toEqual([
|
|
512
|
+
"title",
|
|
513
|
+
"description"
|
|
514
|
+
]);
|
|
515
|
+
});
|
|
516
|
+
it("should replace an existing field when key matches", ()=>{
|
|
517
|
+
const form = createBasicForm();
|
|
518
|
+
form.field("title").setValue("Old Value");
|
|
519
|
+
form.fields((fields)=>({
|
|
520
|
+
title: fields.text().label("Replaced Title").placeholder("New placeholder")
|
|
521
|
+
}));
|
|
522
|
+
expect(form.field("title").config.label).toBe("Replaced Title");
|
|
523
|
+
expect(form.field("title").config.placeholder).toBe("New placeholder");
|
|
524
|
+
expect(form.field("title").getValue()).toBeNull();
|
|
525
|
+
});
|
|
526
|
+
it("should remove a field via undefined", ()=>{
|
|
527
|
+
const form = createBasicForm();
|
|
528
|
+
form.fields(()=>({
|
|
529
|
+
path: void 0
|
|
530
|
+
}));
|
|
531
|
+
expect(()=>form.field("path")).toThrow('Field "path" not found.');
|
|
532
|
+
expect(form.getData()).not.toHaveProperty("path");
|
|
533
|
+
});
|
|
534
|
+
it("should remove a field via field.remove()", ()=>{
|
|
535
|
+
const form = createBasicForm();
|
|
536
|
+
form.field("path").remove();
|
|
537
|
+
expect(()=>form.field("path")).toThrow('Field "path" not found.');
|
|
538
|
+
expect(form.getData()).not.toHaveProperty("path");
|
|
539
|
+
});
|
|
540
|
+
it("should remove field from layout when removed via field.remove()", ()=>{
|
|
541
|
+
const form = createBasicForm();
|
|
542
|
+
expect(form.vm.layout).toHaveLength(2);
|
|
543
|
+
form.field("path").remove();
|
|
544
|
+
expect(form.vm.layout).toHaveLength(1);
|
|
545
|
+
expect(asRow(form.vm.layout[0]).fields[0].name).toBe("title");
|
|
546
|
+
});
|
|
547
|
+
it("should handle add + remove in the same fields() call", ()=>{
|
|
548
|
+
const form = createBasicForm();
|
|
549
|
+
form.fields((fields)=>({
|
|
550
|
+
path: void 0,
|
|
551
|
+
slug: fields.text().label("Slug")
|
|
552
|
+
}));
|
|
553
|
+
expect(()=>form.field("path")).toThrow();
|
|
554
|
+
expect(form.field("slug")).toBeDefined();
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
describe("form.field().setDisabled()", ()=>{
|
|
558
|
+
it("should disable a field via setDisabled(true)", ()=>{
|
|
559
|
+
const form = createBasicForm();
|
|
560
|
+
form.field("title").setDisabled(true);
|
|
561
|
+
expect(form.field("title").vm.disabled).toBe(true);
|
|
562
|
+
});
|
|
563
|
+
it("should re-enable a field via setDisabled(false)", ()=>{
|
|
564
|
+
const form = createBasicForm();
|
|
565
|
+
form.field("title").setDisabled(true);
|
|
566
|
+
form.field("title").setDisabled(false);
|
|
567
|
+
expect(form.field("title").vm.disabled).toBe(false);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
describe("form.field().as() — type narrowing", ()=>{
|
|
571
|
+
it("should return the field when type matches", ()=>{
|
|
572
|
+
const form = createForm({
|
|
573
|
+
fields: (fields)=>({
|
|
574
|
+
lang: fields.text().label("Language").options([
|
|
575
|
+
{
|
|
576
|
+
label: "English",
|
|
577
|
+
value: "en"
|
|
578
|
+
}
|
|
579
|
+
])
|
|
580
|
+
})
|
|
581
|
+
});
|
|
582
|
+
const textField = form.field("lang").as("text");
|
|
583
|
+
expect(textField).toBe(form.field("lang"));
|
|
584
|
+
});
|
|
585
|
+
it("should throw when type does not match", ()=>{
|
|
586
|
+
const form = createBasicForm();
|
|
587
|
+
expect(()=>form.field("title").as("boolean")).toThrow('Field "title" is type "text", not "boolean".');
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
describe("modifier appends callbacks to existing fields", ()=>{
|
|
591
|
+
it("should append beforeChange to an existing field", ()=>{
|
|
592
|
+
const form = createForm({
|
|
593
|
+
fields: (fields)=>({
|
|
594
|
+
path: fields.text().label("Path").beforeChange((value)=>String(value).trim())
|
|
595
|
+
})
|
|
596
|
+
});
|
|
597
|
+
form.field("path").addBeforeChange((value)=>String(value).toLowerCase());
|
|
598
|
+
form.field("path").setValue(" HELLO ");
|
|
599
|
+
expect(form.field("path").getValue()).toBe("hello");
|
|
600
|
+
});
|
|
601
|
+
it("should append afterChange to an existing field", ()=>{
|
|
602
|
+
const received = [];
|
|
603
|
+
const form = createForm({
|
|
604
|
+
fields: (fields)=>({
|
|
605
|
+
title: fields.text().label("Title"),
|
|
606
|
+
path: fields.text().label("Path")
|
|
607
|
+
})
|
|
608
|
+
});
|
|
609
|
+
form.field("title").addAfterChange((value, f)=>{
|
|
610
|
+
received.push(value);
|
|
611
|
+
f.field("path").setValue("/" + String(value).toLowerCase());
|
|
612
|
+
});
|
|
613
|
+
form.field("title").setValue("Hello");
|
|
614
|
+
expect(received).toEqual([
|
|
615
|
+
"Hello"
|
|
616
|
+
]);
|
|
617
|
+
expect(form.field("path").getValue()).toBe("/hello");
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
describe("layout positional modifiers", ()=>{
|
|
621
|
+
function createFormWithLayout() {
|
|
622
|
+
return createForm({
|
|
623
|
+
fields: (fields)=>({
|
|
624
|
+
title: fields.text().label("Title"),
|
|
625
|
+
path: fields.text().label("Path"),
|
|
626
|
+
description: fields.text().label("Description")
|
|
627
|
+
}),
|
|
628
|
+
layout: (layout)=>[
|
|
629
|
+
layout.row("title"),
|
|
630
|
+
layout.row("path"),
|
|
631
|
+
layout.row("description")
|
|
632
|
+
]
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
it("should insert a row before a target", ()=>{
|
|
636
|
+
const form = createFormWithLayout();
|
|
637
|
+
form.fields((fields)=>({
|
|
638
|
+
language: fields.text().label("Language").options([])
|
|
639
|
+
}));
|
|
640
|
+
form.layout((layout)=>[
|
|
641
|
+
layout.row("language").before("path")
|
|
642
|
+
]);
|
|
643
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
644
|
+
expect(names).toEqual([
|
|
645
|
+
"title",
|
|
646
|
+
"language",
|
|
647
|
+
"path",
|
|
648
|
+
"description"
|
|
649
|
+
]);
|
|
650
|
+
});
|
|
651
|
+
it("should insert a row after a target", ()=>{
|
|
652
|
+
const form = createFormWithLayout();
|
|
653
|
+
form.fields((fields)=>({
|
|
654
|
+
language: fields.text().label("Language").options([])
|
|
655
|
+
}));
|
|
656
|
+
form.layout((layout)=>[
|
|
657
|
+
layout.row("language").after("path")
|
|
658
|
+
]);
|
|
659
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
660
|
+
expect(names).toEqual([
|
|
661
|
+
"title",
|
|
662
|
+
"path",
|
|
663
|
+
"language",
|
|
664
|
+
"description"
|
|
665
|
+
]);
|
|
666
|
+
});
|
|
667
|
+
it("should replace a target row", ()=>{
|
|
668
|
+
const form = createFormWithLayout();
|
|
669
|
+
form.fields((fields)=>({
|
|
670
|
+
slug: fields.text().label("Slug")
|
|
671
|
+
}));
|
|
672
|
+
form.layout((layout)=>[
|
|
673
|
+
layout.row("slug").replace("path")
|
|
674
|
+
]);
|
|
675
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
676
|
+
expect(names).toEqual([
|
|
677
|
+
"title",
|
|
678
|
+
"slug",
|
|
679
|
+
"description"
|
|
680
|
+
]);
|
|
681
|
+
});
|
|
682
|
+
it("should remove a field from layout", ()=>{
|
|
683
|
+
const form = createFormWithLayout();
|
|
684
|
+
form.layout((layout)=>{
|
|
685
|
+
layout.remove("path");
|
|
686
|
+
return [];
|
|
687
|
+
});
|
|
688
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
689
|
+
expect(names).toEqual([
|
|
690
|
+
"title",
|
|
691
|
+
"description"
|
|
692
|
+
]);
|
|
693
|
+
});
|
|
694
|
+
it("should append when no position is specified", ()=>{
|
|
695
|
+
const form = createFormWithLayout();
|
|
696
|
+
form.fields((fields)=>({
|
|
697
|
+
language: fields.text().label("Language").options([])
|
|
698
|
+
}));
|
|
699
|
+
form.layout((layout)=>[
|
|
700
|
+
layout.row("language")
|
|
701
|
+
]);
|
|
702
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
703
|
+
expect(names).toEqual([
|
|
704
|
+
"title",
|
|
705
|
+
"path",
|
|
706
|
+
"description",
|
|
707
|
+
"language"
|
|
708
|
+
]);
|
|
709
|
+
});
|
|
710
|
+
it("should append when target is not found", ()=>{
|
|
711
|
+
const form = createFormWithLayout();
|
|
712
|
+
form.fields((fields)=>({
|
|
713
|
+
language: fields.text().label("Language").options([])
|
|
714
|
+
}));
|
|
715
|
+
form.layout((layout)=>[
|
|
716
|
+
layout.row("language").after("nonexistent")
|
|
717
|
+
]);
|
|
718
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
719
|
+
expect(names).toEqual([
|
|
720
|
+
"title",
|
|
721
|
+
"path",
|
|
722
|
+
"description",
|
|
723
|
+
"language"
|
|
724
|
+
]);
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
describe("IFormModifier integration", ()=>{
|
|
728
|
+
it("should support a full modifier workflow: add field + position in layout + append callbacks", ()=>{
|
|
729
|
+
const form = createForm({
|
|
730
|
+
fields: (fields)=>({
|
|
731
|
+
title: fields.text().label("Title").required("Title is required"),
|
|
732
|
+
path: fields.text().label("Path").required("Path is required").beforeChange((value)=>{
|
|
733
|
+
const str = String(value);
|
|
734
|
+
return "/" + str.replace(/^\//, "").toLowerCase().replace(/[^a-z0-9/-]+/g, "-").replace(/^-|-$/g, "");
|
|
735
|
+
})
|
|
736
|
+
}),
|
|
737
|
+
layout: (layout)=>[
|
|
738
|
+
layout.row("title"),
|
|
739
|
+
layout.row("path")
|
|
740
|
+
]
|
|
741
|
+
});
|
|
742
|
+
const modifier = {
|
|
743
|
+
modify (form) {
|
|
744
|
+
form.fields((fields)=>({
|
|
745
|
+
language: fields.text().label("Language").options([
|
|
746
|
+
{
|
|
747
|
+
label: "English",
|
|
748
|
+
value: "en"
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
label: "German",
|
|
752
|
+
value: "de"
|
|
753
|
+
}
|
|
754
|
+
]).afterChange((value, f)=>{
|
|
755
|
+
const current = String(f.field("path").getValue() || "");
|
|
756
|
+
const stripped = current.replace(/^\/[a-z]{2}\//, "/");
|
|
757
|
+
if (value && "en" !== value) f.field("path").setValue("/" + value + stripped);
|
|
758
|
+
else f.field("path").setValue(stripped);
|
|
759
|
+
})
|
|
760
|
+
}));
|
|
761
|
+
form.layout((layout)=>[
|
|
762
|
+
layout.row("language").after("path")
|
|
763
|
+
]);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
modifier.modify(form);
|
|
767
|
+
const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
|
|
768
|
+
expect(names).toEqual([
|
|
769
|
+
"title",
|
|
770
|
+
"path",
|
|
771
|
+
"language"
|
|
772
|
+
]);
|
|
773
|
+
form.field("path").setValue("/demo");
|
|
774
|
+
form.field("language").setValue("de");
|
|
775
|
+
expect(form.field("path").getValue()).toBe("/de/demo");
|
|
776
|
+
const data = form.getData();
|
|
777
|
+
expect(data.language).toBe("de");
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
describe("layout system expansion (Phase 5)", ()=>{
|
|
782
|
+
describe("separator", ()=>{
|
|
783
|
+
it("should include separator nodes in the resolved layout", ()=>{
|
|
784
|
+
const form = createForm({
|
|
785
|
+
fields: (fields)=>({
|
|
786
|
+
title: fields.text().label("Title"),
|
|
787
|
+
description: fields.text().label("Description")
|
|
788
|
+
}),
|
|
789
|
+
layout: (layout)=>[
|
|
790
|
+
layout.row("title"),
|
|
791
|
+
layout.separator(),
|
|
792
|
+
layout.row("description")
|
|
793
|
+
]
|
|
794
|
+
});
|
|
795
|
+
const vm = form.vm;
|
|
796
|
+
expect(vm.layout).toHaveLength(3);
|
|
797
|
+
expect(vm.layout[0].type).toBe("row");
|
|
798
|
+
expect(vm.layout[1].type).toBe("separator");
|
|
799
|
+
expect(vm.layout[2].type).toBe("row");
|
|
800
|
+
});
|
|
801
|
+
it("should support separator via modifier layout API", ()=>{
|
|
802
|
+
const form = createForm({
|
|
803
|
+
fields: (fields)=>({
|
|
804
|
+
title: fields.text().label("Title"),
|
|
805
|
+
description: fields.text().label("Description")
|
|
806
|
+
}),
|
|
807
|
+
layout: (layout)=>[
|
|
808
|
+
layout.row("title"),
|
|
809
|
+
layout.row("description")
|
|
810
|
+
]
|
|
811
|
+
});
|
|
812
|
+
form.layout((layout)=>[
|
|
813
|
+
layout.separator().after("title")
|
|
814
|
+
]);
|
|
815
|
+
expect(form.vm.layout).toHaveLength(3);
|
|
816
|
+
expect(form.vm.layout[1].type).toBe("separator");
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
describe("tabs", ()=>{
|
|
820
|
+
function createFormWithTabs() {
|
|
821
|
+
return createForm({
|
|
822
|
+
fields: (fields)=>({
|
|
823
|
+
title: fields.text().label("Title"),
|
|
824
|
+
slug: fields.text().label("Slug"),
|
|
825
|
+
description: fields.text().label("Description"),
|
|
826
|
+
metaTitle: fields.text().label("Meta Title"),
|
|
827
|
+
metaDescription: fields.text().label("Meta Description")
|
|
828
|
+
}),
|
|
829
|
+
layout: (layout)=>[
|
|
830
|
+
layout.row("title", "slug"),
|
|
831
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
832
|
+
tab.label("General").layout((layout)=>[
|
|
833
|
+
layout.row("description")
|
|
834
|
+
]);
|
|
835
|
+
}).tab("seo", (tab)=>{
|
|
836
|
+
tab.label("SEO").description("Optimize how this page appears in search").layout((layout)=>[
|
|
837
|
+
layout.row("metaTitle"),
|
|
838
|
+
layout.row("metaDescription")
|
|
839
|
+
]);
|
|
840
|
+
})
|
|
841
|
+
]
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
it("should resolve tabs layout node with tab definitions", ()=>{
|
|
845
|
+
const form = createFormWithTabs();
|
|
846
|
+
const vm = form.vm;
|
|
847
|
+
expect(vm.layout).toHaveLength(2);
|
|
848
|
+
expect(vm.layout[0].type).toBe("row");
|
|
849
|
+
expect(vm.layout[1].type).toBe("tabs");
|
|
850
|
+
const tabsNode = vm.layout[1];
|
|
851
|
+
expect(tabsNode.id).toBe("settings");
|
|
852
|
+
expect(tabsNode.tabs).toHaveLength(2);
|
|
853
|
+
expect(tabsNode.tabs[0].id).toBe("general");
|
|
854
|
+
expect(tabsNode.tabs[0].label).toBe("General");
|
|
855
|
+
expect(tabsNode.tabs[1].id).toBe("seo");
|
|
856
|
+
expect(tabsNode.tabs[1].label).toBe("SEO");
|
|
857
|
+
expect(tabsNode.tabs[1].description).toBe("Optimize how this page appears in search");
|
|
858
|
+
});
|
|
859
|
+
it("should resolve fields inside tab layouts", ()=>{
|
|
860
|
+
const form = createFormWithTabs();
|
|
861
|
+
const tabsNode = form.vm.layout[1];
|
|
862
|
+
const generalTab = tabsNode.tabs[0];
|
|
863
|
+
expect(generalTab.layout).toHaveLength(1);
|
|
864
|
+
expect(generalTab.layout[0].type).toBe("row");
|
|
865
|
+
const generalRow = generalTab.layout[0];
|
|
866
|
+
expect(generalRow.fields[0].name).toBe("description");
|
|
867
|
+
const seoTab = tabsNode.tabs[1];
|
|
868
|
+
expect(seoTab.layout).toHaveLength(2);
|
|
869
|
+
});
|
|
870
|
+
it("should default activeTabId to the first tab", ()=>{
|
|
871
|
+
const form = createFormWithTabs();
|
|
872
|
+
const tabsNode = form.vm.layout[1];
|
|
873
|
+
expect(tabsNode.activeTabId).toBe("general");
|
|
874
|
+
});
|
|
875
|
+
it("should switch active tab via setActiveTab", ()=>{
|
|
876
|
+
const form = createFormWithTabs();
|
|
877
|
+
let tabsNode = form.vm.layout[1];
|
|
878
|
+
tabsNode.setActiveTab("seo");
|
|
879
|
+
tabsNode = form.vm.layout[1];
|
|
880
|
+
expect(tabsNode.activeTabId).toBe("seo");
|
|
881
|
+
});
|
|
882
|
+
it("should fall back to first tab when active tab ID is invalid", ()=>{
|
|
883
|
+
const form = createFormWithTabs();
|
|
884
|
+
let tabsNode = form.vm.layout[1];
|
|
885
|
+
tabsNode.setActiveTab("nonexistent");
|
|
886
|
+
tabsNode = form.vm.layout[1];
|
|
887
|
+
expect(tabsNode.activeTabId).toBe("general");
|
|
888
|
+
});
|
|
889
|
+
it("should compute hasErrors for tabs based on referenced fields", async ()=>{
|
|
890
|
+
const form = createForm({
|
|
891
|
+
fields: (fields)=>({
|
|
892
|
+
title: fields.text().label("Title").required("Title is required"),
|
|
893
|
+
metaTitle: fields.text().label("Meta Title").required("Required")
|
|
894
|
+
}),
|
|
895
|
+
layout: (layout)=>[
|
|
896
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
897
|
+
tab.label("General").layout((layout)=>[
|
|
898
|
+
layout.row("title")
|
|
899
|
+
]);
|
|
900
|
+
}).tab("seo", (tab)=>{
|
|
901
|
+
tab.label("SEO").layout((layout)=>[
|
|
902
|
+
layout.row("metaTitle")
|
|
903
|
+
]);
|
|
904
|
+
})
|
|
905
|
+
]
|
|
906
|
+
});
|
|
907
|
+
let tabsNode = form.vm.layout[0];
|
|
908
|
+
expect(tabsNode.tabs[0].hasErrors).toBe(false);
|
|
909
|
+
expect(tabsNode.tabs[1].hasErrors).toBe(false);
|
|
910
|
+
form.field("title").setValue("Hello");
|
|
911
|
+
await form.validate();
|
|
912
|
+
tabsNode = form.vm.layout[0];
|
|
913
|
+
expect(tabsNode.tabs[0].hasErrors).toBe(false);
|
|
914
|
+
expect(tabsNode.tabs[1].hasErrors).toBe(true);
|
|
915
|
+
});
|
|
916
|
+
it("should not warn about fields inside tabs as orphans", ()=>{
|
|
917
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
|
|
918
|
+
createForm({
|
|
919
|
+
fields: (fields)=>({
|
|
920
|
+
title: fields.text().label("Title"),
|
|
921
|
+
description: fields.text().label("Description")
|
|
922
|
+
}),
|
|
923
|
+
layout: (layout)=>[
|
|
924
|
+
layout.row("title"),
|
|
925
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
926
|
+
tab.label("General").layout((layout)=>[
|
|
927
|
+
layout.row("description")
|
|
928
|
+
]);
|
|
929
|
+
})
|
|
930
|
+
]
|
|
931
|
+
});
|
|
932
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
933
|
+
warnSpy.mockRestore();
|
|
934
|
+
});
|
|
935
|
+
it("should return null for tabs with empty tabs array", ()=>{
|
|
936
|
+
const form = createForm({
|
|
937
|
+
fields: (fields)=>({
|
|
938
|
+
title: fields.text().label("Title")
|
|
939
|
+
}),
|
|
940
|
+
layout: (layout)=>[
|
|
941
|
+
layout.row("title"),
|
|
942
|
+
layout.tabs("empty")
|
|
943
|
+
]
|
|
944
|
+
});
|
|
945
|
+
expect(form.vm.layout).toHaveLength(1);
|
|
946
|
+
expect(form.vm.layout[0].type).toBe("row");
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
describe("element", ()=>{
|
|
950
|
+
it("should include element nodes in the resolved layout", ()=>{
|
|
951
|
+
const form = createForm({
|
|
952
|
+
fields: (fields)=>({
|
|
953
|
+
title: fields.text().label("Title")
|
|
954
|
+
}),
|
|
955
|
+
layout: (layout)=>[
|
|
956
|
+
layout.row("title"),
|
|
957
|
+
layout.element("usage-stats", {
|
|
958
|
+
plan: "enterprise"
|
|
959
|
+
})
|
|
960
|
+
]
|
|
961
|
+
});
|
|
962
|
+
const vm = form.vm;
|
|
963
|
+
expect(vm.layout).toHaveLength(2);
|
|
964
|
+
expect(vm.layout[1].type).toBe("element");
|
|
965
|
+
const elementNode = vm.layout[1];
|
|
966
|
+
expect(elementNode.renderer).toBe("usage-stats");
|
|
967
|
+
expect(elementNode.props).toEqual({
|
|
968
|
+
plan: "enterprise"
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
it("should support element without props", ()=>{
|
|
972
|
+
const form = createForm({
|
|
973
|
+
fields: (fields)=>({
|
|
974
|
+
title: fields.text().label("Title")
|
|
975
|
+
}),
|
|
976
|
+
layout: (layout)=>[
|
|
977
|
+
layout.row("title"),
|
|
978
|
+
layout.element("divider")
|
|
979
|
+
]
|
|
980
|
+
});
|
|
981
|
+
const elementNode = form.vm.layout[1];
|
|
982
|
+
expect(elementNode.renderer).toBe("divider");
|
|
983
|
+
expect(elementNode.props).toBeUndefined();
|
|
984
|
+
});
|
|
985
|
+
});
|
|
986
|
+
describe("named layout node access — form.layout(nodeId)", ()=>{
|
|
987
|
+
function createFormWithTabs() {
|
|
988
|
+
return createForm({
|
|
989
|
+
fields: (fields)=>({
|
|
990
|
+
title: fields.text().label("Title"),
|
|
991
|
+
description: fields.text().label("Description"),
|
|
992
|
+
metaTitle: fields.text().label("Meta Title")
|
|
993
|
+
}),
|
|
994
|
+
layout: (layout)=>[
|
|
995
|
+
layout.row("title"),
|
|
996
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
997
|
+
tab.label("General").layout((layout)=>[
|
|
998
|
+
layout.row("description")
|
|
999
|
+
]);
|
|
1000
|
+
}).tab("seo", (tab)=>{
|
|
1001
|
+
tab.label("SEO").layout((layout)=>[
|
|
1002
|
+
layout.row("metaTitle")
|
|
1003
|
+
]);
|
|
1004
|
+
})
|
|
1005
|
+
]
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
it("should access a tabs node by ID and add a new tab", ()=>{
|
|
1009
|
+
const form = createFormWithTabs();
|
|
1010
|
+
form.fields((fields)=>({
|
|
1011
|
+
trackingId: fields.text().label("Tracking ID")
|
|
1012
|
+
}));
|
|
1013
|
+
form.layout("settings").as("tabs").tab("analytics", (tab)=>{
|
|
1014
|
+
tab.label("Analytics").layout((layout)=>[
|
|
1015
|
+
layout.row("trackingId")
|
|
1016
|
+
]);
|
|
1017
|
+
}).after("seo");
|
|
1018
|
+
const tabsNode = form.vm.layout[1];
|
|
1019
|
+
expect(tabsNode.tabs).toHaveLength(3);
|
|
1020
|
+
expect(tabsNode.tabs[2].id).toBe("analytics");
|
|
1021
|
+
expect(tabsNode.tabs[2].label).toBe("Analytics");
|
|
1022
|
+
});
|
|
1023
|
+
it("should add a tab before an existing tab", ()=>{
|
|
1024
|
+
const form = createFormWithTabs();
|
|
1025
|
+
form.fields((fields)=>({
|
|
1026
|
+
trackingId: fields.text().label("Tracking ID")
|
|
1027
|
+
}));
|
|
1028
|
+
form.layout("settings").as("tabs").tab("analytics", (tab)=>{
|
|
1029
|
+
tab.label("Analytics").layout((layout)=>[
|
|
1030
|
+
layout.row("trackingId")
|
|
1031
|
+
]);
|
|
1032
|
+
}).before("seo");
|
|
1033
|
+
const tabsNode = form.vm.layout[1];
|
|
1034
|
+
expect(tabsNode.tabs).toHaveLength(3);
|
|
1035
|
+
expect(tabsNode.tabs[0].id).toBe("general");
|
|
1036
|
+
expect(tabsNode.tabs[1].id).toBe("analytics");
|
|
1037
|
+
expect(tabsNode.tabs[2].id).toBe("seo");
|
|
1038
|
+
});
|
|
1039
|
+
it("should throw when accessing a non-existent node ID", ()=>{
|
|
1040
|
+
const form = createFormWithTabs();
|
|
1041
|
+
expect(()=>form.layout("nonexistent").as("tabs")).toThrow('Layout node "nonexistent" not found.');
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
describe("positional modifiers targeting tabs/element nodes", ()=>{
|
|
1045
|
+
it("should insert a row before a tabs node by ID", ()=>{
|
|
1046
|
+
const form = createForm({
|
|
1047
|
+
fields: (fields)=>({
|
|
1048
|
+
title: fields.text().label("Title"),
|
|
1049
|
+
subtitle: fields.text().label("Subtitle"),
|
|
1050
|
+
description: fields.text().label("Description")
|
|
1051
|
+
}),
|
|
1052
|
+
layout: (layout)=>[
|
|
1053
|
+
layout.row("title"),
|
|
1054
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
1055
|
+
tab.label("General").layout((layout)=>[
|
|
1056
|
+
layout.row("description")
|
|
1057
|
+
]);
|
|
1058
|
+
})
|
|
1059
|
+
]
|
|
1060
|
+
});
|
|
1061
|
+
form.layout((layout)=>[
|
|
1062
|
+
layout.row("subtitle").before("settings")
|
|
1063
|
+
]);
|
|
1064
|
+
expect(form.vm.layout).toHaveLength(3);
|
|
1065
|
+
expect(form.vm.layout[0].type).toBe("row");
|
|
1066
|
+
expect(form.vm.layout[1].type).toBe("row");
|
|
1067
|
+
expect(form.vm.layout[2].type).toBe("tabs");
|
|
1068
|
+
const row = form.vm.layout[1];
|
|
1069
|
+
expect(row.fields[0].name).toBe("subtitle");
|
|
1070
|
+
});
|
|
1071
|
+
it("should remove a tabs node by ID", ()=>{
|
|
1072
|
+
const form = createForm({
|
|
1073
|
+
fields: (fields)=>({
|
|
1074
|
+
title: fields.text().label("Title"),
|
|
1075
|
+
description: fields.text().label("Description")
|
|
1076
|
+
}),
|
|
1077
|
+
layout: (layout)=>[
|
|
1078
|
+
layout.row("title"),
|
|
1079
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
1080
|
+
tab.label("General").layout((layout)=>[
|
|
1081
|
+
layout.row("description")
|
|
1082
|
+
]);
|
|
1083
|
+
})
|
|
1084
|
+
]
|
|
1085
|
+
});
|
|
1086
|
+
form.layout((layout)=>{
|
|
1087
|
+
layout.remove("settings");
|
|
1088
|
+
return [];
|
|
1089
|
+
});
|
|
1090
|
+
expect(form.vm.layout).toHaveLength(1);
|
|
1091
|
+
expect(form.vm.layout[0].type).toBe("row");
|
|
1092
|
+
});
|
|
1093
|
+
it("should replace a tabs node by ID", ()=>{
|
|
1094
|
+
const form = createForm({
|
|
1095
|
+
fields: (fields)=>({
|
|
1096
|
+
title: fields.text().label("Title"),
|
|
1097
|
+
description: fields.text().label("Description"),
|
|
1098
|
+
metaTitle: fields.text().label("Meta Title")
|
|
1099
|
+
}),
|
|
1100
|
+
layout: (layout)=>[
|
|
1101
|
+
layout.row("title"),
|
|
1102
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
1103
|
+
tab.label("General").layout((layout)=>[
|
|
1104
|
+
layout.row("description")
|
|
1105
|
+
]);
|
|
1106
|
+
})
|
|
1107
|
+
]
|
|
1108
|
+
});
|
|
1109
|
+
form.layout((layout)=>[
|
|
1110
|
+
layout.row("metaTitle").replace("settings")
|
|
1111
|
+
]);
|
|
1112
|
+
expect(form.vm.layout).toHaveLength(2);
|
|
1113
|
+
expect(form.vm.layout[0].type).toBe("row");
|
|
1114
|
+
expect(form.vm.layout[1].type).toBe("row");
|
|
1115
|
+
const row = form.vm.layout[1];
|
|
1116
|
+
expect(row.fields[0].name).toBe("metaTitle");
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
describe("modifier integration with tabs", ()=>{
|
|
1120
|
+
it("should support a full modifier workflow: base form with tabs + modifier adds tab + modifier appends to existing tab", ()=>{
|
|
1121
|
+
const form = createForm({
|
|
1122
|
+
fields: (fields)=>({
|
|
1123
|
+
title: fields.text().label("Title"),
|
|
1124
|
+
description: fields.text().label("Description"),
|
|
1125
|
+
metaTitle: fields.text().label("Meta Title")
|
|
1126
|
+
}),
|
|
1127
|
+
layout: (layout)=>[
|
|
1128
|
+
layout.row("title"),
|
|
1129
|
+
layout.separator(),
|
|
1130
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
1131
|
+
tab.label("General").layout((layout)=>[
|
|
1132
|
+
layout.row("description")
|
|
1133
|
+
]);
|
|
1134
|
+
}).tab("seo", (tab)=>{
|
|
1135
|
+
tab.label("SEO").layout((layout)=>[
|
|
1136
|
+
layout.row("metaTitle")
|
|
1137
|
+
]);
|
|
1138
|
+
})
|
|
1139
|
+
]
|
|
1140
|
+
});
|
|
1141
|
+
form.fields((fields)=>({
|
|
1142
|
+
trackingId: fields.text().label("Tracking ID")
|
|
1143
|
+
}));
|
|
1144
|
+
form.layout("settings").as("tabs").tab("analytics", (tab)=>{
|
|
1145
|
+
tab.label("Analytics").layout((layout)=>[
|
|
1146
|
+
layout.row("trackingId")
|
|
1147
|
+
]);
|
|
1148
|
+
}).after("seo");
|
|
1149
|
+
const vm = form.vm;
|
|
1150
|
+
expect(vm.layout).toHaveLength(3);
|
|
1151
|
+
expect(vm.layout[0].type).toBe("row");
|
|
1152
|
+
expect(vm.layout[1].type).toBe("separator");
|
|
1153
|
+
expect(vm.layout[2].type).toBe("tabs");
|
|
1154
|
+
const tabsNode = vm.layout[2];
|
|
1155
|
+
expect(tabsNode.tabs).toHaveLength(3);
|
|
1156
|
+
expect(tabsNode.tabs[0].id).toBe("general");
|
|
1157
|
+
expect(tabsNode.tabs[1].id).toBe("seo");
|
|
1158
|
+
expect(tabsNode.tabs[2].id).toBe("analytics");
|
|
1159
|
+
const seoTab = tabsNode.tabs[1];
|
|
1160
|
+
expect(seoTab.layout).toHaveLength(1);
|
|
1161
|
+
const data = form.getData();
|
|
1162
|
+
expect(data).toHaveProperty("trackingId");
|
|
1163
|
+
});
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
describe("object fields (Phase 6)", ()=>{
|
|
1167
|
+
describe("non-list object field", ()=>{
|
|
1168
|
+
function createFormWithObject() {
|
|
1169
|
+
return createForm({
|
|
1170
|
+
fields: (fields)=>({
|
|
1171
|
+
title: fields.text().label("Title"),
|
|
1172
|
+
address: fields.object().label("Address").fields((f)=>({
|
|
1173
|
+
street: f.text().label("Street").required("Street is required"),
|
|
1174
|
+
city: f.text().label("City").required("City is required")
|
|
1175
|
+
}))
|
|
1176
|
+
}),
|
|
1177
|
+
layout: (layout)=>[
|
|
1178
|
+
layout.row("title"),
|
|
1179
|
+
layout.row("address")
|
|
1180
|
+
]
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
it("should create an object field with children", ()=>{
|
|
1184
|
+
const form = createFormWithObject();
|
|
1185
|
+
const field = form.field("address");
|
|
1186
|
+
expect(field).toBeDefined();
|
|
1187
|
+
expect(field.type).toBe("object");
|
|
1188
|
+
});
|
|
1189
|
+
it("should access child fields via dot-notation", ()=>{
|
|
1190
|
+
const form = createFormWithObject();
|
|
1191
|
+
const street = form.field("address.street");
|
|
1192
|
+
expect(street).toBeDefined();
|
|
1193
|
+
expect(street.type).toBe("text");
|
|
1194
|
+
});
|
|
1195
|
+
it("should throw on invalid dot-notation", ()=>{
|
|
1196
|
+
const form = createFormWithObject();
|
|
1197
|
+
expect(()=>form.field("address.zipcode")).toThrow('Field "address.zipcode" not found.');
|
|
1198
|
+
});
|
|
1199
|
+
it("should return nested object from getData", ()=>{
|
|
1200
|
+
const form = createFormWithObject();
|
|
1201
|
+
form.field("address.street").setValue("123 Main St");
|
|
1202
|
+
form.field("address.city").setValue("Springfield");
|
|
1203
|
+
const data = form.getData();
|
|
1204
|
+
expect(data.address).toEqual({
|
|
1205
|
+
street: "123 Main St",
|
|
1206
|
+
city: "Springfield"
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
it("should hydrate children from setData", ()=>{
|
|
1210
|
+
const form = createFormWithObject();
|
|
1211
|
+
form.setData({
|
|
1212
|
+
title: "Home",
|
|
1213
|
+
address: {
|
|
1214
|
+
street: "456 Oak Ave",
|
|
1215
|
+
city: "Portland"
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
expect(form.field("address.street").getValue()).toBe("456 Oak Ave");
|
|
1219
|
+
expect(form.field("address.city").getValue()).toBe("Portland");
|
|
1220
|
+
});
|
|
1221
|
+
it("should not be dirty after setData", ()=>{
|
|
1222
|
+
const form = createFormWithObject();
|
|
1223
|
+
form.setData({
|
|
1224
|
+
title: "Home",
|
|
1225
|
+
address: {
|
|
1226
|
+
street: "456 Oak Ave",
|
|
1227
|
+
city: "Portland"
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
expect(form.isDirty).toBe(false);
|
|
1231
|
+
});
|
|
1232
|
+
it("should be dirty after changing a child field", ()=>{
|
|
1233
|
+
const form = createFormWithObject();
|
|
1234
|
+
form.setData({
|
|
1235
|
+
title: "Home",
|
|
1236
|
+
address: {
|
|
1237
|
+
street: "456 Oak Ave",
|
|
1238
|
+
city: "Portland"
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
form.field("address.street").setValue("789 Pine Rd");
|
|
1242
|
+
expect(form.isDirty).toBe(true);
|
|
1243
|
+
});
|
|
1244
|
+
it("should validate children recursively", async ()=>{
|
|
1245
|
+
const form = createFormWithObject();
|
|
1246
|
+
const valid = await form.validate();
|
|
1247
|
+
expect(valid).toBe(false);
|
|
1248
|
+
expect(form.errors.length).toBeGreaterThan(0);
|
|
1249
|
+
expect(form.errors.some((e)=>"address" === e.path)).toBe(true);
|
|
1250
|
+
});
|
|
1251
|
+
it("should pass validation when children are valid", async ()=>{
|
|
1252
|
+
const form = createFormWithObject();
|
|
1253
|
+
form.field("title").setValue("Home");
|
|
1254
|
+
form.field("address.street").setValue("123 Main St");
|
|
1255
|
+
form.field("address.city").setValue("Springfield");
|
|
1256
|
+
const valid = await form.validate();
|
|
1257
|
+
expect(valid).toBe(true);
|
|
1258
|
+
});
|
|
1259
|
+
it("should expose IObjectFieldVM from field.vm", ()=>{
|
|
1260
|
+
const form = createFormWithObject();
|
|
1261
|
+
form.field("address.street").setValue("123 Main St");
|
|
1262
|
+
form.field("address.city").setValue("Springfield");
|
|
1263
|
+
const vm = form.field("address").vm;
|
|
1264
|
+
expect(vm.type).toBe("object");
|
|
1265
|
+
expect(vm.isList).toBe(false);
|
|
1266
|
+
expect(vm.fields).toHaveLength(2);
|
|
1267
|
+
expect(vm.fields[0].name).toBe("street");
|
|
1268
|
+
expect(vm.fields[0].value).toBe("123 Main St");
|
|
1269
|
+
expect(vm.fields[1].name).toBe("city");
|
|
1270
|
+
expect(vm.items).toHaveLength(0);
|
|
1271
|
+
});
|
|
1272
|
+
it("should render object field in a row via default layout", ()=>{
|
|
1273
|
+
const form = createForm({
|
|
1274
|
+
fields: (fields)=>({
|
|
1275
|
+
address: fields.object().label("Address").fields((f)=>({
|
|
1276
|
+
street: f.text().label("Street"),
|
|
1277
|
+
city: f.text().label("City")
|
|
1278
|
+
}))
|
|
1279
|
+
})
|
|
1280
|
+
});
|
|
1281
|
+
const vm = form.vm;
|
|
1282
|
+
expect(vm.layout).toHaveLength(1);
|
|
1283
|
+
expect(vm.layout[0].type).toBe("row");
|
|
1284
|
+
const row = asRow(vm.layout[0]);
|
|
1285
|
+
expect(row.fields[0].type).toBe("object");
|
|
1286
|
+
expect(row.fields[0].name).toBe("address");
|
|
1287
|
+
});
|
|
1288
|
+
it("should reset to baseline values", ()=>{
|
|
1289
|
+
const form = createFormWithObject();
|
|
1290
|
+
form.setData({
|
|
1291
|
+
title: "Home",
|
|
1292
|
+
address: {
|
|
1293
|
+
street: "Original St",
|
|
1294
|
+
city: "Original City"
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1297
|
+
form.field("address.street").setValue("Changed St");
|
|
1298
|
+
expect(form.isDirty).toBe(true);
|
|
1299
|
+
form.reset();
|
|
1300
|
+
expect(form.field("address.street").getValue()).toBe("Original St");
|
|
1301
|
+
expect(form.isDirty).toBe(false);
|
|
1302
|
+
});
|
|
1303
|
+
it("should submit nested data when valid", async ()=>{
|
|
1304
|
+
const form = createFormWithObject();
|
|
1305
|
+
form.field("title").setValue("Home");
|
|
1306
|
+
form.field("address.street").setValue("123 Main St");
|
|
1307
|
+
form.field("address.city").setValue("Springfield");
|
|
1308
|
+
const result = await form.submit();
|
|
1309
|
+
expect(result).toEqual({
|
|
1310
|
+
title: "Home",
|
|
1311
|
+
address: {
|
|
1312
|
+
street: "123 Main St",
|
|
1313
|
+
city: "Springfield"
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
});
|
|
1317
|
+
});
|
|
1318
|
+
describe("list object field", ()=>{
|
|
1319
|
+
function createFormWithList() {
|
|
1320
|
+
return createForm({
|
|
1321
|
+
fields: (fields)=>({
|
|
1322
|
+
presets: fields.object().label("Presets").fields((f)=>({
|
|
1323
|
+
name: f.text().label("Name").required("Name is required"),
|
|
1324
|
+
model: f.text().label("Model").required("Model is required")
|
|
1325
|
+
})).list()
|
|
1326
|
+
}),
|
|
1327
|
+
layout: (layout)=>[
|
|
1328
|
+
layout.row("presets")
|
|
1329
|
+
]
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
it("should create a list object field", ()=>{
|
|
1333
|
+
const form = createFormWithList();
|
|
1334
|
+
const field = form.field("presets");
|
|
1335
|
+
expect(field.type).toBe("object");
|
|
1336
|
+
});
|
|
1337
|
+
it("should return empty array from getData initially", ()=>{
|
|
1338
|
+
const form = createFormWithList();
|
|
1339
|
+
expect(form.getData().presets).toEqual([]);
|
|
1340
|
+
});
|
|
1341
|
+
it("should add items via the field VM", ()=>{
|
|
1342
|
+
const form = createFormWithList();
|
|
1343
|
+
const vm = form.field("presets").vm;
|
|
1344
|
+
expect(vm.isList).toBe(true);
|
|
1345
|
+
expect(vm.items).toHaveLength(0);
|
|
1346
|
+
vm.addItem();
|
|
1347
|
+
const updatedVm = form.field("presets").vm;
|
|
1348
|
+
expect(updatedVm.items).toHaveLength(1);
|
|
1349
|
+
expect(updatedVm.items[0].fields).toHaveLength(2);
|
|
1350
|
+
expect(updatedVm.items[0].fields[0].name).toBe("name");
|
|
1351
|
+
});
|
|
1352
|
+
it("should hydrate list from setData", ()=>{
|
|
1353
|
+
const form = createFormWithList();
|
|
1354
|
+
form.setData({
|
|
1355
|
+
presets: [
|
|
1356
|
+
{
|
|
1357
|
+
name: "Default",
|
|
1358
|
+
model: "claude-sonnet"
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
name: "Fast",
|
|
1362
|
+
model: "claude-haiku"
|
|
1363
|
+
}
|
|
1364
|
+
]
|
|
1365
|
+
});
|
|
1366
|
+
const data = form.getData();
|
|
1367
|
+
expect(data.presets).toEqual([
|
|
1368
|
+
{
|
|
1369
|
+
name: "Default",
|
|
1370
|
+
model: "claude-sonnet"
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
name: "Fast",
|
|
1374
|
+
model: "claude-haiku"
|
|
1375
|
+
}
|
|
1376
|
+
]);
|
|
1377
|
+
});
|
|
1378
|
+
it("should expose items via IObjectFieldVM", ()=>{
|
|
1379
|
+
const form = createFormWithList();
|
|
1380
|
+
form.setData({
|
|
1381
|
+
presets: [
|
|
1382
|
+
{
|
|
1383
|
+
name: "Default",
|
|
1384
|
+
model: "claude-sonnet"
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
name: "Fast",
|
|
1388
|
+
model: "claude-haiku"
|
|
1389
|
+
}
|
|
1390
|
+
]
|
|
1391
|
+
});
|
|
1392
|
+
const vm = form.field("presets").vm;
|
|
1393
|
+
expect(vm.items).toHaveLength(2);
|
|
1394
|
+
expect(vm.items[0].fields[0].value).toBe("Default");
|
|
1395
|
+
expect(vm.items[1].fields[1].value).toBe("claude-haiku");
|
|
1396
|
+
});
|
|
1397
|
+
it("should remove items via item.remove()", ()=>{
|
|
1398
|
+
const form = createFormWithList();
|
|
1399
|
+
form.setData({
|
|
1400
|
+
presets: [
|
|
1401
|
+
{
|
|
1402
|
+
name: "Default",
|
|
1403
|
+
model: "claude-sonnet"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
name: "Fast",
|
|
1407
|
+
model: "claude-haiku"
|
|
1408
|
+
}
|
|
1409
|
+
]
|
|
1410
|
+
});
|
|
1411
|
+
const vm = form.field("presets").vm;
|
|
1412
|
+
vm.items[0].remove();
|
|
1413
|
+
expect(form.getData().presets).toEqual([
|
|
1414
|
+
{
|
|
1415
|
+
name: "Fast",
|
|
1416
|
+
model: "claude-haiku"
|
|
1417
|
+
}
|
|
1418
|
+
]);
|
|
1419
|
+
});
|
|
1420
|
+
it("should remove items via removeItem()", ()=>{
|
|
1421
|
+
const form = createFormWithList();
|
|
1422
|
+
form.setData({
|
|
1423
|
+
presets: [
|
|
1424
|
+
{
|
|
1425
|
+
name: "A",
|
|
1426
|
+
model: "a"
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
name: "B",
|
|
1430
|
+
model: "b"
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
name: "C",
|
|
1434
|
+
model: "c"
|
|
1435
|
+
}
|
|
1436
|
+
]
|
|
1437
|
+
});
|
|
1438
|
+
const vm = form.field("presets").vm;
|
|
1439
|
+
vm.removeItem(1);
|
|
1440
|
+
expect(form.getData().presets).toEqual([
|
|
1441
|
+
{
|
|
1442
|
+
name: "A",
|
|
1443
|
+
model: "a"
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
name: "C",
|
|
1447
|
+
model: "c"
|
|
1448
|
+
}
|
|
1449
|
+
]);
|
|
1450
|
+
});
|
|
1451
|
+
it("should validate all items", async ()=>{
|
|
1452
|
+
const form = createFormWithList();
|
|
1453
|
+
form.setData({
|
|
1454
|
+
presets: [
|
|
1455
|
+
{
|
|
1456
|
+
name: "Default",
|
|
1457
|
+
model: "claude-sonnet"
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
name: "",
|
|
1461
|
+
model: ""
|
|
1462
|
+
}
|
|
1463
|
+
]
|
|
1464
|
+
});
|
|
1465
|
+
const valid = await form.validate();
|
|
1466
|
+
expect(valid).toBe(false);
|
|
1467
|
+
});
|
|
1468
|
+
it("should pass validation when all items are valid", async ()=>{
|
|
1469
|
+
const form = createFormWithList();
|
|
1470
|
+
form.setData({
|
|
1471
|
+
presets: [
|
|
1472
|
+
{
|
|
1473
|
+
name: "Default",
|
|
1474
|
+
model: "claude-sonnet"
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
name: "Fast",
|
|
1478
|
+
model: "claude-haiku"
|
|
1479
|
+
}
|
|
1480
|
+
]
|
|
1481
|
+
});
|
|
1482
|
+
const valid = await form.validate();
|
|
1483
|
+
expect(valid).toBe(true);
|
|
1484
|
+
});
|
|
1485
|
+
it("should validate with listSchema", async ()=>{
|
|
1486
|
+
const form = createForm({
|
|
1487
|
+
fields: (fields)=>({
|
|
1488
|
+
presets: fields.object().label("Presets").fields((f)=>({
|
|
1489
|
+
name: f.text().label("Name")
|
|
1490
|
+
})).list().listSchema(z.array(z.any()).min(2, "At least 2 presets are required"))
|
|
1491
|
+
})
|
|
1492
|
+
});
|
|
1493
|
+
form.setData({
|
|
1494
|
+
presets: [
|
|
1495
|
+
{
|
|
1496
|
+
name: "Only one"
|
|
1497
|
+
}
|
|
1498
|
+
]
|
|
1499
|
+
});
|
|
1500
|
+
const valid = await form.validate();
|
|
1501
|
+
expect(valid).toBe(false);
|
|
1502
|
+
expect(form.errors[0].message).toBe("At least 2 presets are required");
|
|
1503
|
+
});
|
|
1504
|
+
it("should be dirty after adding an item", ()=>{
|
|
1505
|
+
const form = createFormWithList();
|
|
1506
|
+
form.setData({
|
|
1507
|
+
presets: []
|
|
1508
|
+
});
|
|
1509
|
+
expect(form.isDirty).toBe(false);
|
|
1510
|
+
const vm = form.field("presets").vm;
|
|
1511
|
+
vm.addItem();
|
|
1512
|
+
expect(form.isDirty).toBe(true);
|
|
1513
|
+
});
|
|
1514
|
+
it("should have stable keys across re-renders", ()=>{
|
|
1515
|
+
const form = createFormWithList();
|
|
1516
|
+
form.setData({
|
|
1517
|
+
presets: [
|
|
1518
|
+
{
|
|
1519
|
+
name: "A",
|
|
1520
|
+
model: "a"
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
name: "B",
|
|
1524
|
+
model: "b"
|
|
1525
|
+
}
|
|
1526
|
+
]
|
|
1527
|
+
});
|
|
1528
|
+
const vm1 = form.field("presets").vm;
|
|
1529
|
+
const key0 = vm1.items[0].key;
|
|
1530
|
+
const key1 = vm1.items[1].key;
|
|
1531
|
+
const vm2 = form.field("presets").vm;
|
|
1532
|
+
expect(vm2.items[0].key).toBe(key0);
|
|
1533
|
+
expect(vm2.items[1].key).toBe(key1);
|
|
1534
|
+
});
|
|
1535
|
+
it("should pass empty list validation when not required", async ()=>{
|
|
1536
|
+
const form = createFormWithList();
|
|
1537
|
+
const valid = await form.validate();
|
|
1538
|
+
expect(valid).toBe(true);
|
|
1539
|
+
});
|
|
1540
|
+
it("should modify item field values via VM onChange", ()=>{
|
|
1541
|
+
const form = createFormWithList();
|
|
1542
|
+
form.setData({
|
|
1543
|
+
presets: [
|
|
1544
|
+
{
|
|
1545
|
+
name: "Default",
|
|
1546
|
+
model: "claude-sonnet"
|
|
1547
|
+
}
|
|
1548
|
+
]
|
|
1549
|
+
});
|
|
1550
|
+
const vm = form.field("presets").vm;
|
|
1551
|
+
vm.items[0].fields[0].onChange("Updated");
|
|
1552
|
+
expect(form.getData().presets[0].name).toBe("Updated");
|
|
1553
|
+
});
|
|
1554
|
+
});
|
|
1555
|
+
describe("hasErrors rollup through tabs", ()=>{
|
|
1556
|
+
it("should report hasErrors in tabs containing object fields", async ()=>{
|
|
1557
|
+
const form = createForm({
|
|
1558
|
+
fields: (fields)=>({
|
|
1559
|
+
title: fields.text().label("Title"),
|
|
1560
|
+
address: fields.object().label("Address").fields((f)=>({
|
|
1561
|
+
street: f.text().label("Street").required("Required")
|
|
1562
|
+
}))
|
|
1563
|
+
}),
|
|
1564
|
+
layout: (layout)=>[
|
|
1565
|
+
layout.tabs("settings").tab("general", (tab)=>{
|
|
1566
|
+
tab.label("General").layout((layout)=>[
|
|
1567
|
+
layout.row("title")
|
|
1568
|
+
]);
|
|
1569
|
+
}).tab("details", (tab)=>{
|
|
1570
|
+
tab.label("Details").layout((layout)=>[
|
|
1571
|
+
layout.row("address")
|
|
1572
|
+
]);
|
|
1573
|
+
})
|
|
1574
|
+
]
|
|
1575
|
+
});
|
|
1576
|
+
form.field("title").setValue("Hello");
|
|
1577
|
+
await form.validate();
|
|
1578
|
+
const tabsNode = form.vm.layout[0];
|
|
1579
|
+
expect(tabsNode.tabs[0].hasErrors).toBe(false);
|
|
1580
|
+
expect(tabsNode.tabs[1].hasErrors).toBe(true);
|
|
1581
|
+
});
|
|
1582
|
+
});
|
|
1583
|
+
});
|
|
1584
|
+
describe("templated object fields (Phase 8a)", ()=>{
|
|
1585
|
+
function createFormWithTemplatedObject() {
|
|
1586
|
+
return createForm({
|
|
1587
|
+
fields: (fields)=>({
|
|
1588
|
+
content: fields.object().label("Content").template("hero", (t)=>{
|
|
1589
|
+
t.label("Hero Banner").fields((f)=>({
|
|
1590
|
+
heading: f.text().label("Heading").required("Required"),
|
|
1591
|
+
image: f.text().label("Image")
|
|
1592
|
+
}));
|
|
1593
|
+
}).template("text", (t)=>{
|
|
1594
|
+
t.label("Rich Text").fields((f)=>({
|
|
1595
|
+
body: f.text().label("Body").required("Required")
|
|
1596
|
+
}));
|
|
1597
|
+
})
|
|
1598
|
+
})
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
describe("shape", ()=>{
|
|
1602
|
+
it("starts with no active template and empty children", ()=>{
|
|
1603
|
+
const form = createFormWithTemplatedObject();
|
|
1604
|
+
const field = form.field("content");
|
|
1605
|
+
expect(field.isTemplated).toBe(true);
|
|
1606
|
+
expect(field.activeTemplateId).toBeNull();
|
|
1607
|
+
expect(field.children.size).toBe(0);
|
|
1608
|
+
});
|
|
1609
|
+
it("exposes available templates via VM", ()=>{
|
|
1610
|
+
const form = createFormWithTemplatedObject();
|
|
1611
|
+
const vm = form.field("content").vm;
|
|
1612
|
+
expect(vm.isTemplated).toBe(true);
|
|
1613
|
+
expect(vm.availableTemplates).toEqual([
|
|
1614
|
+
{
|
|
1615
|
+
id: "hero",
|
|
1616
|
+
label: "Hero Banner"
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
id: "text",
|
|
1620
|
+
label: "Rich Text"
|
|
1621
|
+
}
|
|
1622
|
+
]);
|
|
1623
|
+
expect(vm.activeTemplateId).toBeNull();
|
|
1624
|
+
expect(vm.fields).toEqual([]);
|
|
1625
|
+
});
|
|
1626
|
+
it("rejects .fields() alongside .template() at build time", ()=>{
|
|
1627
|
+
expect(()=>createForm({
|
|
1628
|
+
fields: (fields)=>({
|
|
1629
|
+
content: fields.object().fields((f)=>({
|
|
1630
|
+
x: f.text()
|
|
1631
|
+
})).template("a", (t)=>{
|
|
1632
|
+
t.label("A").fields((f)=>({
|
|
1633
|
+
y: f.text()
|
|
1634
|
+
}));
|
|
1635
|
+
})
|
|
1636
|
+
})
|
|
1637
|
+
})).toThrow(/both .fields\(\) and .template\(\)/);
|
|
1638
|
+
});
|
|
1639
|
+
it("rejects duplicate template ids", ()=>{
|
|
1640
|
+
expect(()=>createForm({
|
|
1641
|
+
fields: (fields)=>({
|
|
1642
|
+
content: fields.object().template("a", (t)=>{
|
|
1643
|
+
t.label("A1").fields((f)=>({
|
|
1644
|
+
x: f.text()
|
|
1645
|
+
}));
|
|
1646
|
+
}).template("a", (t)=>{
|
|
1647
|
+
t.label("A2").fields((f)=>({
|
|
1648
|
+
y: f.text()
|
|
1649
|
+
}));
|
|
1650
|
+
})
|
|
1651
|
+
})
|
|
1652
|
+
})).toThrow(/Duplicate template id "a"/);
|
|
1653
|
+
});
|
|
1654
|
+
it("rejects reserved _templateId as template id", ()=>{
|
|
1655
|
+
expect(()=>createForm({
|
|
1656
|
+
fields: (fields)=>({
|
|
1657
|
+
content: fields.object().template("_templateId", (t)=>{
|
|
1658
|
+
t.label("X").fields((f)=>({
|
|
1659
|
+
x: f.text()
|
|
1660
|
+
}));
|
|
1661
|
+
})
|
|
1662
|
+
})
|
|
1663
|
+
})).toThrow(/reserved/);
|
|
1664
|
+
});
|
|
1665
|
+
it("rejects _templateId as a child field name in a template", ()=>{
|
|
1666
|
+
expect(()=>createForm({
|
|
1667
|
+
fields: (fields)=>({
|
|
1668
|
+
content: fields.object().template("hero", (t)=>{
|
|
1669
|
+
t.label("Hero").fields((f)=>({
|
|
1670
|
+
_templateId: f.text()
|
|
1671
|
+
}));
|
|
1672
|
+
})
|
|
1673
|
+
})
|
|
1674
|
+
})).toThrow(/reserved field "_templateId"/);
|
|
1675
|
+
});
|
|
1676
|
+
it("allows combining .list() with .template() (Phase 8b)", ()=>{
|
|
1677
|
+
expect(()=>createForm({
|
|
1678
|
+
fields: (fields)=>({
|
|
1679
|
+
content: fields.object().list().template("a", (t)=>{
|
|
1680
|
+
t.label("A").fields((f)=>({
|
|
1681
|
+
x: f.text()
|
|
1682
|
+
}));
|
|
1683
|
+
})
|
|
1684
|
+
})
|
|
1685
|
+
})).not.toThrow();
|
|
1686
|
+
});
|
|
1687
|
+
});
|
|
1688
|
+
describe("setTemplate / switching", ()=>{
|
|
1689
|
+
it("builds children when a template is selected", ()=>{
|
|
1690
|
+
const form = createFormWithTemplatedObject();
|
|
1691
|
+
const field = form.field("content");
|
|
1692
|
+
field.setTemplate("hero");
|
|
1693
|
+
expect(field.activeTemplateId).toBe("hero");
|
|
1694
|
+
expect(field.children.size).toBe(2);
|
|
1695
|
+
expect(form.field("content.heading").type).toBe("text");
|
|
1696
|
+
expect(form.field("content.image").type).toBe("text");
|
|
1697
|
+
});
|
|
1698
|
+
it("discards data when switching to a different template", ()=>{
|
|
1699
|
+
const form = createFormWithTemplatedObject();
|
|
1700
|
+
const field = form.field("content");
|
|
1701
|
+
field.setTemplate("hero");
|
|
1702
|
+
form.field("content.heading").setValue("Hello");
|
|
1703
|
+
field.setTemplate("text");
|
|
1704
|
+
expect(field.activeTemplateId).toBe("text");
|
|
1705
|
+
expect(form.field("content.body")).toBeDefined();
|
|
1706
|
+
expect(()=>form.field("content.heading")).toThrow();
|
|
1707
|
+
});
|
|
1708
|
+
it("is a no-op when setting the currently active template", ()=>{
|
|
1709
|
+
const form = createFormWithTemplatedObject();
|
|
1710
|
+
const field = form.field("content");
|
|
1711
|
+
field.setTemplate("hero");
|
|
1712
|
+
form.field("content.heading").setValue("Preserved");
|
|
1713
|
+
field.setTemplate("hero");
|
|
1714
|
+
expect(form.field("content.heading").getValue()).toBe("Preserved");
|
|
1715
|
+
});
|
|
1716
|
+
it("throws when setting an unknown template id", ()=>{
|
|
1717
|
+
const form = createFormWithTemplatedObject();
|
|
1718
|
+
const field = form.field("content");
|
|
1719
|
+
expect(()=>field.setTemplate("missing")).toThrow(/Template "missing" not found/);
|
|
1720
|
+
});
|
|
1721
|
+
it("wires the form reference on newly created children", ()=>{
|
|
1722
|
+
const form = createFormWithTemplatedObject();
|
|
1723
|
+
const field = form.field("content");
|
|
1724
|
+
field.setTemplate("hero");
|
|
1725
|
+
expect(()=>form.field("content.heading").setValue("OK")).not.toThrow();
|
|
1726
|
+
});
|
|
1727
|
+
});
|
|
1728
|
+
describe("getData / setData", ()=>{
|
|
1729
|
+
it("returns null when no template is active", ()=>{
|
|
1730
|
+
const form = createFormWithTemplatedObject();
|
|
1731
|
+
expect(form.getData().content).toBeNull();
|
|
1732
|
+
});
|
|
1733
|
+
it("includes _templateId and child values when active", ()=>{
|
|
1734
|
+
const form = createFormWithTemplatedObject();
|
|
1735
|
+
const field = form.field("content");
|
|
1736
|
+
field.setTemplate("hero");
|
|
1737
|
+
form.field("content.heading").setValue("Welcome");
|
|
1738
|
+
form.field("content.image").setValue("cover.jpg");
|
|
1739
|
+
expect(form.getData().content).toEqual({
|
|
1740
|
+
_templateId: "hero",
|
|
1741
|
+
heading: "Welcome",
|
|
1742
|
+
image: "cover.jpg"
|
|
1743
|
+
});
|
|
1744
|
+
});
|
|
1745
|
+
it("hydrates via setData by reading _templateId", ()=>{
|
|
1746
|
+
const form = createFormWithTemplatedObject();
|
|
1747
|
+
form.setData({
|
|
1748
|
+
content: {
|
|
1749
|
+
_templateId: "text",
|
|
1750
|
+
body: "Lorem ipsum"
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
const field = form.field("content");
|
|
1754
|
+
expect(field.activeTemplateId).toBe("text");
|
|
1755
|
+
expect(form.field("content.body").getValue()).toBe("Lorem ipsum");
|
|
1756
|
+
});
|
|
1757
|
+
it("setData with null clears the active template", ()=>{
|
|
1758
|
+
const form = createFormWithTemplatedObject();
|
|
1759
|
+
const field = form.field("content");
|
|
1760
|
+
field.setTemplate("hero");
|
|
1761
|
+
form.field("content.heading").setValue("X");
|
|
1762
|
+
form.setData({
|
|
1763
|
+
content: null
|
|
1764
|
+
});
|
|
1765
|
+
expect(field.activeTemplateId).toBeNull();
|
|
1766
|
+
expect(field.children.size).toBe(0);
|
|
1767
|
+
});
|
|
1768
|
+
it("setData ignores unknown template id silently", ()=>{
|
|
1769
|
+
const form = createFormWithTemplatedObject();
|
|
1770
|
+
form.setData({
|
|
1771
|
+
content: {
|
|
1772
|
+
_templateId: "nope",
|
|
1773
|
+
foo: "bar"
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
const field = form.field("content");
|
|
1777
|
+
expect(field.activeTemplateId).toBeNull();
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
describe("validation", ()=>{
|
|
1781
|
+
it("required templated object fails validation when no template active", async ()=>{
|
|
1782
|
+
const form = createForm({
|
|
1783
|
+
fields: (fields)=>({
|
|
1784
|
+
content: fields.object().required("Pick a template").template("hero", (t)=>{
|
|
1785
|
+
t.label("Hero").fields((f)=>({
|
|
1786
|
+
heading: f.text()
|
|
1787
|
+
}));
|
|
1788
|
+
})
|
|
1789
|
+
})
|
|
1790
|
+
});
|
|
1791
|
+
const valid = await form.validate();
|
|
1792
|
+
expect(valid).toBe(false);
|
|
1793
|
+
expect(form.errors.some((e)=>"content" === e.path)).toBe(true);
|
|
1794
|
+
});
|
|
1795
|
+
it("required templated object passes when template active with valid children", async ()=>{
|
|
1796
|
+
const form = createForm({
|
|
1797
|
+
fields: (fields)=>({
|
|
1798
|
+
content: fields.object().required("Pick a template").template("hero", (t)=>{
|
|
1799
|
+
t.label("Hero").fields((f)=>({
|
|
1800
|
+
heading: f.text().required("Required")
|
|
1801
|
+
}));
|
|
1802
|
+
})
|
|
1803
|
+
})
|
|
1804
|
+
});
|
|
1805
|
+
const field = form.field("content");
|
|
1806
|
+
field.setTemplate("hero");
|
|
1807
|
+
form.field("content.heading").setValue("Hi");
|
|
1808
|
+
const valid = await form.validate();
|
|
1809
|
+
expect(valid).toBe(true);
|
|
1810
|
+
});
|
|
1811
|
+
it("validates child fields inside active template", async ()=>{
|
|
1812
|
+
const form = createFormWithTemplatedObject();
|
|
1813
|
+
const field = form.field("content");
|
|
1814
|
+
field.setTemplate("hero");
|
|
1815
|
+
const valid = await form.validate();
|
|
1816
|
+
expect(valid).toBe(false);
|
|
1817
|
+
});
|
|
1818
|
+
it("passes validation when object is optional and no template selected", async ()=>{
|
|
1819
|
+
const form = createFormWithTemplatedObject();
|
|
1820
|
+
const valid = await form.validate();
|
|
1821
|
+
expect(valid).toBe(true);
|
|
1822
|
+
});
|
|
1823
|
+
});
|
|
1824
|
+
describe("template visibility", ()=>{
|
|
1825
|
+
it("filters availableTemplates by reactive visible callback", ()=>{
|
|
1826
|
+
const form = createForm({
|
|
1827
|
+
fields: (fields)=>({
|
|
1828
|
+
plan: fields.text().defaultValue("free"),
|
|
1829
|
+
content: fields.object().template("basic", (t)=>{
|
|
1830
|
+
t.label("Basic").fields((f)=>({
|
|
1831
|
+
x: f.text()
|
|
1832
|
+
}));
|
|
1833
|
+
}).template("premium", (t)=>{
|
|
1834
|
+
t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
|
|
1835
|
+
y: f.text()
|
|
1836
|
+
}));
|
|
1837
|
+
})
|
|
1838
|
+
})
|
|
1839
|
+
});
|
|
1840
|
+
const vm1 = form.field("content").vm;
|
|
1841
|
+
expect(vm1.availableTemplates.map((t)=>t.id)).toEqual([
|
|
1842
|
+
"basic"
|
|
1843
|
+
]);
|
|
1844
|
+
form.field("plan").setValue("enterprise");
|
|
1845
|
+
const vm2 = form.field("content").vm;
|
|
1846
|
+
expect(vm2.availableTemplates.map((t)=>t.id)).toEqual([
|
|
1847
|
+
"basic",
|
|
1848
|
+
"premium"
|
|
1849
|
+
]);
|
|
1850
|
+
});
|
|
1851
|
+
it("hiding a template does not clear an already-active selection", ()=>{
|
|
1852
|
+
const form = createForm({
|
|
1853
|
+
fields: (fields)=>({
|
|
1854
|
+
plan: fields.text().defaultValue("enterprise"),
|
|
1855
|
+
content: fields.object().template("premium", (t)=>{
|
|
1856
|
+
t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
|
|
1857
|
+
y: f.text()
|
|
1858
|
+
}));
|
|
1859
|
+
})
|
|
1860
|
+
})
|
|
1861
|
+
});
|
|
1862
|
+
const field = form.field("content");
|
|
1863
|
+
field.setTemplate("premium");
|
|
1864
|
+
form.field("content.y").setValue("set");
|
|
1865
|
+
form.field("plan").setValue("free");
|
|
1866
|
+
const vm = form.field("content").vm;
|
|
1867
|
+
expect(vm.availableTemplates).toEqual([]);
|
|
1868
|
+
expect(vm.activeTemplateId).toBe("premium");
|
|
1869
|
+
expect(form.field("content.y").getValue()).toBe("set");
|
|
1870
|
+
});
|
|
1871
|
+
});
|
|
1872
|
+
describe("isDirty", ()=>{
|
|
1873
|
+
it("is not dirty after setData with template", ()=>{
|
|
1874
|
+
const form = createFormWithTemplatedObject();
|
|
1875
|
+
form.setData({
|
|
1876
|
+
content: {
|
|
1877
|
+
_templateId: "hero",
|
|
1878
|
+
heading: "Hello",
|
|
1879
|
+
image: ""
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
expect(form.isDirty).toBe(false);
|
|
1883
|
+
});
|
|
1884
|
+
it("becomes dirty after switching template", ()=>{
|
|
1885
|
+
const form = createFormWithTemplatedObject();
|
|
1886
|
+
form.setData({
|
|
1887
|
+
content: {
|
|
1888
|
+
_templateId: "hero",
|
|
1889
|
+
heading: "Hello",
|
|
1890
|
+
image: ""
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
form.field("content").setTemplate("text");
|
|
1894
|
+
expect(form.isDirty).toBe(true);
|
|
1895
|
+
});
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
describe("templated list fields (Phase 8b)", ()=>{
|
|
1899
|
+
function createFormWithTemplatedList() {
|
|
1900
|
+
return createForm({
|
|
1901
|
+
fields: (fields)=>({
|
|
1902
|
+
sections: fields.object().label("Sections").list().template("hero", (t)=>{
|
|
1903
|
+
t.label("Hero").fields((f)=>({
|
|
1904
|
+
heading: f.text().required("Required"),
|
|
1905
|
+
image: f.text()
|
|
1906
|
+
}));
|
|
1907
|
+
}).template("text", (t)=>{
|
|
1908
|
+
t.label("Text").fields((f)=>({
|
|
1909
|
+
body: f.text().required("Required")
|
|
1910
|
+
}));
|
|
1911
|
+
})
|
|
1912
|
+
})
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
describe("addItem / templateId", ()=>{
|
|
1916
|
+
it("requires a template id when adding to a templated list", ()=>{
|
|
1917
|
+
const form = createFormWithTemplatedList();
|
|
1918
|
+
const field = form.field("sections");
|
|
1919
|
+
expect(()=>field.addItem()).toThrow(/require a template id/);
|
|
1920
|
+
});
|
|
1921
|
+
it("rejects unknown template ids", ()=>{
|
|
1922
|
+
const form = createFormWithTemplatedList();
|
|
1923
|
+
const field = form.field("sections");
|
|
1924
|
+
expect(()=>field.addItem("missing")).toThrow(/Template "missing" not found/);
|
|
1925
|
+
});
|
|
1926
|
+
it("adds an item with the picked template's children", ()=>{
|
|
1927
|
+
const form = createFormWithTemplatedList();
|
|
1928
|
+
const field = form.field("sections");
|
|
1929
|
+
field.addItem("hero");
|
|
1930
|
+
expect(field.items.length).toBe(1);
|
|
1931
|
+
expect(field.items[0].templateId).toBe("hero");
|
|
1932
|
+
expect(field.items[0].children.has("heading")).toBe(true);
|
|
1933
|
+
expect(field.items[0].children.has("image")).toBe(true);
|
|
1934
|
+
});
|
|
1935
|
+
it("allows mixing different templates across items", ()=>{
|
|
1936
|
+
const form = createFormWithTemplatedList();
|
|
1937
|
+
const field = form.field("sections");
|
|
1938
|
+
field.addItem("hero");
|
|
1939
|
+
field.addItem("text");
|
|
1940
|
+
field.addItem("hero");
|
|
1941
|
+
expect(field.items.map((i)=>i.templateId)).toEqual([
|
|
1942
|
+
"hero",
|
|
1943
|
+
"text",
|
|
1944
|
+
"hero"
|
|
1945
|
+
]);
|
|
1946
|
+
});
|
|
1947
|
+
});
|
|
1948
|
+
describe("getData", ()=>{
|
|
1949
|
+
it("includes _templateId per item", ()=>{
|
|
1950
|
+
const form = createFormWithTemplatedList();
|
|
1951
|
+
const field = form.field("sections");
|
|
1952
|
+
field.addItem("hero");
|
|
1953
|
+
field.items[0].children.get("heading").setValue("Welcome");
|
|
1954
|
+
field.addItem("text");
|
|
1955
|
+
field.items[1].children.get("body").setValue("Lorem");
|
|
1956
|
+
expect(form.getData().sections).toEqual([
|
|
1957
|
+
{
|
|
1958
|
+
_templateId: "hero",
|
|
1959
|
+
heading: "Welcome",
|
|
1960
|
+
image: null
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
_templateId: "text",
|
|
1964
|
+
body: "Lorem"
|
|
1965
|
+
}
|
|
1966
|
+
]);
|
|
1967
|
+
});
|
|
1968
|
+
});
|
|
1969
|
+
describe("setData", ()=>{
|
|
1970
|
+
it("hydrates items by reading each item's _templateId", ()=>{
|
|
1971
|
+
const form = createFormWithTemplatedList();
|
|
1972
|
+
form.setData({
|
|
1973
|
+
sections: [
|
|
1974
|
+
{
|
|
1975
|
+
_templateId: "hero",
|
|
1976
|
+
heading: "H1",
|
|
1977
|
+
image: "img.jpg"
|
|
1978
|
+
},
|
|
1979
|
+
{
|
|
1980
|
+
_templateId: "text",
|
|
1981
|
+
body: "Body copy"
|
|
1982
|
+
}
|
|
1983
|
+
]
|
|
1984
|
+
});
|
|
1985
|
+
const field = form.field("sections");
|
|
1986
|
+
expect(field.items.length).toBe(2);
|
|
1987
|
+
expect(field.items[0].templateId).toBe("hero");
|
|
1988
|
+
expect(field.items[0].children.get("heading").getValue()).toBe("H1");
|
|
1989
|
+
expect(field.items[1].templateId).toBe("text");
|
|
1990
|
+
expect(field.items[1].children.get("body").getValue()).toBe("Body copy");
|
|
1991
|
+
});
|
|
1992
|
+
it("silently drops items with invalid or missing _templateId", ()=>{
|
|
1993
|
+
const form = createFormWithTemplatedList();
|
|
1994
|
+
form.setData({
|
|
1995
|
+
sections: [
|
|
1996
|
+
{
|
|
1997
|
+
_templateId: "hero",
|
|
1998
|
+
heading: "Keep"
|
|
1999
|
+
},
|
|
2000
|
+
{
|
|
2001
|
+
_templateId: "nope",
|
|
2002
|
+
x: 1
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
heading: "no-id"
|
|
2006
|
+
},
|
|
2007
|
+
null,
|
|
2008
|
+
{
|
|
2009
|
+
_templateId: "text",
|
|
2010
|
+
body: "Also keep"
|
|
2011
|
+
}
|
|
2012
|
+
]
|
|
2013
|
+
});
|
|
2014
|
+
const field = form.field("sections");
|
|
2015
|
+
expect(field.items.length).toBe(2);
|
|
2016
|
+
expect(field.items.map((i)=>i.templateId)).toEqual([
|
|
2017
|
+
"hero",
|
|
2018
|
+
"text"
|
|
2019
|
+
]);
|
|
2020
|
+
});
|
|
2021
|
+
});
|
|
2022
|
+
describe("duplicate / move / remove", ()=>{
|
|
2023
|
+
it("duplicates an item preserving templateId and values", ()=>{
|
|
2024
|
+
const form = createFormWithTemplatedList();
|
|
2025
|
+
const field = form.field("sections");
|
|
2026
|
+
field.addItem("hero");
|
|
2027
|
+
field.items[0].children.get("heading").setValue("Original");
|
|
2028
|
+
field.items[0].children.get("image").setValue("pic.jpg");
|
|
2029
|
+
field.duplicateItem(0);
|
|
2030
|
+
expect(field.items.length).toBe(2);
|
|
2031
|
+
expect(field.items[1].templateId).toBe("hero");
|
|
2032
|
+
expect(field.items[1].children.get("heading").getValue()).toBe("Original");
|
|
2033
|
+
expect(field.items[1].children.get("image").getValue()).toBe("pic.jpg");
|
|
2034
|
+
expect(field.items[0].key).not.toBe(field.items[1].key);
|
|
2035
|
+
});
|
|
2036
|
+
it("moves items while preserving templateId", ()=>{
|
|
2037
|
+
const form = createFormWithTemplatedList();
|
|
2038
|
+
const field = form.field("sections");
|
|
2039
|
+
field.addItem("hero");
|
|
2040
|
+
field.addItem("text");
|
|
2041
|
+
field.moveItem(0, 1);
|
|
2042
|
+
expect(field.items.map((i)=>i.templateId)).toEqual([
|
|
2043
|
+
"text",
|
|
2044
|
+
"hero"
|
|
2045
|
+
]);
|
|
2046
|
+
});
|
|
2047
|
+
it("removes items by index", ()=>{
|
|
2048
|
+
const form = createFormWithTemplatedList();
|
|
2049
|
+
const field = form.field("sections");
|
|
2050
|
+
field.addItem("hero");
|
|
2051
|
+
field.addItem("text");
|
|
2052
|
+
field.removeItem(0);
|
|
2053
|
+
expect(field.items.length).toBe(1);
|
|
2054
|
+
expect(field.items[0].templateId).toBe("text");
|
|
2055
|
+
});
|
|
2056
|
+
});
|
|
2057
|
+
describe("VM", ()=>{
|
|
2058
|
+
it("exposes templateId and duplicate on each item VM", ()=>{
|
|
2059
|
+
const form = createFormWithTemplatedList();
|
|
2060
|
+
const field = form.field("sections");
|
|
2061
|
+
field.addItem("hero");
|
|
2062
|
+
const vm = form.field("sections").vm;
|
|
2063
|
+
expect(vm.items.length).toBe(1);
|
|
2064
|
+
expect(vm.items[0].templateId).toBe("hero");
|
|
2065
|
+
expect(typeof vm.items[0].duplicate).toBe("function");
|
|
2066
|
+
});
|
|
2067
|
+
it("VM addItem(templateId) appends an item", ()=>{
|
|
2068
|
+
const form = createFormWithTemplatedList();
|
|
2069
|
+
const vm = form.field("sections").vm;
|
|
2070
|
+
vm.addItem("hero");
|
|
2071
|
+
vm.addItem("text");
|
|
2072
|
+
const field = form.field("sections");
|
|
2073
|
+
expect(field.items.map((i)=>i.templateId)).toEqual([
|
|
2074
|
+
"hero",
|
|
2075
|
+
"text"
|
|
2076
|
+
]);
|
|
2077
|
+
});
|
|
2078
|
+
it("availableTemplates respects reactive visible() on templated lists", ()=>{
|
|
2079
|
+
const form = createForm({
|
|
2080
|
+
fields: (fields)=>({
|
|
2081
|
+
plan: fields.text().defaultValue("free"),
|
|
2082
|
+
sections: fields.object().list().template("basic", (t)=>{
|
|
2083
|
+
t.label("Basic").fields((f)=>({
|
|
2084
|
+
x: f.text()
|
|
2085
|
+
}));
|
|
2086
|
+
}).template("premium", (t)=>{
|
|
2087
|
+
t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
|
|
2088
|
+
y: f.text()
|
|
2089
|
+
}));
|
|
2090
|
+
})
|
|
2091
|
+
})
|
|
2092
|
+
});
|
|
2093
|
+
const vm1 = form.field("sections").vm;
|
|
2094
|
+
expect(vm1.availableTemplates.map((t)=>t.id)).toEqual([
|
|
2095
|
+
"basic"
|
|
2096
|
+
]);
|
|
2097
|
+
form.field("plan").setValue("enterprise");
|
|
2098
|
+
const vm2 = form.field("sections").vm;
|
|
2099
|
+
expect(vm2.availableTemplates.map((t)=>t.id)).toEqual([
|
|
2100
|
+
"basic",
|
|
2101
|
+
"premium"
|
|
2102
|
+
]);
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
describe("validation", ()=>{
|
|
2106
|
+
it("validates each item's children under its template", async ()=>{
|
|
2107
|
+
const form = createFormWithTemplatedList();
|
|
2108
|
+
const field = form.field("sections");
|
|
2109
|
+
field.addItem("hero");
|
|
2110
|
+
field.addItem("text");
|
|
2111
|
+
field.items[1].children.get("body").setValue("ok");
|
|
2112
|
+
const valid = await form.validate();
|
|
2113
|
+
expect(valid).toBe(false);
|
|
2114
|
+
expect(form.errors.some((e)=>e.path.startsWith("sections"))).toBe(true);
|
|
2115
|
+
});
|
|
2116
|
+
it("passes when every item's required fields are filled", async ()=>{
|
|
2117
|
+
const form = createFormWithTemplatedList();
|
|
2118
|
+
const field = form.field("sections");
|
|
2119
|
+
field.addItem("hero");
|
|
2120
|
+
field.items[0].children.get("heading").setValue("H");
|
|
2121
|
+
field.addItem("text");
|
|
2122
|
+
field.items[1].children.get("body").setValue("B");
|
|
2123
|
+
const valid = await form.validate();
|
|
2124
|
+
expect(valid).toBe(true);
|
|
2125
|
+
});
|
|
2126
|
+
});
|
|
2127
|
+
describe("isDirty", ()=>{
|
|
2128
|
+
it("is not dirty after setData with templated list", ()=>{
|
|
2129
|
+
const form = createFormWithTemplatedList();
|
|
2130
|
+
form.setData({
|
|
2131
|
+
sections: [
|
|
2132
|
+
{
|
|
2133
|
+
_templateId: "hero",
|
|
2134
|
+
heading: "H",
|
|
2135
|
+
image: ""
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
_templateId: "text",
|
|
2139
|
+
body: "B"
|
|
2140
|
+
}
|
|
2141
|
+
]
|
|
2142
|
+
});
|
|
2143
|
+
expect(form.isDirty).toBe(false);
|
|
2144
|
+
});
|
|
2145
|
+
it("becomes dirty after adding an item", ()=>{
|
|
2146
|
+
const form = createFormWithTemplatedList();
|
|
2147
|
+
form.setData({
|
|
2148
|
+
sections: [
|
|
2149
|
+
{
|
|
2150
|
+
_templateId: "hero",
|
|
2151
|
+
heading: "H",
|
|
2152
|
+
image: ""
|
|
2153
|
+
}
|
|
2154
|
+
]
|
|
2155
|
+
});
|
|
2156
|
+
form.field("sections").addItem("text");
|
|
2157
|
+
expect(form.isDirty).toBe(true);
|
|
2158
|
+
});
|
|
2159
|
+
});
|
|
2160
|
+
});
|
|
2161
|
+
describe("per-template / inner object layouts (Phase 8c)", ()=>{
|
|
2162
|
+
describe("non-templated single object", ()=>{
|
|
2163
|
+
it("defaults to one row per visible child when no layout.object() is registered", ()=>{
|
|
2164
|
+
const form = createForm({
|
|
2165
|
+
fields: (fields)=>({
|
|
2166
|
+
meta: fields.object().fields((f)=>({
|
|
2167
|
+
a: f.text().label("A"),
|
|
2168
|
+
b: f.text().label("B")
|
|
2169
|
+
}))
|
|
2170
|
+
})
|
|
2171
|
+
});
|
|
2172
|
+
const vm = form.field("meta").vm;
|
|
2173
|
+
expect(vm.layout.length).toBe(2);
|
|
2174
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2175
|
+
"a"
|
|
2176
|
+
]);
|
|
2177
|
+
expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2178
|
+
"b"
|
|
2179
|
+
]);
|
|
2180
|
+
});
|
|
2181
|
+
it("resolves the registered inner layout against the children", ()=>{
|
|
2182
|
+
const form = createForm({
|
|
2183
|
+
fields: (fields)=>({
|
|
2184
|
+
meta: fields.object().fields((f)=>({
|
|
2185
|
+
a: f.text().label("A"),
|
|
2186
|
+
b: f.text().label("B")
|
|
2187
|
+
}))
|
|
2188
|
+
}),
|
|
2189
|
+
layout: (layout)=>[
|
|
2190
|
+
layout.object("meta", (l)=>[
|
|
2191
|
+
l.row("a", "b")
|
|
2192
|
+
])
|
|
2193
|
+
]
|
|
2194
|
+
});
|
|
2195
|
+
const vm = form.field("meta").vm;
|
|
2196
|
+
expect(vm.layout.length).toBe(1);
|
|
2197
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2198
|
+
"a",
|
|
2199
|
+
"b"
|
|
2200
|
+
]);
|
|
2201
|
+
});
|
|
2202
|
+
it("throws when a per-template map is passed to a non-templated field", ()=>{
|
|
2203
|
+
expect(()=>createForm({
|
|
2204
|
+
fields: (fields)=>({
|
|
2205
|
+
meta: fields.object().fields((f)=>({
|
|
2206
|
+
a: f.text()
|
|
2207
|
+
}))
|
|
2208
|
+
}),
|
|
2209
|
+
layout: (layout)=>[
|
|
2210
|
+
layout.object("meta", {
|
|
2211
|
+
tplA: (l)=>[
|
|
2212
|
+
l.row("a")
|
|
2213
|
+
]
|
|
2214
|
+
})
|
|
2215
|
+
]
|
|
2216
|
+
})).toThrow(/not templated/);
|
|
2217
|
+
});
|
|
2218
|
+
});
|
|
2219
|
+
describe("non-templated list", ()=>{
|
|
2220
|
+
it("applies the inner layout to every list item", ()=>{
|
|
2221
|
+
const form = createForm({
|
|
2222
|
+
fields: (fields)=>({
|
|
2223
|
+
rows: fields.object().list().fields((f)=>({
|
|
2224
|
+
a: f.text(),
|
|
2225
|
+
b: f.text()
|
|
2226
|
+
}))
|
|
2227
|
+
}),
|
|
2228
|
+
layout: (layout)=>[
|
|
2229
|
+
layout.object("rows", (l)=>[
|
|
2230
|
+
l.row("a", "b")
|
|
2231
|
+
])
|
|
2232
|
+
]
|
|
2233
|
+
});
|
|
2234
|
+
const field = form.field("rows");
|
|
2235
|
+
field.addItem();
|
|
2236
|
+
field.addItem();
|
|
2237
|
+
const vm = field.vm;
|
|
2238
|
+
expect(vm.items.length).toBe(2);
|
|
2239
|
+
for (const item of vm.items){
|
|
2240
|
+
expect(item.layout.length).toBe(1);
|
|
2241
|
+
expect(asRow(item.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2242
|
+
"a",
|
|
2243
|
+
"b"
|
|
2244
|
+
]);
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
});
|
|
2248
|
+
describe("templated single object", ()=>{
|
|
2249
|
+
function buildTemplatedForm(layoutFactory) {
|
|
2250
|
+
return createForm({
|
|
2251
|
+
fields: (fields)=>({
|
|
2252
|
+
content: fields.object().template("hero", (t)=>{
|
|
2253
|
+
t.label("Hero").fields((f)=>({
|
|
2254
|
+
heading: f.text().label("Heading"),
|
|
2255
|
+
subheading: f.text().label("Subheading")
|
|
2256
|
+
}));
|
|
2257
|
+
}).template("cta", (t)=>{
|
|
2258
|
+
t.label("CTA").fields((f)=>({
|
|
2259
|
+
text: f.text().label("Text"),
|
|
2260
|
+
url: f.text().label("URL")
|
|
2261
|
+
}));
|
|
2262
|
+
})
|
|
2263
|
+
}),
|
|
2264
|
+
layout: layoutFactory
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
it("uses the active template's per-template layout", ()=>{
|
|
2268
|
+
const form = buildTemplatedForm((layout)=>[
|
|
2269
|
+
layout.object("content", {
|
|
2270
|
+
hero: (l)=>[
|
|
2271
|
+
l.row("heading", "subheading")
|
|
2272
|
+
],
|
|
2273
|
+
cta: (l)=>[
|
|
2274
|
+
l.row("text"),
|
|
2275
|
+
l.row("url")
|
|
2276
|
+
]
|
|
2277
|
+
})
|
|
2278
|
+
]);
|
|
2279
|
+
const field = form.field("content");
|
|
2280
|
+
field.setTemplate("hero");
|
|
2281
|
+
let vm = form.field("content").vm;
|
|
2282
|
+
expect(vm.layout.length).toBe(1);
|
|
2283
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2284
|
+
"heading",
|
|
2285
|
+
"subheading"
|
|
2286
|
+
]);
|
|
2287
|
+
field.setTemplate("cta");
|
|
2288
|
+
vm = form.field("content").vm;
|
|
2289
|
+
expect(vm.layout.length).toBe(2);
|
|
2290
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2291
|
+
"text"
|
|
2292
|
+
]);
|
|
2293
|
+
expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2294
|
+
"url"
|
|
2295
|
+
]);
|
|
2296
|
+
});
|
|
2297
|
+
it("falls back to default one-row-per-child when active template has no entry", ()=>{
|
|
2298
|
+
const form = buildTemplatedForm((layout)=>[
|
|
2299
|
+
layout.object("content", {
|
|
2300
|
+
hero: (l)=>[
|
|
2301
|
+
l.row("heading", "subheading")
|
|
2302
|
+
]
|
|
2303
|
+
})
|
|
2304
|
+
]);
|
|
2305
|
+
const field = form.field("content");
|
|
2306
|
+
field.setTemplate("cta");
|
|
2307
|
+
const vm = form.field("content").vm;
|
|
2308
|
+
expect(vm.layout.length).toBe(2);
|
|
2309
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2310
|
+
"text"
|
|
2311
|
+
]);
|
|
2312
|
+
expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2313
|
+
"url"
|
|
2314
|
+
]);
|
|
2315
|
+
});
|
|
2316
|
+
it("returns an empty layout when no template is active", ()=>{
|
|
2317
|
+
const form = buildTemplatedForm((layout)=>[
|
|
2318
|
+
layout.object("content", {
|
|
2319
|
+
hero: (l)=>[
|
|
2320
|
+
l.row("heading", "subheading")
|
|
2321
|
+
]
|
|
2322
|
+
})
|
|
2323
|
+
]);
|
|
2324
|
+
const vm = form.field("content").vm;
|
|
2325
|
+
expect(vm.activeTemplateId).toBeNull();
|
|
2326
|
+
expect(vm.layout).toEqual([]);
|
|
2327
|
+
});
|
|
2328
|
+
it("silently ignores an unknown template id in the layout map", ()=>{
|
|
2329
|
+
const form = buildTemplatedForm((layout)=>[
|
|
2330
|
+
layout.object("content", {
|
|
2331
|
+
hero: (l)=>[
|
|
2332
|
+
l.row("heading", "subheading")
|
|
2333
|
+
],
|
|
2334
|
+
unknown: (l)=>[
|
|
2335
|
+
l.row("xxx")
|
|
2336
|
+
]
|
|
2337
|
+
})
|
|
2338
|
+
]);
|
|
2339
|
+
const field = form.field("content");
|
|
2340
|
+
field.setTemplate("hero");
|
|
2341
|
+
const vm = form.field("content").vm;
|
|
2342
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2343
|
+
"heading",
|
|
2344
|
+
"subheading"
|
|
2345
|
+
]);
|
|
2346
|
+
});
|
|
2347
|
+
it("throws when a single LayoutNode[] is passed to a templated field", ()=>{
|
|
2348
|
+
expect(()=>buildTemplatedForm((layout)=>[
|
|
2349
|
+
layout.object("content", (l)=>[
|
|
2350
|
+
l.row("x")
|
|
2351
|
+
])
|
|
2352
|
+
])).toThrow(/is templated/);
|
|
2353
|
+
});
|
|
2354
|
+
it("falls back to default when no layout.object() is registered", ()=>{
|
|
2355
|
+
const form = buildTemplatedForm();
|
|
2356
|
+
const field = form.field("content");
|
|
2357
|
+
field.setTemplate("hero");
|
|
2358
|
+
const vm = form.field("content").vm;
|
|
2359
|
+
expect(vm.layout.length).toBe(2);
|
|
2360
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2361
|
+
"heading"
|
|
2362
|
+
]);
|
|
2363
|
+
expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2364
|
+
"subheading"
|
|
2365
|
+
]);
|
|
2366
|
+
});
|
|
2367
|
+
});
|
|
2368
|
+
describe("templated list", ()=>{
|
|
2369
|
+
function buildTemplatedListForm() {
|
|
2370
|
+
return createForm({
|
|
2371
|
+
fields: (fields)=>({
|
|
2372
|
+
sections: fields.object().list().template("hero", (t)=>{
|
|
2373
|
+
t.label("Hero").fields((f)=>({
|
|
2374
|
+
heading: f.text().label("Heading"),
|
|
2375
|
+
subheading: f.text().label("Subheading")
|
|
2376
|
+
}));
|
|
2377
|
+
}).template("cta", (t)=>{
|
|
2378
|
+
t.label("CTA").fields((f)=>({
|
|
2379
|
+
text: f.text().label("Text"),
|
|
2380
|
+
url: f.text().label("URL")
|
|
2381
|
+
}));
|
|
2382
|
+
})
|
|
2383
|
+
}),
|
|
2384
|
+
layout: (layout)=>[
|
|
2385
|
+
layout.object("sections", {
|
|
2386
|
+
hero: (l)=>[
|
|
2387
|
+
l.row("heading", "subheading")
|
|
2388
|
+
],
|
|
2389
|
+
cta: (l)=>[
|
|
2390
|
+
l.row("text"),
|
|
2391
|
+
l.row("url")
|
|
2392
|
+
]
|
|
2393
|
+
})
|
|
2394
|
+
]
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
it("each item resolves layout against its own template", ()=>{
|
|
2398
|
+
const form = buildTemplatedListForm();
|
|
2399
|
+
const field = form.field("sections");
|
|
2400
|
+
field.addItem("hero");
|
|
2401
|
+
field.addItem("cta");
|
|
2402
|
+
const vm = form.field("sections").vm;
|
|
2403
|
+
expect(vm.items.length).toBe(2);
|
|
2404
|
+
expect(asRow(vm.items[0].layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2405
|
+
"heading",
|
|
2406
|
+
"subheading"
|
|
2407
|
+
]);
|
|
2408
|
+
expect(asRow(vm.items[1].layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2409
|
+
"text"
|
|
2410
|
+
]);
|
|
2411
|
+
expect(asRow(vm.items[1].layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2412
|
+
"url"
|
|
2413
|
+
]);
|
|
2414
|
+
});
|
|
2415
|
+
it("hides hidden child fields from the resolved layout row", ()=>{
|
|
2416
|
+
const form = createForm({
|
|
2417
|
+
fields: (fields)=>({
|
|
2418
|
+
content: fields.object().template("hero", (t)=>{
|
|
2419
|
+
t.label("Hero").fields((f)=>({
|
|
2420
|
+
heading: f.text(),
|
|
2421
|
+
secret: f.text().hidden()
|
|
2422
|
+
}));
|
|
2423
|
+
})
|
|
2424
|
+
}),
|
|
2425
|
+
layout: (layout)=>[
|
|
2426
|
+
layout.object("content", {
|
|
2427
|
+
hero: (l)=>[
|
|
2428
|
+
l.row("heading", "secret")
|
|
2429
|
+
]
|
|
2430
|
+
})
|
|
2431
|
+
]
|
|
2432
|
+
});
|
|
2433
|
+
const field = form.field("content");
|
|
2434
|
+
field.setTemplate("hero");
|
|
2435
|
+
const vm = form.field("content").vm;
|
|
2436
|
+
expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2437
|
+
"heading"
|
|
2438
|
+
]);
|
|
2439
|
+
});
|
|
2440
|
+
});
|
|
2441
|
+
describe("interaction with form.vm.layout", ()=>{
|
|
2442
|
+
it("the form layout exposes a single-field row for the object", ()=>{
|
|
2443
|
+
const form = createForm({
|
|
2444
|
+
fields: (fields)=>({
|
|
2445
|
+
content: fields.object().template("hero", (t)=>{
|
|
2446
|
+
t.label("Hero").fields((f)=>({
|
|
2447
|
+
heading: f.text()
|
|
2448
|
+
}));
|
|
2449
|
+
})
|
|
2450
|
+
}),
|
|
2451
|
+
layout: (layout)=>[
|
|
2452
|
+
layout.object("content", {
|
|
2453
|
+
hero: (l)=>[
|
|
2454
|
+
l.row("heading")
|
|
2455
|
+
]
|
|
2456
|
+
})
|
|
2457
|
+
]
|
|
2458
|
+
});
|
|
2459
|
+
const layout = form.vm.layout;
|
|
2460
|
+
expect(layout.length).toBe(1);
|
|
2461
|
+
const row = asRow(layout[0]);
|
|
2462
|
+
expect(row.fields.map((f)=>f.name)).toEqual([
|
|
2463
|
+
"content"
|
|
2464
|
+
]);
|
|
2465
|
+
});
|
|
2466
|
+
it("hides the object node from form.vm.layout when the field is not visible", ()=>{
|
|
2467
|
+
const form = createForm({
|
|
2468
|
+
fields: (fields)=>({
|
|
2469
|
+
content: fields.object().hidden().template("hero", (t)=>{
|
|
2470
|
+
t.label("Hero").fields((f)=>({
|
|
2471
|
+
heading: f.text()
|
|
2472
|
+
}));
|
|
2473
|
+
})
|
|
2474
|
+
}),
|
|
2475
|
+
layout: (layout)=>[
|
|
2476
|
+
layout.object("content", {
|
|
2477
|
+
hero: (l)=>[
|
|
2478
|
+
l.row("heading")
|
|
2479
|
+
]
|
|
2480
|
+
})
|
|
2481
|
+
]
|
|
2482
|
+
});
|
|
2483
|
+
expect(form.vm.layout).toEqual([]);
|
|
2484
|
+
});
|
|
2485
|
+
});
|
|
2486
|
+
});
|
|
2487
|
+
describe("nested object layouts (Phase 8c.1)", ()=>{
|
|
2488
|
+
it("registers layout.object() nested inside another object's inner layout (non-templated)", ()=>{
|
|
2489
|
+
const form = createForm({
|
|
2490
|
+
fields: (fields)=>({
|
|
2491
|
+
page: fields.object().fields((f)=>({
|
|
2492
|
+
title: f.text(),
|
|
2493
|
+
seo: f.object().fields((g)=>({
|
|
2494
|
+
metaTitle: g.text(),
|
|
2495
|
+
metaDescription: g.text()
|
|
2496
|
+
}))
|
|
2497
|
+
}))
|
|
2498
|
+
}),
|
|
2499
|
+
layout: (layout)=>[
|
|
2500
|
+
layout.object("page", (l)=>[
|
|
2501
|
+
l.row("title"),
|
|
2502
|
+
l.object("seo", (inner)=>[
|
|
2503
|
+
inner.row("metaTitle", "metaDescription")
|
|
2504
|
+
])
|
|
2505
|
+
])
|
|
2506
|
+
]
|
|
2507
|
+
});
|
|
2508
|
+
const pageVm = form.field("page").vm;
|
|
2509
|
+
const seoRow = asRow(pageVm.layout[1]);
|
|
2510
|
+
const seoVm = seoRow.fields[0];
|
|
2511
|
+
expect(seoVm.layout.length).toBe(1);
|
|
2512
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2513
|
+
"metaTitle",
|
|
2514
|
+
"metaDescription"
|
|
2515
|
+
]);
|
|
2516
|
+
});
|
|
2517
|
+
it("supports three levels of nesting", ()=>{
|
|
2518
|
+
const form = createForm({
|
|
2519
|
+
fields: (fields)=>({
|
|
2520
|
+
a: fields.object().fields((f)=>({
|
|
2521
|
+
b: f.object().fields((g)=>({
|
|
2522
|
+
c: g.object().fields((h)=>({
|
|
2523
|
+
x: h.text(),
|
|
2524
|
+
y: h.text()
|
|
2525
|
+
}))
|
|
2526
|
+
}))
|
|
2527
|
+
}))
|
|
2528
|
+
}),
|
|
2529
|
+
layout: (layout)=>[
|
|
2530
|
+
layout.object("a", (l1)=>[
|
|
2531
|
+
l1.object("b", (l2)=>[
|
|
2532
|
+
l2.object("c", (l3)=>[
|
|
2533
|
+
l3.row("x", "y")
|
|
2534
|
+
])
|
|
2535
|
+
])
|
|
2536
|
+
])
|
|
2537
|
+
]
|
|
2538
|
+
});
|
|
2539
|
+
const aVm = form.field("a").vm;
|
|
2540
|
+
const bVm = asRow(aVm.layout[0]).fields[0];
|
|
2541
|
+
const cVm = asRow(bVm.layout[0]).fields[0];
|
|
2542
|
+
expect(cVm.layout.length).toBe(1);
|
|
2543
|
+
expect(asRow(cVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2544
|
+
"x",
|
|
2545
|
+
"y"
|
|
2546
|
+
]);
|
|
2547
|
+
});
|
|
2548
|
+
it("registers nested layouts on a templated single object when its template activates", ()=>{
|
|
2549
|
+
const form = createForm({
|
|
2550
|
+
fields: (fields)=>({
|
|
2551
|
+
block: fields.object().template("hero", (t)=>{
|
|
2552
|
+
t.label("Hero").fields((f)=>({
|
|
2553
|
+
heading: f.text(),
|
|
2554
|
+
seo: f.object().fields((g)=>({
|
|
2555
|
+
metaTitle: g.text(),
|
|
2556
|
+
metaDescription: g.text()
|
|
2557
|
+
}))
|
|
2558
|
+
}));
|
|
2559
|
+
})
|
|
2560
|
+
}),
|
|
2561
|
+
layout: (layout)=>[
|
|
2562
|
+
layout.object("block", {
|
|
2563
|
+
hero: (l)=>[
|
|
2564
|
+
l.row("heading"),
|
|
2565
|
+
l.object("seo", (inner)=>[
|
|
2566
|
+
inner.row("metaTitle", "metaDescription")
|
|
2567
|
+
])
|
|
2568
|
+
]
|
|
2569
|
+
})
|
|
2570
|
+
]
|
|
2571
|
+
});
|
|
2572
|
+
const field = form.field("block");
|
|
2573
|
+
field.setTemplate("hero");
|
|
2574
|
+
const vm = form.field("block").vm;
|
|
2575
|
+
const seoRow = asRow(vm.layout[1]);
|
|
2576
|
+
const seoVm = seoRow.fields[0];
|
|
2577
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2578
|
+
"metaTitle",
|
|
2579
|
+
"metaDescription"
|
|
2580
|
+
]);
|
|
2581
|
+
});
|
|
2582
|
+
it("registers nested layouts on a templated list — each item gets its own", ()=>{
|
|
2583
|
+
const form = createForm({
|
|
2584
|
+
fields: (fields)=>({
|
|
2585
|
+
sections: fields.object().list().template("hero", (t)=>{
|
|
2586
|
+
t.label("Hero").fields((f)=>({
|
|
2587
|
+
heading: f.text(),
|
|
2588
|
+
seo: f.object().fields((g)=>({
|
|
2589
|
+
metaTitle: g.text(),
|
|
2590
|
+
metaDescription: g.text()
|
|
2591
|
+
}))
|
|
2592
|
+
}));
|
|
2593
|
+
})
|
|
2594
|
+
}),
|
|
2595
|
+
layout: (layout)=>[
|
|
2596
|
+
layout.object("sections", {
|
|
2597
|
+
hero: (l)=>[
|
|
2598
|
+
l.row("heading"),
|
|
2599
|
+
l.object("seo", (inner)=>[
|
|
2600
|
+
inner.row("metaTitle", "metaDescription")
|
|
2601
|
+
])
|
|
2602
|
+
]
|
|
2603
|
+
})
|
|
2604
|
+
]
|
|
2605
|
+
});
|
|
2606
|
+
const field = form.field("sections");
|
|
2607
|
+
field.addItem("hero");
|
|
2608
|
+
field.addItem("hero");
|
|
2609
|
+
const vm = form.field("sections").vm;
|
|
2610
|
+
expect(vm.items.length).toBe(2);
|
|
2611
|
+
for (const item of vm.items){
|
|
2612
|
+
const seoVm = asRow(item.layout[1]).fields[0];
|
|
2613
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2614
|
+
"metaTitle",
|
|
2615
|
+
"metaDescription"
|
|
2616
|
+
]);
|
|
2617
|
+
}
|
|
2618
|
+
});
|
|
2619
|
+
it("registers nested layouts on a non-templated list item upon creation", ()=>{
|
|
2620
|
+
const form = createForm({
|
|
2621
|
+
fields: (fields)=>({
|
|
2622
|
+
rows: fields.object().list().fields((f)=>({
|
|
2623
|
+
label: f.text(),
|
|
2624
|
+
seo: f.object().fields((g)=>({
|
|
2625
|
+
metaTitle: g.text(),
|
|
2626
|
+
metaDescription: g.text()
|
|
2627
|
+
}))
|
|
2628
|
+
}))
|
|
2629
|
+
}),
|
|
2630
|
+
layout: (layout)=>[
|
|
2631
|
+
layout.object("rows", (l)=>[
|
|
2632
|
+
l.row("label"),
|
|
2633
|
+
l.object("seo", (inner)=>[
|
|
2634
|
+
inner.row("metaTitle", "metaDescription")
|
|
2635
|
+
])
|
|
2636
|
+
])
|
|
2637
|
+
]
|
|
2638
|
+
});
|
|
2639
|
+
const field = form.field("rows");
|
|
2640
|
+
field.addItem();
|
|
2641
|
+
const vm = form.field("rows").vm;
|
|
2642
|
+
const seoVm = asRow(vm.items[0].layout[1]).fields[0];
|
|
2643
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2644
|
+
"metaTitle",
|
|
2645
|
+
"metaDescription"
|
|
2646
|
+
]);
|
|
2647
|
+
});
|
|
2648
|
+
it("registers nested layouts on children added via field.as('object').fields() at runtime", ()=>{
|
|
2649
|
+
const form = createForm({
|
|
2650
|
+
fields: (fields)=>({
|
|
2651
|
+
page: fields.object().fields((f)=>({
|
|
2652
|
+
title: f.text()
|
|
2653
|
+
}))
|
|
2654
|
+
}),
|
|
2655
|
+
layout: (layout)=>[
|
|
2656
|
+
layout.object("page", (l)=>[
|
|
2657
|
+
l.row("title"),
|
|
2658
|
+
l.object("seo", (inner)=>[
|
|
2659
|
+
inner.row("metaTitle", "metaDescription")
|
|
2660
|
+
])
|
|
2661
|
+
])
|
|
2662
|
+
]
|
|
2663
|
+
});
|
|
2664
|
+
form.field("page").as("object").fields((f)=>({
|
|
2665
|
+
seo: f.object().fields((g)=>({
|
|
2666
|
+
metaTitle: g.text(),
|
|
2667
|
+
metaDescription: g.text()
|
|
2668
|
+
}))
|
|
2669
|
+
}));
|
|
2670
|
+
const pageVm = form.field("page").vm;
|
|
2671
|
+
const seoRow = asRow(pageVm.layout[1]);
|
|
2672
|
+
const seoVm = seoRow.fields[0];
|
|
2673
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2674
|
+
"metaTitle",
|
|
2675
|
+
"metaDescription"
|
|
2676
|
+
]);
|
|
2677
|
+
});
|
|
2678
|
+
it("recurses through tabs nested inside an inner layout", ()=>{
|
|
2679
|
+
const form = createForm({
|
|
2680
|
+
fields: (fields)=>({
|
|
2681
|
+
page: fields.object().fields((f)=>({
|
|
2682
|
+
title: f.text(),
|
|
2683
|
+
seo: f.object().fields((g)=>({
|
|
2684
|
+
metaTitle: g.text(),
|
|
2685
|
+
metaDescription: g.text()
|
|
2686
|
+
}))
|
|
2687
|
+
}))
|
|
2688
|
+
}),
|
|
2689
|
+
layout: (layout)=>[
|
|
2690
|
+
layout.object("page", (l)=>[
|
|
2691
|
+
l.tabs().tab("main", (tab)=>{
|
|
2692
|
+
tab.label("Main").layout((l)=>[
|
|
2693
|
+
l.row("title")
|
|
2694
|
+
]);
|
|
2695
|
+
}).tab("seoTab", (tab)=>{
|
|
2696
|
+
tab.label("SEO").layout((l)=>[
|
|
2697
|
+
l.object("seo", (inner)=>[
|
|
2698
|
+
inner.row("metaTitle", "metaDescription")
|
|
2699
|
+
])
|
|
2700
|
+
]);
|
|
2701
|
+
})
|
|
2702
|
+
])
|
|
2703
|
+
]
|
|
2704
|
+
});
|
|
2705
|
+
const seoVm = form.field("page.seo").vm;
|
|
2706
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2707
|
+
"metaTitle",
|
|
2708
|
+
"metaDescription"
|
|
2709
|
+
]);
|
|
2710
|
+
});
|
|
2711
|
+
it("resolves tabs inside an inner layout against the children scope", ()=>{
|
|
2712
|
+
const form = createForm({
|
|
2713
|
+
fields: (fields)=>({
|
|
2714
|
+
page: fields.object().fields((f)=>({
|
|
2715
|
+
title: f.text(),
|
|
2716
|
+
body: f.text(),
|
|
2717
|
+
seo: f.object().fields((g)=>({
|
|
2718
|
+
metaTitle: g.text(),
|
|
2719
|
+
metaDescription: g.text()
|
|
2720
|
+
}))
|
|
2721
|
+
}))
|
|
2722
|
+
}),
|
|
2723
|
+
layout: (layout)=>[
|
|
2724
|
+
layout.object("page", (l)=>[
|
|
2725
|
+
l.tabs("pageTabs").tab("general", (tab)=>{
|
|
2726
|
+
tab.label("General").layout((l)=>[
|
|
2727
|
+
l.row("title"),
|
|
2728
|
+
l.row("body")
|
|
2729
|
+
]);
|
|
2730
|
+
}).tab("seo", (tab)=>{
|
|
2731
|
+
tab.label("SEO").layout((l)=>[
|
|
2732
|
+
l.object("seo", (inner)=>[
|
|
2733
|
+
inner.row("metaTitle", "metaDescription")
|
|
2734
|
+
])
|
|
2735
|
+
]);
|
|
2736
|
+
})
|
|
2737
|
+
])
|
|
2738
|
+
]
|
|
2739
|
+
});
|
|
2740
|
+
const pageVm = form.field("page").vm;
|
|
2741
|
+
expect(pageVm.layout.length).toBe(1);
|
|
2742
|
+
const tabs = pageVm.layout[0];
|
|
2743
|
+
expect(tabs.type).toBe("tabs");
|
|
2744
|
+
expect(tabs.tabs.map((t)=>t.id)).toEqual([
|
|
2745
|
+
"general",
|
|
2746
|
+
"seo"
|
|
2747
|
+
]);
|
|
2748
|
+
const generalTab = tabs.tabs[0];
|
|
2749
|
+
expect(asRow(generalTab.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2750
|
+
"title"
|
|
2751
|
+
]);
|
|
2752
|
+
expect(asRow(generalTab.layout[1]).fields.map((f)=>f.name)).toEqual([
|
|
2753
|
+
"body"
|
|
2754
|
+
]);
|
|
2755
|
+
const seoTab = tabs.tabs[1];
|
|
2756
|
+
const seoVm = asRow(seoTab.layout[0]).fields[0];
|
|
2757
|
+
expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
|
|
2758
|
+
"metaTitle",
|
|
2759
|
+
"metaDescription"
|
|
2760
|
+
]);
|
|
2761
|
+
});
|
|
2762
|
+
});
|
|
2763
|
+
describe("runtime template modification (Phase 8d)", ()=>{
|
|
2764
|
+
function createSingleTemplatedForm() {
|
|
2765
|
+
return createForm({
|
|
2766
|
+
fields: (fields)=>({
|
|
2767
|
+
content: fields.object().template("hero", (t)=>{
|
|
2768
|
+
t.label("Hero").fields((f)=>({
|
|
2769
|
+
heading: f.text()
|
|
2770
|
+
}));
|
|
2771
|
+
})
|
|
2772
|
+
})
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
function createListTemplatedForm() {
|
|
2776
|
+
return createForm({
|
|
2777
|
+
fields: (fields)=>({
|
|
2778
|
+
blocks: fields.object().list().template("hero", (t)=>{
|
|
2779
|
+
t.label("Hero").fields((f)=>({
|
|
2780
|
+
heading: f.text()
|
|
2781
|
+
}));
|
|
2782
|
+
}).template("text", (t)=>{
|
|
2783
|
+
t.label("Text").fields((f)=>({
|
|
2784
|
+
body: f.text()
|
|
2785
|
+
}));
|
|
2786
|
+
})
|
|
2787
|
+
})
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
it("templates.add appends a template and the picker lists it", ()=>{
|
|
2791
|
+
const form = createSingleTemplatedForm();
|
|
2792
|
+
const field = form.field("content").as("object");
|
|
2793
|
+
field.templates.add("text", (t)=>{
|
|
2794
|
+
t.label("Text").fields((f)=>({
|
|
2795
|
+
body: f.text()
|
|
2796
|
+
}));
|
|
2797
|
+
});
|
|
2798
|
+
const vm = form.field("content").vm;
|
|
2799
|
+
expect(vm.availableTemplates.map((t)=>t.id)).toEqual([
|
|
2800
|
+
"hero",
|
|
2801
|
+
"text"
|
|
2802
|
+
]);
|
|
2803
|
+
});
|
|
2804
|
+
it("templates.add throws on duplicate id", ()=>{
|
|
2805
|
+
const form = createSingleTemplatedForm();
|
|
2806
|
+
const field = form.field("content").as("object");
|
|
2807
|
+
expect(()=>field.templates.add("hero", (t)=>{
|
|
2808
|
+
t.label("Other").fields((f)=>({
|
|
2809
|
+
x: f.text()
|
|
2810
|
+
}));
|
|
2811
|
+
})).toThrow(/Duplicate template id "hero"/);
|
|
2812
|
+
});
|
|
2813
|
+
it("templates.add throws on reserved _templateId", ()=>{
|
|
2814
|
+
const form = createSingleTemplatedForm();
|
|
2815
|
+
const field = form.field("content").as("object");
|
|
2816
|
+
expect(()=>field.templates.add("_templateId", (t)=>{
|
|
2817
|
+
t.label("Reserved").fields((f)=>({
|
|
2818
|
+
x: f.text()
|
|
2819
|
+
}));
|
|
2820
|
+
})).toThrow(/reserved/);
|
|
2821
|
+
});
|
|
2822
|
+
it("templates.add throws when the template defines a reserved _templateId field", ()=>{
|
|
2823
|
+
const form = createSingleTemplatedForm();
|
|
2824
|
+
const field = form.field("content").as("object");
|
|
2825
|
+
expect(()=>field.templates.add("bad", (t)=>{
|
|
2826
|
+
t.label("Bad").fields((f)=>({
|
|
2827
|
+
_templateId: f.text()
|
|
2828
|
+
}));
|
|
2829
|
+
})).toThrow(/reserved field "_templateId"/);
|
|
2830
|
+
});
|
|
2831
|
+
it("templates.remove removes the template from the picker", ()=>{
|
|
2832
|
+
const form = createListTemplatedForm();
|
|
2833
|
+
const field = form.field("blocks").as("object");
|
|
2834
|
+
field.templates.remove("text");
|
|
2835
|
+
const vm = form.field("blocks").vm;
|
|
2836
|
+
expect(vm.availableTemplates.map((t)=>t.id)).toEqual([
|
|
2837
|
+
"hero"
|
|
2838
|
+
]);
|
|
2839
|
+
});
|
|
2840
|
+
it("templates.remove silently no-ops on unknown id", ()=>{
|
|
2841
|
+
const form = createListTemplatedForm();
|
|
2842
|
+
const field = form.field("blocks").as("object");
|
|
2843
|
+
const before = form.field("blocks").vm.availableTemplates.length;
|
|
2844
|
+
expect(()=>field.templates.remove("nonExistent")).not.toThrow();
|
|
2845
|
+
const after = form.field("blocks").vm.availableTemplates.length;
|
|
2846
|
+
expect(after).toBe(before);
|
|
2847
|
+
});
|
|
2848
|
+
it("templates.remove clears active template on a single-object field via onChange(null) semantics", ()=>{
|
|
2849
|
+
const form = createSingleTemplatedForm();
|
|
2850
|
+
const field = form.field("content").as("object");
|
|
2851
|
+
field.setTemplate("hero");
|
|
2852
|
+
expect(field.activeTemplateId).toBe("hero");
|
|
2853
|
+
field.templates.remove("hero");
|
|
2854
|
+
expect(field.activeTemplateId).toBeNull();
|
|
2855
|
+
expect(field.children.size).toBe(0);
|
|
2856
|
+
expect(form.field("content").getValue()).toBeNull();
|
|
2857
|
+
});
|
|
2858
|
+
it("templates.remove drops list items whose _templateId matches", ()=>{
|
|
2859
|
+
const form = createListTemplatedForm();
|
|
2860
|
+
const field = form.field("blocks").as("object");
|
|
2861
|
+
field.addItem("hero", {
|
|
2862
|
+
heading: "H1"
|
|
2863
|
+
});
|
|
2864
|
+
field.addItem("text", {
|
|
2865
|
+
body: "B1"
|
|
2866
|
+
});
|
|
2867
|
+
field.addItem("hero", {
|
|
2868
|
+
heading: "H2"
|
|
2869
|
+
});
|
|
2870
|
+
expect(field.items.length).toBe(3);
|
|
2871
|
+
field.templates.remove("hero");
|
|
2872
|
+
expect(field.items.length).toBe(1);
|
|
2873
|
+
expect(field.items[0].templateId).toBe("text");
|
|
2874
|
+
});
|
|
2875
|
+
it("templates.add throws when called on a non-templated object field", ()=>{
|
|
2876
|
+
const form = createForm({
|
|
2877
|
+
fields: (fields)=>({
|
|
2878
|
+
plain: fields.object().fields((f)=>({
|
|
2879
|
+
x: f.text()
|
|
2880
|
+
}))
|
|
2881
|
+
})
|
|
2882
|
+
});
|
|
2883
|
+
const field = form.field("plain").as("object");
|
|
2884
|
+
expect(()=>field.templates.add("x", (t)=>{
|
|
2885
|
+
t.label("X").fields((f)=>({
|
|
2886
|
+
y: f.text()
|
|
2887
|
+
}));
|
|
2888
|
+
})).toThrow(/not templated/);
|
|
2889
|
+
});
|
|
2890
|
+
it("templates.remove throws when called on a non-templated object field", ()=>{
|
|
2891
|
+
const form = createForm({
|
|
2892
|
+
fields: (fields)=>({
|
|
2893
|
+
plain: fields.object().fields((f)=>({
|
|
2894
|
+
x: f.text()
|
|
2895
|
+
}))
|
|
2896
|
+
})
|
|
2897
|
+
});
|
|
2898
|
+
const field = form.field("plain").as("object");
|
|
2899
|
+
expect(()=>field.templates.remove("anything")).toThrow(/not templated/);
|
|
2900
|
+
});
|
|
2901
|
+
it("orphan layout entry persists silently and is reused when the same template id is re-added", ()=>{
|
|
2902
|
+
const form = createForm({
|
|
2903
|
+
fields: (fields)=>({
|
|
2904
|
+
content: fields.object().template("hero", (t)=>{
|
|
2905
|
+
t.label("Hero").fields((f)=>({
|
|
2906
|
+
heading: f.text(),
|
|
2907
|
+
subheading: f.text()
|
|
2908
|
+
}));
|
|
2909
|
+
})
|
|
2910
|
+
}),
|
|
2911
|
+
layout: (layout)=>[
|
|
2912
|
+
layout.object("content", {
|
|
2913
|
+
hero: (l)=>[
|
|
2914
|
+
l.row("heading", "subheading")
|
|
2915
|
+
]
|
|
2916
|
+
})
|
|
2917
|
+
]
|
|
2918
|
+
});
|
|
2919
|
+
const field = form.field("content").as("object");
|
|
2920
|
+
field.setTemplate("hero");
|
|
2921
|
+
const vmBefore = form.field("content").vm;
|
|
2922
|
+
const rowBefore = asRow(vmBefore.layout[0]);
|
|
2923
|
+
expect(rowBefore.fields.map((f)=>f.name)).toEqual([
|
|
2924
|
+
"heading",
|
|
2925
|
+
"subheading"
|
|
2926
|
+
]);
|
|
2927
|
+
field.templates.remove("hero");
|
|
2928
|
+
field.templates.add("hero", (t)=>{
|
|
2929
|
+
t.label("Hero v2").fields((f)=>({
|
|
2930
|
+
heading: f.text(),
|
|
2931
|
+
subheading: f.text()
|
|
2932
|
+
}));
|
|
2933
|
+
});
|
|
2934
|
+
field.setTemplate("hero");
|
|
2935
|
+
const vmAfter = form.field("content").vm;
|
|
2936
|
+
const rowAfter = asRow(vmAfter.layout[0]);
|
|
2937
|
+
expect(rowAfter.fields.map((f)=>f.name)).toEqual([
|
|
2938
|
+
"heading",
|
|
2939
|
+
"subheading"
|
|
2940
|
+
]);
|
|
2941
|
+
});
|
|
2942
|
+
it("removing all templates does not emit dev warnings (orphan suppression)", ()=>{
|
|
2943
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(()=>{});
|
|
2944
|
+
const form = createSingleTemplatedForm();
|
|
2945
|
+
const field = form.field("content").as("object");
|
|
2946
|
+
field.templates.remove("hero");
|
|
2947
|
+
const vm = form.field("content").vm;
|
|
2948
|
+
expect(vm.availableTemplates).toEqual([]);
|
|
2949
|
+
expect(warn).not.toHaveBeenCalled();
|
|
2950
|
+
warn.mockRestore();
|
|
2951
|
+
});
|
|
2952
|
+
});
|
|
2953
|
+
describe("requiredWhen (Phase 11)", ()=>{
|
|
2954
|
+
it("makes a field required when the callback returns true", async ()=>{
|
|
2955
|
+
const form = createForm({
|
|
2956
|
+
fields: (fields)=>({
|
|
2957
|
+
plan: fields.text().defaultValue("free"),
|
|
2958
|
+
seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Seats required")
|
|
2959
|
+
})
|
|
2960
|
+
});
|
|
2961
|
+
expect(form.field("seats").vm.required).toBe(false);
|
|
2962
|
+
expect(await form.validate()).toBe(true);
|
|
2963
|
+
form.field("plan").setValue("pro");
|
|
2964
|
+
expect(form.field("seats").vm.required).toBe(true);
|
|
2965
|
+
expect(await form.validate()).toBe(false);
|
|
2966
|
+
expect(form.field("seats").vm.validation.message).toBe("Seats required");
|
|
2967
|
+
});
|
|
2968
|
+
it("chains requiredWhen callbacks — first truthy wins", async ()=>{
|
|
2969
|
+
const form = createForm({
|
|
2970
|
+
fields: (fields)=>({
|
|
2971
|
+
plan: fields.text().defaultValue("free"),
|
|
2972
|
+
flag: fields.text().defaultValue("off"),
|
|
2973
|
+
seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Pro requires it").requiredWhen((f)=>"on" === f.field("flag").getValue(), "Flag requires it")
|
|
2974
|
+
})
|
|
2975
|
+
});
|
|
2976
|
+
form.field("flag").setValue("on");
|
|
2977
|
+
await form.validate();
|
|
2978
|
+
expect(form.field("seats").vm.validation.message).toBe("Flag requires it");
|
|
2979
|
+
form.field("plan").setValue("pro");
|
|
2980
|
+
await form.validate();
|
|
2981
|
+
expect(form.field("seats").vm.validation.message).toBe("Pro requires it");
|
|
2982
|
+
});
|
|
2983
|
+
it("hard .required() always wins over requiredWhen messages", async ()=>{
|
|
2984
|
+
const form = createForm({
|
|
2985
|
+
fields: (fields)=>({
|
|
2986
|
+
seats: fields.text().required("Always required").requiredWhen(()=>true, "Conditional message")
|
|
2987
|
+
})
|
|
2988
|
+
});
|
|
2989
|
+
await form.validate();
|
|
2990
|
+
expect(form.field("seats").vm.required).toBe(true);
|
|
2991
|
+
expect(form.field("seats").vm.validation.message).toBe("Always required");
|
|
2992
|
+
});
|
|
2993
|
+
it("modifier-added requiredWhen chains with builder-defined ones", async ()=>{
|
|
2994
|
+
const form = createForm({
|
|
2995
|
+
fields: (fields)=>({
|
|
2996
|
+
plan: fields.text().defaultValue("free"),
|
|
2997
|
+
other: fields.text().defaultValue("off"),
|
|
2998
|
+
seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Pro required")
|
|
2999
|
+
})
|
|
3000
|
+
});
|
|
3001
|
+
form.field("seats").addRequiredWhen((f)=>"on" === f.field("other").getValue(), "Other required");
|
|
3002
|
+
expect(form.field("seats").vm.required).toBe(false);
|
|
3003
|
+
form.field("other").setValue("on");
|
|
3004
|
+
await form.validate();
|
|
3005
|
+
expect(form.field("seats").vm.validation.message).toBe("Other required");
|
|
3006
|
+
});
|
|
3007
|
+
});
|
|
3008
|
+
describe("computed / computedUntilDirty (Phase 11)", ()=>{
|
|
3009
|
+
it("computed field exposes derived value reactively", ()=>{
|
|
3010
|
+
const form = createForm({
|
|
3011
|
+
fields: (fields)=>({
|
|
3012
|
+
first: fields.text().defaultValue("Ada"),
|
|
3013
|
+
last: fields.text().defaultValue("Lovelace"),
|
|
3014
|
+
full: fields.text().computed((f)=>`${f.field("first").getValue()} ${f.field("last").getValue()}`)
|
|
3015
|
+
})
|
|
3016
|
+
});
|
|
3017
|
+
expect(form.field("full").getValue()).toBe("Ada Lovelace");
|
|
3018
|
+
form.field("first").setValue("Grace");
|
|
3019
|
+
expect(form.field("full").getValue()).toBe("Grace Lovelace");
|
|
3020
|
+
});
|
|
3021
|
+
it("computed field stays editable but value remains derived", ()=>{
|
|
3022
|
+
const form = createForm({
|
|
3023
|
+
fields: (fields)=>({
|
|
3024
|
+
src: fields.text().defaultValue("A"),
|
|
3025
|
+
derived: fields.text().computed((f)=>f.field("src").getValue())
|
|
3026
|
+
})
|
|
3027
|
+
});
|
|
3028
|
+
expect(form.field("derived").vm.disabled).toBe(false);
|
|
3029
|
+
form.field("derived").vm.onChange("manual override");
|
|
3030
|
+
expect(form.field("derived").getValue()).toBe("A");
|
|
3031
|
+
});
|
|
3032
|
+
it("computedUntilDirty switches to manual after first UI edit", ()=>{
|
|
3033
|
+
const form = createForm({
|
|
3034
|
+
fields: (fields)=>({
|
|
3035
|
+
src: fields.text().defaultValue("A"),
|
|
3036
|
+
derived: fields.text().computedUntilDirty((f)=>`derived-${f.field("src").getValue()}`)
|
|
3037
|
+
})
|
|
3038
|
+
});
|
|
3039
|
+
expect(form.field("derived").getValue()).toBe("derived-A");
|
|
3040
|
+
form.field("derived").vm.onChange("manual");
|
|
3041
|
+
expect(form.field("derived").getValue()).toBe("manual");
|
|
3042
|
+
form.field("src").setValue("B");
|
|
3043
|
+
expect(form.field("derived").getValue()).toBe("manual");
|
|
3044
|
+
});
|
|
3045
|
+
it("computed field still participates in validation", async ()=>{
|
|
3046
|
+
const form = createForm({
|
|
3047
|
+
fields: (fields)=>({
|
|
3048
|
+
src: fields.text().defaultValue(""),
|
|
3049
|
+
derived: fields.text().required("Derived must not be empty").computed((f)=>f.field("src").getValue())
|
|
3050
|
+
})
|
|
3051
|
+
});
|
|
3052
|
+
const valid = await form.validate();
|
|
3053
|
+
expect(valid).toBe(false);
|
|
3054
|
+
expect(form.field("derived").vm.validation.message).toBe("Derived must not be empty");
|
|
3055
|
+
form.field("src").setValue("hello");
|
|
3056
|
+
expect(await form.validate()).toBe(true);
|
|
3057
|
+
});
|
|
3058
|
+
it("modifier setComputed converts a regular field into a computed one", ()=>{
|
|
3059
|
+
const form = createForm({
|
|
3060
|
+
fields: (fields)=>({
|
|
3061
|
+
src: fields.text().defaultValue("X"),
|
|
3062
|
+
derived: fields.text().defaultValue("initial")
|
|
3063
|
+
})
|
|
3064
|
+
});
|
|
3065
|
+
form.field("derived").setComputed((f)=>`from-${f.field("src").getValue()}`);
|
|
3066
|
+
expect(form.field("derived").getValue()).toBe("from-X");
|
|
3067
|
+
form.field("src").setValue("Y");
|
|
3068
|
+
expect(form.field("derived").getValue()).toBe("from-Y");
|
|
3069
|
+
});
|
|
3070
|
+
});
|
|
3071
|
+
describe('field("...").as("object").fields() (Phase 11)', ()=>{
|
|
3072
|
+
it("adds new children to an existing object field at runtime", ()=>{
|
|
3073
|
+
const form = createForm({
|
|
3074
|
+
fields: (fields)=>({
|
|
3075
|
+
profile: fields.object().fields((f)=>({
|
|
3076
|
+
firstName: f.text().label("First")
|
|
3077
|
+
}))
|
|
3078
|
+
})
|
|
3079
|
+
});
|
|
3080
|
+
form.field("profile").as("object").fields((f)=>({
|
|
3081
|
+
lastName: f.text().label("Last")
|
|
3082
|
+
}));
|
|
3083
|
+
form.field("profile.firstName").setValue("Ada");
|
|
3084
|
+
form.field("profile.lastName").setValue("Lovelace");
|
|
3085
|
+
expect(form.getData()).toEqual({
|
|
3086
|
+
profile: {
|
|
3087
|
+
firstName: "Ada",
|
|
3088
|
+
lastName: "Lovelace"
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
});
|
|
3092
|
+
it("replaces existing children when keys collide", ()=>{
|
|
3093
|
+
const form = createForm({
|
|
3094
|
+
fields: (fields)=>({
|
|
3095
|
+
profile: fields.object().fields((f)=>({
|
|
3096
|
+
firstName: f.text().label("Old")
|
|
3097
|
+
}))
|
|
3098
|
+
})
|
|
3099
|
+
});
|
|
3100
|
+
form.field("profile").as("object").fields((f)=>({
|
|
3101
|
+
firstName: f.text().label("New")
|
|
3102
|
+
}));
|
|
3103
|
+
const profile = form.field("profile").as("object");
|
|
3104
|
+
expect(profile.children.get("firstName")?.config.label).toBe("New");
|
|
3105
|
+
});
|
|
3106
|
+
it("removes children when factory returns undefined", ()=>{
|
|
3107
|
+
const form = createForm({
|
|
3108
|
+
fields: (fields)=>({
|
|
3109
|
+
profile: fields.object().fields((f)=>({
|
|
3110
|
+
firstName: f.text(),
|
|
3111
|
+
lastName: f.text()
|
|
3112
|
+
}))
|
|
3113
|
+
})
|
|
3114
|
+
});
|
|
3115
|
+
form.field("profile").as("object").fields(()=>({
|
|
3116
|
+
lastName: void 0
|
|
3117
|
+
}));
|
|
3118
|
+
const profile = form.field("profile").as("object");
|
|
3119
|
+
expect(profile.children.has("lastName")).toBe(false);
|
|
3120
|
+
expect(profile.children.has("firstName")).toBe(true);
|
|
3121
|
+
});
|
|
3122
|
+
it("propagates added children to existing list items", ()=>{
|
|
3123
|
+
const form = createForm({
|
|
3124
|
+
fields: (fields)=>({
|
|
3125
|
+
contacts: fields.object().list().fields((f)=>({
|
|
3126
|
+
name: f.text()
|
|
3127
|
+
}))
|
|
3128
|
+
})
|
|
3129
|
+
});
|
|
3130
|
+
const contacts = form.field("contacts").as("object");
|
|
3131
|
+
contacts.addItem({
|
|
3132
|
+
name: "Ada"
|
|
3133
|
+
});
|
|
3134
|
+
contacts.addItem({
|
|
3135
|
+
name: "Grace"
|
|
3136
|
+
});
|
|
3137
|
+
contacts.fields((f)=>({
|
|
3138
|
+
email: f.text()
|
|
3139
|
+
}));
|
|
3140
|
+
expect(contacts.items[0].children.has("email")).toBe(true);
|
|
3141
|
+
expect(contacts.items[1].children.has("email")).toBe(true);
|
|
3142
|
+
contacts.addItem({
|
|
3143
|
+
name: "Linus",
|
|
3144
|
+
email: "linus@example.com"
|
|
3145
|
+
});
|
|
3146
|
+
expect(contacts.items[2].children.get("email")?.getValue()).toBe("linus@example.com");
|
|
3147
|
+
});
|
|
3148
|
+
it("throws when called on a templated object field", ()=>{
|
|
3149
|
+
const form = createForm({
|
|
3150
|
+
fields: (fields)=>({
|
|
3151
|
+
block: fields.object().template("a", (t)=>{
|
|
3152
|
+
t.label("A").fields((f)=>({
|
|
3153
|
+
x: f.text()
|
|
3154
|
+
}));
|
|
3155
|
+
})
|
|
3156
|
+
})
|
|
3157
|
+
});
|
|
3158
|
+
expect(()=>{
|
|
3159
|
+
form.field("block").as("object").fields((f)=>({
|
|
3160
|
+
y: f.text()
|
|
3161
|
+
}));
|
|
3162
|
+
}).toThrow(/templated/);
|
|
3163
|
+
});
|
|
3164
|
+
});
|
|
3165
|
+
describe("form.addRule() (Phase 11)", ()=>{
|
|
3166
|
+
it("runs a Zod schema against getData() and surfaces issues", async ()=>{
|
|
3167
|
+
const form = createForm({
|
|
3168
|
+
fields: (fields)=>({
|
|
3169
|
+
password: fields.text().defaultValue("a"),
|
|
3170
|
+
confirm: fields.text().defaultValue("b")
|
|
3171
|
+
})
|
|
3172
|
+
});
|
|
3173
|
+
form.addRule(z.object({
|
|
3174
|
+
password: z.string(),
|
|
3175
|
+
confirm: z.string()
|
|
3176
|
+
}).refine((d)=>d.password === d.confirm, {
|
|
3177
|
+
message: "Passwords must match",
|
|
3178
|
+
path: [
|
|
3179
|
+
"confirm"
|
|
3180
|
+
]
|
|
3181
|
+
}));
|
|
3182
|
+
const valid = await form.validate();
|
|
3183
|
+
expect(valid).toBe(false);
|
|
3184
|
+
expect(form.errors.some((e)=>"Passwords must match" === e.message)).toBe(true);
|
|
3185
|
+
expect(form.field("confirm").vm.validation.isValid).toBe(false);
|
|
3186
|
+
expect(form.field("confirm").vm.validation.message).toBe("Passwords must match");
|
|
3187
|
+
});
|
|
3188
|
+
it("runs an imperative function and merges returned errors", async ()=>{
|
|
3189
|
+
const form = createForm({
|
|
3190
|
+
fields: (fields)=>({
|
|
3191
|
+
age: fields.text().defaultValue("17")
|
|
3192
|
+
})
|
|
3193
|
+
});
|
|
3194
|
+
form.addRule((f)=>{
|
|
3195
|
+
if (Number(f.field("age").getValue()) < 18) return [
|
|
3196
|
+
{
|
|
3197
|
+
path: "age",
|
|
3198
|
+
message: "Must be 18+"
|
|
3199
|
+
}
|
|
3200
|
+
];
|
|
3201
|
+
return [];
|
|
3202
|
+
});
|
|
3203
|
+
expect(await form.validate()).toBe(false);
|
|
3204
|
+
expect(form.field("age").vm.validation.message).toBe("Must be 18+");
|
|
3205
|
+
form.field("age").setValue("21");
|
|
3206
|
+
expect(await form.validate()).toBe(true);
|
|
3207
|
+
});
|
|
3208
|
+
it("supports async imperative rules", async ()=>{
|
|
3209
|
+
const form = createForm({
|
|
3210
|
+
fields: (fields)=>({
|
|
3211
|
+
name: fields.text().defaultValue("taken")
|
|
3212
|
+
})
|
|
3213
|
+
});
|
|
3214
|
+
form.addRule(async (f)=>{
|
|
3215
|
+
await Promise.resolve();
|
|
3216
|
+
if ("taken" === f.field("name").getValue()) return [
|
|
3217
|
+
{
|
|
3218
|
+
path: "name",
|
|
3219
|
+
message: "Already taken"
|
|
3220
|
+
}
|
|
3221
|
+
];
|
|
3222
|
+
return [];
|
|
3223
|
+
});
|
|
3224
|
+
expect(await form.validate()).toBe(false);
|
|
3225
|
+
expect(form.field("name").vm.validation.message).toBe("Already taken");
|
|
3226
|
+
});
|
|
3227
|
+
});
|
|
3228
|
+
describe("form.setLayout() (Phase 11)", ()=>{
|
|
3229
|
+
it("replaces the layout entirely", ()=>{
|
|
3230
|
+
const form = createForm({
|
|
3231
|
+
fields: (fields)=>({
|
|
3232
|
+
a: fields.text(),
|
|
3233
|
+
b: fields.text(),
|
|
3234
|
+
c: fields.text()
|
|
3235
|
+
}),
|
|
3236
|
+
layout: (layout)=>[
|
|
3237
|
+
layout.row("a"),
|
|
3238
|
+
layout.row("b"),
|
|
3239
|
+
layout.row("c")
|
|
3240
|
+
]
|
|
3241
|
+
});
|
|
3242
|
+
form.setLayout((layout)=>[
|
|
3243
|
+
layout.row("c", "a")
|
|
3244
|
+
]);
|
|
3245
|
+
const layout = form.vm.layout;
|
|
3246
|
+
expect(layout).toHaveLength(1);
|
|
3247
|
+
const row = asRow(layout[0]);
|
|
3248
|
+
expect(row.fields.map((f)=>f.name)).toEqual([
|
|
3249
|
+
"c",
|
|
3250
|
+
"a"
|
|
3251
|
+
]);
|
|
3252
|
+
});
|
|
3253
|
+
it("emits orphan warnings for fields not in the new layout", ()=>{
|
|
3254
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(()=>{});
|
|
3255
|
+
const form = createForm({
|
|
3256
|
+
fields: (fields)=>({
|
|
3257
|
+
a: fields.text(),
|
|
3258
|
+
b: fields.text()
|
|
3259
|
+
})
|
|
3260
|
+
});
|
|
3261
|
+
warn.mockClear();
|
|
3262
|
+
form.setLayout((layout)=>[
|
|
3263
|
+
layout.row("a")
|
|
3264
|
+
]);
|
|
3265
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('Field "b" is not in the layout'));
|
|
3266
|
+
warn.mockRestore();
|
|
3267
|
+
});
|
|
3268
|
+
});
|
|
3269
|
+
describe("Phase 10: Advanced Validation", ()=>{
|
|
3270
|
+
describe("field.vm.validating", ()=>{
|
|
3271
|
+
it("should be false initially", ()=>{
|
|
3272
|
+
const form = createBasicForm();
|
|
3273
|
+
expect(form.field("title").vm.validating).toBe(false);
|
|
3274
|
+
});
|
|
3275
|
+
it("should be true while async schema validates", async ()=>{
|
|
3276
|
+
let resolveValidation;
|
|
3277
|
+
const form = createForm({
|
|
3278
|
+
fields: (fields)=>({
|
|
3279
|
+
email: fields.text().schema(z.string().refine(async ()=>{
|
|
3280
|
+
await new Promise((r)=>{
|
|
3281
|
+
resolveValidation = r;
|
|
3282
|
+
});
|
|
3283
|
+
return true;
|
|
3284
|
+
}))
|
|
3285
|
+
})
|
|
3286
|
+
});
|
|
3287
|
+
form.field("email").setValue("test@test.com");
|
|
3288
|
+
const promise = form.validate();
|
|
3289
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3290
|
+
expect(form.field("email").vm.validating).toBe(true);
|
|
3291
|
+
resolveValidation();
|
|
3292
|
+
await promise;
|
|
3293
|
+
expect(form.field("email").vm.validating).toBe(false);
|
|
3294
|
+
});
|
|
3295
|
+
it("should be false after sync-only validation", async ()=>{
|
|
3296
|
+
const form = createForm({
|
|
3297
|
+
fields: (fields)=>({
|
|
3298
|
+
name: fields.text().required()
|
|
3299
|
+
})
|
|
3300
|
+
});
|
|
3301
|
+
form.field("name").setValue("hello");
|
|
3302
|
+
await form.validate();
|
|
3303
|
+
expect(form.field("name").vm.validating).toBe(false);
|
|
3304
|
+
});
|
|
3305
|
+
});
|
|
3306
|
+
describe("form.submitted", ()=>{
|
|
3307
|
+
it("should be false initially", ()=>{
|
|
3308
|
+
const form = createBasicForm();
|
|
3309
|
+
expect(form.submitted).toBe(false);
|
|
3310
|
+
});
|
|
3311
|
+
it("should be true after validate()", async ()=>{
|
|
3312
|
+
const form = createBasicForm();
|
|
3313
|
+
form.field("title").setValue("t");
|
|
3314
|
+
form.field("path").setValue("/p");
|
|
3315
|
+
await form.validate();
|
|
3316
|
+
expect(form.submitted).toBe(true);
|
|
3317
|
+
});
|
|
3318
|
+
it("should be true after failed validate()", async ()=>{
|
|
3319
|
+
const form = createBasicForm();
|
|
3320
|
+
await form.validate();
|
|
3321
|
+
expect(form.submitted).toBe(true);
|
|
3322
|
+
});
|
|
3323
|
+
it("should reset to false on setData()", async ()=>{
|
|
3324
|
+
const form = createBasicForm();
|
|
3325
|
+
form.field("title").setValue("t");
|
|
3326
|
+
form.field("path").setValue("/p");
|
|
3327
|
+
await form.validate();
|
|
3328
|
+
expect(form.submitted).toBe(true);
|
|
3329
|
+
form.setData({
|
|
3330
|
+
title: "new",
|
|
3331
|
+
path: "/new"
|
|
3332
|
+
});
|
|
3333
|
+
expect(form.submitted).toBe(false);
|
|
3334
|
+
});
|
|
3335
|
+
it("should reset to false on reset()", async ()=>{
|
|
3336
|
+
const form = createBasicForm();
|
|
3337
|
+
form.field("title").setValue("t");
|
|
3338
|
+
form.field("path").setValue("/p");
|
|
3339
|
+
await form.validate();
|
|
3340
|
+
expect(form.submitted).toBe(true);
|
|
3341
|
+
form.reset();
|
|
3342
|
+
expect(form.submitted).toBe(false);
|
|
3343
|
+
});
|
|
3344
|
+
});
|
|
3345
|
+
describe("validate-on-blur after submit", ()=>{
|
|
3346
|
+
it("should not validate on blur before first submit", async ()=>{
|
|
3347
|
+
const form = createForm({
|
|
3348
|
+
fields: (fields)=>({
|
|
3349
|
+
email: fields.text().required("Required")
|
|
3350
|
+
})
|
|
3351
|
+
});
|
|
3352
|
+
form.field("email").vm.onBlur();
|
|
3353
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3354
|
+
expect(form.field("email").vm.validation.isValid).toBeNull();
|
|
3355
|
+
});
|
|
3356
|
+
it("should validate on blur after first submit", async ()=>{
|
|
3357
|
+
const form = createForm({
|
|
3358
|
+
fields: (fields)=>({
|
|
3359
|
+
email: fields.text().required("Required").schema(z.string().email("Invalid email"))
|
|
3360
|
+
})
|
|
3361
|
+
});
|
|
3362
|
+
form.field("email").setValue("bad");
|
|
3363
|
+
await form.submit();
|
|
3364
|
+
expect(form.field("email").vm.validation.isValid).toBe(false);
|
|
3365
|
+
form.field("email").setValue("valid@email.com");
|
|
3366
|
+
form.field("email").vm.onBlur();
|
|
3367
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3368
|
+
expect(form.field("email").vm.validation.isValid).toBe(true);
|
|
3369
|
+
});
|
|
3370
|
+
it("should show error on blur for invalid value after submit", async ()=>{
|
|
3371
|
+
const form = createForm({
|
|
3372
|
+
fields: (fields)=>({
|
|
3373
|
+
name: fields.text().required("Name is required")
|
|
3374
|
+
})
|
|
3375
|
+
});
|
|
3376
|
+
form.field("name").setValue("hello");
|
|
3377
|
+
await form.submit();
|
|
3378
|
+
expect(form.field("name").vm.validation.isValid).toBe(true);
|
|
3379
|
+
form.field("name").setValue("");
|
|
3380
|
+
form.field("name").vm.onBlur();
|
|
3381
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3382
|
+
expect(form.field("name").vm.validation.isValid).toBe(false);
|
|
3383
|
+
expect(form.field("name").vm.validation.message).toBe("Name is required");
|
|
3384
|
+
});
|
|
3385
|
+
});
|
|
3386
|
+
describe("validation memoization", ()=>{
|
|
3387
|
+
it("should not re-run schema on blur when value unchanged", async ()=>{
|
|
3388
|
+
const schemaSpy = vi.fn().mockReturnValue(true);
|
|
3389
|
+
const form = createForm({
|
|
3390
|
+
fields: (fields)=>({
|
|
3391
|
+
slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
|
|
3392
|
+
})
|
|
3393
|
+
});
|
|
3394
|
+
form.field("slug").setValue("hello");
|
|
3395
|
+
await form.submit();
|
|
3396
|
+
expect(schemaSpy).toHaveBeenCalledTimes(1);
|
|
3397
|
+
form.field("slug").vm.onBlur();
|
|
3398
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3399
|
+
expect(schemaSpy).toHaveBeenCalledTimes(1);
|
|
3400
|
+
});
|
|
3401
|
+
it("should re-run schema on blur when value changed", async ()=>{
|
|
3402
|
+
const schemaSpy = vi.fn().mockReturnValue(true);
|
|
3403
|
+
const form = createForm({
|
|
3404
|
+
fields: (fields)=>({
|
|
3405
|
+
slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
|
|
3406
|
+
})
|
|
3407
|
+
});
|
|
3408
|
+
form.field("slug").setValue("hello");
|
|
3409
|
+
await form.submit();
|
|
3410
|
+
expect(schemaSpy).toHaveBeenCalledTimes(1);
|
|
3411
|
+
form.field("slug").setValue("world");
|
|
3412
|
+
form.field("slug").vm.onBlur();
|
|
3413
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3414
|
+
expect(schemaSpy).toHaveBeenCalledTimes(2);
|
|
3415
|
+
});
|
|
3416
|
+
it("should always re-run schema on form.validate()", async ()=>{
|
|
3417
|
+
const schemaSpy = vi.fn().mockReturnValue(true);
|
|
3418
|
+
const form = createForm({
|
|
3419
|
+
fields: (fields)=>({
|
|
3420
|
+
slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
|
|
3421
|
+
})
|
|
3422
|
+
});
|
|
3423
|
+
form.field("slug").setValue("hello");
|
|
3424
|
+
await form.validate();
|
|
3425
|
+
expect(schemaSpy).toHaveBeenCalledTimes(1);
|
|
3426
|
+
await form.validate();
|
|
3427
|
+
expect(schemaSpy).toHaveBeenCalledTimes(2);
|
|
3428
|
+
});
|
|
3429
|
+
it("should clear cache on resetValidation()", async ()=>{
|
|
3430
|
+
const schemaSpy = vi.fn().mockReturnValue(true);
|
|
3431
|
+
const form = createForm({
|
|
3432
|
+
fields: (fields)=>({
|
|
3433
|
+
slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
|
|
3434
|
+
})
|
|
3435
|
+
});
|
|
3436
|
+
form.field("slug").setValue("hello");
|
|
3437
|
+
await form.submit();
|
|
3438
|
+
expect(schemaSpy).toHaveBeenCalledTimes(1);
|
|
3439
|
+
form.field("slug").resetValidation();
|
|
3440
|
+
form.field("slug").vm.onBlur();
|
|
3441
|
+
await new Promise((r)=>setTimeout(r, 0));
|
|
3442
|
+
expect(schemaSpy).toHaveBeenCalledTimes(2);
|
|
3443
|
+
});
|
|
3444
|
+
});
|
|
3445
|
+
describe("async validation with z.refine", ()=>{
|
|
3446
|
+
it("should validate async refine on submit", async ()=>{
|
|
3447
|
+
const form = createForm({
|
|
3448
|
+
fields: (fields)=>({
|
|
3449
|
+
slug: fields.text().schema(z.string().refine(async (value)=>"taken" !== value, "This slug is already taken"))
|
|
3450
|
+
})
|
|
3451
|
+
});
|
|
3452
|
+
form.field("slug").setValue("taken");
|
|
3453
|
+
const result = await form.submit();
|
|
3454
|
+
expect(result).toBe(false);
|
|
3455
|
+
expect(form.field("slug").vm.validation.isValid).toBe(false);
|
|
3456
|
+
expect(form.field("slug").vm.validation.message).toBe("This slug is already taken");
|
|
3457
|
+
});
|
|
3458
|
+
it("should pass async refine with valid value", async ()=>{
|
|
3459
|
+
const form = createForm({
|
|
3460
|
+
fields: (fields)=>({
|
|
3461
|
+
slug: fields.text().schema(z.string().refine(async (value)=>"taken" !== value, "This slug is already taken"))
|
|
3462
|
+
})
|
|
3463
|
+
});
|
|
3464
|
+
form.field("slug").setValue("available");
|
|
3465
|
+
const result = await form.submit();
|
|
3466
|
+
expect(result).toEqual({
|
|
3467
|
+
slug: "available"
|
|
3468
|
+
});
|
|
3469
|
+
expect(form.field("slug").vm.validation.isValid).toBe(true);
|
|
3470
|
+
});
|
|
3471
|
+
});
|
|
3472
|
+
});
|
|
3473
|
+
describe("normalizeValue", ()=>{
|
|
3474
|
+
it("should coerce string to number on setValue for number fields", ()=>{
|
|
3475
|
+
const form = createForm({
|
|
3476
|
+
fields: (fields)=>({
|
|
3477
|
+
count: fields.number().label("Count")
|
|
3478
|
+
})
|
|
3479
|
+
});
|
|
3480
|
+
form.field("count").setValue("42");
|
|
3481
|
+
expect(form.field("count").getValue()).toBe(42);
|
|
3482
|
+
});
|
|
3483
|
+
it("should normalize invalid values to null for number fields", ()=>{
|
|
3484
|
+
const form = createForm({
|
|
3485
|
+
fields: (fields)=>({
|
|
3486
|
+
count: fields.number().label("Count")
|
|
3487
|
+
})
|
|
3488
|
+
});
|
|
3489
|
+
form.field("count").setValue("");
|
|
3490
|
+
expect(form.field("count").getValue()).toBe(null);
|
|
3491
|
+
form.field("count").setValue(null);
|
|
3492
|
+
expect(form.field("count").getValue()).toBe(null);
|
|
3493
|
+
form.field("count").setValue("abc");
|
|
3494
|
+
expect(form.field("count").getValue()).toBe(null);
|
|
3495
|
+
});
|
|
3496
|
+
it("should store number for number field with options", ()=>{
|
|
3497
|
+
const form = createForm({
|
|
3498
|
+
fields: (fields)=>({
|
|
3499
|
+
tier: fields.number().label("Tier").options([
|
|
3500
|
+
{
|
|
3501
|
+
label: "Tier 1",
|
|
3502
|
+
value: 100
|
|
3503
|
+
},
|
|
3504
|
+
{
|
|
3505
|
+
label: "Tier 2",
|
|
3506
|
+
value: 200
|
|
3507
|
+
}
|
|
3508
|
+
])
|
|
3509
|
+
})
|
|
3510
|
+
});
|
|
3511
|
+
form.field("tier").setValue("100");
|
|
3512
|
+
expect(form.field("tier").getValue()).toBe(100);
|
|
3513
|
+
expect(typeof form.field("tier").getValue()).toBe("number");
|
|
3514
|
+
});
|
|
3515
|
+
it("should coerce to boolean on setValue for boolean fields", ()=>{
|
|
3516
|
+
const form = createForm({
|
|
3517
|
+
fields: (fields)=>({
|
|
3518
|
+
active: fields.boolean().label("Active")
|
|
3519
|
+
})
|
|
3520
|
+
});
|
|
3521
|
+
form.field("active").setValue(1);
|
|
3522
|
+
expect(form.field("active").getValue()).toBe(true);
|
|
3523
|
+
form.field("active").setValue(0);
|
|
3524
|
+
expect(form.field("active").getValue()).toBe(false);
|
|
3525
|
+
});
|
|
3526
|
+
it("should apply normalizeValue on setData (via setValueSilent)", ()=>{
|
|
3527
|
+
const form = createForm({
|
|
3528
|
+
fields: (fields)=>({
|
|
3529
|
+
count: fields.number().label("Count")
|
|
3530
|
+
})
|
|
3531
|
+
});
|
|
3532
|
+
form.setData({
|
|
3533
|
+
count: "42"
|
|
3534
|
+
});
|
|
3535
|
+
expect(form.field("count").getValue()).toBe(42);
|
|
3536
|
+
expect(typeof form.field("count").getValue()).toBe("number");
|
|
3537
|
+
});
|
|
3538
|
+
it("should not alter text field values", ()=>{
|
|
3539
|
+
const form = createForm({
|
|
3540
|
+
fields: (fields)=>({
|
|
3541
|
+
name: fields.text().label("Name")
|
|
3542
|
+
})
|
|
3543
|
+
});
|
|
3544
|
+
form.field("name").setValue("hello");
|
|
3545
|
+
expect(form.field("name").getValue()).toBe("hello");
|
|
3546
|
+
form.field("name").setValue(42);
|
|
3547
|
+
expect(form.field("name").getValue()).toBe(42);
|
|
3548
|
+
});
|
|
3549
|
+
it("should run normalizeValue before beforeChange", ()=>{
|
|
3550
|
+
const log = [];
|
|
3551
|
+
const form = createForm({
|
|
3552
|
+
fields: (fields)=>({
|
|
3553
|
+
count: fields.number().label("Count").beforeChange((value)=>{
|
|
3554
|
+
log.push(value);
|
|
3555
|
+
return value;
|
|
3556
|
+
})
|
|
3557
|
+
})
|
|
3558
|
+
});
|
|
3559
|
+
form.field("count").setValue("7");
|
|
3560
|
+
expect(log).toEqual([
|
|
3561
|
+
7
|
|
3562
|
+
]);
|
|
3563
|
+
});
|
|
3564
|
+
});
|
|
3565
|
+
describe("focusField", ()=>{
|
|
3566
|
+
it("should set focusRequested on the target field", ()=>{
|
|
3567
|
+
const form = createForm({
|
|
3568
|
+
fields: (fields)=>({
|
|
3569
|
+
title: fields.text().label("Title"),
|
|
3570
|
+
path: fields.text().label("Path")
|
|
3571
|
+
})
|
|
3572
|
+
});
|
|
3573
|
+
form.focusField("title");
|
|
3574
|
+
expect(form.field("title").vm.focusRequested).toBe(true);
|
|
3575
|
+
expect(form.field("path").vm.focusRequested).toBe(false);
|
|
3576
|
+
});
|
|
3577
|
+
it("should activate the correct tab", ()=>{
|
|
3578
|
+
const form = createForm({
|
|
3579
|
+
fields: (fields)=>({
|
|
3580
|
+
title: fields.text().label("Title"),
|
|
3581
|
+
slug: fields.text().label("Slug")
|
|
3582
|
+
}),
|
|
3583
|
+
layout: (layout)=>[
|
|
3584
|
+
layout.tabs("mainTabs").tab("general", (tab)=>{
|
|
3585
|
+
tab.label("General").layout((l)=>[
|
|
3586
|
+
l.row("title")
|
|
3587
|
+
]);
|
|
3588
|
+
}).tab("seo", (tab)=>{
|
|
3589
|
+
tab.label("SEO").layout((l)=>[
|
|
3590
|
+
l.row("slug")
|
|
3591
|
+
]);
|
|
3592
|
+
})
|
|
3593
|
+
]
|
|
3594
|
+
});
|
|
3595
|
+
expect(form.vm.layout[0].activeTabId).toBe("general");
|
|
3596
|
+
form.focusField("slug");
|
|
3597
|
+
expect(form.field("slug").vm.focusRequested).toBe(true);
|
|
3598
|
+
expect(form.vm.layout[0].activeTabId).toBe("seo");
|
|
3599
|
+
});
|
|
3600
|
+
it("should activate nested tabs inside object fields", ()=>{
|
|
3601
|
+
const form = createForm({
|
|
3602
|
+
fields: (fields)=>({
|
|
3603
|
+
page: fields.object().fields((f)=>({
|
|
3604
|
+
title: f.text().label("Title"),
|
|
3605
|
+
metaTitle: f.text().label("Meta Title")
|
|
3606
|
+
}))
|
|
3607
|
+
}),
|
|
3608
|
+
layout: (layout)=>[
|
|
3609
|
+
layout.object("page", (l)=>[
|
|
3610
|
+
l.tabs("pageTabs").tab("general", (tab)=>{
|
|
3611
|
+
tab.label("General").layout((l)=>[
|
|
3612
|
+
l.row("title")
|
|
3613
|
+
]);
|
|
3614
|
+
}).tab("seo", (tab)=>{
|
|
3615
|
+
tab.label("SEO").layout((l)=>[
|
|
3616
|
+
l.row("metaTitle")
|
|
3617
|
+
]);
|
|
3618
|
+
})
|
|
3619
|
+
])
|
|
3620
|
+
]
|
|
3621
|
+
});
|
|
3622
|
+
form.focusField("page.metaTitle");
|
|
3623
|
+
expect(form.field("page.metaTitle").vm.focusRequested).toBe(true);
|
|
3624
|
+
});
|
|
3625
|
+
it("should clear previous focus when focusing a new field", ()=>{
|
|
3626
|
+
const form = createForm({
|
|
3627
|
+
fields: (fields)=>({
|
|
3628
|
+
title: fields.text().label("Title"),
|
|
3629
|
+
path: fields.text().label("Path")
|
|
3630
|
+
})
|
|
3631
|
+
});
|
|
3632
|
+
form.focusField("title");
|
|
3633
|
+
expect(form.field("title").vm.focusRequested).toBe(true);
|
|
3634
|
+
form.focusField("path");
|
|
3635
|
+
expect(form.field("title").vm.focusRequested).toBe(false);
|
|
3636
|
+
expect(form.field("path").vm.focusRequested).toBe(true);
|
|
3637
|
+
});
|
|
3638
|
+
it("should allow renderer to clear focus via clearFocusRequest", ()=>{
|
|
3639
|
+
const form = createForm({
|
|
3640
|
+
fields: (fields)=>({
|
|
3641
|
+
title: fields.text().label("Title")
|
|
3642
|
+
})
|
|
3643
|
+
});
|
|
3644
|
+
form.focusField("title");
|
|
3645
|
+
expect(form.field("title").vm.focusRequested).toBe(true);
|
|
3646
|
+
form.field("title").vm.clearFocusRequest();
|
|
3647
|
+
expect(form.field("title").vm.focusRequested).toBe(false);
|
|
3648
|
+
});
|
|
3649
|
+
it("should not throw on unknown field", ()=>{
|
|
3650
|
+
const form = createForm({
|
|
3651
|
+
fields: (fields)=>({
|
|
3652
|
+
title: fields.text().label("Title")
|
|
3653
|
+
})
|
|
3654
|
+
});
|
|
3655
|
+
expect(()=>form.focusField("nonexistent")).not.toThrow();
|
|
3656
|
+
});
|
|
3657
|
+
it("should propagate qualifiedName through nested objects", ()=>{
|
|
3658
|
+
const form = createForm({
|
|
3659
|
+
fields: (fields)=>({
|
|
3660
|
+
page: fields.object().fields((f)=>({
|
|
3661
|
+
seo: f.object().fields((g)=>({
|
|
3662
|
+
metaTitle: g.text().label("Meta Title")
|
|
3663
|
+
}))
|
|
3664
|
+
}))
|
|
3665
|
+
})
|
|
3666
|
+
});
|
|
3667
|
+
const meta = form.field("page.seo.metaTitle");
|
|
3668
|
+
expect(meta.qualifiedName).toBe("page.seo.metaTitle");
|
|
3669
|
+
});
|
|
3670
|
+
it("field.focus() should delegate to form.focusField", ()=>{
|
|
3671
|
+
const form = createForm({
|
|
3672
|
+
fields: (fields)=>({
|
|
3673
|
+
title: fields.text().label("Title"),
|
|
3674
|
+
path: fields.text().label("Path")
|
|
3675
|
+
}),
|
|
3676
|
+
layout: (layout)=>[
|
|
3677
|
+
layout.tabs("tabs").tab("t1", (tab)=>{
|
|
3678
|
+
tab.label("T1").layout((l)=>[
|
|
3679
|
+
l.row("title")
|
|
3680
|
+
]);
|
|
3681
|
+
}).tab("t2", (tab)=>{
|
|
3682
|
+
tab.label("T2").layout((l)=>[
|
|
3683
|
+
l.row("path")
|
|
3684
|
+
]);
|
|
3685
|
+
})
|
|
3686
|
+
]
|
|
3687
|
+
});
|
|
3688
|
+
form.field("path").focus();
|
|
3689
|
+
expect(form.field("path").vm.focusRequested).toBe(true);
|
|
3690
|
+
expect(form.vm.layout[0].activeTabId).toBe("t2");
|
|
3691
|
+
});
|
|
3692
|
+
});
|
|
3693
|
+
describe("primitive list addItem / removeItem", ()=>{
|
|
3694
|
+
it("addItem appends to empty list", ()=>{
|
|
3695
|
+
const form = createForm({
|
|
3696
|
+
fields: (f)=>({
|
|
3697
|
+
tags: f.text().list()
|
|
3698
|
+
}),
|
|
3699
|
+
layout: (l)=>[
|
|
3700
|
+
l.row("tags")
|
|
3701
|
+
]
|
|
3702
|
+
});
|
|
3703
|
+
const vm = form.field("tags").vm;
|
|
3704
|
+
expect(vm.value).toEqual([]);
|
|
3705
|
+
vm.addItem("hello");
|
|
3706
|
+
expect(form.field("tags").vm.value).toEqual([
|
|
3707
|
+
"hello"
|
|
3708
|
+
]);
|
|
3709
|
+
});
|
|
3710
|
+
it("addItem appends with default null when no value given", ()=>{
|
|
3711
|
+
const form = createForm({
|
|
3712
|
+
fields: (f)=>({
|
|
3713
|
+
tags: f.text().list()
|
|
3714
|
+
}),
|
|
3715
|
+
layout: (l)=>[
|
|
3716
|
+
l.row("tags")
|
|
3717
|
+
]
|
|
3718
|
+
});
|
|
3719
|
+
form.field("tags").vm.addItem();
|
|
3720
|
+
expect(form.field("tags").vm.value).toEqual([
|
|
3721
|
+
null
|
|
3722
|
+
]);
|
|
3723
|
+
});
|
|
3724
|
+
it("addItem appends to existing values", ()=>{
|
|
3725
|
+
const form = createForm({
|
|
3726
|
+
fields: (f)=>({
|
|
3727
|
+
tags: f.text().list().defaultValue([
|
|
3728
|
+
"a",
|
|
3729
|
+
"b"
|
|
3730
|
+
])
|
|
3731
|
+
}),
|
|
3732
|
+
layout: (l)=>[
|
|
3733
|
+
l.row("tags")
|
|
3734
|
+
]
|
|
3735
|
+
});
|
|
3736
|
+
form.field("tags").vm.addItem("c");
|
|
3737
|
+
expect(form.field("tags").vm.value).toEqual([
|
|
3738
|
+
"a",
|
|
3739
|
+
"b",
|
|
3740
|
+
"c"
|
|
3741
|
+
]);
|
|
3742
|
+
});
|
|
3743
|
+
it("removeItem removes at index", ()=>{
|
|
3744
|
+
const form = createForm({
|
|
3745
|
+
fields: (f)=>({
|
|
3746
|
+
tags: f.text().list().defaultValue([
|
|
3747
|
+
"a",
|
|
3748
|
+
"b",
|
|
3749
|
+
"c"
|
|
3750
|
+
])
|
|
3751
|
+
}),
|
|
3752
|
+
layout: (l)=>[
|
|
3753
|
+
l.row("tags")
|
|
3754
|
+
]
|
|
3755
|
+
});
|
|
3756
|
+
form.field("tags").vm.removeItem(1);
|
|
3757
|
+
expect(form.field("tags").vm.value).toEqual([
|
|
3758
|
+
"a",
|
|
3759
|
+
"c"
|
|
3760
|
+
]);
|
|
3761
|
+
});
|
|
3762
|
+
it("removeItem from beginning preserves order", ()=>{
|
|
3763
|
+
const form = createForm({
|
|
3764
|
+
fields: (f)=>({
|
|
3765
|
+
tags: f.text().list().defaultValue([
|
|
3766
|
+
"a",
|
|
3767
|
+
"b",
|
|
3768
|
+
"c"
|
|
3769
|
+
])
|
|
3770
|
+
}),
|
|
3771
|
+
layout: (l)=>[
|
|
3772
|
+
l.row("tags")
|
|
3773
|
+
]
|
|
3774
|
+
});
|
|
3775
|
+
form.field("tags").vm.removeItem(0);
|
|
3776
|
+
expect(form.field("tags").vm.value).toEqual([
|
|
3777
|
+
"b",
|
|
3778
|
+
"c"
|
|
3779
|
+
]);
|
|
3780
|
+
});
|
|
3781
|
+
it("operations go through beforeChange pipeline", ()=>{
|
|
3782
|
+
const log = [];
|
|
3783
|
+
const form = createForm({
|
|
3784
|
+
fields: (f)=>({
|
|
3785
|
+
tags: f.text().list().defaultValue([
|
|
3786
|
+
"a"
|
|
3787
|
+
]).beforeChange((value, _form)=>{
|
|
3788
|
+
log.push(value);
|
|
3789
|
+
return value;
|
|
3790
|
+
})
|
|
3791
|
+
}),
|
|
3792
|
+
layout: (l)=>[
|
|
3793
|
+
l.row("tags")
|
|
3794
|
+
]
|
|
3795
|
+
});
|
|
3796
|
+
form.field("tags").vm.addItem("b");
|
|
3797
|
+
form.field("tags").vm.removeItem(0);
|
|
3798
|
+
expect(log).toEqual([
|
|
3799
|
+
[
|
|
3800
|
+
"a",
|
|
3801
|
+
"b"
|
|
3802
|
+
],
|
|
3803
|
+
[
|
|
3804
|
+
"b"
|
|
3805
|
+
]
|
|
3806
|
+
]);
|
|
3807
|
+
});
|
|
3808
|
+
});
|
|
3809
|
+
});
|
|
3810
|
+
|
|
3811
|
+
//# sourceMappingURL=FormModel.test.js.map
|