mirror of
https://github.com/navikt/aksel.git
synced 2026-10-16 10:42:03 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cb25e44de | ||
|
|
5812d6ee87 | ||
|
|
a402777c70 | ||
|
|
d4064bd461 | ||
|
|
1c482e03b8 | ||
|
|
608e1f3944 | ||
|
|
107d6f3d93 | ||
|
|
09d41e9b6d | ||
|
|
ebacbddd44 | ||
|
|
e58b072c84 | ||
|
|
e7cb3753a0 | ||
|
|
7486ba11f9 | ||
|
|
cf01a6e162 | ||
|
|
a67f95433f |
No files matched your search
@@ -75,7 +75,7 @@ jobs:
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: github-comment-finder
|
||||
body-includes: Storybook demo
|
||||
|
||||
- name: Create or update comment
|
||||
if: steps.chromatic_tests.outcome == 'success' && !contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
### [<img id="github-comment-finder" src="https://raw.githubusercontent.com/storybookjs/brand/main/logo/logo-storybook-inverse.svg" height="32"/>](${{steps.storybook_url.outputs.replaced}})
|
||||
## [Storybook demo](${{steps.storybook_url.outputs.replaced}})
|
||||
|
||||
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
|
||||
edit-mode: replace
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
## 4.11.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
## 4.11.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"author": "Aksel | NAV",
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
|
||||
"repository": {
|
||||
@@ -35,8 +35,8 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^4.11.2",
|
||||
"@navikt/ds-tokens": "^4.11.2",
|
||||
"@navikt/ds-css": "^4.12.0",
|
||||
"@navikt/ds-tokens": "^4.12.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"concurrently": "7.2.1",
|
||||
"copyfiles": "2.4.1",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 4.12.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1c482e03b`](https://github.com/navikt/aksel/commit/1c482e03b845c4e6f6f1e174a58992fbeeabb52e), [`107d6f3d9`](https://github.com/navikt/aksel/commit/107d6f3d935cbc9bb94bc15cea63431cd15bbd61), [`09d41e9b6`](https://github.com/navikt/aksel/commit/09d41e9b6da5486a55faaf8088191b4a8374e420), [`a402777c7`](https://github.com/navikt/aksel/commit/a402777c70a18899ef4239b10a6335a671e26167)]:
|
||||
- @navikt/ds-css@4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"description": "Aksel command line interface. Handles css-imports, codemods and more",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "4.11.2",
|
||||
"@navikt/ds-css": "4.12.0",
|
||||
"axios": "1.3.6",
|
||||
"chalk": "4.1.0",
|
||||
"clipboardy": "^2.3.0",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 4.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
|
||||
|
||||
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
|
||||
|
||||
- Alert: Fikset alignment av status-ikon mot tekst ([#2179](https://github.com/navikt/aksel/pull/2179))
|
||||
|
||||
## 4.11.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -24,10 +24,17 @@
|
||||
font-size: 1.5rem;
|
||||
align-self: flex-start;
|
||||
height: var(--a-font-line-height-xlarge);
|
||||
margin: var(--a-spacing-1);
|
||||
margin-inline: var(--a-spacing-1);
|
||||
margin-block: 0;
|
||||
margin-block-start: -1px;
|
||||
background: radial-gradient(circle, var(--a-surface-default) 50%, 0, transparent);
|
||||
}
|
||||
|
||||
.navds-alert--close-button > .navds-alert__icon,
|
||||
.navds-alert--small > .navds-alert__icon {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
.navds-alert--small > .navds-alert__icon {
|
||||
height: var(--a-font-line-height-large);
|
||||
}
|
||||
|
||||
@@ -52,9 +52,16 @@
|
||||
.navds-copybutton__icon {
|
||||
font-size: 1.5rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navds-copybutton__icon:first-of-type {
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
|
||||
.navds-copybutton__icon:last-of-type {
|
||||
margin-right: -0.25rem;
|
||||
}
|
||||
|
||||
.navds-copybutton__icon:only-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,11 @@
|
||||
padding-left: calc(var(--a-spacing-3) - 4px);
|
||||
}
|
||||
|
||||
.navds-form-field--small .navds-combobox__list-item--focus,
|
||||
.navds-form-field--small .navds-combobox__list-item:hover {
|
||||
padding-left: calc(var(--a-spacing-2) - 4px);
|
||||
}
|
||||
|
||||
.navds-combobox__list-item--selected {
|
||||
background-color: var(--ac-combobox-list-item-selected-bg, var(--a-surface-selected));
|
||||
}
|
||||
|
||||
@@ -114,14 +114,27 @@
|
||||
}
|
||||
|
||||
.navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMyAxMCI+ICAgIDxnPiAgICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNCwxMGMtMC40LDAtMC44LTAuMS0xLjEtMC40TDAuNCw3LjFDMC4xLDYuOCwwLDYuNCwwLDZzMC4yLTAuOCwwLjUtMS4xQzEsNC40LDIsNC40LDIuNSw0LjlMNCw2LjRsNi40LTYgICAgQzEwLjgsMC4xLDExLjEsMCwxMS41LDBjMC40LDAsMC44LDAuMiwxLDAuNWMwLjYsMC42LDAuNSwxLjYtMC4xLDIuMXYwTDUsOS42QzQuNyw5LjksNC40LDEwLDQsMTB6IE0xMS44LDEuOUwxMS44LDEuOSAgICBDMTEuOCwxLjksMTEuOCwxLjksMTEuOCwxLjl6IE0xMS4yLDEuMUMxMS4yLDEuMSwxMS4yLDEuMSwxMS4yLDEuMUwxMS4yLDEuMXoiLz4gICAgPC9nPjwvc3ZnPg==);
|
||||
background-position: 0.375rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 0.8125rem;
|
||||
box-shadow: none;
|
||||
background-color: var(--ac-radio-checkbox-action, var(--a-surface-action));
|
||||
}
|
||||
|
||||
.navds-checkbox__input:where(:not(:checked)) + .navds-checkbox__label > .navds-checkbox__icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
|
||||
color: var(--ac-radio-checkbox-bg, var(--a-surface-default));
|
||||
position: absolute;
|
||||
height: 1.5rem;
|
||||
transform: translate(0.375rem);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navds-checkbox--small .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
|
||||
transform: translate(0.25rem, -10%);
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.navds-checkbox--small > .navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
background-position: 0.25rem center;
|
||||
}
|
||||
@@ -283,11 +296,14 @@
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label::before {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMyAxMCI+ICAgIDxnPiAgICA8cGF0aCBmaWxsPSJyZ2JhKDAsIDAsIDAsIDAuNjUpIiBkPSJNNCwxMGMtMC40LDAtMC44LTAuMS0xLjEtMC40TDAuNCw3LjFDMC4xLDYuOCwwLDYuNCwwLDZzMC4yLTAuOCwwLjUtMS4xQzEsNC40LDIsNC40LDIuNSw0LjlMNCw2LjRsNi40LTYgICAgQzEwLjgsMC4xLDExLjEsMCwxMS41LDBjMC40LDAsMC44LDAuMiwxLDAuNWMwLjYsMC42LDAuNSwxLjYtMC4xLDIuMXYwTDUsOS42QzQuNyw5LjksNC40LDEwLDQsMTB6IE0xMS44LDEuOUwxMS44LDEuOSAgICBDMTEuOCwxLjksMTEuOCwxLjksMTEuOCwxLjl6IE0xMS4yLDEuMUMxMS4yLDEuMSwxMS4yLDEuMSwxMS4yLDEuMUwxMS4yLDEuMXoiLz4gICAgPC9nPjwvc3ZnPg==);
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border);
|
||||
background-color: var(--__ac-radio-checkbox-readonly-bg);
|
||||
}
|
||||
|
||||
.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label > .navds-checkbox__icon {
|
||||
color: var(--a-icon-subtle);
|
||||
}
|
||||
|
||||
.navds-radio--readonly > .navds-radio__input:checked + .navds-radio__label::before {
|
||||
box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border), inset 0 0 0 4px var(--__ac-radio-checkbox-readonly-bg);
|
||||
background-color: var(--a-icon-subtle);
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
.navds-list ul,
|
||||
.navds-list ol {
|
||||
padding: 0;
|
||||
margin-block: var(--a-spacing-4);
|
||||
}
|
||||
|
||||
.navds-list--nested {
|
||||
margin: var(--a-spacing-4) 0 var(--a-spacing-8) 0;
|
||||
.navds-list--nested ul,
|
||||
.navds-list--nested ol {
|
||||
margin-block: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-list li:not(.navds-list__item--noMargin) {
|
||||
margin-bottom: var(--a-spacing-5);
|
||||
.navds-list li {
|
||||
margin-block-end: var(--a-spacing-5);
|
||||
}
|
||||
|
||||
.navds-list--small li {
|
||||
margin-block-end: var(--a-spacing-2);
|
||||
}
|
||||
|
||||
.navds-list--nested li {
|
||||
margin-block-end: var(--a-spacing-3);
|
||||
}
|
||||
|
||||
.navds-list:last-child > ul > li:last-child,
|
||||
@@ -34,17 +44,25 @@
|
||||
|
||||
.navds-list .navds-list__item-marker--bullet {
|
||||
display: flex;
|
||||
margin-top: 0.375rem;
|
||||
align-items: center;
|
||||
height: var(--a-font-line-height-large);
|
||||
grid-column: 1 / 2;
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
color: var(--ac-list-marker-ul-color, var(--ac-list-marker-color, var(--a-icon-default)));
|
||||
}
|
||||
|
||||
.navds-list--small .navds-list__item-marker--bullet {
|
||||
height: var(--a-font-line-height-medium);
|
||||
}
|
||||
|
||||
.navds-list__item-marker--icon {
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
color: var(--ac-list-marker-icon-color, var(--ac-list-marker-color, var(--a-icon-default)));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--a-font-line-height-large);
|
||||
padding-block-end: 1px;
|
||||
}
|
||||
|
||||
.navds-list--small .navds-list__item-marker--icon {
|
||||
height: var(--a-font-line-height-medium);
|
||||
}
|
||||
|
||||
@@ -58,6 +76,7 @@
|
||||
|
||||
.navds-list ol {
|
||||
padding-left: var(--a-spacing-2);
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.navds-list ol li {
|
||||
@@ -67,7 +86,12 @@
|
||||
|
||||
.navds-list ol li::marker {
|
||||
font-weight: var(--a-font-weight-bold);
|
||||
font-size: var(--a-font-size-medium);
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
font-size: var(--a-font-size-large);
|
||||
color: var(--ac-list-marker-ol-color, var(--ac-list-marker-color, var(--a-icon-default)));
|
||||
line-height: var(--a-font-line-height-large);
|
||||
}
|
||||
|
||||
.navds-list--small ol li::marker {
|
||||
font-size: var(--a-font-size-small);
|
||||
line-height: var(--a-font-line-height-medium);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"description": "CSS for NAV Designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -27,7 +27,7 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^4.11.2",
|
||||
"@navikt/ds-tokens": "^4.12.0",
|
||||
"cssnano": "6.0.0",
|
||||
"fast-glob": "3.2.11",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 4.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Combobox: La til støtte for feilmeldinger i Combobox ([#2182](https://github.com/navikt/aksel/pull/2182))
|
||||
|
||||
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
|
||||
|
||||
- Combobox: Kjører nå 'onChange' + 'onClear' når input blir reset programmatisk ([#2183](https://github.com/navikt/aksel/pull/2183))
|
||||
|
||||
- Combobox: Fikset custom-options i singleselect ([#2180](https://github.com/navikt/aksel/pull/2180))
|
||||
|
||||
- Combobox: Fjernet unødvendige 'onClear'-calls når man velger verdier ([#2170](https://github.com/navikt/aksel/pull/2170))
|
||||
|
||||
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
|
||||
|
||||
- Combobox: Lukker nå nedtrekksmeny hvis man legger til ny option i singleselect ([#2177](https://github.com/navikt/aksel/pull/2177))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/aksel-icons@4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"description": "Aksel react-components for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.24.1",
|
||||
"@navikt/aksel-icons": "^4.11.2",
|
||||
"@navikt/aksel-icons": "^4.12.0",
|
||||
"@radix-ui/react-tabs": "1.0.0",
|
||||
"@radix-ui/react-toggle-group": "1.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
|
||||
@@ -102,6 +102,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
|
||||
{
|
||||
"navds-alert--full-width": fullWidth,
|
||||
"navds-alert--inline": inline,
|
||||
"navds-alert--close-button": closeButton,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -185,6 +185,16 @@ export const WithCloseButton: Story = {
|
||||
</BodyLong>
|
||||
<Link href="#">Id elit esse enim reprehenderit</Link>
|
||||
</AlertWithCloseButton>
|
||||
<AlertWithCloseButton>
|
||||
<DsHeading spacing size="small" level="3">
|
||||
Aliquip duis est in commodo pariatur
|
||||
</DsHeading>
|
||||
<BodyLong>
|
||||
Ullamco ullamco laborum et commodo sint culpa cupidatat culpa qui
|
||||
laboris ex. Labore ex occaecat proident qui qui fugiat magna. Fugiat
|
||||
sint commodo consequat eu aute.
|
||||
</BodyLong>
|
||||
</AlertWithCloseButton>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -197,6 +207,6 @@ export const WithCloseButton: Story = {
|
||||
});
|
||||
|
||||
const buttonsAfter = canvas.getAllByTitle("Lukk Alert");
|
||||
expect(buttonsAfter.length).toBe(1);
|
||||
expect(buttonsAfter.length).toBe(2);
|
||||
},
|
||||
};
|
||||
@@ -63,6 +63,11 @@ export interface CopyButtonProps
|
||||
* @default 'Kopiert'
|
||||
*/
|
||||
activeTitle?: string;
|
||||
/**
|
||||
* Icon position in Button
|
||||
* @default "left"
|
||||
*/
|
||||
iconPosition?: "left" | "right";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +96,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
activeDuration = 2000,
|
||||
title = "Kopier",
|
||||
activeTitle = "Kopiert",
|
||||
iconPosition = "left",
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
@@ -119,6 +125,25 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
}, activeDuration);
|
||||
};
|
||||
|
||||
const CopyIcon = () => {
|
||||
return active ? (
|
||||
<span className="navds-copybutton__icon">
|
||||
{activeIcon ?? (
|
||||
<CheckmarkIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : activeTitle}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="navds-copybutton__icon">
|
||||
{icon ?? (
|
||||
<FilesIcon aria-hidden={!!text} title={text ? undefined : title} />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
@@ -138,25 +163,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span className="navds-copybutton__content">
|
||||
{active ? (
|
||||
<span className="navds-copybutton__icon">
|
||||
{activeIcon ?? (
|
||||
<CheckmarkIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : activeTitle}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="navds-copybutton__icon">
|
||||
{icon ?? (
|
||||
<FilesIcon
|
||||
aria-hidden={!!text}
|
||||
title={text ? undefined : title}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{iconPosition === "left" && <CopyIcon />}
|
||||
|
||||
{text &&
|
||||
(active ? (
|
||||
@@ -176,6 +183,7 @@ export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
||||
{text}
|
||||
</Label>
|
||||
))}
|
||||
{iconPosition === "right" && <CopyIcon />}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -60,6 +60,16 @@ export const Variants = {
|
||||
),
|
||||
};
|
||||
|
||||
export const IconPosition = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
<CopyButton copyText="3.14" iconPosition="left" text="Kopier" />
|
||||
|
||||
<CopyButton copyText="3.14" iconPosition="right" text="Kopier" />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const Sizes = {
|
||||
render: () => (
|
||||
<div className="colgap">
|
||||
|
||||
@@ -86,6 +86,23 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={inputProps.id} className="navds-checkbox__label">
|
||||
<span className="navds-checkbox__icon">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="13"
|
||||
height="10"
|
||||
viewBox="0 0 13 10"
|
||||
fill="none"
|
||||
focusable={false}
|
||||
role="img"
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d="M4.03524 6.41478L10.4752 0.404669C11.0792 -0.160351 12.029 -0.130672 12.5955 0.47478C13.162 1.08027 13.1296 2.03007 12.5245 2.59621L5.02111 9.59934C4.74099 9.85904 4.37559 10 4.00025 10C3.60651 10 3.22717 9.84621 2.93914 9.56111L0.439143 7.06111C-0.146381 6.47558 -0.146381 5.52542 0.439143 4.93989C1.02467 4.35437 1.97483 4.35437 2.56036 4.93989L4.03524 6.41478Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
className={cl("navds-checkbox__content", {
|
||||
"navds-sr-only": props.hideLabel,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useMemo, useRef } from "react";
|
||||
import { BodyShort, Label, mergeRefs } from "../..";
|
||||
import { BodyShort, ErrorMessage, Label, mergeRefs } from "../..";
|
||||
import ClearButton from "./ClearButton";
|
||||
import FilteredOptions from "./FilteredOptions/FilteredOptions";
|
||||
import { useFilteredOptionsContext } from "./FilteredOptions/filteredOptionsContext";
|
||||
@@ -39,12 +39,15 @@ export const Combobox = forwardRef<
|
||||
|
||||
const {
|
||||
clearInput,
|
||||
error,
|
||||
errorId,
|
||||
focusInput,
|
||||
hasError,
|
||||
inputDescriptionId,
|
||||
inputProps,
|
||||
inputRef,
|
||||
value,
|
||||
showErrorMsg,
|
||||
size = "medium",
|
||||
} = useInputContext();
|
||||
|
||||
@@ -129,6 +132,14 @@ export const Combobox = forwardRef<
|
||||
</div>
|
||||
<FilteredOptions />
|
||||
</div>
|
||||
<div
|
||||
className="navds-form-field__error"
|
||||
id={errorId}
|
||||
aria-relevant="additions removals"
|
||||
aria-live="polite"
|
||||
>
|
||||
{showErrorMsg && <ErrorMessage size={size}>{error}</ErrorMessage>}
|
||||
</div>
|
||||
</ComboboxWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useInputContext } from "../Input/inputContext";
|
||||
|
||||
const FilteredOptions = () => {
|
||||
const {
|
||||
clearInput,
|
||||
inputProps: { id },
|
||||
size,
|
||||
value,
|
||||
@@ -51,7 +50,8 @@ const FilteredOptions = () => {
|
||||
tabIndex={-1}
|
||||
onPointerUp={(event) => {
|
||||
toggleOption(value, event);
|
||||
clearInput(event);
|
||||
if (!isMultiSelect && !selectedOptions.includes(value))
|
||||
toggleIsListOpen(false);
|
||||
}}
|
||||
id={`${id}-combobox-new-option`}
|
||||
className={cl("navds-combobox__list-item__new-option", {
|
||||
@@ -92,10 +92,8 @@ const FilteredOptions = () => {
|
||||
tabIndex={-1}
|
||||
onPointerUp={(event) => {
|
||||
toggleOption(option, event);
|
||||
clearInput(event);
|
||||
if (!isMultiSelect) {
|
||||
if (!isMultiSelect && !selectedOptions.includes(option))
|
||||
toggleIsListOpen(false);
|
||||
}
|
||||
}}
|
||||
role="option"
|
||||
aria-selected={selectedOptions.includes(option)}
|
||||
|
||||
@@ -8,6 +8,7 @@ import React, {
|
||||
useRef,
|
||||
useLayoutEffect,
|
||||
} from "react";
|
||||
import cl from "clsx";
|
||||
import { useCustomOptionsContext } from "../customOptionsContext";
|
||||
import { useInputContext } from "../Input/inputContext";
|
||||
import usePrevious from "../../../util/usePrevious";
|
||||
@@ -58,7 +59,7 @@ export const FilteredOptionsProvider = ({ children, value: props }) => {
|
||||
} = props;
|
||||
const filteredOptionsRef = useRef<HTMLUListElement | null>(null);
|
||||
const {
|
||||
inputProps: { id },
|
||||
inputProps: { "aria-describedby": partialAriaDescribedBy, id },
|
||||
value,
|
||||
searchTerm,
|
||||
setValue,
|
||||
@@ -124,18 +125,26 @@ export const FilteredOptionsProvider = ({ children, value: props }) => {
|
||||
}, [allowNewValues, isValueNew]);
|
||||
|
||||
const ariaDescribedBy = useMemo(() => {
|
||||
let activeOption;
|
||||
if (!isLoading && filteredOptions.length === 0) {
|
||||
return `${id}-no-hits`;
|
||||
activeOption = `${id}-no-hits`;
|
||||
} else if ((value && value !== "") || isLoading) {
|
||||
if (shouldAutocomplete && filteredOptions[0]) {
|
||||
return `${id}-option-${filteredOptions[0].replace(" ", "-")}`;
|
||||
} else if (isLoading) {
|
||||
return `${id}-is-loading`;
|
||||
activeOption = `${id}-option-${filteredOptions[0].replace(" ", "-")}`;
|
||||
} else if (isListOpen && isLoading) {
|
||||
activeOption = `${id}-is-loading`;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}, [isLoading, value, shouldAutocomplete, filteredOptions, id]);
|
||||
return cl(activeOption, partialAriaDescribedBy) || undefined;
|
||||
}, [
|
||||
isListOpen,
|
||||
isLoading,
|
||||
value,
|
||||
partialAriaDescribedBy,
|
||||
shouldAutocomplete,
|
||||
filteredOptions,
|
||||
id,
|
||||
]);
|
||||
|
||||
const currentOption = useMemo(() => {
|
||||
if (filteredOptionsIndex == null) {
|
||||
|
||||
@@ -22,8 +22,12 @@ interface InputProps
|
||||
const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
({ inputClassName, error, errorId, ...rest }, ref) => {
|
||||
const { clearInput, inputProps, onChange, size, value } = useInputContext();
|
||||
const { selectedOptions, removeSelectedOption, toggleOption } =
|
||||
useSelectedOptionsContext();
|
||||
const {
|
||||
selectedOptions,
|
||||
removeSelectedOption,
|
||||
toggleOption,
|
||||
isMultiSelect,
|
||||
} = useSelectedOptionsContext();
|
||||
const {
|
||||
activeDecendantId,
|
||||
allowNewValues,
|
||||
@@ -47,7 +51,8 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
event.preventDefault();
|
||||
// Selecting a value from the dropdown / FilteredOptions
|
||||
toggleOption(currentOption, event);
|
||||
clearInput(event);
|
||||
if (!isMultiSelect && !selectedOptions.includes(currentOption))
|
||||
toggleIsListOpen(false);
|
||||
} else if (shouldAutocomplete && selectedOptions.includes(value)) {
|
||||
event.preventDefault();
|
||||
// Trying to set the same value that is already set, so just clearing the input
|
||||
@@ -56,15 +61,18 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
event.preventDefault();
|
||||
// Autocompleting or adding a new value
|
||||
toggleOption(value, event);
|
||||
clearInput(event);
|
||||
if (!isMultiSelect && !selectedOptions.includes(value))
|
||||
toggleIsListOpen(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
allowNewValues,
|
||||
clearInput,
|
||||
currentOption,
|
||||
isMultiSelect,
|
||||
selectedOptions,
|
||||
shouldAutocomplete,
|
||||
toggleIsListOpen,
|
||||
toggleOption,
|
||||
value,
|
||||
]
|
||||
@@ -162,6 +170,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
aria-autocomplete={shouldAutocomplete ? "both" : "list"}
|
||||
aria-activedescendant={activeDecendantId}
|
||||
aria-describedby={ariaDescribedBy}
|
||||
aria-invalid={inputProps["aria-invalid"]}
|
||||
className={cl(
|
||||
inputClassName,
|
||||
"navds-combobox__input",
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useFormField, FormFieldType } from "../../useFormField";
|
||||
|
||||
interface InputContextType extends FormFieldType {
|
||||
clearInput: (event: React.PointerEvent | React.KeyboardEvent) => void;
|
||||
error?: string;
|
||||
focusInput: () => void;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
value: string;
|
||||
@@ -80,10 +81,11 @@ export const InputContextProvider = ({ children, value: props }) => {
|
||||
const clearInput = useCallback(
|
||||
(event: React.PointerEvent | React.KeyboardEvent) => {
|
||||
onClear?.(event);
|
||||
externalOnChange?.(null, "");
|
||||
setValue("");
|
||||
setSearchTerm("");
|
||||
},
|
||||
[onClear, setSearchTerm, setValue]
|
||||
[externalOnChange, onClear, setValue]
|
||||
);
|
||||
|
||||
const focusInput = useCallback(() => {
|
||||
@@ -101,6 +103,7 @@ export const InputContextProvider = ({ children, value: props }) => {
|
||||
value={{
|
||||
...formFieldProps,
|
||||
clearInput,
|
||||
error,
|
||||
focusInput,
|
||||
inputRef,
|
||||
value,
|
||||
|
||||
@@ -42,8 +42,12 @@ export const SelectedOptionsProvider = ({
|
||||
>;
|
||||
}) => {
|
||||
const { clearInput, focusInput } = useInputContext();
|
||||
const { customOptions, removeCustomOption, addCustomOption } =
|
||||
useCustomOptionsContext();
|
||||
const {
|
||||
customOptions,
|
||||
removeCustomOption,
|
||||
addCustomOption,
|
||||
setCustomOptions,
|
||||
} = useCustomOptionsContext();
|
||||
const {
|
||||
allowNewValues,
|
||||
isMultiSelect,
|
||||
@@ -60,11 +64,12 @@ export const SelectedOptionsProvider = ({
|
||||
|
||||
const addSelectedOption = useCallback(
|
||||
(option: string) => {
|
||||
const isAddedByUser = !options
|
||||
const isCustomOption = !options
|
||||
.map((opt) => opt.toLowerCase())
|
||||
.includes(option?.toLowerCase?.());
|
||||
if (isAddedByUser) {
|
||||
if (isCustomOption) {
|
||||
allowNewValues && addCustomOption(option);
|
||||
!isMultiSelect && setSelectedOptions([]);
|
||||
} else if (isMultiSelect) {
|
||||
setSelectedOptions((prevSelectedOptions) => [
|
||||
...prevSelectedOptions,
|
||||
@@ -72,16 +77,24 @@ export const SelectedOptionsProvider = ({
|
||||
]);
|
||||
} else {
|
||||
setSelectedOptions([option]);
|
||||
setCustomOptions([]);
|
||||
}
|
||||
onToggleSelected?.(option, true, isAddedByUser);
|
||||
onToggleSelected?.(option, true, isCustomOption);
|
||||
},
|
||||
[addCustomOption, allowNewValues, isMultiSelect, onToggleSelected, options]
|
||||
[
|
||||
addCustomOption,
|
||||
allowNewValues,
|
||||
isMultiSelect,
|
||||
onToggleSelected,
|
||||
options,
|
||||
setCustomOptions,
|
||||
]
|
||||
);
|
||||
|
||||
const removeSelectedOption = useCallback(
|
||||
(option: string) => {
|
||||
const isAddedByUser = customOptions.includes(option);
|
||||
if (isAddedByUser) {
|
||||
const isCustomOption = customOptions.includes(option);
|
||||
if (isCustomOption) {
|
||||
removeCustomOption(option);
|
||||
} else {
|
||||
setSelectedOptions((prevSelectedOptions) =>
|
||||
@@ -90,7 +103,7 @@ export const SelectedOptionsProvider = ({
|
||||
)
|
||||
);
|
||||
}
|
||||
onToggleSelected?.(option, false, isAddedByUser);
|
||||
onToggleSelected?.(option, false, isCustomOption);
|
||||
},
|
||||
[customOptions, onToggleSelected, removeCustomOption]
|
||||
);
|
||||
@@ -102,16 +115,13 @@ export const SelectedOptionsProvider = ({
|
||||
} else {
|
||||
addSelectedOption(option);
|
||||
}
|
||||
if (!isMultiSelect) {
|
||||
clearInput(event);
|
||||
}
|
||||
clearInput(event);
|
||||
focusInput();
|
||||
},
|
||||
[
|
||||
addSelectedOption,
|
||||
clearInput,
|
||||
focusInput,
|
||||
isMultiSelect,
|
||||
removeSelectedOption,
|
||||
selectedOptions,
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Meta } from "@storybook/react";
|
||||
import React, { useState, useId, useMemo } from "react";
|
||||
import { userEvent, within } from "@storybook/testing-library";
|
||||
import { Chips, UNSAFE_Combobox, TextField } from "../../index";
|
||||
import { expect } from "@storybook/jest";
|
||||
import { expect, jest } from "@storybook/jest";
|
||||
|
||||
export default {
|
||||
title: "ds-react/Combobox",
|
||||
@@ -94,6 +94,27 @@ MultiSelect.args = {
|
||||
size: "medium",
|
||||
};
|
||||
|
||||
export function WithAddNewOptions(props) {
|
||||
const id = useId();
|
||||
return (
|
||||
<DemoContainer dataTheme={props.darkMode}>
|
||||
<UNSAFE_Combobox
|
||||
id={id}
|
||||
label="Komboboks med mulighet for å legge til nye verdier"
|
||||
options={props.options}
|
||||
allowNewValues={props.allowNewValues}
|
||||
shouldAutocomplete={props.shouldAutoComplete}
|
||||
/>
|
||||
</DemoContainer>
|
||||
);
|
||||
}
|
||||
|
||||
WithAddNewOptions.args = {
|
||||
options,
|
||||
allowNewValues: true,
|
||||
shouldAutoComplete: true,
|
||||
};
|
||||
|
||||
export function MultiSelectWithAddNewOptions(props) {
|
||||
const id = useId();
|
||||
return (
|
||||
@@ -101,7 +122,7 @@ export function MultiSelectWithAddNewOptions(props) {
|
||||
<UNSAFE_Combobox
|
||||
id={id}
|
||||
isMultiSelect={props.isMultiSelect}
|
||||
label="Komboboks (med mulighet for å legge til nye verdier)"
|
||||
label="Multiselect komboboks med mulighet for å legge til nye verdier"
|
||||
options={props.options}
|
||||
allowNewValues={props.allowNewValues}
|
||||
/>
|
||||
@@ -295,6 +316,33 @@ ComboboxSizes.args = {
|
||||
options,
|
||||
};
|
||||
|
||||
export const WithError = {
|
||||
args: {
|
||||
error: "Du må velge en favorittfrukt.",
|
||||
isLoading: true,
|
||||
},
|
||||
render: (props) => {
|
||||
const [hasSelectedValue, setHasSelectedValue] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
return (
|
||||
<DemoContainer dataTheme={props.darkMode}>
|
||||
<UNSAFE_Combobox
|
||||
filteredOptions={isLoading ? [] : undefined}
|
||||
options={options}
|
||||
label="Hva er dine favorittfrukter?"
|
||||
error={!hasSelectedValue && props.error}
|
||||
isLoading={isLoading}
|
||||
onChange={() => {
|
||||
setIsLoading(true);
|
||||
setTimeout(() => setIsLoading(false), 2000);
|
||||
}}
|
||||
onToggleSelected={(_, isSelected) => setHasSelectedValue(isSelected)}
|
||||
/>
|
||||
</DemoContainer>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -324,6 +372,7 @@ export const CancelInputTest = {
|
||||
userEvent.keyboard("{Escape}");
|
||||
await sleep(1000);
|
||||
userEvent.keyboard("{ArrowDown}");
|
||||
await sleep(500);
|
||||
const banana = canvas.getByText("banana");
|
||||
userEvent.click(banana);
|
||||
},
|
||||
@@ -421,3 +470,42 @@ export const AddWhenAddNewDisabledTest = {
|
||||
expect(invalidSelect).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const TestThatCallbacksOnlyFireWhenExpected = {
|
||||
args: {
|
||||
onChange: jest.fn(),
|
||||
onClear: jest.fn(),
|
||||
onToggleSelected: jest.fn(),
|
||||
},
|
||||
render: (props) => {
|
||||
return (
|
||||
<DemoContainer dataTheme={props.darkMode}>
|
||||
<UNSAFE_Combobox
|
||||
options={options}
|
||||
label="Hva er dine favorittfrukter?"
|
||||
{...props}
|
||||
/>
|
||||
</DemoContainer>
|
||||
);
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
args.onToggleSelected.mockClear();
|
||||
args.onClear.mockClear();
|
||||
args.onChange.mockClear();
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const input = canvas.getByLabelText("Hva er dine favorittfrukter?");
|
||||
const searchWord = "tangerine";
|
||||
|
||||
userEvent.click(input);
|
||||
await userEvent.type(input, searchWord, { delay: 200 });
|
||||
await sleep(250);
|
||||
userEvent.keyboard("{ArrowDown}");
|
||||
await sleep(250);
|
||||
userEvent.keyboard("{Enter}");
|
||||
await sleep(250);
|
||||
expect(args.onClear.mock.calls).toHaveLength(1);
|
||||
expect(args.onToggleSelected.mock.calls).toHaveLength(1);
|
||||
expect(args.onChange.mock.calls).toHaveLength(searchWord.length + 1);
|
||||
},
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useState, useCallback, createContext, useContext } from "react";
|
||||
import { useInputContext } from "./Input/inputContext";
|
||||
import { useSelectedOptionsContext } from "./SelectedOptions/selectedOptionsContext";
|
||||
|
||||
type CustomOptionsContextType = {
|
||||
customOptions: string[];
|
||||
removeCustomOption: (option: string) => void;
|
||||
addCustomOption: (option: string) => void;
|
||||
setCustomOptions: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
};
|
||||
|
||||
const CustomOptionsContext = createContext<CustomOptionsContextType>(
|
||||
@@ -14,6 +16,7 @@ const CustomOptionsContext = createContext<CustomOptionsContextType>(
|
||||
export const CustomOptionsProvider = ({ children }) => {
|
||||
const [customOptions, setCustomOptions] = useState<string[]>([]);
|
||||
const { focusInput } = useInputContext();
|
||||
const { isMultiSelect } = useSelectedOptionsContext();
|
||||
|
||||
const removeCustomOption = useCallback(
|
||||
(option) => {
|
||||
@@ -27,16 +30,21 @@ export const CustomOptionsProvider = ({ children }) => {
|
||||
|
||||
const addCustomOption = useCallback(
|
||||
(option) => {
|
||||
setCustomOptions((prevOptions) => [...prevOptions, option]);
|
||||
if (isMultiSelect) {
|
||||
setCustomOptions((prevOptions) => [...prevOptions, option]);
|
||||
} else {
|
||||
setCustomOptions([option]);
|
||||
}
|
||||
focusInput();
|
||||
},
|
||||
[focusInput, setCustomOptions]
|
||||
[focusInput, isMultiSelect, setCustomOptions]
|
||||
);
|
||||
|
||||
const customOptionsState = {
|
||||
customOptions,
|
||||
removeCustomOption,
|
||||
addCustomOption,
|
||||
setCustomOptions,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -66,7 +66,10 @@ export interface ComboboxProps
|
||||
* @param event
|
||||
* @returns
|
||||
*/
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onChange?: (
|
||||
event: ChangeEvent<HTMLInputElement> | null,
|
||||
value?: string
|
||||
) => void;
|
||||
/**
|
||||
* Callback function triggered whenever the input field is cleared
|
||||
*
|
||||
@@ -79,13 +82,13 @@ export interface ComboboxProps
|
||||
*
|
||||
* @param option
|
||||
* @param isSelected - Whether the option has been selected or unselected
|
||||
* @param isAddedByUser - Whether the option comes from user input, instead of from the list
|
||||
* @param isCustomOption - Whether the option comes from user input, instead of from the list
|
||||
* @returns
|
||||
*/
|
||||
onToggleSelected?: (
|
||||
option: string,
|
||||
isSelected: boolean,
|
||||
isAddedByUser: boolean
|
||||
isCustomOption: boolean
|
||||
) => void;
|
||||
/**
|
||||
* List of selected options.
|
||||
|
||||
@@ -24,6 +24,11 @@ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
* @default "h3"
|
||||
*/
|
||||
headingTag?: React.ElementType<any>;
|
||||
/**
|
||||
* Changes padding, height and font-size
|
||||
* @default medium
|
||||
*/
|
||||
size?: "medium" | "small";
|
||||
}
|
||||
|
||||
export interface ListComponent
|
||||
@@ -37,13 +42,15 @@ export interface ListComponent
|
||||
}
|
||||
|
||||
interface ListContextProps {
|
||||
listType: "ul" | "ol";
|
||||
listType: ListProps["as"];
|
||||
isNested: boolean | null;
|
||||
size: ListProps["size"];
|
||||
}
|
||||
|
||||
export const ListContext = createContext<ListContextProps>({
|
||||
listType: "ul",
|
||||
isNested: null,
|
||||
size: "medium",
|
||||
});
|
||||
|
||||
export const List = forwardRef<HTMLDivElement, ListProps>(
|
||||
@@ -55,35 +62,44 @@ export const List = forwardRef<HTMLDivElement, ListProps>(
|
||||
title,
|
||||
description,
|
||||
headingTag = "h3",
|
||||
size,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const ariaId = useId();
|
||||
|
||||
const { isNested } = useContext(ListContext);
|
||||
const { isNested, size: _size } = useContext(ListContext);
|
||||
|
||||
const listSize = size ?? _size ?? "medium";
|
||||
return (
|
||||
<ListContext.Provider
|
||||
value={{
|
||||
listType: ListTag,
|
||||
isNested: isNested === null ? false : true,
|
||||
size: listSize,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("navds-list", className, {
|
||||
className={cl("navds-list", `navds-list--${listSize}`, className, {
|
||||
"navds-list--nested": isNested === null ? false : true,
|
||||
})}
|
||||
>
|
||||
{title && (
|
||||
<Heading id={`tittel-${ariaId}`} size="small" as={headingTag}>
|
||||
<Heading
|
||||
id={`tittel-${ariaId}`}
|
||||
size={listSize === "medium" ? "small" : "xsmall"}
|
||||
as={headingTag}
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
)}
|
||||
{description && (
|
||||
<BodyShort id={`description-${ariaId}`}>{description}</BodyShort>
|
||||
<BodyShort size={listSize} id={`description-${ariaId}`}>
|
||||
{description}
|
||||
</BodyShort>
|
||||
)}
|
||||
<ListTag
|
||||
aria-labelledby={title && `tittel-${ariaId}`}
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface ListItemProps extends React.HTMLAttributes<HTMLLIElement> {
|
||||
|
||||
export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
|
||||
({ className, children, title, icon, ...rest }, ref) => {
|
||||
const { listType, isNested } = useContext(ListContext);
|
||||
const { listType, size } = useContext(ListContext);
|
||||
|
||||
if (listType === "ol" && icon) {
|
||||
console.warn(
|
||||
@@ -29,13 +29,7 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
{...rest}
|
||||
ref={ref}
|
||||
className={cl("navds-list__item", className, {
|
||||
"navds-list__item--noMargin": isNested,
|
||||
})}
|
||||
>
|
||||
<li {...rest} ref={ref} className={cl("navds-list__item", className)}>
|
||||
{listType === "ul" && (
|
||||
<div
|
||||
className={cl({
|
||||
@@ -62,9 +56,9 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
|
||||
</div>
|
||||
)}
|
||||
|
||||
<BodyShort as="div" size="small" className="navds-list__item-content">
|
||||
<BodyShort as="div" size={size} className="navds-list__item-content">
|
||||
{title && (
|
||||
<Label as="p" size="small">
|
||||
<Label as="p" size={size}>
|
||||
{title}
|
||||
</Label>
|
||||
)}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "@navikt/aksel-icons";
|
||||
import { Meta } from "@storybook/react";
|
||||
import React from "react";
|
||||
import { List } from "..";
|
||||
import { List, VStack } from "..";
|
||||
|
||||
export default {
|
||||
title: "ds-react/List",
|
||||
@@ -91,6 +91,137 @@ export const WithHeading = {
|
||||
},
|
||||
};
|
||||
|
||||
export const SizesUl = {
|
||||
render: () => {
|
||||
return (
|
||||
<VStack gap="8">
|
||||
<List
|
||||
title="Medium list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="medium"
|
||||
>
|
||||
<List.Item title="Lorem Ipsum Dolor Sit Amet">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item title="Consectetur Adipiscing Elit">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
<List
|
||||
title="Small list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="small"
|
||||
>
|
||||
<List.Item title="Lorem Ipsum Dolor Sit Amet">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item title="Consectetur Adipiscing Elit">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
</VStack>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const SizesOl = {
|
||||
render: () => {
|
||||
return (
|
||||
<VStack gap="8">
|
||||
<List
|
||||
title="Medium list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="medium"
|
||||
as="ol"
|
||||
>
|
||||
<List.Item title="Lorem Ipsum Dolor Sit Amet">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item title="Consectetur Adipiscing Elit">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
<List
|
||||
title="Small list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="small"
|
||||
as="ol"
|
||||
>
|
||||
<List.Item title="Lorem Ipsum Dolor Sit Amet">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item title="Consectetur Adipiscing Elit">
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
</VStack>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const SizesIcons = {
|
||||
render: () => {
|
||||
return (
|
||||
<VStack gap="8">
|
||||
<List
|
||||
title="Medium list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="medium"
|
||||
>
|
||||
<List.Item
|
||||
icon={<HeadHeartIcon aria-hidden />}
|
||||
title="Lorem Ipsum Dolor Sit Amet"
|
||||
>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item
|
||||
icon={<HeadHeartIcon aria-hidden />}
|
||||
title="Consectetur Adipiscing Elit"
|
||||
>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item icon={<HeadHeartIcon aria-hidden />}>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
<List
|
||||
title="Small list"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
|
||||
size="small"
|
||||
>
|
||||
<List.Item
|
||||
icon={<HeadHeartIcon aria-hidden />}
|
||||
title="Lorem Ipsum Dolor Sit Amet"
|
||||
>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item
|
||||
icon={<HeadHeartIcon aria-hidden />}
|
||||
title="Consectetur Adipiscing Elit"
|
||||
>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
<List.Item icon={<HeadHeartIcon aria-hidden />}>
|
||||
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
|
||||
</List.Item>
|
||||
</List>
|
||||
</VStack>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Icons = {
|
||||
render: () => {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
## 4.11.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"description": "Tailwind config based on @navikt/ds-tokens",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
@@ -22,7 +22,7 @@
|
||||
"directory": "@navikt/core/tailwind"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^4.11.2",
|
||||
"@navikt/ds-tokens": "^4.12.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"color": "4.2.3",
|
||||
"jest": "^29.0.0",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 4.12.0
|
||||
|
||||
## 4.11.2
|
||||
|
||||
## 4.11.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "4.11.2",
|
||||
"version": "4.12.0",
|
||||
"description": "Design-tokens for NAV designsystem",
|
||||
"author": "Aksel | NAV designsystem team",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 4.12.0
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
|
||||
|
||||
<!---->
|
||||
|
||||
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
|
||||
|
||||
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
|
||||
|
||||
- Alert: Fikset alignment av status-ikon mot tekst ([#2179](https://github.com/navikt/aksel/pull/2179))
|
||||
|
||||
### @navikt/ds-react
|
||||
|
||||
- Combobox: La til støtte for feilmeldinger i Combobox ([#2182](https://github.com/navikt/aksel/pull/2182))
|
||||
|
||||
- CopyButton: Har nå prop 'iconPosition' for å høyre/venstre aligne ikon ([#2173](https://github.com/navikt/aksel/pull/2173))
|
||||
|
||||
<!---->
|
||||
|
||||
- List: Fikset sentrering, margins ([#2168](https://github.com/navikt/aksel/pull/2168))
|
||||
|
||||
- Combobox: Kjører nå 'onChange' + 'onClear' når input blir reset programmatisk ([#2183](https://github.com/navikt/aksel/pull/2183))
|
||||
|
||||
- Combobox: Fikset custom-options i singleselect ([#2180](https://github.com/navikt/aksel/pull/2180))
|
||||
|
||||
- Combobox: Fjernet unødvendige 'onClear'-calls når man velger verdier ([#2170](https://github.com/navikt/aksel/pull/2170))
|
||||
|
||||
- Checkbox: Checkmark er nå SVG-ikon og ikke Base64 ([#2171](https://github.com/navikt/aksel/pull/2171))
|
||||
|
||||
- Combobox: Lukker nå nedtrekksmeny hvis man legger til ny option i singleselect ([#2177](https://github.com/navikt/aksel/pull/2177))
|
||||
|
||||
## 4.11.2
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
"website"
|
||||
],
|
||||
"dependencies": {
|
||||
"@navikt/aksel-icons": "^4.11.2",
|
||||
"@navikt/ds-css": "^4.11.2",
|
||||
"@navikt/ds-react": "^4.11.2",
|
||||
"@navikt/ds-tailwind": "^4.11.2",
|
||||
"@navikt/ds-tokens": "^4.11.2"
|
||||
"@navikt/aksel-icons": "^4.12.0",
|
||||
"@navikt/ds-css": "^4.12.0",
|
||||
"@navikt/ds-react": "^4.12.0",
|
||||
"@navikt/ds-tailwind": "^4.12.0",
|
||||
"@navikt/ds-tokens": "^4.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier-plugin-tailwindcss": "^0.2.3"
|
||||
|
||||
@@ -44,11 +44,11 @@ const GetIcon = (s: string) => {
|
||||
const getText = (s: string) => {
|
||||
switch (s) {
|
||||
case "do":
|
||||
return "Do: ";
|
||||
return "Gjør: ";
|
||||
case "dont":
|
||||
return "Don't: ";
|
||||
return "Unngå: ";
|
||||
case "warning":
|
||||
return "Fare på ferde: ";
|
||||
return "Utfordrende: ";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -74,7 +74,7 @@ const Element = ({ block }: { block: DoDontT["blokker"][number] }) => {
|
||||
)}
|
||||
aria-hidden
|
||||
>
|
||||
<span className="absolute right-[10px] z-10 translate-y-2">
|
||||
<span className="absolute right-[2px] z-10">
|
||||
{GetIcon(block.variant)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -36,14 +36,14 @@ export const Example = () => {
|
||||
const onToggleSelected = (
|
||||
option: string,
|
||||
isSelected: boolean,
|
||||
isAddedByUser: boolean
|
||||
isCustomOption: boolean
|
||||
) => {
|
||||
if (isSelected) {
|
||||
setSelectedOptions([...selectedOptions, option]);
|
||||
} else {
|
||||
setSelectedOptions(selectedOptions.filter((o) => o !== option));
|
||||
}
|
||||
if (isAddedByUser) {
|
||||
if (isCustomOption) {
|
||||
mockPersistUserAddedValues(option, isSelected);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { UNSAFE_Combobox } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
|
||||
const initialOptions = [
|
||||
"banana",
|
||||
"apple",
|
||||
"tangerine",
|
||||
"pear",
|
||||
"grape",
|
||||
"kiwi",
|
||||
"mango",
|
||||
"passion fruit",
|
||||
"pineapple",
|
||||
"strawberry",
|
||||
"watermelon",
|
||||
"grape fruit",
|
||||
];
|
||||
|
||||
export const Example = () => {
|
||||
return (
|
||||
<div>
|
||||
<UNSAFE_Combobox
|
||||
label="Hva er din favorittfrukt?"
|
||||
options={initialOptions}
|
||||
error="Du må velge en favorittfrukt."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withDsExample(Example, "static");
|
||||
|
||||
export const args = {
|
||||
index: 0,
|
||||
desc: "Ved Single Select velger brukeren kun ett valg fra nedtrekkslisten.",
|
||||
};
|
||||
@@ -41,6 +41,6 @@ export const Demo = {
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 2,
|
||||
index: 3,
|
||||
desc: "Husk å sette aria-hidden på ikonet.",
|
||||
};
|
||||
@@ -25,6 +25,6 @@ export const Demo = {
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 1,
|
||||
desc: "Husk å sette riktig nivå/tag på List elementet hvis du bruker en tittel.",
|
||||
index: 2,
|
||||
desc: "Husk å sette riktig heading-nivå/tag hvis du bruker en title.",
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { List } from "@navikt/ds-react";
|
||||
import { withDsExample } from "components/website-modules/examples/withDsExample";
|
||||
|
||||
const Example = () => {
|
||||
return (
|
||||
<List as="ul" size="small" title="Det kan også være aktuelt hvis du:">
|
||||
<List.Item>
|
||||
står i fare for å miste jobben etter å ha vært sykmeldt helt eller
|
||||
delvis i 12 måneder, eller
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
har full eller gradert uføretrygd, men ønsker å jobbe. NAV må ha vurdert
|
||||
om andre arbeidsmarkedstiltak og virkemidler er aktuelle.
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
er en arbeidssøker med varig og vesentlig nedsatt arbeidsevne som kan
|
||||
bli ansatt i en vanlig jobb.
|
||||
</List.Item>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
export default withDsExample(Example);
|
||||
|
||||
/* Storybook story */
|
||||
export const Demo = {
|
||||
render: Example,
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 1,
|
||||
};
|
||||
@@ -32,5 +32,5 @@ export const Demo = {
|
||||
};
|
||||
|
||||
export const args = {
|
||||
index: 3,
|
||||
index: 4,
|
||||
};
|
||||
@@ -10,8 +10,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "4.11.2",
|
||||
"@navikt/ds-react": "4.11.2",
|
||||
"@navikt/ds-css": "4.12.0",
|
||||
"@navikt/ds-react": "4.12.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
|
||||
+12
-12
@@ -211,22 +211,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "2.26.2",
|
||||
"@storybook/addon-a11y": "7.2.3",
|
||||
"@storybook/addon-actions": "7.2.3",
|
||||
"@storybook/addon-essentials": "7.2.3",
|
||||
"@storybook/addon-interactions": "7.2.3",
|
||||
"@storybook/addon-storysource": "7.2.3",
|
||||
"@storybook/addon-a11y": "7.3.1",
|
||||
"@storybook/addon-actions": "7.3.1",
|
||||
"@storybook/addon-essentials": "7.3.1",
|
||||
"@storybook/addon-interactions": "7.3.1",
|
||||
"@storybook/addon-storysource": "7.3.1",
|
||||
"@storybook/addons": "7.2.3",
|
||||
"@storybook/csf-tools": "7.2.3",
|
||||
"@storybook/jest": "^0.1.0",
|
||||
"@storybook/react": "7.2.3",
|
||||
"@storybook/react-vite": "7.2.3",
|
||||
"@storybook/csf-tools": "7.3.1",
|
||||
"@storybook/jest": "^0.2.0",
|
||||
"@storybook/react": "7.3.1",
|
||||
"@storybook/react-vite": "7.3.1",
|
||||
"@storybook/testing-library": "^0.2.0",
|
||||
"@storybook/theming": "7.2.3",
|
||||
"@storybook/theming": "7.3.1",
|
||||
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
|
||||
"@typescript-eslint/parser": "^5.41.0",
|
||||
"@whitespace/storybook-addon-html": "^5.1.6",
|
||||
"chromatic": "6.21.0",
|
||||
"chromatic": "6.22.0",
|
||||
"concurrently": "7.2.1",
|
||||
"cross-env": "^7.0.0",
|
||||
"eslint": "^8.16.0",
|
||||
@@ -244,7 +244,7 @@
|
||||
"react": "^18.0.0",
|
||||
"react-docgen-typescript": "2.2.2",
|
||||
"react-dom": "^18.0.0",
|
||||
"storybook": "7.2.3",
|
||||
"storybook": "7.3.1",
|
||||
"stylelint": "^14.8.5",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
|
||||
Reference in new issue
Block a user