Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] 9bbc3e6813 Version Packages (#2835)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-09 15:25:38 +02:00
Ken 5250b47ca4 💄 Border-radius på FileUpload.Item er nå rund (#2834) 2024-04-09 15:20:40 +02:00
Ken fc7709ae2a 🐛 Søk filtrerte ut beste treff fra 'nyeste artikler' 2024-04-09 09:40:20 +02:00
github-actions[bot]andgithub-actions[bot] 2364e689ed Version Packages (#2831)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-09 09:12:06 +02:00
Halvor Haugan d57c0b2c2c Justering av eksempler på Checkbox (#2833) 2024-04-09 09:06:58 +02:00
Ken 868bb543a5 🐛 Preview brakk for god-praksis
Siden 'userState' ikke hentes fra Sanity, blir den undefined ved lasting av previewdata
2024-04-09 07:33:16 +02:00
Torkjel SkjetneandHalvor Haugan 8f9ed7a404 FileUpload.Item: 💥 Endret API og støtte for å sette ID på knappen (#2824)
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-04-08 09:16:53 +02:00
Halvor HauganandKen 5ea6255f6d Justeringer av eksempler på Radio og Checkbox med mer (#2830)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-04-04 20:54:12 +02:00
github-actions[bot]andgithub-actions[bot] 491fccbe80 Version Packages (#2828)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-03 11:56:35 +02:00
Ken cca2a8263f 💄 Fjernet Illustrasjon fra bloggsider (#2827) 2024-04-03 11:50:12 +02:00
Ken 3140ec2765 🔥 Fjernet role=gridcell fra datoknapper (#2826)
Siden knappene allerede er i en table og da også td er det unødvendig med gricell
2024-04-03 11:37:05 +02:00
Halvor Haugan d8fce83ae3 Oppgrader til Storybook 8 (#2825) 2024-04-03 10:03:44 +02:00
github-actions[bot]andgithub-actions[bot] f88b00daf6 Version Packages (#2821)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-03 09:45:19 +02:00
Ken 6d0baea5c1 👽 Oppdatert om Aksel lenke i footer
Side er flyttet fra god-praksis til standalone-side url
Er opprettet automatisk redirect slik at eldre lenker ikke brekker
2024-04-03 09:25:54 +02:00
Ken 880e77b9c2 ♻️ Fjernet max-krav for antall tegn i Relaterte lenker-module
Siden lenketeksten ofte refererer til en sidetittel skaper det utfordringer da de ofte er litt lengre
2024-04-03 09:03:57 +02:00
Ken 6d16373f85 👽 Oppdatert skriv for aksel lenke i footer
Side er flyttet fra god-praksis til standalone-side url
Er opprettet automatisk redirect slik at eldre lenker ikke brekker
2024-04-03 08:42:08 +02:00
Halvor Haugan ae078254dc Modal: Fjern tomt element (#2819) 2024-04-02 10:20:00 +02:00
github-actions[bot]andgithub-actions[bot] 638e8260b4 Version Packages (#2811)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-02 08:45:10 +02:00
Halvor Haugan d991011690 FileUpload.Dropzone: id prop settes på button (#2817) 2024-03-22 11:03:05 +01:00
KenandHalvor Haugan b6086f7b1f 📝 Oppdatert radio-error dokumentasjon (#2800)
* 📝 Oppdatert radio-error dokumentasjon

* Update aksel.nav.no/website/pages/eksempler/radio/error.tsx

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

* Update aksel.nav.no/website/pages/eksempler/radio/error.tsx

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

* Update aksel.nav.no/website/pages/eksempler/radio/error.tsx

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

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-03-22 10:42:38 +01:00
Ken 05462081aa useControllableState i Expansioncard og Switch state-bug (#2810)
* ♻️ Bruker controllableState i ExpansionCard

* 🐛 Fikset tilfelle der checked switch var visuelt unchecked

* 📝 Changeset

* 🔥 Fjernet utkommentert kode
2024-03-22 10:24:35 +01:00
Ken 63e42de3ee 🆙 Oppdatert Playroom (#2815) 2024-03-22 09:31:00 +01:00
Halvor Haugan 2e85178166 yarn.lock sync 2024-03-21 16:36:11 +01:00
Ken 456d4e2e07 🐛 Slack var ikke i outbound accessPolicy for feedback 2024-03-21 11:52:48 +01:00
Ken b5a0cb8cdf Oppdatert logging for Aksel.nav.no + bumpet feedback pagination page-size (#2812)
* 🆙 Oppgradert logging

* 🎨 Bedre loggeoutput

* 🎨 Bruker childlogger

* 🧪 Custome error-logg

* ⚡ Større paginering-page for slackusers

* 📝 error -> info
2024-03-21 11:36:43 +01:00
Ken a2af9bcc37 Lagt til mer padding-right på ReadMore (#2806)
* 💄 Oppdatert padding-right på readmore

* 📝 Changeset

* 🐛 Manglet chromatic-disable på readmore story
2024-03-21 10:16:14 +01:00
bd45754adb [aksel.nav.no] Slackbot for feedback (#2785)
* 🎉 Satt opp boilerplate for auth på Aksel

* 🎉 Auth boilerplate

* 🎨 Demo-setup for å teste auth i dev

* 🚚 Flyttet auth til getserversideprops

* 🔥 Fjernet authprovider fra _app

* 🐛 revalidate er ikke valid key for getserversideprops i nextjs

* 🚚 rename auth-dir

* 🔥 Fjernet auth types

* 🎉 Auth for api

* 🎨 Oppdatert workflows med token

* 🔥 Fjernet testing av auth på god-praksis sider

* 🎨 La til sidecar på prod-deploy

* 🐛 only return boolean in Promise<boolean>

* ✨ feedback form

* 🎨 showcase all states of new FeedBackForm

* 🔥 remove prop name (will use auth api)

* 🎨 use getAuthUser()

* 🎨 use useAuth() for login/logout

* 🎉 Slack feedback-api

* 🐛 handle case where token is null

* 🎨 scroll to anchor on login redirect

* ⚡ Utility-funksjoner for slack-api

* 🧪 Tests for slack-utilities

* 🎨 Ryddet opp i henting av slack memebers

* 🚚 Flyttet caching inn i funkson

* 📱 make FeedbackForm responsive

* 🎉 Strengere typet slack-utilities

* 🎨 Fikset typefeil i feedback-api

* ⬆️ Migrert til @navikt/oasis

* 🏷️ Satt typer til strict for å kjøre tester

* 🎨 Slackbot-API with protected auth and actual user

* 🏷️ strengere typer

* ⚡ Implementert paginering av slacbot

* 🎨 Lagt til slackbot-token i workflows

* 🏷️ strict tsconfig

* 🏷️ Typer for slackbot input

* 🔥 Fjernet demo-body

* 🎨 oppdatert medvirkende tekst

* ⬆️ Yarn lock

* 🎨 move state handling down to Feedback

* 🏷️ typing fixes

* 🚧 API usage

* 🏷️ fix type error

* 👷 update AccessPolicy

* 🦺 add validation to feedback form

* 💄 justify-items-stretch on TextArea needed 🤔

* ♻️ avoid prop drilling, use sanity data hook for document id

* 🔧 add auth to webpack config for storybook

* ♻️ Feedback form  (also add error state handling)

* 👷 add stories for Feedback form

* 🔊 log feedback results metadata

* ♻️ remove refs, use formData and event.currentTarget

* 🧪 retry flaky test

* ⚗️ test promises for dynamic import in test

* 🧪 flaky test config

* ⬆️ yarn lock sync

* ⬆️ yarn lock sync

* ♻️ Bruker nå egen userState-type for fortelle komponenter om bruker er logget inn eller ikke

* 🔥 remove unused CSS

* 📝 lagt til todods

* 🔥 remove anonymous feedback submission

* 🔥 remove CSS

* :goals_net: catch fetch clientside, feedback

* ✨ send feedback to private slack channel as well

notify everyone if there are 0 editors

* 💄 + ♿ aria-hiddens and better styling, Feedback

* ♻️ refactor + add more feedback (loading state + alert)

* ⚗️ test encoding uri for anchor (redirect)

* ⚗️ more testing URL redirect

* ⚗️ more testing

* ⚗️ testing

* ✅ stories for Feedback

* 🔒 sanitize user objects (then cache)

* 🏷️ type SanitizeUser in find-user

* 🐛 optionally render the message about zero editors

* ⚗️ more testing for URL redirect / fragment issue

* ⚗️ log the redirect URL

* ⚗️ more testing

* ♻️ Gjennbruker error-return for feedback-api

* 🧪 Tester alternativ løsning for scroll etter login

* 🐛 Query update slapte loop i useEffect

* 🧪 Tester alternativ løsning for scroll etter login

* 🚚 Flyttet autofocus inn i headingseksjon

* 🐛 Wonderwall fjerner & fra query

* 🐛 encodeUrl as defined in wonderwall docs

* 🧪 Reversert til anchor-løsning

* 🧪 Reversert til anchor-løsning med encoding

* 🐛 Fikset edgecase der asPath med allerede eksisterende hash brakk login-redirect

* 🔥 Cleanup etter anchor-testing

* ⬆️ yarn lock sync

* 🎨 Oppdatert til navikt channelid

* 📝 Endret på error-tekst i feedback

* Fjernet ts-expect-error

* ⬆️ yarn lock sync

* 🔥 Fjernet filterering på godkjent mail

* 📝 Oppdatert slackbot-block

---------

Co-authored-by: Julian Nymark <julian.nymark@nav.no>
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-03-20 15:48:49 +01:00
github-actions[bot]andgithub-actions[bot] a882a8a389 Version Packages (#2807)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-20 13:26:51 +01:00
Ken d1606d358a 🐛 useDatepicker håndterer isInvalid validation riktig (#2809)
* 🐛 useDatepicker håndterer isInvalid validation riktig nå

* Update @navikt/core/react/src/date/hooks/useDatepicker.tsx

* Update @navikt/core/react/src/date/hooks/useDatepicker.tsx
2024-03-20 13:21:05 +01:00
Ken bb87837632 🧪 Bump timeout and retry of dateStr testing 2024-03-20 12:33:56 +01:00
Halvor Haugan 9c8b2301ed FileUpload.Dropzone: Fiks feil med duplisert id (#2805) 2024-03-20 12:31:08 +01:00
Ken 54ba483d2d ✅ Oppdatert Chromatic-tester
✅ Oppdatert dropdown-tester

🐛 satt dropdown-story til open

✅ Oppdatert errorsummary-tester

✅ Oppdatert fieldset-tester
2024-03-20 11:51:46 +01:00
github-actions[bot]andgithub-actions[bot] 85f5458c06 Version Packages (#2804)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-20 09:51:54 +01:00
Halvor Haugan 19abec80f7 FileUpload: Fiks feil ved import av json (#2803) 2024-03-20 09:46:45 +01:00
Julian NymarkandHalvor Haugan b2ddce5279 📝 Search docs review (#2797)
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-03-19 15:39:12 +01:00
github-actions[bot]andgithub-actions[bot] bd7c945158 Version Packages (#2799)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-19 15:07:37 +01:00
Ken ca24684a25 🐛 Dropzone med disabled stoppet ikke filopplasting (#2798)
inputfelt settes nå til disabled ved bruk av prop for å unngå dette
2024-03-19 15:02:06 +01:00
Julian Nymark 1564363985 💄 break-normal on body text, but break-all on types, as they… (#2790)
* 💄 break-normal on body text, but break-all on types, as they get long and complicated without spaces

* 🔥 remove break-normal, as it's the default
2024-03-19 14:58:58 +01:00
Ken ca8d36e885 🔥 Fjernet UX-tweaks card på forside
Kode er kommentert ut fra forside, men vil være lett tilgjengelig hvis vi ønsker å legge til fremtidige undersøkelser
2024-03-19 14:43:00 +01:00
github-actions[bot]andgithub-actions[bot] 8efc6c58be Version Packages (#2794)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-19 14:00:30 +01:00
2ea2a91a37 Combobox complex options (#2716)
* Remove unused function

* Support for complex options, using objects with label and value instead of strings

* Better typing for isTextInSelectedOptions value (boolean instead of truthy/falsy)

* Stricter value type: Only accept string values. This will work better with the allowNewValues prop, and simplify returning a value from onToggleSelected.

* More JSDoc

* Add test for complex options with label and value

* Add example for complex options on the aksel-website

* yarn.lock

* Remove other properties than label and value from Combobox options in Storybook-examples, as we have limited the format for now

* Array.includes does not work with objects, so should use isInList utility function. Fixes missing checkmark for selected items in FilteredOptions.

* Add changeset

* Remove navds-dependencies in playroom that break playwright tests. These are outdated/unsynced, and will be inherited from parent package.json instead.

* Do not try to remove an undefined option

* "it" is from jest. Use "test" instead.

* Update @navikt/core/react/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx

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

* Update @navikt/core/react/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx

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

* Update @navikt/core/react/src/form/combobox/Input/Input.tsx

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

* Update @navikt/core/react/src/form/combobox/types.ts

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

* Raise change level to minor, to signal the new feature

* Spread props instead of adding them separately

* JSDoc @returns doesn't do anything without a value specified, and neither of these return a value

* Options are used for multiple features, so will just skip specifying them

* Doesn't need a guaranteed unique ID for storybook examples. Use hard-coded instead.

* selectedOptions is a list of strings anyhow, so should just use the value

* More realistic example for complex options, where the option label is larger, and the SelectedOptions label uses a short code (the value)

Also made the isInList function better, by checking both label and value when checking ComboboxOptions. Pass just the value or just the label if you only want to check either of them.

* Add tests for mapToComboboxOptionArray (renamed from mapFromStringArrayToComboboxOptionArray)

* Add test for toComboboxOption and use this function in mapToComboboxOptionArray

* Update combobox unit test for complex options to use a more realistic example

* Don't say anything about what might be added in the future. That doesn't belong in the documentation

* Oppdater beskrivelsen for complex options-eksempelet (var kopiert fra annet eksempel)

* Fix Storybook example bug where de-selecting a value selected it an extra time

* Fix example

* Remove listing options because it breaks with allowNewValues

* Revert earlier change that always showed selected values in FilteredOptions, even if it didn't match the search text. This broke autocomplete.

* Remove unneccessary optional check

* yarn.lock

* Don't need to use toLocaleLowerCase

---------

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-03-19 13:47:13 +01:00
Halvor HauganandKen aebba20fd4 🆙 Minor-oppgradering av deps (#2795)
Co-authored-by: Ken <ken.aleksander@gmail.com>
2024-03-18 18:51:11 +01:00
Ken 5ce4b4ea79 📱 Tilpasset aksel-header for 320px (#2793) 2024-03-17 15:27:43 +01:00
Ken 548d9bb169 💄 Bedre eksempel for logg-ut i dropdown (#2792) 2024-03-17 15:27:33 +01:00
25e0d15d67 Ny komponent FileUpload (#2504)
---------

Co-authored-by: Ken <ken.aleksander@gmail.com>
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-03-15 12:54:35 +01:00
Halvor Haugan 98f6d7dde4 Forbedret beskrivelse for ref i prop-tabell (#2791) 2024-03-15 10:04:09 +01:00
6ac605dd48 Version Packages (#2775)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-03-14 16:15:40 +01:00
Ken d293d44b30 Fikset CDN deploy av Aksel og designsystem assets (#2789)
* 🐛 Oppdatert assetprefix

* 🐛 Fikset cdn asset upload
2024-03-14 16:05:09 +01:00
Julian Nymark 5754a08f6c 👷 update workflow for prod (new NAV CDN) 2024-03-14 14:20:08 +01:00
Julian Nymark 924c63324c 👷 swap team aksel for team designsystem (used elsewhere within NAIS) 2024-03-14 14:11:20 +01:00
Julian Nymark b78b818fea 👷 update workflow (new NAV CDN) 2024-03-14 14:00:44 +01:00
Ken dc5ea3b842 [Ikoner] Nytt ikon OpenSource (#2787)
* 🎉 Oppdatert ikonpakke

* 📝 changeset
2024-03-13 18:59:41 +01:00
Halvor Haugan bd315a239b [aksel.nav.no] Fiks link til token-guide (#2786) 2024-03-13 15:53:59 +01:00
Julian NymarkandKen <ken.aleksander@gmail.com>, Halvor 4469c641ed 💄 pretty up the props box on aksel.nav.no + typos (#2729)
* 💄 pretty up the props box on aksel.nav.no + typos

- also update some props docs here & there

* ♻️ pull out component, lazy() it

* 🔥 remove testing css

* 🎨 add fallback with <Suspense>

* 💬 correct @default strings

* 💄 spacing in Props table

* 🔥 @note is not a real JSDoc tag

* ✨ docgen extracts example, render fancy example box

* ✨ pretty @return and @params

* 💄 improve table layout / CSS

* 💄 + update CopyButton & Combobox jsdoc.

* 📝 update jsdoc

* 🎨 Oppdatert layout og visning av prop-table

* 🎨 use more semantic html, better nested <dl>

* ♿ add h4 to prop items

* 👷 use production sanity data

* ♿ revert nested <dl> so Axe tests pass

* 🏷️ correct some types for tsc to pass

* ♿ change from div -> ul

* 👷 commit lockfile

* 💄 make ul bottom rounded and align type with it\'s code block

* ♿ set language tag for props written in english

* 🏷️ type improvements for props section

* 🙈 update .gitignore

* 📝 jsdoc update

* 💄 props section

* ♻️ rename and less indentation (continues > ifs)

* 💄 style changes for Prop section

- breakpoint fail fix
- breakpoint col / row flex for prop fields
- padding tweaks

* 📝 update example jsdoc

---------

Co-authored-by: Ken <ken.aleksander@gmail.com>, Halvor <halvor.haugan@nav.no>
2024-03-12 13:59:35 +01:00
Halvor Haugan b15218e1cb Oppdatering av eksempler på Textarea (#2784) 2024-03-12 09:11:18 +01:00
Ken 73a69f7048 🎉 Fikset search-bug der for god score fjernet resultat (#2783) 2024-03-11 12:48:02 +01:00
Halvor Haugan 7dcd58ff76 Select: Justert eksempler, JSDOC og stories (#2782) 2024-03-11 11:04:49 +01:00
Halvor Haugan 2f3c99f5f6 Justering av eksempel på Table med TextField (#2780) 2024-03-08 09:35:00 +01:00
KenandJulian Nymark 8835291a78 [Aksel.nav.no] Auth på Aksel (#2757)
* 🎉 Satt opp boilerplate for auth på Aksel

* 🎉 Auth boilerplate

* 🎨 Demo-setup for å teste auth i dev

* 🚚 Flyttet auth til getserversideprops

* 🔥 Fjernet authprovider fra _app

* 🐛 revalidate er ikke valid key for getserversideprops i nextjs

* 🚚 rename auth-dir

* 🔥 Fjernet auth types

* 🎉 Auth for api

* 🎨 Oppdatert workflows med token

* 🔥 Fjernet testing av auth på god-praksis sider

* 🎨 La til sidecar på prod-deploy

* 🐛 only return boolean in Promise<boolean>

* ⬆️ Migrert til @navikt/oasis

* 🏷️ Satt typer til strict for å kjøre tester

* ⬆️ Yarn lock

---------

Co-authored-by: Julian Nymark <julian.nymark@nav.no>
2024-03-06 11:53:30 +01:00
Halvor Haugan 2971129d1f Oppgrader eslint-plugin-storybook (#2777) 2024-03-05 16:34:36 +01:00
Ken 9915cf8232 🔥 Fjernet muligheten til å sette seg som anonym i sanity
Nye forfattere blir anbefalt å bruke et alias eller bare fornavn hvis man ønsker.

Ingen er satt som anonym i dag, slik at dette ikke vil påvirke noen forfattere
2024-03-05 14:34:20 +01:00
Halvor Haugan fa00774b0d Forberedelse for Storybook 8 (#2776) 2024-03-05 14:33:58 +01:00
Ken 966de79621 [aksel.nav.no] Migrert editor-felt til variant med email (#2760)
* 🎨 Satt opp migrering for editorsider

* 🎨 Oppdatert initialvalues for mail og oppretting av nye artikler

* 📝 Description på email-field

* Update aksel.nav.no/website/sanity/schema/documents/editors.tsx

* Update aksel.nav.no/website/sanity/schema/documents/editors.tsx

* Update aksel.nav.no/website/sanity/schema/documents/editors.tsx

* 🎨 Bedre validering av nav-mail i sanity

* 🐛 Feilet etter ts oppdatering
2024-03-05 13:35:48 +01:00
KenandHalvor Haugan 0ce2300f92 [aksel.nav.no] Satt strict -> true i tsconfig (#2766)
* 🏷️ sanity-video typer

* 🏷️ Typer storybook

* 🏷️ Typer usecheckauth

* 🏷️ Typer formateddate

* 🏷️ Typer useSanityDate

* 🏷️ Typer footerlink

* 🏷️ Typer ny gp

* 🏷️ Typer layout

* 🏷️ Typer logging

* 🏷️ Typer logging av href

* 🏷️ Typer codeexamples

* 🏷️ Typer sanity-modules

* 🏷️ Typer for alle sanity-modules

* 🏷️ Typer resolvers

* 🏷️ Typer serach

* 🏷️ Typer search

* 🏷️ Typer useToc

* 🏷️ Typer _app

* 🏷️ Typer pages

* 🔥 Fjernet buildlogs fra tsc

* ⬇️ Reversert til gammel logging av lenker

* Update aksel.nav.no/website/pages/eksempler/timeline-intern/aria-controls.tsx

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

* 🏷️ Bruker satisifies for array-type sjekk

* 🏷️ Forenklet typer

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-03-05 11:46:10 +01:00
Halvor Haugan 08e9876176 Sandbox: Alltid bruk siste versjon av DS-pakkene (#2774) 2024-03-04 14:10:36 +01:00
Ken 331b4c4c00 🎉 Nytt ikon archive@ (#2772) 2024-03-04 14:10:04 +01:00
Halvor Haugan 2b6e9c8798 Oppdater stories for Link (#2770) 2024-03-01 20:00:18 +01:00
Ken 4065ee3987 💄 UXTweaks-hero på forside bruker Amber-farger
Designer ønsket å gjøre hero mer tydelig da dagens 'violet' forsvant mellom elementene
2024-02-29 19:33:30 +01:00
github-actions[bot]andgithub-actions[bot] 59756f32f7 Version Packages (#2764)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-29 12:21:22 +01:00
Halvor Haugan 9510327abe Link: Fjern hover-farge (#2767) 2024-02-28 15:18:25 +01:00
Christian Skrøvseth 356119e074 Modal: Fiks lukking ved slipp av museknapp på backdrop (#2752) 2024-02-27 15:02:02 +01:00
Halvor Haugan 3d2eab4fda Modal: fiks fokus bug i Safari + endre title på lukkeknappen (#2688) 2024-02-27 09:48:26 +01:00
Halvor Haugan 299c5f1db4 [aksel.nav.no] 🔨 Bytt ut cp med copyfiles (#2753) 2024-02-26 13:28:46 +01:00
Halvor Haugan 6267d0c7fb [aksel.nav.no] 🆙 Oppgradert start-server-and-test (#2759) 2024-02-26 13:26:59 +01:00
Halvor Haugan 44ba2ef7e8 [aksel.nav.no] 🚨 Ikke tillat @ts-ignore (#2758) 2024-02-26 13:07:30 +01:00
Ken 002f6a1b9e [Sanity] Oppdatert sanity minor (#2733)
* 🎉 Oppdatert sanity

* 🐛 Oppdatert import av deprecated desktool i sanity

* ⬆️ yarn sync

* 🐛 IntentButton var oppdatert i ny sanity-versjon
2024-02-26 10:43:00 +01:00
Halvor Haugan 124c28eb0e Oppdatering av eksempler på TextField (#2754) 2024-02-26 10:05:46 +01:00
Halvor Haugan ae7d4d5f07 Oppgradering av deps (#2756) 2024-02-26 09:07:11 +01:00
424 changed files with 42016 additions and 36395 deletions

No files matched your search

-1
View File
@@ -72,7 +72,6 @@ module.exports = {
"error",
"aksel.nav.no/website/pages/",
],
"@typescript-eslint/ban-ts-comment": "off", // Temporary
},
},
{
+11 -11
View File
@@ -14,12 +14,18 @@ jobs:
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "20"
- uses: actions/checkout@v3
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: add tokens to .env
run: |
echo "SLACK_BOT_TOKEN=${{ secrets.SLACK_BOT_TOKEN }}" >> aksel.nav.no/website/.env
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
@@ -29,14 +35,6 @@ jobs:
run: |
echo "User-agent: *\nDisallow: /" > aksel.nav.no/website/public/robots.txt
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-dev-${{ github.sha }}
restore-keys: |
${{ runner.os }}-dev-
- name: Install dependencies
run: yarn
@@ -46,11 +44,13 @@ jobs:
yarn workspace aksel.nav.no build:next
- name: Upload static files to NAV CDN
uses: navikt/frontend/actions/cdn-upload/v1@main
uses: nais/deploy/actions/cdn-upload/v2@master
with:
cdn-team-name: aksel
team: designsystem
source: ./aksel.nav.no/website/.next/static
destination: "/website/_next"
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
- name: Get complete tag
run: echo "TAG=DEV-$( date +%s )" >> $GITHUB_ENV
+19 -20
View File
@@ -14,22 +14,20 @@ jobs:
name: Update component data
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "20"
- uses: actions/checkout@v3
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: add tokens to .env
run: |
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-prod-${{ github.sha }}
restore-keys: |
${{ runner.os }}-prod-
- name: Install dependencies
run: yarn
@@ -49,25 +47,24 @@ jobs:
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "20"
- uses: actions/checkout@v3
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: add tokens to .env
run: |
echo "SLACK_BOT_TOKEN=${{ secrets.SLACK_BOT_TOKEN }}" >> aksel.nav.no/website/.env
echo "SANITY_WRITE_KEY=${{ secrets.SANITY_FEEDBACK_WRITE_KEY }}" >> aksel.nav.no/website/.env
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
echo "USE_CDN_ASSETS=true" >> aksel.nav.no/website/.env
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-prod-${{ github.sha }}
restore-keys: |
${{ runner.os }}-prod-
- name: Install dependencies
run: yarn
@@ -81,11 +78,13 @@ jobs:
run: yarn workspace aksel.nav.no build:next
- name: Upload static files to NAV CDN
uses: navikt/frontend/actions/cdn-upload/v1@main
uses: nais/deploy/actions/cdn-upload/v2@master
with:
cdn-team-name: aksel
team: designsystem
source: ./aksel.nav.no/website/.next/static
destination: "/website/_next"
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
- name: Get complete tag
run: echo "TAG=PROD-$( date +%s )" >> $GITHUB_ENV
+5 -7
View File
@@ -11,17 +11,15 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Required to retrieve git history
- uses: actions/setup-node@v3
with:
node-version: "20"
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
- name: Install dependencies
run: cd aksel.nav.no/website && yarn
+3 -8
View File
@@ -24,14 +24,9 @@ jobs:
with:
node-version: "20"
cache: yarn
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-test-${{ github.sha }}
restore-keys: |
${{ runner.os }}-test
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: Install dependencies
run: yarn
+4 -8
View File
@@ -19,6 +19,10 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: "20"
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- name: add tokens to .env
run: |
@@ -26,14 +30,6 @@ jobs:
echo "SANITY_PREVIEW_TOKEN=${{ secrets.SANITY_PREVIEW_TOKEN }}" >> aksel.nav.no/website/.env
echo "SANITY_PRIVATE_NO_DRAFTS=${{ secrets.SANITY_PRIVATE_NO_DRAFTS }}" >> aksel.nav.no/website/.env
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-e2e-${{ github.sha }}
restore-keys: |
${{ runner.os }}-e2e-
- name: Install dependencies
run: yarn
+4 -2
View File
@@ -20,11 +20,13 @@ jobs:
- uses: "actions/checkout@v3"
- id: upload
uses: navikt/frontend/actions/cdn-upload/v1@main
uses: nais/deploy/actions/cdn-upload/v2@master
with:
cdn-team-name: aksel
team: aksel
source: ./fonts
destination: "" # uploads folder to team root
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
- run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
shell: bash
+11 -12
View File
@@ -30,19 +30,14 @@ jobs:
with:
node-version: "20"
cache: yarn
- name: set-env for yarn install to @navikt scope
shell: bash
run: echo "NPM_AUTH_TOKEN=${{ secrets.READER_TOKEN }}" >> $GITHUB_ENV
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Restore cache
uses: actions/cache@v3
with:
path: "**/node_modules"
key: ${{ runner.os }}-prod-${{ github.sha }}
restore-keys: |
${{ runner.os }}-prod
- name: Install dependencies
run: yarn
@@ -85,11 +80,13 @@ jobs:
- name: Upload updated icon.zip to NAV CDN
if: steps.changesets.outputs.published == 'true'
uses: navikt/frontend/actions/cdn-upload/v1@main
uses: nais/deploy/actions/cdn-upload/v2@master
with:
cdn-team-name: aksel
team: aksel
source: ./@navikt/aksel-icons/aksel-icons.zip
destination: "/icons/zip"
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
- name: Get @navikt/ds-css version
run: |
@@ -99,8 +96,10 @@ jobs:
- name: Upload CSS to NAV CDN
if: steps.changesets.outputs.published == 'true'
uses: navikt/frontend/actions/cdn-upload/v1@main
uses: nais/deploy/actions/cdn-upload/v2@master
with:
cdn-team-name: aksel
team: aksel
source: ./@navikt/core/css/dist/version/${{env.version}}
destination: "@navikt/ds-css"
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
File renamed without changes.
+4 -9
View File
@@ -1,16 +1,12 @@
import { mergeConfig } from "vite";
import turbosnap from "vite-plugin-turbosnap";
export default {
staticDirs: ["./public"],
stories: () => [
"../@navikt/**/*.stories.@(js|jsx|ts|tsx|mdx)",
"./*.stories.mdx",
],
stories: () => ["../@navikt/**/*.stories.@(js|jsx|ts|tsx|mdx)", "./*.mdx"],
addons: [
"@storybook/addon-a11y",
"@xfinx/storybook-addon-html",
"@whitespace/storybook-addon-html",
"@storybook/addon-interactions",
{
name: "@storybook/addon-storysource",
@@ -29,14 +25,12 @@ export default {
},
},
},
"storybook-addon-pseudo-states",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
features: {
storyStoreV7: true,
},
docs: {
autodocs: "tag",
},
@@ -47,6 +41,7 @@ export default {
},
async viteFinal(config, { configType }) {
const { mergeConfig } = await import("vite");
return mergeConfig(config, {
plugins:
configType === "PRODUCTION"
+6
View File
@@ -2,6 +2,12 @@ enableImmutableInstalls: false
nodeLinker: node-modules
npmScopes:
navikt:
npmAlwaysAuth: true
npmAuthToken: "${NPM_AUTH_TOKEN:-}"
npmRegistryServer: "https://npm.pkg.github.com"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
+28
View File
@@ -1,5 +1,33 @@
# @navikt/aksel-icons
## 6.4.1
## 6.4.0
## 6.3.6
## 6.3.5
## 6.3.4
## 6.3.3
## 6.3.2
## 6.3.1
## 6.3.0
## 6.2.0
### Minor Changes
- Ikoner: Nytt ikon `archive` ([#2772](https://github.com/navikt/aksel/pull/2772))
- Ikoner: Nytt ikon `opensource` ([#2787](https://github.com/navikt/aksel/pull/2787))
## 6.1.1
## 6.1.0
## 6.0.0
@@ -6,5 +6,8 @@
"ui": "dist/index.html",
"capabilities": [],
"enableProposedApi": false,
"editorType": ["figma", "figjam"]
"editorType": ["figma", "figjam"],
"networkAccess": {
"allowedDomains": ["none"]
}
}
+4 -4
View File
@@ -3,12 +3,12 @@ import React from "react";
import * as AkselIcons from "@navikt/aksel-icons";
import { StarFillIcon } from "@navikt/aksel-icons";
const meta = {
const meta: Meta<typeof StarFillIcon> = {
title: "aksel-icons/Icons",
} satisfies Meta<typeof StarFillIcon>;
};
export default meta;
type Story = StoryObj<typeof meta>;
type Story = StoryObj<typeof StarFillIcon>;
export const Icons: Story = {
render: () => {
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.25 4C3.25 3.58579 3.58579 3.25 4 3.25H20C20.4142 3.25 20.75 3.58579 20.75 4V8C20.75 8.41421 20.4142 8.75 20 8.75H19.75V19C19.75 19.9665 18.9665 20.75 18 20.75H6C5.0335 20.75 4.25 19.9665 4.25 19V8.75H4C3.58579 8.75 3.25 8.41421 3.25 8V4ZM5.75 8.75H18.25V19C18.25 19.1381 18.1381 19.25 18 19.25H6C5.86193 19.25 5.75 19.1381 5.75 19V8.75ZM4.75 4.75V7.25H19.25V4.75H4.75ZM14 13.25C14.4142 13.25 14.75 12.9142 14.75 12.5C14.75 12.0858 14.4142 11.75 14 11.75H10C9.58579 11.75 9.25 12.0858 9.25 12.5C9.25 12.9142 9.58579 13.25 10 13.25H14Z" fill="#23262A"/>
</svg>

After

Width:  |  Height:  |  Size: 707 B

+10
View File
@@ -0,0 +1,10 @@
name: Archive
category: Interface
sub_category: Communication
keywords:
- arkiv
- box
- boks
variant: Stroke
updated_at: 22.02.2024
created_at: 22.02.2024
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 3.25C3.58579 3.25 3.25 3.58579 3.25 4V7.5C3.25 7.91421 3.58579 8.25 4 8.25H20C20.4142 8.25 20.75 7.91421 20.75 7.5V4C20.75 3.58579 20.4142 3.25 20 3.25H4ZM19.5 9.25C19.6381 9.25 19.75 9.36193 19.75 9.5V19C19.75 19.9665 18.9665 20.75 18 20.75H6C5.0335 20.75 4.25 19.9665 4.25 19V9.5C4.25 9.36193 4.36193 9.25 4.5 9.25H19.5ZM14.75 12.5C14.75 12.9142 14.4142 13.25 14 13.25H10C9.58579 13.25 9.25 12.9142 9.25 12.5C9.25 12.0858 9.58579 11.75 10 11.75H14C14.4142 11.75 14.75 12.0858 14.75 12.5Z" fill="#23262A"/>
</svg>

After

Width:  |  Height:  |  Size: 663 B

+10
View File
@@ -0,0 +1,10 @@
name: ArchiveFill
category: Interface
sub_category: Communication
keywords:
- arkiv
- box
- boks
variant: Fill
updated_at: 22.02.2024
created_at: 22.02.2024
@@ -13,5 +13,5 @@ keywords:
- reload
- oppdater
variant: Stroke
updated_at: 04.12.2023
updated_at: 14.02.2024
created_at: 07.03.2023
+1 -1
View File
@@ -9,5 +9,5 @@ keywords:
- nivå
- innrykk
variant: Stroke
updated_at: 04.12.2023
updated_at: 16.01.2024
created_at: 04.12.2023
+1 -1
View File
@@ -13,5 +13,5 @@ keywords:
- forward
- videresend
variant: Stroke
updated_at: 04.12.2023
updated_at: 23.02.2024
created_at: 07.03.2023
@@ -13,5 +13,5 @@ keywords:
- oppfrisking
- oppdatering
variant: Stroke
updated_at: 04.12.2023
updated_at: 29.02.2024
created_at: 07.03.2023
+1 -1
View File
@@ -9,5 +9,5 @@ keywords:
- sortering
- sorting
variant: Stroke
updated_at: 04.12.2023
updated_at: 22.02.2024
created_at: 07.03.2023
+1 -1
View File
@@ -15,5 +15,5 @@ keywords:
- Sammenføye
- Kildekontroll
variant: Stroke
updated_at: 04.12.2023
updated_at: 18.01.2024
created_at: 07.03.2023
@@ -5,5 +5,5 @@ keywords:
- ned
- down
variant: Stroke
updated_at: 04.12.2023
updated_at: 25.01.2024
created_at: 07.03.2023
@@ -5,5 +5,5 @@ keywords:
- left
- venstre
variant: Stroke
updated_at: 04.12.2023
updated_at: 11.03.2024
created_at: 07.03.2023
@@ -5,5 +5,5 @@ keywords:
- opp
- up
variant: Stroke
updated_at: 04.12.2023
updated_at: 25.01.2024
created_at: 07.03.2023
+1 -1
View File
@@ -8,5 +8,5 @@ keywords:
- Utvide/skjule
- vise/skjule
variant: Stroke
updated_at: 04.12.2023
updated_at: 04.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -15,5 +15,5 @@ keywords:
- markup-språk
- kildekode
variant: Stroke
updated_at: 04.12.2023
updated_at: 01.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -7,5 +7,5 @@ keywords:
- downloading
- last ned
variant: Stroke
updated_at: 04.12.2023
updated_at: 08.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -5,5 +5,5 @@ keywords:
- trapp
- rulletrapp
variant: Stroke
updated_at: 04.12.2023
updated_at: 12.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -9,5 +9,5 @@ keywords:
- open
- åpne
variant: Stroke
updated_at: 04.12.2023
updated_at: 24.01.2024
created_at: 07.03.2023
+1 -1
View File
@@ -13,5 +13,5 @@ keywords:
- fremdrift
- nedtelling
variant: Stroke
updated_at: 04.12.2023
updated_at: 21.02.2024
created_at: 07.03.2023
@@ -13,5 +13,5 @@ keywords:
- fremdrift
- nedtelling
variant: Stroke
updated_at: 04.12.2023
updated_at: 22.01.2024
created_at: 07.03.2023
@@ -13,5 +13,5 @@ keywords:
- fremdrift
- nedtelling
variant: Stroke
updated_at: 04.12.2023
updated_at: 16.01.2024
created_at: 07.03.2023
+1 -1
View File
@@ -8,5 +8,5 @@ keywords:
- replace
- new version
variant: Stroke
updated_at: 04.12.2023
updated_at: 18.01.2024
created_at: 07.03.2023
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.06458 4.26481C7.76736 2.95822 9.85369 2.25 12 2.25C14.1463 2.25 16.2326 2.95821 17.9354 4.2648C19.6382 5.57139 20.8623 7.40334 21.4178 9.47651C21.9733 11.5497 21.8292 13.7482 21.0078 15.7312C20.1865 17.7141 18.7338 19.3706 16.875 20.4437C16.7027 20.5432 16.498 20.5702 16.3059 20.5187C16.1138 20.4672 15.9499 20.3415 15.8505 20.1692L12.7005 14.7133C12.601 14.541 12.5741 14.3363 12.6256 14.1442C12.677 13.952 12.8027 13.7882 12.975 13.6887C13.3468 13.4741 13.6373 13.1428 13.8016 12.7462C13.9658 12.3496 13.9947 11.9099 13.8836 11.4953C13.7725 11.0807 13.5276 10.7143 13.1871 10.453C12.8465 10.1916 12.4293 10.05 12 10.05C11.5707 10.05 11.1535 10.1916 10.8129 10.453C10.4724 10.7143 10.2275 11.0807 10.1164 11.4953C10.0053 11.9099 10.0342 12.3496 10.1984 12.7462C10.3627 13.1428 10.6533 13.4741 11.025 13.6887C11.1973 13.7882 11.323 13.952 11.3744 14.1442C11.4259 14.3363 11.399 14.541 11.2995 14.7133L8.14952 20.1692C8.05007 20.3415 7.88625 20.4672 7.69412 20.5187C7.50198 20.5702 7.29727 20.5432 7.125 20.4437C5.26625 19.3706 3.81354 17.7141 2.99218 15.7312C2.17082 13.7482 2.02672 11.5497 2.58223 9.47652C3.13773 7.40334 4.3618 5.5714 6.06458 4.26481ZM12 3.75C10.1839 3.75 8.41854 4.34926 6.97772 5.45484C5.53691 6.56041 4.50116 8.11052 4.03112 9.86474C3.56107 11.619 3.683 13.4793 4.378 15.1571C4.97686 16.6029 5.97237 17.8436 7.24256 18.7401L9.66674 14.5413C9.29868 14.2034 9.00613 13.7874 8.81262 13.3203C8.52199 12.6186 8.471 11.8407 8.66756 11.1071C8.86412 10.3735 9.29725 9.72526 9.89978 9.26293C10.5023 8.8006 11.2405 8.55 12 8.55C12.7595 8.55 13.4977 8.8006 14.1002 9.26293C14.7028 9.72526 15.1359 10.3735 15.3324 11.1071C15.529 11.8407 15.478 12.6186 15.1874 13.3203C14.9939 13.7874 14.7013 14.2034 14.3333 14.5413L16.7574 18.7401C18.0276 17.8436 19.0231 16.6029 19.622 15.1571C20.317 13.4793 20.4389 11.619 19.9689 9.86474C19.4988 8.11052 18.4631 6.56041 17.0223 5.45483C15.5815 4.34926 13.8161 3.75 12 3.75Z" fill="#23262A"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+19
View File
@@ -0,0 +1,19 @@
name: OpenSource
category: Development
sub_category: Development
keywords:
- location
- tracking
- identification
- signaling
- broadcasting
- detection
- lokalisering
- sporing
- identifikasjon
- signalisering
- kringkasting
- deteksjon
variant: Stroke
updated_at: 08.03.2024
created_at: 07.03.2023
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.06458 4.26481C7.76736 2.95822 9.85369 2.25 12 2.25C14.1463 2.25 16.2326 2.95821 17.9354 4.2648C19.6382 5.57139 20.8623 7.40334 21.4178 9.47651C21.9733 11.5497 21.8292 13.7482 21.0078 15.7312C20.1865 17.7141 18.7338 19.3706 16.875 20.4437C16.7027 20.5432 16.498 20.5702 16.3059 20.5187C16.1138 20.4672 15.9499 20.3415 15.8505 20.1692L12.7005 14.7133C12.601 14.541 12.5741 14.3363 12.6256 14.1442C12.677 13.952 12.8027 13.7882 12.975 13.6887C13.3468 13.4741 13.6373 13.1428 13.8016 12.7462C13.9658 12.3496 13.9947 11.9099 13.8836 11.4953C13.7725 11.0807 13.5276 10.7143 13.1871 10.453C12.8465 10.1916 12.4293 10.05 12 10.05C11.5707 10.05 11.1535 10.1916 10.8129 10.453C10.4724 10.7143 10.2275 11.0807 10.1164 11.4953C10.0053 11.9099 10.0342 12.3496 10.1984 12.7462C10.3627 13.1428 10.6533 13.4741 11.025 13.6887C11.1973 13.7882 11.323 13.952 11.3744 14.1442C11.4259 14.3363 11.399 14.541 11.2995 14.7133L8.14952 20.1692C8.05007 20.3415 7.88625 20.4672 7.69412 20.5187C7.50198 20.5702 7.29727 20.5432 7.125 20.4437C5.26625 19.3706 3.81354 17.7141 2.99218 15.7312C2.17082 13.7482 2.02672 11.5497 2.58223 9.47652C3.13773 7.40334 4.3618 5.5714 6.06458 4.26481Z" fill="#23262A"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,19 @@
name: OpenSourceFill
category: Development
sub_category: Development
keywords:
- location
- tracking
- identification
- signaling
- broadcasting
- detection
- lokalisering
- sporing
- identifikasjon
- signalisering
- kringkasting
- deteksjon
variant: Fill
updated_at: 08.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -9,5 +9,5 @@ keywords:
- hidden
- show/hide
variant: Stroke
updated_at: 04.12.2023
updated_at: 18.01.2024
created_at: 07.03.2023
+1 -1
View File
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.4445 6.61612C12.679 6.3817 12.9969 6.25 13.3284 6.25H17.5C17.6381 6.25 17.75 6.36193 17.75 6.5V10.6716C17.75 11.0031 17.6183 11.321 17.3839 11.5555L9.17678 19.7626C9.07915 19.8602 8.92086 19.8602 8.82322 19.7626L4.23744 15.1768C4.13981 15.0791 4.13981 14.9209 4.23744 14.8232L12.4445 6.61612ZM13.3284 4.75C12.5991 4.75 11.8996 5.03973 11.3839 5.55546L3.17678 13.7626C2.49336 14.446 2.49336 15.554 3.17678 16.2374L7.76256 20.8232C8.44598 21.5066 9.55402 21.5066 10.2374 20.8232L18.4445 12.6161C18.9603 12.1004 19.25 11.4009 19.25 10.6716V6.5C19.25 5.5335 18.4665 4.75 17.5 4.75H13.3284ZM14.5 11C15.3284 11 16 10.3284 16 9.5C16 8.67157 15.3284 8 14.5 8C13.6716 8 13 8.67157 13 9.5C13 10.3284 13.6716 11 14.5 11Z" fill="#23262A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.4403 5.61612C13.6748 5.3817 13.9927 5.25 14.3242 5.25H18.4958C18.6339 5.25 18.7458 5.36193 18.7458 5.5V9.67157C18.7458 10.0031 18.6141 10.321 18.3797 10.5555L10.1726 18.7626C10.0749 18.8602 9.91664 18.8602 9.81901 18.7626L5.23323 14.1768C5.1356 14.0791 5.1356 13.9209 5.23323 13.8232L13.4403 5.61612ZM14.3242 3.75C13.5949 3.75 12.8954 4.03973 12.3797 4.55546L4.17257 12.7626C3.48915 13.446 3.48915 14.554 4.17257 15.2374L8.75835 19.8232C9.44177 20.5066 10.5498 20.5066 11.2332 19.8232L19.4403 11.6161C19.9561 11.1004 20.2458 10.4009 20.2458 9.67157V5.5C20.2458 4.5335 19.4623 3.75 18.4958 3.75H14.3242ZM15.4958 10C16.3242 10 16.9958 9.32843 16.9958 8.5C16.9958 7.67157 16.3242 7 15.4958 7C14.6674 7 13.9958 7.67157 13.9958 8.5C13.9958 9.32843 14.6674 10 15.4958 10Z" fill="#23262A"/>
</svg>

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 940 B

+1 -1
View File
@@ -12,5 +12,5 @@ keywords:
- organisering
- identifisering
variant: Stroke
updated_at: 12.12.2023
updated_at: 13.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.3284 4.75C12.5991 4.75 11.8996 5.03973 11.3839 5.55546L3.17678 13.7626C2.49336 14.446 2.49336 15.554 3.17678 16.2374L7.76256 20.8232C8.44598 21.5066 9.55402 21.5066 10.2374 20.8232L18.4445 12.6161C18.9603 12.1004 19.25 11.4009 19.25 10.6716V6.5C19.25 5.5335 18.4665 4.75 17.5 4.75H13.3284ZM14.5 11C15.3284 11 16 10.3284 16 9.5C16 8.67157 15.3284 8 14.5 8C13.6716 8 13 8.67157 13 9.5C13 10.3284 13.6716 11 14.5 11Z" fill="#23262A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3242 3.75C13.5949 3.75 12.8954 4.03973 12.3797 4.55546L4.17257 12.7626C3.48915 13.446 3.48915 14.554 4.17257 15.2374L8.75835 19.8232C9.44177 20.5066 10.5498 20.5066 11.2332 19.8232L19.4403 11.6161C19.9561 11.1004 20.2458 10.4009 20.2458 9.67157V5.5C20.2458 4.5335 19.4623 3.75 18.4958 3.75H14.3242ZM15.4958 10C16.3242 10 16.9958 9.32843 16.9958 8.5C16.9958 7.67157 16.3242 7 15.4958 7C14.6674 7 13.9958 7.67157 13.9958 8.5C13.9958 9.32843 14.6674 10 15.4958 10Z" fill="#23262A"/>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 636 B

+1 -1
View File
@@ -12,5 +12,5 @@ keywords:
- organisering
- identifisering
variant: Fill
updated_at: 12.12.2023
updated_at: 13.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -6,5 +6,5 @@ keywords:
- vektløfting
- tungt
variant: Stroke
updated_at: 04.12.2023
updated_at: 11.03.2024
created_at: 07.03.2023
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "6.1.0",
"version": "6.4.1",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+22
View File
@@ -1,5 +1,27 @@
# @navikt/aksel-stylelint
## 6.4.1
## 6.4.0
## 6.3.6
## 6.3.5
## 6.3.4
## 6.3.3
## 6.3.2
## 6.3.1
## 6.3.0
## 6.2.0
## 6.1.1
## 6.1.0
## 6.0.0
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "6.1.0",
"version": "6.4.1",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
"repository": {
@@ -35,10 +35,9 @@
"dev": "yarn watch:lint"
},
"devDependencies": {
"@navikt/ds-css": "^6.1.0",
"@navikt/ds-tokens": "^6.1.0",
"@navikt/ds-css": "^6.4.1",
"@navikt/ds-tokens": "^6.4.1",
"concurrently": "7.2.1",
"copyfiles": "2.4.1",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0",
"stylelint": "^14.8.5",
+77
View File
@@ -1,5 +1,82 @@
# @navikt/aksel
## 6.4.1
### Patch Changes
- Updated dependencies [[`5250b47ca`](https://github.com/navikt/aksel/commit/5250b47ca410f9cd57c5350bacc77b1b2ec9b2c0)]:
- @navikt/ds-css@6.4.1
## 6.4.0
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.4.0
## 6.3.6
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.3.6
## 6.3.5
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.3.5
## 6.3.4
### Patch Changes
- Updated dependencies [[`a2af9bcc3`](https://github.com/navikt/aksel/commit/a2af9bcc37eb12cc7cf3a1596d57dc0421898657)]:
- @navikt/ds-css@6.3.4
## 6.3.3
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.3.3
## 6.3.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.3.2
## 6.3.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.3.1
## 6.3.0
### Patch Changes
- Updated dependencies [[`25e0d15d6`](https://github.com/navikt/aksel/commit/25e0d15d67cebd36ee58aa3b7d9f1c7104d92de6)]:
- @navikt/ds-css@6.3.0
## 6.2.0
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@6.2.0
## 6.1.1
### Patch Changes
- Updated dependencies [[`9510327ab`](https://github.com/navikt/aksel/commit/9510327abe96142943c6e92bd7c7da93a9721fd5), [`3d2eab4fd`](https://github.com/navikt/aksel/commit/3d2eab4fda07cac8071822e1f8e8fc5f69e7d234)]:
- @navikt/ds-css@6.1.1
## 6.1.0
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "6.1.0",
"version": "6.4.1",
"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": "6.1.0",
"@navikt/ds-css": "6.4.1",
"axios": "1.6.0",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+40
View File
@@ -1,5 +1,45 @@
# @navikt/ds-css
## 6.4.1
### Patch Changes
- FileUpload: Item-illustrasjon er nå radius-full ([#2834](https://github.com/navikt/aksel/pull/2834))
## 6.4.0
## 6.3.6
## 6.3.5
## 6.3.4
### Patch Changes
- ReadMore: Padding-right på button er bumpet noen px ([#2806](https://github.com/navikt/aksel/pull/2806))
## 6.3.3
## 6.3.2
## 6.3.1
## 6.3.0
### Minor Changes
- FileUpload: Ny komponent `FileUpload` for å håndtere opplasting av filer, Dropzone og fil-visning ([#2504](https://github.com/navikt/aksel/pull/2504))
## 6.2.0
## 6.1.1
### Patch Changes
- Link: Fjern hover-farge ([#2767](https://github.com/navikt/aksel/pull/2767))
- Modal: Får nå fokus når åpnes i Safari ([#2688](https://github.com/navikt/aksel/pull/2688))
## 6.1.0
## 6.0.0
+5
View File
@@ -120,6 +120,11 @@ const StyleMappings = {
dependencies: [typoCss],
},
{ component: "Fieldset", main: formCss, dependencies: [typoCss] },
{
component: "FileUpload",
main: formCss,
dependencies: [typoCss, "button.css", "loader.css", "link.css"],
},
{ component: "Grid", main: "grid.css", dependencies: [typoCss] },
{
component: "GuidePanel",
+230
View File
@@ -0,0 +1,230 @@
/**
* Makes it easier for user to use FileItem in semantic lists
*/
.navds-file-upload :is(ul, li) {
list-style: none;
margin: 0;
padding: 0;
}
/**
* FileUpload.Dropzone
*/
.navds-dropzone__area {
--__ac-dropzone-background: var(--a-surface-subtle);
--__ac-dropzone-text-color: var(--a-text-default);
--__ac-dropzone-padding: var(--a-spacing-6);
--__ac-dropzone-icon-padding: var(--a-spacing-2);
--__ac-dropzone-animation-length-long: 0.6s;
--__ac-dropzone-animation-length-short: 250ms;
--__ac-dropzone-animation-ease-out: cubic-bezier(0.3, 1, 0.3, 1);
--__ac-dropzone-animation-over-under: cubic-bezier(0.3, 1.4, 0.3, 1);
position: relative;
width: 100%;
display: flex;
align-items: center;
flex-direction: column;
gap: var(--a-spacing-4);
text-align: center;
padding: var(--__ac-dropzone-padding);
outline: 1px dashed var(--a-border-subtle);
outline-offset: -1px;
border-radius: var(--a-border-radius-large);
background-color: var(--__ac-dropzone-background);
color: var(--__ac-dropzone-text-color);
transition: background-color var(--__ac-dropzone-animation-length-short) var(--__ac-dropzone-animation-ease-out);
cursor: pointer;
}
.navds-dropzone__area:hover {
box-shadow: inset 0 2px 4px 1px rgb(11 11 11/ 0.05);
outline-color: var(--a-border-default);
}
.navds-dropzone--disabled > .navds-dropzone__area:hover {
outline-color: var(--a-border-subtle);
box-shadow: initial;
cursor: default;
}
.navds-dropzone--dragging > .navds-dropzone__area {
--__ac-dropzone-background: var(--a-surface-action-subtle-hover);
}
.navds-dropzone--dragging > .navds-dropzone__area::after {
outline: 1px dashed var(--a-border-subtle);
outline-offset: -1px;
color: var(--a-text-action-selected);
background-color: var(--a-surface-hover);
content: "";
inset: 0;
position: absolute;
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
box-shadow: inset 0 2px 7px 3px rgb(11 11 11/ 0.1);
border-radius: var(--a-border-radius-large);
animation: akselDropzoneDragoverAnimation var(--__ac-dropzone-animation-length-short) var(--__ac-dropzone-animation-ease-out);
}
@keyframes akselDropzoneDragoverAnimation {
0% {
-webkit-backdrop-filter: blur(0);
backdrop-filter: blur(0);
background-color: var(--a-surface-transparent);
}
70% {
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
100% {
background-color: var(--a-surface-hover);
}
}
.navds-dropzone__area-icon {
display: grid;
padding: var(--__ac-dropzone-icon-padding);
visibility: hidden;
}
.navds-dropzone--error:where(:not(.navds-dropzone--dragging)) > .navds-dropzone__area {
outline-color: var(--a-surface-danger);
outline-width: 2px;
}
.navds-dropzone__area:hover > .navds-dropzone__area-button {
color: var(--ac-button-secondary-hover-text, var(--__ac-button-secondary-hover-text, var(--a-text-action-on-action-subtle)));
background-color: var(
--ac-button-secondary-hover-bg,
var(--__ac-button-secondary-hover-bg, var(--a-surface-action-subtle-hover))
);
}
.navds-dropzone__area:active .navds-dropzone__area-button {
color: var(--ac-button-secondary-active-text, var(--__ac-button-secondary-active-text, var(--a-text-on-action)));
background-color: var(--ac-button-secondary-active-bg, var(--__ac-button-secondary-active-bg, var(--a-surface-action-active)));
box-shadow: none;
}
.navds-dropzone__area-release {
top: var(--__ac-dropzone-padding);
display: grid;
position: absolute;
z-index: 1;
pointer-events: none;
transition:
top var(--__ac-dropzone-animation-length-long) var(--__ac-dropzone-animation-over-under),
transform var(--__ac-dropzone-animation-length-long) var(--__ac-dropzone-animation-over-under);
}
.navds-dropzone__area-release__icon {
display: grid;
padding: var(--__ac-dropzone-icon-padding);
border-radius: var(--a-border-radius-full);
background-color: var(--a-surface-neutral-subtle);
transition:
background-color 300ms var(--__ac-dropzone-animation-ease-out),
font-size 300ms var(--__ac-dropzone-animation-ease-out);
font-size: 1.5rem;
}
.navds-dropzone--dragging .navds-dropzone__area-release > .navds-dropzone__area-release__icon {
background-color: transparent;
font-size: 2rem;
}
.navds-dropzone__area-release__text {
visibility: hidden;
}
.navds-dropzone--dragging .navds-dropzone__area-release > .navds-dropzone__area-release__text {
transition: visibility var(--__ac-dropzone-animation-length-long) var(--__ac-dropzone-animation-over-under);
visibility: visible;
}
.navds-dropzone--dragging .navds-dropzone__area-release {
color: var(--a-text-action-selected);
top: 50%;
transform: translateY(-50%);
}
.navds-dropzone__area-disabled {
color: var(--a-text-subtle);
display: flex;
align-items: center;
flex-direction: column;
gap: var(--a-spacing-2);
}
/**
* FileUpload.Item
*/
.navds-file-item__inner {
outline: 1px solid var(--a-border-subtle);
outline-offset: -1px;
transition: outline-color 250ms cubic-bezier(0, 0.3, 0.15, 1);
border-radius: var(--a-border-radius-large);
padding: var(--a-spacing-3);
display: flex;
gap: var(--a-spacing-3);
align-items: flex-start;
}
.navds-file-item--error > .navds-file-item__inner {
outline: 2px solid var(--a-surface-danger);
}
.navds-file-item__icon {
background-color: var(--a-surface-neutral-subtle);
color: var(--a-icon-default);
border-radius: var(--a-border-radius-full);
min-height: 3rem;
min-width: 3rem;
display: grid;
place-content: center;
transition: background-color 250ms cubic-bezier(0, 0.3, 0.15, 1);
}
.navds-file-item__icon--loading {
background-color: transparent;
}
.navds-file-item__file-info {
word-break: break-word;
}
.navds-file-item__button {
margin-left: auto;
}
.navds-file-item__error {
color: var(--a-text-danger);
display: grid;
transition-property: grid-template-rows, padding-top;
transition-duration: 250ms;
transition-timing-function: cubic-bezier(0, 0.3, 0.15, 1);
overflow: hidden;
grid-template-rows: 0fr;
}
.navds-file-item--error .navds-file-item__error {
grid-template-rows: 1fr;
padding-top: var(--a-spacing-1);
}
.navds-file-item__error-content > svg {
flex-shrink: 0;
height: 1rem;
margin-top: 0.1rem;
}
.navds-file-item__error-content {
min-height: 0;
display: flex;
gap: var(--a-spacing-1);
}
+4 -2
View File
@@ -18,13 +18,15 @@
}
.navds-form-field .navds-error-message,
.navds-fieldset .navds-error-message {
.navds-fieldset .navds-error-message,
.navds-file-item .navds-error-message {
display: flex;
gap: var(--a-spacing-2);
}
.navds-form-field .navds-error-message::before,
.navds-fieldset .navds-error-message::before {
.navds-fieldset .navds-error-message::before,
.navds-file-item .navds-error-message::before {
content: "•";
}
+1
View File
@@ -2,6 +2,7 @@
Order matters; rearrange with care
*/
@import "fieldset.css";
@import "file-upload.css";
@import "form.css";
@import "error-summary.css";
@import "confirmation-panel.css";
-1
View File
@@ -20,7 +20,6 @@
.navds-link:hover {
text-decoration: none;
color: var(--ac-link-hover-text, var(--a-text-action-hover));
}
.navds-link.navds-link--remove-underline {
+1 -1
View File
@@ -146,7 +146,7 @@
@keyframes akselModalFadeIn {
from {
opacity: 0;
opacity: 0.0001; /* Safari will not set focus inside the modal when it opens if we set this to 0 */
transform: translate3d(0, calc(5% + 4px), 0);
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "6.1.0",
"version": "6.4.1",
"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": "^6.1.0",
"@navikt/ds-tokens": "^6.4.1",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
+2 -2
View File
@@ -8,12 +8,12 @@
gap: var(--a-spacing-05);
color: var(--ac-read-more-text, var(--a-text-action));
border-radius: var(--a-border-radius-small);
padding: var(--a-spacing-1) var(--a-spacing-1) var(--a-spacing-1) var(--a-spacing-05);
padding: var(--a-spacing-1) var(--a-spacing-2) var(--a-spacing-1) var(--a-spacing-05);
text-align: start;
}
.navds-read-more--small .navds-read-more__button {
padding: var(--a-spacing-05) var(--a-spacing-1) var(--a-spacing-05) var(--a-spacing-05);
padding: var(--a-spacing-05) var(--a-spacing-1-alt) var(--a-spacing-05) var(--a-spacing-05);
}
@media (forced-colors: active) {
-1
View File
@@ -174,7 +174,6 @@
},
"link": {
"--ac-link-text": "--a-text-action",
"--ac-link-hover-text": "--a-text-action-hover",
"--ac-link-focus-text": "--a-text-on-action",
"--ac-link-focus-bg": "--a-border-focus",
"--ac-link-focus-border": "--a-border-focus",
+120
View File
@@ -1,5 +1,125 @@
# @navikt/ds-react
## 6.4.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@6.4.1
- @navikt/aksel-icons@6.4.1
## 6.4.0
### Minor Changes
- FileUpload.Item: :boom: Endret API og støtte for å sette ID på knappen ([#2824](https://github.com/navikt/aksel/pull/2824))
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@6.4.0
- @navikt/aksel-icons@6.4.0
## 6.3.6
### Patch Changes
- DatePicker: Fjernet role gridcell fra dato-knapper. ([#2826](https://github.com/navikt/aksel/pull/2826))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.6
- @navikt/aksel-icons@6.3.6
## 6.3.5
### Patch Changes
- Modal: Fjern tomt element ([#2819](https://github.com/navikt/aksel/pull/2819))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.5
- @navikt/aksel-icons@6.3.5
## 6.3.4
### Patch Changes
- Switch: Fikset edgecase der checked Switch var visuelt unchecked ([#2810](https://github.com/navikt/aksel/pull/2810))
- FileUpload.Dropzone: id prop settes på button ([#2817](https://github.com/navikt/aksel/pull/2817))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.4
- @navikt/aksel-icons@6.3.4
## 6.3.3
### Patch Changes
- FileUpload.Dropzone: Fiks feil med duplisert id ([#2805](https://github.com/navikt/aksel/pull/2805))
- useDatePicker: `isInvalid`-key i onValidate var hadde flipper boolean i noen tilfeller ([#2809](https://github.com/navikt/aksel/pull/2809))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.3
- @navikt/aksel-icons@6.3.3
## 6.3.2
### Patch Changes
- FileUpload: Fiks feil ved import av json ([#2803](https://github.com/navikt/aksel/pull/2803))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.2
- @navikt/aksel-icons@6.3.2
## 6.3.1
### Patch Changes
- FileUpload: Disabled dropzone stopper opplasting ([#2798](https://github.com/navikt/aksel/pull/2798))
- Updated dependencies []:
- @navikt/ds-tokens@6.3.1
- @navikt/aksel-icons@6.3.1
## 6.3.0
### Minor Changes
- Allow Combobox options as objects to support separate display text and value ([#2716](https://github.com/navikt/aksel/pull/2716))
- FileUpload: Ny komponent `FileUpload` for å håndtere opplasting av filer, Dropzone og fil-visning ([#2504](https://github.com/navikt/aksel/pull/2504))
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@6.3.0
- @navikt/aksel-icons@6.3.0
## 6.2.0
### Patch Changes
- Select: Fjern 'multiple' fra SelectProps ([#2782](https://github.com/navikt/aksel/pull/2782))
- Updated dependencies [[`331b4c4c0`](https://github.com/navikt/aksel/commit/331b4c4c00c323ad7a212bcd45889d2b80c09dee), [`dc5ea3b84`](https://github.com/navikt/aksel/commit/dc5ea3b8420a02690bc88dbbbb97eefe4ee7564d)]:
- @navikt/aksel-icons@6.2.0
- @navikt/ds-tokens@6.2.0
## 6.1.1
### Patch Changes
- Modal: Fikser uønsket lukking ved klikk-og-dra mellom backdrop og modal (f.eks. ved markering av tekst) dersom `closeOnBackdropClick` er `true`. ([#2752](https://github.com/navikt/aksel/pull/2752))
- Modal: Endre title på lukkeknapp til bare 'Lukk' ([#2688](https://github.com/navikt/aksel/pull/2688))
- Updated dependencies []:
- @navikt/ds-tokens@6.1.1
- @navikt/aksel-icons@6.1.1
## 6.1.0
### Minor Changes
+13 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "6.1.0",
"version": "6.4.1",
"description": "React components from the Norwegian Labour and Welfare Administration.",
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
"license": "MIT",
@@ -539,6 +539,16 @@
"default": "./cjs/form/textfield/index.js"
}
},
"./FileUpload": {
"import": {
"types": "./esm/form/file-upload/index.d.ts",
"default": "./esm/form/file-upload/index.js"
},
"require": {
"types": "./cjs/form/file-upload/index.d.ts",
"default": "./cjs/form/file-upload/index.js"
}
},
"./package.json": "./package.json"
},
"scripts": {
@@ -552,8 +562,8 @@
},
"dependencies": {
"@floating-ui/react": "0.25.4",
"@navikt/aksel-icons": "^6.1.0",
"@navikt/ds-tokens": "^6.1.0",
"@navikt/aksel-icons": "^6.4.1",
"@navikt/ds-tokens": "^6.4.1",
"@radix-ui/react-tabs": "1.0.0",
"@radix-ui/react-toggle-group": "1.0.0",
"clsx": "^2.1.0",
@@ -567,7 +577,6 @@
"@testing-library/user-event": "^14.2.0",
"@types/faker": "5.5.8",
"concurrently": "7.2.1",
"copyfiles": "^2.4.1",
"faker": "5.5.3",
"fast-glob": "3.2.11",
"jsdom": "24.0.0",
@@ -31,11 +31,11 @@ interface AccordionComponent
export interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* @default 'default'
* @default "default"
*/
variant?: "default" | "neutral";
/**
* @default 'small'
* @default "small"
*/
headingSize?: "large" | "medium" | "small" | "xsmall";
/**
@@ -43,12 +43,12 @@ export interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
*/
size?: "large" | "medium" | "small";
/**
* Whether to indent content or not
* Whether to indent content or not.
* @default true
*/
indent?: boolean;
/**
* Instances of Accordion.Item
* Instances of `Accordion.Item`.
*/
children: React.ReactNode;
}
@@ -162,9 +162,7 @@ export const Variants: Story = {
export const ControlledState: Story = {
render: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [open, setOpen] = useState(false);
// eslint-disable-next-line react-hooks/rules-of-hooks
const [open2, setOpen2] = useState(false);
return (
+12 -10
View File
@@ -12,37 +12,39 @@ import { BodyLong } from "../typography";
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Alert content
* Alert content.
*/
children: React.ReactNode;
/**
* Changes colors and icon usage when changed
* Changes colors and icon usage when changed.
*/
variant: "error" | "warning" | "info" | "success";
/**
* Changes padding and font-sizes
* @default medium
* Changes padding and font-sizes.
* @default "medium"
*/
size?: "medium" | "small";
/**
* Removes border-radius
* Removes `border-radius`.
* @default false
*/
fullWidth?: boolean;
/**
* Removes background from Alert
* Removes background from Alert.
* @default false
*/
inline?: boolean;
/**
* Removes close-button(X) when false
* Requires onClose to be set
* Removes close-button(X) when false.
*
* **Requires onClose to be set**.
* @default true
*/
closeButton?: boolean;
/**
* Callback for alert wanting to close
* requires closeButton to be true
* Callback for alert wanting to close.
*
* **Requires closeButton to be true**.
*/
onClose?: () => void;
}
+9 -8
View File
@@ -11,11 +11,11 @@ import { OverridableComponent } from "../util/types";
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/**
* Button content
* Button content.
*/
children?: React.ReactNode;
/**
* Changes design and interaction-visuals
* Changes design and interaction-visuals.
* @default "primary"
*/
variant?:
@@ -27,26 +27,27 @@ export interface ButtonProps
| "tertiary-neutral"
| "danger";
/**
* Changes padding, height and font-size
* @default medium
* Changes padding, height, and font-size.
* @default "medium"
*/
size?: "medium" | "small" | "xsmall";
/**
* **Avoid using if possible for accessibility purposes**.
*
* Prevent the user from interacting with the button: it cannot be pressed or focused.
* @note Avoid using if possible for accessibility purposes
*/
disabled?: boolean;
/**
* Replaces button content with a Loader component, keeps width
* Replaces button content with a Loader component, keeps width.
* @default false
*/
loading?: boolean;
/**
* Button Icon
* Button Icon.
*/
icon?: React.ReactNode;
/**
* Icon position in Button
* Icon position in Button.
* @default "left"
*/
iconPosition?: "left" | "right";
+6 -6
View File
@@ -8,15 +8,15 @@ export const SIZES = ["medium", "small"] as const;
export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
/**
* Children of type <Chat.Bubble />
* Children of type `<Chat.Bubble />`.
*/
children: React.ReactNode;
/**
* Chat-message name
* Chat-message name.
*/
name?: string;
/**
* Timestamp for sent message
* Timestamp for sent message.
*/
timestamp?: string;
/**
@@ -30,17 +30,17 @@ export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
*/
variant?: "subtle" | "info" | "neutral";
/**
* Positions avatar and bubbles
* Positions avatar and bubbles.
* @default "left"
*/
position?: (typeof POSITIONS)[number];
/**
* Hoizontal position of toptext
* Horizontal position of toptext.
* @default Same as position
*/
toptextPosition?: (typeof POSITIONS)[number];
/**
* Affects padding and font size in bubbles
* Affects padding and font size in bubbles.
* @default "medium"
*/
size?: (typeof SIZES)[number];
+2 -2
View File
@@ -7,7 +7,7 @@ import ToggleChips, { ChipsToggleProps } from "./Toggle";
export interface ChipsProps extends HTMLAttributes<HTMLUListElement> {
children: React.ReactNode;
/**
* Changes padding and font-sizes
* Changes padding and font-sizes.
* @default "medium"
*/
size?: "medium" | "small";
@@ -24,7 +24,7 @@ interface ChipsComponent
*/
Toggle: OverridableComponent<ChipsToggleProps, HTMLButtonElement>;
/**
* Remove filter or the likes on click
* Remove filter or the likes on click.
* @see 🏷️ {@link RemovableChipsProps}
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*/
@@ -14,7 +14,7 @@ import copy from "../util/copy";
export interface CopyButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
/**
* 'xsmall' should only be used in tables
* `"xsmall"` should _only_ be used in tables.
* @default "medium"
*/
size?: "medium" | "small" | "xsmall";
@@ -23,49 +23,52 @@ export interface CopyButtonProps
*/
variant?: "action" | "neutral";
/**
* Text to copy to clipboard
* Text to copy to clipboard.
*/
copyText: string;
/**
* Optional text in button
* Optional text in button.
*/
text?: string;
/**
* Text shown when button is clicked.
* Only set if used with `text`-prop.
* @default "Kopiert!"
*/
activeText?: string;
/**
* Callback when 'copied'-state is active
* Callback that is called when internal copy-state changes.
*
* @param state `true` when copy-state is activated, `false` when copy-state is deactivated.
*/
onActiveChange?: (state: boolean) => void;
/**
* Icon shown when button is not clicked
* Icon shown when button is not clicked.
* @default <FilesIcon />
*/
icon?: React.ReactNode;
/**
* Icon shown when active
* Icon shown when active.
* @default <CheckmarkIcon />
*/
activeIcon?: React.ReactNode;
/**
* Timeout duration in milliseconds
* Timeout duration in milliseconds.
* @default 2000
*/
activeDuration?: number;
/**
* Accessible label for icon (ignored if text is set)
* @default 'Kopier'
* Accessible label for icon (ignored if text is set).
* @default "Kopier"
*/
title?: string;
/**
* Accessible label for icon in active-state (ignored if text is set)
* @default 'Kopiert'
* Accessible label for icon in active-state (ignored if text is set).
* @default "Kopiert"
*/
activeTitle?: string;
/**
* Icon position in button
* Icon position in button.
* @default "left"
*/
iconPosition?: "left" | "right";
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { Meta, StoryObj } from "@storybook/react";
import { isSameDay } from "date-fns";
import React, { useId, useState } from "react";
@@ -161,6 +160,7 @@ export const UseDatepicker = () => {
fromDate: new Date("Aug 23 2019"),
toDate: new Date("Feb 23 2024"),
onDateChange: console.log,
onValidate: console.log,
});
return (
@@ -20,6 +20,7 @@ const DayButton = (props: DayProps) => {
name="day"
ref={buttonRef}
{...dayRender.buttonProps}
role={undefined}
aria-label={dateTime}
aria-hidden={dayRender.activeModifiers.outside}
/>
@@ -16,7 +16,7 @@ export type MultipleMode = {
min?: number;
max?: number;
/**
* Allows selecting a week at a time. Only used with mode="multiple".
* Allows selecting a week at a time. Only used with `mode` is set to "multiple".
*/
onWeekNumberClick?: DayPickerBase["onWeekNumberClick"];
};
@@ -83,21 +83,22 @@ export interface DatePickerDefaultProps
*/
showWeekNumber?: boolean;
/**
* Open state for user-controlled state. Component controlled by default
* Open state for user-controlled state. Component controlled by default.
*/
open?: boolean;
/**
* onClose callback for user-controlled state
* onClose callback for user-controlled state.
*/
onClose?: () => void;
/**
* onOpenToggle callback for user-controlled state. Only called if `<DatePicker.Input />` is used
* onOpenToggle callback for user-controlled state. Only called if `<DatePicker.Input />` is used.
*/
onOpenToggle?: () => void;
/**
* Avoid using if possible!
* **Avoid using if possible!**
*
* Changes what CSS position property to use.
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape.
* @default See Popover
*/
strategy?: "absolute" | "fixed";
@@ -49,10 +49,11 @@ export interface UseDatepickerOptions
*/
onValidate?: (val: DateValidationT) => void;
/**
* Allows input of with 'yy' year format.
* Allows input of with `yy` year format.
*
* Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* e.g. In 2024 this equals to 1944 - 2043.
* @default true
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* In 2024 this equals to 1944 - 2043
*/
allowTwoDigitYear?: boolean;
}
@@ -271,7 +272,7 @@ export const useDatepicker = (
) {
updateDate(undefined);
updateValidation({
isInvalid: isValidDate(day),
isInvalid: !isValidDate(day),
isWeekend: disableWeekends && isWeekend(day),
isDisabled: disabled && isMatch(day, disabled),
isValidDate: false,
@@ -36,10 +36,11 @@ export interface UseMonthPickerOptions
*/
defaultYear?: Date;
/**
* Allows input of with 'yy' year format.
* @default true
* @Note Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* Allows input of with `yy` year format.
*
* Decision between 20th and 21st century is based on before(todays year - 80) ? 21st : 20th.
* In 2024 this equals to 1944 - 2043
* @default true
*/
allowTwoDigitYear?: boolean;
}
@@ -69,7 +69,8 @@ export interface MonthPickerProps extends React.HTMLAttributes<HTMLDivElement> {
*/
onYearChange?: (y?: Date) => void;
/**
* Avoid using if possible!
* **Avoid using if possible!**
*
* Changes what CSS position property to use
* You want to use "fixed" if parent wrapper has position relative, but you want popover to escape
* @default "absolute"
@@ -21,7 +21,7 @@ export interface DateInputProps
hideLabel?: boolean;
/**
* Changes padding and font-sizes
* @default medium
* @default "medium"
*/
size?: "medium" | "small";
/**
@@ -1,4 +1,4 @@
import { Meta } from "@storybook/react";
import { Meta, StoryObj } from "@storybook/react";
import React, { useState } from "react";
import { Button } from "../button";
import Dropdown from "./Dropdown";
@@ -6,10 +6,15 @@ import Dropdown from "./Dropdown";
export default {
title: "ds-react/Dropdown",
component: Dropdown,
} as Meta;
parameters: {
chromatic: { disable: true },
},
} satisfies Meta<typeof Dropdown>;
export const Default = () => {
return (
type Story = StoryObj<typeof Dropdown>;
export const Default: Story = {
render: () => (
<Dropdown onSelect={(event) => console.log(event)}>
<Dropdown.Toggle>Toggle</Dropdown.Toggle>
<Dropdown.Menu strategy="fixed">
@@ -33,10 +38,10 @@ export const Default = () => {
</Dropdown.Menu.List>
</Dropdown.Menu>
</Dropdown>
);
),
};
export const DefaultOpen = {
export const DefaultOpen: Story = {
render: () => (
<Dropdown onSelect={(event) => console.log(event)} defaultOpen>
<Button as={Dropdown.Toggle}>Toggle</Button>
@@ -57,20 +62,41 @@ export const DefaultOpen = {
</Dropdown.Menu>
</Dropdown>
),
args: { chromatic: { delay: 300 } },
};
export const ControlledOpen = () => {
const [openState, setOpenState] = useState(true);
return (
<Dropdown onSelect={(event) => console.log(event)} open={openState}>
<Button as={Dropdown.Toggle} onClick={() => setOpenState(!openState)}>
Toggle
</Button>
<Dropdown.Menu
strategy="fixed"
onClose={() => console.log("ONCLOSE CONTROLLED")}
>
export const ControlledOpen: Story = {
render: () => {
const [openState, setOpenState] = useState(true);
return (
<Dropdown onSelect={(event) => console.log(event)} open={openState}>
<Button as={Dropdown.Toggle} onClick={() => setOpenState(!openState)}>
Toggle
</Button>
<Dropdown.Menu
strategy="fixed"
onClose={() => console.log("ONCLOSE CONTROLLED")}
>
<Dropdown.Menu.GroupedList>
<Dropdown.Menu.GroupedList.Heading>
Systemer og oppslagsverk
</Dropdown.Menu.GroupedList.Heading>
<Dropdown.Menu.GroupedList.Item
onClick={() => console.log("GroupedList.Item-click")}
>
Gosys
</Dropdown.Menu.GroupedList.Item>
</Dropdown.Menu.GroupedList>
</Dropdown.Menu>
</Dropdown>
);
},
};
export const Chromatic: Story = {
render: () => (
<Dropdown onSelect={(event) => console.log(event)} open>
<Dropdown.Toggle>Toggle</Dropdown.Toggle>
<Dropdown.Menu strategy="fixed">
<Dropdown.Menu.GroupedList>
<Dropdown.Menu.GroupedList.Heading>
Systemer og oppslagsverk
@@ -81,8 +107,18 @@ export const ControlledOpen = () => {
Gosys
</Dropdown.Menu.GroupedList.Item>
</Dropdown.Menu.GroupedList>
<Dropdown.Menu.Divider />
<Dropdown.Menu.List>
<Dropdown.Menu.List.Item onClick={() => console.log("Item-click")}>
Gosys
</Dropdown.Menu.List.Item>
<Dropdown.Menu.List.Item>Psys</Dropdown.Menu.List.Item>
<Dropdown.Menu.List.Item disabled>Infotrygd</Dropdown.Menu.List.Item>
</Dropdown.Menu.List>
</Dropdown.Menu>
</Dropdown>
);
),
parameters: {
chromatic: { disable: false, delay: 300 },
},
};
ControlledOpen.args = { chromatic: { delay: 300 } };
@@ -1,5 +1,6 @@
import cl from "clsx";
import React, { forwardRef, useRef, useState } from "react";
import React, { forwardRef, useRef } from "react";
import { useControllableState } from "../util/hooks";
import { OverridableComponent } from "../util/types";
import ExpansionCardContent, {
ExpansionCardContentProps,
@@ -116,24 +117,24 @@ export const ExpansionCard = forwardRef<HTMLDivElement, ExpansionCardProps>(
},
ref,
) => {
const [_open, _setOpen] = useState(defaultOpen);
const shouldFade = useRef<boolean>(!(Boolean(open) || defaultOpen));
const handleOpen = () => {
if (open === undefined) {
const newOpen = !_open;
_setOpen(newOpen);
onToggle?.(newOpen);
} else {
onToggle?.(!open);
}
shouldFade.current = true;
};
const [_open, _setOpen] = useControllableState({
value: open,
onChange: (newValue) => {
onToggle?.(newValue);
shouldFade.current = true;
},
defaultValue: defaultOpen,
});
return (
<ExpansionCardContext.Provider
value={{ open: open ?? _open, toggleOpen: handleOpen, size }}
value={{
open: open ?? _open,
toggleOpen: () => _setOpen((x) => !x),
size,
}}
>
<section
{...rest}
@@ -7,7 +7,7 @@ export interface ExpansionCardTitleProps
children: React.ReactNode;
/**
* Changes text-sizing
* @default medium
* @default "medium"
*/
size?: "large" | "medium" | "small";
}
@@ -16,10 +16,10 @@ export const CheckboxGroupContext = createContext<CheckboxGroupState | null>(
export interface CheckboxGroupProps
extends Omit<
FieldsetProps,
"onChange" | "errorPropagation" | "defaultValue"
"onChange" | "errorPropagation" | "defaultValue" | "nativeReadOnly"
> {
/**
* Collection of <Checkbox/>
* Collection of `<Checkbox/>`.
*/
children: React.ReactNode;
/**
@@ -27,11 +27,11 @@ export interface CheckboxGroupProps
*/
value?: any[];
/**
* Default checked checkboxes on render
* Default checked checkboxes.
*/
defaultValue?: any[];
/**
* Returns current checked checkboxes in group
* Returns current checked checkboxes in group.
*/
onChange?: (value: any[]) => void;
}
@@ -44,10 +44,10 @@ export interface CheckboxGroupProps
*
* @example
* ```jsx
* <CheckboxGroup legend="Hvor vil du sitte?">
* <Checkbox value="Bakerst">Bakerst</Checkbox>
* <Checkbox value="Midterst">Midterst</Checkbox>
* <Checkbox value="Fremst">Fremst</Checkbox>
* <CheckboxGroup legend="Transportmiddel">
* <Checkbox value="car">Bil</Checkbox>
* <Checkbox value="taxi">Drosje</Checkbox>
* <Checkbox value="public">Kollektivt</Checkbox>
* </CheckboxGroup>
* ```
*/
@@ -5,16 +5,16 @@ export interface CheckboxProps
extends FormFieldProps,
Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "value"> {
/**
* Adds error indication on checkbox
* Adds error indication on checkbox.
* @default false
*/
error?: boolean;
/**
* Id for error resulting in checkbox having error
* Id for error resulting in checkbox having error.
*/
errorId?: string;
/**
* Checkbox label
* Checkbox label.
*/
children: React.ReactNode;
/**
@@ -22,16 +22,16 @@ export interface CheckboxProps
*/
hideLabel?: boolean;
/**
* The value of the HTML element.
* Checkbox value.
*/
value?: any;
/**
* Specify whether the Checkbox is in an indeterminate state
* Specify whether the Checkbox is in an indeterminate state.
* @default false
*/
indeterminate?: boolean;
/**
* Adds a description to extend labling of Checkbox
* Adds a description to extend the labeling.
*/
description?: string;
}
@@ -3,6 +3,7 @@ import Combobox from "./Combobox";
import { FilteredOptionsProvider } from "./FilteredOptions/filteredOptionsContext";
import { InputContextProvider } from "./Input/inputContext";
import { SelectedOptionsProvider } from "./SelectedOptions/selectedOptionsContext";
import { mapToComboboxOptionArray } from "./combobox-utils";
import { CustomOptionsProvider } from "./customOptionsContext";
import { ComboboxProps } from "./types";
@@ -36,15 +37,15 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
defaultValue,
error,
errorId,
filteredOptions,
filteredOptions: externalFilteredOptions,
id,
isListOpen,
isLoading = false,
isMultiSelect,
onToggleSelected,
selectedOptions,
selectedOptions: externalSelectedOptions,
maxSelected,
options,
options: externalOptions,
value,
onChange,
onClear,
@@ -52,6 +53,9 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
size,
...rest
} = props;
const options = mapToComboboxOptionArray(externalOptions) || [];
const filteredOptions = mapToComboboxOptionArray(externalFilteredOptions);
const selectedOptions = mapToComboboxOptionArray(externalSelectedOptions);
return (
<InputContextProvider
value={{
@@ -5,6 +5,8 @@ import { Loader } from "../../../loader";
import { BodyShort, Label } from "../../../typography";
import { useInputContext } from "../Input/inputContext";
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
import { isInList, toComboboxOption } from "../combobox-utils";
import { ComboboxOption } from "../types";
import filteredOptionsUtil from "./filtered-options-util";
import { useFilteredOptionsContext } from "./filteredOptionsContext";
@@ -30,8 +32,8 @@ const FilteredOptions = () => {
const { isMultiSelect, selectedOptions, toggleOption, maxSelected } =
useSelectedOptionsContext();
const isDisabled = (option) =>
maxSelected?.isLimitReached && !selectedOptions.includes(option);
const isDisabled = (option: ComboboxOption) =>
maxSelected?.isLimitReached && !isInList(option.value, selectedOptions);
const shouldRenderNonSelectables =
maxSelected?.isLimitReached || // Render maxSelected message
@@ -102,8 +104,8 @@ const FilteredOptions = () => {
}
}}
onPointerUp={(event) => {
toggleOption(value, event);
if (!isMultiSelect && !selectedOptions.includes(value))
toggleOption(toComboboxOption(value), event);
if (!isMultiSelect && !isInList(value, selectedOptions))
toggleIsListOpen(false);
}}
id={filteredOptionsUtil.getAddNewOptionId(id)}
@@ -132,21 +134,23 @@ const FilteredOptions = () => {
className={cl("navds-combobox__list-item", {
"navds-combobox__list-item--focus":
activeDecendantId ===
filteredOptionsUtil.getOptionId(id, option),
"navds-combobox__list-item--selected":
selectedOptions.includes(option),
filteredOptionsUtil.getOptionId(id, option.label),
"navds-combobox__list-item--selected": isInList(
option.value,
selectedOptions,
),
})}
data-no-focus={isDisabled(option) || undefined}
id={filteredOptionsUtil.getOptionId(id, option)}
key={option}
id={filteredOptionsUtil.getOptionId(id, option.label)}
key={option.label}
tabIndex={-1}
onMouseMove={() => {
if (
activeDecendantId !==
filteredOptionsUtil.getOptionId(id, option)
filteredOptionsUtil.getOptionId(id, option.label)
) {
virtualFocus.moveFocusToElement(
filteredOptionsUtil.getOptionId(id, option),
filteredOptionsUtil.getOptionId(id, option.label),
);
setIsMouseLastUsedInputDevice(true);
}
@@ -156,16 +160,19 @@ const FilteredOptions = () => {
return;
}
toggleOption(option, event);
if (!isMultiSelect && !selectedOptions.includes(option)) {
if (
!isMultiSelect &&
!isInList(option.value, selectedOptions)
) {
toggleIsListOpen(false);
}
}}
role="option"
aria-selected={selectedOptions.includes(option)}
aria-selected={isInList(option.value, selectedOptions)}
aria-disabled={isDisabled(option) || undefined}
>
<BodyShort size={size}>{option}</BodyShort>
{selectedOptions.includes(option) && <CheckmarkIcon />}
<BodyShort size={size}>{option.label}</BodyShort>
{isInList(option.value, selectedOptions) && <CheckmarkIcon />}
</li>
))}
</ul>
@@ -1,17 +1,13 @@
import { ComboboxOption } from "../types";
const normalizeText = (text: string): string =>
typeof text === "string" ? text.toLocaleLowerCase().trim() : "";
const isPartOfText = (value, text) =>
const isPartOfText = (value: string, text: string) =>
normalizeText(text).startsWith(normalizeText(value ?? ""));
const isValueInList = (value, list) =>
list?.find((listItem) => normalizeText(value) === normalizeText(listItem));
const getMatchingValuesFromList = (value, list, alwaysIncluded) =>
list?.filter(
(listItem) =>
isPartOfText(value, listItem) || alwaysIncluded.includes(listItem),
);
const getMatchingValuesFromList = (value: string, list: ComboboxOption[]) =>
list.filter((listItem) => isPartOfText(value, listItem.label));
const getFilteredOptionsId = (comboboxId: string) =>
`${comboboxId}-filtered-options`;
@@ -34,7 +30,6 @@ const getMaxSelectedOptionsId = (comboboxId: string) =>
export default {
normalizeText,
isPartOfText,
isValueInList,
getMatchingValuesFromList,
getFilteredOptionsId,
getAddNewOptionId,
@@ -10,21 +10,18 @@ import React, {
import { useClientLayoutEffect, usePrevious } from "../../../util/hooks";
import { useInputContext } from "../Input/inputContext";
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
import { toComboboxOption } from "../combobox-utils";
import { useCustomOptionsContext } from "../customOptionsContext";
import { ComboboxProps } from "../types";
import { ComboboxOption, ComboboxProps } from "../types";
import filteredOptionsUtils from "./filtered-options-util";
import useVirtualFocus, { VirtualFocusType } from "./useVirtualFocus";
type FilteredOptionsProps = {
children: any;
value: Pick<
ComboboxProps,
| "allowNewValues"
| "filteredOptions"
| "isListOpen"
| "isLoading"
| "options"
>;
children: React.ReactNode;
value: Pick<ComboboxProps, "allowNewValues" | "isListOpen" | "isLoading"> & {
filteredOptions?: ComboboxOption[];
options: ComboboxOption[];
};
};
type FilteredOptionsContextType = {
@@ -36,12 +33,12 @@ type FilteredOptionsContextType = {
>;
isListOpen: boolean;
isLoading?: boolean;
filteredOptions: string[];
filteredOptions: ComboboxOption[];
isMouseLastUsedInputDevice: boolean;
setIsMouseLastUsedInputDevice: React.Dispatch<SetStateAction<boolean>>;
isValueNew: boolean;
toggleIsListOpen: (newState?: boolean) => void;
currentOption?: string;
currentOption?: ComboboxOption;
shouldAutocomplete?: boolean;
virtualFocus: VirtualFocusType;
};
@@ -71,7 +68,7 @@ export const FilteredOptionsProvider = ({
setSearchTerm,
shouldAutocomplete,
} = useInputContext();
const { selectedOptions, maxSelected } = useSelectedOptionsContext();
const { maxSelected } = useSelectedOptionsContext();
const [isInternalListOpen, setInternalListOpen] = useState(false);
const { customOptions } = useCustomOptionsContext();
@@ -81,18 +78,8 @@ export const FilteredOptionsProvider = ({
return externalFilteredOptions;
}
const opts = [...customOptions, ...options];
return filteredOptionsUtils.getMatchingValuesFromList(
searchTerm,
opts,
selectedOptions,
);
}, [
customOptions,
externalFilteredOptions,
options,
searchTerm,
selectedOptions,
]);
return filteredOptionsUtils.getMatchingValuesFromList(searchTerm, opts);
}, [customOptions, externalFilteredOptions, options, searchTerm]);
const previousSearchTerm = usePrevious(searchTerm);
@@ -104,11 +91,11 @@ export const FilteredOptionsProvider = ({
options.reduce(
(map, _option) => ({
...map,
[filteredOptionsUtils.getOptionId(id, _option)]: _option,
[filteredOptionsUtils.getOptionId(id, _option.label)]: _option,
}),
{
[filteredOptionsUtils.getAddNewOptionId(id)]: allowNewValues
? value
? toComboboxOption(value)
: undefined,
},
),
@@ -123,7 +110,7 @@ export const FilteredOptionsProvider = ({
filteredOptions.length > 0
) {
setValue(
`${searchTerm}${filteredOptions[0].substring(searchTerm.length)}`,
`${searchTerm}${filteredOptions[0].label.substring(searchTerm.length)}`,
);
setSearchTerm(searchTerm);
}
@@ -161,7 +148,10 @@ export const FilteredOptionsProvider = ({
activeOption = filteredOptionsUtils.getNoHitsId(id);
} else if ((value && value !== "") || isLoading) {
if (shouldAutocomplete && filteredOptions[0]) {
activeOption = filteredOptionsUtils.getOptionId(id, filteredOptions[0]);
activeOption = filteredOptionsUtils.getOptionId(
id,
filteredOptions[0].label,
);
} else if (isListOpen && isLoading) {
activeOption = filteredOptionsUtils.getIsLoadingId(id);
}
@@ -43,17 +43,17 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
const onEnter = useCallback(
(event: React.KeyboardEvent) => {
const isTextInSelectedOptions = (text: string) => {
return selectedOptions.find(
(item) => item.toLocaleLowerCase() === text.toLocaleLowerCase(),
const isTextInSelectedOptions = (text: string) =>
selectedOptions.some(
(option) =>
option.label.toLocaleLowerCase() === text.toLocaleLowerCase(),
);
};
if (currentOption) {
event.preventDefault();
// Selecting a value from the dropdown / FilteredOptions
toggleOption(currentOption, event);
if (!isMultiSelect && !isTextInSelectedOptions(currentOption)) {
if (!isMultiSelect && !isTextInSelectedOptions(currentOption.label)) {
toggleIsListOpen(false);
}
} else if (shouldAutocomplete && isTextInSelectedOptions(value)) {
@@ -64,11 +64,15 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
event.preventDefault();
// Autocompleting or adding a new value
const selectedValue =
allowNewValues && isValueNew ? value : filteredOptions[0];
allowNewValues && isValueNew
? { label: value, value }
: filteredOptions[0];
toggleOption(selectedValue, event);
if (
!isMultiSelect &&
!isTextInSelectedOptions(filteredOptions[0] || selectedValue)
!isTextInSelectedOptions(
filteredOptions[0].label || selectedValue.label,
)
) {
toggleIsListOpen(false);
}
@@ -120,7 +124,9 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
if (value === "") {
const lastSelectedOption =
selectedOptions[selectedOptions.length - 1];
removeSelectedOption(lastSelectedOption);
if (lastSelectedOption) {
removeSelectedOption(lastSelectedOption);
}
}
} else if (e.key === "ArrowDown") {
// Check that cursor position is at the end of the input field,
@@ -1,15 +1,16 @@
import React from "react";
import { Chips } from "../../../chips";
import { useInputContext } from "../Input/inputContext";
import { ComboboxOption } from "../types";
import { useSelectedOptionsContext } from "./selectedOptionsContext";
interface SelectedOptionsProps {
selectedOptions?: string[];
selectedOptions?: ComboboxOption[];
size?: "medium" | "small";
children: React.ReactNode;
}
const Option = ({ option }: { option: string }) => {
const Option = ({ option }: { option: ComboboxOption }) => {
const { isMultiSelect, removeSelectedOption } = useSelectedOptionsContext();
const { focusInput } = useInputContext();
@@ -21,11 +22,13 @@ const Option = ({ option }: { option: string }) => {
if (!isMultiSelect) {
return (
<div className="navds-combobox__selected-options--no-bg">{option}</div>
<div className="navds-combobox__selected-options--no-bg">
{option.label}
</div>
);
}
return <Chips.Removable onClick={onClick}>{option}</Chips.Removable>;
return <Chips.Removable onClick={onClick}>{option.label}</Chips.Removable>;
};
const SelectedOptions: React.FC<SelectedOptionsProps> = ({
@@ -37,7 +40,7 @@ const SelectedOptions: React.FC<SelectedOptionsProps> = ({
<Chips className="navds-combobox__selected-options" size={size}>
{selectedOptions.length
? selectedOptions.map((option, i) => (
<Option key={option + i} option={option} />
<Option key={option.label + i} option={option} />
))
: []}
{children}
@@ -7,19 +7,20 @@ import React, {
} from "react";
import { usePrevious } from "../../../util/hooks";
import { useInputContext } from "../Input/inputContext";
import { isInList } from "../combobox-utils";
import { useCustomOptionsContext } from "../customOptionsContext";
import { ComboboxProps, MaxSelected } from "../types";
import { ComboboxOption, ComboboxProps, MaxSelected } from "../types";
type SelectedOptionsContextType = {
addSelectedOption: (option: string) => void;
addSelectedOption: (option: ComboboxOption) => void;
isMultiSelect?: boolean;
removeSelectedOption: (option: string) => void;
prevSelectedOptions?: string[];
selectedOptions: string[];
removeSelectedOption: (option: ComboboxOption) => void;
prevSelectedOptions?: ComboboxOption[];
selectedOptions: ComboboxOption[];
maxSelected?: MaxSelected & { isLimitReached: boolean };
setSelectedOptions: (any) => void;
toggleOption: (
option: string,
option: ComboboxOption,
event: React.KeyboardEvent | React.PointerEvent,
) => void;
};
@@ -35,13 +36,8 @@ export const SelectedOptionsProvider = ({
children: any;
value: Pick<
ComboboxProps,
| "allowNewValues"
| "isMultiSelect"
| "options"
| "selectedOptions"
| "onToggleSelected"
| "maxSelected"
>;
"allowNewValues" | "isMultiSelect" | "onToggleSelected" | "maxSelected"
> & { options: ComboboxOption[]; selectedOptions?: ComboboxOption[] };
}) => {
const { clearInput, focusInput } = useInputContext();
const {
@@ -58,7 +54,9 @@ export const SelectedOptionsProvider = ({
options,
maxSelected,
} = value;
const [internalSelectedOptions, setSelectedOptions] = useState<string[]>([]);
const [internalSelectedOptions, setSelectedOptions] = useState<
ComboboxOption[]
>([]);
const selectedOptions = useMemo(
() =>
externalSelectedOptions ?? [...customOptions, ...internalSelectedOptions],
@@ -66,10 +64,8 @@ export const SelectedOptionsProvider = ({
);
const addSelectedOption = useCallback(
(option: string) => {
const isCustomOption = !options
.map((opt) => opt.toLowerCase())
.includes(option?.toLowerCase?.());
(option: ComboboxOption) => {
const isCustomOption = !isInList(option, options);
if (isCustomOption) {
allowNewValues && addCustomOption(option);
!isMultiSelect && setSelectedOptions([]);
@@ -82,7 +78,7 @@ export const SelectedOptionsProvider = ({
setSelectedOptions([option]);
setCustomOptions([]);
}
onToggleSelected?.(option, true, isCustomOption);
onToggleSelected?.(option.value, true, isCustomOption);
},
[
addCustomOption,
@@ -95,8 +91,8 @@ export const SelectedOptionsProvider = ({
);
const removeSelectedOption = useCallback(
(option: string) => {
const isCustomOption = customOptions.includes(option);
(option: ComboboxOption) => {
const isCustomOption = isInList(option, customOptions);
if (isCustomOption) {
removeCustomOption(option);
} else {
@@ -106,14 +102,17 @@ export const SelectedOptionsProvider = ({
),
);
}
onToggleSelected?.(option, false, isCustomOption);
onToggleSelected?.(option.value, false, isCustomOption);
},
[customOptions, onToggleSelected, removeCustomOption],
);
const toggleOption = useCallback(
(option: string, event: React.KeyboardEvent | React.PointerEvent) => {
if (selectedOptions.includes(option)) {
(
option: ComboboxOption,
event: React.KeyboardEvent | React.PointerEvent,
) => {
if (isInList(option.value, selectedOptions)) {
removeSelectedOption(option);
} else {
addSelectedOption(option);
@@ -130,7 +129,7 @@ export const SelectedOptionsProvider = ({
],
);
const prevSelectedOptions = usePrevious<string[]>(selectedOptions);
const prevSelectedOptions = usePrevious<ComboboxOption[]>(selectedOptions);
const isLimitReached =
!!maxSelected?.limit && selectedOptions.length >= maxSelected.limit;
@@ -0,0 +1,67 @@
import {
isInList,
mapToComboboxOptionArray,
toComboboxOption,
} from "./combobox-utils";
const list = [
{ label: "Hjelpemidler", value: "HJE" },
{ label: "Oppfølging", value: "OPP" },
{ label: "Sykepenger", value: "SYK" },
{ label: "Sykemelding", value: "SYM" },
];
describe("isInList", () => {
test("finds a string value in a list of ComboboxOptions", () => {
expect(isInList("Oppfølging", list)).toBe(true);
expect(isInList("SYM", list)).toBe(true);
expect(isInList("Arbeidsavklaringspenger", list)).toBe(false);
expect(isInList("AAP", list)).toBe(false);
});
test("finds a ComboboxOption in a list of ComboboxOptions", () => {
expect(isInList({ label: "Oppfølging", value: "OPP" }, list)).toBe(true);
expect(isInList({ label: "Sykemelding", value: "SYM" }, list)).toBe(true);
expect(
isInList({ label: "Arbeidsavklaringspenger", value: "AAP" }, list),
).toBe(false);
expect(
isInList({ label: "Arbeidsavklaringspenger", value: "AAP" }, list),
).toBe(false);
});
test("returns false for ComboboxOptions which do not match both label and value", () => {
expect(isInList({ label: "Oppfølging", value: "SYM" }, list)).toBe(false);
expect(isInList({ label: "Sykemelding", value: "OPP" }, list)).toBe(false);
});
});
describe("mapToComboboxOptionArray", () => {
test("maps an array of strings to an array of ComboboxOptions", () => {
const stringArray = ["Hjelpemidler", "Oppfølging", "Sykepenger"];
const comboboxOptions = [
{ label: "Hjelpemidler", value: "Hjelpemidler" },
{ label: "Oppfølging", value: "Oppfølging" },
{ label: "Sykepenger", value: "Sykepenger" },
];
expect(mapToComboboxOptionArray(stringArray)).toEqual(comboboxOptions);
});
test("does not change an array of ComboboxOptions", () => {
const comboboxOptions = [
{ label: "Hjelpemidler", value: "Hjelpemidler" },
{ label: "Oppfølging", value: "Oppfølging" },
{ label: "Sykepenger", value: "Sykepenger" },
];
expect(mapToComboboxOptionArray(comboboxOptions)).toEqual(comboboxOptions);
});
});
describe("toComboboxOption", () => {
test("creates a ComboboxOption from a string", () => {
expect(toComboboxOption("Hjelpemidler")).toEqual({
label: "Hjelpemidler",
value: "Hjelpemidler",
});
});
});
@@ -0,0 +1,32 @@
import { ComboboxOption } from "./types";
/**
*
* @param option ComboboxOption will be compared by both label and value, while a string option is checked if it matches either the label or value in the list.
* @param list
*/
const isInList = (option: ComboboxOption | string, list: ComboboxOption[]) => {
if (typeof option === "string") {
return list.some(
(listItem) => listItem.label === option || listItem.value === option,
);
} else {
return list.some(
(listItem) =>
listItem.label === option.label && listItem.value === option.value,
);
}
};
const toComboboxOption = (value: string): ComboboxOption => ({
label: value,
value,
});
const mapToComboboxOptionArray = (options?: string[] | ComboboxOption[]) => {
return options?.map((option: string | ComboboxOption) =>
typeof option === "string" ? toComboboxOption(option) : option,
);
};
export { isInList, mapToComboboxOptionArray, toComboboxOption };
@@ -1,6 +1,6 @@
import { Meta, StoryFn, StoryObj } from "@storybook/react";
import { expect, fn, userEvent, within } from "@storybook/test";
import React, { useId, useMemo, useRef, useState } from "react";
import React, { useMemo, useRef, useState } from "react";
import { Chips } from "../../chips";
import { TextField } from "../textfield";
import { ComboboxProps, UNSAFE_Combobox } from "./index";
@@ -30,10 +30,10 @@ const options = [
"grape fruit",
];
export const Default: StoryFunction = (props) => {
const id = useId();
return <UNSAFE_Combobox {...props} id={id} />;
};
export const Default: StoryFunction = (props) => (
<UNSAFE_Combobox {...props} id="combobox" />
);
Default.args = {
options,
label: "Hva er dine favorittfrukter?",
@@ -57,10 +57,9 @@ Default.argTypes = {
};
export const MultiSelect: StoryFunction = (props) => {
const id = useId();
return (
<UNSAFE_Combobox
id={id}
id="combobox-with-multiselect"
label="Komboboks - velg flere"
options={props.options}
isMultiSelect={props.isMultiSelect}
@@ -75,11 +74,55 @@ MultiSelect.args = {
size: "medium",
};
export const MultiSelectWithComplexOptions: StoryFunction = (props) => {
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
return (
<>
<UNSAFE_Combobox
{...props}
options={props.options.map((option) => ({
...option,
label: `${option.label} [${option.value}]`,
}))}
id="combobox-with-complex-options"
label="Velg temakoder"
allowNewValues
onToggleSelected={(value, isSelected) =>
isSelected
? setSelectedOptions([...selectedOptions, value])
: setSelectedOptions(selectedOptions.filter((o) => o !== value))
}
selectedOptions={selectedOptions}
/>
</>
);
};
MultiSelectWithComplexOptions.args = {
options: [
{ label: "Hjelpemidler", value: "HJE" },
{ label: "Oppfølging", value: "OPP" },
{ label: "Sykepenger", value: "SYK" },
{ label: "Sykemelding", value: "SYM" },
{ label: "Foreldre- og svangerskapspenger", value: "FOR" },
{ label: "Arbeidsavklaringspenger", value: "AAP" },
{ label: "Uføretrygd", value: "UFO" },
{ label: "Pensjon", value: "PEN" },
{ label: "Barnetrygd", value: "BAR" },
{ label: "Kontantstøtte", value: "KON" },
{ label: "Bostøtte", value: "BOS" },
{ label: "Barnebidrag", value: "BBI" },
{ label: "Bidragsforskudd", value: "BIF" },
{ label: "Grunn- og hjelpestønad", value: "GRU" },
],
isMultiSelect: true,
size: "medium",
};
export const WithAddNewOptions: StoryFunction = (props) => {
const id = useId();
return (
<UNSAFE_Combobox
id={id}
id="combobox-with-add-new-options"
label="Komboboks med mulighet for å legge til nye verdier"
options={props.options}
allowNewValues={props.allowNewValues}
@@ -95,10 +138,9 @@ WithAddNewOptions.args = {
};
export const MultiSelectWithAddNewOptions: StoryFunction = (props) => {
const id = useId();
return (
<UNSAFE_Combobox
id={id}
id="combobox-with-multiselect-and-add-new-options"
isMultiSelect={props.isMultiSelect}
label="Multiselect komboboks med mulighet for å legge til nye verdier"
options={props.options}
@@ -120,7 +162,6 @@ export const MultiSelectWithExternalChips: StoryFn<{
}> = (props) => {
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
const [value, setValue] = useState("");
const id = useId();
const toggleSelected = (option: string) =>
selectedOptions.includes(option)
@@ -142,6 +183,7 @@ export const MultiSelectWithExternalChips: StoryFn<{
</Chips>
)}
<UNSAFE_Combobox
id="combobox-with-external-chips"
options={props.options}
selectedOptions={selectedOptions}
onToggleSelected={(option) => toggleSelected(option)}
@@ -154,7 +196,6 @@ export const MultiSelectWithExternalChips: StoryFn<{
}
label="Komboboks"
size="medium"
id={id}
shouldShowSelectedOptions={false}
/>
</>
@@ -166,19 +207,16 @@ MultiSelectWithExternalChips.args = {
options,
};
export const Loading: StoryFunction = (props) => {
const id = useId();
return (
<UNSAFE_Combobox
id={id}
label="Komboboks (laster)"
options={[]}
selectedOptions={[]}
isListOpen={props.isListOpen}
isLoading={props.isLoading}
/>
);
};
export const Loading: StoryFunction = (props) => (
<UNSAFE_Combobox
id="combobox-with-loading-indicator"
label="Komboboks (laster)"
options={[]}
selectedOptions={[]}
isListOpen={props.isListOpen}
isLoading={props.isLoading}
/>
);
Loading.args = {
isLoading: true,
@@ -186,11 +224,10 @@ Loading.args = {
};
export const ComboboxWithNoHits: StoryFunction = (props) => {
const id = useId();
const [value, setValue] = useState(props.value);
return (
<UNSAFE_Combobox
id={id}
id="combobox-with-no-hits"
label="Komboboks (uten søketreff)"
options={props.options}
value={value}
@@ -210,7 +247,6 @@ export const Controlled: StoryFn<{
options: string[];
initialSelectedOptions: string[];
}> = (props) => {
const id = useId();
const [value, setValue] = useState(props.value);
const [selectedOptions, setSelectedOptions] = useState(
props.initialSelectedOptions,
@@ -238,7 +274,7 @@ export const Controlled: StoryFn<{
<br />
<UNSAFE_Combobox
label="Hva er dine favorittfrukter?"
id={id}
id="combobox-controlled"
filteredOptions={filteredOptions}
isMultiSelect
options={props.options}
@@ -292,7 +328,6 @@ export const ComboboxSizes = () => (
);
export const MaxSelectedOptions: StoryFunction = () => {
const id = useId();
const [value, setValue] = useState<string | undefined>("");
const [selectedOptions, setSelectedOptions] = useState([
options[0],
@@ -301,7 +336,7 @@ export const MaxSelectedOptions: StoryFunction = () => {
const comboboxRef = useRef<HTMLInputElement>(null);
return (
<UNSAFE_Combobox
id={id}
id="combobox-with-max-selected-options"
label="Komboboks med begrenset antall valg"
options={options}
maxSelected={{ limit: 2 }}
@@ -3,7 +3,7 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React, { useId } from "react";
import { act } from "react-dom/test-utils";
import { describe, expect, test } from "vitest";
import { describe, expect, test, vi } from "vitest";
import { UNSAFE_Combobox } from "./index";
const options = [
@@ -123,4 +123,35 @@ describe("Combobox state-handling", () => {
await screen.findByRole("option", { name: "banana" }),
).toBeInTheDocument();
});
test("Should handle complex options with label and value", async () => {
const onToggleSelected = vi.fn();
render(
<App
options={[
{ label: "Hjelpemidler [HJE]", value: "HJE" },
{ label: "Oppfølging [OPP]", value: "OPP" },
{ label: "Sykepenger [SYK]", value: "SYK" },
{ label: "Sykemelding [SYM]", value: "SYM" },
]}
onToggleSelected={onToggleSelected}
/>,
);
expect(screen.getByRole("combobox")).toBeInTheDocument();
const bananaOption = screen.getByRole("option", {
name: "Hjelpemidler [HJE]",
selected: false,
});
await act(async () => {
await userEvent.click(bananaOption);
});
expect(onToggleSelected).toHaveBeenCalledWith("HJE", true, false);
expect(
screen.getByRole("option", {
name: "Hjelpemidler [HJE]",
selected: true,
}),
).toBeInTheDocument();
});
});
@@ -1,11 +1,12 @@
import React, { createContext, useCallback, useContext, useState } from "react";
import { useInputContext } from "./Input/inputContext";
import { ComboboxOption } from "./types";
type CustomOptionsContextType = {
customOptions: string[];
removeCustomOption: (option: string) => void;
addCustomOption: (option: string) => void;
setCustomOptions: React.Dispatch<React.SetStateAction<string[]>>;
customOptions: ComboboxOption[];
removeCustomOption: (option: ComboboxOption) => void;
addCustomOption: (option: ComboboxOption) => void;
setCustomOptions: React.Dispatch<React.SetStateAction<ComboboxOption[]>>;
};
const CustomOptionsContext = createContext<CustomOptionsContextType>(
@@ -19,14 +20,14 @@ export const CustomOptionsProvider = ({
children: any;
value: { isMultiSelect?: boolean };
}) => {
const [customOptions, setCustomOptions] = useState<string[]>([]);
const [customOptions, setCustomOptions] = useState<ComboboxOption[]>([]);
const { focusInput } = useInputContext();
const { isMultiSelect } = value;
const removeCustomOption = useCallback(
(option: string) => {
(option: ComboboxOption) => {
setCustomOptions((prevCustomOptions) =>
prevCustomOptions.filter((o) => o !== option),
prevCustomOptions.filter((o) => o.label !== option.label),
);
focusInput();
},
@@ -34,7 +35,7 @@ export const CustomOptionsProvider = ({
);
const addCustomOption = useCallback(
(option: string) => {
(option: ComboboxOption) => {
if (isMultiSelect) {
setCustomOptions((prevOptions) => [...prevOptions, option]);
} else {
+34 -22
View File
@@ -1,6 +1,21 @@
import React, { ChangeEvent, InputHTMLAttributes } from "react";
import { FormFieldProps } from "../useFormField";
/**
* A more complex version of options for the Combobox.
* Used for separating the label and the value of the option.
*/
export type ComboboxOption = {
/**
* The label to display in the dropdown list
*/
label: string;
/**
* The programmatic value of the option, for use internally. Will be returned from onToggleSelected.
*/
value: string;
};
export type MaxSelected = {
/**
* The limit for maximum selected options
@@ -16,23 +31,23 @@ export interface ComboboxProps
extends FormFieldProps,
Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "onChange" | "value"> {
/**
* Combobox label
* Combobox label.
*/
label: React.ReactNode;
/**
* List of options to use for autocompletion
* List of options
*/
options: string[];
options: string[] | ComboboxOption[];
/**
* If enabled, adds an option to add the value of the input as an option whenever there are no options matching the value.
*/
allowNewValues?: boolean;
/**
* If "true" adds a button to clear the value in the input field
* If `true` adds a button to clear the value in the input field
*/
clearButton?: boolean;
/**
* Custom name for the clear button. Requires "clearButton" to be "true".
* Custom name for the clear button. Requires `clearButton` to be `true`.
*
* @default "Tøm"
*/
@@ -42,7 +57,7 @@ export interface ComboboxProps
* If provided, this overrides the internal search logic in the component.
* Useful for e.g. searching on a server or when overriding the search algorithm to search for synonyms or similar.
*/
filteredOptions?: string[];
filteredOptions?: string[] | ComboboxOption[];
/**
* Optionally hide the label visually.
* Not recommended, but can be considered for e.g. search fields in the top menu.
@@ -59,13 +74,13 @@ export interface ComboboxProps
*/
isListOpen?: boolean;
/**
* Set to "true" when doing an async search and waiting for new filteredOptions.
* Set to `true` when doing an async search and waiting for new filteredOptions.
*
* Will show a spinner in the dropdown and announce to screen readers that it is loading.
*/
isLoading?: boolean;
/**
* Set to "true" to allow multiple selections
* Set to `true` to allow multiple selections.
*
* This will display selected values as a list of Chips in front of the input field, instead of a selection replacing the value of the input.
*
@@ -75,29 +90,26 @@ export interface ComboboxProps
* Callback function triggered whenever the value of the input field is triggered.
*
* @param event
* @returns
*/
onChange?: (
event: ChangeEvent<HTMLInputElement> | null,
value?: string,
) => void;
/**
* Callback function triggered whenever the input field is cleared
* Callback function triggered whenever the input field is cleared.
*
* @param event
* @returns
*/
onClear?: (event: React.PointerEvent | React.KeyboardEvent) => void;
/**
* Callback function triggered whenever an option is selected or de-selected
* Callback function triggered whenever an option is selected or de-selected.
*
* @param option
* @param isSelected - Whether the option has been selected or unselected
* @param isCustomOption - Whether the option comes from user input, instead of from the list
* @returns
* @param option The option value
* @param isSelected Whether the option has been selected or unselected
* @param isCustomOption Whether the option comes from user input, instead of from the list
*/
onToggleSelected?: (
option: string,
option: ComboboxOption["value"],
isSelected: boolean,
isCustomOption: boolean,
) => void;
@@ -107,31 +119,31 @@ export interface ComboboxProps
* Use this prop when controlling the selected state outside for the component,
* e.g. for a filter, where options can be toggled elsewhere/programmatically.
*/
selectedOptions?: string[];
selectedOptions?: string[] | ComboboxOption[];
/**
* Options for the maximum number of selected options.
*/
maxSelected?: MaxSelected;
/**
* Set to "true" to enable inline autocomplete.
* Set to `true` to enable inline autocomplete.
*
* @default false
*/
shouldAutocomplete?: boolean;
/**
* When set to "true" displays selected options as Chips before the input field
* When set to `true` displays selected options as Chips before the input field
*
* @default true
*/
shouldShowSelectedOptions?: boolean;
/**
* When set to "true" displays the toggle button for opening/closing the dropdown list
* When set to `true` displays the toggle button for opening/closing the dropdown list
*
* @default true
*/
toggleListButton?: boolean;
/**
* Custom name for the toggle list-button. Requires "toggleListButton" to be "true".
* Custom name for the toggle list-button. Requires "toggleListButton" to be `true`.
*
* @default "Alternativer"
*/
@@ -6,20 +6,20 @@ import ErrorSummaryItem, { ErrorSummaryItemType } from "./ErrorSummaryItem";
export interface ErrorSummaryProps extends HTMLAttributes<HTMLDivElement> {
/**
* Collectipn of ErrorSummary.Item
* Collection of `ErrorSummary.Item`.
*/
children: React.ReactNode;
/**
* Changes padding and font-sizes
* Changes padding and font-sizes.
* @default "medium"
*/
size?: "medium" | "small";
/**
* Heading above links
* Heading above links.
*/
heading?: React.ReactNode;
/**
* Allows setting a different HTML h-tag
* Allows setting a different HTML h-tag.
* @default "h2"
*/
headingTag?: React.ElementType<any>;
@@ -30,7 +30,7 @@ interface ErrorSummaryComponent
ErrorSummaryProps & React.RefAttributes<HTMLDivElement>
> {
/**
* Link to error
* Link to error.
*
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*
@@ -1,5 +1,6 @@
import { Meta } from "@storybook/react";
import { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { VStack } from "../../layout/stack";
import { ErrorSummary } from "./ErrorSummary";
export default {
@@ -18,9 +19,14 @@ export default {
},
},
},
} as Meta;
parameters: {
chromatic: { disable: true },
},
} satisfies Meta<typeof ErrorSummary>;
export const Default = {
type Story = StoryObj<typeof ErrorSummary>;
export const Default: Story = {
render: (props) => (
<ErrorSummary
heading="Feiloppsummering komponent"
@@ -35,11 +41,41 @@ export const Default = {
),
};
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>
);
export const Small: Story = {
render: () => (
<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>
),
};
export const Chromatic: Story = {
render: () => (
<VStack gap="4">
<div>
<h2>Default</h2>
<ErrorSummary 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>
</div>
<div>
<h2>Small</h2>
<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>
</div>
</VStack>
),
parameters: {
chromatic: { disable: false },
},
};
@@ -1,5 +1,6 @@
import { Meta } from "@storybook/react";
import { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { VStack } from "../../layout/stack";
import TextField from "../textfield/TextField";
import { Fieldset } from "./index";
@@ -26,7 +27,12 @@ export default {
type: "boolean",
},
},
} as Meta;
parameters: {
chromatic: { disable: true },
},
} satisfies Meta<typeof Fieldset>;
type Story = StoryObj<typeof Fieldset>;
const content = (
<>
@@ -35,88 +41,117 @@ const content = (
</>
);
export const Default = {
render: (props) => {
return (
<Fieldset legend="Mollit eiusmod" {...props}>
{content}
</Fieldset>
);
},
const contentWithError = (
<>
<TextField label="Textfield label" hideLabel error="Må være fylt ut" />
<TextField label="Textfield label" hideLabel />
</>
);
export const Default: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
errorPropagation: true,
children: content,
},
};
export const Small = () => (
<Fieldset
size="small"
legend="Mollit eiusmod"
description="Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure."
>
{content}
</Fieldset>
);
export const Small: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
errorPropagation: true,
children: content,
size: "small",
},
};
export const Description = () => (
<Fieldset
legend="Mollit eiusmod"
description="Esse cupidatat reprehenderit est culpa consectetur sit dolor esse."
>
<TextField
label="Textfield label"
description="Amet quis cillum incididunt "
/>
<TextField
label="Textfield label"
description="Enim et occaecat voluptate labore sit do exercitation laborum non "
/>
</Fieldset>
);
export const ErrorPropagation: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
errorPropagation: false,
children: contentWithError,
},
};
export const ErrorPropagation = () => (
<Fieldset
legend="Mollit eiusmod"
error="Fieldsett error"
errorPropagation={false}
>
<TextField label="Textfield label" hideLabel error="Må være fylt ut" />
<TextField label="Textfield label" hideLabel />
</Fieldset>
);
export const Error: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
children: content,
error: "Laborum officia nisi aliqua esse minim in amet.",
},
};
export const Error = () => (
<div className="colgap">
<Fieldset
legend="Mollit eiusmod"
error="Laborum officia nisi aliqua esse minim in amet."
>
{content}
</Fieldset>
<Fieldset
size="small"
legend="Mollit eiusmod"
error="Laborum officia nisi aliqua esse minim in amet."
>
{content}
</Fieldset>
</div>
);
export const Disabled: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
children: content,
disabled: true,
},
};
export const Disabled = () => (
<div className="colgap">
<Fieldset legend="Mollit eiusmod" disabled>
{content}
</Fieldset>
<Fieldset size="small" legend="Mollit eiusmod" disabled>
{content}
</Fieldset>
</div>
);
export const HideLegend: Story = {
args: {
legend: "Mollit eiusmod",
description:
"Do ullamco amet mollit labore tempor minim cupidatat dolore voluptate velit irure.",
children: content,
hideLegend: true,
},
};
export const HideLegend = () => (
<Fieldset legend="Mollit eiusmod" hideLegend>
{content}
</Fieldset>
);
export const Chromatic: Story = {
render: () => {
return (
<VStack gap="4">
<div>
<h2>Default</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...Default.args} />
</div>
<div>
<h2>Small</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...Small.args} />
</div>
<div>
<h2>ErrorPropagation</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...ErrorPropagation.args} />
</div>
<div>
<h2>Error</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...Error.args} />
</div>
<div>
<h2>Error small</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...Error.args} size="small" />
</div>
<div>
<h2>Disabled</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...Disabled.args} />
</div>
<div>
<h2>HideLegend</h2>
{/* @ts-expect-error Args are Partial here */}
<Fieldset {...HideLegend.args} />
</div>
</VStack>
);
},
parameters: {
chromatic: { disable: false },
},
};
@@ -0,0 +1,9 @@
import { createContext } from "../../util/create-context";
import { ComponentTranslation } from "./i18n/i18n.types";
export type FileUploadLocaleContextProps = {
translations?: ComponentTranslation<"FileUpload">;
};
export const [FileUploadLocaleContextProvider, useFileUploadTranslation] =
createContext<FileUploadLocaleContextProps>();
@@ -0,0 +1,142 @@
import cl from "clsx";
import React, { HTMLAttributes, forwardRef } from "react";
import { FileUploadLocaleContextProvider } from "./FileUpload.context";
import { ComponentTranslation } from "./i18n/i18n.types";
import Trigger from "./parts/Trigger";
import Dropzone from "./parts/dropzone/Dropzone";
import Item from "./parts/item/Item";
interface FileUploadProps extends HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
/**
* i18n-API for customizing texts and labels
*/
translations?: ComponentTranslation<"FileUpload">;
}
interface FileUploadComponent
extends React.ForwardRefExoticComponent<
FileUploadProps & React.RefAttributes<HTMLDivElement>
> {
/**
* Framed area to drag-n-drop files, upload files with button-click or copy-paste.
* @example
* Single file
* ```jsx
* <FileUpload.Dropzone
* label="Last opp fil"
* multiple={false}
* onSelect={onSelect}
* />
* ```
*
* @example
* Multiple files
* ```jsx
* <FileUpload.Dropzone
* label="Last opp fil"
* multiple={true}
* onSelect={onSelect}
* />
* ```
*
* @example
* Error
* ```jsx
* <FileUpload.Dropzone
* label="Last opp filer"
* onSelect={onSelect}
* error={error}
* />
* ```
*/
Dropzone: typeof Dropzone;
/**
* Displays a file with status, file size, action and error message.
* @example
* Single
* ```jsx
* <FileUpload.Item file={file} status="uploading" />
* ```
*
* @example
* Multiple items can be semantically grouped as a list.
* ```jsx
* <FileUpload>
* <VStack gap="4" as="ul">
* <FileUpload.Item as="li" file={file} />
* <FileUpload.Item as="li" file={file2} />
* <FileUpload.Item as="li" file={file3} status="uploading" />
* </VStack>
* </FileUpload>
* ```
*
* @example
* Custom file object
* ```jsx
* <FileUpload.Item file={{ name: "fileName.pdf", size: 1_048_576 }} />
* ```
*
* @example
* Error
* ```jsx
* <FileUpload.Item file={file} error="Something went wrong" />
* ```
*
* @example
* Status & actions
* ```jsx
* <FileUpload.Item file={file} status="uploading" />
* <FileUpload.Item file={file} status="downloading" />
* <FileUpload.Item file={file} button={{ action:"retry", onClick:... }} />
* <FileUpload.Item file={file} button={{ action:"delete", onClick:... }} />
* ```
*/
Item: typeof Item;
/**
* Wrapper for a button to trigger file select.
* @example
* ```jsx
* <FileUpload.Trigger onSelect={...}>
* <Button variant="secondary">Last opp filer</Button>
* </FileUpload.Trigger>
* ```
*/
Trigger: typeof Trigger;
}
/**
* A set of components used to upload and display files.
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/fileupload)
* @example Dropzone
* ```jsx
* <FileUpload.Dropzone />
* ```
*
* @example
* Items
* ```jsx
* <VStack gap="4" as="ul">
* <FileUpload.Item as="li" file={myFile} />
* <FileUpload.Item as="li" file={mySecondFile} />
* </VStack>
* ```
*/
export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(
({ children, className, translations, ...rest }: FileUploadProps, ref) => {
return (
<FileUploadLocaleContextProvider translations={translations}>
<div ref={ref} {...rest} className={cl("navds-file-upload", className)}>
{children}
</div>
</FileUploadLocaleContextProvider>
);
},
) as FileUploadComponent;
FileUpload.Dropzone = Dropzone;
FileUpload.Item = Item;
FileUpload.Trigger = Trigger;
export default FileUpload;
Loaded 100 of 424 files, more files were not shown because too many files have changed in this diff. Show more