@owlmeans/web-panel 0.1.18-rc.9 → 0.1.18
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 +510 -143
- package/THIRD-PARTY-NOTICES.txt +24 -0
- package/agent-meta/manifest.json +163 -4
- package/agent-meta/skills/web-panel/SKILL.md +136 -83
- package/agent-meta/skills/web-panel/parts/consent.md +114 -0
- package/agent-meta/skills/web-panel/parts/editor.md +47 -0
- package/agent-meta/skills/web-panel/parts/forms.md +85 -0
- package/agent-meta/skills/web-panel/parts/navigation.md +203 -0
- package/agent-meta/skills/web-panel/parts/picker.md +164 -0
- package/agent-meta/skills/web-panel/parts/reload.md +46 -0
- package/agent-meta/skills/web-panel/parts/sign-in.md +65 -0
- package/agent-meta/skills/web-panel/parts/theme.md +74 -0
- package/build/@/components/ui/alert-dialog.d.ts +15 -0
- package/build/@/components/ui/alert-dialog.d.ts.map +1 -0
- package/build/@/components/ui/alert-dialog.js +39 -0
- package/build/@/components/ui/alert-dialog.js.map +1 -0
- package/build/@/components/ui/alert.js +1 -1
- package/build/@/components/ui/alert.js.map +1 -1
- package/build/@/components/ui/button.js +1 -1
- package/build/@/components/ui/button.js.map +1 -1
- package/build/@/components/ui/card.js +1 -1
- package/build/@/components/ui/card.js.map +1 -1
- package/build/@/components/ui/input.js +1 -1
- package/build/@/components/ui/input.js.map +1 -1
- package/build/@/components/ui/label.js +1 -1
- package/build/@/components/ui/label.js.map +1 -1
- package/build/@/components/ui/navigation-menu.js +1 -1
- package/build/@/components/ui/navigation-menu.js.map +1 -1
- package/build/@/components/ui/popover.d.ts +8 -0
- package/build/@/components/ui/popover.d.ts.map +1 -0
- package/build/@/components/ui/popover.js +17 -0
- package/build/@/components/ui/popover.js.map +1 -0
- package/build/@/components/ui/progress.js +1 -1
- package/build/@/components/ui/progress.js.map +1 -1
- package/build/@/components/ui/separator.js +1 -1
- package/build/@/components/ui/separator.js.map +1 -1
- package/build/@/components/ui/sheet.d.ts +15 -0
- package/build/@/components/ui/sheet.d.ts.map +1 -0
- package/build/@/components/ui/sheet.js +40 -0
- package/build/@/components/ui/sheet.js.map +1 -0
- package/build/auth/context.d.ts.map +1 -1
- package/build/auth/context.js +0 -1
- package/build/auth/context.js.map +1 -1
- package/build/auth/entrypoints.d.ts +10 -0
- package/build/auth/entrypoints.d.ts.map +1 -0
- package/build/auth/entrypoints.js +4 -0
- package/build/auth/entrypoints.js.map +1 -0
- package/build/auth/exports.d.ts +4 -4
- package/build/auth/exports.d.ts.map +1 -1
- package/build/auth/exports.js +3 -3
- package/build/auth/exports.js.map +1 -1
- package/build/auth/modules.d.ts +1 -1
- package/build/auth/modules.d.ts.map +1 -1
- package/build/auth/plugins/basic-ed25519.js +4 -4
- package/build/auth/plugins/basic-ed25519.js.map +1 -1
- package/build/auth/plugins/re-captcha.js +3 -3
- package/build/auth/plugins/re-captcha.js.map +1 -1
- package/build/auth/plugins/tunnel-consumer.js +1 -1
- package/build/auth/plugins/tunnel-consumer.js.map +1 -1
- package/build/components/block.js +2 -2
- package/build/components/block.js.map +1 -1
- package/build/components/consts.local.d.ts +14 -0
- package/build/components/consts.local.d.ts.map +1 -0
- package/build/components/consts.local.js +26 -0
- package/build/components/consts.local.js.map +1 -0
- package/build/components/footer/component.d.ts +19 -1
- package/build/components/footer/component.d.ts.map +1 -1
- package/build/components/footer/component.js +38 -11
- package/build/components/footer/component.js.map +1 -1
- package/build/components/footer/credit.d.ts +27 -0
- package/build/components/footer/credit.d.ts.map +1 -0
- package/build/components/footer/credit.js +42 -0
- package/build/components/footer/credit.js.map +1 -0
- package/build/components/footer/index.d.ts +1 -0
- package/build/components/footer/index.d.ts.map +1 -1
- package/build/components/footer/index.js +1 -0
- package/build/components/footer/index.js.map +1 -1
- package/build/components/footer/types.d.ts +22 -1
- package/build/components/footer/types.d.ts.map +1 -1
- package/build/components/form/button/component.d.ts.map +1 -1
- package/build/components/form/button/component.js +5 -3
- package/build/components/form/button/component.js.map +1 -1
- package/build/components/form/component.js +2 -2
- package/build/components/form/component.js.map +1 -1
- package/build/components/form/text/component.js +2 -2
- package/build/components/form/text/component.js.map +1 -1
- package/build/components/helper.d.ts.map +1 -1
- package/build/components/helper.js +1 -12
- package/build/components/helper.js.map +1 -1
- package/build/components/index.d.ts +4 -0
- package/build/components/index.d.ts.map +1 -1
- package/build/components/index.js +4 -0
- package/build/components/index.js.map +1 -1
- package/build/components/link.js +3 -3
- package/build/components/link.js.map +1 -1
- package/build/components/login/append.d.ts +15 -0
- package/build/components/login/append.d.ts.map +1 -0
- package/build/components/login/append.js +26 -0
- package/build/components/login/append.js.map +1 -0
- package/build/components/login/consts.d.ts +3 -0
- package/build/components/login/consts.d.ts.map +1 -0
- package/build/components/login/consts.js +3 -0
- package/build/components/login/consts.js.map +1 -0
- package/build/components/login/consts.local.d.ts +4 -0
- package/build/components/login/consts.local.d.ts.map +1 -0
- package/build/components/login/consts.local.js +8 -0
- package/build/components/login/consts.local.js.map +1 -0
- package/build/components/login/credit.d.ts +12 -0
- package/build/components/login/credit.d.ts.map +1 -0
- package/build/components/login/credit.js +18 -0
- package/build/components/login/credit.js.map +1 -0
- package/build/components/login/i18n/be.json +10 -0
- package/build/components/login/i18n/de.json +10 -0
- package/build/components/login/i18n/en.json +10 -0
- package/build/components/login/i18n/es.json +10 -0
- package/build/components/login/i18n/fr.json +10 -0
- package/build/components/login/i18n/pl.json +10 -0
- package/build/components/login/i18n/ru.json +10 -0
- package/build/components/login/i18n/uk.json +10 -0
- package/build/components/login/i18n.d.ts +2 -0
- package/build/components/login/i18n.d.ts.map +1 -0
- package/build/components/login/i18n.js +28 -0
- package/build/components/login/i18n.js.map +1 -0
- package/build/components/login/icons.d.ts +14 -0
- package/build/components/login/icons.d.ts.map +1 -0
- package/build/components/login/icons.js +29 -0
- package/build/components/login/icons.js.map +1 -0
- package/build/components/login/index.d.ts +11 -0
- package/build/components/login/index.d.ts.map +1 -0
- package/build/components/login/index.js +10 -0
- package/build/components/login/index.js.map +1 -0
- package/build/components/login/notify.d.ts +18 -0
- package/build/components/login/notify.d.ts.map +1 -0
- package/build/components/login/notify.js +41 -0
- package/build/components/login/notify.js.map +1 -0
- package/build/components/login/provider.d.ts +20 -0
- package/build/components/login/provider.d.ts.map +1 -0
- package/build/components/login/provider.js +31 -0
- package/build/components/login/provider.js.map +1 -0
- package/build/components/login/screen.d.ts +24 -0
- package/build/components/login/screen.d.ts.map +1 -0
- package/build/components/login/screen.js +92 -0
- package/build/components/login/screen.js.map +1 -0
- package/build/components/login/terms.d.ts +19 -0
- package/build/components/login/terms.d.ts.map +1 -0
- package/build/components/login/terms.js +32 -0
- package/build/components/login/terms.js.map +1 -0
- package/build/components/login/types.d.ts +28 -0
- package/build/components/login/types.d.ts.map +1 -0
- package/build/components/login/types.js +2 -0
- package/build/components/login/types.js.map +1 -0
- package/build/components/nav/consts.local.d.ts +41 -0
- package/build/components/nav/consts.local.d.ts.map +1 -0
- package/build/components/nav/consts.local.js +60 -0
- package/build/components/nav/consts.local.js.map +1 -0
- package/build/components/nav/index.d.ts +1 -0
- package/build/components/nav/index.d.ts.map +1 -1
- package/build/components/nav/index.js +1 -0
- package/build/components/nav/index.js.map +1 -1
- package/build/components/nav/layout.d.ts +9 -2
- package/build/components/nav/layout.d.ts.map +1 -1
- package/build/components/nav/layout.js +48 -7
- package/build/components/nav/layout.js.map +1 -1
- package/build/components/nav/mobile.d.ts +21 -0
- package/build/components/nav/mobile.d.ts.map +1 -0
- package/build/components/nav/mobile.js +55 -0
- package/build/components/nav/mobile.js.map +1 -0
- package/build/components/nav/side.d.ts.map +1 -1
- package/build/components/nav/side.js +10 -10
- package/build/components/nav/side.js.map +1 -1
- package/build/components/nav/top.d.ts.map +1 -1
- package/build/components/nav/top.js +12 -8
- package/build/components/nav/top.js.map +1 -1
- package/build/components/nav/types.d.ts +64 -1
- package/build/components/nav/types.d.ts.map +1 -1
- package/build/components/nav/viewport.d.ts +3 -0
- package/build/components/nav/viewport.d.ts.map +1 -0
- package/build/components/nav/viewport.js +14 -0
- package/build/components/nav/viewport.js.map +1 -0
- package/build/components/panel-app/component.d.ts.map +1 -1
- package/build/components/panel-app/component.js +4 -3
- package/build/components/panel-app/component.js.map +1 -1
- package/build/components/picker/color-picker.d.ts +9 -0
- package/build/components/picker/color-picker.d.ts.map +1 -0
- package/build/components/picker/color-picker.js +9 -0
- package/build/components/picker/color-picker.js.map +1 -0
- package/build/components/picker/consts.local.d.ts +76 -0
- package/build/components/picker/consts.local.d.ts.map +1 -0
- package/build/components/picker/consts.local.js +214 -0
- package/build/components/picker/consts.local.js.map +1 -0
- package/build/components/picker/dot.d.ts +8 -0
- package/build/components/picker/dot.d.ts.map +1 -0
- package/build/components/picker/dot.js +10 -0
- package/build/components/picker/dot.js.map +1 -0
- package/build/components/picker/grid.d.ts +17 -0
- package/build/components/picker/grid.d.ts.map +1 -0
- package/build/components/picker/grid.js +73 -0
- package/build/components/picker/grid.js.map +1 -0
- package/build/components/picker/i18n/be.json +60 -0
- package/build/components/picker/i18n/de.json +60 -0
- package/build/components/picker/i18n/en.json +60 -0
- package/build/components/picker/i18n/es.json +60 -0
- package/build/components/picker/i18n/fr.json +60 -0
- package/build/components/picker/i18n/pl.json +60 -0
- package/build/components/picker/i18n/ru.json +60 -0
- package/build/components/picker/i18n/uk.json +60 -0
- package/build/components/picker/i18n.d.ts +2 -0
- package/build/components/picker/i18n.d.ts.map +1 -0
- package/build/components/picker/i18n.js +18 -0
- package/build/components/picker/i18n.js.map +1 -0
- package/build/components/picker/icon-color-picker.d.ts +14 -0
- package/build/components/picker/icon-color-picker.d.ts.map +1 -0
- package/build/components/picker/icon-color-picker.js +15 -0
- package/build/components/picker/icon-color-picker.js.map +1 -0
- package/build/components/picker/icon-picker.d.ts +8 -0
- package/build/components/picker/icon-picker.d.ts.map +1 -0
- package/build/components/picker/icon-picker.js +8 -0
- package/build/components/picker/icon-picker.js.map +1 -0
- package/build/components/picker/index.d.ts +11 -0
- package/build/components/picker/index.d.ts.map +1 -0
- package/build/components/picker/index.js +10 -0
- package/build/components/picker/index.js.map +1 -0
- package/build/components/picker/panel.d.ts +9 -0
- package/build/components/picker/panel.d.ts.map +1 -0
- package/build/components/picker/panel.js +23 -0
- package/build/components/picker/panel.js.map +1 -0
- package/build/components/picker/root.d.ts +13 -0
- package/build/components/picker/root.d.ts.map +1 -0
- package/build/components/picker/root.js +55 -0
- package/build/components/picker/root.js.map +1 -0
- package/build/components/picker/roving.d.ts +8 -0
- package/build/components/picker/roving.d.ts.map +1 -0
- package/build/components/picker/roving.js +62 -0
- package/build/components/picker/roving.js.map +1 -0
- package/build/components/picker/surface.d.ts +18 -0
- package/build/components/picker/surface.d.ts.map +1 -0
- package/build/components/picker/surface.js +36 -0
- package/build/components/picker/surface.js.map +1 -0
- package/build/components/picker/swatches/custom.d.ts +10 -0
- package/build/components/picker/swatches/custom.d.ts.map +1 -0
- package/build/components/picker/swatches/custom.js +65 -0
- package/build/components/picker/swatches/custom.js.map +1 -0
- package/build/components/picker/swatches.d.ts +16 -0
- package/build/components/picker/swatches.d.ts.map +1 -0
- package/build/components/picker/swatches.js +35 -0
- package/build/components/picker/swatches.js.map +1 -0
- package/build/components/picker/text.d.ts +5 -0
- package/build/components/picker/text.d.ts.map +1 -0
- package/build/components/picker/text.js +29 -0
- package/build/components/picker/text.js.map +1 -0
- package/build/components/picker/tile.d.ts +11 -0
- package/build/components/picker/tile.d.ts.map +1 -0
- package/build/components/picker/tile.js +19 -0
- package/build/components/picker/tile.js.map +1 -0
- package/build/components/picker/translate.d.ts +9 -0
- package/build/components/picker/translate.d.ts.map +1 -0
- package/build/components/picker/translate.js +13 -0
- package/build/components/picker/translate.js.map +1 -0
- package/build/components/picker/trigger.d.ts +12 -0
- package/build/components/picker/trigger.d.ts.map +1 -0
- package/build/components/picker/trigger.js +29 -0
- package/build/components/picker/trigger.js.map +1 -0
- package/build/components/picker/types.d.ts +114 -0
- package/build/components/picker/types.d.ts.map +1 -0
- package/build/components/picker/types.js +2 -0
- package/build/components/picker/types.js.map +1 -0
- package/build/components/picker/types.local.d.ts +143 -0
- package/build/components/picker/types.local.d.ts.map +1 -0
- package/build/components/picker/types.local.js +2 -0
- package/build/components/picker/types.local.js.map +1 -0
- package/build/components/scheme/consts.local.d.ts +8 -0
- package/build/components/scheme/consts.local.d.ts.map +1 -0
- package/build/components/scheme/consts.local.js +13 -0
- package/build/components/scheme/consts.local.js.map +1 -0
- package/build/components/scheme/hook.d.ts +16 -0
- package/build/components/scheme/hook.d.ts.map +1 -0
- package/build/components/scheme/hook.js +52 -0
- package/build/components/scheme/hook.js.map +1 -0
- package/build/components/scheme/index.d.ts +4 -0
- package/build/components/scheme/index.d.ts.map +1 -0
- package/build/components/scheme/index.js +3 -0
- package/build/components/scheme/index.js.map +1 -0
- package/build/components/scheme/toggle.d.ts +14 -0
- package/build/components/scheme/toggle.d.ts.map +1 -0
- package/build/components/scheme/toggle.js +24 -0
- package/build/components/scheme/toggle.js.map +1 -0
- package/build/components/scheme/types.d.ts +21 -0
- package/build/components/scheme/types.d.ts.map +1 -0
- package/build/components/scheme/types.js +2 -0
- package/build/components/scheme/types.js.map +1 -0
- package/build/components/socket/i18n/be.json +9 -0
- package/build/components/socket/i18n/de.json +9 -0
- package/build/components/socket/i18n/en.json +9 -0
- package/build/components/socket/i18n/es.json +9 -0
- package/build/components/socket/i18n/fr.json +9 -0
- package/build/components/socket/i18n/pl.json +9 -0
- package/build/components/socket/i18n/ru.json +9 -0
- package/build/components/socket/i18n/uk.json +9 -0
- package/build/components/socket/i18n.d.ts +2 -0
- package/build/components/socket/i18n.d.ts.map +1 -0
- package/build/components/socket/i18n.js +18 -0
- package/build/components/socket/i18n.js.map +1 -0
- package/build/components/socket/index.d.ts +3 -0
- package/build/components/socket/index.d.ts.map +1 -0
- package/build/components/socket/index.js +3 -0
- package/build/components/socket/index.js.map +1 -0
- package/build/components/socket/reload-dialog.d.ts +22 -0
- package/build/components/socket/reload-dialog.d.ts.map +1 -0
- package/build/components/socket/reload-dialog.js +34 -0
- package/build/components/socket/reload-dialog.js.map +1 -0
- package/build/components/status.js +1 -1
- package/build/components/status.js.map +1 -1
- package/build/components/text.d.ts.map +1 -1
- package/build/components/text.js +2 -13
- package/build/components/text.js.map +1 -1
- package/build/components/toaster.d.ts +16 -0
- package/build/components/toaster.d.ts.map +1 -0
- package/build/components/toaster.js +52 -0
- package/build/components/toaster.js.map +1 -0
- package/build/components/uploader/consts.local.d.ts +4 -0
- package/build/components/uploader/consts.local.d.ts.map +1 -0
- package/build/components/uploader/consts.local.js +6 -0
- package/build/components/uploader/consts.local.js.map +1 -0
- package/build/components/uploader/image.d.ts.map +1 -1
- package/build/components/uploader/image.js +2 -6
- package/build/components/uploader/image.js.map +1 -1
- package/build/consent/component.d.ts +27 -0
- package/build/consent/component.d.ts.map +1 -0
- package/build/consent/component.js +60 -0
- package/build/consent/component.js.map +1 -0
- package/build/consent/consts.d.ts +2 -0
- package/build/consent/consts.d.ts.map +1 -0
- package/build/consent/consts.js +2 -0
- package/build/consent/consts.js.map +1 -0
- package/build/consent/consts.local.d.ts +9 -0
- package/build/consent/consts.local.d.ts.map +1 -0
- package/build/consent/consts.local.js +9 -0
- package/build/consent/consts.local.js.map +1 -0
- package/build/consent/geo.d.ts +17 -0
- package/build/consent/geo.d.ts.map +1 -0
- package/build/consent/geo.js +19 -0
- package/build/consent/geo.js.map +1 -0
- package/build/consent/index.d.ts +11 -0
- package/build/consent/index.d.ts.map +1 -0
- package/build/consent/index.js +9 -0
- package/build/consent/index.js.map +1 -0
- package/build/consent/language.d.ts +12 -0
- package/build/consent/language.d.ts.map +1 -0
- package/build/consent/language.js +11 -0
- package/build/consent/language.js.map +1 -0
- package/build/consent/menu-widget.d.ts +13 -0
- package/build/consent/menu-widget.d.ts.map +1 -0
- package/build/consent/menu-widget.js +20 -0
- package/build/consent/menu-widget.js.map +1 -0
- package/build/consent/presence.d.ts +30 -0
- package/build/consent/presence.d.ts.map +1 -0
- package/build/consent/presence.js +40 -0
- package/build/consent/presence.js.map +1 -0
- package/build/consent/service.d.ts +18 -0
- package/build/consent/service.d.ts.map +1 -0
- package/build/consent/service.js +53 -0
- package/build/consent/service.js.map +1 -0
- package/build/consent/translate.d.ts +14 -0
- package/build/consent/translate.d.ts.map +1 -0
- package/build/consent/translate.js +28 -0
- package/build/consent/translate.js.map +1 -0
- package/build/consent/types.d.ts +39 -0
- package/build/consent/types.d.ts.map +1 -0
- package/build/consent/types.js +2 -0
- package/build/consent/types.js.map +1 -0
- package/build/consent/types.local.d.ts +6 -0
- package/build/consent/types.local.d.ts.map +1 -0
- package/build/consent/types.local.js +2 -0
- package/build/consent/types.local.js.map +1 -0
- package/build/context.d.ts.map +1 -1
- package/build/context.js +10 -0
- package/build/context.js.map +1 -1
- package/build/editor/component.d.ts +4 -0
- package/build/editor/component.d.ts.map +1 -0
- package/build/editor/component.js +103 -0
- package/build/editor/component.js.map +1 -0
- package/build/editor/consts.local.d.ts +6 -0
- package/build/editor/consts.local.d.ts.map +1 -0
- package/build/editor/consts.local.js +58 -0
- package/build/editor/consts.local.js.map +1 -0
- package/build/editor/index.d.ts +3 -0
- package/build/editor/index.d.ts.map +1 -0
- package/build/editor/index.js +2 -0
- package/build/editor/index.js.map +1 -0
- package/build/editor/types.d.ts +24 -0
- package/build/editor/types.d.ts.map +1 -0
- package/build/editor/types.js +2 -0
- package/build/editor/types.js.map +1 -0
- package/build/entrypoints.d.ts +8 -0
- package/build/entrypoints.d.ts.map +1 -0
- package/build/entrypoints.js +4 -0
- package/build/entrypoints.js.map +1 -0
- package/build/exports.d.ts +6 -5
- package/build/exports.d.ts.map +1 -1
- package/build/exports.js +5 -5
- package/build/exports.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/main.d.ts +1 -4
- package/build/main.d.ts.map +1 -1
- package/build/main.js.map +1 -1
- package/build/modules.d.ts +1 -1
- package/build/modules.d.ts.map +1 -1
- package/build/permission/i18n/be.json +13 -0
- package/build/permission/i18n/de.json +13 -0
- package/build/permission/i18n/en.json +13 -0
- package/build/permission/i18n/es.json +13 -0
- package/build/permission/i18n/fr.json +13 -0
- package/build/permission/i18n/pl.json +13 -0
- package/build/permission/i18n/ru.json +13 -0
- package/build/permission/i18n/uk.json +13 -0
- package/build/permission/i18n.d.ts +2 -0
- package/build/permission/i18n.d.ts.map +1 -0
- package/build/permission/i18n.js +13 -0
- package/build/permission/i18n.js.map +1 -0
- package/build/permission/index.d.ts +6 -0
- package/build/permission/index.d.ts.map +1 -0
- package/build/permission/index.js +27 -0
- package/build/permission/index.js.map +1 -0
- package/build/permission/toast.d.ts +5 -0
- package/build/permission/toast.d.ts.map +1 -0
- package/build/permission/toast.js +8 -0
- package/build/permission/toast.js.map +1 -0
- package/build/permission/types.d.ts +6 -0
- package/build/permission/types.d.ts.map +1 -0
- package/build/permission/types.js +2 -0
- package/build/permission/types.js.map +1 -0
- package/build/picker/icons/consts.d.ts +9 -0
- package/build/picker/icons/consts.d.ts.map +1 -0
- package/build/picker/icons/consts.js +264 -0
- package/build/picker/icons/consts.js.map +1 -0
- package/build/picker/icons/types.d.ts +18 -0
- package/build/picker/icons/types.d.ts.map +1 -0
- package/build/picker/icons/types.js +2 -0
- package/build/picker/icons/types.js.map +1 -0
- package/build/picker/icons.d.ts +4 -0
- package/build/picker/icons.d.ts.map +1 -0
- package/build/picker/icons.js +54 -0
- package/build/picker/icons.js.map +1 -0
- package/build/picker/index.d.ts +11 -0
- package/build/picker/index.d.ts.map +1 -0
- package/build/picker/index.js +10 -0
- package/build/picker/index.js.map +1 -0
- package/build/picker/utils/color/types.d.ts +35 -0
- package/build/picker/utils/color/types.d.ts.map +1 -0
- package/build/picker/utils/color/types.js +2 -0
- package/build/picker/utils/color/types.js.map +1 -0
- package/build/picker/utils/color.d.ts +4 -0
- package/build/picker/utils/color.d.ts.map +1 -0
- package/build/picker/utils/color.js +96 -0
- package/build/picker/utils/color.js.map +1 -0
- package/build/picker/vocabulary/consts.d.ts +26 -0
- package/build/picker/vocabulary/consts.d.ts.map +1 -0
- package/build/picker/vocabulary/consts.js +336 -0
- package/build/picker/vocabulary/consts.js.map +1 -0
- package/build/picker/vocabulary/types.d.ts +67 -0
- package/build/picker/vocabulary/types.d.ts.map +1 -0
- package/build/picker/vocabulary/types.js +2 -0
- package/build/picker/vocabulary/types.js.map +1 -0
- package/build/picker/vocabulary.d.ts +11 -0
- package/build/picker/vocabulary.d.ts.map +1 -0
- package/build/picker/vocabulary.js +83 -0
- package/build/picker/vocabulary.js.map +1 -0
- package/build/scheme/consts.d.ts +8 -0
- package/build/scheme/consts.d.ts.map +1 -0
- package/build/scheme/consts.js +8 -0
- package/build/scheme/consts.js.map +1 -0
- package/build/scheme/index.d.ts +4 -0
- package/build/scheme/index.d.ts.map +1 -0
- package/build/scheme/index.js +3 -0
- package/build/scheme/index.js.map +1 -0
- package/build/scheme/scheme.d.ts +16 -0
- package/build/scheme/scheme.d.ts.map +1 -0
- package/build/scheme/scheme.js +58 -0
- package/build/scheme/scheme.js.map +1 -0
- package/build/scheme/types.d.ts +30 -0
- package/build/scheme/types.d.ts.map +1 -0
- package/build/scheme/types.js +2 -0
- package/build/scheme/types.js.map +1 -0
- package/build/types.d.ts +18 -2
- package/build/types.d.ts.map +1 -1
- package/package.json +107 -52
- package/src/@/components/ui/alert-dialog.tsx +156 -0
- package/src/@/components/ui/alert.tsx +1 -1
- package/src/@/components/ui/button.tsx +1 -1
- package/src/@/components/ui/card.tsx +1 -1
- package/src/@/components/ui/input.tsx +1 -1
- package/src/@/components/ui/label.tsx +1 -1
- package/src/@/components/ui/navigation-menu.tsx +1 -1
- package/src/@/components/ui/popover.tsx +47 -0
- package/src/@/components/ui/progress.tsx +1 -1
- package/src/@/components/ui/separator.tsx +1 -1
- package/src/@/components/ui/sheet.tsx +142 -0
- package/src/auth/context.ts +0 -2
- package/src/auth/entrypoints.ts +5 -0
- package/src/auth/exports.ts +4 -4
- package/src/auth/plugins/basic-ed25519.tsx +4 -4
- package/src/auth/plugins/re-captcha.tsx +3 -3
- package/src/auth/plugins/tunnel-consumer.tsx +1 -1
- package/src/components/block.tsx +2 -2
- package/src/components/consts.local.ts +29 -0
- package/src/components/footer/component.tsx +58 -16
- package/src/components/footer/credit.tsx +61 -0
- package/src/components/footer/index.ts +1 -0
- package/src/components/footer/types.ts +22 -1
- package/src/components/form/button/component.tsx +7 -4
- package/src/components/form/component.tsx +2 -2
- package/src/components/form/text/component.tsx +2 -2
- package/src/components/helper.ts +1 -13
- package/src/components/index.ts +4 -0
- package/src/components/link.tsx +3 -3
- package/src/components/login/append.ts +34 -0
- package/src/components/login/consts.local.ts +10 -0
- package/src/components/login/consts.ts +2 -0
- package/src/components/login/credit.tsx +33 -0
- package/src/components/login/i18n/be.json +10 -0
- package/src/components/login/i18n/de.json +10 -0
- package/src/components/login/i18n/en.json +10 -0
- package/src/components/login/i18n/es.json +10 -0
- package/src/components/login/i18n/fr.json +10 -0
- package/src/components/login/i18n/pl.json +10 -0
- package/src/components/login/i18n/ru.json +10 -0
- package/src/components/login/i18n/uk.json +10 -0
- package/src/components/login/i18n.ts +29 -0
- package/src/components/login/icons.tsx +39 -0
- package/src/components/login/index.ts +11 -0
- package/src/components/login/notify.ts +48 -0
- package/src/components/login/provider.tsx +57 -0
- package/src/components/login/screen.tsx +178 -0
- package/src/components/login/terms.tsx +75 -0
- package/src/components/login/types.ts +30 -0
- package/src/components/nav/consts.local.ts +64 -0
- package/src/components/nav/index.ts +1 -0
- package/src/components/nav/layout.tsx +184 -26
- package/src/components/nav/mobile.tsx +165 -0
- package/src/components/nav/side.tsx +43 -27
- package/src/components/nav/top.tsx +44 -22
- package/src/components/nav/types.ts +65 -2
- package/src/components/nav/viewport.ts +16 -0
- package/src/components/panel-app/component.tsx +9 -1
- package/src/components/picker/color-picker.tsx +15 -0
- package/src/components/picker/consts.local.ts +267 -0
- package/src/components/picker/dot.tsx +21 -0
- package/src/components/picker/grid.tsx +138 -0
- package/src/components/picker/i18n/be.json +60 -0
- package/src/components/picker/i18n/de.json +60 -0
- package/src/components/picker/i18n/en.json +60 -0
- package/src/components/picker/i18n/es.json +60 -0
- package/src/components/picker/i18n/fr.json +60 -0
- package/src/components/picker/i18n/pl.json +60 -0
- package/src/components/picker/i18n/ru.json +60 -0
- package/src/components/picker/i18n/uk.json +60 -0
- package/src/components/picker/i18n.ts +19 -0
- package/src/components/picker/icon-color-picker.tsx +25 -0
- package/src/components/picker/icon-picker.tsx +13 -0
- package/src/components/picker/index.ts +11 -0
- package/src/components/picker/panel.tsx +61 -0
- package/src/components/picker/root.tsx +81 -0
- package/src/components/picker/roving.ts +70 -0
- package/src/components/picker/surface.tsx +62 -0
- package/src/components/picker/swatches/custom.tsx +110 -0
- package/src/components/picker/swatches.tsx +67 -0
- package/src/components/picker/text.ts +46 -0
- package/src/components/picker/tile.tsx +35 -0
- package/src/components/picker/translate.ts +15 -0
- package/src/components/picker/trigger.tsx +54 -0
- package/src/components/picker/types.local.ts +154 -0
- package/src/components/picker/types.ts +125 -0
- package/src/components/scheme/consts.local.ts +13 -0
- package/src/components/scheme/hook.ts +62 -0
- package/src/components/scheme/index.ts +4 -0
- package/src/components/scheme/toggle.tsx +32 -0
- package/src/components/scheme/types.ts +23 -0
- package/src/components/socket/i18n/be.json +9 -0
- package/src/components/socket/i18n/de.json +9 -0
- package/src/components/socket/i18n/en.json +9 -0
- package/src/components/socket/i18n/es.json +9 -0
- package/src/components/socket/i18n/fr.json +9 -0
- package/src/components/socket/i18n/pl.json +9 -0
- package/src/components/socket/i18n/ru.json +9 -0
- package/src/components/socket/i18n/uk.json +9 -0
- package/src/components/socket/i18n.ts +19 -0
- package/src/components/socket/index.ts +2 -0
- package/src/components/socket/reload-dialog.tsx +61 -0
- package/src/components/status.tsx +1 -1
- package/src/components/text.tsx +2 -14
- package/src/components/toaster.tsx +65 -0
- package/src/components/uploader/consts.local.ts +7 -0
- package/src/components/uploader/image.tsx +2 -6
- package/src/consent/component.tsx +84 -0
- package/src/consent/consts.local.ts +10 -0
- package/src/consent/consts.ts +1 -0
- package/src/consent/geo.ts +26 -0
- package/src/consent/index.ts +24 -0
- package/src/consent/language.ts +12 -0
- package/src/consent/menu-widget.tsx +22 -0
- package/src/consent/presence.ts +45 -0
- package/src/consent/service.ts +75 -0
- package/src/consent/translate.ts +31 -0
- package/src/consent/types.local.ts +6 -0
- package/src/consent/types.ts +43 -0
- package/src/context.ts +12 -0
- package/src/editor/component.tsx +172 -0
- package/src/editor/consts.local.ts +62 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/types.ts +24 -0
- package/src/entrypoints.ts +5 -0
- package/src/exports.ts +6 -5
- package/src/index.ts +1 -1
- package/src/main.tsx +2 -6
- package/src/permission/i18n/be.json +13 -0
- package/src/permission/i18n/de.json +13 -0
- package/src/permission/i18n/en.json +13 -0
- package/src/permission/i18n/es.json +13 -0
- package/src/permission/i18n/fr.json +13 -0
- package/src/permission/i18n/pl.json +13 -0
- package/src/permission/i18n/ru.json +13 -0
- package/src/permission/i18n/uk.json +13 -0
- package/src/permission/i18n.ts +13 -0
- package/src/permission/index.ts +27 -0
- package/src/permission/toast.tsx +20 -0
- package/src/permission/types.ts +5 -0
- package/src/picker/icons/consts.ts +309 -0
- package/src/picker/icons/types.ts +18 -0
- package/src/picker/icons.ts +70 -0
- package/src/picker/index.ts +11 -0
- package/src/picker/utils/color/types.ts +37 -0
- package/src/picker/utils/color.ts +119 -0
- package/src/picker/vocabulary/consts.ts +347 -0
- package/src/picker/vocabulary/types.ts +72 -0
- package/src/picker/vocabulary.ts +111 -0
- package/src/scheme/consts.ts +8 -0
- package/src/scheme/index.ts +3 -0
- package/src/scheme/scheme.ts +66 -0
- package/src/scheme/types.ts +30 -0
- package/src/types.ts +19 -2
- package/tests/consent-language.spec.ts +31 -0
- package/tests/consent.spec.ts +112 -0
- package/tests/context.ts +4 -0
- package/tests/editor.spec.ts +58 -0
- package/tests/harness/editor.tsx +32 -0
- package/tests/harness/index.html +1 -0
- package/tests/harness/lazy-screen.tsx +9 -0
- package/tests/harness/mount.tsx +370 -29
- package/tests/harness/picker.tsx +110 -0
- package/tests/lazy.spec.ts +104 -0
- package/tests/login-provider.spec.ts +114 -0
- package/tests/login.spec.ts +447 -0
- package/tests/nav.spec.ts +449 -3
- package/tests/package-boundary.spec.ts +19 -0
- package/tests/picker-shots.spec.ts +174 -0
- package/tests/picker-vocabulary.spec.ts +217 -0
- package/tests/picker.spec.ts +333 -0
- package/tests/scheme.spec.ts +149 -0
- package/tests/smoke.spec.ts +2 -2
- package/tests/socket-dialog.spec.ts +154 -0
- package/tests/toaster.spec.ts +59 -0
- package/zz-tw.d.mts +2 -0
- package/zz-tw.d.mts.map +1 -0
- package/build/.gitkeep +0 -0
- package/src/auth/modules.ts +0 -5
- package/src/modules.ts +0 -5
package/tests/nav.spec.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { afterAll, describe, expect, test } from 'bun:test'
|
|
2
|
-
import {
|
|
2
|
+
import { mountComponent, browserHelper } from '@owlmeans/test-ui'
|
|
3
3
|
import { HARNESS_URL } from './context.js'
|
|
4
4
|
|
|
5
5
|
// Browser work does not fit the 5s default: a cold harness compiles the app on first request.
|
|
6
6
|
const TIMEOUT = 30_000
|
|
7
7
|
|
|
8
8
|
afterAll(async () => {
|
|
9
|
-
await closeBrowser()
|
|
9
|
+
await browserHelper.closeBrowser()
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
const open = async (path: string) => mountComponent({ url: `${HARNESS_URL.replace(/\/$/, '')}${path}` })
|
|
13
13
|
|
|
14
14
|
describe('@owlmeans/web-panel — two-layer navigation', () => {
|
|
15
|
+
test('a sibling detail route keeps the section selected through its pathname', async () => {
|
|
16
|
+
const { page, close } = await open('/reports')
|
|
17
|
+
try {
|
|
18
|
+
await page.locator('#to-sibling').click()
|
|
19
|
+
await page.waitForSelector('#sibling-detail')
|
|
20
|
+
expect(await page.locator('[data-slot="navigation-menu-link"][data-active]').textContent()).toBe('Work')
|
|
21
|
+
} finally { await close() }
|
|
22
|
+
}, TIMEOUT)
|
|
15
23
|
test('a deep link resolves the active section with no history state', async () => {
|
|
16
24
|
// The hard-load case: `location.state` is null here, so the section can only come from
|
|
17
25
|
// the pathname. A menu that highlights nothing on a deep link is the bug this pins.
|
|
@@ -84,7 +92,7 @@ describe('@owlmeans/web-panel — two-layer navigation', () => {
|
|
|
84
92
|
const { page, close } = await open('/dash')
|
|
85
93
|
try {
|
|
86
94
|
await page.waitForSelector('#dash')
|
|
87
|
-
const links = page.locator('footer a')
|
|
95
|
+
const links = page.locator('footer a:not([data-login-powered])')
|
|
88
96
|
expect(await links.count()).toBe(2)
|
|
89
97
|
expect(await links.nth(0).getAttribute('href')).toBe('/dash')
|
|
90
98
|
expect(await links.nth(1).getAttribute('href')).toBe('https://owlmeans.com')
|
|
@@ -97,3 +105,441 @@ describe('@owlmeans/web-panel — two-layer navigation', () => {
|
|
|
97
105
|
}
|
|
98
106
|
}, TIMEOUT)
|
|
99
107
|
})
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Perceptual lightness of a computed colour, 0 (black) to 1 (white).
|
|
111
|
+
*
|
|
112
|
+
* The theme is authored in `oklch(L C H)` and Chromium hands that back from
|
|
113
|
+
* `getComputedStyle` unchanged — it does not convert to `rgb()`, and neither does a canvas
|
|
114
|
+
* `fillStyle` round-trip — so L is read straight off. `rgb()` still turns up for the
|
|
115
|
+
* transparent case, and is approximated through its luminance.
|
|
116
|
+
*
|
|
117
|
+
* Comparing lightness rather than colour strings is the point: the failure being pinned is
|
|
118
|
+
* not a wrong colour, it is two colours too CLOSE to tell apart, which equality never sees.
|
|
119
|
+
*/
|
|
120
|
+
const lightness = (value: string): number => {
|
|
121
|
+
const oklch = /^oklch\(\s*([\d.]+%?)/i.exec(value.trim())
|
|
122
|
+
if (oklch != null) {
|
|
123
|
+
const raw = oklch[1]
|
|
124
|
+
|
|
125
|
+
return raw.endsWith('%') ? parseFloat(raw) / 100 : parseFloat(raw)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const [r, g, b] = (value.match(/[\d.]+/g) ?? []).slice(0, 3).map(Number)
|
|
129
|
+
const channel = (c: number): number => {
|
|
130
|
+
const v = c / 255
|
|
131
|
+
|
|
132
|
+
return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The cube root maps luminance onto roughly the same perceptual scale as OKLab's L.
|
|
136
|
+
return (0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b)) ** (1 / 3)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The gap below which text stops being readable on its surface — as `ensureReadableTheme`. */
|
|
140
|
+
const READABLE = 0.35
|
|
141
|
+
|
|
142
|
+
describe('@owlmeans/web-panel — the header is its own surface', () => {
|
|
143
|
+
// The harness root carries `bg-primary text-primary-foreground`: a dark application shell,
|
|
144
|
+
// both halves of the pair correct. The header paints `bg-background` over it and is
|
|
145
|
+
// therefore a DIFFERENT surface — a near-white foreground inherited from the root lands on
|
|
146
|
+
// a near-white bar, and every child stating no colour of its own goes invisible. Nothing
|
|
147
|
+
// fails at build or at run time, so this is pinned from outside, on rendered pixels.
|
|
148
|
+
test('header content stays legible when the shell around it is dark', async () => {
|
|
149
|
+
const { page, close } = await open('/dash')
|
|
150
|
+
try {
|
|
151
|
+
await page.waitForSelector('#dash')
|
|
152
|
+
const style = async (selector: string, property: 'color' | 'backgroundColor') =>
|
|
153
|
+
page.locator(selector).first().evaluate(
|
|
154
|
+
(el, prop) => window.getComputedStyle(el)[prop as 'color'], property
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const surface = lightness(await style('header', 'backgroundColor'))
|
|
158
|
+
const legible = async (selector: string) =>
|
|
159
|
+
Math.abs(lightness(await style(selector, 'color')) - surface) >= READABLE
|
|
160
|
+
|
|
161
|
+
// The brand and the action slot state no colour of their own, so they are exactly the
|
|
162
|
+
// elements that inherit across the boundary. The action slot is the reported case: a
|
|
163
|
+
// ghost-variant button rendered white on a white bar.
|
|
164
|
+
expect({
|
|
165
|
+
brand: await legible('a.brand'),
|
|
166
|
+
actions: await legible('#action-slot'),
|
|
167
|
+
section: await legible('[data-slot="navigation-menu-link"]'),
|
|
168
|
+
}).toEqual({ brand: true, actions: true, section: true })
|
|
169
|
+
} finally {
|
|
170
|
+
await close()
|
|
171
|
+
}
|
|
172
|
+
}, TIMEOUT)
|
|
173
|
+
|
|
174
|
+
test('the header keeps an opaque background of its own', async () => {
|
|
175
|
+
// This is what makes it a separate surface, and therefore what makes the pairing above
|
|
176
|
+
// mandatory rather than redundant: it is sticky, so content scrolls beneath it and a
|
|
177
|
+
// transparent bar would show that content through the menu.
|
|
178
|
+
const { page, close } = await open('/dash')
|
|
179
|
+
try {
|
|
180
|
+
await page.waitForSelector('#dash')
|
|
181
|
+
const background = await page.locator('header').first()
|
|
182
|
+
.evaluate(el => window.getComputedStyle(el).backgroundColor)
|
|
183
|
+
|
|
184
|
+
expect(background).not.toMatch(/rgba\(0, 0, 0, 0\)|transparent/)
|
|
185
|
+
} finally {
|
|
186
|
+
await close()
|
|
187
|
+
}
|
|
188
|
+
}, TIMEOUT)
|
|
189
|
+
|
|
190
|
+
test('a broken headerClassName still leaves the backdrop layer opaque', async () => {
|
|
191
|
+
// `?header=broken` hands the header a Tailwind v3 arbitrary-value class v4 never emits, plus
|
|
192
|
+
// a bare `bg-transparent` — exactly what a layout-restyle pass produced once. tailwind-merge
|
|
193
|
+
// drops the header's own `bg-background` for both, so `header` itself legitimately reports a
|
|
194
|
+
// transparent computed background — that is `headerClassName` doing exactly what it says.
|
|
195
|
+
// What must NOT be transparent is the backdrop layer painted behind it: that is the element
|
|
196
|
+
// actually responsible for keeping content from showing through the bar.
|
|
197
|
+
const { page, close } = await open('/dash?header=broken')
|
|
198
|
+
try {
|
|
199
|
+
await page.waitForSelector('#dash')
|
|
200
|
+
const background = await page.locator('[data-nav-backdrop]').first()
|
|
201
|
+
.evaluate(el => window.getComputedStyle(el).backgroundColor)
|
|
202
|
+
|
|
203
|
+
expect(background).not.toMatch(/rgba\(0, 0, 0, 0\)|transparent/)
|
|
204
|
+
} finally {
|
|
205
|
+
await close()
|
|
206
|
+
}
|
|
207
|
+
}, TIMEOUT)
|
|
208
|
+
|
|
209
|
+
test('the backdrop layer never changes which div the shared-rhythm query finds first', async () => {
|
|
210
|
+
// `data-nav-backdrop` sits last in `header`'s DOM but paints behind everything via negative
|
|
211
|
+
// z-index — this pins that a position-based query for the header's own content div
|
|
212
|
+
// (`header > div`, `.first()`) still finds the rhythm div, not the backdrop.
|
|
213
|
+
const { page, close } = await open('/dash')
|
|
214
|
+
try {
|
|
215
|
+
await page.waitForSelector('#dash')
|
|
216
|
+
const first = await page.locator('header > div').first().getAttribute('data-nav-backdrop')
|
|
217
|
+
expect(first).toBeNull()
|
|
218
|
+
} finally {
|
|
219
|
+
await close()
|
|
220
|
+
}
|
|
221
|
+
}, TIMEOUT)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
describe('@owlmeans/web-panel — the shell footer credit', () => {
|
|
225
|
+
test('the platform credit and the owner notice render, owner notice first', async () => {
|
|
226
|
+
// The harness configures `credit: { poweredBy: true, product: 'Harness', organization: 'Acme' }`
|
|
227
|
+
// with no explicit `line`, so `resolveCredit` composes one from product/organization — the
|
|
228
|
+
// same resolver the sign-in screen uses. Order here is the opposite of the sign-in screen's:
|
|
229
|
+
// the owner's own notice leads, the platform credit follows.
|
|
230
|
+
const { page, close } = await open('/dash')
|
|
231
|
+
try {
|
|
232
|
+
await page.waitForSelector('#dash')
|
|
233
|
+
const credit = page.locator('footer [data-shell-credit]')
|
|
234
|
+
await credit.waitFor()
|
|
235
|
+
const text = await credit.textContent()
|
|
236
|
+
expect(text).toContain('Acme')
|
|
237
|
+
expect(text).toContain('Powered by OwlMeans')
|
|
238
|
+
expect(text?.indexOf('Acme')).toBeLessThan(text!.indexOf('Powered by OwlMeans'))
|
|
239
|
+
const powered = page.locator('footer [data-shell-credit] a[data-login-powered]')
|
|
240
|
+
expect(await powered.getAttribute('href')).toBe('https://owlmeans.com')
|
|
241
|
+
expect(await powered.getAttribute('target')).toBe('_blank')
|
|
242
|
+
} finally {
|
|
243
|
+
await close()
|
|
244
|
+
}
|
|
245
|
+
}, TIMEOUT)
|
|
246
|
+
|
|
247
|
+
test('the footer links and the credit are centred, not left-aligned', async () => {
|
|
248
|
+
const { page, close } = await open('/dash')
|
|
249
|
+
try {
|
|
250
|
+
await page.waitForSelector('#dash')
|
|
251
|
+
const justify = await page.locator('footer > div').first()
|
|
252
|
+
.evaluate(el => window.getComputedStyle(el).alignItems)
|
|
253
|
+
expect(justify).toBe('center')
|
|
254
|
+
} finally {
|
|
255
|
+
await close()
|
|
256
|
+
}
|
|
257
|
+
}, TIMEOUT)
|
|
258
|
+
|
|
259
|
+
test('a layout with no footer prop still shows the credit', async () => {
|
|
260
|
+
// `NavLayout` used to skip `Footer` entirely when no `footer` prop was passed, which made the
|
|
261
|
+
// credit disappear from any area layout that never wired footer links. `?footer=none` omits
|
|
262
|
+
// the prop from the harness's own `NavLayout` call.
|
|
263
|
+
const { page, close } = await open('/dash?footer=none')
|
|
264
|
+
try {
|
|
265
|
+
await page.waitForSelector('#dash')
|
|
266
|
+
expect(await page.locator('footer a:not([data-login-powered])').count()).toBe(0)
|
|
267
|
+
expect(await page.locator('footer [data-shell-credit]').count()).toBe(1)
|
|
268
|
+
} finally {
|
|
269
|
+
await close()
|
|
270
|
+
}
|
|
271
|
+
}, TIMEOUT)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
describe('@owlmeans/web-panel — layout rhythm and link styling', () => {
|
|
275
|
+
test('header, content and footer share one horizontal rhythm', async () => {
|
|
276
|
+
// A content area with a width of its own sits visibly inset from a full-width header, which
|
|
277
|
+
// is what "the screen padding is much narrower than the header" looks like from outside.
|
|
278
|
+
const { page, close } = await open('/prefs')
|
|
279
|
+
try {
|
|
280
|
+
await page.waitForSelector('#prefs')
|
|
281
|
+
const box = async (sel: string) => {
|
|
282
|
+
const b = await page.locator(sel).first().boundingBox()
|
|
283
|
+
if (b == null) throw new Error(`no box for ${sel}`)
|
|
284
|
+
return { x: Math.round(b.x), w: Math.round(b.width) }
|
|
285
|
+
}
|
|
286
|
+
const header = await box('header > div')
|
|
287
|
+
const content = await box('main > div')
|
|
288
|
+
const footer = await box('footer > div')
|
|
289
|
+
|
|
290
|
+
expect({ ...content, of: 'content' }).toEqual({ ...header, of: 'content' })
|
|
291
|
+
expect({ ...footer, of: 'footer' }).toEqual({ ...header, of: 'footer' })
|
|
292
|
+
} finally {
|
|
293
|
+
await close()
|
|
294
|
+
}
|
|
295
|
+
}, TIMEOUT)
|
|
296
|
+
|
|
297
|
+
test('a width-only rhythm override keeps the side padding', async () => {
|
|
298
|
+
// The harness passes `containerClassName="max-w-[1280px]"` — a width and nothing else.
|
|
299
|
+
// `containerClassName` is merged over the shell's default rhythm rather than substituted
|
|
300
|
+
// for it, so `px-4` and `mx-auto` survive an override that never mentioned them. Replacing
|
|
301
|
+
// instead produced a header, content and footer all flush to the window edge.
|
|
302
|
+
const { page, close } = await open('/prefs')
|
|
303
|
+
try {
|
|
304
|
+
await page.waitForSelector('#prefs')
|
|
305
|
+
const padding = async (selector: string) => page.locator(selector).first().evaluate(el => {
|
|
306
|
+
const style = window.getComputedStyle(el)
|
|
307
|
+
|
|
308
|
+
return [parseFloat(style.paddingLeft), parseFloat(style.paddingRight)]
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const [header, content, footer] = await Promise.all([
|
|
312
|
+
padding('header > div'), padding('main > div'), padding('footer > div'),
|
|
313
|
+
])
|
|
314
|
+
|
|
315
|
+
expect({
|
|
316
|
+
header: header.every(value => value > 0),
|
|
317
|
+
content: content.every(value => value > 0),
|
|
318
|
+
footer: footer.every(value => value > 0),
|
|
319
|
+
}).toEqual({ header: true, content: true, footer: true })
|
|
320
|
+
} finally {
|
|
321
|
+
await close()
|
|
322
|
+
}
|
|
323
|
+
}, TIMEOUT)
|
|
324
|
+
|
|
325
|
+
test('section entries render as links, not buttons', async () => {
|
|
326
|
+
const { page, close } = await open('/dash')
|
|
327
|
+
try {
|
|
328
|
+
await page.waitForSelector('#dash')
|
|
329
|
+
const active = page.locator('[data-slot="navigation-menu-link"][data-active]')
|
|
330
|
+
const style = await active.evaluate(el => {
|
|
331
|
+
const s = window.getComputedStyle(el)
|
|
332
|
+
return { bg: s.backgroundColor, radius: s.borderTopLeftRadius, deco: s.textDecorationLine }
|
|
333
|
+
})
|
|
334
|
+
// A filled background plus a radius is the tile look the shadcn primitive ships; a link
|
|
335
|
+
// carries neither, and marks the current section with an underline instead.
|
|
336
|
+
expect(style.bg).toMatch(/rgba\(0, 0, 0, 0\)|transparent/)
|
|
337
|
+
expect(style.radius).toBe('0px')
|
|
338
|
+
expect(style.deco).toBe('underline')
|
|
339
|
+
} finally {
|
|
340
|
+
await close()
|
|
341
|
+
}
|
|
342
|
+
}, TIMEOUT)
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
describe('@owlmeans/web-panel — a node footer', () => {
|
|
346
|
+
test('a node footer spans the container, which stays centred, and the credit follows it', async () => {
|
|
347
|
+
// `?footer=node` hands `NavLayout` an application's own footer layout. It used to land inside
|
|
348
|
+
// the centred link row, shrink-wrapped to its content in the middle of the page; it is a
|
|
349
|
+
// full-width block of the container now, while the container itself keeps `items-center`
|
|
350
|
+
// for the link row and the credit.
|
|
351
|
+
const { page, close } = await open('/prefs?footer=node')
|
|
352
|
+
try {
|
|
353
|
+
await page.waitForSelector('#prefs')
|
|
354
|
+
await page.locator('footer [data-shell-credit]').waitFor()
|
|
355
|
+
const container = page.locator('footer > div').first()
|
|
356
|
+
const layout = await container.evaluate(el => {
|
|
357
|
+
const style = window.getComputedStyle(el)
|
|
358
|
+
const box = el.getBoundingClientRect()
|
|
359
|
+
const block = el.querySelector(':scope > [data-footer-content]')
|
|
360
|
+
const inner = block?.getBoundingClientRect()
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
align: style.alignItems,
|
|
364
|
+
inner: {
|
|
365
|
+
x: Math.round(box.x + parseFloat(style.paddingLeft)),
|
|
366
|
+
w: Math.round(box.width - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight)),
|
|
367
|
+
},
|
|
368
|
+
block: inner == null ? null : { x: Math.round(inner.x), w: Math.round(inner.width) },
|
|
369
|
+
hasHarnessBlock: block?.querySelector('#footer-block') != null,
|
|
370
|
+
last: el.lastElementChild?.hasAttribute('data-shell-credit') ?? false,
|
|
371
|
+
creditAfterBlock: block != null && el.querySelector(':scope > [data-shell-credit]') != null
|
|
372
|
+
&& (block.compareDocumentPosition(el.querySelector(':scope > [data-shell-credit]')!)
|
|
373
|
+
& Node.DOCUMENT_POSITION_FOLLOWING) !== 0,
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
expect(layout.align).toBe('center')
|
|
378
|
+
expect(layout.block).toEqual(layout.inner)
|
|
379
|
+
expect(layout.hasHarnessBlock).toBe(true)
|
|
380
|
+
expect({ last: layout.last, creditAfterBlock: layout.creditAfterBlock })
|
|
381
|
+
.toEqual({ last: true, creditAfterBlock: true })
|
|
382
|
+
} finally {
|
|
383
|
+
await close()
|
|
384
|
+
}
|
|
385
|
+
}, TIMEOUT)
|
|
386
|
+
|
|
387
|
+
test('a node footer keeps the shared rhythm', async () => {
|
|
388
|
+
const { page, close } = await open('/prefs?footer=node')
|
|
389
|
+
try {
|
|
390
|
+
await page.waitForSelector('#prefs')
|
|
391
|
+
const box = async (sel: string) => {
|
|
392
|
+
const b = await page.locator(sel).first().boundingBox()
|
|
393
|
+
if (b == null) throw new Error(`no box for ${sel}`)
|
|
394
|
+
return { x: Math.round(b.x), w: Math.round(b.width) }
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
expect(await box('footer > div')).toEqual(await box('header > div'))
|
|
398
|
+
} finally {
|
|
399
|
+
await close()
|
|
400
|
+
}
|
|
401
|
+
}, TIMEOUT)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
/** The narrow viewport the menu sheet is for — below Tailwind's `md` (768px). */
|
|
405
|
+
const PHONE = { width: 375, height: 800 }
|
|
406
|
+
|
|
407
|
+
describe('@owlmeans/web-panel — the narrow-viewport menu', () => {
|
|
408
|
+
test('configured selectors identify a single destination in desktop and mobile navigation', async () => {
|
|
409
|
+
const { page, close } = await open('/dash?mobileMenu=1')
|
|
410
|
+
try {
|
|
411
|
+
await page.getByTestId('harness-nav').waitFor()
|
|
412
|
+
expect(await page.getByTestId('settings-nav').count()).toBe(1)
|
|
413
|
+
expect(await page.getByTestId('dashboard-nav').count()).toBe(1)
|
|
414
|
+
await page.setViewportSize(PHONE)
|
|
415
|
+
await page.getByRole('button', { name: 'Menu' }).click()
|
|
416
|
+
expect(await page.getByTestId('settings-nav').count()).toBe(1)
|
|
417
|
+
expect(await page.getByTestId('dashboard-nav').count()).toBe(1)
|
|
418
|
+
await page.getByTestId('settings-nav').click()
|
|
419
|
+
await page.waitForSelector('#prefs')
|
|
420
|
+
await page.getByRole('dialog', { name: 'Menu' }).waitFor({ state: 'detached' })
|
|
421
|
+
} finally { await close() }
|
|
422
|
+
}, TIMEOUT)
|
|
423
|
+
test('with mobileMenu, a phone gets a menu button instead of the section menu', async () => {
|
|
424
|
+
const { page, close } = await open('/dash?mobileMenu=1')
|
|
425
|
+
try {
|
|
426
|
+
await page.setViewportSize(PHONE)
|
|
427
|
+
await page.waitForSelector('#dash')
|
|
428
|
+
const trigger = page.getByRole('button', { name: 'Menu' })
|
|
429
|
+
expect(await trigger.isVisible()).toBe(true)
|
|
430
|
+
const box = await trigger.boundingBox()
|
|
431
|
+
expect((box?.width ?? 0) >= 44 && (box?.height ?? 0) >= 44).toBe(true)
|
|
432
|
+
// The section menu hides, the action slot stays, and the screen strip is not mounted at all:
|
|
433
|
+
// the sheet lists the same screens.
|
|
434
|
+
expect(await page.locator('header nav[aria-label="Sections"]').first().isVisible()).toBe(false)
|
|
435
|
+
expect(await page.locator('#action-slot').isVisible()).toBe(true)
|
|
436
|
+
expect(await page.locator('header nav[aria-label="Screens"]').count()).toBe(0)
|
|
437
|
+
expect(await page.locator('[data-nav-sheet]').count()).toBe(0)
|
|
438
|
+
} finally {
|
|
439
|
+
await close()
|
|
440
|
+
}
|
|
441
|
+
}, TIMEOUT)
|
|
442
|
+
|
|
443
|
+
test('the sheet lists sections and their screens as links, and a press navigates and closes it', async () => {
|
|
444
|
+
const { page, close } = await open('/dash?mobileMenu=1')
|
|
445
|
+
try {
|
|
446
|
+
await page.setViewportSize(PHONE)
|
|
447
|
+
await page.waitForSelector('#dash')
|
|
448
|
+
await page.getByRole('button', { name: 'Menu' }).click()
|
|
449
|
+
const sheet = page.getByRole('dialog', { name: 'Menu' })
|
|
450
|
+
await sheet.waitFor()
|
|
451
|
+
|
|
452
|
+
// A multi-screen section lists its screens under its name; a single-screen section is one
|
|
453
|
+
// link carrying the section's label — no destination twice. Labels resolve as the menus'.
|
|
454
|
+
const links = sheet.getByRole('link')
|
|
455
|
+
expect(await links.allTextContents()).toEqual(['Dashboard', 'Reports', 'Settings', 'Extra'])
|
|
456
|
+
expect(await links.evaluateAll(els => els.map(el => el.getAttribute('href'))))
|
|
457
|
+
.toEqual(['/dash', '/reports', '/prefs', '/'])
|
|
458
|
+
expect(await sheet.getByRole('link', { name: 'Dashboard' }).getAttribute('aria-current')).toBe('page')
|
|
459
|
+
expect(await sheet.getByText('Work').count()).toBe(1)
|
|
460
|
+
|
|
461
|
+
await sheet.getByRole('link', { name: 'Settings' }).click()
|
|
462
|
+
await page.waitForSelector('#prefs')
|
|
463
|
+
expect(new URL(page.url()).pathname).toBe('/prefs')
|
|
464
|
+
await page.locator('[data-nav-sheet]').waitFor({ state: 'detached' })
|
|
465
|
+
} finally {
|
|
466
|
+
await close()
|
|
467
|
+
}
|
|
468
|
+
}, TIMEOUT)
|
|
469
|
+
|
|
470
|
+
test('with mobileMenu, a wide viewport is the ordinary shell', async () => {
|
|
471
|
+
const { page, close } = await open('/dash?mobileMenu=1')
|
|
472
|
+
try {
|
|
473
|
+
await page.waitForSelector('#dash')
|
|
474
|
+
expect(await page.locator('[data-nav-menu-trigger]').isVisible()).toBe(false)
|
|
475
|
+
expect(await page.locator('header nav[aria-label="Sections"]').first().isVisible()).toBe(true)
|
|
476
|
+
expect(await page.locator('aside nav button').count()).toBe(2)
|
|
477
|
+
} finally {
|
|
478
|
+
await close()
|
|
479
|
+
}
|
|
480
|
+
}, TIMEOUT)
|
|
481
|
+
|
|
482
|
+
test('without mobileMenu, a phone keeps the section menu and the screen strip', async () => {
|
|
483
|
+
const { page, close } = await open('/dash')
|
|
484
|
+
try {
|
|
485
|
+
await page.setViewportSize(PHONE)
|
|
486
|
+
await page.waitForSelector('#dash')
|
|
487
|
+
expect(await page.locator('[data-nav-menu-trigger]').count()).toBe(0)
|
|
488
|
+
expect(await page.locator('header nav[aria-label="Sections"]').first().isVisible()).toBe(true)
|
|
489
|
+
const strip = page.locator('header nav[aria-label="Screens"]')
|
|
490
|
+
expect(await strip.isVisible()).toBe(true)
|
|
491
|
+
expect(await strip.locator('button').count()).toBe(2)
|
|
492
|
+
} finally {
|
|
493
|
+
await close()
|
|
494
|
+
}
|
|
495
|
+
}, TIMEOUT)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
describe('@owlmeans/web-panel — the skip link', () => {
|
|
499
|
+
test('Tab from the page start reaches the skip link first, and it moves focus to main#main', async () => {
|
|
500
|
+
const { page, close } = await open('/dash')
|
|
501
|
+
try {
|
|
502
|
+
await page.waitForSelector('#dash')
|
|
503
|
+
const link = page.locator('[data-skip-link]')
|
|
504
|
+
// First in the shell's root, before the header — never inside it, so `header > div` is
|
|
505
|
+
// still the shared-rhythm row.
|
|
506
|
+
expect(await link.evaluate(el => el.nextElementSibling?.tagName)).toBe('HEADER')
|
|
507
|
+
expect(await page.locator('header [data-skip-link]').count()).toBe(0)
|
|
508
|
+
expect(await link.isVisible()).toBe(true) // `sr-only` is still rendered, just clipped
|
|
509
|
+
const clipped = await link.boundingBox()
|
|
510
|
+
expect((clipped?.width ?? 0) <= 1).toBe(true)
|
|
511
|
+
|
|
512
|
+
await page.keyboard.press('Tab')
|
|
513
|
+
expect(await page.evaluate(() => document.activeElement?.hasAttribute('data-skip-link'))).toBe(true)
|
|
514
|
+
expect(await link.textContent()).toBe('Skip to content')
|
|
515
|
+
const shown = await link.evaluate(el => {
|
|
516
|
+
const style = window.getComputedStyle(el)
|
|
517
|
+
return { position: style.position, width: el.getBoundingClientRect().width }
|
|
518
|
+
})
|
|
519
|
+
expect(shown.position).toBe('fixed')
|
|
520
|
+
expect(shown.width > 1).toBe(true)
|
|
521
|
+
|
|
522
|
+
await page.keyboard.press('Enter')
|
|
523
|
+
expect(await page.evaluate(() => {
|
|
524
|
+
const active = document.activeElement
|
|
525
|
+
return active?.tagName === 'MAIN' ? active.id : null
|
|
526
|
+
})).toBe('main')
|
|
527
|
+
// In-app: the location is left alone, so the router sees no navigation.
|
|
528
|
+
expect(new URL(page.url()).hash).toBe('')
|
|
529
|
+
} finally {
|
|
530
|
+
await close()
|
|
531
|
+
}
|
|
532
|
+
}, TIMEOUT)
|
|
533
|
+
|
|
534
|
+
test('skipLinkLabel={false} renders no link and claims no #main', async () => {
|
|
535
|
+
const { page, close } = await open('/dash?skip=off')
|
|
536
|
+
try {
|
|
537
|
+
await page.waitForSelector('#dash')
|
|
538
|
+
expect(await page.locator('[data-skip-link]').count()).toBe(0)
|
|
539
|
+
expect(await page.locator('#main').count()).toBe(0)
|
|
540
|
+
expect(await page.locator('main').count()).toBe(1)
|
|
541
|
+
} finally {
|
|
542
|
+
await close()
|
|
543
|
+
}
|
|
544
|
+
}, TIMEOUT)
|
|
545
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
const source = join(import.meta.dir, '../src')
|
|
6
|
+
|
|
7
|
+
const sourceFiles = (dir: string): string[] => readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
|
|
8
|
+
const path = join(dir, entry.name)
|
|
9
|
+
|
|
10
|
+
return entry.isDirectory() ? sourceFiles(path) : [path]
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('@owlmeans/web-panel — package boundary', () => {
|
|
14
|
+
test('does not use consumer-project UI aliases in package source', () => {
|
|
15
|
+
for (const file of sourceFiles(source).filter(file => /\.tsx?$/.test(file))) {
|
|
16
|
+
expect(readFileSync(file, 'utf8')).not.toMatch(/from\s+['"]@\//)
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { afterAll, describe, expect, test } from 'bun:test'
|
|
2
|
+
import { mkdirSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { browserHelper } from '@owlmeans/test-ui'
|
|
5
|
+
import type { Page } from '@owlmeans/test-ui'
|
|
6
|
+
import { HARNESS_URL } from './context.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Visual review material, not an assertion suite: every picker scenario at 1280 and 375, light
|
|
10
|
+
* and dark, written as PNGs for a person to look at. Runs only when `PICKER_SHOTS_DIR` names the
|
|
11
|
+
* directory to write to — `PICKER_SHOTS_DIR=/tmp/shots bun test tests/picker-shots.spec.ts`.
|
|
12
|
+
*/
|
|
13
|
+
const dir = process.env.PICKER_SHOTS_DIR
|
|
14
|
+
const TIMEOUT = 120_000
|
|
15
|
+
|
|
16
|
+
afterAll(async () => {
|
|
17
|
+
await browserHelper.closeBrowser()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
interface Shot {
|
|
21
|
+
name: string
|
|
22
|
+
path: string
|
|
23
|
+
phone?: boolean
|
|
24
|
+
dark?: 'class' | 'os'
|
|
25
|
+
act?: (page: Page) => Promise<void>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const openMark = async (page: Page): Promise<void> => {
|
|
29
|
+
await page.locator('[data-picker-trigger]').first().click()
|
|
30
|
+
await page.locator('[data-picker-surface]').waitFor()
|
|
31
|
+
await page.waitForTimeout(250)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const shots: Shot[] = [
|
|
35
|
+
{ name: 'desktop-closed', path: '/picker' },
|
|
36
|
+
{ name: 'desktop-open', path: '/picker', act: openMark },
|
|
37
|
+
{ name: 'desktop-open-dark', path: '/picker?dark=1', dark: 'class', act: openMark },
|
|
38
|
+
{ name: 'desktop-open-os-dark', path: '/picker', dark: 'os', act: openMark },
|
|
39
|
+
{ name: 'desktop-closed-dark', path: '/picker?dark=1', dark: 'class' },
|
|
40
|
+
{
|
|
41
|
+
name: 'desktop-search', path: '/picker', act: async page => {
|
|
42
|
+
await openMark(page)
|
|
43
|
+
await page.locator('[data-picker-search]').fill('user')
|
|
44
|
+
await page.waitForTimeout(150)
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'desktop-empty-search', path: '/picker', act: async page => {
|
|
49
|
+
await openMark(page)
|
|
50
|
+
await page.locator('[data-picker-search]').fill('zzzz')
|
|
51
|
+
await page.waitForTimeout(150)
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'desktop-category', path: '/picker', act: async page => {
|
|
56
|
+
await openMark(page)
|
|
57
|
+
await page.locator('[data-picker-category="commerce"]').click()
|
|
58
|
+
await page.waitForTimeout(150)
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'desktop-keyboard', path: '/picker', act: async page => {
|
|
63
|
+
await openMark(page)
|
|
64
|
+
await page.keyboard.press('ArrowDown')
|
|
65
|
+
await page.keyboard.press('ArrowRight')
|
|
66
|
+
await page.keyboard.press('ArrowDown')
|
|
67
|
+
await page.waitForTimeout(150)
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'desktop-custom', path: '/picker?custom=1', act: async page => {
|
|
72
|
+
await openMark(page)
|
|
73
|
+
await page.locator('[data-picker-color="custom"]').click()
|
|
74
|
+
await page.locator('[data-picker-hex]').fill('#0f766e')
|
|
75
|
+
await page.waitForTimeout(150)
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'desktop-custom-dark', path: '/picker?custom=1&dark=1', dark: 'class', act: async page => {
|
|
80
|
+
await openMark(page)
|
|
81
|
+
await page.locator('[data-picker-color="custom"]').click()
|
|
82
|
+
await page.locator('[data-picker-hex]').fill('#ff8800')
|
|
83
|
+
await page.waitForTimeout(150)
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'desktop-color-picker', path: '/picker', act: async page => {
|
|
88
|
+
await page.locator('[data-picker-trigger]').nth(1).click()
|
|
89
|
+
await page.locator('[data-picker-surface]').waitFor()
|
|
90
|
+
await page.waitForTimeout(250)
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'desktop-icon-picker', path: '/picker', act: async page => {
|
|
95
|
+
await page.locator('[data-picker-trigger]').nth(2).click()
|
|
96
|
+
await page.locator('[data-picker-surface]').waitFor()
|
|
97
|
+
await page.waitForTimeout(250)
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{ name: 'desktop-empty', path: '/picker?empty=1' },
|
|
101
|
+
{ name: 'desktop-compact', path: '/picker?compact=1' },
|
|
102
|
+
{ name: 'desktop-compact-open', path: '/picker?compact=1', act: openMark },
|
|
103
|
+
{
|
|
104
|
+
name: 'desktop-in-sheet', path: '/picker?inSheet=1', act: async page => {
|
|
105
|
+
await page.locator('#open-sheet').click()
|
|
106
|
+
await page.locator('[data-slot="sheet-content"]').waitFor()
|
|
107
|
+
await page.waitForTimeout(400)
|
|
108
|
+
await openMark(page)
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{ name: 'desktop-inline', path: '/picker?inline=1&custom=1' },
|
|
112
|
+
{ name: 'desktop-bare', path: '/picker?bare=1' },
|
|
113
|
+
{ name: 'phone-closed', path: '/picker', phone: true },
|
|
114
|
+
{ name: 'phone-open', path: '/picker', phone: true, act: openMark },
|
|
115
|
+
{ name: 'phone-open-dark', path: '/picker?dark=1', dark: 'class', phone: true, act: openMark },
|
|
116
|
+
{
|
|
117
|
+
name: 'phone-scrolled', path: '/picker', phone: true, act: async page => {
|
|
118
|
+
await openMark(page)
|
|
119
|
+
await page.locator('[data-picker-surface] [data-picker-scroll]').evaluate(element => { element.scrollTop = 420 })
|
|
120
|
+
await page.waitForTimeout(150)
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'phone-custom', path: '/picker?custom=1', phone: true, act: async page => {
|
|
125
|
+
await openMark(page)
|
|
126
|
+
await page.locator('[data-picker-color="custom"]').click()
|
|
127
|
+
await page.waitForTimeout(150)
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'phone-color-picker', path: '/picker', phone: true, act: async page => {
|
|
132
|
+
await page.locator('[data-picker-trigger]').nth(1).click()
|
|
133
|
+
await page.locator('[data-picker-surface]').waitFor()
|
|
134
|
+
await page.waitForTimeout(250)
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
{ name: 'phone-inline', path: '/picker?inline=1', phone: true },
|
|
138
|
+
{
|
|
139
|
+
name: 'phone-in-sheet', path: '/picker?inSheet=1', phone: true, act: async page => {
|
|
140
|
+
await page.locator('#open-sheet').click()
|
|
141
|
+
await page.locator('[data-slot="sheet-content"]').waitFor()
|
|
142
|
+
await page.waitForTimeout(400)
|
|
143
|
+
await openMark(page)
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{ name: 'phone-empty', path: '/picker?empty=1', phone: true, act: openMark },
|
|
147
|
+
{ name: 'phone-compact', path: '/picker?compact=1', phone: true },
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
describe.skipIf(dir == null)('@owlmeans/web-panel/picker — screenshots', () => {
|
|
151
|
+
test('every scenario', async () => {
|
|
152
|
+
const out = dir ?? ''
|
|
153
|
+
mkdirSync(out, { recursive: true })
|
|
154
|
+
const browser = await browserHelper.launchBrowser()
|
|
155
|
+
for (const shot of shots) {
|
|
156
|
+
const context = await browser.newContext(shot.phone === true
|
|
157
|
+
? { viewport: { width: 375, height: 812 }, deviceScaleFactor: 2, hasTouch: true, isMobile: true }
|
|
158
|
+
: { viewport: { width: 1280, height: 800 } })
|
|
159
|
+
const page = await context.newPage()
|
|
160
|
+
try {
|
|
161
|
+
if (shot.dark === 'os') {
|
|
162
|
+
await page.emulateMedia({ colorScheme: 'dark' })
|
|
163
|
+
}
|
|
164
|
+
await page.goto(`${HARNESS_URL.replace(/\/$/, '')}${shot.path}`, { waitUntil: 'domcontentloaded' })
|
|
165
|
+
await page.locator('#picker').waitFor()
|
|
166
|
+
await shot.act?.(page)
|
|
167
|
+
await page.screenshot({ path: join(out, `${shot.name}.png`) })
|
|
168
|
+
} finally {
|
|
169
|
+
await context.close()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
expect(shots.length).toBeGreaterThan(0)
|
|
173
|
+
}, TIMEOUT * 3)
|
|
174
|
+
})
|