Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] 6cb25e44de Version Packages (#2172)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-17 14:24:27 +02:00
Ken 5812d6ee87 📝 Oppdatert changesets 2023-08-17 14:14:01 +02:00
Ken a402777c70 🐛 Alignet alert-ikon med tekst (#2179) 2023-08-17 14:02:56 +02:00
Vegard Haugstvedt d4064bd461 Call Combobox.onClear when clearin input field programatically (#2183) 2023-08-17 14:00:05 +02:00
KenandHalvor Haugan 1c482e03b8 Oppdatert list: Small-variant, bugfixes (#2168)
Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2023-08-17 13:56:59 +02:00
Vegard HaugstvedtandKen 608e1f3944 Feature/combobox error message (#2182)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2023-08-17 13:26:10 +02:00
Ken 107d6f3d93 CopyButton prop for iconPosition (#2173) 2023-08-17 13:21:54 +02:00
Ken 09d41e9b6d 🎨 Checkbox checkmark er nå SVG, ikke base64 (#2171) 2023-08-17 12:49:33 +02:00
Ken ebacbddd44 🐛 Reverserte major-update av stylelint 2023-08-17 12:47:34 +02:00
Ken e58b072c84 🎨 Oppdatert storybook, fikset chromatic-PR kommentar 2023-08-17 12:02:03 +02:00
Lars Eirik Korsgaard HansenandKen e7cb3753a0 🐛 fix: Combobox , SingleSelect: allow custom options (#2180)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2023-08-17 11:38:31 +02:00
Lars Eirik Korsgaard Hansen 7486ba11f9 🐛 Combobox, SingleSelect: close dropdown menu upon selecting option (#2177)
* isAddedByUser changed to isCustomOption to match language used elsewhere

* list closes when choosing option on SingleSelect

* changeset
2023-08-16 15:43:59 +02:00
Vegard Haugstvedt cf01a6e162 Bugfix/combobox.on clear is fired more times than expected (#2170) 2023-08-16 10:45:02 +02:00
Ken a67f95433f 🐛 Fikset dodont tekst og ikon for god-praksis 2023-08-16 10:44:41 +02:00
github-actions[bot]andgithub-actions[bot] dd03078e33 Version Packages (#2169)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-15 14:12:40 +02:00
Halvor Haugan 72321e3846 💄 ToggleGroup: fjern semibold fra selected button (#2167) 2023-08-15 13:42:20 +02:00
Ken 1cd8ac93ed 🎨 Oppdatert ikonside med forslag fra Slack (#2164) 2023-08-15 10:50:54 +02:00
Ken 48d6f14103 🐛 Fikset for restriktiv validering av sidetittel i sanity 2023-08-15 10:15:11 +02:00
github-actions[bot]andgithub-actions[bot] 44da7fcdc6 Version Packages (#2160)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-14 14:43:22 +02:00
Ken e68ab5cb2c 🎨 Fikset hardkodet border-radius i diverse komponenter (#2159) 2023-08-14 14:40:47 +02:00
Ken c721b392f9 ♻️ Chores Sanity, Storybook
- Oppdatert Storybook, Sanity
- Fikset validering for InputWithCounter i Sanity
- Fikset Accordion-stories rekkefølge
- La til egne knapper for Desktop/mobil-view for eksempler
- Bruker DS-knapper i kode-eksempler på Aksel
2023-08-14 13:40:10 +02:00
Ken 1d0d0ce665 Merge branch 'main' of https://github.com/navikt/aksel 2023-08-13 20:56:42 +02:00
Ken 9d02940543 🔥 Fjernet console.log 2023-08-13 20:50:54 +02:00
Ken 7ff63a2c63 🐛 Fikset validering for sidetittel i sanity 2023-08-13 20:50:13 +02:00
github-actions[bot]andgithub-actions[bot] 49f33a78d8 Version Packages (#2151)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-11 15:59:23 +02:00
Ken 06aaec70f0 📝 Oppdatert changeset for HGrid 2023-08-11 15:43:40 +02:00
Ken 5fbf230e47 🎨 Justert antall viste artikkler i masonary 2023-08-11 13:24:26 +02:00
Vegard Haugstvedt 53265c247e It is not possible to close combobox dropdown after clicking a chip (#2155)
* Fixes bug where it is not possible to close dropdown by pressing escape or clicking outside the combobox after clicking the chips to de-select an option

In short, the focus were lost/reset when the chip disappears, but now is reset to the input instead.
Also fixed issue where clicking the chip while virtual focus is in the dropdown caused a click on both the option and the chip, causing it to be de-selected and instantly re-selected.

* Updated "remove selected"-test to also check that closing the dropdown after clicking a chip is possible.

* Added changeset
2023-08-11 12:38:22 +02:00
Ken d78f33df79 🚚 Markert gammelt grid som deprecated (#2153) 2023-08-11 12:37:02 +02:00
Ken 4375c55ab6 📝 Oppdatert storybook template i workflow 2023-08-11 09:45:02 +02:00
Christian SkrøvsethandKen 0de8238ecf Add semibold (600) fonts (#2150)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2023-08-11 09:01:59 +02:00
KenandHalvor Haugan 647b14a238 [Primitives] HGrid (#1838)
Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2023-08-10 15:38:58 +02:00
Ken 64901adccb 🎨 Oppdatert List-dokumentasjon, fikset bruk av ikoner med text (#2149) 2023-08-10 13:06:48 +02:00
Ken 49ddb2094c Hotfix: Fikset visited state for Link og vertical-align for SVG i tailwind 2023-08-09 17:06:15 +02:00
github-actions[bot]andgithub-actions[bot] 8a5f209735 Version Packages (#2148)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-09 16:30:12 +02:00
Ken d3bd1202ed Hotfix: Fikset visited state for Link og SVG-reset ved bruk av tailwind 2023-08-09 16:19:49 +02:00
github-actions[bot]andgithub-actions[bot] 4bf6027518 Version Packages (#2147)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-09 15:34:18 +02:00
Halvor Haugan c8e203c523 ✏️ Fiks skrivefeil i suggestionblock (#2146) 2023-08-09 15:15:41 +02:00
267799ca0e update links (#2093)
* style links with inline

* tweak css spacing for inline links with SVG

* add break-all prop to link

* wrap link in span to allow for spacing

* more obvious spacing example

* remove spacing prop from link

* implement pr review comments

* add random icon visual fuzz testing in stories

* remove console.log

* changeset

* add examples to aksel.nav.no

* 🎨 make inline-flex version opt-in, improve prop naming

* 🐛 Fixed comments

* 🎨 Oppdatert props

* 🎨 refactor links, add examples (remove underline & inline)

* change is no longer major (pr review comment)

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* cleaner changeset (pr review comment)

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* norwegian changeset (pr review comment)

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* update changeset (pr review comment)

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* use neutral token for neutral link variant

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* use neutral token for neutral link variant

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>

* Update .changeset/proud-tigers-share.md

---------

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Ken <ken.aleksander@gmail.com>
2023-08-09 15:00:44 +02:00
104 changed files with 3055 additions and 1242 deletions

No files matched your search

+3 -3
View File
@@ -75,7 +75,7 @@ jobs:
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: "github-actions[bot]"
body-includes: github-comment-finder
body-includes: Storybook demo
- name: Create or update comment
if: steps.chromatic_tests.outcome == 'success' && !contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'
@@ -84,7 +84,7 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
### [<img id="github-comment-finder" src="https://raw.githubusercontent.com/storybookjs/brand/main/logo/logo-storybook-inverse.svg" height="32"/>](${{steps.storybook_url.outputs.replaced}})
## [Storybook demo](${{steps.storybook_url.outputs.replaced}})
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
edit-mode: replace
@@ -96,7 +96,7 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
### [<img id="github-comment-finder" src="https://raw.githubusercontent.com/storybookjs/brand/main/logo/logo-storybook-inverse.svg" height="32"/>](${{steps.storybook_url.outputs.replaced}})
## [Storybook demo](${{steps.storybook_url.outputs.replaced}})
### [Endringer til review](${{steps.chromatic_tests.outputs.url}}): ${{steps.chromatic_tests.outputs.changeCount}}
+14
View File
@@ -1,5 +1,19 @@
# @navikt/aksel-icons
## 4.12.0
## 4.11.2
## 4.11.1
## 4.11.0
## 4.10.2
## 4.10.1
## 4.10.0
## 4.9.1
## 4.9.0
@@ -58,7 +58,7 @@
.icon-button {
min-height: 3rem;
min-width: 3rem;
border-radius: 2px;
border-radius: var(--a-border-radius-small);
display: grid;
place-content: center;
background-color: var(--a-surface-default);
@@ -1,9 +1,9 @@
import meta from "@navikt/aksel-icons/metadata";
const subCategorizeIcons = (
icons: typeof meta[1][]
): { sub_category: string; icons: typeof meta[1][] }[] => {
const categories: { sub_category: string; icons: typeof meta[1][] }[] = [];
icons: (typeof meta)[1][]
): { sub_category: string; icons: (typeof meta)[1][] }[] => {
const categories: { sub_category: string; icons: (typeof meta)[1][] }[] = [];
for (const icon of icons) {
const i = categories.findIndex(
@@ -19,14 +19,14 @@ const subCategorizeIcons = (
};
export const categorizeIcons = (
icons: typeof meta[1][]
icons: (typeof meta)[1][]
): {
category: string;
sub_categories: { sub_category: string; icons: typeof meta[1][] }[];
sub_categories: { sub_category: string; icons: (typeof meta)[1][] }[];
}[] => {
const categories: {
category: string;
icons: typeof meta[1][];
icons: (typeof meta)[1][];
}[] = [];
for (const icon of icons) {
@@ -42,14 +42,14 @@ export const categorizeIcons = (
.map((x) => ({ ...x, sub_categories: subCategorizeIcons(x.icons) }));
};
const noFill = (icon: typeof meta[1], icons: typeof meta[1][]) => {
const noFill = (icon: (typeof meta)[1], icons: (typeof meta)[1][]) => {
const foundFill = icons.find(
(x) => x.name.endsWith("Fill") && x.name.replace("Fill", "") === icon.name
);
return !foundFill;
};
export const getFillIcon = (icons: typeof meta[1][]) => {
export const getFillIcon = (icons: (typeof meta)[1][]) => {
return icons.filter(
(x, _, z) => x.variant.toLowerCase() === "fill" || noFill(x, z)
);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "4.9.1",
"version": "4.12.0",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+14
View File
@@ -1,5 +1,19 @@
# @navikt/aksel-stylelint
## 4.12.0
## 4.11.2
## 4.11.1
## 4.11.0
## 4.10.2
## 4.10.1
## 4.10.0
## 4.9.1
## 4.9.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "4.9.1",
"version": "4.12.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": "^4.9.1",
"@navikt/ds-tokens": "^4.9.1",
"@navikt/ds-css": "^4.12.0",
"@navikt/ds-tokens": "^4.12.0",
"@types/jest": "^29.0.0",
"concurrently": "7.2.1",
"copyfiles": "2.4.1",
+49
View File
@@ -1,5 +1,54 @@
# @navikt/aksel
## 4.12.0
### Patch Changes
- Updated dependencies [[`1c482e03b`](https://github.com/navikt/aksel/commit/1c482e03b845c4e6f6f1e174a58992fbeeabb52e), [`107d6f3d9`](https://github.com/navikt/aksel/commit/107d6f3d935cbc9bb94bc15cea63431cd15bbd61), [`09d41e9b6`](https://github.com/navikt/aksel/commit/09d41e9b6da5486a55faaf8088191b4a8374e420), [`a402777c7`](https://github.com/navikt/aksel/commit/a402777c70a18899ef4239b10a6335a671e26167)]:
- @navikt/ds-css@4.12.0
## 4.11.2
### Patch Changes
- Updated dependencies [[`72321e384`](https://github.com/navikt/aksel/commit/72321e3846f785318ebdba9f8e06cf13b09cd524)]:
- @navikt/ds-css@4.11.2
## 4.11.1
### Patch Changes
- Updated dependencies [[`e68ab5cb2`](https://github.com/navikt/aksel/commit/e68ab5cb2cd91c2f0234c864ee71821ed07a8203)]:
- @navikt/ds-css@4.11.1
## 4.11.0
### Patch Changes
- Updated dependencies [[`64901adcc`](https://github.com/navikt/aksel/commit/64901adccb8f19fbe06cdab4a70d47978e90e343), [`0de8238ec`](https://github.com/navikt/aksel/commit/0de8238ecf22e9f1fbc0e1b0cfe7a5cd7254901e), [`647b14a23`](https://github.com/navikt/aksel/commit/647b14a23888f82bacc4806d3afaacb5e39178fa)]:
- @navikt/ds-css@4.11.0
## 4.10.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@4.10.2
## 4.10.1
### Patch Changes
- Updated dependencies [[`d3bd1202e`](https://github.com/navikt/aksel/commit/d3bd1202eddb23dd3f1f09c3e4086ff3b19737d6)]:
- @navikt/ds-css@4.10.1
## 4.10.0
### Patch Changes
- Updated dependencies [[`267799ca0`](https://github.com/navikt/aksel/commit/267799ca0ecc0c549e159f94124befb654e29331)]:
- @navikt/ds-css@4.10.0
## 4.9.1
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "4.9.1",
"version": "4.12.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": "4.9.1",
"@navikt/ds-css": "4.12.0",
"axios": "1.3.6",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+50
View File
@@ -1,5 +1,55 @@
# @navikt/ds-css
## 4.12.0
### Minor Changes
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
### Patch Changes
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
- Alert: Fikset alignment av status-ikon mot tekst ([#2179](https://github.com/navikt/aksel/pull/2179))
## 4.11.2
### Patch Changes
- :lipstick: ToggleGroup: fjern semibold fra selected button ([#2167](https://github.com/navikt/aksel/pull/2167))
## 4.11.1
### Patch Changes
- Border-radius: Fikset hardkodet border-radius i Datepicker, ToggleGroup og Combobox. ([#2159](https://github.com/navikt/aksel/pull/2159))
## 4.11.0
### Minor Changes
- HGrid: Ny komponent 'HGrid' er ute i Beta: [Dokumentasjon](https://aksel.nav.no/komponenter/core/hgrid) ([#1838](https://github.com/navikt/aksel/pull/1838))
### Patch Changes
- List: Fikset alignment av ikoner ([#2149](https://github.com/navikt/aksel/pull/2149))
- Font: La til egen font for semibold italic for bedre skalering cross-browser ([#2150](https://github.com/navikt/aksel/pull/2150))
## 4.10.2
### Patch Changes
- Link: Fikset visited farger og inline SVG-reset ved bruk av tailwind
## 4.10.0
### Minor Changes
- Link: La til 'variant', 'underline' og 'inlineText'-prop ([#2093](https://github.com/navikt/aksel/pull/2093))
## 4.9.1
### Patch Changes
+8 -1
View File
@@ -24,10 +24,17 @@
font-size: 1.5rem;
align-self: flex-start;
height: var(--a-font-line-height-xlarge);
margin: var(--a-spacing-1);
margin-inline: var(--a-spacing-1);
margin-block: 0;
margin-block-start: -1px;
background: radial-gradient(circle, var(--a-surface-default) 50%, 0, transparent);
}
.navds-alert--close-button > .navds-alert__icon,
.navds-alert--small > .navds-alert__icon {
margin-block-start: 0;
}
.navds-alert--small > .navds-alert__icon {
height: var(--a-font-line-height-large);
}
+7
View File
@@ -5,6 +5,13 @@
font-display: swap;
src: url("https://cdn.nav.no/aksel/fonts/SourceSans3-italic.woff2") format("woff2");
}
@font-face {
font-family: "Source Sans Pro";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("https://cdn.nav.no/aksel/fonts/SourceSans3-italic-latin-semibold") format("woff2");
}
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
+4
View File
@@ -99,6 +99,10 @@ const StyleMappings = {
main: "help-text.css",
dependencies: ["popover.css"],
},
{
component: "HGrid",
main: "hgrid.css",
},
{ component: "Ingress", main: typoCss },
{
component: "InternalHeader",
+7
View File
@@ -52,9 +52,16 @@
.navds-copybutton__icon {
font-size: 1.5rem;
display: flex;
}
.navds-copybutton__icon:first-of-type {
margin-left: -0.25rem;
}
.navds-copybutton__icon:last-of-type {
margin-right: -0.25rem;
}
.navds-copybutton__icon:only-child {
margin: 0;
}
+1 -1
View File
@@ -233,7 +233,7 @@
text-decoration: none;
border: none;
background: none;
border-radius: 3px;
border-radius: calc(var(--a-border-radius-medium) - 1px);
padding: var(--a-spacing-3);
align-items: center;
justify-content: center;
+6 -1
View File
@@ -253,6 +253,11 @@
padding-left: calc(var(--a-spacing-3) - 4px);
}
.navds-form-field--small .navds-combobox__list-item--focus,
.navds-form-field--small .navds-combobox__list-item:hover {
padding-left: calc(var(--a-spacing-2) - 4px);
}
.navds-combobox__list-item--selected {
background-color: var(--ac-combobox-list-item-selected-bg, var(--a-surface-selected));
}
@@ -283,7 +288,7 @@
.navds-combobox__list-item__new-option--focus {
box-shadow: var(--a-shadow-focus) inset, var(--a-border-action) 0 0 0 5px inset;
border-radius: 3px;
border-radius: calc(var(--a-border-radius-medium) - 1px);
}
/* Mobile */
+21 -5
View File
@@ -114,14 +114,27 @@
}
.navds-checkbox__input:checked + .navds-checkbox__label::before {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMyAxMCI+ICAgIDxnPiAgICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNCwxMGMtMC40LDAtMC44LTAuMS0xLjEtMC40TDAuNCw3LjFDMC4xLDYuOCwwLDYuNCwwLDZzMC4yLTAuOCwwLjUtMS4xQzEsNC40LDIsNC40LDIuNSw0LjlMNCw2LjRsNi40LTYgICAgQzEwLjgsMC4xLDExLjEsMCwxMS41LDBjMC40LDAsMC44LDAuMiwxLDAuNWMwLjYsMC42LDAuNSwxLjYtMC4xLDIuMXYwTDUsOS42QzQuNyw5LjksNC40LDEwLDQsMTB6IE0xMS44LDEuOUwxMS44LDEuOSAgICBDMTEuOCwxLjksMTEuOCwxLjksMTEuOCwxLjl6IE0xMS4yLDEuMUMxMS4yLDEuMSwxMS4yLDEuMSwxMS4yLDEuMUwxMS4yLDEuMXoiLz4gICAgPC9nPjwvc3ZnPg==);
background-position: 0.375rem center;
background-repeat: no-repeat;
background-size: 0.8125rem;
box-shadow: none;
background-color: var(--ac-radio-checkbox-action, var(--a-surface-action));
}
.navds-checkbox__input:where(:not(:checked)) + .navds-checkbox__label > .navds-checkbox__icon {
display: none;
}
.navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
color: var(--ac-radio-checkbox-bg, var(--a-surface-default));
position: absolute;
height: 1.5rem;
transform: translate(0.375rem);
pointer-events: none;
}
.navds-checkbox--small .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
transform: translate(0.25rem, -10%);
height: 1.25rem;
}
.navds-checkbox--small > .navds-checkbox__input:checked + .navds-checkbox__label::before {
background-position: 0.25rem center;
}
@@ -283,11 +296,14 @@
}
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label::before {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMyAxMCI+ICAgIDxnPiAgICA8cGF0aCBmaWxsPSJyZ2JhKDAsIDAsIDAsIDAuNjUpIiBkPSJNNCwxMGMtMC40LDAtMC44LTAuMS0xLjEtMC40TDAuNCw3LjFDMC4xLDYuOCwwLDYuNCwwLDZzMC4yLTAuOCwwLjUtMS4xQzEsNC40LDIsNC40LDIuNSw0LjlMNCw2LjRsNi40LTYgICAgQzEwLjgsMC4xLDExLjEsMCwxMS41LDBjMC40LDAsMC44LDAuMiwxLDAuNWMwLjYsMC42LDAuNSwxLjYtMC4xLDIuMXYwTDUsOS42QzQuNyw5LjksNC40LDEwLDQsMTB6IE0xMS44LDEuOUwxMS44LDEuOSAgICBDMTEuOCwxLjksMTEuOCwxLjksMTEuOCwxLjl6IE0xMS4yLDEuMUMxMS4yLDEuMSwxMS4yLDEuMSwxMS4yLDEuMUwxMS4yLDEuMXoiLz4gICAgPC9nPjwvc3ZnPg==);
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
background-color: var(--__ac-radio-checkbox-readonly-bg);
}
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
color: var(--a-icon-subtle);
}
.navds-radio--readonly > .navds-radio__input:checked + .navds-radio__label::before {
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border), inset 0 0 0 4px var(--__ac-radio-checkbox-readonly-bg);
background-color: var(--a-icon-subtle);
+55
View File
@@ -0,0 +1,55 @@
.navds-hgrid {
--__ac-hgrid-columns-xs: initial;
--__ac-hgrid-columns-sm: initial;
--__ac-hgrid-columns-md: initial;
--__ac-hgrid-columns-lg: initial;
--__ac-hgrid-columns-xl: initial;
--__ac-hgrid-columns: var(--__ac-hgrid-columns-xs);
--__ac-hgrid-gap-xs: initial;
--__ac-hgrid-gap-sm: initial;
--__ac-hgrid-gap-md: initial;
--__ac-hgrid-gap-lg: initial;
--__ac-hgrid-gap-xl: initial;
--__ac-hgrid-gap: var(--__ac-hgrid-gap-xs);
display: grid;
grid-template-columns: var(--__ac-hgrid-columns);
gap: var(--__ac-hgrid-gap);
}
@media (min-width: 480px) {
.navds-hgrid {
--__ac-hgrid-columns: var(--__ac-hgrid-columns-sm, var(--__ac-hgrid-columns-xs));
--__ac-hgrid-gap: var(--__ac-hgrid-gap-sm, var(--__ac-hgrid-gap-xs));
}
}
@media (min-width: 768px) {
.navds-hgrid {
--__ac-hgrid-columns: var(--__ac-hgrid-columns-md, var(--__ac-hgrid-columns-sm, var(--__ac-hgrid-columns-xs)));
--__ac-hgrid-gap: var(--__ac-hgrid-gap-md, var(--__ac-hgrid-gap-sm, var(--__ac-hgrid-gap-xs)));
}
}
@media (min-width: 1024px) {
.navds-hgrid {
--__ac-hgrid-columns: var(
--__ac-hgrid-columns-lg,
var(--__ac-hgrid-columns-md, var(--__ac-hgrid-columns-sm, var(--__ac-hgrid-columns-xs)))
);
--__ac-hgrid-gap: var(--__ac-hgrid-gap-lg, var(--__ac-hgrid-gap-md, var(--__ac-hgrid-gap-sm, var(--__ac-hgrid-gap-xs))));
}
}
@media (min-width: 1280px) {
.navds-hgrid {
--__ac-hgrid-columns: var(
--__ac-hgrid-columns-xl,
var(--__ac-hgrid-columns-lg, var(--__ac-hgrid-columns-md, var(--__ac-hgrid-columns-sm, var(--__ac-hgrid-columns-xs))))
);
--__ac-hgrid-gap: var(
--__ac-hgrid-gap-xl,
var(--__ac-hgrid-gap-lg, var(--__ac-hgrid-gap-md, var(--__ac-hgrid-gap-sm, var(--__ac-hgrid-gap-xs))))
);
}
}
+1
View File
@@ -14,6 +14,7 @@
@import "guide-panel.css";
@import "form/index.css";
@import "help-text.css";
@import "hgrid.css";
@import "internalheader.css";
@import "link.css";
@import "loader.css";
+35 -3
View File
@@ -6,15 +6,35 @@
gap: var(--a-spacing-1);
}
.navds-alert .navds-link,
.navds-confirmation-panel .navds-link {
color: var(--a-text-default);
.navds-link.navds-link--inline-text {
display: inline;
}
.navds-link.navds-link--inline-text > svg {
/* stylelint-disable-next-line plugin/declaration-block-no-ignored-properties */
margin: 0.1em 0.1em -0.1em;
transform: translateY(0.05em);
display: inline;
vertical-align: baseline;
}
.navds-link:hover {
text-decoration: none;
}
.navds-link.navds-link--remove-underline {
text-decoration: none;
}
.navds-link.navds-link--remove-underline:hover {
text-decoration: underline;
}
.navds-alert .navds-link,
.navds-confirmation-panel .navds-link {
color: var(--a-text-default);
}
.navds-link:focus-visible {
outline: none;
color: var(--ac-link-focus-text, var(--a-text-on-action));
@@ -45,3 +65,15 @@
color: inherit;
flex-shrink: 0;
}
.navds-link--action {
color: var(--ac-link-action-text, var(--a-text-action));
}
.navds-link--neutral {
color: var(--ac-link-neutral-text, var(--a-text-default));
}
.navds-link--subtle {
color: var(--ac-link-subtle-text, var(--a-text-subtle));
}
+40 -9
View File
@@ -1,14 +1,24 @@
.navds-list ul,
.navds-list ol {
padding: 0;
margin-block: var(--a-spacing-4);
}
.navds-list--nested {
margin: var(--a-spacing-4) 0 var(--a-spacing-8) 0;
.navds-list--nested ul,
.navds-list--nested ol {
margin-block: var(--a-spacing-2);
}
.navds-list li:not(.navds-list__item--noMargin) {
margin-bottom: var(--a-spacing-5);
.navds-list li {
margin-block-end: var(--a-spacing-5);
}
.navds-list--small li {
margin-block-end: var(--a-spacing-2);
}
.navds-list--nested li {
margin-block-end: var(--a-spacing-3);
}
.navds-list:last-child > ul > li:last-child,
@@ -34,15 +44,30 @@
.navds-list .navds-list__item-marker--bullet {
display: flex;
margin-top: 0.375rem;
align-items: center;
height: var(--a-font-line-height-large);
grid-column: 1 / 2;
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
color: var(--ac-list-marker-ul-color, var(--ac-list-marker-color, var(--a-icon-default)));
}
.navds-list--small .navds-list__item-marker--bullet {
height: var(--a-font-line-height-medium);
}
.navds-list__item-marker--icon {
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
color: var(--ac-list-marker-icon-color, var(--ac-list-marker-color, var(--a-icon-default)));
display: flex;
align-items: center;
height: var(--a-font-line-height-large);
padding-block-end: 1px;
}
.navds-list--small .navds-list__item-marker--icon {
height: var(--a-font-line-height-medium);
}
.navds-list__item-marker--icon svg {
flex-shrink: 0;
}
.navds-list ul .navds-list__item-content {
@@ -51,6 +76,7 @@
.navds-list ol {
padding-left: var(--a-spacing-2);
list-style: decimal;
}
.navds-list ol li {
@@ -60,7 +86,12 @@
.navds-list ol li::marker {
font-weight: var(--a-font-weight-bold);
font-size: var(--a-font-size-medium);
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
font-size: var(--a-font-size-large);
color: var(--ac-list-marker-ol-color, var(--ac-list-marker-color, var(--a-icon-default)));
line-height: var(--a-font-line-height-large);
}
.navds-list--small ol li::marker {
font-size: var(--a-font-size-small);
line-height: var(--a-font-line-height-medium);
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "4.9.1",
"version": "4.12.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": "^4.9.1",
"@navikt/ds-tokens": "^4.12.0",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
+18 -20
View File
@@ -1,22 +1,20 @@
/* stylelint-disable csstools/value-no-unknown-custom-properties */
/* stylelint-disable aksel/design-token-exists */
.navds-stack {
--ac-stack-align: initial;
--ac-stack-justify: initial;
--ac-stack-direction: initial;
--ac-stack-wrap: initial;
--ac-stack-gap-xs: initial;
--ac-stack-gap-sm: initial;
--ac-stack-gap-md: initial;
--ac-stack-gap-lg: initial;
--ac-stack-gap-xl: initial;
--__ac-stack-gap: var(--ac-stack-gap-xs);
--__ac-stack-align: initial;
--__ac-stack-justify: initial;
--__ac-stack-direction: initial;
--__ac-stack-wrap: initial;
--__ac-stack-gap-xs: initial;
--__ac-stack-gap-sm: initial;
--__ac-stack-gap-md: initial;
--__ac-stack-gap-lg: initial;
--__ac-stack-gap-xl: initial;
--__ac-stack-gap: var(--__ac-stack-gap-xs);
gap: var(--__ac-stack-gap);
display: flex;
align-items: var(--ac-stack-align);
justify-content: var(--ac-stack-justify);
flex-flow: var(--ac-stack-direction) var(--ac-stack-wrap);
align-items: var(--__ac-stack-align);
justify-content: var(--__ac-stack-justify);
flex-flow: var(--__ac-stack-direction) var(--__ac-stack-wrap);
}
.navds-stack__spacer {
@@ -35,27 +33,27 @@
@media (min-width: 480px) {
.navds-stack {
--__ac-stack-gap: var(--ac-stack-gap-sm, var(--ac-stack-gap-xs));
--__ac-stack-gap: var(--__ac-stack-gap-sm, var(--__ac-stack-gap-xs));
}
}
@media (min-width: 768px) {
.navds-stack {
--__ac-stack-gap: var(--ac-stack-gap-md, var(--ac-stack-gap-sm, var(--ac-stack-gap-xs)));
--__ac-stack-gap: var(--__ac-stack-gap-md, var(--__ac-stack-gap-sm, var(--__ac-stack-gap-xs)));
}
}
@media (min-width: 1024px) {
.navds-stack {
--__ac-stack-gap: var(--ac-stack-gap-lg, var(--ac-stack-gap-md, var(--ac-stack-gap-sm, var(--ac-stack-gap-xs))));
--__ac-stack-gap: var(--__ac-stack-gap-lg, var(--__ac-stack-gap-md, var(--__ac-stack-gap-sm, var(--__ac-stack-gap-xs))));
}
}
@media (min-width: 1280px) {
.navds-stack {
--__ac-stack-gap: var(
--ac-stack-gap-xl,
var(--ac-stack-gap-lg, var(--ac-stack-gap-md, var(--ac-stack-gap-sm, var(--ac-stack-gap-xs))))
--__ac-stack-gap-xl,
var(--__ac-stack-gap-lg, var(--__ac-stack-gap-md, var(--__ac-stack-gap-sm, var(--__ac-stack-gap-xs))))
);
}
}
+1 -2
View File
@@ -34,7 +34,7 @@
cursor: pointer;
background-color: var(--ac-toggle-group-button-bg, var(--a-surface-transparent));
color: var(--ac-toggle-group-button-text, var(--a-text-default));
border-radius: 2px;
border-radius: var(--a-border-radius-small);
min-width: fit-content;
font-weight: var(--a-font-weight-regular);
}
@@ -113,7 +113,6 @@
.navds-toggle-group__button[aria-checked="true"] {
background-color: var(--ac-toggle-group-selected-bg, var(--a-surface-action-selected));
color: var(--ac-toggle-group-selected-text, var(--a-text-on-action));
font-weight: var(--a-font-weight-bold);
}
.navds-toggle-group--neutral > .navds-toggle-group__button[aria-pressed="true"],
+4 -1
View File
@@ -175,7 +175,10 @@
"--ac-link-focus-border": "--a-border-focus",
"--ac-link-active-text": "--a-text-on-action",
"--ac-link-active-bg": "--a-border-focus",
"--ac-link-active-border": "--a-border-focus"
"--ac-link-active-border": "--a-border-focus",
"--ac-link-action-text": "--a-text-action",
"--ac-link-neutral-text": "--a-text-default",
"--ac-link-subtle-text": "--a-text-subtle"
},
"list": {
"--ac-list-marker-ul-color": "--ac-list-marker-color",
+79
View File
@@ -1,5 +1,84 @@
# @navikt/ds-react
## 4.12.0
### Minor Changes
- Combobox: La til støtte for feilmeldinger i Combobox ([#2182](https://github.com/navikt/aksel/pull/2182))
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
### Patch Changes
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
- Combobox: Kjører nå 'onChange' + 'onClear' når input blir reset programmatisk ([#2183](https://github.com/navikt/aksel/pull/2183))
- Combobox: Fikset custom-options i singleselect ([#2180](https://github.com/navikt/aksel/pull/2180))
- Combobox: Fjernet unødvendige 'onClear'-calls når man velger verdier ([#2170](https://github.com/navikt/aksel/pull/2170))
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
- Combobox: Lukker nå nedtrekksmeny hvis man legger til ny option i singleselect ([#2177](https://github.com/navikt/aksel/pull/2177))
- Updated dependencies []:
- @navikt/aksel-icons@4.12.0
## 4.11.2
### Patch Changes
- Updated dependencies []:
- @navikt/aksel-icons@4.11.2
## 4.11.1
### Patch Changes
- Updated dependencies []:
- @navikt/aksel-icons@4.11.1
## 4.11.0
### Minor Changes
- HGrid: Ny komponent 'HGrid' er ute i Beta: [Dokumentasjon](https://aksel.nav.no/komponenter/core/hgrid) ([#1838](https://github.com/navikt/aksel/pull/1838))
### Patch Changes
- Fixes bug where combobox list could not be closed after clicking a chip ([#2155](https://github.com/navikt/aksel/pull/2155))
- Grid: Markert som deprecated. Bruk nye 'HGrid' ([#2153](https://github.com/navikt/aksel/pull/2153))
- Updated dependencies []:
- @navikt/aksel-icons@4.11.0
## 4.10.2
### Patch Changes
- Updated dependencies []:
- @navikt/aksel-icons@4.10.2
## 4.10.1
### Patch Changes
- Updated dependencies []:
- @navikt/aksel-icons@4.10.1
## 4.10.0
### Minor Changes
- Link: La til 'variant', 'underline' og 'inlineText'-prop ([#2093](https://github.com/navikt/aksel/pull/2093))
### Patch Changes
- Updated dependencies []:
- @navikt/aksel-icons@4.10.0
## 4.9.1
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "4.9.1",
"version": "4.12.0",
"description": "Aksel react-components for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -38,7 +38,7 @@
},
"dependencies": {
"@floating-ui/react": "0.24.1",
"@navikt/aksel-icons": "^4.9.1",
"@navikt/aksel-icons": "^4.12.0",
"@radix-ui/react-tabs": "1.0.0",
"@radix-ui/react-toggle-group": "1.0.0",
"clsx": "^1.2.1",
+1
View File
@@ -102,6 +102,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
{
"navds-alert--full-width": fullWidth,
"navds-alert--inline": inline,
"navds-alert--close-button": closeButton,
}
)}
>
+11 -1
View File
@@ -185,6 +185,16 @@ export const WithCloseButton: Story = {
</BodyLong>
<Link href="#">Id elit esse enim reprehenderit</Link>
</AlertWithCloseButton>
<AlertWithCloseButton>
<DsHeading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
</DsHeading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
sint commodo consequat eu aute.
</BodyLong>
</AlertWithCloseButton>
</div>
);
},
@@ -197,6 +207,6 @@ export const WithCloseButton: Story = {
});
const buttonsAfter = canvas.getAllByTitle("Lukk Alert");
expect(buttonsAfter.length).toBe(1);
expect(buttonsAfter.length).toBe(2);
},
};
@@ -63,6 +63,11 @@ export interface CopyButtonProps
* @default 'Kopiert'
*/
activeTitle?: string;
/**
* Icon position in Button
* @default "left"
*/
iconPosition?: "left" | "right";
}
/**
@@ -91,6 +96,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
activeDuration = 2000,
title = "Kopier",
activeTitle = "Kopiert",
iconPosition = "left",
...rest
},
ref
@@ -119,6 +125,25 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
}, activeDuration);
};
const CopyIcon = () => {
return active ? (
<span className="navds-copybutton__icon">
{activeIcon ?? (
<CheckmarkIcon
aria-hidden={!!text}
title={text ? undefined : activeTitle}
/>
)}
</span>
) : (
<span className="navds-copybutton__icon">
{icon ?? (
<FilesIcon aria-hidden={!!text} title={text ? undefined : title} />
)}
</span>
);
};
return (
<button
ref={ref}
@@ -138,25 +163,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
onClick={handleClick}
>
<span className="navds-copybutton__content">
{active ? (
<span className="navds-copybutton__icon">
{activeIcon ?? (
<CheckmarkIcon
aria-hidden={!!text}
title={text ? undefined : activeTitle}
/>
)}
</span>
) : (
<span className="navds-copybutton__icon">
{icon ?? (
<FilesIcon
aria-hidden={!!text}
title={text ? undefined : title}
/>
)}
</span>
)}
{iconPosition === "left" && <CopyIcon />}
{text &&
(active ? (
@@ -176,6 +183,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
{text}
</Label>
))}
{iconPosition === "right" && <CopyIcon />}
</span>
</button>
);
@@ -60,6 +60,16 @@ export const Variants = {
),
};
export const IconPosition = {
render: () => (
<div className="colgap">
<CopyButton copyText="3.14" iconPosition="left" text="Kopier" />
<CopyButton copyText="3.14" iconPosition="right" text="Kopier" />
</div>
),
};
export const Sizes = {
render: () => (
<div className="colgap">
@@ -86,6 +86,23 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
}}
/>
<label htmlFor={inputProps.id} className="navds-checkbox__label">
<span className="navds-checkbox__icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="13"
height="10"
viewBox="0 0 13 10"
fill="none"
focusable={false}
role="img"
aria-hidden
>
<path
d="M4.03524 6.41478L10.4752 0.404669C11.0792 -0.160351 12.029 -0.130672 12.5955 0.47478C13.162 1.08027 13.1296 2.03007 12.5245 2.59621L5.02111 9.59934C4.74099 9.85904 4.37559 10 4.00025 10C3.60651 10 3.22717 9.84621 2.93914 9.56111L0.439143 7.06111C-0.146381 6.47558 -0.146381 5.52542 0.439143 4.93989C1.02467 4.35437 1.97483 4.35437 2.56036 4.93989L4.03524 6.41478Z"
fill="currentColor"
/>
</svg>
</span>
<span
className={cl("navds-checkbox__content", {
"navds-sr-only": props.hideLabel,
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { forwardRef, useMemo, useRef } from "react";
import { BodyShort, Label, mergeRefs } from "../..";
import { BodyShort, ErrorMessage, Label, mergeRefs } from "../..";
import ClearButton from "./ClearButton";
import FilteredOptions from "./FilteredOptions/FilteredOptions";
import { useFilteredOptionsContext } from "./FilteredOptions/filteredOptionsContext";
@@ -39,12 +39,15 @@ export const Combobox = forwardRef<
const {
clearInput,
error,
errorId,
focusInput,
hasError,
inputDescriptionId,
inputProps,
inputRef,
value,
showErrorMsg,
size = "medium",
} = useInputContext();
@@ -129,6 +132,14 @@ export const Combobox = forwardRef<
</div>
<FilteredOptions />
</div>
<div
className="navds-form-field__error"
id={errorId}
aria-relevant="additions removals"
aria-live="polite"
>
{showErrorMsg && <ErrorMessage size={size}>{error}</ErrorMessage>}
</div>
</ComboboxWrapper>
);
});
@@ -8,7 +8,6 @@ import { useInputContext } from "../Input/inputContext";
const FilteredOptions = () => {
const {
clearInput,
inputProps: { id },
size,
value,
@@ -51,7 +50,8 @@ const FilteredOptions = () => {
tabIndex={-1}
onPointerUp={(event) => {
toggleOption(value, event);
clearInput(event);
if (!isMultiSelect && !selectedOptions.includes(value))
toggleIsListOpen(false);
}}
id={`${id}-combobox-new-option`}
className={cl("navds-combobox__list-item__new-option", {
@@ -92,10 +92,8 @@ const FilteredOptions = () => {
tabIndex={-1}
onPointerUp={(event) => {
toggleOption(option, event);
clearInput(event);
if (!isMultiSelect) {
if (!isMultiSelect && !selectedOptions.includes(option))
toggleIsListOpen(false);
}
}}
role="option"
aria-selected={selectedOptions.includes(option)}
@@ -8,6 +8,7 @@ import React, {
useRef,
useLayoutEffect,
} from "react";
import cl from "clsx";
import { useCustomOptionsContext } from "../customOptionsContext";
import { useInputContext } from "../Input/inputContext";
import usePrevious from "../../../util/usePrevious";
@@ -58,7 +59,7 @@ export const FilteredOptionsProvider = ({ children, value: props }) => {
} = props;
const filteredOptionsRef = useRef<HTMLUListElement | null>(null);
const {
inputProps: { id },
inputProps: { "aria-describedby": partialAriaDescribedBy, id },
value,
searchTerm,
setValue,
@@ -124,18 +125,26 @@ export const FilteredOptionsProvider = ({ children, value: props }) => {
}, [allowNewValues, isValueNew]);
const ariaDescribedBy = useMemo(() => {
let activeOption;
if (!isLoading && filteredOptions.length === 0) {
return `${id}-no-hits`;
activeOption = `${id}-no-hits`;
} else if ((value && value !== "") || isLoading) {
if (shouldAutocomplete && filteredOptions[0]) {
return `${id}-option-${filteredOptions[0].replace(" ", "-")}`;
} else if (isLoading) {
return `${id}-is-loading`;
activeOption = `${id}-option-${filteredOptions[0].replace(" ", "-")}`;
} else if (isListOpen && isLoading) {
activeOption = `${id}-is-loading`;
}
} else {
return undefined;
}
}, [isLoading, value, shouldAutocomplete, filteredOptions, id]);
return cl(activeOption, partialAriaDescribedBy) || undefined;
}, [
isListOpen,
isLoading,
value,
partialAriaDescribedBy,
shouldAutocomplete,
filteredOptions,
id,
]);
const currentOption = useMemo(() => {
if (filteredOptionsIndex == null) {
@@ -22,8 +22,12 @@ interface InputProps
const Input = forwardRef<HTMLInputElement, InputProps>(
({ inputClassName, error, errorId, ...rest }, ref) => {
const { clearInput, inputProps, onChange, size, value } = useInputContext();
const { selectedOptions, removeSelectedOption, toggleOption } =
useSelectedOptionsContext();
const {
selectedOptions,
removeSelectedOption,
toggleOption,
isMultiSelect,
} = useSelectedOptionsContext();
const {
activeDecendantId,
allowNewValues,
@@ -37,6 +41,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
ariaDescribedBy,
moveFocusToInput,
moveFocusToEnd,
setFilteredOptionsIndex,
shouldAutocomplete,
} = useFilteredOptionsContext();
@@ -46,7 +51,8 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
event.preventDefault();
// Selecting a value from the dropdown / FilteredOptions
toggleOption(currentOption, event);
clearInput(event);
if (!isMultiSelect && !selectedOptions.includes(currentOption))
toggleIsListOpen(false);
} else if (shouldAutocomplete && selectedOptions.includes(value)) {
event.preventDefault();
// Trying to set the same value that is already set, so just clearing the input
@@ -55,15 +61,18 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
event.preventDefault();
// Autocompleting or adding a new value
toggleOption(value, event);
clearInput(event);
if (!isMultiSelect && !selectedOptions.includes(value))
toggleIsListOpen(false);
}
},
[
allowNewValues,
clearInput,
currentOption,
isMultiSelect,
selectedOptions,
shouldAutocomplete,
toggleIsListOpen,
toggleOption,
value,
]
@@ -139,6 +148,10 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
[filteredOptions.length, onChange, toggleIsListOpen]
);
const onBlur = () => {
setFilteredOptionsIndex(-1);
};
return (
<input
{...rest}
@@ -148,6 +161,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
onChange={onChangeHandler}
type="text"
role="combobox"
onBlur={onBlur}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
aria-controls={`${inputProps.id}-filtered-options`}
@@ -156,6 +170,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
aria-autocomplete={shouldAutocomplete ? "both" : "list"}
aria-activedescendant={activeDecendantId}
aria-describedby={ariaDescribedBy}
aria-invalid={inputProps["aria-invalid"]}
className={cl(
inputClassName,
"navds-combobox__input",
@@ -13,6 +13,7 @@ import { useFormField, FormFieldType } from "../../useFormField";
interface InputContextType extends FormFieldType {
clearInput: (event: React.PointerEvent | React.KeyboardEvent) => void;
error?: string;
focusInput: () => void;
inputRef: React.RefObject<HTMLInputElement>;
value: string;
@@ -80,10 +81,11 @@ export const InputContextProvider = ({ children, value: props }) => {
const clearInput = useCallback(
(event: React.PointerEvent | React.KeyboardEvent) => {
onClear?.(event);
externalOnChange?.(null, "");
setValue("");
setSearchTerm("");
},
[onClear, setSearchTerm, setValue]
[externalOnChange, onClear, setValue]
);
const focusInput = useCallback(() => {
@@ -101,6 +103,7 @@ export const InputContextProvider = ({ children, value: props }) => {
value={{
...formFieldProps,
clearInput,
error,
focusInput,
inputRef,
value,
@@ -1,6 +1,7 @@
import React from "react";
import { Chips } from "../../..";
import { useSelectedOptionsContext } from "./selectedOptionsContext";
import { useInputContext } from "../Input/inputContext";
interface SelectedOptionsProps {
selectedOptions?: string[];
@@ -10,10 +11,12 @@ interface SelectedOptionsProps {
const Option = ({ option }: { option: string }) => {
const { isMultiSelect, removeSelectedOption } = useSelectedOptionsContext();
const { focusInput } = useInputContext();
const onClick = (e) => {
e.stopPropagation();
removeSelectedOption(option);
focusInput();
};
if (!isMultiSelect) {
@@ -42,8 +42,12 @@ export const SelectedOptionsProvider = ({
>;
}) => {
const { clearInput, focusInput } = useInputContext();
const { customOptions, removeCustomOption, addCustomOption } =
useCustomOptionsContext();
const {
customOptions,
removeCustomOption,
addCustomOption,
setCustomOptions,
} = useCustomOptionsContext();
const {
allowNewValues,
isMultiSelect,
@@ -60,11 +64,12 @@ export const SelectedOptionsProvider = ({
const addSelectedOption = useCallback(
(option: string) => {
const isAddedByUser = !options
const isCustomOption = !options
.map((opt) => opt.toLowerCase())
.includes(option?.toLowerCase?.());
if (isAddedByUser) {
if (isCustomOption) {
allowNewValues && addCustomOption(option);
!isMultiSelect && setSelectedOptions([]);
} else if (isMultiSelect) {
setSelectedOptions((prevSelectedOptions) => [
...prevSelectedOptions,
@@ -72,16 +77,24 @@ export const SelectedOptionsProvider = ({
]);
} else {
setSelectedOptions([option]);
setCustomOptions([]);
}
onToggleSelected?.(option, true, isAddedByUser);
onToggleSelected?.(option, true, isCustomOption);
},
[addCustomOption, allowNewValues, isMultiSelect, onToggleSelected, options]
[
addCustomOption,
allowNewValues,
isMultiSelect,
onToggleSelected,
options,
setCustomOptions,
]
);
const removeSelectedOption = useCallback(
(option: string) => {
const isAddedByUser = customOptions.includes(option);
if (isAddedByUser) {
const isCustomOption = customOptions.includes(option);
if (isCustomOption) {
removeCustomOption(option);
} else {
setSelectedOptions((prevSelectedOptions) =>
@@ -90,7 +103,7 @@ export const SelectedOptionsProvider = ({
)
);
}
onToggleSelected?.(option, false, isAddedByUser);
onToggleSelected?.(option, false, isCustomOption);
},
[customOptions, onToggleSelected, removeCustomOption]
);
@@ -102,16 +115,13 @@ export const SelectedOptionsProvider = ({
} else {
addSelectedOption(option);
}
if (!isMultiSelect) {
clearInput(event);
}
clearInput(event);
focusInput();
},
[
addSelectedOption,
clearInput,
focusInput,
isMultiSelect,
removeSelectedOption,
selectedOptions,
]
@@ -3,7 +3,7 @@ import { Meta } from "@storybook/react";
import React, { useState, useId, useMemo } from "react";
import { userEvent, within } from "@storybook/testing-library";
import { Chips, UNSAFE_Combobox, TextField } from "../../index";
import { expect } from "@storybook/jest";
import { expect, jest } from "@storybook/jest";
export default {
title: "ds-react/Combobox",
@@ -94,6 +94,27 @@ MultiSelect.args = {
size: "medium",
};
export function WithAddNewOptions(props) {
const id = useId();
return (
<DemoContainer dataTheme={props.darkMode}>
<UNSAFE_Combobox
id={id}
label="Komboboks med mulighet for å legge til nye verdier"
options={props.options}
allowNewValues={props.allowNewValues}
shouldAutocomplete={props.shouldAutoComplete}
/>
</DemoContainer>
);
}
WithAddNewOptions.args = {
options,
allowNewValues: true,
shouldAutoComplete: true,
};
export function MultiSelectWithAddNewOptions(props) {
const id = useId();
return (
@@ -101,7 +122,7 @@ export function MultiSelectWithAddNewOptions(props) {
<UNSAFE_Combobox
id={id}
isMultiSelect={props.isMultiSelect}
label="Komboboks (med mulighet for å legge til nye verdier)"
label="Multiselect komboboks med mulighet for å legge til nye verdier"
options={props.options}
allowNewValues={props.allowNewValues}
/>
@@ -295,6 +316,33 @@ ComboboxSizes.args = {
options,
};
export const WithError = {
args: {
error: "Du må velge en favorittfrukt.",
isLoading: true,
},
render: (props) => {
const [hasSelectedValue, setHasSelectedValue] = useState(false);
const [isLoading, setIsLoading] = useState(false);
return (
<DemoContainer dataTheme={props.darkMode}>
<UNSAFE_Combobox
filteredOptions={isLoading ? [] : undefined}
options={options}
label="Hva er dine favorittfrukter?"
error={!hasSelectedValue && props.error}
isLoading={isLoading}
onChange={() => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
}}
onToggleSelected={(_, isSelected) => setHasSelectedValue(isSelected)}
/>
</DemoContainer>
);
},
};
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
@@ -324,6 +372,7 @@ export const CancelInputTest = {
userEvent.keyboard("{Escape}");
await sleep(1000);
userEvent.keyboard("{ArrowDown}");
await sleep(500);
const banana = canvas.getByText("banana");
userEvent.click(banana);
},
@@ -365,12 +414,20 @@ export const RemoveSelectedMultiSelectTest = {
await sleep(250);
userEvent.keyboard("{Enter}");
await sleep(250);
userEvent.keyboard("{Escape}");
await sleep(250);
const appleSlett = canvas.getByLabelText("apple slett");
appleSlett.focus();
await sleep(250);
userEvent.click(appleSlett);
await sleep(250);
const appleOption = canvas.getByRole("option", {
name: "apple",
selected: false,
});
expect(appleOption).toBeVisible();
userEvent.keyboard("{Escape}");
await sleep(250);
expect(appleOption).not.toBeVisible();
const bananaSlett = canvas.getByLabelText("banana slett");
expect(bananaSlett).toBeInTheDocument();
@@ -413,3 +470,42 @@ export const AddWhenAddNewDisabledTest = {
expect(invalidSelect).not.toBeInTheDocument();
},
};
export const TestThatCallbacksOnlyFireWhenExpected = {
args: {
onChange: jest.fn(),
onClear: jest.fn(),
onToggleSelected: jest.fn(),
},
render: (props) => {
return (
<DemoContainer dataTheme={props.darkMode}>
<UNSAFE_Combobox
options={options}
label="Hva er dine favorittfrukter?"
{...props}
/>
</DemoContainer>
);
},
play: async ({ canvasElement, args }) => {
args.onToggleSelected.mockClear();
args.onClear.mockClear();
args.onChange.mockClear();
const canvas = within(canvasElement);
const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
const searchWord = "tangerine";
userEvent.click(input);
await userEvent.type(input, searchWord, { delay: 200 });
await sleep(250);
userEvent.keyboard("{ArrowDown}");
await sleep(250);
userEvent.keyboard("{Enter}");
await sleep(250);
expect(args.onClear.mock.calls).toHaveLength(1);
expect(args.onToggleSelected.mock.calls).toHaveLength(1);
expect(args.onChange.mock.calls).toHaveLength(searchWord.length + 1);
},
};
@@ -1,10 +1,12 @@
import React, { useState, useCallback, createContext, useContext } from "react";
import { useInputContext } from "./Input/inputContext";
import { useSelectedOptionsContext } from "./SelectedOptions/selectedOptionsContext";
type CustomOptionsContextType = {
customOptions: string[];
removeCustomOption: (option: string) => void;
addCustomOption: (option: string) => void;
setCustomOptions: React.Dispatch<React.SetStateAction<string[]>>;
};
const CustomOptionsContext = createContext<CustomOptionsContextType>(
@@ -14,6 +16,7 @@ const CustomOptionsContext = createContext<CustomOptionsContextType>(
export const CustomOptionsProvider = ({ children }) => {
const [customOptions, setCustomOptions] = useState<string[]>([]);
const { focusInput } = useInputContext();
const { isMultiSelect } = useSelectedOptionsContext();
const removeCustomOption = useCallback(
(option) => {
@@ -27,16 +30,21 @@ export const CustomOptionsProvider = ({ children }) => {
const addCustomOption = useCallback(
(option) => {
setCustomOptions((prevOptions) => [...prevOptions, option]);
if (isMultiSelect) {
setCustomOptions((prevOptions) => [...prevOptions, option]);
} else {
setCustomOptions([option]);
}
focusInput();
},
[focusInput, setCustomOptions]
[focusInput, isMultiSelect, setCustomOptions]
);
const customOptionsState = {
customOptions,
removeCustomOption,
addCustomOption,
setCustomOptions,
};
return (
@@ -66,7 +66,10 @@ export interface ComboboxProps
* @param event
* @returns
*/
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
onChange?: (
event: ChangeEvent<HTMLInputElement> | null,
value?: string
) => void;
/**
* Callback function triggered whenever the input field is cleared
*
@@ -79,13 +82,13 @@ export interface ComboboxProps
*
* @param option
* @param isSelected - Whether the option has been selected or unselected
* @param isAddedByUser - Whether the option comes from user input, instead of from the list
* @param isCustomOption - Whether the option comes from user input, instead of from the list
* @returns
*/
onToggleSelected?: (
option: string,
isSelected: boolean,
isAddedByUser: boolean
isCustomOption: boolean
) => void;
/**
* List of selected options.
+4
View File
@@ -25,6 +25,10 @@ export interface CellProps extends HTMLAttributes<HTMLDivElement> {
lg?: Column;
}
/**
* @deprecated Use <HGrid /> instead. <Grid /> will be removed in the future.
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/hgrid)
*/
export const Cell = forwardRef<HTMLDivElement, CellProps>(
({ children, xs, sm, md, lg, className, ...rest }, ref) => {
return (
+4
View File
@@ -8,6 +8,10 @@ export interface GridProps extends HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
/**
* @deprecated Use <HGrid /> instead. <Grid /> will be removed in the future.
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/hgrid)
*/
export const Grid = forwardRef<HTMLDivElement, GridProps>(
({ children, className, ...rest }, ref) => (
<div ref={ref} className={cl("navds-grid", className)} {...rest}>
-4
View File
@@ -1,6 +1,2 @@
export { default as Cell, type CellProps } from "./Cell";
export { default as Grid, type GridProps } from "./Grid";
export {
default as ContentContainer,
type ContentContainerProps,
} from "./ContentContainer";
+2
View File
@@ -33,3 +33,5 @@ export * from "./tooltip";
export * from "./typography";
export * from "./util";
export * from "./layout/stack";
export * from "./layout/grid";
export * from "./layout/content-container";
@@ -0,0 +1,4 @@
export {
default as ContentContainer,
type ContentContainerProps,
} from "./ContentContainer";
@@ -0,0 +1,101 @@
import React, { forwardRef, HTMLAttributes } from "react";
import cl from "clsx";
import {
getResponsiveProps,
getResponsiveValue,
ResponsiveProp,
SpacingScale,
} from "../utilities/css";
export interface HGridProps extends HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
/**
* Number of columns to display. Can be a number, a string with a unit or tokens for spesific breakpoints.
* Sets `grid-template-columns`, so `fr`, `minmax` etc. works.
* @example
* columns={{ sm: 1, md: 1, lg: "1fr auto", xl: "1fr auto"}}
* @example
* columns={3}
* @example
* columns="repeat(3, minmax(0, 1fr))"
*/
columns?: ResponsiveProp<number | string>;
/** Spacing between columns. Based on spacing-tokens.
* @example
* gap="6"
* gap={{ sm: "2", md: "2", lg: "6", xl: "6"}}
*/
gap?: ResponsiveProp<SpacingScale>;
}
/**
* Horizontal Grid Primitive with dynamic columns and gap based on breakpoints.
*
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/hgrid)
* @see 🏷️ {@link HGridProps}
*
* @example
* <HGrid gap="6" columns={3}>
* <div />
* <div />
* <div />
* </HGrid>
* @example
* <HGrid gap={{xs: "2", md: "6"}} columns={3}>
* <div />
* <div />
* <div />
* </HGrid>
* @example
* <HGrid gap="6" columns={{ sm: 1, md: 1, lg: "1fr auto", xl: "1fr auto"}}>
* <div />
* <div />
* <div />
* </HGrid>
*/
export const HGrid = forwardRef<HTMLDivElement, HGridProps>(
({ className, columns, gap, style, ...rest }, ref) => {
const styles: React.CSSProperties = {
...style,
...getResponsiveProps(`hgrid`, "gap", "spacing", gap),
...getResponsiveValue(`hgrid`, "columns", formatGrid(columns)),
};
return (
<div
{...rest}
ref={ref}
className={cl("navds-hgrid", className)}
style={styles}
/>
);
}
);
function formatGrid(
props?: ResponsiveProp<number | string>
): ResponsiveProp<number | string> {
if (!props) {
return {};
}
if (typeof props === "string" || typeof props === "number") {
return getColumnValue(props);
}
return Object.fromEntries(
Object.entries(props).map(([breakpointToken, columnValue]) => [
breakpointToken,
getColumnValue(columnValue),
])
);
}
const getColumnValue = (prop: number | string) => {
if (typeof prop === "number") {
return `repeat(${prop}, minmax(0, 1fr))`;
}
return prop;
};
export default HGrid;
@@ -0,0 +1,102 @@
import React from "react";
import { HGrid } from ".";
const columnsVariants = {
Number: "columnNumber",
String: "columnString",
Object: "columnObject",
};
export default {
title: "ds-react/HGrid",
component: HGrid,
parameters: {
layout: "fullscreen",
},
argTypes: {
columnsType: {
defaultValue: Object.keys(columnsVariants)[0],
options: Object.keys(columnsVariants),
control: { type: "radio" },
},
},
};
/* const getColumnsProp = () */
export const Default = {
render: (props) => (
<HGrid
gap={props?.gap ?? 4}
columns={props[columnsVariants[props.columnsType]]}
>
<Placeholder text="1" />
<Placeholder text="2" />
<Placeholder text="3" />
<Placeholder text="4" />
</HGrid>
),
args: {
columnNumber: 4,
columnObject: { xs: 1, md: 4 },
columnString: "repeat(3, minmax(0, 1fr))",
gap: "4",
},
};
export const Gap = {
render: () => (
<HGrid gap="6">
<Placeholder text="1" />
<Placeholder text="2" />
<Placeholder text="3" />
<Placeholder text="4" />
</HGrid>
),
};
export const DynamicGap = {
render: () => (
<HGrid gap={{ xs: "2", md: "8" }}>
<Placeholder text="1" />
<Placeholder text="2" />
<Placeholder text="3" />
<Placeholder text="4" />
</HGrid>
),
};
export const Columns = {
render: () => (
<HGrid gap="4" columns={2}>
<Placeholder text="1" />
<Placeholder text="2" />
<Placeholder text="3" />
<Placeholder text="4" />
</HGrid>
),
};
export const DynamicColumns = {
render: () => (
<HGrid gap="4" columns={{ sm: "1fr 5fr", md: "2fr 2fr" }}>
<Placeholder text="1" />
<Placeholder text="2" />
</HGrid>
),
};
function Placeholder({ text }) {
return (
<div
style={{
background: "var(--a-deepblue-900)",
height: "5rem",
width: "auto",
color: "white",
}}
>
{text}
</div>
);
}
@@ -0,0 +1 @@
export { default as HGrid, type HGridProps } from "./HGrid";
@@ -53,12 +53,12 @@ export const Stack: OverridableComponent<StackProps, HTMLDivElement> =
ref
) => {
const style = {
"--ac-stack-direction": direction,
"--ac-stack-align": align,
"--ac-stack-justify": justify,
"--ac-stack-wrap": wrap ? "wrap" : "nowrap",
...getResponsiveProps(`stack`, "gap", "spacing", gap),
..._style,
"--__ac-stack-direction": direction,
"--__ac-stack-align": align,
"--__ac-stack-justify": justify,
"--__ac-stack-wrap": wrap ? "wrap" : "nowrap",
...getResponsiveProps(`stack`, "gap", "spacing", gap),
} as React.CSSProperties;
return (
+38 -16
View File
@@ -21,36 +21,58 @@ export type SpacingScale =
| "24"
| "32";
export type ResponsiveProp<T> =
| T
| {
// eslint-disable-next-line no-unused-vars
[Breakpoint in BreakpointsAlias]?: T;
};
type ResponsivePropConfig<T = string> = {
// eslint-disable-next-line no-unused-vars
[Breakpoint in BreakpointsAlias]?: T;
};
export function getResponsiveProps(
export type ResponsiveProp<T> = T | ResponsivePropConfig<T>;
export type ResponsiveValue<T = string> = undefined | ResponsiveProp<T>;
export function getResponsiveProps<T = string>(
componentName: string,
componentProp: string,
tokenSubgroup: string,
responsiveProp?:
| string
| {
// eslint-disable-next-line no-unused-vars
[Breakpoint in BreakpointsAlias]?: string;
}
responsiveProp?: ResponsiveProp<T>
) {
if (!responsiveProp) return {};
if (!responsiveProp) {
return {};
}
if (typeof responsiveProp === "string") {
return {
[`--ac-${componentName}-${componentProp}-xs`]: `var(--a-${tokenSubgroup}-${responsiveProp})`,
[`--__ac-${componentName}-${componentProp}-xs`]: `var(--a-${tokenSubgroup}-${responsiveProp})`,
};
}
return Object.fromEntries(
Object.entries(responsiveProp).map(([breakpointAlias, aliasOrScale]) => [
`--ac-${componentName}-${componentProp}-${breakpointAlias}`,
`--__ac-${componentName}-${componentProp}-${breakpointAlias}`,
`var(--a-${tokenSubgroup}-${aliasOrScale})`,
])
);
}
export function getResponsiveValue<T = string>(
componentName: string,
componentProp: string,
responsiveProp?: ResponsiveValue<T>
) {
if (!responsiveProp) {
return {};
}
if (typeof responsiveProp === "string") {
return {
[`--__ac-${componentName}-${componentProp}-xs`]: responsiveProp,
};
}
return Object.fromEntries(
Object.entries(responsiveProp).map(([breakpointAlias, responsiveValue]) => [
`--__ac-${componentName}-${componentProp}-${breakpointAlias}`,
responsiveValue,
])
);
}
+47 -3
View File
@@ -4,6 +4,23 @@ import { OverridableComponent } from "../util/OverridableComponent";
export interface LinkProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
/**
* Inverts when the underline appears. If this is false,
* the underline does not appear by default, but does appear when the link is hovered.
* This makes it more suitable for use when inlined in text.
* @default true
*/
underline?: boolean;
/**
* Renders link with `display: inline` for better wrapping in text.
* @default false
*/
inlineText?: boolean;
/**
* Variant of the component to use.
* @default "action"
*/
variant?: "action" | "neutral" | "subtle";
/**
* Link text
*/
@@ -22,10 +39,37 @@ export interface LinkProps
* <Link href="#">lenke til ny side</Link>
* <Link as={ReactRouter.Link} to="#">lenke til ny side</Link>
* ```
* @example
* ```jsx
* <BodyLong>
* ...
* <Link href="#" inlineText>lenke til ny side</Link>
* ...
* </BodyLong>
* ```
*/
export const Link: OverridableComponent<LinkProps, HTMLAnchorElement> =
forwardRef(({ as: Component = "a", className, ...rest }, ref) => (
<Component {...rest} ref={ref} className={cl("navds-link", className)} />
));
forwardRef(
(
{
as: Component = "a",
className,
underline = true,
variant = "action",
inlineText = false,
...rest
},
ref
) => (
<Component
{...rest}
ref={ref}
className={cl("navds-link", className, `navds-link--${variant}`, {
"navds-link--remove-underline": !underline,
"navds-link--inline-text": inlineText,
})}
/>
)
);
export default Link;
+143 -13
View File
@@ -6,29 +6,76 @@ import {
ConfirmationPanel as DsConfirmationPanel,
Link,
} from "..";
import { RandomIcon } from "../util/RandomIcon";
export default {
title: "ds-react/Link",
component: Link,
};
const LinkWrapper = ({
children = "Ex aliqua incididunt",
iconRight = false,
iconLeft = false,
variant = "action",
underline = false,
inlineText = true,
}) => (
<>
{" "}
<Link
href="#"
underline={underline}
variant={variant as "action" | "neutral" | "subtle"}
inlineText={inlineText}
>
{iconLeft && (
<>
<RandomIcon />{" "}
</>
)}
{children}
{iconRight && (
<>
{" "}
<RandomIcon />
</>
)}
</Link>{" "}
</>
);
export const Default = {
render: (props) => {
render: ({ icon, inline }) => {
const LinkD = () => (
<Link href="#">
{props.icon && <PlusCircleFillIcon />}Ex aliqua incididunt{" "}
{props.icon && <PlusCircleFillIcon />}
</Link>
<>
{" "}
<Link href="#" underline={!inline} inlineText={inline}>
{icon && <PlusCircleFillIcon />}Ex aliqua incididunt
{icon && <PlusCircleFillIcon />}
</Link>{" "}
</>
);
if (props.inline) {
if (inline) {
return (
<BodyLong>
Incididunt laborum nisi nisi Lorem <LinkD /> in. Laborum aute fugiat
officia adipisicing non veniam dolor nulla non ex consectetur fugiat
eiusmod aute. Culpa sit aute est duis minim in in voluptate velit
fugiat. Laboris est id deserunt ut ea Lorem eu. Esse elit laboris aute
commodo sint laborum fugiat aliqua.
</BodyLong>
<div
className="colgap"
style={{
width: "800px",
border: "1px solid black",
borderRadius: "8px",
}}
>
<BodyLong>
Incididunt laborum nisi nisi Lorem
<LinkD />
in. Laborum aute fugiat officia adipisicing non veniam dolor nulla
non ex consectetur fugiat eiusmod aute. Culpa sit aute est duis
minim in in voluptate velit fugiat. Laboris est id deserunt ut ea
Lorem eu. Esse elit laboris aute commodo sint laborum fugiat aliqua.
</BodyLong>
</div>
);
}
return <LinkD />;
@@ -40,6 +87,49 @@ export const Default = {
},
};
export const InlineInsideBodyLong = {
render: ({ iconLeft, iconRight }) => {
return (
<div
className="colgap"
style={{
width: "800px",
border: "1px solid black",
borderRadius: "8px",
}}
>
<style>{`.storybook-custom-spacing { white-space: pre;}`}</style>
<BodyLong>
<LinkWrapper underline iconLeft={iconLeft} iconRight={iconRight} />
Eiusmod aute.
<LinkWrapper underline iconLeft={iconLeft} iconRight={iconRight} />
Culpa sit aute est duis minim in in voluptate velit Incididunt laborum
nisi nisi Lorem officia adipisicing non veniam
<LinkWrapper underline iconLeft={iconLeft} iconRight={iconRight}>
blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah
</LinkWrapper>
dolor eu. Esse elit laboris aute commodo sint laborum fugiat aliqua.
<LinkWrapper underline iconLeft={iconLeft} iconRight={iconRight}>
Link
</LinkWrapper>
</BodyLong>
<BodyLong className="storybook-custom-spacing">
Custom{" "}
<LinkWrapper underline iconLeft={iconLeft} iconRight={iconRight}>
link
</LinkWrapper>
{" "}spacing.
</BodyLong>
</div>
);
},
args: {
iconLeft: false,
iconRight: false,
},
};
const DemoLink = () => (
<Link href="#">
<PlusCircleFillIcon aria-hidden /> Ex aliqua incididunt{" "}
@@ -83,3 +173,43 @@ export const ConfirmationPanel = () => {
</div>
);
};
export const Variants = {
render: ({ iconLeft, iconRight }) => {
return (
<div className="colgap">
{["action", "neutral", "subtle"].map((variant) => (
<>
<div>
<LinkWrapper
iconLeft={iconLeft}
iconRight={iconRight}
variant={variant}
/>
</div>
</>
))}
</div>
);
},
args: {
iconLeft: false,
iconRight: false,
},
};
export const InlineLink = {
render: () => (
<BodyLong>
Officia incididunt Culpa sit aute est duis minim in in voluptate velit
Incididunt laborum nisi nisi Lorem officia adipisicing non veniam{" "}
<Link href="#" inlineText={true}>
lenke til ny side
<PlusCircleFillIcon aria-hidden />
</Link>{" "}
Culpa sit aute est duis minim in in voluptate velit Incididunt laborum
nisi nisi Lorem officia adipisicing non veniam occaecat commodo id ad
aliquip.
</BodyLong>
),
};
+21 -5
View File
@@ -24,6 +24,11 @@ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
* @default "h3"
*/
headingTag?: React.ElementType<any>;
/**
* Changes padding, height and font-size
* @default medium
*/
size?: "medium" | "small";
}
export interface ListComponent
@@ -37,13 +42,15 @@ export interface ListComponent
}
interface ListContextProps {
listType: "ul" | "ol";
listType: ListProps["as"];
isNested: boolean | null;
size: ListProps["size"];
}
export const ListContext = createContext<ListContextProps>({
listType: "ul",
isNested: null,
size: "medium",
});
export const List = forwardRef<HTMLDivElement, ListProps>(
@@ -55,35 +62,44 @@ export const List = forwardRef<HTMLDivElement, ListProps>(
title,
description,
headingTag = "h3",
size,
...rest
},
ref
) => {
const ariaId = useId();
const { isNested } = useContext(ListContext);
const { isNested, size: _size } = useContext(ListContext);
const listSize = size ?? _size ?? "medium";
return (
<ListContext.Provider
value={{
listType: ListTag,
isNested: isNested === null ? false : true,
size: listSize,
}}
>
<div
{...rest}
ref={ref}
className={cl("navds-list", className, {
className={cl("navds-list", `navds-list--${listSize}`, className, {
"navds-list--nested": isNested === null ? false : true,
})}
>
{title && (
<Heading id={`tittel-${ariaId}`} size="small" as={headingTag}>
<Heading
id={`tittel-${ariaId}`}
size={listSize === "medium" ? "small" : "xsmall"}
as={headingTag}
>
{title}
</Heading>
)}
{description && (
<BodyShort id={`description-${ariaId}`}>{description}</BodyShort>
<BodyShort size={listSize} id={`description-${ariaId}`}>
{description}
</BodyShort>
)}
<ListTag
aria-labelledby={title && `tittel-${ariaId}`}
+4 -10
View File
@@ -20,7 +20,7 @@ export interface ListItemProps extends React.HTMLAttributes<HTMLLIElement> {
export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
({ className, children, title, icon, ...rest }, ref) => {
const { listType, isNested } = useContext(ListContext);
const { listType, size } = useContext(ListContext);
if (listType === "ol" && icon) {
console.warn(
@@ -29,13 +29,7 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
}
return (
<li
{...rest}
ref={ref}
className={cl("navds-list__item", className, {
"navds-list__item--noMargin": isNested,
})}
>
<li {...rest} ref={ref} className={cl("navds-list__item", className)}>
{listType === "ul" && (
<div
className={cl({
@@ -62,9 +56,9 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
</div>
)}
<BodyShort as="div" size="small" className="navds-list__item-content">
<BodyShort as="div" size={size} className="navds-list__item-content">
{title && (
<Label as="p" size="small">
<Label as="p" size={size}>
{title}
</Label>
)}
+132 -1
View File
@@ -6,7 +6,7 @@ import {
} from "@navikt/aksel-icons";
import { Meta } from "@storybook/react";
import React from "react";
import { List } from "..";
import { List, VStack } from "..";
export default {
title: "ds-react/List",
@@ -91,6 +91,137 @@ export const WithHeading = {
},
};
export const SizesUl = {
render: () => {
return (
<VStack gap="8">
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="medium"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Small list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="small"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
</VStack>
);
},
};
export const SizesOl = {
render: () => {
return (
<VStack gap="8">
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="medium"
as="ol"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Small list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="small"
as="ol"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
</VStack>
);
},
};
export const SizesIcons = {
render: () => {
return (
<VStack gap="8">
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="medium"
>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Lorem Ipsum Dolor Sit Amet"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Consectetur Adipiscing Elit"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item icon={<HeadHeartIcon aria-hidden />}>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Small list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="small"
>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Lorem Ipsum Dolor Sit Amet"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Consectetur Adipiscing Elit"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item icon={<HeadHeartIcon aria-hidden />}>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
</VStack>
);
},
};
export const Icons = {
render: () => {
return (
@@ -0,0 +1,9 @@
import * as AllIcons from "@navikt/aksel-icons";
import React from "react";
const icons = Object.keys(AllIcons).map((key) => AllIcons[key]);
export const RandomIcon = () => {
const Icon = icons[Math.floor(Math.random() * icons.length)];
return <Icon />;
};
+14
View File
@@ -1,5 +1,19 @@
# @navikt/ds-tailwind
## 4.12.0
## 4.11.2
## 4.11.1
## 4.11.0
## 4.10.2
## 4.10.1
## 4.10.0
## 4.9.1
## 4.9.0
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "4.9.1",
"version": "4.12.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": "^4.9.1",
"@navikt/ds-tokens": "^4.12.0",
"@types/jest": "^29.0.0",
"color": "4.2.3",
"jest": "^29.0.0",
+14
View File
@@ -1,5 +1,19 @@
# @navikt/ds-tokens
## 4.12.0
## 4.11.2
## 4.11.1
## 4.11.0
## 4.10.2
## 4.10.1
## 4.10.0
## 4.9.1
## 4.9.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "4.9.1",
"version": "4.12.0",
"description": "Design-tokens for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
+84
View File
@@ -1,5 +1,89 @@
# Changelog
## 4.12.0
### @navikt/ds-css
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
<!---->
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
- Alert: Fikset alignment av status-ikon mot tekst ([#2179](https://github.com/navikt/aksel/pull/2179))
### @navikt/ds-react
- Combobox: La til støtte for feilmeldinger i Combobox ([#2182](https://github.com/navikt/aksel/pull/2182))
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
<!---->
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
- Combobox: Kjører nå 'onChange' + 'onClear' når input blir reset programmatisk ([#2183](https://github.com/navikt/aksel/pull/2183))
- Combobox: Fikset custom-options i singleselect ([#2180](https://github.com/navikt/aksel/pull/2180))
- Combobox: Fjernet unødvendige 'onClear'-calls når man velger verdier ([#2170](https://github.com/navikt/aksel/pull/2170))
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
- Combobox: Lukker nå nedtrekksmeny hvis man legger til ny option i singleselect ([#2177](https://github.com/navikt/aksel/pull/2177))
## 4.11.2
### @navikt/ds-css
- :lipstick: ToggleGroup: fjern semibold fra selected button ([#2167](https://github.com/navikt/aksel/pull/2167))
## 4.11.1
### @navikt/ds-css
- Border-radius: Fikset hardkodet border-radius i Datepicker, ToggleGroup og Combobox. ([#2159](https://github.com/navikt/aksel/pull/2159))
## 4.11.0
### @navikt/ds-css
- HGrid: Ny komponent 'HGrid' er ute i Beta: [Dokumentasjon](https://aksel.nav.no/komponenter/core/hgrid) ([#1838](https://github.com/navikt/aksel/pull/1838))
<!---->
- List: Fikset alignment av ikoner ([#2149](https://github.com/navikt/aksel/pull/2149))
- Font: La til egen font for semibold italic for bedre skalering cross-browser ([#2150](https://github.com/navikt/aksel/pull/2150))
### @navikt/ds-react
- HGrid: Ny komponent 'HGrid' er ute i Beta: [Dokumentasjon](https://aksel.nav.no/komponenter/core/hgrid) ([#1838](https://github.com/navikt/aksel/pull/1838))
<!---->
- Fixes bug where combobox list could not be closed after clicking a chip ([#2155](https://github.com/navikt/aksel/pull/2155))
- Grid: Markert som deprecated. Bruk nye 'HGrid' ([#2153](https://github.com/navikt/aksel/pull/2153))
## 4.10.2
### @navikt/ds-css
- Link: Fikset visited farger og inline SVG-reset ved bruk av tailwind
## 4.10.0
### @navikt/ds-css
- Link: La til 'variant', 'underline' og 'inlineText'-prop ([#2093](https://github.com/navikt/aksel/pull/2093))
### @navikt/ds-react
- Link: La til 'variant', 'underline' og 'inlineText'-prop ([#2093](https://github.com/navikt/aksel/pull/2093))
## 4.9.1
### @navikt/ds-css
+5 -5
View File
@@ -15,11 +15,11 @@
"website"
],
"dependencies": {
"@navikt/aksel-icons": "^4.9.1",
"@navikt/ds-css": "^4.9.1",
"@navikt/ds-react": "^4.9.1",
"@navikt/ds-tailwind": "^4.9.1",
"@navikt/ds-tokens": "^4.9.1"
"@navikt/aksel-icons": "^4.12.0",
"@navikt/ds-css": "^4.12.0",
"@navikt/ds-react": "^4.12.0",
"@navikt/ds-tailwind": "^4.12.0",
"@navikt/ds-tokens": "^4.12.0"
},
"devDependencies": {
"prettier-plugin-tailwindcss": "^0.2.3"
@@ -44,11 +44,11 @@ const GetIcon = (s: string) => {
const getText = (s: string) => {
switch (s) {
case "do":
return "Do: ";
return "Gjør: ";
case "dont":
return "Don't: ";
return "Unngå: ";
case "warning":
return "Fare på ferde: ";
return "Utfordrende: ";
default:
return "";
}
@@ -74,7 +74,7 @@ const Element = ({ block }: { block: DoDontT["blokker"][number] }) => {
)}
aria-hidden
>
<span className="absolute right-[10px] z-10 translate-y-2">
<span className="absolute right-[2px] z-10">
{GetIcon(block.variant)}
</span>
</div>
@@ -1,4 +1,5 @@
import { PencilIcon } from "@navikt/aksel-icons";
import { Button } from "@navikt/ds-react";
import { getParameters } from "codesandbox/lib/api/define";
const indexTsx = `import React from 'react';
@@ -50,16 +51,18 @@ export const CodeSandbox = ({ code }: { code: string }) => {
action="https://codesandbox.io/api/v1/sandboxes/define"
method="POST"
target="_blank"
className="h-8"
>
<input type="hidden" name="parameters" value={parameters} />
<input type="hidden" name="query" value="module=App.js" />
<button
<Button
variant="tertiary-neutral"
size="small"
type="submit"
className="focus-visible:shadow-focus-inset flex items-center justify-center gap-1 rounded-sm px-3 py-2 hover:bg-gray-100 focus:outline-none active:bg-gray-200"
icon={<PencilIcon aria-hidden fontSize="1.5rem" />}
>
<PencilIcon aria-hidden fontSize="1.5rem" />
CodeSandbox
</button>
</Button>
</form>
);
};
@@ -1,11 +1,16 @@
import { capitalize, Snippet } from "@/components";
import { withErrorBoundary } from "@/error-boundary";
import { CodeExapmplesT } from "@/types";
import { BodyLong, Chips, Link } from "@navikt/ds-react";
import {
ExternalLinkIcon,
LaptopIcon,
MobileSmallIcon,
} from "@navikt/aksel-icons";
import { BodyLong, Button, Chips, HStack } from "@navikt/ds-react";
import cl from "clsx";
import { useEffect, useState } from "react";
import { CodeSandbox } from "./CodeSandbox";
import { useRouter } from "next/router";
import { useEffect, useRef, useState } from "react";
import { CodeSandbox } from "./CodeSandbox";
const iframePadding = 192;
const iframeId = "example-iframe";
@@ -15,6 +20,7 @@ const ComponentExamples = ({ node }: { node: CodeExapmplesT }) => {
const [frameState, setFrameState] = useState(300);
const [unloaded, setUnloaded] = useState(true);
const router = useRouter();
const iframeRef = useRef<HTMLIFrameElement>(null);
const handleExampleLoad = () => {
let attempts = 0;
@@ -139,10 +145,11 @@ const ComponentExamples = ({ node }: { node: CodeExapmplesT }) => {
id={iframeId}
aria-label={`${node?.dir?.title} ${fil.navn} eksempel`}
className={cl(
"min-w-80 block w-full max-w-full resize-x overflow-auto bg-white shadow-[20px_0_20px_-20px_rgba(0,0,0,0.22)]",
"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 }
)}
title="Kode-eksempler"
ref={iframeRef}
/>
{unloaded && (
<div className="absolute inset-0 mx-auto flex flex-col items-center justify-center gap-2">
@@ -153,18 +160,50 @@ const ComponentExamples = ({ node }: { node: CodeExapmplesT }) => {
</div>
)}
</div>
<div className="mb-2 flex justify-center gap-2 rounded-b border border-gray-300 px-2 py-1 text-base sm:justify-end ">
<CodeSandbox code={fil.innhold.trim()} />
<Link
href={`/eksempler/${node.dir.title}/${fil.navn.replace(
".tsx",
""
)}`}
className="si-ignore text-gray-900"
target="_blank"
>
Åpne i nytt vindu
</Link>
<div className="mb-2 rounded-b border border-gray-300 p-1">
<HStack gap="4" justify="space-between">
<div className="hidden sm:block">
<HStack gap="2">
<Button
variant="tertiary-neutral"
size="small"
icon={
<MobileSmallIcon title="Sett eksempel til mobilbredde" />
}
onClick={() =>
(iframeRef.current.style.width = "360px")
}
/>
<Button
variant="tertiary-neutral"
size="small"
icon={
<LaptopIcon title="Sett eksempel til desktopbredde" />
}
onClick={() => (iframeRef.current.style.width = "")}
/>
</HStack>
</div>
<HStack gap="2">
<CodeSandbox code={fil.innhold.trim()} />
<Button
variant="tertiary-neutral"
size="small"
icon={
<ExternalLinkIcon title="Åpne eksempel i nytt vindu" />
}
target="_blank"
className="si-ignore"
as="a"
href={`/eksempler/${node.dir.title}/${fil.navn.replace(
".tsx",
""
)}`}
/>
</HStack>
</HStack>
</div>
<Snippet
@@ -137,7 +137,7 @@ export function TableOfContents({
hidden: !renderToc,
"col-start-3 max-w-prose md:sticky md:top-20 lg:flex":
aksel && renderToc,
"mt-12 mr-auto h-full xl:flex": !aksel && renderToc,
"mr-auto mt-12 h-full xl:flex": !aksel && renderToc,
}
)}
>
@@ -1,7 +1,7 @@
import { useMedia } from "@/utils";
import * as Icons from "@navikt/aksel-icons";
import meta from "@navikt/aksel-icons/metadata";
import { Heading, Modal, Search, ToggleGroup } from "@navikt/ds-react";
import { HGrid, Heading, Modal, Search, ToggleGroup } from "@navikt/ds-react";
import cl from "clsx";
import Footer from "components/layout/footer/Footer";
import { Header } from "components/layout/header/Header";
@@ -76,6 +76,12 @@ export const IconPage = ({ name }: { name: string }) => {
hideModal && Modal.setAppElement("#__next");
}, [hideModal]);
const subCategoryWordLength = Math.max(
...categories.map((cat) =>
Math.max(...cat.sub_categories.map((sub) => sub.sub_category.length))
)
);
return (
<>
<Head>
@@ -177,56 +183,68 @@ export const IconPage = ({ name }: { name: string }) => {
>
{cat.category}
</Heading>
<div className="grid w-full gap-2">
<div className="grid w-full">
{cat.sub_categories.map((sub) => {
return (
<div key={sub.sub_category}>
<Heading
level="3"
size="xsmall"
className="text-text-subtle mb-1"
<div
key={sub.sub_category}
className="border-t-border-subtle border-t py-2"
>
<HGrid
columns={{
xs: "1fr",
md: `${subCategoryWordLength}ch 1fr`,
}}
gap={{ xs: "0", md: "2" }}
>
{sub.sub_category}
</Heading>
<div className="gap-05 flex flex-wrap">
{sub.icons.map((i) => {
const T = Icons[`${i.id}Icon`];
if (T === undefined) {
return null;
}
return (
<Link
href={`/ikoner/${i.id}`}
scroll={false}
key={i.id}
prefetch={false}
id={i.id}
tabIndex={0}
ref={(el) => {
if (name === i.id) {
focusRef.current = el;
}
}}
className={cl(
"hover:bg-surface-hover bg-surface-default active:bg-surface-neutral-subtle-hover group relative grid aspect-square w-11 shrink-0 place-items-center rounded focus:outline-none focus:ring-blue-800 focus-visible:ring-2",
{
"from-surface-selected bg-surface-selected ring-border-alt-3 z-10 bg-gradient-to-br to-teal-50 ring-1":
i.id === name,
}
)}
>
<span className="navds-sr-only">
{i.name}
</span>
<T
className="text-3xl"
aria-hidden
alt=""
/>
</Link>
);
})}
</div>
<Heading
level="3"
size="xsmall"
className="text-text-subtle leading-[44px]"
>
{sub.sub_category}
</Heading>
<div className="gap-05 flex flex-wrap">
{sub.icons.map((i) => {
const T = Icons[`${i.id}Icon`];
if (T === undefined) {
return null;
}
return (
<Link
href={`/ikoner/${i.id}`}
scroll={false}
key={i.id}
prefetch={false}
id={i.id}
tabIndex={0}
ref={(el) => {
if (name === i.id) {
focusRef.current = el;
}
}}
className={cl(
"hover:bg-surface-neutral-subtle-hover bg-surface-subtle active:bg-surface-neutral-subtle-hover group relative grid aspect-square w-11 shrink-0 place-items-center rounded focus:outline-none focus:ring-blue-800 focus-visible:ring-2",
{
"from-surface-selected bg-surface-selected ring-border-alt-3 z-10 bg-gradient-to-br to-teal-50 ring-1":
i.id === name,
}
)}
>
<span className="navds-sr-only">
{i.name}
</span>
<T
className="text-3xl"
aria-hidden
alt=""
/>
</Link>
);
})}
</div>
</HGrid>
</div>
);
})}
@@ -87,7 +87,7 @@ export const IconSidebar = ({
href="/ikoner"
scroll={false}
prefetch={false}
className="min-h-11 hover:bg-surface-hover focus-visible:shadow-focus active:bg-surface-neutral-subtle-hover absolute right-2 top-2 grid aspect-square place-content-center rounded text-xl focus:outline-none"
className="min-h-11 hover:bg-surface-hover focus-visible:shadow-focus active:bg-surface-neutral-subtle-hover absolute right-2 top-4 grid aspect-square place-content-center rounded text-xl focus:outline-none"
onClick={() => {
focusRef?.current?.focus?.();
}}
@@ -1,9 +1,9 @@
import meta from "@navikt/aksel-icons/metadata";
const subCategorizeIcons = (
icons: typeof meta[1][]
): { sub_category: string; icons: typeof meta[1][] }[] => {
const categories: { sub_category: string; icons: typeof meta[1][] }[] = [];
icons: (typeof meta)[1][]
): { sub_category: string; icons: (typeof meta)[1][] }[] => {
const categories: { sub_category: string; icons: (typeof meta)[1][] }[] = [];
for (const icon of icons) {
const i = categories.findIndex(
@@ -19,14 +19,14 @@ const subCategorizeIcons = (
};
export const categorizeIcons = (
icons: typeof meta[1][]
icons: (typeof meta)[1][]
): {
category: string;
sub_categories: { sub_category: string; icons: typeof meta[1][] }[];
sub_categories: { sub_category: string; icons: (typeof meta)[1][] }[];
}[] => {
const categories: {
category: string;
icons: typeof meta[1][];
icons: (typeof meta)[1][];
}[] = [];
for (const icon of icons) {
@@ -42,14 +42,14 @@ export const categorizeIcons = (
.map((x) => ({ ...x, sub_categories: subCategorizeIcons(x.icons) }));
};
const noFill = (icon: typeof meta[1], icons: typeof meta[1][]) => {
const noFill = (icon: (typeof meta)[1], icons: (typeof meta)[1][]) => {
const foundFill = icons.find(
(x) => x.name.endsWith("Fill") && x.name.replace("Fill", "") === icon.name
);
return !foundFill;
};
export const getFillIcon = (icons: typeof meta[1][]) => {
export const getFillIcon = (icons: (typeof meta)[1][]) => {
return icons.filter(
(x, _, z) => x.variant.toLowerCase() === "fill" || noFill(x, z)
);
@@ -186,7 +186,7 @@ const options: {
icon: <ChangeLogIconOutline />,
},
"komponent-beta": {
text: "Komponenten er under utvikling, men klar for adopsjon. Vi er ønsker gjerne innspill på hvordan den fungerer og hvilken forbedringer vi kan gjøre.",
text: "Komponenten er under utvikling, men klar for adopsjon. Vi ønsker gjerne innspill på hvordan den fungerer og hvilken forbedringer vi kan gjøre.",
link: `${issueUrl}&labels=forespørsel+🥰%2Ckomponenter+🧩%2Cbeta+🧪&template=update-component.yml&title=%5BInnspill+til+komponent%5D%3A+`,
heading: "Beta",
icon: (
+8 -8
View File
@@ -30,15 +30,15 @@
"dependencies": {
"@amplitude/analytics-browser": "^2.0.0",
"@sanity/block-content-to-react": "^3.0.0",
"@sanity/client": "^6.1.5",
"@sanity/client": "^6.4.6",
"@sanity/code-input": "4.1.1",
"@sanity/color-input": "^3.1.0",
"@sanity/icons": "2.4.1",
"@sanity/image-url": "1.0.2",
"@sanity/preview-kit": "^2.3.0",
"@sanity/table": "1.0.1",
"@sanity/ui": "^1.7.0",
"@sanity/vision": "3.14.1",
"@sanity/ui": "^1.7.4",
"@sanity/vision": "3.15.1",
"@svgr/webpack": "^5.5.0",
"boring-avatars": "1.10.1",
"clsx": "^1.2.1",
@@ -48,7 +48,7 @@
"lodash": "^4.17.21",
"next": "^13.2.1",
"next-logger": "^3.0.2",
"next-sanity": "^5.1.0",
"next-sanity": "^5.2.7",
"pino": "^8.14.1",
"prism-react-renderer": "^1.3.0",
"prismjs": "^1.27.0",
@@ -56,14 +56,14 @@
"react-dom": "^18.0.0",
"react-modal": "3.15.1",
"react-responsive-masonry": "^2.1.7",
"sanity": "3.14.1",
"sanity": "3.15.1",
"sanity-plugin-asset-source-unsplash": "^1.1.0",
"sanity-plugin-media": "^2.0.5",
"sharp": "0.31.3",
"sitemap": "^7.0.0",
"styled-components": "^5.2",
"swr": "^1.1.2",
"tailwindcss": "^3.2.0",
"tailwindcss": "^3.3.3",
"tinycolor2": "^1.6.0"
},
"devDependencies": {
@@ -72,8 +72,8 @@
"@next/bundle-analyzer": "^12.1.0",
"@next/eslint-plugin-next": "^12.2.0",
"@playwright/test": "1.35.1",
"@sanity/block-tools": "^3.12.2",
"@sanity/schema": "^3.12.2",
"@sanity/block-tools": "^3.15.1",
"@sanity/schema": "^3.15.1",
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^13.3.0",
"@types/jest": "^29.0.0",
@@ -41,5 +41,5 @@ export const Demo = {
};
export const args = {
index: 1,
index: 4,
};
@@ -40,5 +40,5 @@ export const Demo = {
};
export const args = {
index: 0,
index: 1,
};
@@ -59,5 +59,5 @@ export const Demo = {
};
export const args = {
index: 0,
index: 2,
};
@@ -40,5 +40,5 @@ export const Demo = {
};
export const args = {
index: 0,
index: 3,
};
@@ -36,14 +36,14 @@ export const Example = () => {
const onToggleSelected = (
option: string,
isSelected: boolean,
isAddedByUser: boolean
isCustomOption: boolean
) => {
if (isSelected) {
setSelectedOptions([...selectedOptions, option]);
} else {
setSelectedOptions(selectedOptions.filter((o) => o !== option));
}
if (isAddedByUser) {
if (isCustomOption) {
mockPersistUserAddedValues(option, isSelected);
}
};
@@ -0,0 +1,36 @@
import { UNSAFE_Combobox } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const initialOptions = [
"banana",
"apple",
"tangerine",
"pear",
"grape",
"kiwi",
"mango",
"passion fruit",
"pineapple",
"strawberry",
"watermelon",
"grape fruit",
];
export const Example = () => {
return (
<div>
<UNSAFE_Combobox
label="Hva er din favorittfrukt?"
options={initialOptions}
error="Du må velge en favorittfrukt."
/>
</div>
);
};
export default withDsExample(Example, "static");
export const args = {
index: 0,
desc: "Ved Single Select velger brukeren kun ett valg fra nedtrekkslisten.",
};
@@ -0,0 +1,63 @@
import { HGrid } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<Background>
<HGrid
gap="6"
columns={{ xs: "repeat(auto-fit, minmax(10rem, 1fr))", md: 4 }}
>
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
</HGrid>
</Background>
);
};
const Placeholder = ({ height = "auto", width = "auto" }) => {
return (
<div
style={{
display: "inherit",
background: "var(--a-deepblue-500)",
height: height ?? undefined,
width: width ?? undefined,
}}
/>
);
};
const Background = ({
children,
width = "100%",
}: {
children: React.ReactNode;
width?: string;
}) => {
return (
<div
style={{
background: "var(--a-deepblue-100)",
width,
height: "auto",
}}
>
{children}
</div>
);
};
export default withDsExample(Example, "static");
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 1,
desc: "Columns støtter både statisk antall kolonner med 'number' og mer fleksible kolonner med 'string'. ",
};
@@ -0,0 +1,59 @@
import { HGrid } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<Background>
<HGrid gap="6" columns={3}>
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
</HGrid>
</Background>
);
};
const Placeholder = ({ height = "auto", width = "auto" }) => {
return (
<div
style={{
display: "inherit",
background: "var(--a-deepblue-500)",
height: height ?? undefined,
width: width ?? undefined,
}}
/>
);
};
const Background = ({
children,
width = "100%",
}: {
children: React.ReactNode;
width?: string;
}) => {
return (
<div
style={{
background: "var(--a-deepblue-100)",
width,
height: "auto",
}}
>
{children}
</div>
);
};
export default withDsExample(Example, "static");
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 0,
desc: "HGrid lar deg enkelt dele innholdet opp i kolonner. Basert på CSS-grid",
};
@@ -0,0 +1,60 @@
import { HGrid } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<Background>
<HGrid gap="6" columns={{ xs: 1, sm: 2, md: 4 }}>
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
</HGrid>
</Background>
);
};
const Placeholder = ({ height = "auto", width = "auto" }) => {
return (
<div
style={{
display: "inherit",
background: "var(--a-deepblue-500)",
height: height ?? undefined,
width: width ?? undefined,
}}
/>
);
};
const Background = ({
children,
width = "100%",
}: {
children: React.ReactNode;
width?: string;
}) => {
return (
<div
style={{
background: "var(--a-deepblue-100)",
width,
height: "auto",
}}
>
{children}
</div>
);
};
export default withDsExample(Example, "static");
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 2,
desc: "Med responsive kolonner kan man dynamiskt tilpasse dem basert på brekkpunktene våre.",
};
@@ -0,0 +1,60 @@
import { HGrid } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<Background>
<HGrid gap={{ xs: "2", md: "8" }} columns={3}>
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
<Placeholder height="240px" />
</HGrid>
</Background>
);
};
const Placeholder = ({ height = "auto", width = "auto" }) => {
return (
<div
style={{
display: "inherit",
background: "var(--a-deepblue-500)",
height: height ?? undefined,
width: width ?? undefined,
}}
/>
);
};
const Background = ({
children,
width = "100%",
}: {
children: React.ReactNode;
width?: string;
}) => {
return (
<div
style={{
background: "var(--a-deepblue-100)",
width,
height: "auto",
}}
>
{children}
</div>
);
};
export default withDsExample(Example, "static");
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 3,
desc: "Med responsiv gap kan man dynamiskt tilpasse spacing basert på brekkpunktene våre.",
};
@@ -0,0 +1,30 @@
import { PlusCircleFillIcon } from "@navikt/aksel-icons";
import { BodyLong, Link } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<BodyLong>
Officia incididunt Culpa sit aute est duis minim in in voluptate velit
Incididunt laborum nisi nisi Lorem officia adipisicing non veniam{" "}
<Link href="#" inlineText>
lenke til ny side
<PlusCircleFillIcon aria-hidden />
</Link>{" "}
Culpa sit aute est duis minim in in voluptate velit Incididunt laborum
nisi nisi Lorem officia adipisicing non veniam occaecat commodo id ad
aliquip.
</BodyLong>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 0,
};
@@ -0,0 +1,25 @@
import { BodyLong, Link } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<BodyLong>
Officia incididunt{" "}
<Link href="#" underline={false}>
lenke til ny side
</Link>{" "}
occaecat commodo id ad aliquip.
</BodyLong>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 0,
};
@@ -0,0 +1,29 @@
import { Link } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<div className="colgap">
{["action", "neutral", "subtle"].map((variant) => (
<>
<div>
<Link variant={variant as "action" | "neutral" | "subtle"}>
Ex aliqua incididunt
</Link>
</div>
</>
))}
</div>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 0,
};
@@ -1,8 +1,8 @@
import {
ArrowCirclepathIcon,
BrailleIcon,
ChangingRoomIcon,
HeadHeartIcon,
ChevronDownCircleIcon,
ChevronUpCircleIcon,
ChevronUpDoubleCircleIcon,
ExclamationmarkTriangleIcon,
} from "@navikt/aksel-icons";
import { List } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
@@ -10,18 +10,24 @@ import { withDsExample } from "components/website-modules/examples/withDsExample
const Example = () => {
return (
<List>
<List.Item icon={<BrailleIcon aria-hidden />}>
Blinkende innhold. Vi vil ikke gi brukerne våre anfall.
<List.Item
title="Kritisk"
icon={<ExclamationmarkTriangleIcon aria-hidden />}
>
dette er problemer som må løses så fort som mulig. De hindrer bruk av
siden og kan i verste fall være farlig for bruker.
</List.Item>
<List.Item icon={<HeadHeartIcon aria-hidden />}>
Kritiske problemer og problemer med høye barrierer på innhold med høy
effekt.
<List.Item title="Høy" icon={<ChevronUpDoubleCircleIcon aria-hidden />}>
brukere er forhindret i å forstå innholdet eller utføre kritiske
oppgaver, og det finnes ingen alternative løsninger.
</List.Item>
<List.Item icon={<ChangingRoomIcon aria-hidden />}>
Middels barrierer på innhold med høy effekt.
<List.Item title="Medium" icon={<ChevronUpCircleIcon aria-hidden />}>
vanskelige, tidkrevende eller frustrerende for brukere å få tilgang til
innhold eller funksjonalitet.
</List.Item>
<List.Item icon={<ArrowCirclepathIcon aria-hidden />}>
Middels barrierer på innhold med middels effekt.
<List.Item title="Lav" icon={<ChevronDownCircleIcon aria-hidden />}>
brukeren få tilgang til alt innhold og funksjonalitet, men
brukeropplevelsen er dårlig.
</List.Item>
</List>
);
@@ -35,6 +41,6 @@ export const Demo = {
};
export const args = {
index: 2,
index: 3,
desc: "Husk å sette aria-hidden på ikonet.",
};
@@ -1,44 +0,0 @@
import { List } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<List>
<List.Item title="Kritiske">
disse er problemer som må løses så fort som mulig—de hindrer folk i å
bruke siden i det hele tatt og kan være direkte farlig.
</List.Item>
<List.Item title="Høy">
brukere er forhindret i å forstå innholdet eller utføre kritiske
oppgaver, og det finnes ingen forsvarsstrategier eller alternative
løsninger.
<List>
<List.Item>
Rammeverket, inkludert navigasjon, påloggings- og søkefunksjoner
</List.Item>
<List.Item>Gjenbrukbare komponenter</List.Item>
<List.Item>Nøkkelstier gjennom nettstedet</List.Item>
</List>
</List.Item>
<List.Item title="Medium">
det er vanskelig, tidkrevende eller frustrerende for brukere å få
tilgang til innhold eller funksjonalitet.
</List.Item>
<List.Item title="Lav">
brukeren få tilgang til alt innhold og funksjonalitet, men
brukeropplevelsen er dårlig.
</List.Item>
</List>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 4,
};
@@ -8,10 +8,7 @@ const Example = () => {
Blinkende innhold. Vi vil ikke gi brukerne våre anfall.
</List.Item>
<List.Item>
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item>
Kritiske problemer og problemer med høye barrierer på innhold med høy
Kritiske problemer og problemer med høye barrierer for innhold med høy
effekt.
</List.Item>
<List.Item>Middels barrierer på innhold med høy effekt.</List.Item>
@@ -28,6 +25,6 @@ export const Demo = {
};
export const args = {
index: 1,
desc: "Husk å sette riktig nivå/tag på List elementet hvis du bruker en tittel.",
index: 2,
desc: "Husk å sette riktig heading-nivå/tag hvis du bruker en title.",
};
@@ -0,0 +1,32 @@
import { List } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
const Example = () => {
return (
<List as="ul" size="small" title="Det kan også være aktuelt hvis du:">
<List.Item>
står i fare for å miste jobben etter å ha vært sykmeldt helt eller
delvis i 12 måneder, eller
</List.Item>
<List.Item>
har full eller gradert uføretrygd, men ønsker å jobbe. NAV må ha vurdert
om andre arbeidsmarkedstiltak og virkemidler er aktuelle.
</List.Item>
<List.Item>
er en arbeidssøker med varig og vesentlig nedsatt arbeidsevne som kan
bli ansatt i en vanlig jobb.
</List.Item>
</List>
);
};
export default withDsExample(Example);
/* Storybook story */
export const Demo = {
render: Example,
};
export const args = {
index: 1,
};
@@ -4,18 +4,17 @@ import { withDsExample } from "components/website-modules/examples/withDsExample
const Example = () => {
return (
<List as="ul">
<List.Item title="Kritiske">
disse er problemer som må løses så fort som mulig—de hindrer folk i å
bruke siden i det hele tatt og kan være direkte farlig.
<List.Item title="Kritisk">
dette er problemer som må løses så fort som mulig. De hindrer bruk av
siden og kan i verste fall være farlig for bruker.
</List.Item>
<List.Item title="Høy">
brukere er forhindret i å forstå innholdet eller utføre kritiske
oppgaver, og det finnes ingen forsvarsstrategier eller alternative
løsninger.
oppgaver, og det finnes ingen alternative løsninger.
</List.Item>
<List.Item title="Medium">
det er vanskelig, tidkrevende eller frustrerende for brukere å få
tilgang til innhold eller funksjonalitet.
vanskelige, tidkrevende eller frustrerende for brukere å få tilgang til
innhold eller funksjonalitet.
</List.Item>
<List.Item title="Lav">
brukeren få tilgang til alt innhold og funksjonalitet, men
@@ -33,5 +32,5 @@ export const Demo = {
};
export const args = {
index: 3,
index: 4,
};
@@ -3,12 +3,19 @@ import { withDsExample } from "components/website-modules/examples/withDsExample
const Example = () => {
return (
<List as="ul" title="Vi bør fikse uu-issues i følgende rekkefølge:">
<List as="ul" title="Det kan også være aktuelt hvis du:">
<List.Item>
Rammeverket, inkludert navigasjon, påloggings- og søkefunksjoner
står i fare for å miste jobben etter å ha vært sykmeldt helt eller
delvis i 12 måneder, eller
</List.Item>
<List.Item>
har full eller gradert uføretrygd, men ønsker å jobbe. NAV må ha vurdert
om andre arbeidsmarkedstiltak og virkemidler er aktuelle.
</List.Item>
<List.Item>
er en arbeidssøker med varig og vesentlig nedsatt arbeidsevne som kan
bli ansatt i en vanlig jobb.
</List.Item>
<List.Item>Gjenbrukbare komponenter</List.Item>
<List.Item>Nøkkelstier gjennom nettstedet</List.Item>
</List>
);
};
+1 -1
View File
@@ -86,7 +86,7 @@ export const query = `*[_type == "aksel_forside"][0]{
seo,
${contributorsAll}
},
"artikler": *[_type == "aksel_artikkel" && defined(publishedAt) && !(_id in ^.highlights[]._ref)] | order(publishedAt desc)[0...3]{
"artikler": *[_type == "aksel_artikkel" && defined(publishedAt) && !(_id in ^.highlights[]._ref)] | order(publishedAt desc)[0...4]{
_type,
_id,
heading,
@@ -95,7 +95,6 @@ export const structure = async (
);
outdated = outdated.filter((x) => isAfter(x.updateInfo?.lastVerified));
console.log(outdated.length);
return S.list()
.title("Innholdstyper")
@@ -125,7 +124,7 @@ export const structure = async (
? [
S.listItem()
.title(
`Nye tilbakemeldinger (${
`Tilbakemeldinger (${
feedback.filter(
(x) => !x._id.includes("draft") && x.behandlet === false
).length
@@ -134,7 +133,7 @@ export const structure = async (
.icon(CommentIcon)
.child(
S.list()
.title("Tilbakemeldinger")
.title("Tilbakemeldinger.")
.items([
S.listItem()
.title(
@@ -3,7 +3,7 @@ import { codeInput } from "@sanity/code-input";
import { colorInput } from "@sanity/color-input";
import { table } from "@sanity/table";
import { visionTool } from "@sanity/vision";
import { createAuthStore, defineConfig } from "sanity";
import { defineConfig } from "sanity";
import { unsplashImageAsset } from "sanity-plugin-asset-source-unsplash";
import { media } from "sanity-plugin-media";
import { deskTool } from "sanity/desk";
@@ -94,9 +94,9 @@ function defaultConfig() {
}
function authStore(dataset: string) {
return createAuthStore({
return {
redirectOnSingle: false,
mode: "replace",
mode: "replace" as const,
projectId,
dataset,
providers: [
@@ -111,5 +111,5 @@ function authStore(dataset: string) {
url: "https://api.sanity.io/v1/auth/login/github",
},
],
});
};
}
@@ -28,7 +28,7 @@ export const GodPraksisArtikkel = defineType({
defineField({
title: "Kobling til tema",
description:
"Gjør det mulig for temaredaktør å vise artikkelen på en temaside",
"Lenker artikkel til Tema og tilgjengeliggjør den for tema-redaktører.",
name: "tema",
type: "array",
of: [{ type: "reference", to: [{ type: "aksel_tema" }] }],
@@ -4,7 +4,7 @@ export const ingressField = defineField({
title: "Ingress",
name: "ingress",
description:
"Brukes til ingress, card description og seo description-tag. Bør bære konsis og oppsummerende.",
"Brukes til diverse metadata-felt. Bør bære konsis og oppsummere for hva artikkel innebærer.",
type: "text",
group: "innhold",
rows: 3,
@@ -7,8 +7,11 @@ export const titleField = defineField({
type: "string",
group: "innhold",
description: "Bruk en kort og konsis tittel om mulig.",
validation: (Rule) =>
Rule.required().max(60).error("Sidetittel kan ikke være over 60 tegn"),
validation: (Rule) => [
Rule.required().min(4).error("Sidetittel må være på minst 4 tegn"),
Rule.max(60).error("Sidetittel kan ikke være over 60 tegn"),
],
options: {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
+1 -1
View File
@@ -30,7 +30,7 @@ p.override-text-no-max {
position: absolute;
width: 8px;
height: 8px;
border-radius: 2px;
border-radius: var(--a-border-radius-small);
background-color: var(--a-deepblue-500);
transform: translateY(10px) translateX(2px) rotate(45deg);
left: 0;
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more