@jant/core 0.6.14 → 0.6.15
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/bin/commands/import-site.js +81 -13
- package/bin/commands/migrate.js +55 -0
- package/bin/commands/posts/rebuild-html.js +184 -0
- package/bin/commands/site/snapshot/import.js +14 -10
- package/bin/lib/d1-query.js +23 -5
- package/bin/lib/migration-runner.js +29 -7
- package/bin/lib/node-database.js +32 -0
- package/bin/lib/site-snapshot.js +347 -46
- package/bin/lib/sql-export.js +1 -1
- package/bin/lib/thread-collection-migration.js +326 -0
- package/dist/app-BxCOR3Uc.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C-oi_t8W.js} +7408 -6343
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-B_eGKN5p.css +2 -0
- package/dist/client/_assets/{client-BvID5ucz.js → client-DxY5BFe8.js} +58 -37
- package/dist/client/_assets/client-auth-CakPESv9.js +5533 -0
- package/dist/{export-DWn149b7.js → export-CsFx6F_M.js} +1259 -125
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-Cq1pzzOI.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-ppWXN3jb.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/node.js +5 -5
- package/package.json +11 -10
- package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
- package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
- package/src/__tests__/export-hugo-build.test.ts +63 -4
- package/src/__tests__/export-import-roundtrip.test.ts +6 -4
- package/src/__tests__/export-service.test.ts +142 -9
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +2 -0
- package/src/__tests__/import-site-command.test.ts +65 -0
- package/src/__tests__/vite-config.test.ts +45 -0
- package/src/app.tsx +16 -3
- package/src/client/__tests__/collection-created-notice.test.ts +36 -0
- package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
- package/src/client/__tests__/collection-page-actions.test.ts +71 -2
- package/src/client/__tests__/compose-bridge.test.ts +64 -0
- package/src/client/__tests__/feed-video-player.test.ts +16 -1
- package/src/client/__tests__/footnote-rail.test.ts +472 -0
- package/src/client/__tests__/hydrate-partial.test.ts +3 -0
- package/src/client/__tests__/toast.test.ts +18 -1
- package/src/client/collection-created-notice.ts +66 -0
- package/src/client/collection-form-bridge.ts +17 -0
- package/src/client/collection-navigation.ts +44 -0
- package/src/client/collection-page-actions.ts +57 -1
- package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +370 -5
- package/src/client/components/__tests__/jant-compose-editor.test.ts +97 -0
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +104 -0
- package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
- package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
- package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-manager-types.ts +9 -3
- package/src/client/components/compose-types.ts +8 -0
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +2 -1
- package/src/client/components/jant-compose-dialog.ts +85 -21
- package/src/client/components/jant-compose-editor.ts +13 -7
- package/src/client/components/jant-compose-fullscreen.ts +24 -4
- package/src/client/components/jant-config-editor.ts +722 -0
- package/src/client/components/jant-media-lightbox.ts +100 -18
- package/src/client/components/jant-nav-manager.ts +1283 -54
- package/src/client/components/jant-post-form.ts +3 -3
- package/src/client/components/jant-post-menu.ts +86 -74
- package/src/client/components/nav-manager-types.ts +51 -2
- package/src/client/components/post-form-types.ts +1 -1
- package/src/client/compose-bridge.ts +4 -1
- package/src/client/feed-video-player.ts +62 -2
- package/src/client/footnote-rail.ts +478 -0
- package/src/client/hydrate-partial.ts +2 -0
- package/src/client/media-lightbox-events.ts +63 -0
- package/src/client/site-header-fragment.ts +38 -0
- package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
- package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +727 -4
- package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
- package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
- package/src/client/tiptap/bubble-menu.ts +88 -10
- package/src/client/tiptap/continuous-lists.ts +102 -0
- package/src/client/tiptap/create-editor.ts +3 -0
- package/src/client/tiptap/extensions.ts +14 -5
- package/src/client/tiptap/markdown-clipboard.ts +608 -11
- package/src/client/tiptap/slash-commands.ts +8 -1
- package/src/client/tiptap/structural-keymap.ts +211 -0
- package/src/client/tiptap/tab-indent.ts +8 -22
- package/src/client/tiptap/table-control-labels.ts +30 -0
- package/src/client/tiptap/table-controls.ts +537 -0
- package/src/client/tiptap/table-size-picker.ts +254 -0
- package/src/client/toast.ts +8 -6
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -0
- package/src/db/__tests__/d1-query.test.ts +43 -1
- package/src/db/__tests__/migration-runner.test.ts +63 -0
- package/src/db/__tests__/migrations.test.ts +469 -0
- package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
- package/src/db/migrations/0027_old_blue_blade.sql +39 -0
- package/src/db/migrations/0028_superb_post.sql +2 -0
- package/src/db/migrations/0029_boring_magma.sql +1 -0
- package/src/db/migrations/0030_typical_vivisector.sql +50 -0
- package/src/db/migrations/meta/0027_snapshot.json +2511 -0
- package/src/db/migrations/meta/0028_snapshot.json +2511 -0
- package/src/db/migrations/meta/0029_snapshot.json +2519 -0
- package/src/db/migrations/meta/0030_snapshot.json +2541 -0
- package/src/db/migrations/meta/_journal.json +28 -0
- package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
- package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
- package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
- package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
- package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
- package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
- package/src/db/migrations/pg/meta/_journal.json +28 -0
- package/src/db/pg/schema.ts +28 -20
- package/src/db/rehearsal-fixtures/demo-current.json +39 -4
- package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
- package/src/db/schema.ts +28 -20
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +133 -30
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +128 -25
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +128 -25
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +513 -10
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +515 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +515 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/api-settings.test.ts +299 -0
- package/src/lib/__tests__/constants.test.ts +5 -0
- package/src/lib/__tests__/feed-policy.test.ts +25 -0
- package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +32 -4
- package/src/lib/__tests__/markdown.test.ts +8 -6
- package/src/lib/__tests__/navigation.test.ts +59 -2
- package/src/lib/__tests__/post-body-html.test.ts +176 -0
- package/src/lib/__tests__/post-display.test.ts +64 -0
- package/src/lib/__tests__/resolve-config.test.ts +113 -1
- package/src/lib/__tests__/summary.test.ts +82 -0
- package/src/lib/__tests__/timeline.test.ts +109 -70
- package/src/lib/__tests__/timezones.test.ts +31 -3
- package/src/lib/__tests__/tiptap-render.test.ts +268 -9
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
- package/src/lib/__tests__/view.test.ts +53 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
- package/src/lib/api-settings.ts +239 -12
- package/src/lib/collection-sort.ts +7 -5
- package/src/lib/constants.ts +2 -0
- package/src/lib/feed-path.ts +25 -0
- package/src/lib/feed-policy.ts +64 -0
- package/src/lib/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +10 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/markdown-manager.ts +188 -5
- package/src/lib/markdown.ts +10 -3
- package/src/lib/navigation.ts +16 -3
- package/src/lib/post-body-html.ts +131 -0
- package/src/lib/post-display.ts +7 -1
- package/src/lib/render.tsx +9 -2
- package/src/lib/resolve-config.ts +100 -16
- package/src/lib/schemas.ts +133 -1
- package/src/lib/settings-paths.ts +5 -0
- package/src/lib/site-skill.ts +78 -0
- package/src/lib/summary.ts +16 -5
- package/src/lib/timeline.ts +98 -122
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +355 -81
- package/src/lib/view.ts +30 -20
- package/src/lib/worker-response-cache.ts +11 -18
- package/src/middleware/__tests__/public-content-access.test.ts +91 -0
- package/src/middleware/config.ts +2 -2
- package/src/middleware/public-content-access.ts +87 -0
- package/src/node/__tests__/cli-export.test.ts +14 -0
- package/src/node/__tests__/cli-migrate.test.ts +6 -0
- package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
- package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
- package/src/node/index.ts +2 -1
- package/src/preset.css +16 -2
- package/src/routes/__tests__/compose.test.ts +51 -0
- package/src/routes/__tests__/site-skill.test.ts +112 -0
- package/src/routes/api/__tests__/collections.test.ts +91 -19
- package/src/routes/api/__tests__/mcp.test.ts +65 -1
- package/src/routes/api/__tests__/nav-items.test.ts +91 -0
- package/src/routes/api/__tests__/public-access.test.ts +81 -0
- package/src/routes/api/__tests__/settings.test.ts +217 -0
- package/src/routes/api/collections.ts +22 -26
- package/src/routes/api/export.ts +2 -0
- package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
- package/src/routes/api/internal/post-body-html.ts +43 -0
- package/src/routes/api/internal/sites.ts +24 -0
- package/src/routes/api/nav-items.ts +24 -1
- package/src/routes/api/posts.ts +2 -2
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +208 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +41 -24
- package/src/routes/api/search.ts +3 -0
- package/src/routes/api/settings.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +7 -4
- package/src/routes/compose.tsx +2 -1
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +92 -3
- package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
- package/src/routes/pages/__tests__/collections.test.ts +9 -9
- package/src/routes/pages/__tests__/preview.test.ts +174 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +27 -7
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +83 -11
- package/src/routes/site-skill.ts +18 -0
- package/src/services/__tests__/auth.test.ts +7 -5
- package/src/services/__tests__/collection.test.ts +159 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +109 -30
- package/src/services/__tests__/post.test.ts +567 -5
- package/src/services/__tests__/search.test.ts +48 -1
- package/src/services/__tests__/settings.test.ts +8 -0
- package/src/services/__tests__/site-admin.test.ts +121 -1
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +3 -2
- package/src/services/collection.ts +226 -167
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +46 -25
- package/src/services/export-theme/layouts/_default/list.html +18 -4
- package/src/services/export-theme/layouts/_default/rss.xml +8 -12
- package/src/services/export-theme/layouts/featured/list.html +5 -4
- package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
- package/src/services/export-theme/styles/main.css +30 -2
- package/src/services/export.ts +91 -80
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/post.ts +874 -271
- package/src/services/search.ts +8 -2
- package/src/services/settings.ts +2 -10
- package/src/services/site-admin.ts +63 -43
- package/src/style-cjk-jp.css +1 -1
- package/src/style-cjk-kr.css +1 -1
- package/src/style-cjk-tc.css +1 -1
- package/src/style-cjk.css +1 -1
- package/src/styles/components.css +540 -0
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +14 -9
- package/src/styles/ui.css +829 -29
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +243 -12
- package/src/types/constants.ts +22 -1
- package/src/types/entities.ts +9 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +8 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +16 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +92 -2
- package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
- package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
- package/src/ui/dash/settings/GeneralContent.tsx +15 -5
- package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
- package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
- package/src/ui/feed/LinkCard.tsx +2 -12
- package/src/ui/feed/NoteCard.tsx +10 -20
- package/src/ui/feed/QuoteCard.tsx +2 -12
- package/src/ui/feed/ThreadPreview.tsx +26 -34
- package/src/ui/feed/TimelineFeed.tsx +2 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
- package/src/ui/feed/__tests__/timeline-cards.test.ts +178 -3
- package/src/ui/feed/thread-preview-state.ts +5 -7
- package/src/ui/font-themes.ts +74 -26
- package/src/ui/layouts/BaseLayout.tsx +22 -8
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
- package/src/ui/pages/CollectionPage.tsx +101 -22
- package/src/ui/pages/CollectionsPage.tsx +3 -1
- package/src/ui/pages/PostPage.tsx +39 -12
- package/src/ui/pages/SearchPage.tsx +6 -14
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +7 -0
- package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -9
- package/src/ui/shared/CollectionsManager.tsx +17 -7
- package/src/ui/shared/DraftPreviewBar.tsx +33 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +3 -1
- package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
- package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
- package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
- package/src/ui/shared/collection-management-labels.ts +33 -2
- package/src/ui/shared/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +46 -0
- package/dist/app-DZDlmh7C.js +0 -6
- package/dist/client/_assets/client-Cevfpm8H.css +0 -2
- package/dist/client/_assets/client-auth-v3yvhbgZ.js +0 -4736
- package/src/client/tiptap/wrapping-input-rules.ts +0 -102
|
@@ -15,19 +15,56 @@ vi.mock("sortablejs", () => ({
|
|
|
15
15
|
},
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
|
+
vi.mock("../../lazy-slugify.js", () => ({
|
|
19
|
+
slugify: (text: string) =>
|
|
20
|
+
Promise.resolve(
|
|
21
|
+
text
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.trim()
|
|
24
|
+
.replace(/[^\w\s-]/g, "")
|
|
25
|
+
.replace(/[\s_-]+/g, "-")
|
|
26
|
+
.replace(/^-+|-+$/g, ""),
|
|
27
|
+
),
|
|
28
|
+
}));
|
|
29
|
+
|
|
18
30
|
import type {
|
|
19
31
|
NavManagerItem,
|
|
20
32
|
NavManagerLabels,
|
|
21
33
|
NavManagerSuggestedLink,
|
|
22
34
|
} from "../nav-manager-types.js";
|
|
35
|
+
import type { CollectionFormLabels } from "../collection-types.js";
|
|
23
36
|
import "../jant-nav-manager.js";
|
|
24
37
|
import type { JantNavManager } from "../jant-nav-manager.js";
|
|
25
38
|
|
|
39
|
+
const collectionFormLabels: CollectionFormLabels = {
|
|
40
|
+
titleLabel: "Title",
|
|
41
|
+
titlePlaceholder: "My Collection",
|
|
42
|
+
slugLabel: "Collection link",
|
|
43
|
+
slugHelp: "This is the last part of the collection link.",
|
|
44
|
+
slugInvalidHelp: "Use lowercase letters, numbers, and hyphens only.",
|
|
45
|
+
slugReservedHelp: "This link is reserved. Choose something else.",
|
|
46
|
+
slugTooLongHelp: "Keep this link under 200 characters.",
|
|
47
|
+
editSlugLabel: "Edit link",
|
|
48
|
+
resetSlugLabel: "Reset link",
|
|
49
|
+
quickHint: "More options are available after you create it.",
|
|
50
|
+
quickSubmitLabel: "Create Collection",
|
|
51
|
+
createdLabel: "Collection created.",
|
|
52
|
+
descriptionLabel: "Description",
|
|
53
|
+
descriptionPlaceholder: "What's this collection about?",
|
|
54
|
+
sortOrderLabel: "Sort Order",
|
|
55
|
+
sortNewest: "Newest first",
|
|
56
|
+
sortOldest: "Oldest first",
|
|
57
|
+
sortRatingDesc: "Highest rated",
|
|
58
|
+
submitLabel: "Save",
|
|
59
|
+
cancelLabel: "Cancel",
|
|
60
|
+
};
|
|
61
|
+
|
|
26
62
|
const labels: NavManagerLabels = {
|
|
27
63
|
preview: "Preview",
|
|
28
64
|
navigationItems: "Navigation items",
|
|
29
65
|
emptyState: "Add a link to get started.",
|
|
30
66
|
link: "Link",
|
|
67
|
+
page: "Page",
|
|
31
68
|
system: "System",
|
|
32
69
|
toggleEdit: "Toggle edit",
|
|
33
70
|
label: "Label",
|
|
@@ -36,6 +73,8 @@ const labels: NavManagerLabels = {
|
|
|
36
73
|
delete: "Delete",
|
|
37
74
|
remove: "Remove",
|
|
38
75
|
confirmDeleteLink: "Delete this navigation link?",
|
|
76
|
+
confirmDeletePage:
|
|
77
|
+
"Remove this page from navigation? The page itself won't be deleted.",
|
|
39
78
|
orderSaved: "Navigation order updated.",
|
|
40
79
|
labelRequired: "Add a label.",
|
|
41
80
|
saveFailed: "Save failed.",
|
|
@@ -51,6 +90,36 @@ const labels: NavManagerLabels = {
|
|
|
51
90
|
suggestedLinksDescription: "Add common destinations.",
|
|
52
91
|
addSuggestedLink: "Add",
|
|
53
92
|
suggestedLinkAdded: "Link added to navigation.",
|
|
93
|
+
addPageToNavigation: "Add page to navigation",
|
|
94
|
+
addPageDescription:
|
|
95
|
+
"Choose a titled note that isn't already in navigation, or create a new page.",
|
|
96
|
+
addPage: "Add Page",
|
|
97
|
+
searchPages: "Search pages",
|
|
98
|
+
recentPages: "Recently updated",
|
|
99
|
+
searchingPages: "Searching pages…",
|
|
100
|
+
noMatchingPages: "No matching pages available to add.",
|
|
101
|
+
noPages: "No pages available.",
|
|
102
|
+
pageSearchFailed: "Couldn't load pages.",
|
|
103
|
+
createNewPage: "Create new page",
|
|
104
|
+
createPage: "Create Page",
|
|
105
|
+
createPageDescription: "Create a public page that won't appear in Latest.",
|
|
106
|
+
pageTitle: "Title",
|
|
107
|
+
pageAddress: "Page address",
|
|
108
|
+
pageVisibilityHint: "The page is public but stays out of Latest.",
|
|
109
|
+
titleRequired: "Enter a page title.",
|
|
110
|
+
slugInvalid: "Use lowercase letters, numbers, and hyphens.",
|
|
111
|
+
slugReserved: "That address is reserved.",
|
|
112
|
+
slugTooLong: "Keep the page address under 200 characters.",
|
|
113
|
+
slugUnavailable: "That address is already in use.",
|
|
114
|
+
checkingAddress: "Checking address…",
|
|
115
|
+
creatingPage: "Creating page…",
|
|
116
|
+
createPageFailed: "Couldn't create the page.",
|
|
117
|
+
pageCreated: "Page created.",
|
|
118
|
+
pageCreatedDescription: "Add it to navigation or open the editor.",
|
|
119
|
+
addToNavigation: "Add to Navigation",
|
|
120
|
+
editPage: "Edit Page",
|
|
121
|
+
pageAdded: "Page added to navigation.",
|
|
122
|
+
back: "Back",
|
|
54
123
|
headerSection: "Header",
|
|
55
124
|
moreSection: "More",
|
|
56
125
|
moreEmptyHint: "Move links here to hide them under More.",
|
|
@@ -62,8 +131,16 @@ const labels: NavManagerLabels = {
|
|
|
62
131
|
addCollectionDescription:
|
|
63
132
|
"Pin a collection to your navigation bar. An asterisk (*) appears next to collections updated in the last 48 hours.",
|
|
64
133
|
allCollectionsAdded: "All collections are already in your navigation.",
|
|
65
|
-
noCollections:
|
|
66
|
-
|
|
134
|
+
noCollections: "No collections yet. Create one here to add it to navigation.",
|
|
135
|
+
createNewCollection: "Create new collection",
|
|
136
|
+
createCollection: "Create Collection",
|
|
137
|
+
creatingCollection: "Creating collection…",
|
|
138
|
+
createCollectionFailed: "Couldn't create the collection.",
|
|
139
|
+
collectionCreatedDescription:
|
|
140
|
+
"Add it to navigation now or open the editor to add details.",
|
|
141
|
+
editCollection: "Edit Collection",
|
|
142
|
+
collectionAdded: "Collection added to navigation.",
|
|
143
|
+
collectionFormLabels,
|
|
67
144
|
confirmDeleteCollection:
|
|
68
145
|
"Remove this collection from navigation? The collection itself won't be deleted.",
|
|
69
146
|
};
|
|
@@ -152,6 +229,20 @@ function requireElement<T>(value: T | null, message: string): T {
|
|
|
152
229
|
return value;
|
|
153
230
|
}
|
|
154
231
|
|
|
232
|
+
function findButton(
|
|
233
|
+
root: {
|
|
234
|
+
querySelectorAll<E extends globalThis.Element = globalThis.Element>(
|
|
235
|
+
selectors: string,
|
|
236
|
+
): globalThis.NodeListOf<E>;
|
|
237
|
+
},
|
|
238
|
+
text: string,
|
|
239
|
+
): HTMLButtonElement {
|
|
240
|
+
const button = Array.from(
|
|
241
|
+
root.querySelectorAll<HTMLButtonElement>("button"),
|
|
242
|
+
).find((candidate) => candidate.textContent?.trim().includes(text));
|
|
243
|
+
return requireElement(button ?? null, `expected button containing ${text}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
155
246
|
function getListIds(list: HTMLElement): string[] {
|
|
156
247
|
return Array.from(list.querySelectorAll<HTMLElement>("[data-nav-id]")).map(
|
|
157
248
|
(item) => item.dataset.navId ?? "",
|
|
@@ -221,6 +312,180 @@ describe("JantNavManager", () => {
|
|
|
221
312
|
);
|
|
222
313
|
});
|
|
223
314
|
|
|
315
|
+
it("shows collection actions before custom link actions", async () => {
|
|
316
|
+
const el = await createElement();
|
|
317
|
+
const headings = Array.from(el.querySelectorAll("section > h2")).map(
|
|
318
|
+
(heading) => heading.textContent?.trim(),
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
expect(headings.indexOf(labels.addCollectionToNavigation)).toBeLessThan(
|
|
322
|
+
headings.indexOf(labels.addCustomLinkToNavigation),
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("offers quick collection creation at the bottom of the picker", async () => {
|
|
327
|
+
const el = await createElement();
|
|
328
|
+
el.collections = [
|
|
329
|
+
{ id: "col-reading", title: "Reading", slug: "reading", group: null },
|
|
330
|
+
];
|
|
331
|
+
await el.updateComplete;
|
|
332
|
+
|
|
333
|
+
findButton(el, "Add Collection").click();
|
|
334
|
+
await el.updateComplete;
|
|
335
|
+
const picker = requireElement(
|
|
336
|
+
el.querySelector<HTMLElement>(".collection-picker"),
|
|
337
|
+
"expected collection picker",
|
|
338
|
+
);
|
|
339
|
+
picker.dispatchEvent(
|
|
340
|
+
new KeyboardEvent("keydown", {
|
|
341
|
+
key: "Escape",
|
|
342
|
+
bubbles: true,
|
|
343
|
+
cancelable: true,
|
|
344
|
+
}),
|
|
345
|
+
);
|
|
346
|
+
await el.updateComplete;
|
|
347
|
+
expect(el.querySelector(".collection-picker")).toBeNull();
|
|
348
|
+
|
|
349
|
+
findButton(el, "Add Collection").click();
|
|
350
|
+
await el.updateComplete;
|
|
351
|
+
const createCollectionButton = findButton(
|
|
352
|
+
requireElement(
|
|
353
|
+
el.querySelector<HTMLElement>(".collection-picker"),
|
|
354
|
+
"expected reopened collection picker",
|
|
355
|
+
),
|
|
356
|
+
"Create new collection",
|
|
357
|
+
);
|
|
358
|
+
expect(
|
|
359
|
+
createCollectionButton.querySelectorAll('[aria-hidden="true"]'),
|
|
360
|
+
).toHaveLength(1);
|
|
361
|
+
expect(
|
|
362
|
+
createCollectionButton.querySelector('[aria-hidden="true"]')?.textContent,
|
|
363
|
+
).toBe("+");
|
|
364
|
+
createCollectionButton.click();
|
|
365
|
+
await flushAsyncWork();
|
|
366
|
+
await el.updateComplete;
|
|
367
|
+
|
|
368
|
+
const dialog = requireElement(
|
|
369
|
+
el.querySelector<HTMLDialogElement>("#nav-collection-dialog"),
|
|
370
|
+
"expected collection dialog",
|
|
371
|
+
);
|
|
372
|
+
expect(dialog.open).toBe(true);
|
|
373
|
+
|
|
374
|
+
dialog.dispatchEvent(
|
|
375
|
+
new KeyboardEvent("keydown", {
|
|
376
|
+
key: "Escape",
|
|
377
|
+
bubbles: true,
|
|
378
|
+
cancelable: true,
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
381
|
+
await el.updateComplete;
|
|
382
|
+
expect(el.querySelector("#nav-collection-dialog")).toBeNull();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("quick-creates a collection and adds it to navigation", async () => {
|
|
386
|
+
const el = await createElement();
|
|
387
|
+
const fetchMock = vi
|
|
388
|
+
.fn()
|
|
389
|
+
.mockImplementation((input: string, init?: globalThis.RequestInit) => {
|
|
390
|
+
if (input === "/api/collections" && init?.method === "POST") {
|
|
391
|
+
return Promise.resolve({
|
|
392
|
+
ok: true,
|
|
393
|
+
status: 201,
|
|
394
|
+
json: async () => ({
|
|
395
|
+
id: "col-books",
|
|
396
|
+
title: "Books",
|
|
397
|
+
slug: "books",
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (input === "/api/nav-items" && init?.method === "POST") {
|
|
402
|
+
return Promise.resolve({
|
|
403
|
+
ok: true,
|
|
404
|
+
status: 201,
|
|
405
|
+
json: async () => ({
|
|
406
|
+
id: "nav-books",
|
|
407
|
+
type: "collection",
|
|
408
|
+
collectionId: "col-books",
|
|
409
|
+
label: "Books",
|
|
410
|
+
url: "/books",
|
|
411
|
+
placement: "header",
|
|
412
|
+
}),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
throw new Error(`Unexpected fetch: ${input}`);
|
|
416
|
+
});
|
|
417
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
418
|
+
|
|
419
|
+
findButton(el, "Create Collection").click();
|
|
420
|
+
await flushAsyncWork();
|
|
421
|
+
await el.updateComplete;
|
|
422
|
+
|
|
423
|
+
const dialog = requireElement(
|
|
424
|
+
el.querySelector<HTMLDialogElement>("#nav-collection-dialog"),
|
|
425
|
+
"expected collection dialog",
|
|
426
|
+
);
|
|
427
|
+
const titleInput = requireElement(
|
|
428
|
+
dialog.querySelector<HTMLInputElement>("[data-collection-title-input]"),
|
|
429
|
+
"expected collection title input",
|
|
430
|
+
);
|
|
431
|
+
titleInput.value = "Books";
|
|
432
|
+
titleInput.dispatchEvent(new Event("input", { bubbles: true }));
|
|
433
|
+
await flushAsyncWork();
|
|
434
|
+
await el.updateComplete;
|
|
435
|
+
|
|
436
|
+
const createButton = requireElement(
|
|
437
|
+
dialog.querySelector<HTMLButtonElement>("footer .btn"),
|
|
438
|
+
"expected create collection button",
|
|
439
|
+
);
|
|
440
|
+
createButton.click();
|
|
441
|
+
await flushAsyncWork();
|
|
442
|
+
await el.updateComplete;
|
|
443
|
+
|
|
444
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
445
|
+
"/api/collections",
|
|
446
|
+
expect.objectContaining({
|
|
447
|
+
method: "POST",
|
|
448
|
+
body: JSON.stringify({ title: "Books", slug: "books" }),
|
|
449
|
+
}),
|
|
450
|
+
);
|
|
451
|
+
const editLink = requireElement(
|
|
452
|
+
el.querySelector<HTMLAnchorElement>(
|
|
453
|
+
'a[href="/collections/books/edit?returnTo=%2Fsettings%2Fnavigation"]',
|
|
454
|
+
),
|
|
455
|
+
"expected collection edit link",
|
|
456
|
+
);
|
|
457
|
+
expect(editLink.target).toBe("_blank");
|
|
458
|
+
expect(editLink.rel).toContain("noopener");
|
|
459
|
+
expect(el.collections).toContainEqual({
|
|
460
|
+
id: "col-books",
|
|
461
|
+
title: "Books",
|
|
462
|
+
slug: "books",
|
|
463
|
+
group: null,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
findButton(dialog, "Add to Navigation").click();
|
|
467
|
+
await flushAsyncWork();
|
|
468
|
+
await el.updateComplete;
|
|
469
|
+
|
|
470
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
471
|
+
"/api/nav-items",
|
|
472
|
+
expect.objectContaining({
|
|
473
|
+
method: "POST",
|
|
474
|
+
body: JSON.stringify({
|
|
475
|
+
type: "collection",
|
|
476
|
+
collectionId: "col-books",
|
|
477
|
+
placement: "header",
|
|
478
|
+
}),
|
|
479
|
+
}),
|
|
480
|
+
);
|
|
481
|
+
expect(el.querySelector("#nav-collection-dialog")).toBeNull();
|
|
482
|
+
expect(
|
|
483
|
+
getListIds(
|
|
484
|
+
requireElement(el.querySelector("#nav-items-header"), "header list"),
|
|
485
|
+
),
|
|
486
|
+
).toEqual(["nav-1", "nav-2", "nav-books"]);
|
|
487
|
+
});
|
|
488
|
+
|
|
224
489
|
it("reconciles a cross-list drag after Sortable mutates the DOM", async () => {
|
|
225
490
|
const el = await createElement();
|
|
226
491
|
const headerList = requireElement(
|
|
@@ -321,6 +586,31 @@ describe("JantNavManager", () => {
|
|
|
321
586
|
expect(popover.getAttribute("aria-hidden")).toBe("true");
|
|
322
587
|
});
|
|
323
588
|
|
|
589
|
+
it("hides a saved RSS item from the preview without removing its editor", async () => {
|
|
590
|
+
const el = await createElement();
|
|
591
|
+
el.items = [
|
|
592
|
+
...items,
|
|
593
|
+
{
|
|
594
|
+
id: "nav-rss",
|
|
595
|
+
type: "system",
|
|
596
|
+
systemKey: "rss",
|
|
597
|
+
label: "Feed",
|
|
598
|
+
displayLabel: "Feed",
|
|
599
|
+
url: "/feed",
|
|
600
|
+
placement: "header",
|
|
601
|
+
},
|
|
602
|
+
];
|
|
603
|
+
el.rssFeedsEnabled = false;
|
|
604
|
+
await el.updateComplete;
|
|
605
|
+
|
|
606
|
+
expect(getPreviewHeaderLabels(el)).not.toContain("Feed");
|
|
607
|
+
expect(el.querySelector('[data-nav-id="nav-rss"]')).not.toBeNull();
|
|
608
|
+
|
|
609
|
+
el.rssFeedsEnabled = true;
|
|
610
|
+
await el.updateComplete;
|
|
611
|
+
expect(getPreviewHeaderLabels(el)).toContain("Feed");
|
|
612
|
+
});
|
|
613
|
+
|
|
324
614
|
it("adds a suggested collection link through the nav items API", async () => {
|
|
325
615
|
const el = await createElement();
|
|
326
616
|
el.suggestedLinks = suggestedLinks;
|
|
@@ -484,4 +774,250 @@ describe("JantNavManager", () => {
|
|
|
484
774
|
|
|
485
775
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
486
776
|
});
|
|
777
|
+
|
|
778
|
+
it("searches for an existing page and adds it by post ID", async () => {
|
|
779
|
+
const el = await createElement();
|
|
780
|
+
const page = {
|
|
781
|
+
id: "pst_about",
|
|
782
|
+
title: "About me",
|
|
783
|
+
slug: "about-me",
|
|
784
|
+
updatedAt: 123,
|
|
785
|
+
};
|
|
786
|
+
const fetchMock = vi
|
|
787
|
+
.fn()
|
|
788
|
+
.mockImplementation((input: string, init?: globalThis.RequestInit) => {
|
|
789
|
+
if (input.startsWith("/api/nav-items/pages?")) {
|
|
790
|
+
return Promise.resolve({
|
|
791
|
+
ok: true,
|
|
792
|
+
status: 200,
|
|
793
|
+
json: async () => ({ pages: [page] }),
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
if (input === "/api/nav-items" && init?.method === "POST") {
|
|
797
|
+
return Promise.resolve({
|
|
798
|
+
ok: true,
|
|
799
|
+
status: 201,
|
|
800
|
+
json: async () => ({
|
|
801
|
+
id: "nav-page",
|
|
802
|
+
type: "page",
|
|
803
|
+
postId: page.id,
|
|
804
|
+
label: page.title,
|
|
805
|
+
url: `/${page.slug}`,
|
|
806
|
+
placement: "header",
|
|
807
|
+
}),
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
throw new Error(`Unexpected fetch: ${input}`);
|
|
811
|
+
});
|
|
812
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
813
|
+
|
|
814
|
+
findButton(el, "Add Page").click();
|
|
815
|
+
await flushAsyncWork();
|
|
816
|
+
await el.updateComplete;
|
|
817
|
+
findButton(el, "About me").click();
|
|
818
|
+
await flushAsyncWork();
|
|
819
|
+
await el.updateComplete;
|
|
820
|
+
|
|
821
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
822
|
+
"/api/nav-items",
|
|
823
|
+
expect.objectContaining({
|
|
824
|
+
method: "POST",
|
|
825
|
+
body: JSON.stringify({
|
|
826
|
+
type: "page",
|
|
827
|
+
postId: page.id,
|
|
828
|
+
placement: "header",
|
|
829
|
+
}),
|
|
830
|
+
}),
|
|
831
|
+
);
|
|
832
|
+
expect(
|
|
833
|
+
getListIds(
|
|
834
|
+
requireElement(el.querySelector("#nav-items-header"), "header list"),
|
|
835
|
+
),
|
|
836
|
+
).toEqual(["nav-1", "nav-2", "nav-page"]);
|
|
837
|
+
expect(el.querySelector("#nav-page-dialog")).toBeNull();
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
it("keeps page-list keyboard shortcuts scoped to the search field", async () => {
|
|
841
|
+
const el = await createElement();
|
|
842
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
843
|
+
ok: true,
|
|
844
|
+
status: 200,
|
|
845
|
+
json: async () => ({
|
|
846
|
+
pages: [
|
|
847
|
+
{
|
|
848
|
+
id: "pst_about",
|
|
849
|
+
title: "About me",
|
|
850
|
+
slug: "about-me",
|
|
851
|
+
updatedAt: 123,
|
|
852
|
+
},
|
|
853
|
+
],
|
|
854
|
+
}),
|
|
855
|
+
});
|
|
856
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
857
|
+
|
|
858
|
+
findButton(el, "Add Page").click();
|
|
859
|
+
await flushAsyncWork();
|
|
860
|
+
await el.updateComplete;
|
|
861
|
+
|
|
862
|
+
const createButton = findButton(el, "Create new page");
|
|
863
|
+
createButton.dispatchEvent(
|
|
864
|
+
new KeyboardEvent("keydown", {
|
|
865
|
+
key: "Enter",
|
|
866
|
+
bubbles: true,
|
|
867
|
+
cancelable: true,
|
|
868
|
+
}),
|
|
869
|
+
);
|
|
870
|
+
await flushAsyncWork();
|
|
871
|
+
|
|
872
|
+
expect(
|
|
873
|
+
fetchMock.mock.calls.some(
|
|
874
|
+
([, init]) =>
|
|
875
|
+
(init as globalThis.RequestInit | undefined)?.method === "POST",
|
|
876
|
+
),
|
|
877
|
+
).toBe(false);
|
|
878
|
+
|
|
879
|
+
createButton.click();
|
|
880
|
+
await el.updateComplete;
|
|
881
|
+
expect(el.querySelector("#nav-new-page-title")).not.toBeNull();
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("quick-creates a hidden-from-Latest page and offers edit or add", async () => {
|
|
885
|
+
vi.useFakeTimers();
|
|
886
|
+
try {
|
|
887
|
+
const el = await createElement();
|
|
888
|
+
const fetchMock = vi
|
|
889
|
+
.fn()
|
|
890
|
+
.mockImplementation((input: string, init?: globalThis.RequestInit) => {
|
|
891
|
+
if (input.startsWith("/api/nav-items/pages?")) {
|
|
892
|
+
return Promise.resolve({
|
|
893
|
+
ok: true,
|
|
894
|
+
status: 200,
|
|
895
|
+
json: async () => ({ pages: [] }),
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
if (input.startsWith("/api/posts/slug?mode=suggest")) {
|
|
899
|
+
return Promise.resolve({
|
|
900
|
+
ok: true,
|
|
901
|
+
status: 200,
|
|
902
|
+
json: async () => ({ slug: "about-me" }),
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
if (input === "/api/posts" && init?.method === "POST") {
|
|
906
|
+
return Promise.resolve({
|
|
907
|
+
ok: true,
|
|
908
|
+
status: 201,
|
|
909
|
+
json: async () => ({
|
|
910
|
+
id: "pst_created",
|
|
911
|
+
title: "About me",
|
|
912
|
+
slug: "about-me",
|
|
913
|
+
}),
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
if (input === "/api/nav-items" && init?.method === "POST") {
|
|
917
|
+
return Promise.resolve({
|
|
918
|
+
ok: true,
|
|
919
|
+
status: 201,
|
|
920
|
+
json: async () => ({
|
|
921
|
+
id: "nav-created",
|
|
922
|
+
type: "page",
|
|
923
|
+
postId: "pst_created",
|
|
924
|
+
label: "About me",
|
|
925
|
+
url: "/about-me",
|
|
926
|
+
placement: "header",
|
|
927
|
+
}),
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
throw new Error(`Unexpected fetch: ${input}`);
|
|
931
|
+
});
|
|
932
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
933
|
+
|
|
934
|
+
findButton(el, "Add Page").click();
|
|
935
|
+
await flushAsyncWork();
|
|
936
|
+
await el.updateComplete;
|
|
937
|
+
findButton(el, "Create new page").click();
|
|
938
|
+
await el.updateComplete;
|
|
939
|
+
|
|
940
|
+
const titleInput = requireElement(
|
|
941
|
+
el.querySelector<HTMLInputElement>("#nav-new-page-title"),
|
|
942
|
+
"title input",
|
|
943
|
+
);
|
|
944
|
+
titleInput.value = "About me";
|
|
945
|
+
titleInput.dispatchEvent(new Event("input", { bubbles: true }));
|
|
946
|
+
await vi.advanceTimersByTimeAsync(250);
|
|
947
|
+
await flushAsyncWork();
|
|
948
|
+
await el.updateComplete;
|
|
949
|
+
expect(
|
|
950
|
+
requireElement(
|
|
951
|
+
el.querySelector<HTMLInputElement>("#nav-new-page-slug"),
|
|
952
|
+
"slug input",
|
|
953
|
+
).value,
|
|
954
|
+
).toBe("about-me");
|
|
955
|
+
|
|
956
|
+
findButton(el, "Create Page").click();
|
|
957
|
+
await flushAsyncWork();
|
|
958
|
+
await el.updateComplete;
|
|
959
|
+
|
|
960
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
961
|
+
"/api/posts",
|
|
962
|
+
expect.objectContaining({
|
|
963
|
+
method: "POST",
|
|
964
|
+
body: JSON.stringify({
|
|
965
|
+
format: "note",
|
|
966
|
+
title: "About me",
|
|
967
|
+
slug: "about-me",
|
|
968
|
+
status: "published",
|
|
969
|
+
visibility: "latest_hidden",
|
|
970
|
+
}),
|
|
971
|
+
}),
|
|
972
|
+
);
|
|
973
|
+
expect(
|
|
974
|
+
el.querySelector<HTMLAnchorElement>('a[href="/about-me?edit=1"]')?.rel,
|
|
975
|
+
).toContain("noopener");
|
|
976
|
+
|
|
977
|
+
findButton(el, "Add to Navigation").click();
|
|
978
|
+
await flushAsyncWork();
|
|
979
|
+
await el.updateComplete;
|
|
980
|
+
expect(el.querySelector("#nav-page-dialog")).toBeNull();
|
|
981
|
+
expect(
|
|
982
|
+
getListIds(
|
|
983
|
+
requireElement(el.querySelector("#nav-items-header"), "header list"),
|
|
984
|
+
),
|
|
985
|
+
).toEqual(["nav-1", "nav-2", "nav-created"]);
|
|
986
|
+
} finally {
|
|
987
|
+
vi.useRealTimers();
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
it("renders page items with label and page editing", async () => {
|
|
992
|
+
const el = await createElement();
|
|
993
|
+
el.items = [
|
|
994
|
+
{
|
|
995
|
+
id: "nav-page",
|
|
996
|
+
type: "page",
|
|
997
|
+
postId: "pst_page",
|
|
998
|
+
label: "About",
|
|
999
|
+
url: "/about",
|
|
1000
|
+
placement: "header",
|
|
1001
|
+
},
|
|
1002
|
+
];
|
|
1003
|
+
await el.updateComplete;
|
|
1004
|
+
|
|
1005
|
+
requireElement(
|
|
1006
|
+
el.querySelector<HTMLElement>(".nav-item-info"),
|
|
1007
|
+
"page row",
|
|
1008
|
+
).click();
|
|
1009
|
+
await el.updateComplete;
|
|
1010
|
+
|
|
1011
|
+
expect(el.querySelector(".badge-secondary")?.textContent).toContain("Page");
|
|
1012
|
+
expect(el.querySelectorAll(".nav-item-edit input")).toHaveLength(1);
|
|
1013
|
+
expect(
|
|
1014
|
+
el.querySelector(".nav-item-edit input")?.getAttribute("maxlength"),
|
|
1015
|
+
).toBe("100");
|
|
1016
|
+
const editPageLink = requireElement(
|
|
1017
|
+
el.querySelector<HTMLAnchorElement>('a[href="/about?edit=1"]'),
|
|
1018
|
+
"edit page link",
|
|
1019
|
+
);
|
|
1020
|
+
expect(editPageLink.target).toBe("_blank");
|
|
1021
|
+
expect(editPageLink.rel).toContain("noopener");
|
|
1022
|
+
});
|
|
487
1023
|
});
|
|
@@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach } from "vitest";
|
|
|
4
4
|
import type {
|
|
5
5
|
PostFormLabels,
|
|
6
6
|
PostFormInitial,
|
|
7
|
-
|
|
7
|
+
ThreadCollectionOption,
|
|
8
8
|
PostMediaItem,
|
|
9
9
|
PostSubmitDetail,
|
|
10
10
|
} from "../post-form-types.js";
|
|
@@ -64,7 +64,7 @@ const initial: PostFormInitial = {
|
|
|
64
64
|
mediaIds: [],
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const collections:
|
|
67
|
+
const collections: ThreadCollectionOption[] = [
|
|
68
68
|
{ id: 1, title: "General" },
|
|
69
69
|
{ id: 2, title: "Favorites" },
|
|
70
70
|
];
|