Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] 37165e9f38 Version Packages (#2459)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-08 16:24:10 +01:00
KenandHalvor Haugan d19920ca78 [aksel.nav.no] 404 template (#2455)
* ✨ Initial template

* 💄 Automatisk skalert svg

* 🎉 Maler v1

* 📝 Bedre endringslogg

* Update aksel.nav.no/website/pages/templates/404/complete.tsx

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>

* Update aksel.nav.no/website/pages/templates/404/complete.tsx

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>

* 📝 Bedre kommentarer

* 📝 rename stories

* ♻️ Bruker nå page

* 🎨 Oppdatert mal med endringer

* 🎨 Bruker 404-mal på Aksel

* :trucK: Flyttet data-atributt

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2023-11-08 15:23:15 +01:00
Ken 60d3481300 [Primitives] Page-primitive (#2456)
* ✨ Frame-component

* 🏷️ rename to page

* 🎨 Belowfold

* 🎨 Gutters

* 🎨 Fjernet content width

* 🎨 Page-stories

* 📝 Config

* 📝 changeset

* 📝 Bedre props

* ♻️ Ryddet opp i CSS og prop-navn

* ♻️ Tokens for padding-verdier

* ♻️ Bedre background example

* ♻️ Bedre contentblockpadding example

* ♻️ Bedre gutters example

* 🔥 Fjernet dekoratør-story fra chromatic

* 🐛 Riktig page-padding

* 💄 Justert maksbredder

* 📝 kommentarer

* 📝 JSDoc

* 📝 jsdoc update

* 📝 Kommentarer
2023-11-08 12:14:35 +01:00
Halvor Haugan f486937439 ⬆️ Storybook 7.5.3 (#2458) 2023-11-08 09:12:00 +01:00
Halvor Haugan c62058abc1 🚨 eslint: sjekk avhengighetssykluser når i CI (#2430) 2023-11-07 14:29:51 +01:00
Halvor Haugan 84d9b8260f Fiks typo i timeline eksempel desc 2023-11-07 13:56:58 +01:00
Halvor Haugan c108063dcb Justering av eksempler på Timeline (#2454) 2023-11-06 08:55:31 +01:00
Ken 2a8533ad56 [aksel.nav.no] Templates vises nå riktig på mønster/mal-sider (#2450)
* 🐛 Fikset visning av templates

* 🐛 autoGenerateArrayKeys fungerte ikke for nested arrays

* 🐛 Fikset tester
2023-11-03 12:18:38 +01:00
KenandAnders Olsen Sandvik 9b990a2178 [aksel.nav.no] Legg til RSS feed på produktbloggen (#2439) (#2453)
* [aksel.nav.no] Legg til RSS feed på produktbloggen

* Adjust generateRssFeed to accept a path parameter

* 🎉 Oppdatert generering av rss

---------

Co-authored-by: Anders Olsen Sandvik <github@mlsv.no>
2023-11-03 10:26:35 +01:00
github-actions[bot]andgithub-actions[bot] 5fec1f24f4 Version Packages (#2452)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-02 22:03:10 +01:00
Halvor Haugan c27c1cfe26 🐛 Datepicker lukker ikke lenger modalen den er i (#2451) 2023-11-02 21:59:06 +01:00
Ken 5a649d2282 🐛 Kunne ikke se preview av mønster forside (#2449) 2023-11-02 15:11:34 +01:00
Ken baac9cc251 [aksel.nav.no] Sidemaler for Mønster og maler (#2438)
* 🎉 Sanity-schema for mønster og maler

* 🎨 La til mønster og maler i sanity structure

* 🎨 Støtte for templates i søk

* 🎨 Sidemaler for Mønster og Maler

* 🎨 Riktig håndtering av breadcrumbs

* 🐛 Fikset story

* 🐛 Fikset search-indeksering

* 🐛 Fikset search-test

* 🐛 Fikset typo

* [aksel.nav.no] Refaktorert synking av eksempler/templates mellom kode og sanity (#2442)

* 🎨 Template pages

* 🔥 Fjernet templates fra logging

* ♻️ Refaktorert example-generering

* ✅ extractArgs tests

* ✅ filter-code tester

* ✅ Ny code-parsing tester

* 🚚 Flyttet filer

* 🎨 Oppdaterer sanity remote

* 🎨 sanity schema for templates

* 🎨 Schema for metadata in templates

* 🎨 henter nå ut metadata fra templates

* 🎨 Flyttet createId funksjon

* 🚚 rename folder

* 🎨 schema for templates i sanity

* 🐛 Fjernet dryrun fra oppdatering

* 🎨 Make sure to delete old documents after updating

* 🐛 Riktige typer for version-schema

* [aksel.nav.no] Integrert mønster og maler templates inn på aksel (#2447)

* 🎨 Template pages

* 🔥 Fjernet templates fra logging

* ♻️ Refaktorert example-generering

* ✅ extractArgs tests

* ✅ filter-code tester

* ✅ Ny code-parsing tester

* 🚚 Flyttet filer

* 🎨 Oppdaterer sanity remote

* 🎨 sanity schema for templates

* 🎨 Schema for metadata in templates

* 🎨 henter nå ut metadata fra templates

* 🎨 Flyttet createId funksjon

* 🚚 rename folder

* 🎨 schema for templates i sanity

* 🐛 Fjernet dryrun fra oppdatering

* 🎨 Make sure to delete old documents after updating

* 🐛 Riktige typer for version-schema

* 🎨 Kan nå vise eksempler på side

* 🎨 Automatisk visning av changelog

* 📝 typo

* 📝 Rename fra changelog til Endringer

* 🐛 Fikset story-typer

* 🎨 Forsideblokk for templates

* 🐛 feil riktekst ble brukt
2023-11-02 11:47:57 +01:00
Sjur d924d523e4 Update in-the-wild.yml (#2448)
Tok bort "assignees"
2023-11-02 10:38:51 +01:00
Sjur fc5a77081d Create in-the-wild.yml (#2445)
Issue template for å ta imot eksempler fra folk
2023-11-02 10:01:49 +01:00
101 changed files with 30451 additions and 28046 deletions

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
name: Eksempel på bruk
description: Send et eksempel på ting fra Aksel som er brukt i det fri (tokens, komponenter, maler, etc)
title: "[Eksempel på bruk]: "
labels: ["dokumentasjon 📖"]
body:
- type: markdown
attributes:
value: |
Takk for at du bidrar til Aksel!
- type: input
id: title
attributes:
label: Lenke til side
description: Lenke til siden hvor eksemplet er
placeholder:
validations:
required: true
- type: textarea
id: examples
attributes:
label: Forklaring
description: Forklar om det er noe man må gjøre for å se eksemplet, eller hvor på siden det ligger
+2 -1
View File
@@ -151,4 +151,5 @@ playwright-report/
backup-*.tar.gz
sitemap-urls.json
*.tar.gz
searchindex.json
searchindex.json
aksel.nav.no/website/public/rss
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-icons
## 5.10.0
## 5.9.2
## 5.9.1
## 5.9.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "5.9.1",
"version": "5.10.0",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-stylelint
## 5.10.0
## 5.9.2
## 5.9.1
## 5.9.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "5.9.1",
"version": "5.10.0",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
"repository": {
@@ -35,8 +35,8 @@
"dev": "yarn watch:lint"
},
"devDependencies": {
"@navikt/ds-css": "^5.9.1",
"@navikt/ds-tokens": "^5.9.1",
"@navikt/ds-css": "^5.10.0",
"@navikt/ds-tokens": "^5.10.0",
"@types/jest": "^29.0.0",
"concurrently": "7.2.1",
"copyfiles": "2.4.1",
+14
View File
@@ -1,5 +1,19 @@
# @navikt/aksel
## 5.10.0
### Patch Changes
- Updated dependencies [[`60d348130`](https://github.com/navikt/aksel/commit/60d34813009377a1a7d84bed6627c60524849957)]:
- @navikt/ds-css@5.10.0
## 5.9.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.9.2
## 5.9.1
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "5.9.1",
"version": "5.10.0",
"description": "Aksel command line interface. Handles css-imports, codemods and more",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
"dependencies": {
"@navikt/ds-css": "5.9.1",
"@navikt/ds-css": "5.10.0",
"axios": "1.3.6",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+8
View File
@@ -1,5 +1,13 @@
# @navikt/ds-css
## 5.10.0
### Minor Changes
- Primitives: Ny komponent `Page` og `Page.Block` ([#2456](https://github.com/navikt/aksel/pull/2456))
## 5.9.2
## 5.9.1
### Patch Changes
+4
View File
@@ -35,6 +35,10 @@ const StyleMappings = {
component: "Box",
main: primitivesCss,
},
{
component: "Page",
main: primitivesCss,
},
{
component: "HGrid",
main: primitivesCss,
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "5.9.1",
"version": "5.10.0",
"description": "CSS for NAV Designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -27,7 +27,7 @@
"css:get-version": "node config/get-version.js"
},
"devDependencies": {
"@navikt/ds-tokens": "^5.9.1",
"@navikt/ds-tokens": "^5.10.0",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
+1
View File
@@ -1,5 +1,6 @@
@import "box.css";
@import "bleed.css";
@import "page.css";
@import "hgrid.css";
@import "stack.css";
@import "responsive.css";
+50
View File
@@ -0,0 +1,50 @@
.navds-page {
display: flex;
flex-direction: column;
min-height: 100vh;
min-height: 100lvh;
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
background-color: var(--__ac-page-background);
}
/* Page.Block */
.navds-pageblock {
margin-inline: auto;
width: 100%;
}
.navds-page__content--grow {
flex-grow: 1;
}
.navds-page__content--fullheight {
min-height: 100vh;
min-height: 100lvh;
}
.navds-page__content--padding {
padding-block-end: var(--a-spacing-8);
}
.navds-pageblock--lg {
max-width: 1024px;
}
.navds-pageblock--xl {
max-width: 1280px;
}
.navds-pageblock--2xl {
max-width: 1440px;
}
.navds-pageblock--gutters {
padding-inline: var(--a-spacing-4);
}
@media (min-width: 1024px) {
.navds-pageblock--gutters {
padding-inline: var(--a-spacing-12);
}
}
+22
View File
@@ -1,5 +1,27 @@
# @navikt/ds-react
## 5.10.0
### Minor Changes
- Primitives: Ny komponent `Page` og `Page.Block` ([#2456](https://github.com/navikt/aksel/pull/2456))
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@5.10.0
- @navikt/aksel-icons@5.10.0
## 5.9.2
### Patch Changes
- :bug: Datepicker lukker ikke lenger modalen den er i ([#2451](https://github.com/navikt/aksel/pull/2451))
- Updated dependencies []:
- @navikt/ds-tokens@5.9.2
- @navikt/aksel-icons@5.9.2
## 5.9.1
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "5.9.1",
"version": "5.10.0",
"description": "Aksel react-components for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -38,8 +38,8 @@
},
"dependencies": {
"@floating-ui/react": "0.25.4",
"@navikt/aksel-icons": "^5.9.1",
"@navikt/ds-tokens": "^5.9.1",
"@navikt/aksel-icons": "^5.10.0",
"@navikt/ds-tokens": "^5.10.0",
"@radix-ui/react-tabs": "1.0.0",
"@radix-ui/react-toggle-group": "1.0.0",
"clsx": "^1.2.1",
@@ -437,28 +437,36 @@ export const ModalDemo = () => {
toDate: new Date("Feb 23 2024"),
onDateChange: console.log,
});
const [open, setOpen] = useState(true);
return (
<Modal open header={{ heading: "Modal-demo" }}>
<Modal.Body style={{ position: "relative" }}>
<BodyLong spacing>
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
</BodyLong>
<>
<Button onClick={() => setOpen(!open)}>Open modal</Button>
<Modal
open={open}
onClose={() => setOpen(false)}
header={{ heading: "Modal-demo" }}
>
<Modal.Body style={{ position: "relative" }}>
<BodyLong spacing>
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
</BodyLong>
<DatePicker {...datepickerProps} dropdownCaption>
<DatePicker.Input
{...inputProps}
label="Velg dato"
description="Format: dd.mm.yyyy"
/>
</DatePicker>
</Modal.Body>
<Modal.Footer>
<Button>Neste</Button>
<Button variant="secondary">Tilbake</Button>
<Button variant="tertiary">Avbryt</Button>
</Modal.Footer>
</Modal>
<DatePicker {...datepickerProps} dropdownCaption>
<DatePicker.Input
{...inputProps}
label="Velg dato"
description="Format: dd.mm.yyyy"
/>
</DatePicker>
</Modal.Body>
<Modal.Footer>
<Button>Neste</Button>
<Button variant="secondary">Tilbake</Button>
<Button variant="tertiary">Avbryt</Button>
</Modal.Footer>
</Modal>
</>
);
};
ModalDemo.parameters = { chromatic: { pauseAnimationAtEnd: true } };
@@ -57,7 +57,10 @@ export const DateWrapper = ({
<Modal
ref={modalRef}
open={open}
onClose={onClose}
onClose={(event) => {
event.stopPropagation();
onClose();
}}
aria-label={modalLabel(locale, variant)}
className={cl("navds-date__modal", {
"navds-date__nested-modal": isInModal,
+1
View File
@@ -38,3 +38,4 @@ export * from "./layout/stack";
export * from "./layout/grid";
export * from "./layout/content-container";
export * from "./layout/responsive";
export * from "./layout/page";
@@ -0,0 +1,271 @@
import bgColors from "@navikt/ds-tokens/src/colors-bg.json";
import type { Meta, StoryFn } from "@storybook/react";
import React, { useEffect } from "react";
import { Box } from "../box";
import { HGrid } from "../grid";
import Page from "./Page";
import { widths } from "./parts/PageBlock";
const meta: Meta = {
title: "ds-react/Primitives/Page",
component: Page,
parameters: {
layout: "fullscreen",
},
};
export default meta;
export const Default: StoryFn = ({
belowFold,
background,
width,
gutters,
contentBlockPadding,
}) => (
<Page
footer={<Footer width={width} gutters={gutters} />}
footerPosition={belowFold ? "belowFold" : undefined}
background={background}
contentBlockPadding={contentBlockPadding}
>
<Header width={width} gutters={gutters} />
<Content width={width} gutters={gutters} />
</Page>
);
Default.argTypes = {
width: {
control: "radio",
options: widths,
},
background: {
control: "radio",
options: Object.keys(bgColors.a),
},
belowFold: {
control: "boolean",
},
gutters: {
control: "boolean",
},
contentBlockPadding: {
control: "radio",
options: ["end", "none"],
},
};
Default.args = {
belowFold: false,
gutters: false,
contentBlockPadding: "end",
};
export const BelowFold: StoryFn = () => (
<Page
footer={<Footer />}
footerPosition="belowFold"
contentBlockPadding="end"
>
<Header />
<Content />
</Page>
);
export const Background: StoryFn = () => (
<HGrid columns={2} gap="4">
<Page
background="bg-default"
footer={<div>footer</div>}
contentBlockPadding="end"
>
<div>header</div>
<div>content</div>
</Page>
<Page
background="bg-subtle"
footer={<div>footer</div>}
contentBlockPadding="end"
>
<div>header</div>
<div>content</div>
</Page>
</HGrid>
);
export const ContentBlockPadding: StoryFn = () => (
<HGrid columns={2} gap="6" align="start">
<Page
footer={<Footer width="lg" gutters />}
contentBlockPadding="end"
background="bg-subtle"
>
<Header width="lg" gutters />
<Page.Block width="lg" gutters as="main">
<Box background="surface-alt-3-subtle" style={{ height: "80vh" }}>
Main
</Box>
</Page.Block>
</Page>
<Page
footer={<Footer width="lg" gutters />}
contentBlockPadding="none"
background="bg-subtle"
>
<Header width="lg" gutters />
<Page.Block width="lg" gutters as="main">
<Box background="surface-alt-3-subtle" style={{ height: "80vh" }}>
Main
</Box>
</Page.Block>
</Page>
</HGrid>
);
export const Gutters: StoryFn = () => (
<HGrid columns={2} gap="6" align="start">
<Page footer={<Footer width="lg" gutters />} background="bg-subtle">
<Header width="lg" gutters />
<Page.Block width="lg" gutters as="main">
<Box background="surface-alt-3-subtle" style={{ height: "80vh" }}>
Main
</Box>
</Page.Block>
</Page>
<Page footer={<Footer width="lg" />} background="bg-subtle">
<Header width="lg" />
<Page.Block width="lg" as="main">
<Box background="surface-alt-3-subtle" style={{ height: "80vh" }}>
Main
</Box>
</Page.Block>
</Page>
</HGrid>
);
Gutters.parameters = {
chromatic: {
modes: {
mobile_portrait: {
viewport: {
width: 400,
height: 850,
},
},
desktop: {
viewport: {
width: 1280,
height: 960,
},
},
},
},
};
const MILJO_URL = "https://www.nav.no/dekoratoren";
export const WithDecorator: StoryFn = () => {
return (
<Page
contentBlockPadding="end"
footerPosition="belowFold"
footer={<div id="decorator-footer"></div>}
>
<div id="decorator-header"></div>
<Content width="lg" gutters />
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
></div>
</Page>
);
};
WithDecorator.decorators = [
(Story) => {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
return <Story />;
},
];
WithDecorator.parameters = {
chromatic: {
disable: true,
},
};
export const OutsideBackground = () => {
return (
<Page
contentBlockPadding="end"
footer={
<Box
background="surface-alt-3-subtle"
style={{ height: 100 }}
as="footer"
>
<Page.Block width="lg" gutters>
Footer
</Page.Block>
</Box>
}
>
<Box background="surface-alt-1-subtle" style={{ height: 64 }} as="header">
<Page.Block width="lg" gutters>
Header
</Page.Block>
</Box>
<Box background="surface-alt-2-subtle" style={{ height: 300 }} as="main">
<Page.Block width="lg" gutters>
main
</Page.Block>
</Box>
</Page>
);
};
function Header({ width = "lg", gutters = false }: any) {
return (
<Page.Block as="header" width={width} gutters={gutters}>
<Box background="surface-alt-3-subtle" style={{ height: 64 }}>
Header
</Box>
</Page.Block>
);
}
function Content({ width = "lg", gutters = false }: any) {
return (
<Page.Block width={width} gutters={gutters} as="main">
<Box background="surface-alt-3-subtle" style={{ height: 300 }}>
Main
</Box>
</Page.Block>
);
}
function Footer({ width = "lg", gutters = false }: any) {
return (
<Page.Block width={width} gutters={gutters} as="footer">
<Box background="surface-alt-3-subtle" style={{ height: 100 }}>
Footer
</Box>
</Page.Block>
);
}
+115
View File
@@ -0,0 +1,115 @@
import bgColors from "@navikt/ds-tokens/src/colors-bg.json";
import cl from "clsx";
import React, { forwardRef } from "react";
import { OverridableComponent } from "../../util";
import { PageBlock } from "./parts/PageBlock";
export interface PageProps extends React.HTMLAttributes<HTMLElement> {
/**
* Overrides html-tag
* @default "div"
*/
as?: "div" | "body";
/**
* Background color. Accepts a color token.
* @default bg-default
*/
background?: keyof typeof bgColors.a;
/**
* Allows better positioning of footer
*/
footer?: React.ReactNode;
/**
* Places footer below page-fold
*/
footerPosition?: "belowFold";
/**
* Adds a standardised padding of 4rem between content and footer
* @default block-end
*/
contentBlockPadding?: "end" | "none";
}
interface PageComponentType
extends OverridableComponent<PageProps, HTMLElement> {
Block: typeof PageBlock;
}
export const PageComponent: OverridableComponent<PageProps, HTMLElement> =
forwardRef(
(
{
as: Component = "div",
className,
style: _style,
footer,
children,
footerPosition,
background = "bg-default",
contentBlockPadding = "end",
...rest
},
ref
) => {
const style: React.CSSProperties = {
..._style,
"--__ac-page-background": `var(--a-${background})`,
};
const belowFold = footerPosition === "belowFold";
return (
<Component
{...rest}
className={cl("navds-page", className)}
ref={ref}
style={style}
>
<div
className={cl({
"navds-page__content--fullheight": belowFold,
"navds-page__content--grow": !belowFold,
"navds-page__content--padding": contentBlockPadding === "end",
})}
>
{children}
</div>
{footer}
</Component>
);
}
);
/**
* Page helps with establishing a top-level layout for your page
*
* @see [📝 Documentation](https://aksel.nav.no/komponenter/primitives/page)
* @see 🏷️ {@link PageProps}
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*
* @example
* ```jsx
* <Page
* footer={<Page.Block width="xl" gutters />}
* >
* <Page.Block width="xl" gutters />// Header
* <Page.Block width="xl" gutters />// Content
* </Page>
* ```
* @example
* Footer placed below page-fold
* ```jsx
* <Page
* footer={<Page.Block width="xl" gutters />}
* footerPosition="belowFold"
* >
* <Page.Block width="xl" gutters />// Header
* <Page.Block width="xl" gutters />// Content
* </Page>
* ```
*/
const Page = PageComponent as PageComponentType;
Page.Block = PageBlock;
export default Page;
@@ -0,0 +1,2 @@
export { default as Page, type PageProps } from "./Page";
export { type PageBlockProps } from "./parts/PageBlock";
@@ -0,0 +1,72 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { OverridableComponent } from "../../../util/OverridableComponent";
export const widths = ["lg", "xl", "2xl"] as const;
export interface PageBlockProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Predefined max-width
* @example
* lg: 1024px
* xl: 1280px
* 2xl: 1440px
* @default max-width: 100%;
*/
width?: (typeof widths)[number];
/**
* Adds a standardised responsive padding-inline
* @example
* 3rem on > md
* 1rem on < md
* @default false
*/
gutters?: boolean;
}
/**
* Acts as a top-level container for defining max-width, gutters and horizontal centering
*
* @see [📝 Documentation](https://aksel.nav.no/komponenter/primitives/page)
* @see 🏷️ {@link PageBlockProps}
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*
* @example
* ```jsx
* <Page
* footer={<Page.Block width="xl" gutters />}
* >
* <Page.Block width="xl" gutters />// Header
* <Page.Block width="xl" gutters />// Content
* </Page>
* ```
* @example
* With background bleed
* Wrapping `Page.Block` with `Box` allows the background-color to use full screen-width
* ```jsx
* <Page
* footer={<Box background="..."><Page.Block width="xl" gutters /></Box>}
* footerPosition="belowFold"
* >
* <Box background="..."><Page.Block width="xl" gutters /></Box>//Header
* <Box background="..."><Page.Block width="xl" gutters /></Box>//Content
* </Page>
* ```
*/
export const PageBlock: OverridableComponent<PageBlockProps, HTMLDivElement> =
forwardRef(
({ as: Component = "div", gutters, className, width, ...rest }, ref) => {
return (
<Component
{...rest}
className={cl(
"navds-pageblock",
`navds-pageblock--${width}`,
className,
{ "navds-pageblock--gutters": gutters }
)}
ref={ref}
/>
);
}
);
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tailwind
## 5.10.0
## 5.9.2
## 5.9.1
## 5.9.0
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "5.9.1",
"version": "5.10.0",
"description": "Tailwind config based on @navikt/ds-tokens",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -22,7 +22,7 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^5.9.1",
"@navikt/ds-tokens": "^5.10.0",
"@types/jest": "^29.0.0",
"color": "4.2.3",
"jest": "^29.0.0",
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tokens
## 5.10.0
## 5.9.2
## 5.9.1
## 5.9.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "5.9.1",
"version": "5.10.0",
"description": "Design-tokens for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
+275 -259
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -15,11 +15,11 @@
"website"
],
"dependencies": {
"@navikt/aksel-icons": "^5.9.1",
"@navikt/ds-css": "^5.9.1",
"@navikt/ds-react": "^5.9.1",
"@navikt/ds-tailwind": "^5.9.1",
"@navikt/ds-tokens": "^5.9.1"
"@navikt/aksel-icons": "^5.10.0",
"@navikt/ds-css": "^5.10.0",
"@navikt/ds-react": "^5.10.0",
"@navikt/ds-tailwind": "^5.10.0",
"@navikt/ds-tokens": "^5.10.0"
},
"devDependencies": {
"prettier-plugin-tailwindcss": "^0.2.3"
+3 -4
View File
@@ -1,8 +1,7 @@
import type { StorybookConfig } from "@storybook/nextjs";
import { readFileSync } from "fs";
import { join, dirname } from "path";
import { loadCsf } from "@storybook/csf-tools";
import type { StorybookConfig } from "@storybook/nextjs";
import { readFileSync } from "fs";
import { dirname, join } from "path";
/**
* This function is used to resolve the absolute path of a package.
@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { WithSidebar } from "./WithSidebar";
import { SanityBlockContent } from "@/sanity-block";
import { getBlocks } from "@/sb-util";
import type { Meta, StoryObj } from "@storybook/react";
import { WithSidebar } from "./WithSidebar";
const meta = {
title: "Layout/WithSidebar",
@@ -28,8 +28,10 @@ export const KomponenterLanding: Story = {
args: {
children,
pageType: {
type: "Komponenter",
type: "komponenter",
title: "Heading",
rootUrl: "/komponenter",
rootTitle: "Komponenter",
},
pageProps: { content: "1" },
variant: "landingPage",
@@ -65,8 +67,10 @@ export const Komponenter: Story = {
args: {
children,
pageType: {
type: "Komponenter",
type: "komponenter",
title: "Heading",
rootUrl: "/komponenter",
rootTitle: "Komponenter",
},
pageProps: { content: "1" },
variant: "page",
@@ -20,8 +20,10 @@ export const WithSidebar = ({
children: React.ReactNode;
sidebar: AkselSidebarT;
pageType: {
type: "Komponenter" | "Grunnleggende";
type: "komponenter" | "grunnleggende" | "templates";
title: string;
rootUrl: string;
rootTitle: string;
};
pageProps: any;
intro?: React.ReactNode;
@@ -29,7 +31,11 @@ export const WithSidebar = ({
variant?: "page" | "landingPage";
}) => {
return (
<Box background="bg-default" paddingBlock="6 24">
<Box
background="bg-default"
paddingBlock="6 24"
className="min-h-screen-header"
>
<div className="mx-auto flex w-full max-w-screen-2xl gap-6">
<Sidebar kategori={pageType.type} links={sidebar} />
<main
@@ -52,13 +58,9 @@ export const WithSidebar = ({
<div className="z-[1]">
{variant === "page" && pageProps?.kategori && (
<Detail as="div" className="mb-2">
<NextLink
href={`/${pageType.type.toLowerCase()}`}
passHref
legacyBehavior
>
<NextLink href={pageType.rootUrl} passHref legacyBehavior>
<Link className="text-text-default">
{capitalize(pageType.type)}
{pageType.rootTitle}
</Link>
</NextLink>{" "}
/ {capitalize(pageProps.kategori)}
@@ -95,6 +95,7 @@ describe("createSearchResult", () => {
aksel_blogg: 1,
aksel_prinsipp: 1,
aksel_standalone: 1,
templates_artikkel: 0,
};
const actual: SearchResultsT = createSearchResult(result, rawResults);
expect(actual.hits).toEqual(expected);
@@ -29,6 +29,9 @@ export function createSearchResult(
.length,
aksel_blogg: rawResults.filter((x: any) => x.item._type === "aksel_blogg")
.length,
templates_artikkel: rawResults.filter(
(x: any) => x.item._type === "templates_artikkel"
).length,
aksel_prinsipp: rawResults.filter(
(x: any) => x.item._type === "aksel_prinsipp"
).length,
@@ -114,7 +114,7 @@ export const Sidebar = ({
kategori,
links,
}: {
kategori: "Komponenter" | "Grunnleggende";
kategori: "komponenter" | "grunnleggende" | "templates";
links: AkselSidebarT;
}) => {
const sections = useSection({ kategori, links });
@@ -1,23 +1,17 @@
import { AkselSidebarT } from "@/types";
import { useMemo } from "react";
import {
grunnleggendeKategorier,
komponentKategorier,
} from "../../../sanity/config";
import { sanityCategoryLookup } from "../../../sanity/config";
export const useSection = ({
kategori,
links,
}: {
kategori: "Komponenter" | "Grunnleggende";
kategori: "komponenter" | "grunnleggende" | "templates";
links: AkselSidebarT;
}) => {
const sections = useMemo(
() =>
(kategori === "Komponenter"
? komponentKategorier
: grunnleggendeKategorier
)
sanityCategoryLookup(kategori)
.map((x) => ({
...x,
pages: links
@@ -15,8 +15,10 @@ export const Designsystem: Story = {
args: {
node: {
title: "button",
dir: {
title: "button",
variant: "templates",
filer: [
{
_key: getKey(),
@@ -24,6 +26,8 @@ export const Designsystem: Story = {
innhold:
'import { Button } from "@navikt/ds-react";\n\nconst Example = () => {\n return (\n <div className="flex flex-wrap gap-2">\n <Button variant="primary">Primary</Button>\n <Button variant="secondary">Secondary</Button>\n <Button variant="tertiary">Tertiary</Button>\n <Button variant="danger">Danger</Button>\n </div>\n );\n};',
navn: "default",
title: "title",
index: 1,
},
{
_key: getKey(),
@@ -31,6 +35,8 @@ export const Designsystem: Story = {
innhold:
'import { Button } from "@navikt/ds-react";\n\nconst Example = () => {\n return (\n <div className="flex flex-wrap gap-2">\n <Button variant="primary-neutral">Primary</Button>\n <Button variant="secondary-neutral">Secondary</Button>\n <Button variant="tertiary-neutral">Tertiary</Button>\n </div>\n );\n};',
navn: "neutral",
title: "title",
index: 1,
},
{
_key: getKey(),
@@ -38,6 +44,8 @@ export const Designsystem: Story = {
innhold:
'import { PencilIcon } from "@navikt/aksel-icons";\nimport { Button } from "@navikt/ds-react";\n\nconst Example = () => {\n return (\n <div className="flex flex-wrap gap-2">\n <Button size="small" variant="primary" icon={<PencilIcon aria-hidden />}>\n Primary\n </Button>\n <Button size="small" variant="secondary">\n Secondary\n </Button>\n <Button size="small" variant="tertiary">\n Tertiary\n </Button>\n <Button size="small" variant="danger">\n Danger\n </Button>\n </div>\n );\n};',
navn: "small",
title: "title",
index: 1,
},
],
},
@@ -34,7 +34,15 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
iframeId
) as HTMLIFrameElement;
const exampleIframeDOM = exampleIframe?.contentDocument;
const exampleWrapper = exampleIframeDOM?.getElementById("ds-example");
let exampleWrapper: HTMLElement;
if (node.dir.variant === "templates") {
exampleWrapper = exampleIframeDOM?.getElementById("__next");
} else {
exampleWrapper = exampleIframeDOM?.getElementById("ds-example");
}
if (exampleWrapper && exampleWrapper.offsetHeight) {
const newHeight = iframePadding + exampleWrapper.offsetHeight;
clearInterval(waitForExampleContentToRender);
@@ -75,7 +83,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
const fixName = (str: string) =>
capitalize(
str
.replace(/[^\w]|_/g, " ")
.replace(/[^\wæøå]|_/g, " ")
.replace(/\s+/g, " ")
.trim()
) ?? str;
@@ -85,6 +93,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
}
const active = activeExample ?? node?.dir?.filer?.[0]?.navn;
const demoVariant = node.dir?.variant;
return (
<div>
@@ -107,7 +116,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
);
}}
>
{fixName(fil.navn)}
{fixName(fil.title)}
</Chips.Toggle>
);
})}
@@ -138,7 +147,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
)}
>
<iframe
src={`/eksempler/${node.dir.title}/${fil.navn.replace(
src={`/${demoVariant}/${node.dir.title}/${fil.navn.replace(
".tsx",
""
)}`}
@@ -148,7 +157,10 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
aria-label={`${node?.dir?.title} ${fil.navn} eksempel`}
className={cl(
"min-w-80 block w-full max-w-full resize-x bg-white shadow-[20px_0_20px_-20px_rgba(0,0,0,0.22)]",
{ invisible: unloaded }
{
invisible: unloaded,
"p-6": demoVariant === "templates",
}
)}
title="Kode-eksempler"
ref={iframeRef}
@@ -199,10 +211,9 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
target="_blank"
className="si-ignore"
as="a"
href={`/eksempler/${node.dir.title}/${fil.navn.replace(
".tsx",
""
)}`}
href={`/${demoVariant}/${
node.dir.title
}/${fil.navn.replace(".tsx", "")}`}
/>
</HStack>
</HStack>
@@ -43,6 +43,7 @@ const Card = ({ article, visible, index }: CardProps) => {
"ds_artikkel",
"komponent_artikkel",
"aksel_blogg",
"templates_artikkel",
].includes(article._type);
return (
@@ -1,4 +1,4 @@
import { Heading, Label, Tag as DsTag } from "@navikt/ds-react";
import { Tag as DsTag, Heading, Label } from "@navikt/ds-react";
import cl from "clsx";
export const Tag = ({
@@ -65,7 +65,26 @@ export const Tag = ({
<Comp className="text-deepblue-600 flex items-center gap-3">
Grunnleggende
{beta && (
<DsTag variant="alt1" size="small">
<DsTag
variant="alt1"
size="small"
className="border-violet-300 bg-violet-50"
>
Beta
</DsTag>
)}
</Comp>
);
case "templates_artikkel":
return (
<Comp className="text-deepblue-600 flex items-center gap-3">
Mønster og Maler
{beta && (
<DsTag
variant="alt1"
size="small"
className="border-violet-300 bg-violet-50"
>
Beta
</DsTag>
)}
@@ -56,6 +56,12 @@ export function SEO({
</>
)}
{canonical && <link rel="canonical" href={canonical} />}
<link
rel="alternate"
type="application/rss+xml"
title="Produktbloggen - aksel.nav.no"
href="https://aksel.nav.no/rss/produktbloggen-rss.xml"
/>
</Head>
);
}
@@ -29,10 +29,11 @@ const useAmplitudeInit = () => {
useEffect(() => {
const isProdUrl = () => window.location.host === "aksel.nav.no";
const isExample = () => window.location.pathname.includes("eksempler/");
const isTemplate = () => window.location.pathname.includes("templates/");
const isPreview = () => !!document.getElementById("exit-preview-id");
const initAmplitude = async () => {
if (isExample()) {
if (isExample() || isTemplate()) {
return;
} else if (!isProdUrl()) {
mockAmplitude();
+5 -4
View File
@@ -6,14 +6,15 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
const cdnUrl = "https://cdn.nav.no";
const hotjarUrl = "https://*.hotjar.com";
const dekoratorUrl = "https://www.nav.no";
const ContentSecurityPolicy = `
default-src 'self' 'unsafe-inline' ${cdnUrl};
font-src 'self' ${cdnUrl} ${hotjarUrl} data:;
img-src 'self' cdn.sanity.io https://avatars.githubusercontent.com data: ${cdnUrl} ${hotjarUrl};
script-src 'self' ${cdnUrl} ${hotjarUrl} https://in2.taskanalytics.com/tm.js 'nonce-4e1aa203a32e' 'unsafe-eval';
style-src 'self' ${cdnUrl} ${hotjarUrl} 'unsafe-inline';
connect-src 'self' ${cdnUrl} ${hotjarUrl} https://*.hotjar.io wss://*.hotjar.com https://raw.githubusercontent.com/navikt/ https://hnbe3yhs.apicdn.sanity.io wss://hnbe3yhs.api.sanity.io cdn.sanity.io *.api.sanity.io https://amplitude.nav.no https://in2.taskanalytics.com/03346;
img-src 'self' cdn.sanity.io ${dekoratorUrl} https://avatars.githubusercontent.com data: ${cdnUrl} ${hotjarUrl};
script-src 'self' ${dekoratorUrl} ${cdnUrl} ${hotjarUrl} https://in2.taskanalytics.com/tm.js 'nonce-4e1aa203a32e' 'unsafe-eval';
style-src 'self' ${dekoratorUrl} ${cdnUrl} ${hotjarUrl} 'unsafe-inline';
connect-src 'self' ${dekoratorUrl} ${cdnUrl} ${hotjarUrl} https://*.hotjar.io wss://*.hotjar.com https://raw.githubusercontent.com/navikt/ https://hnbe3yhs.apicdn.sanity.io wss://hnbe3yhs.api.sanity.io cdn.sanity.io *.api.sanity.io https://amplitude.nav.no https://in2.taskanalytics.com/03346;
frame-ancestors 'self' localhost:3000;
media-src 'self' ${cdnUrl} cdn.sanity.io;
frame-src 'self' https://web.microsoftstream.com localhost:3000 https://aksel.ekstern.dev.nav.no;
+4 -2
View File
@@ -7,7 +7,7 @@
"scripts": {
"dev": "next | pino-pretty",
"build:old": "next build",
"build": "next build && yarn update:searchindex && yarn cp:assets",
"build": "next build && yarn update:searchindex && yarn update:rss && yarn cp:assets",
"start": "cd .next/standalone/aksel.nav.no/website && PORT=3000 HOSTNAME=0.0.0.0 node server.js",
"cp:assets": "cross-env cp -R public .next/standalone/aksel.nav.no/website/ && cross-env cp -R .next/static .next/standalone/aksel.nav.no/website/.next/static",
"update:searchindex": "ts-node --project tsconfig.json ./scripts/create-index",
@@ -15,9 +15,10 @@
"e2e:full": "yarn build && ts-node ./e2e/genUrls.ts && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"e2e:ci": " yarn start-server-and-test start http://localhost:3000 playwright",
"e2e:ci-full": "ts-node ./e2e/genUrls.ts && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"update:rss": "ts-node --project tsconfig.tsnode.json ./scripts/generate-rss-feed",
"update:props": "ts-node --project tsconfig.tsnode.json ./scripts/update-props",
"update:tokens": "ts-node --project tsconfig.tsnode.json ./scripts/update-tokens",
"update:eksempler": "ts-node --project tsconfig.tsnode.json ./scripts/examples/update-sanity",
"update:eksempler": "ts-node --project tsconfig.tsnode.json ./scripts/update-examples/update-sanity",
"update:changelog": "ts-node --project tsconfig.tsnode.json ./scripts/changelog",
"update:sanity": "yarn update:tokens && yarn update:props && yarn update:eksempler && yarn update:changelog",
"analyze": "cross-env ANALYZE=true yarn build",
@@ -85,6 +86,7 @@
"postcss": "^8.4.31",
"postcss-import": "^14.1.0",
"postcss-loader": "7.0.0",
"rss": "^1.2.2",
"showdown": "^2.1.0",
"start-server-and-test": "^1.14.0",
"ts-jest": "^29.0.0",
+97 -22
View File
@@ -1,10 +1,20 @@
import { AmplitudeEvents, amplitude } from "@/logging";
import { BodyLong, Heading, Link } from "@navikt/ds-react";
import { AkselCubeStatic } from "components/website-modules/aksel-cube/AkselCube";
import NextLink from "next/link";
import { BugIcon } from "@navikt/aksel-icons";
import {
BodyShort,
Box,
HGrid,
Heading,
Link,
List,
Page,
VStack,
} from "@navikt/ds-react";
import Footer from "components/layout/footer/Footer";
import { Header } from "components/layout/header/Header";
import { useEffect } from "react";
function Page() {
export default function NotFound() {
useEffect(() => {
amplitude.track(AmplitudeEvents.notfound, {
side: window.location.pathname,
@@ -12,24 +22,89 @@ function Page() {
}, []);
return (
<div
id="vk-notFoundId"
className="relative flex h-screen w-screen items-center justify-center gap-8 overflow-hidden"
>
<AkselCubeStatic className="text-deepblue-300 opacity-10" />
<div>
<Heading spacing level="1" size="large">
404 - Fant ikke siden
</Heading>
<BodyLong className="mb-2">
Vi fant ikke siden du var ute etter{" "}
</BodyLong>
<NextLink href="/" passHref legacyBehavior>
<Link>Tilbake til forsiden</Link>
</NextLink>
</div>
</div>
<Page data-aksel-template="404-v1" footer={<Footer />} id="vk-notFoundId">
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<VStack gap="16">
<VStack gap="12" align="start">
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en
feil i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="/">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<Link href="https://github.com/navikt/aksel/issues/new?assignees=&labels=bug+%F0%9F%90%9B&projects=&template=bug-report.md&title=[Aksel.nav.no%20-%20404]">
<BugIcon aria-hidden />
Meld gjerne fra om at lenken ikke virker
</Link>
</VStack>
<div>
<Heading level="2" size="large" spacing>
Page not found
</Heading>
<BodyShort spacing>
The page you requested cannot be found.
</BodyShort>
<BodyShort>
Go to the <Link href="/">front page</Link>, or use one of the
links in the menu.
</BodyShort>
</div>
</VStack>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
</Page>
);
}
export default Page;
function StatusSvg() {
return (
<svg
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
+2 -1
View File
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { createClient } from "next-sanity";
import { clientConfig } from "../../sanity/config";
import type { NextApiRequest, NextApiResponse } from "next";
const previewClient = createClient({
...clientConfig,
@@ -23,6 +23,7 @@ const landingPages = [
"god-praksis",
"produktbloggen",
"prinsipper",
"monster-maler",
];
export default async function preview(
@@ -1,72 +1,70 @@
import {
VirusIcon,
PencilIcon,
HospitalIcon,
PiggybankIcon,
PersonIcon,
ParasolBeachIcon,
PencilIcon,
PersonIcon,
PiggybankIcon,
VirusIcon,
} from "@navikt/aksel-icons";
import { Timeline } from "@navikt/ds-react";
import { Timeline, TimelinePeriodProps } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
import { useState } from "react";
const Example = () => {
const [activePeriod, setActivePeriod] = useState<any>();
const [activePeriod, setActivePeriod] = useState("");
return (
<div className="min-w-[800px] overflow-x-auto">
<div className="min-w-[800px]">
<Timeline>
<Timeline.Row label="Person" icon={<PersonIcon aria-hidden />}>
{person.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
/>
);
})}
{person.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
/>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus A" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
/>
))}
</Timeline.Row>
</Timeline>
{activePeriod && (
<div
aria-controls={activePeriod.id}
aria-controls={activePeriod}
id="timeline-panel"
className="mt-8"
>{`${activePeriod}: ${
[...person, ...jobb].find((x) => x.id === activePeriod).start
[...person, ...jobb].find((p) => p.id === activePeriod).start
}`}</div>
)}
</div>
);
};
const person = [
const person: TimelinePeriodProps[] = [
{
id: 1,
id: "1",
start: new Date("Jan 1 2022"),
end: new Date("Jan 31 2022"),
status: "warning",
@@ -74,16 +72,15 @@ const person = [
statusLabel: "Sykemeldt",
},
{
id: 2,
id: "2",
start: new Date("Apr 1 2022"),
end: new Date("Apr 30 2022"),
status: "neutral",
icon: <ParasolBeachIcon aria-hidden />,
label: "test",
statusLabel: "Ferie",
},
{
id: 3,
id: "3",
start: new Date("Jul 1 2022"),
end: new Date("Jul 31 2022"),
status: "warning",
@@ -91,7 +88,7 @@ const person = [
statusLabel: "Sykemeldt",
},
{
id: 4,
id: "4",
start: new Date("Aug 1 2022"),
end: new Date("Aug 30 2022"),
status: "warning",
@@ -100,9 +97,9 @@ const person = [
},
];
const jobb = [
const jobb: TimelinePeriodProps[] = [
{
id: 5,
id: "5",
start: new Date("Feb 2 2022"),
end: new Date("Mar 1 2022"),
status: "success",
@@ -110,7 +107,7 @@ const jobb = [
icon: <PiggybankIcon aria-hidden />,
},
{
id: 6,
id: "6",
start: new Date("Mar 2 2022"),
end: new Date("Apr 1 2022"),
status: "success",
@@ -118,7 +115,7 @@ const jobb = [
icon: <PiggybankIcon aria-hidden />,
},
{
id: 7,
id: "7",
start: new Date("May 2 2022"),
end: new Date("June 1 2022"),
status: "success",
@@ -126,7 +123,7 @@ const jobb = [
icon: <PiggybankIcon aria-hidden />,
},
{
id: 8,
id: "8",
start: new Date("June 2 2022"),
end: new Date("July 1 2022"),
status: "success",
@@ -1,77 +1,75 @@
import {
VirusIcon,
PencilIcon,
HospitalIcon,
PiggybankIcon,
PersonGroupIcon,
ParasolBeachIcon,
PencilIcon,
PersonGroupIcon,
PiggybankIcon,
VirusIcon,
} from "@navikt/aksel-icons";
import { Timeline } from "@navikt/ds-react";
import { Timeline, TimelinePeriodProps } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
import { useState } from "react";
const Example = () => {
const [activePeriod, setActivePeriod] = useState<any>();
const [activePeriod, setActivePeriod] = useState("");
return (
<div className="min-w-[800px] overflow-x-auto">
<div className="min-w-[800px]">
<Timeline>
<Timeline.Pin date={new Date("Mar 15 2022")}>
<p>Utbetalt: 12 345 kr</p>
</Timeline.Pin>
<Timeline.Row label="Person" icon={<PersonGroupIcon aria-hidden />}>
{person.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
>
{p?.children ?? null}
</Timeline.Period>
);
})}
{person.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
>
{p.children ?? null}
</Timeline.Period>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus A" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
>
{p?.children ?? null}
</Timeline.Period>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
onSelectPeriod={() => setActivePeriod(p.id)}
isActive={activePeriod && activePeriod === p.id}
aria-controls="timeline-panel"
id={p.id}
>
{p.children ?? null}
</Timeline.Period>
))}
</Timeline.Row>
</Timeline>
{activePeriod && (
<div
aria-controls={activePeriod.id}
aria-controls={activePeriod}
id="timeline-panel"
className="mt-8"
>{`${activePeriod}: ${
[...person, ...jobb].find((x) => x.id === activePeriod).start
[...person, ...jobb].find((p) => p.id === activePeriod).start
}`}</div>
)}
</div>
);
};
const person = [
const person: TimelinePeriodProps[] = [
{
start: new Date("Jan 1 2022"),
end: new Date("Jan 31 2022"),
@@ -79,16 +77,15 @@ const person = [
icon: <PencilIcon aria-hidden />,
statusLabel: "Sykemeldt",
children: <div>50% sykemeldt</div>,
id: 1,
id: "1",
},
{
start: new Date("Apr 1 2022"),
end: new Date("Apr 30 2022"),
status: "neutral",
icon: <ParasolBeachIcon aria-hidden />,
label: "test",
statusLabel: "Ferie",
id: 2,
id: "2",
},
{
start: new Date("Jul 1 2022"),
@@ -96,7 +93,7 @@ const person = [
status: "warning",
icon: <PencilIcon aria-hidden />,
statusLabel: "Sykemeldt",
id: 3,
id: "3",
},
{
start: new Date("Aug 1 2022"),
@@ -104,11 +101,11 @@ const person = [
status: "warning",
icon: <VirusIcon aria-hidden />,
statusLabel: "Stønad korona",
id: 4,
id: "4",
},
];
const jobb = [
const jobb: TimelinePeriodProps[] = [
{
start: new Date("Feb 2 2022"),
end: new Date("Mar 1 2022"),
@@ -116,7 +113,7 @@ const jobb = [
statusLabel: "Utbetaling",
icon: <PiggybankIcon aria-hidden />,
children: <div>100% utbetaling</div>,
id: 11,
id: "11",
},
{
start: new Date("Mar 2 2022"),
@@ -125,7 +122,7 @@ const jobb = [
statusLabel: "Utbetaling",
icon: <PiggybankIcon aria-hidden />,
children: <div>100% utbetaling</div>,
id: 22,
id: "22",
},
{
start: new Date("May 2 2022"),
@@ -134,7 +131,7 @@ const jobb = [
statusLabel: "Utbetaling",
icon: <PiggybankIcon aria-hidden />,
children: <div>100% utbetaling</div>,
id: 33,
id: "33",
},
{
start: new Date("June 2 2022"),
@@ -142,7 +139,7 @@ const jobb = [
status: "success",
statusLabel: "Utbetaling",
icon: <PiggybankIcon aria-hidden />,
id: 44,
id: "44",
},
];
@@ -155,5 +152,5 @@ export const Demo = {
export const args = {
index: 4,
desc: "Vi anbefaler å bruke 'aria-controls' begge veier mellom aktive perioder og panelet det refererer til. Dette gir brukere av enkelte skjermlesere keyboard-shortcuts for å hoppe mellom perioder og panelet.",
desc: "Vi anbefaler å bruke 'aria-controls' begge veier mellom aktive perioder og panelet det refererer til. Dette gir brukere av enkelte skjermlesere hurtigtaster for å hoppe mellom perioder og panelet.",
};
@@ -1,17 +1,17 @@
import {
VirusIcon,
PencilIcon,
HospitalIcon,
PiggybankIcon,
PersonIcon,
ParasolBeachIcon,
PencilIcon,
PersonIcon,
PiggybankIcon,
VirusIcon,
} from "@navikt/aksel-icons";
import { Timeline } from "@navikt/ds-react";
import { Timeline, TimelinePeriodProps } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<div className="min-w-[800px] overflow-x-auto">
<div className="min-w-[800px]">
<Timeline>
<Timeline.Pin date={new Date("Mar 15 2022")}>
<p>Periode: 01.03.2022 - 01.04.2022</p>
@@ -29,50 +29,47 @@ const Example = () => {
<p style={{ color: "red" }}>Dager igjen: 1</p>
</Timeline.Pin>
<Timeline.Row label="Person" icon={<PersonIcon aria-hidden />}>
{person.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{person.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus A" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus B" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
</Timeline>
</div>
);
};
const person = [
const person: TimelinePeriodProps[] = [
{
start: new Date("Jan 1 2022"),
end: new Date("Jan 31 2022"),
@@ -85,7 +82,6 @@ const person = [
end: new Date("Apr 30 2022"),
status: "neutral",
icon: <ParasolBeachIcon aria-hidden />,
label: "test",
statusLabel: "Ferie",
},
{
@@ -104,7 +100,7 @@ const person = [
},
];
const jobb = [
const jobb: TimelinePeriodProps[] = [
{
start: new Date("Feb 2 2022"),
end: new Date("Mar 1 2022"),
@@ -1,67 +1,64 @@
import {
VirusIcon,
PencilIcon,
HospitalIcon,
PiggybankIcon,
PersonIcon,
ParasolBeachIcon,
PencilIcon,
PersonIcon,
PiggybankIcon,
VirusIcon,
} from "@navikt/aksel-icons";
import { Timeline } from "@navikt/ds-react";
import { Timeline, TimelinePeriodProps } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<div className="min-w-[800px] overflow-x-auto">
<div className="min-w-[800px]">
<Timeline>
<Timeline.Row label="Person" icon={<PersonIcon aria-hidden />}>
{person.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
>
{p?.children ?? null}
</Timeline.Period>
);
})}
{person.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
>
{p.children ?? null}
</Timeline.Period>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus A" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
>
{p?.children ?? null}
</Timeline.Period>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
>
{p.children ?? null}
</Timeline.Period>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus B" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
</Timeline>
</div>
);
};
const person = [
const person: TimelinePeriodProps[] = [
{
start: new Date("Jan 1 2022"),
end: new Date("Jan 31 2022"),
@@ -75,7 +72,6 @@ const person = [
end: new Date("Apr 30 2022"),
status: "neutral",
icon: <ParasolBeachIcon aria-hidden />,
label: "test",
statusLabel: "Ferie",
},
{
@@ -94,7 +90,7 @@ const person = [
},
];
const jobb = [
const jobb: TimelinePeriodProps[] = [
{
start: new Date("Feb 2 2022"),
end: new Date("Mar 1 2022"),
@@ -1,56 +1,53 @@
import {
VirusIcon,
PencilIcon,
HospitalIcon,
PiggybankIcon,
PersonIcon,
ParasolBeachIcon,
PencilIcon,
PersonIcon,
PiggybankIcon,
VirusIcon,
} from "@navikt/aksel-icons";
import { Timeline } from "@navikt/ds-react";
import { Timeline, TimelinePeriodProps } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<div className="min-w-[800px] overflow-x-auto">
<div className="min-w-[800px]">
<Timeline>
<Timeline.Row label="Person" icon={<PersonIcon aria-hidden />}>
{person.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{person.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus A" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
<Timeline.Row label="Sykehus B" icon={<HospitalIcon aria-hidden />}>
{jobb.map((p: any, i) => {
return (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
/>
);
})}
{jobb.map((p, i) => (
<Timeline.Period
key={i}
start={p.start}
end={p.end}
status={p.status}
icon={p.icon}
statusLabel={p.statusLabel}
/>
))}
</Timeline.Row>
<Timeline.Zoom>
<Timeline.Zoom.Button label="3 mnd" interval="month" count={3} />
@@ -63,7 +60,7 @@ const Example = () => {
);
};
const person = [
const person: TimelinePeriodProps[] = [
{
start: new Date("Jan 1 2022"),
end: new Date("Jan 31 2022"),
@@ -76,7 +73,6 @@ const person = [
end: new Date("Apr 30 2022"),
status: "neutral",
icon: <ParasolBeachIcon aria-hidden />,
label: "test",
statusLabel: "Ferie",
},
{
@@ -95,7 +91,7 @@ const person = [
},
];
const jobb = [
const jobb: TimelinePeriodProps[] = [
{
start: new Date("Feb 2 2022"),
end: new Date("Mar 1 2022"),
@@ -135,5 +131,5 @@ export const Demo = {
export const args = {
index: 3,
desc: "Lar deg 'zoome' til predefinerte tidsperioder. Bruk 'startDate/endDate'-prop i Timeline for å justere hvilken tidsperiode som blir vist.",
desc: "Lar deg \"zoome\" til predefinerte tidsperioder. Bruk 'startDate/endDate'-prop i Timeline for å justere hvilken tidsperiode som blir vist.",
};
@@ -40,7 +40,7 @@ export const query = `{
${destructureBlocks}
},
},
"seo": *[_type == "komponenter_landingsside"][0].seo.image,
"seo": *[_type == "grunnleggende_landingsside"][0].seo.image,
${sidebarQuery}
}`;
@@ -102,7 +102,12 @@ const Page = ({ page, sidebar, seo, publishDate }: PageProps["props"]) => {
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{ type: "Grunnleggende", title: page?.heading }}
pageType={{
type: "grunnleggende",
title: page?.heading,
rootUrl: "/grunnleggende",
rootTitle: "Grunnleggende",
}}
intro={
<Detail as="div">
<div className="mt-2 flex flex-wrap items-center gap-3">
@@ -62,7 +62,12 @@ const Page = ({ page, sidebar, links }: PageProps["props"]) => {
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{ type: "Grunnleggende", title: "Grunnleggende" }}
pageType={{
type: "grunnleggende",
title: "Grunnleggende",
rootUrl: "/grunnleggende",
rootTitle: "Grunnleggende",
}}
intro={<Ingress className="text-text-on-action">{page?.intro}</Ingress>}
pageProps={page}
>
+1 -1
View File
@@ -97,7 +97,7 @@ export const query = `*[_type == "aksel_forside"][0]{
seo,
${contributorsAll}
},
"komponenter": *[_type in ["komponent_artikkel", "ds_artikkel"] && defined(publishedAt) && !(_id in ^.highlights[]._ref)] | order(publishedAt desc)[0...3]{
"komponenter": *[_type in ["komponent_artikkel", "ds_artikkel", "templates_artikkel"] && defined(publishedAt) && !(_id in ^.highlights[]._ref)] | order(publishedAt desc)[0...3]{
_type,
_id,
heading,
@@ -261,7 +261,12 @@ const Page = ({
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{ type: "Komponenter", title: page?.heading }}
pageType={{
type: "komponenter",
title: page?.heading,
rootUrl: "/komponenter",
rootTitle: "Komponenter",
}}
pageProps={page}
variant="page"
intro={
@@ -74,7 +74,12 @@ const Page = ({ page, sidebar, links }: PageProps["props"]) => {
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{ type: "Komponenter", title: "Komponenter" }}
pageType={{
type: "komponenter",
title: "Komponenter",
rootUrl: "/komponenter",
rootTitle: "Komponenter",
}}
intro={
<Ingress className="text-text-on-action">
{page?.intro}
@@ -0,0 +1,194 @@
import { dateStr } from "@/components";
import { SanityBlockContent } from "@/sanity-block";
import { getClient } from "@/sanity/client.server";
import { getDocumentsTmp } from "@/sanity/interface";
import { destructureBlocks, sidebarQuery } from "@/sanity/queries";
import {
AkselSidebarT,
AkselTemplatesDocT,
ArticleListT,
CodeExampleSchemaT,
NextPageT,
ResolveContributorsT,
ResolveSlugT,
} from "@/types";
import { Detail, Heading } from "@navikt/ds-react";
import { WithSidebar } from "components/layout/WithSidebar";
import Footer from "components/layout/footer/Footer";
import { Header } from "components/layout/header/Header";
import IntroSeksjon from "components/sanity-modules/intro-seksjon/IntroSeksjon";
import { StatusTag } from "components/website-modules/StatusTag";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
import { SEO } from "components/website-modules/seo/SEO";
import { GetStaticPaths, GetStaticProps } from "next/types";
import { Suspense, lazy } from "react";
import NotFotfund from "../404";
type PageProps = NextPageT<{
page: ResolveContributorsT<ResolveSlugT<AkselTemplatesDocT>>;
sidebar: AkselSidebarT;
seo: any;
refs: ArticleListT;
publishDate: string;
}>;
const query = `{
"page": *[_type == "templates_artikkel" && slug.current == $slug] | order(_updatedAt desc)[0]
{
...,
"slug": slug.current,
content[]{
...,
${destructureBlocks}
},
},
"seo": *[_type == "templates_landingsside"][0].seo.image,
${sidebarQuery}
}`;
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: await getDocumentsTmp("templates_artikkel").then((paths) =>
paths.map((slug) => ({
params: {
slug: slug.split("/").filter((x) => x !== "monster-maler"),
},
}))
),
fallback: "blocking",
};
};
export const getStaticProps: GetStaticProps = async ({
params: { slug },
preview = false,
}: {
params: { slug: string[] };
preview?: boolean;
}): Promise<PageProps> => {
const { page, sidebar, seo } = await getClient().fetch(query, {
slug: `monster-maler/${slug.slice(0, 2).join("/")}`,
type: "templates_artikkel",
});
return {
props: {
page,
slug: slug.slice(0, 2).join("/"),
seo,
sidebar,
preview,
title: page?.heading ?? "",
id: page?._id ?? "",
refs: [],
publishDate: await dateStr(page?._updatedAt ?? page?._createdAt),
},
notFound: !page && !preview,
revalidate: 60,
};
};
const Page = ({ page, sidebar, seo, publishDate }: PageProps["props"]) => {
if (!page) {
return <NotFotfund />;
}
const metadata: CodeExampleSchemaT["metadata"] = page.content.find(
(x) => x._type === "kode_eksempler"
)?.dir?.metadata;
return (
<>
<SEO
title={page?.heading}
description={page?.seo?.meta}
image={page?.seo?.image ?? seo}
/>
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{
type: "templates",
title: page?.heading,
rootUrl: "/monster-maler",
rootTitle: "Mønster og Maler",
}}
intro={
<Detail as="div">
<div className="mt-2 flex flex-wrap items-center gap-3">
<span>
Oppdatert <time>{publishDate}</time>
</span>
<StatusTag showStable status={page?.status?.tag} />
</div>
</Detail>
}
pageProps={page}
variant="page"
>
<IntroSeksjon node={page?.intro} />
<SanityBlockContent blocks={page["content"]} />
{metadata && metadata.changelog && (
<>
<Heading
tabIndex={-1}
id="changelog"
level="2"
size="large"
className="max-w-text text-deepblue-800 mb-4 mt-12 scroll-mt-20 focus:outline-none"
>
Endringer
</Heading>
<AkselTable
th={[
{ text: "Dato" },
{ text: "Versjon" },
{ text: "Endringer" },
]}
>
{metadata.changelog
.sort((a, b) => a.version - b.version)
.map((log) => (
<AkselTableRow
key={log.version}
tr={[
{ text: log.date },
{ text: log.version },
{ text: log.description },
]}
/>
))}
</AkselTable>
</>
)}
</WithSidebar>
<Footer />
</>
);
};
const WithPreview = lazy(() => import("../../components/WithPreview"));
const Wrapper = (props: any) => {
if (props?.preview) {
return (
<Suspense fallback={<Page {...props} />}>
<WithPreview
comp={Page}
query={query}
params={{
slug: `monster-maler/${props.slug}`,
type: "templates_artikkel",
}}
props={props}
/>
</Suspense>
);
}
return <Page {...props} />;
};
export default Wrapper;
@@ -0,0 +1,138 @@
import { SanityBlockContent } from "@/sanity-block";
import { getClient } from "@/sanity/client.server";
import { landingPageQuery, sidebarQuery } from "@/sanity/queries";
import {
AkselLandingPageDocT,
AkselSidebarT,
ArticleListT,
NextPageT,
} from "@/types";
import { BodyLong, Heading } from "@navikt/ds-react";
import cl from "clsx";
import { WithSidebar } from "components/layout/WithSidebar";
import Footer from "components/layout/footer/Footer";
import { Header } from "components/layout/header/Header";
import ComponentOverview from "components/sanity-modules/component-overview/ComponentOverview";
import { SEO } from "components/website-modules/seo/SEO";
import { GetStaticProps } from "next/types";
import { Suspense, lazy } from "react";
import { templatesKategorier } from "../../sanity/config";
type PageProps = NextPageT<{
page: AkselLandingPageDocT;
sidebar: AkselSidebarT;
links: ArticleListT;
}>;
export const query = `{${sidebarQuery}, ${landingPageQuery(
"templates"
)}, "links": *[_type == "templates_artikkel" && defined(kategori)]{_id,heading,"slug": slug,status,kategori,"sidebarindex": sidebarindex}}`;
export const getStaticProps: GetStaticProps = async ({
preview = false,
}): Promise<PageProps> => {
const { sidebar, page, links } = await getClient().fetch(query, {
type: "templates_artikkel",
});
return {
props: {
page,
sidebar,
links,
slug: "/monster-maler",
preview,
title: "Forside Mønster og Maler",
id: page?._id ?? "",
},
revalidate: 60,
notFound: false,
};
};
const Page = ({ page, sidebar, links }: PageProps["props"]) => {
return (
<>
<SEO
title="Mønster og Maler"
description={page?.seo?.meta}
image={page?.seo?.image}
/>
<Header />
<WithSidebar
sidebar={sidebar}
pageType={{
type: "templates",
title: "Mønster og Maler",
rootUrl: "/monster-maler",
rootTitle: "Mønster og Maler",
}}
intro={
<BodyLong size="large" className="text-text-on-action">
{page?.intro}
</BodyLong>
}
pageProps={page}
>
{templatesKategorier
.filter(
(kat) => links?.filter((x) => x.kategori === kat.value).length > 0
)
.map((kat, i) => (
<div
key={i}
className={cl({ "pb-8": i + 1 < templatesKategorier.length })}
>
<Heading
level="2"
size="large"
spacing
className="text-deepblue-800 scoll-mt-20"
id={kat.value}
>
{kat.title}
</Heading>
<div>
{page?.[`ingress_${kat.value}`] && (
<BodyLong size="large" className="mb-4 only:mb-7">
{page[`ingress_${kat.value}`]}
</BodyLong>
)}
{page?.[`intro_${kat.value}`] && (
<SanityBlockContent blocks={page[`intro_${kat.value}`]} />
)}
</div>
<ComponentOverview
node={links.filter((x) => x.kategori === kat.value)}
/>
</div>
))}
</WithSidebar>
<Footer />
</>
);
};
const WithPreview = lazy(() => import("../../components/WithPreview"));
const Wrapper = (props: any) => {
if (props?.preview) {
return (
<Suspense fallback={<Page {...props} />}>
<WithPreview
comp={Page}
query={query}
props={props}
params={{
type: "templates_artikkel",
}}
/>
</Suspense>
);
}
return <Page {...props} />;
};
export default Wrapper;
@@ -0,0 +1,131 @@
import {
BodyShort,
Box,
HGrid,
Heading,
Link,
List,
Page,
} from "@navikt/ds-react";
import { useEffect } from "react";
export default function Example() {
useDekorator();
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en feil
i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
<Env />
</Page>
);
}
function Header() {
return <div id="decorator-header" />;
}
function Footer() {
return <div id="decorator-footer" />;
}
const MILJO_URL = "https://www.nav.no/dekoratoren";
function Env() {
return (
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
/>
);
}
/**
* OBS: Dette er ikke anbefalt metode for å laste dekoratør!
* Se `nav-dekoratoren`-dokumentasjon for riktig implementasjon
* @see https://github.com/navikt/nav-dekoratoren
*/
function useDekorator() {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 1,
title: "Enkel",
desc: "I sin enkleste form skal 404-side inneholde en tittel, feilmelding, løsningsforslag og illustrasjon.",
};
@@ -0,0 +1,158 @@
import { BugIcon } from "@navikt/aksel-icons";
import {
BodyShort,
Box,
Button,
HGrid,
Heading,
Link,
List,
VStack,
Page,
} from "@navikt/ds-react";
import { useEffect } from "react";
export default function Example() {
useDekorator();
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<VStack gap="16">
<VStack gap="12" align="start">
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en
feil i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<Button as="a" href="#">
Gå til Min side
</Button>
<Link href="#">
<BugIcon aria-hidden />
Meld gjerne fra om at lenken ikke virker
</Link>
</VStack>
<div>
<Heading level="2" size="large" spacing>
Page not found
</Heading>
<BodyShort spacing>
The page you requested cannot be found.
</BodyShort>
<BodyShort>
Go to the <Link href="#">front page</Link>, or use one of the
links in the menu.
</BodyShort>
</div>
</VStack>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
<Env />
</Page>
);
}
function Header() {
return <div id="decorator-header" />;
}
function Footer() {
return <div id="decorator-footer" />;
}
const MILJO_URL = "https://www.nav.no/dekoratoren";
function Env() {
return (
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
/>
);
}
/**
* OBS: Dette er ikke anbefalt metode for å laste dekoratør!
* Se `nav-dekoratoren`-dokumentasjon for riktig implementasjon
* @see https://github.com/navikt/nav-dekoratoren
*/
function useDekorator() {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 0,
title: "Komplett",
desc: "I sin fullstendige form kan en 404-side inneholde tittel, feilmelding, løsningsforslag, CTA, tilbakemeldingsfunksjon, flere språk og illustrasjon.",
};
@@ -0,0 +1,138 @@
import {
BodyShort,
Box,
Button,
HGrid,
Heading,
Link,
List,
Page,
VStack,
} from "@navikt/ds-react";
import { useEffect } from "react";
export default function Example() {
useDekorator();
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<VStack gap="12" align="start">
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en
feil i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<Button as="a" href="#">
Gå til Min side
</Button>
</VStack>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
<Env />
</Page>
);
}
function Header() {
return <div id="decorator-header" />;
}
function Footer() {
return <div id="decorator-footer" />;
}
const MILJO_URL = "https://www.nav.no/dekoratoren";
function Env() {
return (
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
/>
);
}
/**
* OBS: Dette er ikke anbefalt metode for å laste dekoratør!
* Se `nav-dekoratoren`-dokumentasjon for riktig implementasjon
* @see https://github.com/navikt/nav-dekoratoren
*/
function useDekorator() {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 2,
title: "Med CTA til side",
desc: "En 404-side kan inneholde en CTA som leder til et relevant område eller side, avhengig av konteksten.",
};
@@ -0,0 +1,139 @@
import { BugIcon } from "@navikt/aksel-icons";
import {
BodyShort,
Box,
HGrid,
Heading,
Link,
List,
Page,
VStack,
} from "@navikt/ds-react";
import { useEffect } from "react";
export default function Example() {
useDekorator();
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<VStack gap="12" align="start">
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en
feil i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<Link href="#">
<BugIcon aria-hidden />
Meld gjerne fra om at lenken ikke virker
</Link>
</VStack>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
<Env />
</Page>
);
}
function Header() {
return <div id="decorator-header" />;
}
function Footer() {
return <div id="decorator-footer" />;
}
const MILJO_URL = "https://www.nav.no/dekoratoren";
function Env() {
return (
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
/>
);
}
/**
* OBS: Dette er ikke anbefalt metode for å laste dekoratør!
* Se `nav-dekoratoren`-dokumentasjon for riktig implementasjon
* @see https://github.com/navikt/nav-dekoratoren
*/
function useDekorator() {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 3,
title: "Med tilbakemeldingsfunksjon",
desc: "Hvis løsningen din støtter det, kan du vurdere å gi brukerne muligheten til å rapportere avvik.",
};
@@ -0,0 +1,147 @@
import {
BodyShort,
Box,
HGrid,
Heading,
Link,
List,
Page,
VStack,
} from "@navikt/ds-react";
import { useEffect } from "react";
export default function Example() {
useDekorator();
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<VStack gap="16">
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en
feil i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<div>
<Heading level="2" size="large" spacing>
Page not found
</Heading>
<BodyShort spacing>
The page you requested cannot be found.
</BodyShort>
<BodyShort>
Go to the <Link href="#">front page</Link>, or use one of the
links in the menu.
</BodyShort>
</div>
</VStack>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
<Env />
</Page>
);
}
function Header() {
return <div id="decorator-header" />;
}
function Footer() {
return <div id="decorator-footer" />;
}
const MILJO_URL = "https://www.nav.no/dekoratoren";
function Env() {
return (
<div
id="decorator-env"
data-src={`${MILJO_URL}/env?context=privatperson`}
/>
);
}
/**
* OBS: Dette er ikke anbefalt metode for å laste dekoratør!
* Se `nav-dekoratoren`-dokumentasjon for riktig implementasjon
* @see https://github.com/navikt/nav-dekoratoren
*/
function useDekorator() {
useEffect(() => {
const script = document.createElement("script");
script.src = `${MILJO_URL}/client.js`;
script.async = true;
document.body.appendChild(script);
const styles = document.createElement("link");
styles.href = `${MILJO_URL}/css/client.css`;
styles.rel = "stylesheet";
document.head.appendChild(styles);
return () => {
document.body.removeChild(script);
document.head.removeChild(styles);
};
}, []);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 4,
title: "Flerspråk",
desc: "En 404-feil kan være frustrerende, spesielt hvis den er på et ukjent språk. En melding på engelsk kan gjøre det lettere å forstå problemet og hva du skal gjøre videre",
};
@@ -0,0 +1,10 @@
{
"version": 1,
"changelog": [
{
"description": "Publiserte varianter med CTA, tilbakemelding og språkvalg",
"date": "03.11.2023",
"version": 1
}
]
}
@@ -0,0 +1,87 @@
import {
BodyShort,
Box,
HGrid,
Heading,
Link,
List,
Page,
} from "@navikt/ds-react";
import Footer from "components/layout/footer/Footer";
import { Header } from "components/layout/header/Header";
export default function Example() {
return (
<Page footer={<Footer />}>
<Header />
<Page.Block as="main" width="xl" gutters>
<Box paddingBlock="20 16">
<HGrid gap="12" columns={{ sm: 1, md: 2 }}>
<div>
<Heading level="1" size="large" spacing>
Beklager, vi fant ikke siden
</Heading>
<BodyShort>
Denne siden kan være slettet eller flyttet, eller det er en feil
i lenken.
</BodyShort>
<List>
<List.Item>Bruk gjerne søket eller menyen</List.Item>
<List.Item>
<Link href="#">Gå til forsiden</Link>
</List.Item>
</List>
</div>
<StatusSvg />
</HGrid>
</Box>
</Page.Block>
</Page>
);
}
function StatusSvg() {
return (
<svg
data-aksel-template="404-v1"
width="min(100%, 500px)"
viewBox="0 0 550 340"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby="404-illustration"
>
<title id="404-illustration">404 - fant ikke siden</title>
<path
d="M60.0927 243.567L95.542 245.427L96.7806 221.769C97.1476 214.76 97.7964 206.556 98.7272 197.16C99.8768 187.775 100.745 179.584 101.33 172.585L100.017 172.516C97.0704 178.512 94.0083 184.612 90.831 190.815C87.6652 196.799 84.3842 202.887 80.9881 209.079L60.0927 243.567ZM90.8631 334.801L93.3058 288.143L4.68237 283.493L6.74662 244.063L84.0445 125.551L154.287 129.236L148.06 248.182L170.38 249.353L168.143 292.069L145.823 290.898L143.381 337.556L90.8631 334.801Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M265.703 258.53C242.695 258.53 223.96 249.317 209.498 230.891C195.255 212.246 188.133 185.046 188.133 149.291C188.133 113.536 195.255 86.7747 209.498 69.0069C223.96 51.0198 242.695 42.0263 265.703 42.0263C288.711 42.0263 307.337 51.0198 321.58 69.0069C336.042 86.994 343.273 113.755 343.273 149.291C343.273 185.046 336.042 212.246 321.58 230.891C307.337 249.317 288.711 258.53 265.703 258.53ZM265.703 215.097C270.085 215.097 274.139 213.452 277.864 210.162C281.589 206.871 284.548 200.51 286.739 191.078C288.93 181.646 290.026 167.717 290.026 149.291C290.026 130.865 288.93 117.155 286.739 108.162C284.548 98.9489 281.589 92.9166 277.864 90.065C274.139 86.994 270.085 85.4585 265.703 85.4585C261.321 85.4585 257.267 86.994 253.542 90.065C249.817 92.9166 246.858 98.9489 244.667 108.162C242.476 117.155 241.38 130.865 241.38 149.291C241.38 167.717 242.476 181.646 244.667 191.078C246.858 200.51 249.817 206.871 253.542 210.162C257.267 213.452 261.321 215.097 265.703 215.097Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
<path
d="M429.98 129.301L465.429 127.441L464.191 103.783C463.824 96.7733 463.613 88.5473 463.557 79.105C463.721 69.6512 463.728 61.4137 463.58 54.3925L462.267 54.4614C459.962 60.7326 457.554 67.1192 455.042 73.621C452.518 79.9038 449.891 86.3018 447.16 92.8151L429.98 129.301ZM470.108 216.815L467.666 170.156L379.042 174.806L376.978 135.376L441.478 9.42432L511.72 5.7392L517.947 124.686L540.267 123.515L542.503 166.23L520.183 167.401L522.626 214.06L470.108 216.815Z"
stroke="var(--a-border-default)"
strokeWidth="2.19124"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6.57 8.76"
/>
</svg>
);
}
export const args = {
index: 5,
title: "Uten dekoratøren",
desc: "404-malen kan også enkelt brukes på sider uten nav-dekoratøren.",
sandbox: false,
};
+1
View File
@@ -17,6 +17,7 @@ Disallow: /*_ssgManifest.js$
Disallow: /*.js$
Disallow: /eksempler
Disallow: /templates
Disallow: /admin
Sitemap: https://aksel.nav.no/sitemap.xml
+22 -16
View File
@@ -12,6 +12,7 @@ export const allArticleDocuments = [
"aksel_blogg",
"aksel_prinsipp",
"aksel_standalone",
"templates_artikkel",
] as const;
export const previews = [
@@ -21,12 +22,14 @@ export const previews = [
"aksel_blogg",
"aksel_prinsipp",
"aksel_standalone",
"templates_artikkel",
];
export const landingsider = [
{ name: "godpraksis_landingsside", url: "god-praksis" },
{ name: "blogg_landingsside", url: "produktbloggen" },
{ name: "grunnleggende_landingsside", url: "grunnleggende" },
{ name: "templates_landingsside", url: "monster-maler" },
{ name: "komponenter_landingsside", url: "komponenter" },
{ name: "prinsipper_landingsside", url: "prinsipper" },
];
@@ -46,7 +49,10 @@ export const grunnleggendeKategorier = [
{ title: "Kode", value: "kode" },
];
export const monsterKategorier = [];
export const templatesKategorier = [
{ title: "Mønster", value: "monster" },
{ title: "Maler", value: "maler" },
];
export const bloggKategorier = [
{ title: "Nytt fra teamene", value: "nytt-fra-teamene" },
@@ -58,19 +64,19 @@ export const prinsippKategorier = [
{ title: "Brukeropplevelse", value: "brukeropplevelse" },
];
const allDocumentTypes = [
"aksel_artikkel",
"aksel_blogg",
"aksel_prinsipp",
"aksel_standalone",
"blogg_landingsside",
"ds_artikkel",
"godpraksis_landingsside",
"grunnleggende_landingsside",
"komponent_artikkel",
"komponenter_landingsside",
"prinsipper_landingsside",
"aksel_forside",
];
export const sanityCategoryLookup = (
category: "komponenter" | "grunnleggende" | "templates"
) => {
const _category = category.toLowerCase();
export const allDocsRef = allDocumentTypes.map((x) => ({ type: x }));
switch (_category) {
case "komponenter":
return komponentKategorier;
case "grunnleggende":
return grunnleggendeKategorier;
case "templates":
return templatesKategorier;
default:
return [];
}
};
@@ -25,6 +25,7 @@ export async function sitemapPages(
"godpraksis": *[_type == "godpraksis_landingsside"][0]._updatedAt,
"grunnleggende": *[_type == "grunnleggende_landingsside"][0]._updatedAt,
"komponenter": *[_type == "komponenter_landingsside"][0]._updatedAt,
"templates": *[_type == "templates_landingsside"][0]._updatedAt,
"blogg": *[_type == "blogg_landingsside"][0]._updatedAt,
}`
);
@@ -35,6 +36,7 @@ export async function sitemapPages(
{ path: "ikoner", lastmod: undefined },
{ path: "grunnleggende", lastmod: pages.grunnleggende },
{ path: "komponenter", lastmod: pages.komponenter },
{ path: "monster-maler", lastmod: pages.templates },
{ path: "produktbloggen", lastmod: pages.blogg },
...temaer.map((x) => ({
path: `god-praksis/${x.path}`,
@@ -61,7 +63,7 @@ export const getAkselDocuments = async (
};
export const getDocumentsTmp = async (
source: "komponent_artikkel" | "ds_artikkel",
source: "komponent_artikkel" | "ds_artikkel" | "templates_artikkel",
token?: string
): Promise<string[]> => {
const docs = await getDocuments(source, token);
@@ -91,6 +93,7 @@ async function getDocuments(
| "aksel_blogg"
| "aksel_prinsipp"
| "aksel_standalone"
| "templates_artikkel"
| "all",
token?: string
): Promise<{ slug: string; lastmod: string }[]> {
@@ -107,6 +110,7 @@ async function getDocuments(
"aksel_blogg",
"aksel_prinsipp",
"aksel_standalone",
"templates_artikkel",
]
: [source],
}
@@ -1,4 +1,4 @@
import { grunnleggendeKategorier, komponentKategorier } from "../config";
import { sanityCategoryLookup } from "../config";
const markDef = `
markDefs[]{
@@ -95,7 +95,6 @@ const defaultBlock = `
_type == "kode_eksempler" =>{
...,
dir->,
filnavn->,
},
_type == "kode_ref" => @->,
${tips},
@@ -185,12 +184,7 @@ export const sidebarQuery = `"sidebar": *[_type == $type && defined(kategori)] {
}`;
export const landingPageQuery = (t) => {
const kat =
t === "komponenter"
? komponentKategorier
: t === "grunnleggende"
? grunnleggendeKategorier
: [];
const kat = sanityCategoryLookup(t);
return `"page": *[_type == "${t}_landingsside"][0]{
...,
@@ -6,6 +6,7 @@ import {
landingsider,
previews,
prinsippKategorier,
templatesKategorier,
} from "../../config";
import { Iframe } from "./IFrame";
@@ -18,6 +19,7 @@ import {
LightBulbIcon,
NewspaperIcon,
PencilBoardIcon,
RectangleSectionsIcon,
TokenIcon,
} from "@navikt/aksel-icons";
import differenceInMonths from "date-fns/differenceInMonths";
@@ -33,6 +35,8 @@ const filtered = [
"ds_artikkel",
"komponent_artikkel",
"grunnleggende_landingsside",
"templates_landingsside",
"templates_artikkel",
"komponenter_landingsside",
"media.tag",
"editor",
@@ -231,6 +235,26 @@ export const structure = async (
)),
])
),
S.listItem()
.title("Mønster og Maler")
.icon(RectangleSectionsIcon)
.child(
S.list()
.title("Mønster og Maler")
.items([
S.documentListItem()
.title(`Landingsside`)
.schemaType(`templates_landingsside`)
.id(`templates_landingsside_id1`),
S.divider(),
...(await PanesWithCount(
"templates_artikkel",
templatesKategorier,
getClient,
S
)),
])
),
S.listItem()
.title("Komponenter")
.icon(ComponentIcon)
@@ -322,7 +346,7 @@ export const structure = async (
]) */
),
S.listItem()
.title("Komponent-eksempler Designsystemet")
.title("Eksempler/Templates")
.child(
S.documentList()
.title("Eksempler")
@@ -19,3 +19,5 @@ export * from "./publiseringsflyt";
export * from "./redirects";
export * from "./skrivehjelp";
export * from "./standalone-artikkel";
export * from "./templates/artikkel";
export * from "./templates/landingsside";
@@ -14,13 +14,26 @@ export const KodeEksempelDoc = defineType({
validation: (Rule) => Rule.required(),
readOnly: true,
}),
defineField({
title: "Toggle om eksemplet er et dir eller filnavn",
name: "dir",
type: "boolean",
title: "Variant",
name: "variant",
type: "string",
validation: (Rule) => Rule.required(),
initialValue: false,
readOnly: true,
options: {
list: [
{
title: "Eksempler",
value: "eksempler",
},
{
title: "Templates",
value: "templates",
},
],
layout: "radio",
},
}),
defineField({
title: "Filer",
@@ -33,24 +46,56 @@ export const KodeEksempelDoc = defineType({
name: "fil",
type: "object",
fields: [
{ title: "Title", name: "title", type: "string" },
{ title: "Filnavn", name: "navn", type: "string" },
{ title: "Innhold", name: "innhold", type: "string" },
{ title: "Beskrivelse", name: "description", type: "text" },
{ title: "Index", name: "index", type: "number" },
],
},
],
}),
defineField({
title: "Metadata",
name: "metadata",
type: "object",
readOnly: true,
fields: [
defineField({
title: "Version",
name: "version",
type: "number",
}),
defineField({
title: "Changelog",
name: "changelog",
type: "array",
of: [
{
title: "Changelog-entry",
name: "entry",
type: "object",
fields: [
{ title: "Description", name: "description", type: "string" },
{ title: "Version", name: "version", type: "number" },
{ title: "Date", name: "date", type: "string" },
],
},
],
}),
],
}),
],
preview: {
select: {
title: "title",
dir: "dir",
variant: "variant",
},
prepare(selection) {
const { title, dir } = selection;
const { title, variant } = selection;
return {
title,
subtitle: dir ? "Alle kode-eksempler" : "Spesifikt kode-eksempel",
subtitle: variant,
};
},
},
@@ -0,0 +1,89 @@
import { defineField, defineType } from "sanity";
import { templatesKategorier } from "../../../config";
import { artikkelPreview } from "../presets/artikkel-preview";
import { editorField } from "../presets/editors";
import { groups } from "../presets/groups";
import { hiddenFields } from "../presets/hidden-fields";
import { oppdateringsvarsel } from "../presets/oppdateringsvarsel";
import { SEOFields } from "../presets/seo";
import { kategoriSlug } from "../presets/slug";
import { titleField } from "../presets/title-field";
const prefix = "monster-maler/";
export const TemplatesArtikkel = defineType({
title: "Mønster/Maler artikkel",
name: "templates_artikkel",
type: "document",
groups,
...artikkelPreview("Mønster/Maler"),
fields: [
oppdateringsvarsel,
...hiddenFields,
titleField,
editorField,
defineField({
title: "Kategori",
name: "kategori",
type: "string",
validation: (Rule) => Rule.required(),
options: {
list: templatesKategorier.map((x) => ({
title: x.title,
value: x.value,
})),
layout: "radio",
},
}),
kategoriSlug(prefix),
defineField({
title: "Metadata",
name: "status",
group: "innhold",
type: "object",
fields: [
{
title: "Status",
name: "tag",
type: "string",
initialValue: "new",
options: {
list: [
{ title: "Beta", value: "beta" },
{ title: "New", value: "new" },
{ title: "Stable", value: "ready" },
{ title: "Deprecated", value: "deprecated" },
],
layout: "radio",
},
},
{
name: "bilde",
title: "Thumbnail",
type: "image",
},
],
options: {
collapsible: true,
collapsed: false,
},
}),
defineField({
title: "Sidebar index",
description:
"Overstyrer sortering av artikler i sidebar. Hvis feltet er tomt, sorteres den alfabetisk.",
name: "sidebarindex",
type: "number",
group: "settings",
}),
defineField({
title: "Innhold",
description:
"Innholdet i artikkelen er riktekst. Tips: klikk på ikon i høyre hjørne for å skrive i fullskjerm.",
name: "content",
type: "riktekst_templates",
group: "innhold",
}),
SEOFields,
],
});
@@ -0,0 +1,49 @@
import { HouseIcon } from "@navikt/aksel-icons";
import { defineField, defineType } from "sanity";
import { templatesKategorier } from "../../../config";
import { groups } from "../presets/groups";
import { SEOFields } from "../presets/seo";
const views = () => {
const list = [];
templatesKategorier.forEach((kat) => {
list.push(
defineField({
title: `Ingress ${kat.title}`,
name: `ingress_${kat.value}`,
type: "text",
rows: 2,
})
);
list.push(
defineField({
title: `Intro ${kat.title}`,
name: `intro_${kat.value}`,
type: "riktekst_standard",
})
);
});
return list;
};
export const TemplatesLandingSide = defineType({
title: "Landingsside Mønster og Maler",
name: "templates_landingsside",
type: "document",
groups,
fields: [
defineField({
title: "Intro",
name: "intro",
type: "text",
}),
...views(),
SEOFields,
],
preview: {
prepare: () => ({
title: "Landingsside Mønster og Maler",
media: HouseIcon,
}),
},
});
@@ -23,6 +23,10 @@ export const schema: SchemaPluginOptions = {
document.GrunnleggendeLandingSide,
document.GrunnleggendeArtikkel,
/* Møster og Maler */
document.TemplatesLandingSide,
document.TemplatesArtikkel,
/* God-praksis */
document.Tema,
document.GodPraksisArtikkel,
@@ -58,6 +62,7 @@ export const schema: SchemaPluginOptions = {
object.RiktekstKomponent,
object.RiktekstEnkel,
object.RiktekstGrunnleggende,
object.RiktekstTemplates,
object.RiktekstStandard,
object.RiktekstPrinsipp,
object.RiktekstStandalone,
@@ -20,19 +20,17 @@ export const KodeEksempler = defineType({
name: "dir",
type: "reference",
to: [{ type: "kode_eksempler_fil" }],
options: {
filter: "dir == true",
},
}),
],
preview: {
select: {
dir: "dir.title",
variant: "dir.variant",
},
prepare({ dir }) {
prepare({ dir, variant }) {
return {
title: "Komponent-demo",
subtitle: dir ?? "",
subtitle: `${variant} | ${dir}`,
media: ComponentIcon,
};
},
@@ -131,6 +131,7 @@ const Riktekst = (
type:
| "god-praksis"
| "grunnleggende"
| "templates"
| "komponent"
| "prinsipp"
| "standard"
@@ -169,6 +170,8 @@ const Riktekst = (
"token_ref",
];
const templates = ["kode_eksempler"];
const grunnleggende = ["spesial_seksjon"];
fields.push(...standard);
@@ -186,6 +189,9 @@ const Riktekst = (
case "accordion":
fields = [...accordion];
break;
case "templates":
fields.push(...templates);
break;
default:
break;
}
@@ -256,3 +262,11 @@ export const RiktekstStandalone = {
of: Riktekst("standalone"),
icon: FileTextIcon,
};
export const RiktekstTemplates = {
title: "Riktekst",
name: "riktekst_templates",
type: "array",
of: Riktekst("templates"),
icon: FileTextIcon,
};
@@ -1,11 +0,0 @@
import { getExampleFiles } from "../get-example-files";
describe("Reading code-examples files", () => {
test("getExampleFiles", () => {
const files = getExampleFiles();
expect(files.length).toBeTruthy();
});
});
export {};
@@ -1,89 +0,0 @@
import { getExampleFiles } from "../get-example-files";
import {
filterCode,
getDesc,
getIndex,
readExampleFile,
readExampleFiles,
} from "../read-example-files";
const d = `import { Button } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return <Button>Primary</Button>;
};`;
const testStr1 = `${d}
export default withDsExample(Example);
export const args = {
index: 0,
desc: "description-text"
};`;
const testStr2 = `${d}
export default withDsExample(Example);
export const args = {
};`;
const testStr3 = `${d}
export default withDsExample(Example);
`;
const testStr4 = `${d}
export default withDsExample(Example, {
variant: "full",
showBreakpoints: true,
});
export const args = {
};`;
const testStr4Res = `import { Button } from "@navikt/ds-react";
const Example = () => {
return <Button>Primary</Button>;
};`;
const codeRes = `import { Button } from "@navikt/ds-react";
const Example = () => {
return <Button>Primary</Button>;
};`;
describe("Reading code-examples", () => {
test("getDesc", () => {
expect(getDesc(testStr1)).toEqual("description-text");
expect(getDesc(testStr2)).toBeNull();
expect(getDesc(testStr3)).toBeNull();
});
test("getIndex", () => {
expect(getIndex(testStr1)).toEqual(0);
expect(getIndex(testStr2)).toEqual(1);
expect(getIndex(testStr3)).toEqual(1);
});
test("filterCode", () => {
expect(filterCode(testStr1)).toEqual(codeRes);
expect(filterCode(testStr4)).toEqual(testStr4Res);
});
test("readExampleFiles", () => {
const files = getExampleFiles();
expect(readExampleFiles(files[0].path.split("/")[0])).toBeTruthy();
});
test("readExampleFile", () => {
const files = getExampleFiles();
expect(readExampleFile(files[0].path)).toBeTruthy();
});
});
export {};
@@ -1,26 +0,0 @@
import path from "path";
import fs from "fs";
/**
* @returns array of all paths and if they are a dir or not
*/
export const getExampleFiles = (): { path: string; dir: boolean }[] => {
const examplePath = path.resolve(process.cwd(), `pages/eksempler`);
const res = [];
if (fs.existsSync(examplePath)) {
const dirs = fs
.readdirSync(examplePath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
dirs.map((dir) => {
const dirPath = path.resolve(process.cwd(), `pages/eksempler/${dir}`);
const files = fs.readdirSync(dirPath);
files.forEach((file) => res.push({ path: `${dir}/${file}`, dir: false }));
});
dirs.forEach((dir) => res.push({ path: `${dir}`, dir: true }));
}
return res;
};
@@ -1 +0,0 @@
export * from "./read-example-files";
@@ -1,105 +0,0 @@
import path from "path";
import fs from "fs";
import JSON5 from "json5";
export const getDesc = (str: string) => {
const args = str.match(/export const args = {([^}]+)}/)?.[1];
if (args) {
const obj = JSON5.parse(`{${args}}`);
return obj?.desc ?? null;
}
return null;
};
export const filterCode = (code: string) => {
const exampleList = code
.substring(0, code.indexOf("export const args ="))
.split("\n")
.filter((x) => !x.includes("examples/withDsExample"));
const storyIndex = exampleList.findIndex(
(x) => x === "/* Storybook story */"
);
if (storyIndex === -1) {
return exampleList
.join("\n")
.replace(/^\s*export default withDsExample\(Example[\s\S]*?;\s*/gm, "")
.trim();
}
return exampleList
.filter((_, idx) => idx < storyIndex || idx > storyIndex + 3)
.join("\n")
.replace(/^\s*export default withDsExample\(Example[\s\S]*?;\s*/gm, "")
.trim();
};
export const getIndex = (str: string) => {
const args = str.match(/export const args = {([^}]+)}/)?.[1];
if (args) {
const obj = JSON5.parse(`{${args}}`);
return obj?.index ?? 1;
}
return 1;
};
type FileT = { innhold: string; navn: string; description: string | null }[];
const sortResult = (res: FileT) => {
return res.sort((a, b) => {
return getIndex(a.innhold) - getIndex(b.innhold);
});
};
/**
*
* @param dirName Directory name
* @returns File-content for files in dir + filename
*/
export const readExampleFiles = (dirName: string): FileT => {
const examplePath = path.resolve(process.cwd(), `pages/eksempler/${dirName}`);
if (fs.existsSync(examplePath)) {
const files = fs.readdirSync(examplePath);
const res = files.map((file) => {
let code = "";
const filepath = path.resolve(
process.cwd(),
`pages/eksempler/${dirName}/${file}`
);
code = fs.readFileSync(filepath, "utf-8");
return {
innhold: code,
navn: file.replace(".tsx", ""),
description: getDesc(code),
};
});
return sortResult(res).map((x) => ({
...x,
innhold: filterCode(x.innhold),
}));
}
return [];
};
/**
*
* @param fileName example filename including dir: button/primary.tsx
* @returns File-content for file + filename
*/
export const readExampleFile = (
fileName: string
): { innhold: string; navn: string } => {
const examplePath = path.resolve(
process.cwd(),
`pages/eksempler/${fileName}`
);
let code = "";
code = fs.readFileSync(examplePath, "utf-8");
return {
innhold: filterCode(code),
navn: fileName.replace(".tsx", ""),
};
};
@@ -1,48 +0,0 @@
import dotenv from "dotenv";
import { readExampleFile, readExampleFiles } from ".";
import { noCdnClient } from "../../sanity/interface/client.server";
import { getExampleFiles } from "./get-example-files";
dotenv.config();
const createId = (s: string) =>
`kode_eksempelid_${s
.match(/\w/g)
?.join("")
?.match(/\D/g)
?.join("")}`.toLowerCase();
const main = async () => {
const token = process.env.SANITY_WRITE_KEY;
const transactionClient = noCdnClient(token).transaction();
const examples = getExampleFiles();
const docs = await noCdnClient(token).fetch(
`*[_type == "kode_eksempler_fil"]`
);
/* Delete fremoved examples */
for (const doc of docs) {
if (!examples.some((x) => doc._id === createId(x.path))) {
transactionClient.delete(doc._id);
}
}
for (const ex of examples) {
const data = {
_id: createId(ex.path),
_type: "kode_eksempler_fil",
title: ex.path,
dir: ex.dir,
filer: ex.dir ? readExampleFiles(ex.path) : [readExampleFile(ex.path)],
};
transactionClient.createOrReplace(data);
}
await transactionClient
.commit({ autoGenerateArrayKeys: true })
.then(() => console.log(`Oppdaterte kode-eksempler`))
.catch((e) => console.error(e.message));
};
main();
@@ -0,0 +1,60 @@
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
import RSS from "rss";
import { noCdnClient } from "../sanity/interface/client.server";
dotenv.config();
dotenv.config({ path: `.env.local` });
const token = process.env.SANITY_PRIVATE_NO_DRAFTS;
if (!token) {
throw new Error(
"Missing token 'SANITY_PRIVATE_NO_DRAFTS' when updating RSS-feed"
);
}
generateRssFeed();
async function generateRssFeed() {
const site_url = "https://aksel.nav.no";
const rss_path = "produktbloggen-rss.xml";
const rss_dir = "public/rss";
const query = `*[_type == "aksel_blogg"] | order(publishedAt desc) {
heading,
ingress,
publishedAt,
_id,
"slug": slug.current,
}`;
const bloggposts = await noCdnClient(token).fetch(query);
const feedOptions = {
title: "Produktbloggen - aksel.nav.no",
site_url,
feed_url: `${site_url}/rss/${rss_path}`,
pubDate: new Date(),
};
const feed = new RSS(feedOptions);
bloggposts.forEach((post) => {
feed.item({
title: post.heading,
description: post.ingress,
url: `${site_url}/${post.slug}`,
date: post.publishedAt,
});
});
const rssPath = path.resolve(process.cwd(), rss_dir);
if (!fs.existsSync(rssPath)) {
fs.mkdirSync(rssPath);
}
fs.writeFileSync(`${rssPath}/${rss_path}`, feed.xml({ indent: true }));
}
@@ -0,0 +1,31 @@
import { extractArgs } from "../parts/extract-args";
describe("Testing extractArgs function", () => {
test("extractArgs should return null on failed parsing", () => {
const code = `export const args {
title: "Tittel"
desc: "Description",
index 2
}`;
const args = extractArgs(code, "filename", "test");
expect(args).toBeNull();
});
test("extractArgs should parse args from codesnippet", () => {
const code = `export const args = {
title: "Tittel",
desc: "Description",
index: 2
}`;
const args = extractArgs(code, "filename", "test");
expect(args).toEqual({
title: "Tittel",
desc: "Description",
index: 2,
});
});
});
@@ -0,0 +1,36 @@
import fs from "fs";
import { extractMetadata } from "../parts/extract-metadata";
jest.mock("fs");
const sampleObj = {
version: 1,
changelog: [
{
description: "Oppdaterte X Y og Z",
date: "31.10.2023",
version: 1,
},
],
};
const sampleString = JSON.stringify(sampleObj, null, 2);
describe("Testing extractMetadata function", () => {
test("extractMetadata should return undefined if directory does not exist", () => {
(fs.existsSync as jest.Mock).mockReturnValue(false);
const metadata = extractMetadata("nonexistent", "templates");
expect(metadata).toBeUndefined();
});
test("extractMetadata should read meta.json from /pages/templates directory", () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readdirSync as jest.Mock).mockReturnValue(["file1.tsx", "meta.json"]);
(fs.readFileSync as jest.Mock).mockReturnValue(sampleString);
const metadata = extractMetadata("eksempler", "templates");
expect(metadata).toEqual(sampleObj);
});
});
@@ -0,0 +1,8 @@
import { filterCode } from "../parts/filter-code";
import { codeAfter, codeBefore } from "./mockdata";
test("filterCode should remove unwanted code", () => {
const codeResult = filterCode(codeBefore);
expect(codeResult).toEqual(codeAfter);
});
@@ -0,0 +1,15 @@
import { getDirectories } from "../parts/get-directories";
describe("Testing getDirectories function", () => {
test("getDirectories should list directories in /pages/eksempler", () => {
const folders = getDirectories("eksempler");
expect(folders.length).toBeGreaterThan(0);
});
test("getDirectories should list directories in /pages/templates", () => {
const folders = getDirectories("templates");
expect(folders.length).toBeGreaterThan(0);
});
});
@@ -0,0 +1,28 @@
export const codeBefore = `import { Alert } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<Alert variant="error">Noe gikk galt! Prøv igjen om noen minutter.</Alert>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 0,
};
`;
export const codeAfter = `import { Alert } from "@navikt/ds-react";
const Example = () => {
return (
<Alert variant="error">Noe gikk galt! Prøv igjen om noen minutter.</Alert>
);
};`;
@@ -0,0 +1,36 @@
import fs from "fs";
import { parseCodeFiles } from "../parts/parse-code-files";
import { codeAfter, codeBefore } from "./mockdata";
jest.mock("fs");
describe("Testing parseCodeFiles function", () => {
test("parseCodeFiles should return an empty array if directory does not exist", () => {
(fs.existsSync as jest.Mock).mockReturnValue(false);
const files = parseCodeFiles("nonexistent", "eksempler");
expect(files).toEqual([]);
});
test("parseCodeFiles should read files from /pages/eksempler directory", () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readdirSync as jest.Mock).mockReturnValue(["file1.tsx", "file2.tsx"]);
(fs.readFileSync as jest.Mock).mockReturnValue(codeBefore);
const files = parseCodeFiles("eksempler", "eksempler");
expect(files.length).toBeGreaterThan(0);
expect(files[0].innhold).toEqual(codeAfter);
});
test("parseCodeFiles should read files from /pages/templates directory", () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readdirSync as jest.Mock).mockReturnValue(["file1.tsx", "file2.tsx"]);
(fs.readFileSync as jest.Mock).mockReturnValue(codeBefore);
const files = parseCodeFiles("templates", "templates");
expect(files.length).toBeGreaterThan(0);
expect(files[0].innhold).toEqual(codeAfter);
});
});
@@ -0,0 +1,20 @@
import JSON5 from "json5";
import { ArgsT } from "../types";
export function extractArgs(
code: string,
fileName: string,
env?: "test"
): ArgsT | null {
const args = code.match(/export const args = {([^}]+)}/)?.[1];
if (!args) {
env !== "test" &&
console.warn(
`Missing args when parsing code for examples/template: ${fileName}`
);
return null;
}
return JSON5.parse(`{${args}}`) ?? null;
}
@@ -0,0 +1,29 @@
import fs from "fs";
import JSON5 from "json5";
import path from "path";
import { CodeExampleSchemaT } from "../../../types";
import { RootDirectoriesT } from "../types";
export function extractMetadata(
dirName: string,
rootDir: RootDirectoriesT
): CodeExampleSchemaT["metadata"] {
const codeDirPath = path.resolve(
process.cwd(),
`pages/${rootDir}/${dirName}`
);
if (
!fs.existsSync(codeDirPath) ||
!fs.readdirSync(codeDirPath).find((x) => x === "meta.json")
) {
return undefined;
}
const metadata = fs.readFileSync(
path.resolve(process.cwd(), `pages/${rootDir}/${dirName}/meta.json`),
"utf-8"
);
return JSON5.parse(metadata);
}
@@ -0,0 +1,21 @@
export function filterCode(code: string) {
const _code = code
.substring(0, code.indexOf("export const args ="))
.split("\n")
.filter((x) => !x.includes("examples/withDsExample"));
const storyIndex = _code.findIndex((x) => x === "/* Storybook story */");
if (storyIndex === -1) {
return _code
.join("\n")
.replace(/^\s*export default withDsExample\(Example[\s\S]*?;\s*/gm, "")
.trim();
}
return _code
.filter((_, idx) => idx < storyIndex || idx > storyIndex + 3)
.join("\n")
.replace(/^\s*export default withDsExample\(Example[\s\S]*?;\s*/gm, "")
.trim();
}
@@ -0,0 +1,23 @@
import fs from "fs";
import path from "path";
import { DirectoryDataT, RootDirectoriesT } from "../types";
/**
* Returns metadata for each 'page' found in directory
* @param directory
* @returns DirectoryDataT
*/
export function getDirectories(directory: RootDirectoriesT): DirectoryDataT {
const rootPath = path.resolve(process.cwd(), `pages/${directory}`);
if (!fs.existsSync(rootPath)) {
return [];
}
const folders = fs
.readdirSync(rootPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
return folders.map((dir) => ({ path: `${dir}` }));
}
@@ -0,0 +1,48 @@
import fs from "fs";
import path from "path";
import { FileArrayT, RootDirectoriesT } from "../types";
import { extractArgs } from "./extract-args";
import { filterCode } from "./filter-code";
import { sortResult } from "./sort";
export function parseCodeFiles(
dirName: string,
rootDir: RootDirectoriesT
): FileArrayT {
const codeDirPath = path.resolve(
process.cwd(),
`pages/${rootDir}/${dirName}`
);
if (!fs.existsSync(codeDirPath)) {
return [];
}
const codeFiles = fs
.readdirSync(codeDirPath)
.filter((x) => !x.includes(".json"));
const parsedCode: FileArrayT = codeFiles.map((file) => {
let code = "";
code = fs.readFileSync(
path.resolve(process.cwd(), `pages/${rootDir}/${dirName}/${file}`),
"utf-8"
);
const args = extractArgs(code, `pages/${rootDir}/${dirName}/${file}`);
return {
innhold: code,
title: args?.title ?? file.replace(".tsx", ""),
navn: file.replace(".tsx", ""),
description: args?.desc,
index: args?.index ?? 1,
};
});
return sortResult(parsedCode).map((x) => ({
...x,
innhold: filterCode(x.innhold),
}));
}
@@ -0,0 +1,7 @@
import { FileArrayT } from "../types";
export function sortResult(res: FileArrayT) {
return res.sort((a, b) => {
return a.index - b.index;
});
}
@@ -0,0 +1,16 @@
export type FileArrayT = {
innhold: string;
title: string;
navn: string;
index: number;
description?: string;
}[];
export type DirectoryDataT = { path: string }[];
export type ArgsT = {
title?: string;
index?: number;
desc?: string;
};
export type RootDirectoriesT = "eksempler" | "templates";
@@ -0,0 +1,98 @@
import dotenv from "dotenv";
import { noCdnClient } from "../../sanity/interface/client.server";
import { CodeExampleSchemaT } from "../../types";
import { extractMetadata } from "./parts/extract-metadata";
import { getDirectories } from "./parts/get-directories";
import { parseCodeFiles } from "./parts/parse-code-files";
import { RootDirectoriesT } from "./types";
dotenv.config();
const token = process.env.SANITY_WRITE_KEY;
if (!token) {
throw new Error("Missing token 'SANITY_WRITE_KEY' for updating sanity");
}
updateSanity("templates", false);
updateSanity("eksempler", false);
export async function updateSanity(
directory: RootDirectoriesT,
isDryRun: boolean = false
) {
const transactionClient = noCdnClient(token).transaction();
const folders = getDirectories(directory);
/* First we add/update examples */
for (const folder of folders) {
const data: CodeExampleSchemaT = {
_id: createId(folder.path),
_type: "kode_eksempler_fil",
title: folder.path,
variant: directory,
filer: parseCodeFiles(folder.path, directory).map((x) => ({
...x,
_key: x.navn.split(".")[0],
})),
metadata: extractMetadata(folder.path, directory),
};
transactionClient.createOrReplace(data);
}
await transactionClient
.commit({ autoGenerateArrayKeys: true, dryRun: isDryRun })
.then(() => console.log(`Oppdaterte ${directory}-dokumenter i sanity`))
.catch((e) => {
throw new Error(e.message);
});
const oldSanityDocuments = await noCdnClient(token).fetch(
`*[_type == "kode_eksempler_fil" && variant == "${directory}"]`
);
let deletedIds: string[] = [];
/* Delete old examples */
for (const document of oldSanityDocuments) {
if (!folders.some((folder) => document._id === createId(folder.path))) {
transactionClient.delete(document._id);
deletedIds.push(document._id);
}
}
await transactionClient
.commit()
.then(() => console.log(`Successfully deleted unused ${directory}`))
.catch((e) => {
/**
* Errormessage includes all ids that failed.
*/
deletedIds = deletedIds.filter((id) => e.message.includes(id));
console.log("\n");
console.log(
`Found ${deletedIds.length} ${directory} documents longer documented locally, but referenced in sanity.
How to fix:
- Go to links provided under and try to manually delete document.
- You will then be prompted to update referenced document before deleting.
- After updating reference(s) and deleting document(s) there is no need to run the script again.`
);
console.log(
JSON.stringify(
deletedIds.map(
(x) =>
`https://aksel.nav.no/admin/prod/desk/admin;eksemplerTemplates;${x}`
),
null,
2
)
);
throw new Error(
`Failed when deleting old ${directory}-documentation from sanity, see warning above.`
);
});
}
function createId(s: string) {
return `kode_eksempelid_${s.match(/\w/g)?.join("")}`.toLowerCase();
}
+40 -3
View File
@@ -2,6 +2,7 @@ import {
bloggKategorier,
grunnleggendeKategorier,
komponentKategorier,
templatesKategorier,
} from "../sanity/config";
export type ResolveSlugT<T> = Omit<T, "slug"> & {
@@ -138,6 +139,24 @@ export interface AkselGrunnleggendeDocT
};
}
export interface AkselTemplatesDocT extends DocumentT<"ds_artikkel">, ArticleT {
kategori: (typeof templatesKategorier)[number]["value"];
status: {
tag?: "beta" | "new" | "ready" | "deprecated";
unsafe?: boolean;
bilde?: any;
};
intro: {
body?: any[];
brukes_til: string[];
brukes_ikke_til?: string[];
};
content: any[];
updateInfo?: {
lastVerified?: string;
};
}
export type AkselSidebarT = Array<{
heading: string;
slug: string;
@@ -282,13 +301,31 @@ export interface RelatertInnholdT {
>;
}
export type CodeExampleSchemaT = {
_id: string;
_type: string;
title: string;
variant: "eksempler" | "templates";
filer: {
title: string;
innhold: string;
navn: string;
index: number;
description?: string;
}[];
metadata?: {
version: number;
changelog: { description: string; version: number; date: string }[];
};
};
export interface CodeExamplesT {
title: string;
dir: {
title: string;
filer: Array<
ArrayObjectT<{ navn: string; innhold: string; description?: string }>
>;
filer: Array<ArrayObjectT<CodeExampleSchemaT["filer"][0]>>;
metadata?: CodeExampleSchemaT["metadata"];
variant: CodeExampleSchemaT["variant"];
};
}
+3 -2
View File
@@ -1,5 +1,5 @@
import { allArticleDocuments } from "../sanity/config";
import Fuse from "fuse.js";
import { allArticleDocuments } from "../sanity/config";
export const searchOptions: {
[K in (typeof allArticleDocuments)[number]]: {
@@ -11,7 +11,8 @@ export const searchOptions: {
komponent_artikkel: { display: "Komponenter", index: 0 },
aksel_artikkel: { display: "God praksis", index: 1 },
ds_artikkel: { display: "Grunnleggende", index: 2 },
aksel_blogg: { display: "Blogg", index: 3 },
templates_artikkel: { display: "Mønster og Maler", index: 3 },
aksel_blogg: { display: "Blogg", index: 4 },
aksel_prinsipp: { display: "Prinsipper", index: 5 },
aksel_standalone: { display: "Unike sider", index: 6, hidden: true },
};
+16 -16
View File
@@ -15,7 +15,7 @@
"chromatic": "npx chromatic --project-token x3xqdfgkujg --build-script-name build:storybook",
"build:storybook": "storybook build",
"test": "yarn workspaces foreach -p run test",
"lint": "yarn eslint . && yarn stylelint @navikt/**/*.css",
"lint": "yarn eslint --rule \"import/no-cycle:error\" . && yarn stylelint @navikt/**/*.css",
"lint:css": "yarn stylelint @navikt/**/*.css",
"changeset": "changeset",
"version": "changeset version && yarn changelog",
@@ -120,19 +120,19 @@
},
"devDependencies": {
"@changesets/cli": "2.26.2",
"@storybook/addon-a11y": "^7.4.1",
"@storybook/addon-essentials": "^7.4.1",
"@storybook/addon-interactions": "^7.4.1",
"@storybook/addon-storysource": "^7.4.1",
"@storybook/blocks": "^7.4.1",
"@storybook/csf-tools": "^7.4.1",
"@storybook/jest": "^0.2.2",
"@storybook/manager-api": "^7.4.1",
"@storybook/nextjs": "^7.4.1",
"@storybook/react": "^7.4.1",
"@storybook/react-vite": "^7.4.1",
"@storybook/testing-library": "^0.2.0",
"@storybook/theming": "^7.4.1",
"@storybook/addon-a11y": "^7.5.3",
"@storybook/addon-essentials": "^7.5.3",
"@storybook/addon-interactions": "^7.5.3",
"@storybook/addon-storysource": "^7.5.3",
"@storybook/blocks": "^7.5.3",
"@storybook/csf-tools": "^7.5.3",
"@storybook/jest": "^0.2.3",
"@storybook/manager-api": "^7.5.3",
"@storybook/nextjs": "^7.5.3",
"@storybook/react": "^7.5.3",
"@storybook/react-vite": "^7.5.3",
"@storybook/testing-library": "^0.2.2",
"@storybook/theming": "^7.5.3",
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
@@ -146,7 +146,7 @@
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^0.6.14",
"eslint-plugin-storybook": "^0.6.15",
"eslint-plugin-testing-library": "^6.0.2",
"fast-glob": "3.2.11",
"fs-extra": "^10.0.1",
@@ -158,7 +158,7 @@
"react": "^18.0.0",
"react-docgen-typescript": "2.2.2",
"react-dom": "^18.0.0",
"storybook": "^7.4.1",
"storybook": "^7.5.3",
"stylelint": "^14.8.5",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard": "^25.0.0",
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more