Skip to content

Commit 4bed5a8

Browse files
authored
[Appdir] Sitemap for new setup (#3779)
* :WIP: Sitemap * Move manual page mapping * 🎉 use global routing config for sidebar generation * 🔥 Remove darkside styling from `Sidebar.group.tsx` and `Sidebar.module.css` * ✨ Add unique/hardcoded pages to sitmap * 🔥 Removed commented code * 🐛 Stricter typing with 'as const' broke typechecking
1 parent 76e325a commit 4bed5a8

File tree

11 files changed

+268
-81
lines changed

11 files changed

+268
-81
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import "server-only";
2+
import { sanityFetch } from "@/app/_sanity/live";
3+
import {
4+
DESIGNSYSTEM_GRUNNLEGGENDE_LANDINGPAGE_QUERY,
5+
DESIGNSYSTEM_KOMPONENTER_LANDINGPAGE_QUERY,
6+
DESIGNSYSTEM_TEMPLATES_LANDINGPAGE_QUERY,
7+
GOD_PRAKSIS_ALL_TEMA_QUERY,
8+
SITEMAP_ARTICLES_BY_TYPE_QUERY,
9+
SITEMAP_LANDINGPAGES_QUERY,
10+
} from "@/app/_sanity/queries";
11+
import { PAGE_ROUTES } from "@/app/routing-config";
12+
import { allArticleDocuments } from "@/sanity/config";
13+
14+
async function fetchAllSanityPages(): Promise<
15+
{
16+
slug: string;
17+
lastMod: string | null;
18+
}[]
19+
> {
20+
const [
21+
{ data: landingPageData },
22+
{ data: articleListData },
23+
{ data: temaListData },
24+
{ data: dsKomponenterData },
25+
{ data: dsGrunnleggendeData },
26+
{ data: dsTemplatesData },
27+
] = await Promise.all([
28+
sanityFetch({
29+
query: SITEMAP_LANDINGPAGES_QUERY,
30+
stega: false,
31+
perspective: "published",
32+
}),
33+
sanityFetch({
34+
query: SITEMAP_ARTICLES_BY_TYPE_QUERY,
35+
params: {
36+
doctypes: allArticleDocuments,
37+
},
38+
stega: false,
39+
perspective: "published",
40+
}),
41+
sanityFetch({
42+
query: GOD_PRAKSIS_ALL_TEMA_QUERY,
43+
stega: false,
44+
perspective: "published",
45+
}),
46+
sanityFetch({
47+
query: DESIGNSYSTEM_KOMPONENTER_LANDINGPAGE_QUERY,
48+
stega: false,
49+
perspective: "published",
50+
}),
51+
sanityFetch({
52+
query: DESIGNSYSTEM_GRUNNLEGGENDE_LANDINGPAGE_QUERY,
53+
stega: false,
54+
perspective: "published",
55+
}),
56+
sanityFetch({
57+
query: DESIGNSYSTEM_TEMPLATES_LANDINGPAGE_QUERY,
58+
stega: false,
59+
perspective: "published",
60+
}),
61+
]);
62+
63+
const paths = [
64+
{ slug: "", lastMod: landingPageData.frontpage },
65+
{ slug: "/god-praksis", lastMod: landingPageData.godpraksis },
66+
{ slug: "/produktbloggen", lastMod: landingPageData.blogg },
67+
{ slug: "/designsystemet", lastMod: new Date().toISOString() },
68+
...articleListData.map((page) => {
69+
return { slug: `/${page.slug}`, lastMod: page._updatedAt };
70+
}),
71+
...temaListData
72+
.filter((tema) => tema.articles.length > 0)
73+
.map((page) => {
74+
return { slug: `/god-praksis/${page.slug}`, lastMod: page._updatedAt };
75+
}),
76+
];
77+
78+
dsKomponenterData?.overview_pages?.forEach((overviewPage) => {
79+
paths.push({ slug: `/komponenter/${overviewPage}`, lastMod: null });
80+
});
81+
82+
dsGrunnleggendeData?.overview_pages?.forEach((overviewPage) => {
83+
paths.push({ slug: `/grunnleggende/${overviewPage}`, lastMod: null });
84+
});
85+
86+
dsTemplatesData?.overview_pages?.forEach((overviewPage) => {
87+
paths.push({ slug: `/monster-maler/${overviewPage}`, lastMod: null });
88+
});
89+
90+
Object.values(PAGE_ROUTES).forEach((category) => {
91+
category.root.forEach((page) =>
92+
paths.push({ slug: `/${page.slug}`, lastMod: null }),
93+
);
94+
95+
if (category?.nested) {
96+
Object.values(category.nested).forEach((nestedCategory) => {
97+
nestedCategory.forEach((page) =>
98+
paths.push({ slug: `/${page.slug}`, lastMod: null }),
99+
);
100+
});
101+
}
102+
});
103+
104+
return paths;
105+
}
106+
107+
export { fetchAllSanityPages };

aksel.nav.no/website/app/_sanity/queries.ts

+19
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ const SLUG_BY_TYPE_QUERY = defineQuery(`
157157
const GOD_PRAKSIS_ALL_TEMA_QUERY =
158158
defineQuery(`*[_type == "gp.tema"] | order(lower(title)){
159159
title,
160+
_updatedAt,
160161
description,
161162
pictogram,
162163
"slug": slug.current,
@@ -262,6 +263,22 @@ const DOCUMENT_BY_ID_FOR_SLACK_QUERY = defineQuery(`*[_id == $id][0]{
262263
"contacts": undertema[]->tema->contacts[]->email
263264
}`);
264265

266+
/* --------------------------------- Sitemap -------------------------------- */
267+
const SITEMAP_LANDINGPAGES_QUERY = defineQuery(`
268+
{
269+
"frontpage": *[_type == "aksel_forside"][0]._updatedAt,
270+
"godpraksis": *[_type == "godpraksis_landingsside"][0]._updatedAt,
271+
"blogg": *[_type == "blogg_landingsside"][0]._updatedAt,
272+
}
273+
`);
274+
275+
const SITEMAP_ARTICLES_BY_TYPE_QUERY = defineQuery(`
276+
*[_type in $doctypes]{
277+
"slug": slug.current,
278+
_updatedAt
279+
}
280+
`);
281+
265282
/* --------------------------------- Exports -------------------------------- */
266283
export {
267284
DESIGNSYSTEM_SIDEBAR_QUERY,
@@ -288,4 +305,6 @@ export {
288305
DOCUMENT_BY_ID_FOR_SLACK_QUERY,
289306
SIDE_ARTICLE_BY_SLUG_QUERY,
290307
PRINSIPPER_BY_SLUG_QUERY,
308+
SITEMAP_LANDINGPAGES_QUERY,
309+
SITEMAP_ARTICLES_BY_TYPE_QUERY,
291310
};

aksel.nav.no/website/app/_sanity/query-types.ts

+24-2
Large diffs are not rendered by default.

aksel.nav.no/website/app/dev/(designsystemet)/_ui/sidebar/Sidebar.group.tsx

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { stegaClean } from "next-sanity";
21
import { useId } from "react";
32
import { BodyShort, Detail } from "@navikt/ds-react";
43
import { DesignsystemSidebarSectionT } from "@/types";
@@ -16,16 +15,10 @@ function DesignsystemSidebarGroup(props: DesignsystemSidebarGroupT) {
1615
const { label, links, layout } = props;
1716
const id = useId();
1817

19-
const isDarkside = stegaClean(label.toLowerCase()) === "darkside";
20-
2118
const LabelComponent = layout === "sidebar" ? Detail : BodyShort;
2219

2320
return (
24-
<li
25-
className={styles.navListGroup}
26-
data-type={isDarkside ? "darkside" : "neutral"}
27-
data-layout={layout}
28-
>
21+
<li className={styles.navListGroup} data-layout={layout}>
2922
<LabelComponent
3023
as="div"
3124
className={styles.navListGroupLabel}

aksel.nav.no/website/app/dev/(designsystemet)/_ui/sidebar/Sidebar.module.css

-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,6 @@
4242

4343
.navListGroup {
4444
border-radius: var(--ax-border-radius-medium);
45-
46-
&[data-type="darkside"] {
47-
background-color: var(--ax-bg-brand-magenta-moderate);
48-
}
4945
}
5046

5147
.navListGroupLabel {

aksel.nav.no/website/app/dev/(designsystemet)/_ui/sidebar/Sidebar.subnav.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ function DesignsystemSidebarSubNav(
1616
const { pages, title, layout } = props;
1717
const pathName = usePathname();
1818

19+
const isDarkside = title.toLowerCase() === "darkside";
20+
1921
const isSectionActive = pages.some((page) => {
2022
return pathName?.split("#")[0] === stegaClean(`/${page.slug}`);
2123
});
2224

2325
const [open, setOpen] = useState(isSectionActive);
2426

2527
return (
26-
<li>
28+
<li data-type={isDarkside ? "darkside" : "neutral"}>
2729
<button
2830
onClick={() => {
2931
setOpen(!open);

aksel.nav.no/website/app/dev/(designsystemet)/_ui/sidebar/Sidebar.util.ts

+18-62
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,32 @@ import {
44
DESIGNSYSTEM_OVERVIEW_PAGES_QUERYResult,
55
DESIGNSYSTEM_SIDEBAR_QUERYResult,
66
} from "@/app/_sanity/query-types";
7+
import { PAGE_ROUTES } from "@/app/routing-config";
78
import { sanityCategoryLookup } from "@/sanity/config";
89
import { DesignsystemSidebarSectionT, SidebarPageT } from "@/types";
910

10-
/**
11-
* TODO: We currently do not support sorting "unique" pages
12-
*/
13-
const pageTypes = {
14-
grunnleggende: {
15-
name: "Grunnleggende",
16-
_type: "ds_artikkel",
17-
uniquePages: [],
18-
},
19-
komponenter: {
20-
name: "Komponenter",
21-
_type: "komponent_artikkel",
22-
uniquePages: [
23-
{
24-
heading: "Ikoner",
25-
slug: "komponenter/ikoner",
26-
tag: "ready",
27-
},
28-
],
29-
},
30-
templates: {
31-
name: "Mønster og Maler",
32-
_type: "templates_artikkel",
33-
uniquePages: [],
34-
},
35-
} as const;
36-
3711
type DesignsystemSidebarDataT = {
3812
label: string;
3913
links: DesignsystemSidebarSectionT;
4014
}[];
4115

16+
function typedKeys<T extends object>(obj: T): (keyof T)[] {
17+
return Object.keys(obj) as (keyof T)[];
18+
}
19+
4220
function generateSidebar(
4321
input: DESIGNSYSTEM_SIDEBAR_QUERYResult,
4422
overviewPages: DESIGNSYSTEM_OVERVIEW_PAGES_QUERYResult,
4523
): DesignsystemSidebarDataT {
46-
return Object.keys(pageTypes).map((type) => {
24+
return typedKeys(PAGE_ROUTES).map((type) => {
4725
const overviewPageList = overviewPages.find((page) =>
4826
stegaClean(page._type).includes(type),
4927
)?.overview_pages;
5028

51-
const categories = sanityCategoryLookup(type as keyof typeof pageTypes);
29+
const categories = sanityCategoryLookup(type);
30+
5231
const filteredInput = input.filter(
53-
(doc) => stegaClean(doc._type) === pageTypes[type]._type,
32+
(doc) => stegaClean(doc._type) === PAGE_ROUTES[type]._type,
5433
);
5534

5635
const standalonePages: SidebarPageT[] = filteredInput
@@ -79,7 +58,6 @@ function generateSidebar(
7958
.sort(sortIndex)
8059
.sort(sortDeprecated),
8160
}))
82-
.filter((category) => !(!category.pages || category.pages.length === 0))
8361
.map((category) => {
8462
const hasOverviewPage = overviewPageList?.some(
8563
(page) => stegaClean(category.value) === page,
@@ -99,45 +77,23 @@ function generateSidebar(
9977
});
10078
}
10179

102-
if (type === "grunnleggende" && category.value === "darkside") {
103-
pages.push({
104-
heading: "Tokens darkside",
105-
slug: `${type}/${category.value}/design-tokens`,
106-
tag: "ready",
107-
});
80+
if (
81+
PAGE_ROUTES[type].nested &&
82+
category.value in PAGE_ROUTES[type].nested
83+
) {
84+
pages.push(...PAGE_ROUTES[type].nested[category.value]);
10885
}
10986

11087
return {
11188
...category,
11289
pages,
11390
};
114-
});
115-
116-
// TODO: Remove this when we have published any darkside pages from Sanity, so the entry above is activated
117-
if (
118-
type === "grunnleggende" &&
119-
!groupedPages.some((page) => page.value === "darkside")
120-
) {
121-
groupedPages.push({
122-
title: "Darkside",
123-
value: "darkside",
124-
pages: [
125-
{
126-
heading: "Design tokens",
127-
slug: `${type}/darkside/design-tokens`,
128-
tag: "ready",
129-
},
130-
],
131-
});
132-
}
91+
})
92+
.filter((category) => category.pages.length > 0);
13393

13494
return {
135-
label: pageTypes[type].name,
136-
links: [
137-
...pageTypes[type].uniquePages,
138-
...standalonePages,
139-
...groupedPages,
140-
],
95+
label: PAGE_ROUTES[type].title,
96+
links: [...PAGE_ROUTES[type].root, ...standalonePages, ...groupedPages],
14197
};
14298
});
14399
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { MetadataRoute } from "next";
2+
import { fetchAllSanityPages } from "@/app/_sanity/live-fetch";
3+
4+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
5+
const pages = await fetchAllSanityPages();
6+
7+
return pages.map((page) => ({
8+
url: `https://www.aksel.nav.no${page.slug}`,
9+
lastModified: page?.lastMod?.split("T")[0] ?? undefined,
10+
}));
11+
}

0 commit comments

Comments
 (0)