Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] c0f334061b Version Packages (#2482)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-16 14:01:11 +01:00
Ken 79bddd9caa Primitives er nå mer spesifikk (#2469)
* 🐛 Mer spesifikke primitives

* 🔥 Fjernet story

* 📝 changeset

* 🎨 Oppdatert mappings
2023-11-16 11:41:31 +01:00
Ken 4a68409898 🎨 Oppdatert overridablecomponent (#2474)
* 🎨 Oppdatert overridablecomponent

* 📝 Kommentarer
2023-11-16 11:30:15 +01:00
Ken 786bff67ab 🔥 Fjernet assignees fra issue-templates 2023-11-15 13:14:59 +01:00
Ken 8a17caea6c 🐛 Fjernet TableOfContents fra bloggsider 2023-11-14 21:06:33 +01:00
Ken 3f43d0bf3b 🐛 Kjører nå ikke playroom build ved boot 2023-11-14 20:29:29 +01:00
Ken 26541984ac [aksel.nav.no] Sidebar & TableOfContents refactor (#2470)
* 🎨 toc setup

* 🎉 Implementert TOC-generering

* 💄 Nytt design på Toc

* 🎨 forenklet toc-styling

* 🎨 Scroll ved overflow

* 🎨 Bruker ny TOC på aksel

* 🐛 Bedre word-wrap

* 💄 Shadow on toc

* 🐛 Fikset fokusmarkering

* :trukket ut generateSidebar funksjon

* 🧪 tester for sidebar-generering

* 💄 Bedre design på toc

* 🎨 Mindre refactor av TOC

* 🎉 Ny sidebar

* 🎨 Oppdatert bruk av sidebar

* 🐛 Oppdatert sidebar-story

* 💄 Justert sidebar

* 💄 Indent på toc

* 💄 Hide scrollbar on toc

* 🎨 scrollfade toc

* ♻️ Flyttet scrollfade ut i egen komponent

* 💄 Smoother scroll-fade

* ⚡ Færre state-updates i toc

* 🎨 data atrrb for css

* 🎨 data-attrb i sidebar

* ⚡ Mindre css dom updates

* 🎨 Fjerner emojies fra toc

* 🎨 automatisk scroll-top

* 🎨 Next-link for sidebar

* 🎨 Toggle toc-button onClick

* 🐛 Fikset scroll-padding

* 🐛 Fikset fokushåndering av toc

* 🎨 Mindre tydelig fade for firefox

* 🔥 Fjernet console.log

* 🐛 Fikset recalculation ved page-change

* 🐛 Reversert til next 13.2.1
2023-11-14 20:10:42 +01:00
Julian NymarkandKen 10303602aa playroom (#2413)
* 🚧 playroom

testing it out, needs yarn run playroom:build before yarn dev (in aksel.nav.no)

* 🚧 width and height for iframe

* 🚚 move playroom into its own module

* 🚧

* 🐛 Fikset lasting av ts-filer

* build playroom & copy dist to public/

* 🚧 yarn maintenance

* 🚧 build the playroom before copy

* ✨ now examples might work in the sandbox? maybe 90% of them?!

* ✨ + 🔥 + ♻️

- remove route for /sandbox, in public/ means it gets served regardless?
- for each code example:
   - use jscodeshift to remove imports
   - also transpile to ESNext
- bump jscodeshift to 0.15.1 (for all uses)
- the sandbox button now sends you to /sandbox/index.html

* :refactor: j -> parse

* add non-ideal string replacer to make Examples work in Playroom

* ♻️ bring back CodeSandox link as fallback

* 🚚 Sandbox -> parts/Sandbox

* 🚚🚚 Sandbox -> parts/Sandbox

* seemingly working smoke tests for all sandbox examples

* ♻️ shuffle example code to work well with playroom

* ♻️ shuffle example code to work in sandbox (again)

* update sanity eksempler, do base64 url code work there

* PR comment fixes

* 📝 Sanity-schema for sandbox-snippets

* 🚚 generic cubeIcon -> codesandboxLogo

* 🔥 unused prop

* ♻️ do not run all tests in smoketest

* 🐛 pr comment fixes

* PR comment fixes, remove unneeded domcontentloaded await in tests

* ♻️ restructure so <Example> comes first

* 🐛 sandbox tests now for templates and examples

* 🐛 Fikset imports

* 🐛 disable some sandbox tests (for templates)

* 🐛 forgot to disable one template test

---------

Co-authored-by: Ken <ken.aleksander@gmail.com>
2023-11-13 15:12:52 +01:00
Ken 42254543d8 [aksel.nav.no] ♻️ Ryddet opp i imports, kodestruktur og ubrukte configs/moduler (#2465)
* 🚚 Oppdatert layout exports

* ⚡ Nextjs-config kan nå bruke webpack-builder

* 🐛 reverserte til gammel image-config i next.config

* 🚚 Oppdaterte assets exports

* 🚚 Fjernet barrel-exports av komponenter

* 🚚 Flyttet types

* 🚚 Flyttet sanityblock og withpreview

* 🚚 Flyttet styles inn i components

* ♻️ Forenklet jest-config

* 🚚 Flyttet logger

* ⚡ Kjører nå ikke middleware på alle side

* ♻️ next config analyze setup

* 🔥 fjernet getdocumentstmp

* 🔥 Fjernet getAkselDocuments

* 🎨 Forenklet imagebuilder

* ♻️ Fjernet ubrukte tailwind configs

* 🎨 Grupperer changelog med minor

* 🔥 Fjernet duplicate changelog

* 🎨 Ny ts alias

* ♻️ Refactor av utils dir

* ♻️ Header er nå default export

* ♻️ Byttet ut deprecated ingress med bodylong

* ♻️ Oppdatert assets export/imports

* ♻️ Oppdatert website/sanity imports

* 🐛 henter ut logger i dockerfile

* ⚡ Enda mer filtrering i middleware

* 🐛 Riktig generering av sider for e2e-sider

* 🐛 Fikset import i codeexamples update

* 🐛 la til mønster og maler i hamburgermeny
2023-11-10 18:44:42 +01:00
Ken 81c84e4817 📝 Liten M i Maler 2023-11-10 14:51:10 +01:00
Ken b441175028 📝 Oppdatert readme 2023-11-10 14:00:21 +01:00
dependabot[bot] 75362a0dc1 ⬆️ Bump axios from 1.3.6 to 1.6.0 in /@navikt/aksel (#2467)
Bumps [axios](https://github.com/axios/axios) from 1.3.6 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.3.6...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 11:12:38 +01:00
Ken d8732ffa0e 🎉 Lagt til Mønster og Maler i header 2023-11-10 10:58:16 +01:00
github-actions[bot]andgithub-actions[bot] 0ea9155085 Version Packages (#2466)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-10 10:40:12 +01:00
Ken 92b9687ed1 Håndterer target-detection bedre i ExpandableRow (#2461)
* 🎨 Håndterer nå target-detection bedre

* 🎨 Forenklet if-tester, tester 2x nested elementer
2023-11-10 10:36:34 +01:00
Ken 1c022b14c9 🎨 Lagt til closeOnBackdropClick for mobilmeny på Aksel 2023-11-08 21:16:17 +01:00
Ken abdeb616d2 🎨 Fikste søk + codeexamples for templates (#2462) 2023-11-08 20:41:47 +01:00
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
571 changed files with 10044 additions and 6300 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
View File
@@ -2,8 +2,6 @@ name: Ny komponent
description: Send et ønske om en eller flere nye komponenter
title: "[Ny komponent]: "
labels: ["nytt ✨", "forespørsel 🥰", "komponenter 🧩"]
assignees:
- KenAJoh
body:
- type: markdown
attributes:
-2
View File
@@ -2,8 +2,6 @@ name: Nytt ikon
description: Send et ønske om et eller flere nye ikoner
title: "[Nytt ikon]: "
labels: ["nytt ✨", "ikoner 🖼", "forespørsel 🥰"]
assignees:
- KenAJoh
body:
- type: markdown
attributes:
@@ -2,8 +2,6 @@ name: Innspill til komponent
description: Send inn innspill til komponent
title: "[Innspill til komponent]: "
labels: ["forespørsel 🥰", "komponenter 🧩", "nytt ✨", "beta 🧪"]
assignees:
- KenAJoh
body:
- type: markdown
attributes:
-2
View File
@@ -2,8 +2,6 @@ name: Innspill til ikon
description: Send inn innspill til ikon
title: "[Tilbakemelding på ikon]: "
labels: ["forespørsel 🥰", "ikoner 🖼️"]
assignees:
- KenAJoh
body:
- type: markdown
attributes:
+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
+8
View File
@@ -1,5 +1,13 @@
# @navikt/aksel-icons
## 5.10.2
## 5.10.1
## 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.2",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+8
View File
@@ -1,5 +1,13 @@
# @navikt/aksel-stylelint
## 5.10.2
## 5.10.1
## 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.2",
"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.2",
"@navikt/ds-tokens": "^5.10.2",
"@types/jest": "^29.0.0",
"concurrently": "7.2.1",
"copyfiles": "2.4.1",
+28
View File
@@ -1,5 +1,33 @@
# @navikt/aksel
## 5.10.2
### Patch Changes
- Updated dependencies [[`79bddd9ca`](https://github.com/navikt/aksel/commit/79bddd9caa9467b60e1e0c1c15b16f76dccbc304)]:
- @navikt/ds-css@5.10.2
## 5.10.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.10.1
## 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
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "5.9.1",
"version": "5.10.2",
"description": "Aksel command line interface. Handles css-imports, codemods and more",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -29,8 +29,8 @@
},
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
"dependencies": {
"@navikt/ds-css": "5.9.1",
"axios": "1.3.6",
"@navikt/ds-css": "5.10.2",
"axios": "1.6.0",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
"commander": "10.0.1",
@@ -38,7 +38,7 @@
"fast-glob": "3.2.11",
"figlet": "1.6.0",
"is-git-clean": "1.1.0",
"jscodeshift": "0.13.1",
"jscodeshift": "^0.15.1",
"jscodeshift-add-imports": "1.0.10",
"lodash": "4.17.21",
"react-scanner": "^1.1.0"
+16
View File
@@ -1,5 +1,21 @@
# @navikt/ds-css
## 5.10.2
### Patch Changes
- Primtives: CSS-import for primitives er nå mer spesifikk ([#2469](https://github.com/navikt/aksel/pull/2469))
## 5.10.1
## 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
+5 -1
View File
@@ -35,6 +35,10 @@ const StyleMappings = {
component: "Box",
main: primitivesCss,
},
{
component: "Page",
main: primitivesCss,
},
{
component: "HGrid",
main: primitivesCss,
@@ -183,7 +187,7 @@ const StyleMappings = {
{
component: "DatePicker",
main: "date.css",
dependencies: [typoCss, primitivesCss, "button.css", "popover.css"],
dependencies: [typoCss, "button.css", "popover.css", primitivesCss],
},
{
component: "MonthPicker",
+1 -1
View File
@@ -1,7 +1,6 @@
@charset "UTF-8";
@import "baseline/index.css";
@import "typography.css";
@import "primitives/index.css";
@import "grid.css";
@import "accordion.css";
@import "alert.css";
@@ -34,3 +33,4 @@
@import "table.css";
@import "tabs.css";
@import "list.css";
@import "primitives/index.css";
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "5.9.1",
"version": "5.10.2",
"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.2",
"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);
}
}
+42
View File
@@ -1,5 +1,47 @@
# @navikt/ds-react
## 5.10.2
### Patch Changes
- OverridableComponent: Fungerer nå bedre ved bruk av typeof <komponent> ([#2474](https://github.com/navikt/aksel/pull/2474))
- Updated dependencies []:
- @navikt/ds-tokens@5.10.2
- @navikt/aksel-icons@5.10.2
## 5.10.1
### Patch Changes
- Table: Håndterer nå sjekk for interaktive elementer i ExpandableRow bedre ([#2461](https://github.com/navikt/aksel/pull/2461))
- Updated dependencies []:
- @navikt/ds-tokens@5.10.1
- @navikt/aksel-icons@5.10.1
## 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.2",
"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.2",
"@navikt/ds-tokens": "^5.10.2",
"@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";
@@ -1,8 +1,8 @@
import React from "react";
import type { Meta } from "@storybook/react";
import React from "react";
import { Box, HStack, VStack } from "../..";
import { BodyLong } from "../../typography";
import { Bleed } from "./Bleed";
import { Box, HStack, VStack } from "../..";
export default {
title: "ds-react/Primitives/Bleed",
@@ -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}
/>
);
}
);
+17 -5
View File
@@ -81,11 +81,8 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
e.stopPropagation();
};
const onRowClick = (e) => {
if (e.target.nodeName === "TD" || e.target.nodeName === "TH") {
expansionHandler(e);
}
};
const onRowClick = (e) =>
!isInteractiveTarget(e.target) && expansionHandler(e);
return (
<>
@@ -143,4 +140,19 @@ export const ExpandableRow: ExpandableRowType = forwardRef(
}
);
function isInteractiveTarget(elm: HTMLElement) {
if (elm.nodeName === "TD" || elm.nodeName === "TH" || !elm.parentElement) {
return false;
}
if (
["BUTTON", "DETAILS", "LABEL", "SELECT", "TEXTAREA", "INPUT", "A"].includes(
elm.nodeName
)
) {
return true;
}
return isInteractiveTarget(elm.parentElement);
}
export default ExpandableRow;
@@ -0,0 +1,100 @@
import { expect, jest } from "@storybook/jest";
import { userEvent, within } from "@storybook/testing-library";
import React from "react";
import { Table } from "../..";
export default {
title: "ds-react/Table/Tests",
component: Table,
};
export const ClickableRowTest = {
render: ({ onOpenChange }) => {
return (
<>
<Table zebraStripes>
<Table.Header>
<Table.Row>
<Table.HeaderCell aria-hidden />
<Table.HeaderCell aria-hidden />
<Table.HeaderCell aria-hidden />
<Table.HeaderCell aria-hidden />
<Table.HeaderCell aria-hidden />
<Table.HeaderCell aria-hidden />
</Table.Row>
</Table.Header>
<Table.Body>
<Table.ExpandableRow
content={<div>placeholder row 2</div>}
togglePlacement="right"
data-testid="row1"
expandOnRowClick
onOpenChange={onOpenChange}
>
<Table.DataCell>
<div data-testid="cell1">Should be clickable</div>
</Table.DataCell>
<Table.DataCell data-testid="cell2">
Should also be clickable
</Table.DataCell>
<Table.DataCell>
<button data-testid="cell3">Should not be clickable</button>
</Table.DataCell>
<Table.DataCell>
<div>
<div>
<button data-testid="cell4">
Nested should not be clickable
</button>
</div>
</div>
</Table.DataCell>
<Table.DataCell>
<div>
<div>
<button data-testid="cell4">
<span>2x nested should not be clickable</span>
</button>
</div>
</div>
</Table.DataCell>
</Table.ExpandableRow>
</Table.Body>
</Table>
</>
);
},
args: {
onOpenChange: jest.fn(),
},
play: async ({ canvasElement, args }) => {
args.onOpenChange.mockClear();
const canvas = within(canvasElement);
const cell1 = canvas.getByText("Should be clickable");
const cell2 = canvas.getByText("Should also be clickable");
const cell3 = canvas.getByText("Should not be clickable");
const cell4 = canvas.getByText("Nested should not be clickable");
const cell5 = canvas.getByText("2x nested should not be clickable");
await userEvent.click(cell1);
expect(args.onOpenChange.mock.calls).toHaveLength(1);
await userEvent.click(cell1);
expect(args.onOpenChange.mock.calls).toHaveLength(2);
await userEvent.click(cell2);
expect(args.onOpenChange.mock.calls).toHaveLength(3);
await userEvent.click(cell2);
expect(args.onOpenChange.mock.calls).toHaveLength(4);
await userEvent.click(cell3);
expect(args.onOpenChange.mock.calls).toHaveLength(4);
await userEvent.click(cell4);
expect(args.onOpenChange.mock.calls).toHaveLength(4);
await userEvent.click(cell5);
expect(args.onOpenChange.mock.calls).toHaveLength(4);
},
};
@@ -1,11 +1,28 @@
import React from "react";
/**
* This interface represents a component that can be overridden with different props and elements.
* @template Component The type of the props for the component.
* @template Element The type of the HTML element that the component renders.
*/
export interface OverridableComponent<Component, Element extends HTMLElement> {
/**
* This is a function type that takes props and returns a React functional component.
* @param props The props for the component, which are a combination of the Component type and React.RefAttributes for the Element type.
* @returns A React functional component.
*/
(props: Component & React.RefAttributes<Element>): ReturnType<React.FC>;
/**
* This is a function type that takes props and returns a React functional component.
* It allows for the 'as' prop to override the type of element that the component renders.
* @template As The type of the element that the component should render as.
* @param props The props for the component, which are a combination of the Component type, the 'as' prop, and any other props that are not part of the Component type or the 'as' prop.
* @returns A React functional component.
*/
<As extends React.ElementType>(
props: {
as: As;
as?: As;
} & Component &
Omit<React.ComponentPropsWithRef<As>, keyof Component | "as">
): ReturnType<React.FC>;
+8
View File
@@ -1,5 +1,13 @@
# @navikt/ds-tailwind
## 5.10.2
## 5.10.1
## 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.2",
"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.2",
"@types/jest": "^29.0.0",
"color": "4.2.3",
"jest": "^29.0.0",
+8
View File
@@ -1,5 +1,13 @@
# @navikt/ds-tokens
## 5.10.2
## 5.10.1
## 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.2",
"description": "Design-tokens for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
+430 -858
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -4,7 +4,7 @@ ENV NODE_ENV production
WORKDIR /app
COPY aksel.nav.no/website/.next/standalone ./
COPY aksel.nav.no/website/next-logger.config.js ./
COPY aksel.nav.no/website/config/next-logger.config.js ./
ENV NODE_OPTIONS '-r next-logger'
+1 -1
View File
@@ -19,7 +19,7 @@ Det publiserte studioet finner man på [aksel.nav.no/admin](https://aksel.nav.no
### Bidrag
Branch `main` blir pusher rett til prod -> aksel.nav.no. For å utvikle nye features kan branch `next` brukes, som ved push publiseres til devmiljø https://aksel.ekstern.dev.nav.no/. Kan også publisere andre branches til dev ved bruk av workflow-dispatch.
Branch `main` pusher rett til prod -> aksel.nav.no. For å utvikle nye features kan branch `next` brukes, som ved push publiseres til devmiljø https://aksel.ekstern.dev.nav.no/. Kan også publisere andre branches til dev ved bruk av workflow-dispatch.
## Kode-eksempler
+7 -6
View File
@@ -12,14 +12,15 @@
"test": "yarn workspace website test"
},
"workspaces": [
"website"
"website",
"playroom"
],
"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.2",
"@navikt/ds-css": "^5.10.2",
"@navikt/ds-react": "^5.10.2",
"@navikt/ds-tailwind": "^5.10.2",
"@navikt/ds-tokens": "^5.10.2"
},
"devDependencies": {
"prettier-plugin-tailwindcss": "^0.2.3"
+5
View File
@@ -0,0 +1,5 @@
# playroom
https://github.com/seek-oss/playroom
This is the playroom / code sandbox that is built as a standalone static app, and then it's added to the aksel website through an iframe.
+49
View File
@@ -0,0 +1,49 @@
/**
* @type {import('@babel/core').TransformOptions}
*/
module.exports = function (api) {
const envName = api.env();
const development = envName === "development" || envName === "test";
return {
presets: [
[
"@babel/preset-env",
{ useBuiltIns: "entry", corejs: "3.0", bugfixes: true },
],
["@babel/preset-typescript"],
["@babel/preset-react", { development, useBuiltIns: true }],
],
assumptions: {
setPublicClassFields: true,
privateFieldsAsProperties: true,
// nothing accesses `document.all`:
noDocumentAll: true,
// nothing relies on class constructors invoked without `new` throwing:
noClassCalls: true,
// nothing should be relying on tagged template strings being frozen:
mutableTemplateObject: true,
// nothing is relying on Function.prototype.length:
ignoreFunctionLength: true,
// nothing is relying on mutable re-exported bindings:
constantReexports: true,
// don't bother marking Module records non-enumerable:
enumerableModuleMeta: true,
// nothing uses [[Symbol.toPrimitive]]:
// (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive)
ignoreToPrimitiveHint: true,
// nothing relies on spread copying Symbol keys: ({...{ [Symbol()]: 1 }})
objectRestNoSymbols: true,
// nothing relies on `new (() => {})` throwing:
noNewArrows: true,
// transpile object spread to assignment instead of defineProperty():
setSpreadProperties: true,
// nothing should be using custom iterator protocol:
skipForOfIteratorClosing: true,
// nothing inherits from a constructor function with explicit return value:
superIsCallableConstructor: true,
// nothing relies on CJS-transpiled namespace imports having all properties prior to module execution completing:
noIncompleteNsImportDetection: true,
},
babelrcRoots: ["."],
};
};
+22
View File
@@ -0,0 +1,22 @@
{
"name": "playroom",
"scripts": {
"start": "playroom start",
"build:playroom": "playroom build"
},
"devDependencies": {
"@babel/preset-env": "^7.23.2",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.2",
"@navikt/ds-css": "^5.9.2",
"@navikt/ds-react": "^5.9.2",
"babel-loader": "^9.1.3",
"babel-plugin-preval": "^5.1.0",
"css-loader": "^6.8.1",
"playroom": "^0.32.1",
"style-loader": "^3.3.3"
},
"dependencies": {
"react": "^18.0.0"
}
}
+61
View File
@@ -0,0 +1,61 @@
// Note: Hot module reloading for dev has been turned off for this environment
// Why? We embed playroom in an iframe, and that iframe is loaded from the public asset folder
// So we need to build into that public asset folder. Hence no dev mode and HMR
// Workaround: restart the next dev server after each change to the playroom config and associated files.
const path = require("path");
module.exports = {
baseUrl: "./",
components: path.resolve("./src/components.ts"),
outputPath: path.resolve("./dist"),
// Optional:
title: "Aksel Sandbox",
snippets: path.resolve("./src/snippets.ts"),
widths: [480, 768, 1024, 1280],
port: 9000,
frameComponent: path.resolve("./src/FrameComponent.tsx"),
useScope: path.resolve("./src/useScope.tsx"),
openBrowser: false,
paramType: "search", // default is 'hash'
iframeSandbox: "allow-scripts allow-same-origin",
exampleCode: `
<Heading>This is a sandbox!</Heading>
<HStack gap="10">
<Box> box 1</Box> <Box> box 2</Box> <Box> box 3</Box>
</HStack>
<Button>test</Button>
<BodyShort>This is a bodyshort</BodyShort>
`,
webpackConfig: () => ({
module: {
rules: [
{
test: /\.(js|ts|tsx)$/,
include: [path.resolve("./")],
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
[
require.resolve("@babel/preset-env"),
{ shippedProposals: true },
],
require.resolve("@babel/preset-react"),
require.resolve("@babel/preset-typescript"),
],
plugins: [require.resolve("babel-plugin-preval")],
},
},
],
},
{
test: /\.css$/i,
include: [require.resolve("@navikt/ds-css")],
use: ["style-loader", "css-loader"],
},
],
},
}),
};
@@ -0,0 +1,8 @@
import "@navikt/ds-css";
import * as React from "react";
const FrameComponent = ({ children }: { children: React.ReactNode }) => {
return <div id="sandbox-wrapper">{children}</div>;
};
export default FrameComponent;
+2
View File
@@ -0,0 +1,2 @@
export * from "@navikt/aksel-icons";
export * from "@navikt/ds-react";
+29
View File
@@ -0,0 +1,29 @@
const snippets = [
{
group: "Templates",
name: "Single Component",
code: `<Button variant="primary">Button</Button>`,
},
{
group: "Templates",
name: "Multiple Components",
code: ` <Button variant="primary">First</Button>
<Button>Second</Button>`,
},
{
group: "Templates",
name: "Component with state",
code: `{
(() => {
const [count, setCount] = React.useState(0);
return (
<Button variant="primary" onClick={() => setCount(count + 1)}>Count: {count}</Button>
)
})()
}
`,
},
];
export default snippets;
+2
View File
@@ -0,0 +1,2 @@
public/sandbox
test-results/
+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.
@@ -199,3 +199,20 @@ export const StorybookIcon = ({ ...props }) => (
</g>
</svg>
);
export const CodeSandboxLogo = ({ ...props }) => (
<svg
width="24"
height="24"
fill="none"
viewBox="8 3 24 24"
aria-hidden="true"
focusable="false"
{...props}
>
<path
fill="currentColor"
d="M20.54 25a.681.681 0 00.158-.074l6.174-3.69c.734-.438 1.128-.885 1.128-2.084v-6.444c0-.248-.016-.447-.063-.629l-7.397 4.434zm-8.477-12.921l7.397 4.434V25a.514.514 0 01-.15-.074l-6.182-3.69c-.726-.438-1.128-.885-1.128-2.084v-6.444c0-.248.016-.447.063-.629zM20.004 7c.443 0 .9.143 1.359.406l.068.04 5.567 3.326c.155.088.284.177.394.282l.04.04-7.428 4.426-7.436-4.425c.11-.109.233-.198.378-.286l.063-.037 4.07-2.442h.002l1.488-.883c.434-.262.868-.415 1.296-.443l.071-.003z"
></path>
</svg>
);
@@ -1,6 +1,6 @@
import React from "react";
const Logo = (props) => (
const AkselLogo = (props) => (
<svg
width="119"
height="26"
@@ -43,4 +43,4 @@ const Logo = (props) => (
</svg>
);
export default Logo;
export default AkselLogo;
@@ -1 +0,0 @@
export * from "./Icons";
@@ -1,5 +1,5 @@
import { dateStr } from "components/website-modules/utils/date-string";
import { useEffect, useState } from "react";
import { dateStr } from "@/utils";
export const useFormatedDate = (date: string) => {
const [_date, setDate] = useState(null);
@@ -8,5 +8,6 @@ export const useFormatedDate = (date: string) => {
const handleDate = async () => setDate(await dateStr(date));
handleDate();
}, [date]);
return _date;
};
@@ -2,7 +2,7 @@ import { useEffect } from "react";
// https://stackoverflow.com/questions/38588346/anchor-a-tags-not-working-in-chrome-when-using/38588927#38588927
// https://github.com/vercel/next.js/discussions/13134
export function useScrollToHashOnPageLoad(): void {
export function useHashScroll(): void {
useEffect(() => {
if (window.location.hash) {
const hash = window.location.hash;
@@ -13,9 +13,3 @@ export function useScrollToHashOnPageLoad(): void {
}
}, []);
}
export const removeEmojies = (s: string) =>
s.replace(
/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g,
""
);
@@ -0,0 +1,13 @@
import { createContext, useContext } from "react";
export const SanityDocIdContext = createContext<{ id?: string }>(null);
export function useSanityDocId() {
const ctx = useContext(SanityDocIdContext);
if (!ctx) {
console.error(
"useSanityDocId has to be used inside SanityDocIdContext.provider"
);
}
return ctx;
}
-4
View File
@@ -1,4 +0,0 @@
export * from "./assets";
export * from "./website-modules/BreadCrumbs";
export * from "./website-modules/toc/TOC";
export * from "./website-modules/utils";
@@ -1,18 +1,13 @@
import { FigmaIcon, GithubIcon, SlackIcon } from "@/assets/Icons";
import AkselLogo from "@/assets/Logo";
import { amplitudeLogNavigation } from "@/logging";
import { BodyShort, Heading, Link } from "@navikt/ds-react";
import { FigmaIcon, GithubIcon, SlackIcon } from "components/assets";
import Logo from "components/assets/Logo";
import dynamic from "next/dynamic";
/* import FooterForm from "./FooterForm"; */
export const EditButton = dynamic(
() => import("components/website-modules/EditButton"),
{
ssr: false,
}
);
export const EditButton = dynamic(() => import("@/web/EditButton"), {
ssr: false,
});
/* FooterForm is disabled to better comply with policy */
const Footer = () => {
return (
<footer
@@ -26,7 +21,6 @@ const Footer = () => {
<Snarveier />
<SideLenker />
<Kontakt />
{/* <FooterForm /> */}
</div>
<EditButton />
</footer>
@@ -36,7 +30,7 @@ const Footer = () => {
function LogoBlock() {
return (
<div>
<Logo className="fill-white" />
<AkselLogo className="fill-white" />
<p className="mt-4">&copy; {new Date().getFullYear()} NAV</p>
<p>Arbeids- og velferdsetaten</p>
</div>
@@ -1,104 +0,0 @@
import { Button, Label, Textarea } from "@navikt/ds-react";
import { IdContext } from "components/website-modules/utils/contexts/id-context";
import { FeedbackT } from "@/types";
import { useRouter } from "next/router";
import { useContext, useEffect, useState } from "react";
const FooterForm = () => {
const idCtx = useContext(IdContext);
const [contactForm, setContactForm] = useState({
content: "",
hasWritten: false,
});
const [contentError, setContentError] = useState({ content: "" });
const [sent, setSent] = useState({ status: false });
const { asPath, basePath } = useRouter();
const handleSubmit = (e) => {
e.preventDefault();
let fail = false;
if (!contactForm?.content || contactForm?.content.trim().length === 0) {
setContentError({
...contentError,
content: "Melding kan ikke være tom. Fyll inn meldingen.",
});
fail = true;
}
if (fail) return;
setContentError({ content: "" });
const body: FeedbackT = {
message: contactForm.content,
url: `${basePath}${asPath}`,
type: "footer",
docId: idCtx?.id,
};
fetch("/api/feedback", {
method: "POST",
body: JSON.stringify(body),
});
setSent({ status: true });
setContactForm({ content: "", hasWritten: false });
};
useEffect(() => {
setSent({ status: false });
setContactForm({ content: "", hasWritten: false });
setContentError({ content: "" });
}, [asPath]);
return (
<>
<div
className="flex w-full max-w-md flex-col lg:col-span-2 xl:col-span-1"
data-theme="dark"
>
<div aria-live="polite">
{sent.status && (
<Label spacing as="p">
Meldingen er sendt til Aksel, takk for tilbakemeldingen!
</Label>
)}
</div>
{!sent.status && (
<form onSubmit={(e) => handleSubmit(e)} className="w-full">
<div className="mb-3 flex flex-col gap-4">
<Textarea
className="textarea-override"
error={contentError.content}
autoComplete="off"
label="Gi oss en tilbakemelding"
value={contactForm.content}
onChange={(e) => {
setContactForm({
...contactForm,
content: e.target.value,
hasWritten: true,
});
e.target.value &&
e.target.value.trim().length !== 0 &&
setContentError({ ...contentError, content: "" });
}}
description="Ikke skriv inn personopplysninger"
minRows={2}
/>
</div>
<Button size="small">Send melding</Button>
<style>{`.textarea-override {
--ac-textarea-bg: var(--a-surface-default);
--ac-textarea-text: var(--a-text-default);
}`}</style>
</form>
)}
</div>
</>
);
};
export default FooterForm;
@@ -10,12 +10,12 @@ import { useState } from "react";
const LinkElement = ({ name, href, onClick }) => {
const { asPath } = useRouter();
return (
<li className="flex h-11 items-center">
<li className="my-2 flex h-11 items-center">
<Link
href={href}
passHref
className={cl(
"hover:bg-surface-action-subtle-hover focus-visible:shadow-focus-inset relative flex h-full w-full items-center rounded px-2 focus:outline-none",
"hover:bg-surface-action-subtle-hover focus-visible:shadow-focus relative flex h-full w-full items-center rounded px-2 focus:outline-none",
{
"before:bg-surface-action-selected pl-4 font-semibold before:absolute before:left-0 before:h-6 before:w-1 before:rounded-full":
asPath.startsWith(href),
@@ -58,6 +58,7 @@ export const Hamburger = () => {
onClose={() => setOpen(false)}
header={{ heading: "Meny" }}
width="small"
closeOnBackdropClick
>
<Modal.Body>
<nav aria-label="hovedmeny">
@@ -82,6 +83,11 @@ export const Hamburger = () => {
name="Komponenter"
href="/komponenter"
/>
<LinkElement
onClick={() => setOpen(false)}
name="Mønster & Maler"
href="/monster-maler"
/>
<LinkElement
onClick={() => setOpen(false)}
name="Blogg"
@@ -1,33 +1,36 @@
import AkselLogo from "@/assets/Logo";
import { amplitudeLogNavigation } from "@/logging";
import { MagnifyingGlassIcon } from "@navikt/aksel-icons";
import { Button } from "@navikt/ds-react";
import cl from "clsx";
import Logo from "components/assets/Logo";
import { Hamburger } from "components/layout/header/Hamburger";
import dynamic from "next/dynamic";
import Link from "next/link";
import { useRouter } from "next/router";
import { Hamburger } from "./Hamburger";
export const GlobalSearch = dynamic(() => import("./search"), {
loading: () => (
<Button
variant="primary"
className="hover:bg-deepblue-700 bg-deepblue-600 h-11"
aria-keyshortcuts="Control+b"
icon={
<MagnifyingGlassIcon
className="pointer-events-none -mt-[1px] shrink-0 text-2xl"
aria-label="Åpne meny"
aria-hidden
/>
}
iconPosition="left"
>
Søk
</Button>
),
ssr: false,
});
export const GlobalSearch = dynamic(
() => import("../../website-modules/search/Search"),
{
loading: () => (
<Button
variant="primary"
className="hover:bg-deepblue-700 bg-deepblue-600 h-11"
aria-keyshortcuts="Control+b"
icon={
<MagnifyingGlassIcon
className="pointer-events-none -mt-[1px] shrink-0 text-2xl"
aria-label="Åpne meny"
aria-hidden
/>
}
iconPosition="left"
>
Søk
</Button>
),
ssr: false,
}
);
const LinkElement = ({ name, href, prefetch = undefined }) => {
const { asPath } = useRouter();
@@ -55,7 +58,7 @@ const LinkElement = ({ name, href, prefetch = undefined }) => {
);
};
export const Header = ({
const Header = ({
variant = "default",
}: {
variant?: "blogg" | "subtle" | "default" | "transparent";
@@ -90,7 +93,7 @@ export const Header = ({
}
className="focus-visible:shadow-focus grid h-full place-items-center rounded px-2 focus:outline-none"
>
<Logo className="text-deepblue-800" />
<AkselLogo className="text-deepblue-800" />
<span className="sr-only">Aksel</span>
</Link>
</div>
@@ -103,6 +106,7 @@ export const Header = ({
<LinkElement name="Grunnleggende" href="/grunnleggende" />
<LinkElement name="Ikoner" href="/ikoner" prefetch={false} />
<LinkElement name="Komponenter" href="/komponenter" />
<LinkElement name="Mønster & Maler" href="/monster-maler" />
<LinkElement name="Bloggen" href="/produktbloggen" />
</ul>
</nav>
@@ -116,3 +120,5 @@ export const Header = ({
</>
);
};
export default Header;
@@ -1 +0,0 @@
export { default as Footer } from "./footer/Footer";
@@ -1,136 +1,79 @@
import { amplitudeLogNavigation } from "@/logging";
import { AkselSidebarT } from "@/types";
import {
ChevronDownIcon,
SparklesIcon,
TestFlaskIcon,
TrashIcon,
} from "@navikt/aksel-icons";
import { Detail } from "@navikt/ds-react";
import cl from "clsx";
import { SidebarT } from "@/types";
import { StatusTag } from "@/web/StatusTag";
import { Box, Label, Show } from "@navikt/ds-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useState } from "react";
import { useSection } from "./useSection";
const NavItem = ({
link,
const Sidebar = ({
kategori,
links,
}: {
link: { heading: string; slug: string; kategori: string; tag?: string };
kategori: "komponenter" | "grunnleggende" | "templates";
links: SidebarT;
}) => {
const { asPath } = useRouter();
const isActive = asPath.split("#")[0] === `/${link.slug}`;
return (
<li>
<Link
aria-current={isActive ? "page" : undefined}
href={`/${link.slug}`}
onClick={(e) => {
amplitudeLogNavigation("meny", e.currentTarget.getAttribute("href"));
}}
className={cl(
"hover:text-deepblue-800 focus-visible:shadow-focus text-medium pr-05 relative flex overflow-hidden py-1 pl-4 leading-snug before:rounded-full hover:before:transition-colors focus:outline-none focus-visible:z-10 focus-visible:rounded-sm",
{
"before:border-l-border-action-selected before:absolute before:left-0 before:top-1/2 before:h-6 before:-translate-y-1/2 before:border-l-[4px]":
isActive,
"text-deepblue-800 font-semibold": isActive,
"text-text-subtle": !isActive,
"before:absolute before:left-0 before:top-0 before:h-full before:border-l before:border-l-gray-200 hover:before:top-1/2 hover:before:h-6 hover:before:min-h-[70%] hover:before:-translate-y-1/2 hover:before:border-l-2 hover:before:border-l-gray-400":
!isActive,
}
)}
<Show asChild above="md">
<Box
paddingBlock="0 8"
paddingInline="2"
aria-label={kategori}
as="nav"
className="w-sidebar shrink-0 self-start"
>
{link.heading}
<span className="ml-auto capitalize">
{link?.tag === "beta" && (
<TestFlaskIcon
aria-hidden
className="text-violet-800"
fontSize="1.25rem"
/>
)}
{link?.tag === "new" && (
<SparklesIcon
aria-hidden
className="text-lightblue-700"
fontSize="1.25rem"
/>
)}
{link?.tag === "deprecated" && (
<TrashIcon
aria-hidden
className="text-text-default"
fontSize="1.25rem"
/>
)}
</span>
</Link>
</li>
<ul className="space-y-6">
{links.map((section) => (
<Section key={section.value} section={section} />
))}
</ul>
</Box>
</Show>
);
};
const Dropdown = ({
title,
links,
}: {
title: string;
links: AkselSidebarT;
}) => {
const [open, setOpen] = useState(true);
function Section({ section }: { section: SidebarT[number] }) {
const { asPath } = useRouter();
const isActive = (slug: string) => {
return asPath.split("#")[0] === `/${slug}`;
};
return (
<li className="peer w-full peer-data-[open=true]:mt-6" data-open={open}>
<button
onClick={() => setOpen((x) => !x)}
className="min-h-8 text-text-subtle hover:text-deepblue-800 group z-10 flex w-full cursor-pointer items-center justify-between focus:outline-none"
aria-expanded={open}
<li>
<Label
as="div"
size="small"
textColor="subtle"
className="min-h-8 flex items-center px-4"
>
<Detail
as="span"
className="group-focus-visible:shadow-focus group-hover:bg-surface-neutral-subtle group-active:bg-surface-neutral-subtle-hover mt-6 flex w-full items-center justify-between rounded-sm pl-2 font-semibold transition-colors first:mt-0"
>
{title}
<span className="flex h-6 w-6 items-center justify-center rounded">
<ChevronDownIcon
className={cl("text-base", { "rotate-180": open })}
aria-hidden
title={!open ? `åpne ${title}` : `lukk ${title}`}
/>
</span>
</Detail>
</button>
<ul hidden={!open} className="pl-2">
{links.map((z) => (
<NavItem link={z} key={z?.heading} />
{section.title}
</Label>
<ul className="space-y-px pl-3">
{section.pages.map((page) => (
<li
aria-current={isActive(page.slug) ? "page" : undefined}
key={page.slug}
data-active={isActive(page.slug)}
className="rounded-medium hover:bg-surface-hover min-h-8 data-active:bg-surface-selected data-active:text-deepblue-800 data-active:shadow-xsmall data-active:font-semibold relative mx-2 flex items-center transition-[background-color] ease-out"
>
<Link
prefetch={false}
href={`/${page.slug}`}
className="text-medium min-h-8 rounded-medium focus-visible:shadow-focus flex w-full items-center justify-between gap-2 break-words px-2 focus:outline-none focus-visible:z-10"
onClick={(e) => {
amplitudeLogNavigation(
"meny",
e.currentTarget.getAttribute("href")
);
}}
>
{page.heading}
<StatusTag size="xsmall" status={page.tag} />
</Link>
</li>
))}
</ul>
</li>
);
};
}
export const Sidebar = ({
kategori,
links,
}: {
kategori: "Komponenter" | "Grunnleggende";
links: AkselSidebarT;
}) => {
const sections = useSection({ kategori, links });
return (
<div
data-testid="ds-sidebar"
className="toc-ignore w-sidebar bg-surface-default z-[1002] hidden shrink-0 self-start overflow-x-auto pb-8 pl-6 pr-2 md:block"
>
<nav aria-label={kategori}>
<ul>
{sections.map((kat) => (
<Dropdown links={kat.pages} title={kat.title} key={kat.title} />
))}
</ul>
</nav>
</div>
);
};
export default Sidebar;
@@ -1,52 +0,0 @@
import { AkselSidebarT } from "@/types";
import { useMemo } from "react";
import {
grunnleggendeKategorier,
komponentKategorier,
} from "../../../sanity/config";
export const useSection = ({
kategori,
links,
}: {
kategori: "Komponenter" | "Grunnleggende";
links: AkselSidebarT;
}) => {
const sections = useMemo(
() =>
(kategori === "Komponenter"
? komponentKategorier
: grunnleggendeKategorier
)
.map((x) => ({
...x,
pages: links
.filter((y) => y?.kategori === x.value)
.sort((a, b) => {
if (a?.tag === "deprecated" && b?.tag === "deprecated") {
return 0;
} else if (a?.tag === "deprecated") {
return 1;
} else if (b.tag === "deprecated") {
return -1;
}
if (a.sidebarindex !== null || b.sidebarindex !== null) {
if (a.sidebarindex !== null && b.sidebarindex !== null) {
return a.sidebarindex - b.sidebarindex;
} else if (a.sidebarindex !== null) {
return -1;
} else {
return 1;
}
}
return a?.heading.localeCompare(b?.heading);
}),
}))
.filter((x) => !(!x.pages || x.pages.length === 0)),
[links, kategori]
);
return sections;
};
@@ -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",
@@ -24,40 +24,53 @@ const children = (
</>
);
const baseItem = { slug: "/123", tag: "beta" as const, sidebarindex: null };
const sidebar = [
{
pages: [
{ ...baseItem, kategori: "primitives", heading: "f", sidebarindex: 0 },
{ ...baseItem, kategori: "primitives", heading: "e", sidebarindex: 1 },
{
...baseItem,
kategori: "primitives",
heading: "d",
tag: "deprecated" as const,
sidebarindex: 2,
},
],
title: "Primitives",
value: "primitives",
},
{
pages: [
{ ...baseItem, kategori: "core", heading: "b" },
{
...baseItem,
kategori: "core",
heading: "c",
tag: "deprecated" as const,
},
],
title: "Core",
value: "core",
},
];
export const KomponenterLanding: Story = {
args: {
children,
pageType: {
type: "Komponenter",
type: "komponenter",
title: "Heading",
rootUrl: "/komponenter",
rootTitle: "Komponenter",
},
pageProps: { content: "1" },
variant: "landingPage",
footer: "footer",
intro: "intro",
sidebar: [
{
heading: "Link 1",
slug: "#",
kategori: "core",
tag: "ready",
sidebarindex: null,
},
{
heading: "Link 2",
slug: "#",
kategori: "primitives",
tag: "ready",
sidebarindex: null,
},
{
heading: "Link e",
slug: "#",
kategori: "primitives",
tag: "ready",
sidebarindex: null,
},
],
sidebar,
},
};
@@ -65,35 +78,15 @@ export const Komponenter: Story = {
args: {
children,
pageType: {
type: "Komponenter",
type: "komponenter",
title: "Heading",
rootUrl: "/komponenter",
rootTitle: "Komponenter",
},
pageProps: { content: "1" },
variant: "page",
footer: "footer",
intro: "intro",
sidebar: [
{
heading: "Link 1",
slug: "#",
kategori: "core",
tag: "ready",
sidebarindex: null,
},
{
heading: "Link 2",
slug: "#",
kategori: "primitives",
tag: "ready",
sidebarindex: null,
},
{
heading: "Link 3",
slug: "#",
kategori: "primitives",
tag: "ready",
sidebarindex: null,
},
],
sidebar,
},
};
@@ -1,10 +1,10 @@
import Sidebar from "@/layout/sidebar/Sidebar";
import { urlFor } from "@/sanity/interface";
import { AkselSidebarT } from "@/types";
import { SidebarT, TableOfContentsT } from "@/types";
import { capitalize } from "@/utils";
import TableOfContents from "@/web/toc/TableOfContents";
import { Box, Detail, Heading, Link } from "@navikt/ds-react";
import cl from "clsx";
import { Sidebar } from "components/layout/sidebar/Sidebar";
import { TableOfContentsv2 } from "components/website-modules/toc/TOCv2";
import Image from "next/legacy/image";
import NextLink from "next/link";
@@ -16,12 +16,16 @@ export const WithSidebar = ({
pageProps,
variant = "landingPage",
footer,
toc,
}: {
children: React.ReactNode;
sidebar: AkselSidebarT;
sidebar: SidebarT;
toc?: TableOfContentsT;
pageType: {
type: "Komponenter" | "Grunnleggende";
type: "komponenter" | "grunnleggende" | "templates";
title: string;
rootUrl: string;
rootTitle: string;
};
pageProps: any;
intro?: React.ReactNode;
@@ -29,13 +33,17 @@ 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
tabIndex={-1}
id="hovedinnhold"
className="md:max-w-screen-sidebar relative z-0 w-full px-4 focus:outline-none sm:pl-6 sm:pr-6 md:pl-0"
className="relative z-0 w-full px-4 focus:outline-none sm:pl-6 sm:pr-6 md:pl-0"
>
<div
className={cl(
@@ -52,13 +60,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)}
@@ -109,8 +113,12 @@ export const WithSidebar = ({
)}
</div>
<div className={cl("sm:px-6 md:px-10", { flex: variant === "page" })}>
{variant === "page" && <TableOfContentsv2 />}
<div
className={cl("sm:px-6 md:px-10", {
"flex gap-10": variant === "page",
})}
>
{variant === "page" && toc && <TableOfContents toc={toc} />}
<div className="w-full">
{children}
{footer && <div className="w-full">{footer}</div>}
@@ -1,3 +1,3 @@
export { amplitudeLogNavigation } from "./event-tracking";
export { AmplitudeEvents } from "./events";
export { amplitude } from "./useAmplitude";
export { default as useAmplitudeInit, amplitude } from "./useAmplitude";
@@ -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();
@@ -59,7 +60,7 @@ const useAmplitudeInit = () => {
.forEach(([event, eventData]) => amplitude.track(event, eventData))
)
.catch(async () => {
const { logger } = await import("logger");
const { logger } = await import("../../config/logger");
logger.error("Failed logging batched events");
});
};
@@ -1,4 +1,5 @@
import ErrorBoundary from "@/error-boundary";
import { useFormatedDate } from "@/hooks/useFormatedDate";
import { amplitudeLogNavigation } from "@/logging";
import {
@@ -7,10 +8,9 @@ import {
ResolveSlugT,
ResolveTemaT,
} from "@/types";
import { abbrName } from "@/utils";
import { BodyShort, Detail, Heading } from "@navikt/ds-react";
import { useFormatedDate } from "components/website-modules/utils/getDate";
import NextLink from "next/link";
import { abbrName } from "../..";
type ArtikkelCardProps = ResolveContributorsSingleT<
ResolveTemaT<ResolveSlugT<AkselGodPraksisDocT>>
@@ -1,9 +1,9 @@
import ErrorBoundary from "@/error-boundary";
import { useFormatedDate } from "@/hooks/useFormatedDate";
import { amplitudeLogNavigation } from "@/logging";
import { AkselBloggDocT, ResolveContributorsT, ResolveSlugT } from "@/types";
import { getAuthors } from "@/utils";
import { BodyLong, BodyShort, Heading, Link } from "@navikt/ds-react";
import { useFormatedDate } from "components/website-modules/utils/getDate";
import NextLink from "next/link";
type BloggCardProps = {
@@ -15,8 +15,10 @@ export const Designsystem: Story = {
args: {
node: {
title: "button",
dir: {
title: "button",
variant: "templates",
filer: [
{
_key: getKey(),
@@ -24,6 +26,9 @@ 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,
sandboxBase64: "fakebase64",
},
{
_key: getKey(),
@@ -31,6 +36,9 @@ 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,
sandboxBase64: "fakebase64",
},
{
_key: getKey(),
@@ -38,6 +46,9 @@ 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,
sandboxBase64: "fakebase64",
},
],
},
@@ -1,6 +1,7 @@
import { capitalize } from "@/components";
import SnippetLazy from "@/cms/code-snippet/SnippetLazy";
import ErrorBoundary from "@/error-boundary";
import { CodeExamplesT } from "@/types";
import { capitalize } from "@/utils";
import {
ExternalLinkIcon,
LaptopIcon,
@@ -8,9 +9,9 @@ import {
} from "@navikt/aksel-icons";
import { BodyLong, Button, Chips, HStack } from "@navikt/ds-react";
import cl from "clsx";
import SnippetLazy from "components/sanity-modules/code-snippet/SnippetLazy";
import { useRouter } from "next/router";
import { useEffect, useRef, useState } from "react";
import { Sandbox } from "./parts/Sandbox";
import { CodeSandbox } from "./parts/CodeSandbox";
const iframePadding = 192;
@@ -34,11 +35,19 @@ 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);
setFrameState(newHeight < 300 ? 300 : newHeight);
setFrameState(Math.min(Math.max(newHeight, 300), 900));
setUnloaded(false);
}
@@ -75,7 +84,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
const fixName = (str: string) =>
capitalize(
str
.replace(/[^\w]|_/g, " ")
.replace(/[^\wæøå]|_/g, " ")
.replace(/\s+/g, " ")
.trim()
) ?? str;
@@ -85,6 +94,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
}
const active = activeExample ?? node?.dir?.filer?.[0]?.navn;
const demoVariant = node.dir?.variant;
return (
<div>
@@ -107,7 +117,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
);
}}
>
{fixName(fil.navn)}
{fixName(fil.title)}
</Chips.Toggle>
);
})}
@@ -138,7 +148,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 +158,9 @@ 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,
}
)}
title="Kode-eksempler"
ref={iframeRef}
@@ -189,6 +201,9 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
</div>
<HStack gap="2">
{fil?.sandboxEnabled && (
<Sandbox code={fil?.sandboxBase64} />
)}
<CodeSandbox code={fil.innhold.trim()} />
<Button
variant="tertiary-neutral"
@@ -199,10 +214,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>
@@ -1,4 +1,4 @@
import { PencilIcon } from "@navikt/aksel-icons";
import { CodeSandboxLogo } from "@/assets/Icons";
import { Button } from "@navikt/ds-react";
import { getParameters } from "codesandbox/lib/api/define";
@@ -58,11 +58,10 @@ export const CodeSandbox = ({ code }: { code: string }) => {
<Button
variant="tertiary-neutral"
size="small"
aria-label="Codesandbox"
type="submit"
icon={<PencilIcon aria-hidden fontSize="1.5rem" />}
>
CodeSandbox
</Button>
icon={<CodeSandboxLogo aria-hidden />}
/>
</form>
);
};
@@ -0,0 +1,18 @@
import { PencilIcon } from "@navikt/aksel-icons";
import { Button } from "@navikt/ds-react";
export const Sandbox = ({ code }: { code: string }) => {
return (
<Button
href={`/sandbox/index.html?code=${code}`}
rel="noreferrer"
target="_blank"
as="a"
variant="tertiary-neutral"
size="small"
icon={<PencilIcon aria-hidden fontSize="1.5rem" />}
>
Sandbox
</Button>
);
};
@@ -1,8 +1,8 @@
import { urlFor } from "@/sanity/interface";
import { ArticleListT } from "@/types";
import { StatusTag } from "@/web/StatusTag";
import { Heading } from "@navikt/ds-react";
import cl from "clsx";
import { StatusTag } from "components/website-modules/StatusTag";
import Image from "next/legacy/image";
import Nextlink from "next/link";
@@ -1,14 +1,13 @@
import ErrorBoundary from "@/error-boundary";
import { amplitudeLogNavigation } from "@/logging";
import { urlFor } from "@/sanity/interface";
import { abbrName } from "@/utils";
import { abbrName, getImage } from "@/utils";
import { BodyShort, Detail, Heading } from "@navikt/ds-react";
import cl from "clsx";
import { getImage } from "components/website-modules/utils/get-image";
import { useFormatedDate } from "components/website-modules/utils/getDate";
import NextImage from "next/legacy/image";
import NextLink from "next/link";
import { Tag } from "./Tag";
import { useFormatedDate } from "@/hooks/useFormatedDate";
export type ArticleT = {
_key: string;
@@ -43,6 +42,7 @@ const Card = ({ article, visible, index }: CardProps) => {
"ds_artikkel",
"komponent_artikkel",
"aksel_blogg",
"templates_artikkel",
].includes(article._type);
return (
@@ -1,14 +1,13 @@
import { amplitudeLogNavigation } from "@/logging";
import { urlFor } from "@/sanity/interface";
import { getAuthors } from "@/utils";
import { getAuthors, getImage } from "@/utils";
import { BodyLong, BodyShort, Heading, Link } from "@navikt/ds-react";
import cl from "clsx";
import { getImage } from "components/website-modules/utils/get-image";
import { useFormatedDate } from "components/website-modules/utils/getDate";
import Image from "next/legacy/image";
import NextLink from "next/link";
import { ArticleT } from "./Card";
import { Tag } from "./Tag";
import { useFormatedDate } from "@/hooks/useFormatedDate";
export const Highlight = ({
article,
@@ -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>
)}
@@ -9,11 +9,11 @@ export const DtList = ({ prop, parent }: { prop: any; parent: string }) => {
return (
<Detail
as="div"
className="block overflow-x-auto border border-t-0 border-gray-300 p-2 font-mono first-of-type:border-t last-of-type:rounded-b"
className="block overflow-x-auto border border-t-0 border-gray-300 p-2 first-of-type:border-t last-of-type:rounded-b"
>
<dt>
{!prop.defaultValue ? (
<span className="font-semibold">{`${prop.name}${
<span className="font-mono font-semibold">{`${prop.name}${
prop?.required ? "" : "?"
} `}</span>
) : (
@@ -22,21 +22,21 @@ export const DtList = ({ prop, parent }: { prop: any; parent: string }) => {
arrow={false}
delay={0}
>
<span className="mr-2 cursor-pointer border-b border-dashed border-gray-600 font-semibold">{`${
<span className="mr-2 cursor-pointer border-b border-dashed border-gray-600 font-mono font-semibold">{`${
prop.name
}${prop?.required ? "" : "?"}`}</span>
</Tooltip>
)}
<span>{prop.type ? <>{Highlighter({ type: prop.type })}</> : ""}</span>
<span className="font-mono">
{prop.type ? <>{Highlighter({ type: prop.type })}</> : ""}
</span>
</dt>
{prop.description && (
<dd className="whitespace-pre-wrap font-sans text-base">
{prop.description}
</dd>
<dd className="whitespace-pre-wrap text-base">{prop.description}</dd>
)}
{prop.name === "ref" && prop.type.includes("Ref<") && (
<dd className="font-sans text-base">
<dd className="text-base">
{`${parent} extends ${prop.type.slice(
prop.type.indexOf("<") + 1,
prop.type.lastIndexOf(">")
@@ -14,7 +14,7 @@ const PropTable = ({ komponent }: PropTableProps) => {
size="xsmall"
level="3"
className="scroll-m-8 rounded-t-lg border border-b-0 border-gray-300 bg-gray-50 p-2"
id={`${komponent._key}`}
id={komponent._key}
>
{komponent?.title ? komponent.title : "Props"}
</Heading>
@@ -1,8 +1,8 @@
import ErrorBoundary from "@/error-boundary";
import { UUTableT } from "@/types";
import KBD from "components/website-modules/KBD";
import KBD from "@/web/KBD";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
import { AkselTable, AkselTableRow } from "@/web/Table";
import React from "react";
type UUTableProps = { node: UUTableT };
@@ -1,8 +1,8 @@
import ErrorBoundary from "@/error-boundary";
import { TokenTableT } from "@/types";
import { AkselTable, AkselTableRow } from "@/web/Table";
import core from "@navikt/ds-css/tokens.json";
import { Bleed, BodyLong, CopyButton, Label, Link } from "@navikt/ds-react";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
import NextLink from "next/link";
import ShowMore from "./parts/ShowMore";
@@ -1,49 +0,0 @@
import { CheckmarkIcon, ClipboardIcon } from "@navikt/aksel-icons";
import { useEffect, useRef, useState } from "react";
import cl from "clsx";
export const Copy = ({ text, copyText }) => {
const [active, setActive] = useState(false);
const [canfade, setCanfade] = useState(false);
const timeoutRef = useRef<number>();
useEffect(() => {
return () => {
timeoutRef.current && clearTimeout(timeoutRef.current);
};
}, []);
const handleClick = () => {
timeoutRef.current && clearTimeout(timeoutRef.current);
setCanfade(true);
navigator.clipboard.writeText(copyText);
setActive(true);
timeoutRef.current = window.setTimeout(() => {
setActive(false);
}, 2000);
};
return (
<button
type="button"
aria-live="polite"
className="py-05 focus-visible:shadow-focus min-h-8 text-medium hover:bg-surface-neutral-subtle-hover group rounded px-1 leading-none focus:outline-none"
onClick={handleClick}
>
<span className="inline-flex flex-row-reverse items-center gap-1">
<span>
{active ? (
<CheckmarkIcon title="Kopiert" className="animate-textbounce" />
) : (
<ClipboardIcon
title="Kopier css-variabel"
className={cl(canfade && "animate-fadeIn")}
/>
)}
</span>
<span aria-live="polite">{text}</span>
</span>
</button>
);
};
@@ -1,3 +1,4 @@
import { AkselTable, AkselTableRow } from "@/web/Table";
import {
LaptopIcon,
MobileIcon,
@@ -6,7 +7,6 @@ import {
TabletIcon,
} from "@navikt/aksel-icons";
import docs from "@navikt/ds-tokens/docs.json";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
export const BreakpointsView = ({ cat }: { cat: string }) => {
const breakpoints = docs[cat];
@@ -1,6 +1,6 @@
import { AkselTable, AkselTableRow } from "@/web/Table";
import docs from "@navikt/ds-tokens/docs.json";
import { sanitizeName } from "../utilities";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
export const FontView = ({ cat }: { cat: string }) => {
const fonts = docs[cat];
@@ -1,7 +1,7 @@
import docs from "@navikt/ds-tokens/docs.json";
import { Grid } from "../Grid";
import { getColorString, sanitizeName } from "../utilities";
import { Copy } from "../Copy";
import { CopyButton } from "@navikt/ds-react";
export const GlobalView = ({ cat }: { cat: string }) => {
const colors: { name: string; value: string }[] = docs[cat];
@@ -21,7 +21,12 @@ export const GlobalView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy text={sanitizeName(x.name)} copyText={x.name} />
<CopyButton
text={sanitizeName(x.name)}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
{getColorString(x.value)}
@@ -2,9 +2,9 @@ import { PersonCircleIcon } from "@navikt/aksel-icons";
import docs from "@navikt/ds-tokens/docs.json";
import Link from "next/link";
import color from "tinycolor2";
import { Copy } from "../Copy";
import { Grid } from "../Grid";
import { getColorString, getGlobalReference, sanitizeName } from "../utilities";
import { CopyButton } from "@navikt/ds-react";
export const SemanticView = ({ cat }: { cat: string }) => {
const colors = docs[cat];
@@ -31,9 +31,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
text={sanitizeName(x.name.replace("text-", ""))}
<CopyButton
copyText={x.name}
text={sanitizeName(x.name.replace("text-", ""))}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -80,9 +82,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("icon-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -121,9 +125,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
/>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("border-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -191,9 +197,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("surface-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -243,9 +251,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("surface-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -286,9 +296,11 @@ export const SemanticView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("surface-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -1,7 +1,7 @@
import docs from "@navikt/ds-tokens/docs.json";
import { Copy } from "../Copy";
import { Grid } from "../Grid";
import { sanitizeName } from "../utilities";
import { CopyButton } from "@navikt/ds-react";
export const ShadowView = ({ cat }: { cat: string }) => {
const shadows = docs[cat];
@@ -19,9 +19,11 @@ export const ShadowView = ({ cat }: { cat: string }) => {
/>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("shadow-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -1,7 +1,7 @@
import docs from "@navikt/ds-tokens/docs.json";
import { Copy } from "../Copy";
import { Grid } from "../Grid";
import { sanitizeName } from "../utilities";
import { CopyButton } from "@navikt/ds-react";
export const ShapesView = ({ cat }: { cat: string }) => {
const shapes = docs[cat];
@@ -17,9 +17,11 @@ export const ShapesView = ({ cat }: { cat: string }) => {
/>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(x.name.replace("border-radius-", ""))}
copyText={x.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -1,5 +1,5 @@
import { AkselTable, AkselTableRow } from "@/web/Table";
import docs from "@navikt/ds-tokens/docs.json";
import { AkselTable, AkselTableRow } from "components/website-modules/Table";
export const SpacingView = ({ cat }: { cat: string }) => {
const spacings = docs[cat];
@@ -1,6 +1,6 @@
import { CopyButton } from "@navikt/ds-react";
import docs from "@navikt/ds-tokens/docs.json";
import cl from "clsx";
import { Copy } from "../Copy";
import { Grid } from "../Grid";
import { sanitizeName } from "../utilities";
@@ -36,9 +36,11 @@ export const ZindexView = ({ cat }: { cat: string }) => {
</div>
<dl className="grid h-full">
<dt className="inline-flex items-center gap-2">
<Copy
<CopyButton
text={sanitizeName(token.name.replace("z-index-", ""))}
copyText={token.name}
size="small"
iconPosition="right"
/>
</dt>
<dd className="text-text-subtle text-medium mt-auto">
@@ -1,4 +1,4 @@
export * from "./sanity-schema";
export * from "./next";
export * from "./feedback";
export * from "./sanity-schema";
export * from "./search";
export * from "./toc";
Loaded 100 of 571 files, more files were not shown because too many files have changed in this diff. Show more