Compare commits

..
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] 99465ddbeb Version Packages (#3490)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-01-07 12:02:45 +01:00
Ken 25305ebba0 🐛 Use inside Alert got wrong text-color while focused or active (#3489) 2025-01-07 11:50:28 +01:00
Halvor HauganandKen ef3464c96c Support contributions from outside Nav + update some README files (#3483)
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2025-01-06 11:36:42 +01:00
Ken 0480d785c4 🔇 Removed amplitude logging (#3485) 2025-01-01 12:56:36 +01:00
Halvor Haugan ef5e671cfe i18n: rename some files, add tests for useDateLocale (#3481) 2024-12-28 17:05:15 +01:00
Halvor HauganandKen fa156773e0 Upgrade vitest and some related packages (#3482)
* vitest

* Upgrade @testing-library

* Fix error building ds-react

* Upgrade "jsdom" in react-workspace (fixes "The `punycode` module is deprecated")

* ♻️ Matched JSDOM-versions across workspace (v25)

---------

Co-authored-by: Ken <ken.aleksander@gmail.com>
2024-12-28 17:03:37 +01:00
Halvor HauganandKen 137b4c49c1 Automatically add JSDoc with default values to nb.ts (#3479)
* Automatically add JSDoc with default values to nb.ts

* Added jscodeshift to devDeps

* 🐛 Use same jscodeshift versions

* 🐛 Use same types for jscodeshift

---------

Co-authored-by: Ken <ken.aleksander@gmail.com>
2024-12-23 12:03:07 +01:00
Ken 1fb72241fe [Aksel.nav.no] Image comparison module (#3473)
* WIP

* WIP CompareImages

* ✨ Handle more keys

* ♻️ Re-use positioning code

* 💄 Adjust for width

* ✨ Added sanity schema

* 💄 Moved focus to box, allow handle to overflow leftside by 1 px

* ✨ Added bg and alt-text support

* 🐛 Change env back to production
2024-12-23 11:55:52 +01:00
Julian Nymark 42470baaaf ❄️ ⛄ 🎶 🎄 Website: add snowfall (again!) (#3475)
* Website: add snowfall

* website: add egregious christmas ambience

* Revert "website: add egregious christmas ambience"

This reverts commit 3fb858b6ad.

* move dep

* yarn.lock
2024-12-20 15:23:19 +01:00
github-actions[bot]andgithub-actions[bot] 8cd6512181 Version Packages (#3474)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-20 14:48:03 +01:00
Halvor Haugan 7e976f9e20 [aksel.nav.no] Add example of ErrorMessage with icon (#3476) 2024-12-20 12:13:30 +01:00
Ken db828bddd3 [Switch] Updated default design to better reflect unchecked state (#3468)
* WIP Switch update

* 💄 Updated Switch to reflect unchecked state better

* 📝 Synced token for switch
2024-12-20 11:27:33 +01:00
443b129fe1 Bugfix/button loading issue in modal (#3252)
* Check loading prop for showing spinner, not widthOverride.

Making this change because widthOverride sometimes gets set to 0 (should also be fixed), meaning this check fails as "falsy".

* Prevent setting width to 0 while showing spinner, to avoid issue in Modals

Checking that width is truthy (not zero) will avoid issue where widthOverride gets set to 0 because the button is not yet rendered.

* Add changeset

* Replace width calculation with just visually hiding label + icon and absolutely positioning the loading icon.

* Update @navikt/core/react/src/button/Button.tsx

* add story for bug case

* We no longer use buttonRef/mergedRef internally, so removed them and set ref directly

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

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

* Hiding everything except loading spinner is more robust than hiding every specific item.

* In loading-state, the button icon will no longer be able to be the only child (hidden), because of the loader icon. Instead checks for no label.

Could consider :not(:has(:is(.navds-button__icon, .navds-loader))) as well, to be more robust, but it might be overkill.

* Much simpler/easier to read

* Don't need to destructure style, only to add it along with rest.

* Should destructure onKeyUp from props, instead of having it added through filteredProps and then overridden afterwards.

* Quick and easy conversion for darkside

* Unnecessary :is after refactoring

---------

Co-authored-by: Julian Nymark <julian.nymark@nav.no>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
2024-12-20 10:44:04 +01:00
Ken 561fecbcac [Darkside] Updated <Theme /> component (#3467)
* 🚚 Rename and move theme-component

* 🐛 Portal now preserves theme when out of cascade

* 🐛 useThemeInternal missing strict-mode false

* 🐛 Removed unused storybook story

* Update @navikt/core/react/src/theme/Theme.tsx

* 📝 remove commented out code
2024-12-20 09:29:22 +01:00
Ken 00177898f5 [Select] 🐛 Focus-border no longer cancels out error-border. (#3465)
* 🐛 Focus-border no longer cancels out error-border.

* ♻️ Optimize CSS selectors, fix textarea

* 📝 Changeset
2024-12-19 17:20:10 +01:00
Ken 38f5e63008 🐛 Removed default tokens added to tailwind, added opacity-token (#3472) 2024-12-19 16:33:42 +01:00
Halvor Haugan c3d65a681e Make Chromatic workflow fail on errors but not on changes (#3471) 2024-12-19 16:05:07 +01:00
Ken 3025e4eb5c [Darkside] ✨ Now adds version-tokens to tokens css (#3466)
* ✨ Now adds version-tokens to tokens css

* ♻️ Remove redundant exist-check
2024-12-19 15:30:49 +01:00
Ken 043277f79b [Darkside] 💄 New ToggleGroup design (#3460)
* 💄 New ToggleGroup design

* 🐛 Removed hover-state from selected togglegroup in forced-colors mode

* 💄 Add px-gap to elements

* 🐛 Hide divider on both sides when selected
2024-12-19 15:16:03 +01:00
Julian NymarkandHalvor Haugan a2b3936878 Textarea: resize immediately upon receiving resize event (#3463)
* change: add story to demonstrate buggy textarea behaviour inside modal

* use lodash debounce with our own default value for wait

* Revert "use lodash debounce with our own default value for wait"

This reverts commit 7638297765.

* change: add leading to debounce + use it in TextareaAutoSize

* changeset

* changeset + co-author

Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>

---------

Co-authored-by: Halvor Haugan <halvor.haugan@nav.no>
2024-12-19 14:23:39 +01:00
Julian Nymark c90abe7c23 [Darkside] Warning triangle icon in error messages (#3445)
* change: support icon in ErrorMessage + warning icon for textarea error

* change: ignore icon on old CSS + tweaks

* change: do not nest in old css

* wip commit (works nicely)

* clean up css for errormessage, remove it from form.css

* 🐛 tweaks + use icon in Search

* use icon for the rest of our form components

* backport new css to old form.css

* 🔥 remove unnecessary specification bump

* cleaner example story

* changeset

* ♻️ icon -> showIcon

* make flex & gap opt-in with 'icon'

* change: inline ErrorMessageIcon

* change: nest css more

* revert test changes (utility in multiple variants on one for DOM diffing, but probably no longer useful)

* debloat changeset

* mention form components in changeset

* change: typo in changeset

* change: typo in changeset
2024-12-19 14:17:50 +01:00
150 changed files with 2340 additions and 2247 deletions

No files matched your search

+14 -10
View File
@@ -1,31 +1,35 @@
# Changesets
For versjonering bruker vi løsningen [Changesets](https://github.com/changesets/changesets) for [versjonering](https://semver.org/) og publisering av pakker.
Vi bruker [Changesets](https://github.com/changesets/changesets) for [versjonering](https://semver.org/) og publisering av pakker.
## Howto
## Lage ny versjon
- For å lage en ny versjons-entry: `yarn changeset`
- Velg pakkene som skal versjoneres med `arrowkeys` + `Space`
1. `yarn changeset`
2. Svar på spørsmålene som kommer opp:
`🦋 Which packages would you like to include?`
Trykk `Space` for å velge pakkene som skal versjoneres, for så trykke `Enter` for å gå videre.
Trykk `Space` for å velge pakkene som skal versjoneres, trykk så `Enter` for å gå videre.
`🦋 Which packages should have a major bump?`
Trykk `Enter` for å gå videre _*eller*_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`
Trykk `Enter` for å gå videre _eller_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`.
`🦋 Which packages should have a minor bump?`
Trykk `Enter` for å gå videre _*eller*_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`
Trykk `Enter` for å gå videre _eller_ velg pakkene som skal minor-bumpes med `arrowkeys` + `Space`.
Hvis du ikke velger major eller minor, så blir pakkene få en patch-bump
Hvis du ikke velger major eller minor, så vil pakkene få en patch-bump.
- Commit og push.
`Please enter a summary for this change`
Skriv inn en kort beskrivelse av endringen på formatet `<Komponent>: <gitmoji?> <Beskrivelse>`, f.eks. "Button: ✨ Add feature xyz".
3. Commit og push.
## Fixed versjonering
Alle base-pakkene våre har `fixed versjonering`. Dette tilsier at en bump på en av disse vil bumpe alle til samme versjon
Alle base-pakkene våre har `fixed versjonering`. Dette betyr at en bump på en av disse vil bumpe alle til samme versjon
```sh
"@navikt/ds-react",
+9 -22
View File
@@ -47,12 +47,12 @@ jobs:
- name: Chromatic
uses: chromaui/action@latest
id: chromatic_tests
continue-on-error: true
continue-on-error: false
with:
autoAcceptChanges: "main"
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitZeroOnChanges: false
exitZeroOnChanges: true
buildScriptName: "build:storybook-chromatic"
onlyChanged: true
untraced: "**/package.json|yarn.lock|**/*.md"
@@ -62,14 +62,14 @@ jobs:
STORYBOOK_GITHUB_SHA: ${{ github.sha }}
- name: Get commit sha
if: "!contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'"
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
id: vars
shell: bash
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Find Comment
if: "!contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'"
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
uses: peter-evans/find-comment@v2
id: fc
with:
@@ -78,19 +78,7 @@ jobs:
body-includes: Storybook demo
- name: Create or update comment
if: steps.chromatic_tests.outcome == 'success' && !contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'
uses: peter-evans/create-or-update-comment@v3
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## [Storybook demo](${{steps.chromatic_tests.outputs.storybookUrl}})
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
edit-mode: replace
- name: Create or update failed comment
if: steps.chromatic_tests.outcome != 'success' && !contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
uses: peter-evans/create-or-update-comment@v3
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
@@ -98,10 +86,9 @@ jobs:
body: |
## [Storybook demo](${{steps.chromatic_tests.outputs.storybookUrl}}) / [Chromatic](${{steps.chromatic_tests.outputs.url}})
### 📝 Endringer til review: ${{steps.chromatic_tests.outputs.changeCount}}
${{ steps.chromatic_tests.outputs.changeCount > 0 && format('### 📝 Changes to review: {0}', steps.chromatic_tests.outputs.changeCount) || '' }}
${{ steps.chromatic_tests.outputs.errorCount > 0 && format('### ❌ Stories with errors: {0}', steps.chromatic_tests.outputs.errorCount) || '' }}
${{ steps.chromatic_tests.outputs.interactionTestFailuresCount > 0 && format('### ❌ Failed interaction tests: {0}', steps.chromatic_tests.outputs.interactionTestFailuresCount) || '' }}
${{ steps.chromatic_tests.outputs.errorCount > 0 && format('### ❌ Stories med feil: {0}', steps.chromatic_tests.outputs.errorCount) || '' }}
${{ steps.chromatic_tests.outputs.interactionTestFailuresCount > 0 && format('### ❌ Feilede interaksjonstester: {0}', steps.chromatic_tests.outputs.interactionTestFailuresCount) || '' }}
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} components | ${{steps.chromatic_tests.outputs.specCount}} stories
edit-mode: replace
+4 -6
View File
@@ -5,10 +5,8 @@ import React, { useLayoutEffect } from "react";
import darksideCss from "../@navikt/core/css/darkside/index.css?inline";
// @ts-expect-error - Temporary
import defaultCss from "../@navikt/core/css/index.css?inline";
import {
Provider,
UNSAFE_AkselTheme,
} from "../@navikt/core/react/src/provider";
import { Provider } from "../@navikt/core/react/src/provider";
import { Theme } from "../@navikt/core/react/src/theme";
import en from "../@navikt/core/react/src/util/i18n/locales/en";
import nb from "../@navikt/core/react/src/util/i18n/locales/nb";
import nn from "../@navikt/core/react/src/util/i18n/locales/nn";
@@ -31,9 +29,9 @@ const ModeDecorator = ({ children, mode, theme }) => {
}, [mode]);
return mode === "darkside" ? (
<UNSAFE_AkselTheme theme={theme || undefined} hasBackground={false}>
<Theme theme={theme || undefined} hasBackground={false}>
{children}
</UNSAFE_AkselTheme>
</Theme>
) : (
children
);
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-icons
## 7.9.1
## 7.9.0
## 7.8.1
## 7.8.0
+2 -2
View File
@@ -1,6 +1,6 @@
# Aksel icons
800+ open source icons made by Aksel, NAVs designsystem-team
800+ open source icons made by Aksel, Nav's design system team.
- Designed for 24x24px
- Available in React and SVG
@@ -30,7 +30,7 @@ Tip: Use the `title` prop for accessible icons.
### Sizing
Each icons `width` and `height` is default `1em`. This allows you to use the native `fontSize`-prop to adjust sizing. We recommend using at least 1.5rem/24px for best possible visual representation.
Each icon's `width` and `height` is default `1em`. This allows you to use the native `fontSize`-prop to adjust sizing. We recommend using at least 1.5rem/24px for the best possible visual representation.
```jsx
<StarIcon fontSize="1.5rem" />
+2 -2
View File
@@ -2,9 +2,9 @@
# Aksel Icons plugin
Pluginen gir deg mulighet til å se gjennom og hente ut alle ikonene i fra ikonpakken `@navikt/aksel-icons`. Du slipper da å hente nye oppdateringer fra Figma Community.
Pluginen gir deg mulighet til å se gjennom og hente ut alle ikonene fra ikonpakken `@navikt/aksel-icons`. Du slipper da å hente nye oppdateringer fra Figma Community.
Referer til [ikon-dokumentasjonen](https://aksel.nav.no/ikoner) for å se alle ikonene.
Gå til [ikon-dokumentasjonen](https://aksel.nav.no/ikoner) for å se alle ikonene.
Har du ønsker om nye ikoner, eller ønsker å bidra med å lage nye, lag et issue på [GitHub-repoet for designsystemet](https://github.com/navikt/aksel).
## Quickstart
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "7.8.1",
"version": "7.9.1",
"author": "Aksel | Nav",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
@@ -87,6 +87,6 @@
"tsx": "^4.7.1",
"typescript": "5.5.4",
"unified": "10.1.2",
"vitest": "^1.2.2"
"vitest": "^2.1.8"
}
}
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-stylelint
## 7.9.1
## 7.9.0
## 7.8.1
## 7.8.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "7.8.1",
"version": "7.9.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.8.1",
"@navikt/ds-tokens": "^7.8.1",
"@navikt/ds-css": "^7.9.1",
"@navikt/ds-tokens": "^7.9.1",
"concurrently": "9.0.1",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0",
+14
View File
@@ -1,5 +1,19 @@
# @navikt/aksel
## 7.9.1
### Patch Changes
- Updated dependencies [[`25305eb`](https://github.com/navikt/aksel/commit/25305ebba04a37e6315bc37bfabb39f04f83f146)]:
- @navikt/ds-css@7.9.1
## 7.9.0
### Patch Changes
- Updated dependencies [[`db828bd`](https://github.com/navikt/aksel/commit/db828bddd393ba1d03d8e68fe80c9211da43e54a), [`0017789`](https://github.com/navikt/aksel/commit/00177898f55573a682a43819b08304c1f60a732a), [`c90abe7`](https://github.com/navikt/aksel/commit/c90abe7c23e2520e5131d138f3193a4de0e28b9d), [`0017789`](https://github.com/navikt/aksel/commit/00177898f55573a682a43819b08304c1f60a732a)]:
- @navikt/ds-css@7.9.0
## 7.8.1
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "7.8.1",
"version": "7.9.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.8.1",
"@navikt/ds-css": "7.9.1",
"axios": "1.7.4",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
@@ -46,7 +46,7 @@
"devDependencies": {
"rimraf": "6.0.1",
"typescript": "5.5.4",
"vitest": "^1.2.2"
"vitest": "^2.1.8"
},
"sideEffects": false,
"engines": {
+21
View File
@@ -1,5 +1,26 @@
# @navikt/ds-css
## 7.9.1
### Patch Changes
- Link: :bug: Use inside Alert got wrong text-color while focused or active. ([#3489](https://github.com/navikt/aksel/pull/3489))
## 7.9.0
### Minor Changes
- ErrorSummary: :sparkles: new prop to prefix error with a warning icon. ([#3445](https://github.com/navikt/aksel/pull/3445))
All form components: replace dot with warning icon in error message.
### Patch Changes
- Switch: Update switch to better reflect unchecked state. ([#3468](https://github.com/navikt/aksel/pull/3468))
- Select: :bug: Focus-border no longer cancels out error-border. ([#3465](https://github.com/navikt/aksel/pull/3465))
- Textarea: :bug: Focus-border no longer cancels out error-border. ([#3465](https://github.com/navikt/aksel/pull/3465))
## 7.8.1
### Patch Changes
+6 -6
View File
@@ -1,11 +1,11 @@
# Aksel styling
CSS for NAVs designsystem.
CSS for Nav's design system.
- Css tokens (Colors, border, shadows etc)
- Component-styling for `@navikt/ds-react` packages
- Css tokens (Colors, border, shadows etc.)
- Component styling for `@navikt/ds-react` packages
- Normalize.css v8
- Font-loading
- Font loading
## Installation
@@ -16,7 +16,7 @@ CSS for NAVs designsystem.
## Usage
Import styles once to make it accessible for your entire project. Typically you will add the following line to your root .tsx file.
Import styles once to make it accessible for your entire project. Typically you will add the following line to your root .tsx file:
```javascript
import "@navikt/ds-css";
@@ -26,7 +26,7 @@ function App() {
}
```
But you can import it in a .css-file
But you can import it in a .css-file as well:
```css
@import "@navikt/ds-css";
+9 -1
View File
@@ -109,7 +109,7 @@
margin-right: var(--ac-button-icon-margin);
}
.navds-button__icon:only-child {
.navds-button--icon-only .navds-button__icon {
margin: 0;
}
@@ -497,6 +497,10 @@
}
/* Loader overrides */
.navds-button .navds-loader {
position: absolute;
}
.navds-button .navds-loader .navds-loader__foreground {
stroke: var(--ac-button-loader-stroke, currentColor);
}
@@ -506,6 +510,10 @@
stroke: var(--ac-button-primary-loader-stroke-bg, rgb(255 255 255 / 0.3));
}
.navds-button--loading > :not(.navds-loader) {
visibility: hidden;
}
@media (forced-colors: active) {
.navds-button:not(.navds-button--loading):where(:disabled, .navds-button--disabled) {
opacity: 1;
@@ -69,7 +69,7 @@
margin-right: var(--__axc-button-icon-margin);
}
&:only-child {
.navds-button--icon-only & {
margin: 0;
}
}
@@ -264,6 +264,10 @@
}
/* Loader overrides */
.navds-button .navds-loader {
position: absolute;
}
.navds-button .navds-loader .navds-loader__foreground {
stroke: currentColor;
}
@@ -273,6 +277,10 @@
stroke: rgb(255 255 255 / 0.3);
}
.navds-button--loading > :not(.navds-loader) {
visibility: hidden;
}
@media (forced-colors: active) {
.navds-button:not(.navds-button--loading):where(:disabled, .navds-button--disabled) {
opacity: 1;
@@ -9,19 +9,6 @@
color: var(--ax-text-subtle);
}
.navds-form-field .navds-error-message,
.navds-fieldset .navds-error-message,
.navds-file-item .navds-error-message {
display: flex;
gap: var(--ax-spacing-2);
}
.navds-form-field .navds-error-message::before,
.navds-fieldset .navds-error-message::before,
.navds-file-item .navds-error-message::before {
content: "•";
}
.navds-form-field--disabled {
opacity: var(--ax-opacity-disabled);
cursor: not-allowed;
@@ -13,11 +13,10 @@
border-radius: var(--ax-border-radius-medium);
background-color: transparent;
box-shadow: inset 0 0 0 1px var(--ax-border-default);
padding: var(--ax-spacing-1);
gap: var(--ax-spacing-1);
display: inline-grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
gap: 1px;
}
/* Button */
@@ -26,13 +25,14 @@
align-items: center;
justify-content: center;
padding: var(--ax-spacing-1) var(--ax-spacing-3);
min-height: 2.5rem;
min-height: 3rem;
border: none;
cursor: pointer;
background-color: transparent;
color: var(--ax-text-default);
border-radius: var(--ax-border-radius-small);
border-radius: var(--ax-border-radius-medium);
min-width: fit-content;
position: relative;
&:hover {
background-color: var(--ax-bg-accent-moderate-hoverA);
@@ -42,12 +42,23 @@
&:focus-visible {
outline: 2px solid var(--ax-border-focus);
outline-offset: 2px;
z-index: 1;
}
&[aria-checked="true"] {
&[data-selected="true"] {
background-color: var(--ax-bg-accent-strong-pressed);
color: var(--ax-text-accent-contrast);
}
&:not([data-selected="true"], :first-child, &[data-selected="true"] + &)::before {
content: "";
display: block;
width: 1px;
height: 50%;
background-color: var(--ax-border-subtleA);
left: -1px;
position: absolute;
}
}
.navds-toggle-group__button-inner {
@@ -70,7 +81,7 @@
background-color: var(--ax-bg-neutral-moderate-hoverA);
}
&[aria-checked="true"] {
&[data-selected="true"] {
background-color: var(--ax-bg-neutral-strong-pressed);
color: var(--ax-text-neutral-contrast);
}
@@ -80,7 +91,7 @@
.navds-toggle-group--small {
& .navds-toggle-group__button {
padding: var(--ax-spacing-05) var(--ax-spacing-3);
min-height: 1.5rem;
min-height: 2rem;
}
& .navds-toggle-group__button-inner > svg {
@@ -94,14 +105,13 @@
border: 2px solid transparent;
}
.navds-toggle-group__button[aria-checked="true"]:hover,
.navds-toggle-group__button:hover {
background-color: highlighttext;
color: highlight;
outline: 1px solid highlight;
}
.navds-toggle-group__button[aria-checked="true"] {
.navds-toggle-group__button[data-selected="true"] {
background-color: selecteditem;
color: selecteditemtext;
forced-color-adjust: none;
@@ -204,6 +204,18 @@
.navds-error-message {
color: var(--ax-text-danger);
&.navds-error-message--show-icon {
display: flex;
gap: var(--ax-spacing-1);
& svg {
margin-top: 0.15em;
flex: 0 0 auto;
width: 1em;
height: 1em;
}
}
}
/* -------------------------------- Utilities ------------------------------- */
-13
View File
@@ -17,19 +17,6 @@
color: var(--ac-form-description, var(--__ac-form-description, var(--a-text-subtle)));
}
.navds-form-field .navds-error-message,
.navds-fieldset .navds-error-message,
.navds-file-item .navds-error-message {
display: flex;
gap: var(--a-spacing-2);
}
.navds-form-field .navds-error-message::before,
.navds-fieldset .navds-error-message::before,
.navds-file-item .navds-error-message::before {
content: "•";
}
.navds-form-field--disabled {
opacity: 0.3;
cursor: not-allowed;
+5 -1
View File
@@ -73,11 +73,15 @@
/**
Error handling
*/
.navds-select--error > * select:not(:hover, :focus, :disabled) {
.navds-select--error > * .navds-select__input:not(:hover, :disabled) {
box-shadow: 0 0 0 1px var(--ac-select-error-border, var(--a-surface-danger));
border-color: var(--ac-select-error-border, var(--a-surface-danger));
}
.navds-select--error .navds-select__input:focus {
box-shadow: var(--a-shadow-focus);
}
/**
Disabled
*/
+30 -19
View File
@@ -72,12 +72,15 @@
.navds-switch__track {
width: 2.75rem;
height: 1.5rem;
background-color: var(--ac-switch-bg, var(--a-surface-neutral));
background-color: var(--ac-switch-bg, var(--a-surface-default));
border: 2px solid var(--a-border-default);
position: absolute;
top: var(--a-spacing-3);
left: 0;
border-radius: var(--a-border-radius-full);
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1);
transition-property: background-color, border-color;
transition-duration: 100ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.navds-switch--small > .navds-switch__track {
@@ -92,18 +95,20 @@
.navds-switch__input:checked ~ .navds-switch__track {
background-color: var(--ac-switch-checked-bg, var(--a-surface-action-selected));
border-color: var(--ac-switch-checked-bg, var(--a-surface-action-selected));
}
.navds-switch__input:hover ~ .navds-switch__track {
background-color: var(--ac-switch-hover-bg, var(--a-surface-neutral-hover));
background-color: var(--ac-switch-hover-bg, var(--a-surface-subtle));
}
.navds-switch__input:hover:checked ~ .navds-switch__track {
.navds-switch__input:hover:checked:not(:disabled) ~ .navds-switch__track {
background-color: var(--ac-switch-checked-hover-bg, var(--a-surface-action-selected-hover));
border-color: var(--ac-switch-checked-hover-bg, var(--a-surface-action-selected-hover));
}
.navds-switch__input:disabled ~ .navds-switch__track {
background-color: var(--ac-switch-bg, var(--a-surface-neutral));
background-color: var(--ac-switch-bg, var(--a-surface-default));
}
.navds-switch__input:checked:disabled ~ .navds-switch__track {
@@ -129,15 +134,15 @@
/* Thumb */
.navds-switch__thumb {
background-color: var(--ac-switch-thumb-bg, var(--a-surface-default));
background-color: var(--ac-switch-thumb-bg, var(--a-surface-neutral));
color: var(--ac-switch-thumb-icon, var(--a-icon-subtle));
border-radius: var(--a-border-radius-full);
width: 1.25rem;
height: 1.25rem;
width: 1.125rem;
height: 1.125rem;
position: absolute;
transform: translateX(0);
left: var(--a-spacing-05);
top: var(--a-spacing-05);
left: 0.0625rem;
top: 0.0625rem;
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
@@ -147,6 +152,11 @@
.navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb {
transform: translateX(1.25rem);
color: var(--ac-switch-thumb-icon-checked, var(--a-icon-action-selected));
background-color: var(--a-surface-default);
width: 1.25rem;
height: 1.25rem;
left: 0;
top: 0;
}
.navds-switch__input:checked ~ .navds-switch__track .navds-switch__checkmark {
@@ -195,6 +205,7 @@
.navds-switch--readonly > .navds-switch__input:checked ~ .navds-switch__track,
.navds-switch--readonly > .navds-switch__input:checked:hover ~ .navds-switch__track {
background-color: var(--a-surface-neutral-moderate);
border-color: var(--a-border-subtle);
}
.navds-switch--readonly > .navds-switch__label-wrapper,
@@ -212,12 +223,12 @@
}
.navds-switch--readonly .navds-switch__thumb {
background-color: var(--a-surface-subtle);
box-shadow: 0 0 0 2px var(--a-border-default);
background-color: var(--a-surface-neutral);
}
.navds-switch--readonly > .navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb {
color: var(--a-icon-subtle);
color: var(--a-icon-on-neutral);
background-color: var(--a-surface-neutral);
}
@media (hover: hover) and (pointer: fine) {
@@ -237,7 +248,9 @@
}
.navds-switch__thumb,
.navds-switch--readonly .navds-switch__thumb {
.navds-switch--readonly .navds-switch__thumb,
.navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb,
.navds-switch--readonly .navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb {
background-color: fieldtext;
}
@@ -246,11 +259,8 @@
color: field;
}
.navds-switch__track {
width: calc(2.75rem + 4px);
height: calc(1.5rem + 4px);
top: var(--a-spacing-3);
border: 2px solid fieldtext;
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb {
background-color: graytext;
}
.navds-switch__input:focus-visible ~ .navds-switch__track {
@@ -264,6 +274,7 @@
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__thumb {
background-color: graytext;
border-color: graytext;
}
.navds-switch--disabled:not(.navds-switch--loading) .navds-switch__track {
+3 -6
View File
@@ -111,16 +111,13 @@
/**
Error handling
*/
.navds-textarea--error .navds-textarea__input:not(:hover, :focus-visible, :disabled) {
.navds-textarea--error .navds-textarea__input:not(:hover, :disabled) {
box-shadow: 0 0 0 1px var(--ac-textarea-error-border, var(--__ac-textarea-error-border, var(--a-border-danger)));
border-color: var(--ac-textarea-error-border, var(--__ac-textarea-error-border, var(--a-border-danger)));
}
@supports not selector(:focus-visible) {
.navds-textarea--error .navds-textarea__input:not(:hover, :focus, :disabled) {
box-shadow: 0 0 0 1px var(--ac-textarea-error-border, var(--__ac-textarea-error-border, var(--a-border-danger)));
border-color: var(--ac-textarea-error-border, var(--__ac-textarea-error-border, var(--a-border-danger)));
}
.navds-textarea--error .navds-textarea__input:focus-visible {
box-shadow: var(--a-shadow-focus);
}
.navds-textarea__input:disabled {
+1 -1
View File
@@ -30,7 +30,7 @@
text-decoration: underline;
}
.navds-alert:not(.navds-alert--inline) .navds-link,
.navds-alert:not(.navds-alert--inline) .navds-link:not(:active, :focus-visible),
.navds-confirmation-panel .navds-link {
color: var(--a-text-default);
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "7.8.1",
"version": "7.9.1",
"description": "CSS for Nav Designsystem",
"author": "Aksel | Nav designsystem team",
"keywords": [
@@ -28,7 +28,7 @@
"test": "yarn build && vitest run"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.8.1",
"@navikt/ds-tokens": "^7.9.1",
"@types/clean-css": "4.2.11",
"autoprefixer": "^10.4.20",
"browserslist": "^4.24.2",
@@ -44,7 +44,7 @@
"postcss-combine-duplicated-selectors": "10.0.3",
"postcss-import": "^14.1.0",
"tsx": "^4.7.1",
"vitest": "^1.2.2"
"vitest": "^2.1.8"
},
"browserslist": [
">0.2%",
+3 -3
View File
@@ -411,11 +411,11 @@
},
"switch": {
"--ac-switch-action": "--a-surface-action-selected",
"--ac-switch-bg": "--a-surface-neutral",
"--ac-switch-bg": "--a-surface-default",
"--ac-switch-checked-bg": "--a-surface-action-selected",
"--ac-switch-hover-bg": "--a-surface-neutral-hover",
"--ac-switch-hover-bg": "--a-surface-subtle",
"--ac-switch-checked-hover-bg": "--a-surface-action-selected-hover",
"--ac-switch-thumb-bg": "--a-surface-default",
"--ac-switch-thumb-bg": "--a-surface-neutral",
"--ac-switch-thumb-icon": "--a-icon-subtle",
"--ac-switch-thumb-icon-checked": "--a-icon-action-selected"
},
+12
View File
@@ -216,6 +216,18 @@
color: var(--ac-typo-error-text, var(--__ac-typo-error-text, var(--a-text-danger)));
}
.navds-error-message.navds-error-message--show-icon {
display: flex;
gap: var(--a-spacing-1);
}
.navds-error-message.navds-error-message--show-icon svg {
margin-top: 0.15em;
flex: 0 0 auto;
width: 1em;
height: 1em;
}
.navds-typo--truncate {
overflow: hidden;
white-space: nowrap;
+1
View File
@@ -0,0 +1 @@
scripts/*.backup
+27
View File
@@ -1,5 +1,32 @@
# @navikt/ds-react
## 7.9.1
### Patch Changes
- Updated dependencies []:
- @navikt/ds-tokens@7.9.1
- @navikt/aksel-icons@7.9.1
## 7.9.0
### Minor Changes
- ErrorSummary: :sparkles: new prop to prefix error with a warning icon. ([#3445](https://github.com/navikt/aksel/pull/3445))
All form components: replace dot with warning icon in error message.
### Patch Changes
- Button: Fix edge-case where setting "loading={true}" in a Modal caused the button to get 0 width and not show spinner ([#3252](https://github.com/navikt/aksel/pull/3252))
- Switch: Update switch to better reflect unchecked state. ([#3468](https://github.com/navikt/aksel/pull/3468))
- Textarea: resize immediately upon receiving resize event (eg. inside modal opening). ([#3463](https://github.com/navikt/aksel/pull/3463))
- Updated dependencies []:
- @navikt/ds-tokens@7.9.0
- @navikt/aksel-icons@7.9.0
## 7.8.1
### Patch Changes
+2 -2
View File
@@ -1,9 +1,9 @@
# Aksel React-components
React-components for NAVs designsystem.
React-components for Nav's design system.
- Uses styling defined in `@navikt/ds-css`
- All components implemented with React.forwardRef!
- All components are implemented with React.forwardRef!
- Most components extend native html-elements.
## Installation
+17 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "7.8.1",
"version": "7.9.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",
@@ -603,10 +603,11 @@
"./package.json": "./package.json"
},
"scripts": {
"docgen": "yarn tsx ../../../scripts/docgen.ts",
"docgen": "tsx ../../../scripts/docgen.ts",
"i18n-jsdoc": "tsx ./scripts/addJsdocToLocales.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\" ",
"build": "yarn i18n-jsdoc && concurrently \"tsc -p tsconfig.build.json\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" && yarn i18n-jsdoc --cleanup",
"watch": "tsc --watch -p tsconfig.esm.json",
"test": "TZ=UTC vitest run -c tests/vitest.config.ts",
"test:watch": "vitest watch"
@@ -614,20 +615,23 @@
"dependencies": {
"@floating-ui/react": "0.25.4",
"@floating-ui/react-dom": "^2.0.9",
"@navikt/aksel-icons": "^7.8.1",
"@navikt/ds-tokens": "^7.8.1",
"@navikt/aksel-icons": "^7.9.1",
"@navikt/ds-tokens": "^7.9.1",
"clsx": "^2.1.0",
"date-fns": "^3.0.0",
"react-day-picker": "8.10.1"
},
"devDependencies": {
"@testing-library/dom": "9.3.4",
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.2.0",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/jscodeshift": "^0.11.11",
"aksel": "workspace:^",
"concurrently": "9.0.1",
"fast-glob": "3.2.11",
"jsdom": "24.0.0",
"jscodeshift": "^0.15.1",
"jsdom": "25.0.1",
"react-dom": "^18.0.0",
"react-router-dom": "^6.3.0",
"rimraf": "6.0.1",
@@ -635,7 +639,7 @@
"tsc-alias": "1.8.8",
"tsx": "^4.19.1",
"typescript": "5.5.4",
"vitest": "^1.2.2"
"vitest": "^2.1.8"
},
"peerDependencies": {
"@types/react": ">=17.0.30",
@@ -645,5 +649,6 @@
"@types/react": {
"optional": true
}
}
},
"_comment": "The 'aksel' dependency tells yarn to install the root deps when installing deps for this package. Helps make it possible to contribute without access to GPR."
}
@@ -0,0 +1,59 @@
import fs from "fs";
import jscodeshift from "jscodeshift";
const j = jscodeshift.withParser("ts");
const file = "src/util/i18n/locales/nb.ts";
const backup = "scripts/nb.backup";
function addJsdoc() {
if (fs.existsSync(backup)) {
// If last build failed, we need to restore the file so that we don't create duplicate JSDoc
console.info(
`WARNING: Restoring backup of ${file} - Uncommitted changes will be lost!`,
);
fs.copyFileSync(backup, file);
}
fs.copyFileSync(file, backup); // Create a copy of the original file that we can restore afterwards
const code = fs.readFileSync(file, "utf-8");
const parsedCode = j(code);
const defaultExport = parsedCode.find(j.ExportDefaultDeclaration);
const keys = defaultExport.find(j.ObjectProperty);
keys
.filter((key) => key.value.value.type === "StringLiteral")
.forEach((key) => {
// @ts-expect-error It works, doesn't it?
const value = key.value.value.value;
const foundCommentBlock = key.value.comments?.find(
(comment) => comment.type === "CommentBlock",
);
if (foundCommentBlock) {
foundCommentBlock.value = `${foundCommentBlock.value}\n* @default "${value}" `;
} else {
key.value.comments = [
...(key.value.comments || []),
{
type: "CommentBlock",
value: `* @default "${value}" `,
leading: true,
trailing: false,
},
];
}
});
const modifiedCode = parsedCode.toSource();
fs.writeFileSync(file, modifiedCode);
}
function cleanup() {
fs.copyFileSync(backup, file);
fs.unlinkSync(backup);
}
if (process.argv.includes("--cleanup")) {
cleanup();
} else {
addJsdoc();
}
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { BodyLong } from "../typography";
import { AccordionItemContext } from "./AccordionItem";
@@ -16,7 +16,7 @@ const AccordionContent = forwardRef<HTMLDivElement, AccordionContentProps>(
({ children, className, ...rest }, ref) => {
const context = useContext(AccordionItemContext);
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
if (context === null) {
console.error(
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { ChevronDownIcon } from "@navikt/aksel-icons";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { Heading } from "../typography";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { AccordionContext } from "./AccordionContext";
@@ -20,7 +20,7 @@ const AccordionHeader = forwardRef<HTMLButtonElement, AccordionHeaderProps>(
const itemContext = useContext(AccordionItemContext);
const accordionContext = useContext(AccordionContext);
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
if (itemContext === null) {
console.error(
+1 -1
View File
@@ -9,7 +9,7 @@ import {
} from "@navikt/aksel-icons";
import { Button } from "../button";
import { BodyLong } from "../typography";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
/**
+20 -50
View File
@@ -1,11 +1,9 @@
import cl from "clsx";
import React, { forwardRef, useRef, useState } from "react";
import React, { forwardRef } from "react";
import { Loader } from "../loader";
import { Label } from "../typography";
import { omit } from "../util";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useClientLayoutEffect } from "../util/hooks";
import { useMergeRefs } from "../util/hooks/useMergeRefs";
import { OverridableComponent } from "../util/types";
export interface ButtonProps
@@ -74,37 +72,18 @@ export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
size = "medium",
loading = false,
disabled,
style,
icon,
iconPosition = "left",
onKeyUp,
...rest
},
ref,
) => {
const buttonRef = useRef<HTMLButtonElement | null>(null);
const [widthOverride, setWidthOverride] = useState<number>();
const mergedRef = useMergeRefs(buttonRef, ref);
useClientLayoutEffect(() => {
if (loading) {
const requestID = window.requestAnimationFrame(() => {
setWidthOverride(
buttonRef?.current?.getBoundingClientRect()?.width,
);
});
return () => {
setWidthOverride(undefined);
cancelAnimationFrame(requestID);
};
}
}, [loading, children]);
const filterProps: React.ButtonHTMLAttributes<HTMLButtonElement> =
disabled ?? widthOverride ? omit(rest, ["href"]) : rest;
disabled || loading ? omit(rest, ["href"]) : rest;
const handleKeyUp = (e: React.KeyboardEvent<HTMLButtonElement>) => {
if (e.key === " " && !disabled && !widthOverride) {
if (e.key === " " && !disabled && !loading) {
e.currentTarget.click();
}
};
@@ -113,41 +92,32 @@ export const Button: OverridableComponent<ButtonProps, HTMLButtonElement> =
<Component
{...(Component !== "button" ? { role: "button" } : {})}
{...filterProps}
ref={mergedRef}
onKeyUp={composeEventHandlers(filterProps.onKeyUp, handleKeyUp)}
ref={ref}
onKeyUp={composeEventHandlers(onKeyUp, handleKeyUp)}
className={cl(
className,
"navds-button",
`navds-button--${variant}`,
`navds-button--${size}`,
{
"navds-button--loading": widthOverride,
"navds-button--loading": loading,
"navds-button--icon-only": !!icon && !children,
"navds-button--disabled": disabled ?? widthOverride,
"navds-button--disabled": disabled ?? loading,
},
)}
style={{
...style,
width: widthOverride,
}}
disabled={disabled ?? widthOverride ? true : undefined}
disabled={disabled ?? loading ? true : undefined}
>
{widthOverride ? (
<Loader size={size} />
) : (
<>
{icon && iconPosition === "left" && (
<span className="navds-button__icon">{icon}</span>
)}
{children && (
<Label as="span" size={size === "medium" ? "medium" : "small"}>
{children}
</Label>
)}
{icon && iconPosition === "right" && (
<span className="navds-button__icon">{icon}</span>
)}
</>
{icon && iconPosition === "left" && (
<span className="navds-button__icon">{icon}</span>
)}
{loading && <Loader size={size} />}
{children && (
<Label as="span" size={size === "medium" ? "medium" : "small"}>
{children}
</Label>
)}
{icon && iconPosition === "right" && (
<span className="navds-button__icon">{icon}</span>
)}
</Component>
);
@@ -2,6 +2,8 @@ import { StoryObj } from "@storybook/react";
import React from "react";
import { StarIcon as BaseStarIcon } from "@navikt/aksel-icons";
import { HStack, VStack } from "../layout/stack";
import { Modal } from "../modal";
import { BodyLong } from "../typography";
import { Button } from "./index";
export default {
@@ -154,6 +156,51 @@ export const DisabledAsLink: Story = {
render: () => <ButtonGrid disabled href="#" as="a" />,
};
export const InsideModal: Story = {
render: () => {
const ref = React.useRef<HTMLDialogElement>(null);
return (
<div className="py-16">
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
<Modal ref={ref} header={{ heading: "Overskrift" }}>
<Modal.Body>
<BodyLong>
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt
duis laborum. Proident aute culpa qui nostrud velit adipisicing
minim. Consequat aliqua aute dolor do sit Lorem nisi mollit velit.
Aliqua exercitation non minim minim pariatur sunt laborum ipsum.
Exercitation nostrud est laborum magna non non aliqua qui esse.
</BodyLong>
</Modal.Body>
<Modal.Footer>
<Button loading type="button" onClick={() => ref.current?.close()}>
Primær
</Button>
<Button
loading
type="button"
variant="secondary"
onClick={() => ref.current?.close()}
>
Sekundær
</Button>
<Button
type="button"
variant="tertiary"
onClick={() => ref.current?.close()}
>
Tertiær
</Button>
</Modal.Footer>
</Modal>
</div>
);
},
};
export const Chromatic: Story = {
render: () => (
<VStack gap="6" align="center">
+1 -1
View File
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { forwardRef } from "react";
import { XMarkIcon } from "@navikt/aksel-icons";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export interface ChipsRemovableProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
@@ -10,7 +10,7 @@ import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons";
import { Label } from "../typography";
import { composeEventHandlers } from "../util/composeEventHandlers";
import copy from "../util/copy";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export interface CopyButtonProps
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
@@ -1,5 +1,5 @@
import { createContext } from "../../util/create-context";
import { TFunction } from "../../util/i18n/i18n.context";
import { TFunction } from "../../util/i18n/i18n.types";
interface DateTranslationContextProps {
translate: TFunction<"DatePicker">;
@@ -5,7 +5,7 @@ import { DateRange, DayPicker, isMatch } from "react-day-picker";
import { omit } from "../../util";
import { useId } from "../../util/hooks";
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
import { DateInputContext, DateTranslationContextProvider } from "../context";
import { DatePickerInput } from "../parts/DateInput";
import { DateWrapper } from "../parts/DateWrapper";
@@ -3,7 +3,7 @@ import { isWeekend } from "date-fns";
import React, { forwardRef } from "react";
import { DateRange, DayPicker, isMatch } from "react-day-picker";
import { omit } from "../../util";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
import { DateTranslationContextProvider } from "../context";
import { getLocaleFromString, getTranslations } from "../utils";
import Caption from "./parts/Caption";
@@ -1,4 +1,4 @@
import { act, render, screen } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import { describe, test } from "vitest";
@@ -23,9 +23,6 @@ describe("Render datepicker", () => {
test("Should not crash when e.target is window", async () => {
render(<App />);
// eslint-disable-next-line testing-library/no-unnecessary-act -- https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning
await act(async () => {
await userEvent.click(screen.getByText("Velg dato"));
});
await userEvent.click(screen.getByText("Velg dato"));
});
});
@@ -2,7 +2,7 @@
import React from "react";
import { Button as RDPButton, useDayPicker } from "react-day-picker";
import { Button } from "../../../button";
import { UNSAFE_useAkselTheme } from "../../../provider";
import { useThemeInternal } from "../../../theme/Theme";
import { Detail } from "../../../typography";
import { useDateTranslationContext } from "../../context";
@@ -21,7 +21,7 @@ function WeekNumber({
dates,
}: WeekNumberProps): JSX.Element {
const { onWeekNumberClick, styles, classNames } = useDayPicker();
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const translate = useDateTranslationContext().translate;
if (!onWeekNumberClick) {
@@ -1,7 +1,7 @@
import { differenceInCalendarDays, isWeekend } from "date-fns";
import React, { useCallback, useState } from "react";
import { DayClickEventHandler, isMatch } from "react-day-picker";
import { useDateLocale } from "../../util/i18n/i18n.context";
import { useDateLocale } from "../../util/i18n/i18n.hooks";
import { DatePickerProps } from "../datepicker/DatePicker";
import { DateInputProps } from "../parts/DateInput";
import {
@@ -1,5 +1,5 @@
import React, { useCallback, useMemo, useState } from "react";
import { useDateLocale } from "../../util/i18n/i18n.context";
import { useDateLocale } from "../../util/i18n/i18n.hooks";
import { MonthPickerProps } from "../monthpicker/types";
import { DateInputProps } from "../parts/DateInput";
import {
@@ -1,5 +1,4 @@
/* eslint-disable testing-library/no-unnecessary-act -- https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning */
import { act, render, screen } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import { describe, expect, test } from "vitest";
@@ -29,10 +28,8 @@ describe("Writing in input sets correct values", () => {
const fraInput = screen.getByRole("textbox", { name: "Fra" });
const tilInput = screen.getByRole("textbox", { name: "Til" });
await act(async () => {
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "03.08.2022");
});
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "03.08.2022");
const res = screen.getByTitle("res");
expect(res.innerHTML).toEqual(
JSON.stringify({
@@ -47,10 +44,8 @@ describe("Writing in input sets correct values", () => {
const fraInput = screen.getByRole("textbox", { name: "Fra" });
const tilInput = screen.getByRole("textbox", { name: "Til" });
await act(async () => {
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "02.08.2022");
});
await userEvent.type(fraInput, "03.08.2022");
await userEvent.type(tilInput, "02.08.2022");
const res = screen.getByTitle("res");
expect(res.innerHTML).toEqual(
JSON.stringify({
@@ -5,7 +5,7 @@ import {
} from "date-fns";
import React, { useState } from "react";
import { DateRange, isMatch } from "react-day-picker";
import { useDateLocale } from "../../util/i18n/i18n.context";
import { useDateLocale } from "../../util/i18n/i18n.hooks";
import { DatePickerProps } from "../datepicker/DatePicker";
import { DateInputProps } from "../parts/DateInput";
import {
@@ -3,7 +3,7 @@ import React, { forwardRef, useState } from "react";
import { DayPickerProvider } from "react-day-picker";
import { useId } from "../../util/hooks";
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
import {
DateInputContext,
DateTranslationContextProvider,
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef, useState } from "react";
import { DayPickerProvider } from "react-day-picker";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
import {
DateTranslationContextProvider,
SharedMonthProvider,
@@ -150,7 +150,11 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
aria-relevant="additions removals"
aria-live="polite"
>
{showErrorMsg && <ErrorMessage size={size}>{props.error}</ErrorMessage>}
{showErrorMsg && (
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
);
@@ -5,7 +5,8 @@ import { Modal } from "../../modal";
import { useModalContext } from "../../modal/Modal.context";
import { Popover } from "../../popover";
import { useMedia } from "../../util/hooks";
import { TFunction, useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import { TFunction } from "../../util/i18n/i18n.types";
import { getGlobalTranslations } from "../utils";
const variantToLabel = {
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { ChevronDownIcon } from "@navikt/aksel-icons";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
import { ExpansionCardContext } from "./context";
export interface ExpansionCardHeaderProps
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react";
import { PadlockLockedFillIcon } from "@navikt/aksel-icons";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export const ReadOnlyIcon = () => (
<PadlockLockedFillIcon
@@ -71,7 +71,11 @@ export const Combobox = forwardRef<
aria-relevant="additions removals"
aria-live="polite"
>
{showErrorMsg && <ErrorMessage size={size}>{error}</ErrorMessage>}
{showErrorMsg && (
<ErrorMessage size={size} showIcon>
{error}
</ErrorMessage>
)}
</div>
</ComboboxWrapper>
);
@@ -2,7 +2,7 @@ import cl from "clsx";
import React from "react";
import { PlusIcon } from "@navikt/aksel-icons";
import { BodyShort, Label } from "../../../typography";
import { useI18n } from "../../../util/i18n/i18n.context";
import { useI18n } from "../../../util/i18n/i18n.hooks";
import { useInputContext } from "../Input/Input.context";
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
import { isInList, toComboboxOption } from "../combobox-utils";
@@ -1,6 +1,6 @@
import React from "react";
import { Loader } from "../../../loader";
import { useI18n } from "../../../util/i18n/i18n.context";
import { useI18n } from "../../../util/i18n/i18n.hooks";
import { useInputContext } from "../Input/Input.context";
import filteredOptionsUtil from "./filtered-options-util";
@@ -1,5 +1,5 @@
import React from "react";
import { useI18n } from "../../../util/i18n/i18n.context";
import { useI18n } from "../../../util/i18n/i18n.hooks";
import { useInputContext } from "../Input/Input.context";
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
import filteredOptionsUtil from "./filtered-options-util";
@@ -91,7 +91,11 @@ export const ConfirmationPanel = forwardRef<
</Checkbox>
</div>
<div className="navds-form-field__error" id={errorId} role="alert">
{showErrorMsg && <ErrorMessage size={size}>{props.error}</ErrorMessage>}
{showErrorMsg && (
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
);
@@ -3,7 +3,7 @@ import React, { HTMLAttributes, forwardRef, useRef } from "react";
import { BodyShort, Heading } from "../../typography";
import { composeEventHandlers } from "../../util/composeEventHandlers";
import { useMergeRefs } from "../../util/hooks";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import ErrorSummaryItem from "./ErrorSummaryItem";
export interface ErrorSummaryProps
@@ -113,7 +113,9 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
className="navds-fieldset__error"
>
{showErrorMsg && (
<ErrorMessage size={size}>{props.error}</ErrorMessage>
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</fieldset>
@@ -5,7 +5,7 @@ import { Button } from "../../../../button";
import { BodyShort, ErrorMessage, Label } from "../../../../typography";
import { composeEventHandlers } from "../../../../util/composeEventHandlers";
import { useId } from "../../../../util/hooks";
import { useI18n } from "../../../../util/i18n/i18n.context";
import { useI18n } from "../../../../util/i18n/i18n.hooks";
import { omit } from "../../../../util/omit";
import { useFormField } from "../../../useFormField";
import { useFileUploadTranslation } from "../../FileUpload.context";
@@ -177,7 +177,7 @@ const Dropzone = forwardRef<HTMLInputElement, FileUploadDropzoneProps>(
aria-relevant="additions removals"
aria-live="polite"
>
{showErrorMsg && <ErrorMessage>{error}</ErrorMessage>}
{showErrorMsg && <ErrorMessage showIcon>{error}</ErrorMessage>}
</div>
</div>
);
@@ -3,7 +3,7 @@ import React, { MouseEvent, forwardRef } from "react";
import { ExclamationmarkTriangleIcon } from "@navikt/aksel-icons";
import { BodyShort } from "../../../../typography";
import { OverridableComponent } from "../../../../util";
import { useI18n } from "../../../../util/i18n/i18n.context";
import { useI18n } from "../../../../util/i18n/i18n.hooks";
import { ComponentTranslation } from "../../../../util/i18n/i18n.types";
import { useFileUploadTranslation } from "../../FileUpload.context";
import { FileItem } from "./Item.types";
@@ -6,7 +6,7 @@ import { HStack } from "../../layout/stack";
import { ProgressBar } from "../../progress-bar";
import { Stepper, StepperStepProps } from "../../stepper";
import { BodyShort } from "../../typography";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import { ComponentTranslation } from "../../util/i18n/i18n.types";
export interface FormProgressProps
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { Link } from "../../link";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import { OverridableComponent } from "../../util/types";
export interface FormSummaryEditProps
@@ -7,11 +7,11 @@ import React, {
} from "react";
import { MagnifyingGlassIcon, XMarkIcon } from "@navikt/aksel-icons";
import { Button } from "../../button";
import { UNSAFE_useAkselTheme } from "../../provider";
import { useThemeInternal } from "../../theme/Theme";
import { BodyShort, ErrorMessage, Label } from "../../typography";
import { omit } from "../../util";
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import { FormFieldProps, useFormField } from "../useFormField";
import SearchButton, { SearchButtonType } from "./SearchButton";
import { SearchContext } from "./context";
@@ -125,7 +125,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
...rest
} = props;
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const searchRef = useRef<HTMLInputElement | null>(null);
const mergedRef = useMergeRefs(searchRef, ref);
@@ -263,7 +263,9 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
aria-live="polite"
>
{showErrorMsg && (
<ErrorMessage size={size}>{props.error}</ErrorMessage>
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
@@ -3,7 +3,7 @@ import React, { forwardRef, useContext } from "react";
import { MagnifyingGlassIcon } from "@navikt/aksel-icons";
import { Button, ButtonProps } from "../../button";
import { composeEventHandlers } from "../../util/composeEventHandlers";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import { SearchContext } from "./context";
export interface SearchButtonProps
@@ -151,7 +151,9 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
aria-live="polite"
>
{showErrorMsg && (
<ErrorMessage size={size}>{props.error}</ErrorMessage>
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
+1 -10
View File
@@ -6,7 +6,6 @@ import React, {
useState,
} from "react";
import { Loader } from "../../loader";
import { UNSAFE_useAkselTheme } from "../../provider";
import { BodyShort } from "../../typography";
import { omit } from "../../util";
import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
@@ -69,8 +68,6 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
defaultChecked ?? checkedProp ?? false,
);
const themeContext = UNSAFE_useAkselTheme(false);
useEffect(() => {
checkedProp !== undefined && setChecked(checkedProp);
}, [checkedProp]);
@@ -121,13 +118,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
<Loader
size="xsmall"
aria-live="polite"
variant={
checked
? "interaction"
: themeContext
? "inverted"
: "neutral"
}
variant={checked ? "interaction" : "inverted"}
/>
) : (
<svg
@@ -193,7 +193,9 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
aria-live="polite"
>
{showErrorMsg && (
<ErrorMessage size={size}>{props.error}</ErrorMessage>
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { useEffect, useState } from "react";
import { BodyShort } from "../../typography";
import debounce from "../../util/debounce";
import { useI18n } from "../../util/i18n/i18n.context";
import { useI18n } from "../../util/i18n/i18n.hooks";
import type { TextareaProps } from "./Textarea";
interface Props {
@@ -2,6 +2,7 @@ import { Meta, StoryFn, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import React, { useState } from "react";
import { Button } from "../../button";
import { VStack } from "../../layout/stack";
import { Modal } from "../../modal";
import { Textarea } from "./index";
@@ -74,6 +75,24 @@ export const WithError: StoryFn = () => {
size="small"
/>
<VStack
style={{
maxWidth: "400px",
}}
gap="4"
>
<Textarea
label="Ipsum enim quis culpa"
error="Consectetur labore velit eiusmod Lorem ut nostrud mollit labore ullamco laboris laboris in."
/>
<Textarea
label="Ipsum enim quis culpa"
error="Consectetur labore velit eiusmod Lorem ut nostrud mollit labore ullamco laboris laboris in."
size="small"
/>
</VStack>
<Textarea
label="Ipsum enim quis culpa"
error="Consectetur labore velit eiusmod Lorem ut nostrud mollit labore ullamco laboris laboris in."
@@ -207,6 +226,27 @@ AutoScrollbar.argTypes = {
minRows: { type: "number" },
};
export const InsideModal: StoryFn<typeof Textarea> = () => {
const ref = React.useRef<HTMLDialogElement>(null);
return (
<>
<Button onClick={() => ref.current?.showModal()}>Open modal</Button>
<React.StrictMode>
<Modal
ref={ref}
header={{ heading: "Skjema" }}
aria-label="Modal med textarea"
>
<Modal.Body>
<Textarea label="Har du noen tilbakemeldinger?" />
</Modal.Body>
</Modal>
</React.StrictMode>
</>
);
};
export const ModalStrictMode: StoryFn<typeof Textarea> = () => {
// Story added after fixing an issue where TextareaAutoSize would reach max re-renders
// and set the height to 2px when used in StrictMode in a Modal that is initially open.
@@ -127,7 +127,9 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
aria-live="polite"
>
{showErrorMsg && (
<ErrorMessage size={size}>{props.error}</ErrorMessage>
<ErrorMessage size={size} showIcon>
{props.error}
</ErrorMessage>
)}
</div>
</div>
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { HTMLAttributes, forwardRef } from "react";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { DefaultIllustration } from "./Illustration";
import { DarksideGudiepanelIllustration } from "./Illustration.darkside";
@@ -37,7 +37,7 @@ export interface GuidePanelProps extends HTMLAttributes<HTMLDivElement> {
*/
export const GuidePanel = forwardRef<HTMLDivElement, GuidePanelProps>(
({ children, className, illustration, poster, ...rest }, ref) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
return (
<div
@@ -1,5 +1,5 @@
import React from "react";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export const DarksideGudiepanelIllustration = () => {
const translate = useI18n("GuidePanel");
@@ -1,5 +1,5 @@
import React from "react";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export const DefaultIllustration = () => {
const translate = useI18n("GuidePanel");
@@ -1,10 +1,10 @@
import cl from "clsx";
import React, { forwardRef, useRef, useState } from "react";
import { Popover, PopoverProps } from "../popover";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useMergeRefs } from "../util/hooks/useMergeRefs";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
import { HelpTextIcon } from "./HelpTextIcon";
export interface HelpTextProps
@@ -52,7 +52,7 @@ export const HelpText = forwardRef<HTMLButtonElement, HelpTextProps>(
const buttonRef = useRef<HTMLButtonElement | null>(null);
const mergedRef = useMergeRefs(buttonRef, ref);
const [open, setOpen] = useState(false);
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const translate = useI18n("HelpText");
const titleWithFallback = title || translate("title");
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { HTMLAttributes, forwardRef } from "react";
import { UNSAFE_AkselTheme, UNSAFE_useAkselTheme } from "../provider";
import { Theme, useThemeInternal } from "../theme/Theme";
import { OverridableComponent } from "../util/types";
import InternalHeaderButton, {
InternalHeaderButtonProps,
@@ -78,20 +78,20 @@ interface InternalHeaderComponent
* ```
*/
export const InternalHeader = forwardRef(({ className, ...rest }, ref) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
/*
* Component is always in "dark" mode, so we manually override global theme.
*/
if (themeContext) {
return (
<UNSAFE_AkselTheme theme="dark" asChild>
<Theme theme="dark" asChild>
<header
{...rest}
ref={ref}
className={cl("navds-internalheader", className)}
/>
</UNSAFE_AkselTheme>
</Theme>
);
}
@@ -1,7 +1,7 @@
import cl from "clsx";
import React from "react";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { getResponsiveProps, getResponsiveValue } from "../utilities/css";
import { ResponsiveProp, SpacingScale } from "../utilities/types";
@@ -252,7 +252,7 @@ export const BasePrimitive = ({
flexShrink,
gridColumn,
}: BasePrimitiveProps) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const prefix = themeContext ? "ax" : "a";
const style: React.CSSProperties = {
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { getResponsiveProps } from "../utilities/css";
import { ResponsiveProp, SpacingScale } from "../utilities/types";
@@ -81,7 +81,7 @@ export const Bleed = forwardRef<HTMLDivElement, BleedProps>(
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const prefix = themeContext ? "ax" : "a";
let style: React.CSSProperties = {
+2 -2
View File
@@ -1,8 +1,8 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { type BorderRadiusKeys } from "@navikt/ds-tokens/types";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { omit } from "../../util";
import { OverridableComponent } from "../../util/types";
import BasePrimitive, {
@@ -109,7 +109,7 @@ export const BoxComponent: OverridableComponent<BoxProps, HTMLDivElement> =
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
if (
process.env.NODE_ENV !== "production" &&
+2 -2
View File
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { OverridableComponent, omit } from "../../util";
import BasePrimitive, {
PRIMITIVE_PROPS,
@@ -78,7 +78,7 @@ export const HGrid: OverridableComponent<HGridProps, HTMLDivElement> =
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const prefix = themeContext ? "ax" : "a";
const styles: React.CSSProperties = {
+2 -2
View File
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { UNSAFE_useAkselTheme } from "../../provider";
import { useThemeInternal } from "../../theme/Theme";
import { OverridableComponent } from "../../util";
import { BackgroundColorToken } from "../utilities/types";
import { PageBlock } from "./parts/PageBlock";
@@ -54,7 +54,7 @@ export const PageComponent: OverridableComponent<PageProps, HTMLElement> =
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
if (process.env.NODE_ENV !== "production" && themeContext && background) {
console.warn(
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { HTMLAttributes, forwardRef } from "react";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { omit } from "../../util";
import { OverridableComponent } from "../../util/types";
import BasePrimitive, {
@@ -84,7 +84,7 @@ export const Stack: OverridableComponent<StackProps, HTMLDivElement> =
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const prefix = themeContext ? "ax" : "a";
const style: React.CSSProperties = {
+1 -1
View File
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { SVGProps, forwardRef } from "react";
import { omit } from "../util";
import { useId } from "../util/hooks";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
export interface LoaderProps extends Omit<SVGProps<SVGSVGElement>, "ref"> {
/**
+1 -1
View File
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { forwardRef } from "react";
import { XMarkIcon } from "@navikt/aksel-icons";
import { Button } from "../button";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
import { useModalContext } from "./Modal.context";
export interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -2,8 +2,8 @@ import cl from "clsx";
import React, { forwardRef, useRef } from "react";
import { ChevronRightIcon } from "@navikt/aksel-icons";
import { useModalContext } from "../../modal/Modal.context";
import { UNSAFE_useAkselTheme } from "../../provider";
import { Slot } from "../../slot/Slot";
import { useThemeInternal } from "../../theme/Theme";
import { OverridableComponent, useId } from "../../util";
import { composeEventHandlers } from "../../util/composeEventHandlers";
import { createContext } from "../../util/create-context";
@@ -736,7 +736,7 @@ export const ActionMenuRadioItem = forwardRef<
{ children, className, onSelect, ...rest }: ActionMenuRadioItemProps,
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
return (
<Menu.RadioItem
@@ -3,7 +3,7 @@ import React, { forwardRef } from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "@navikt/aksel-icons";
import { BodyShort, Heading } from "../typography";
import { useId } from "../util";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
import PaginationItem, {
PaginationItemProps,
PaginationItemType,
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { Button, ButtonProps } from "../button";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { OverridableComponent } from "../util/types";
export interface PaginationItemProps extends ButtonProps {
@@ -40,7 +40,7 @@ export const Item: PaginationItemType = forwardRef(
},
ref,
) => {
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
return (
<Button
+2 -2
View File
@@ -11,7 +11,7 @@ import React, { HTMLAttributes, forwardRef, useContext, useRef } from "react";
import { DateInputContext } from "../date/context";
import { useModalContext } from "../modal/Modal.context";
import { DismissableLayer } from "../overlays/dismissablelayer/DismissableLayer";
import { UNSAFE_useAkselTheme } from "../provider";
import { useThemeInternal } from "../theme/Theme";
import { useClientLayoutEffect } from "../util/hooks";
import { useMergeRefs } from "../util/hooks/useMergeRefs";
import PopoverContent, { PopoverContentType } from "./PopoverContent";
@@ -125,7 +125,7 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
const chosenStrategy = userStrategy ?? (isInModal ? "fixed" : "absolute");
const chosenFlip = isInDatepicker ? false : _flip;
const themeContext = UNSAFE_useAkselTheme(false);
const themeContext = useThemeInternal(false);
const {
update,
+17
View File
@@ -2,6 +2,7 @@ import React, { HTMLAttributes, forwardRef } from "react";
import ReactDOM from "react-dom";
import { useProvider } from "../provider/Provider";
import { Slot } from "../slot/Slot";
import { Theme, useThemeInternal } from "../theme/Theme";
import { AsChildProps } from "../util/types";
interface PortalBaseProps extends HTMLAttributes<HTMLDivElement> {
@@ -15,11 +16,27 @@ export type PortalProps = PortalBaseProps & AsChildProps;
export const Portal = forwardRef<HTMLDivElement, PortalProps>(
({ rootElement, asChild, ...rest }, ref) => {
const themeContext = useThemeInternal(false);
const contextRoot = useProvider()?.rootElement;
const root = rootElement ?? contextRoot ?? globalThis?.document?.body;
const Component = asChild ? Slot : "div";
/**
* Portal can be mounted outside of theme-classNames.
* If a theme is present, we want to make sure that theme cascades to portaled element.
*/
if (themeContext) {
return root
? ReactDOM.createPortal(
<Theme theme={themeContext.theme} asChild hasBackground={false}>
<Component ref={ref} data-aksel-portal="" {...rest} />
</Theme>,
root,
)
: null;
}
return root
? ReactDOM.createPortal(
<Component ref={ref} data-aksel-portal="" {...rest} />,
@@ -1,6 +1,6 @@
import cl from "clsx";
import React, { HTMLAttributes, forwardRef, useEffect, useRef } from "react";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
interface ProgressBarPropsBase
extends Omit<HTMLAttributes<HTMLDivElement>, "role"> {
-4
View File
@@ -1,6 +1,2 @@
"use client";
export { default as Provider, type ProviderProps } from "./Provider";
export {
AkselTheme as UNSAFE_AkselTheme,
useAkselTheme as UNSAFE_useAkselTheme,
} from "./theme/AkselTheme";
@@ -1,70 +0,0 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { Slot } from "../../slot/Slot";
import { createContext } from "../../util/create-context";
type AkselThemeContext = {
/**
* Color theme
* @default "light"
*/
theme?: "light" | "dark";
/**
* Brand volume
* @default "low"
* This is experimental and subject to changes
*/
volume?: "high" | "low";
};
const [ThemeProvider, useAkselTheme] = createContext<AkselThemeContext>({
errorMessage: "useAkselTheme must be used within AkselThemeProvider",
hookName: "useAkselTheme",
name: "AkselThemeProvider",
providerName: "AkselThemeProvider",
});
type AkselThemeProps = {
children: React.ReactNode;
className?: string;
hasBackground?: boolean;
/* TODO: Handle this correctly with types */
asChild?: boolean;
} & AkselThemeContext;
const AkselTheme = forwardRef<HTMLDivElement, AkselThemeProps>(
(props: AkselThemeProps, ref) => {
const context = useAkselTheme(false);
const {
children,
className,
asChild = false,
theme = context?.theme ?? "light",
volume = context?.volume ?? "low",
hasBackground: hasBackgroundProp = true,
} = props;
const isRoot = context === undefined;
const hasBackground =
hasBackgroundProp ?? (isRoot || props.theme !== undefined);
const SlotElement = asChild ? Slot : "div";
return (
<ThemeProvider theme={theme} volume={volume}>
<SlotElement
ref={ref}
className={cl("navds-theme", className, theme)}
data-background={hasBackground}
data-volume={volume}
>
{children}
</SlotElement>
</ThemeProvider>
);
},
);
export { AkselTheme, useAkselTheme };
@@ -1,7 +1,6 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { ChevronDownIcon } from "@navikt/aksel-icons";
import { UNSAFE_useAkselTheme } from "../provider";
import { BodyLong } from "../typography";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useControllableState } from "../util/hooks/useControllableState";
@@ -75,8 +74,6 @@ export const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
onChange: onOpenChange,
});
const themeContext = UNSAFE_useAkselTheme(false);
const typoSize = size === "small" ? "small" : "medium";
return (
@@ -87,7 +84,7 @@ export const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
className,
{ "navds-read-more--open": _open },
)}
data-volume={themeContext?.volume}
data-volume="low"
>
<button
{...rest}
@@ -3,7 +3,6 @@ import { fn } from "@storybook/test";
import React from "react";
import { ReadMore } from ".";
import { VStack } from "../layout/stack";
import { UNSAFE_AkselTheme } from "../provider";
export default {
title: "ds-react/ReadMore",
@@ -68,17 +67,15 @@ export const Open: Story = {
export const BrandVolumeLow: Story = {
render: () => (
<UNSAFE_AkselTheme volume="low">
<VStack gap="4">
<ReadMore size="large" header={Default.args?.header}>
{Content}
</ReadMore>
<ReadMore header={Default.args?.header}>{Content}</ReadMore>
<ReadMore size="small" header={Default.args?.header}>
{Content}
</ReadMore>
</VStack>
</UNSAFE_AkselTheme>
<VStack gap="4">
<ReadMore size="large" header={Default.args?.header}>
{Content}
</ReadMore>
<ReadMore header={Default.args?.header}>{Content}</ReadMore>
<ReadMore size="small" header={Default.args?.header}>
{Content}
</ReadMore>
</VStack>
),
args: {
...Default.args,
@@ -86,22 +83,6 @@ export const BrandVolumeLow: Story = {
},
};
export const BrandVolumeHigh: Story = {
render: () => (
<UNSAFE_AkselTheme volume="high">
<VStack gap="4">
<ReadMore size="large" header={Default.args?.header}>
{Content}
</ReadMore>
<ReadMore header={Default.args?.header}>{Content}</ReadMore>
<ReadMore size="small" header={Default.args?.header}>
{Content}
</ReadMore>
</VStack>
</UNSAFE_AkselTheme>
),
};
export const Chromatic: Story = {
render: (...props) => {
return (
@@ -135,10 +116,6 @@ export const Chromatic: Story = {
<h2>BrandVolumeLow</h2>
{BrandVolumeLow?.render?.(...props)}
</div>
<div>
<h2>BrandVolumeHigh</h2>
{BrandVolumeHigh?.render?.(...props)}
</div>
</VStack>
);
},
@@ -4,7 +4,7 @@ import { ChevronDownIcon } from "@navikt/aksel-icons";
import { composeEventHandlers } from "../util/composeEventHandlers";
import { useId } from "../util/hooks";
import { useControllableState } from "../util/hooks/useControllableState";
import { useI18n } from "../util/i18n/i18n.context";
import { useI18n } from "../util/i18n/i18n.hooks";
import AnimateHeight from "./AnimateHeight";
import DataCell from "./DataCell";
import Row, { RowProps } from "./Row";
+4 -12
View File
@@ -1,4 +1,4 @@
import { act, fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import { describe, expect, test } from "vitest";
@@ -154,19 +154,11 @@ describe("Tabs", () => {
expect(screen.getByTestId("tab2")).toHaveFocus();
/* Move focus to tabPanel */
// eslint-disable-next-line testing-library/no-unnecessary-act
await act(async () => {
/* Tablist handles tabbing with setTimeout, so we need to use act */
await userEvent.tab();
});
await userEvent.tab();
expect(screen.getByTestId("tabpanel1")).toHaveFocus();
/* Move focus back to tablist, now tab1 should have focus */
// eslint-disable-next-line testing-library/no-unnecessary-act
await act(async () => {
await userEvent.tab({ shift: true });
});
/* Move focus back to tablist, now tab1 should have focus */
await userEvent.tab({ shift: true });
expect(screen.getByTestId("tab1")).toHaveFocus();
});
});
+60
View File
@@ -0,0 +1,60 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { Slot } from "../slot/Slot";
import { createContext } from "../util/create-context";
import { AsChildProps } from "../util/types";
type ThemeContext = {
/**
* Color theme
* @default "light"
*/
theme: "light" | "dark";
};
const [ThemeProvider, useThemeInternal] = createContext<ThemeContext>({
hookName: "useTheme",
name: "ThemeProvider",
providerName: "ThemeProvider",
});
type ThemeProps = {
className?: string;
hasBackground?: boolean;
} & ThemeContext &
AsChildProps;
const Theme = forwardRef<HTMLDivElement, ThemeProps>(
(props: ThemeProps, ref) => {
const context = useThemeInternal(false);
const {
children,
className,
asChild = false,
theme = context?.theme ?? "light",
hasBackground: hasBackgroundProp = true,
} = props;
const isRoot = context === undefined;
const hasBackground =
hasBackgroundProp ?? (isRoot && props.theme !== undefined);
const SlotElement = asChild ? Slot : "div";
return (
<ThemeProvider theme={theme}>
<SlotElement
ref={ref}
className={cl("navds-theme", className, theme)}
data-background={hasBackground}
>
{children}
</SlotElement>
</ThemeProvider>
);
},
);
export { Theme, useThemeInternal };
+2
View File
@@ -0,0 +1,2 @@
"use client";
export { Theme } from "./Theme";
@@ -16,7 +16,8 @@ import {
} from "date-fns";
import React from "react";
import { Detail } from "../typography/Detail";
import { TFunction, useDateLocale, useI18n } from "../util/i18n/i18n.context";
import { useDateLocale, useI18n } from "../util/i18n/i18n.hooks";
import { TFunction } from "../util/i18n/i18n.types";
import { useTimelineContext } from "./hooks/useTimelineContext";
import { isVisible } from "./utils";
import { horizontalPositionAndWidth } from "./utils/calc";
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff. Show more