Compare commits

...
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] f9aecb4242 Version Packages (#1720)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-21 11:30:02 +01:00
Vebjørn NordbyandKen d9352982d4 [BUG] parentSelector returneres ikke selv om den er definert (#1717)
Co-authored-by: Ken <ken.aleksander@gmail.com>
2022-11-21 11:25:26 +01:00
Ken 328f33c913 🐛 Fikset changeset config 2022-11-19 17:12:15 +01:00
Ken 67fd406396 📝 Bedre build-logging 2022-11-19 15:20:34 +01:00
Ken 41320584a3 📝 Build loging 2022-11-19 15:03:40 +01:00
Ken ac838681f1 🔥 Fjernet next-eksempel 2022-11-19 14:48:13 +01:00
Ken 3f17539e17 🧑‍💻 lint-staged for raskere linting 2022-11-18 21:44:45 +01:00
Ken 633856677f 🎨 lint-staged 2022-11-18 21:41:51 +01:00
Ken 5aa3026561 ⚡ Raskere tester og build 2022-11-18 21:32:29 +01:00
Ken 90eb056b1c ⬆️ yarn lock 2022-11-18 14:55:00 +01:00
Ken 581c26953e Revert "🌐 Fonter med latin-extended"
This reverts commit 0b0d87a5db.
2022-11-18 14:54:28 +01:00
Ken 0b0d87a5db 🌐 Fonter med latin-extended 2022-11-18 12:58:26 +01:00
Ken 7ef1be47b7 📝 Changeset 2022-11-16 15:29:46 +01:00
Ken dde30617e3 💄 Toggle-small gap 2022-11-16 15:29:03 +01:00
Ken 609678667f 🔥 Fjernet console.log 2022-11-16 09:46:42 +01:00
Ken 180ecd89bf 🎨 defaultMonth/Year prop 2022-11-15 12:15:09 +01:00
Ken 635b0859b8 defaultMonth/Year prop 2022-11-15 12:13:02 +01:00
Ken 10cbe105bc 🐛 Redirect funker nå med æøå 2022-11-12 21:20:11 +01:00
Ken f97eed8396 📝 Hide redirects from sanity search 2022-11-12 20:03:33 +01:00
Ken 119d9df7b5 📝 Sanity types 2022-11-12 19:45:02 +01:00
Ken 846d18cd23 🎨 Dynamic redirects 2022-11-12 19:40:14 +01:00
Ken 833c35cbbc 🐛 Headings i alert scrollet ikke ved linking 2022-11-11 12:43:35 +01:00
Ken 08353f50b5 💄 tema-lenker 2022-11-11 11:34:55 +01:00
Ken fba81fb4eb 🐛 Fikset tags komponentoversikt 2022-11-10 12:00:49 +01:00
Ken 9d9c6e9bfa 🐛 Fikset tags komponentoversikt 2022-11-10 11:07:34 +01:00
Ken 4dad81714f 🎨 Oppdatert tilbakemeldingsform for personvern 2022-11-09 13:19:34 +01:00
Ken 8e9135149b 🐛 Datepicker funket ikke i shadow-dom (open)
🐛 Datepicker funket ikke i shadow-dom

📝 Changeset@
2022-11-08 15:06:02 +01:00
KenandKenAJoh 852b53d6b9 Ikonsynk Figma (#1713)
Co-authored-by: KenAJoh <KenAJoh@users.noreply.github.com>
2022-11-08 09:34:22 +01:00
github-actions[bot]andgithub-actions[bot] ede1f5858a Version Packages (#1711)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-04 14:50:55 +01:00
Ken ad9e7165bb 💚 dispatch cdn-publish 2022-11-04 14:46:55 +01:00
Kenandolejorgenbakken 77302a30b8 Ikonsynk Figma (#1709)
Co-authored-by: olejorgenbakken <olejorgenbakken@users.noreply.github.com>
2022-11-04 14:45:15 +01:00
Andreas Nordahl 0ef12ef601 upload font files to cdn (#1710) 2022-11-04 14:29:56 +01:00
Ken 4174f9a7b4 📝 Patch-version 2022-11-02 23:37:19 +01:00
Ken 608f158d70 🐛 Datepicker validation error var flipped 2022-11-02 22:40:36 +01:00
Ken 34d41ecc78 📝 Patch-version 2022-11-02 20:42:21 +01:00
Ken 7e62365a60 🔥 Fjernet flaky e2e test 2022-11-02 19:29:52 +01:00
Ken 8565f410a1 💄 Nye tags i sidebar 2022-11-02 18:16:42 +01:00
Ken 46d70820df 📝 Version-bump 2022-11-02 18:09:27 +01:00
github-actions[bot]andgithub-actions[bot] 67e66ffe02 Version Packages (#1707)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-02 16:29:26 +01:00
Ken e19bf67b33 [FEATURE] Tag varianter (#1684) 2022-11-02 16:25:31 +01:00
Ken 97c5f60e91 Chips (#1668) 2022-11-02 16:20:42 +01:00
Ken ea5293c28d ⬆️ Patch-versjon 2022-11-02 10:57:45 +01:00
Ken 6a58efa9a9 changeset 2022-11-02 10:55:34 +01:00
Ken c95f33679e 💚 Fikset timezones for jest-tester
✨ Timekeeper-demo

✨ timezone for jest-tester
2022-11-02 10:53:19 +01:00
Ken b52a5e02a0 ✨ Patch-versjoner 2022-11-01 23:57:38 +01:00
Ken ab3ef0d34b ✨ Validering i Date-komponenter
🐛 Date-formats skaper problemer for ranged-input

🐛 UX tweaks for inputs

🐛 Fikset weekendValidation

🎨 Custom input-format

🎨 validation api

🎉 range-validation

🎨 Export validation-types

🎨 Datepicker-validation

🎨 monthpicker validation

🎨 Småbugs med disabled datepicker

💄 reversert capitalization i månedsnavn
2022-11-01 23:54:27 +01:00
Ken 69798f6ee2 🔥 Fjernet flaky e2e test 2022-11-01 11:00:35 +01:00
Ken ee14586beb ⬆️ yarn-lock 2022-11-01 10:40:34 +01:00
135 changed files with 2931 additions and 1140 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Changesets
For versjonering bruker vi løsningen [Changesets](https://github.com/changesets/changesets) for å [versjonering](https://semver.org/) og publisering av pakker.
For versjonering bruker vi løsningen [Changesets](https://github.com/changesets/changesets) for [versjonering](https://semver.org/) og publisering av pakker.
## Howto
+1 -1
View File
@@ -21,7 +21,7 @@
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": ["nextjs-demo", "shadow-dom", "aksel.nav.no"],
"ignore": ["shadow-dom", "aksel.nav.no"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"updateInternalDependents": "always"
+31
View File
@@ -0,0 +1,31 @@
name: "push-to-cdn"
on:
workflow_dispatch:
push:
branches:
- "master"
paths:
- "fonts/**"
jobs:
upload:
runs-on: ubuntu-latest
permissions:
contents: "read"
id-token: "write"
steps:
- uses: "actions/checkout@v3"
- id: upload
uses: nais/frontend-plattform-cdn-upload-action@main
with:
cdn-environment: cdn.nav.no
cdn-team-name: aksel
source: ./fonts
destination: "" # uploads folder to team root
- run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
shell: bash
-1
View File
@@ -139,7 +139,6 @@ _docs.json
!*/sanity/lib/
!*/sanity/static/
out
test-urls.json
videos
sanity/scripts/renameField.js
tmp.json
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
yarn pretty-quick --staged
yarn pretty-quick --staged && yarn lint-staged
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
yarn lint
+4
View File
@@ -15,3 +15,7 @@
.rowgap[data-theme="dark"] {
background-color: #262626;
}
.rowgap-wrap {
flex-wrap: wrap;
}
+26
View File
@@ -1,5 +1,31 @@
# @navikt/ds-codemod
## 1.5.10
## 1.5.9
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
## 1.5.4
## 1.5.3
## 1.5.2
## 1.5.1
## 1.5.0
## 1.4.4
## 1.4.3
## 1.4.2
## 1.4.1
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-codemod",
"version": "1.4.2",
"version": "1.5.10",
"description": "NAV designsystem codemods",
"author": "NAV Designsystem team",
"license": "MIT",
+56
View File
@@ -1,5 +1,61 @@
# @navikt/ds-css
## 1.5.10
## 1.5.9
### Patch Changes
- 4px -> 2px gap chips-toggle active
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
## 1.5.4
### Patch Changes
- [#1709](https://github.com/navikt/Designsystemet/pull/1709) [`77302a30b`](https://github.com/navikt/Designsystemet/commit/77302a30b8eef8dc00b2d2e9c904a3f57da81a11) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Oppdatert ikoner
## 1.5.3
### Patch Changes
- Byttet om på alt-farge rekkefølge
## 1.5.2
### Patch Changes
- :memo: Rename FilterChips to ToggleChips
## 1.5.1
### Patch Changes
- Flex-wrap chips.css
## 1.5.0
### Minor Changes
- [#1684](https://github.com/navikt/Designsystemet/pull/1684) [`e19bf67b3`](https://github.com/navikt/Designsystemet/commit/e19bf67b337dea39989c68b5e9c2591cf0d5b40f) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :sparkles: Oppdaterte tag-varianter
- [#1668](https://github.com/navikt/Designsystemet/pull/1668) [`97c5f60e9`](https://github.com/navikt/Designsystemet/commit/97c5f60e9111da7e08f55c8d0aa29581f0a9b1ca) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Chips komponent :sparkles:
## 1.4.4
## 1.4.3
### Patch Changes
- :sparkles: Validering og inputFormat i Date-komponenter
## 1.4.2
## 1.4.1
+137
View File
@@ -0,0 +1,137 @@
.navds-chips {
display: flex;
gap: var(--navds-spacing-2);
margin: 0;
padding: 0;
list-style: none;
flex-wrap: wrap;
}
.navds-chips :where(li) {
margin: 0;
padding: 0;
list-style: none;
display: block;
}
.navds-chips__chip {
all: unset;
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
gap: 0.125rem;
margin: 0;
padding: 0 var(--navds-spacing-3);
text-decoration: none;
border-radius: var(--navds-border-radius-full);
min-height: 32px;
}
.navds-chips--small .navds-chips__chip {
min-height: 24px;
padding: 0 var(--navds-spacing-2);
}
.navds-chips__toggle {
box-shadow: inset 0 0 0 1px var(--ac-chip-toggle-border, var(--navds-global-color-gray-500));
background-color: var(--ac-chip-toggle-bg, rgb(0 0 0 / 0.055));
}
.navds-chips__toggle:hover {
box-shadow: inset 0 0 0 1px var(--ac-chip-toggle-border-hover, var(--navds-global-color-gray-600));
background-color: var(--ac-chip-toggle-bg-hover, rgb(0 0 0 / 0.1));
}
.navds-chips__toggle[aria-pressed="true"] {
box-shadow: none;
background-color: var(--ac-chip-toggle-bg-pressed, var(--navds-global-color-deepblue-500));
color: var(--ac-chip-toggle-text-pressed, var(--navds-global-color-white));
}
.navds-chips__toggle[aria-pressed="true"]:hover {
background-color: var(--ac-chip-toggle-bg-pressed-hover, var(--navds-global-color-deepblue-600));
}
.navds-chips__toggle:focus-visible {
box-shadow: 0 0 0 2px var(--navds-global-color-blue-800);
}
.navds-chips__toggle[aria-pressed="true"]:focus-visible,
.navds-chips__toggle:active:focus-visible {
box-shadow: inset 0 0 0 1px var(--navds-global-color-white), 0 0 0 2px var(--navds-global-color-blue-800);
}
.navds-chips__removable--action {
background-color: var(--ac-chip-removable-action-bg, var(--navds-global-color-deepblue-500));
color: var(--ac-chip-removable-action-text, var(--navds-global-color-white));
}
.navds-chips__removable--neutral {
color: var(--ac-chip-removable-neutral-text, var(--navds-global-color-gray-900));
background-color: var(--ac-chip-removable-neutral-bg, rgb(0 0 0/ 0.055));
box-shadow: inset 0 0 0 1px var(--ac-chip-removable-neutral-border, var(--navds-global-color-gray-500));
}
.navds-chips__removable-icon {
width: 1.5rem;
height: 1.5rem;
display: grid;
place-items: center;
border-radius: var(--navds-border-radius-full);
}
.navds-chips__toggle-icon {
width: 1.25rem;
height: 1.25rem;
display: grid;
place-items: center;
border-radius: var(--navds-border-radius-full);
}
.navds-chips--small .navds-chips__toggle-icon {
width: 1rem;
height: 1rem;
}
.navds-chips--small .navds-chips__removable-icon {
width: 1.25rem;
height: 1.25rem;
}
.navds-chips--small .navds-chips__removable-icon > svg {
width: 0.75rem;
}
.navds-chips__removable--action:focus-visible {
box-shadow: inset 0 0 0 1px var(--navds-global-color-white), 0 0 0 2px var(--navds-global-color-blue-800);
}
.navds-chips__removable--neutral:focus-visible {
box-shadow: 0 0 0 2px var(--navds-global-color-blue-800);
}
.navds-chips__removable--action:hover {
background-color: var(--ac-chip-removable-action-bg-hover, var(--navds-global-color-deepblue-700));
}
.navds-chips__removable--neutral:hover {
background-color: var(--ac-chip-removable-action-bg-hover, rgb(0 0 0 / 0.1));
box-shadow: inset 0 0 0 1px var(--ac-chip-removable-neutral-border-hover, var(--navds-global-color-gray-600));
}
.navds-chips--icon-left {
padding-left: 0.375rem;
}
.navds-chips--icon-right {
padding-right: var(--navds-spacing-1);
}
.navds-chips--small .navds-chips--icon-right {
padding-right: 0.125rem;
}
.navds-chips--small .navds-chips--icon-left {
padding-left: 0.375rem;
}
+5
View File
@@ -13,6 +13,10 @@
gap: var(--navds-spacing-5);
}
.navds-date__caption-label {
text-transform: capitalize;
}
.navds-date .rdp-head_cell {
text-transform: capitalize;
font-size: var(--navds-font-size-small);
@@ -86,6 +90,7 @@
text-align: center;
width: 3rem;
height: 2.75rem;
text-transform: capitalize;
border-radius: var(--navds-border-radius-medium);
cursor: pointer;
}
+1
View File
@@ -7,6 +7,7 @@
@import "button.css";
@import "content-container.css";
@import "chat.css";
@import "chips.css";
@import "guide-panel.css";
@import "form/index.css";
@import "help-text.css";
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "1.4.2",
"version": "1.5.10",
"description": "Css for NAV Designsystem components",
"author": "NAV Designsystem team",
"keywords": [
@@ -20,7 +20,7 @@
"watch": "postcss --watch --use postcss-import -o dist/index.css index.css"
},
"devDependencies": {
"@navikt/ds-tokens": "^1.4.2",
"@navikt/ds-tokens": "^1.5.10",
"normalize.css": "^8.0.1",
"postcss": "^8.4.0",
"postcss-cli": "^9.0.0",
+93 -14
View File
@@ -14,38 +14,117 @@
.navds-tag {
border: 1px solid;
border-color: var(--navds-tag-color-border);
border-radius: var(--navds-border-radius-medium);
background-color: var(--navds-tag-color-background);
border-radius: var(--navds-border-radius-small);
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.125rem var(--navds-spacing-3);
padding: 0.125rem var(--navds-spacing-2);
min-height: 32px;
line-height: 1;
}
.navds-tag--small {
min-height: 24px;
padding-right: var(--navds-spacing-1);
padding-left: var(--navds-spacing-1);
padding: 0 0.375rem;
}
.navds-tag--xsmall {
min-height: 20px;
padding: 0 var(--navds-spacing-1);
}
.navds-tag--error {
background-color: var(--navds-tag-color-error-background);
border-color: var(--navds-tag-color-error-border);
border-color: var(--ac-tag-error-border, var(--navds-global-color-red-500));
background-color: var(--ac-tag-error-bg, var(--navds-global-color-red-50));
color: var(--ac-tag-error-text, var(--navds-global-color-gray-900));
}
.navds-tag--error-filled {
border-color: transparent;
background-color: var(--ac-tag-error-filled-bg, var(--navds-global-color-red-500));
color: var(--ac-tag-error-filled-text, var(--navds-global-color-white));
}
.navds-tag--success {
background-color: var(--navds-tag-color-success-background);
border-color: var(--navds-tag-color-success-border);
border-color: var(--ac-tag-success-border, var(--navds-global-color-green-500));
background-color: var(--ac-tag-success-bg, var(--navds-global-color-green-50));
color: var(--ac-tag-success-text, var(--navds-global-color-gray-900));
}
.navds-tag--success-filled {
border-color: transparent;
background-color: var(--ac-tag-success-filled-bg, var(--navds-global-color-green-500));
color: var(--ac-tag-success-filled-text, var(--navds-global-color-white));
}
.navds-tag--warning {
background-color: var(--navds-tag-color-warning-background);
border-color: var(--navds-tag-color-warning-border);
border-color: var(--ac-tag-warning-border, var(--navds-global-color-orange-600));
background-color: var(--ac-tag-warning-bg, var(--navds-global-color-orange-50));
color: var(--ac-tag-warning-text, var(--navds-global-color-gray-900));
}
.navds-tag--warning-filled {
border-color: transparent;
background-color: var(--ac-tag-warning-filled-bg, var(--navds-global-color-orange-500));
color: var(--ac-tag-warning-filled-text, var(--navds-global-color-gray-900));
}
.navds-tag--info {
background-color: var(--navds-tag-color-info-background);
border-color: var(--navds-tag-color-info-border);
border-color: var(--ac-tag-info-border, var(--navds-global-color-lightblue-700));
background-color: var(--ac-tag-info-bg, var(--navds-global-color-lightblue-50));
color: var(--ac-tag-info-text, var(--navds-global-color-gray-900));
}
.navds-tag--info-filled {
border-color: transparent;
background-color: var(--ac-tag-info-filled-bg, var(--navds-global-color-lightblue-500));
color: var(--ac-tag-info-filled-text, var(--navds-global-color-gray-900));
}
.navds-tag--neutral {
border-color: var(--ac-tag-neutral-border, var(--navds-global-color-gray-500));
background-color: var(--ac-tag-neutral-bg, var(--navds-global-color-gray-100));
color: var(--ac-tag-neutral-text, var(--navds-global-color-gray-900));
}
.navds-tag--neutral-filled {
border-color: transparent;
background-color: var(--ac-tag-neutral-filled-bg, var(--navds-global-color-gray-800));
color: var(--ac-tag-neutral-filled-text, var(--navds-global-color-white));
}
.navds-tag--alt1 {
border-color: var(--ac-tag-alt1-border, var(--navds-global-color-purple-400));
background-color: var(--ac-tag-alt1-bg, var(--navds-global-color-purple-100));
color: var(--ac-tag-alt1-text, var(--navds-global-color-gray-900));
}
.navds-tag--alt1-filled {
border-color: transparent;
background-color: var(--ac-tag-alt1-filled-bg, var(--navds-global-color-purple-400));
color: var(--ac-tag-alt1-filled-text, var(--navds-global-color-white));
}
.navds-tag--alt2 {
border-color: var(--ac-tag-alt2-border, var(--navds-global-color-limegreen-500));
background-color: var(--ac-tag-alt2-bg, var(--navds-global-color-limegreen-100));
color: var(--ac-tag-alt2-text, var(--navds-global-color-gray-900));
}
.navds-tag--alt2-filled {
border-color: transparent;
background-color: var(--ac-tag-alt2-filled-bg, var(--navds-global-color-limegreen-400));
color: var(--ac-tag-alt2-filled-text, var(--navds-global-color-gray-900));
}
.navds-tag--alt3 {
border-color: var(--ac-tag-alt3-border, var(--navds-global-color-deepblue-400));
background-color: var(--ac-tag-alt3-bg, var(--navds-global-color-deepblue-100));
color: var(--ac-tag-alt3-text, var(--navds-global-color-gray-900));
}
.navds-tag--alt3-filled {
border-color: transparent;
background-color: var(--ac-tag-alt3-filled-bg, var(--navds-global-color-deepblue-500));
color: var(--ac-tag-alt3-filled-text, var(--navds-global-color-white));
}
+113
View File
@@ -1,5 +1,118 @@
# @navikt/ds-react
## 1.5.10
### Patch Changes
- [#1717](https://github.com/navikt/Designsystemet/pull/1717) [`d9352982d`](https://github.com/navikt/Designsystemet/commit/d9352982d42d5d12a9c5fc345e546f57c753738d) Thanks [@vebnor](https://github.com/vebnor)! - Fikset parentSelector prop i Modal
- Updated dependencies []:
- @navikt/ds-icons@1.5.10
## 1.5.9
### Patch Changes
- Updated dependencies []:
- @navikt/ds-icons@1.5.9
## 1.5.8
### Patch Changes
- console.log removed
- Updated dependencies []:
- @navikt/ds-icons@1.5.8
## 1.5.7
### Patch Changes
- defaultMonth/Year prop date-komponent
- Updated dependencies []:
- @navikt/ds-icons@1.5.7
## 1.5.6
### Patch Changes
- Datepicker fungerte ikke med open shadow-dom
- Updated dependencies []:
- @navikt/ds-icons@1.5.6
## 1.5.5
### Patch Changes
- Updated dependencies []:
- @navikt/ds-icons@1.5.5
## 1.5.4
### Patch Changes
- Updated dependencies [[`77302a30b`](https://github.com/navikt/Designsystemet/commit/77302a30b8eef8dc00b2d2e9c904a3f57da81a11)]:
- @navikt/ds-icons@1.5.4
## 1.5.3
### Patch Changes
- Updated dependencies []:
- @navikt/ds-icons@1.5.3
## 1.5.2
### Patch Changes
- :memo: Rename FilterChips to ToggleChips
- Updated dependencies []:
- @navikt/ds-icons@1.5.2
## 1.5.1
### Patch Changes
- Flex-wrap chips.css
- Updated dependencies []:
- @navikt/ds-icons@1.5.1
## 1.5.0
### Minor Changes
- [#1684](https://github.com/navikt/Designsystemet/pull/1684) [`e19bf67b3`](https://github.com/navikt/Designsystemet/commit/e19bf67b337dea39989c68b5e9c2591cf0d5b40f) Thanks [@KenAJoh](https://github.com/KenAJoh)! - :sparkles: Oppdaterte tag-varianter
- [#1668](https://github.com/navikt/Designsystemet/pull/1668) [`97c5f60e9`](https://github.com/navikt/Designsystemet/commit/97c5f60e9111da7e08f55c8d0aa29581f0a9b1ca) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Chips komponent :sparkles:
### Patch Changes
- Updated dependencies []:
- @navikt/ds-icons@1.5.0
## 1.4.4
### Patch Changes
- Eksponerer validation-typer
- Updated dependencies []:
- @navikt/ds-icons@1.4.4
## 1.4.3
### Patch Changes
- :sparkles: Validering og inputFormat i Date-komponenter
- Updated dependencies []:
- @navikt/ds-icons@1.4.3
## 1.4.2
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "1.4.2",
"version": "1.5.10",
"description": "NAV designsystem react components",
"author": "NAV Designsystem team",
"license": "MIT",
@@ -33,11 +33,11 @@
"clean": "rimraf cjs esm",
"build": "concurrently \"tsc\" \"tsc -p tsconfig.esm.json\" && yarn create-package-json-pointers-to-esm",
"watch": "tsc --watch -p tsconfig.esm.json",
"test": "jest"
"test": "TZ=UTC jest"
},
"dependencies": {
"@floating-ui/react-dom-interactions": "0.9.2",
"@navikt/ds-icons": "^1.4.2",
"@navikt/ds-icons": "^1.5.10",
"@radix-ui/react-tabs": "1.0.0",
"@radix-ui/react-toggle-group": "1.0.0",
"clsx": "^1.2.1",
+45
View File
@@ -0,0 +1,45 @@
import cl from "clsx";
import React, { forwardRef, HTMLAttributes } from "react";
import ToggleChips, { ToggleChipsType } from "./Toggle";
import RemovableChips, { RemovableChipsType } from "./Removable";
export interface ChipsProps extends HTMLAttributes<HTMLUListElement> {
children: React.ReactNode;
/**
* Changes padding and font-sizes
* @default "medium"
*/
size?: "medium" | "small";
}
interface ChipsComponent
extends React.ForwardRefExoticComponent<
ChipsProps & React.RefAttributes<HTMLUListElement>
> {
Toggle: ToggleChipsType;
Removable: RemovableChipsType;
}
export const Chips: ChipsComponent = forwardRef<HTMLUListElement, ChipsProps>(
({ className, size = "medium", children, ...rest }, ref) => {
return (
<ul
{...rest}
ref={ref}
className={cl("navds-chips", className, `navds-chips--${size}`, {
"navds-body-short navds-body-short--small": size === "medium",
"navds-detail navds-detail--small": size === "small",
})}
>
{React.Children.map(children, (chip, index) => {
return <li key={index + (chip?.toString() ?? "")}>{chip}</li>;
})}
</ul>
);
}
) as ChipsComponent;
Chips.Toggle = ToggleChips;
Chips.Removable = RemovableChips;
export default Chips;
@@ -0,0 +1,68 @@
import { Close } from "@navikt/ds-icons";
import cl from "clsx";
import React, { forwardRef } from "react";
import { OverridableComponent } from "..";
export interface RemovableChipsProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: string;
/**
* Chip-variants
* @default "action"
*/
variant?: "action" | "neutral";
/**
* Replaces label read for screen-readers
* @default "slett filter"
*/
removeLabel?: string;
/**
* Click callback
*/
onDelete?: () => void;
}
export interface RemovableChipsType
extends OverridableComponent<RemovableChipsProps, HTMLButtonElement> {}
export const RemovableChips: OverridableComponent<
RemovableChipsProps,
HTMLButtonElement
> = forwardRef(
(
{
className,
children,
variant = "action",
as: Component = "button",
removeLabel = "slett",
onDelete,
...rest
},
ref
) => {
return (
<Component
{...rest}
ref={ref}
className={cl(
"navds-chips__chip navds-chips__removable navds-chips--icon-right",
className,
`navds-chips__removable--${variant}`
)}
aria-label={`${children} ${removeLabel}`}
onClick={(e) => {
onDelete?.();
rest?.onClick?.(e);
}}
>
<span className="navds-chips__chip-text">{children}</span>
<span className="navds-chips__removable-icon">
<Close aria-hidden />
</span>
</Component>
);
}
) as RemovableChipsType;
export default RemovableChips;
+41
View File
@@ -0,0 +1,41 @@
import { SuccessStroke } from "@navikt/ds-icons";
import cl from "clsx";
import React, { forwardRef } from "react";
import { OverridableComponent } from "..";
export interface ToggleChipsProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: string;
/**
* Toggles aria-pressed and visual-changes
*/
selected?: boolean;
}
export interface ToggleChipsType
extends OverridableComponent<ToggleChipsProps, HTMLButtonElement> {}
export const ToggleChips: ToggleChipsType = forwardRef(
(
{ className, children, selected, as: Component = "button", ...rest },
ref
) => {
return (
<Component
{...rest}
ref={ref}
className={cl("navds-chips__chip navds-chips__toggle", className, {
"navds-chips--icon-left": selected,
})}
aria-pressed={selected}
>
{selected && (
<SuccessStroke aria-hidden className="navds-chips__toggle-icon" />
)}
<span className="navds-chips__chip-text">{children}</span>
</Component>
);
}
);
export default ToggleChips;
@@ -0,0 +1,165 @@
import React, { useState } from "react";
import { Chips } from ".";
export default {
title: "ds-react/Chips",
component: Chips,
argTypes: {
type: {
control: {
type: "radio",
options: ["toggle", "removable"],
},
},
size: {
control: {
type: "radio",
options: ["medium", "small"],
},
},
},
};
const options = ["Norsk", "Dansk", "Svensk", "Tysk", "Spansk"];
export const Default = (props) => {
const [selected, setSelected] = useState(["Dansk", "Svensk"]);
const [filter, setFilter] = useState(options);
if (props.type === "toggle") {
return (
<Chips size={props.size ?? "medium"}>
{options.map((c) => (
<Chips.Toggle
selected={selected.includes(c)}
key={c}
onClick={() =>
setSelected(
selected.includes(c)
? selected.filter((x) => x !== c)
: [...selected, c]
)
}
>
{c}
</Chips.Toggle>
))}
</Chips>
);
}
return (
<Chips size={props.size ?? "medium"}>
{filter.map((c) => (
<Chips.Removable
key={c}
onClick={() => setFilter((x) => x.filter((y) => y !== c))}
>
{c}
</Chips.Removable>
))}
</Chips>
);
};
export const Toggle = () => {
const [selected, setSelected] = useState<number[]>([]);
return (
<Chips>
{options.map((c, y) => (
<Chips.Toggle
selected={selected.includes(y)}
onClick={() =>
setSelected(
selected.includes(y)
? selected.filter((x) => x !== y)
: [...selected, y]
)
}
key={y}
>
{c}
</Chips.Toggle>
))}
</Chips>
);
};
export const Removable = () => {
return (
<div className="colgap">
<Chips>
{options.map((c, y) => (
<Chips.Removable key={y}>{c}</Chips.Removable>
))}
</Chips>
<Chips>
{options.map((c, y) => (
<Chips.Removable variant="neutral" key={y}>
{c}
</Chips.Removable>
))}
</Chips>
</div>
);
};
export const Regular = () => {
const [selected, setSelected] = useState<number[]>([]);
return (
<div className="colgap">
<Chips>
{options.map((c, y) => (
<Chips.Removable key={y}>{c}</Chips.Removable>
))}
</Chips>
<Chips>
{options.map((c, y) => (
<Chips.Toggle
selected={selected.includes(y)}
onClick={() =>
setSelected(
selected.includes(y)
? selected.filter((x) => x !== y)
: [...selected, y]
)
}
key={y}
>
{c}
</Chips.Toggle>
))}
</Chips>
</div>
);
};
export const Small = () => {
const [selected, setSelected] = useState<number[]>([]);
return (
<div className="colgap">
<Chips size="small">
{options.map((c, y) => (
<Chips.Removable key={y}>{c}</Chips.Removable>
))}
</Chips>
<Chips size="small">
{options.map((c, y) => (
<Chips.Toggle
selected={selected.includes(y)}
onClick={() =>
setSelected(
selected.includes(y)
? selected.filter((x) => x !== y)
: [...selected, y]
)
}
key={y}
>
{c}
</Chips.Toggle>
))}
</Chips>
</div>
);
};
+1
View File
@@ -0,0 +1 @@
export { default as Chips, ChipsProps } from "./Chips";
@@ -1,4 +1,3 @@
import isSaturday from "date-fns/isSaturday";
import React, { useId, useState } from "react";
import { UNSAFE_useDatepicker, UNSAFE_useRangeDatepicker } from "..";
import { Button } from "../..";
@@ -221,8 +220,13 @@ export const UserControlled = () => {
};
export const Validering = () => {
const { datepickerProps, selectedDay, inputProps } = UNSAFE_useDatepicker({
fromDate: new Date("Aug 23 2019"),
const [error, setError] = useState(false);
const { datepickerProps, inputProps } = UNSAFE_useDatepicker({
fromDate: new Date("Aug 2 2019"),
onValidate: (val) => setError(val.isWeekend),
defaultSelected: new Date("Nov 26 2022"),
disableWeekends: true,
onDateChange: console.log,
});
return (
@@ -230,8 +234,8 @@ export const Validering = () => {
<DatePicker {...datepickerProps}>
<DatePicker.Input
error={
selectedDay && isSaturday(selectedDay)
? "NAV-kontoret er ikke åpent på lørdager. Velg en annen dag."
error
? "NAV-kontoret er ikke åpent i helger. Velg en annen dag."
: undefined
}
{...inputProps}
@@ -261,3 +265,40 @@ export const ErrorInput = () => {
</div>
);
};
export const UseRangedDatepickerValidation = () => {
const { datepickerProps, fromInputProps, toInputProps } =
UNSAFE_useRangeDatepicker({
fromDate: new Date("Aug 23 2019"),
disableWeekends: true,
disabled: [new Date("Oct 10 2022")],
onValidate: console.table,
});
return (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker {...datepickerProps}>
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker.Input {...fromInputProps} label="Fra" />
<DatePicker.Input {...toInputProps} label="Til" />
</div>
</DatePicker>
</div>
);
};
export const defaultShownMonth = () => {
const { datepickerProps, inputProps } = UNSAFE_useDatepicker({
fromDate: new Date("Aug 23 2019"),
onDateChange: console.log,
defaultMonth: new Date("Oct 23 2022"),
});
return (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker {...datepickerProps}>
<DatePicker.Input {...inputProps} label="Velg dato" />
</DatePicker>
</div>
);
};
@@ -1,6 +1,9 @@
export { useDatepicker as UNSAFE_useDatepicker } from "./useDatepicker";
export type { DateValidationT } from "./useDatepicker";
export { useRangeDatepicker as UNSAFE_useRangeDatepicker } from "./useRangeDatepicker";
export type { RangeValidationT } from "./useRangeDatepicker";
export { useMonthpicker as UNSAFE_useMonthpicker } from "./useMonthPicker";
export type { MonthValidationT } from "./useMonthPicker";
export { useDateInputContext, DateContext } from "./useDateInputContext";
export {
useSharedMonthContext,
@@ -28,6 +28,10 @@ export interface UseDatepickerOptions
* The initially selected Date
*/
defaultSelected?: Date;
/**
* Default shown month
*/
defaultMonth?: Date;
/**
* Make selection of Date required
*/
@@ -36,6 +40,15 @@ export interface UseDatepickerOptions
* Callback for changed state
*/
onDateChange?: (val?: Date) => void;
/**
* Input-format
* @default "dd.MM.yyyy"
*/
inputFormat?: string;
/**
* validation-callback
*/
onValidate?: (val: DateValidationT) => void;
}
interface UseDatepickerValue {
@@ -62,6 +75,27 @@ interface UseDatepickerValue {
setSelected: (date?: Date) => void;
}
export type DateValidationT = {
isDisabled: boolean;
isWeekend: boolean;
isEmpty: boolean;
isInvalid: boolean;
isValidDate: boolean;
isBefore: boolean;
isAfter: boolean;
};
const getValidationMessage = (val = {}): DateValidationT => ({
isDisabled: false,
isWeekend: false,
isEmpty: false,
isInvalid: false,
isBefore: false,
isAfter: false,
isValidDate: true,
...val,
});
export const useDatepicker = (
opt: UseDatepickerOptions = {}
): UseDatepickerValue => {
@@ -75,6 +109,9 @@ export const useDatepicker = (
disabled,
disableWeekends,
onDateChange,
inputFormat,
onValidate,
defaultMonth,
} = opt;
const locale = getLocaleFromString(_locale);
@@ -85,12 +122,12 @@ export const useDatepicker = (
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
// Initialize states
const [month, setMonth] = useState(defaultSelected ?? today);
const [month, setMonth] = useState(defaultSelected ?? defaultMonth ?? today);
const [selectedDay, setSelectedDay] = useState(defaultSelected);
const [open, setOpen] = useState(false);
const defaultInputValue = defaultSelected
? formatDateForInput(defaultSelected, locale, "date")
? formatDateForInput(defaultSelected, locale, "date", inputFormat)
: "";
const [inputValue, setInputValue] = useState(defaultInputValue);
@@ -99,16 +136,26 @@ export const useDatepicker = (
setSelectedDay(date);
};
const updateValidation = (val: Partial<DateValidationT> = {}) => {
const msg = getValidationMessage(val);
onValidate?.(msg);
};
const handleFocusIn = useCallback(
(e) => {
if (!e?.target || !e?.target?.nodeType) {
/* Workaround for shadow-dom users (open) */
const composed = e.composedPath?.()?.[0];
if (!e?.target || !e?.target?.nodeType || !composed) {
return;
}
![
daypickerRef.current,
inputRef.current,
inputRef.current?.nextSibling,
].some((element) => element?.contains(e.target)) &&
].some(
(element) => element?.contains(e.target) || element?.contains(composed)
) &&
open &&
setOpen(false);
},
@@ -126,30 +173,32 @@ export const useDatepicker = (
const reset = () => {
updateDate(defaultSelected);
setMonth(defaultSelected ?? today);
setMonth(defaultSelected ?? defaultMonth ?? today);
setInputValue(defaultInputValue ?? "");
setDefaultSelected(_defaultSelected);
};
const setSelected = (date: Date | undefined) => {
updateDate(date);
setMonth(date ?? today);
setInputValue(date ? formatDateForInput(date, locale, "date") : "");
setMonth(date ?? defaultMonth ?? today);
setInputValue(
date ? formatDateForInput(date, locale, "date", inputFormat) : ""
);
};
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
!open && setOpen(true);
let day = parseDate(e.target.value, today, locale, "date");
if (isValidDate(day)) {
setMonth(day);
setInputValue(formatDateForInput(day, locale, "date"));
setInputValue(formatDateForInput(day, locale, "date", inputFormat));
}
};
const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
let day = parseDate(e.target.value, today, locale, "date");
isValidDate(day) && setInputValue(formatDateForInput(day, locale, "date"));
isValidDate(day) &&
setInputValue(formatDateForInput(day, locale, "date", inputFormat));
};
/* Only allow de-selecting if not required */
@@ -162,11 +211,15 @@ export const useDatepicker = (
if (!required && selected) {
updateDate(undefined);
setInputValue("");
updateValidation({ isValidDate: false, isEmpty: true });
return;
}
updateDate(day);
updateValidation();
setMonth(day);
setInputValue(day ? formatDateForInput(day, locale, "date") : "");
setInputValue(
day ? formatDateForInput(day, locale, "date", inputFormat) : ""
);
};
// When changing the input field, save its value in state and check if the
@@ -176,23 +229,40 @@ export const useDatepicker = (
setInputValue(e.target.value);
const day = parseDate(e.target.value, today, locale, "date");
const isBefore =
fromDate && day && differenceInCalendarDays(fromDate, day) > 0;
const isAfter = toDate && day && differenceInCalendarDays(day, toDate) > 0;
if (
!isValidDate(day) ||
(disabled &&
((disableWeekends && isWeekend(day)) || isMatch(day, disabled)))
(disableWeekends && isWeekend(day)) ||
(disabled && isMatch(day, disabled))
) {
updateDate(undefined);
updateValidation({
isInvalid: isValidDate(day),
isWeekend: disableWeekends && isWeekend(day),
isDisabled: disabled && isMatch(day, disabled),
isValidDate: false,
isEmpty: !e.target.value,
isBefore: isBefore ?? false,
isAfter: isAfter ?? false,
});
return;
}
const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
if (isBefore || isAfter) {
updateDate(undefined);
updateValidation({
isValidDate: false,
isBefore: isBefore ?? false,
isAfter: isAfter ?? false,
});
return;
}
updateDate(day);
setMonth(day);
updateValidation();
setMonth(defaultMonth ?? day);
};
const handleClose = useCallback(() => {
@@ -217,13 +287,15 @@ export const useDatepicker = (
month,
onMonthChange: (month) => setMonth(month),
onDayClick: handleDayClick,
selected: selectedDay,
selected: selectedDay ?? new Date("Invalid date"),
locale: _locale,
fromDate,
toDate,
today,
open,
onOpenToggle: () => setOpen((x) => !x),
disabled,
disableWeekends,
ref: daypickerRef,
};
@@ -22,6 +22,19 @@ export interface UseMonthPickerOptions
* Callback for month-change
*/
onMonthChange?: (date?: Date) => void;
/**
* Input-format
* @default "MMMM yyyy"
*/
inputFormat?: string;
/**
* validation-callback
*/
onValidate?: (val: MonthValidationT) => void;
/**
* Default shown year
*/
defaultYear?: Date;
}
interface UseMonthPickerValue {
@@ -48,6 +61,25 @@ interface UseMonthPickerValue {
reset: () => void;
}
export type MonthValidationT = {
isDisabled: boolean;
isEmpty: boolean;
isInvalid: boolean;
isValidMonth: boolean;
isBefore: boolean;
isAfter: boolean;
};
const getValidationMessage = (val = {}): MonthValidationT => ({
isDisabled: false,
isEmpty: false,
isInvalid: false,
isBefore: false,
isAfter: false,
isValidMonth: true,
...val,
});
export const useMonthpicker = (
opt: UseMonthPickerOptions = {}
): UseMonthPickerValue => {
@@ -59,6 +91,9 @@ export const useMonthpicker = (
disabled,
required,
onMonthChange,
inputFormat,
onValidate,
defaultYear,
} = opt;
const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
@@ -70,12 +105,12 @@ export const useMonthpicker = (
const monthpickerRef = useRef<HTMLDivElement>(null);
// Initialize states
const [year, setYear] = useState(defaultSelected ?? today);
const [year, setYear] = useState(defaultSelected ?? defaultYear ?? today);
const [selectedMonth, setSelectedMonth] = useState(defaultSelected);
const [open, setOpen] = useState(false);
const defaultInputValue = defaultSelected
? formatDateForInput(defaultSelected, locale, "month")
? formatDateForInput(defaultSelected, locale, "month", inputFormat)
: "";
const [inputValue, setInputValue] = useState(defaultInputValue);
@@ -85,16 +120,25 @@ export const useMonthpicker = (
setSelectedMonth(date);
};
const updateValidation = (val: Partial<MonthValidationT> = {}) => {
const msg = getValidationMessage(val);
onValidate?.(msg);
};
const handleFocusIn = useCallback(
(e) => {
if (!e?.target || !e?.target?.nodeType) {
/* Workaround for shadow-dom users (open) */
const composed = e.composedPath?.()?.[0];
if (!e?.target || !e?.target?.nodeType || !composed) {
return;
}
![
monthpickerRef.current,
inputRef.current,
inputRef.current?.nextSibling,
].some((element) => element?.contains(e.target)) &&
].some(
(element) => element?.contains(e.target) || element?.contains(composed)
) &&
open &&
setOpen(false);
},
@@ -112,15 +156,17 @@ export const useMonthpicker = (
const reset = () => {
updateMonth(defaultSelected);
setYear(defaultSelected ?? today);
setYear(defaultSelected ?? defaultYear ?? today);
setInputValue(defaultInputValue ?? "");
setDefaultSelected(_defaultSelected);
};
const setSelected = (date: Date | undefined) => {
updateMonth(date);
setYear(date ?? today);
setInputValue(date ? formatDateForInput(date, locale, "month") : "");
setYear(date ?? defaultYear ?? today);
setInputValue(
date ? formatDateForInput(date, locale, "month", inputFormat) : ""
);
};
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
@@ -128,13 +174,14 @@ export const useMonthpicker = (
let day = parseDate(e.target.value, today, locale, "month");
if (isValidDate(day)) {
setYear(day);
setInputValue(formatDateForInput(day, locale, "month"));
setInputValue(formatDateForInput(day, locale, "month", inputFormat));
}
};
const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
let day = parseDate(e.target.value, today, locale, "month");
isValidDate(day) && setInputValue(formatDateForInput(day, locale, "month"));
isValidDate(day) &&
setInputValue(formatDateForInput(day, locale, "month", inputFormat));
};
/* Only allow de-selecting if not required */
@@ -146,11 +193,15 @@ export const useMonthpicker = (
if (!required && !month) {
updateMonth(undefined);
updateValidation({ isValidMonth: false, isEmpty: true });
setInputValue("");
return;
}
updateMonth(month);
setInputValue(month ? formatDateForInput(month, locale, "month") : "");
updateValidation();
setInputValue(
month ? formatDateForInput(month, locale, "month", inputFormat) : ""
);
};
// When changing the input field, save its value in state and check if the
@@ -160,32 +211,48 @@ export const useMonthpicker = (
setInputValue(e.target.value);
const month = parseDate(e.target.value, today, locale, "month");
if (!isValidDate(month) || (disabled && isMatch(month, disabled))) {
updateMonth(undefined);
return;
}
const isBefore =
fromDate &&
month &&
(fromDate.getFullYear() > month.getFullYear() ||
(fromDate.getFullYear() === month.getFullYear() &&
fromDate.getMonth() > month.getMonth()));
const isAfter =
toDate &&
month &&
(toDate.getFullYear() < month.getFullYear() ||
(toDate.getFullYear() === month.getFullYear() &&
toDate.getMonth() < month.getMonth()));
if (!isValidDate(month) || (disabled && isMatch(month, disabled))) {
updateMonth(undefined);
updateValidation({
isInvalid: isValidDate(month),
isDisabled: disabled && isMatch(month, disabled),
isValidMonth: false,
isEmpty: !e.target.value,
isBefore: isBefore ?? false,
isAfter: isAfter ?? false,
});
return;
}
if (
isAfter ||
isBefore ||
(fromDate && toDate && !isMatch(month, [{ from: fromDate, to: toDate }]))
) {
updateMonth(undefined);
updateValidation({
isValidMonth: false,
isBefore: isBefore ?? false,
isAfter: isAfter ?? false,
});
return;
}
updateMonth(month);
updateValidation();
setYear(month);
};
@@ -0,0 +1,59 @@
/* eslint-disable react/jsx-pascal-case */
import { act, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import { UNSAFE_DatePicker, UNSAFE_useRangeDatepicker } from "..";
const RangeDemo = () => {
const { datepickerProps, fromInputProps, selectedRange, toInputProps } =
UNSAFE_useRangeDatepicker({
fromDate: new Date("Aug 23 2019"),
});
return (
<div style={{ display: "flex", gap: "1rem" }}>
<UNSAFE_DatePicker {...datepickerProps}>
<UNSAFE_DatePicker.Input {...fromInputProps} label="Fra" />
<UNSAFE_DatePicker.Input {...toInputProps} label="Til" />
</UNSAFE_DatePicker>
<div title="res">{JSON.stringify(selectedRange)}</div>
</div>
);
};
describe("Writing in input sets correct values", () => {
it("useRangeDatepicker same date", async () => {
const utils = render(<RangeDemo />);
const fraInput = utils.getByRole("textbox", { name: "Fra" });
const tilInput = utils.getByRole("textbox", { name: "Til" });
await act(async () => {
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "03.08.2022");
});
const res = utils.getByTitle("res");
expect(res.innerHTML).toEqual(
JSON.stringify({
from: "2022-08-03T00:00:00.000Z",
to: "2022-08-03T00:00:00.000Z",
})
);
});
it("useRangeDatepicker before after to ", async () => {
const utils = render(<RangeDemo />);
const fraInput = utils.getByRole("textbox", { name: "Fra" });
const tilInput = utils.getByRole("textbox", { name: "Til" });
await act(async () => {
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "02.08.2022");
});
const res = utils.getByTitle("res");
expect(res.innerHTML).toEqual(
JSON.stringify({
from: "2022-08-03T00:00:00.000Z",
})
);
});
});
@@ -10,10 +10,42 @@ import {
isValidDate,
parseDate,
} from "../utils";
import { UseDatepickerOptions } from "./useDatepicker";
import { DateValidationT, UseDatepickerOptions } from "./useDatepicker";
export type RangeValidationT = {
from: DateValidationT;
to: DateValidationT & { isBeforeFrom?: boolean };
};
const getValidationMessage = (from = {}, to = {}): RangeValidationT => ({
from: {
isDisabled: false,
isWeekend: false,
isEmpty: false,
isInvalid: false,
isBefore: false,
isAfter: false,
isValidDate: true,
...from,
},
to: {
isDisabled: false,
isWeekend: false,
isEmpty: false,
isInvalid: false,
isBefore: false,
isAfter: false,
isBeforeFrom: false,
isValidDate: true,
...to,
},
});
interface UseRangeDatepickerOptions
extends Omit<UseDatepickerOptions, "defaultSelected" | "onDateChange"> {
extends Omit<
UseDatepickerOptions,
"defaultSelected" | "onDateChange" | "onValidate"
> {
/**
* The initially selected DateRange
*/
@@ -22,6 +54,10 @@ interface UseRangeDatepickerOptions
* Callback for changed state
*/
onRangeChange?: (val?: DateRange) => void;
/**
* validation-callback
*/
onValidate?: (val: RangeValidationT) => void;
}
interface UseRangeDatepickerValue {
@@ -65,6 +101,92 @@ const RANGE = {
type RangeT = typeof RANGE[keyof typeof RANGE];
const fromValidation = (day: Date, opt?: UseRangeDatepickerOptions) => {
const isBefore =
opt?.fromDate && day && differenceInCalendarDays(opt?.fromDate, day) > 0;
const isAfter =
opt?.toDate && day && differenceInCalendarDays(day, opt?.toDate) > 0;
if (
isValidDate(day) &&
!(opt?.disableWeekends && isWeekend(day)) &&
!(opt?.disabled && isMatch(day, opt.disabled))
) {
return {
isValidDate: false,
isInvalid: !isValidDate(day),
isWeekend: opt?.disableWeekends && isWeekend(day),
isDisabled: opt?.disabled && isMatch(day, opt.disabled),
isBefore: isBefore,
isAfter: isAfter,
};
}
if (isBefore || isAfter) {
return {
isValidDate: false,
isBefore: isBefore,
isAfter: isAfter,
};
}
};
const toValidation = (
day: Date,
from: Date,
opt?: UseRangeDatepickerOptions
) => {
const isBefore =
opt?.fromDate && day && differenceInCalendarDays(opt?.fromDate, day) > 0;
const isAfter =
opt?.toDate && day && differenceInCalendarDays(day, opt?.toDate) > 0;
const isBeforeFrom =
(from && differenceInCalendarDays(from, day) > 0) ?? false;
if (
isValidDate(day) &&
!(opt?.disableWeekends && isWeekend(day)) &&
!(opt?.disabled && isMatch(day, opt.disabled))
) {
return {
isValidDate: false,
isInvalid: !isValidDate(day),
isWeekend: opt?.disableWeekends && isWeekend(day),
isDisabled: opt?.disabled && isMatch(day, opt.disabled),
isBefore,
isAfter,
isBeforeFrom,
};
}
if (isBefore || isAfter || isBeforeFrom) {
return {
isValidDate: false,
isBefore,
isAfter,
isBeforeFrom,
};
}
};
const initialValidation = (
range?: DateRange,
opt?: UseRangeDatepickerOptions
): RangeValidationT => {
if (!range || !range?.from) {
return getValidationMessage(
{ isEmpty: true, isValidDate: false },
{ isEmpty: true, isValidDate: false }
);
}
const fromVal = fromValidation(range.from, opt);
const toVal = range.to
? toValidation(range.to, range.from, opt)
: { isEmpty: true, isValidDate: false };
return getValidationMessage({ ...fromVal }, { ...toVal });
};
export const useRangeDatepicker = (
opt: UseRangeDatepickerOptions = {}
): UseRangeDatepickerValue => {
@@ -77,6 +199,9 @@ export const useRangeDatepicker = (
disabled,
disableWeekends,
onRangeChange,
inputFormat,
onValidate,
defaultMonth,
} = opt;
const locale = getLocaleFromString(_locale);
@@ -89,7 +214,7 @@ export const useRangeDatepicker = (
// Initialize states
const [month, setMonth] = useState(
defaultSelected ? defaultSelected?.from : today
defaultSelected ? defaultSelected?.from : defaultMonth ?? today
);
const [selectedRange, setSelectedRange] = useState<DateRange | undefined>(
defaultSelected ?? { from: undefined, to: undefined }
@@ -97,15 +222,20 @@ export const useRangeDatepicker = (
const [fromInputValue, setFromInputValue] = useState(
defaultSelected?.from
? formatDateForInput(defaultSelected.from, locale, "date")
? formatDateForInput(defaultSelected.from, locale, "date", inputFormat)
: ""
);
const [toInputValue, setToInputValue] = useState(
defaultSelected?.to
? formatDateForInput(defaultSelected.to, locale, "date")
? formatDateForInput(defaultSelected.to, locale, "date", inputFormat)
: ""
);
const [validation, setValidation] = useState<RangeValidationT>(
initialValidation(selectedRange, opt)
);
const [open, setOpen] = useState(false);
const updateRange = (range?: DateRange) => {
@@ -113,9 +243,20 @@ export const useRangeDatepicker = (
setSelectedRange(range);
};
const updateValidation = (
from: Partial<RangeValidationT["from"]> = {},
to: Partial<RangeValidationT["to"]> = {}
) => {
const msg = getValidationMessage(from, to);
setValidation(msg);
onValidate?.(msg);
};
const handleFocusIn = useCallback(
(e) => {
if (!e?.target || !e?.target?.nodeType) {
/* Workaround for shadow-dom users (open) */
const composed = e.composedPath?.()?.[0];
if (!e?.target || !e?.target?.nodeType || !composed) {
return;
}
![
@@ -124,7 +265,9 @@ export const useRangeDatepicker = (
inputRefFrom.current,
inputRefTo.current?.nextSibling,
inputRefFrom.current?.nextSibling,
].some((element) => element?.contains(e.target)) &&
].some(
(element) => element?.contains(e.target) || element?.contains(composed)
) &&
open &&
setOpen(false);
},
@@ -142,15 +285,21 @@ export const useRangeDatepicker = (
const reset = () => {
updateRange(defaultSelected ?? { from: undefined, to: undefined });
setMonth(defaultSelected ? defaultSelected?.from : today);
setMonth(defaultSelected ? defaultSelected?.from : defaultMonth ?? today);
setValidation(
initialValidation(
defaultSelected ?? { from: undefined, to: undefined },
opt
)
);
setFromInputValue(
defaultSelected?.from
? formatDateForInput(defaultSelected.from, locale, "date")
? formatDateForInput(defaultSelected.from, locale, "date", inputFormat)
: ""
);
setToInputValue(
defaultSelected?.to
? formatDateForInput(defaultSelected.to, locale, "date")
? formatDateForInput(defaultSelected.to, locale, "date", inputFormat)
: ""
);
setDefaultSelected(_defaultSelected);
@@ -159,11 +308,16 @@ export const useRangeDatepicker = (
const setSelected = (range?: DateRange) => {
updateRange(range);
setFromInputValue(
range?.from ? formatDateForInput(range.from, locale, "date") : ""
range?.from
? formatDateForInput(range.from, locale, "date", inputFormat)
: ""
);
setToInputValue(
range?.to ? formatDateForInput(range?.to, locale, "date") : ""
range?.to
? formatDateForInput(range?.to, locale, "date", inputFormat)
: ""
);
setValidation(initialValidation(range, opt));
};
const handleFocus = (e, src: RangeT) => {
@@ -172,52 +326,10 @@ export const useRangeDatepicker = (
if (isValidDate(day)) {
setMonth(day);
src === RANGE.FROM
? setFromInputValue(formatDateForInput(day, locale, "date"))
: setToInputValue(formatDateForInput(day, locale, "date"));
}
};
const handleInputs = (day: Date, src: RangeT) => {
if (src === RANGE.FROM) {
const isAfter =
toInputValue &&
differenceInCalendarDays(
day,
parseDate(toInputValue, today, locale, "date")
) > 0;
if (isAfter) {
setFromInputValue(
formatDateForInput(
parseDate(toInputValue, today, locale, "date"),
locale,
"date"
? setFromInputValue(
formatDateForInput(day, locale, "date", inputFormat)
)
);
setToInputValue(formatDateForInput(day, locale, "date"));
} else {
setFromInputValue(formatDateForInput(day, locale, "date"));
}
} else if (src === RANGE.TO) {
const isBefore =
fromInputValue &&
differenceInCalendarDays(
parseDate(fromInputValue, today, locale, "date"),
day
) > 0;
if (isBefore) {
setToInputValue(
formatDateForInput(
parseDate(fromInputValue, today, locale, "date"),
locale,
"date"
)
);
setFromInputValue(formatDateForInput(day, locale, "date"));
} else {
setToInputValue(formatDateForInput(day, locale, "date"));
}
: setToInputValue(formatDateForInput(day, locale, "date", inputFormat));
}
};
@@ -227,7 +339,19 @@ export const useRangeDatepicker = (
return;
}
handleInputs(day, src);
if (src === RANGE.FROM) {
setFromInputValue(formatDateForInput(day, locale, "date", inputFormat));
} else if (src === RANGE.TO) {
setToInputValue(formatDateForInput(day, locale, "date", inputFormat));
}
};
const validateDay = (day: any) => {
return (
isValidDate(day) &&
!(disableWeekends && isWeekend(day)) &&
!(disabled && isMatch(day, disabled))
);
};
const handleSelect = (range) => {
@@ -238,68 +362,140 @@ export const useRangeDatepicker = (
!selectedRange?.from && selectedRange?.to ? selectedRange?.to : range?.to;
range?.from
? setFromInputValue(formatDateForInput(range?.from, locale, "date"))
? setFromInputValue(
formatDateForInput(range?.from, locale, "date", inputFormat)
)
: setFromInputValue("");
prevToRange
? setToInputValue(formatDateForInput(prevToRange, locale, "date"))
? setToInputValue(
formatDateForInput(prevToRange, locale, "date", inputFormat)
)
: setToInputValue("");
updateValidation(
{ isValidDate: !!range?.from, isEmpty: !range?.from },
{ isValidDate: !!range?.to, isEmpty: !prevToRange }
);
updateRange({ from: range?.from, to: prevToRange });
};
/* live-update datepicker based on changes in inputfields */
const handleChange = (e, src: RangeT) => {
src === RANGE.FROM
? setFromInputValue(e.target.value)
: setToInputValue(e.target.value);
const day = parseDate(e.target.value, today, locale, "date");
if (
!isValidDate(day) ||
(disabled &&
((disableWeekends && isWeekend(day)) || isMatch(day, disabled)))
) {
updateRange(
src === RANGE.FROM
? { ...selectedRange, from: undefined }
: { from: selectedRange?.from, to: undefined }
const fromChange = (
val: string = "",
day: Date,
isBefore = false,
isAfter = false
) => {
setFromInputValue(val);
if (!validateDay(day)) {
updateRange({ ...selectedRange, from: undefined });
updateValidation(
{
isEmpty: !val,
isValidDate: false,
isInvalid: !isValidDate(day),
isWeekend: disableWeekends && isWeekend(day),
isDisabled: disabled && isMatch(day, disabled),
isBefore,
isAfter,
},
validation.to
);
return;
}
if (isBefore || isAfter) {
updateRange({ ...selectedRange, from: undefined });
updateValidation(
{
isValidDate: false,
isBefore,
isAfter,
},
validation.to
);
return;
}
if (
selectedRange?.to &&
differenceInCalendarDays(day, selectedRange?.to) > 0
) {
updateRange({ to: day, from: day });
setToInputValue(formatDateForInput(day, locale, "date", inputFormat));
setMonth(day);
updateValidation();
return;
}
if (toInputValue && !selectedRange?.to) {
const toDay = parseDate(toInputValue, today, locale, "date");
if (validateDay(toDay)) {
updateRange({ from: day, to: toDay });
setMonth(day);
updateValidation();
return;
}
}
updateRange({ ...selectedRange, from: day });
updateValidation({}, validation.to);
setMonth(day);
};
const toChange = (
val: string = "",
day: Date,
isBefore = false,
isAfter = false
) => {
setToInputValue(val);
if (!validateDay(day)) {
updateRange({ from: selectedRange?.from, to: undefined });
updateValidation(validation.from, {
isEmpty: !val,
isValidDate: false,
isInvalid: !isValidDate(day),
isWeekend: disableWeekends && isWeekend(day),
isDisabled: disabled && isMatch(day, disabled),
isBefore,
isAfter,
});
return;
}
if (isBefore || isAfter) {
updateRange({ from: selectedRange?.from, to: undefined });
updateValidation(validation.from, {
isValidDate: false,
isBefore,
isAfter,
});
return;
}
/* If to-value < from-value */
if (
selectedRange?.from &&
differenceInCalendarDays(selectedRange?.from, day) > 0
) {
updateRange({ from: selectedRange?.from, to: undefined });
updateValidation(validation.from, {
isValidDate: false,
isBeforeFrom: true,
});
return;
}
updateRange({ from: selectedRange?.from, to: day });
updateValidation(validation.from, {});
setMonth(day);
};
/* live-update datepicker based on changes in inputfields */
const handleChange = (e, src: RangeT) => {
const day = parseDate(e.target.value, today, locale, "date");
const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
if (isBefore || isAfter) {
src === RANGE.FROM
? updateRange({ ...selectedRange, from: undefined })
: updateRange({ from: selectedRange?.from, to: undefined });
return;
}
/* If to-value < from-value, switch places in state */
if (
src === RANGE.TO &&
selectedRange?.from &&
differenceInCalendarDays(selectedRange?.from, day) >= 0
) {
updateRange({ from: day, to: selectedRange?.from });
setMonth(day);
return;
}
/* If from-value > to-value , switch places in state */
if (
src === RANGE.FROM &&
selectedRange?.to &&
differenceInCalendarDays(day, selectedRange?.to) >= 0
) {
updateRange({ to: day, from: selectedRange?.to });
setMonth(day);
return;
}
src === RANGE.FROM && updateRange({ ...selectedRange, from: day });
src === RANGE.TO && updateRange({ from: selectedRange?.from, to: day });
setMonth(day);
return src === RANGE.FROM
? fromChange(e.target.value, day, isBefore, isAfter)
: toChange(e.target.value, day, isBefore, isAfter);
};
const handleClose = useCallback(() => {
@@ -336,6 +532,8 @@ export const useRangeDatepicker = (
mode: "range" as const,
open,
onOpenToggle: () => setOpen((x) => !x),
disabled,
disableWeekends,
ref: datePickerRef,
};
+3
View File
@@ -9,6 +9,9 @@ export {
UNSAFE_useDatepicker,
UNSAFE_useMonthpicker,
UNSAFE_useRangeDatepicker,
RangeValidationT,
DateValidationT,
MonthValidationT,
} from "./hooks";
export {
default as UNSAFE_MonthPicker,
@@ -90,6 +90,26 @@ export const UseMonthpicker = () => {
);
};
export const UseMonthpickerFormat = () => {
const { inputProps, monthpickerProps } = UNSAFE_useMonthpicker({
disabled: [new Date("Apr 1 2022")],
onMonthChange: console.log,
inputFormat: "MM.yyyy",
});
return (
<div style={{ height: "20rem" }}>
<MonthPicker {...monthpickerProps}>
<MonthPicker.Input
{...inputProps}
label="Velg måned"
variant="monthpicker"
/>
</MonthPicker>
</div>
);
};
export const Required = () => {
const { inputProps, monthpickerProps } = UNSAFE_useMonthpicker({
locale: "nb",
@@ -6,16 +6,16 @@ const parse = (inp: string) => parseDate(inp, new Date(), nb, "date");
describe("Format date to correct output", () => {
test("formatDateForInput", () => {
expect(formatDateForInput(parse("15/05/22"), nb, "date")).toEqual(
expect(formatDateForInput(parse("15/05/2022"), nb, "date")).toEqual(
"15.05.2022"
);
expect(formatDateForInput(parse("1/5/22"), nb, "date")).toEqual(
expect(formatDateForInput(parse("1/5/2022"), nb, "date")).toEqual(
"01.05.2022"
);
expect(formatDateForInput(parse("1/05/22"), nb, "date")).toEqual(
expect(formatDateForInput(parse("1/05/2022"), nb, "date")).toEqual(
"01.05.2022"
);
expect(formatDateForInput(parse("15/5/22"), nb, "date")).toEqual(
expect(formatDateForInput(parse("15/5/2022"), nb, "date")).toEqual(
"15.05.2022"
);
});
@@ -2,7 +2,6 @@ import { parseDate } from "..";
import { isValidDate } from "..";
import nb from "date-fns/locale/nb";
import getMonth from "date-fns/getMonth";
import getYear from "date-fns/getYear";
const check = (inp: string) =>
expect(isValidDate(parseDate(inp, new Date(), nb, "date")));
@@ -11,71 +10,32 @@ const parse = (inp: string) => parseDate(inp, new Date(), nb, "date");
describe("Parse date-inputs", () => {
test("No spaces", () => {
check("150522").toBeTruthy();
check("11052022").toBeTruthy();
check("15052022").toBeTruthy();
});
test(". divider", () => {
check("1.5.22").toBeTruthy();
check("1.5.2022").toBeTruthy();
check("11.05.22").toBeTruthy();
check("11.05.2022").toBeTruthy();
});
test("/ divider", () => {
check("1/5/22").toBeTruthy();
check("10/5/22").toBeTruthy();
check("1/5/2022").toBeTruthy();
check("10/5/2022").toBeTruthy();
check("1/05/22").toBeTruthy();
check("10/05/22").toBeTruthy();
check("1/05/2022").toBeTruthy();
check("10/05/2022").toBeTruthy();
});
test("- divider", () => {
check("1-5-22").toBeTruthy();
check("10-5-22").toBeTruthy();
check("1-5-2022").toBeTruthy();
check("10-5-2022").toBeTruthy();
check("1-05-22").toBeTruthy();
check("10-05-22").toBeTruthy();
check("1-05-2022").toBeTruthy();
check("10-05-2022").toBeTruthy();
});
});
describe("Parse date-inputs to correct year", () => {
test("No spaces", () => {
expect(getYear(parse("150522"))).toEqual(2022);
});
test(". divider", () => {
expect(getYear(parse("1.05.22"))).toEqual(2022);
expect(getYear(parse("11.05.22"))).toEqual(2022);
expect(getYear(parse("1.5.22"))).toEqual(2022);
expect(getYear(parse("11.5.22"))).toEqual(2022);
});
test("/ divider", () => {
expect(getYear(parse("1/5/22"))).toEqual(2022);
expect(getYear(parse("10/5/22"))).toEqual(2022);
expect(getYear(parse("1/05/22"))).toEqual(2022);
expect(getYear(parse("10/05/22"))).toEqual(2022);
});
test("- divider", () => {
expect(getYear(parse("1-5-22"))).toEqual(2022);
expect(getYear(parse("10-5-22"))).toEqual(2022);
expect(getYear(parse("1-05-22"))).toEqual(2022);
expect(getYear(parse("10-05-22"))).toEqual(2022);
});
});
describe("Parse date-inputs to correct month", () => {
test("No spaces", () => {
expect(getMonth(parse("150522"))).toEqual(4);
expect(getMonth(parse("11052022"))).toEqual(4);
expect(getMonth(parse("15052022"))).toEqual(4);
});
@@ -7,11 +7,13 @@ import {
export const formatDateForInput = (
date: Date,
locale: Locale,
type: "date" | "month"
type: "date" | "month",
inputFormat?: string
) => {
const INPUT_DATE_STRING_FORMAT =
type === "date"
inputFormat ??
(type === "date"
? INPUT_DATE_STRING_FORMAT_DATE
: INPUT_DATE_STRING_FORMAT_MONTH;
: INPUT_DATE_STRING_FORMAT_MONTH);
return format(date, INPUT_DATE_STRING_FORMAT, { locale });
};
@@ -6,16 +6,10 @@ export const INPUT_DATE_STRING_FORMAT_DATE = "dd.MM.yyyy";
export const INPUT_DATE_STRING_FORMAT_MONTH = "MMMM yyyy";
const ALLOWED_INPUT_FORMATS_DATE = [
"ddMMyy",
"d.M.yy",
"dd.MM.yy",
"dd/MM/yy",
"dd-MM-yy",
INPUT_DATE_STRING_FORMAT_DATE,
"ddMMyyyy",
"dd/MM/yyyy",
"dd-MM-yyyy",
"d.M.yyyy",
];
const ALLOWED_INPUT_FORMATS_MONTH = [
@@ -28,20 +22,35 @@ const ALLOWED_INPUT_FORMATS_MONTH = [
...ALLOWED_INPUT_FORMATS_DATE,
];
const isTwoDigitYear = (dateString, today, locale, formats) => {
let parsed;
const newFormat = formats.map((x) => x.replace("yyyy", "yy"));
for (const format of newFormat) {
parsed = parse(dateString, format, today, { locale });
if (isValidDate(parsed)) {
return true;
}
}
return false;
};
export const parseDate = (
date: string,
today: Date,
locale: Locale,
type: "date" | "month"
) => {
): Date => {
let parsed;
const ALLOWED_FORMATS =
type === "date" ? ALLOWED_INPUT_FORMATS_DATE : ALLOWED_INPUT_FORMATS_MONTH;
for (const format of ALLOWED_FORMATS) {
parsed = parse(date, format, today, { locale });
if (isValidDate(parsed)) {
if (
isValidDate(parsed) &&
!isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)
) {
return parsed;
}
}
return parsed;
return new Date("Invalid date");
};
+1
View File
@@ -16,6 +16,7 @@ export * from "./panel";
export * from "./popover";
export * from "./read-more";
export * from "./chat";
export * from "./chips";
export * from "./stepper";
export * from "./tag";
export * from "./toggle-group";
+10 -5
View File
@@ -104,14 +104,19 @@ export const Modal = forwardRef<ReactModal, ModalProps>(
}
};
const getParentSelector = () => {
if (parentSelector) {
return parentSelector;
}
return rootElement !== undefined
? () => rootElement as HTMLElement
: undefined;
};
return (
<ReactModal
{...rest}
parentSelector={
parentSelector ?? rootElement !== undefined
? () => rootElement as HTMLElement
: undefined
}
parentSelector={getParentSelector()}
style={style}
isOpen={open}
ref={mergedRef}
+33 -7
View File
@@ -20,17 +20,10 @@ export const Default = (props) => {
return (
<>
<Button onClick={() => setOpen(true)}>Open Modal</Button>
<p>
We can also custom style the modal by passing in a react-modal style
object. Here the backdrop is red.
</p>
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="header123"
style={{
overlay: { backgroundColor: "#ff0000aa" },
}}
{...props}
>
<Modal.Content>
@@ -51,3 +44,36 @@ Default.args = {
shouldCloseOnOverlayClick: true,
closeButton: true,
};
export const ParentSelector = () => {
const [open, setOpen] = useState(true);
useEffect(() => {
Modal.setAppElement?.("#root");
}, []);
const parentEl = document.getElementById("custom-container");
return (
<>
<div id="custom-container" />
<Button onClick={() => setOpen(true)}>Open Modal</Button>
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="header123"
parentSelector={parentEl ? () => parentEl : undefined}
>
<Modal.Content>
<Heading spacing id="header123" level="1" size="large">
Header
</Heading>
<Heading spacing level="2" size="medium">
Header
</Heading>
<BodyLong>Voluptate laboris mollit dolore qui. Magna elit.</BodyLong>
</Modal.Content>
</Modal>
</>
);
};
+20 -8
View File
@@ -3,19 +3,31 @@ import cl from "clsx";
import { BodyShort, Detail } from "..";
export interface TagProps extends HTMLAttributes<HTMLSpanElement> {
/**
* Tag label
*/
children: React.ReactNode;
/**
* Changes background and border color
* Changes visual profile of tag
*/
variant: "warning" | "error" | "info" | "success";
variant:
| "warning"
| "warning-filled"
| "error"
| "error-filled"
| "info"
| "info-filled"
| "success"
| "success-filled"
| "neutral"
| "neutral-filled"
| "alt1"
| "alt1-filled"
| "alt2"
| "alt2-filled"
| "alt3"
| "alt3-filled";
/**
* Changes padding and font-sizes
* @default "medium"
*/
size?: "medium" | "small";
size?: "medium" | "small" | "xsmall";
}
export const Tag = forwardRef<HTMLSpanElement, TagProps>(
@@ -27,7 +39,7 @@ export const Tag = forwardRef<HTMLSpanElement, TagProps>(
{...rest}
ref={ref}
as="span"
size={size}
size={size === "medium" ? "medium" : "small"}
className={cl(
"navds-tag",
className,
+82 -5
View File
@@ -9,17 +9,70 @@ export default {
defaultValue: "info",
control: {
type: "radio",
options: ["error", "warning", "info", "success"],
options: [
"warning",
"error",
"info",
"success",
"neutral",
"alt1",
"alt2",
"alt3",
"warning-filled",
"error-filled",
"info-filled",
"success-filled",
"neutral-filled",
"alt1-filled",
"alt2-filled",
"alt3-filled",
],
},
},
size: {
defaultValue: "medium",
control: {
type: "radio",
options: ["xsmall", "small", "medium"],
},
},
},
};
const variants: Array<"error" | "warning" | "info" | "success"> = [
"error",
const variants: Array<
| "warning"
| "warning-filled"
| "error"
| "error-filled"
| "info"
| "info-filled"
| "success"
| "success-filled"
| "neutral"
| "neutral-filled"
| "alt1"
| "alt1-filled"
| "alt2"
| "alt2-filled"
| "alt3"
| "alt3-filled"
> = [
"warning",
"error",
"info",
"success",
"neutral",
"alt1",
"alt2",
"alt3",
"warning-filled",
"error-filled",
"info-filled",
"success-filled",
"neutral-filled",
"alt1-filled",
"alt2-filled",
"alt3-filled",
];
export const Default = (props) => (
@@ -34,10 +87,34 @@ Default.args = {
export const Small = () => {
return (
<div className="rowgap">
<div className="rowgap rowgap-wrap">
{variants.map((variant, i) => (
<Tag key={variant} variant={variant} size="small">
{new Array(i + 1).fill("Id elit esse")}
{variant}
</Tag>
))}
</div>
);
};
export const xSmall = () => {
return (
<div className="rowgap rowgap-wrap">
{variants.map((variant, i) => (
<Tag key={variant} variant={variant} size="xsmall">
{variant}
</Tag>
))}
</div>
);
};
export const Variants = () => {
return (
<div className="rowgap rowgap-wrap">
{variants.map((variant, i) => (
<Tag key={variant} variant={variant}>
{variant}
</Tag>
))}
</div>
+26
View File
@@ -1,5 +1,31 @@
# @navikt/ds-tailwind
## 1.5.10
## 1.5.9
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
## 1.5.4
## 1.5.3
## 1.5.2
## 1.5.1
## 1.5.0
## 1.4.4
## 1.4.3
## 1.4.2
## 1.4.1
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "1.4.2",
"version": "1.5.10",
"description": "Auto-generated tailwind config based on @navikt/ds-tokens",
"author": "NAV Designsystem team",
"keywords": [
@@ -22,7 +22,7 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^1.4.2",
"@navikt/ds-tokens": "^1.5.10",
"@types/jest": "^29.0.0",
"@types/node": "^17.0.35",
"jest": "^29.0.0",
-2
View File
@@ -31,5 +31,3 @@ const config = {
const outputString = `module.exports = ${JSON.stringify(config, null, 2)};`;
writeFileSync("tailwind.config.js", outputString);
console.log("Success: Tailwind config ");
+26
View File
@@ -1,5 +1,31 @@
# @navikt/ds-tokens
## 1.5.10
## 1.5.9
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
## 1.5.4
## 1.5.3
## 1.5.2
## 1.5.1
## 1.5.0
## 1.4.4
## 1.4.3
## 1.4.2
## 1.4.1
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "1.4.2",
"version": "1.5.10",
"description": "Auto-generated design-tokens for NAV design-systems framework",
"author": "NAV Designsystem team",
"keywords": [
@@ -17,7 +17,7 @@
"/tailwind"
],
"scripts": {
"build": "node ./build.js",
"build": "node ./build.js > /dev/null",
"watch": "nodemon --watch src/index.js --exec \"yarn build\"",
"token": "style-dictionary build",
"test": "jest",
+34
View File
@@ -1,5 +1,39 @@
# @navikt/ds-icons
## 1.5.10
## 1.5.9
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
### Patch Changes
- Error-ikon hadde avrundede kanter
## 1.5.4
### Patch Changes
- [#1709](https://github.com/navikt/Designsystemet/pull/1709) [`77302a30b`](https://github.com/navikt/Designsystemet/commit/77302a30b8eef8dc00b2d2e9c904a3f57da81a11) Thanks [@KenAJoh](https://github.com/KenAJoh)! - Oppdatert ikoner
## 1.5.3
## 1.5.2
## 1.5.1
## 1.5.0
## 1.4.4
## 1.4.3
## 1.4.2
## 1.4.1
+126
View File
@@ -311,6 +311,102 @@
"created_at": "2021-04-28T14:11:08.212Z",
"pageName": "People"
},
{
"name": "ChevronDownCircle",
"description": "",
"created_at": "2022-11-02T08:31:54.965Z",
"pageName": "Arrows"
},
{
"name": "ChevronDownCircleFilled",
"description": "",
"created_at": "2022-11-02T08:31:55.048Z",
"pageName": "Arrows"
},
{
"name": "ChevronDownDouble",
"description": "",
"created_at": "2022-11-02T08:31:54.957Z",
"pageName": "Arrows"
},
{
"name": "ChevronDownDoubleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.949Z",
"pageName": "Arrows"
},
{
"name": "ChevronLeftCircle",
"description": "",
"created_at": "2022-11-02T08:31:54.938Z",
"pageName": "Arrows"
},
{
"name": "ChevronLeftCircleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.930Z",
"pageName": "Arrows"
},
{
"name": "ChevronLeftDouble",
"description": "",
"created_at": "2022-11-02T08:31:55.040Z",
"pageName": "Arrows"
},
{
"name": "ChevronLeftDoubleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.922Z",
"pageName": "Arrows"
},
{
"name": "ChevronRightCircle",
"description": "",
"created_at": "2022-11-02T08:31:54.993Z",
"pageName": "Arrows"
},
{
"name": "ChevronRightCircleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.985Z",
"pageName": "Arrows"
},
{
"name": "ChevronRightDouble",
"description": "",
"created_at": "2022-11-02T08:31:55.003Z",
"pageName": "Arrows"
},
{
"name": "ChevronRightDoubleFilled",
"description": "",
"created_at": "2022-11-02T08:31:55.013Z",
"pageName": "Arrows"
},
{
"name": "ChevronUpCircle",
"description": "",
"created_at": "2022-11-02T08:31:54.906Z",
"pageName": "Arrows"
},
{
"name": "ChevronUpCircleFilled",
"description": "",
"created_at": "2022-11-02T08:31:55.021Z",
"pageName": "Arrows"
},
{
"name": "ChevronUpDouble",
"description": "",
"created_at": "2022-11-02T08:31:55.031Z",
"pageName": "Arrows"
},
{
"name": "ChevronUpDoubleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.914Z",
"pageName": "Arrows"
},
{
"name": "Child",
"description": "",
@@ -761,6 +857,12 @@
"created_at": "2021-01-11T13:37:55.499Z",
"pageName": "Status"
},
{
"name": "ErrorStroke",
"description": "",
"created_at": "2022-10-27T14:42:57.018Z",
"pageName": "Status"
},
{
"name": "Eu",
"description": "",
@@ -1355,6 +1457,12 @@
"created_at": "2021-01-11T13:37:55.736Z",
"pageName": "Status"
},
{
"name": "InformationStroke",
"description": "",
"created_at": "2022-10-27T14:42:57.034Z",
"pageName": "Status"
},
{
"name": "Laptop",
"description": "",
@@ -1613,6 +1721,18 @@
"created_at": "2020-07-01T10:37:42.664Z",
"pageName": "Arrows"
},
{
"name": "NextDouble",
"description": "",
"created_at": "2022-11-02T08:31:54.896Z",
"pageName": "Arrows"
},
{
"name": "NextDoubleFilled",
"description": "",
"created_at": "2022-11-02T08:31:54.975Z",
"pageName": "Arrows"
},
{
"name": "NextFilled",
"description": "",
@@ -2651,6 +2771,12 @@
"created_at": "2021-01-11T13:37:55.590Z",
"pageName": "Status"
},
{
"name": "WarningStroke",
"description": "",
"created_at": "2022-10-27T14:42:57.027Z",
"pageName": "Status"
},
{
"name": "Washing",
"description": "",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-icons",
"version": "1.4.2",
"version": "1.5.10",
"description": "Aksel icons",
"repository": {
"type": "git",
+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12ZM12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 15.5607L7.46967 11.0303L8.53033 9.96967L12 13.4393L15.4697 9.96967L16.5303 11.0303L12 15.5607Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 640 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25ZM12 15.5607L16.5303 11.0303L15.4697 9.96967L12 13.4393L8.53033 9.96967L7.46967 11.0303L12 15.5607Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 420 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12ZM12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25Z" fill="#262626"/>
<path d="M12 13.5607L16.5303 9.03033L15.4697 7.96967L12 11.4393L8.53033 7.96967L7.46967 9.03033L12 13.5607Z" fill="#262626"/>
<path d="M12 17.0607L16.5303 12.5303L15.4697 11.4697L12 14.9393L8.53033 11.4697L7.46967 12.5303L12 17.0607Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 726 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12ZM11.9984 13.4579L6.94365 8.40316L8.27389 7.07292L11.9984 10.7974L15.7228 7.07292L17.0531 8.40316L11.9984 13.4579ZM6.94365 13.241L11.9984 18.2957L17.0531 13.241L15.7228 11.9108L11.9984 15.6352L8.27389 11.9108L6.94365 13.241Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25ZM22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.43934 12L12.9697 7.46967L14.0303 8.53033L10.5607 12L14.0303 15.4697L12.9697 16.5303L8.43934 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 641 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12ZM8.43934 12L12.9697 16.5303L14.0303 15.4697L10.5607 12L14.0303 8.53033L12.9697 7.46967L8.43934 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 421 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25ZM22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12Z" fill="#262626"/>
<path d="M10.4393 12L14.9697 16.5303L16.0303 15.4697L12.5607 12L16.0303 8.53033L14.9697 7.46967L10.4393 12Z" fill="#262626"/>
<path d="M6.93934 12L11.4697 16.5303L12.5303 15.4697L9.06066 12L12.5303 8.53033L11.4697 7.46967L6.93934 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 727 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75ZM10.5421 11.9984L15.5968 6.94365L16.9271 8.27389L13.2026 11.9984L16.9271 15.7228L15.5968 17.0531L10.5421 11.9984ZM10.759 6.94365L5.70428 11.9984L10.759 17.0531L12.0892 15.7228L8.36477 11.9984L12.0892 8.27389L10.759 6.94365Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75ZM1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5607 12L11.0303 16.5303L9.96967 15.4697L13.4393 12L9.96967 8.53033L11.0303 7.46967L15.5607 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 639 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12ZM15.5607 12L11.0303 7.46967L9.96967 8.53033L13.4393 12L9.96967 15.4697L11.0303 16.5303L15.5607 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 420 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75ZM1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12Z" fill="#262626"/>
<path d="M13.5607 12L9.03033 7.46967L7.96967 8.53033L11.4393 12L7.96967 15.4697L9.03033 16.5303L13.5607 12Z" fill="#262626"/>
<path d="M17.0607 12L12.5303 7.46967L11.4697 8.53033L14.9393 12L11.4697 15.4697L12.5303 16.5303L17.0607 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25ZM13.4579 12.0016L8.40316 17.0564L7.07292 15.7261L10.7974 12.0016L7.07292 8.27718L8.40316 6.94694L13.4579 12.0016ZM13.241 17.0564L18.2957 12.0016L13.241 6.94694L11.9108 8.27718L15.6352 12.0016L11.9108 15.7261L13.241 17.0564Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12ZM12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 8.43934L16.5303 12.9697L15.4697 14.0303L12 10.5607L8.53033 14.0303L7.46967 12.9697L12 8.43934Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 640 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75ZM12 8.43934L7.46967 12.9697L8.53033 14.0303L12 10.5607L15.4697 14.0303L16.5303 12.9697L12 8.43934Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 421 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12ZM12 22.75C6.06294 22.75 1.25 17.9371 1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75Z" fill="#262626"/>
<path d="M12 10.4393L7.46967 14.9697L8.53033 16.0303L12 12.5607L15.4697 16.0303L16.5303 14.9697L12 10.4393Z" fill="#262626"/>
<path d="M12 6.93934L7.46967 11.4697L8.53033 12.5303L12 9.06066L15.4697 12.5303L16.5303 11.4697L12 6.93934Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 726 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 12C1.25 6.06294 6.06294 1.25 12 1.25C17.9371 1.25 22.75 6.06294 22.75 12C22.75 17.9371 17.9371 22.75 12 22.75C6.06294 22.75 1.25 17.9371 1.25 12ZM12.0016 10.5421L17.0564 15.5968L15.7261 16.9271L12.0016 13.2026L8.27718 16.9271L6.94694 15.5968L12.0016 10.5421ZM17.0564 10.759L12.0016 5.70428L6.94694 10.759L8.27718 12.0892L12.0016 8.36477L15.7261 12.0892L17.0564 10.759Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

+1 -1
View File
@@ -1,4 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.999 0C5.39479 0 0.0125453 5.37181 2.35603e-05 11.976C-0.00623729 15.1815 1.2355 18.1972 3.49775 20.4688C5.76001 22.7394 8.77148 23.9937 11.977 23.9999H12C18.6031 23.9999 23.9864 18.6271 24 12.0219C24.0125 5.40625 18.6396 0.0125217 11.999 0Z" fill="#BA3A26"/>
<path d="M12 10.6512L15.3719 7.27932C15.7443 6.90687 16.3482 6.90687 16.7206 7.27932C17.0931 7.65177 17.0931 8.25563 16.7206 8.62808L13.3487 12L16.7206 15.3719C17.0931 15.7443 17.0931 16.3482 16.7206 16.7206C16.3482 17.0931 15.7443 17.0931 15.3719 16.7206L12 13.3487L8.62808 16.7206C8.25563 17.0931 7.65177 17.0931 7.27932 16.7206C6.90687 16.3482 6.90687 15.7443 7.27932 15.3719L10.6512 12L7.27932 8.62808C6.90687 8.25563 6.90687 7.65177 7.27932 7.27932C7.65177 6.90687 8.25563 6.90687 8.62808 7.27932L12 10.6512Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5714 6.99998L17 8.42855L13.428 12L17 15.5714L15.5714 17L12 13.428L8.42855 17L6.99998 15.5714L10.572 12L6.99998 8.42855L8.42855 6.99998L12 10.572L15.5714 6.99998Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 905 B

After

Width:  |  Height:  |  Size: 606 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 5L19 7L13.9992 12L19 17L17 19L12 13.9992L7 19L5 17L10.0008 12L5 7L7 5L12 10.0008L17 5Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 261 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.57141 20V17.7143H10.8571V12H8.57141V9.71429H13.1428V17.7143H15.4286V20H8.57141ZM12 4C12.9467 4 13.7142 4.76751 13.7142 5.71429C13.7142 6.66106 12.9467 7.42857 12 7.42857C11.0532 7.42857 10.2857 6.66106 10.2857 5.71429C10.2857 4.76751 11.0532 4 12 4Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 423 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1213 12L6.41421 19.7071L5 18.2929L11.2929 12L5 5.70711L6.41421 4.29289L14.1213 12Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.62 12L11.9129 19.7071L10.4987 18.2929L16.7916 12L10.4987 5.70711L11.9129 4.29289L19.62 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.182 12L6.12132 20.0607L4 17.9393L9.93934 12L4 6.06067L6.12132 3.93935L14.182 12Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2426 12L13.182 20.0607L11.0607 17.9393L17 12L11.0607 6.06067L13.182 3.93935L21.2426 12Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

+1 -1
View File
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0465 7.67114L18.4456 9.10034L9.71768 17.4992L6 14.0182L7.39516 12.5852L9.71359 14.7034L17.0465 7.67114Z" fill="#262626"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2014 6L20 7.89047L8.77943 19L4 14.3955L5.79361 12.5L8.77418 15.3019L18.2014 6Z" fill="#262626"/>
</svg>

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 253 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6667 4V13.3333H13.3333V4H10.6667ZM12 20C13.1046 20 14 19.1046 14 18C14 16.8954 13.1046 16 12 16C10.8954 16 10 16.8954 10 18C10 19.1046 10.8954 20 12 20Z" fill="#262626"/>
</svg>

After

Width:  |  Height:  |  Size: 327 B

+26
View File
@@ -1,5 +1,31 @@
# @navikt/ds-css-internal
## 1.5.10
## 1.5.9
## 1.5.8
## 1.5.7
## 1.5.6
## 1.5.5
## 1.5.4
## 1.5.3
## 1.5.2
## 1.5.1
## 1.5.0
## 1.4.4
## 1.4.3
## 1.4.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css-internal",
"version": "1.4.2",
"version": "1.5.10",
"description": "Css for NAV internal Designsystem components",
"author": "NAV Designsystem team",
"keywords": [
+104
View File
@@ -1,5 +1,109 @@
# @navikt/ds-react-internal
## 1.5.10
### Patch Changes
- Updated dependencies [[`d9352982d`](https://github.com/navikt/Designsystemet/commit/d9352982d42d5d12a9c5fc345e546f57c753738d)]:
- @navikt/ds-react@1.5.10
- @navikt/ds-icons@1.5.10
## 1.5.9
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.9
- @navikt/ds-icons@1.5.9
## 1.5.8
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.8
- @navikt/ds-icons@1.5.8
## 1.5.7
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.7
- @navikt/ds-icons@1.5.7
## 1.5.6
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.6
- @navikt/ds-icons@1.5.6
## 1.5.5
### Patch Changes
- Updated dependencies []:
- @navikt/ds-icons@1.5.5
- @navikt/ds-react@1.5.5
## 1.5.4
### Patch Changes
- Updated dependencies [[`77302a30b`](https://github.com/navikt/Designsystemet/commit/77302a30b8eef8dc00b2d2e9c904a3f57da81a11)]:
- @navikt/ds-icons@1.5.4
- @navikt/ds-react@1.5.4
## 1.5.3
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.3
- @navikt/ds-icons@1.5.3
## 1.5.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.2
- @navikt/ds-icons@1.5.2
## 1.5.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.5.1
- @navikt/ds-icons@1.5.1
## 1.5.0
### Patch Changes
- Updated dependencies [[`e19bf67b3`](https://github.com/navikt/Designsystemet/commit/e19bf67b337dea39989c68b5e9c2591cf0d5b40f), [`97c5f60e9`](https://github.com/navikt/Designsystemet/commit/97c5f60e9111da7e08f55c8d0aa29581f0a9b1ca)]:
- @navikt/ds-react@1.5.0
- @navikt/ds-icons@1.5.0
## 1.4.4
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.4.4
- @navikt/ds-icons@1.4.4
## 1.4.3
### Patch Changes
- Updated dependencies []:
- @navikt/ds-react@1.4.3
- @navikt/ds-icons@1.4.3
## 1.4.2
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react-internal",
"version": "1.4.2",
"version": "1.5.10",
"description": "NAV internal designsystem react components",
"author": "NAV Designsystem team",
"license": "MIT",
@@ -35,8 +35,8 @@
"watch": "tsc --watch -p tsconfig.esm.json"
},
"dependencies": {
"@navikt/ds-icons": "^1.4.2",
"@navikt/ds-react": "^1.4.2",
"@navikt/ds-icons": "^1.5.10",
"@navikt/ds-react": "^1.5.10",
"clsx": "^1.1.1",
"copy-to-clipboard": "^3.3.1"
},
+36 -10
View File
@@ -1,4 +1,4 @@
# Bidra med utvikling av nye og eksisterende komponenter
# Utvikling av nye eller eksisterende løsninger
## TL;DR
@@ -9,28 +9,54 @@ git clone git@github.com:navikt/Designsystemet.git
cd Designsystemet
yarn install
yarn boot
yarn dev/storybook
// Storybook
yarn storybook
// Nettside
yarn dev
```
[http://localhost:6006](http://localhost:6006) (port auto-incrementeres hvis den allerede er i bruk av en annen server)
Storybook: [http://localhost:6006](http://localhost:6006)
Nettside: [http://localhost:3000](http://localhost:3000)
## Utviklingsmiljø
Vi bruker storybook [https://storybook.js.org/](https://storybook.js.org/) som utviklermiljø.
`yarn storybook` vil starte opp dette miljøet og kan da skrive `stories` for komponentene man vil teste [hvordan skrive storybook stories](https://storybook.js.org/docs/react/writing-stories/introduction)
Vi bruker storybook [https://storybook.js.org/](https://storybook.js.org/) for utvikling av nye komponenter.
Skriv `stories` for komponentene man vil teste [(hvordan skrive storybook stories)](https://storybook.js.org/docs/react/writing-stories/introduction)
## Utvikling av nye komponenter
Om en modul du lager har behov for en npm-pakke så kan du legge den til i devDep/dep i pakken for så å kjøre `yarn install` i root. Yarn workspaces fikser dependency treet selv. Hvis du lurer på hvordan strukturen er satt opp, så hjelper vi deg gjerne!
Det er ikke satt opp en template for nye komponenter, men vi følger til dels en mal.
```sh
@navikt/pakkenavn
└─ src # Komponenter
├─ index.ts # Alle exports
└─ pakkenavn
├─ index.ts # Alle exports fra komponent
├─ komponent.tsx # Komponentkode
└─ komponent.stories.tsx # Stories
```
## Dokumentasjon
Dokumentasjon ang komponentter finner man på [aksel.nav.no](https://design.nav.no/). Hvis man ønsker å skrive noe dokumentasjon selv gjør vi det via CMS et Sanity. Kode-eksempler blir også skrevet i (https://github.com/navikt/aksel-website/tree/main/website/pages/eksempler)[aksel-website repo]
Dokumentasjon ang komponentter finner man på [aksel.nav.no](https://design.nav.no/). Hvis man ønsker å skrive noe dokumentasjon selv gjør vi det via CMS et Sanity. Kode-eksempler blir skrevet lokalt i (https://github.com/navikt/aksel-website/tree/main/website/pages/eksempler)[aksel-website repo]
## Versjonering av pakker
Vi bruker `changeset` for versionering av pakker. Se [README](https://github.com/navikt/Designsystemet/blob/master/.changeset/README.md) for mer informasjon.
## Kodekvalitet og testing
Noen komponenter og løsninger blir testet med jest + react-testing-library hvert build.
Noen komponenter og løsninger blir testet med jest + react-testing-library.
- Commit: Kjører prettier på endrede filer
- Push: Kjører tester, linter
- Commit: Kjører prettier på endrede filer + linter
- Ved PR: Kjører tester, linter og visuell regresjonstester
## Oppdatere Aksel.nav.no
Push til `master` pusher endringer til prod
Push til `next` pusher endringer til aksel.dev.nav.no
+13 -10
View File
@@ -5,7 +5,7 @@
<div align="center">
<p>
NAVs designsystem og felleskomponenter
NAVs designsystem + Aksel.nav.no
</p>
<div align="center">
<a href="https://master--5f801fb2aea7820022de2936.chromatic.com/">
@@ -38,8 +38,8 @@
└── codemod # @navikt/ds-codemod, migrerings-scripts
aksel.nav.no/
├── /website # NEXT.JS app (v12)
└── /sanity # Sanity CMS-app (v2)
├── website # NEXT.JS app (v12)
└── sanity # Sanity CMS-app (v2)
```
## Commands
@@ -50,13 +50,16 @@ Installer dependencies og bygg workspace
yarn && yarn boot
```
| Command | Script |
| ---------------- | ------------------------------------------------------------------------------------------------------ |
| `yarn changeset` | Ny versjon-entry [(how-to)](https://github.com/navikt/Designsystemet/blob/master/.changeset/README.md) |
| `yarn lint` | Lints react and css workspaces |
| `yarn test` | Runs tests |
| `yarn clean` | Fjerner genererte filer |
| `yarn docgen` | Generer ts-docs |
| Command | Script |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `yarn boot` | Bygger alle pakker |
| `yarn storybook` | Starter storybook |
| `yarn dev` | Starter aksel-nettsiden [(Mer info)](https://github.com/navikt/Designsystemet/blob/master/aksel.nav.no/README.md) |
| `yarn example:shadow-dom` | Starter demo-app med shadow-dom |
| `yarn lint` | Lints react and css workspaces |
| `yarn test` | Runs tests |
| `yarn clean` | Fjerner genererte filer |
| `yarn changeset` | Ny versjon-entry [(how-to)](https://github.com/navikt/Designsystemet/blob/master/.changeset/README.md) |
## Bidra
+8 -8
View File
@@ -18,14 +18,14 @@
"website"
],
"dependencies": {
"@navikt/ds-codemod": "^1.4.2",
"@navikt/ds-css": "^1.4.2",
"@navikt/ds-css-internal": "^1.4.2",
"@navikt/ds-icons": "^1.4.2",
"@navikt/ds-react": "^1.4.2",
"@navikt/ds-react-internal": "^1.4.2",
"@navikt/ds-tailwind": "^1.4.2",
"@navikt/ds-tokens": "^1.4.2"
"@navikt/ds-codemod": "^1.5.10",
"@navikt/ds-css": "^1.5.10",
"@navikt/ds-css-internal": "^1.5.10",
"@navikt/ds-icons": "^1.5.10",
"@navikt/ds-react": "^1.5.10",
"@navikt/ds-react-internal": "^1.5.10",
"@navikt/ds-tailwind": "^1.5.10",
"@navikt/ds-tokens": "^1.5.10"
},
"devDependencies": {
"prettier-plugin-tailwindcss": "^0.1.13"
@@ -19,8 +19,6 @@ export default function SetAndPublishAction(props) {
}
}, [props.draft]);
console.log(markers);
return {
disabled:
publish.disabled ||
@@ -19,5 +19,6 @@ export const adminPanel = async (roles) =>
S.listItem()
.title("Standalone-sider")
.child(S.documentTypeList("aksel_standalone")),
S.listItem().title("Redirects").child(S.documentTypeList("redirect")),
])
);
@@ -0,0 +1,49 @@
const limits = [
"/blogg/",
"/artikkel/",
"/prinsipper/",
"/designsystem/side/",
"/designsystem/komponenter/",
];
export default {
name: "redirect",
title: "Redirect",
type: "document",
fields: [
{
name: "source",
title: "Fra",
type: "string",
validation: (Rule) =>
Rule.required().custom((_, { parent }) => {
if (!parent?.source?.startsWith?.("/")) {
return "Kan bare redirecte fra relativ-url, eks /min/gamle/sideurl";
}
const valid = limits.find(
(x) => parent?.source?.startsWith?.(x) && parent.source > x
);
if (!valid) {
return `Kan bare redirecte fra sider som starter med en av disse url-ene ${limits.join(
", "
)}`;
}
return true;
}),
},
{
name: "destination",
title: "Til",
type: "string",
validation: (Rule) => Rule.required(),
},
{
name: "permanent",
title: "Permanent",
type: "boolean",
initialValue: () => true,
},
],
__experimental_omnisearch_visibility: false,
};
+2
View File
@@ -6,6 +6,7 @@ import schemaTypes from "all:part:@sanity/base/schema-type";
import editors from "./documents/editors";
import navigation, { dropdown, link } from "./documents/navigation";
import frontpage from "./documents/frontpage";
import redirect from "./documents/redirects";
import * as Designsystem from "./documents/designsystem";
import * as Aksel from "./documents/aksel";
@@ -20,6 +21,7 @@ export default createSchema({
navigation,
link,
dropdown,
redirect,
Designsystem.Color,
Designsystem.ds_header_heading,
@@ -1,26 +1,16 @@
import {
BodyLong,
Button,
Heading,
Label,
Textarea,
TextField,
} from "@navikt/ds-react";
import cl from "classnames";
import { Button, Heading, Label, Textarea } from "@navikt/ds-react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import isEmail from "validator/lib/isEmail";
import isEmpty from "validator/lib/isEmpty";
const FooterForm = () => {
const [contactForm, setContactForm] = useState({
content: "",
mail: "",
hasWritten: false,
});
const [contentError, setContentError] = useState({ content: "", mail: "" });
const [sent, setSent] = useState({ status: false, hadMail: false });
const [contentError, setContentError] = useState({ content: "" });
const [sent, setSent] = useState({ status: false });
const { asPath, basePath } = useRouter();
@@ -34,36 +24,26 @@ const FooterForm = () => {
});
fail = true;
}
if (
!isEmpty(contactForm.mail, { ignore_whitespace: true }) &&
!isEmail(contactForm.mail)
) {
setContentError({
...contentError,
mail: "Email ikke gyldig.",
});
fail = true;
}
if (fail) return;
setContentError({ content: "", mail: "" });
setContentError({ content: "" });
fetch("/api/generalFeedback", {
method: "POST",
body: JSON.stringify({
message: contactForm.content,
user: contactForm.mail,
url: `${basePath}${asPath}`,
}),
});
setSent({ status: true, hadMail: !!contactForm?.mail });
setContactForm({ content: "", mail: "", hasWritten: false });
setSent({ status: true });
setContactForm({ content: "", hasWritten: false });
};
useEffect(() => {
setSent({ status: false, hadMail: false });
setContactForm({ content: "", mail: "", hasWritten: false });
setContentError({ content: "", mail: "" });
setSent({ status: false });
setContactForm({ content: "", hasWritten: false });
setContentError({ content: "" });
}, [asPath]);
return (
@@ -72,13 +52,8 @@ const FooterForm = () => {
{sent.status ? (
<div>
<Label spacing as="p">
Melding er sendt til designsystemet
Meldingen er sendt til Aksel, takk for tilbakemeldingen!
</Label>
<BodyLong>
{`Takk skal du ha!${
sent.hadMail ? " Vi svarer deg så fort som mulig." : ""
}`}
</BodyLong>
</div>
) : (
<form onSubmit={(e) => handleSubmit(e)} className="w-full">
@@ -102,27 +77,9 @@ const FooterForm = () => {
!isEmpty(e.target.value, { ignore_whitespace: true }) &&
setContentError({ ...contentError, content: "" });
}}
description="Ikke skriv inn navn eller andre personopplysninger"
minRows={2}
/>
{contactForm.hasWritten && (
<TextField
className={cl("inverted-textfield")}
label="Vi svarer til e-post (valgfritt)"
error={contentError.mail}
value={contactForm.mail}
autoComplete="work email"
onChange={(e) => {
setContactForm({
...contactForm,
mail: e.target.value,
hasWritten: true,
});
e.target.value &&
isEmail(e.target.value) &&
setContentError({ ...contentError, mail: "" });
}}
/>
)}
</div>
{contactForm.hasWritten && (
<Button className="override-primary-button-dark">
@@ -1,5 +1,5 @@
import { Expand } from "@navikt/ds-icons";
import { BodyShort, Detail } from "@navikt/ds-react";
import { BodyShort, Detail, Tag } from "@navikt/ds-react";
import cl from "classnames";
import NextLink from "next/link";
import React, { useContext, useMemo, useState } from "react";
@@ -59,22 +59,23 @@ const NavItem = ({
)}
>
{item.title}
{item?.link?.status &&
["deprecated", "beta"].includes(item.link.status?.tag) && (
<Detail
className={cl(
"font-regular ml-2 rounded-full border-none px-2 capitalize",
{
"text-text bg-gray-200 capitalize ring-1 ring-inset ring-gray-900/10":
item.link.status?.tag === "deprecated",
"text-text bg-purple-50 ring-1 ring-inset ring-purple-900/10":
item.link.status?.tag === "beta",
<span className="ml-2 capitalize">
{item?.link?.status &&
["deprecated", "beta", "new"].includes(item.link.status?.tag) && (
<Tag
size="xsmall"
variant={
item.link.status?.tag === "beta"
? "alt1"
: item.link.status?.tag === "deprecated"
? "neutral"
: "info"
}
)}
>
{item.link.status?.tag}
</Detail>
)}
>
{item.link.status?.tag}
</Tag>
)}
</span>
</BodyShort>
</NextLink>
</li>
@@ -35,7 +35,7 @@ const AkselArtikkelTemplate = ({
<FooterSlope />
<div className="relative bg-gray-100 pt-12 pb-16">
<div className="dynamic-wrapper">
<Heading level="2" size="medium" className="px-4 text-deepblue-700">
<Heading level="2" size="medium" className="text-deepblue-700 px-4">
{data?.relevante_artikler?.length === 1
? `Les også`
: `Relevante artikler`}
@@ -85,7 +85,7 @@ const AkselArtikkelTemplate = ({
<Heading
level="1"
size="large"
className="algolia-index-lvl1 mt-4 text-deepblue-700 md:text-5xl"
className="algolia-index-lvl1 text-deepblue-700 mt-4 md:text-5xl"
>
{data.heading}
</Heading>
@@ -98,7 +98,7 @@ const AkselArtikkelTemplate = ({
<BodyShort
size="small"
as="span"
className="whitespace-nowrap text-text-muted"
className="text-text-muted whitespace-nowrap"
>
{dateStr(data?._updatedAt)}
</BodyShort>
@@ -120,7 +120,7 @@ const AkselArtikkelTemplate = ({
<BodyShort
size="small"
as="a"
className="algolia-index-lvl5 flex min-h-8 items-center justify-center gap-[2px] rounded-full bg-gray-200 pl-4 pr-1 capitalize text-deepblue-800 no-underline hover:bg-gray-100 hover:underline focus:outline-none focus-visible:shadow-focus"
className="algolia-index-lvl5 min-h-8 text-deepblue-800 focus-visible:shadow-focus flex items-center justify-center gap-[2px] rounded-full bg-gray-200 pl-4 pr-1 capitalize no-underline hover:underline focus:outline-none"
>
{title}
<Next aria-hidden />
@@ -141,14 +141,14 @@ const AkselArtikkelTemplate = ({
<SanityBlockContent blocks={data?.content ?? []} variant="aksel" />
<div className="mt-12">
{authors?.length > 0 && (
<Label className="mb-2 text-deepblue-700" as="p">
<Label className="text-deepblue-700 mb-2" as="p">
Bidragsytere
</Label>
)}
{authors?.length > 0 && (
<BodyShort
as="div"
className="mb-1 flex flex-wrap gap-1 text-text/80"
className="text-text/80 mb-1 flex flex-wrap gap-1"
>
{authors.map(abbrName).map((x, y) => (
<address className="not-italic" key={x}>
@@ -158,7 +158,7 @@ const AkselArtikkelTemplate = ({
))}
</BodyShort>
)}
<BodyShort as="span" className="whitespace-nowrap text-text/80">
<BodyShort as="span" className="text-text/80 whitespace-nowrap">
Publisert: {dateStr(data?.publishedAt ?? data?._updatedAt)}
</BodyShort>
</div>
@@ -12,7 +12,13 @@ const Alert = ({
<div className="mb-7 max-w-2xl">
<DsAlert variant={node.variant}>
{node.heading && (
<Heading spacing size="small" as={node.heading_level} id={node._key}>
<Heading
spacing
size="small"
as={node.heading_level}
id={node._key}
className="scroll-m-20"
>
{node.heading}
</Heading>
)}
@@ -1,9 +1,7 @@
import { capitalize, Snippet } from "@/components";
import { withErrorBoundary } from "@/error-boundary";
import { SanityT } from "@/lib";
import { SuccessStroke } from "@navikt/ds-icons";
import { BodyLong, Link } from "@navikt/ds-react";
import * as Tabs from "@radix-ui/react-tabs";
import { BodyLong, Link, Chips } from "@navikt/ds-react";
import cl from "classnames";
import { useEffect, useState } from "react";
import { CodeSandbox } from "./CodeSandbox";
@@ -57,8 +55,6 @@ const ComponentExamples = ({
str
.replace(/[^\w]|_/g, " ")
.replace(/\s+/g, " ")
.match(/\D/g)
.join("")
.trim()
) ?? str;
@@ -107,51 +103,46 @@ const ComponentExamples = ({
);
}
const active = activeExample ?? node?.dir?.filer?.[0]?.navn;
return (
<>
<Tabs.Root
defaultValue={node.dir.filer[0].navn}
onValueChange={(v) => setActiveExample(v)}
>
<Tabs.List className="mb-5 flex max-w-xl flex-wrap gap-2">
<div>
<div className="mb-5 max-w-xl">
<Chips>
{node.dir.filer.map((fil) => {
return (
<Tabs.Trigger
<Chips.Toggle
key={fil._key}
value={fil.navn}
className={cl(
"flex h-8 items-center justify-center rounded-full text-base ring-inset ring-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-800",
{
"gap-1 bg-gray-200 pr-3 pl-[10px] ring-2 hover:bg-gray-100":
activeExample === fil.navn,
"bg-white px-3 ring-1 hover:bg-gray-100":
activeExample !== fil.navn,
}
)}
selected={active === fil.navn}
onClick={() => setActiveExample(fil.navn)}
>
{activeExample === fil.navn && (
<SuccessStroke aria-hidden className="h-6 w-6" />
)}
{fixName(fil.navn)}
</Tabs.Trigger>
</Chips.Toggle>
);
})}
</Tabs.List>
{node.dir.filer.map((fil) => {
return (
<Tabs.Content key={fil._key} value={fil.navn} tabIndex={-1}>
{fil?.description && (
<BodyLong className="mb-2">{fil.description}</BodyLong>
)}
{element(
`/eksempler/${node.dir.title}/${fil.navn.replace(".tsx", "")}`,
fil.innhold
)}
</Tabs.Content>
);
})}
</Tabs.Root>
</>
</Chips>
</div>
{node.dir.filer.map((fil) => {
return (
<div
key={fil._key}
className={cl({
visible: active === fil.navn,
hidden: active !== fil.navn,
})}
>
{fil?.description && (
<BodyLong className="mb-2">{fil.description}</BodyLong>
)}
{element(
`/eksempler/${node.dir.title}/${fil.navn.replace(".tsx", "")}`,
fil.innhold
)}
</div>
);
})}
</div>
);
};
@@ -1,6 +1,6 @@
/* eslint-disable @next/next/no-img-element */
import { SanityT, urlFor } from "@/lib";
import { Heading } from "@navikt/ds-react";
import { Heading, Tag } from "@navikt/ds-react";
import Nextlink from "next/link";
import cl from "classnames";
@@ -8,25 +8,24 @@ const getStatus = (status?: SanityT.Schema.komponent_artikkel["status"]) => {
switch (status?.tag) {
case "beta":
return (
<span className="ml-2 rounded bg-purple-400 px-1 text-sm text-white">
<Tag variant="alt1" size="small">
Beta
</span>
</Tag>
);
case "new":
return (
<span className="ml-2 rounded bg-green-300 px-1 text-sm text-gray-900">
Ready
</span>
<Tag variant="info" size="small">
New
</Tag>
);
case "ready":
return null;
case "deprecated":
return (
<span className="ml-2 rounded bg-gray-200 px-1 text-sm text-gray-900">
<Tag variant="neutral" size="small">
Deprecated
</span>
</Tag>
);
default:
return null;
}
@@ -61,7 +60,7 @@ const ComponentOverview = ({
<ul className="component-card-grid">
{sorted.map((x) => (
<li key={x._id}>
<div className="group relative rounded shadow-small ring-1 ring-gray-900/10 focus-within:shadow-focus hover:shadow-medium">
<div className="shadow-small focus-within:shadow-focus hover:shadow-medium group relative rounded ring-1 ring-gray-900/10">
<div
className={cl(
"flex max-h-64 items-center justify-center overflow-hidden rounded-t bg-gray-50",
@@ -87,7 +86,7 @@ const ComponentOverview = ({
aria-hidden
xmlns="http://www.w3.org/2000/svg"
viewBox="-11.5 -10.23174 23 20.46348"
className="aspect-[1.25/1] h-auto max-h-64 min-h-40 p-4"
className="min-h-40 aspect-[1.25/1] h-auto max-h-64 p-4"
>
<title>React Logo</title>
<circle
@@ -152,7 +152,7 @@ export function TableOfContents({
</Heading>
<div
id="toc-scroll"
className="flex max-h-[80vh] flex-col overflow-y-scroll"
className="flex max-h-[80vh] flex-col overflow-y-auto"
>
<nav aria-labelledby="toc-heading">
<ul>
@@ -172,7 +172,7 @@ const Feedback = ({
<Button
variant="secondary"
className={cl({
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-2 ring-inset ring-deepblue-800 focus-visible:shadow-focus focus-visible:ring-1 focus-visible:ring-white":
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-deepblue-800 focus-visible:shadow-focus ring-2 ring-inset focus-visible:ring-1 focus-visible:ring-white":
activeState === HelpfulArticleEnum.JA,
})}
onClick={() =>
@@ -186,7 +186,7 @@ const Feedback = ({
<Button
variant="secondary"
className={cl({
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-2 ring-inset ring-deepblue-800 focus-visible:shadow-focus focus-visible:ring-1 focus-visible:ring-white":
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-deepblue-800 focus-visible:shadow-focus ring-2 ring-inset focus-visible:ring-1 focus-visible:ring-white":
activeState === HelpfulArticleEnum.NEI,
})}
onClick={() =>
@@ -200,7 +200,7 @@ const Feedback = ({
<Button
variant="secondary"
className={cl({
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-2 ring-inset ring-deepblue-800 focus-visible:shadow-focus focus-visible:ring-1 focus-visible:ring-white":
"override-secondary-button-active bg-deepblue-800 text-text-inverted ring-deepblue-800 focus-visible:shadow-focus ring-2 ring-inset focus-visible:ring-1 focus-visible:ring-white":
activeState === HelpfulArticleEnum.MISC,
})}
onClick={() =>
@@ -215,7 +215,7 @@ const Feedback = ({
{activeState !== null && (
<form
className={cl(
"mt-4 flex w-full max-w-sm animate-fadeIn flex-col gap-4",
"animate-fadeIn mt-4 flex w-full max-w-sm flex-col gap-4",
{
"override-primary-button": akselFeedback,
}
@@ -229,6 +229,7 @@ const Feedback = ({
onChange={(e) => setTextValue(e.target.value)}
maxLength={600}
minRows={3}
description="Ikke skriv inn navn eller andre personopplysninger"
/>
<Button className="mr-auto" onClick={handleSend}>
Send inn svar
@@ -1,101 +0,0 @@
import { test, expect } from "@playwright/test";
test("Navigate tema and prinsipp", async ({ page }) => {
await page.goto("http://localhost:3000/tema/innholdsarbeid");
await page.getByRole("link", { name: "Temaer" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema");
await page.getByRole("link", { name: "Brukerinnsikt 16 Artikler" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema/brukerinnsikt");
await page.getByRole("link", { name: "Temaer" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema");
await page
.getByRole("link", { name: "Universell utforming 26 Artikler" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/tema/universell-utforming"
);
await page.getByRole("link", { name: "Temaer" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema");
await page.getByRole("link", { name: "Interne flater 3 Artikler" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema/interne-flater");
await page.getByRole("link", { name: "Temaer" }).click();
await expect(page).toHaveURL("http://localhost:3000/tema");
await page.getByRole("link", { name: "Aksel" }).click();
await expect(page).toHaveURL("http://localhost:3000/");
await page.getByRole("link", { name: "Utforsk alle prinsippene" }).click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse"
);
await page
.getByRole("link", { name: "1. Jeg får tillit og muligheter" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse/jeg-far-tillit-og-muligheter"
);
await page
.getByRole("link", { name: "Prinsipper for brukeropplevelse" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse"
);
await page
.getByRole("heading", {
name: "2. Jeg blir møtt på min situasjon og mine behov",
})
.getByRole("link", {
name: "2. Jeg blir møtt på min situasjon og mine behov",
})
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse/jeg-blir-mott-pa-min-situasjon-og-behov"
);
await page
.getByRole("link", { name: "Prinsipper for brukeropplevelse" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse"
);
await page
.getByRole("heading", { name: "3. NAV er min støttespiller" })
.getByRole("link", { name: "3. NAV er min støttespiller" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse/nav-er-min-stottespiller"
);
await page
.getByRole("link", { name: "Prinsipper for brukeropplevelse" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse"
);
await page
.getByRole("heading", { name: "4. Jeg blir inkludert" })
.getByRole("link", { name: "4. Jeg blir inkludert" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse/jeg-blir-inkludert"
);
await page
.getByRole("link", { name: "Prinsipper for brukeropplevelse" })
.click();
await expect(page).toHaveURL(
"http://localhost:3000/prinsipper/brukeropplevelse"
);
});
-65
View File
@@ -1,65 +0,0 @@
import { test, expect } from "@playwright/test";
test("Test sandbox-examples", async ({ page }) => {
await page.goto("http://localhost:3000/designsystem/komponenter/checkbox");
await page.frameLocator("#Kode-eksempler").getByLabel("Bakerst").check();
await page.frameLocator("#Kode-eksempler").getByLabel("Midterst").check();
await page.frameLocator("#Kode-eksempler").getByLabel("Fremst").check();
await page.getByRole("tab", { name: "Small" }).click();
await page.frameLocator("#Kode-eksempler").getByLabel("Midterst").check();
await page.getByRole("tab", { name: "Description" }).click();
await page.frameLocator("#Kode-eksempler").getByLabel("Midterst").check();
await page.getByRole("tab", { name: "Indeterminate" }).click();
await page.frameLocator("#Kode-eksempler").getByLabel("Fremst").check();
await page.frameLocator("#Kode-eksempler").getByLabel("Bakerst").check();
await page.frameLocator("#Kode-eksempler").getByLabel("Midterst").check();
await page.getByRole("link", { name: "Button" }).click();
await expect(page).toHaveURL(
"http://localhost:3000/designsystem/komponenter/button"
);
await page
.frameLocator("#Kode-eksempler")
.getByRole("button", { name: "Primary" })
.click();
await page.getByRole("tab", { name: "Icon" }).click();
await page
.frameLocator("#Kode-eksempler")
.getByRole("button", { name: "Rediger" })
.click();
await page.getByRole("link", { name: "Search" }).click();
await expect(page).toHaveURL(
"http://localhost:3000/designsystem/komponenter/search"
);
await page
.frameLocator("#Kode-eksempler")
.getByLabel("Søk alle NAV sine sider")
.click();
await page
.frameLocator("#Kode-eksempler")
.getByLabel("Søk alle NAV sine sider")
.fill("test");
await page
.frameLocator("#Kode-eksempler")
.getByRole("button", { name: "Søk" })
.click();
});
+14
View File
@@ -0,0 +1,14 @@
[
"/",
"/designsystem",
"/tema",
"/blogg",
"/designsystem/komponenter/tag",
"/designsystem/side/migrering",
"/designsystem/side/ikoner",
"/tema/innholdsarbeid",
"/tema/brukerinnsikt",
"/tema/universell-utforming",
"/artikkel/innholdsstrategi-for-nav-no?tema=innholdsarbeid",
"/blogg/ny-forside-pa-navno"
]
@@ -1,31 +0,0 @@
import dotenv from "dotenv";
import fs from "fs";
import { getAllPages } from "../sanity/santiy";
dotenv.config();
const generateUrls = async () => {
const pages = await getAllPages(process.env.SANITY_WRITE_KEY);
const formated = pages.map((x) => `/${x}`);
console.log(`Pages ${formated.length}`);
if (formated.length < 70) {
throw new Error(
"generateUrls found less than 70 pages. Did fetching remote pages fail?"
);
}
fs.writeFile(
"./e2e/test-urls.json",
JSON.stringify(pages.map((x) => `/${x}`)),
(err) => {
if (err) {
console.error(err);
return;
}
}
);
};
generateUrls();
+23
View File
@@ -117,6 +117,28 @@ Linker eller dropdowns med linker. Maks dybde på 2 dropdowns er støttet. Sider
>;
}
/**
* Redirect
*/
interface redirect extends Sanity.Document {
_type: "redirect";
/**
* Fra - `String`
*/
source?: string;
/**
* Til - `String`
*/
destination?: string;
/**
* Permanent - `Boolean`
*/
permanent?: boolean;
}
/**
* Fargekategori
*/
@@ -1546,6 +1568,7 @@ Husk å legge denne til i menyen også, hvis ikke blir den bare tilgjengelig via
| vk_frontpage
| editor
| navigation
| redirect
| ds_color_categories
| ds_tokens
| kode_eksempler_fil
+30
View File
@@ -0,0 +1,30 @@
import { getClient } from "@/sanity-client";
import { NextResponse } from "next/server";
export async function middleware(req) {
try {
const redirects = await getClient().fetch(`
*[_type == 'redirect'] {
destination,
source,
permanent
}
`);
const redirect = redirects.find(
(redirect) =>
decodeURIComponent(req.nextUrl.pathname) === redirect?.source
);
if (redirect) {
if (redirect.destination.startsWith("http")) {
return NextResponse.redirect(new URL(redirect.destination));
}
return NextResponse.redirect(new URL(redirect.destination, req.url));
}
return NextResponse.next();
} catch (e) {
return NextResponse.next();
}
}
+1 -2
View File
@@ -8,14 +8,13 @@
"dev": "next",
"build": "next build",
"start": "next start",
"e2e": "yarn generate:testurls && yarn build && yarn start-server-and-test start http://localhost:3000 playwright",
"e2e": "yarn build && yarn start-server-and-test start http://localhost:3000 playwright",
"update:colors": "npx ts-node --project tsconfig.tsnode.json ./lib/update-documents/update-color-examples",
"update:props": "npx ts-node --project tsconfig.tsnode.json ./lib/update-documents/update-props",
"update:tokens": "npx ts-node --project tsconfig.tsnode.json ./lib/update-documents/tokens",
"update:search": "npx ts-node --project tsconfig.tsnode.json ./lib/search",
"update:eksempler": "npx ts-node --project tsconfig.tsnode.json ./lib/examples/update-sanity",
"update:sanity": "yarn update:tokens && yarn update:props && yarn update:colors && yarn update:eksempler",
"generate:testurls": "npx ts-node --project tsconfig.tsnode.json ./lib/testing/generateTestUrls",
"analyze": "cross-env ANALYZE=true next build",
"test": "jest",
"playwright": "playwright test",
@@ -19,9 +19,7 @@ export default async function handler(
return;
}
const card = `&name=${data.message}&desc=**Bruker:** %0A ${
data.user ? data.user : "Ukjent"
} %0A **Side:** %0A ${data.url}`;
const card = `&name=${data.message}&desc=**Bruker:** %0A **Side:** %0A ${data.url}`;
const url = `https://api.trello.com/1/cards?idList=619cc5721924df5816bcc45f&idLabels=${labels}&key=${trelloKey}&token=${trelloToken}${card}`;
const postRes = await fetch(url, {
@@ -0,0 +1,52 @@
import { Chips } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
import { useState } from "react";
const Example = () => {
const options = ["Norsk", "Dansk", "Svensk", "Tysk", "Spansk"];
const [selected, setSelected] = useState(["Dansk", "Svensk"]);
const [filter, setFilter] = useState(options);
return (
<div className="grid gap-4">
<Chips>
{options.map((c) => (
<Chips.Toggle
selected={selected.includes(c)}
key={c}
onClick={() =>
setSelected(
selected.includes(c)
? selected.filter((x) => x !== c)
: [...selected, c]
)
}
>
{c}
</Chips.Toggle>
))}
</Chips>
<Chips>
{filter.map((c) => (
<Chips.Removable
key={c}
onClick={() =>
setFilter((x) =>
x.length === 1 ? options : x.filter((y) => y !== c)
)
}
>
{c}
</Chips.Removable>
))}
</Chips>
</div>
);
};
export default withDsExample(Example);
export const args = {
index: 0,
};
@@ -0,0 +1,32 @@
import { Chips } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
import { useState } from "react";
const Example = () => {
const options = ["Norsk", "Dansk", "Svensk", "Tysk", "Spansk"];
const [filter, setFilter] = useState(options);
return (
<Chips>
{filter.map((c) => (
<Chips.Removable
key={c}
onClick={() =>
setFilter((x) =>
x.length === 1 ? options : x.filter((y) => y !== c)
)
}
>
{c}
</Chips.Removable>
))}
</Chips>
);
};
export default withDsExample(Example);
export const args = {
index: 2,
};
@@ -0,0 +1,52 @@
import { Chips } from "@navikt/ds-react";
import { withDsExample } from "components/website-modules/examples/withDsExample";
import { useState } from "react";
const Example = () => {
const options = ["Norsk", "Dansk", "Svensk", "Tysk", "Spansk"];
const [selected, setSelected] = useState(["Dansk", "Svensk"]);
const [filter, setFilter] = useState(options);
return (
<div className="grid gap-4">
<Chips size="small">
{options.map((c) => (
<Chips.Toggle
selected={selected.includes(c)}
key={c}
onClick={() =>
setSelected(
selected.includes(c)
? selected.filter((x) => x !== c)
: [...selected, c]
)
}
>
{c}
</Chips.Toggle>
))}
</Chips>
<Chips size="small">
{filter.map((c) => (
<Chips.Removable
key={c}
onClick={() =>
setFilter((x) =>
x.length === 1 ? options : x.filter((y) => y !== c)
)
}
>
{c}
</Chips.Removable>
))}
</Chips>
</div>
);
};
export default withDsExample(Example);
export const args = {
index: 3,
};
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more