Compare commits

...
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] 6324d63745 Version Packages (#3226)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-18 14:20:08 +02:00
Halvor Haugan debfb3fd15 [Translations] Chips.Removable: Remove translations-prop (partly reverts #3232) (#3255) 2024-10-18 14:15:23 +02:00
Ken a3e8457baf change: Reduced height for actionemu examples (#3246) 2024-10-18 12:45:36 +02:00
Halvor Haugan 0560f84acd ➖ Remove faker (#3254) 2024-10-18 12:20:35 +02:00
Halvor Haugan 663dfbe8a4 🆙 Upgrade some dependencies (#3247) 2024-10-18 12:02:05 +02:00
Halvor Haugan 536f088b97 List.Item: Center icon horizontally inside it's container (#3253) 2024-10-18 11:55:46 +02:00
Ken f659599787 [ActionMenu] Removed defaultOpen-prop, moved rootElement, added Modal-support (#3240)
* change: Updated actionmenu examples

* change: Removed flipAlignment prop for better alignment

* change: Removed defaultOpen-prop from ActionMenu

* change: Moved rootElement prop to component root

* change: Added support for use inside Modal

* change: Use MenuPortalProps instead of Pick

* change: Revert to using Pick for prop inheritance

* change: Menucontext uses required definition of rootElement
2024-10-17 16:19:27 +02:00
Ken ed34bc034a [Darkside]: Move text-contrast tokens into semantic groups 2024-10-17 14:26:20 +02:00
Ken 2a2028fd95 [BasePrimitive] Hide className from basePrimitive type (#3241)
* change: Hide className from basePrimitive type

* change: Re-order baseprimitive types (#3242)
2024-10-17 13:04:50 +02:00
Ken 386145d63a [Darkside] Made breakpoints and font-tokens avaliable in css output
[Darkside] Added breakpoints to token-config
[Darkside] Added font to token-config
2024-10-17 11:51:54 +02:00
Ken 1ed809c872 [Actionmenu] Updated examples for docs, removed flipAlignment-prop (#3236)
* change: Updated actionmenu examples

* change: Removed flipAlignment prop for better alignment
2024-10-17 09:49:56 +02:00
Ken e2359ddf33 [Darkside] Bumped role-raised tokens on darkmode one tick on the scale, updated darkmode input token 2024-10-16 19:45:15 +02:00
Ken 069e3346ec [darkside] Added spacing and border-radius token support for JS/TS file-format 2024-10-16 14:34:06 +02:00
Halvor Haugan c941af9de5 Refactor examples of Vstack and HStack (#3244) 2024-10-16 14:15:08 +02:00
Halvor Haugan a273a22172 Adjust examples of VStack and HStack (#3243) 2024-10-16 13:31:09 +02:00
Ken 0e94031f1e [Darkside] Added alpha-variant to border-subtle 2024-10-16 13:01:10 +02:00
Ken 64a9762286 change: Updated exports to make docgen work (#3235) 2024-10-16 09:29:39 +02:00
Ken a05dbadfa4 [darkside]: Added support for darkside views in chromatic (#3239) 2024-10-15 16:55:05 +02:00
Halvor Haugan 7c468b18d1 Adjust dates in Date/MonthPicker examples (#3238) 2024-10-15 16:35:45 +02:00
Ken cfcd78a450 [sanity] Grunnleggende now has access to props block 2024-10-15 15:40:45 +02:00
Halvor Haugan 9e677697a7 [aksel.nav.no] Fix copy-pasting snippets in Firefox 2024-10-15 12:15:24 +02:00
Halvor Haugan 8a8bf0a274 [aksel.nav.no] Adjust list of outdated articles (#3234) 2024-10-15 10:28:03 +02:00
Halvor Haugan 1e40d96510 Chips.Removable: Added new i18n API (#3232) 2024-10-15 09:55:30 +02:00
Ken db56141b2e [Darkside] Updated CSS-readme with syntax hints 2024-10-15 09:43:14 +02:00
Ken ec351ba27f [Darkside] Added CSS-resets, utilities, fonts ++ to darkside css
[Darkside] Removed unused 'baseline' css
[Referansesider] Updated css-import to layer 'framework'
2024-10-14 22:32:39 +02:00
Ken 2e98a09540 [Linting] Update @typescript-eslint/eslint-plugin to v8 (#3222)
* linting: update ts eslint package to v8

* linting: Update ts eslint package to v8

* change: removed redundant off-toggle of native eslint rules

* change: update interface declaration
2024-10-14 16:43:46 +02:00
Halvor Haugan 2d438e1d37 [Translations] Alert: i18n support (#3231) 2024-10-14 16:42:41 +02:00
Ken 8b373e8b80 bug: Use relative units instead of tokens for storybook markdown 2024-10-14 16:21:32 +02:00
KenandHalvor Haugan 179eb51221 [Darkside] Added development environment for new CSS (#3233)
* change: Added stoybook decorators for darkside toggle

* feat: Added dynamic CSS-loading based on mode

* memo: Added readme for new CSS

* change: Added basic body styles for darkside, including fonts

* change: Added loading of new CSS to referansesider

* bug: Removed old preview file in storybook

* change: Added utf-8 to darkside css

* change: Updated darkside css readme with new instructions

* change: Removed old bg style on wrapper

* Update .storybook/preview.jsx

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

* change: Refactored theme decorator

* Update .storybook/preview.jsx

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

* bug: Re-added return

* change: themedecorator -> modedecorator

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-14 16:12:03 +02:00
Halvor Haugan d98624618c [Translations] Accordion: Remove redundant icon title (#3230) 2024-10-14 15:36:20 +02:00
Julian Nymark ecc501a0a8 Referansesider: dagoversikten tweaks to table, remove Timeline 2024-10-14 11:49:51 +02:00
Ken fec3b12d5f [Darkside] Added alpha-scale for global and moderate
[darkside] Updated naming transform to support alpha-tokens
[darkside] Updated input-token to use opacity
[darkside] Added alpha-tokens to global config
[darkside] Now auto-generates the alpha-values
[darkside] Added semantic role tokens using alpha-values
bug: removed tempfile added by vitest
2024-10-14 11:36:23 +02:00
KenandHalvor Haugan 8f99d3a102 [Icons] Some icons that should be ignored were fetched and caused exceptions (#3228)
* bug: Some icons that should be ignored were fetched and caused exceptions

* change: Added some comments

* change: Better math for icon-check

* change: Better messaging

* Update @navikt/aksel-icons/config/figma/index.ts

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

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-14 10:19:21 +02:00
Julian Nymark 7fa7719c24 Referansesider: dagoversikten tweaks to MiniTag 2024-10-11 16:41:22 +02:00
Julian Nymark bc1b9fdad9 Referansesider: dagoversikten table placeholder 2024-10-11 16:34:15 +02:00
Julian Nymark 755d75903a Referansesider: dagoversikten correct tokens for borders 2024-10-11 16:13:57 +02:00
Julian Nymark 5ebc71470a Referansesider: dagoversikten completed rightside 2024-10-11 16:03:17 +02:00
Halvor Haugan 17daddab86 Add description to GuidePanel examples (#3224) 2024-10-11 14:14:34 +02:00
20ced9b084 ActionMenu component (#3081)
* 🎨 Setup content

* 🎨 Fungerende popover med story

* 🎨 useDescendant-API kan bruke uten context-error på nesting

* 🐛 Fikset tilfeller der oppdatering av descendant-options ikke ble reflektert i descendants-map

* 🐛 Satte enabled til optional

* bug: import av Portal brakk stories for Popover og Floating

* bug: DescenadantContext er nå riktig typet som undefined

* label: scopet navn for bruk av descendant-provider i dismissablelayer

* memo: Bedre kommentarer

* feat: Sender ikke callbacks hvis layer er disabled

* memo: Formatering og kommentarer

* memo: Kommentarer

* copy: Kopiert over basekomponent

* refactor: Oppdatert imports

* refactor: Oppdatert Primitive bruk

* feat: Støtter forwardRef

* fire: Fjernet Radix import

* feat: Bruker Floating og ikke PopperPrimitive

* type: Bedre typet composeEventHandler

* refactor: Fjernet scopedProps

* feat: Custom RowingFocus

* wip feat: Replaced rowingfocus

* wip feat: Implemented initial focus for rowingfocus

* wip feat: Removed old collection api

* refactor: Use internal Slot component

* refactor: Fixed all scoped context-use

* feat: Fullført migrering til internt API

* test: Setup stories

* bug: Content is added to descendants

* bug: Found issue for nesten descendants

* copy: Kopiert over refactor for useDescendant

* bug: Fikset descendants nesting

* feat: Fikset keyboard nav med nested descendants

* bug: Removed early return

* memo: Kommentarer

* label: Typer

* refactor: Fjernet Root export

* refactor: Fjernet typeahead

* test: Setup story for entryfocus

* refactor: Forenklet FocusScope

* refactor: bedre dokumentert utils

* refactor: Moved utils to separate file

* refactor: Moved slotted-div to separate file

* refactor: Moved MenuContext out to context-file

* 🚚 Moved focus-files

* refactor: Update syntax

* refactor: Moved MenuAnchor to file

* refactor: Moved MenuGroup to file

* refactor: Moved Separator to file

* refactor: Moved Label to file

* refactor: Moved utility-components

* refactor: Flyttet item til mappe

* refactor: Flyttet checkbox

* refactor: Content in separate file

* refactor: Radio i separate filer

* refactor: SubComponents in files

* refactor: Bedre typer

* refactor: Re-written back to single file

* refactor: Fjernet gamle commits

* bug: Fikset import feil og feil extension av type

* refactor: data-attrb fra radix -> aksel

* refactor: Fjernet console.logs

* bug: Fixed import and test-type error

* refactor: Remove unwanted exports

* feat: Re-added ItemIndicator wrapper

* refactor: Updated exports

* refactor: removed focusguards

* refactor: FocusScope now assumes Slot

* refactor: Removed extra tabIndex

* feat: Implemented ItemIndicator and styling for testing

* refactor: Rowing -> Roving

* refactor: Moved Menu-component into overlays-dir

* bug: Fixed import of Slot

* bug: Re-add tabIndex to MenuItem for correct focus-handling

* memo: Rename Tab -> menu

* refactor: removed unwanted exports in story

* Update @navikt/core/react/src/overlays/floating-menu/Menu.stories.tsx

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

* bug: Added missing meta import

* bug: Aligned dash with checkmark

* bug: Circle icon now shows for radio-demo

* bug: keyframes has extra ':', breaking animations

* bug: Re-added ItemIndicator for all TickIcon

* bug: WithLabel story now shows complete dropdown

* feat: Added disabled-styling to submenu

* refactor: Now uses ownerdocument where needed instead of document

* adjustments after review 1

* adjustments after review 2

* refactor: Updated to correct function type syntax

* refactor: renamed floating content propname

* refactor: Consistent destructuring of props

* refactor: rename FocusScope props

* memo: Added explanation for why we use flushEvent

* refactor: added consistent use of composeEventHandlers

* refactor: Force userSelect none style

* demo: Allow canceling opening if pointerdown is released outside of menu

* memo: Better line-dividers

* refactor: removed data-highlighted logic

* Update @navikt/core/react/src/overlays/floating-menu/Menu.tsx

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

* Update @navikt/core/react/src/overlays/floating-menu/Menu.tsx

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

* feat: DropdowmMenu boilerplate

* feat: MenuTrigger & MenuPortal

* feat: MenuContent

* feat: MenuGroup

* feat: lvl-1 components added

* feat: Implement each component

* feat: Implement export

* refactor: Fixeed proptypes for sub

* feat: Implemented trigger

* testing: Updated stories

* feat: Added experimental API 'requireReactelement'

* feat: Implemented content

* feat: Implemented label

* feat: Implemented Item

* feat: Implemented Item

* feat: Implemented group

* feat: Implemented radoigroup

* feat: Implemented radio

* feat: Implemented checkbox

* feat: Implemented sub-trigger

* feat: Implemented sub-content

* feat: Implemented separator classname

* feat: Added cancelable event for pointerdown

* feat: Added dropdown-menu css

* WIP: CSS

* WIP: Added label styling

* feat: Remove checkmarks whe unchecked

* feat: Allow use of 'il' in floating-element

* feat: Implemented semantic tags for items and content

* feat: Added test-markup

* Revert "feat: Implemented semantic tags for items and content"

This reverts commit 6bf473328d.

* Revert "feat: Allow use of 'il' in floating-element"

This reverts commit 5307facbca.

* css: Item active styling

* testing: Setup storybook test framework

* testing: setup basic feature stories

* testing: Un-nested variants

* testing: advanced demos

* feat: Disabled and submenu css

* feat: Added bg on open submenu-trigger

* feat: Added destructive item

* feat: Stop selecting checkbox from closing dropdown

* feat: Use actual radio and checkbox svg for components

* feat: Animate checkbox/radio checked

* feat: Added indeterminate checkbox

* feat: Added indeterminate demo

* feat: added story for shortcuts

* TODOS

* feat: added possibility to close submenus with arrowright when opened to the left of content

* refactor: Better implementation of Shortcuts and aria-keyboardshortcut

* feat: added thin scrollbar

* feat: Added animationsupport for all directions when opening dropdown

* refactor: Cleaned up CSS

* testing: Added chromatic stories for dropdown

* refactor: Dropdowm CSS -> Actionmenu CSS

* refactor: Rename files

* refactor: Move file to utils

* refactor: Inline Slot button

* refactor: rename react dropdown -> action

* refactor: Solved alignment issue with checkbox/radio and text

* feat: use div instead of removed Menu.Label

* refactor: Better useRef reference

* memo: Updated comments

* memo: removed old comments

* memo: Prop-comments

* memo: JSDOC

* bug: remove shortcut from exports

* refactor: Avoid exposing extra props for content

* refactor: Avoid exposing extra props for subcontent

* refactor: Updated CSS animation name

* feat: Updated exports making ActionMenu avaliable from the library

* memo: Added actionmenu to CSS-mappings

* feat: Added option to use links in actionmenu

* feat: Icon-prop for Item and Subtrigger

* refactor: Separator -> Divider

* feat: Updated danger variant item text color

* feat: Stronger label typings for group

* bug: Fixed edgecase where focus on trigger would close menu

* bug: removed storybook addon omit

* refactor: Used relative units and tokens for spacing/width

* feat: Added max-width, removed nowrap

* bug: removed demo story

* refactor: Removed old scale(1) property from animation

* Update @navikt/core/css/action-menu.css

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

* feat: Added direct import of actionmenu

* Update @navikt/core/react/src/overlays/action-menu/ActionMenu.stories.tsx

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

* Update @navikt/core/react/src/overlays/action-menu/ActionMenu.stories.tsx

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

* Minor adjustments

* Remove unused CSS

* feat: De-select all checkboxes when every box is allready checked in indeterminate

* memo: Removed extra stories

* memo: Removed demo used for testing

* feat: More dynamic max-width with 'min'

* memo: Removed cryptic comments

* feat: Avoid spread in reduce

* refactor: Removed middle-step function to handle radio state update

* refactor: Better css-naming for marker css

* refactor: removed extra unused variables, added double underscore prefix

* refactor: Removed extra onSelect on disabled story

* feat: Actionmenu now proplery handles overflow

* feat: Avoid auto-closing modal

* feat: Menu is now Modal

* feat: Now always focuses trigger when closed

* Update @navikt/core/css/action-menu.css

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

* feat: Fix space auto closing modal

* feat: Fallback to bottom of anchor when no avaliable space

* memo: Added todo

* ,memo: added todos

* memo: changeset

* refactor: Simplified SVG with groups

* [ActionMenu] Remove extra CSS-classes, fixed centering bug in safari (#3194)

* refactor: Remove extra CSS-classes, fixed center bug in safari

* refactor: Removed unused CSS

* [ActionMenu] Sub-menu now uses onClick for opening and closing instead of pointermove (#3193)

* feat: ActionMenu now only opens submeny onClick

* memo: removed TODO

* feat: Closes all adjacent submenues when opening a new one

* refactor: Undo unwrapping of onDismiss

* refactor: Remove unused code in Menu.tsx

* feat: Handle submenu keys correctly

* feat: use native onClick for Trigger

* refactor: Remove un-needed disabled check on trigger

* bug: Reuse already used context

* Update @navikt/core/react/src/overlays/floating-menu/Menu.tsx

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

* bug: Rename open prop

* Memo: update comment

* feat: Use looser types for RovingFocus

* bug: Aboid auto-closing menu when opening with pointer

---------

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

* Update @navikt/core/react/src/modal/ModalHeader.tsx

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

* Update @navikt/core/css/action-menu.css

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

* Update .changeset/dry-flowers-dance.md

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

* 💄 Manually set zIndex value

* ActionMenu documentation (#3085)

* memo: ActionMenu documentation

* refactor: Update to use Icons

* bug: Fixed use of Item with nextjs link-component

* memo: Updated examples

* memo: Updated examples, removed div-wrapper

* TODOS

* memo: Oppdatert submenu med table-vising

* feat: Oppdatert eksempler

* memo: Default open first submenu in demo

* memo: Use norwegian in link docs

* Update aksel.nav.no/website/pages/eksempler/actionmenu/shortcuts.tsx

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

* memo: Shortcut easter egg

* ⚡ Simplify indeterminate toggle for actionmenu

* refactor: remove redundant aria-label

* Update aksel.nav.no/website/pages/eksempler/actionmenu/sub-menu.tsx

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

* Update aksel.nav.no/website/pages/eksempler/actionmenu/links.tsx

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

* memo: removed reference to nextjs app-router

* bug: Removed empty aria-label

---------

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

* [ActionMenu] Fixes overlapping elements for SubMenus (#3212)

* bug: Fixed scroll + wrapping issue

* bug: undo scrollbar gutter change

* change: Updated chromatic to not overlap

* change: Updated yarn.lock

* change: submenu changes floating position more flexibly than main content now

* change: Chromatic clips unused margins, so moved submenu higher in view

* change: Updated chromatic-story to better show submenu

* memo: changeset for modal head update

* Update .changeset/proud-rings-float.md

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

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-10-11 14:13:55 +02:00
Julian Nymark 6d2115cdc9 Referansesider: dagoversikten structure + completed leftside 2024-10-11 13:57:20 +02:00
Halvor Haugan 17ae19c5fe Add translations for NN and EN (#3209) 2024-10-11 12:01:11 +02:00
KenandHalvor Haugan a60298f232 [Icons] Refactored Figma fetch to TS (#3218)
* ♻️ Refactored Icon fetching from figma

* bug: Fixed types for YML-handling

* update: Lock update

* Update @navikt/aksel-icons/config/figma/index.ts

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

* Update @navikt/aksel-icons/config/figma/index.ts

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

* Update @navikt/aksel-icons/config/figma/make-configs.ts

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

* refactor: pages -> parts

* refactor: Better naming

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-11 09:10:31 +02:00
github-actions[bot]andgithub-actions[bot] 5866992e7d Version Packages (#3208)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-10 15:56:34 +02:00
KenandHalvor Haugan 8ab241a980 [Linting] Comment-cleanup in eslint (#3220)
* memo: Cleanup comments

* memo: Updated eslintRc

* bug: Removed ts from global eslint

* Update .eslintrc.js

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

* memo: Updated temp comment

* Update .eslintrc.js

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

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-10 15:48:47 +02:00
KenandHalvor Haugan 363d76b9b2 [Linting] Fixed noUselessTernary and useOptionalChain-rules (#3215)
* memo: Added figma-plugin as ignore

* memo: Removed unused noSelfAssigned

* refactor: Fixed useJsxKeyInIterable

* memo: Removed unused noInnerDeclarations

* memo: Removed unused noUselessTernary

* refactor: Fixed useOptionalChan rule biome

* Update @navikt/aksel-icons/__tests__/validate-svg.test.ts

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

* feat: Better null-check

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-10 15:06:49 +02:00
Ken 5da970fd62 [Linting] Fixed useLiteralKeys rule from biome (#3219)
* memo: Added figma-plugin as ignore

* memo: Removed unused noSelfAssigned

* refactor: Fixed useJsxKeyInIterable

* memo: Removed unused noInnerDeclarations

* refactor: Fixed useLiteralKeys rule

* bug: Fixed typings for sanity publication action

* bug: Fixed typings for sanity publication action

* bug: Fixed typings for sanity publication action

* bug: added null-check
2024-10-10 14:47:11 +02:00
Ken 296e43cab2 [Linting] Fixed useJsxKeyInIterable ,noSelfAssign and noInnerDeclarations rules from biome (#3214)
* memo: Added figma-plugin as ignore

* memo: Removed unused noSelfAssigned

* refactor: Fixed useJsxKeyInIterable

* memo: Removed unused noInnerDeclarations
2024-10-10 14:33:26 +02:00
Ken 85fec974dc Added autoprefixer to @navikt/ds-css devDependencies (#3213)
* bug: added autoprefixer to ds-css devDeps

* ⬆️ Yarn lock
2024-10-10 13:29:56 +02:00
Vegard HaugstvedtandKen 6ddea4eaf2 Feature/combobox pageup and pagedown (#3158)
* Add support for PageUp and PageDown in Combobox.FilteredOptions

* Remove console.log statements

* Add test for PageDown/PageUp

* Open FilteredOptions when pressing PageDown

* Prevent scrolling from moving the whole page

* Consistently scroll to the active element in useVirtualFocus by using useLayoutEffect

It seems like doing it with the state update sometimes encounters a race condition preventing the scrolling.
This removes the need for a private function handling index update and scrolling, simplifying the code.

* Updated test to fit new options order

* Prevent "Home" and "End" in Combobox from scrolling as well

* Add changeset

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

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

* useEffect seems to work the same way, and works better for SSR

* Removed code that did nothing

* Added the same check we have for PageDown, etc. Unsure if it is neccessary.

* Change behaviour for "Home" to move focus to the first element instead of Input

* Once we changed virtualFocus.moveFocusToTop to move focus to first element instead of the Input, we now need another function to move it to the Input where applicable.

* Update .changeset/tasty-cats-hear.md

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

---------

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-10-10 12:05:13 +02:00
Halvor Haugan acea9ebb78 Change "personnummer" to "fødselsnummer" (#3211) 2024-10-10 11:59:28 +02:00
Julian Nymark fdbd634012 Referansesider: dagoversikten BreadCrumbs 2024-10-09 12:37:13 +02:00
Julian Nymark dcf5452d28 Referansesider: dagoversikten content 2024-10-08 16:34:15 +02:00
Sjur 0a8c33bbbe Justerte Danger-farger i LightMode (#3210)
Noen av HEX-verdiene i Danger LightMode er endret.
2024-10-08 15:26:07 +02:00
Vegard Haugstvedt 18b0cf5fa5 Bugfix/combobox disable autocomplete in firefox on android (#3201)
* Disable autocomplete in Combobox to "fix" issue where autocomplete is broken in Firefox on Android

The bug is that after three "correct" characters are entered in a row, the autocomplete inserts the third character incorrectly, messing up the text in the input.
This also happens in other implementations of autocomple, like W3C's ARIA Authoring Practices Guide combobox with inline autocomple.

* Add changeset

* Use type checking instead of useEffect to prevent client code from running server side
2024-10-08 12:37:07 +02:00
Julian Nymark 374a679bc3 Referansesider: ✨ dagoversikten.tsx 2024-10-08 11:53:09 +02:00
github-actions[bot]andgithub-actions[bot] a2e020d4f3 Version Packages (#3206)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-08 11:50:48 +02:00
Halvor Haugan 074c8da536 List: ✨ New size 'large' (#3207) 2024-10-08 11:30:47 +02:00
KenandHalvor Haugan bdbd6c24cc [Dependencies] Replaced ts-node with tsx, removed dotenv (#3198)
* refactor: ds-react uses tsx instead of ts-node

* refactor: ds-tailwind uses tsx

* refactor: Website backup and search uses tsx

* refactor: Test urls and rss-feed uses tsx

* refactor: Prop-updates uses tsx

* refactor: All website scripts uses tsx

* refactor: removed dotenv dependency from website

* feat: removed all dependencies to dotenv

* bug: Remove need for env in validate build script

* test: Create empty env file

* test: Scope env-file to build-dir

* bug: Fallback to empty env when no env exsists

* feat: Better error-messaging when fetching new icons

* Update @navikt/aksel-icons/package.json

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

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-08 10:18:51 +02:00
Ken f0e6c944d8 [Referansesider] Updated contrast tokens to new naming 2024-10-08 10:16:17 +02:00
Ken 22be148832 [Darkside] Moved contrast-tokens to text group
feat: Moved contrast tokens to 'text' collection
feat: Added support for adding custom scopes for each token
2024-10-08 10:06:43 +02:00
KenandHalvor Haugan aac12fe44f [Linting] Fixed useExhaustiveDependencies in biome (#3197)
* feat: Update biome

* feat: migrate biome config

* misc: Ignore semantic-tag rule biome

* refactor: resolved breaking changes after biome update

* feat: remove old excluded biome rule

* feat: Removed a11y/useFocusableInteractive ignore

* refactor: Fix useExhaustiveDependencies  biome

* memo: changeset

* memo: changeset gitmoji

* bug: Re-add selectedOptions to dependency array

* Update @navikt/core/react/src/overlays/dismissablelayer/DismissableLayer.tsx

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

* bug: Fix initial focus for sidebar icon

* ⚡ remove reduntant memoization of isLimitReached

* bug: re-add sanity-plugin-iframe-pane

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-08 09:05:59 +02:00
Halvor Haugan 324358d086 FileUpload: 💥 Remove UNSAFE-prefix (#3205) 2024-10-07 15:16:25 +02:00
Ken 946cbeb016 [Liniting] Updated to biome v1.9 (#3196)
* feat: Update biome

* feat: migrate biome config

* misc: Ignore semantic-tag rule biome

* refactor: resolved breaking changes after biome update

* feat: remove old excluded biome rule

* feat: Removed a11y/useFocusableInteractive ignore

* refactor: Remove ariaRole rule

* bug: Remove extra fragments in dartepicker stories

* bug: Remove custom image rendering exception for safari on website
2024-10-05 15:41:23 +02:00
Sjur f4d22dae68 Endret Neutral-farger Darth Mode (#3204)
Neutral-fargene ble hentet fra fargene med lavere saturation, som gjorde at de mistet hintet av blått. Endret til den originale skalaen.
2024-10-04 22:09:27 +02:00
Ken ce2f5a9b90 [Darkside] Fixed missing test updates 2024-10-04 16:01:58 +02:00
github-actions[bot]andgithub-actions[bot] cf167064eb Version Packages (#3203)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-04 15:25:08 +02:00
Ken 73a6ae1bf1 [Darkside] Fixed no_cache_paths for figma config CDN 2024-10-04 15:24:06 +02:00
Halvor Haugan 95c84e1d01 List: Explicitly set list-style on ol to override reset-CSS (#3202) 2024-10-04 15:14:16 +02:00
Ken 4675857e8f [Darkside] Updated global colors config
feat: Avoid caching config for color-tokens
feat: Remove use of leonardo, hardcode global config
feat: Update 100-400 scale for darkmode
feat: Update 500-1000 scale for global colors
2024-10-04 15:06:32 +02:00
Julian Nymark 2a6e416b5c Referansesider: 🐛 use master branch for GA 2024-10-04 12:50:43 +02:00
Julian Nymark d3c3cafc97 Referansesider: 👷 point to maintained spa-deploy yml (#3199)
* Referansesider: 👷 point to maintained spa-deploy yml

new location: https://github.com/nais/deploy/tree/master/actions/spa-deploy

* Referansesider: 🐛 probably correct yml url (GH action)
2024-10-04 11:47:33 +02:00
KenandHalvor Haugan 678fa6692b [Darkside] Figma plugin for updating variables (#3191)
* feat: Implemented figma plugin

* feat: Can now detect production/development build

* feat: Now supports remote fetch for config

* misc: Stop reset from running by default

* feat:Semantic value handling

* feat: Pre-semantic implementation

* feat: Finished plugin API for semantic variables

* feat: Use this for Figma plugin extension

* bug: Fixed semantic collection naming

* bug: Handle reset correctly

* misc: avoid resetting when updating

* memo: Added comments

* bug: Ignore plugin buildfile from lint

* bug: Fix support for dynamic-page load in figma

* 📝 Better naming, simplified code-def syntax

* feat: Scoped ignorePattern for plugin.js

* memo: better logs for completing update

* Update @navikt/core/tokens/darkside/figma/plugin/AkselVariablesInterface.ts

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

* Update @navikt/core/tokens/darkside/figma/plugin/AkselVariablesInterface.ts

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

* memo: Removed extra space for info logging

* refactor: Simplify for-loop for semantic color modes

* refactor: Inline creation of mode, collection and variable

* refactor: readable mapping variables

* feat: Better error-handling if variable is null

* refactor: Remove redundant getModes function

* refactor: Remove redundant getModes function

* refactor: Remove plugin watch function

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-04 11:19:43 +02:00
Ken 87aeb94c64 [Dependencies] Sanity 3.59 update (#3195)
* feat: Updated to sanity 3.59

* bug: Sanity updated icon and media types, so everything broke

* bug: Fallback to default media in article previews if everything is normal
2024-10-03 16:35:00 +02:00
github-actions[bot]andgithub-actions[bot] 4714a5ff32 Version Packages (#3181)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-03 16:01:01 +02:00
Halvor HauganandKen ca1d9be88f List: Add indentation and remove aria attributes (#3184)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-10-03 15:06:06 +02:00
Halvor Haugan 2654da8b64 update-article-views: Use latest docker image + remove reduntant stuff in workflow (#3189) 2024-10-03 08:52:48 +02:00
Julian Nymark 8d6418a594 Referansesider: 💄 use correct tokens 2024-10-02 14:24:10 +02:00
Julian Nymark 8269091fb8 Referansesider: 💄 outline offset for focus 2024-10-02 14:15:06 +02:00
Julian Nymark fd82184057 Referansesider: 🎨 aktivitetsplan buttons & select 2024-10-02 14:11:31 +02:00
Julian Nymark 39f2cb8614 Referansesider: 🎨 aktivitetsplan main card 2024-10-02 13:57:07 +02:00
Julian Nymark 4bf298f363 Referansesider: 🎨 Tag to own component, flesh out cards for aktivitetsplan 2024-10-02 12:55:54 +02:00
Halvor HauganandKen 3a239e4e17 [aksel.nav.no] Show breakpoints in GuidePanel examples (#3192)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-10-02 12:23:10 +02:00
Halvor Haugan 4fcfaf3deb [aksel.nav.no] Storybook: Change to Vite framework (#3185) 2024-10-02 10:14:17 +02:00
Julian Nymark 5dbc66daaa Referansesider: 🚧 Aktivitetsplan 2024-10-02 09:53:17 +02:00
Halvor Haugan 555a46b5da Adjust examples of Switch (#3188) 2024-10-01 13:55:03 +02:00
Julian Nymark 73b2bca156 Combobox: 💄 tweak the visibility of chevron (#3187)
* Combobox: 💄 tweak the visibility of chevron

* Combobox: 💄 dim visibility of icons for readonly datepicker, select, and combobox

* Combobox: 💄 dim visibility of icons for readonly datepicker, select, and combobox

* changeset
2024-10-01 13:47:53 +02:00
Julian Nymark 01411dc11f referansesider: 💄 sykepenger++ 2024-10-01 12:02:38 +02:00
Julian Nymark b1b0d23863 Referansesider: 💄 use more tokens 2024-09-30 14:56:17 +02:00
Julian Nymark cf59ce81bf Combobox disabled fixes (#3183)
* Combobox: 💄 remove hover border color when disabled

* Combobox: 💄 remove hover effect on chips when disabled
2024-09-30 13:53:48 +02:00
Julian Nymark 00c0ff31e1 Combobox: 💄 remove hover border color when disabled (#3182) 2024-09-30 13:38:22 +02:00
Julian Nymark d278060e59 Combobox: 🐛 implement Readonly + disabled (#3180)
* 🐛 [Combobox] implement disabled

* 💄 [Combobox] disabled

* ✨ [Combobox] implement readOnly

* 📝 [Combobox] add examples

* 📝 [Combobox] changeset

* 📝 [Combobox] changeset++

* 📝 [Combobox] stories (review fixes)

* 📝 [Combobox] stories (review fixes++)

* Combobox: 🎨 remove ToggleListButton when readonly or disabled

* Combobox: ♿ combobox input does not support native readonly, use nativeReadOnly={false}

* Revert "Combobox: 🎨 remove ToggleListButton when readonly or disabled"

This reverts commit e54a2eca32.

* Combobox: remove unused code
2024-09-30 11:24:47 +02:00
Ken e5d5b67262 [Referansesider] Migrated to new tokens 2024-09-27 14:13:54 +02:00
Ken 246f25cbaa misc: Update darkmode config for tokens 2024-09-27 14:01:51 +02:00
Ken b2a22438ad bug: Fixed CDN-location for figma config 2024-09-27 12:58:30 +02:00
KenandHalvor Haugan 8b8b724e40 [Darkside] Generate Token config to be consumed by Figma plugin (#3172)
* feat: Build figma config

* bug: Fix ignored gitignore config

* refactor: Rename collection -> name key

* feat: pre-update value to figma config

* feat: Re-resolve name with grouping

* feat: Better handling of relative units

* feat: Added better typings, workflow file

* feat: Updated name-parsing for pregen of config

* feat: Removed Unused eslint-disable directive

* feat: Extract figma scope when creating variables

* refactor: Simplified code by extracting logic

* feat: Updated workflow naming

* bug: re-add missing group for semantic color

* feat: Add metadata to figma config

* bug: Added missing 'contrast declaration

* bug: Calculate correct radius values

* refactor: Simplified config structure by unwrapping scopes

* refactor: Extracted logic for group check

* refactor: Only allow string or number as figma value in config

* feat: Added tests for value conversions

* refactor: Renamed figma config file

* update: Lock update

* refactor: Avoid using format as a workaround to extract token list

* Update @navikt/core/tokens/darkside/figma/index.ts

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

* Update @navikt/core/tokens/darkside/figma/create-tokens.ts

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

* Update @navikt/core/tokens/darkside/util.ts

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

* refactor: inline filter calls

* refactor: Better typing for as group declaration

* refactor: Avoid using objects as type definintions

* refactor: Inline semantic token check for roles

* refactor: date -> timestamp and NO locale

* refactor: token -> tokens

* refactor: removed redundant redaclaration of token name in test

* refactor: Avoid extra tests for same check

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-09-27 12:35:42 +02:00
Ken 9f9bdcc645 bug: Fixed old test for global tokens 2024-09-26 19:13:38 +02:00
Ken 82bdf8075a [Tokens]: Update darkmode tokens and separate config support
feat: Updated darkmode tokens
feat: Split global color config into separate files
2024-09-26 19:09:01 +02:00
Julian Nymark 410e543825 🎨 [referansesider] minside eslint 2024-09-26 15:47:29 +02:00
Julian Nymark 40308e0e77 🎨 [referansesider] minside dokumentarkiv 2024-09-26 15:39:02 +02:00
Julian Nymark fff309b573 💄 [referansesider] minside NotificationsPanel lightmode match figma 2024-09-26 14:44:44 +02:00
Julian Nymark fe7c2de8fa 💄 [referansesider] minside PassiveCards++ 2024-09-26 14:37:42 +02:00
Julian Nymark 6fedea0ba4 💄 [referansesider] minside PassiveCards 2024-09-25 15:02:05 +02:00
Halvor HauganandKen 9811327509 [aksel.nav.no] Adjust examples of Switch (#3175)
---------

Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-09-25 14:21:19 +02:00
Ken cf2034f914 [Aksel.nav.no] Open all Accordions when searched (#3179)
* feat: Open all Accordions when searched

* refactor: Type node as possibly undefined
2024-09-25 14:19:15 +02:00
dependabot[bot] 9e5e7f8fdc build(deps): Bump rollup from 3.29.4 to 3.29.5 (#3178)
Bumps [rollup](https://github.com/rollup/rollup) from 3.29.4 to 3.29.5.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v3.29.4...v3.29.5)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 13:49:55 +02:00
Ken e9dc171d74 Referansesider: Dynamic route-list generation for header and homepage 2024-09-25 13:25:43 +02:00
Julian Nymark 544b80eeae 💄 [referansesider] Minside Cards 2024-09-25 13:17:10 +02:00
Julian Nymark 4358b53095 🎨 [referansesider] move Styled components to top layer++ 2024-09-25 12:07:57 +02:00
Julian Nymark de57099b09 🎨 [referansesider] move Styled components to top layer 2024-09-25 11:59:57 +02:00
Ken 5a97cb422d feat: Add cache-control to index.html metadata referansesider 2024-09-25 11:16:03 +02:00
Ken 67fb3bb3a1 feat: Added theme switcher for referansesider 2024-09-25 10:52:03 +02:00
Ken 7bdd5cb748 bug: Fixes darkmode cascading for darkside tokens 2024-09-25 10:10:24 +02:00
github-actions[bot]andgithub-actions[bot] 62bcab1ab0 Version Packages (#3170)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-25 09:36:07 +02:00
Halvor Haugan 61e60d9556 Modify changeset text for Accordion change 2024-09-24 17:17:18 +02:00
Halvor Haugan 9c1eba4450 Accordion: Remove position:relative to fix popover clipping 2024-09-24 16:53:46 +02:00
Julian Nymark 63986d9872 💄 [referansesider] remove overflow 2024-09-24 16:43:04 +02:00
Ken 761c8ef64f refactor: Update tokens use in Piktogram for sykepenger page 2024-09-24 16:31:30 +02:00
Ken 4b86b555d8 feat: Referansesider now properly handles nested routing 2024-09-24 16:10:58 +02:00
Julian Nymark 8e1e294fda 💄 [referansesider] minside add icons 2024-09-24 15:51:55 +02:00
Halvor Haugan f71594e477 ErrorSummary: Remove aria-attributes. (#3171) 2024-09-24 15:41:49 +02:00
Julian Nymark d2a89851cd 💄 [referansesider] responsive design fix 2024-09-24 15:31:25 +02:00
Ken d27da71304 feat: Refactored to use more tokens in referansesider 2024-09-24 15:26:19 +02:00
Julian Nymark fcf17fc750 💄 [referansesider] minside card CSS 2024-09-24 15:24:41 +02:00
Julian Nymark e1b091358b 🎨 [referansesider] add content to minside 2024-09-24 15:15:15 +02:00
dependabot[bot] 988ad39ad1 build(deps): Bump rollup in /examples/referansesider (#3173)
Bumps [rollup](https://github.com/rollup/rollup) from 4.21.3 to 4.22.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.21.3...v4.22.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 14:00:52 +02:00
Ken 524c87e3bd feat: Update referansesider header fontsize 2024-09-24 13:54:48 +02:00
Ken 8cf6123656 feat: Created /komponenter route for component-testing, refactored button-component
feat: Use func for sykepenger function
2024-09-24 13:32:24 +02:00
Julian Nymark 866e6a168c 💄 [referansesider] make sykepenger more responsive 2024-09-24 13:16:47 +02:00
Ken bc2be15c55 Update referansesider
- feat: Refactor to use aksel-icons package
- refactor: use google import for font instead of inline files
2024-09-24 13:06:55 +02:00
Julian Nymark 4e6fc073fe 🐛 [referansesider] only have tanstack router devtools in dev mode 2024-09-24 12:59:56 +02:00
Ken e895c691ce [Referansesider] SPA deploy to aksel-referansesider.ansatt.dev.nav.no (#3174)
* bug: Fix build-script

* feat: Workflow for SPA deploy of referansesider

* feat: Add workflow dispatch to SPA deploy
2024-09-24 12:40:08 +02:00
Halvor Haugan 186c0fec07 🆙 Upgrade some packages (#3165) 2024-09-23 17:24:04 +02:00
Ken 746a58b663 feat: Updated darkside tokens with figma 2024-09-23 16:30:13 +02:00
Julian Nymark d767aadde3 🎨 [referansesider] add crude dekoratør 2024-09-23 14:02:30 +02:00
KenandHalvor Haugan 4359428b40 [Pagination] Deprecated page-prop, synced design with Figma (#3164)
* feat: Mark page-prop as deprecated in pagination

* feat: Mark page-prop as deprecated in pagination with better comment

* feat: Synced pagination with Figma

* memo: Changeset

* memo: Add classes to deprecationlist in stylelint config

* memo: changeset for stylelint

* memo: changeset for stylelint

* Update @navikt/aksel-stylelint/src/deprecations.ts

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

* Update .changeset/thirty-snakes-doubt.md

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-09-23 12:28:52 +02:00
Halvor Haugan d583276428 Combobox: 🏷️ Better typing for maxSelected prop (#3169) 2024-09-23 10:07:06 +02:00
368 changed files with 16705 additions and 12921 deletions

No files matched your search

+31 -13
View File
@@ -1,4 +1,11 @@
// TODO: Convert to new format: https://eslint.org/docs/latest/use/configure/migration-guide
/**
* TODO:
* - Convert to new format (v9): https://eslint.org/docs/latest/use/configure/migration-guide
* - Look into opinionated stylistic plugin
* - Replace @typescript-eslint/recommended with @typescript-eslint/recommended-type-checked
* - We should add "plugin:react/jsx-runtime", but not sure if this will cause problems for projects not using the new JSX transform
* - Consider adding the rule "id-length"
*/
module.exports = {
env: {
browser: true,
@@ -8,15 +15,13 @@ module.exports = {
extends: [
"eslint:recommended",
"plugin:react/recommended",
//"plugin:react/jsx-runtime", // Not sure if this will cause problems for projects not using the new JSX transform
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:storybook/recommended",
// TODO: Look into opinionated stylistic plugin
// TODO: Replace @typescript-eslint/recommended with @typescript-eslint/recommended-type-checked
],
/* Allows eslint-plugin-react to detect installed react-version */
settings: { react: { version: "detect" } },
rules: {
"import/no-unresolved": "off",
@@ -26,12 +31,6 @@ module.exports = {
],
"array-callback-return": "error",
"object-shorthand": "error",
// TODO: Consider adding the rule "id-length"
// Temporary:
"react/prop-types": "off",
"react/display-name": "off",
"import/no-named-as-default": "off",
// "import/no-cycle": [1],
"no-else-return": "error",
"no-console": [
"warn",
@@ -53,7 +52,12 @@ module.exports = {
],
},
],
/* Temporarily turned off until code is updated */
"react/prop-types": "off",
"react/display-name": "off",
"import/no-named-as-default": "off",
},
/* https://eslint.org/docs/latest/use/configure/rules#report-unused-eslint-disable-comments */
reportUnusedDisableDirectives: true,
overrides: [
{
@@ -66,15 +70,28 @@ module.exports = {
files: ["**/*.ts?(x)"],
extends: ["plugin:@typescript-eslint/recommended"],
rules: {
"@typescript-eslint/no-shadow": ["error", { hoist: "all" }], // TODO: Consider { builtinGlobals: true }
/*
* TODO:
* - Consider { builtinGlobals: true }
*/
"@typescript-eslint/no-shadow": ["error", { hoist: "all" }],
"@typescript-eslint/no-explicit-any": "off", // Temporary
"@typescript-eslint/array-type": "error",
"@typescript-eslint/no-unused-expressions": [
"error",
/* https://eslint.org/docs/latest/rules/no-unused-expressions#allowshortcircuit-and-allowternary */
{ allowShortCircuit: true, allowTernary: true },
],
},
},
{
files: ["**/*.test.*", "**/__tests__/*"],
plugins: ["vitest"],
extends: ["plugin:vitest/recommended", "plugin:testing-library/react"],
plugins: ["@vitest"],
extends: [
"plugin:testing-library/react",
/* Needed to support v8 eslint config-syntax */
"plugin:@vitest/legacy-recommended",
],
},
{
@@ -153,5 +170,6 @@ module.exports = {
"**/codemod/**/*.js",
"!.storybook",
"**/playwright-report/**",
"**/tokens/**/plugin.js",
],
};
+3
View File
@@ -34,6 +34,9 @@ jobs:
- name: Install dependencies
run: yarn
- name: Create empty .env file (Can be removed when env-file-if-exists is implemented in node v22)
run: echo "" > ./aksel.nav.no/website/.env
- name: Build application
env:
SANITY_READ: ${{ secrets.SANITY_READ }}
+6
View File
@@ -32,6 +32,9 @@ jobs:
yarn boot
yarn docgen
- name: Create empty .env file (Can be removed when env-file-if-exists is implemented in node v22)
run: echo "" > ./aksel.nav.no/website/.env
- name: Update sanity sync
env:
SANITY_WRITE: ${{ secrets.SANITY_WRITE }}
@@ -68,6 +71,9 @@ jobs:
- name: Tests
run: yarn workspace website test
- name: Create empty .env file (Can be removed when env-file-if-exists is implemented in node v22)
run: echo "" > ./aksel.nav.no/website/.env
- name: Build application
env:
SANITY_READ: ${{ secrets.SANITY_READ }}
+3
View File
@@ -23,6 +23,9 @@ jobs:
- name: Install dependencies
run: cd aksel.nav.no/website && yarn
- name: Create empty .env file (Can be removed when env-file-if-exists is implemented in node v22)
run: echo "" > ./aksel.nav.no/website/.env
- name: Export dataset
env:
SANITY_READ: ${{ secrets.SANITY_READ }}
+4 -1
View File
@@ -13,7 +13,7 @@ jobs:
playwright:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.35.1-jammy
image: mcr.microsoft.com/playwright:v1.48.1-jammy
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@@ -31,6 +31,9 @@ jobs:
run: |
yarn boot
- name: Create empty .env file (Can be removed when env-file-if-exists is implemented in node v22)
run: echo "" > ./aksel.nav.no/website/.env
- name: Build application
env:
SANITY_READ: ${{ secrets.SANITY_READ }}
+50
View File
@@ -0,0 +1,50 @@
name: "Update remote token config for figma variables plugin"
on:
workflow_dispatch:
push:
branches:
- "main"
paths:
- "@navikt/core/tokens/**"
jobs:
upload:
runs-on: ubuntu-latest
permissions:
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: yarn
- 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
- name: Build tokens
run: yarn workspace @navikt/ds-tokens build
- id: upload
uses: nais/deploy/actions/cdn-upload/v2@master
with:
team: designsystem
source: "./@navikt/core/tokens/figma-config.json"
destination: "./@navikt/tokens"
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
source_keep_parent_name: false
no_cache_paths: "@navikt/tokens/figma-config.json"
- run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
shell: bash
@@ -0,0 +1,37 @@
name: Deploy referansesider SPA
on:
workflow_dispatch:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: yarn
- run: yarn install
env:
NPM_AUTH_TOKEN: ${{ secrets.READER_TOKEN }}
- run: yarn boot
- run: cd ./examples/referansesider && yarn && yarn build
- uses: nais/deploy/actions/spa-deploy/v2@master
with:
app: aksel-referansesider
team: designsystem
source: ./examples/referansesider/dist
ingress: https://aksel-referansesider.ansatt.dev.nav.no
environment: dev
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
@@ -5,24 +5,11 @@ jobs:
execute:
runs-on: ubuntu-latest
permissions:
packages: "write"
contents: "read"
id-token: "write"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Restore cache
uses: actions/cache@v4
with:
path: "**/node_modules"
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
- uses: nais/docker-build-push@v0
id: docker-push
with:
+6 -6
View File
@@ -136,34 +136,34 @@ import Setup from "./assets/setup.png";
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: var(--a-spacing-6);
grid-gap: 1.5rem;
}
.sb-link{
text-decoration: underline;
color: var(--a-text-default);
color: #111;
}
img {
object-fit: cover;
padding-bottom: var(--a-spacing-5);
padding-bottom: 1.25rem;
border-radius: 8px;
}
.sb-section-title {
margin-bottom: var(--a-spacing-8);
margin-bottom: 2rem;
}
.sb-section-item-heading {
padding-bottom: var(--a-spacing-1);
padding-bottom: 0.25rem
margin: 0 !important;
}
.sb-section-item-paragraph {
margin: 0;
padding-bottom: var(--a-spacing-3);
padding-bottom: 0.75rem;
}
@media screen and (max-width: 768px) {
+1
View File
@@ -53,6 +53,7 @@ export default {
"@storybook/addon-a11y",
"@whitespace/storybook-addon-html",
"@storybook/addon-interactions",
"@storybook/addon-themes",
{
name: "@storybook/addon-storysource",
options: {
+51 -30
View File
@@ -1,5 +1,7 @@
import React from "react";
import "@navikt/ds-css/index.css";
import { withThemeByClassName } from "@storybook/addon-themes";
import React, { useLayoutEffect } from "react";
import darksideCss from "@navikt/ds-css/darkside/index.css?inline";
import defaultCss from "@navikt/ds-css/index.css?inline";
import "./layout.css";
export const parameters = {
@@ -16,44 +18,63 @@ export const parameters = {
},
},
layout: "centered",
backgrounds: {
values: [
{
name: "Canvas",
value: "#ffffff",
},
{
name: "Darkmode",
value: "#23262a",
},
],
},
backgrounds: { disable: true },
};
export const globalTypes = {
theme: {
name: "Theme",
mode: {
name: "Darkside",
toolbar: {
icon: "circlehollow",
items: [
{ value: "light", icon: "circlehollow", title: "light" },
{ value: "dark", icon: "circle", title: "dark" },
{
value: "default",
icon: "hourglass",
title: "Default (old)",
},
{ value: "darkside", icon: "beaker", title: "Darkside (new)" },
],
showName: true,
dynamicTitle: true,
},
},
};
export const withTheme = (StoryFn, context) => {
return (
<div
data-theme={context.parameters.theme || context.globals.theme || "light"}
lang="no"
id="root"
>
<StoryFn />
</div>
);
export const initialGlobals = {
mode: "default",
};
export const decorators = [withTheme];
const ModeDecorator = ({ children, mode }) => {
useLayoutEffect(() => {
const style = document.createElement("style");
style.innerHTML = mode === "darkside" ? darksideCss : defaultCss;
document.head.appendChild(style);
if (mode === "darkside") {
document.body.style.setProperty("background", "var(--a-bg-default)");
}
return () => {
document.head.removeChild(style);
document.body.style.removeProperty("background");
};
}, [mode]);
return children;
};
export const decorators = [
withThemeByClassName({
themes: {
light: "light",
dark: "dark",
},
defaultTheme: "light",
}),
(StoryFn, context) => {
return (
<ModeDecorator mode={context.globals.mode}>
<StoryFn />
</ModeDecorator>
);
},
];
+12
View File
@@ -1,5 +1,17 @@
# @navikt/aksel-icons
## 7.3.0
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
## 7.1.0
## 7.0.1
## 7.0.0
@@ -0,0 +1,16 @@
import { describe, expect, test } from "vitest";
import { splitIntoParts } from "../config/figma/fetch-icons";
describe("splitIntoParts", () => {
test("should split array into parts correctly with odd count array", () => {
const arr = ["a", "b", "c", "d", "e"];
const result = splitIntoParts(arr, 4);
expect(result).toEqual(["a,b", "c,d", "e"]);
});
test("should split array into parts correctly with even count array", () => {
const arr = ["a", "b", "c", "d", "e", "f"];
const result = splitIntoParts(arr, 4);
expect(result).toEqual(["a,b", "c,d", "e,f"]);
});
});
@@ -1,8 +1,9 @@
import fastglob from "fast-glob";
import fs from "fs";
import jsYaml from "js-yaml";
import { JSON_SCHEMA, load } from "js-yaml";
import path from "path";
import { describe, expect, test } from "vitest";
import { IconYml } from "../config/figma/make-configs";
const basePath = path.resolve(__dirname, "../icons");
@@ -13,9 +14,10 @@ const ymlList = fastglob
describe(`Each icons YML-config is valid`, () => {
ymlList.forEach((file) => {
test(`${file} has valid YML-config`, () => {
const ymlData = jsYaml.load(fs.readFileSync(`${basePath}/${file}`), {
schema: jsYaml.JSON_SCHEMA,
});
const ymlData = load(fs.readFileSync(`${basePath}/${file}`).toString(), {
schema: JSON_SCHEMA,
}) as IconYml;
expect(ymlData.name).toBeTruthy();
expect(ymlData.category).toBeTruthy();
expect(ymlData.sub_category).toBeTruthy();
@@ -30,7 +32,7 @@ describe(`Each icons YML-config is valid`, () => {
});
});
function isDate(dateStr) {
function isDate(dateStr: string) {
return !Number.isNaN(
new Date(dateStr.split(".").reverse().join(".")).getDate(),
);
@@ -21,8 +21,7 @@ describe(`Each icons has a valid code`, () => {
test(`has valid attributes on root-node`, () => {
const root = select(":root", iconAst);
const properties =
root && root.properties ? Object.keys(root.properties).sort() : [];
const properties = Object.keys(root?.properties ?? []).sort();
expect(properties).toStrictEqual(
["viewBox", "xmlns", "height", "width", "fill"].sort(),
@@ -31,45 +30,41 @@ describe(`Each icons has a valid code`, () => {
test(`has valid xml-attr`, () => {
const root = select(":root", iconAst);
const xmlns = root && root.properties ? root.properties.xmlns : null;
expect(xmlns).toBe("http://www.w3.org/2000/svg");
expect(root?.properties?.xmlns).toBe("http://www.w3.org/2000/svg");
});
test(`has valid viewbox`, () => {
const root = select(":root", iconAst);
const viewbox =
root && root.properties ? root.properties.viewBox : null;
expect(viewbox).toBe("0 0 24 24");
expect(root?.properties?.viewBox).toBe("0 0 24 24");
});
test(`root fill is none`, () => {
const root = select(":root", iconAst);
const fill = root && root.properties ? root.properties.fill : null;
expect(fill).toBe("none");
expect(root?.properties?.fill).toBe("none");
});
test(`has valid width and height`, () => {
const root = select(":root", iconAst);
const width = root && root.properties ? root.properties.width : null;
const height = root && root.properties ? root.properties.height : null;
expect(width).toBe("24");
expect(height).toBe("24");
expect(root?.properties?.width).toBe("24");
expect(root?.properties?.height).toBe("24");
});
test(`has valid stroke`, () => {
const nodes = selectAll("*", iconAst);
nodes.forEach((n) => {
n.properties?.stroke &&
if (n.properties?.stroke) {
expect(n.properties.stroke).toEqual("#23262A");
}
});
});
test(`has valid stroke-width`, () => {
const nodes = selectAll("*", iconAst);
nodes.forEach((n) => {
n.properties?.strokeWidth &&
if (n.properties?.strokeWidth) {
expect(n.properties?.strokeWidth).toEqual("1.5");
}
});
});
});
@@ -1,58 +0,0 @@
const file_key = "wEdyFjCQSBR3U7FvrMbPXa";
export const fetchIcons = async () => {
const data = await fetch(
`https://api.figma.com/v1/files/${file_key}/components`,
{
headers: {
"Content-type": "application/json",
"X-FIGMA-TOKEN": process.env.FIGMA_TOKEN,
},
},
)
.then((x) => x.json())
.catch((e) => {
throw e.message;
});
if (!data?.meta?.components || data?.meta?.components.length === 0) {
throw new Error("Fant ingen publiserte ikoner");
}
return data?.meta?.components;
};
export const fetchDownloadUrls = async (idArray) => {
const urls = { images: null };
for (let ids of idArray) {
await fetch(
`https://api.figma.com/v1/images/${file_key}/?ids=${ids}&format=svg`,
{
headers: {
"Content-type": "application/json",
"X-FIGMA-TOKEN": process.env.FIGMA_TOKEN,
},
},
)
.then((x) => x.json())
.then((x) => {
urls.images = {
...urls.images,
...x.images,
};
})
.catch((e) => {
throw e.message;
});
}
return urls;
};
export const fetchIcon = async (url) => {
return await fetch(url)
.then((x) => x.text())
.catch((e) => {
throw e.message;
});
};
@@ -0,0 +1,114 @@
import {
BadRequestErrorResponseWithErrMessage,
ErrorResponsePayloadWithErrMessage,
GetFileComponentsResponse,
GetImagesResponse,
PublishedComponent,
} from "@figma/rest-api-spec";
/* https://www.figma.com/developers/api#library-items */
const FIGMA_FILE_KEY = "wEdyFjCQSBR3U7FvrMbPXa";
export const fetchIcons = async (): Promise<PublishedComponent[]> => {
console.group("Fetching list of published icons from Figma...");
const data:
| GetFileComponentsResponse
| BadRequestErrorResponseWithErrMessage = await fetch(
`https://api.figma.com/v1/files/${FIGMA_FILE_KEY}/components`,
{
headers: {
"Content-type": "application/json",
"X-FIGMA-TOKEN": process.env.FIGMA_TOKEN ?? "",
},
},
)
.then((x) => x.json())
.catch((e) => {
throw e.message;
});
if (data.status !== 200) {
throw new Error(data.err);
}
if (data.meta.components.length === 0) {
throw new Error("No published icons found");
}
/* Filtes out any 'random' icons not meant for the icon-library */
const validIcons = data.meta.components.filter((icon) => {
const validIcon =
!!icon.containing_frame?.pageName && !!icon.containing_frame?.name;
if (!validIcon) {
console.warn(`Ignoring icon '${icon.name}' as it is missing metadata`);
}
return validIcon;
});
console.info(
`Found ${validIcons.length} published icons in Figma. ${
data.meta.components.length - validIcons.length
} icons were ignored due to missing metadata.`,
);
console.groupEnd();
return validIcons;
};
export const fetchDownloadUrls = async (
nodeIds: string[],
): Promise<Record<string, string>> => {
/* Would just using Record be better here? Yes. But is a self-referencing return kinda cool? Also yes */
const idUrlPairs: Awaited<ReturnType<typeof fetchDownloadUrls>> = {};
/* Figma accepts a comma-separated list of ids: GET /v1/images/:key?ids=1:2,1:3,1:4 */
const paginatedNodeIds = splitIntoParts(nodeIds, 5);
for (const ids of paginatedNodeIds) {
const data: GetImagesResponse | ErrorResponsePayloadWithErrMessage =
await fetch(
`https://api.figma.com/v1/images/${FIGMA_FILE_KEY}/?ids=${ids}&format=svg`,
{
headers: {
"Content-type": "application/json",
"X-FIGMA-TOKEN": process.env.FIGMA_TOKEN ?? "",
},
},
)
.then((x) => x.json())
.catch((e) => {
throw e.message;
});
if (data.err !== null) {
throw new Error(data.err);
}
for (const [key, value] of Object.entries(data.images)) {
if (!value) {
console.warn(`No image found for ${key}`);
continue;
}
idUrlPairs[key] = value;
}
}
return idUrlPairs;
};
/**
* Splits an array of strings into parts where each part is a comma-separated string of all the strings for that part.
* @returns An array of comma-separated strings.
*/
export function splitIntoParts(arr: string[], parts: number): string[] {
const partSize = Math.ceil(arr.length / parts);
const blocks: string[] = [];
for (let i = 0; i < parts && i * partSize < arr.length; i++) {
const part = arr.slice(i * partSize, (i + 1) * partSize);
blocks.push(part.join(","));
}
return blocks;
}
@@ -1,15 +0,0 @@
import camelCase from "lodash.camelCase";
import startCase from "lodash.startcase";
export const resolveName = (icon) => {
const isVariant = icon.name.startsWith("Variant=");
if (isVariant) {
const variant = icon.name.replace("Variant=", "").replace("Stroke", "");
const actualName = icon["containing_frame"]?.["containingStateGroup"]?.name;
const name = `${actualName}${variant}`;
return `${startCase(camelCase(name)).replace(/ /g, "")}.svg`;
}
return `${startCase(camelCase(icon.name)).replace(/ /g, "")}.svg`;
};
@@ -0,0 +1,17 @@
import { PublishedComponent } from "@figma/rest-api-spec";
import { camelCase, startCase } from "lodash";
export const resolveName = (icon: PublishedComponent) => {
const isVariant = icon.name.startsWith("Variant=");
if (isVariant) {
const variant = icon.name.replace("Variant=", "").replace("Stroke", "");
/* @ts-expect-error Figma now includes the key:value pair `containingStateGroup: { name: string, nodeId: string }` */
const actualName = icon.containing_frame?.containingStateGroup?.name;
const name = `${actualName}${variant}`;
return `${startCase(camelCase(name)).replace(/ /g, "")}.svg`;
}
return `${startCase(camelCase(icon.name)).replace(/ /g, "")}.svg`;
};
@@ -1,65 +0,0 @@
import { config } from "dotenv";
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
import { resolve } from "path";
import { fetchDownloadUrls, fetchIcon, fetchIcons } from "./fetch-icons.mjs";
import { resolveName } from "./icon-name.mjs";
import { makeConfig } from "./make-configs.mjs";
import { paginate } from "./paginate.mjs";
config();
/* https://www.figma.com/file/wEdyFjCQSBR3U7FvrMbPXa/Core-Icons-Next?node-id=277%3A1221&t=mUJzFvnsceYYXNL5-0 */
const iconFolder = "./icons";
if (!process.env.FIGMA_TOKEN) {
throw new Error("FIGMA_TOKEN not set in .env");
}
async function main() {
let icons = await fetchIcons();
const totalIcons = icons.length;
const { images } = await fetchDownloadUrls(
paginate(
icons.map((x) => x.node_id),
400,
).map((x) => x.join(",")),
);
if (existsSync(iconFolder)) {
rmSync(iconFolder, { recursive: true, force: true });
}
mkdirSync(iconFolder);
console.info(`Laster ned ${Object.keys(images).length} ikoner`);
for (const [key, value] of Object.entries(images)) {
const icon = await fetchIcon(value).catch((e) => {
throw e;
});
await new Promise((r) => setTimeout(r, 20));
icon &&
writeFileSync(
resolve(iconFolder, resolveName(icons.find((x) => x.node_id === key))),
icon,
{
encoding: "utf8",
},
);
}
makeConfig(icons, iconFolder);
readdirSync(iconFolder, (_, files) => {
if (files.length * 2 !== totalIcons) {
throw new Error(`Ikoner mangler!`);
}
});
console.info(`Success! ${Object.keys(images).length} ikoner lastet ned`);
}
main();
+106
View File
@@ -0,0 +1,106 @@
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
import { resolve } from "path";
import { fetchDownloadUrls, fetchIcons } from "./fetch-icons";
import { resolveName } from "./icon-name";
import { makeConfig } from "./make-configs";
/**
* https://www.figma.com/file/wEdyFjCQSBR3U7FvrMbPXa/Core-Icons-Next?node-id=277%3A1221&t=mUJzFvnsceYYXNL5-0
* This is the current source for all icons we are working with.
*/
const iconFolder = "./icons";
if (!process.env.FIGMA_TOKEN) {
throw new Error("FIGMA_TOKEN not set in .env");
}
main();
async function main() {
console.info("Started icon-update from Figma");
/* Icons are published as 'components' in Figma */
const publishedIconComponents = await fetchIcons();
/* When we have all the published icons, we can ask figma for URL's for downloading them as assets */
const imagesUrls = await fetchDownloadUrls(
publishedIconComponents.map((x) => x.node_id),
);
/* Lets do a clean install */
if (existsSync(iconFolder)) {
rmSync(iconFolder, { recursive: true, force: true });
}
mkdirSync(iconFolder);
console.group(`Processing ${Object.keys(imagesUrls).length} icons...`);
const fileNames = new Set<string>();
await Promise.all(
Object.entries(imagesUrls).map(async ([nodeId, iconUrl]) => {
/* Each icon is now a raw string for a complete SVG */
const iconSvg = await fetch(iconUrl)
.then((x) => x.text())
.catch((e) => {
throw e.message;
});
const matchingIcon = publishedIconComponents.find(
(x) => x.node_id === nodeId,
);
if (!matchingIcon) {
throw new Error(
`No matching icon found for ${nodeId}. It should not be possible to dowload icon without a matching icon in the list of icons fetched from Figma.`,
);
}
const fileName = resolveName(matchingIcon);
/*
* In some cases if multiple icons are published in Figma with the same name,
* we will end up overwriting the icon with the same name.
*/
if (fileNames.has(fileName)) {
console.warn(`Duplicate name detected: ${fileName}.`);
}
fileNames.add(fileName);
writeFileSync(resolve(iconFolder, resolveName(matchingIcon)), iconSvg, {
encoding: "utf8",
});
}),
);
if (fileNames.size !== publishedIconComponents.length) {
console.warn(
`Duplicate icon names from Figma leads to them being overwritten. This will cause the icon-library to be out of sync with Figma.`,
);
}
console.info(`Completed processing 🎉`);
console.groupEnd();
makeConfig(publishedIconComponents, iconFolder);
const filesInDir = readdirSync(iconFolder);
const svgFiles = filesInDir.filter((x) => x.endsWith(".svg"));
const ymlFiles = filesInDir.filter((x) => x.endsWith(".yml"));
if (
svgFiles.length !== publishedIconComponents.length ||
ymlFiles.length !== publishedIconComponents.length
) {
throw new Error(
`Icons (${svgFiles.length}) and configs (${ymlFiles.length}) written to directory does not match the expected amount of icons located in Figma (${publishedIconComponents.length}).\nThis is most likely caused by duplicate icon names from figma.`,
);
}
console.info(
`Success! A total of ${
Object.keys(imagesUrls).length
} were fetched and downloaded from Figma 🎉`,
);
}
@@ -1,17 +1,40 @@
import { PublishedComponent } from "@figma/rest-api-spec";
import { writeFileSync } from "fs";
import jsYaml from "js-yaml";
import { dump } from "js-yaml";
import { resolve } from "path";
import { resolveName } from "./icon-name.mjs";
import { resolveName } from "./icon-name";
export const makeConfig = (icons, folder) => {
icons.forEach((icon) => {
export type IconYml = {
name: string;
category: string;
sub_category: string;
keywords: string[];
variant: string;
updated_at: string;
created_at: string;
};
export const makeConfig = (
icons: PublishedComponent[],
dirLocation: string,
) => {
console.group("Creating icon yml...");
let counter = 0;
for (const icon of icons) {
const name = resolveName(icon).replace(".svg", "");
const keywords = icon.description
.split(",")
.map((x) => x.trim())
.filter(Boolean);
const config = {
if (!icon.containing_frame?.pageName || !icon.containing_frame?.name) {
throw new Error(
`Missing containing_frame.pageName or containing_frame.name for icon ${name}`,
);
}
const config: IconYml = {
name,
category: icon.containing_frame.pageName,
sub_category: icon.containing_frame.name,
@@ -33,14 +56,18 @@ export const makeConfig = (icons, folder) => {
.join("."),
};
const yml = jsYaml.dump(config, {
const yml = dump(config, {
noRefs: true,
skipInvalid: false,
quotingType: '"',
});
writeFileSync(resolve(folder, `${config.name}.yml`), yml, {
writeFileSync(resolve(dirLocation, `${config.name}.yml`), yml, {
encoding: "utf8",
});
});
counter++;
}
console.info(`Created ${counter} icon yml files`);
console.groupEnd();
};
@@ -1,12 +0,0 @@
export function paginate(arr, size) {
return arr.reduce((acc, val, i) => {
let idx = Math.floor(i / size);
if (!acc[idx]) {
acc[idx] = [];
}
let page = acc[idx];
page.push(val);
return acc;
}, []);
}
+1
View File
@@ -15,6 +15,7 @@ ymlList.forEach((file) => {
const ymlData = jsYaml.load(fs.readFileSync(`${basePath}/${file}`), {
schema: jsYaml.JSON_SCHEMA,
});
// biome-ignore lint/complexity/useOptionalChain: optional chain not supported in enviroment. Consider revriting to Typescript
if (ymlData.keywords && ymlData.keywords.includes("[ignore-docs]")) {
return;
}
@@ -18,7 +18,7 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@figma/plugin-typings": "^1.84.0",
"@figma/plugin-typings": "^1.100.2",
"@svgr/core": "^8.1.0",
"@types/node": "^18.15.11",
"@types/postcss-url": "^10.0.4",
@@ -27,8 +27,8 @@
"@vitejs/plugin-react": "^4.2.1",
"postcss": "^8.4.31",
"postcss-url": "^10.1.3",
"typescript": "^5.1.6",
"vite": "^5.1.7",
"typescript": "5.5.4",
"vite": "^5.4.6",
"vite-plugin-singlefile": "^1.0.0",
"vite-plugin-svgr": "^4.2.0"
}
@@ -109,7 +109,7 @@ const App = () => {
icon.removeEventListener("dragend", dragEvent);
}
};
}, [categories]);
}, []);
return (
<main tabIndex={-1} className="wrapper">
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "7.0.1",
"version": "7.3.0",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
@@ -62,28 +62,30 @@
"update-metadata": "node config/metadata.js",
"write-packagejson": "echo '{\"type\": \"module\"}' > dist/react/esm/package.json",
"build": "yarn copy && yarn create-icons && concurrently \"tsc\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" && node config/cleanTypes.js && yarn update-metadata && yarn parse-svg",
"fetch-new:icons": "node config/figma/index.mjs",
"fetch-new:icons": "tsx --env-file=.env config/figma/index.ts",
"zip:icons": "node config/zip.js",
"test": "vitest run",
"test:watch": "vitest watch"
},
"devDependencies": {
"@figma/rest-api-spec": "^0.19.0",
"@svgr/cli": "8.1.0",
"@types/js-yaml": "^4.0.9",
"@types/react": "^18.3.11",
"adm-zip": "^0.5.10",
"concurrently": "7.2.1",
"concurrently": "9.0.1",
"copyfiles": "^2.4.1",
"cross-zip-cli": "1.0.0",
"dotenv": "^10.0.0",
"fast-glob": "3.2.11",
"hast-util-select": "5.0.5",
"js-yaml": "^4.1.0",
"lodash.camelcase": "4.3.0",
"lodash.startcase": "4.4.0",
"lodash": "^4.17.21",
"prettier": "3.1.1",
"react": "^18.0.0",
"rehype-parse": "8.0.4",
"tsc-alias": "1.8.8",
"typescript": "^5.1.6",
"tsx": "^4.7.1",
"typescript": "5.5.4",
"unified": "10.1.2",
"vitest": "^1.2.2"
}
+20
View File
@@ -1,5 +1,25 @@
# @navikt/aksel-stylelint
## 7.3.0
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
### Patch Changes
- List: :boom: Removed class names "navds-list--nested" and "navds-list\_\_item-content" ([#3184](https://github.com/navikt/aksel/pull/3184))
## 7.1.0
### Minor Changes
- Pagination: :boom: Added some classNames to list of deprecated (removed) classes. ([#3164](https://github.com/navikt/aksel/pull/3164))
## 7.0.1
## 7.0.0
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "7.0.1",
"version": "7.3.0",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
"repository": {
@@ -35,14 +35,14 @@
"dev": "yarn watch:lint"
},
"devDependencies": {
"@navikt/ds-css": "^7.0.1",
"@navikt/ds-tokens": "^7.0.1",
"concurrently": "7.2.1",
"@navikt/ds-css": "^7.3.0",
"@navikt/ds-tokens": "^7.3.0",
"concurrently": "9.0.1",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0",
"stylelint": "^14.8.5",
"stylelint-test-rule-node": "^0.2.1",
"tsx": "^4.7.1",
"typescript": "^5.1.6"
"typescript": "5.5.4"
}
}
@@ -60,4 +60,16 @@ export const deprecations: DeprecatedList = [
classes: ["navds-textarea__wrapper"],
message: "Removed in v6.0.0",
},
{
classes: [
"navds-pagination__prev-next-icon",
"navds-pagination__next-text",
"navds-pagination__prev-text",
],
message: "These classes had no effect and were removed in v7.1.0",
},
{
classes: ["navds-list--nested", "navds-list__item-content"],
message: "Removed in v7.1.1",
},
];
+42
View File
@@ -1,5 +1,47 @@
# @navikt/aksel
## 7.3.0
### Patch Changes
- Updated dependencies [[`20ced9b`](https://github.com/navikt/aksel/commit/20ced9b084a1f4bb56a1e1c07ef92010c9f4d71c), [`536f088`](https://github.com/navikt/aksel/commit/536f088b97ab9ae139d6974deb2a3c69e4fb6db3)]:
- @navikt/ds-css@7.3.0
## 7.2.1
### Patch Changes
- Updated dependencies [[`6ddea4eaf`](https://github.com/navikt/aksel/commit/6ddea4eaf2e37f20f93d75e1a3a327b038fb209b)]:
- @navikt/ds-css@7.2.1
## 7.2.0
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@7.2.0
## 7.1.2
### Patch Changes
- Updated dependencies [[`95c84e1d0`](https://github.com/navikt/aksel/commit/95c84e1d014d880bc22eb0053a875771e8399540)]:
- @navikt/ds-css@7.1.2
## 7.1.1
### Patch Changes
- Updated dependencies [[`ca1d9be88`](https://github.com/navikt/aksel/commit/ca1d9be88f45f936ee8546af146f8783d8c43db2), [`73b2bca15`](https://github.com/navikt/aksel/commit/73b2bca156072c945c2fc4e4e849f50a00398eb6), [`d278060e5`](https://github.com/navikt/aksel/commit/d278060e59b17638cbda22dc194e89b01dc2113e)]:
- @navikt/ds-css@7.1.1
## 7.1.0
### Patch Changes
- Updated dependencies [[`9c1eba445`](https://github.com/navikt/aksel/commit/9c1eba44508f295cc6261ff96822b18557bd7ddc)]:
- @navikt/ds-css@7.1.0
## 7.0.1
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "7.0.1",
"version": "7.3.0",
"description": "Aksel command line interface. Handles css-imports, codemods and more",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
"dependencies": {
"@navikt/ds-css": "7.0.1",
"@navikt/ds-css": "7.3.0",
"axios": "1.7.4",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
@@ -45,7 +45,7 @@
},
"devDependencies": {
"rimraf": "6.0.1",
"typescript": "^5.1.6",
"typescript": "5.5.4",
"vitest": "^1.2.2"
},
"sideEffects": false,
+1
View File
@@ -1,6 +1,7 @@
import { applyTransform } from "jscodeshift/dist/testUtils";
import fs from "node:fs";
import path from "node:path";
// eslint-disable-next-line import/default
import prettier from "prettier";
import { describe, expect, test } from "vitest";
+1 -1
View File
@@ -43,7 +43,7 @@ export function validateGit(options: any, program: Command) {
try {
clean = isGitClean.sync(process.cwd());
} catch (err: any) {
if (err && err.stderr && err.stderr.indexOf("Not a git repository") >= 0) {
if (err?.stderr && err.stderr.indexOf("Not a git repository") >= 0) {
clean = true;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ export async function getAllVersions() {
const npmPackageData = await axios.get(
`https://registry.npmjs.org/@navikt/ds-css`,
);
return Object.keys(npmPackageData.data["versions"]);
return Object.keys(npmPackageData.data.versions);
} catch (e) {
console.error(e);
return [];
+1 -1
View File
@@ -137,7 +137,7 @@ export async function cssImportsCommand() {
let foundComponents: string[] = [];
if (answers["autoscan"] === "yes") {
if (answers.autoscan === "yes") {
foundComponents = await new Promise((resolve) => {
exec(`node ${__dirname}/scan-code.js ${answers.scandir}`, (_, stdout) => {
resolve(
+40
View File
@@ -1,5 +1,45 @@
# @navikt/ds-css
## 7.3.0
### Minor Changes
- ActionMenu: :tada: New component! Replaces DropdownMenu as the go-to solution for floating menus. Includes support for grouping, sub-menus, checkboxes, radios and dividers. Read more in the [documentation](https://aksel.nav.no/komponenter/core/actionmenu). ([#3081](https://github.com/navikt/aksel/pull/3081))
### Patch Changes
- List.Item: Center icon horizontally inside it's container ([#3253](https://github.com/navikt/aksel/pull/3253))
## 7.2.1
### Patch Changes
- Combobox: Support PageUp/PageDown in dropdown list. ([#3158](https://github.com/navikt/aksel/pull/3158))
## 7.2.0
## 7.1.2
### Patch Changes
- List: Explicitly set list-style on ol to override reset-CSS ([#3202](https://github.com/navikt/aksel/pull/3202))
## 7.1.1
### Patch Changes
- List: Add indentation and remove overly verbose aria attributes ([#3184](https://github.com/navikt/aksel/pull/3184))
- Datepicker/Select: :lipstick: dim non-interactable icons when readonly is set. ([#3187](https://github.com/navikt/aksel/pull/3187))
- Combobox: :bug: implement missing readonly and disabled ([#3180](https://github.com/navikt/aksel/pull/3180))
## 7.1.0
### Patch Changes
- Accordion: Remove 'relative' positioning when focused. This resolves some issues where floating elements like popover ended up clipping. ([`9c1eba445`](https://github.com/navikt/aksel/commit/9c1eba44508f295cc6261ff96822b18557bd7ddc))
## 7.0.1
### Patch Changes
-4
View File
@@ -2,10 +2,6 @@
--__ac-accordion-header-shadow-color: var(--ac-accordion-header-border, var(--a-border-divider));
}
.navds-accordion__item:focus-within {
position: relative;
}
/*************************
* Header *
*************************/
+227
View File
@@ -0,0 +1,227 @@
.navds-action-menu__content {
overflow: hidden;
box-shadow: var(--a-shadow-medium);
border-radius: var(--a-border-radius-large);
}
/* stylelint-disable csstools/value-no-unknown-custom-properties */
.navds-action-menu__content > .navds-action-menu__content-inner {
--__ac-action-menu-content-p: var(--a-spacing-2);
--__ac-action-menu-item-pr: var(--a-spacing-3);
--__ac-action-menu-item-pl: var(--a-spacing-2);
--__ac-action-menu-item-height: 2rem;
border-radius: var(--a-border-radius-large);
background-color: var(--a-surface-default);
min-width: 128px;
max-width: min(95vw, 640px);
transform-origin: var(--__ac-action-menu-content-transform-origin);
animation-duration: 160ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
padding: var(--__ac-action-menu-content-p);
overflow: auto;
max-height: var(--__ac-action-menu-content-available-height);
}
.navds-action-menu__content:where([data-state="open"]):where([data-side="bottom"]) {
animation-name: aksel-action-from-bottom, aksel-action-fade-in;
}
.navds-action-menu__content:where([data-state="open"]):where([data-side="top"]) {
animation-name: aksel-action-from-top, aksel-action-fade-in;
}
.navds-action-menu__content:where([data-state="open"]):where([data-side="left"]) {
animation-name: aksel-action-from-left, aksel-action-fade-in;
}
.navds-action-menu__content:where([data-state="open"]):where([data-side="right"]) {
animation-name: aksel-action-from-right, aksel-action-fade-in;
}
@keyframes aksel-action-from-bottom {
from {
transform: translateY(-4px);
}
to {
transform: translateY(0);
}
}
@keyframes aksel-action-from-top {
from {
transform: translateY(4px);
}
to {
transform: translateY(0);
}
}
@keyframes aksel-action-from-left {
from {
transform: translateX(4px);
}
to {
transform: translateY(0);
}
}
@keyframes aksel-action-from-right {
from {
transform: translateX(-4px);
}
to {
transform: translateY(0);
}
}
@keyframes aksel-action-fade-in {
from {
opacity: 0.01;
}
to {
opacity: 1;
}
}
.navds-action-menu__item {
display: flex;
align-items: center;
gap: var(--a-spacing-2);
min-height: var(--__ac-action-menu-item-height);
cursor: default;
border-radius: var(--a-border-radius-medium);
position: relative;
padding-left: var(--__ac-action-menu-item-pl);
padding-right: var(--__ac-action-menu-item-pr);
font-size: var(--a-font-size-medium);
scroll-margin-block: var(--__ac-action-menu-content-p);
line-height: 1.5;
color: var(--a-text-default);
text-decoration: none;
}
.navds-action-menu__item svg {
flex-shrink: 0;
}
.navds-action-menu__item--has-icon {
--__ac-action-menu-item-pl: var(--a-spacing-6);
}
.navds-action-menu__sub-trigger {
--__ac-action-menu-item-pr: var(--a-spacing-05);
}
.navds-action-menu__item:focus {
outline: none;
background-color: var(--a-surface-action-subtle-hover);
color: var(--a-text-default);
}
.navds-action-menu__item--danger {
color: var(--a-text-danger);
}
.navds-action-menu__item--danger:focus {
background-color: var(--a-surface-danger-subtle);
}
.navds-action-menu__marker {
display: flex;
align-items: center;
gap: var(--a-spacing-1);
}
.navds-action-menu__marker--right {
margin-left: auto;
padding-left: var(--a-spacing-4);
}
.navds-action-menu__marker--left {
position: absolute;
left: 0;
width: var(--__ac-action-menu-item-pl);
display: inline-flex;
justify-content: center;
}
.navds-action-menu__marker-icon svg {
font-size: 18px;
flex-shrink: 0;
}
.navds-action-menu__shortcut {
background-color: var(--a-surface-neutral-subtle);
color: var(--a-text-default);
border-radius: var(--a-border-radius-small);
padding: 0 var(--a-spacing-1);
min-width: 1.125rem;
height: 1.125rem;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: var(--a-font-size-small);
}
.navds-action-menu__label {
display: flex;
align-items: center;
min-height: calc(var(--__ac-action-menu-item-height) - 6px);
padding-right: var(--__ac-action-menu-item-pr);
padding-left: var(--__ac-action-menu-item-pl);
color: var(--a-text-subtle);
border-radius: var(--a-border-radius-medium);
user-select: none;
cursor: default;
font-size: var(--a-font-size-small);
}
.navds-action-menu__divider {
height: 1px;
margin-block: var(--a-spacing-2);
background-color: var(--a-border-divider);
}
.navds-action-menu__indicator {
display: grid;
place-content: center;
}
.navds-action-menu__indicator-icon {
font-size: 14px;
}
.navds-action-menu__indicator-icon--unchecked,
.navds-action-menu__indicator-icon--checked,
.navds-action-menu__indicator-icon--indeterminate {
display: none;
}
.navds-action-menu__indicator[data-state="unchecked"] .navds-action-menu__indicator-icon--unchecked {
display: block;
}
.navds-action-menu__indicator:where([data-state="checked"]) .navds-action-menu__indicator-icon--checked {
display: block;
}
.navds-action-menu__indicator:where([data-state="indeterminate"]) .navds-action-menu__indicator-icon--indeterminate {
display: block;
}
.navds-action-menu__item:where([aria-disabled="true"]) {
color: var(--a-text-subtle);
opacity: 0.5;
pointer-events: none;
}
.navds-action-menu__sub-trigger:where([data-state="open"]) {
background-color: var(--a-surface-neutral-subtle);
}
+3 -1
View File
@@ -47,6 +47,8 @@ legend {
white-space: normal;
}
/* Make elements with the HTML hidden attribute stay hidden by default. */
[hidden] {
display: none;
display: none !important;
}
+5
View File
@@ -69,6 +69,11 @@ const StyleMappings = {
dependencies: [typoCss],
},
{ component: "Alert", main: "alert.css", dependencies: [typoCss] },
{
component: "ActionMenu",
main: "action-menu.css",
dependencies: [typoCss],
},
{
component: "BodyLong",
main: typoCss,
-1
View File
@@ -31,7 +31,6 @@
@media (forced-colors: active) {
.navds-copybutton {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
+25
View File
@@ -0,0 +1,25 @@
# Template for writing new CSS
## File setup
1. Create new CSS-file for component: "alert.darkside.css", "button.darkside.css" etc. Remember to add `.darkside.` for easier lookup.
2. Import file in "darkside/index.css": `@import "./alert.darkside.css`
Files will not be included in `/dist` for now to avoid it potentially polluting production-code. This will need to be revisited when we want to expose the new systems outside the team.
## Development
1. Write new CSS for component. Use same prefix `.navds` as before.
2. Run storybook and change from "Default (old)" to "Darkside (new)" in toolbar. You are now developing new CSS 🎉
## Syntax
- Test using outline + outline-offset for focus-markings.
## Checklist
- Add new CSS-files under `core/css/darkside`.
- New files should include `.darkside.` for easier lookup.
- Import new CSS-files in `core/css/darkside/index.css`
- New CSS-selectors should have `.navds` as prefix, same as original code.
- Test changes in Storybook by changing mode in toolbar from "default (old)" -> "darkside (new)"
+129
View File
@@ -0,0 +1,129 @@
/* cyrillic-ext */
@font-face {
font-family: "Source Sans 3";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic-cyrillic_ext.woff2) format("woff2");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: "Source Sans 3";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic-cyrillic.woff2) format("woff2");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* latin-ext */
@font-face {
font-family: "Source Sans 3";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic-latin_ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Source Sans 3";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: "Source Sans 3";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal-cyrillic_ext.woff2) format("woff2");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: "Source Sans 3";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal-cyrillic.woff2) format("woff2");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* latin-ext */
@font-face {
font-family: "Source Sans 3";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal-latin_ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Source Sans 3";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Legacy support */
/* latin-ext */
@font-face {
font-family: "Source Sans Pro";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic-latin_ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Source Sans Pro";
font-style: italic;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-italic.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal-latin_ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400 600;
font-display: swap;
src: url(https://cdn.nav.no/aksel/fonts/SourceSans3-normal.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@@ -0,0 +1,75 @@
/* ==========================================================================
Print styles copied from https://github.com/h5bp/html5-boilerplate/blob/master/dist/css/style.css
Inlined to avoid the additional HTTP request:
https://www.phpied.com/delay-loading-your-print-css/
========================================================================== */
@media print {
*,
*::before,
*::after {
background: #fff !important;
color: #000 !important;
/* Black prints faster */
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]::after {
content: " (" attr(href) ")";
}
abbr[title]::after {
content: " (" attr(title) ")";
}
/*
* Don't show links that are fragment identifiers,
* or use the `javascript:` pseudo protocol
*/
a[href^="#"]::after,
a[href^="javascript:"]::after {
content: "";
}
pre {
white-space: pre-wrap !important;
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
/*
* Printing Tables:
* https://web.archive.org/web/20180815150934/http://css-discuss.incutio.com/wiki/Printing_Tables
*/
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}
@@ -0,0 +1,148 @@
/* Modern-normalize: https://github.com/sindresorhus/modern-normalize/blob/main/modern-normalize.css */
/* Better box-model */
*,
::before,
::after {
box-sizing: border-box;
}
html {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
line-height: 1.15; /* Correct the line height in all browsers. */
-webkit-text-size-adjust: 100%; /* Prevent adjustments of font size after orientation changes in iOS. */
tab-size: 4; /* Use a more readable tab size (opinionated). */
}
body {
margin: 0; /* Remove the margin in all browsers. */
}
b,
strong {
font-weight: bolder;
}
/**
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
2. Correct the odd 'em' font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/**
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
*/
table {
border-color: currentColor;
}
/**
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
*/
legend {
padding: 0;
}
/**
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/**
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/**
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to 'inherit' in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
@@ -0,0 +1,10 @@
/* To support nested theming, we want to declare color on each scoped elements */
:root,
:host,
.light {
color: var(--a-text-default);
}
.dark {
color: var(--a-text-default);
}
@@ -0,0 +1,65 @@
[hidden] {
display: none !important;
}
/* https://web.dev/prefers-reduced-motion/ */
@media (prefers-reduced-motion: reduce) {
*:not(.navds-loader *):not(.navds-loader):not(.navds-progress-bar *):not(.navds-progress-bar),
::before,
::after {
animation-delay: -1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
background-attachment: initial !important;
scroll-behavior: auto !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}
}
/*
* From https://github.com/h5bp/html5-boilerplate/blob/master/dist/css/style.css
*
* Hide only visually, but have it available for screen readers:
* https://snook.ca/archives/html_and_css/hiding-content-for-accessibility
*
* 1. For long content, line feeds are not interpreted as spaces and small width
* causes content to wrap 1 word per line:
* https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
*/
.sr-only,
.navds-sr-only {
border: 0;
clip: rect(0, 0, 0, 0);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
}
/*
* From https://github.com/h5bp/html5-boilerplate/blob/master/dist/css/style.css
*
* Extends the .sr-only class to allow the element
* to be focusable when navigated to via the keyboard:
* https://www.drupal.org/node/897638
*/
.sr-only.focusable:active,
.sr-only.focusable:focus,
.navds-sr-only.focusable:active,
.navds-sr-only.focusable:focus {
clip: auto;
clip-path: none;
height: auto;
margin: 0;
overflow: visible;
position: static;
white-space: inherit;
width: auto;
}
@@ -0,0 +1,6 @@
/* stylelint-disable csstools/value-no-unknown-custom-properties */
/* stylelint-disable aksel/design-token-exists */
.navds-button {
background-color: var(--a-bg-accent-strong);
}
+17
View File
@@ -0,0 +1,17 @@
@charset "UTF-8";
@layer aksel.reset, aksel.typography, aksel.theme, aksel.components, aksel.utilities;
/* CSS foundations */
@import url("./baseline/reset.darkside.css") layer(aksel.reset);
@import url("./baseline/print.darkside.css") layer(aksel.reset);
@import url("./baseline/fonts.darkside.css") layer(aksel.typography);
@import url("./baseline/theme.darkside.css") layer(aksel.theme);
@import url("./typography.darkside.css") layer(aksel.typography);
@import url("./baseline/utilities.darkside.css") layer(aksel.utilities);
/* Theming */
@import url("@navikt/ds-tokens/dist/darkside/tokens.css") layer(aksel.theme);
/* Components */
@import url("./button.darkside.css") layer(aksel.components);
@@ -0,0 +1,6 @@
body,
:host {
font-family: "Source Sans 3", "Source Sans Pro", Arial, sans-serif;
line-height: 1.333;
font-size: 1.125rem;
}
+1
View File
@@ -281,6 +281,7 @@
/* Readonly */
.navds-date__field--readonly .navds-date__field-button {
cursor: default;
color: var(--a-gray-500);
}
.navds-date__caption-button {
+31
View File
@@ -20,6 +20,36 @@
--__ac-combobox-input-height: 1.5rem;
}
.navds-combobox--disabled {
opacity: 0.3;
}
.navds-combobox--disabled .navds-combobox__wrapper *:hover {
cursor: not-allowed;
}
.navds-combobox--disabled .navds-text-field__input {
border: 1px solid var(--a-border-default);
}
.navds-combobox--disabled .navds-combobox__selected-options {
pointer-events: none;
}
.navds-combobox--readonly {
pointer-events: none;
}
.navds-combobox--readonly .navds-combobox__button-toggle-list {
color: var(--a-gray-500);
}
.navds-combobox--readonly .navds-text-field__input,
.navds-combobox--readonly .navds-combobox__input {
background-color: var(--a-surface-subtle);
border-color: var(--a-border-subtle);
}
.navds-combobox__button-clear svg,
.navds-combobox__button-toggle-list svg,
.navds-combobox__list svg {
@@ -233,6 +263,7 @@
border-radius: var(--a-border-radius-medium);
background-color: var(--ac-combobox-list-bg, var(--a-surface-default));
color: var(--ac-combobox-list-text, var(--a-text-default));
overscroll-behavior: contain;
}
.navds-combobox__list--closed {
+4
View File
@@ -109,3 +109,7 @@
border-color: var(--a-border-subtle);
cursor: default;
}
.navds-select--readonly .navds-select__chevron {
color: var(--a-gray-500);
}
+1
View File
@@ -8,6 +8,7 @@
@import "chips.css";
@import "copybutton.css";
@import "dropdown.css";
@import "action-menu.css";
@import "expansioncard.css";
@import "guide-panel.css";
@import "form/index.css";
-1
View File
@@ -10,7 +10,6 @@
@media (forced-colors: active) {
.navds-internalheader {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
+61 -71
View File
@@ -4,92 +4,82 @@
margin-block: var(--a-spacing-4);
}
.navds-list--nested ul,
.navds-list--nested ol {
margin-block: var(--a-spacing-2);
}
.navds-list--small ul,
.navds-list--small ol {
margin-block: var(--a-spacing-3);
}
.navds-list li {
margin-block-end: var(--a-spacing-2);
}
.navds-list:last-child > ul > li:last-child,
.navds-list:last-child > ol > li:last-child {
margin-bottom: 0;
}
.navds-list ul .navds-list__item {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--a-spacing-3);
padding-left: 0;
}
.navds-list ul .navds-list__item::marker {
content: none;
}
.navds-list ol .navds-list__item-marker--icon {
grid-column: 1 / 2;
display: flex;
}
.navds-list .navds-list__item-marker--bullet {
display: flex;
align-items: center;
height: var(--a-font-line-height-xlarge);
grid-column: 1 / 2;
color: var(--ac-list-marker-ul-color, var(--ac-list-marker-color, var(--a-icon-default)));
}
.navds-list--small .navds-list__item-marker--bullet {
height: var(--a-font-line-height-large);
}
.navds-list__item-marker--icon {
color: var(--ac-list-marker-icon-color, var(--ac-list-marker-color, var(--a-icon-default)));
display: flex;
align-items: center;
height: var(--a-font-line-height-xlarge);
padding-block-end: 1px;
font-size: 1.5rem;
}
.navds-list--small .navds-list__item-marker--icon {
height: var(--a-font-line-height-large);
}
.navds-list__item-marker--icon svg {
flex-shrink: 0;
}
.navds-list ul .navds-list__item-content {
grid-column: 2 / 3;
.navds-list .navds-list :where(ul, ol) {
margin-block: var(--a-spacing-2) 0;
}
.navds-list ol {
padding-left: var(--a-spacing-2);
list-style: decimal;
list-style: decimal; /* This is the default value, but some frameworks have `ol,ul { list-style:none }` */
padding-left: 1.7rem;
}
.navds-list ol li {
padding-left: var(--a-spacing-1);
margin-left: var(--a-spacing-2);
.navds-list ol > .navds-list__item {
padding-left: 0.3rem;
}
/* SAFARI HACK START */
/* stylelint-disable selector-type-no-unknown */
_::-webkit-full-page-media,
_:future,
:root .navds-list ol {
padding-left: 2.1rem;
}
_::-webkit-full-page-media,
_:future,
:root .navds-list ol .navds-list__item {
padding-left: 0;
}
/* SAFARI HACK END */
/* stylelint-enable selector-type-no-unknown */
.navds-list__item {
margin-block-end: var(--a-spacing-2);
}
.navds-list__item:last-child {
margin-block-end: 0;
}
.navds-list ul > .navds-list__item {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--a-spacing-2);
}
.navds-list ol li::marker {
font-weight: var(--a-font-weight-bold);
font-size: var(--a-font-size-large);
color: var(--ac-list-marker-ol-color, var(--ac-list-marker-color, var(--a-icon-default)));
line-height: var(--a-font-line-height-xlarge);
}
.navds-list--small ol li::marker {
font-size: var(--a-font-size-small);
line-height: var(--a-font-line-height-large);
.navds-list__item-marker {
width: 1.5rem;
}
.navds-list ul > li > .navds-list__item-marker {
display: flex;
align-items: center;
height: var(--a-font-line-height-xlarge);
}
.navds-list--small ul > li > .navds-list__item-marker {
height: var(--a-font-line-height-large);
}
.navds-list__item-marker--bullet {
padding-left: 0.8rem;
color: var(--ac-list-marker-ul-color, var(--ac-list-marker-color, var(--a-icon-default)));
}
.navds-list__item-marker--icon {
font-size: 1.5rem;
justify-content: center;
color: var(--ac-list-marker-icon-color, var(--ac-list-marker-color, var(--a-icon-default)));
}
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "7.0.1",
"version": "7.3.0",
"description": "CSS for NAV Designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -27,10 +27,11 @@
"css:get-version": "node config/get-version.js"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.0.1",
"@navikt/ds-tokens": "^7.3.0",
"autoprefixer": "^10.4.20",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
"lodash": "^4.17.21",
"nodemon": "^3.0.1",
"normalize.css": "^8.0.1",
"postcss": "^8.4.31",
-1
View File
@@ -19,7 +19,6 @@
@media (forced-colors: active) {
.navds-read-more__button {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
+80
View File
@@ -1,5 +1,85 @@
# @navikt/ds-react
## 7.3.0
### Minor Changes
- ActionMenu: :tada: New component! Replaces DropdownMenu as the go-to solution for floating menus. Includes support for grouping, sub-menus, checkboxes, radios and dividers. Read more in the [documentation](https://aksel.nav.no/komponenter/core/actionmenu). ([#3081](https://github.com/navikt/aksel/pull/3081))
### Patch Changes
- Accordion: Remove redundant icon title ([#3230](https://github.com/navikt/aksel/pull/3230))
- Modal: Close button now avoids triggering when repeated keystrokes are detected. This helps stop accidental closing of Modal when 'Enter'-key is held down. ([#3081](https://github.com/navikt/aksel/pull/3081))
- Updated dependencies []:
- @navikt/ds-tokens@7.3.0
- @navikt/aksel-icons@7.3.0
## 7.2.1
### Patch Changes
- Combobox: Disable autocomple in Firefox on Android to prevent bug ([#3201](https://github.com/navikt/aksel/pull/3201))
- Combobox: Support PageUp/PageDown in dropdown list. ([#3158](https://github.com/navikt/aksel/pull/3158))
- Updated dependencies []:
- @navikt/ds-tokens@7.2.1
- @navikt/aksel-icons@7.2.1
## 7.2.0
### Minor Changes
- FileUpload: :boom: Remove UNSAFE-prefix ([#3205](https://github.com/navikt/aksel/pull/3205))
- List: :sparkles: New size 'large' ([#3207](https://github.com/navikt/aksel/pull/3207))
### Patch Changes
- Performance: :zap: Optimized memoization for rerendring in some components. ([#3197](https://github.com/navikt/aksel/pull/3197))
- Updated dependencies []:
- @navikt/ds-tokens@7.2.0
- @navikt/aksel-icons@7.2.0
## 7.1.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@7.1.2
- @navikt/aksel-icons@7.1.2
## 7.1.1
### Patch Changes
- List: Add indentation and remove overly verbose aria attributes ([#3184](https://github.com/navikt/aksel/pull/3184))
- Combobox: :bug: implement missing readonly and disabled ([#3180](https://github.com/navikt/aksel/pull/3180))
- Updated dependencies []:
- @navikt/ds-tokens@7.1.1
- @navikt/aksel-icons@7.1.1
## 7.1.0
### Minor Changes
- Pagination: :lipstick: Updated font-weight to 600 and marked 'page'-prop on 'Item' as deprecated. ([#3164](https://github.com/navikt/aksel/pull/3164))
### Patch Changes
- Combobox: :label: Better typing for maxSelected prop ([#3169](https://github.com/navikt/aksel/pull/3169))
- ErrorSummary: Remove aria-attributes. ([#3171](https://github.com/navikt/aksel/pull/3171))
- Updated dependencies []:
- @navikt/ds-tokens@7.1.0
- @navikt/aksel-icons@7.1.0
## 7.0.1
### Patch Changes
+17 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "7.0.1",
"version": "7.3.0",
"description": "React components from the Norwegian Labour and Welfare Administration.",
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
"license": "MIT",
@@ -50,6 +50,16 @@
"default": "./cjs/accordion/index.js"
}
},
"./ActionMenu": {
"import": {
"types": "./esm/overlays/action-menu/index.d.ts",
"default": "./esm/overlays/action-menu/index.js"
},
"require": {
"types": "./cjs/overlays/action-menu/index.d.ts",
"default": "./cjs/overlays/action-menu/index.js"
}
},
"./Alert": {
"import": {
"types": "./esm/alert/index.d.ts",
@@ -583,7 +593,7 @@
"./package.json": "./package.json"
},
"scripts": {
"docgen": "yarn ts-node ../../../scripts/docgen.ts",
"docgen": "yarn tsx ../../../scripts/docgen.ts",
"write-packagejson": "echo '{\"type\": \"module\"}' > esm/package.json",
"clean": "rimraf cjs esm",
"build": "concurrently \"tsc -p tsconfig.build.json\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" ",
@@ -594,8 +604,8 @@
"dependencies": {
"@floating-ui/react": "0.25.4",
"@floating-ui/react-dom": "^2.0.9",
"@navikt/aksel-icons": "^7.0.1",
"@navikt/ds-tokens": "^7.0.1",
"@navikt/aksel-icons": "^7.3.0",
"@navikt/ds-tokens": "^7.3.0",
"clsx": "^2.1.0",
"date-fns": "^3.0.0",
"react-day-picker": "8.10.0"
@@ -605,18 +615,16 @@
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.2.0",
"@types/faker": "5.5.8",
"concurrently": "7.2.1",
"faker": "5.5.3",
"concurrently": "9.0.1",
"fast-glob": "3.2.11",
"jsdom": "24.0.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.3.0",
"rimraf": "6.0.1",
"swr": "^1.1.2",
"ts-node": "^10.9.1",
"tsc-alias": "1.8.8",
"typescript": "^5.1.6",
"tsx": "^4.19.1",
"typescript": "5.5.4",
"vitest": "^1.2.2"
},
"peerDependencies": {
@@ -38,7 +38,6 @@ const AccordionHeader = forwardRef<HTMLButtonElement, AccordionHeaderProps>(
<span className="navds-accordion__icon-wrapper">
<ChevronDownIcon
className="navds-accordion__header-chevron"
title="Vis mer"
aria-hidden
/>
</span>
+11 -20
View File
@@ -9,6 +9,7 @@ import {
} from "@navikt/aksel-icons";
import { Button } from "../button";
import { BodyLong } from "../typography";
import { useI18n } from "../util/i18n/i18n.context";
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
/**
@@ -54,25 +55,13 @@ export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
onClose?: () => void;
}
const Icon = ({ variant, ...props }) => {
switch (variant) {
case "error":
return <XMarkOctagonFillIcon title="Feil" {...props} />;
case "warning":
return <ExclamationmarkTriangleFillIcon title="Advarsel" {...props} />;
case "info":
return <InformationSquareFillIcon title="Informasjon" {...props} />;
case "success":
return <CheckmarkCircleFillIcon title="Suksess" {...props} />;
default:
return null;
}
const IconMap = {
error: XMarkOctagonFillIcon,
warning: ExclamationmarkTriangleFillIcon,
info: InformationSquareFillIcon,
success: CheckmarkCircleFillIcon,
};
export interface AlertContextProps {
size: "medium" | "small";
}
/**
* A component for displaying alerts
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/alert)
@@ -98,6 +87,8 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
},
ref,
) => {
const translate = useI18n("Alert");
const Icon = IconMap[variant];
return (
<div
{...rest}
@@ -114,7 +105,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
},
)}
>
<Icon variant={variant} className="navds-alert__icon" />
<Icon title={translate(variant)} className="navds-alert__icon" />
<BodyLong
as="div"
size={size}
@@ -137,8 +128,8 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
<XMarkIcon
title={
["error", "warning"].includes(variant)
? "Lukk varsel"
: "Lukk melding"
? translate("closeAlert")
: translate("closeMessage")
}
/>
}
@@ -190,3 +190,19 @@ export const ChromaticForcedColors: Story = {
chromatic: { disable: false, forcedColors: "active" },
},
};
export const ChromaticDarksideDark: Story = {
render: Chromatic.render,
parameters: {
chromatic: { disable: false },
},
globals: { theme: "dark", mode: "darkside" },
};
export const ChromaticDarksideLight: Story = {
render: Chromatic.render,
parameters: {
chromatic: { disable: false },
},
globals: { theme: "light", mode: "darkside" },
};
+4 -4
View File
@@ -58,7 +58,7 @@ export const Size: Story = {
render: () => (
<VStack gap="4">
{SIZES.map((size) => (
<>
<React.Fragment key={size}>
<h3>{size}</h3>
<Chat
avatar="ON"
@@ -75,7 +75,7 @@ export const Size: Story = {
laboris labore nisi ut.
</Chat.Bubble>
</Chat>
</>
</React.Fragment>
))}
</VStack>
),
@@ -85,7 +85,7 @@ export const Variants: Story = {
render: () => (
<VStack gap="4">
{VARIANTS.map((variant) => (
<>
<React.Fragment key={variant}>
<h3>{variant}</h3>
<Chat
avatar="NAV"
@@ -97,7 +97,7 @@ export const Variants: Story = {
Aute minim nisi sunt mollit duis sunt nulla minim non proident.
</Chat.Bubble>
</Chat>
</>
</React.Fragment>
))}
</VStack>
),
+13 -9
View File
@@ -2,6 +2,7 @@ import cl from "clsx";
import React, { forwardRef } from "react";
import { XMarkIcon } from "@navikt/aksel-icons";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useI18n } from "../util/i18n/i18n.context";
export interface ChipsRemovableProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
@@ -11,15 +12,15 @@ export interface ChipsRemovableProps
* @default "action"
*/
variant?: "action" | "neutral";
/**
* Replaces label read for screen-readers
* @default "slett filter"
*/
removeLabel?: string;
/**
* Click callback
*/
onDelete?: () => void;
/**
* Replaces label read for screen-readers
* @default "slett"
*/
removeLabel?: string;
}
export const RemovableChips = forwardRef<
@@ -28,17 +29,18 @@ export const RemovableChips = forwardRef<
>(
(
{
className,
children,
variant = "action",
removeLabel = "slett",
onDelete,
type = "button",
removeLabel,
className,
onClick,
type = "button",
...rest
},
ref,
) => {
const translate = useI18n("Chips");
return (
<button
{...rest}
@@ -49,7 +51,9 @@ export const RemovableChips = forwardRef<
className,
`navds-chips__removable--${variant}`,
)}
aria-label={`${children} ${removeLabel}`}
aria-label={`${children} ${
removeLabel ?? translate("Removable.labelSuffix")
}`}
onClick={composeEventHandlers(onClick, onDelete)}
>
<span className="navds-chips__chip-text">{children}</span>
@@ -27,8 +27,8 @@ interface DatePickerComponent
* ```jsx
* <DatePicker.Standalone
* dropdownCaption
* fromDate={new Date("2020-10-01")}
* toDate={new Date("2024-10-01")}
* fromDate={new Date("2022-10-01")}
* toDate={new Date("2026-10-01")}
* />
* ```
*/
@@ -73,40 +73,35 @@ export const Default: StoryObj<DefaultStoryProps> = {
locale={props.locale}
disableWeekends={props.disableWeekends}
>
{!props.standalone && (
<>
{props.inputfield && props.mode !== "multiple" ? (
<>
{props.mode === "range" ? (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker.Input
label="Fra"
size={props?.size}
{...rangeCtx.fromInputProps}
/>
<DatePicker.Input
label="Til"
size={props?.size}
{...rangeCtx.toInputProps}
/>
</div>
) : (
<>
<DatePicker.Input
label="Velg dato"
size={props?.size}
{...singleCtx.inputProps}
/>
</>
)}
</>
) : (
<Button onClick={() => setOpen((x) => !x)}>
Åpne datovelger
</Button>
)}
</>
)}
{!props.standalone &&
(props.inputfield && props.mode !== "multiple" ? (
<>
{props.mode === "range" ? (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker.Input
label="Fra"
size={props?.size}
{...rangeCtx.fromInputProps}
/>
<DatePicker.Input
label="Til"
size={props?.size}
{...rangeCtx.toInputProps}
/>
</div>
) : (
<DatePicker.Input
label="Velg dato"
size={props?.size}
{...singleCtx.inputProps}
/>
)}
</>
) : (
<Button onClick={() => setOpen((x) => !x)}>
Åpne datovelger
</Button>
))}
</Comp>
</div>
);
@@ -19,7 +19,7 @@ const App = () => {
};
describe("Render datepicker", () => {
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("Should not crash when e.target is window", async () => {
render(<App />);
@@ -19,8 +19,8 @@ interface MonthPickerComponent
* ```jsx
* <MonthPicker.Standalone
* dropdownCaption
* fromDate={new Date("1 Oct 2020")}
* toDate={new Date("1 Oct 2024")}
* fromDate={new Date("1 Oct 2022")}
* toDate={new Date("1 Oct 2026")}
* />
* ```
*/
@@ -4,29 +4,29 @@ import { describe, expect, test } from "vitest";
import { isValidDate, parseDate } from "..";
const check = (inp: string) =>
// eslint-disable-next-line vitest/valid-expect
// eslint-disable-next-line @vitest/valid-expect
expect(isValidDate(parseDate(inp, new Date(), nb, "date", false)));
const checkTwoDigit = (inp: string) =>
// eslint-disable-next-line vitest/valid-expect
// eslint-disable-next-line @vitest/valid-expect
expect(isValidDate(parseDate(inp, new Date(), nb, "date", true)));
const parse = (inp: string) => parseDate(inp, new Date(), nb, "date", false);
describe("Parse date-inputs with 4-digit years", () => {
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("No spaces", () => {
check("11052022").toBeTruthy();
check("15052022").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test(". divider", () => {
check("1.5.2022").toBeTruthy();
check("11.05.2022").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("/ divider", () => {
check("1/5/2022").toBeTruthy();
check("10/5/2022").toBeTruthy();
@@ -34,7 +34,7 @@ describe("Parse date-inputs with 4-digit years", () => {
check("10/05/2022").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("- divider", () => {
check("1-5-2022").toBeTruthy();
check("10-5-2022").toBeTruthy();
@@ -44,19 +44,19 @@ describe("Parse date-inputs with 4-digit years", () => {
});
describe("Parse date-inputs with 2-digit years", () => {
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("No spaces", () => {
checkTwoDigit("110522").toBeTruthy();
checkTwoDigit("150522").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test(". divider", () => {
checkTwoDigit("1.5.22").toBeTruthy();
checkTwoDigit("11.05.22").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("/ divider", () => {
checkTwoDigit("1/5/22").toBeTruthy();
checkTwoDigit("10/5/22").toBeTruthy();
@@ -64,7 +64,7 @@ describe("Parse date-inputs with 2-digit years", () => {
checkTwoDigit("10/05/22").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("- divider", () => {
checkTwoDigit("1-5-22").toBeTruthy();
checkTwoDigit("10-5-22").toBeTruthy();
@@ -72,14 +72,14 @@ describe("Parse date-inputs with 2-digit years", () => {
checkTwoDigit("10-05-22").toBeTruthy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("Dissallow 1 and 3 digit years", () => {
checkTwoDigit("11052").toBeFalsy();
checkTwoDigit("1105222").toBeFalsy();
checkTwoDigit("1105999").toBeFalsy();
});
// eslint-disable-next-line vitest/expect-expect
// eslint-disable-next-line @vitest/expect-expect
test("Dissallow date before year 1000", () => {
checkTwoDigit("11050999").toBeFalsy();
checkTwoDigit("11050010").toBeFalsy();
@@ -82,7 +82,7 @@ export const Menu = forwardRef<HTMLDivElement, DropdownMenuProps>(
open={isOpen}
onClose={() => {
handleToggle(false);
onClose && onClose();
onClose?.();
}}
/>
);
@@ -1,5 +1,4 @@
import { fireEvent, render, screen } from "@testing-library/react";
import faker from "faker";
import React from "react";
import { describe, expect, test, vi } from "vitest";
import { Checkbox, CheckboxGroup } from ".";
@@ -10,8 +9,8 @@ const firstArgumentOfFirstCall = (fn: ReturnType<typeof vi.fn>) =>
test("checkbox group chains onChange calls", async () => {
const onGroupChange = vi.fn();
const onChange = vi.fn();
const value = faker.datatype.string();
const label = faker.datatype.string();
const value = "Checkbox value";
const label = "My pretty label";
render(
<CheckboxGroup legend="legend" onChange={onGroupChange}>
@@ -44,8 +44,8 @@ const useCheckbox = (props: CheckboxProps) => {
if (readOnly) {
return;
}
props.onChange && props.onChange(e);
checkboxGroup && checkboxGroup.toggleValue(props.value);
props.onChange?.(e);
checkboxGroup?.toggleValue(props.value);
},
onClick: (e) => {
if (readOnly) {
@@ -1,6 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { BodyShort, ErrorMessage, Label } from "../../typography";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import ComboboxWrapper from "./ComboboxWrapper";
import FilteredOptions from "./FilteredOptions/FilteredOptions";
import { useFilteredOptionsContext } from "./FilteredOptions/filteredOptionsContext";
@@ -27,6 +28,7 @@ export const Combobox = forwardRef<
inputProps,
showErrorMsg,
size = "medium",
readOnly,
} = useInputContext();
return (
@@ -44,6 +46,7 @@ export const Combobox = forwardRef<
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon nativeReadOnly={false} readOnly={readOnly} />
{label}
</Label>
{!!description && (
@@ -36,6 +36,7 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
children,
defaultValue,
disabled,
readOnly,
error,
errorId,
filteredOptions: externalFilteredOptions,
@@ -50,19 +51,28 @@ const ComboboxProvider = forwardRef<HTMLInputElement, ComboboxProps>(
value,
onChange,
onClear,
shouldAutocomplete,
shouldAutocomplete: externalShouldAutocomplete,
size,
...rest
} = props;
const options = mapToComboboxOptionArray(externalOptions) || [];
const filteredOptions = mapToComboboxOptionArray(externalFilteredOptions);
const selectedOptions = mapToComboboxOptionArray(externalSelectedOptions);
const userAgent =
typeof navigator === "undefined" ? "" : navigator.userAgent;
const isFirefoxOnAndroid =
userAgent.includes("Android") && userAgent.includes("Firefox/");
const shouldAutocomplete =
!isFirefoxOnAndroid && externalShouldAutocomplete;
return (
<InputContextProvider
value={{
defaultValue,
description: rest.description,
disabled,
readOnly,
error,
errorId,
id,
@@ -9,6 +9,7 @@ type ComboboxWrapperProps = {
inputProps: {
disabled?: boolean;
};
readOnly?: boolean;
inputSize: string;
toggleIsListOpen: (isListOpen: boolean) => void;
};
@@ -21,7 +22,7 @@ const ComboboxWrapper = ({
inputSize,
toggleIsListOpen,
}: ComboboxWrapperProps) => {
const { toggleOpenButtonRef, clearInput } = useInputContext();
const { toggleOpenButtonRef, clearInput, readOnly } = useInputContext();
const wrapperRef = useRef<HTMLDivElement | null>(null);
const [hasFocusWithin, setHasFocusWithin] = useState(false);
@@ -55,6 +56,8 @@ const ComboboxWrapper = ({
"navds-combobox--error": hasError,
"navds-combobox--disabled": !!inputProps.disabled,
"navds-combobox--focused": hasFocusWithin,
"navds-combobox--readonly": readOnly,
"navds-form-field--readonly": readOnly,
},
)}
onFocus={onFocusInsideWrapper}
@@ -54,6 +54,7 @@ const FilteredOptions = () => {
)}
{shouldRenderFilteredOptionsList && (
/* biome-ignore lint/a11y/useFocusableInteractive: Interaction is not handeled by listbox itself. */
<ul
ref={setFilteredOptionsRef}
role="listbox"
@@ -58,7 +58,8 @@ const FilteredOptionsProvider = ({
useState<HTMLUListElement | null>(null);
const virtualFocus = useVirtualFocus(filteredOptionsRef);
const {
inputProps: { "aria-describedby": partialAriaDescribedBy, id },
inputProps: { "aria-describedby": partialAriaDescribedBy, id, disabled },
readOnly,
value,
searchTerm,
setValue,
@@ -136,7 +137,10 @@ const FilteredOptionsProvider = ({
const toggleIsListOpen = useCallback(
(newState?: boolean) => {
virtualFocus.moveFocusToTop();
if (disabled || readOnly) {
return;
}
virtualFocus.resetFocus();
if (newState ?? !isInternalListOpen) {
setHideCaret(!!maxSelected?.isLimitReached);
}
@@ -147,6 +151,8 @@ const FilteredOptionsProvider = ({
maxSelected?.isLimitReached,
isInternalListOpen,
setHideCaret,
disabled,
readOnly,
],
);
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
export type VirtualFocusType = {
activeElement: HTMLElement | undefined;
@@ -10,6 +10,9 @@ export type VirtualFocusType = {
moveFocusToElement: (id: string) => void;
moveFocusToTop: () => void;
moveFocusToBottom: () => void;
moveFocusUpBy: (numberOfElements: number) => void;
moveFocusDownBy: (numberOfElements: number) => void;
resetFocus: () => void;
};
const useVirtualFocus = (
@@ -40,11 +43,6 @@ const useVirtualFocus = (
: false;
};
const _moveFocusAndScrollTo = (_element?: HTMLElement) => {
setActiveElement(_element);
_element?.scrollIntoView?.({ block: "nearest" });
};
const moveFocusUp = () => {
if (!activeElement) {
return;
@@ -55,14 +53,14 @@ const useVirtualFocus = (
if (_currentIndex === 0) {
setActiveElement(undefined);
} else {
_moveFocusAndScrollTo(elementAbove);
setActiveElement(elementAbove);
}
};
const moveFocusDown = () => {
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
if (!activeElement) {
_moveFocusAndScrollTo(elementsAbleToReceiveFocus[0]);
setActiveElement(elementsAbleToReceiveFocus[0]);
return;
}
const _currentIndex = elementsAbleToReceiveFocus.indexOf(activeElement);
@@ -70,13 +68,17 @@ const useVirtualFocus = (
return;
}
_moveFocusAndScrollTo(elementsAbleToReceiveFocus[_currentIndex + 1]);
setActiveElement(elementsAbleToReceiveFocus[_currentIndex + 1]);
};
const moveFocusToTop = () => _moveFocusAndScrollTo(undefined);
const resetFocus = () => setActiveElement(undefined);
const moveFocusToTop = () => {
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
setActiveElement(elementsAbleToReceiveFocus[0]);
};
const moveFocusToBottom = () => {
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
return _moveFocusAndScrollTo(
setActiveElement(
elementsAbleToReceiveFocus[elementsAbleToReceiveFocus.length - 1],
);
};
@@ -89,6 +91,32 @@ const useVirtualFocus = (
}
};
const moveFocusUpBy = (numberOfElements: number) => {
if (!activeElement) {
return;
}
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
const currentIndex = elementsAbleToReceiveFocus.indexOf(activeElement);
const newIndex = Math.max(currentIndex - numberOfElements, 0);
setActiveElement(elementsAbleToReceiveFocus[newIndex]);
};
const moveFocusDownBy = (numberOfElements: number) => {
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
const currentIndex = activeElement
? elementsAbleToReceiveFocus.indexOf(activeElement)
: -1;
const newIndex = Math.min(
currentIndex + numberOfElements,
elementsAbleToReceiveFocus.length - 1,
);
setActiveElement(elementsAbleToReceiveFocus[newIndex]);
};
useEffect(() => {
activeElement?.scrollIntoView?.({ block: "nearest" });
}, [activeElement]);
return {
activeElement,
getElementById,
@@ -99,6 +127,9 @@ const useVirtualFocus = (
moveFocusToElement,
moveFocusToTop,
moveFocusToBottom,
moveFocusUpBy,
moveFocusDownBy,
resetFocus,
};
};
@@ -32,6 +32,7 @@ interface Props {
defaultValue: ComboboxProps["defaultValue"];
description: ComboboxProps["description"];
disabled: ComboboxProps["disabled"];
readOnly: ComboboxProps["readOnly"];
error: ComboboxProps["error"];
errorId: ComboboxProps["errorId"];
id: ComboboxProps["id"];
@@ -48,6 +49,7 @@ const InputProvider = ({ children, value: props }: Props) => {
defaultValue = "",
description,
disabled,
readOnly,
error,
errorId,
id: externalId,
@@ -61,6 +63,7 @@ const InputProvider = ({ children, value: props }: Props) => {
{
description,
disabled,
readOnly,
error,
errorId,
id: externalId,
@@ -96,7 +99,7 @@ const InputProvider = ({ children, value: props }: Props) => {
setInternalValue("");
setSearchTerm("");
},
[externalOnChange, onClear, setInternalValue],
[externalOnChange, onClear],
);
const focusInput = useCallback(() => {
@@ -49,6 +49,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
setValue,
hideCaret,
setHideCaret,
readOnly,
} = useInputContext();
const {
selectedOptions,
@@ -133,14 +134,6 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
case "Accept":
onEnter(e);
break;
case "Home":
toggleIsListOpen(false);
virtualFocus.moveFocusToTop();
break;
case "End":
toggleIsListOpen(true);
virtualFocus.moveFocusToBottom();
break;
default:
break;
}
@@ -149,6 +142,9 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
setIsMouseLastUsedInputDevice(false);
if (readOnly) {
return;
}
if (e.key === "Backspace") {
if (value === "" && shouldShowSelectedOptions) {
const lastSelectedOption =
@@ -198,6 +194,24 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
}
virtualFocus.moveFocusUp();
}
} else if (e.key === "Home") {
e.preventDefault();
virtualFocus.moveFocusToTop();
} else if (e.key === "End") {
e.preventDefault();
if (virtualFocus.activeElement === null || !isListOpen) {
toggleIsListOpen(true);
}
virtualFocus.moveFocusToBottom();
} else if (e.key === "PageUp") {
e.preventDefault();
virtualFocus.moveFocusUpBy(6);
} else if (e.key === "PageDown") {
e.preventDefault();
if (virtualFocus.activeElement === null || !isListOpen) {
toggleIsListOpen(true);
}
virtualFocus.moveFocusDownBy(6);
}
},
[
@@ -214,6 +228,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
setValue,
searchTerm,
shouldShowSelectedOptions,
readOnly,
],
);
@@ -225,10 +240,9 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
} else if (filteredOptions.length === 0) {
toggleIsListOpen(false);
}
virtualFocus.moveFocusToTop();
onChange(newValue);
},
[filteredOptions.length, virtualFocus, onChange, toggleIsListOpen],
[filteredOptions.length, onChange, toggleIsListOpen],
);
return (
@@ -262,6 +276,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
aria-activedescendant={activeDecendantId}
aria-describedby={ariaDescribedBy}
aria-invalid={inputProps["aria-invalid"]}
readOnly={readOnly}
/>
);
},
@@ -45,6 +45,7 @@ export const InputController = forwardRef<
size = "medium",
inputRef,
toggleOpenButtonRef,
readOnly,
} = useInputContext();
const { activeDecendantId } = useFilteredOptionsContext();
@@ -65,6 +66,7 @@ export const InputController = forwardRef<
id={inputProps.id}
ref={mergedInputRef}
inputClassName={inputClassName}
readOnly={readOnly}
{...rest}
/>
) : (
@@ -74,6 +76,7 @@ export const InputController = forwardRef<
ref={mergedInputRef}
inputClassName={inputClassName}
shouldShowSelectedOptions={shouldShowSelectedOptions}
readOnly={readOnly}
{...rest}
/>
</SelectedOptions>
@@ -12,7 +12,7 @@ interface SelectedOptionsProps {
const Option = ({ option }: { option: ComboboxOption }) => {
const { isMultiSelect, removeSelectedOption } = useSelectedOptionsContext();
const { focusInput } = useInputContext();
const { focusInput, readOnly, inputProps } = useInputContext();
const onClick = (e) => {
e.stopPropagation();
@@ -28,7 +28,13 @@ const Option = ({ option }: { option: ComboboxOption }) => {
);
}
return <Chips.Removable onClick={onClick}>{option.label}</Chips.Removable>;
return readOnly || inputProps.disabled ? (
<Chips.Toggle variant="neutral" checkmark={false} as="div">
{option.label}
</Chips.Toggle>
) : (
<Chips.Removable onClick={onClick}>{option.label}</Chips.Removable>
);
};
const SelectedOptions: React.FC<SelectedOptionsProps> = ({
@@ -4,7 +4,7 @@ import { usePrevious } from "../../../util/hooks";
import { useInputContext } from "../Input/Input.context";
import { isInList } from "../combobox-utils";
import { useComboboxCustomOptions } from "../customOptionsContext";
import { ComboboxOption, ComboboxProps, MaxSelected } from "../types";
import { ComboboxOption, ComboboxProps } from "../types";
type SelectedOptionsContextValue = {
addSelectedOption: (option: ComboboxOption) => void;
@@ -12,7 +12,7 @@ type SelectedOptionsContextValue = {
removeSelectedOption: (option: ComboboxOption) => void;
prevSelectedOptions?: ComboboxOption[];
selectedOptions: ComboboxOption[];
maxSelected?: MaxSelected & { isLimitReached: boolean };
maxSelected?: ComboboxProps["maxSelected"] & { isLimitReached: boolean };
setSelectedOptions: (any) => void;
toggleOption: (
option: ComboboxOption,
@@ -105,9 +105,10 @@ const SelectedOptionsProvider = ({
(!!maxSelected?.limit && selectedOptions.length >= maxSelected.limit) ||
(!isMultiSelect && selectedOptions.length > 0);
// biome-ignore lint/correctness/useExhaustiveDependencies: We explicitly want to run this effect when selectedOptions changes to match the view with the selected options.
useEffect(() => {
setHideCaret(isLimitReached);
}, [selectedOptions, setHideCaret, isLimitReached]);
}, [isLimitReached, selectedOptions, setHideCaret]);
const toggleOption = useCallback(
(
@@ -291,4 +291,40 @@ describe("Render combobox", () => {
);
});
});
describe("has keyboard navigation", () => {
test("for PageDown and PageUp", async () => {
render(<App options={options} />);
const combobox = screen.getByRole("combobox", {
name: "Hva er dine favorittfrukter?",
});
const pressKey = async (key: string) => {
await act(async () => {
await userEvent.keyboard(`{${key}}`);
});
};
const hasVirtualFocus = (option: string) =>
expect(combobox.getAttribute("aria-activedescendant")).toBe(
screen.getByRole("option", { name: option }).id,
);
await act(async () => {
await userEvent.click(combobox);
});
await pressKey("ArrowDown");
hasVirtualFocus("apple");
await pressKey("PageDown");
hasVirtualFocus("mango");
await pressKey("PageDown");
hasVirtualFocus("watermelon");
await pressKey("PageUp");
hasVirtualFocus("mango");
await pressKey("PageUp");
hasVirtualFocus("apple");
});
});
});
@@ -7,7 +7,7 @@ import { VStack } from "../../layout/stack";
import { Modal } from "../../modal";
import { BodyLong } from "../../typography";
import { TextField } from "../textfield";
import { UNSAFE_Combobox } from "./index";
import { ComboboxProps, UNSAFE_Combobox } from "./index";
export default {
title: "ds-react/Combobox",
@@ -38,14 +38,16 @@ const options = [
"grapefruit",
];
export const Default: StoryFunction = (props) => (
export const Default: StoryFn<ComboboxProps & { maxSelected?: number }> = ({
maxSelected,
...rest
}) => (
<UNSAFE_Combobox
{...props}
maxSelected={{ limit: Number(props.maxSelected) }}
{...rest}
maxSelected={maxSelected && { limit: maxSelected }}
id="combobox"
/>
);
Default.args = {
options,
label: "Hva er din favorittfrukt?",
@@ -65,6 +67,9 @@ Default.argTypes = {
disabled: {
control: { type: "boolean" },
},
readOnly: {
control: { type: "boolean" },
},
isListOpen: {
control: { type: "boolean" },
},
@@ -482,12 +487,50 @@ export const InModal: StoryFn = () => {
};
export const Disabled: StoryFn = () => {
const selectedOptionsMultiple = ["napoleonskake", "donut"];
const selectedOptionsSingle = ["pushups"];
return (
<UNSAFE_Combobox
options={options}
label="Hva er dine favorittfrukter?"
disabled
/>
<VStack gap="2">
<UNSAFE_Combobox
options={options}
label="Hva er dine favorittfrukter?"
description="Bare de fruktene som du spiser minst 5 av om dagen teller vi som en favorittfrukt."
selectedOptions={selectedOptionsMultiple}
isMultiSelect
disabled
/>
<UNSAFE_Combobox
options={options}
label="Hva er din favoritthobby?"
description="Her snakker vi ting du bruker minst én time på hver dag."
selectedOptions={selectedOptionsSingle}
disabled
/>
</VStack>
);
};
export const Readonly: StoryFn = () => {
const selectedOptionsMultiple = ["napoleonskake", "donut"];
const selectedOptionsSingle = ["pushups"];
return (
<VStack gap="2">
<UNSAFE_Combobox
options={options}
label="Hva er dine favorittfrukter?"
description="Bare de fruktene som du spiser minst 5 av om dagen teller vi som en favorittfrukt."
selectedOptions={selectedOptionsMultiple}
isMultiSelect
readOnly
/>
<UNSAFE_Combobox
options={options}
label="Hva er din favoritthobby?"
description="Her snakker vi ting du bruker minst én time på hver dag."
selectedOptions={selectedOptionsSingle}
readOnly
/>
</VStack>
);
};
@@ -523,6 +566,8 @@ export const Chromatic: StoryFn = () => {
<WithError />
<H2>Disabled</H2>
<Disabled />
<H2>Readonly</H2>
<Readonly />
</VStack>
);
};
@@ -35,7 +35,7 @@ const CustomOptionsProvider = ({
);
focusInput();
},
[focusInput, setCustomOptions],
[focusInput],
);
const addCustomOption = useCallback(
@@ -47,7 +47,7 @@ const CustomOptionsProvider = ({
}
focusInput();
},
[focusInput, isMultiSelect, setCustomOptions],
[focusInput, isMultiSelect],
);
const customOptionsState = {
+10 -12
View File
@@ -16,17 +16,6 @@ export type ComboboxOption = {
value: string;
};
export type MaxSelected = {
/**
* The limit for maximum selected options
*/
limit: number;
/**
* Override the message to display when the limit for maximum selected options has been reached
*/
message?: string;
};
export interface ComboboxProps
extends FormFieldProps,
Omit<
@@ -135,7 +124,16 @@ export interface ComboboxProps
/**
* Options for the maximum number of selected options.
*/
maxSelected?: MaxSelected;
maxSelected?: {
/**
* The limit for maximum selected options
*/
limit: number;
/**
* Override the message to display when the limit for maximum selected options has been reached
*/
message?: string;
};
/**
* Set to `true` to enable inline autocomplete.
*
@@ -1,11 +1,10 @@
import { render, screen } from "@testing-library/react";
import faker from "faker";
import React from "react";
import { expect, test } from "vitest";
import { ConfirmationPanel } from ".";
test("omits error id from input", async () => {
const label = faker.datatype.string();
const label = "My label";
render(<ConfirmationPanel label={label} errorId="wat"></ConfirmationPanel>);
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { HTMLAttributes, forwardRef, useRef } from "react";
import { BodyShort, Heading } from "../../typography";
import { composeEventHandlers } from "../../util/composeEventHandlers";
import { useId, useMergeRefs } from "../../util/hooks";
import { useMergeRefs } from "../../util/hooks";
import ErrorSummaryItem from "./ErrorSummaryItem";
export interface ErrorSummaryProps
@@ -33,13 +33,13 @@ interface ErrorSummaryComponent
ErrorSummaryProps & React.RefAttributes<HTMLDivElement>
> {
/**
* Link to error.
* Error message with link to field.
*
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
*
* @example
* ```jsx
* <ErrorSummary.Item href="#1">
* <ErrorSummary.Item href="#id-til-alderfelt">
* Felt må fylles ut med alder
* </ErrorSummary.Item>
* ```
@@ -48,7 +48,7 @@ interface ErrorSummaryComponent
}
/**
* A component that displays a summary of errors.
* Summary of errors in a form.
*
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/errorsummary)
* @see 🏷️ {@link ErrorSummaryProps}
@@ -77,15 +77,13 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
},
ref,
) => {
const headingId = useId();
const sectionRef = useRef<HTMLDivElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
const mergedRef = useMergeRefs(ref, sectionRef);
const mergedRef = useMergeRefs(ref, wrapperRef);
return (
<section
<div
ref={mergedRef}
{...rest}
className={cl(
@@ -94,11 +92,8 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
`navds-error-summary--${size}`,
)}
tabIndex={-1}
aria-live="polite"
aria-relevant="all"
aria-labelledby={headingId}
onFocus={composeEventHandlers(rest.onFocus, (event) => {
if (event.target === sectionRef.current) {
if (event.target === wrapperRef.current) {
headingRef?.current?.focus();
}
})}
@@ -107,7 +102,6 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
className="navds-error-summary__heading"
as={headingTag}
size="small"
id={headingId}
ref={headingRef}
tabIndex={-1}
>
@@ -116,7 +110,7 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
<BodyShort as="ul" size={size} className="navds-error-summary__list">
{children}
</BodyShort>
</section>
</div>
);
},
) as ErrorSummaryComponent;
@@ -2,11 +2,7 @@ import { Meta, StoryFn, StoryObj } from "@storybook/react";
import { fireEvent, within } from "@storybook/test";
import React from "react";
import { ImageIcon } from "@navikt/aksel-icons";
import {
type FileObject,
UNSAFE_FileUpload as FileUpload,
type FilesPartitioned,
} from ".";
import { type FileObject, FileUpload, type FilesPartitioned } from ".";
const meta: Meta<typeof FileUpload.Dropzone> = {
title: "ds-react/FileUpload/Dropzone",
@@ -1,6 +1,6 @@
import { Meta, StoryFn } from "@storybook/react";
import React from "react";
import { FileItem, UNSAFE_FileUpload as FileUpload } from ".";
import { FileItem, FileUpload } from ".";
import { VStack } from "../../layout/stack";
const meta: Meta<typeof FileUpload.Item> = {
@@ -1,7 +1,7 @@
import { Meta, StoryFn } from "@storybook/react";
import React, { useEffect, useState } from "react";
import { UploadIcon } from "@navikt/aksel-icons";
import { UNSAFE_FileUpload as FileUpload, FileUploadItemProps } from ".";
import { FileUpload, FileUploadItemProps } from ".";
import { Alert } from "../../alert";
import { Button } from "../../button";
import { VStack } from "../../layout/stack";
@@ -1,9 +1,9 @@
"use client";
export { default as UNSAFE_FileUpload } from "./FileUpload";
export { default as UNSAFE_FileUploadDropzone } from "./parts/dropzone/Dropzone";
export { default as FileUpload } from "./FileUpload";
export { default as FileUploadDropzone } from "./parts/dropzone/Dropzone";
export { type FileUploadDropzoneProps } from "./parts/dropzone/dropzone.types";
export {
default as UNSAFE_FileUploadTrigger,
default as FileUploadTrigger,
type FileUploadTriggerProps,
} from "./parts/Trigger";
export {
@@ -15,7 +15,7 @@ export {
type FileRejectionReason,
} from "./FileUpload.types";
export {
default as UNSAFE_FileUploadItem,
default as FileUploadItem,
type FileUploadItemProps,
} from "./parts/item/Item";
export { type FileItem, type FileMetadata } from "./parts/item/Item.types";
@@ -22,14 +22,17 @@ export interface FormSummaryEditProps
export const FormSummaryEditLink: OverridableComponent<
FormSummaryEditProps,
HTMLAnchorElement
> = forwardRef(({ children = "Endre svar", className, ...rest }, ref) => (
<Link
ref={ref}
{...rest}
className={cl("navds-form-summary__edit", className)}
>
{children}
</Link>
));
> = forwardRef(
({ children = "Endre svar", className, as = "a", ...rest }, ref) => (
<Link
ref={ref}
as={as}
{...rest}
className={cl("navds-form-summary__edit", className)}
>
{children}
</Link>
),
);
export default FormSummaryEditLink;
@@ -1,14 +1,13 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import faker from "faker";
import React from "react";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { Radio, RadioGroup } from ".";
const value1 = faker.datatype.string();
const label1 = faker.datatype.string();
const value2 = faker.datatype.string();
const label2 = faker.datatype.string();
const value1 = "My first value";
const label1 = "World's best radio label";
const value2 = "Life changing value";
const label2 = "Radio label of the year";
const Group = (props) => (
<RadioGroup {...props} legend="legend">
@@ -41,8 +41,8 @@ export const useRadio = (props: RadioProps) => {
if (readOnly) {
return;
}
props.onChange && props.onChange(e);
radioGroup?.onChange && radioGroup.onChange(props.value);
props.onChange?.(e);
radioGroup?.onChange?.(props.value);
},
onClick: (e) => {
if (readOnly) {
@@ -144,7 +144,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
(event: SearchClearEvent) => {
onClear?.(event);
handleChange("");
searchRef.current && searchRef.current?.focus?.();
searchRef.current?.focus?.();
},
[handleChange, onClear],
);
@@ -122,7 +122,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
return;
}
setChecked(e.target.checked);
props.onChange && props.onChange(e);
props.onChange?.(e);
}}
onClick={(e) => {
if (readOnly) {
Loaded 100 of 368 files, more files were not shown because too many files have changed in this diff. Show more