Compare commits

..
Author SHA1 Message Date
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
266 changed files with 11703 additions and 11070 deletions

No files matched your search

+26 -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,23 @@ 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",
},
},
{
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 +165,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 }}
+3
View File
@@ -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:
+10
View File
@@ -1,5 +1,15 @@
# @navikt/aksel-icons
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
## 7.1.0
## 7.0.1
## 7.0.0
@@ -1,3 +1,4 @@
/* eslint-disable import/no-named-as-default-member */
import fastglob from "fast-glob";
import fs from "fs";
import jsYaml from "js-yaml";
@@ -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,6 +1,7 @@
const file_key = "wEdyFjCQSBR3U7FvrMbPXa";
export const fetchIcons = async () => {
console.info("Fetching new icons from Figma...");
const data = await fetch(
`https://api.figma.com/v1/files/${file_key}/components`,
{
@@ -15,6 +16,10 @@ export const fetchIcons = async () => {
throw e.message;
});
if (data?.err) {
throw new Error(data.err);
}
if (!data?.meta?.components || data?.meta?.components.length === 0) {
throw new Error("Fant ingen publiserte ikoner");
}
@@ -6,7 +6,7 @@ export const resolveName = (icon) => {
if (isVariant) {
const variant = icon.name.replace("Variant=", "").replace("Stroke", "");
const actualName = icon["containing_frame"]?.["containingStateGroup"]?.name;
const actualName = icon.containing_frame?.containingStateGroup?.name;
const name = `${actualName}${variant}`;
return `${startCase(camelCase(name)).replace(/ /g, "")}.svg`;
}
+1 -2
View File
@@ -1,4 +1,3 @@
import { config } from "dotenv";
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
import { resolve } from "path";
import { fetchDownloadUrls, fetchIcon, fetchIcons } from "./fetch-icons.mjs";
@@ -6,7 +5,6 @@ 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";
@@ -16,6 +14,7 @@ if (!process.env.FIGMA_TOKEN) {
}
async function main() {
console.info("Started icon-update from Figma");
let icons = await fetchIcons();
const totalIcons = icons.length;
+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",
@@ -28,7 +28,7 @@
"postcss": "^8.4.31",
"postcss-url": "^10.1.3",
"typescript": "^5.1.6",
"vite": "^5.1.7",
"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">
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "7.0.1",
"version": "7.2.1",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
@@ -62,7 +62,7 @@
"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.mjs",
"zip:icons": "node config/zip.js",
"test": "vitest run",
"test:watch": "vitest watch"
@@ -73,7 +73,6 @@
"concurrently": "7.2.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",
@@ -83,6 +82,7 @@
"react": "^18.0.0",
"rehype-parse": "8.0.4",
"tsc-alias": "1.8.8",
"tsx": "^4.7.1",
"typescript": "^5.1.6",
"unified": "10.1.2",
"vitest": "^1.2.2"
+18
View File
@@ -1,5 +1,23 @@
# @navikt/aksel-stylelint
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "7.0.1",
"version": "7.2.1",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
"repository": {
@@ -35,8 +35,8 @@
"dev": "yarn watch:lint"
},
"devDependencies": {
"@navikt/ds-css": "^7.0.1",
"@navikt/ds-tokens": "^7.0.1",
"@navikt/ds-css": "^7.2.1",
"@navikt/ds-tokens": "^7.2.1",
"concurrently": "7.2.1",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0",
@@ -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",
},
];
+35
View File
@@ -1,5 +1,40 @@
# @navikt/aksel
## 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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "7.0.1",
"version": "7.2.1",
"description": "Aksel command line interface. Handles css-imports, codemods and more",
"author": "Aksel | NAV designsystem team",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
"dependencies": {
"@navikt/ds-css": "7.0.1",
"@navikt/ds-css": "7.2.1",
"axios": "1.7.4",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+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(
+30
View File
@@ -1,5 +1,35 @@
# @navikt/ds-css
## 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 *
*************************/
-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;
}
+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
@@ -10,7 +10,6 @@
@media (forced-colors: active) {
.navds-internalheader {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
+60 -71
View File
@@ -4,92 +4,81 @@
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;
color: var(--ac-list-marker-icon-color, var(--ac-list-marker-color, var(--a-icon-default)));
}
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "7.0.1",
"version": "7.2.1",
"description": "CSS for NAV Designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -27,7 +27,8 @@
"css:get-version": "node config/get-version.js"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.0.1",
"@navikt/ds-tokens": "^7.2.1",
"autoprefixer": "^10.4.20",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
"lodash": "4.17.21",
-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;
}
+64
View File
@@ -1,5 +1,69 @@
# @navikt/ds-react
## 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
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "7.0.1",
"version": "7.2.1",
"description": "React components from the Norwegian Labour and Welfare Administration.",
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
"license": "MIT",
@@ -583,7 +583,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 +594,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.2.1",
"@navikt/ds-tokens": "^7.2.1",
"clsx": "^2.1.0",
"date-fns": "^3.0.0",
"react-day-picker": "8.10.0"
@@ -614,8 +614,8 @@
"react-router-dom": "^6.3.0",
"rimraf": "6.0.1",
"swr": "^1.1.2",
"ts-node": "^10.9.1",
"tsc-alias": "1.8.8",
"tsx": "^4.19.1",
"typescript": "^5.1.6",
"vitest": "^1.2.2"
},
+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>
),
@@ -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 />);
@@ -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?.();
}}
/>
);
@@ -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.
*
@@ -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;
@@ -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) {
@@ -49,7 +49,7 @@ Default.argTypes = {
export const Open: StoryFn = () => {
const ref = useRef<HTMLButtonElement | null>(null);
useEffect(() => {
ref.current && ref.current.click();
ref.current?.click();
}, []);
return (
+1 -1
View File
@@ -127,7 +127,7 @@ export {
export { ErrorSummary, type ErrorSummaryProps } from "./form/error-summary";
export { Fieldset, type FieldsetProps } from "./form/fieldset";
export {
UNSAFE_FileUpload,
FileUpload,
type FileAccepted,
type FileItem,
type FileMetadata,
@@ -63,7 +63,7 @@ export const PageBlock: OverridableComponent<PageBlockProps, HTMLDivElement> =
{...rest}
className={cl(
"navds-pageblock",
`navds-pageblock--${width}`,
width && `navds-pageblock--${width}`,
className,
{ "navds-pageblock--gutters": gutters },
)}
@@ -26,8 +26,8 @@ export type HStackProps = PrimitiveAsChildProps & Omit<StackProps, "direction">;
* </HStack>
*/
export const HStack: OverridableComponent<HStackProps, HTMLDivElement> =
forwardRef((props, ref) => {
return <Stack {...props} ref={ref} direction="row" />;
forwardRef(({ as = "div", ...rest }, ref) => {
return <Stack as={as} {...rest} ref={ref} direction="row" />;
});
export default HStack;
@@ -27,8 +27,10 @@ export type VStackProps = PrimitiveAsChildProps &
* </VStack>
*/
export const VStack: OverridableComponent<VStackProps, HTMLDivElement> =
forwardRef((props, ref) => {
return <Stack {...props} ref={ref} direction="column" wrap={false} />;
forwardRef(({ as = "div", ...rest }, ref) => {
return (
<Stack as={as} {...rest} ref={ref} direction="column" wrap={false} />
);
});
export default VStack;
+25 -29
View File
@@ -1,15 +1,27 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { BodyShort, Heading } from "../typography";
import { useId } from "../util/hooks";
import { BodyLong, BodyShort, Heading, HeadingProps } from "../typography";
import { ListItem } from "./ListItem";
import { ListContext } from "./context";
import { ListProps } from "./types";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { ListItemProps, ListProps } from "./types";
const headingSizeMap: Record<
Exclude<ListProps["size"], undefined>,
HeadingProps["size"]
> = {
small: "xsmall",
medium: "small",
large: "medium",
};
export interface ListComponent
extends React.ForwardRefExoticComponent<
ListProps & React.RefAttributes<HTMLDivElement>
> {
/**
* @see 🏷️ {@link ListItemProps}
*/
Item: typeof ListItem;
}
@@ -42,47 +54,31 @@ export const List = forwardRef<HTMLDivElement, ListProps>(
},
ref,
) => {
const ariaId = useId();
const { size: contextSize } = useContext(ListContext);
const { isNested, size: _size } = useContext(ListContext);
const listSize = size ?? _size ?? "medium";
const listSize = size ?? contextSize;
return (
<ListContext.Provider
value={{
listType: ListTag,
isNested: isNested === null ? false : true,
size: listSize,
}}
>
<div
<BodyLong
as="div"
{...rest}
size={listSize}
ref={ref}
className={cl("navds-list", `navds-list--${listSize}`, className, {
"navds-list--nested": isNested === null ? false : true,
})}
className={cl("navds-list", `navds-list--${listSize}`, className)}
>
{title && (
<Heading
id={`tittel-${ariaId}`}
size={listSize === "medium" ? "small" : "xsmall"}
as={headingTag}
>
<Heading size={headingSizeMap[listSize]} as={headingTag}>
{title}
</Heading>
)}
{description && (
<BodyShort size={listSize} id={`description-${ariaId}`}>
{description}
</BodyShort>
)}
<ListTag
aria-labelledby={title && `tittel-${ariaId}`}
aria-describedby={description && `description-${ariaId}`}
>
{children}
</ListTag>
</div>
{description && <BodyShort size={listSize}>{description}</BodyShort>}
<ListTag role="list">{children}</ListTag>
</BodyLong>
</ListContext.Provider>
);
},
+6 -6
View File
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { BodyLong } from "../typography";
import { ListContext } from "./context";
import { ListItemProps } from "./types";
import type { ListItemProps } from "./types";
/**
* @see 🏷️ {@link ListItemProps}
@@ -21,7 +21,7 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
<li {...rest} ref={ref} className={cl("navds-list__item", className)}>
{listType === "ul" && (
<div
className={cl({
className={cl("navds-list__item-marker", {
"navds-list__item-marker--icon": icon,
"navds-list__item-marker--bullet": !icon,
})}
@@ -30,8 +30,8 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
icon
) : (
<svg
width="6"
height="6"
width="0.375rem"
height="0.375rem"
viewBox="0 0 6 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -45,14 +45,14 @@ export const ListItem = forwardRef<HTMLLIElement, ListItemProps>(
</div>
)}
<BodyLong as="div" size={size} className="navds-list__item-content">
<div>
{title && (
<BodyLong as="p" size={size} weight="semibold">
{title}
</BodyLong>
)}
{children}
</BodyLong>
</div>
</li>
);
},
+2 -4
View File
@@ -2,13 +2,11 @@ import { createContext } from "react";
import { ListProps } from "./types";
interface ListContextProps {
listType: ListProps["as"];
isNested: boolean | null;
size: ListProps["size"];
listType: Exclude<ListProps["as"], undefined>;
size: Exclude<ListProps["size"], undefined>;
}
export const ListContext = createContext<ListContextProps>({
listType: "ul",
isNested: null,
size: "medium",
});
+203 -95
View File
@@ -7,6 +7,7 @@ import {
HeadHeartIcon,
SplitHorizontalIcon,
} from "@navikt/aksel-icons";
import { Box } from "../layout/box";
import { VStack } from "../layout/stack";
import { BodyLong } from "../typography";
import List from "./List";
@@ -21,73 +22,97 @@ export default {
type Story = StoryObj<typeof List>;
export const Default = {
export const Default: Story = {
render: (props) => {
return (
<Box borderWidth="0 0 0 1" borderColor="border-subtle" maxWidth="500px">
<List {...props}>
<List.Item title="Lorem Ipsum Dolor Sit Amet">Beskrivelse.</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto
fugiat atque accusantium iure sunt, ipsum voluptas, impedit harum,
minus rerum recusandae.
</List.Item>
<List.Item>
Impedit nemo eos sit adipisci non dolores.
<List>
<List.Item title="Tempor Incididunt">
Error assumenda officia
</List.Item>
<List.Item>Suscipit odit voluptatum</List.Item>
</List>
</List.Item>
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto
fugiat atque accusantium iure sunt, ipsum voluptas.
<List as="ol">
<List.Item title="Tempor Incididunt">
Error assumenda officia
</List.Item>
<List.Item>Suscipit odit voluptatum</List.Item>
</List>
</List.Item>
</List>
</Box>
);
},
args: {
title: "",
description: "",
},
argTypes: {
size: { control: { type: "radio" }, options: ["small", "medium", "large"] },
as: { control: { type: "radio" }, options: ["ul", "ol"] },
},
};
export const Ordered: Story = {
render: () => {
return (
<List>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Lorem Ipsum Dolor Sit Amet
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto fugiat
atque accusantium iure sunt, ipsum voluptas, impedit harum, minus
rerum recusandae. Consequuntur sint distinctio nulla reprehenderit eum
suscipit quae libero.
</List.Item>
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto fugiat
atque accusantium iure sunt, ipsum voluptas, impedit harum, minus
rerum recusandae. Consequuntur sint distinctio nulla reprehenderit eum
suscipit quae libero.
</List.Item>
</List>
<Box borderWidth="0 0 0 1" borderColor="border-subtle" maxWidth="500px">
<List as="ol">
<List.Item title="Lorem Ipsum Dolor Sit Amet">Beskrivelse.</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Quis Nostrud Exercitation Ullamco Eu Fugiat Nulla Pariatur Dolore">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis. Men
hvis den likevel blir lang, skal teksten brekke.
</List.Item>
<List.Item>Sed Do Eiusmod Tempor Incididunt</List.Item>
<List.Item>Ut Labore Et Dolore Magna Aliqua</List.Item>
<List.Item>Enim Ad Minim Veniam</List.Item>
<List.Item>Laboris Nisi Ut Aliquip Ex Ea Commodo</List.Item>
<List.Item>Duis Aute Irure Dolor In Reprehenderit</List.Item>
<List.Item>
Impedit nemo eos sit adipisci non dolores.
<List as="ol">
<List.Item title="Tempor Incididunt">
Error assumenda officia
</List.Item>
<List.Item>Suscipit odit voluptatum</List.Item>
</List>
</List.Item>
<List.Item title="Voluptate Velit Esse Cillum Dolore">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
<List>
<List.Item title="Tempor Incididunt">
Error assumenda officia
</List.Item>
<List.Item>Suscipit odit voluptatum</List.Item>
</List>
</List.Item>
<List.Item>Eu Fugiat Nulla Pariatur</List.Item>
</List>
</Box>
);
},
};
export const Ordered = {
render: () => {
return (
<List as="ol">
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Lorem Ipsum Dolor Sit Amet
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Sed Do Eiusmod Tempor Incididunt">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Ut Labore Et Dolore Magna Aliqua">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Enim Ad Minim Veniam">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Quis Nostrud Exercitation Ullamco">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Laboris Nisi Ut Aliquip Ex Ea Commodo">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Duis Aute Irure Dolor In Reprehenderit">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Voluptate Velit Esse Cillum Dolore">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
<List.Item title="Eu Fugiat Nulla Pariatur">
Beskrivelse på punktet i lista. Prøv å hold den kort og konsis.
</List.Item>
</List>
);
},
};
export const WithHeading = {
export const WithHeading: Story = {
render: () => {
return (
<List
@@ -108,10 +133,25 @@ export const WithHeading = {
},
};
export const SizesUl = {
export const SizesUl: Story = {
render: () => {
return (
<VStack gap="8">
<List
title="Large list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="large"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
@@ -147,10 +187,26 @@ export const SizesUl = {
},
};
export const SizesOl = {
export const SizesOl: Story = {
render: () => {
return (
<VStack gap="8">
<List
title="Large list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="large"
as="ol"
>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
@@ -188,10 +244,31 @@ export const SizesOl = {
},
};
export const SizesIcons = {
export const SizesIcons: Story = {
render: () => {
return (
<VStack gap="8">
<List
title="Large list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
size="large"
>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Lorem Ipsum Dolor Sit Amet"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item
icon={<HeadHeartIcon aria-hidden />}
title="Consectetur Adipiscing Elit"
>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item icon={<HeadHeartIcon aria-hidden />}>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
</List>
<List
title="Medium list"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam vel risus volutpat."
@@ -239,7 +316,7 @@ export const SizesIcons = {
},
};
export const Icons = {
export const Icons: Story = {
render: () => {
return (
<List title="Best title">
@@ -266,39 +343,70 @@ export const Icons = {
},
};
export const Nested = {
export const Tokens: Story = {
render: () => {
return (
<List>
<List.Item title="Sed Do Eiusmod Tempor Incididunt">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
<List>
<List.Item title="Lorem Ipsum Dolor Sit Amet">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item title="Consectetur Adipiscing Elit">
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
<List style={{ "--ac-list-marker-color": "red" }}>
<List.Item>Consectetur Adipiscing Elit</List.Item>
<List.Item>
Impedit nemo eos sit adipisci non dolores.
<List
style={{
"--ac-list-marker-icon-color": "blue",
"--ac-list-marker-ul-color": "pink",
}}
>
<List.Item>Error assumenda officia</List.Item>
<List.Item icon={<HeadHeartIcon aria-hidden />}>
Suscipit odit voluptatum
</List.Item>
</List>
</List.Item>
<List.Item>
Beskrivelsen på punkter er nærmere forklart <a href="/">her</a>
<List.Item style={{ "--ac-list-marker-ol-color": "green" }}>
Iusto fugiat atque accusantium iure sunt.
<List as="ol">
<List.Item>Error assumenda officia</List.Item>
</List>
<List>
<List.Item>Suscipit odit voluptatum</List.Item>
</List>
</List.Item>
</List>
);
},
};
export const Spacing = {
export const Spacing: Story = {
render: () => {
return (
<>
<h2>Large</h2>
<BodyLong size="large" spacing>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit minus
aliquid, veniam quibusdam saepe autem quia odio? Tenetur dicta
voluptates iste maiores perspiciatis? Sapiente in possimus iusto
numquam? Esse, voluptatibus.
</BodyLong>
<List size="large">
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto
fugiat atque accusantium iure sunt, ipsum voluptas, impedit harum,
minus rerum recusandae. Consequuntur sint distinctio nulla
reprehenderit eum suscipit quae libero.
</List.Item>
<List.Item>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Iusto
fugiat atque accusantium iure sunt, ipsum voluptas, impedit harum,
minus rerum recusandae. Consequuntur sint distinctio nulla
reprehenderit eum suscipit quae libero.
</List.Item>
</List>
<BodyLong size="large" spacing>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit minus
aliquid, veniam quibusdam saepe autem quia odio? Tenetur dicta
voluptates iste maiores perspiciatis? Sapiente in possimus iusto
numquam? Esse, voluptatibus.
</BodyLong>
<h2>Medium</h2>
<BodyLong spacing>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit minus
@@ -359,43 +467,43 @@ export const Spacing = {
};
export const Chromatic: Story = {
render: () => (
render: (...args) => (
<VStack gap="2">
<div>
<h2>Default</h2>
<Default.render />
{Default.render?.(...args)}
</div>
<div>
<h2>Ordered</h2>
<Ordered.render />
{Ordered.render?.(...args)}
</div>
<div>
<h2>WithHeading</h2>
<WithHeading.render />
{WithHeading.render?.(...args)}
</div>
<div>
<h2>SizesUl</h2>
<SizesUl.render />
{SizesUl.render?.(...args)}
</div>
<div>
<h2>SizesOl</h2>
<SizesOl.render />
{SizesOl.render?.(...args)}
</div>
<div>
<h2>SizesIcons</h2>
<SizesIcons.render />
{SizesIcons.render?.(...args)}
</div>
<div>
<h2>Icons</h2>
<Icons.render />
{Icons.render?.(...args)}
</div>
<div>
<h2>Nested</h2>
<Nested.render />
<h2>Tokens</h2>
{Tokens.render?.(...args)}
</div>
<div>
<h2>Spacing</h2>
<Spacing.render />
{Spacing.render?.(...args)}
</div>
</VStack>
),
+2 -2
View File
@@ -19,10 +19,10 @@ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
*/
headingTag?: React.ElementType<any>;
/**
* Changes padding, height and font-size.
* Changes margin-block on list and font size on items.
* @default "medium"
*/
size?: "medium" | "small";
size?: "small" | "medium" | "large";
}
export interface ListItemProps extends React.HTMLAttributes<HTMLLIElement> {
+2 -2
View File
@@ -127,7 +127,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
// We have to use JS because it doesn't work to set it with a prop (React bug?)
// Currently doesn't seem to work in Chrome. See also Tooltip.tsx
if (modalRef.current && portalNode) modalRef.current.autofocus = true;
}, [modalRef, portalNode]);
}, [portalNode]);
useEffect(() => {
// We need to have this in a useEffect so that the content renders before the modal is displayed,
@@ -140,7 +140,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
modalRef.current.close();
}
}
}, [modalRef, portalNode, open]);
}, [portalNode, open]);
useBodyScrollLock(modalRef, portalNode, isNested);
@@ -337,6 +337,8 @@ const DismissableLayerNode = forwardRef<HTMLDivElement, DismissableLayerProps>(
* If `disableOutsidePointerEvents` is true,
* we want to disable pointer events on the body when the first layer is opened.
*/
// biome-ignore lint/correctness/useExhaustiveDependencies: Every time the descendants change, we want to update the body pointer events since we might have added or removed a layer.
useEffect(() => {
if (!node || !enabled || !disableOutsidePointerEvents) return;
@@ -362,6 +364,7 @@ const DismissableLayerNode = forwardRef<HTMLDivElement, DismissableLayerProps>(
/**
* To make sure pointerEvents are enabled for all parents and siblings when the layer is removed from the DOM
*/
// biome-ignore lint/correctness/useExhaustiveDependencies: We explicitly want to run this on unmount, including every time the node updates to make sure we don't lock the application behind pointer-events: none.
useEffect(() => {
return () => descendants.values().forEach((x) => x.forceUpdate());
}, [descendants, node]);
@@ -55,19 +55,19 @@ const RovingFocus = forwardRef<HTMLDivElement, RovingFocusProps>(
const nextItem = () => {
const next = descendants.nextEnabled(idx, loop);
next && next.node?.focus();
next?.node?.focus();
};
const prevItem = () => {
const prev = descendants.prevEnabled(idx, loop);
prev && prev.node?.focus();
prev?.node?.focus();
};
const firstItem = () => {
const first = descendants.firstEnabled();
first && first.node?.focus();
first?.node?.focus();
};
const lastItem = () => {
const last = descendants.lastEnabled();
last && last.node?.focus();
last?.node?.focus();
};
const keyMap: Record<string, React.KeyboardEventHandler> = {
@@ -189,21 +189,13 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
size={size}
icon={
<ChevronLeftIcon
className="navds-pagination__prev-next-icon"
{...(prevNextTexts
? { "aria-hidden": true }
: { title: "Forrige" })}
/>
}
>
{prevNextTexts && (
<BodyShort
size={size === "xsmall" ? "small" : size}
className="navds-pagination__prev-text"
>
Forrige
</BodyShort>
)}
{prevNextTexts && `Forrige`}
</Item>
</li>
{getSteps({ page, count, siblingCount, boundaryCount }).map(
@@ -211,7 +203,10 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
const n = Number(step);
return Number.isNaN(n) ? (
<li className="navds-pagination__ellipsis" key={`${step}${i}`}>
<BodyShort size={size === "xsmall" ? "small" : size}>
<BodyShort
size={size === "xsmall" ? "small" : size}
as="span"
>
...
</BodyShort>
</li>
@@ -223,9 +218,7 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
page={n}
size={size}
>
<BodyShort size={size === "xsmall" ? "small" : size}>
{n}
</BodyShort>
{n}
</Item>
</li>
);
@@ -243,7 +236,6 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
size={size}
icon={
<ChevronRightIcon
className="navds-pagination__prev-next-icon"
{...(prevNextTexts
? { "aria-hidden": true }
: { title: "Neste" })}
@@ -251,14 +243,7 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
}
iconPosition="right"
>
{prevNextTexts && (
<BodyShort
size={size === "xsmall" ? "small" : size}
className="navds-pagination__next-text"
>
Neste
</BodyShort>
)}
{prevNextTexts && `Neste`}
</Item>
</li>
</ul>
@@ -11,9 +11,10 @@ export interface PaginationItemProps extends ButtonProps {
*/
selected?: boolean;
/**
* The page the item represents
* Currently only sets `data-page` attribute.
* @deprecated Use `data-page` instead if you need to access the items page number in the future.
*/
page: number;
page?: number;
/**
* Changes padding, height and font-size
* @default "medium"
@@ -48,7 +49,7 @@ export const Item: PaginationItemType = forwardRef(
"navds-pagination__item--selected": selected,
})}
data-page={page}
/* TODO: Breaking change to remove. Add to future major version. */
/* TODO: Breaking change to remove since it's in use by some applications. Add to future major version. */
page={page}
{...(Component === "button" && { type: "button" })}
{...rest}
@@ -107,6 +107,7 @@ export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
}, [simulated?.seconds]);
return (
/* biome-ignore lint/a11y/useFocusableInteractive: Progressbar is not interactive. */
<div
ref={ref}
className={cl(
@@ -121,6 +122,7 @@ export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
? `Fremdrift kan ikke beregnes, antatt tid er: ${simulated?.seconds} sekunder`
: `${Math.round(value)} av ${Math.round(valueMax)}`
}
// biome-ignore lint/a11y/useAriaPropsForRole: We found that adding valueMin was not needed
role="progressbar"
aria-labelledby={ariaLabelledBy}
aria-label={ariaLabel}
+15 -16
View File
@@ -74,37 +74,36 @@ const AnimateHeight: React.FC<AnimateHeightProps> = ({
const animationClassesTimeoutID = useRef<Timeout>();
const timeoutID = useRef<Timeout>();
const initialHeight = useRef<Height>(height);
const initialOverflow = useRef<Overflow>("visible");
const duration = prefersReducedMotion ? 0 : userDuration;
let initHeight: Height = height;
let initOverflow: Overflow = "visible";
if (typeof initHeight === "number") {
if (typeof initialHeight.current === "number") {
// Reset negative height to 0
if (typeof height !== "string") {
initHeight = height < 0 ? 0 : height;
initialHeight.current = height < 0 ? 0 : height;
}
initOverflow = "hidden";
} else if (isPercentage(initHeight)) {
initialOverflow.current = "hidden";
} else if (isPercentage(initialHeight.current)) {
// If value is string "0%" make sure we convert it to number 0
initHeight = height === "0%" ? 0 : height;
initOverflow = "hidden";
initialHeight.current = height === "0%" ? 0 : height;
initialOverflow.current = "hidden";
}
const [currentHeight, setCurrentHeight] = useState<Height>(initHeight);
const [overflow, setOverflow] = useState<Overflow>(initOverflow);
const [currentHeight, setCurrentHeight] = useState<Height>(
initialHeight.current,
);
const [overflow, setOverflow] = useState<Overflow>(initialOverflow.current);
const [useTransitions, setUseTransitions] = useState<boolean>(false);
// ------------------ Did mount
useEffect(() => {
// Hide content if height is 0 (to prevent tabbing into it)
hideContent(contentElement.current, currentHeight);
// This should be explicitly run only on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
hideContent(contentElement.current, initialHeight.current);
}, []);
// ------------------ Height update
// biome-ignore lint/correctness/useExhaustiveDependencies: This should be explicitly run only on height change
useEffect(() => {
if (height !== prevHeight.current && contentElement.current) {
showContent(contentElement.current, prevHeight.current);
@@ -98,61 +98,6 @@ export const SizeSmall = () => <TableComponent size="small" />;
export const Buttons = () => <TableComponent size="small" button />;
export const WithDivs = () => {
return (
<div className="navds-table" role="table">
<div className="navds-table__header" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__header-cell" role="columnheader">
Fornavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Etternavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Rolle
</div>
</div>
</div>
<div className="navds-table__body" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Jean-Luc
</div>
<div className="navds-table__data-cell" role="cell">
Picard
</div>
<div className="navds-table__data-cell" role="cell">
Kaptein
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
William
</div>
<div className="navds-table__data-cell" role="cell">
Riker
</div>
<div className="navds-table__data-cell" role="cell">
Kommandør
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Geordi
</div>
<div className="navds-table__data-cell" role="cell">
La Forge
</div>
<div className="navds-table__data-cell" role="cell">
Sjefsingeniør
</div>
</div>
</div>
</div>
);
};
const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
const useToggleList = (initialState) => {
const [list, setList] = useState(initialState);
@@ -259,10 +204,6 @@ export const Chromatic = {
<h3>With Buttons</h3>
<Buttons />
</div>
<div>
<h3>Custom with divs</h3>
<WithDivs />
</div>
<div>
<h3>Selection</h3>
<Selection />
@@ -41,7 +41,7 @@ export function useScrollButtons(listRef: React.RefObject<HTMLDivElement>) {
return () => {
win.removeEventListener("resize", handleResize);
resizeObserver && resizeObserver.disconnect();
resizeObserver?.disconnect();
updateScrollButtonState.clear();
};
}, [listRef, updateScrollButtonState]);
@@ -26,19 +26,19 @@ export function useTabList() {
const nextTab = () => {
const next = descendants.nextEnabled(idx, loop);
next && next.node?.focus();
next?.node?.focus();
};
const prevTab = () => {
const prev = descendants.prevEnabled(idx, loop);
prev && prev.node?.focus();
prev?.node?.focus();
};
const firstTab = () => {
const first = descendants.firstEnabled();
first && first.node?.focus();
first?.node?.focus();
};
const lastTab = () => {
const last = descendants.lastEnabled();
last && last.node?.focus();
last?.node?.focus();
};
const keyMap: Record<string, React.KeyboardEventHandler> = {
+20 -21
View File
@@ -91,27 +91,26 @@ export const TimelineRow = forwardRef<HTMLOListElement, TimelineRowProps>(
}
}}
>
{periods &&
periods.map((period) => {
return (
<li key={`period-${period.id}`}>
<PeriodContext.Provider
value={{
periodId: period.id,
firstFocus: firstFocusable?.id === period.id,
restProps: period?.restProps,
}}
>
<Period
start={period.start}
end={period.endInclusive}
icon={period.icon}
ref={period?.ref}
/>
</PeriodContext.Provider>
</li>
);
})}
{periods?.map((period) => {
return (
<li key={`period-${period.id}`}>
<PeriodContext.Provider
value={{
periodId: period.id,
firstFocus: firstFocusable?.id === period.id,
restProps: period?.restProps,
}}
>
<Period
start={period.start}
end={period.endInclusive}
icon={period.icon}
ref={period?.ref}
/>
</PeriodContext.Provider>
</li>
);
})}
</ol>
</div>
</>
@@ -55,19 +55,19 @@ export function useToggleItem<P extends UseToggleItemProps>(
const nextTab = () => {
const next = descendants.nextEnabled(idx, false);
next && next.node?.focus();
next?.node?.focus();
};
const prevTab = () => {
const prev = descendants.prevEnabled(idx, false);
prev && prev.node?.focus();
prev?.node?.focus();
};
const firstTab = () => {
const first = descendants.firstEnabled();
first && first.node?.focus();
first?.node?.focus();
};
const lastTab = () => {
const last = descendants.lastEnabled();
last && last.node?.focus();
last?.node?.focus();
};
const keyMap: Record<string, React.KeyboardEventHandler> = {
@@ -152,14 +152,14 @@ export const FontWeight: Story = {
return (
<>
{fontWeights.map((weight) => (
<>
<React.Fragment key={weight}>
<h2>{weight}</h2>
<FontComponent
key={weight}
weight={weight}
family="Source Sans 3"
/>
</>
</React.Fragment>
))}
</>
);
@@ -40,7 +40,7 @@ const checkState = (
* https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow/ownerWindow.ts
*/
const ownerWindow = (node: Node | undefined): Window => {
const doc = (node && node.ownerDocument) || document;
const doc = node?.ownerDocument || document;
return doc.defaultView || window;
};
@@ -205,6 +205,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
syncHeight();
});
// biome-ignore lint/correctness/useExhaustiveDependencies: Since value is an external prop, we want to reset the renders on every time it changes, even when it is undefined or empty.
useEffect(() => {
renders.current = 0;
}, [value]);
@@ -222,7 +223,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
};
const mainStyle: React.CSSProperties = {
["--__ac-textarea-height"]: state.outerHeightStyle + "px",
"--__ac-textarea-height": state.outerHeightStyle + "px",
// Need a large enough difference to allow scrolling.
// This prevents infinite rendering loop.
overflow:
@@ -40,11 +40,12 @@ export function createContext<T>(options: CreateContextOptions<T> = {}) {
* We use forwardRef to allow `ref` to be used as a regular context value
* @see https://reactjs.org/docs/forwarding-refs.html#forwarding-refs-to-dom-components
*/
const Provider = forwardRef(
({ children, ...context }: ProviderProps<T>, ref) => {
const Provider = forwardRef<unknown, ProviderProps<T>>(
({ children, ...context }, ref) => {
// Only re-memoize when prop values change
// eslint-disable-next-line react-hooks/exhaustive-deps
const value = React.useMemo(() => context, Object.values(context)) as T;
// biome-ignore lint/correctness/useExhaustiveDependencies: Object.values(context) includes all dependencies.
const value = React.useMemo(() => context, Object.values(context)) as T; // eslint-disable-line react-hooks/exhaustive-deps
return (
<Context.Provider value={ref ? { ...value, ref } : value}>
@@ -1,5 +1,4 @@
import * as React from "react";
import { Box } from "../../../layout/box";
import { HStack } from "../../../layout/stack";
import { createDescendantContext } from "./useDescendant";
@@ -16,12 +15,16 @@ const [
_useDescendantsContext,
useDescendants,
useDescendant,
] = createDescendantContext<HTMLDivElement, { value?: string }>();
] = createDescendantContext<
HTMLButtonElement | HTMLInputElement,
{ value?: string }
>();
function Select({ children }: { children?: React.ReactNode }) {
const descendants = useDescendants();
const count = descendants.count();
// biome-ignore lint/correctness/useExhaustiveDependencies: We want count to be a trigger for focusing the last descendant here.
React.useEffect(() => {
descendants.last()?.node.focus();
}, [descendants, count]);
@@ -37,9 +40,8 @@ function Option({ value, disabled }: { value?: string; disabled?: boolean }) {
});
return (
<Box
<button
ref={register}
role="button"
tabIndex={0}
data-value={value}
onKeyDown={(event) => {
@@ -51,7 +53,7 @@ function Option({ value, disabled }: { value?: string; disabled?: boolean }) {
}}
>
Option {index + 1}
</Box>
</button>
);
}
@@ -60,7 +60,7 @@ export class DescendantsManager<
this.descendants.forEach((descendant) => {
const index = descendants.indexOf(descendant.node);
descendant.index = index;
descendant.node.dataset["index"] = descendant.index.toString();
descendant.node.dataset.index = descendant.index.toString();
});
};
@@ -52,7 +52,7 @@ export function createDescendantContext<
useClientLayoutEffect(() => {
if (!ref.current) return;
const dataIndex = Number(ref.current.dataset["index"]);
const dataIndex = Number(ref.current.dataset.index);
if (index !== dataIndex && !Number.isNaN(dataIndex)) {
setIndex(dataIndex);
}
+10
View File
@@ -1,5 +1,15 @@
# @navikt/ds-tailwind
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
## 7.1.0
## 7.0.1
## 7.0.0
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "7.0.1",
"version": "7.2.1",
"description": "Tailwind config based on @navikt/ds-tokens",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -15,7 +15,7 @@
"tailwind.config.d.ts"
],
"scripts": {
"build": "ts-node ./src",
"build": "yarn tsx ./src",
"test": "vitest run",
"test:watch": "vitest watch"
},
@@ -25,11 +25,11 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.0.1",
"@navikt/ds-tokens": "^7.2.1",
"color": "4.2.3",
"lodash": "^4.17.21",
"tailwindcss": "^3.3.3",
"ts-node": "^10.9.1",
"tsx": "^4.19.1",
"typescript": "^5.1.6",
"vitest": "^1.2.2"
},
+2
View File
@@ -0,0 +1,2 @@
figma-config.json
plugin.js
Loaded 100 of 266 files, more files were not shown because too many files have changed in this diff. Show more