Compare commits

..
Author SHA1 Message Date
github-actions[bot] 2887ecef6c Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.26
 - @navikt/ds-react-internal@0.12.29
2022-04-20 07:59:53 +00:00
Andreas Nordahl b79b1aba80 [fix] restrict text field value to string for clarity/simplicity (#1503) 2022-04-20 09:51:09 +02:00
Andreas Nordahl 21cfce48c7 add dropdown to next example app (#1506) 2022-04-19 13:11:07 +02:00
github-actions[bot] dcef0bf82f Lerna versionbump [ci skip]
- @navikt/ds-react-internal@0.12.28
2022-04-19 08:45:31 +00:00
Sigurd GrønengandAndreas Nordahl 2ec92d5b01 [fix] add "children" field to Dropdown-prop-type (#1504)
* Add "children" field to Dropdown-prop-type

Hei! Hvis jeg forstår denne komponenten riktig så mangler den typingen til "children" hvis man bruker den i React 18. I React 18 så er ikke children feltet lenger en del av React.FC typen og man ikke lenger gi Dropdown noe children ifølge Typescript :/ (Men det vet dere sikkert bedre enn meg som lager designsystemet :D)  Mulig dette ikke skal inn hit (master) eller må sjekkes, dette er bare et naivt forsøk på å fikse

* slightly more expressive code

Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
2022-04-19 10:37:34 +02:00
Andreas Nordahl f3e6791d44 Next example app (#1498)
* add next example app

* add css and button

* add accordion

* tailwindcss

* more components

* @import ./tailwind to fix style order

* a lot more components

* update readme

* remove unused files
2022-04-19 10:02:40 +02:00
github-actions[bot] 96a8db13c6 Lerna versionbump [ci skip]
- @navikt/ds-icons@0.8.11
 - @navikt/ds-react@0.17.25
 - @navikt/ds-react-internal@0.12.27
2022-04-08 15:05:36 +00:00
Daniel Fjeldstad 379bbbe61a [fix] Støtte for @types/react 18.0.0 (#1502) 2022-04-08 16:57:11 +02:00
github-actions[bot] 8029c9553a Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.24
 - @navikt/ds-react-internal@0.12.26
2022-04-08 10:37:31 +00:00
KenandAndreas Nordahl 86127c9d9d [FIX] Faktisk fix for Tooltip (#1496)
* faktisk fiks for tooltip

* fjernet comments

* fix use event lister at source

Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
2022-04-08 12:29:21 +02:00
github-actions[bot] f845f20d94 Lerna versionbump [ci skip]
- @navikt/ds-react-internal@0.12.25
2022-04-08 09:46:20 +00:00
Andreas Nordahl 4034ae5e37 [fix] add back copy button text (#1497) 2022-04-08 11:38:12 +02:00
github-actions[bot] d5f2e044e8 Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.23
 - @navikt/ds-react-internal@0.12.24
2022-04-07 13:02:47 +00:00
Ken d7fdc6d73c [fix] patch tooltip (#1495) 2022-04-07 14:52:04 +02:00
github-actions[bot] c6c721a469 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.19
 - @navikt/ds-icons@0.8.10
 - @navikt/ds-react@0.17.22
 - @navikt/ds-react-internal@0.12.23
2022-04-07 07:31:34 +00:00
Ken b26a906300 [fix] flyttet max-width til token (#1493) 2022-04-07 09:22:04 +02:00
Ken d18a731eed ErrorSummary og Search stories (#1489)
* errorsummary stories

* search story

* rename story
2022-04-07 09:18:10 +02:00
Andreas NordahlandKen cbc5940a40 [fix] prepend svg title id to avoid id starting with num (#1490)
* prepend svg title id to avoid id starting with num

* Update @navikt/core/icons/template.js

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

* dont pass unnecessary arg to useId

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2022-04-07 09:17:07 +02:00
github-actions[bot] 104ab0d51a Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.18
2022-04-06 13:32:28 +00:00
Ken 9a73b23e09 [FIX] Readme.md 2022-04-06 15:22:11 +02:00
Martin Berglund 81a18ea271 Prevent SVG-icons in Link from shrinking (#1492) 2022-04-06 15:21:10 +02:00
github-actions[bot] e21bd15423 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.17
2022-04-05 09:08:55 +00:00
larseirikhansen 329124dda9 [fix] invisible line between labels (#1487) 2022-04-05 11:00:02 +02:00
github-actions[bot] 1662dc91c0 Lerna versionbump [ci skip]
- @navikt/ds-icons@0.8.9
 - @navikt/ds-react@0.17.21
 - @navikt/ds-react-internal@0.12.22
2022-04-05 08:38:26 +00:00
André Roaldseth 071962fc29 [fix] Bruk OR-operator mellom React 17 og 18 (#1488)
* Bruk OR-operator mellom React 17 og 18

* Bruk OR-operator mellom React 17 og 18

* Bruk OR-operator mellom React 17 og 18
2022-04-05 10:29:09 +02:00
Ken 0c5085002f Checkbox + Radio stories (#1486)
* checkbox stories

* radio-stories

* radio-stories
2022-04-05 10:20:34 +02:00
github-actions[bot] 2a468378dd Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.16
 - @navikt/ds-tailwind@0.1.9
 - @navikt/ds-tokens@0.8.7
 - @navikt/ds-react-internal@0.12.21
2022-04-04 14:37:36 +00:00
Ken 626b9069d2 [fix]
riktig aksel-repo link
2022-04-04 16:29:08 +02:00
KenandAndreas Nordahl 49c5e8f284 Tokens stories (#1484)
* tokens stories

* Update @navikt/core/tokens/stories/spacing.stories.tsx

Co-authored-by: Andreas Nordahl <andnorda@gmail.com>

Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
2022-04-04 16:03:55 +02:00
Andreas Nordahl 0d549cb87b [fix] overridable title for copy-to-clipboard (#1485) 2022-04-04 16:02:09 +02:00
Ken c0ef8a4d7d Permalenke til chromatic build 2022-04-04 15:06:25 +02:00
github-actions[bot] 3daa36efa7 Lerna versionbump [ci skip]
- @navikt/ds-icons@0.8.8
 - @navikt/ds-react@0.17.20
 - @navikt/ds-react-internal@0.12.20
2022-04-04 13:05:20 +00:00
Ken 16bec93d1f [FIX] storybook-lenke 2022-04-04 14:56:10 +02:00
Ken ad033d9b1a Flyttet noen komponenter under (deprecated) (#1483) 2022-04-04 14:30:46 +02:00
Ken b55ead2301 button stories (#1481) 2022-04-04 14:29:20 +02:00
Ken 92532dd586 [FIX] React v18 upgrade (#1482) 2022-04-04 14:25:38 +02:00
Ken 5d3361615a Alert stories (#1478) 2022-04-01 22:41:49 +02:00
Ken 7779d93bf3 Storybook + Accordion-story rydding (#1477) 2022-04-01 14:13:13 +02:00
github-actions[bot] 31bd40c02c Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.15
 - @navikt/ds-icons@0.8.7
 - @navikt/ds-react@0.17.19
 - @navikt/ds-css-internal@0.7.3
 - @navikt/ds-react-internal@0.12.19
2022-04-01 11:48:01 +00:00
Ken 52eda50e3c [FIX] Script for å gjøre :root mer lesbar (#1476) 2022-04-01 13:37:51 +02:00
larseirikhansen e9e5084214 Patch updates (#1474)
* patch-update pretty-quick, fast-glob, fs-extra

* publish and share .git-blame-ignore-revs-file
2022-04-01 11:14:56 +02:00
github-actions[bot] dd14eb272e Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.14
 - @navikt/ds-react@0.17.18
 - @navikt/ds-tailwind@0.1.8
 - @navikt/ds-tokens@0.8.6
 - @navikt/ds-css-internal@0.7.2
 - @navikt/ds-react-internal@0.12.18
2022-04-01 07:38:03 +00:00
Andreas NordahlandKen 022d3a6a6f [fix] New/suggestfield (#1472)
* satt opp react

* stylet suggest-field

* stylet inverted versjon

* inverted label-farge

* bedre eksempel

* lint fix

* fikset border på inverted

* bruker spacing

* form -> div

* fikset stories

* fjernet clearbutton

* fikset story

* wip darkmode and combined search and suggest field

* no-button fixes

* paddings

* icon sizes

* default value clear bug

* focus

* placeholder

* search icon color

* primary and secondary button

* update tokens from figma

* purge suggest field

* internal header with data-theme dark

* fix some dark theme text color issues

* stylelint

* dark clear hover color

* no-button -> simple

Co-authored-by: Ken <ken.aleksander@gmail.com>
2022-04-01 09:29:45 +02:00
github-actions[bot] fcbd614a34 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.13
 - @navikt/ds-react@0.17.17
 - @navikt/ds-tailwind@0.1.7
 - @navikt/ds-tokens@0.8.5
 - @navikt/ds-react-internal@0.12.17
2022-03-31 10:50:28 +00:00
0a9aa6d3a1 [FIX] Tooltip komponent (#1462)
Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Ken <ken.aleksander@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Martin Berglund <martin.berglund@nav.no>
Co-authored-by: larseirikhansen <71271458+larseirikhansen@users.noreply.github.com>
2022-03-31 11:38:09 +02:00
Ida 926437f06f Update README.md (#1475) 2022-03-31 11:34:34 +02:00
Ken 288456a460 type button på readmore (#1473) 2022-03-30 14:46:01 +02:00
github-actions[bot] 61f6b62c9e Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.16
 - @navikt/ds-react-internal@0.12.16
2022-03-28 15:02:20 +00:00
Andreas Nordahl dc9ffdebaf [fix] expandable icon title (#1471) 2022-03-28 16:53:05 +02:00
github-actions[bot] c80cb199a1 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.12
 - @navikt/ds-react@0.17.15
 - @navikt/ds-react-internal@0.12.15
2022-03-28 14:44:27 +00:00
Andreas Nordahl 5ec431456e [fix] Expantable table row (#1468)
* wip

* non-working api sketches

* working example of Table.ExpandableRow

* right|left toggle

* controlled/uncontrolled

* toggle fills cell

* collapse animation

* small

* oopsie

* docs

* inline toggle

* aria-label -> title (like helptext comp)

* oopsie

* fix visual glitch

* muted open border color

* expanded-row-cell padding issue

* expanded content alignment
2022-03-28 16:34:32 +02:00
dependabot[bot] 9e0af943a1 Bump minimist from 1.2.5 to 1.2.6 (#1467)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 10:58:50 +02:00
github-actions[bot] 9ed31d5401 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.11
2022-03-28 08:50:52 +00:00
Ken 5248625027 [FIX] Pointer på Scrollbutton i Tabs (#1470) 2022-03-28 10:42:37 +02:00
github-actions[bot] f62b9a6f7b Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.10
2022-03-28 07:28:10 +00:00
Andreas Nordahl c07dd59399 [fix] update button styling to match figma (#1469) 2022-03-28 09:20:24 +02:00
github-actions[bot] 88e2c2b2fe Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.14
 - @navikt/ds-react-internal@0.12.14
2022-03-24 14:37:53 +00:00
Ken 924c21365b [FIX] fikset tabs-scroll når scrollleft=0 2022-03-24 15:28:19 +01:00
github-actions[bot] 9e1b0ab1d1 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.9
 - @navikt/ds-react@0.17.13
 - @navikt/ds-react-internal@0.12.13
2022-03-24 12:38:43 +00:00
9c4d41e4c8 [FIX] Tabs komponent (#1451)
Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
Co-authored-by: larseirikhansen <71271458+larseirikhansen@users.noreply.github.com>
2022-03-24 13:29:52 +01:00
16e141a624 [NEW] New/stepper (#1429)
* satt opp stepper react

* satt opp stepper css

* oppdatert react-kode

* førsteutkast styling

* ♿ aria-label added to icons

* Stepper spacing and last-child focus

* add aria-labelledby

* add aria-labelledby to sb

* fix spelling mistake

* Remove 'finished' step api-option

* refactor Stepper-children jsx

* create Stepper example for sb

* WIP: Stepper mdx with practical props examples

* intro mdx disabled state

* ds intro overridable tags example

* add flex gap and fix last-child margin

* step-line is now pseudo-element

* cleanup css

* last child no margin-bottom

* remove disabled state

* update storybook

* aria-current to active step

* styling and color fixes

* color change line step

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/css/stepper.css

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

* Update @navikt/core/react/src/stepper/Step.tsx

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

* [FIX] Bedre width for search (#1455)

* Lerna versionbump [ci skip]

 - @navikt/ds-css@0.16.3

* [fix] Helptext uu fix (#1407)

* storybook no div in p fix

* move title inside svg

* extra helptext in story

* remove duplicate title

* remove aria-props and refactor

* remove unused style

* simplify styling some

* default help text content to body-short

* cleanup

* tabindex 0 på popover

* flyttet story

* fjernet tabindex

* Update @navikt/core/react/src/help-text/HelpText.tsx

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

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

* Lerna versionbump [ci skip]

 - @navikt/ds-css@0.16.4
 - @navikt/ds-react@0.17.4
 - @navikt/ds-react-internal@0.12.4

* [FIX] Oppdatert ikon-template, SSR-safe uuid (#1458)

* oppdatert ikon-template

* oppdatert pakkversjoner

* fikset pakkeversjoner

* oppdatert ikon-template

* fikset config

* Lerna versionbump [ci skip]

 - @navikt/ds-icons@0.8.5
 - @navikt/ds-react@0.17.5
 - @navikt/ds-react-internal@0.12.5

* [fix] Read more component (#1456)

* scrips yo

* unused import

* stylez

* aria-expanded

* add link to storybook

* oopsie

* read more focus style

* read more collapse animation

* Lerna versionbump [ci skip]

 - @navikt/ds-css@0.16.5
 - @navikt/ds-react@0.17.6
 - @navikt/ds-react-internal@0.12.6

* remove ariaLabelledBy as its in by default

* Update @navikt/core/css/stepper.css

* anchor tag is default overridable

* rm space

* default from button to anchor

* add y-axis click affordance to steps

* click affordance, flex-align-start and stretching h-line with long label

* Update @navikt/core/css/stepper.css

Co-authored-by: Ken <ken.aleksander@gmail.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
2022-03-24 12:58:54 +01:00
github-actions[bot] 08be6c0e8b Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.12
 - @navikt/ds-react-internal@0.12.12
2022-03-23 18:50:44 +00:00
Andreas Nordahl becb01d429 [fix] Label react node (#1466) 2022-03-23 19:41:50 +01:00
github-actions[bot] 62ba36ae09 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.8
 - @navikt/ds-react@0.17.11
 - @navikt/ds-react-internal@0.12.11
2022-03-22 13:40:06 +00:00
Martin BerglundandAndreas Nordahl 2114c2bb53 [FIX] Replace hard coded values with tokens (#1463)
* Replace hard coded values with tokens

* Update yarn.lock

Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
2022-03-22 14:30:48 +01:00
Ken 213ef258aa [FIX] Fikset title på HelpText (#1464) 2022-03-22 14:27:22 +01:00
github-actions[bot] 85dd252e43 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.7
 - @navikt/ds-react@0.17.10
 - @navikt/ds-react-internal@0.12.10
2022-03-22 11:18:22 +00:00
Ken 9ebeff4ae0 [FIX] Endringsforslag Pagination (#1457) 2022-03-22 12:09:23 +01:00
github-actions[bot] b383a6baec Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.6
 - @navikt/ds-react@0.17.9
 - @navikt/ds-react-internal@0.12.9
2022-03-22 10:06:07 +00:00
b09ea568d4 [FIX] Alert styling og ikoner (#1447)
Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Ken <ken.aleksander@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-03-22 10:57:09 +01:00
github-actions[bot] a3b28bb9ad Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.8
 - @navikt/ds-react-internal@0.12.8
2022-03-17 11:33:19 +00:00
Andreas Nordahl 1d04a06885 [fix] up-down for default sort state (#1461) 2022-03-17 12:23:39 +01:00
129 changed files with 5242 additions and 2251 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
509e1f2ded31a7d804422d05025454fac5a4c988
+12
View File
@@ -0,0 +1,12 @@
import { Meta, Canvas } from "@storybook/addon-docs";
<Meta title="ds-react(deprecated)/intro" />
# Utdaterte pakker
På sikt vil komponentene under bli flyttet ut av ds-react eller erstattes av andre komponenter
- PageHeader -> @navikt/ds-react-navno
- MicroCard -> @navikt/ds-react-navno
- StepIndicator -> Horisontal `<Stepper />`
+17
View File
@@ -0,0 +1,17 @@
.rowgap {
display: flex;
gap: 1rem;
padding: 1rem;
}
.colgap {
display: flex;
gap: 1rem;
flex-direction: column;
padding: 1rem;
}
.colgap[data-theme="dark"],
.rowgap[data-theme="dark"] {
background-color: #262626;
}
+1 -1
View File
@@ -10,7 +10,7 @@ module.exports = {
return config;
},
devServer: { stats: "errors-only" },
stories: () => getStories(),
stories: () => [...getStories(), "./*.stories.mdx"],
addons: [
"@storybook/addon-docs",
"@storybook/addon-a11y",
+5 -2
View File
@@ -1,5 +1,6 @@
import "@navikt/ds-css/index.css";
import "@navikt/ds-css-internal/index.css";
import "./layout.css";
import { initialize, mswDecorator } from "msw-storybook-addon";
initialize();
@@ -7,12 +8,14 @@ initialize();
export const decorators = [mswDecorator];
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
/* actions: { argTypesRegex: "^on[A-Z].*" }, */
options: {
storySort: {
method: "",
order: [],
order: ["ds-icons", "ds-react", ["Default"]],
locales: "",
},
},
viewMode: "docs",
layout: "centered",
};
-3
View File
@@ -1,3 +0,0 @@
# Designsystem v2
Storybook: https://navikt.github.io/Designsystemet/
+15 -37
View File
@@ -3,53 +3,32 @@
--navds-alert-color-error-border: var(
--navds-semantic-color-feedback-danger-border
);
--navds-alert-color-error-background: var(
--navds-semantic-color-feedback-danger-background
);
--navds-alert-color-error-background: var(--navds-global-color-red-50);
--navds-alert-color-warning-border: var(
--navds-semantic-color-feedback-warning-border
);
--navds-alert-color-warning-background: var(
--navds-semantic-color-feedback-warning-background
);
--navds-alert-color-warning-background: var(--navds-global-color-orange-50);
--navds-alert-color-info-border: var(
--navds-semantic-color-feedback-info-border
);
--navds-alert-color-info-background: var(
--navds-semantic-color-feedback-info-background
);
--navds-alert-color-info-background: var(--navds-global-color-lightblue-50);
--navds-alert-color-success-border: var(
--navds-semantic-color-feedback-success-border
);
--navds-alert-color-success-background: var(
--navds-semantic-color-feedback-success-background
);
--navds-alert-color-success-background: var(--navds-global-color-green-50);
--navds-alert-color-error-icon: var(
--navds-semantic-color-feedback-danger-icon
);
--navds-alert-color-error-icon-background: var(
--navds-semantic-color-component-background-light
);
--navds-alert-color-warning-icon: var(
--navds-semantic-color-feedback-warning-icon
);
--navds-alert-color-warning-icon-background: var(
--navds-semantic-color-component-background-inverted
);
--navds-alert-color-warning-icon: var(--navds-global-color-orange-600);
--navds-alert-color-info-icon: var(--navds-semantic-color-feedback-info-icon);
--navds-alert-color-info-icon-background: var(
--navds-semantic-color-component-background-light
);
--navds-alert-color-success-icon: var(
--navds-semantic-color-feedback-success-icon
);
--navds-alert-color-success-icon-background: var(
--navds-semantic-color-component-background-light
);
--navds-alert-color-icon-background: var(--navds-global-color-white);
}
.navds-alert {
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 1px solid;
border-color: var(--navds-alert-color-border);
padding: calc(var(--navds-spacing-4) + 1px) calc(var(--navds-spacing-4) + 1px);
@@ -91,7 +70,7 @@
color: var(--navds-alert-color-error-icon);
background: radial-gradient(
circle,
var(--navds-alert-color-error-icon-background) 50%,
var(--navds-alert-color-icon-background) 50%,
0,
transparent
);
@@ -104,12 +83,11 @@
.navds-alert--warning > .navds-alert__icon {
color: var(--navds-alert-color-warning-icon);
background: radial-gradient(
circle,
var(--navds-alert-color-warning-icon-background) 50%,
0,
transparent
);
background: linear-gradient(
var(--navds-alert-color-icon-background),
var(--navds-alert-color-icon-background)
)
center / 16% 60% no-repeat;
}
.navds-alert--info {
@@ -121,7 +99,7 @@
color: var(--navds-alert-color-info-icon);
background: radial-gradient(
circle,
var(--navds-alert-color-info-icon-background) 50%,
var(--navds-alert-color-icon-background) 50%,
0,
transparent
);
@@ -136,7 +114,7 @@
color: var(--navds-alert-color-success-icon);
background: radial-gradient(
circle,
var(--navds-alert-color-success-icon-background) 50%,
var(--navds-alert-color-icon-background) 50%,
0,
transparent
);
+13 -1
View File
@@ -20,10 +20,22 @@ html {
box-sizing: inherit;
}
:root,
[data-theme="light"] {
--navds-shadow-focus: 0 0 0 3px var(--navds-semantic-color-focus);
color: var(--navds-semantic-color-text);
}
[data-theme="dark"] {
--navds-shadow-focus: var(--navds-shadow-focus-inverted);
color: var(--navds-semantic-color-text-inverted);
}
body {
font-family: var(--navds-font-family);
line-height: 1.333;
color: var(--navds-semantic-color-text);
font-size: 1.125rem;
}
+41 -9
View File
@@ -1,4 +1,5 @@
:root {
:root,
[data-theme="light"] {
/* Primary */
--navds-button-color-primary-text: var(--navds-semantic-color-text-inverted);
--navds-button-color-primary-border-focus: var(
@@ -19,7 +20,7 @@
--navds-semantic-color-interaction-primary
);
--navds-button-color-secondary-text-hover: var(
--navds-semantic-color-text-inverted
--navds-semantic-color-interaction-primary
);
--navds-button-color-secondary-text-active: var(
--navds-semantic-color-text-inverted
@@ -34,7 +35,7 @@
--navds-semantic-color-component-background-light
);
--navds-button-color-secondary-background-hover: var(
--navds-semantic-color-interaction-primary-hover
--navds-semantic-color-interaction-primary-hover-subtle
);
--navds-button-color-secondary-background-active: var(
--navds-semantic-color-interaction-primary-selected
@@ -47,8 +48,8 @@
--navds-button-color-tertiary-text-active: var(
--navds-semantic-color-text-inverted
);
--navds-button-color-tertiary-border-hover: var(
--navds-semantic-color-interaction-primary
--navds-button-color-tertiary-background-hover: var(
--navds-semantic-color-interaction-primary-hover-subtle
);
--navds-button-color-tertiary-border-focus: var(
--navds-semantic-color-interaction-primary
@@ -80,6 +81,39 @@
--navds-loader-color-on-button-foreground: currentColor;
}
[data-theme="dark"] {
/* Primary */
--navds-button-color-primary-text: var(--navds-semantic-color-text);
--navds-button-color-primary-border-focus: var(--navds-semantic-color-text);
--navds-button-color-primary-background: var(--navds-global-color-blue-200);
--navds-button-color-primary-background-hover: var(
--navds-global-color-blue-300
);
--navds-button-color-primary-background-active: var(
--navds-global-color-blue-400
);
/* Secondary */
--navds-button-color-secondary-text: var(--navds-global-color-blue-200);
--navds-button-color-secondary-text-hover: var(--navds-global-color-blue-200);
--navds-button-color-secondary-text-active: var(
--navds-global-color-blue-200
);
--navds-button-color-secondary-border: var(--navds-global-color-blue-200);
--navds-button-color-secondary-border-focus-active-hover: var(
--navds-semantic-color-text
);
--navds-button-color-secondary-background: var(
--navds-semantic-color-component-background-inverted
);
--navds-button-color-secondary-background-hover: var(
--navds-global-color-blue-900
);
--navds-button-color-secondary-background-active: var(
--navds-global-color-blue-800
);
}
.navds-button {
display: inline-flex;
cursor: pointer;
@@ -88,7 +122,7 @@
text-decoration: none;
border: none;
background: none;
border-radius: 2px;
border-radius: var(--navds-border-radius-small);
min-width: 48px;
padding: var(--navds-spacing-3);
}
@@ -165,7 +199,6 @@
.navds-button--secondary:hover {
color: var(--navds-button-color-secondary-text-hover);
background-color: var(--navds-button-color-secondary-background-hover);
box-shadow: none;
}
.navds-button--secondary:focus {
@@ -179,7 +212,6 @@
box-shadow: none;
}
.navds-button--secondary:focus:hover,
.navds-button--secondary:focus:active {
box-shadow: inset 0 0 0 1px
var(--navds-button-color-secondary-border-focus-active-hover),
@@ -202,7 +234,7 @@
}
.navds-button--tertiary:hover {
box-shadow: inset 0 0 0 2px var(--navds-button-color-tertiary-border-hover);
background-color: var(--navds-button-color-tertiary-background-hover);
}
.navds-button--tertiary:focus {
+1 -1
View File
@@ -27,7 +27,7 @@
*/
.navds-card__micro {
border-radius: 1rem;
border-radius: var(--navds-border-radius-full);
color: var(--navds-card-micro-color-text);
display: inline-flex;
padding: var(--navds-spacing-1) var(--navds-spacing-2);
+5 -1
View File
@@ -1,7 +1,11 @@
:root {
--navds-content-container-max-width: 79.5rem;
}
.navds-content-container {
margin-left: auto;
margin-right: auto;
max-width: 79.5rem;
max-width: var(--navds-content-container-max-width);
padding: var(--navds-spacing-4);
}
+1 -1
View File
@@ -39,7 +39,7 @@
flex-direction: column;
gap: var(--navds-spacing-4);
padding: var(--navds-spacing-4);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 1px solid var(--navds-confirmation-panel-color-border);
background-color: var(--navds-confirmation-panel-color-background);
transition: background-color linear 100ms;
+3 -3
View File
@@ -57,12 +57,12 @@
box-shadow: inset 0 0 0 2px var(--navds-radio-checkbox-color-shadow);
width: 1.5rem;
height: 1.5rem;
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
flex-shrink: 0;
}
.navds-radio__label::before {
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
}
.navds-checkbox__content,
@@ -121,7 +121,7 @@
background-color: var(--navds-radio-checkbox-color-background);
width: 0.75rem;
height: 0.25rem;
border-radius: 1px;
border-radius: 1px; /* Custom value OK */
flex-shrink: 0;
}
+47 -14
View File
@@ -1,8 +1,18 @@
:root {
:root,
[data-theme="light"] {
--navds-search-color-text: var(--navds-semantic-color-text);
--navds-search-color-border: var(--navds-semantic-color-border);
--navds-search-color-border-hover: var(
--navds-semantic-color-interaction-primary
);
--navds-search-color-clear-hover: var(--navds-global-color-blue-500);
}
[data-theme="dark"] {
--navds-search-color-text: var(--navds-semantic-color-text-inverted);
--navds-search-color-border: var(--navds-semantic-color-border-inverted);
--navds-search-color-border-hover: var(--navds-global-color-blue-200);
--navds-search-color-clear-hover: var(--navds-global-color-blue-200);
}
.navds-search {
@@ -22,22 +32,45 @@
}
.navds-search__input {
padding-right: var(--navds-spacing-12);
padding-right: var(--navds-spacing-10);
}
.navds-form-field--small .navds-search__input {
padding-right: var(--navds-spacing-8);
}
.navds-search__input--simple {
padding-left: var(--navds-spacing-11);
}
.navds-form-field--small .navds-search__input--simple {
padding-left: var(--navds-spacing-7);
}
.navds-search__input:not(.navds-search__input--simple) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.navds-form-field--small .navds-search__input {
min-height: 32px;
padding: 0.15rem;
padding-right: var(--navds-spacing-8);
.navds-search__search-icon {
position: absolute;
left: var(--navds-spacing-3);
top: 50%;
transform: translateY(-50%);
pointer-events: none;
font-size: 1.5rem;
}
.navds-form-field--small .navds-search__search-icon {
left: var(--navds-spacing-2);
font-size: 1rem;
}
.navds-search__button-clear {
position: absolute;
right: 0.75rem;
border-radius: var(--navds-border-radius-medium);
color: var(--navds-semantic-color-text);
color: var(--navds-search-color-text);
height: 32px;
width: 32px;
top: 50%;
@@ -59,7 +92,7 @@
}
.navds-search__button-clear:hover {
color: var(--navds-global-color-blue-500);
color: var(--navds-search-color-clear-hover);
}
.navds-search__button-clear:focus {
@@ -84,13 +117,13 @@
min-width: 40px;
}
.navds-search__button-search.navds-button--tertiary {
.navds-search__button-search.navds-button--secondary {
box-shadow: -1px 0 0 0 var(--navds-search-color-border) inset,
0 1px 0 0 var(--navds-search-color-border) inset,
0 -1px 0 0 var(--navds-search-color-border) inset;
}
.navds-search__button-search.navds-button--tertiary:hover {
.navds-search__button-search.navds-button--secondary:hover {
box-shadow: -1px 0 0 0 var(--navds-search-color-border-hover) inset,
0 1px 0 0 var(--navds-search-color-border-hover) inset,
0 -1px 0 0 var(--navds-search-color-border-hover) inset,
@@ -99,21 +132,21 @@
}
.navds-search__wrapper-inner:focus-within
+ .navds-search__button-search.navds-button--tertiary:hover {
+ .navds-search__button-search.navds-button--secondary:hover {
z-index: auto;
}
.navds-search__button-search.navds-button--tertiary:focus {
.navds-search__button-search.navds-button--secondary:focus {
box-shadow: 0 0 0 1px var(--navds-search-color-border) inset,
var(--navds-shadow-focus);
}
.navds-search__button-search.navds-button--tertiary:focus:hover {
.navds-search__button-search.navds-button--secondary:focus:hover {
box-shadow: 0 0 0 1px var(--navds-search-color-border-hover) inset,
var(--navds-shadow-focus);
}
.navds-search__button-search.navds-button--tertiary:focus:active {
.navds-search__button-search.navds-button--secondary:focus:active {
box-shadow: 0 0 0 1px white inset, var(--navds-shadow-focus);
}
+1 -1
View File
@@ -22,7 +22,7 @@
.navds-select__input {
appearance: none;
background-color: var(--navds-select-color-background);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 1px solid var(--navds-select-color-border);
width: 100%;
box-sizing: border-box;
+2 -2
View File
@@ -112,7 +112,7 @@
position: absolute;
top: var(--navds-spacing-3);
left: 0;
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -155,7 +155,7 @@
.navds-switch__thumb {
background-color: var(--navds-switch-color-thumb);
color: var(--navds-switch-color-thumb-icon);
border-radius: 2.5px;
border-radius: var(--navds-border-radius-small);
width: 20px;
height: 20px;
position: absolute;
+22 -4
View File
@@ -1,4 +1,5 @@
:root {
:root,
[data-theme="light"] {
--navds-text-field-color-text: var(--navds-semantic-color-text);
--navds-text-field-color-background: var(
--navds-semantic-color-component-background-light
@@ -18,20 +19,37 @@
--navds-semantic-color-component-background-alternate
);
--navds-text-field-color-text-disabled: var(--navds-semantic-color-text);
--navds-text-field-color-placeholder-text: var(
--navds-semantic-color-text-muted
);
}
[data-theme="dark"] {
--navds-text-field-color-text: var(--navds-semantic-color-text-inverted);
--navds-text-field-color-background: var(
--navds-semantic-color-component-background-dark
);
--navds-text-field-color-border: var(--navds-semantic-color-border-inverted);
--navds-text-field-color-border-hover: var(--navds-global-color-blue-200);
--navds-text-field-color-placeholder-text: var(--navds-global-color-gray-500);
}
.navds-text-field__input {
appearance: none;
padding: 0.5rem;
padding: var(--navds-spacing-2);
background-color: var(--navds-text-field-color-background);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 1px solid var(--navds-text-field-color-border);
min-height: 48px;
width: 100%;
color: var(--navds-text-field-color-text);
}
.navds-form-field--small > .navds-text-field__input {
.navds-text-field__input::placeholder {
color: var(--navds-text-field-color-placeholder-text);
}
.navds-form-field--small .navds-text-field__input {
padding: 0 0.25rem;
min-height: 32px;
}
+1 -1
View File
@@ -32,7 +32,7 @@
appearance: none;
padding: var(--navds-spacing-2);
background-color: var(--navds-textarea-color-background);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 1px solid var(--navds-textarea-color-border);
resize: none;
width: 100%;
+2 -2
View File
@@ -18,7 +18,7 @@
.navds-guide-panel__content {
background-color: var(--navds-guide-panel-color-background);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
border: 2px solid var(--navds-guide-panel-color-border);
min-height: 7.25rem;
padding: 1.5rem;
@@ -58,7 +58,7 @@
/* Guide illustration frame */
.navds-guide__illustration {
background: var(--navds-guide-panel-color-illustration-background);
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
overflow: hidden;
}
+2 -2
View File
@@ -27,7 +27,7 @@
border: 0;
cursor: pointer;
background-color: transparent;
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
display: flex;
justify-content: center;
align-items: center;
@@ -49,7 +49,7 @@
}
.navds-help-text__icon {
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
}
.navds-help-text__popover > .navds-popover__arrow::before {
+3
View File
@@ -17,13 +17,16 @@
@import "pagination.css";
@import "popover.css";
@import "tag.css";
@import "tooltip.css";
@import "toggle-group.css";
@import "panel.css";
@import "link-panel.css";
@import "read-more.css";
@import "speech-bubble.css";
@import "step-indicator.css";
@import "stepper.css";
@import "table.css";
@import "tabs.css";
/* Navno spesific packages */
@import "page-header.css";
+1
View File
@@ -49,6 +49,7 @@
.navds-link svg {
color: var(--navds-link-color-icon);
flex-shrink: 0;
}
.navds-link:focus svg {
+1 -1
View File
@@ -11,7 +11,7 @@
.navds-modal {
background-color: var(--navds-modal-color-background);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
display: block;
position: relative;
overflow: auto;
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "0.16.5",
"version": "0.16.19",
"private": false,
"description": "Css for NAV Designsystem components",
"author": "NAV Designsystem team",
@@ -17,14 +17,15 @@
},
"main": "./dist/index.css",
"scripts": {
"build": "postcss --use postcss-import -o dist/index.css index.css",
"build": "postcss --use postcss-import postcss-combine-duplicated-selectors -o dist/index.css index.css",
"watch": "postcss --watch --use postcss-import -o dist/index.css index.css"
},
"devDependencies": {
"@navikt/ds-tokens": "^0.8.4",
"@navikt/ds-tokens": "^0.8.7",
"normalize.css": "^8.0.1",
"postcss": "^8.3.6",
"postcss-cli": "^8.3.1",
"postcss-combine-duplicated-selectors": "10.0.3",
"postcss-import": "^14.0.2"
}
}
+6 -2
View File
@@ -47,7 +47,7 @@
text-decoration: none;
border: none;
background: none;
border-radius: 2px;
border-radius: var(--navds-border-radius-small);
padding: var(--navds-spacing-1) var(--navds-spacing-3);
}
@@ -87,7 +87,7 @@
color: var(--navds-semantic-color-link);
align-items: center;
gap: var(--navds-spacing-1);
border-radius: 2px;
border-radius: var(--navds-border-radius-small);
}
.navds-pagination__prev-next:focus {
@@ -116,3 +116,7 @@
.navds-pagination__prev-text {
padding-right: 0.75rem;
}
.navds-pagination--invisible {
visibility: hidden;
}
+1 -1
View File
@@ -8,7 +8,7 @@
.navds-panel {
background-color: var(--navds-panel-color-background);
padding: var(--navds-spacing-4);
border-radius: 4px;
border-radius: var(--navds-border-radius-small);
border: 1px solid transparent;
}
+1 -1
View File
@@ -11,7 +11,7 @@
box-shadow: var(--navds-shadow-popover);
border: 1px solid;
border-color: var(--navds-popover-color-border);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
}
.navds-popover__content {
+3 -3
View File
@@ -38,7 +38,7 @@
align-items: center;
background: var(--navds-speechbubble-color-illustration-background);
color: var(--navds-speechbubble-color-illustration);
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
display: flex;
flex-shrink: 0;
justify-content: center;
@@ -59,7 +59,7 @@
width: fit-content;
max-width: 37.5rem;
background-color: var(--navds-speechbubble-color-background);
border-radius: 12px;
border-radius: var(--navds-border-radius-xlarge);
border-bottom-left-radius: 2px;
display: flex;
flex-direction: column;
@@ -72,7 +72,7 @@
}
.navds-speechbubble--right .navds-speechbubble__bubble {
border-radius: 12px;
border-radius: var(--navds-border-radius-xlarge);
border-bottom-right-radius: 2px;
}
+2 -2
View File
@@ -72,7 +72,7 @@
width: 2rem;
height: 2rem;
margin: 0.5rem;
border-radius: 50%;
border-radius: var(--navds-border-radius-full);
align-items: center;
justify-content: center;
box-shadow: 0 0 0 1px var(--navds-step-indicator-color-step-number-shadow);
@@ -107,7 +107,7 @@
left: calc(-50% + 2rem);
display: block;
border-top: 2px solid var(--navds-step-indicator-color-step-line);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
pointer-events: none;
}
+99
View File
@@ -0,0 +1,99 @@
.navds-stepper {
display: flex;
flex-direction: column;
padding: 0;
margin: 0;
list-style: none;
}
/* Step */
.navds-stepper__step {
position: relative;
cursor: pointer;
padding: calc(2.25rem / 2) 0;
display: flex;
background: none;
border: none;
appearance: none;
width: 100%;
color: var(--navds-semantic-color-text);
text-decoration: none;
flex-shrink: 0;
}
.navds-stepper__step-number {
flex-shrink: 0;
display: flex;
width: 28px;
height: 28px;
margin: 0;
margin-right: var(--navds-spacing-3);
border-radius: var(--navds-border-radius-full);
align-items: center;
justify-content: center;
color: var(--navds-semantic-color-interaction-primary);
box-shadow: inset 0 0 0 2px var(--navds-semantic-color-interaction-primary);
background-color: var(--navds-semantic-color-component-background-light);
}
/* Step-states: focus, active, hover */
.navds-stepper__step-wrapper:hover
> .navds-stepper__step
> .navds-stepper__step-number {
background-color: var(
--navds-semantic-color-interaction-primary-hover-subtle
);
}
.navds-stepper__step--active > .navds-stepper__step-number,
.navds-stepper__step-wrapper:hover
> .navds-stepper__step--active
> .navds-stepper__step-number {
color: var(--navds-semantic-color-text-inverted);
background-color: var(--navds-semantic-color-interaction-primary-selected);
box-shadow: inset 0 0 0 2px
var(--navds-semantic-color-interaction-primary-selected);
}
.navds-stepper__step:focus {
box-shadow: var(--navds-shadow-focus);
outline: none;
}
.navds-stepper__step--active {
cursor: default;
}
/* Step label */
.navds-stepper__step-label {
margin-top: 2px;
color: var(--navds-semantic-color-interaction-primary);
text-align: start;
}
.navds-stepper__step:hover > .navds-stepper__step-label {
text-decoration: underline;
}
.navds-stepper__step--active > .navds-stepper__step-label {
color: black;
}
.navds-stepper__step--active:hover > .navds-stepper__step-label {
text-decoration: none;
}
/* Step Horizontal Lines */
.navds-stepper__step-wrapper:not(:last-child) > .navds-stepper__step::after {
content: "";
position: absolute;
top: calc(28px + 8px + (2.25rem / 2));
left: 13px;
height: calc(100% - 2.75rem);
display: block;
border-left: 2px solid var(--navds-semantic-color-border-muted);
border-radius: var(--navds-border-radius-medium);
pointer-events: none;
z-index: 1;
}
+77
View File
@@ -141,3 +141,80 @@
font-size: 1rem;
flex-shrink: 0;
}
.navds-table__expandable-row--open .navds-table__header-cell,
.navds-table__expandable-row--open .navds-table__data-cell {
border-color: var(--navds-semantic-color-border-muted);
}
.navds-table__expandable-row--open .navds-table__toggle-expand-cell--open {
border-color: transparent;
}
.navds-table__toggle-expand-cell {
padding: 0;
width: 56px;
}
.navds-table--small .navds-table__toggle-expand-cell {
padding: 0;
width: 36px;
}
.navds-table__toggle-expand-button {
all: unset;
display: flex;
align-items: center;
cursor: pointer;
padding: var(--navds-spacing-4);
}
.navds-table--small .navds-table__toggle-expand-button {
padding: var(--navds-spacing-2);
}
.navds-table__toggle-expand-button:focus {
box-shadow: inset var(--navds-shadow-focus);
}
.navds-table__expandable-icon {
font-size: 1.5rem;
}
.navds-table--small .navds-table__expandable-icon {
font-size: 1.25rem;
}
.navds-table__toggle-expand-button:hover .navds-table__expandable-icon,
.navds-table__expandable-icon--filled {
display: none;
}
.navds-table__toggle-expand-button:hover .navds-table__expandable-icon--filled {
display: block;
}
.navds-table__toggle-expand-cell--open .navds-table__expandable-icon {
transform: rotate(180deg);
}
.navds-table__expanded-row-cell {
padding: 0;
}
.navds-table__expanded-row-cell:empty {
display: none;
}
.navds-table__expanded-row-collapse {
transition: height 250ms cubic-bezier(0.4, 0, 0.2, 1);
border-bottom: 1px solid var(--navds-table-cell-color-border);
}
.navds-table__expanded-row-content {
padding: var(--navds-spacing-4) 4.25rem;
}
.navds-table--small .navds-table__expanded-row-content {
padding: var(--navds-spacing-2) var(--navds-spacing-12);
}
+109
View File
@@ -0,0 +1,109 @@
.navds-tabs__tablist-wrapper {
box-shadow: inset 0 -1px 0 0 var(--navds-global-color-gray-300);
width: 100%;
display: flex;
}
.navds-tabs__tablist {
display: flex;
max-width: 100%;
width: auto;
scroll-behavior: smooth;
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
overflow-x: scroll;
}
.navds-tabs__tablist::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}
.navds-tabs__scroll-button {
padding: var(--navds-spacing-3) var(--navds-spacing-4);
width: 44px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.navds-tabs__scroll-button--hidden {
visibility: hidden;
}
.navds-tabs__scroll-button svg {
font-size: 1.25rem;
flex-shrink: 0;
}
.navds-tabs--small .navds-tabs__scroll-button {
padding: 0.375rem var(--navds-spacing-4);
width: 32px;
}
.navds-tabs__tab {
min-height: 48px;
padding: var(--navds-spacing-3) var(--navds-spacing-4);
display: inline-flex;
justify-content: center;
align-items: center;
background: none;
border: none;
color: var(--navds-semantic-color-text-muted);
cursor: pointer;
}
.navds-tabs__tab:hover {
box-shadow: inset 0 -3px 0 0 var(--navds-global-color-gray-300);
}
.navds-tabs__tab[aria-selected="true"] {
box-shadow: inset 0 -3px 0 0 var(--navds-semantic-color-interaction-primary);
color: var(--navds-semantic-color-text);
}
.navds-tabs__tab:focus {
outline: none;
box-shadow: inset var(--navds-shadow-focus);
color: var(--navds-semantic-color-text);
}
.navds-tabs__tab-inner {
display: flex;
align-items: center;
gap: var(--navds-spacing-1);
}
.navds-tabs__tab-icon--top > .navds-tabs__tab-inner {
flex-direction: column;
gap: 0;
}
.navds-tabs__tab--small {
min-height: 32px;
padding: 0.375rem var(--navds-spacing-4);
}
.navds-tabs__tab-icon--top,
.navds-tabs__tab--small.navds-tabs__tab-icon--top {
padding: var(--navds-spacing-1) var(--navds-spacing-4);
}
.navds-tabs__tab svg,
.navds-tabs__tab--small.navds-tabs__tab--icon-only svg,
.navds-tabs__tab--small.navds-tabs__tab-icon--top svg {
font-size: 1.25rem;
}
.navds-tabs__tab--small svg {
font-size: 1rem;
}
.navds-tabs__tab--icon-only svg,
.navds-tabs__tab-icon--top svg {
font-size: 1.5rem;
}
.navds-tabs__tabpanel:focus {
outline: none;
}
+1 -1
View File
@@ -32,7 +32,7 @@
.navds-tag {
border: 1px solid;
border-color: var(--navds-tag-color-border);
border-radius: 4px;
border-radius: var(--navds-border-radius-medium);
background-color: var(--navds-tag-color-background);
display: inline-flex;
align-items: center;
+2 -2
View File
@@ -19,7 +19,7 @@
}
.navds-toggle-group {
border-radius: 7px;
border-radius: 7px; /* Custom value OK */
background-color: var(--navds-toggle-group-color-background);
box-shadow: inset 0 0 0 1px var(--navds-toggle-group-color-border);
padding: calc(var(--navds-spacing-1) + 1px);
@@ -39,7 +39,7 @@
cursor: pointer;
background-color: var(--navds-toggle-group-color-background);
color: var(--navds-toggle-group-color-text);
border-radius: 2px;
border-radius: var(--navds-border-radius-small);
min-width: fit-content;
}
+56
View File
@@ -0,0 +1,56 @@
@keyframes tooltipFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.navds-tooltip {
z-index: var(--navds-z-index-tooltip);
}
.navds-tooltip__inner {
background-color: var(--navds-semantic-color-component-background-inverted);
color: var(--navds-semantic-color-text-inverted);
border-radius: var(--navds-border-radius-small);
padding: 0 var(--navds-spacing-2);
align-items: center;
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.05))
drop-shadow(0 1px 3px rgba(0, 0, 0, 0.1));
display: flex;
flex-direction: column;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
animation-duration: 0.2s;
animation-name: tooltipFadeIn;
text-align: center;
}
.navds-tooltip__arrow {
height: 7px;
width: 7px;
transform: rotate(45deg);
z-index: -1;
background-color: var(--navds-semantic-color-component-background-inverted);
position: absolute;
}
.navds-tooltip__keys {
padding-bottom: var(--navds-spacing-1);
display: flex;
gap: var(--navds-spacing-1);
}
.navds-tooltip__key {
font-family: var(--navds-font-family);
background-color: var(--navds-global-color-gray-700);
color: var(--navds-semantic-color-text-inverted);
border-radius: var(--navds-border-radius-small);
padding: 0 var(--navds-spacing-1);
min-width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-icons",
"version": "0.8.6",
"version": "0.8.11",
"private": false,
"description": "NAVs egne ikoner i SVG og React format",
"repository": {
@@ -26,7 +26,7 @@
"/meta.json"
],
"scripts": {
"create-package-json-pointers-to-esm": "node ../../../utilities/scripts/createPackageJsonsWithESMPointers.js",
"create-package-json-pointers-to-esm": "node ../../../scripts/createPackageJsonsWithESMPointers.js",
"create-icons": "svgr --out-dir src svg",
"clean": "rimraf src cjs esm",
"build": "yarn clean && copyfiles util/* src && yarn create-icons && tsc && tsc -p tsconfig.esm.json && yarn create-package-json-pointers-to-esm",
@@ -36,8 +36,8 @@
"uuid": "^8.3.2"
},
"peerDependencies": {
"@types/react": "^17.0.30",
"react": "^17.0.0"
"@types/react": "^17.0.30 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@svgr/cli": "^5.5.0",
@@ -49,7 +49,7 @@
"dotenv": "^10.0.0",
"lodash.kebabcase": "^4.1.1",
"lodash.startcase": "4.4.0",
"react": "^16.8.0 || ^17.0.0",
"react": "^18.0.0",
"rimraf": "3.0.2",
"sharp": "0.29.1",
"ts-node": "^10.4.0"
+2 -2
View File
@@ -35,8 +35,8 @@ function defaultTemplate(
${imports}
${interfaces}
const ${componentName} = React.forwardRef((${props}) => {
let titleId: string | undefined = useId(_titleId);
titleId = title ? titleId : undefined;
let titleId: string | undefined = useId();
titleId = title ? _titleId ? _titleId : "title-" + titleId : undefined;
return ${jsx};
});
export default ${componentName}`;
+15 -13
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "0.17.7",
"version": "0.17.26",
"private": false,
"description": "NAV designsystem react components",
"author": "NAV Designsystem team",
@@ -27,16 +27,18 @@
"/src"
],
"scripts": {
"create-package-json-pointers-to-esm": "node ../../../utilities/scripts/createPackageJsonsWithESMPointers.js",
"create-package-json-pointers-to-esm": "node ../../../scripts/createPackageJsonsWithESMPointers.js",
"clean": "rimraf cjs esm",
"build": "yarn run clean && tsc && tsc -p tsconfig.esm.json && yarn create-package-json-pointers-to-esm",
"watch": "tsc --watch -p tsconfig.esm.json",
"test": "jest"
},
"dependencies": {
"@floating-ui/react-dom": "0.6.0",
"@material-ui/core": "^4.12.3",
"@navikt/ds-icons": "^0.8.6",
"@navikt/ds-icons": "^0.8.11",
"@popperjs/core": "^2.10.1",
"@radix-ui/react-tabs": "0.1.5",
"@radix-ui/react-toggle-group": "0.1.5",
"classnames": "^2.2.6",
"react-collapse": "^5.1.0",
@@ -46,25 +48,25 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.0",
"@testing-library/user-event": "^13.2.1",
"@types/faker": "^5.5.8",
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^14.0.0",
"@types/faker": "5.5.8",
"@types/jest": "^27.0.1",
"@types/react-collapse": "^5.0.1",
"@types/react-modal": "^3.13.1",
"@types/styled-components": "^5.1.14",
"copyfiles": "^2.4.1",
"faker": "^5.5.3",
"jest": "^27.2.0",
"react-router-dom": "^6.1.0",
"faker": "5.5.3",
"jest": "^27.5.0",
"react-router-dom": "^6.3.0",
"rimraf": "3.0.2",
"styled-components": "^5.3.1",
"swr": "^1.1.2",
"ts-jest": "^27.0.5"
"ts-jest": "^27.1.0"
},
"peerDependencies": {
"@types/react": "^17.0.30",
"react": "^17.0.0"
"@types/react": "^17.0.30 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
}
}
@@ -5,7 +5,7 @@ import AccordionItem from "./AccordionItem";
import { Accordion } from ".";
export default {
title: "ds-react/accordion",
title: "ds-react/Accordion",
component: Accordion,
subcomponents: {
AccordionItem,
@@ -14,77 +14,89 @@ export default {
},
};
export const All = () => {
const Content = () => (
<Accordion.Content>
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam officia
laboris voluptate officia pariatur.Lorem est ex anim velit occaecat nisi qui
nostrud sit consectetur consectetur officia nostrud ullamco. Est ex duis
proident nostrud elit qui laborum anim minim eu eiusmod. Veniam in nostrud
sunt tempor velit incididunt sint ex dolor qui velit id eu. Deserunt magna
sunt velit in. Est exercitation id cillum qui do. Minim adipisicing nostrud
commodo proident occaecat aliquip nulla anim proident reprehenderit. Magna
ipsum officia veniam cupidatat duis veniam dolore reprehenderit mollit
velit.Ut consequat commodo minim occaecat id pariatur. Nisi enim tempor
laborum commodo. Tempor sit quis nostrud eu cupidatat sunt commodo
reprehenderit irure deserunt eiusmod ipsum. Exercitation quis commodo cillum
eiusmod eiusmod. Do laborum qui proident commodo adipisicing eiusmod id.
</Accordion.Content>
);
export const Default = (props) => {
const [open, setOpen] = useState(false);
const Item = () =>
props.controlled ? (
<Accordion.Item
open={open}
renderContentWhenClosed={props.renderContentWhenClosed}
>
<Accordion.Header onClick={() => setOpen(!open)}>
Accordion header text
</Accordion.Header>
<Content />
</Accordion.Item>
) : (
<Accordion.Item renderContentWhenClosed={props.renderContentWhenClosed}>
<Accordion.Header>Accordion header text</Accordion.Header>
<Content />
</Accordion.Item>
);
return (
<>
<h1>Accordion</h1>
<h2>Controlled</h2>
<Accordion>
<Accordion.Item open={open}>
<Accordion.Header onClick={() => setOpen(!open)}>
Accordion header text
</Accordion.Header>
<Accordion.Content>
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam
officia laboris voluptate officia pariatur.Lorem est ex anim velit
occaecat nisi qui nostrud sit consectetur consectetur officia
nostrud ullamco. Est ex duis proident nostrud elit qui laborum anim
minim eu eiusmod. Veniam in nostrud sunt tempor velit incididunt
sint ex dolor qui velit id eu. Deserunt magna sunt velit in. Est
exercitation id cillum qui do. Minim adipisicing nostrud commodo
proident occaecat aliquip nulla anim proident reprehenderit. Magna
ipsum officia veniam cupidatat duis veniam dolore reprehenderit
mollit velit.Ut consequat commodo minim occaecat id pariatur. Nisi
enim tempor laborum commodo. Tempor sit quis nostrud eu cupidatat
sunt commodo reprehenderit irure deserunt eiusmod ipsum.
Exercitation quis commodo cillum eiusmod eiusmod. Do laborum qui
proident commodo adipisicing eiusmod id.
</Accordion.Content>
</Accordion.Item>
</Accordion>
<h2>Un-controlled</h2>
<Accordion>
<Accordion.Item>
<Accordion.Header>Accordion header text</Accordion.Header>
<Accordion.Content>
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam
officia laboris voluptate officia pariatur.Lorem est ex anim velit
occaecat nisi qui nostrud sit consectetur consectetur officia
nostrud ullamco. Est ex duis proident nostrud elit qui laborum anim
minim eu eiusmod. Veniam in nostrud sunt tempor velit incididunt
sint ex dolor qui velit id eu. Deserunt magna sunt velit in. Est
exercitation id cillum qui do. Minim adipisicing nostrud commodo
proident occaecat aliquip nulla anim proident reprehenderit. Magna
ipsum officia veniam cupidatat duis veniam dolore reprehenderit
mollit velit.Ut consequat commodo minim occaecat id pariatur. Nisi
enim tempor laborum commodo. Tempor sit quis nostrud eu cupidatat
sunt commodo reprehenderit irure deserunt eiusmod ipsum.
Exercitation quis commodo cillum eiusmod eiusmod. Do laborum qui
proident commodo adipisicing eiusmod id.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>Accordion header text</Accordion.Header>
<Accordion.Content>
Magna aliquip aliquip fugiat nostrud nostrud velit pariatur veniam
officia laboris voluptate officia pariatur.Lorem est ex anim velit
occaecat nisi qui nostrud sit consectetur consectetur officia
nostrud ullamco. Est ex duis proident nostrud elit qui laborum anim
minim eu eiusmod. Veniam in nostrud sunt tempor velit incididunt
sint ex dolor qui velit id eu. Deserunt magna sunt velit in. Est
exercitation id cillum qui do. Minim adipisicing nostrud commodo
proident occaecat aliquip nulla anim proident reprehenderit. Magna
ipsum officia veniam cupidatat duis veniam dolore reprehenderit
mollit velit.Ut consequat commodo minim occaecat id pariatur. Nisi
enim tempor laborum commodo. Tempor sit quis nostrud eu cupidatat
sunt commodo reprehenderit irure deserunt eiusmod ipsum.
Exercitation quis commodo cillum eiusmod eiusmod. Do laborum qui
proident commodo adipisicing eiusmod id.
</Accordion.Content>
</Accordion.Item>
</Accordion>
</>
<Accordion>
{[...Array(props.nItems ? props.nItems : 2)].map((_, y) => (
<Item key={y} />
))}
</Accordion>
);
};
Default.args = {
controlled: false,
renderContentWhenClosed: false,
nItems: 2,
};
export const Controlled = () => {
const [open, setOpen] = useState(false);
const [open2, setOpen2] = useState(false);
return (
<Accordion>
<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>
);
};
export const Uncontrolled = () => (
<Accordion>
<Accordion.Item>
<Accordion.Header>Accordion header text</Accordion.Header>
<Content />
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>Accordion header text</Accordion.Header>
<Content />
</Accordion.Item>
</Accordion>
);
+3 -3
View File
@@ -1,11 +1,11 @@
import React, { forwardRef } from "react";
import cl from "classnames";
import {
ErrorFilled,
WarningFilled,
InformationFilled,
SuccessFilled,
WarningFilled,
} from "@navikt/ds-icons";
import cl from "classnames";
import React, { forwardRef } from "react";
import { BodyLong } from "..";
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
+85 -95
View File
@@ -1,9 +1,9 @@
import React from "react";
import { Alert } from ".";
import { BodyLong, Heading } from "..";
import { BodyLong, Heading as DsHeading } from "..";
export default {
title: "ds-react/alert",
title: "ds-react/Alert",
component: Alert,
};
@@ -14,111 +14,101 @@ const variants: Array<"error" | "warning" | "info" | "success"> = [
"success",
];
export const All = () => {
export const Default = (props) => (
<Alert
variant={props.variant}
size={props.size}
fullWidth={props.fullWidth}
inline={props.inline}
>
{props.children}
</Alert>
);
Default.args = {
variant: "info",
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
};
export const Small = () => {
return (
<>
<h1>Alert</h1>
<div className="colgap">
{variants.map((variant, i) => (
<Alert key={variant} variant={variant}>
<Alert key={variant} variant={variant} size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
<h2>Small</h2>
<div>
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
</div>
<h2>Alert fullWidth</h2>
</div>
);
};
export const FullWidth = () => {
return (
<div className="colgap">
{variants.map((variant, i) => (
<Alert fullWidth key={variant} variant={variant}>
<Alert key={variant} variant={variant} fullWidth>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
<h2>Small</h2>
<div>
{variants.map((variant, i) => (
<Alert fullWidth key={variant} variant={variant} size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
</div>
<h2>Inline</h2>
<div>
{variants.map((variant, i) => (
<Alert inline key={variant} variant={variant}>
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
</div>
<h2>Inline small</h2>
<div>
{variants.map((variant, i) => (
<Alert inline key={variant} variant={variant} size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
</div>
<h2>Med Heading</h2>
<div>
{variants.map((variant, i) => (
<Alert key={variant} variant={variant}>
<Heading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
</Heading>
<BodyLong spacing>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna.
Fugiat sint commodo consequat eu aute.
</BodyLong>
<Heading level="4" size="xsmall" spacing>
Ullamco eiusmod Lorem eiusmod eu.
</Heading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna.
Fugiat sint commodo consequat eu aute.
</BodyLong>
</Alert>
))}
</div>
<h2>Med Heading small</h2>
<div>
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} size="small">
<Heading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
</Heading>
<BodyLong spacing>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna.
Fugiat sint commodo consequat eu aute.
</BodyLong>
<Heading level="4" size="xsmall" spacing>
Ullamco eiusmod Lorem eiusmod eu.
</Heading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna.
Fugiat sint commodo consequat eu aute.
</BodyLong>
</Alert>
))}
</div>
</>
{variants.map((variant, i) => (
<Alert key={variant} variant={variant} fullWidth size="small">
{new Array(i + 1).fill(
"Id elit esse enim reprehenderit enim nisi veniam nostrud."
)}
</Alert>
))}
</div>
);
};
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>
);
};
export const Heading = () => {
return (
<div className="colgap">
<Alert variant="info">
<DsHeading spacing size="small" level="3">
Aliquip duis est in commodo pariatur
</DsHeading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
sint commodo consequat eu aute.
</BodyLong>
</Alert>
<Alert variant="info" size="small">
<DsHeading spacing size="xsmall" level="3">
Aliquip duis est in commodo pariatur
</DsHeading>
<BodyLong>
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
sint commodo consequat eu aute.
</BodyLong>
</Alert>
</div>
);
};
+143 -131
View File
@@ -1,30 +1,29 @@
import React, { useState } from "react";
import { Star } from "@navikt/ds-icons";
import React from "react";
import { Button } from "./index";
import { Success } from "@navikt/ds-icons";
export default {
title: "ds-react/button",
title: "ds-react/Button",
component: Button,
parameters: {
chromatic: { delay: 300 },
},
argTypes: {
variant: {
control: {
type: "radio",
options: ["primary", "secondary", "tertiary", "danger"],
},
},
size: {
control: {
type: "radio",
options: ["medium", "small"],
},
},
},
};
const Section = ({ children }) => (
<div
style={{
display: "flex",
gap: 16,
gridAutoFlow: "column",
justifyContent: "start",
padding: 24,
paddingLeft: 0,
}}
>
{children}
</div>
);
const variants: Array<"primary" | "secondary" | "tertiary" | "danger"> = [
"primary",
"secondary",
@@ -39,119 +38,132 @@ const varSwitch = {
danger: "Danger",
};
export const All = () => {
const [loadingState, setLoadingState] = useState(true);
const [content, setContent] = useState<string>("");
const toggleLoading = () => {
setLoadingState(!loadingState);
};
export const Default = (props) => {
return (
<div style={{ paddingLeft: "1rem" }}>
<h1>Button</h1>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant}>
{varSwitch[variant]}
</Button>
))}
</Section>
<h2>disabled</h2>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant} disabled>
{varSwitch[variant]}
</Button>
))}
</Section>
<h2>As link</h2>
<Section>
{variants.map((variant) => (
<Button as="a" key={variant} variant={variant} href="the-link">
{varSwitch[variant]}
</Button>
))}
</Section>
<h2>Small</h2>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
{varSwitch[variant]}
</Button>
))}
</Section>
<h2>Xsmall</h2>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant} size="xsmall">
{varSwitch[variant]}
</Button>
))}
</Section>
<h2>Button w/icon</h2>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant}>
{variant} <Success />
</Button>
))}
</Section>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant}>
<span className="navds-sr-only">Success ikon</span>
<Success />
</Button>
))}
</Section>
<h2>Small w/icon</h2>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
<Success /> {variant}
</Button>
))}
</Section>
<Section>
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
<span className="navds-sr-only">Success ikon</span>
<Success />
</Button>
))}
</Section>
<h2>Button w/loader</h2>
<Button onClick={toggleLoading}>Toggle loaders</Button>
<Button onClick={() => setContent((content) => `${content} wat`)}>
Change content
</Button>
<Section>
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
loading={loadingState}
onClick={toggleLoading}
>
{content || varSwitch[variant]}
</Button>
))}
</Section>
<h2>Small w/loader</h2>
<Section>
{variants.map((variant) => (
<Button
key={variant}
variant={variant}
size="small"
loading={loadingState}
onClick={toggleLoading}
>
{varSwitch[variant]}
</Button>
))}
</Section>
</div>
<Button variant={props.variant} size={props.size} loading={props.loading}>
{props.icon ? <Star /> : null}
{props.children}
{props.icon ? <Star /> : null}
</Button>
);
};
Default.args = {
icon: false,
loading: false,
children: "Knapp",
};
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 Loading = () => (
<div className="colgap">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} loading>
{varSwitch[variant]}
</Button>
))}
</div>
<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="xsmall">
{varSwitch[variant]}
</Button>
))}
</div>
</div>
);
export const Icon = () => (
<div className="colgap">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant}>
<Star />
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
<Star />
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="xsmall">
<Star />
</Button>
))}
</div>
</div>
);
export const IconWText = () => (
<div className="colgap">
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant}>
<Star />
{varSwitch[variant]}
<Star />
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="small">
<Star />
{varSwitch[variant]}
<Star />
</Button>
))}
</div>
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} size="xsmall">
<Star />
{varSwitch[variant]}
<Star />
</Button>
))}
</div>
</div>
);
export const Link = () => (
<div className="rowgap">
{variants.map((variant) => (
<Button key={variant} variant={variant} as="a" href="#thecakeisalie">
{varSwitch[variant]}
</Button>
))}
</div>
);
@@ -1,7 +1,7 @@
import { Meta, Canvas } from "@storybook/addon-docs";
import { MicroCard } from "../index.ts";
<Meta title="ds-react/card/intro" />
<Meta title="ds-react(deprecated)/card/intro" />
# Hvordan ta i bruk card
@@ -2,7 +2,7 @@ import React from "react";
import { MicroCard } from "../index";
import { Meta } from "@storybook/react/types-6-0";
export default {
title: "ds-react/card",
title: "ds-react(deprecated)/card",
component: MicroCard,
} as Meta;
@@ -10,7 +10,7 @@ export interface ConfirmationPanelProps extends Partial<CheckboxProps> {
/**
* Checkbox label
*/
label: string;
label: React.ReactNode;
/**
* Checked state for checkbox
*/
+1 -1
View File
@@ -19,7 +19,7 @@ export interface SelectProps
/**
* Label for select
*/
label: string;
label: React.ReactNode;
/**
* If enabled shows the label and description for screenreaders only
*/
@@ -7,6 +7,14 @@ import { FormFieldProps, useFormField } from "./useFormField";
export interface TextFieldProps
extends FormFieldProps,
Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
/**
* The current value (controlled).
*/
value?: string;
/**
* The default value (uncontrolled).
*/
defaultValue?: string;
/**
* Expose the HTML size attribute
*/
@@ -6,11 +6,12 @@ import { Checkbox, CheckboxGroup } from ".";
const firstArgumentOfFirstCall = (fn: jest.Mock) => fn.mock.calls[0][0];
test("checkbox group chains onChange calls", () => {
test("checkbox group chains onChange calls", async () => {
const onGroupChange = jest.fn();
const onChange = jest.fn();
const value = faker.datatype.string();
const label = faker.datatype.string();
const user = userEvent.setup();
render(
<CheckboxGroup legend="legend" onChange={onGroupChange}>
@@ -20,7 +21,7 @@ test("checkbox group chains onChange calls", () => {
</CheckboxGroup>
);
userEvent.click(screen.getByLabelText(label));
await user.click(screen.getByLabelText(label));
expect(onGroupChange).toBeCalledTimes(1);
expect(onGroupChange).toBeCalledWith([value]);
@@ -36,21 +37,24 @@ describe("Checkbox handles controlled-state correctly", () => {
</CheckboxGroup>
);
test("Checkbox is still checked after click when controlled", () => {
test("Checkbox is still checked after click when controlled", async () => {
const user = userEvent.setup();
render(<CheckboxComponent value={["value1", "value2"]} />);
userEvent.click(screen.getByLabelText("label1"));
userEvent.click(screen.getByLabelText("label2"));
await user.click(screen.getByLabelText("label1"));
await user.click(screen.getByLabelText("label2"));
expect((screen.getByLabelText("label1") as HTMLInputElement).checked).toBe(
true
);
expect((screen.getByLabelText("label2") as HTMLInputElement).checked).toBe(
true
);
});
test("onChange called with expected values", () => {
test("onChange called with expected values", async () => {
const onGroupChange = jest.fn();
const user = userEvent.setup();
render(
<CheckboxComponent
@@ -59,11 +63,11 @@ describe("Checkbox handles controlled-state correctly", () => {
/>
);
userEvent.click(screen.getByLabelText("label1"));
await user.click(screen.getByLabelText("label1"));
expect(onGroupChange).lastCalledWith(["value2"]);
userEvent.click(screen.getByLabelText("label2"));
await user.click(screen.getByLabelText("label2"));
expect(onGroupChange).lastCalledWith(["value1"]);
});
@@ -0,0 +1,178 @@
import React, { useState } from "react";
import { Checkbox, CheckboxGroup } from "../../index";
import { Meta } from "@storybook/react/types-6-0";
export default {
title: "ds-react/Form/Checkbox",
component: Checkbox,
subcomponents: {
CheckboxGroup,
},
} as Meta;
export const Default = (props) => {
const [state, setState] = useState(["checkbox1"]);
return (
<CheckboxGroup
legend={props.legend}
description={props.description}
value={props.controlled ? state : undefined}
onChange={props.controlled ? setState : undefined}
hideLegend={props.hideLegend}
error={props.errorGroup ? "Errormelding" : undefined}
{...props}
>
<Checkbox
value="checkbox1"
indeterminate={props.indeterminate}
hideLabel={props.hideLabel}
>
{props.children || "Apple"}
</Checkbox>
<Checkbox
value="checkbox2"
error={props.errorSingle}
description={
props.checkboxDescription ? "Orange description" : undefined
}
indeterminate={props.indeterminate}
hideLabel={props.hideLabel}
>
{props.children || "Orange"}
</Checkbox>
<Checkbox
value="checkbox3"
indeterminate={props.indeterminate}
hideLabel={props.hideLabel}
>
{props.children || "Banana"}
</Checkbox>
<Checkbox
value="checkbox4"
indeterminate={props.indeterminate}
hideLabel={props.hideLabel}
>
{props.children || "Melon"}
</Checkbox>
</CheckboxGroup>
);
};
Default.args = {
controlled: false,
legend: "Legend-tekst",
checkboxDescription: false,
hideLegend: false,
errorSingle: false,
children: "",
description: "",
};
export const Group = () => (
<CheckboxGroup legend="Group legend" defaultValue={["tekst2"]}>
<Checkbox value="tekst">Checkboxtekst</Checkbox>
<Checkbox value="tekst2">Checkboxtekst</Checkbox>
</CheckboxGroup>
);
export const GroupError = () => (
<CheckboxGroup
legend="Group legend"
defaultValue={["tekst2"]}
error="Group errormelding"
>
<Checkbox value="tekst">Checkboxtekst</Checkbox>
<Checkbox value="tekst2">Checkboxtekst</Checkbox>
</CheckboxGroup>
);
export const GroupSmall = () => (
<CheckboxGroup legend="Group legend" defaultValue={["tekst2"]} size="small">
<Checkbox value="tekst">Checkboxtekst</Checkbox>
<Checkbox value="tekst2">Checkboxtekst</Checkbox>
</CheckboxGroup>
);
export const GroupDescription = () => (
<CheckboxGroup
legend="Group legend"
defaultValue={["tekst2"]}
description="Group description"
>
<Checkbox value="tekst">Checkboxtekst</Checkbox>
<Checkbox value="tekst2">Checkboxtekst</Checkbox>
</CheckboxGroup>
);
export const Single = () => (
<div className="colspan">
<Checkbox value="tekst">Checkboxtekst</Checkbox>
<Checkbox value="tekst" defaultChecked>
Checkboxtekst
</Checkbox>
</div>
);
export const SingleSmall = () => (
<div className="colspan">
<Checkbox value="tekst" size="small">
Checkboxtekst
</Checkbox>
<Checkbox value="tekst" defaultChecked size="small">
Checkboxtekst
</Checkbox>
</div>
);
export const SingleDescription = () => (
<div className="colspan">
<Checkbox value="tekst" description="checkboxdescription">
Checkboxtekst
</Checkbox>
<Checkbox value="tekst" defaultChecked description="checkboxdescription">
Checkboxtekst
</Checkbox>
</div>
);
export const SingleError = () => (
<div className="colspan">
<Checkbox value="tekst" error>
Checkboxtekst
</Checkbox>
<Checkbox value="tekst" defaultChecked error>
Checkboxtekst
</Checkbox>
</div>
);
export const Indeterminate = () => {
const [checked, setChecked] = useState([true, false]);
return (
<>
<Checkbox
checked={checked[0] && checked[1]}
indeterminate={checked[0] !== checked[1]}
onChange={(e) => setChecked([e.target.checked, e.target.checked])}
>
Parent
</Checkbox>
<div style={{ paddingLeft: "2rem" }}>
<Checkbox
checked={checked[0]}
onChange={(e) => setChecked([e.target.checked, checked[1]])}
>
Child 1
</Checkbox>
<Checkbox
checked={checked[1]}
onChange={(e) => setChecked([checked[0], e.target.checked])}
>
Child 2
</Checkbox>
</div>
</>
);
};
@@ -1,203 +0,0 @@
import { Meta, Canvas } from "@storybook/addon-docs";
import { Checkbox, CheckboxGroup } from "../index";
<Meta title="ds-react/form/checkbox/intro" />
# Hvordan ta i bruk Checkbox
```jsx
<CheckboxGroup legend="Mollit eiusmod">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup legend="Mollit eiusmod">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
Label å checkbox settes her da med children
## Description
Man kan både sette en description på CheckboxGroup og selve checkbox
```jsx
<CheckboxGroup legend="Culpa cupidatat" description="Exercitation do labore">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange" description="Laborum ad">
Orange
</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup legend="Mollit eiusmod" description="Exercitation do labore">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange" description="Laborum ad">
Orange
</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
## Errors
Feilmeldinger kan bare settes på gruppa
```jsx
<CheckboxGroup legend="Mollit eiusmod" error="Boks nr 2 må være valgt">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup legend="Mollit eiusmod" error="Boks nr 2 må være valgt">
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
Enkelt-checkboxer tar en `boolean` error prop
```jsx
<Checkbox value="Apple" error>
Apple
</Checkbox>
```
<Canvas>
<Checkbox value="Apple" error>
Apple
</Checkbox>
</Canvas>
## Sizing
Checkboxer har default 48px høy klikkflate. Med size="small" blir klikkflaten 32px
```jsx
<CheckboxGroup
legend="Mollit eiusmod"
error="Boks nr 2 må være valgt"
size="small"
>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup
legend="Mollit eiusmod"
error="Boks nr 2 må være valgt"
size="small"
>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange" error="Boksen må være valgt">
Orange
</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
## defaultValues
```jsx
<CheckboxGroup legend="Mollit eiusmod" defaultValue={["Orange", "Melon"]}>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup legend="Mollit eiusmod" defaultValue={["Orange", "Melon"]}>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
## hideLegend
Ved bruk av `hideLegend` på CheckboxGroup kan man gjøre slik at legend/description bare vises for skjermlesere
```jsx
<CheckboxGroup legend="Mollit eiusmod" hideLegend>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<CheckboxGroup legend="Mollit eiusmod" hideLegend>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</Canvas>
## hideLabel
Ved bruk av `hideLabel` kan man lettere ta i bruk checkbox internt i eks tabeller.
```jsx
<Checkbox value="Orange" hideLabel>
Orange
</Checkbox>
```
<Canvas>
<Checkbox value="Orange" hideLabel>
Orange
</Checkbox>
</Canvas>
## Disabled
Ved bruk av `disabled` kan man både disabled enkelte checkboxer eller checkboxgruppen.
NOTE: Husk at disabled bør unngås!
```jsx
<CheckboxGroup legend="Mollit eiusmod" disabled>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
<CheckboxGroup legend="Mollit eiusmod">
<Checkbox value="Apple" disabled>Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
```
<Canvas>
<div>
<CheckboxGroup legend="Mollit eiusmod" disabled>
<Checkbox value="Apple">Apple</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
<CheckboxGroup legend="Mollit eiusmod">
<Checkbox value="Apple" disabled>
Apple
</Checkbox>
<Checkbox value="Orange">Orange</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
</div>
</Canvas>
@@ -1,109 +0,0 @@
import React, { useState } from "react";
import { Checkbox, CheckboxGroup } from "../../index";
import { Meta } from "@storybook/react/types-6-0";
import { CheckboxGroupProps } from "..";
export default {
title: "ds-react/form/checkbox",
component: Checkbox,
} as Meta;
export const Indeterminate = () => {
const [checked, setChecked] = useState([true, false]);
return (
<>
<Checkbox
checked={checked[0] && checked[1]}
indeterminate={checked[0] !== checked[1]}
onChange={(e) => setChecked([e.target.checked, e.target.checked])}
>
Parent
</Checkbox>
<div style={{ paddingLeft: "2rem" }}>
<Checkbox
checked={checked[0]}
onChange={(e) => setChecked([e.target.checked, checked[1]])}
>
Child 1
</Checkbox>
<Checkbox
checked={checked[1]}
onChange={(e) => setChecked([checked[0], e.target.checked])}
>
Child 2
</Checkbox>
</div>
</>
);
};
export const All = () => {
const Checkboxes = (
props: Omit<CheckboxGroupProps, "legend" | "description" | "children">
) => (
<CheckboxGroup
legend="Mollit eiusmod"
description="Exercitation do labore"
{...props}
>
<Checkbox value={1}>Apple</Checkbox>
<Checkbox value="Orange" description="Laborum ad">
Orange
</Checkbox>
<Checkbox value="Banana" description={<div>Laborum ad</div>}>
Banana
</Checkbox>
<Checkbox value="Melon">Melon</Checkbox>
</CheckboxGroup>
);
return (
<>
<h1>Checkbox</h1>
<h2>Single checkbox</h2>
<Checkbox value="Apple">Apple</Checkbox>
<h3>Desription</h3>
<Checkbox value={1} description="Laborum ad" defaultChecked>
Apple
</Checkbox>
<h3>Error</h3>
<Checkbox value="Apple" error>
Apple
</Checkbox>
<Checkbox value="Apple" error defaultChecked>
Orange
</Checkbox>
<h3>Hide label</h3>
<Checkbox value="Apple" hideLabel description="Laborum ad">
Apple
</Checkbox>
<h3>Disabled</h3>
<Checkbox value="Apple" disabled>
Apple
</Checkbox>
<Checkbox value="Orange" description="Laborum ad" disabled defaultChecked>
Orange
</Checkbox>
<h3>Indeterminate</h3>
<Checkbox value="Apple" indeterminate>
Apple
</Checkbox>
<Checkbox value="Orange" indeterminate size="small">
Orange
</Checkbox>
<h2>Checkbox group</h2>
<Checkboxes />
<h3>Error</h3>
<Checkboxes error="Dette er en feilmelding" />
<h3>Small</h3>
<Checkboxes size="small" />
<h3>Small + error</h3>
<Checkboxes size="small" error="Dette er en feilmelding" />
<h3>Default value</h3>
<Checkboxes defaultValue={[1, "Melon"]} />
<h3>Disabled</h3>
<Checkboxes disabled />
</>
);
};
@@ -0,0 +1,42 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import { ErrorSummary } from "..";
export default {
title: "ds-react/Form/Errorsummary",
component: ErrorSummary,
argTypes: {
headingTag: {
control: {
type: "text",
},
},
size: {
control: {
type: "radio",
options: ["medium", "small"],
},
},
},
} as Meta;
export const Default = (props) => (
<ErrorSummary
heading="Feiloppsummering komponent"
headingTag={props.headingTag || "h2"}
size={props.size ?? undefined}
>
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
);
export const Small = () => (
<ErrorSummary heading="Feiloppsummering komponent" size="small">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
);
@@ -1,54 +0,0 @@
import { Meta, Canvas } from "@storybook/addon-docs";
import { ErrorSummary } from "../index";
<Meta title="ds-react/form/errorsummary/intro" />
# Hvordan ta i bruk ErrorSummary
`href` på `ErrorSummary.Item` linker til id som er satt på skjema-elementer.
```jsx
<ErrorSummary heading="Eiusmod est cillum mollit pariatur deserunt">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
```
<Canvas>
<ErrorSummary heading="Eiusmod est cillum mollit pariatur deserunt">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
</Canvas>
## Custom headingTag
`headingTag` lar deg sette heading til en annen html-tag en default "h2"
```jsx
<ErrorSummary
heading="Eiusmod est cillum mollit pariatur deserunt"
headingTag="h3"
>
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
```
<Canvas>
<ErrorSummary
heading="Eiusmod est cillum mollit pariatur deserunt"
headingTag="h3"
>
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
</Canvas>
@@ -1,28 +0,0 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import { ErrorSummary } from "..";
export default {
title: "ds-react/form/errorsummary",
component: ErrorSummary,
} as Meta;
export const All = () => {
return (
<>
<h1>Error summary</h1>
<ErrorSummary heading="Feiloppsummering komponent" headingTag="h4">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
<h2>size small</h2>
<ErrorSummary size="small" heading="Feiloppsummering komponent">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
</>
);
};
@@ -31,7 +31,9 @@ describe("Controlled RadioGroup", () => {
});
describe("Uncontrolled RadioGroup", () => {
test("handles state correctly", () => {
test("handles state correctly", async () => {
const user = userEvent.setup();
render(<Group />);
const input1 = screen.getByLabelText(label1) as HTMLInputElement;
@@ -40,13 +42,14 @@ describe("Uncontrolled RadioGroup", () => {
expect(input1.checked).toBe(false);
expect(input2.checked).toBe(false);
userEvent.click(screen.getByLabelText(label2));
await user.click(screen.getByLabelText(label2));
expect(input1.checked).toBe(false);
expect(input2.checked).toBe(true);
});
test("handles defaultValue correctly", () => {
test("handles defaultValue correctly", async () => {
const user = userEvent.setup();
render(<Group defaultValue={value1} />);
const input1 = screen.getByLabelText(label1) as HTMLInputElement;
@@ -55,7 +58,7 @@ describe("Uncontrolled RadioGroup", () => {
expect(input1.checked).toBe(true);
expect(input2.checked).toBe(false);
userEvent.click(screen.getByLabelText(label2));
await user.click(screen.getByLabelText(label2));
expect(input1.checked).toBe(false);
expect(input2.checked).toBe(true);
@@ -0,0 +1,82 @@
import React, { useState } from "react";
import { Radio, RadioGroup } from "../../index";
import { Meta } from "@storybook/react/types-6-0";
export default {
title: "ds-react/Form/Radio",
component: Radio,
subcomponents: {
RadioGroup,
},
} as Meta;
export const Default = (props) => {
const [state, setState] = useState("radio1");
return (
<RadioGroup
legend={props.legend}
description={props.description}
value={props.controlled ? state : undefined}
onChange={props.controlled ? setState : undefined}
hideLegend={props.hideLegend}
error={props.errorGroup ? "Errormelding" : undefined}
{...props}
>
<Radio value="radio1">{props.children || "Apple"}</Radio>
<Radio
value="radio2"
description={props.radioDescription ? "Orange description" : undefined}
>
{props.children || "Orange"}
</Radio>
<Radio value="radio3">{props.children || "Banana"}</Radio>
<Radio value="radio4">{props.children || "Melon"}</Radio>
</RadioGroup>
);
};
Default.args = {
controlled: false,
legend: "Legend-tekst",
radioDescription: false,
hideLegend: false,
children: "",
description: "",
};
export const Group = () => (
<RadioGroup legend="Group legend" defaultValue={"tekst2"}>
<Radio value="tekst">Radiotekst</Radio>
<Radio value="tekst2">Radiotekst</Radio>
</RadioGroup>
);
export const GroupError = () => (
<RadioGroup
legend="Group legend"
defaultValue={"tekst2"}
error="Group errormelding"
>
<Radio value="tekst">Radiotekst</Radio>
<Radio value="tekst2">Radiotekst</Radio>
</RadioGroup>
);
export const GroupSmall = () => (
<RadioGroup legend="Group legend" defaultValue={"tekst2"} size="small">
<Radio value="tekst">Radiotekst</Radio>
<Radio value="tekst2">Radiotekst</Radio>
</RadioGroup>
);
export const GroupDescription = () => (
<RadioGroup
legend="Group legend"
defaultValue={"tekst2"}
description="Group description"
>
<Radio value="tekst">Radiotekst</Radio>
<Radio value="tekst2">Radiotekst</Radio>
</RadioGroup>
);
@@ -1,173 +0,0 @@
import { Meta, Canvas } from "@storybook/addon-docs";
import { Radio, RadioGroup } from "../index";
<Meta title="ds-react/form/radio/intro" />
# Hvordan ta i bruk Radio
```jsx
<RadioGroup legend="Mollit eiusmod">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup legend="Mollit eiusmod">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
Label å Radio settes her da med children
## Description
Man kan både sette en description på RadioGroup og selve Radio
```jsx
<RadioGroup legend="Culpa cupidatat" description="Exercitation do labore">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange" description="Laborum ad">
Orange
</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup legend="Mollit eiusmod" description="Exercitation do labore">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange" description="Laborum ad">
Orange
</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
## Errors
Feilmeldinger kan bare settes på gruppa
```jsx
<RadioGroup legend="Mollit eiusmod" error="Boks nr 2 må være valgt">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup legend="Mollit eiusmod" error="Boks nr 2 må være valgt">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
## Sizing
Radioknapper har default 48px høy klikkflate. Med size="small" blir klikkflaten 32px
```jsx
<RadioGroup
legend="Mollit eiusmod"
error="Boks nr 2 må være valgt"
size="small"
>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup
legend="Mollit eiusmod"
error="Boks nr 2 må være valgt"
size="small"
>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange" error="Boksen må være valgt">
Orange
</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
## defaultValue
```jsx
<RadioGroup legend="Mollit eiusmod" defaultValue="Orange">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup legend="Mollit eiusmod" defaultValue="Orange">
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
## hideLegend
Ved bruk av `hideLegend` på RadioGroup kan man gjøre slik at legend/description bare vises for skjermlesere
```jsx
<RadioGroup legend="Mollit eiusmod" hideLegend>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<RadioGroup legend="Mollit eiusmod" hideLegend>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</Canvas>
## Disabled
Ved bruk av `disabled` kan man både disabled enkelte Radioer eller Radiogruppen.
NOTE: Husk at disabled bør unngås!
```jsx
<RadioGroup legend="Mollit eiusmod" disabled>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
<RadioGroup legend="Mollit eiusmod" >
<Radio value="Apple" disabled>Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
```
<Canvas>
<div>
<RadioGroup legend="Mollit eiusmod" disabled>
<Radio value="Apple">Apple</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
<RadioGroup legend="Mollit eiusmod">
<Radio value="Apple" disabled>
Apple
</Radio>
<Radio value="Orange">Orange</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
</div>
</Canvas>
@@ -1,51 +0,0 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import { RadioGroup, Radio } from "../index";
import { RadioGroupProps } from "../RadioGroup";
export default {
title: "ds-react/form/radio",
component: RadioGroup,
} as Meta;
export const All = () => {
const Radios = (props: Omit<RadioGroupProps, "legend" | "children">) => (
<RadioGroup
legend="Mollit eiusmod"
description={<div>"Exercitation do labore"</div>}
{...props}
>
<Radio value={1}>Apple</Radio>
<Radio value={false} description={<div>Laborum ad</div>}>
Orange
</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
);
return (
<>
<h1>Radio group</h1>
<Radios />
<h2>Error</h2>
<Radios error="Dette er en feilmelding" />
<h2>Small</h2>
<Radios size="small" />
<h2>Small + error</h2>
<Radios size="small" error="Dette er en feilmelding" />
<h2>Default value</h2>
<Radios defaultValue={1} />
<h2>Disabled</h2>
<RadioGroup legend="Mollit eiusmod" description="Exercitation do labore">
<Radio value="Apple" disabled>
Apple
</Radio>
<Radio value={false} description="Laborum ad" disabled>
Orange
</Radio>
<Radio value="Melon">Melon</Radio>
</RadioGroup>
<h2>Disabled group</h2>
<Radios disabled />
</>
);
};
+22 -24
View File
@@ -1,10 +1,9 @@
import { Close } from "@navikt/ds-icons";
import { Close, Search as SearchIcon } from "@navikt/ds-icons";
import cl from "classnames";
import React, {
forwardRef,
InputHTMLAttributes,
useCallback,
useEffect,
useRef,
useState,
} from "react";
@@ -59,9 +58,9 @@ export interface SearchProps
clearButton?: boolean;
/**
* Changes button-variant
* @default "tertiary"
* @default "primary"
*/
variant?: "tertiary" | "primary";
variant?: "primary" | "secondary" | "simple";
}
interface SearchComponent
@@ -74,8 +73,8 @@ interface SearchComponent
export interface SearchContextProps {
disabled?: boolean;
size: "medium" | "small";
variant?: "tertiary" | "primary";
onSearch?: () => void;
variant: "primary" | "secondary" | "simple";
onSearch: () => void;
}
export const SearchContext = React.createContext<SearchContextProps | null>(
@@ -99,7 +98,9 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
clearButton = true,
children,
onSearch,
variant = "tertiary",
variant = "primary",
defaultValue,
onChange,
...rest
} = props;
@@ -107,26 +108,23 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
const mergedRef = mergeRefs([searchRef, ref]);
const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
const [controlledValue, setControlledValue] = useState(value ?? "");
const [internalValue, setInternalValue] = useState(defaultValue ?? "");
const handleChange = useCallback(
(v: string) => {
searchRef.current && value === undefined && setControlledValue(v);
props?.onChange?.(v);
value === undefined && setInternalValue(v);
onChange?.(v);
},
[props, value]
[onChange, value]
);
const handleClear = useCallback(
(event: SearchClearEvent) => {
onClear?.(event);
handleChange("");
if (searchRef.current && value === undefined) {
searchRef.current.value = "";
}
searchRef.current && searchRef.current?.focus?.();
},
[handleChange, onClear, value]
[handleChange, onClear]
);
useEventListener(
@@ -143,10 +141,6 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
wrapperRef
);
useEffect(() => {
value !== undefined && setControlledValue(value);
}, [value]);
return (
<div
ref={setWrapperRef}
@@ -184,23 +178,27 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
)}
<div className="navds-search__wrapper">
<div className="navds-search__wrapper-inner">
{variant === "simple" && (
<SearchIcon aria-hidden className="navds-search__search-icon" />
)}
<input
ref={mergedRef}
{...omit(rest, ["size"])}
{...inputProps}
{...(props.value !== undefined && { value: props.value })}
value={value ?? internalValue}
onChange={(e) => handleChange(e.target.value)}
type="search"
role="searchbox"
className={cl(
className,
"navds-search__input",
`navds-search__input--${variant}`,
"navds-text-field__input",
"navds-body-short",
`navds-body-${size ?? "medium"}`
`navds-body-${size}`
)}
/>
{controlledValue && clearButton && (
{(value ?? internalValue) && clearButton && (
<button
type="button"
onClick={(e) => handleClear({ trigger: "Click", event: e })}
@@ -218,10 +216,10 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
size,
disabled: inputProps.disabled,
variant,
onSearch: () => onSearch?.(controlledValue),
onSearch: () => onSearch?.(value ?? internalValue),
}}
>
{children ? children : <SearchButton />}
{children ? children : variant !== "simple" && <SearchButton />}
</SearchContext.Provider>
</div>
</div>
@@ -33,7 +33,7 @@ const SearchButton: SearchButtonType = forwardRef(
{...rest}
ref={ref}
size={size}
variant={variant}
variant={variant === "secondary" ? "secondary" : "primary"}
className={cl("navds-search__button-search", className)}
disabled={context?.disabled ?? disabled}
onClick={(e) => {
@@ -3,92 +3,140 @@ import React, { useState } from "react";
import { Search } from "../index";
export default {
title: "ds-react/form/search",
title: "ds-react/Form/Search",
component: Search,
argTypes: {
clearButton: {
control: {
type: "boolean",
},
},
size: {
control: {
type: "radio",
options: ["medium", "small"],
},
},
variant: {
control: {
type: "radio",
options: ["primary", "secondary", "simple"],
},
},
},
} as Meta;
export const All = () => {
const [value, setValue] = useState("");
export const Default = (props) => {
const [state, setState] = useState("");
return (
<div
style={{
display: "flex",
alignItems: "center",
flexDirection: "column",
maxWidth: 400,
}}
>
<h1>Search</h1>
<Search label="Søk alle sider om X og Y" onSearch={console.log}></Search>
<h2>Primary search</h2>
<div data-theme={props.darkmode ? "dark" : "light"}>
<Search
label="Søk alle sider om X og Y"
onSearch={console.log}
variant="primary"
></Search>
<h2>Search small</h2>
<Search
label="Søk alle sider om X og Y"
description="Beskrivelse av søket"
size="small"
>
<Search.Button />
</Search>
<br />
<Search
label="Søk alle sider om X og Y"
description="Beskrivelse av søket"
size="small"
hideLabel
variant="primary"
>
<Search.Button />
</Search>
<h2>Med knappe-tekst</h2>
<Search label="Søk alle sider om X og Y" hideLabel={false}>
<Search.Button>Søk</Search.Button>
</Search>
<Search
label="Søk alle sider om X og Y"
hideLabel={false}
variant="primary"
>
<Search.Button>Søk</Search.Button>
</Search>
<h2>Hidelabel false</h2>
<Search label="Søk alle sider om X og Y" hideLabel={false}>
<Search.Button />
</Search>
<h2>Controlled state </h2>
<Search
value={value}
label="Søk alle sider om X og Y"
description="Beskrivelse av søket"
onChange={(e) => setValue(e)}
onClear={() => setValue("")}
>
<Search.Button />
</Search>
<h2>No clear button</h2>
<Search
hideLabel
label="Søk alle sider om X og Y"
description="Beskrivelse av søket"
clearButton={false}
>
<Search.Button />
</Search>
value={props.controlled ? state : undefined}
onChange={props.controlled ? setState : null}
onSearch={(v) => console.log({ onSearch: v })}
label="Søk"
size={props.size}
clearButton={props.clearButton}
variant={props.variant}
hideLabel={props.hideLabel}
/>
</div>
);
};
export const UUDemo = () => {
return (
<Search label="Søk på nav.no" onSearch={console.log} placeholder="Søk">
<Search.Button />
</Search>
);
Default.args = {
controlled: false,
darkmode: false,
hideLabel: true,
};
export const Small = () => (
<div className="rowgap">
<div className="colgap">
<Search label="Søk" size="small" />
<Search label="Søk" variant="secondary" size="small" />
<Search label="Søk" variant="simple" size="small" />
</div>
<div className="colgap" data-theme="dark">
<Search label="Søk" size="small" />
<Search label="Søk" variant="secondary" size="small" />
<Search label="Søk" variant="simple" size="small" />
</div>
</div>
);
export const Variants = () => (
<div className="rowgap">
<div className="colgap">
<Search label="Søk" />
<Search label="Søk" variant="secondary" />
<Search label="Søk" variant="simple" />
</div>
<div className="colgap" data-theme="dark">
<Search label="Søk" />
<Search label="Søk" variant="secondary" />
<Search label="Søk" variant="simple" />
</div>
</div>
);
export const Placeholder = () => (
<div className="rowgap">
<div className="colgap">
<Search label="Søk" placeholder="Søk" />
</div>
<div className="colgap" data-theme="dark">
<Search label="Søk" placeholder="Søk" />
</div>
</div>
);
export const Text = () => (
<div className="rowgap">
<div className="colgap">
<Search label="Søk" value="Søketekst" />
<Search label="Søk" variant="secondary" value="Søketekst" />
<Search label="Søk" variant="simple" value="Søketekst" />
</div>
<div className="colgap" data-theme="dark">
<Search label="Søk" value="Søketekst" />
<Search label="Søk" variant="secondary" value="Søketekst" />
<Search label="Søk" variant="simple" value="Søketekst" />
</div>
</div>
);
export const WLabel = () => (
<div className="rowgap">
<div className="colgap">
<Search label="Label søk" variant="simple" hideLabel={false} />
<Search
label="Label søk"
description="Description søk"
variant="simple"
hideLabel={false}
/>
</div>
<div className="colgap" data-theme="dark">
<Search label="Label søk" variant="simple" hideLabel={false} />
<Search
label="Label søk"
description="Description søk"
variant="simple"
hideLabel={false}
/>
</div>
</div>
);
export const NoClearButton = () => (
<div className="colgap">
<Search label="Label søk" clearButton={false} value="søketekst" />
<Search
label="Label søk"
variant="simple"
clearButton={false}
value="søketekst"
/>
</div>
);
@@ -1,12 +1,12 @@
.navds-story-cell {
border-radius: 0.25rem;
border-radius: var(--navds-border-radius-medium);
background: white;
padding: 1rem;
}
.navds-story-content-container {
display: flex;
border-radius: 0.25rem;
border-radius: var(--navds-border-radius-medium);
justify-content: center;
align-items: center;
margin-bottom: 1.5rem;
@@ -42,9 +42,8 @@ const HelpText = forwardRef<HTMLButtonElement, HelpTextProps>(
className={cl(className, "navds-help-text__button")}
type="button"
aria-expanded={open}
title={title}
>
<HelpTextIcon className="navds-help-text__icon" aria-hidden/>
<HelpTextIcon className="navds-help-text__icon" title={title} />
</button>
<Popover
onClose={() => setOpen(false)}
+3
View File
@@ -16,9 +16,12 @@ export * from "./popover";
export * from "./read-more";
export * from "./speech-bubble";
export * from "./step-indicator";
export * from "./stepper";
export * from "./tag";
export * from "./toggle-group";
export * from "./table";
export * from "./tabs";
export * from "./tooltip";
export * from "./typography";
export * from "./util";
@@ -2,7 +2,7 @@ import { Meta, Canvas } from "@storybook/addon-docs";
import { PageHeader } from "../index";
import { illustrationPictogram } from "./pictogram";
<Meta title="ds-react/page-header/intro" />
<Meta title="ds-react(deprecated)/page-header/intro" />
# Hvordan ta i bruk PageHeader
@@ -3,7 +3,7 @@ import { PageHeader } from "../index";
import { Meta } from "@storybook/react/types-6-0";
import { illustrationPictogram } from "./pictogram";
export default {
title: "ds-react/page-header",
title: "ds-react(deprecated)/page-header",
component: PageHeader,
} as Meta;
+133 -109
View File
@@ -1,4 +1,4 @@
import React from "react";
import React, { forwardRef } from "react";
import cl from "classnames";
import { Back, Next } from "@navikt/ds-icons";
import { BodyShort } from "..";
@@ -71,117 +71,141 @@ export const getSteps = ({
];
};
const Pagination = ({
page,
onPageChange,
count,
boundaryCount = 1,
siblingCount = 1,
className,
size = "medium",
prevNextTexts = false,
}: PaginationProps) => {
if (page < 1) {
console.error("page cannot be less than 1");
return null;
}
if (count < 1) {
console.error("count cannot be less than 1");
return null;
}
if (boundaryCount < 0) {
console.error("boundaryCount cannot be less than 0");
return null;
}
if (siblingCount < 0) {
console.error("siblingCount cannot be less than 0");
return null;
}
const Pagination = forwardRef<HTMLElement, PaginationProps>(
(
{
page,
onPageChange,
count,
boundaryCount = 1,
siblingCount = 1,
className,
size = "medium",
prevNextTexts = false,
...rest
},
ref
) => {
if (page < 1) {
console.error("page cannot be less than 1");
return null;
}
if (count < 1) {
console.error("count cannot be less than 1");
return null;
}
if (boundaryCount < 0) {
console.error("boundaryCount cannot be less than 0");
return null;
}
if (siblingCount < 0) {
console.error("siblingCount cannot be less than 0");
return null;
}
return (
<nav
className={cl("navds-pagination", `navds-pagination--${size}`, className)}
>
{prevNextTexts && page !== 1 && (
<button
className="navds-pagination__prev-next"
onClick={() => onPageChange(page - 1)}
>
<Back
className="navds-pagination__prev-next-icon"
role="presentation"
/>
<BodyShort size={size} className="navds-pagination__prev-text">
Forrige
</BodyShort>
</button>
)}
<ul className="navds-pagination__list">
{!prevNextTexts && page !== 1 && (
<li>
<button
className="navds-pagination__prev-next"
onClick={() => onPageChange(page - 1)}
>
<Back
className="navds-pagination__prev-next-icon"
title="Forrige"
/>
</button>
</li>
return (
<nav
ref={ref}
{...rest}
className={cl(
"navds-pagination",
`navds-pagination--${size}`,
className
)}
{getSteps({ page, count, siblingCount, boundaryCount }).map(
(step, i) => {
const n = Number(step);
return isNaN(n) ? (
<li className="navds-pagination__ellipsis" key={`${step}${i}`}>
<BodyShort size={size}>...</BodyShort>
</li>
) : (
<li key={step}>
<BodyShort
size={size}
as="button"
className="navds-pagination__item"
onClick={() => onPageChange(n)}
aria-current={page === n ? true : undefined}
>
{n}
</BodyShort>
</li>
);
}
>
{prevNextTexts && (
<button
className={cl("navds-pagination__prev-next", {
"navds-pagination--invisible": page === 1,
})}
disabled={page === 1}
onClick={() => onPageChange(page - 1)}
>
<Back
className="navds-pagination__prev-next-icon"
role="presentation"
/>
<BodyShort size={size} className="navds-pagination__prev-text">
Forrige
</BodyShort>
</button>
)}
{!prevNextTexts && page !== count && (
<li>
<button
className="navds-pagination__prev-next"
onClick={() => onPageChange(page + 1)}
>
<Next
className="navds-pagination__prev-next-icon"
title="Neste"
/>
</button>
</li>
<ul className="navds-pagination__list">
{!prevNextTexts && (
<li>
<button
className={cl("navds-pagination__prev-next", {
"navds-pagination--invisible": page === 1,
})}
disabled={page === 1}
onClick={() => onPageChange(page - 1)}
>
<Back
className="navds-pagination__prev-next-icon"
title="Forrige"
/>
</button>
</li>
)}
{getSteps({ page, count, siblingCount, boundaryCount }).map(
(step, i) => {
const n = Number(step);
return isNaN(n) ? (
<li className="navds-pagination__ellipsis" key={`${step}${i}`}>
<BodyShort size={size}>...</BodyShort>
</li>
) : (
<li key={step}>
<BodyShort
size={size}
as="button"
className="navds-pagination__item"
onClick={() => onPageChange(n)}
aria-current={page === n ? true : undefined}
>
{n}
</BodyShort>
</li>
);
}
)}
{!prevNextTexts && (
<li>
<button
className={cl("navds-pagination__prev-next", {
"navds-pagination--invisible": page === count,
})}
disabled={page === count}
onClick={() => onPageChange(page + 1)}
>
<Next
className="navds-pagination__prev-next-icon"
title="Neste"
/>
</button>
</li>
)}
</ul>
{prevNextTexts && (
<button
className={cl("navds-pagination__prev-next", {
"navds-pagination--invisible": page === count,
})}
disabled={page === count}
onClick={() => onPageChange(page + 1)}
>
<BodyShort size={size} className="navds-pagination__next-text">
Neste
</BodyShort>
<Next
className="navds-pagination__prev-next-icon"
role="presentation"
/>
</button>
)}
</ul>
{prevNextTexts && page !== count && (
<button
className="navds-pagination__prev-next"
onClick={() => onPageChange(page + 1)}
>
<BodyShort size={size} className="navds-pagination__next-text">
Neste
</BodyShort>
<Next
className="navds-pagination__prev-next-icon"
role="presentation"
/>
</button>
)}
</nav>
);
};
</nav>
);
}
);
export default Pagination;
@@ -29,6 +29,7 @@ export const All = (props) => {
</div>
);
};
All.args = {
page: 1,
count: 8,
@@ -55,6 +55,7 @@ const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
return (
<>
<button
type="button"
{...rest}
className={cl(
"navds-read-more",
@@ -2,7 +2,7 @@ import { Meta, Canvas } from "@storybook/addon-docs";
import { StepIndicator } from "..";
import { Example } from "./Example";
<Meta title="ds-react/step-indicator/intro" />
<Meta title="ds-react(deprecated)/step-indicator/intro" />
# Hvordan ta i bruk StepIndicator
@@ -28,7 +28,7 @@ Du må nå selv styre state med `activeStep` og `onStepChange`.
## responsive
Bruk `responsive`-prop for å skjule labels med en gang StepIndicator begynner å overlfowe.
Bruk `responsive`-prop for å skjule labels med en gang StepIndicator begynner å overflowe.
```jsx
<StepIndicator
@@ -4,7 +4,7 @@ import { Meta } from "@storybook/react/types-6-0";
import { Link, HashRouter as Router, useLocation } from "react-router-dom";
export default {
title: "ds-react/step-indicator",
title: "ds-react(deprecated)/step-indicator",
component: StepIndicator,
} as Meta;
+59
View File
@@ -0,0 +1,59 @@
import cl from "classnames";
import React, { forwardRef, useContext } from "react";
import { StepperContext } from ".";
import { Label, OverridableComponent } from "..";
export interface StepperStepProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
/**
* Text content under indicator
*/
children: string;
/**
* Handled by Stepper
*/
index?: number;
}
export interface StepperStepType
extends OverridableComponent<StepperStepProps, HTMLAnchorElement> {}
const StepComponent: OverridableComponent<
StepperStepProps,
HTMLAnchorElement
> = forwardRef(
({ className, children, as: Component = "a", index = 0, ...rest }, ref) => {
const context = useContext(StepperContext);
if (context === null) {
console.error(
"<StepIndicator.Step> has to be used within an <StepIndicator>"
);
return null;
}
const activeStep = context.activeStep === index;
return (
<Component
{...rest}
aria-current={Boolean(activeStep)}
ref={ref}
className={cl("navds-stepper__step", className, {
"navds-stepper__step--active": activeStep,
})}
onClick={(e) => {
context.onStepChange(index);
rest?.onClick?.(e);
}}
>
<Label className="navds-stepper__step-number" as="span">
{activeStep ? `${index + 1}` : index + 1}
</Label>
<Label className="navds-stepper__step-label">{children}</Label>
</Component>
);
}
);
const Step = StepComponent as StepperStepType;
export default Step;
@@ -0,0 +1,73 @@
import React, { createContext, forwardRef } from "react";
import cl from "classnames";
import Step, { StepperStepProps, StepperStepType } from "./Step";
export interface StepperProps extends React.HTMLAttributes<HTMLOListElement> {
/**
* <Stepper.Step /> elements
*/
children: React.ReactNode;
/**
* Adds classname to wrapper
*/
className?: string;
/**
* Current active step index
*/
activeStep: number;
/**
* Callback for clicked step index
*/
onStepChange?: (step: number) => void;
}
interface StepperComponent
extends React.ForwardRefExoticComponent<
StepperProps & React.RefAttributes<HTMLOListElement>
> {
Step: StepperStepType;
}
interface StepperContextProps {
activeStep: number;
onStepChange: (step: number) => void;
lastIndex: number;
}
export const StepperContext = createContext<StepperContextProps | null>(null);
const Stepper: StepperComponent = forwardRef<HTMLOListElement, StepperProps>(
(
{ children, className, activeStep, onStepChange = () => {}, ...rest },
ref
) => {
return (
<ol {...rest} ref={ref} className={cl("navds-stepper", className)}>
<StepperContext.Provider
value={{
activeStep,
onStepChange,
lastIndex: React.Children.count(children),
}}
>
{React.Children.map(children, (step, index) => {
return (
<li
className={cl("navds-stepper__step-wrapper")}
key={index + (children?.toString?.() ?? "")}
>
{React.isValidElement<StepperStepProps>(step)
? React.cloneElement(step, { ...step.props, index })
: step}
</li>
);
})}
</StepperContext.Provider>
</ol>
);
}
) as StepperComponent;
Stepper.Step = Step;
export default Stepper;
+2
View File
@@ -0,0 +1,2 @@
export { default as Stepper } from "./Stepper";
export * from "./Stepper";
@@ -0,0 +1,28 @@
import React, { useState } from "react";
import { Stepper } from "..";
export const Example = (props) => {
const [activeStep, setActiveStep] = useState(
props.initialStep ? props.initialStep : 1
);
const anchorProps = {
href: "",
onClick: (e) => e.preventDefault(),
};
return (
<Stepper {...props} activeStep={activeStep} onStepChange={setActiveStep}>
<Stepper.Step {...anchorProps}>Start</Stepper.Step>
<Stepper.Step {...anchorProps}>
Sunt deserunt qui sit sunt culpa nisi
</Stepper.Step>
<Stepper.Step {...anchorProps}>
Nulla nisi pariatur nulla cupidatat elit.
</Stepper.Step>
<Stepper.Step {...anchorProps}>
Nulla laborum proident consequat laborum elit et dolore ut sunt.
</Stepper.Step>
</Stepper>
);
};
@@ -0,0 +1,61 @@
import { Meta, Canvas } from "@storybook/addon-docs";
import { Stepper } from "..";
import { Example } from "./Example";
<Meta title="ds-react/stepper/intro" />
# Hvordan ta i bruk Stepper
Stepper er det vertikale søskenet til step-indicator/stegindikator.
Du må nå selv styre state med `activeStep` og `onStepChange`.
## Eksempel
```jsx
<Stepper activeStep={activeStep} onStepChange={(x) => setActiveStep(x)}>
<Stepper.Step href="" onClick={(e) => e.preventDefault()}>
Start
</Stepper.Step>
<Stepper.Step href="" onClick={(e) => e.preventDefault()}>
Sunt deserunt qui sit sunt culpa nisi
</Stepper.Step>
<Stepper.Step href="" onClick={(e) => e.preventDefault()}>
Nulla nisi pariatur nulla cupidatat elit.
</Stepper.Step>
<Stepper.Step href="" onClick={(e) => e.preventDefault()}>
Nulla laborum proident consequat laborum elit et dolore ut sunt.
</Stepper.Step>
</Stepper>
```
<Canvas>
<Example />
</Canvas>
## Override tags
`Stepper.Step` er implementert med Overridable-component noe som gjør at du kan endre taggen til
det du vil med `as`-prop. Merk at Step åakkes inn i en `<li>`-wrapper for tilgjengelighet.
```jsx
<Stepper activeStep={activeStep} onStepChange={(x) => setActiveStep(x)}>
{/* <button> er default */}
<Stepper.Step
href=""
onClick={(e) => e.preventDefault()}
>{`Dette er default, et <a>-element`}</Stepper.Step>
<Stepper.Step as="button">{`Dette er et <button>-element`}</Stepper.Step>
<Stepper.Step as="span">{`Dette er et <span>-element`}</Stepper.Step>
</Stepper>
```
<Canvas>
<Stepper activeStep={1} onStepChange={console.log}>
<Stepper.Step
href=""
onClick={(e) => e.preventDefault()}
>{`Dette er default, et <a>-element`}</Stepper.Step>
<Stepper.Step as="button">{`Dette er et <button>-element`}</Stepper.Step>
<Stepper.Step as="span">{`Dette er et <span>-element`}</Stepper.Step>
</Stepper>
</Canvas>
@@ -0,0 +1,54 @@
import { Meta } from "@storybook/react/types-6-0";
import React, { useState } from "react";
import Stepper from "../Stepper";
import { BodyLong } from "../../typography";
export default {
title: "ds-react/stepper",
component: Stepper,
} as Meta;
export const All = () => {
const storyTexts = [
"Minimize backwards overflow agile. Horsehead offer commitment to the cause nor copy and paste from stack overflow problem territories, innovation is hot right now for can you slack it to me?. High touch client table the discussion , and get buy-in so manage expectations loop back, please advise soonest. We need a paradigm shift dogpile that, and i need to pee and then go to another meeting for let's prioritize the low-hanging fruit.",
"Customer centric sorry i didn't get your email proceduralize, and first-order optimal strategies. I dont care if you got some copy, why you dont use officeipsumcom or something like that ? wheelhouse. Viral engagement new economy, this proposal is a win-win situation which will cause a stellar paradigm shift, and produce a multi-fold increase in deliverables Bob called an all-hands this afternoon. Fire up your browser touch base innovation is hot right now so this medium needs to be more dynamic.",
"Touch base define the underlying principles that drive decisions and strategy for your design language. I have zero cycles for this. Cadence social currency, for low engagement execute . Deliverables rehydrate the team or let's circle back to that those options are already baked in with this model teams were able to drive adoption and awareness we need to start advertising on social media circle back. Through the lens of face time.",
"Take five, punch the tree, and come back in here with a clear head those options are already baked in with this model ultimate measure of success and we need to crystallize a plan yet open door policy who's responsible for the ask for this request? what do you feel you would bring to the table if you were hired for this position. Wiggle room guerrilla marketing shelfware. Code feature creep can we parallel path lose client to 10:00 meeting hire the best manage expectations root-and-branch review.",
"Curate downselect tread it daily cc me on that due diligence, or close the loop. All hands on deck my supervisor didn't like the latest revision you gave me can you switch back to the first revision? ping me or game-plan, yet make it a priority, on this journey win-win. Our competitors are jumping the shark we need to build it so that it scales post launch future-proof can we align on lunch orders. Deliverables message the initiative.",
"Out of scope poop, so pre launch. I just wanted to give you a heads-up wiggle room cc me on that I have been doing some research this morning and we need to better, nor dog and pony show prioritize these line items so UX. Big data upstream selling circle back, in an ideal world. Get all your ducks in a row land it in region so code so one-sheet. Action item we need to think big start small and scale fast to energize our clients. Cta due diligence, for this vendor is incompetent nor forcing function and circle back and low engagement.",
"Move the needle a loss a day will keep you focus yet can you put it into a banner that is not alarming, but eye catching and not too giant or strategic fit, nor it is all exactly as i said, but i don't like it or streamline. We've bootstrapped the model. This proposal is a win-win situation which will cause a stellar paradigm shift, and produce a multi-fold increase in deliverables the horse is out of the barn usabiltiy, for going forward but going forward.",
];
const [activeStep, setActiveStep] = useState(1);
const anchorProps = {
href: "",
onClick: (e) => e.preventDefault(),
};
return (
<div style={{ display: "flex", gap: "10rem" }}>
<div>
<h2 id="stepper-heading">Stepper</h2>
<Stepper
aria-labelledby="stepper-heading"
activeStep={activeStep}
onStepChange={setActiveStep}
>
<Stepper.Step {...anchorProps}>Start søknad</Stepper.Step>
<Stepper.Step {...anchorProps}>Personopplysninger</Stepper.Step>
<Stepper.Step {...anchorProps}>Saksopplysninger</Stepper.Step>
<Stepper.Step {...anchorProps}>
Søknadstekst for en veldig spesifikk prosess i NAV som jeg må skrive
om her i denne labelen
</Stepper.Step>
<Stepper.Step {...anchorProps}>Vedlegg</Stepper.Step>
<Stepper.Step {...anchorProps}>Oppsummering</Stepper.Step>
<Stepper.Step {...anchorProps}>Innsending</Stepper.Step>
</Stepper>
</div>
<BodyLong style={{ marginTop: "5rem" }}>
{storyTexts[activeStep]}
</BodyLong>
</div>
);
};
@@ -1,5 +1,5 @@
import React, { forwardRef, useContext } from "react";
import { Down, Up } from "@navikt/ds-icons";
import { Down, Up, UpDown } from "@navikt/ds-icons";
import { TableContext } from "..";
import HeaderCell, { HeaderCellProps } from "./HeaderCell";
@@ -52,11 +52,14 @@ const ColumnHeader: ColumnHeaderType = forwardRef(
}
>
{children}
{context?.sort?.orderBy === sortKey &&
context?.sort?.direction === "descending" ? (
<Down aria-label="sorter synkende" />
{context?.sort?.orderBy === sortKey ? (
context?.sort?.direction === "descending" ? (
<Down aria-hidden />
) : (
<Up aria-hidden />
)
) : (
<Up aria-label="sorter stigende" />
<UpDown aria-hidden />
)}
</button>
) : (
@@ -0,0 +1,115 @@
import React, { forwardRef, useState } from "react";
import cl from "classnames";
import Row, { RowProps } from "./Row";
import DataCell from "./DataCell";
import { UnmountClosed } from "react-collapse";
import { Expand, ExpandFilled } from "@navikt/ds-icons";
import { useId } from "..";
interface ExpandableRowProps extends RowProps {
/**
* Content of the expanded row
*/
content: React.ReactNode;
/**
* Placement of toggle button
* @default "left"
*/
togglePlacement?: "left" | "right";
/**
* Opens component if 'true', closes if 'false'
* Using this props removes automatic control of open-state
*/
open?: boolean;
/**
* Opened state default
* @default false
*/
defaultOpen?: boolean;
/**
* Change handler for open
*/
onOpenChange?: (open: boolean) => void;
}
export interface ExpandableRowType
extends React.ForwardRefExoticComponent<
ExpandableRowProps & React.RefAttributes<HTMLTableRowElement>
> {}
const ExpandableRow: ExpandableRowType = forwardRef(
(
{
className,
children,
content,
togglePlacement = "left",
defaultOpen = false,
open,
onOpenChange,
...rest
},
ref
) => {
const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);
const id = `expandable-${useId()}`;
const isOpen = open ?? internalOpen;
return (
<>
<Row
{...rest}
ref={ref}
className={cl("navds-table__expandable-row", className, {
"navds-table__expandable-row--open": isOpen,
})}
>
{togglePlacement === "right" && children}
<DataCell
className={cl("navds-table__toggle-expand-cell", {
"navds-table__toggle-expand-cell--open": isOpen,
})}
>
<button
className="navds-table__toggle-expand-button"
aria-controls={id}
aria-expanded={isOpen}
onClick={() => {
onOpenChange?.(!isOpen);
if (open === undefined) {
setInternalOpen((open) => !open);
}
}}
>
<Expand
className="navds-table__expandable-icon"
title={isOpen ? "Vis mindre" : "Vis mer"}
/>
<ExpandFilled
className="navds-table__expandable-icon navds-table__expandable-icon--filled"
title={isOpen ? "Vis mindre" : "Vis mer"}
/>
</button>
</DataCell>
{togglePlacement === "left" && children}
</Row>
<tr className="navds-table__expanded-row" aria-hidden={!isOpen} id={id}>
<td colSpan={999} className="navds-table__expanded-row-cell">
<UnmountClosed
isOpened={isOpen}
theme={{
collapse: "navds-table__expanded-row-collapse",
content: "navds-table__expanded-row-content",
}}
>
{content}
</UnmountClosed>
</td>
</tr>
</>
);
}
);
export default ExpandableRow;
+1 -1
View File
@@ -1,7 +1,7 @@
import React, { forwardRef } from "react";
import cl from "classnames";
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
export interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
/**
* Row is selected
* @default false
+3
View File
@@ -6,6 +6,7 @@ import Row, { RowType } from "./Row";
import ColumnHeader, { ColumnHeaderType } from "./ColumnHeader";
import HeaderCell, { HeaderCellType } from "./HeaderCell";
import DataCell, { DataCellType } from "./DataCell";
import ExpandableRow, { ExpandableRowType } from "./ExpandableRow";
export interface SortState {
orderBy: string;
@@ -44,6 +45,7 @@ export interface TableType
DataCell: DataCellType;
HeaderCell: HeaderCellType;
ColumnHeader: ColumnHeaderType;
ExpandableRow: ExpandableRowType;
}
export interface TableContextProps {
@@ -84,5 +86,6 @@ Table.Row = Row;
Table.ColumnHeader = ColumnHeader;
Table.HeaderCell = HeaderCell;
Table.DataCell = DataCell;
Table.ExpandableRow = ExpandableRow;
export default Table;
@@ -0,0 +1,201 @@
import React, { useState } from "react";
import { Table } from "..";
import { Link } from "../..";
export default {
title: "ds-react/table",
component: Table,
};
export const Expandable = () => {
const [open, setOpen] = useState(false);
return (
<>
<Table>
<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((data) => (
<Table.ExpandableRow
content={data.content}
key={data.name}
togglePlacement="right"
>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{data[key]}</Table.DataCell>
))}
</Table.ExpandableRow>
))}
</Table.Body>
</Table>
<h2>defaultOpen/controlled + small</h2>
<Table size="small">
<Table.Header>
<Table.Row>
<Table.HeaderCell />
{columns.map(({ key, name }) => (
<Table.HeaderCell key={key}>{name}</Table.HeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{data.slice(0, 1).map((data) => (
<Table.ExpandableRow
content={data.content}
key={data.name}
defaultOpen
>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{data[key]}</Table.DataCell>
))}
</Table.ExpandableRow>
))}
{data.slice(1, 2).map((data) => (
<Table.ExpandableRow
content={data.content}
key={data.name}
open={open}
onOpenChange={(open) => setOpen(open)}
>
{columns.map(({ key }) => (
<Table.DataCell key={key}>{data[key]}</Table.DataCell>
))}
</Table.ExpandableRow>
))}
</Table.Body>
</Table>
</>
);
};
const columns = [
{
name: "Navn",
key: "name",
},
{
name: "Favoritt dyr",
key: "animal",
},
{
name: "Favoritt farge",
key: "color",
},
{
name: "Minst favoritt dyr",
key: "leastAnimal",
},
{
name: "Minst favoritt farge",
key: "leastColor",
},
{
name: "Status",
key: "status",
},
];
const data = [
{
name: "Ola Conny Brakkestad",
animal: "Ku",
color: "Brun",
leastAnimal: "Sel",
leastColor: "Lyseblå",
status: "Inaktiv",
content: (
<>
Ola Conny Brakkestad sier "hei". Her er litt innhold med en{" "}
<Link href="#">link.</Link>
</>
),
},
{
name: "Henriette Kristensen",
animal: "Bjørn",
color: "Hvit",
leastAnimal: "Grevling",
leastColor: "Transparent",
status: <Link href="http://example.com">Hissig</Link>,
content: (
<>
Henriette Kristensen sier "hei". Her er litt innhold med en{" "}
<Link href="http://example.com">lenke</Link>. I forhold til en betydelig
avveining synliggjøres potensialet med sikte på satsingsområdet. Gitt en
manglende avveining synliggjøres instrumentet på bakgrunn av forholdene.
Under hensyntagen til en inkluderende avveining stimuleres resultatene
med henblikk på løsningen. På grunn av en integrert organisasjon
initieres kunnskapene eller sagt på en annen måte: evalueringen. Grunnet
en gjeldende målsetting dokumenteres relasjonene hva angår egenarten.
</>
),
},
{
name: "Suki Clydesdale",
animal: "Sel",
color: "Grønn",
leastAnimal: "Serval",
leastColor: "Rosa",
status: "Deaktivert",
content: (
<>
Suki Clydesdale sier "hei". Her er litt innhold med en{" "}
<Link href="http://example.com">link</Link>.
</>
),
},
{
name: "Hans-Hermann Hoppe",
animal: "Mudkip",
color: "Oransje",
leastAnimal: "Skare",
leastColor: "Hvit",
status: <Link href="#">Starter</Link>,
content: (
<>
Hans-Hermann Hoppe sier "hei". Her er litt innhold med en{" "}
<Link href="http://example.com">link</Link>.
</>
),
},
{
name: "Max Kraft",
animal: "Løve",
color: "Blå",
leastAnimal: "Hund",
leastColor: "Cyan",
status: <Link href="#">Eksplodert</Link>,
content: (
<>
Max Kraft sier "hei". Her er litt innhold med en{" "}
<Link href="http://example.com">link</Link>.
</>
),
},
{
name: "Pat Ole",
animal: "Bjørnedyr",
color: "Gul",
leastAnimal: "Meitemark",
leastColor: "Svart",
status: <Link href="#">Gunstig</Link>,
content: (
<>
Pat Ole sier "hei". Her er litt innhold med en{" "}
<Link href="http://example.com">lenke</Link>.
</>
),
},
];
+79
View File
@@ -0,0 +1,79 @@
import * as RadixTabs from "@radix-ui/react-tabs";
import cl from "classnames";
import React, { forwardRef, useContext } from "react";
import { Label, OverridableComponent } from "..";
import { TabsContext } from "./Tabs";
export interface TabProps
extends Omit<React.HTMLAttributes<HTMLButtonElement>, "children"> {
/**
* Content
*/
label?: React.ReactNode;
/**
* Icon
*/
icon?: React.ReactNode;
/**
* Value for state-handling
*/
value: string;
/**
* Icon position
* @default "left"
*/
iconPosition?: "left" | "top";
}
export type TabType = OverridableComponent<TabProps, HTMLButtonElement>;
const Tab: TabType = forwardRef(
(
{
className,
as: Component = "button",
label,
icon,
iconPosition,
value,
...rest
},
ref
) => {
const context = useContext(TabsContext);
if (!label && !icon) {
console.error("<Tabs.Tab/> needs label/icon");
return null;
}
return (
<RadixTabs.Trigger value={value} asChild>
<Component
ref={ref}
className={cl(
"navds-tabs__tab",
`navds-tabs__tab--${context?.size ?? "medium"}`,
`navds-tabs__tab-icon--${iconPosition}`,
className,
{
"navds-tabs__tab--icon-only": icon && !label,
}
)}
{...rest}
>
<Label
as="span"
className="navds-tabs__tab-inner"
size={context?.size}
>
{icon}
{label}
</Label>
</Component>
</RadixTabs.Trigger>
);
}
);
export default Tab;
+127
View File
@@ -0,0 +1,127 @@
import { debounce } from "@material-ui/core";
import { Back, Next } from "@navikt/ds-icons";
import { TabsList } from "@radix-ui/react-tabs";
import cl from "classnames";
import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react";
import mergeRefs from "react-merge-refs";
export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Tab elements
*/
children: React.ReactNode;
/**
* Loops back to start when navigating past last item
*/
loop?: boolean;
}
export type ListType = React.ForwardRefExoticComponent<
ListProps & React.RefAttributes<HTMLDivElement>
>;
const List = forwardRef<HTMLDivElement, ListProps>(
({ className, ...rest }, ref) => {
const listRef = useRef<HTMLDivElement | null>(null);
const mergedRef = mergeRefs([listRef, ref]);
const [displayScroll, setDisplayScroll] = useState({
start: false,
end: false,
});
const updateScrollButtonState = useMemo(
() =>
debounce(() => {
if (!listRef?.current) return;
const { scrollWidth, clientWidth } = listRef?.current;
let showStartScroll;
let showEndScroll;
const scrollLeft = listRef?.current?.scrollLeft;
// use 1 for the potential rounding error with browser zooms.
showStartScroll = scrollLeft > 1;
showEndScroll = scrollLeft < scrollWidth - clientWidth - 1;
setDisplayScroll((displayScroll) =>
showStartScroll === displayScroll.start &&
showEndScroll === displayScroll.end
? displayScroll
: { start: showStartScroll, end: showEndScroll }
);
}),
[]
);
useEffect(() => {
const handleResize = () => updateScrollButtonState();
const win = listRef.current?.ownerDocument ?? document ?? window;
win.addEventListener("resize", handleResize);
let resizeObserver;
if (typeof ResizeObserver !== "undefined") {
resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(listRef.current);
}
return () => {
win.removeEventListener("resize", handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
};
}, [updateScrollButtonState]);
useEffect(() => {
updateScrollButtonState();
});
useEffect(() => {
return () => {
updateScrollButtonState.clear();
};
}, [updateScrollButtonState]);
const ScrollButton = ({
dir,
hidden,
}: {
dir: 1 | -1;
hidden: boolean;
}) => (
<div
className={cl("navds-tabs__scroll-button", {
"navds-tabs__scroll-button--hidden": hidden,
})}
onClick={() => {
if (!listRef.current) return;
listRef.current.scrollLeft += dir * 100;
}}
>
{dir === -1 ? (
<Back title="scroll tilbake" />
) : (
<Next title="scroll neste" />
)}
</div>
);
const showSteppers = displayScroll.end || displayScroll.start;
return (
<div className="navds-tabs__tablist-wrapper">
{showSteppers && (
<ScrollButton dir={-1} hidden={!displayScroll.start} />
)}
<TabsList
{...rest}
ref={mergedRef}
onScroll={updateScrollButtonState}
className={cl("navds-tabs__tablist", className)}
/>
{showSteppers && <ScrollButton dir={1} hidden={!displayScroll.end} />}
</div>
);
}
) as ListType;
export default List;
+30
View File
@@ -0,0 +1,30 @@
import { TabsContent } from "@radix-ui/react-tabs";
import cl from "classnames";
import React, { forwardRef } from "react";
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Tab panel
*/
children: React.ReactNode;
/**
* Value for state-handling
*/
value: string;
}
export type PanelType = React.ForwardRefExoticComponent<
PanelProps & React.RefAttributes<HTMLDivElement>
>;
const Panel = forwardRef<HTMLDivElement, PanelProps>(
({ className, ...rest }, ref) => (
<TabsContent
{...rest}
ref={ref}
className={cl("navds-tabs__tabpanel", className)}
/>
)
) as PanelType;
export default Panel;
@@ -0,0 +1,188 @@
import { Cup, Dishwasher, Freezer } from "@navikt/ds-icons";
import { Meta } from "@storybook/react/types-6-0";
import React, { useState } from "react";
import { Tabs } from ".";
import { Link } from "../link";
export default {
title: "ds-react/tabs",
component: Tabs,
} as Meta;
export const UUDemo = () => (
<Tabs defaultValue="skap" lang="no">
<Tabs.List>
<Tabs.Tab value="skap" label="Skap" icon={<Cup aria-hidden />} />
<Tabs.Tab
value="oppvaskmaskin"
label="Oppvaskmaskin"
icon={<Dishwasher aria-hidden />}
/>
<Tabs.Tab value="fryser" icon={<Freezer aria-hidden />} label="Fryser" />
</Tabs.List>
<Tabs.Panel
value="skap"
style={{ background: "var(--navds-global-color-gray-50)", height: 300 }}
>
Innholdspanel for skap med lenke <Link href="#">Dette er en lenke</Link>
</Tabs.Panel>
<Tabs.Panel
value="oppvaskmaskin"
style={{ background: "var(--navds-global-color-green-50)", height: 300 }}
>
Innholdspanel for oppvaskmaskin med lenke{" "}
<Link href="#">Dette er en lenke</Link>
</Tabs.Panel>
<Tabs.Panel
value="fryser"
style={{ background: "var(--navds-global-color-red-50)", height: 300 }}
>
Innholdspanel for fryser med lenke <Link href="#">Dette er en lenke</Link>
</Tabs.Panel>
</Tabs>
);
const Panel = () => {
return (
<>
<Tabs.Panel
value="test1"
style={{ background: "var(--navds-global-color-gray-50)", height: 100 }}
>
Innholdspanel for Skap-tab
</Tabs.Panel>
<Tabs.Panel
value="test2"
style={{
background: "var(--navds-global-color-green-50)",
height: 100,
}}
>
Innholdspanel for Oppvaskmaskin-tab
</Tabs.Panel>
<Tabs.Panel
value="test3"
style={{ background: "var(--navds-global-color-red-50)", height: 100 }}
>
Innholdspanel for Fryser-tab
</Tabs.Panel>
</>
);
};
export const All = () => {
const [activeValue, setActiveValue] = useState("test1");
return (
<div>
<h2>Tabs</h2>
<Tabs defaultValue="test2">
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} label="Skap" />
<Tabs.Tab value="test2" label="Oppvaskmaskin" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} label="Fryser" />
</Tabs.List>
<Panel />
</Tabs>
<h2>Controlled</h2>
<Tabs value={activeValue} onChange={setActiveValue}>
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} label="Skap" />
<Tabs.Tab value="test2" label="Oppvaskmaskin" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} label="Fryser" />
</Tabs.List>
<Panel />
</Tabs>
<h2>selectionFollowsFocus</h2>
<Tabs defaultValue="test2" selectionFollowsFocus>
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} label="Skap" />
<Tabs.Tab value="test2" label="Oppvaskmaskin" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} label="Fryser" />
</Tabs.List>
<Panel />
</Tabs>
<h2>Tabs iconPosition="top"</h2>
<Tabs defaultValue="test2">
<Tabs.List>
<Tabs.Tab
value="test1"
icon={<Cup />}
label="Skap"
iconPosition="top"
/>
<Tabs.Tab
value="test2"
label="Oppvaskmaskin"
icon={<Dishwasher />}
iconPosition="top"
/>
<Tabs.Tab
value="test3"
icon={<Freezer />}
label="Fryser"
iconPosition="top"
/>
</Tabs.List>
<Panel />
</Tabs>
<h2>Tabs small</h2>
<Tabs defaultValue="test2" size="small">
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} label="Skap" />
<Tabs.Tab value="test2" label="Oppvaskmaskin" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} label="Fryser" />
</Tabs.List>
<Panel />
</Tabs>
<br />
<Tabs defaultValue="test2" size="small">
<Tabs.List>
<Tabs.Tab
value="test1"
icon={<Cup />}
label="Skap"
iconPosition="top"
/>
<Tabs.Tab
value="test2"
label="Oppvaskmaskin"
icon={<Dishwasher />}
iconPosition="top"
/>
<Tabs.Tab
value="test3"
icon={<Freezer />}
label="Fryser"
iconPosition="top"
/>
</Tabs.List>
<Panel />
</Tabs>
<h2>Tabs Icon-only</h2>
<Tabs defaultValue="test2">
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} />
<Tabs.Tab value="test2" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} />
</Tabs.List>
<Panel />
</Tabs>
<br />
<Tabs defaultValue="test2" size="small">
<Tabs.List>
<Tabs.Tab value="test1" icon={<Cup />} />
<Tabs.Tab value="test2" icon={<Dishwasher />} />
<Tabs.Tab value="test3" icon={<Freezer />} />
</Tabs.List>
<Panel />
</Tabs>
</div>
);
};
+89
View File
@@ -0,0 +1,89 @@
import cl from "classnames";
import React, { createContext, forwardRef, HTMLAttributes } from "react";
import * as RadixTabs from "@radix-ui/react-tabs";
import Tab, { TabType } from "./Tab";
import List, { ListType } from "./TabList";
import Panel, { PanelType } from "./TabPanel";
export interface TabsProps
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "dir"> {
/**
* Tabs elements
*/
children: React.ReactNode;
/**
* Changes padding and font-size
* @default "medium"
*/
size?: "medium" | "small";
/**
* onChange
*/
onChange?: (value: string) => void;
/**
* Controlled selected value
*/
value?: string;
/**
* If not controlled, a default-value needs to be set
*/
defaultValue?: string;
/**
* Automatically activates tab on focus/navigation
* @default false
*/
selectionFollowsFocus?: boolean;
}
interface TabsComponent
extends React.ForwardRefExoticComponent<
TabsProps & React.RefAttributes<HTMLDivElement>
> {
Tab: TabType;
List: ListType;
Panel: PanelType;
}
interface TabsContextProps {
size: "medium" | "small";
}
export const TabsContext = createContext<TabsContextProps | null>(null);
const Tabs = forwardRef<HTMLDivElement, TabsProps>(
(
{
className,
children,
onChange,
size = "medium",
selectionFollowsFocus = false,
...rest
},
ref
) => {
return (
<RadixTabs.Root
{...rest}
ref={ref}
className={cl("navds-tabs", className, `navds-tabs--${size}`)}
activationMode={selectionFollowsFocus ? "automatic" : "manual"}
onValueChange={onChange}
>
<TabsContext.Provider
value={{
size,
}}
>
{children}
</TabsContext.Provider>
</RadixTabs.Root>
);
}
) as TabsComponent;
Tabs.Tab = Tab;
Tabs.List = List;
Tabs.Panel = Panel;
export default Tabs;
+2
View File
@@ -0,0 +1,2 @@
export { default as Tabs, TabsProps } from "./Tabs";
export { TabProps } from "./Tab";
+303
View File
@@ -0,0 +1,303 @@
import cl from "classnames";
import React, {
cloneElement,
forwardRef,
HTMLAttributes,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { composeEventHandlers, Detail, useEventListener } from "..";
import {
useFloating,
arrow as flArrow,
shift,
autoUpdate,
flip,
hide,
} from "@floating-ui/react-dom";
import mergeRefs from "react-merge-refs";
import Portal from "./portal";
import { useId } from "../util";
export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
/**
* Element tooltip anchors to
*/
children: React.ReactElement & React.RefAttributes<HTMLElement>;
/**
* Open state for contolled tooltip
*/
open?: boolean;
/**
* Tells tooltip to start in open state
* @note "open"-prop overwrites this
*/
defaultOpen?: boolean;
/**
* Orientation for tooltip
* @default "top"
*/
placement?: "top" | "right" | "bottom" | "left";
/**
* Toggles rendering of arrow
* @default true
*/
arrow?: boolean;
/**
* Distance from anchor to tooltip
* @default 10px with arrow, 2px without arrow
*/
offset?: number;
/**
* Content shown in tooltip
*/
content: string;
/**
* Sets max allowed character length
* @default 80
*/
maxChar?: number;
/**
* Adds a delay in milliseconds before opening tooltip
* @default 300
*/
delay?: number;
/**
* List of Keyboard-keys for shortcuts
*/
keys?: string[];
}
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
(
{
children,
className,
arrow: _arrow = true,
placement: _placement = "top",
open,
defaultOpen = false,
offset: _offset,
content,
delay = 150,
id,
keys,
maxChar = 80,
...rest
},
ref
) => {
const arrowRef = useRef<HTMLDivElement | null>(null);
const [isOpen, setIsOpen] = useState(defaultOpen);
const openTimerRef = useRef(0);
const leaveTimerRef = useRef(0);
const isMouseDownRef = useRef(false);
const ariaId = useId(id);
const {
x,
y,
update,
placement,
refs,
middlewareData: {
arrow: { x: arrowX, y: arrowY } = {},
hide: { referenceHidden } = {},
},
} = useFloating({
placement: _placement,
middleware: [
shift(),
flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
flArrow({ element: arrowRef, padding: 5 }),
hide(),
],
});
/* https://floating-ui.com/docs/react-dom#updating */
useEffect(() => {
if (!refs.reference.current || !refs.floating.current) {
return;
}
// Only call this when the floating element is rendered
return autoUpdate(refs.reference.current, refs.floating.current, update);
}, [refs.reference, refs.floating, update, open, isOpen]);
const handleOpen = useCallback(() => {
window.clearTimeout(openTimerRef.current);
window.clearTimeout(leaveTimerRef.current);
setIsOpen(true);
}, [setIsOpen]);
const handleDelayedOpen = useCallback(() => {
window.clearTimeout(openTimerRef.current);
window.clearTimeout(leaveTimerRef.current);
openTimerRef.current = window.setTimeout(() => {
setIsOpen(true);
}, delay);
}, [delay, setIsOpen]);
const handleClose = useCallback(() => {
window.clearTimeout(openTimerRef.current);
leaveTimerRef.current = window.setTimeout(() => {
setIsOpen(false);
}, 50);
}, [setIsOpen]);
const handleMouseUp = useCallback(
() => (isMouseDownRef.current = false),
[]
);
useEffect(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => window.clearTimeout(openTimerRef.current);
}, []);
useEffect(() => {
return () => document.removeEventListener("mouseup", handleMouseUp);
}, [handleMouseUp]);
useEventListener(
"keydown",
useCallback((e) => e.key === "Escape" && handleClose(), [handleClose])
);
/* https://floating-ui.com/docs/react-dom#stable-ref-prop */
const stableRef = useMemo(() => mergeRefs([ref, refs.floating]), [
ref,
refs.floating,
]);
if (
!children ||
children?.type === React.Fragment ||
(children as any) === React.Fragment
) {
console.error(
"<Tooltip> children needs to be a single ReactElement and not <React.Fragment/>/<></>"
);
return null;
}
if (content?.length > maxChar) {
console.error(
`Because of strict accessibility concers we encourage all Tooltips to have less than 80 characters. Can be overwritten with the maxChar-prop`
);
return null;
}
return (
<>
{cloneElement(children, {
...children.props,
"aria-describedby":
open || isOpen
? cl(ariaId, children?.props["aria-describedby"])
: children?.props["aria-describedby"],
ref: mergeRefs([(children as any).ref, refs.reference]),
onMouseEnter: composeEventHandlers(
children.props.onMouseEnter,
handleDelayedOpen
),
onMouseLeave: composeEventHandlers(
children.props.onMouseLeave,
handleClose
),
onMouseDown: composeEventHandlers(children.props.onMouseDown, () => {
isMouseDownRef.current = true;
document &&
document.addEventListener("mouseup", handleMouseUp, {
once: true,
});
}),
onFocus: composeEventHandlers(
children.props.onFocus,
() => !isMouseDownRef.current && handleOpen()
),
onBlur: composeEventHandlers(children.props.onBlur, handleClose),
})}
{(open ?? isOpen) && (
<Portal>
<div
ref={stableRef}
{...rest}
onMouseEnter={handleOpen}
onMouseLeave={handleClose}
role="tooltip"
id={ariaId}
style={{
position: "absolute",
top: y ?? "",
left: x ?? "",
visibility: referenceHidden ? "hidden" : "visible",
}}
data-side={placement}
className={cl(
"navds-tooltip",
"navds-detail navds-detail--small",
className
)}
>
<div
className="navds-tooltip__inner"
style={{
[{
top: "marginBottom",
right: "marginLeft",
bottom: "marginTop",
left: "marginRight",
}[placement]]: _offset ? _offset : _arrow ? 10 : 2,
}}
>
{content}
{keys && (
<span className="navds-tooltip__keys">
{keys.map((key) => (
<Detail
size="small"
as="kbd"
key={key}
className="navds-tooltip__key"
>
{key}
</Detail>
))}
</span>
)}
{_arrow && (
<div
ref={(node) => {
arrowRef.current = node;
}}
className="navds-tooltip__arrow"
style={{
left: arrowX != null ? `${arrowX}px` : "",
top: arrowY != null ? `${arrowY}px` : "",
right: "",
bottom: "",
[{
top: "bottom",
right: "left",
bottom: "top",
left: "right",
}[placement]]: "-3.5px",
}}
/>
)}
</div>
</div>
</Portal>
)}
</>
);
}
);
export default Tooltip;
+2
View File
@@ -0,0 +1,2 @@
export { default as Tooltip } from "./Tooltip";
export * from "./Tooltip";
+15
View File
@@ -0,0 +1,15 @@
/* https://github.com/radix-ui/primitives/blob/main/packages/react/portal/src/Portal.tsx */
import ReactDOM from "react-dom";
const Portal = ({ children }) => {
const hostElement = globalThis?.document?.body;
if (hostElement) {
return ReactDOM.createPortal(children, hostElement);
}
// bail out of ssr
return null;
};
export default Portal;
@@ -0,0 +1,144 @@
import React, { useRef, useState } from "react";
import { Tooltip } from "../index";
import { Meta } from "@storybook/react/types-6-0";
import { Refresh } from "@navikt/ds-icons";
import { Button } from "../..";
import { ToggleGroup } from "../toggle-group";
export default {
title: "ds-react/tooltip",
component: Tooltip,
parameters: {
chromatic: { disable: true },
},
} as Meta;
export const Demo = () => {
const [open, setOpen] = useState(true);
const testRef = useRef(null);
return (
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
flexDirection: "column",
gap: 32,
justifyContent: "center",
alignItems: "center",
}}
>
<Tooltip
content="Tooltip example"
keys={["Cmd", "K"]}
placement="right"
open={open}
ref={testRef}
>
<Button aria-describedby="test123" onClick={() => setOpen(!open)}>
Tooltip C
</Button>
</Tooltip>
<Tooltip
content="Tooltip example"
keys={["Cmd", "K"]}
placement="right"
defaultOpen
>
<Button onClick={() => console.log(testRef.current)}>Tooltip C</Button>
</Tooltip>
<ToggleGroup onChange={null} defaultValue="321">
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="123">Tekst</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="321">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3212">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3213">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3214">tekst 2</ToggleGroup.Item>
</Tooltip>
</ToggleGroup>
</div>
);
};
export const All = () => {
const [open, setOpen] = useState(false);
return (
<div style={{ margin: "4rem 8rem 4rem 8rem" }}>
<h2>Controlled</h2>
<Tooltip open={open} content="Controlled tooltip example" placement="top">
<Button onClick={() => setOpen((x) => !x)}>Toggle tooltip</Button>
</Tooltip>
<h2>no arrow</h2>
<Tooltip content="no arrow" placement="top" arrow={false}>
<Button>Tooltip</Button>
</Tooltip>
<h2>Keys</h2>
<Tooltip content="Tooltip" placement="top" open keys={["Cmd", "K"]}>
<Button>Tooltip</Button>
</Tooltip>
<h2>more offset</h2>
<Tooltip content="Tooltip" placement="top" open offset={20}>
<Button>Tooltip</Button>
</Tooltip>
<h2>all placements</h2>
<div
style={{
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
gap: "3rem",
}}
>
{["top", "left", "bottom", "right"].map((placement) => (
<div key={placement}>
<h3>{placement}</h3>
<div
style={{
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
gap: "3rem",
}}
>
<Tooltip
key={placement}
defaultOpen
content={placement}
placement={placement as any}
>
<Refresh aria-hidden tabIndex={0} />
</Tooltip>
</div>
</div>
))}
</div>
</div>
);
};
export const UUDemo = () => {
return (
<div>
<Button>Placeholder</Button>
<br />
<br />
<Tooltip content="Shortcut" placement="right" keys={["Cmd", "S"]}>
<Button>Lagre</Button>
</Tooltip>
<br />
<br />
<Button>Placeholder</Button>
</div>
);
};
+16 -2
View File
@@ -32,7 +32,7 @@ export interface ListenerT {
export const useEventListener = <T extends ListenerT>(
name: Parameters<ListenerT["addEventListener"]>[0],
handler: Parameters<ListenerT["addEventListener"]>[1],
target: null | T | Window = window
target: null | T | Window = typeof window !== "undefined" ? window : null
): void => {
useEffect(() => {
if (!target) {
@@ -40,7 +40,21 @@ export const useEventListener = <T extends ListenerT>(
}
target?.addEventListener(name, handler);
return () => {
target?.addEventListener(name, handler);
target?.removeEventListener(name, handler);
};
}, [name, handler, target]);
};
/* https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx */
export const composeEventHandlers = <E>(
originalEventHandler?: (event: E) => void,
ourEventHandler?: (event: E) => void
) => {
return function handleEvent(event: E) {
originalEventHandler?.(event);
if (!((event as unknown) as Event).defaultPrevented) {
return ourEventHandler?.(event);
}
};
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "0.1.6",
"version": "0.1.9",
"private": false,
"description": "Auto-generated tailwind config based on @navikt/ds-tokens",
"author": "NAV Designsystem team",
@@ -23,7 +23,7 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^0.8.4",
"@navikt/ds-tokens": "^0.8.7",
"@types/jest": "^27.0.1",
"jest": "^27.2.0",
"lodash": "^4.17.21",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "0.8.4",
"version": "0.8.7",
"private": false,
"description": "Auto-generated design-tokens for NAV design-systems framework",
"author": "NAV Designsystem team",
+1 -1
View File
@@ -396,7 +396,7 @@
},
"focus": {
"inverted": {
"value": "{navds.global.color.blue.200.value}"
"value": "{navds.global.color.blue.300.value}"
},
"@": {
"value": "{navds.global.color.blue.800.value}"
+1
View File
@@ -43,6 +43,7 @@ module.exports = {
modal: { value: 2000 },
popover: { value: 1000 },
focus: { value: 10 },
tooltip: { value: 3000 },
},
},
};
@@ -0,0 +1,42 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import * as tokens from "../dist/tokens.js";
export default {
title: "ds-tokens/Border-radius",
} as Meta;
export const BorderRadius = () => {
const allRadius: { [key: string]: string } = Object.entries(tokens).reduce(
(old, [key, val]) =>
key.startsWith("NavdsBorderRadius") ? { ...old, [key]: val } : { ...old },
{}
);
const sortObject = Object.fromEntries(
Object.entries(allRadius).sort(
(a, b) => Number(a[1].replace("px", "")) - Number(b[1].replace("px", ""))
)
);
return (
<div className="colgap">
{Object.entries(sortObject).map(([key, val]) => (
<div
key={key}
style={{
width: 600,
height: 40,
backgroundColor: "thistle",
borderRadius: val,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
{key}
</div>
))}
</div>
);
};
@@ -0,0 +1,45 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import * as tokens from "../dist/tokens.js";
export default {
title: "ds-tokens/Colors",
} as Meta;
export const Global = () => {
const globalColors: { [key: string]: string } = Object.entries(tokens).reduce(
(old, [key, val]) =>
key.includes("GlobalColor") ? { ...old, [key]: val } : { ...old },
{}
);
return (
<div className="colgap">
{Object.entries(globalColors).map(([key, val]) => (
<div key={key}>
<span>{key}</span>
<div style={{ background: val, width: 600, height: 40 }} />
</div>
))}
</div>
);
};
export const Semantic = () => {
const globalColors: { [key: string]: string } = Object.entries(tokens).reduce(
(old, [key, val]) =>
key.includes("SemanticColor") ? { ...old, [key]: val } : { ...old },
{}
);
return (
<div className="colgap">
{Object.entries(globalColors).map(([key, val]) => (
<div key={key}>
<span>{key}</span>
<div style={{ background: val, width: 600, height: 40 }} />
</div>
))}
</div>
);
};
@@ -0,0 +1,35 @@
import React from "react";
import { Meta } from "@storybook/react/types-6-0";
import * as tokens from "../dist/tokens.js";
export default {
title: "ds-tokens/Shadows",
} as Meta;
export const Shadows = () => {
const allShadows: { [key: string]: string } = Object.entries(tokens).reduce(
(old, [key, val]) =>
key.startsWith("NavdsShadow") ? { ...old, [key]: val } : { ...old },
{}
);
return (
<div className="colgap" style={{ gap: "4rem" }}>
{Object.entries(allShadows).map(([key, val]) => (
<div
key={key}
style={{
height: 40,
width: 600,
boxShadow: val,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
{key}
</div>
))}
</div>
);
};
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more