Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] e5ecd66a57 Version Packages (#2732)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-19 10:11:11 +01:00
Ken 78817fef45 [Aksel.nav.no] Uxtweaks-card på forside (#2735)
* 🎉 Uxtweaks-panel på forsiden til Aksel

* 🎨 Flyttet uxtweaks-box til under god-praksis panel

* 🎨 Panel har nå aside med role presentation

Dette gjør at skjermlesere hopper over 'complementary' og kommer rett til heading

* 💄 Oppdatert heading-tekst
2024-02-16 16:38:46 +01:00
Halvor Haugan 38357b1a2e Oppdatering av eksemplene på Skeleton (#2734) 2024-02-16 13:14:43 +01:00
KenandHalvor Haugan 65c17db2e5 Oppdatert Modal Context til nytt createContext-api (#2714)
* ♻️ Oppdatert hvordan ModalContext leveres

* 🏷️ onBeforeClose krever nå boolean

* ♻️ Undo rename av Modal.types for å unngå conflict

* 📝 Changeset

* 🔥 Fjernet unødvendig story-oppdatering

* 🐛 Fikset forwardRef i createContext

I tilfellene der ref blir sendt inn som en context-value, ble den tidligere slettet av React (native oppførsel)
ForwardRef lar oss beholde ref og sende den inn som context-value

* Endring av strict

* null -> undefined

---------

Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-02-15 19:40:46 +01:00
Halvor Haugan 59e88773d1 🎨 prettier 2024-02-15 09:37:23 +01:00
Ken f175d0f781 🧪 ToggleGroup test (#2728) 2024-02-14 16:50:09 +01:00
Ken 18419f7d98 Stories refactor (delvis) for chromatic (#2721)
* ♻️ Oppdatert Accordion-story

* ♻️ Oppdatert Chat-story

* 🐛 Chromatic story var slått av

* 🎉 ♻️ Typo-stories oppdatert for chromatic

* ♻️ Tokens-stories

* ♻️ Oppdatert Table-stories
2024-02-14 09:43:10 +01:00
Ken f63a7ec36d 📄 Oppdatert License timestamp fra 2023 -> 2024 2024-02-09 09:44:55 +01:00
Halvor Haugan 107489bd3f [aksel.nav.no] 🔥 Fjern noAmps fra InlineCode (#2719) 2024-02-08 15:11:43 +01:00
github-actions[bot]andgithub-actions[bot] 792962592f Version Packages (#2720)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-08 13:08:21 +01:00
Ken 4a02a1987c Deprecate Panel (#2712)
* ⚰️ Markert Panel som deprecated

* 📝 Changeset

* 📝 JSDOC var feil for border på Box

* 🐛 📝 JSDoc manglet border-radius
2024-02-08 11:46:20 +01:00
Ken 700943b4a5 [ci skip] ♻️ Refaktorert Button-story 2024-02-07 20:31:25 +01:00
Ken 521c982457 🚚 Oppdatert imports til direkteimports og ikke fra root-index (#2715) 2024-02-07 16:17:17 +01:00
Ken 8c17099087 🔥 Fjernet hyphen på blogg-headings (#2711)
* 🔥 Fjernet hyphen på blogg-headings

* 🎨 Bare hyphens på mobil
2024-02-07 13:42:58 +01:00
Ken 29241a7153 ⚡ Refaktorert alert-stories for mindre chromatic-tester (#2709)
* ⚡ refaktorert alert-stories for mindre chromatic-tester

* 🐛 Fikset imports
2024-02-07 13:42:26 +01:00
github-actions[bot]andgithub-actions[bot] 7e4ee40a7f Version Packages (#2708)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-07 12:26:07 +01:00
Halvor Haugan 2be97970d6 Date/MonthPicker: Justeringer JSDOC og eksempel-desc (#2704) 2024-02-07 09:42:37 +01:00
Halvor Haugan 5b3c1f33c4 Eksterne typer kommer nå med i docgen (#2705) 2024-02-07 09:30:27 +01:00
KenandHalvor Haugan f44ff44410 Bytter ut FloatingPortal med egen implementasjon (#2697)
* 🎉 Egen Portal-komponent

* 🎨 Portal og Overlay exportert i root

* ♻️ Tooltip bruker egen Portal

* 📝 Changeset

* 🔥 Fjernet ID på Portal-element

* 🎉 Ny type WithAsChild

* ♻️ Mer robust sjekk for conditional elements i WithAsChild

* 📝 WithAsChild JSDOC

* 📝 WithAsChild JSDOC 2

* Update .changeset/lovely-points-heal.md

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

* ♻️ Fikset kommentarer fra git-pr

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-02-06 18:02:00 +01:00
Ken 855669538e ⚡ Kjører bare release-workflow når nødvendige filer er endret (#2701) 2024-02-06 18:01:43 +01:00
github-actions[bot]andgithub-actions[bot] 3d830dc123 Version Packages (#2703)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-06 13:56:46 +01:00
Ken dcca2a9560 Expansioncard padding top (#2699)
* 🎉 Justert padding for expansioncard small

* 🎉 Justert padding for expansioncard small
2024-02-06 13:10:08 +01:00
Ken 26fb028f07 ⚡ Filterer ut chromatic-snapshots for build-files (#2700) 2024-02-06 11:59:51 +01:00
Halvor Haugan d928769d6f [aksel.nav.no] Fiks for teksting i video 2024-02-05 14:55:22 +01:00
Julian Nymark e96d33d13e 👷 update permissions (removed too much) 2024-02-05 14:35:38 +01:00
Julian Nymark 00ebe1c18b 👷 update workflow (post merge) 2024-02-05 14:31:51 +01:00
Julian NymarkandKen 53be894ad0 Gp popular articles (#2647)
* ❇️ new schema for view tracking

* 🚧 wip for article views schema

* 🚧 wip on schema

* 🚧 schema changes for ArticleViews

* 🚧 run prettier with new config

* 🚧 deno script to update article views

* 🚧 working sanity document updates with bogus view data

* 🚧 helper to delete all article_views

* 🚧 add views_day

* ✨ working update for views on all gp articles

* 💄 eye icon for page views in sanity studio

* ♻️ cleanup from PR review / huddle

* 🚧 improvements to updateViews script

* 🚧 seemingly healthy queries (sometimes proxy gives stale data?)

* 👷 add .github/workflow for update views

* 👷 update .github/workflow for update views

* 👷 add env var in workflow

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* ♻️ refactor from PR comments

* 👷 update workflow

* 🐛 correct paths to changelog script

* 🐛 remove unused ?? -1

* 👷 dockerfile and naisjob.yml

* 👷 yml variable formatting

* 👷 yml update

* 👷 yml variable formatting

* 🔨 .prettierignore for all yml/yaml starting with nais

* 👷 unique name for naisjob

* 👷 new nais yaml

* 👷 yml variable formatting

* 👷 remove workflow, the workflow is developed in main

* 👷 update yml

* 🚧 update naisjob yml

* 🚧 update naisjob yml

* 🚧 update Dockerfile

* 🚧 update Dockerfile (permissions)

* 🚧 update Dockerfile

* 🚧 update Dockerfile

* 🚧 update Dockerfile

* 👷 decouple config code from rest of repo (more duplication, more robustness), build with correct docker context

* 👷 cache deps in dockerfile on build-time (no need to fetch at runtime and add to pod AccessPolicy)

* 👷 relative path might work, test Dockerfile live as it works fine locally... buildx?!

* 👷 test github action docker context tweak

* 👷 test script command entrypoint (workdir is not project root)

* 👷 add AccessPolicy for reops-proxy

* 👷 better github workflow naming

* 👷 refactor _and_ optimize the dockerfile

* 👷 new docker command path for nais yml

* Update scripts/article-views/queries.ts

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

* 👷 do not add rows if no data

* 🐛 fix sanity query) + now delete + update in single transaction

---------

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-02-05 14:29:14 +01:00
Julian Nymark 614946097e [aksel.nav.no] ♿ forbedret Video-modul (#2693) 2024-02-05 14:09:43 +01:00
github-actions[bot]andgithub-actions[bot] 74ac20ea7c Version Packages (#2696)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-02 17:54:34 +01:00
Ken d299137774 🐛 Bleed håndtere reflectivePadding og marginInline Full riktig (#2695) 2024-02-02 17:52:45 +01:00
Julian Nymark 304f6d6a12 👷 test workflow changes (update-article-views) 2024-02-02 15:44:15 +01:00
Halvor Haugan bf6952c2b2 Oppdatering av eksempler på ReadMore (#2691) 2024-02-02 09:59:04 +01:00
github-actions[bot]andgithub-actions[bot] 7705dd4ae5 Version Packages (#2692)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-01 14:37:43 +01:00
Halvor Haugan d9ce1299d9 🐛 Textarea: maxLength virker nå med onChange uten value (#2690) 2024-02-01 14:29:56 +01:00
Julian Nymark 0890eed59e 👷 better github workflow naming 2024-02-01 14:24:10 +01:00
Ken dbd7150762 🐛 Sletting av innholdstyper i CMS brakk deploy av Aksel, nye /gp-sider gir nå 404 uten riktig data 2024-02-01 10:14:24 +01:00
Ken 58933e2584 🎨 Bedre kopi visning av designtokens + refererer ikke til nav-red lengre (#2689) 2024-02-01 09:17:21 +01:00
Julian Nymark 86279919c8 👷 test github action docker context tweak 2024-01-31 17:46:37 +01:00
github-actions[bot]andgithub-actions[bot] 8866572e69 Version Packages (#2686)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-31 11:33:47 +01:00
a1accdbd95 ✨ suggest some .vscode workspace settings.json / extensions.json (#2664)
* ✨ suggest some workspace settigs / extensions.json

* 🚚 css-custom-properties -> css-variable-autocomplete

* Update .vscode/settings.json

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

* 🔥 remove some extensions

* ✨ add some settings

* 🔥 remove wrongly added files to branch

* Update .vscode/settings.json

* Update .vscode/settings.json

* Update .vscode/settings.json

* 🎨 Fjernet noen uødvendige extensions

* 🎨 La til lokal TS-bruk

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Ken <ken.aleksander@gmail.com>
2024-01-31 10:47:11 +01:00
Ken fdfdebfa99 🐛 Sentrering av checkmark i Checkbox (#2682)
* 🐛 Checkmark er nå alltid sentrert i checkbox

* 📝 Changeset

* Update @navikt/core/css/form/radio-checkbox.css
2024-01-31 10:47:02 +01:00
Halvor Haugan 56abe568e2 Textarea: debounce ResizeObserver for å unngå feilmelding (#2685) 2024-01-31 10:46:29 +01:00
Halvor Haugan 34acbb87a5 🐛 Textarea: Sett riktig høyde når brukt i Modal + StrictMode (#2679) 2024-01-31 10:35:43 +01:00
Ken fd479e3160 🐞 Datepicker Day Button (#2678)
* 🐛 Fikset Datepicker Day-button

* 📝 Changeset

* 🔥 Fjernet ubrukt referanse til DayButton i Datepicker
2024-01-31 10:30:37 +01:00
github-actions[bot]andgithub-actions[bot] 03ca21b4f6 Version Packages (#2683)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-30 22:09:38 +01:00
Vilde Roksand Fauchald e7804297bf Eksponer variabelen "role" i Search-komponenten (#2667)
* expose role variable so its possible to change it

* changeset
2024-01-30 22:07:49 +01:00
github-actions[bot]andgithub-actions[bot] a246c9d707 Version Packages (#2681)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-30 16:49:36 +01:00
d3fb3d4d96 🔴 Støtte for høykontrast i komponenter (#2680)
* 💄 Accordion tilpasset høykontrast

* 💄 Alert tilpasset høykontrast

* Legg til tokens for high contrast border og -border width. Tanken er å kunne gjenbruke på tvers av komponenter.

* 💄 Tooltip tilpasset høykontrast

* 💄 ToggleGroup tilpasset høykontrast

* 💄 bedre fargebruk på Accordion

* 🐛 Fjernet deprecated systemfarger

* 💄 alert-farger

* 💄 chat tilpasset høykontrast

* ♿ button css for high contrast

* ♿ Radio og checkbox høykontrast

* ♿ Chips høykontrast

* ♿ ConfirmationPanel høykontrast

* ♿ Tab høykontrast

* ♿ Tabs høykontrast

* ♿ Textfield høykontrast

* ♿ Textarea høykontrast

* ♿ Skeleton høykontrast

* ♿ Link høykontrast

* ♿ LinkPanel høykontrast

* ♿ ExpansionCard høykontrast

* ♿ high contrast CSS for select & datepicker / monthpicker

* ♿ Loader høykontrast

* ♿ dropdown høykontrast

* ♿ HelpText høykontrast

* ♿ Popover høykontrast

* ♿ Switch høykontrast

* ♿ high contrast CSS for ReadMore

* ♿ high contrast CSS for CopyButton

* ♿ high contrast CSS for InternalHeader

* ♿ Modal highcontrast

* ♿ Search høykontrast

* 💄 Timeline tilpasset høykontrast

* ♿ high contrast CSS for Stepper

* ♿ Combobox høykontrast

* Fix "diamond"-shape that should be an "arrow" in Timeline popover

* ♿ high contrast CSS for Switch (small tweaks)

* ♿ high contrast CSS for Switch (remove unused css)

* 🐛 Riktige fokusmarkeringer på radio/checkbox

* chips

* 🐛 Fikset ToggleGroup høykontrast

* 📝 Changeset2

* 🔥 Fjernet high-contrast token fra ds-tokens

* ♿ high contrast CSS for checkbox (tweaks)

* 🔥 Fjernet disabled-prop på radio-story

* Timeline focus in high contrast mode

* 🐛 Fikset fokusmarkering på radio/checkbox

* 🐛 Highlighting av outline var feil satt på ToggleGroup

* 📝 Highlight -> highlight

---------

Co-authored-by: Vegard Haugstvedt <it.vegard@gmail.com>
Co-authored-by: Julian Nymark <julian.nymark@nav.no>
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-01-30 16:47:26 +01:00
Julian Nymark d734d3071b 👷 update workflow for chromatic (stop old running action when new is created) 2024-01-30 16:22:04 +01:00
168 changed files with 3498 additions and 1444 deletions

No files matched your search

+6
View File
@@ -11,6 +11,10 @@ on:
paths:
- "@navikt/**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
visual_regression_test:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'ignore-chromatic') }}
@@ -55,7 +59,9 @@ jobs:
exitZeroOnChanges: false
buildScriptName: "build:storybook"
onlyChanged: true
untraced: "**/package.json|yarn.lock|**/*.md"
externals: \@navikt/core/css/**/*.css
traceChanged: true
env:
STORYBOOK_GITHUB_SHA: ${{ github.sha }}
+3
View File
@@ -2,6 +2,9 @@ name: Release
on:
push:
paths:
- ".changeset/**"
- "@navikt/**"
branches:
- main
workflow_dispatch:
+3 -4
View File
@@ -1,17 +1,16 @@
name: Update article views
name: Redeploy Naisjob (update-article-views)
on:
workflow_dispatch:
jobs:
execute:
runs-on: ubuntu-latest
permissions: # TODO: remove once merged
permissions:
packages: "write"
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v3
with:
ref: gp-popular-articles # TODO: remove once merged
fetch-depth: 1
- uses: denoland/setup-deno@v1
with:
@@ -31,7 +30,7 @@ jobs:
with:
team: designsystem
push_image: true
dockerfile: scripts/article-views/Dockerfile
dockerfile: scripts/article-views/Dockerfile.update-article-views
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
- name: start naisjob prod-gcp
+2 -2
View File
@@ -1,5 +1,5 @@
**/nais-dev.yaml
**/nais-prod.yaml
**/nais*.yaml
**/nais*.yml
**/codemod/**/tests/**/*.js
node_modules
.next
+1 -1
View File
@@ -10,7 +10,7 @@ export const parameters = {
"Getting started",
"ds-react",
["Primitives"],
["Default"],
["Default", "Controls"],
"Aksel-icons",
],
},
+13
View File
@@ -0,0 +1,13 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"denoland.vscode-deno",
"esbenp.prettier-vscode",
"github.vscode-github-actions",
"ms-playwright.playwright",
"sanity-io.vscode-sanity",
"stylelint.vscode-stylelint",
"vunguyentuan.vscode-css-variables",
"bradlc.vscode-tailwindcss"
]
}
+15
View File
@@ -0,0 +1,15 @@
{
"cssVariables.lookupFiles": ["@navikt/core/css/dist/index.css"],
"eslint.problems.shortenToSingleLine": true,
"deno.enablePaths": ["scripts/article-views", "scripts/changelog"],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"deno.lint": true,
"files.watcherExclude": {
"**/.yarn/**": true,
"**/dist": true,
"**/esm": true,
"**/cjs": true
},
"playwright.reuseBrowser": true,
"typescript.tsdk": "node_modules/typescript/lib"
}
+18
View File
@@ -1,5 +1,23 @@
# @navikt/aksel-icons
## 5.18.2
## 5.18.1
## 5.18.0
## 5.17.5
## 5.17.4
## 5.17.3
## 5.17.2
## 5.17.1
## 5.17.0
## 5.16.0
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "5.16.0",
"version": "5.18.2",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+18
View File
@@ -1,5 +1,23 @@
# @navikt/aksel-stylelint
## 5.18.2
## 5.18.1
## 5.18.0
## 5.17.5
## 5.17.4
## 5.17.3
## 5.17.2
## 5.17.1
## 5.17.0
## 5.16.0
## 5.15.1
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "5.16.0",
"version": "5.18.2",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
"repository": {
@@ -35,8 +35,8 @@
"dev": "yarn watch:lint"
},
"devDependencies": {
"@navikt/ds-css": "^5.16.0",
"@navikt/ds-tokens": "^5.16.0",
"@navikt/ds-css": "^5.18.2",
"@navikt/ds-tokens": "^5.18.2",
"@types/jest": "^29.0.0",
"concurrently": "7.2.1",
"copyfiles": "2.4.1",
+63
View File
@@ -1,5 +1,68 @@
# @navikt/aksel
## 5.18.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.18.2
## 5.18.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.18.1
## 5.18.0
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.18.0
## 5.17.5
### Patch Changes
- Updated dependencies [[`dcca2a956`](https://github.com/navikt/aksel/commit/dcca2a95600152cb84c3b79ba5bb3894ac63e881)]:
- @navikt/ds-css@5.17.5
## 5.17.4
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.17.4
## 5.17.3
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.17.3
## 5.17.2
### Patch Changes
- Updated dependencies [[`fdfdebfa9`](https://github.com/navikt/aksel/commit/fdfdebfa99e594099bcea7b5addde9cdabc727a7)]:
- @navikt/ds-css@5.17.2
## 5.17.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@5.17.1
## 5.17.0
### Patch Changes
- Updated dependencies [[`d3fb3d4d9`](https://github.com/navikt/aksel/commit/d3fb3d4d962fac028e7d803dc7c50315d48b4987)]:
- @navikt/ds-css@5.17.0
## 5.16.0
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "5.16.0",
"version": "5.18.2",
"description": "Aksel command line interface. Handles css-imports, codemods and more",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
"dependencies": {
"@navikt/ds-css": "5.16.0",
"@navikt/ds-css": "5.18.2",
"axios": "1.6.0",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+30
View File
@@ -1,5 +1,35 @@
# @navikt/ds-css
## 5.18.2
## 5.18.1
## 5.18.0
## 5.17.5
### Patch Changes
- ExpansionCard: Padding-top for Content er justert opp 8px (medium) og 4px (small) ([#2699](https://github.com/navikt/aksel/pull/2699))
## 5.17.4
## 5.17.3
## 5.17.2
### Patch Changes
- Checkbox: I noen edgecaser ved bruk av shadow-dom ble ikke checkmark sentert i Checkbox ([#2682](https://github.com/navikt/aksel/pull/2682))
## 5.17.1
## 5.17.0
### Minor Changes
- Høykontrast: Komponenter støtter nå standard høykontrast-modus på Windows ([#2680](https://github.com/navikt/aksel/pull/2680))
## 5.16.0
### Minor Changes
+16 -1
View File
@@ -74,7 +74,8 @@
}
.navds-accordion__header:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: 3px;
box-shadow: var(--a-shadow-focus);
border-radius: var(--a-border-radius-large);
}
@@ -185,3 +186,17 @@
transform: translateY(0);
}
}
@media (forced-colors: active) {
.navds-accordion__header {
border: 1px solid buttonborder;
background-color: canvas;
color: canvastext;
}
.navds-accordion__header:hover {
background-color: canvas;
border: 1px solid highlight;
color: highlight;
}
}
+10 -2
View File
@@ -33,7 +33,7 @@
font-size: 1.5rem;
align-self: flex-start;
height: var(--a-font-line-height-xlarge);
background: radial-gradient(circle, var(--a-surface-default) 50%, 0, transparent);
background: radial-gradient(circle, var(--a-surface-default) 47%, 0, transparent);
}
.navds-alert--small > .navds-alert__icon {
@@ -64,7 +64,7 @@
}
.navds-alert--warning > .navds-alert__icon {
background: radial-gradient(circle at 50% 57%, var(--a-surface-default) 32%, 0, transparent);
background: radial-gradient(circle at 50% 57%, var(--a-surface-default) 30%, 0, transparent);
color: var(--ac-alert-icon-warning-color, var(--a-icon-warning));
}
@@ -99,3 +99,11 @@
flex-flow: row nowrap;
justify-content: flex-end;
}
@media (forced-colors: active) {
.navds-alert {
border: 1px solid canvastext;
background-color: canvas;
color: canvastext;
}
}
+53 -1
View File
@@ -81,7 +81,8 @@
}
.navds-button:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
}
@@ -129,6 +130,16 @@
background-color: var(--ac-button-primary-bg, var(--__ac-button-primary-bg, var(--a-surface-action)));
color: var(--ac-button-primary-text, var(--__ac-button-primary-text, var(--a-text-on-action)));
}
@media (forced-colors: active) {
.navds-button.navds-button--primary {
background-color: highlight;
color: highlighttext;
}
.navds-button.navds-button--primary span {
forced-color-adjust: none;
}
}
.navds-button--primary:hover {
background-color: var(--ac-button-primary-hover-bg, var(--__ac-button-primary-hover-bg, var(--a-surface-action-hover)));
@@ -139,10 +150,17 @@
}
.navds-button--primary:focus-visible {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
inset 0 0 0 1px var(--ac-button-primary-focus-border, var(--__ac-button-primary-focus-border, var(--a-surface-default))),
var(--a-shadow-focus);
}
@media (forced-colors: active) {
.navds-button--primary:focus-visible {
box-shadow: none;
}
}
@supports not selector(:focus-visible) {
.navds-button--primary:focus {
@@ -478,6 +496,7 @@
opacity: 0.3;
}
/* Loader overrides */
.navds-button .navds-loader .navds-loader__foreground {
stroke: var(--ac-button-loader-stroke, currentColor);
}
@@ -486,3 +505,36 @@
.navds-button--danger .navds-loader .navds-loader__background {
stroke: var(--ac-button-primary-loader-stroke-bg, rgb(255 255 255 / 0.3));
}
@media (forced-colors: active) {
.navds-button:not(.navds-button--loading):where(:disabled, .navds-button--disabled) {
opacity: 1;
color: GrayText;
}
.navds-button {
border: 1px solid transparent;
color: ButtonText;
background-color: ButtonFace;
}
.navds-button:hover {
background-color: highlighttext;
border-color: highlight;
color: highlight;
box-shadow: none;
}
.navds-button:hover span {
forced-color-adjust: none;
}
.navds-button .navds-loader .navds-loader__foreground {
stroke: canvas;
}
.navds-button--primary .navds-loader .navds-loader__background,
.navds-button--danger .navds-loader .navds-loader__background {
stroke: canvastext;
}
}
+13
View File
@@ -105,3 +105,16 @@
.navds-chat--right .navds-chat__top-text--left {
align-self: flex-start;
}
@media (forced-colors: active) {
.navds-chat__bubble,
.navds-chat__avatar {
border: 1px solid canvastext;
background-color: canvas;
color: canvastext;
}
.navds-chat__avatar svg {
forced-color-adjust: none;
}
}
+41
View File
@@ -191,3 +191,44 @@
.navds-chips--small .navds-chips--icon-right {
padding-right: var(--a-spacing-05);
}
@media (forced-colors: active) {
.navds-chips__chip {
border: 1px solid transparent;
}
.navds-chips__chip:hover {
background-color: highlighttext;
color: highlight;
}
.navds-chips__chip:focus-visible {
outline: 2px solid transparent;
outline-offset: 2px;
}
.navds-chips__chip:where([aria-pressed="true"], :active, :hover) > span {
forced-color-adjust: none;
}
.navds-chips__toggle:active {
background-color: highlight;
color: highlighttext;
}
.navds-chips__toggle[aria-pressed="true"] {
background-color: selecteditem;
color: selecteditemtext;
border: 1px solid selecteditem;
}
.navds-chips__toggle[aria-pressed="true"]:hover {
background-color: selecteditemtext;
color: selecteditem;
}
.navds-chips__toggle[aria-pressed="true"]:active {
background-color: highlight;
color: highlighttext;
}
}
+22
View File
@@ -28,6 +28,21 @@
place-content: center;
}
@media (forced-colors: active) {
.navds-copybutton {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
.navds-copybutton.navds-copybutton:focus-visible {
box-shadow: none;
outline: 2px solid highlight;
outline-offset: 2px;
}
}
.navds-copybutton--icon-right {
--__ac-copybutton-padding: var(--a-spacing-3) var(--a-spacing-4) var(--a-spacing-3) var(--a-spacing-5);
}
@@ -201,3 +216,10 @@
cursor: not-allowed;
opacity: 0.3;
}
@media (forced-colors: active) {
.navds-copybutton:where(:disabled) {
opacity: 1;
border-color: GrayText;
}
}
+15 -2
View File
@@ -58,13 +58,15 @@
}
.navds-dropdown__item:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: -2px;
box-shadow: inset 0 0 0 2px var(--a-border-focus);
}
@supports not selector(:focus-visible) {
.navds-dropdown__item:focus {
outline: none;
outline: 2px solid transparent;
outline-offset: -2px;
box-shadow: inset 0 0 0 2px var(--a-border-focus);
}
}
@@ -75,3 +77,14 @@
background: transparent;
cursor: not-allowed;
}
@media (forced-colors: active) {
.navds-dropdown__item:hover {
color: highlight;
}
.navds-dropdown__item:disabled {
opacity: 1;
color: graytext;
}
}
+15 -3
View File
@@ -145,13 +145,13 @@
}
.navds-expansioncard__header-button:focus-visible {
outline: none;
outline: 3px solid transparent;
box-shadow: var(--a-shadow-focus);
}
@supports not selector(:focus-visible) {
.navds-expansioncard__header-button:focus {
outline: none;
outline: 3px solid transparent;
box-shadow: var(--a-shadow-focus);
}
}
@@ -177,7 +177,7 @@
* Content *
*************************/
.navds-expansioncard__content {
--__ac-expansioncard-padding-block: var(--a-spacing-3);
--__ac-expansioncard-padding-block: var(--a-spacing-5);
border-end-end-radius: var(--__ac-expansioncard-border-radius);
border-end-start-radius: var(--__ac-expansioncard-border-radius);
@@ -187,6 +187,10 @@
border-top: none;
}
.navds-expansioncard--small .navds-expansioncard__content {
--__ac-expansioncard-padding-block: var(--a-spacing-4);
}
.navds-expansioncard__content--closed {
display: none;
}
@@ -221,3 +225,11 @@
transform: translateY(0);
}
}
@media (forced-colors: active) {
.navds-expansioncard:hover {
--__ac-expansioncard-border-color: highlight;
outline: 1px solid highlight;
}
}
+57
View File
@@ -39,6 +39,8 @@
.navds-combobox--error
.navds-text-field__input:not(:hover):not(:disabled):not(.navds-combobox__wrapper-inner--virtually-unfocused):focus-within {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
0 0 0 1px var(--ac-combobox-error-border, var(--a-border-danger)),
var(--a-shadow-focus);
@@ -94,10 +96,13 @@
0 0 0 1px var(--a-surface-default) inset,
var(--a-shadow-focus);
box-shadow: var(--a-shadow-focus);
outline: 3px solid transparent;
outline-offset: 2px;
}
.navds-combobox__wrapper-inner:has(.navds-combobox__input:focus-visible).navds-combobox__wrapper-inner--virtually-unfocused {
box-shadow: none;
outline: none;
}
@supports not selector(:focus-visible) {
@@ -112,10 +117,13 @@
0 0 0 1px var(--a-surface-default) inset,
var(--a-shadow-focus);
box-shadow: var(--a-shadow-focus);
outline: 3px solid transparent;
outline-offset: 2px;
}
.navds-combobox__wrapper-inner:has(.navds-combobox__input:focus).navds-combobox__wrapper-inner--virtually-unfocused {
box-shadow: none;
outline: none;
}
}
@@ -125,10 +133,13 @@
0 0 0 1px var(--a-surface-default) inset,
var(--a-shadow-focus);
box-shadow: var(--a-shadow-focus);
outline: 3px solid transparent;
outline-offset: 2px;
}
.navds-combobox--focused .navds-combobox__wrapper-inner.navds-combobox__wrapper-inner--virtually-unfocused {
box-shadow: none;
outline: none;
}
}
@@ -368,3 +379,49 @@
gap: var(--a-spacing-1);
}
}
@media (forced-colors: active) {
.navds-combobox__button-clear:hover {
color: highlight;
}
.navds-combobox__wrapper-inner:has(.navds-combobox__input:focus-visible) {
outline-color: highlight;
}
.navds-combobox__list-item--focus,
.navds-combobox__list--with-hover
.navds-combobox__list-item:not([data-no-focus="true"], .navds-combobox__list-item--new-option):hover {
border-left-color: highlight;
color: highlight;
}
.navds-combobox__list-item[data-no-focus="true"] {
opacity: 1;
color: graytext;
}
.navds-combobox__list-item--selected {
background-color: selecteditem;
color: selecteditemtext;
}
.navds-combobox__list-item--selected > * {
forced-color-adjust: none;
}
.navds-combobox__list-item--selected.navds-combobox__list-item--focus,
.navds-combobox__list--with-hover .navds-combobox__list-item--selected:hover {
border-left-color: highlight;
color: highlight;
}
.navds-combobox__list--with-hover .navds-combobox__list-item--new-option:hover {
color: highlight;
}
.navds-combobox__list-item--new-option--focus {
outline: 2px solid highlight;
outline-offset: -3px;
}
}
@@ -8,6 +8,7 @@
background-color: var(--ac-confirmation-panel-bg, var(--a-surface-warning-subtle));
transition: background-color linear 100ms;
justify-self: stretch;
position: relative;
}
.navds-confirmation-panel__content {
@@ -23,3 +24,26 @@
border-color: var(--ac-confirmation-panel-error-border, var(--a-border-danger));
background-color: var(--ac-confirmation-panel-error-bg, var(--a-surface-danger-subtle));
}
@media (forced-colors: active) {
.navds-confirmation-panel__inner::before {
content: "";
position: absolute;
left: 0;
top: 0;
height: 100%;
border-left: 8px solid;
border-color: orange;
forced-color-adjust: none;
border-start-start-radius: calc(var(--a-border-radius-medium) - 1px);
border-end-start-radius: calc(var(--a-border-radius-medium) - 1px);
}
.navds-confirmation-panel--checked .navds-confirmation-panel__inner::before {
border-color: green;
}
.navds-confirmation-panel--error .navds-confirmation-panel__inner::before {
border-color: red;
}
}
+7
View File
@@ -42,3 +42,10 @@
.navds-fieldset:disabled > .navds-fieldset__description {
opacity: 0.3;
}
@media (forced-colors: active) {
.navds-fieldset:disabled > .navds-fieldset__legend,
.navds-fieldset:disabled > .navds-fieldset__description {
opacity: 1;
}
}
+6
View File
@@ -51,3 +51,9 @@
flex-shrink: 0;
align-self: flex-start;
}
@media (forced-colors: active) {
.navds-form-field--disabled {
opacity: 1;
}
}
+86
View File
@@ -72,6 +72,8 @@
.navds-checkbox__input:focus-visible + .navds-checkbox__label::before,
.navds-radio__input:focus-visible + .navds-radio__label::before {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)),
0 0 0 2px var(--ac-radio-checkbox-bg, var(--a-surface-default)),
@@ -81,6 +83,8 @@
@supports not selector(:focus-visible) {
.navds-checkbox__input:focus + .navds-checkbox__label::before,
.navds-radio__input:focus + .navds-radio__label::before {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
0 0 0 2px var(--ac-radio-checkbox-border, var(--a-border-default)),
0 0 0 4px var(--a-border-focus);
@@ -139,6 +143,10 @@
height: 1.5rem;
transform: translate(var(--a-spacing-1-alt));
pointer-events: none;
/* Fixed edcase where when using in shadow-dom, svg will not center */
display: flex;
align-items: center;
}
.navds-checkbox--small .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
@@ -352,3 +360,81 @@
.navds-checkbox--readonly > .navds-checkbox__input:indeterminate + .navds-checkbox__label::after {
background-color: var(--a-icon-subtle);
}
@media (forced-colors: active) {
.navds-checkbox__input:focus-visible + .navds-checkbox__label::before,
.navds-radio__input:focus-visible + .navds-radio__label::before {
outline-color: highlight;
}
.navds-checkbox,
.navds-radio {
--__ac-radio-checkbox-high-contrast-bg: field;
--__ac-radio-checkbox-high-contrast-text: fieldtext;
--__ac-radio-checkbox-high-contrast-highlight: highlight;
}
.navds-checkbox__label::before,
.navds-radio__label::before {
border: 1px solid var(--__ac-radio-checkbox-high-contrast-text);
background-color: var(--__ac-radio-checkbox-high-contrast-bg);
}
.navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
color: var(--__ac-radio-checkbox-high-contrast-text);
}
.navds-checkbox__input:indeterminate + .navds-checkbox__label::after {
background-color: var(--__ac-radio-checkbox-high-contrast-text);
}
.navds-radio__input:checked + .navds-radio__label::before {
border: 1px solid var(--__ac-radio-checkbox-high-contrast-text);
outline: 3px solid var(--__ac-radio-checkbox-high-contrast-bg);
outline-offset: -4px;
background-color: var(--__ac-radio-checkbox-high-contrast-text);
}
.navds-radio__input:checked:focus-visible + .navds-radio__label::before {
border: 1px solid var(--__ac-radio-checkbox-high-contrast-bg);
outline: 2px solid highlight;
outline-offset: 2px;
}
:not(.navds-checkbox--readonly) > .navds-checkbox__input:hover:not(:disabled) + .navds-checkbox__label,
:not(.navds-radio--readonly) > .navds-radio__input:hover:not(:disabled) + .navds-radio__label {
color: highlight;
}
.navds-checkbox__input:focus-visible + .navds-checkbox__label::before {
outline: 2px solid var(--__ac-radio-checkbox-high-contrast-highlight);
}
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label::before {
border: 1px solid var(--__ac-radio-checkbox-high-contrast-text);
background-color: var(--__ac-radio-checkbox-high-contrast-bg);
}
.navds-checkbox--readonly > .navds-checkbox__input:indeterminate + .navds-checkbox__label::after {
background-color: var(--__ac-radio-checkbox-high-contrast-text);
}
.navds-radio--readonly > .navds-radio__input:checked + .navds-radio__label::before {
border: 1px solid var(--__ac-radio-checkbox-high-contrast-text);
outline: 3px solid var(--__ac-radio-checkbox-high-contrast-bg);
outline-offset: -4px;
background-color: var(--__ac-radio-checkbox-high-contrast-text);
}
.navds-checkbox--disabled,
.navds-radio--disabled {
opacity: 1;
--__ac-radio-checkbox-high-contrast-bg: field;
--__ac-radio-checkbox-high-contrast-text: graytext;
}
:is(.navds-checkbox--disabled, .navds-radio--disabled) :is(.navds-checkbox__label, .navds-radio__label) {
color: graytext;
}
}
+12 -2
View File
@@ -99,14 +99,14 @@
}
.navds-search__button-clear:focus-visible {
outline: 2px solid transparent;
box-shadow: var(--a-shadow-focus);
outline: none;
}
@supports not selector(:focus-visible) {
.navds-search__button-clear:focus {
outline: 2px solid transparent;
box-shadow: var(--a-shadow-focus);
outline: none;
}
}
@@ -216,3 +216,13 @@
z-index: 1;
}
}
@media (forced-colors: active) {
.navds-modal {
outline: 2px solid transparent;
}
.navds-search__button-clear:hover {
color: highlight;
}
}
+21
View File
@@ -13,6 +13,21 @@
padding-right: 2rem;
}
@media (forced-colors: active) {
.navds-select__input.navds-select__input.navds-select__input {
background-color: ButtonFace;
border-color: ButtonText;
color: ButtonText;
forced-color-adjust: none;
box-shadow: none;
}
.navds-select__input.navds-select__input.navds-select__input:focus-visible {
outline: 2px solid highlight;
outline-offset: 2px;
}
}
.navds-select__input:hover {
border-color: var(--ac-select-hover-bg, var(--a-border-action));
cursor: pointer;
@@ -46,6 +61,12 @@
color: var(--ac-select-text, var(--a-text-default));
}
@media (forced-colors: active) {
.navds-select__chevron {
color: ButtonText;
}
}
.navds-form-field--small .navds-select__input {
min-height: 2rem;
padding: 0 var(--a-spacing-8) 0 var(--a-spacing-2);
+49
View File
@@ -111,6 +111,8 @@
}
.navds-switch__input:focus-visible ~ .navds-switch__track {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow:
0 0 0 1px var(--a-surface-default),
var(--a-shadow-focus);
@@ -118,6 +120,7 @@
@supports not selector(:focus-visible) {
.navds-switch__input:focus ~ .navds-switch__track {
outline: 2px solid transparent;
box-shadow:
0 0 0 1px var(--a-surface-default),
var(--a-shadow-focus);
@@ -218,3 +221,49 @@
transform: translateX(1.25rem);
}
}
@media (forced-colors: active) {
.navds-switch__input:hover ~ .navds-switch__label-wrapper,
.navds-switch__label-wrapper:hover {
color: highlight;
}
.navds-switch__thumb,
.navds-switch--readonly .navds-switch__thumb {
background-color: fieldtext;
}
.navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb,
.navds-switch--readonly .navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb {
color: field;
}
.navds-switch__track {
width: calc(2.75rem + 4px);
height: calc(1.5rem + 4px);
top: var(--a-spacing-3);
border: 2px solid fieldtext;
}
.navds-switch__input:focus-visible ~ .navds-switch__track {
outline: 2px solid highlight;
outline-offset: 2px;
}
.navds-switch--disabled:not(.navds-switch--loading) {
opacity: 1;
}
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__thumb {
background-color: graytext;
}
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__track {
border-color: graytext;
}
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__label-wrapper,
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__label-wrapper:hover {
color: graytext !important;
}
}
+9 -1
View File
@@ -45,7 +45,8 @@
}
.navds-text-field__input:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
}
@@ -101,3 +102,10 @@
.navds-text-field__input[type="search"]::-webkit-search-results-decoration {
-webkit-appearance: none;
}
@media (forced-colors: active) {
.navds-text-field__input {
background-color: field;
color: fieldtext;
}
}
+11 -2
View File
@@ -52,13 +52,15 @@
}
.navds-textarea__input:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
}
@supports not selector(:focus-visible) {
.navds-textarea__input:focus {
outline: none;
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
}
}
@@ -141,3 +143,10 @@
border-color: var(--a-border-subtle);
cursor: default;
}
@media (forced-colors: active) {
.navds-textarea__input {
background-color: field;
color: fieldtext;
}
}
+16 -2
View File
@@ -13,7 +13,7 @@
}
.navds-help-text__button:focus-visible {
outline: none;
outline: 2px solid transparent;
box-shadow:
0 0 0 1px var(--a-border-focus),
inset 0 0 0 1px var(--a-border-focus);
@@ -21,7 +21,7 @@
@supports not selector(:focus-visible) {
.navds-help-text__button:focus {
outline: none;
outline: 2px solid transparent;
box-shadow:
0 0 0 1px var(--a-border-focus),
inset 0 0 0 1px var(--a-border-focus);
@@ -64,3 +64,17 @@
display: inherit;
}
}
@media (forced-colors: active) {
.navds-help-text__button:where(:hover, :focus-visible, [aria-expanded="true"]) > .navds-help-text__icon {
display: inherit;
}
.navds-help-text__button:hover > .navds-help-text__icon {
color: highlight;
}
.navds-help-text__button:where(:hover, :focus-visible, [aria-expanded="true"]) > .navds-help-text__icon--filled {
display: none;
}
}
+21
View File
@@ -7,6 +7,27 @@
--navds-internalheader-focus: inset 0 0 0 2px var(--a-border-focus-on-inverted);
}
@media (forced-colors: active) {
.navds-internalheader {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
.navds-internalheader a:focus-visible {
box-shadow: none;
outline: 2px solid highlight;
outline-offset: 2px;
}
.navds-internalheader .navds-internalheader__button:focus-visible {
box-shadow: none;
outline: 2px solid highlight;
outline-offset: 2px;
}
}
.navds-internalheader__title {
border: none;
overflow: visible;
+4 -2
View File
@@ -18,14 +18,16 @@
}
.navds-link-panel:focus-visible {
outline: 3px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
outline: none;
}
@supports not selector(:focus-visible) {
.navds-link-panel:focus {
outline: 3px solid transparent;
outline-offset: 2px;
box-shadow: var(--a-shadow-focus);
outline: none;
}
}
+3 -3
View File
@@ -36,7 +36,7 @@
}
.navds-link:focus-visible {
outline: none;
outline: 2px solid transparent;
color: var(--ac-link-focus-text, var(--a-text-on-action));
text-decoration: none;
background-color: var(--ac-link-focus-bg, var(--a-border-focus));
@@ -45,7 +45,7 @@
@supports not selector(:focus-visible) {
.navds-link:focus {
outline: none;
outline: 2px solid transparent;
color: var(--ac-link-focus-text, var(--a-text-on-action));
text-decoration: none;
background-color: var(--ac-link-focus-bg, var(--a-border-focus));
@@ -54,7 +54,7 @@
}
.navds-link:active {
outline: none;
outline: 2px solid transparent;
color: var(--ac-link-active-text, var(--a-text-on-action));
text-decoration: none;
background-color: var(--ac-link-active-bg, var(--a-border-focus));
+6
View File
@@ -111,3 +111,9 @@
stroke-dashoffset: -120px;
}
}
@media (forced-colors: active) {
.navds-loader__background {
stroke: canvastext;
}
}
+6
View File
@@ -168,3 +168,9 @@
opacity: 1;
}
}
@media (forced-colors: active) {
.navds-modal {
outline: 2px solid transparent;
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "5.16.0",
"version": "5.18.2",
"description": "CSS for NAV Designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -27,7 +27,7 @@
"css:get-version": "node config/get-version.js"
},
"devDependencies": {
"@navikt/ds-tokens": "^5.16.0",
"@navikt/ds-tokens": "^5.18.2",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
+23
View File
@@ -59,3 +59,26 @@
border-right-color: transparent;
border-top-color: transparent;
}
@media (forced-colors: active) {
.navds-popover[data-placement^="top"] > .navds-popover__arrow {
border-left-color: canvas;
border-top-color: canvas;
}
/* prettier-ignore */
.navds-popover[data-placement^="bottom"] > .navds-popover__arrow {
border-bottom-color: canvas;
border-right-color: canvas;
}
.navds-popover[data-placement^="left"] > .navds-popover__arrow {
border-left-color: canvas;
border-bottom-color: canvas;
}
.navds-popover[data-placement^="right"] > .navds-popover__arrow {
border-right-color: canvas;
border-top-color: canvas;
}
}
+15
View File
@@ -16,6 +16,21 @@
padding: var(--a-spacing-05) var(--a-spacing-1) var(--a-spacing-05) var(--a-spacing-05);
}
@media (forced-colors: active) {
.navds-read-more__button {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
.navds-read-more__button.navds-read-more__button:focus-visible {
box-shadow: none;
outline: 2px solid highlight;
outline-offset: 2px;
}
}
.navds-read-more__button:hover {
background-color: var(--ac-read-more-hover-bg, var(--a-surface-hover));
}
+8
View File
@@ -57,3 +57,11 @@
opacity: 0.4;
}
}
@media (forced-colors: active) {
.navds-skeleton {
forced-color-adjust: none;
background-color: var(--a-surface-neutral);
animation-duration: 2s;
}
}
+35
View File
@@ -70,6 +70,41 @@
margin: calc(var(--navds-stepper-border-width) * -1) calc(var(--navds-stepper-border-width) * -1) 1.75rem;
}
@media (forced-colors: active) {
.navds-stepper__step {
background-color: ButtonFace;
color: ButtonText;
}
.navds-stepper__circle.navds-stepper__circle {
border: 0;
}
.navds-stepper__step .navds-stepper__circle {
forced-color-adjust: none;
background-color: ButtonText;
border-color: ButtonText;
color: ButtonFace;
}
.navds-stepper__step.navds-stepper__step:focus-visible {
box-shadow: none;
outline: 2px solid highlight;
outline-offset: 2px;
}
.navds-stepper__step.navds-stepper__step--active .navds-stepper__circle {
forced-color-adjust: none;
background-color: highlight;
border-color: highlighttext;
color: highlighttext;
}
.navds-stepper__line {
background-color: ButtonText;
}
}
button.navds-stepper__step {
appearance: none;
border: none;
+26
View File
@@ -264,3 +264,29 @@
.navds-table--small .navds-table__expanded-row-content {
padding: var(--a-spacing-2) calc(var(--a-spacing-2) + 3rem);
}
@media (forced-colors: active) {
.navds-date.navds-date button.rdp-day_selected {
forced-color-adjust: none;
background-color: highlight;
color: highlighttext;
box-shadow: none;
}
.navds-date.navds-date button {
color: buttontext;
forced-color-adjust: none;
outline: none;
box-shadow: none;
}
.navds-date button:focus-visible {
color: buttontext;
forced-color-adjust: none;
outline: 2px solid highlight;
}
.navds-date.navds-date button.rdp-day_disabled {
color: GrayText;
}
}
+21 -4
View File
@@ -63,14 +63,15 @@
}
.navds-tabs__tab:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: -2px;
box-shadow: inset var(--a-shadow-focus);
color: var(--ac-tabs-focus-text, var(--a-text-default));
}
@supports not selector(:focus-visible) {
.navds-tabs__tab:focus {
outline: none;
outline: 2px solid transparent;
box-shadow: inset var(--a-shadow-focus);
color: var(--ac-tabs-focus-text, var(--a-text-default));
}
@@ -117,13 +118,29 @@
}
.navds-tabs__tabpanel:focus-visible {
outline: none;
outline: 2px solid transparent;
box-shadow: inset 0 0 0 2px var(--a-border-focus);
}
@supports not selector(:focus-visible) {
.navds-tabs__tabpanel:focus {
outline: none;
outline: 2px solid transparent;
box-shadow: inset 0 0 0 2px var(--a-border-focus);
}
}
@media (forced-colors: active) {
.navds-tabs__tab[aria-selected="true"] {
border-bottom: 3px solid canvastext;
padding-block-end: calc(var(--a-spacing-3) - 3px);
}
.navds-tabs__tab--small[aria-selected="true"] {
padding-block-end: calc(var(--a-spacing-1-alt) - 3px);
}
.navds-tabs__tab-icon--top[aria-selected="true"],
.navds-tabs__tab--small.navds-tabs__tab-icon--top[aria-selected="true"] {
padding-block-end: calc(var(--a-spacing-1) - 3px);
}
}
+12
View File
@@ -180,3 +180,15 @@
background-color: var(--ac-tag-alt-3-moderate-bg, var(--a-surface-alt-3-moderate));
color: var(--ac-tag-alt-3-moderate-text, var(--a-text-default));
}
@media (forced-colors: active) {
.navds-tag {
forced-color-adjust: none;
}
.navds-tag--neutral,
.navds-tag--neutral-moderate {
border-color: canvastext;
color: canvastext;
}
}
+54
View File
@@ -393,3 +393,57 @@
border-right-color: transparent;
border-top-color: transparent;
}
@media (forced-colors: active) {
.navds-timeline__period:focus {
outline: 2px solid highlight;
outline-offset: 2px;
}
.navds-timeline__period--success,
.navds-timeline__period--warning,
.navds-timeline__period--info,
.navds-timeline__period--neutral,
.navds-timeline__period--danger {
forced-color-adjust: none;
}
.navds-timeline__row {
border: 1px solid transparent;
}
.navds-timeline__pin-wrapper::before {
border: 1px solid transparent;
}
.navds-timeline__pin-button {
outline: 4px solid transparent;
}
.navds-timeline__pin-button:focus,
.navds-timeline__pin-button:focus-visible {
outline: 4px solid highlight;
outline-offset: 2px;
box-shadow: none;
}
.navds-timeline__popover[data-placement^="top"] > .navds-timeline__popover-arrow {
border-left-color: canvas;
border-top-color: canvas;
}
.navds-timeline__popover[data-placement^="bottom"] > .navds-timeline__popover-arrow {
border-bottom-color: canvas;
border-right-color: canvas;
}
.navds-timeline__popover[data-placement^="left"] > .navds-timeline__popover-arrow {
border-left-color: canvas;
border-bottom-color: canvas;
}
.navds-timeline__popover[data-placement^="right"] > .navds-timeline__popover-arrow {
border-right-color: canvas;
border-top-color: canvas;
}
}
+33 -2
View File
@@ -65,7 +65,8 @@
}
.navds-toggle-group__button:focus-visible {
outline: none;
outline: 2px solid transparent;
outline-offset: 1px;
box-shadow:
0 0 0 1px var(--a-surface-default),
0 0 0 4px var(--a-border-focus);
@@ -73,7 +74,8 @@
@supports not selector(:focus-visible) {
.navds-toggle-group__button:focus {
outline: none;
outline: 2px solid transparent;
outline-offset: 1px;
box-shadow:
0 0 0 1px var(--a-surface-default),
0 0 0 4px var(--a-border-focus);
@@ -144,3 +146,32 @@
.navds-toggle-group--small > .navds-toggle-group__button > .navds-toggle-group__button-inner > svg {
font-size: 1.125rem;
}
@media (forced-colors: active) {
.navds-toggle-group {
border: 2px solid transparent;
}
.navds-toggle-group__button:hover {
color: highlight;
}
.navds-toggle-group__button[aria-checked="true"],
.navds-toggle-group__button[aria-pressed="true"] {
background-color: selecteditem;
color: selecteditemtext;
}
.navds-toggle-group__button[aria-checked="true"] > *,
.navds-toggle-group__button[aria-pressed="true"] > * {
forced-color-adjust: none;
}
.navds-toggle-group__button:active {
background-color: none !important;
}
.navds-toggle-group__button:focus-visible {
outline-color: highlight;
}
}
+28
View File
@@ -51,3 +51,31 @@
align-items: center;
justify-content: center;
}
@media (forced-colors: active) {
.navds-tooltip {
--__a-tooltip-high-contrast-border: 1px solid transparent;
border: var(--__a-tooltip-high-contrast-border);
}
.navds-tooltip[data-side="top"] .navds-tooltip__arrow {
border-bottom: var(--__a-tooltip-high-contrast-border);
border-right: var(--__a-tooltip-high-contrast-border);
}
.navds-tooltip[data-side="right"] .navds-tooltip__arrow {
border-bottom: var(--__a-tooltip-high-contrast-border);
border-left: var(--__a-tooltip-high-contrast-border);
}
.navds-tooltip[data-side="bottom"] .navds-tooltip__arrow {
border-left: var(--__a-tooltip-high-contrast-border);
border-top: var(--__a-tooltip-high-contrast-border);
}
.navds-tooltip[data-side="left"] .navds-tooltip__arrow {
border-top: var(--__a-tooltip-high-contrast-border);
border-right: var(--__a-tooltip-high-contrast-border);
}
}
+96
View File
@@ -1,5 +1,101 @@
# @navikt/ds-react
## 5.18.2
### Patch Changes
- Modal: Oppdatert intern håntering av context i komponent ([#2714](https://github.com/navikt/aksel/pull/2714))
- Updated dependencies []:
- @navikt/ds-tokens@5.18.2
- @navikt/aksel-icons@5.18.2
## 5.18.1
### Patch Changes
- Panel: Komponent er nå markert som deprecated. Bruk `Box` i fremtiden. ([#2712](https://github.com/navikt/aksel/pull/2712))
- Updated dependencies []:
- @navikt/ds-tokens@5.18.1
- @navikt/aksel-icons@5.18.1
## 5.18.0
### Minor Changes
- Portal: Ny komponent `Portal` som lar deg enkelt bruke `createPortal`, også på serversiden ([#2697](https://github.com/navikt/aksel/pull/2697))
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@5.18.0
- @navikt/aksel-icons@5.18.0
## 5.17.5
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@5.17.5
- @navikt/aksel-icons@5.17.5
## 5.17.4
### Patch Changes
- Bleed: `marginInline='full'` og `reflectivePadding` kan nå brukes sammen. ([#2695](https://github.com/navikt/aksel/pull/2695))
- Updated dependencies []:
- @navikt/ds-tokens@5.17.4
- @navikt/aksel-icons@5.17.4
## 5.17.3
### Patch Changes
- :bug: Textarea: maxLength virker nå selv om man sender inn onChange uten å sende inn value ([#2690](https://github.com/navikt/aksel/pull/2690))
- Updated dependencies []:
- @navikt/ds-tokens@5.17.3
- @navikt/aksel-icons@5.17.3
## 5.17.2
### Patch Changes
- :bug: Textarea: Sett riktig høyde når brukt i Modal + StrictMode ([#2679](https://github.com/navikt/aksel/pull/2679))
- DatePicker: Knapper fikk ikke riktig aria-label ([#2678](https://github.com/navikt/aksel/pull/2678))
- Textarea: debounce ResizeObserver for å unngå feilmelding ([#2685](https://github.com/navikt/aksel/pull/2685))
- Updated dependencies []:
- @navikt/ds-tokens@5.17.2
- @navikt/aksel-icons@5.17.2
## 5.17.1
### Patch Changes
- Eksponer variabelen "role" i Search-komponenten ([#2667](https://github.com/navikt/aksel/pull/2667))
- Updated dependencies []:
- @navikt/ds-tokens@5.17.1
- @navikt/aksel-icons@5.17.1
## 5.17.0
### Minor Changes
- Høykontrast: Komponenter støtter nå standard høykontrast-modus på Windows ([#2680](https://github.com/navikt/aksel/pull/2680))
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@5.17.0
- @navikt/aksel-icons@5.17.0
## 5.16.0
### Minor Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "5.16.0",
"version": "5.18.2",
"description": "Aksel react-components for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -38,8 +38,8 @@
},
"dependencies": {
"@floating-ui/react": "0.25.4",
"@navikt/aksel-icons": "^5.16.0",
"@navikt/ds-tokens": "^5.16.0",
"@navikt/aksel-icons": "^5.18.2",
"@navikt/ds-tokens": "^5.18.2",
"@radix-ui/react-tabs": "1.0.0",
"@radix-ui/react-toggle-group": "1.0.0",
"clsx": "^1.2.1",
@@ -1,6 +1,6 @@
import { StoryObj } from "@storybook/react";
import React, { useState } from "react";
import { Accordion, AccordionProps } from ".";
import { Table } from "..";
import AccordionContent from "./AccordionContent";
import AccordionHeader from "./AccordionHeader";
import AccordionItem from "./AccordionItem";
@@ -13,13 +13,15 @@ export default {
AccordionContent,
AccordionHeader,
},
parameters: {
chromatic: { disable: true },
},
decorators: [
(Story) => (
<div
style={{
width: "600px",
minHeight: "100vh",
padding: "10rem 0",
display: "flex",
flexDirection: "column",
gap: "1rem",
@@ -29,25 +31,10 @@ export default {
</div>
),
],
argTypes: {
variant: {
options: ["default", "neutral"],
control: { type: "select" },
},
headingSize: {
options: ["large", "medium", "small", "xsmall"],
control: { type: "select" },
},
size: {
options: ["large", "medium", "small"],
control: { type: "select" },
},
nItems: {
control: { type: "number" },
},
},
};
type Story = StoryObj<typeof Accordion>;
const Content = () => (
<Accordion.Content>
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam officia
@@ -103,22 +90,34 @@ const Item = (props) => {
);
};
export const Default = {
render: ({ nItems, ...props }) => {
export const Controls: Story = {
render: ({ ...props }) => {
return (
<div style={{ width: 500 }}>
<Accordion {...props}>
{[...Array(nItems ? nItems : 2)].map((_, y) => (
{[...Array(4)].map((_, y) => (
<Item key={y} {...props} />
))}
</Accordion>
</div>
);
},
argTypes: {
variant: {
options: ["default", "neutral"],
control: { type: "select" },
},
headingSize: {
options: ["large", "medium", "small", "xsmall"],
control: { type: "select" },
},
size: {
options: ["large", "medium", "small"],
control: { type: "select" },
},
},
args: {
controlled: false,
nItems: 2,
variant: "default",
headingSize: "medium",
size: "medium",
@@ -126,37 +125,31 @@ export const Default = {
},
};
export const DefaultOpen = {
render: ({ nItems, openItems, ...props }) => {
export const DefaultOpen: Story = {
render: () => {
return (
<div style={{ width: 500 }}>
<Accordion {...props}>
{[...Array(nItems ? nItems : 2)].map((_, y) => (
<Item key={y} defaultOpen={openItems.includes(y)} {...props} />
<Accordion>
{[...Array(4)].map((_, y) => (
<Item key={y} defaultOpen={y === 2} />
))}
</Accordion>
</div>
);
},
args: {
openItems: [1, 2],
controlled: false,
nItems: 5,
variant: "neutral",
headingSize: "large",
},
};
export const Variants = {
export const Variants: Story = {
render: () => {
return (
<div style={{ width: 500 }} className="colgap">
<h3>Default</h3>
<Accordion>
{[...Array(2)].map((_, y) => (
<Item key={y} defaultOpen />
))}
</Accordion>
<h3>Neutral</h3>
<Accordion variant="neutral">
{[...Array(2)].map((_, y) => (
<Item key={y} defaultOpen />
@@ -167,96 +160,32 @@ export const Variants = {
},
};
export const Controlled = (props) => {
const [open, setOpen] = useState(false);
const [open2, setOpen2] = useState(false);
return (
<div style={{ width: 500 }}>
<Accordion {...props}>
<Accordion.Item open={open}>
<Accordion.Header onClick={() => setOpen(!open)}>
Accordion header text
</Accordion.Header>
<Content />
</Accordion.Item>
<Accordion.Item open={open2}>
<Accordion.Header onClick={() => setOpen2(!open2)}>
Accordion header text
</Accordion.Header>
<Content />
</Accordion.Item>
</Accordion>
</div>
);
};
export const WithTable = {
render: (props) => {
const ExampleTable = () => {
return (
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>ID</Table.HeaderCell>
<Table.HeaderCell>Fornavn</Table.HeaderCell>
<Table.HeaderCell>Etternavn</Table.HeaderCell>
<Table.HeaderCell>Rolle</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.HeaderCell>1</Table.HeaderCell>
<Table.DataCell>Jean-Luc</Table.DataCell>
<Table.DataCell>Picard</Table.DataCell>
<Table.DataCell>Kaptein</Table.DataCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell>2</Table.HeaderCell>
<Table.DataCell>William</Table.DataCell>
<Table.DataCell>Riker</Table.DataCell>
<Table.DataCell>Kommandør</Table.DataCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell>3</Table.HeaderCell>
<Table.DataCell>Geordi</Table.DataCell>
<Table.DataCell>La Forge</Table.DataCell>
<Table.DataCell>Sjefsingeniør</Table.DataCell>
</Table.Row>
</Table.Body>
</Table>
);
};
const ContentWithTable = () => {
return (
<Accordion.Content>
<ExampleTable />
</Accordion.Content>
);
};
export const ControlledState: Story = {
render: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [open, setOpen] = useState(false);
// eslint-disable-next-line react-hooks/rules-of-hooks
const [open2, setOpen2] = useState(false);
return (
<div style={{ width: 500 }}>
<Accordion {...props}>
<Accordion.Item>
<Accordion.Header>Table of people</Accordion.Header>
<ContentWithTable />
<Accordion>
<Accordion.Item open={open}>
<Accordion.Header onClick={() => setOpen(!open)}>
Accordion header text
</Accordion.Header>
<Content />
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>Table of people</Accordion.Header>
<ContentWithTable />
<Accordion.Item open={open2}>
<Accordion.Header onClick={() => setOpen2(!open2)}>
Accordion header text
</Accordion.Header>
<Content />
</Accordion.Item>
</Accordion>
</div>
);
},
args: {
variant: "default",
headingSize: "large",
indent: false,
},
};
const SingleHeaderAccordion = ({
@@ -269,9 +198,11 @@ const SingleHeaderAccordion = ({
<Accordion.Header>{`${size} size + ${headingSize} heading`}</Accordion.Header>
<Accordion.Content>a</Accordion.Content>
</Accordion.Item>
<Accordion.Item>
<Accordion.Item open>
<Accordion.Header>{`${size} size + ${headingSize} heading`}</Accordion.Header>
<Accordion.Content>a</Accordion.Content>
<Accordion.Content>
lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Accordion.Content>
</Accordion.Item>
</Accordion>
);
@@ -285,18 +216,73 @@ const headingSizes: AccordionProps["headingSize"][] = [
"xsmall",
];
export const Size = (props) => (
<div style={{ width: 500 }} className="colgap">
{sizes.map((size) => (
<SingleHeaderAccordion key={size} size={size} {...props} />
))}
</div>
);
export const Size: Story = {
render: () => (
<div style={{ width: 500 }} className="colgap">
{sizes.map((size) => (
<SingleHeaderAccordion key={size} size={size} />
))}
</div>
),
};
export const HeadingSize = (props) => (
<div style={{ width: 500 }} className="colgap">
{headingSizes.map((size) => (
<SingleHeaderAccordion key={size} headingSize={size} {...props} />
))}
</div>
);
export const HeadingSize: Story = {
render: () => (
<div style={{ width: 500 }} className="colgap">
{headingSizes.map((size) => (
<SingleHeaderAccordion key={size} headingSize={size} />
))}
</div>
),
};
export const Indent: Story = {
render: () => {
return (
<div style={{ width: 500 }} className="colgap">
<h3>No indent</h3>
<Accordion indent>
{[...Array(2)].map((_, y) => (
<Item key={y} defaultOpen />
))}
</Accordion>
<h3>Indent</h3>
<Accordion indent={false}>
{[...Array(2)].map((_, y) => (
<Item key={y} defaultOpen />
))}
</Accordion>
</div>
);
},
};
export const Chromatic: Story = {
render: (...props) => (
<div>
<div>
<h2>Variants</h2>
{Variants.render?.(...props)}
</div>
<div>
<h2>Size</h2>
{Size.render?.(...props)}
</div>
<div>
<h2>HeadingSize</h2>
{HeadingSize.render?.(...props)}
</div>
<div>
<h2>DefaultOpen</h2>
{DefaultOpen.render?.(...props)}
</div>
<div>
<h2>Indent</h2>
{Indent.render?.(...props)}
</div>
</div>
),
parameters: {
chromatic: { disable: false },
},
};
+202 -113
View File
@@ -1,12 +1,16 @@
import type { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import React from "react";
import { Alert, AlertProps } from ".";
import { BodyLong, Heading as DsHeading, Link } from "..";
import { VStack } from "../layout/stack";
import { Link } from "../link";
import { BodyLong, Heading as DsHeading } from "../typography";
import Alert, { AlertProps } from "./Alert";
const meta: Meta<typeof Alert> = {
title: "ds-react/Alert",
component: Alert,
parameters: {
chromatic: { disable: true },
},
};
export default meta;
@@ -20,12 +24,13 @@ const variants: AlertProps["variant"][] = [
"success",
];
export const Default: Story = {
export const Controls: Story = {
render: (props) => <Alert {...props} />,
args: {
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
fullWidth: false,
inline: false,
variant: "info",
size: "medium",
closeButton: false,
@@ -42,43 +47,18 @@ export const Default: Story = {
},
};
export const Small: Story = {
render: (props) => {
return (
<div className="colgap">
{variants.map((variant, i) => (
<Alert
key={variant}
variant={variant}
size="small"
closeButton={props.closeButton}
>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
</Alert>
))}
</div>
);
},
args: {
closeButton: false,
},
};
export const FullWidth = () => {
export const Variants = () => {
return (
<div className="colgap">
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} fullWidth>
<Alert key={variant} variant={variant}>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
</Alert>
))}
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} fullWidth size="small">
<Alert key={variant} variant={variant} size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
@@ -88,24 +68,42 @@ export const FullWidth = () => {
);
};
export const FullWidth = () => {
return (
<VStack gap="4">
<Alert variant="info" fullWidth>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" fullWidth size="small">
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
</VStack>
);
};
export const Inline = () => {
return (
<div className="colgap">
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} inline>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
</Alert>
))}
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} inline size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
</Alert>
))}
</div>
<VStack gap="4">
<h2>Inline</h2>
<Alert variant="info" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim reprehenderit
enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim reprehenderit
enim nisi veniam nostrud.
</Alert>
</VStack>
);
};
@@ -136,82 +134,173 @@ export const Heading = () => {
);
};
export const Links = () => {
return (
<div className="colgap">
{variants.map((variant) => (
<Alert key={variant} variant={variant}>
<Link href="#">Id elit esse enim reprehenderit</Link>
</Alert>
))}
</div>
);
};
const AlertWithCloseButton = ({
children,
size,
}: {
size?: "medium" | "small";
children?: React.ReactNode;
}) => {
const [show, setShow] = React.useState(true);
return show ? (
<Alert
variant="warning"
size={size}
closeButton
onClose={() => setShow(false)}
>
{children || "Content"}
</Alert>
) : null;
};
export const WithCloseButton: Story = {
export const CloseButton: Story = {
render: () => {
return (
<div className="colgap">
<AlertWithCloseButton />
<AlertWithCloseButton>
<BodyLong>Ullamco ullamco laborum et commodo sint culpa</BodyLong>
</AlertWithCloseButton>
<AlertWithCloseButton>
<DsHeading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
<VStack gap="4">
<h2>CloseButton</h2>
<Alert variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert size="small" variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert size="small" variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" closeButton>
<DsHeading size="small" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
sint commodo consequat eu aute.
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</AlertWithCloseButton>
<AlertWithCloseButton size="small">
<BodyLong>Ullamco ullamco laborum et commodo</BodyLong>
</AlertWithCloseButton>
<AlertWithCloseButton size="small">
<DsHeading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
</Alert>
<Alert variant="info" closeButton size="small">
<DsHeading size="xsmall" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
sint commodo consequat eu aute.
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</AlertWithCloseButton>
</div>
</Alert>
</VStack>
);
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
const buttons = canvas.getAllByTitle("Lukk Alert");
};
await step("click button", async () => {
await userEvent.click(buttons[0]);
});
export const Chromatic: Story = {
render: () => (
<VStack gap="6">
<VStack gap="2">
<h2>Variant</h2>
{variants.map((variant, i) => (
<Alert key={variant} variant={variant}>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud.",
)}
</Alert>
))}
</VStack>
<VStack gap="2">
<h2>Small</h2>
<Alert variant="info" size="small">
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small">
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
</VStack>
<VStack gap="2">
<h2>FullWidth</h2>
<Alert variant="info" fullWidth>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small" fullWidth>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
</VStack>
<VStack gap="2">
<h2>Inline</h2>
<Alert variant="info" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" size="small" inline>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
</VStack>
<VStack gap="2">
<h2>Heading</h2>
<Alert variant="info">
<DsHeading size="small" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</Alert>
<Alert variant="info" size="small">
<DsHeading size="xsmall" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong size="small">
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</Alert>
</VStack>
<VStack gap="2">
<h2>CloseButton</h2>
<Alert variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert size="small" variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert size="small" variant="info" closeButton>
Id elit esse enim reprehenderit enim nisi veniam nostrud. Id elit esse
enim reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud. Id elit esse enim
reprehenderit enim nisi veniam nostrud.
</Alert>
<Alert variant="info" closeButton>
<DsHeading size="small" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong>
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</Alert>
<Alert variant="info" size="small" closeButton>
<DsHeading size="xsmall" level="3">
Id elit esse enim reprehenderit
</DsHeading>
<BodyLong size="small">
Id elit esse enim reprehenderit enim nisi veniam nostrud.
</BodyLong>
</Alert>
</VStack>
const buttonsAfter = canvas.getAllByTitle("Lukk Alert");
expect(buttonsAfter.length).toBe(4);
<VStack gap="2">
<h2>Links</h2>
{variants.map((variant) => (
<Alert key={variant} variant={variant}>
<Link href="#">Id elit esse enim reprehenderit</Link>
</Alert>
))}
</VStack>
</VStack>
),
parameters: {
chromatic: { disable: false },
},
};
+96 -193
View File
@@ -1,15 +1,21 @@
import { StoryObj } from "@storybook/react";
import React from "react";
import { StarIcon } from "@navikt/aksel-icons";
import { StarIcon as BaseStarIcon } from "@navikt/aksel-icons";
import { HStack, VStack } from "../layout/stack";
import { Button } from "./index";
export default {
title: "ds-react/Button",
component: Button,
parameters: {
chromatic: { delay: 600 },
chromatic: { disable: true },
},
};
type Story = StoryObj<typeof Button>;
const StarIcon = () => <BaseStarIcon aria-hidden />;
const variants: (
| "primary"
| "secondary"
@@ -28,17 +34,7 @@ const variants: (
"tertiary-neutral",
];
const varSwitch = {
primary: "Primary",
secondary: "Secondary",
tertiary: "Tertiary",
danger: "Danger",
"primary-neutral": "Primary",
"secondary-neutral": "Secondary",
"tertiary-neutral": "Tertiary",
};
export const Default = {
export const Controls: Story = {
render: (props) => {
return (
<Button
@@ -57,7 +53,7 @@ export const Default = {
icon: false,
loading: false,
iconPosition: "left",
children: "Knapp",
children: "Button",
variant: "primary",
size: "medium",
},
@@ -91,192 +87,99 @@ export const Default = {
},
};
export const Small = () => (
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
{varSwitch[variant]}
</Button>
))}
</div>
);
export const XSmall = () => (
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="xsmall">
{varSwitch[variant]}
</Button>
))}
</div>
);
export const Link = () => (
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} as="a" href="#thecakeisalie">
{varSwitch[variant]}
</Button>
))}
</div>
);
export const Loading = {
render: () => (
<div className="colgap chromatic-ignore">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} loading>
{varSwitch[variant]}
function ButtonGrid(props: any) {
return (
<VStack gap="2">
{variants.map((variant) => (
<HStack gap="2" key={variant}>
<Button variant={variant} {...props}>
Button
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} loading size="small">
{varSwitch[variant]}
<Button variant={variant} {...props} icon={<StarIcon />}>
Button
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} loading size="xsmall">
{varSwitch[variant]}
</Button>
))}
</div>
</div>
),
parameters: {
chromatic: { disableSnapshot: true },
},
};
export const Icon = () => (
<div className="colgap ">
<div className="rowgap">
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
icon={<StarIcon title="Stjerne" />}
/>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
size="small"
icon={<StarIcon title="Stjerne" />}
/>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
size="xsmall"
icon={<StarIcon title="Stjerne" />}
/>
))}
</div>
</div>
);
export const IconWText = () => (
<div className="colgap">
<div className="rowgap">
{variants.map((variant, i) => (
<Button
key={variant}
variant={variant}
icon={<StarIcon title="Stjerne" />}
iconPosition={i % 2 ? "left" : "right"}
>
{varSwitch[variant]}
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant, i) => (
<Button
key={variant}
variant={variant}
size="small"
icon={<StarIcon title="Stjerne" />}
iconPosition={i % 2 ? "left" : "right"}
>
{varSwitch[variant]}
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant, i) => (
<Button
key={variant}
variant={variant}
size="xsmall"
icon={<StarIcon title="Stjerne" />}
iconPosition={i % 2 ? "left" : "right"}
>
{varSwitch[variant]}
</Button>
))}
</div>
</div>
);
export const Disabled = () => (
<div className="colgap">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} disabled>
{varSwitch[variant]}
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} disabled as="a" href="#">
{varSwitch[variant]}
</Button>
))}
</div>
</div>
);
export const LoadingWithAs = {
render: () => (
<div className="colgap chromatic-ignore">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} loading size="small">
{varSwitch[variant]}
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
loading
size="small"
as="a"
href="#"
{...props}
icon={<StarIcon />}
iconPosition="right"
>
{varSwitch[variant]}
Button
</Button>
))}
</div>
</div>
),
<Button variant={variant} {...props} icon={<StarIcon />} />
</HStack>
))}
</VStack>
);
}
export const Medium: Story = {
render: () => <ButtonGrid size="medium" />,
};
export const Small: Story = {
render: () => <ButtonGrid size="small" />,
};
export const XSmall: Story = {
render: () => <ButtonGrid size="xsmall" />,
};
export const AsLink: Story = {
render: () => (
<VStack gap="4">
<Button href="#" as="a">
Button as a-tag
</Button>
<Button href="#" as="a" disabled>
Disabled Button as a-tag
</Button>
</VStack>
),
};
export const Loading: Story = {
render: () => <ButtonGrid loading />,
};
export const LoadingAsLink: Story = {
render: () => <ButtonGrid loading href="#" as="a" />,
};
export const Disabled: Story = {
render: () => <ButtonGrid disabled />,
};
export const DisabledAsLink: Story = {
render: () => <ButtonGrid disabled href="#" as="a" />,
};
export const Chromatic: Story = {
render: () => (
<VStack gap="6" align="center">
<div>
<h2>Medium</h2>
<ButtonGrid size="medium" />
</div>
<div>
<h2>Small</h2>
<ButtonGrid size="small" />
</div>
<div>
<h2>XSmall</h2>
<ButtonGrid size="xsmall" />
</div>
<div>
<h2>As Link</h2>
<ButtonGrid as="a" href="#" />
</div>
<div>
<h2>Disabled</h2>
<ButtonGrid disabled />
</div>
</VStack>
),
parameters: {
chromatic: { disableSnapshot: true },
chromatic: { disable: false },
},
};
+284 -206
View File
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { Chat } from "../index";
import { Chat, VStack } from "../index";
import { POSITIONS, SIZES } from "./Chat";
export default {
@@ -30,9 +30,14 @@ export default {
options: SIZES,
},
},
parameters: {
chromatic: { disable: true },
},
} satisfies Meta<typeof Chat>;
export const Default = {
type Story = StoryObj<typeof Chat>;
export const Controls: Story = {
render: (props) => {
return (
<Chat {...props}>
@@ -55,219 +60,292 @@ export const Default = {
},
};
export const Small = () => (
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 14:00" size="small">
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
);
export const Size: Story = {
render: () => (
<VStack gap="4">
<h3>Medium</h3>
<Chat
avatar="ON"
name="Ola Normann"
timestamp="01.01.21 14:00"
size="medium"
>
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
<h3>Small</h3>
<Chat
avatar="ON"
name="Ola Normann"
timestamp="01.01.21 14:00"
size="small"
>
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
</VStack>
),
};
export const Colors = () => (
<Chat
avatar="NAV"
name="NAV"
timestamp="01.01.21 14:00"
avatarBgColor="var(--a-surface-alt-1-subtle)"
backgroundColor="var(--a-surface-warning-subtle)"
>
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
<Chat.Bubble backgroundColor="lightYellow">
Tempor fugiat amet eu sint in in ullamco.
</Chat.Bubble>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
);
export const Variants: Story = {
render: () => (
<VStack gap="4">
<h3>Info</h3>
<Chat avatar="NAV" name="NAV" timestamp="01.01.21 14:00" variant="info">
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
</Chat>
<h3>Neutral</h3>
<Chat
avatar="KN"
name="Kari Nordmann"
timestamp="01.01.21 14:03"
variant="neutral"
position="right"
>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
</Chat>
<h3>Subtle</h3>
<Chat
avatar="ON"
name="Ola Nordmann"
timestamp="01.01.21 14:07"
variant="subtle"
position="right"
>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
</VStack>
),
};
export const Variants = () => (
<div className="colgap">
<Chat avatar="NAV" name="NAV" timestamp="01.01.21 14:00" variant="info">
export const Position: Story = {
render: () => (
<VStack gap="4">
<h3>Default</h3>
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 14:00">
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
<h3>Right</h3>
<Chat
avatar="KH"
name="Kari Høyli"
timestamp="01.01.21 14:32"
position="right"
variant="info"
>
<Chat.Bubble>
Reprehenderit pariatur officia exercitation laboris.
</Chat.Bubble>
<Chat.Bubble>
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit
est dolore incididunt. Occaecat cupidatat magna.
</Chat.Bubble>
</Chat>
<h3>Left</h3>
<Chat
position="left"
avatar="ON"
name="Ola Normann"
timestamp="01.01.21 15:00"
>
<Chat.Bubble>
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo
ea esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
labore sunt aute.
</Chat.Bubble>
</Chat>
</VStack>
),
};
export const ToptextPosition: Story = {
render: () => (
<VStack gap="4">
<h3>Right</h3>
<Chat
avatar="ON"
name="Ola Normann"
timestamp="01.01.21 14:00"
toptextPosition="right"
>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
<h3>Left</h3>
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 15:00">
<Chat.Bubble toptextPosition="left">
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo
ea esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
labore sunt aute.
</Chat.Bubble>
</Chat>
<h3>Left</h3>
<Chat
name="Kari Høyli"
timestamp="01.01.21 14:32"
position="right"
toptextPosition="left"
>
<Chat.Bubble>
Reprehenderit pariatur officia exercitation laboris.
</Chat.Bubble>
<Chat.Bubble>
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit
est dolore incididunt. Occaecat cupidatat magna.
</Chat.Bubble>
</Chat>
<h3>Right</h3>
<Chat
name="Kari Høyli"
timestamp="01.01.21 14:32"
position="right"
toptextPosition="right"
>
<Chat.Bubble>
Reprehenderit pariatur officia exercitation laboris.
</Chat.Bubble>
<Chat.Bubble>
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit
est dolore incididunt. Occaecat cupidatat magna.
</Chat.Bubble>
</Chat>
</VStack>
),
};
export const Avatar: Story = {
render: () => (
<Chat avatar={<Illustration />}>
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
</Chat>
<Chat
avatar="KN"
name="Kari Nordmann"
timestamp="01.01.21 14:03"
variant="neutral"
position="right"
>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
</Chat>
<Chat
avatar="ON"
name="Ola Nordmann"
timestamp="01.01.21 14:07"
variant="subtle"
position="right"
>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
</div>
);
),
};
export const Position = () => (
<div className="colgap">
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 14:00">
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
<Chat
avatar="KH"
name="Kari Høyli"
timestamp="01.01.21 14:32"
position="right"
variant="info"
>
<Chat.Bubble>
Reprehenderit pariatur officia exercitation laboris.
</Chat.Bubble>
<Chat.Bubble>
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit est
dolore incididunt. Occaecat cupidatat magna.
</Chat.Bubble>
</Chat>
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 15:00">
<Chat.Bubble>
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo ea
esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
labore sunt aute.
</Chat.Bubble>
</Chat>
</div>
);
function Illustration() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 93">
<path
fill="#e7e5e2"
d="M14 50.7C15 52.3 17.9 81 26.5 81S39 51.8 39 50.3c-13.2-7.6-25 .4-25 .4z"
/>
<path
fill="#5c4378"
d="M38.7 50.2c6 2.9 15.3 10.9 15.3 18.3V93H0V68.5c0-7.1 8.5-14.8 14.5-18-.3.2-.5.3-.5.3 1 1.7 3.8 9.2 12.4 9.2C35 60 39 51.9 39 50.4c-.1-.1-.2-.2-.3-.2z"
/>
<path
fill="#d2242a"
d="M46.7 76H31.2c-.7 0-1.3-.6-1.2-1.3v-8.5c0-.7.6-1.3 1.3-1.3h15.5c.7 0 1.3.6 1.3 1.3v8.5c-.1.7-.7 1.3-1.4 1.3"
/>
<path
fill="#fff"
d="M42.9 71c0 2.1-1.7 3.8-3.8 3.8-2.1 0-3.8-1.7-3.8-3.8s1.7-3.8 3.8-3.8c2.1 0 3.8 1.7 3.8 3.8m-8.7 1.7h-.7l.8-1.9h.7l-.8 1.9zm9.3 0H43l.8-1.9h.5l-.8 1.9zm1.2 0h-.2l.8-1.9h.2l-.8 1.9z"
/>
<path
fill="#c52d35"
d="M36.2 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.6s-.1 0-.1.1l-.2.6v.1h.2l.1 1.2c0-.1 0 0 0 0"
/>
<path
fill="#c52d35"
d="M37.5 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.9s-.1 0-.1.1l-.2.6-.1.1h.5c.1 0 .2.1.2.2v1c-.1-.1-.1 0 0 0m2.6-1.9h-.6s-.1 0-.1.1v1.8s0 .1.1.1h.6s.1 0 .1-.1l.2-.6V72h-.2l-.1-1.2"
/>
<path
fill="#c52d35"
d="M37.7 72.7h.4s.1 0 .1-.1l.2-.6v-.1h-.2c0 .1-.5.8-.5.8zm3.9-1.9h.7s.1 0 0 .1l-.7 1.8H41l.6-1.9"
/>
<path
fill="#c52d35"
d="M40.8 70.8h-1c-.1 0 .3.1.3.1l.7 1.7s0 .1.1.1h.6l-.7-1.9m-1.3.6v.4s-.1-.4-.3-.4c-.3 0-.3.2-.3.3 0 .1.1.3.2.3h.5l-.3.7H39c-.2 0-.9-.3-.9-.9 0-.6.5-1 .9-1 .2-.1.5.2.5.6 0-.1 0-.1 0 0z"
/>
<path
fill="#5a1f57"
d="M39.9 66.7h-1.6c-.1 0-.2-.1-.2-.2v-.3c0-.1.1-.2.2-.2h1.6c.1 0 .2.1.2.2v.3c0 .2-.1.2-.2.2"
/>
<path fill="#c2b5cf" d="M38.7 66.5h.9V64h-.9v2.5z" />
<path
fill="#e7e5e2"
d="M47.2 35.3C44.7 45.6 36.6 53.1 27 53.1S9.3 45.6 6.8 35.3c-.2.1-.5.1-.8.1-1.1 0-2-.8-2-1.7v-7c0-1 .9-1.7 2-1.7h.2C7.7 13.1 16.4 4 27 4c10.6 0 19.3 9.1 20.8 21h.2c1.1 0 2 .8 2 1.7v7c0 1-.9 1.7-2 1.7-.3 0-.5 0-.8-.1z"
/>
<path
fill="#635e59"
d="M19 27.6c-1.4.1-1.9-2-1.4-3.4.1-.3.6-1.5 1.4-1.5.8 0 1.2.7 1.3.8.6 1.4.3 4-1.3 4.1m16.2 0c1.4.1 1.9-2 1.4-3.4-.1-.3-.6-1.5-1.4-1.5-.8 0-1.2.7-1.3.8-.6 1.4-.3 4 1.3 4.1"
/>
<path
fill="#d1bfa3"
d="M26.8 34.6c-.4 0-.7-.1-1-.2-.3-.1-.4-.4-.3-.7.1-.3.4-.4.7-.3.5.2 1.5.1 2.2-.4.7-.4 1.1-1 1.2-1.5.1-.4-.1-.9-.4-1.3-.2-.2-.8-.2-1.6-.1-.3 0-.5-.1-.6-.4 0-.3.1-.5.4-.6 1.2-.2 2.1 0 2.6.6.5.7.8 1.4.6 2.1-.1.8-.7 1.6-1.7 2.2-.6.3-1.4.6-2.1.6z"
/>
<path
fill="#593a32"
d="M27.1 42.1h-.3c-5.3-.2-7.3-4.1-7.4-4.3-.1-.3 0-.6.2-.7.2-.1.6 0 .7.2.1.1 1.9 3.6 6.6 3.8 4.7.2 6.4-3.7 6.4-3.7.1-.3.4-.4.7-.3.3.1.4.4.3.7-.1 0-2.1 4.3-7.2 4.3z"
/>
<path
fill="#f6b873"
d="M6.6 30.7c.1-.1.1-.2.1-.3v-2c-.1-5.6 1.8-8.1 3.4-10.1 0 0-1 4.3-.3 3.4 3.8-5 21.4-1.6 25-8.1.5 3.6-4.1 4.6-4.1 4.6 3.7.7 6.9-.8 7.7-2.5.3 1.4-.6 2.4-1.9 3.4 4.5-.9 4.6-4 4.6-4 .6 4.1 5.3 2.5 5.3 9.3v6c0 .3.2.6.5.6h.5c.3 0 .5-.3.5-.6V26c.3-15.6-8.5-26-20.6-26C15.9 0 5 10.4 5 24.1v6.3c0 .4.2.6.5.6h.6c.2 0 .3-.1.5-.3"
/>
<path
fill="#f6b873"
d="M25.9 43.4c-4.4 0-8-1.4-8-3.2s3.6-3.2 8-3.2 8 1.4 8 3.2c0 1.8-3.6 3.2-8 3.2m.8-9.4c-2.9 0-4.7.7-8.8 2.1-12.7 4.6-11.6-14-11.6-14C3.4 46 18.6 52 26.5 52c8.1 0 24.1-8.1 21-30 0 0 .4 17.1-12.9 13.8-3.7-.9-5-1.8-7.9-1.8z"
/>
</svg>
);
}
export const ToptextPosition = () => (
<div className="colgap">
<Chat
avatar="ON"
name="Ola Normann"
timestamp="01.01.21 14:00"
toptextPosition="right"
>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
<Chat
name="Kari Høyli"
timestamp="01.01.21 14:32"
position="right"
variant="info"
toptextPosition="left"
>
<Chat.Bubble>
Reprehenderit pariatur officia exercitation laboris.
</Chat.Bubble>
<Chat.Bubble>
Enim velit deserunt do quis. Eu fugiat magna esse dolore ad sunt sit est
dolore incididunt. Occaecat cupidatat magna.
</Chat.Bubble>
</Chat>
<Chat avatar="ON" name="Ola Normann" timestamp="01.01.21 15:00">
<Chat.Bubble toptextPosition="right">
Exercitation irure Lorem cupidatat culpa anim cillum esse ullamco qui
dolore laborum et Lorem. Labore sunt duis id Lorem voluptate commodo ea
esse. Dolore esse aliqua proident ea ad commodo ut dolore voluptate
labore sunt aute.
</Chat.Bubble>
</Chat>
</div>
);
export const Avatar = () => (
<Chat avatar={<Illustration />} timestamp="01.01.21 14:00">
<Chat.Bubble>
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
<Chat.Bubble>Tempor fugiat amet eu sint in in ullamco.</Chat.Bubble>
<Chat.Bubble>
Adipisicing laborum est eu laborum est sit in commodo enim sint laboris
labore nisi ut.
</Chat.Bubble>
</Chat>
);
const Illustration = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 93">
<path
fill="#e7e5e2"
d="M14 50.7C15 52.3 17.9 81 26.5 81S39 51.8 39 50.3c-13.2-7.6-25 .4-25 .4z"
/>
<path
fill="#5c4378"
d="M38.7 50.2c6 2.9 15.3 10.9 15.3 18.3V93H0V68.5c0-7.1 8.5-14.8 14.5-18-.3.2-.5.3-.5.3 1 1.7 3.8 9.2 12.4 9.2C35 60 39 51.9 39 50.4c-.1-.1-.2-.2-.3-.2z"
/>
<path
fill="#d2242a"
d="M46.7 76H31.2c-.7 0-1.3-.6-1.2-1.3v-8.5c0-.7.6-1.3 1.3-1.3h15.5c.7 0 1.3.6 1.3 1.3v8.5c-.1.7-.7 1.3-1.4 1.3"
/>
<path
fill="#fff"
d="M42.9 71c0 2.1-1.7 3.8-3.8 3.8-2.1 0-3.8-1.7-3.8-3.8s1.7-3.8 3.8-3.8c2.1 0 3.8 1.7 3.8 3.8m-8.7 1.7h-.7l.8-1.9h.7l-.8 1.9zm9.3 0H43l.8-1.9h.5l-.8 1.9zm1.2 0h-.2l.8-1.9h.2l-.8 1.9z"
/>
<path
fill="#c52d35"
d="M36.2 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.6s-.1 0-.1.1l-.2.6v.1h.2l.1 1.2c0-.1 0 0 0 0"
/>
<path
fill="#c52d35"
d="M37.5 72.7h.6s.1 0 .1-.1v-1.8s0-.1-.1-.1h-.9s-.1 0-.1.1l-.2.6-.1.1h.5c.1 0 .2.1.2.2v1c-.1-.1-.1 0 0 0m2.6-1.9h-.6s-.1 0-.1.1v1.8s0 .1.1.1h.6s.1 0 .1-.1l.2-.6V72h-.2l-.1-1.2"
/>
<path
fill="#c52d35"
d="M37.7 72.7h.4s.1 0 .1-.1l.2-.6v-.1h-.2c0 .1-.5.8-.5.8zm3.9-1.9h.7s.1 0 0 .1l-.7 1.8H41l.6-1.9"
/>
<path
fill="#c52d35"
d="M40.8 70.8h-1c-.1 0 .3.1.3.1l.7 1.7s0 .1.1.1h.6l-.7-1.9m-1.3.6v.4s-.1-.4-.3-.4c-.3 0-.3.2-.3.3 0 .1.1.3.2.3h.5l-.3.7H39c-.2 0-.9-.3-.9-.9 0-.6.5-1 .9-1 .2-.1.5.2.5.6 0-.1 0-.1 0 0z"
/>
<path
fill="#5a1f57"
d="M39.9 66.7h-1.6c-.1 0-.2-.1-.2-.2v-.3c0-.1.1-.2.2-.2h1.6c.1 0 .2.1.2.2v.3c0 .2-.1.2-.2.2"
/>
<path fill="#c2b5cf" d="M38.7 66.5h.9V64h-.9v2.5z" />
<path
fill="#e7e5e2"
d="M47.2 35.3C44.7 45.6 36.6 53.1 27 53.1S9.3 45.6 6.8 35.3c-.2.1-.5.1-.8.1-1.1 0-2-.8-2-1.7v-7c0-1 .9-1.7 2-1.7h.2C7.7 13.1 16.4 4 27 4c10.6 0 19.3 9.1 20.8 21h.2c1.1 0 2 .8 2 1.7v7c0 1-.9 1.7-2 1.7-.3 0-.5 0-.8-.1z"
/>
<path
fill="#635e59"
d="M19 27.6c-1.4.1-1.9-2-1.4-3.4.1-.3.6-1.5 1.4-1.5.8 0 1.2.7 1.3.8.6 1.4.3 4-1.3 4.1m16.2 0c1.4.1 1.9-2 1.4-3.4-.1-.3-.6-1.5-1.4-1.5-.8 0-1.2.7-1.3.8-.6 1.4-.3 4 1.3 4.1"
/>
<path
fill="#d1bfa3"
d="M26.8 34.6c-.4 0-.7-.1-1-.2-.3-.1-.4-.4-.3-.7.1-.3.4-.4.7-.3.5.2 1.5.1 2.2-.4.7-.4 1.1-1 1.2-1.5.1-.4-.1-.9-.4-1.3-.2-.2-.8-.2-1.6-.1-.3 0-.5-.1-.6-.4 0-.3.1-.5.4-.6 1.2-.2 2.1 0 2.6.6.5.7.8 1.4.6 2.1-.1.8-.7 1.6-1.7 2.2-.6.3-1.4.6-2.1.6z"
/>
<path
fill="#593a32"
d="M27.1 42.1h-.3c-5.3-.2-7.3-4.1-7.4-4.3-.1-.3 0-.6.2-.7.2-.1.6 0 .7.2.1.1 1.9 3.6 6.6 3.8 4.7.2 6.4-3.7 6.4-3.7.1-.3.4-.4.7-.3.3.1.4.4.3.7-.1 0-2.1 4.3-7.2 4.3z"
/>
<path
fill="#f6b873"
d="M6.6 30.7c.1-.1.1-.2.1-.3v-2c-.1-5.6 1.8-8.1 3.4-10.1 0 0-1 4.3-.3 3.4 3.8-5 21.4-1.6 25-8.1.5 3.6-4.1 4.6-4.1 4.6 3.7.7 6.9-.8 7.7-2.5.3 1.4-.6 2.4-1.9 3.4 4.5-.9 4.6-4 4.6-4 .6 4.1 5.3 2.5 5.3 9.3v6c0 .3.2.6.5.6h.5c.3 0 .5-.3.5-.6V26c.3-15.6-8.5-26-20.6-26C15.9 0 5 10.4 5 24.1v6.3c0 .4.2.6.5.6h.6c.2 0 .3-.1.5-.3"
/>
<path
fill="#f6b873"
d="M25.9 43.4c-4.4 0-8-1.4-8-3.2s3.6-3.2 8-3.2 8 1.4 8 3.2c0 1.8-3.6 3.2-8 3.2m.8-9.4c-2.9 0-4.7.7-8.8 2.1-12.7 4.6-11.6-14-11.6-14C3.4 46 18.6 52 26.5 52c8.1 0 24.1-8.1 21-30 0 0 .4 17.1-12.9 13.8-3.7-.9-5-1.8-7.9-1.8z"
/>
</svg>
);
export const Chromatic: Story = {
render: (...props) => (
<div>
<div>
<h2>Size</h2>
{Size.render?.(...props)}
</div>
<div>
<h2>Variants</h2>
{Variants.render?.(...props)}
</div>
<div>
<h2>Position</h2>
{Position.render?.(...props)}
</div>
<div>
<h2>Toptext</h2>
{ToptextPosition.render?.(...props)}
</div>
<div>
<h2>Avatar</h2>
{Avatar.render?.(...props)}
</div>
</div>
),
parameters: {
chromatic: { disable: false },
},
};
@@ -11,7 +11,6 @@ import { DateWrapper } from "../parts/DateWrapper";
import { getLocaleFromString, labels } from "../utils";
import DatePickerStandalone from "./DatePickerStandalone";
import Caption from "./parts/Caption";
import DayButton from "./parts/DayButton";
import DropdownCaption from "./parts/DropdownCaption";
import { HeadRow } from "./parts/HeadRow";
import Row from "./parts/Row";
@@ -124,7 +123,6 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
HeadRow,
WeekNumber,
Row,
Day: DayButton,
}}
className={cl("navds-date", className)}
classNames={{
@@ -5,7 +5,6 @@ import { DateRange, DayPicker, isMatch } from "react-day-picker";
import { omit } from "../../util";
import { getLocaleFromString, labels } from "../utils";
import Caption from "./parts/Caption";
import DayButton from "./parts/DayButton";
import DropdownCaption from "./parts/DropdownCaption";
import { HeadRow } from "./parts/HeadRow";
import Row from "./parts/Row";
@@ -94,7 +93,6 @@ export const DatePickerStandalone: DatePickerStandaloneType = forwardRef<
HeadRow,
WeekNumber,
Row,
Day: DayButton,
}}
className="navds-date"
classNames={{ vhidden: "navds-sr-only" }}
@@ -2,8 +2,12 @@
import { Meta, StoryObj } from "@storybook/react";
import isSameDay from "date-fns/isSameDay";
import React, { useId, useState } from "react";
import { useDatepicker, useRangeDatepicker } from "..";
import { BodyLong, Button, HGrid, Modal, VStack } from "../..";
import { Button } from "../../button";
import { HGrid } from "../../layout/grid";
import { VStack } from "../../layout/stack";
import Modal from "../../modal/Modal";
import { BodyLong } from "../../typography";
import { useDatepicker, useRangeDatepicker } from "../hooks";
import DatePicker, { DatePickerProps } from "./DatePicker";
const disabledDays = [
@@ -27,13 +31,13 @@ export const Default: StoryObj<DefaultStoryProps> = {
const [open, setOpen] = useState(false);
const rangeCtx = useRangeDatepicker({
fromDate: new Date("Aug 23 2020"),
toDate: new Date("Aug 23 2023"),
fromDate: new Date("Aug 23 2023"),
toDate: new Date("Aug 23 2029"),
});
const singleCtx = useDatepicker({
fromDate: new Date("Aug 23 2020"),
toDate: new Date("Aug 23 2023"),
toDate: new Date("Aug 23 2029"),
});
const newProps = {
@@ -42,7 +46,7 @@ export const Default: StoryObj<DefaultStoryProps> = {
open,
onClose: () => setOpen(false),
fromDate: new Date("Aug 23 2020"),
toDate: new Date("Aug 23 2023"),
toDate: new Date("Aug 23 2029"),
}
: {}),
};
@@ -1,7 +1,8 @@
import { getUnixTime } from "date-fns";
import React from "react";
import { Day, useDayPicker } from "react-day-picker";
import { useDayPicker } from "react-day-picker";
import { Hide } from "../../../layout/responsive";
import DayButton from "./DayButton";
import WeekNumber from "./WeekNumber";
/**
@@ -37,7 +38,7 @@ function Row(props: RowProps): JSX.Element {
style={styles.cell}
key={getUnixTime(date)}
>
<Day displayMonth={props.displayMonth} date={date} />
<DayButton displayMonth={props.displayMonth} date={date} />
</td>
))}
</tr>
@@ -53,7 +53,7 @@ export interface UseDatepickerOptions
* Allows input of with 'yy' year format.
* @default true
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* In 2023 this equals to 1943 - 2042
* In 2024 this equals to 1944 - 2043
*/
allowTwoDigitYear?: boolean;
/**
@@ -39,7 +39,7 @@ export interface UseMonthPickerOptions
* Allows input of with 'yy' year format.
* @default true
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* In 2023 this equals to 1943 - 2042
* In 2024 this equals to 1944 - 2043
*/
allowTwoDigitYear?: boolean;
/**
@@ -1,8 +1,9 @@
import { Meta, StoryFn } from "@storybook/react";
import setYear from "date-fns/setYear";
import React, { useId, useState } from "react";
import { Button, DateInputProps } from "../..";
import { Button } from "../../button";
import { useMonthpicker } from "../hooks";
import { DateInputProps } from "../parts/DateInput";
import MonthPicker from "./MonthPicker";
import { MonthPickerProps } from "./types";
@@ -15,7 +15,7 @@ export interface DateInputProps
*/
label: React.ReactNode;
/**
* Shows label and description for screenreaders-only
* Shows label and description for screen readers only
* @default false
*/
hideLabel?: boolean;
@@ -1,8 +1,8 @@
import cl from "clsx";
import React, { useContext, useRef } from "react";
import React, { useRef } from "react";
import { Button } from "../../button";
import { Modal } from "../../modal";
import { ModalContext } from "../../modal/ModalContext";
import { useModalContext } from "../../modal/Modal.context";
import { Popover } from "../../popover";
import { useMedia } from "../../util/hooks";
import { modalCloseButtonLabel, modalLabel } from "../utils/labels";
@@ -30,7 +30,7 @@ export const DateWrapper = ({
popoverProps,
}: DateWrapperProps) => {
const modalRef = useRef<HTMLDialogElement>(null);
const isInModal = useContext(ModalContext) !== null;
const isInModal = useModalContext(false) !== undefined;
const hideModal =
useMedia("screen and (min-width: 768px)", true) && !isInModal;
@@ -1,7 +1,7 @@
import { Meta } from "@storybook/react";
import React, { useState } from "react";
import { Dropdown } from "..";
import { Button } from "../button";
import Dropdown from "./Dropdown";
export default {
title: "ds-react/Dropdown",
+9 -7
View File
@@ -3,6 +3,7 @@ import React, { forwardRef, useState } from "react";
import { BodyShort, ErrorMessage, Label } from "../typography";
import { omit } from "../util";
import TextareaAutosize from "../util/TextareaAutoSize";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useId } from "../util/hooks";
import { ReadOnlyIcon } from "./ReadOnlyIcon";
import Counter from "./TextareaCounter";
@@ -102,7 +103,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
const maxLengthId = useId();
const hasMaxLength = maxLength !== undefined && maxLength > 0;
const [controlledValue, setControlledValue] = useState<string>(
const [uncontrolledValue, setUncontrolledValue] = useState(
props?.defaultValue ?? "",
);
@@ -161,11 +162,12 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
<TextareaAutosize
{...omit(rest, ["error", "errorId", "size"])}
{...inputProps}
onChange={(e) =>
props.onChange
? props.onChange(e)
: setControlledValue(e.target.value)
}
onChange={composeEventHandlers(
props.onChange,
props.value === undefined
? (e) => setUncontrolledValue(e.target.value)
: undefined,
)}
minRows={getMinRows()}
autoScrollbar={UNSAFE_autoScrollbar}
ref={ref}
@@ -184,7 +186,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
</span>
<Counter
maxLength={maxLength}
currentLength={props.value?.length ?? controlledValue?.length}
currentLength={props.value?.length ?? uncontrolledValue.length}
size={size}
i18n={i18n}
/>
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import React from "react";
import { ErrorSummary } from "..";
import { ErrorSummary } from "./ErrorSummary";
export default {
title: "ds-react/Errorsummary",
@@ -66,6 +66,10 @@ export interface SearchProps
* Exposes the HTML size attribute
*/
htmlSize?: number | string;
/*
* Exposes role attribute
*/
role?: string;
}
interface SearchComponent
@@ -114,6 +118,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
onChange,
onSearchClick,
htmlSize,
role,
...rest
} = props;
@@ -205,7 +210,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
value={value ?? internalValue}
onChange={(e) => handleChange(e.target.value)}
type="search"
role="searchbox"
role={role ?? "searchbox"}
className={cl(
className,
"navds-search__input",
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import React, { useState } from "react";
import { Link } from "../..";
import { Link } from "../../link";
import { ConfirmationPanel } from "../index";
export default {
@@ -1,6 +1,8 @@
import { Meta, StoryFn, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import React, { useState } from "react";
import { Button } from "../../button";
import { Modal } from "../../modal";
import { Textarea } from "../index";
const meta: Meta<typeof Textarea> = {
@@ -124,6 +126,24 @@ export const Resize: StoryFn = () => {
return <Textarea resize label="Ipsum enim quis culpa" />;
};
export const OnChange: StoryFn = () => {
return (
<Textarea
label="Ipsum enim quis culpa"
onChange={console.log}
maxLength={50}
/>
);
};
OnChange.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const input = canvas.getByLabelText("Ipsum enim quis culpa");
userEvent.click(input);
await userEvent.type(input, "Aute fugiat ut culpa");
const text = canvas.getByText("30 tegn igjen");
expect(text).toBeVisible();
};
export const Controlled: StoryFn = () => {
const [value, setValue] = useState("");
return (
@@ -186,3 +206,20 @@ AutoScrollbar.argTypes = {
maxRows: { type: "number" },
minRows: { type: "number" },
};
export const ModalStrictMode: StoryFn<typeof Textarea> = () => {
// Story added after fixing an issue where TextareaAutoSize would reach max re-renders
// and set the height to 2px when used in StrictMode in a Modal that is initially open.
return (
<React.StrictMode>
<Modal open>
<Modal.Body>
<Textarea label="Har du noen tilbakemeldinger?" />
</Modal.Body>
</Modal>
</React.StrictMode>
);
};
ModalStrictMode.parameters = {
chromatic: { disable: true }, // Not reproducable in Chromatic
};
@@ -1,6 +1,7 @@
import { Meta, StoryFn, StoryObj } from "@storybook/react";
import React, { useEffect, useRef } from "react";
import { BodyLong, Heading, HelpText } from "..";
import { BodyLong, Heading } from "../typography";
import HelpText from "./HelpText";
const meta: Meta<typeof HelpText> = {
title: "ds-react/HelpText",
+1
View File
@@ -10,6 +10,7 @@ export * from "./expansion-card";
export * from "./form";
export * from "./grid";
export * from "./guide-panel";
export * from "./overlays";
export * from "./help-text";
export * from "./internal-header";
export * from "./link";
@@ -6,7 +6,8 @@ import {
MenuGridIcon,
} from "@navikt/aksel-icons";
import { BodyLong, BodyShort, Detail } from "@navikt/ds-react";
import { Dropdown, InternalHeader } from "..";
import { Dropdown } from "../dropdown";
import InternalHeader from "./InternalHeader";
export default {
title: "ds-react/InternalHeader",
@@ -1,7 +1,8 @@
import type { Meta } from "@storybook/react";
import React from "react";
import { Box, HStack, VStack } from "../..";
import { BodyLong } from "../../typography";
import { Box } from "../box";
import { HStack, VStack } from "../stack";
import { Bleed } from "./Bleed";
export default {
@@ -272,6 +273,19 @@ export const Full = {
</Bleed>
</Box>
</Box>
<Box
className="maxWidth"
background="surface-alt-1-subtle"
padding="10"
>
<Box background="surface-alt-2-subtle" padding="10">
<Bleed marginInline="full" reflectivePadding asChild>
<Box background="surface-success-subtle">
<BodyLong>full + reflectivePadding</BodyLong>
</Box>
</Bleed>
</Box>
</Box>
</VStack>
</>
),
@@ -1,8 +1,9 @@
import type { Meta } from "@storybook/react";
import React, { ReactNode } from "react";
import { ChevronRightIcon } from "@navikt/aksel-icons";
import { HGrid, HStack, VStack } from "../..";
import { BodyLong, Detail, Heading } from "../../typography";
import { HGrid } from "../grid";
import { HStack, VStack } from "../stack";
import { BackgroundToken, BorderRadiiToken } from "../utilities/types";
import { Box } from "./Box";
@@ -1,6 +1,9 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { Box, HGrid, Hide, Show, VStack } from "../..";
import { Box } from "../box";
import { HGrid } from "../grid";
import { Hide, Show } from "../responsive";
import { VStack } from "../stack";
import { Content } from "./Content";
import { FilterCard } from "./Filter";
import { Header } from "./Header";
@@ -13,6 +16,7 @@ const meta = {
title: "kitchen sink/navno-sidemal",
parameters: {
layout: "fullscreen",
chromatic: { disable: true },
},
} satisfies Meta;
@@ -44,6 +44,9 @@ const translateTokenStringToCSS = (
if (componentProp === "margin-inline" && x === "full") {
const width = 100 / arr.length;
return `calc((100vw - ${width}%)/-2)`;
} else if (componentProp === "padding-inline" && x === "full") {
const width = 100 / arr.length;
return `calc((100vw - ${width}%)/2)`;
}
let output = `var(--a-${tokenSubgroup}-${x})`;
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import React from "react";
import { LinkPanel } from "..";
import LinkPanel from "./LinkPanel";
export default {
title: "ds-react/LinkPanel",
@@ -1,11 +1,9 @@
import React from "react";
import { PlusCircleFillIcon } from "@navikt/aksel-icons";
import {
BodyLong,
Alert as DsAlert,
ConfirmationPanel as DsConfirmationPanel,
Link,
} from "../..";
import { Alert } from "../../alert";
import { ConfirmationPanel } from "../../form";
import { BodyLong } from "../../typography";
import Link from "../Link";
import { RandomIcon } from "./RandomIcon";
export default {
@@ -139,37 +137,37 @@ const DemoLink = () => (
export const Icon = () => <DemoLink />;
export const Alert = () => {
export const InAlert = () => {
return (
<div className="colgap">
<DsAlert variant="info">
<Alert variant="info">
<DemoLink />
</DsAlert>
<DsAlert variant="success">
</Alert>
<Alert variant="success">
<DemoLink />
</DsAlert>
<DsAlert variant="warning">
</Alert>
<Alert variant="warning">
<DemoLink />
</DsAlert>
<DsAlert variant="error">
</Alert>
<Alert variant="error">
<DemoLink />
</DsAlert>
</Alert>
</div>
);
};
export const ConfirmationPanel = () => {
export const InConfirmationPanel = () => {
return (
<div className="colgap">
<DsConfirmationPanel label="demo">
<ConfirmationPanel label="demo">
<DemoLink />
</DsConfirmationPanel>
<DsConfirmationPanel checked label="demo">
</ConfirmationPanel>
<ConfirmationPanel checked label="demo">
<DemoLink />
</DsConfirmationPanel>
<DsConfirmationPanel error="demo" label="demo">
</ConfirmationPanel>
<ConfirmationPanel error="demo" label="demo">
<DemoLink />
</DsConfirmationPanel>
</ConfirmationPanel>
</div>
);
};
+2 -1
View File
@@ -6,7 +6,8 @@ import {
HeadHeartIcon,
RecycleIcon,
} from "@navikt/aksel-icons";
import { List, VStack } from "..";
import { VStack } from "../layout/stack";
import List from "./List";
export default {
title: "ds-react/List",
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import React from "react";
import { Loader, LoaderProps } from "..";
import Loader, { LoaderProps } from "./Loader";
export default {
title: "ds-react/Loader",
@@ -0,0 +1,13 @@
import React from "react";
import { createContext } from "../util/create-context";
interface ModalContextProps {
closeHandler?: React.MouseEventHandler<HTMLButtonElement>;
ref: React.RefObject<HTMLDialogElement>;
}
export const [ModalContextProvider, useModalContext] =
createContext<ModalContextProps>({
name: "ModalContext",
errorMessage: "<Modal.Header> must be used within a <Modal>",
});
+6 -8
View File
@@ -8,8 +8,8 @@ import { Detail, Heading } from "../typography";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useId } from "../util/hooks";
import { useMergeRefs } from "../util/hooks/useMergeRefs";
import { ModalContextProvider, useModalContext } from "./Modal.context";
import ModalBody from "./ModalBody";
import { ModalContext } from "./ModalContext";
import ModalFooter from "./ModalFooter";
import ModalHeader from "./ModalHeader";
import { getCloseHandler, useBodyScrollLock } from "./ModalUtils";
@@ -98,7 +98,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
const portalNode = useFloatingPortalNode({ root: rootElement });
const dateContext = useContext(DateContext);
const modalContext = useContext(ModalContext);
const modalContext = useModalContext(false);
if (modalContext && !dateContext) {
console.error("Modals should not be nested");
}
@@ -185,11 +185,9 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
onClick={composeEventHandlers(onClick, handleModalClick)}
aria-labelledby={mergedAriaLabelledBy}
>
<ModalContext.Provider
value={{
closeHandler: getCloseHandler(modalRef, header, onBeforeClose),
ref: modalRef,
}}
<ModalContextProvider
closeHandler={getCloseHandler(modalRef, header, onBeforeClose)}
ref={modalRef}
>
{header && (
<ModalHeader>
@@ -208,7 +206,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
)}
{children}
</ModalContext.Provider>
</ModalContextProvider>
</dialog>
);
@@ -1,7 +0,0 @@
import React from "react";
interface ModalContextProps {
closeHandler?: React.MouseEventHandler<HTMLButtonElement>;
ref: React.RefObject<HTMLDialogElement>;
}
export const ModalContext = React.createContext<ModalContextProps | null>(null);
+3 -7
View File
@@ -1,8 +1,8 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import React, { forwardRef } from "react";
import { XMarkIcon } from "@navikt/aksel-icons";
import { Button } from "../button";
import { ModalContext } from "./ModalContext";
import { useModalContext } from "./Modal.context";
export interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
@@ -15,11 +15,7 @@ export interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
const ModalHeader = forwardRef<HTMLDivElement, ModalHeaderProps>(
({ children, className, closeButton = true, ...rest }, ref) => {
const context = useContext(ModalContext);
if (context === null) {
console.error("<Modal.Header> has to be used within a <Modal>");
return null;
}
const context = useModalContext();
return (
<div {...rest} ref={ref} className={cl("navds-modal__header", className)}>
@@ -1,9 +1,11 @@
import { Meta, StoryFn } from "@storybook/react";
import React, { useRef, useState } from "react";
import { FileIcon } from "@navikt/aksel-icons";
import { BodyLong, Button, Heading, Tooltip } from "..";
import { Button } from "../button";
import { Checkbox, CheckboxGroup } from "../form/checkbox";
import { VStack } from "../layout/stack";
import { Tooltip } from "../tooltip";
import { BodyLong, Heading } from "../typography";
import Modal from "./Modal";
const meta: Meta<typeof Modal> = {
+2
View File
@@ -0,0 +1,2 @@
export { Portal } from "./portal/Portal";
export type { PortalProps } from "./portal/Portal";
@@ -0,0 +1,99 @@
import React from "react";
import { Box } from "../../layout/box";
import { Provider } from "../../provider";
import { Portal } from "./Portal";
export default {
title: "Utilities/Portal",
parameters: {
chromatic: { disable: true },
},
};
export const Default = () => {
return (
<Box background="surface-neutral-subtle" border>
<h1>In regular DOM tree</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Temporibus
necessitatibus quis esse nesciunt est velit voluptatibus. Distinctio eum
commodi tempora unde. Nulla vel tempora incidunt? Voluptatem molestias
impedit commodi. Tenetur!
</p>
<Portal>
<h1>Inside Portal to different DOM tree</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Temporibus
necessitatibus quis esse nesciunt est velit voluptatibus. Distinctio
eum commodi tempora unde. Nulla vel tempora incidunt? Voluptatem
molestias impedit commodi. Tenetur!
</p>
</Portal>
</Box>
);
};
export const CustomPortalRoot = () => {
const [portalContainer, setPortalContainer] =
React.useState<HTMLDivElement | null>(null);
return (
<Box background="surface-neutral-subtle">
<Box background="surface-alt-1-subtle">
<h1>Tree A</h1>
<Portal rootElement={portalContainer}>
<p>This is mounted to Tree B, while created inside Tree A</p>
</Portal>
</Box>
<Box background="surface-alt-3-subtle" ref={setPortalContainer}>
<h1>Tree B</h1>
</Box>
</Box>
);
};
export const CustomPortalRootFromProvider = () => {
const [portalContainer, setPortalContainer] =
React.useState<HTMLDivElement>();
return (
<Provider rootElement={portalContainer}>
<Box background="surface-neutral-subtle">
<Box background="surface-alt-1-subtle">
<h1>Tree A</h1>
<Portal>
<p>This is mounted to Tree B, while created inside Tree A</p>
</Portal>
</Box>
<Box background="surface-alt-3-subtle" ref={setPortalContainer}>
<h1>Tree B</h1>
</Box>
</Box>
</Provider>
);
};
export const AsChild = () => {
return (
<Box background="surface-neutral-subtle" border>
<h1>In regular DOM tree</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Temporibus
necessitatibus quis esse nesciunt est velit voluptatibus. Distinctio eum
commodi tempora unde. Nulla vel tempora incidunt? Voluptatem molestias
impedit commodi. Tenetur!
</p>
<Portal asChild>
<div data-this-is-the-child>
<h1>Inside Portal to different DOM tree</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Temporibus
necessitatibus quis esse nesciunt est velit voluptatibus. Distinctio
eum commodi tempora unde. Nulla vel tempora incidunt? Voluptatem
molestias impedit commodi. Tenetur!
</p>
</div>
</Portal>
</Box>
);
};
@@ -0,0 +1,32 @@
import React, { HTMLAttributes, forwardRef } from "react";
import ReactDOM from "react-dom";
import { useProvider } from "../../provider";
import { Slot } from "../../util/Slot";
import { AsChildProps } from "../../util/types";
interface PortalBaseProps extends HTMLAttributes<HTMLDivElement> {
/**
* An optional container where the portaled content should be appended.
*/
rootElement?: HTMLElement | null;
}
export type PortalProps = PortalBaseProps & AsChildProps;
export const Portal = forwardRef<HTMLDivElement, PortalProps>(
({ rootElement, asChild, ...rest }, ref) => {
const contextRoot = useProvider()?.rootElement;
const root = rootElement ?? contextRoot ?? globalThis?.document?.body;
const Component = asChild ? Slot : "div";
return root
? ReactDOM.createPortal(
<Component ref={ref} data-aksel-portal="" {...rest} />,
root,
)
: null;
},
);
export default Portal;
+9 -16
View File
@@ -2,6 +2,9 @@ import cl from "clsx";
import React, { forwardRef } from "react";
import { OverridableComponent } from "../util/types";
/**
* @deprecated Use Box with padding and border instead
*/
export interface PanelProps extends React.HTMLAttributes<HTMLElement> {
/**
* Panel content
@@ -17,23 +20,13 @@ export interface PanelProps extends React.HTMLAttributes<HTMLElement> {
export type PanelType = OverridableComponent<PanelProps, HTMLElement>;
/**
* A component that displays a bordered panel with heading and body.
*
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/panel)
* @see 🏷️ {@link PanelProps}
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*
* @example
* ```jsx
* <Panel border>
* <Heading spacing level="2" size="large">
* Søk om økonomisk sosialhjelp
* </Heading>
* <BodyLong>
* Du kan søke om det du trenger økonomisk støtte til.
* </BodyLong>
* </Panel>
* @deprecated
* Use Box with padding and border instead
* ```
* <Box padding="4" borderRadius="small" />
* <Box padding="4" borderWidth="1" borderRadius="small" />
* ```
* Component will be removed in a future major release
*/
export const Panel: PanelType = forwardRef(
(
@@ -1,63 +0,0 @@
import { Meta } from "@storybook/react";
import React from "react";
import { Panel } from "../index";
export default {
title: "ds-react/Panel",
component: Panel,
argTypes: {
border: {
defaultValue: true,
control: {
type: "boolean",
},
},
},
} as Meta;
export const Default = {
render: (props) => {
return (
<Panel {...props}>
Eu quis exercitation voluptate ex. Aute irure esse occaecat minim
cupidatat velit minim duis sint culpa anim laboris. Consectetur nulla eu
commodo ea culpa velit commodo incididunt sunt ipsum. Amet anim
adipisicing minim ipsum do. Non laborum culpa reprehenderit est sint
officia quis excepteur. Fugiat eiusmod eiusmod commodo incididunt nisi
minim ex eu cupidatat quis ex. Lorem irure ea ea enim consectetur ea
aliqua cupidatat. Officia aute veniam aute exercitation ipsum aliqua
adipisicing. Est aliqua ad ullamco aute nostrud amet ea adipisicing
consequat id officia irure pariatur. Eu mollit amet culpa culpa velit.
</Panel>
);
},
};
export const Border = () => {
return (
<div className="colgap">
<Panel>
Eu quis exercitation voluptate ex. Aute irure esse occaecat minim
cupidatat velit minim duis sint culpa anim laboris. Consectetur nulla eu
commodo ea culpa velit commodo incididunt sunt ipsum. Amet anim
adipisicing minim ipsum do. Non laborum culpa reprehenderit est sint
officia quis excepteur. Fugiat eiusmod eiusmod commodo incididunt nisi
minim ex eu cupidatat quis ex. Lorem irure ea ea enim consectetur ea
aliqua cupidatat. Officia aute veniam aute exercitation ipsum aliqua
adipisicing. Est aliqua ad ullamco aute nostrud amet ea adipisicing
consequat id officia irure pariatur. Eu mollit amet culpa culpa velit.
</Panel>
<Panel border>
Eu quis exercitation voluptate ex. Aute irure esse occaecat minim
cupidatat velit minim duis sint culpa anim laboris. Consectetur nulla eu
commodo ea culpa velit commodo incididunt sunt ipsum. Amet anim
adipisicing minim ipsum do. Non laborum culpa reprehenderit est sint
officia quis excepteur. Fugiat eiusmod eiusmod commodo incididunt nisi
minim ex eu cupidatat quis ex. Lorem irure ea ea enim consectetur ea
aliqua cupidatat. Officia aute veniam aute exercitation ipsum aliqua
adipisicing. Est aliqua ad ullamco aute nostrud amet ea adipisicing
consequat id officia irure pariatur. Eu mollit amet culpa culpa velit.
</Panel>
</div>
);
};
+2 -2
View File
@@ -18,7 +18,7 @@ import React, {
useRef,
} from "react";
import { DateContext } from "../date/context";
import { ModalContext } from "../modal/ModalContext";
import { useModalContext } from "../modal/Modal.context";
import { useClientLayoutEffect, useEventListener } from "../util/hooks";
import { useMergeRefs } from "../util/hooks/useMergeRefs";
import PopoverContent, { PopoverContentType } from "./PopoverContent";
@@ -126,7 +126,7 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
ref,
) => {
const arrowRef = useRef<HTMLDivElement | null>(null);
const isInModal = useContext(ModalContext) !== null;
const isInModal = useModalContext(false) !== undefined;
const isInDatepicker = useContext(DateContext) !== null;
const chosenStrategy = userStrategy ?? (isInModal ? "fixed" : "absolute");
const chosenFlip = isInDatepicker ? false : _flip;
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import React, { useState } from "react";
import { BodyLong } from "..";
import { BodyLong } from "../typography";
import Stepper from "./Stepper";
const meta: Meta<typeof Stepper> = {
@@ -1,16 +1,27 @@
import React, { useState } from "react";
import { Table, TableProps } from "../";
import { Alert, Button, Checkbox, CheckboxGroup, Link } from "../..";
import { Button } from "../../button";
import { Checkbox } from "../../form";
import { VStack } from "../../layout/stack";
import {
Expandable,
ExpandableLarge,
ExpandableSmall,
} from "./table-2-expandable.stories";
export default {
title: "ds-react/Table",
component: Table,
parameters: {
chromatic: { disable: true },
},
};
interface Props extends TableProps {
button?: boolean;
shadeOnHover?: boolean;
}
const TableComponent = ({ button, shadeOnHover, ...rest }: Props) => (
<Table {...rest}>
<Table.Header>
@@ -89,75 +100,59 @@ export const Buttons = () => <TableComponent size="small" button />;
export const WithDivs = () => {
return (
<>
<Alert variant="warning">
Obs! Hvis man skal bygge tabeller uten å bruke vanlig {"<tabell> "}
-markup er det svært viktig at man supplerer elementene med{" "}
<Link href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/table/table.html">
riktige
</Link>{" "}
role-attributter og display-stiler for å bevare den semantiske verdien i
tabellene. Vi anbefaler fortsatt å ikke ta i bruk denne metoden hvis
mulig, da nettleseren ikke kan tolke tabellen like bra uten riktig
markup.
</Alert>
<div className="navds-table" role="table">
<div className="navds-table__header" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__header-cell" role="columnheader">
Fornavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Etternavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Rolle
</div>
<div className="navds-table" role="table">
<div className="navds-table__header" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__header-cell" role="columnheader">
Fornavn
</div>
</div>
<div className="navds-table__body" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Jean-Luc
</div>
<div className="navds-table__data-cell" role="cell">
Picard
</div>
<div className="navds-table__data-cell" role="cell">
Kaptein
</div>
<div className="navds-table__header-cell" role="columnheader">
Etternavn
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
William
</div>
<div className="navds-table__data-cell" role="cell">
Riker
</div>
<div className="navds-table__data-cell" role="cell">
Kommandør
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Geordi
</div>
<div className="navds-table__data-cell" role="cell">
La Forge
</div>
<div className="navds-table__data-cell" role="cell">
Sjefsingeniør
</div>
<div className="navds-table__header-cell" role="columnheader">
Rolle
</div>
</div>
</div>
</>
<div className="navds-table__body" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Jean-Luc
</div>
<div className="navds-table__data-cell" role="cell">
Picard
</div>
<div className="navds-table__data-cell" role="cell">
Kaptein
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
William
</div>
<div className="navds-table__data-cell" role="cell">
Riker
</div>
<div className="navds-table__data-cell" role="cell">
Kommandør
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Geordi
</div>
<div className="navds-table__data-cell" role="cell">
La Forge
</div>
<div className="navds-table__data-cell" role="cell">
Sjefsingeniør
</div>
</div>
</div>
</div>
);
};
export const Selection = () => <SelectionTable />;
export const SelectionSmall = () => <SelectionTable size="small" />;
const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
const useToggleList = (initialState) => {
const [list, setList] = useState(initialState);
@@ -182,7 +177,7 @@ const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
<Table.DataCell>
<Checkbox
size={size}
checked={selectedRows.includes("all")}
indeterminate
onChange={() => toggleSelectedRow("all")}
>
Select all
@@ -212,12 +207,12 @@ const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
<Table.DataCell>USA</Table.DataCell>
<Table.DataCell>38</Table.DataCell>
</Table.Row>
<Table.Row selected={selectedRows.includes("2")}>
<Table.Row selected>
<Table.DataCell>
<Checkbox
size={size}
hideLabel
checked={selectedRows.includes("2")}
checked
onChange={() => toggleSelectedRow("2")}
aria-labelledby={`x_r2-${size}`}
>
@@ -231,36 +226,67 @@ const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
<Table.DataCell>Denmark</Table.DataCell>
<Table.DataCell>11</Table.DataCell>
</Table.Row>
<Table.Row selected={selectedRows.includes("3")}>
<Table.DataCell>
<CheckboxGroup legend="velg flere felt" hideLegend>
<Checkbox
size={size}
hideLabel
checked={selectedRows.includes("3")}
onChange={() => toggleSelectedRow("3")}
aria-labelledby={`x_r3-${size}`}
>
{" "}
</Checkbox>
<Checkbox
size={size}
hideLabel
checked={selectedRows.includes("3")}
onChange={() => toggleSelectedRow("3")}
aria-labelledby={`x_r3-${size}`}
>
{" "}
</Checkbox>
</CheckboxGroup>
</Table.DataCell>
<Table.HeaderCell scope="row" colSpan={4}>
<span id={`x_r3-${size}`}>
Don&apos;t stack multiple checkboxes
</span>
</Table.HeaderCell>
</Table.Row>
</Table.Body>
</Table>
);
};
export const Selection = () => <SelectionTable />;
export const SelectionSmall = () => <SelectionTable size="small" />;
export const Chromatic = {
render: () => (
<VStack gap="8">
<div>
<h3>Default</h3>
<Default />
<h3>Zebra</h3>
<Zebra />
</div>
<div>
<h3>Large</h3>
<SizeLarge />
</div>
<div>
<h3>Medium</h3>
<SizeMedium />
</div>
<div>
<h3>Small</h3>
<SizeSmall />
</div>
<div>
<h3>With Buttons</h3>
<Buttons />
</div>
<div>
<h3>Custom with divs</h3>
<WithDivs />
</div>
<div>
<h3>Selection</h3>
<Selection />
</div>
<div>
<h3>Selection small</h3>
<SelectionSmall />
</div>
<h2>Expandable</h2>
<div>
<h3>Large</h3>
<ExpandableLarge />
</div>
<div>
<h3>Medium</h3>
<Expandable />
</div>
<div>
<h3>Small</h3>
<ExpandableSmall />
</div>
</VStack>
),
parameters: {
chromatic: { disable: false },
},
};
@@ -1,10 +1,15 @@
import React, { useState } from "react";
import { Table } from "..";
import { Button, Checkbox, Link } from "../..";
import { Button } from "../../button";
import { Checkbox } from "../../form";
import { Link } from "../../link";
import Table from "../Table";
export default {
title: "ds-react/Table",
component: Table,
parameters: {
chromatic: { disable: true },
},
};
export const Expandable = () => {
@@ -25,6 +30,7 @@ export const Expandable = () => {
content={row.content}
key={row.name}
togglePlacement="right"
defaultOpen
>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{row[key]}</Table.DataCell>
@@ -50,7 +56,7 @@ export const ExpandableLarge = () => {
</Table.Header>
<Table.Body>
{data.slice(0, 1).map((row) => (
<Table.ExpandableRow content={row.content} key={row.name}>
<Table.ExpandableRow defaultOpen content={row.content} key={row.name}>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{row[key]}</Table.DataCell>
))}
@@ -87,7 +93,7 @@ export const ExpandableSmall = () => {
</Table.Header>
<Table.Body>
{data.slice(0, 1).map((row) => (
<Table.ExpandableRow content={row.content} key={row.name}>
<Table.ExpandableRow defaultOpen content={row.content} key={row.name}>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{row[key]}</Table.DataCell>
))}
@@ -235,36 +241,6 @@ const data = [
},
];
export const ExpandableOpen = () => {
return (
<Table zebraStripes>
<Table.Header>
<Table.Row>
{columns.map(({ key, name }) => (
<Table.HeaderCell key={key}>{name}</Table.HeaderCell>
))}
<Table.HeaderCell />
</Table.Row>
</Table.Header>
<Table.Body>
{data.map((row) => (
<Table.ExpandableRow
expansionDisabled={row.animal === "Sel"}
content={row.content}
key={row.name}
togglePlacement="right"
defaultOpen
>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{row[key]}</Table.DataCell>
))}
</Table.ExpandableRow>
))}
</Table.Body>
</Table>
);
};
export const ClickableRow = () => {
const [isRowOpen1, setIsRowOpen1] = useState(false);
const [isRowOpen2, setIsRowOpen2] = useState(false);
@@ -1,14 +1,16 @@
import React, { useState } from "react";
import useSWR from "swr";
import { Loader, Pagination, SortState } from "../..";
import { Table } from "../index";
import { Loader } from "../../loader";
import { Pagination } from "../../pagination";
import Table from "../Table";
import { SortState } from "../types";
import peopleJson from "./people.json";
export default {
title: "ds-react/Table",
component: Table,
parameters: {
chromatic: { delay: 500 },
chromatic: { disable: true },
},
};
@@ -1,10 +1,13 @@
import { expect, fn, userEvent, within } from "@storybook/test";
import React from "react";
import { Table } from "../..";
import Table from "../../Table";
export default {
title: "ds-react/Table/Tests",
component: Table,
parameters: {
chromatic: { disable: true },
},
};
export const ClickableRowTest = {
+109
View File
@@ -0,0 +1,109 @@
import { fireEvent, render, screen } from "@testing-library/react";
import React from "react";
import { Tabs } from "./Tabs";
const TestTabs = ({
value,
onChange,
defaultValue,
selectionFollowsFocus,
loop,
iconPosition,
}: any) => (
<Tabs
value={value}
onChange={onChange}
defaultValue={defaultValue}
selectionFollowsFocus={selectionFollowsFocus}
loop={loop}
iconPosition={iconPosition}
>
<Tabs.List data-testid="tablist">
<Tabs.Tab value="tab1" data-testid="tab1" label="Tab 1" />
<Tabs.Tab value="tab2" data-testid="tab2" label="Tab 2" />
<Tabs.Tab value="tab3" data-testid="tab3" label="Tab 3" />
</Tabs.List>
<Tabs.Panel value="tab1" data-testid="tabpanel1">
Tabpanel 1
</Tabs.Panel>
<Tabs.Panel value="tab2" data-testid="tabpanel2">
Tabpanel 2
</Tabs.Panel>
<Tabs.Panel value="tab3" data-testid="tabpanel3">
Tabpanel 3
</Tabs.Panel>
</Tabs>
);
describe("Tabs", () => {
test("sets default value correctly", () => {
render(<TestTabs defaultValue="tab2" />);
const tab = screen.getByTestId("tab2");
const panel = screen.getByTestId("tabpanel2");
expect(tab).toHaveAttribute("aria-selected", "true");
expect(panel).toHaveTextContent("Tabpanel 2");
});
test("label-connection between tab and tabpanel is correct", async () => {
render(<TestTabs defaultValue="tab2" />);
const tab = screen.getByTestId("tab2");
const panel = screen.getByTestId("tabpanel2");
const controlsId = tab.getAttribute("aria-controls");
const panelLabelledBy = panel.getAttribute("aria-labelledby");
expect(controlsId).toEqual(panel.id);
expect(tab.id).toEqual(panelLabelledBy);
});
test("sets correct attributes on active tab", () => {
render(<TestTabs defaultValue="tab2" />);
const tab = screen.getByTestId("tab2");
expect(tab).toHaveAttribute("aria-selected", "true");
expect(tab).toHaveAttribute("role", "tab");
expect(tab).toHaveAttribute("aria-controls");
expect(tab).toHaveAttribute("tabindex", "-1");
});
test("sets correct attributes on idle tab", () => {
render(<TestTabs defaultValue="tab2" />);
const tab = screen.getByTestId("tab1");
expect(tab).toHaveAttribute("aria-selected", "false");
expect(tab).toHaveAttribute("role", "tab");
expect(tab).toHaveAttribute("aria-controls");
expect(tab).toHaveAttribute("tabindex", "-1");
});
test("sets correct attributes on active tabpanel", () => {
render(<TestTabs defaultValue="tab2" />);
const panel = screen.getByTestId("tabpanel2");
expect(panel).toHaveAttribute("aria-labelledby");
expect(panel).toHaveAttribute("role", "tabpanel");
expect(panel).toHaveAttribute("tabindex", "0");
expect(panel).toHaveTextContent("Tabpanel 2");
expect(panel).toHaveStyle({ display: "block" });
});
test("sets correct attributes on idle tabpanel", () => {
render(<TestTabs defaultValue="tab1" />);
const panel = screen.getByTestId("tabpanel2");
expect(panel).toHaveAttribute("aria-labelledby");
expect(panel).toHaveAttribute("role", "tabpanel");
expect(panel).toHaveAttribute("tabindex", "0");
expect(panel).toBeEmptyDOMElement();
expect(panel).toHaveStyle({ display: "none" });
});
test("sets tabindex to 0 when focused", () => {
render(<TestTabs defaultValue="tab2" />);
const tab = screen.getByTestId("tab2");
fireEvent.focus(tab);
expect(tab).toHaveAttribute("tabindex", "0");
});
});
@@ -0,0 +1,54 @@
import { fireEvent, render, screen } from "@testing-library/react";
import React from "react";
import { ToggleGroup } from "./ToggleGroup";
const TestToggleGroup = ({ value, onChange, defaultValue }: any) => (
<ToggleGroup value={value} onChange={onChange} defaultValue={defaultValue}>
<ToggleGroup.Item value="toggle1" data-testid="toggle1">
Toggle 1
</ToggleGroup.Item>
<ToggleGroup.Item value="toggle2" data-testid="toggle2">
Toggle 2
</ToggleGroup.Item>
<ToggleGroup.Item value="toggle3" data-testid="toggle3">
Toggle 3
</ToggleGroup.Item>
</ToggleGroup>
);
describe("ToggleGroup", () => {
test("sets default value correctly", () => {
render(<TestToggleGroup defaultValue="toggle2" />);
const toggle = screen.getByTestId("toggle2");
expect(toggle).toHaveAttribute("aria-checked", "true");
});
test("sets correct attributes on active toggle", () => {
render(<TestToggleGroup defaultValue="toggle2" />);
const toggle = screen.getByTestId("toggle2");
expect(toggle).toHaveAttribute("aria-checked", "true");
expect(toggle).toHaveAttribute("role", "radio");
expect(toggle).toHaveAttribute("type", "button");
expect(toggle).toHaveAttribute("tabindex", "-1");
});
test("sets correct attributes on idle toggle", () => {
render(<TestToggleGroup defaultValue="toggle1" />);
const toggle = screen.getByTestId("toggle2");
expect(toggle).toHaveAttribute("aria-checked", "false");
expect(toggle).toHaveAttribute("role", "radio");
expect(toggle).toHaveAttribute("type", "button");
expect(toggle).toHaveAttribute("tabindex", "-1");
});
test("sets tabindex to 0 when focused", () => {
render(<TestToggleGroup defaultValue="toggle2" />);
const toggle = screen.getByTestId("toggle2");
fireEvent.focus(toggle);
expect(toggle).toHaveAttribute("tabindex", "0");
});
});
+7 -17
View File
@@ -1,5 +1,4 @@
import {
FloatingPortal,
autoUpdate,
arrow as flArrow,
flip,
@@ -13,15 +12,9 @@ import {
useInteractions,
} from "@floating-ui/react";
import cl from "clsx";
import React, {
HTMLAttributes,
cloneElement,
forwardRef,
useContext,
useRef,
} from "react";
import { ModalContext } from "../modal/ModalContext";
import { useProvider } from "../provider";
import React, { HTMLAttributes, cloneElement, forwardRef, useRef } from "react";
import { useModalContext } from "../modal/Modal.context";
import Portal from "../overlays/portal/Portal";
import { Detail } from "../typography";
import { useId } from "../util/hooks";
import { useControllableState } from "../util/hooks/useControllableState";
@@ -122,11 +115,8 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
});
const arrowRef = useRef<HTMLDivElement | null>(null);
const modalContext = useContext(ModalContext);
const providerRootElement = useProvider()?.rootElement;
const rootElement = modalContext
? modalContext.ref.current
: providerRootElement;
const modalContext = useModalContext(false);
const rootElement = modalContext ? modalContext.ref.current : undefined;
const {
x,
@@ -199,7 +189,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
: children?.props["aria-describedby"],
}),
)}
<FloatingPortal root={rootElement}>
<Portal rootElement={rootElement}>
{_open && (
<div
{...getFloatingProps({
@@ -253,7 +243,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
)}
</div>
)}
</FloatingPortal>
</Portal>
</>
);
},
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff. Show more