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
139 changed files with 2614 additions and 1867 deletions

No files matched your search

+21 -12
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,7 +70,11 @@ 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",
},
@@ -75,8 +83,9 @@ module.exports = {
files: ["**/*.test.*", "**/__tests__/*"],
plugins: ["@vitest"],
extends: [
"plugin:@vitest/legacy-recommended",
"plugin:testing-library/react",
/* Needed to support v8 eslint config-syntax */
"plugin:@vitest/legacy-recommended",
],
},
+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 }}
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-icons
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
@@ -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;
}
@@ -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.1.2",
"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"
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-stylelint
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "7.1.2",
"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.1.2",
"@navikt/ds-tokens": "^7.1.2",
"@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",
+14
View File
@@ -1,5 +1,19 @@
# @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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "7.1.2",
"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.1.2",
"@navikt/ds-css": "7.2.1",
"axios": "1.7.4",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+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(
+8
View File
@@ -1,5 +1,13 @@
# @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
-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
@@ -263,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 {
-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;
}
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "7.1.2",
"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.1.2",
"@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;
}
+28
View File
@@ -1,5 +1,33 @@
# @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
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "7.1.2",
"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.1.2",
"@navikt/ds-tokens": "^7.1.2",
"@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>
);
@@ -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) {
@@ -51,13 +51,21 @@ 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={{
@@ -54,6 +54,7 @@ const FilteredOptions = () => {
)}
{shouldRenderFilteredOptionsList && (
/* biome-ignore lint/a11y/useFocusableInteractive: Interaction is not handeled by listbox itself. */
<ul
ref={setFilteredOptionsRef}
role="listbox"
@@ -140,7 +140,7 @@ const FilteredOptionsProvider = ({
if (disabled || readOnly) {
return;
}
virtualFocus.moveFocusToTop();
virtualFocus.resetFocus();
if (newState ?? !isInternalListOpen) {
setHideCaret(!!maxSelected?.isLimitReached);
}
@@ -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,
};
};
@@ -99,7 +99,7 @@ const InputProvider = ({ children, value: props }: Props) => {
setInternalValue("");
setSearchTerm("");
},
[externalOnChange, onClear, setInternalValue],
[externalOnChange, onClear],
);
const focusInput = useCallback(() => {
@@ -134,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;
}
@@ -202,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);
}
},
[
@@ -230,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 (
@@ -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");
});
});
});
@@ -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 = {
@@ -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";
@@ -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,
+12 -6
View File
@@ -1,11 +1,20 @@
import cl from "clsx";
import React, { forwardRef, useContext } from "react";
import { BodyLong, BodyShort, Heading } from "../typography";
import { BodyLong, BodyShort, Heading, HeadingProps } from "../typography";
import { ListItem } from "./ListItem";
import { ListContext } from "./context";
// 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>
@@ -58,15 +67,12 @@ export const List = forwardRef<HTMLDivElement, ListProps>(
<BodyLong
as="div"
{...rest}
size={size}
size={listSize}
ref={ref}
className={cl("navds-list", `navds-list--${listSize}`, className)}
>
{title && (
<Heading
size={listSize === "medium" ? "small" : "xsmall"}
as={headingTag}
>
<Heading size={headingSizeMap[listSize]} as={headingTag}>
{title}
</Heading>
)}
+80 -1
View File
@@ -64,7 +64,7 @@ export const Default: Story = {
description: "",
},
argTypes: {
size: { control: { type: "radio" }, options: ["small", "medium"] },
size: { control: { type: "radio" }, options: ["small", "medium", "large"] },
as: { control: { type: "radio" }, options: ["ul", "ol"] },
},
};
@@ -137,6 +137,21 @@ 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."
@@ -176,6 +191,22 @@ 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."
@@ -217,6 +248,27 @@ 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."
@@ -328,6 +380,33 @@ 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
+1 -1
View File
@@ -22,7 +22,7 @@ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
* 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> = {
@@ -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:
@@ -43,8 +43,9 @@ export function createContext<T>(options: CreateContextOptions<T> = {}) {
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);
}
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tailwind
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "7.1.2",
"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.1.2",
"@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"
},
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tokens
## 7.2.1
## 7.2.0
## 7.1.2
## 7.1.1
@@ -1,10 +1,10 @@
import { contrastTokenConfig } from "./tokens/contrast";
import { globalColorDarkModeConfig } from "./tokens/global-dark";
import { globalColorLightModeConfig } from "./tokens/global-light";
import { radiusTokenConfig } from "./tokens/radius";
import { semanticTokenConfig } from "./tokens/semantic";
import { semanticTokensForAllRolesConfig } from "./tokens/semantic-roles";
import { spacingTokenConfig } from "./tokens/spacing";
import { textContrastTokenConfig } from "./tokens/text-contrast";
import { mergeConfigs, tokensWithPrefix } from "./util";
/**
@@ -17,7 +17,7 @@ export const lightModeTokens = () => {
return tokensWithPrefix(
mergeConfigs([
semanticTokensForAllRolesConfig(),
contrastTokenConfig(),
textContrastTokenConfig(),
semanticTokenConfig("light"),
globalColorLightModeConfig,
]),
@@ -32,7 +32,7 @@ export const darkModeTokens = () => {
return tokensWithPrefix(
mergeConfigs([
semanticTokensForAllRolesConfig(),
contrastTokenConfig(),
textContrastTokenConfig(),
semanticTokenConfig("dark"),
globalColorDarkModeConfig,
]),
@@ -50,15 +50,22 @@ export const getTokensForCollection = async (
*/
return reference.length > 0
? prepareToken(
{ ...token, alias: createTokenName(reference[0]) },
{
...(token as TransformedTokenWithScopes),
alias: createTokenName(reference[0]),
},
dictionary,
)
: prepareToken(token, dictionary);
: prepareToken(token as TransformedTokenWithScopes, dictionary);
});
};
type TransformedTokenWithScopes = TransformedToken & {
scopes: StyleDictionaryToken<TokenTypes>["scopes"];
};
function prepareToken(
token: TransformedToken,
token: TransformedTokenWithScopes,
dictionary: Dictionary,
): FigmaToken {
const formatter = createPropertyFormatter({
@@ -109,35 +116,34 @@ export function figmaValue(token: TransformedToken): string | number {
* Scopes allows us to define where in Figma the token can be used.
* @see https://www.figma.com/plugin-docs/api/VariableScope
*/
function figmaSettings(token: TransformedToken): {
function figmaSettings(token: TransformedTokenWithScopes): {
figmaType: VariableResolvedDataType;
scopes: VariableScope[];
} {
let setting: ReturnType<typeof figmaSettings> | undefined;
if (isGlobalColor(token)) {
return createFigmaSettings("COLOR", ["ALL_FILLS", "STROKE_COLOR"]);
setting = createFigmaSettings("COLOR", ["ALL_FILLS", "STROKE_COLOR"]);
} else if (isTokenOfSemanticColorGroup(token, "background")) {
setting = createFigmaSettings("COLOR", ["FRAME_FILL", "SHAPE_FILL"]);
} else if (isTokenOfSemanticColorGroup(token, "border")) {
setting = createFigmaSettings("COLOR", ["STROKE_COLOR"]);
} else if (isTokenOfSemanticColorGroup(token, "text")) {
setting = createFigmaSettings("COLOR", ["SHAPE_FILL", "TEXT_FILL"]);
} else if (isRadiusToken(token)) {
setting = createFigmaSettings("FLOAT", ["CORNER_RADIUS"]);
} else if (isSpacingToken(token)) {
setting = createFigmaSettings("FLOAT", ["GAP"]);
}
if (isTokenOfSemanticColorGroup(token, "background")) {
return createFigmaSettings("COLOR", ["FRAME_FILL", "SHAPE_FILL"]);
}
if (isTokenOfSemanticColorGroup(token, "border")) {
return createFigmaSettings("COLOR", ["STROKE_COLOR"]);
}
if (
isTokenOfSemanticColorGroup(token, "contrast") ||
isTokenOfSemanticColorGroup(token, "text")
) {
return createFigmaSettings("COLOR", ["SHAPE_FILL", "TEXT_FILL"]);
}
if (isRadiusToken(token)) {
return createFigmaSettings("FLOAT", ["CORNER_RADIUS"]);
}
if (isSpacingToken(token)) {
return createFigmaSettings("FLOAT", ["GAP"]);
if (!setting) {
console.warn(`No fitting type or scope found for token: ${token.name}`);
return createFigmaSettings("STRING", []);
}
console.warn(`No fitting type or scope found for token: ${token.name}`);
return createFigmaSettings("STRING", []);
token.scopes && setting.scopes.push(...token.scopes);
return setting;
}
function createFigmaSettings(
@@ -1,23 +0,0 @@
import { describe, expect, test } from "vitest";
import { config as darkModeConfig } from "../tokens/global-dark";
import { config as lightModeConfig } from "../tokens/global-light";
describe("Validate global token config", () => {
describe("Lightmode should have correct colorKeys and ratios for each role", () => {
Object.entries(lightModeConfig).forEach(([role, { colorKeys, ratios }]) => {
test(`${role} should have correct colorKeys and ratios for each role`, () => {
expect(colorKeys.length).toBeGreaterThan(0);
expect(ratios.length).toEqual(10);
});
});
});
describe("Darkmode should have correct colorKeys and ratios for each role", () => {
Object.entries(darkModeConfig).forEach(([role, { colorKeys, ratios }]) => {
test(`${role} should have correct colorKeys and ratios for each role`, () => {
expect(colorKeys.length).toBeGreaterThan(0);
expect(ratios.length).toEqual(10);
});
});
});
});
@@ -1,19 +1,25 @@
import { describe, expect, test } from "vitest";
import { globalScale } from "../create-configuration";
import { contrastTokenConfig } from "../tokens/contrast";
import { neutralTokenConfig } from "../tokens/neutral";
import { globalColorDarkModeConfig } from "../tokens/global-dark";
import { globalColorLightModeConfig } from "../tokens/global-light";
import { radiusTokenConfig } from "../tokens/radius";
import { semanticTokenConfig } from "../tokens/semantic";
import { semanticTokensForAllRolesConfig } from "../tokens/semantic-roles";
import { spacingTokenConfig } from "../tokens/spacing";
import { textContrastTokenConfig } from "../tokens/text-contrast";
const configKeysWithGroup = ["value", "type", "group"];
const configKeys = ["value", "type"];
describe("Validate token configurations", () => {
test(`Semantic tokens`, () => {
test(`Semantic tokens lightmode`, () => {
expect(
validateConfig(semanticTokenConfig(), configKeysWithGroup),
validateConfig(semanticTokenConfig("light"), configKeysWithGroup),
).toBeTruthy();
});
test(`Semantic tokens darkmode`, () => {
expect(
validateConfig(semanticTokenConfig("dark"), configKeysWithGroup),
).toBeTruthy();
});
@@ -23,33 +29,21 @@ describe("Validate token configurations", () => {
).toBeTruthy();
});
test(`Contrast tokens`, () => {
test(`Text-contrast tokens`, () => {
expect(
validateConfig(contrastTokenConfig(), configKeysWithGroup),
validateConfig(textContrastTokenConfig(), configKeysWithGroup),
).toBeTruthy();
});
test(`Global lightmode scale`, () => {
expect(
validateConfig(globalScale("light"), configKeysWithGroup),
validateConfig(globalColorLightModeConfig, configKeysWithGroup),
).toBeTruthy();
});
test(`Global darkmode scale`, () => {
expect(
validateConfig(globalScale("dark"), configKeysWithGroup),
).toBeTruthy();
});
test(`Neutral light unique tokens`, () => {
expect(
validateConfig(neutralTokenConfig("light"), configKeysWithGroup),
).toBeTruthy();
});
test(`Neutral dark unique tokens`, () => {
expect(
validateConfig(neutralTokenConfig("dark"), configKeysWithGroup),
validateConfig(globalColorDarkModeConfig, configKeysWithGroup),
).toBeTruthy();
});
@@ -67,7 +61,11 @@ function validateConfig(
requiredKeys: string[],
): boolean {
for (const key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
if (
typeof obj[key] === "object" &&
obj[key] !== null &&
!Array.isArray(obj[key])
) {
if (!validateConfig(obj[key], requiredKeys)) {
return false;
}
@@ -7,16 +7,16 @@ export const globalColorDarkModeConfig: GlobalConfigT = {
type: "global-color",
group: "neutral",
},
"100": { value: "#1a1d21", type: "global-color", group: "neutral" },
"200": { value: "#202327", type: "global-color", group: "neutral" },
"300": { value: "#282b2f", type: "global-color", group: "neutral" },
"400": { value: "#33363a", type: "global-color", group: "neutral" },
"500": { value: "#666d77", type: "global-color", group: "neutral" },
"600": { value: "#787e89", type: "global-color", group: "neutral" },
"700": { value: "#8c929d", type: "global-color", group: "neutral" },
"800": { value: "#959ca5", type: "global-color", group: "neutral" },
"900": { value: "#a6acb4", type: "global-color", group: "neutral" },
"1000": { value: "#c5c9ce", type: "global-color", group: "neutral" },
"100": { value: "#161d28", type: "global-color", group: "neutral" },
"200": { value: "#1c232f", type: "global-color", group: "neutral" },
"300": { value: "#242b37", type: "global-color", group: "neutral" },
"400": { value: "#2e3641", type: "global-color", group: "neutral" },
"500": { value: "#656d7b", type: "global-color", group: "neutral" },
"600": { value: "#767e8c", type: "global-color", group: "neutral" },
"700": { value: "#8a92a0", type: "global-color", group: "neutral" },
"800": { value: "#949ba8", type: "global-color", group: "neutral" },
"900": { value: "#a5acb6", type: "global-color", group: "neutral" },
"1000": { value: "#c4c9d0", type: "global-color", group: "neutral" },
},
accent: {
"100": { value: "#101d2d", type: "global-color", group: "accent" },
@@ -55,15 +55,15 @@ export const globalColorLightModeConfig: GlobalConfigT = {
"1000": { value: "#481700", type: "global-color", group: "warning" },
},
danger: {
"100": { value: "#fff3f7", type: "global-color", group: "danger" },
"200": { value: "#ffe9f0", type: "global-color", group: "danger" },
"300": { value: "#ffdae6", type: "global-color", group: "danger" },
"100": { value: "#fff2f7", type: "global-color", group: "danger" },
"200": { value: "#ffe8f0", type: "global-color", group: "danger" },
"300": { value: "#ffd9e6", type: "global-color", group: "danger" },
"400": { value: "#ffc2d7", type: "global-color", group: "danger" },
"500": { value: "#fa4256", type: "global-color", group: "danger" },
"600": { value: "#e81c3b", type: "global-color", group: "danger" },
"700": { value: "#cc0023", type: "global-color", group: "danger" },
"800": { value: "#bd0015", type: "global-color", group: "danger" },
"900": { value: "#a60000", type: "global-color", group: "danger" },
"500": { value: "#ec526e", type: "global-color", group: "danger" },
"600": { value: "#e22a49", type: "global-color", group: "danger" },
"700": { value: "#cb0035", type: "global-color", group: "danger" },
"800": { value: "#bc002a", type: "global-color", group: "danger" },
"900": { value: "#a60017", type: "global-color", group: "danger" },
"1000": { value: "#560000", type: "global-color", group: "danger" },
},
info: {
@@ -14,6 +14,11 @@ function semanticTokensDark(): StyleDictionaryTokenConfig<"color"> {
value: "#0E151F",
type: "color",
group: "background",
/**
* Allows token to be used on 'effect' properties in Figma,
* Bg-default is used between element and focus-marking.
*/
scopes: ["EFFECT_COLOR"],
},
raised: {
value: "{a.neutral.200.value}",
@@ -53,6 +58,11 @@ function semanticTokensLight(): StyleDictionaryTokenConfig<"color"> {
value: "{a.neutral.000.value}",
type: "color",
group: "background",
/**
* Allows token to be used on 'effect' properties in Figma,
* Bg-default is used between element and focus-marking.
*/
scopes: ["EFFECT_COLOR"],
},
input: {
value: "{a.neutral.000.value}",
@@ -99,6 +109,10 @@ function semanticTokensLight(): StyleDictionaryTokenConfig<"color"> {
value: "{a.accent.700.value}",
type: "color",
group: "border",
/**
* Allows token to be used on 'effect' properties in Figma, used for creating focus markings.
*/
scopes: ["EFFECT_COLOR"],
},
},
};
@@ -1,69 +1,69 @@
import { GlobalColorRoles, StyleDictionaryToken } from "../util";
export const contrastTokenConfig = (): {
contrast: {
[key in GlobalColorRoles]: StyleDictionaryToken<"color">;
export const textContrastTokenConfig = (): {
text: {
[key in `${GlobalColorRoles}-contrast`]: StyleDictionaryToken<"color">;
};
} => ({
contrast: {
text: {
/* core */
accent: {
"accent-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
neutral: {
"neutral-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
/* Status */
danger: {
"danger-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
info: {
"info-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
success: {
"success-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
warning: {
"warning-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
/* Brand */
brandOne: {
"brandOne-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
brandTwo: {
"brandTwo-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
brandThree: {
"brandThree-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
/* Data */
dataOne: {
"dataOne-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
dataTwo: {
"dataTwo-contrast": {
value: "{a.neutral.000.value}",
type: "color",
group: "contrast",
group: "text",
},
},
});
+22 -3
View File
@@ -51,19 +51,38 @@ export type TokenTypes =
| "global-radius"
| "global-spacing";
export type SemanticTokenGroups = "background" | "border" | "text" | "contrast";
type SemanticTokenGroupsWithRoles = Exclude<SemanticTokenGroups, "contrast">;
export type SemanticTokenGroups = "background" | "border" | "text";
export type TokenGroup =
| GlobalColorRoles
| SemanticTokenGroups
| `${SemanticTokenGroupsWithRoles}.${GlobalColorRoles}`;
| `${SemanticTokenGroups}.${GlobalColorRoles}`;
export type StyleDictionaryToken<T extends TokenTypes> = {
/**
* Token value
* @example "#000000"
* @example "1px"
* @example "{a.neutral.100.value}"
*/
value: string;
/**
* Token type
*/
type: T;
/**
* Group the token belongs to. Used for auto-documentation and categorization in Figma.
*/
group?: TokenGroup;
/**
* Optional comment for the token. Will be included in the generated documentation and in Figma.
*/
comment?: string;
/**
* Optional extra scopes for the token.
* Token will include all default scopes based on `type`, and any extra specified here.
*/
scopes?: VariableScope[];
};
export type StyleDictionaryTokenConfig<T extends TokenTypes> = {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "7.1.2",
"version": "7.2.1",
"description": "Design-tokens for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
+136 -118
View File
@@ -1,5 +1,23 @@
# Changelog
## 7.2
### @navikt/ds-react
- 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))
* 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))
- Performance: :zap: Optimized memoization for rerendring in some components. ([#3197](https://github.com/navikt/aksel/pull/3197))
### @navikt/ds-css
- Combobox: Support PageUp/PageDown in dropdown list. ([#3158](https://github.com/navikt/aksel/pull/3158))
## 7.1
### @navikt/aksel-stylelint
@@ -38,6 +56,10 @@
- Icons: Removed renamed icons. [See migration guide for changes](https://aksel.nav.no/grunnleggende/kode/migrering#194b60833d9e). ([#3139](https://github.com/navikt/aksel/pull/3139))
### @navikt/ds-tailwind
- Tailwind: Extended 'screens'-config in theme to match Aksel breakpoints. Tailwind and Primitives can now be used side by side with matching dynamic breakpoints. [See migration guide for potential issues when adopting](https://aksel.nav.no/grunnleggende/kode/migrering#3a2340f6f69b). ([#3139](https://github.com/navikt/aksel/pull/3139))
### @navikt/ds-react
- Combobox: Fix issue with whitespace in the ID of options with multiple spaces ([#3162](https://github.com/navikt/aksel/pull/3162))
@@ -48,10 +70,6 @@
- ErrorSummary: Focus heading instead of container for improved experience with screen reader. ([#3139](https://github.com/navikt/aksel/pull/3139))
### @navikt/ds-tailwind
- Tailwind: Extended 'screens'-config in theme to match Aksel breakpoints. Tailwind and Primitives can now be used side by side with matching dynamic breakpoints. [See migration guide for potential issues when adopting](https://aksel.nav.no/grunnleggende/kode/migrering#3a2340f6f69b). ([#3139](https://github.com/navikt/aksel/pull/3139))
### @navikt/ds-css
- List: :lipstick: make icons 24x24px large ([#3148](https://github.com/navikt/aksel/pull/3148))
@@ -70,13 +88,17 @@
## 6.16
### @navikt/aksel-icons
- Icons: Updated Clipboard icons. ([#3113](https://github.com/navikt/aksel/pull/3113))
### @navikt/aksel
- Aksel-CLI: Bump jscodeshift-add-imports to 1.0.11. ([#3099](https://github.com/navikt/aksel/pull/3099))
### @navikt/aksel-icons
### @navikt/ds-tokens
- Icons: Updated Clipboard icons. ([#3113](https://github.com/navikt/aksel/pull/3113))
- Tokens: Adjusted Green 400 to comply with AA 3.00 contrast. ([#3091](https://github.com/navikt/aksel/pull/3091))
### @navikt/ds-react
@@ -96,10 +118,6 @@
- Primitives: Added `gridColumn`-prop for more grid alignment control. ([#3082](https://github.com/navikt/aksel/pull/3082))
### @navikt/ds-tokens
- Tokens: Adjusted Green 400 to comply with AA 3.00 contrast. ([#3091](https://github.com/navikt/aksel/pull/3091))
### @navikt/ds-css
- Combobox: :lipstick: hide caret on select ([#3071](https://github.com/navikt/aksel/pull/3071))
@@ -128,6 +146,10 @@
## 6.14
### @navikt/ds-tokens
- Fonts: Added built-in support for cyrillic, cyrillic-ext and latin-ext ([#3044](https://github.com/navikt/aksel/pull/3044))
### @navikt/ds-react
- Breaking change: Combobox.onChange now receive only value as argument, instead of ChangeEvent ([#2974](https://github.com/navikt/aksel/pull/2974))
@@ -144,10 +166,6 @@
* Prevent Escape in open Combobox from closing Modals ([#3053](https://github.com/navikt/aksel/pull/3053))
### @navikt/ds-tokens
- Fonts: Added built-in support for cyrillic, cyrillic-ext and latin-ext ([#3044](https://github.com/navikt/aksel/pull/3044))
### @navikt/ds-css
- Fonts: Added built-in support for cyrillic, cyrillic-ext and latin-ext ([#3044](https://github.com/navikt/aksel/pull/3044))
@@ -390,20 +408,30 @@
## 6.0
### @navikt/aksel
- Chat: Fjernet deprecated props `backgroundColor` og `avatarBgColor`. Bruk prop `variant` som erstatning. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/aksel-stylelint
- Textarea: Fjernet div med klassen navds-textarea\_\_wrapper ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/aksel-icons
- ESM: ds-react og aksel-icons støtter nå native ESM 🎉 ([#2623](https://github.com/navikt/aksel/pull/2623))
* SSR: ds-react og aksel-icons støtter nå `use client` og kan brukes i nextjs app-router uten lokal re-eksportering. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/aksel-stylelint
- Textarea: Fjernet div med klassen navds-textarea\_\_wrapper ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/aksel
- Chat: Fjernet deprecated props `backgroundColor` og `avatarBgColor`. Bruk prop `variant` som erstatning. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-tokens
- Tokens: La til nytt brekkpunkt `2xl` for `1440px`. Alle primitives er oppdatert for å reflektere oppdateringen. Brukere av Tailwind vil måtte sjekke at overskriving av `screen: "2xl"` ikke brekker app. ([#2623](https://github.com/navikt/aksel/pull/2623))
- Tokens: Fjernet token `--a-z-index-modal` da den ikke lengre blir konsumert i systemet. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-tailwind
- Tokens: La til nytt brekkpunkt `2xl` for `1440px`. Alle primitives er oppdatert for å reflektere oppdateringen. Brukere av Tailwind vil måtte sjekke at overskriving av `screen: "2xl"` ikke brekker app. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-react
- Chat: Fjernet deprecated props `backgroundColor` og `avatarBgColor`. Bruk prop `variant` som erstatning. ([#2623](https://github.com/navikt/aksel/pull/2623))
@@ -426,16 +454,6 @@
* SSR: ds-react og aksel-icons støtter nå `use client` og kan brukes i nextjs app-router uten lokal re-eksportering. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-tailwind
- Tokens: La til nytt brekkpunkt `2xl` for `1440px`. Alle primitives er oppdatert for å reflektere oppdateringen. Brukere av Tailwind vil måtte sjekke at overskriving av `screen: "2xl"` ikke brekker app. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-tokens
- Tokens: La til nytt brekkpunkt `2xl` for `1440px`. Alle primitives er oppdatert for å reflektere oppdateringen. Brukere av Tailwind vil måtte sjekke at overskriving av `screen: "2xl"` ikke brekker app. ([#2623](https://github.com/navikt/aksel/pull/2623))
- Tokens: Fjernet token `--a-z-index-modal` da den ikke lengre blir konsumert i systemet. ([#2623](https://github.com/navikt/aksel/pull/2623))
### @navikt/ds-css
- Tokens: La til nytt brekkpunkt `2xl` for `1440px`. Alle primitives er oppdatert for å reflektere oppdateringen. Brukere av Tailwind vil måtte sjekke at overskriving av `screen: "2xl"` ikke brekker app. ([#2623](https://github.com/navikt/aksel/pull/2623))
@@ -492,14 +510,14 @@
- Ikoner: Nytt ikon `LocationPin` ✨ ([#2672](https://github.com/navikt/aksel/pull/2672))
### @navikt/ds-react
- :sparkles: Combobox: Mulighet for å begrense hvor mange valg bruker kan ta ([#2260](https://github.com/navikt/aksel/pull/2260))
### @navikt/ds-tailwind
- Tailwind: La til ny `maxWidth` for text ([#2652](https://github.com/navikt/aksel/pull/2652))
### @navikt/ds-react
- :sparkles: Combobox: Mulighet for å begrense hvor mange valg bruker kan ta ([#2260](https://github.com/navikt/aksel/pull/2260))
### @navikt/ds-css
- :sparkles: Combobox: Mulighet for å begrense hvor mange valg bruker kan ta ([#2260](https://github.com/navikt/aksel/pull/2260))
@@ -536,6 +554,10 @@
## 5.13
### @navikt/ds-tokens
- Tokens: Ny token `--a-text-width-max: 576px` ([#2572](https://github.com/navikt/aksel/pull/2572))
### @navikt/ds-react
- Accordion.Item, Dropdown, ReadMore og Tooltip: Har en ny prop `onOpenChange?: (open: boolean) => void;` som forteller nå-state når `open`-state endrer seg. Dette vil være nyttig hvis man ikke bruker controlled-state, men fortsatt ønsker å vite om komponenten er `open` eller ikke (f.eks logging). ([#2585](https://github.com/navikt/aksel/pull/2585))
@@ -544,24 +566,20 @@
* Page: Lagt til ny bakgrunnsfarge `surface-subtle` og ny maksbredde på Page.Block `text` ([#2572](https://github.com/navikt/aksel/pull/2572))
### @navikt/ds-tokens
- Tokens: Ny token `--a-text-width-max: 576px` ([#2572](https://github.com/navikt/aksel/pull/2572))
### @navikt/ds-css
- Page: Lagt til ny bakgrunnsfarge `surface-subtle` og ny maksbredde på Page.Block `text` ([#2572](https://github.com/navikt/aksel/pull/2572))
## 5.12
### @navikt/aksel-stylelint
- Stylelint: Støtter nå stylelint v16 ([#2591](https://github.com/navikt/aksel/pull/2591))
### @navikt/aksel-icons
- Ikoner: Oppdatert ikonpakke med diverse bugdfixes og oppdateringer av stroke ([#2538](https://github.com/navikt/aksel/pull/2538))
### @navikt/aksel-stylelint
- Stylelint: Støtter nå stylelint v16 ([#2591](https://github.com/navikt/aksel/pull/2591))
### @navikt/ds-react
- MonthPicker: MonthCaption satt forrige/neste-knapper til `disabled` når de skulle være `enabled`. ([#2601](https://github.com/navikt/aksel/pull/2601))
@@ -626,6 +644,10 @@
## 5.10
### @navikt/ds-tailwind
- typescript types for tailwind preset ([#2486](https://github.com/navikt/aksel/pull/2486))
### @navikt/ds-react
- Gjør lukknapp i Alert til type button ([#2497](https://github.com/navikt/aksel/pull/2497))
@@ -642,10 +664,6 @@
- Primitives: Ny komponent `Page` og `Page.Block` ([#2456](https://github.com/navikt/aksel/pull/2456))
### @navikt/ds-tailwind
- typescript types for tailwind preset ([#2486](https://github.com/navikt/aksel/pull/2486))
### @navikt/ds-css
- :bug: Textarea: Teller flyttet ut av tekstfeltet for å unngå overlapp og misforståelser ([#2483](https://github.com/navikt/aksel/pull/2483))
@@ -704,6 +722,10 @@
- Ikoner: Nye ikoner `ChevronLeftFirstCircle` og `ChevronRightLastCircle` ([#2365](https://github.com/navikt/aksel/pull/2365))
### @navikt/ds-tokens
- Ny spacing token på 0.375rem ([#2370](https://github.com/navikt/aksel/pull/2370))
### @navikt/ds-react
- :lipstick: Chips.Toggle: Ikon i uvalgt tilstand ([#2395](https://github.com/navikt/aksel/pull/2395))
@@ -724,10 +746,6 @@
- :lipstick: CopyButton: Justert padding, gap og animasjon ([#2355](https://github.com/navikt/aksel/pull/2355))
### @navikt/ds-tokens
- Ny spacing token på 0.375rem ([#2370](https://github.com/navikt/aksel/pull/2370))
### @navikt/ds-css
- :lipstick: Chips.Toggle: Ikon i uvalgt tilstand ([#2395](https://github.com/navikt/aksel/pull/2395))
@@ -754,6 +772,10 @@
* Ikoner: Nye ikoner `ClipboardCheckmark` og `ClipboardLink` ([#2307](https://github.com/navikt/aksel/pull/2307))
### @navikt/ds-tokens
- Tokens: Tilbyr nå token literals for typer, tidligere string literals ([#2342](https://github.com/navikt/aksel/pull/2342))
### @navikt/ds-react
- Datepicker: Popover legger seg nå ikke ovenfor input ([#2349](https://github.com/navikt/aksel/pull/2349))
@@ -780,10 +802,6 @@
- Remove tabIndex="-1" on Popover to fix VoiceOver navigation issue inside Popover ([#2303](https://github.com/navikt/aksel/pull/2303))
### @navikt/ds-tokens
- Tokens: Tilbyr nå token literals for typer, tidligere string literals ([#2342](https://github.com/navikt/aksel/pull/2342))
### @navikt/ds-css
- :bug: Modal: Sett riktig tekstfarge ([#2340](https://github.com/navikt/aksel/pull/2340))
@@ -806,16 +824,16 @@
## 5.5
### @navikt/ds-tokens
- Box: Ny primitive, erstatter dagens `Panel` ([#2195](https://github.com/navikt/aksel/pull/2195))
### @navikt/ds-react
- Box: Ny primitive, erstatter dagens `Panel` ([#2195](https://github.com/navikt/aksel/pull/2195))
* Skeleton: Width fungerer nå med inline-variant av Skeleton ([#2273](https://github.com/navikt/aksel/pull/2273))
### @navikt/ds-tokens
- Box: Ny primitive, erstatter dagens `Panel` ([#2195](https://github.com/navikt/aksel/pull/2195))
### @navikt/ds-css
- Box: Ny primitive, erstatter dagens `Panel` ([#2195](https://github.com/navikt/aksel/pull/2195))
@@ -824,6 +842,10 @@
## 5.4
### @navikt/ds-tokens
- Tokens: `data-theme="light"` vil nå tilbakestille theming ([#2238](https://github.com/navikt/aksel/pull/2238))
### @navikt/ds-react
- MonthPicker: Fikset kalkulering av fromDate/toDate ([#2269](https://github.com/navikt/aksel/pull/2269))
@@ -832,10 +854,6 @@
- Search: Kjører nå bare preventDefault ved Escape når `input` inneholder tekst ([#2245](https://github.com/navikt/aksel/pull/2245))
### @navikt/ds-tokens
- Tokens: `data-theme="light"` vil nå tilbakestille theming ([#2238](https://github.com/navikt/aksel/pull/2238))
### @navikt/ds-css
- Primitives: Nye komponenter `Show` og `Hide` er lagt til. ([#2222](https://github.com/navikt/aksel/pull/2222))
@@ -924,6 +942,16 @@
## 5.0
### @navikt/ds-tokens
- ConfirmationPanel: Fikset opplesing av feilmelding ([#2199](https://github.com/navikt/aksel/pull/2199))
- Tokens: `--a-icon-alt-3` er nå i synk med figma ([#2199](https://github.com/navikt/aksel/pull/2199))
* Gray: Gråfarger er nå mindre varme ([#2092](https://github.com/navikt/aksel/pull/2092))
* Action: Selected-varianter av action er nå 'blue' ('deepblue' før) ([#2092](https://github.com/navikt/aksel/pull/2092))
### @navikt/ds-react
- ConfirmationPanel: Fikset opplesing av feilmelding ([#2199](https://github.com/navikt/aksel/pull/2199))
@@ -940,16 +968,6 @@
* Table: Har lagt til ny size: 'large'. ([#2178](https://github.com/navikt/aksel/pull/2178))
### @navikt/ds-tokens
- ConfirmationPanel: Fikset opplesing av feilmelding ([#2199](https://github.com/navikt/aksel/pull/2199))
- Tokens: `--a-icon-alt-3` er nå i synk med figma ([#2199](https://github.com/navikt/aksel/pull/2199))
* Gray: Gråfarger er nå mindre varme ([#2092](https://github.com/navikt/aksel/pull/2092))
* Action: Selected-varianter av action er nå 'blue' ('deepblue' før) ([#2092](https://github.com/navikt/aksel/pull/2092))
### @navikt/ds-css
- ConfirmationPanel: Fikset opplesing av feilmelding ([#2199](https://github.com/navikt/aksel/pull/2199))
@@ -1115,11 +1133,9 @@
## 4.4
### @navikt/ds-react
### @navikt/ds-tokens
- Textarea: Fikset i18n for counter ([`718b3204d`](https://github.com/navikt/aksel/commit/718b3204d8714c4fc515dcad484424214bdc0c77))
* Fikset klassenavn brukt for popover i Datepicker og Monthpicker ([PR](https://github.com/navikt/aksel/pull/2041))
- Shadow-tokens er oppdatert til mer tydeligere varianter ([PR](https://github.com/navikt/aksel/pull/2041))
### @navikt/ds-tailwind
@@ -1127,9 +1143,11 @@
- Oppdatert text-subtle og icon-subtle tokens til 700-skala (tidligere 600). ([PR](https://github.com/navikt/aksel/pull/2036))
### @navikt/ds-tokens
### @navikt/ds-react
- Shadow-tokens er oppdatert til mer tydeligere varianter ([PR](https://github.com/navikt/aksel/pull/2041))
- Textarea: Fikset i18n for counter ([`718b3204d`](https://github.com/navikt/aksel/commit/718b3204d8714c4fc515dcad484424214bdc0c77))
* Fikset klassenavn brukt for popover i Datepicker og Monthpicker ([PR](https://github.com/navikt/aksel/pull/2041))
### @navikt/ds-css
@@ -1161,13 +1179,23 @@
## 4.1
### @navikt/aksel-icons
- Nye ikoner `ChevronRightLast` og `ChevronLeftLast` ([PR](https://github.com/navikt/aksel/pull/2054))
### @navikt/aksel-stylelint
- Deprecated klassenavn `navds-chips--icon-left` ([PR](https://github.com/navikt/aksel/pull/2035))
### @navikt/aksel-icons
### @navikt/ds-tokens
- Nye ikoner `ChevronRightLast` og `ChevronLeftLast` ([PR](https://github.com/navikt/aksel/pull/2054))
- :tada: Fargetokens for datavisualisering. ([PR](https://github.com/navikt/aksel/pull/2032))
- Oppdatert text-subtle og icon-subtle tokens til 700-skala (tidligere 600). ([PR](https://github.com/navikt/aksel/pull/2036))
### @navikt/ds-tailwind
- :tada: Fargetokens for datavisualisering. ([PR](https://github.com/navikt/aksel/pull/2032))
### @navikt/ds-react
@@ -1193,16 +1221,6 @@
- Accordion: La til `indent`-prop ([PR](https://github.com/navikt/aksel/pull/2027))
### @navikt/ds-tailwind
- :tada: Fargetokens for datavisualisering. ([PR](https://github.com/navikt/aksel/pull/2032))
### @navikt/ds-tokens
- :tada: Fargetokens for datavisualisering. ([PR](https://github.com/navikt/aksel/pull/2032))
- Oppdatert text-subtle og icon-subtle tokens til 700-skala (tidligere 600). ([PR](https://github.com/navikt/aksel/pull/2036))
### @navikt/ds-css
- Readmore: setter nå eksplisitt color for å ikke arve text-subtle fra parent. ([PR](https://github.com/navikt/aksel/pull/2049))
@@ -1319,10 +1337,6 @@
## 2.9
### @navikt/aksel
- :truck: Flyttet Codemods fra `@navikt/ds-codemods` -> `@navikt/aksel`. `@navikt/ds-codemods` regnes nå som deprecated ([PR](https://github.com/navikt/aksel/pull/1952))
### @navikt/aksel-icons
- Oppdatert Statusikoner til å være tydeligere og mer konsistente. ([PR](https://github.com/navikt/aksel/pull/1959))
@@ -1333,6 +1347,10 @@
* Nye ikoner `BulletList` og `NumberList` ([PR](https://github.com/navikt/aksel/pull/1950))
### @navikt/aksel
- :truck: Flyttet Codemods fra `@navikt/ds-codemods` -> `@navikt/aksel`. `@navikt/ds-codemods` regnes nå som deprecated ([PR](https://github.com/navikt/aksel/pull/1952))
### @navikt/ds-react
- ExpansionCard: Støtter nå `aria-labelledby` i tillegg til `aria-label` ([PR](https://github.com/navikt/aksel/pull/1944))
@@ -1415,17 +1433,17 @@
## 2.5
### @navikt/ds-react
### @navikt/ds-tokens
- List: Refaktorert nestede lister
- Breakpoint-tokens lagt til ([PR](https://github.com/navikt/aksel/pull/1832))
### @navikt/ds-tailwind
- Breakpoint-tokens er lagt til. Overskriver native tailwind-breakpoints ([PR](https://github.com/navikt/aksel/pull/1832))
### @navikt/ds-tokens
### @navikt/ds-react
- Breakpoint-tokens lagt til ([PR](https://github.com/navikt/aksel/pull/1832))
- List: Refaktorert nestede lister
## 2.4
@@ -1455,18 +1473,18 @@
## 2.2
### @navikt/ds-react
- ToggleGroup: `neutral`-variant ([PR](https://github.com/navikt/aksel/pull/1789))
- Button: `neutral`-variant ([PR](https://github.com/navikt/aksel/pull/1789))
### @navikt/ds-tokens
- Justeringer av semantiske fargetokens, statusfarger nå mer tydelig ([PR](https://github.com/navikt/aksel/pull/1787))
- Oppdatert neutral-tokens ([PR](https://github.com/navikt/aksel/pull/1789))
### @navikt/ds-react
- ToggleGroup: `neutral`-variant ([PR](https://github.com/navikt/aksel/pull/1789))
- Button: `neutral`-variant ([PR](https://github.com/navikt/aksel/pull/1789))
### @navikt/ds-css
- ToggleGroup: `Neutral`-variant. ([PR](https://github.com/navikt/aksel/pull/1789))
@@ -1475,6 +1493,10 @@
## 2.1
### @navikt/ds-tokens
- Fikset feil danger-hover token ([PR](https://github.com/navikt/aksel/pull/1665))
### @navikt/ds-react
- Datepicker: Fikset rekkefølge på `onValidate` og `onRangeChange` i useRangepicker-hook
@@ -1491,16 +1513,20 @@
- Datepicker/Monthpicker: år med 2 siffer i input fungerer nå
### @navikt/ds-tokens
- Fikset feil danger-hover token ([PR](https://github.com/navikt/aksel/pull/1665))
### @navikt/ds-css
- TextField: `small`-variant har nå 8px horisontal padding (før 4px)
## 2.0
### @navikt/ds-tokens
- Alle token er oppdatert til nytt format. ([Guide](https://aksel.nav.no/grunnleggende/kode/migrering#h76f47744d112))
### @navikt/ds-tailwind
- Alle token er oppdatert til nytt format. ([Guide](https://aksel.nav.no/grunnleggende/kode/migrering#h76f47744d112))
### @navikt/ds-react
- Datepicker: Bedre typer for `ref`
@@ -1519,14 +1545,6 @@
* Fonter blir bruk i all typografi blir nå lastet fra CDN
### @navikt/ds-tailwind
- Alle token er oppdatert til nytt format. ([Guide](https://aksel.nav.no/grunnleggende/kode/migrering#h76f47744d112))
### @navikt/ds-tokens
- Alle token er oppdatert til nytt format. ([Guide](https://aksel.nav.no/grunnleggende/kode/migrering#h76f47744d112))
### @navikt/ds-css
- Radio: Fikset default visuell error-state ([PR](https://github.com/navikt/aksel/pull/1737))
+5 -5
View File
@@ -15,10 +15,10 @@
"playroom"
],
"dependencies": {
"@navikt/aksel-icons": "^7.1.2",
"@navikt/ds-css": "^7.1.2",
"@navikt/ds-react": "^7.1.2",
"@navikt/ds-tailwind": "^7.1.2",
"@navikt/ds-tokens": "^7.1.2"
"@navikt/aksel-icons": "^7.2.1",
"@navikt/ds-css": "^7.2.1",
"@navikt/ds-react": "^7.2.1",
"@navikt/ds-tailwind": "^7.2.1",
"@navikt/ds-tokens": "^7.2.1"
}
}
@@ -54,7 +54,7 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
}
}
if (exampleWrapper && exampleWrapper.offsetHeight) {
if (exampleWrapper?.offsetHeight) {
const newHeight = iframePadding + exampleWrapper.offsetHeight;
clearInterval(waitForExampleContentToRender);
setFrameState(Math.min(Math.max(newHeight, 300), 900));
@@ -16,16 +16,13 @@ const PropsSeksjon = ({ node }: PropsSeksjonProps) => {
return (
<div className="mb-16">
{node?.komponenter?.length > 0 && (
<>
{node.komponenter.map((prop) => (
<PropTableV2
komponent={prop as unknown as PropTableT}
key={prop?._key}
/>
))}
</>
)}
{node?.komponenter?.length > 0 &&
node.komponenter.map((prop) => (
<PropTableV2
komponent={prop as unknown as PropTableT}
key={prop?._key}
/>
))}
</div>
);
};
@@ -34,7 +34,7 @@ type Prop = Partial<{
}>;
export const DtList = ({ prop }: { prop: Prop; parent: string }) => {
if (prop?.description && prop.description.includes("@private")) {
if (prop?.description?.includes("@private")) {
return null;
}
@@ -38,25 +38,6 @@ html:has(.globalstyles) {
scroll-margin-block: 5rem;
}
/* Improves rendering of images for chrome on Windows */
/* stylelint-disable-next-line media-feature-name-no-vendor-prefix */
@media screen and (-webkit-min-device-pixel-ratio: 0) and (min-resolution: 0.001dpcm) {
.globalstyles img {
transform: translateZ(0);
/* stylelint-disable-next-line value-no-vendor-prefix */
image-rendering: -webkit-optimize-contrast !important;
}
}
/* Unset for Safari 11+ */
@media not all and (min-resolution: 0.001dpcm) {
@supports (-webkit-appearance: none) and (stroke-color: transparent) {
.globalstyles img {
image-rendering: unset !important;
}
}
}
.skiplink {
@apply absolute left-0 -translate-y-full bg-border-focus p-4 text-text-on-inverted no-underline focus-within:translate-y-0 focus-within:shadow-[inset_0_0_0_2px_white] focus-within:outline-none;
}
@@ -155,23 +155,22 @@ export const FeedbackForm = ({ user }: { user: AuthUser }) => {
>
Send inn
</Button>
<>
{state === "error" && (
<Alert variant="error" className="mt-4" role="alert">
Noe gikk galt. Hvis feilen oppstår flere ganger eller du har lyst
til å sende tilbakemeldingen direkte finner du oss under{" "}
<Link inlineText href="https://nav-it.slack.com/archives/C7NE7A8UF">
#aksel-designsystemet
</Link>{" "}
på slack.
<BodyShort
textColor="subtle"
size="small"
className="mt-3"
>{`Feil-id: ${APIError}`}</BodyShort>
</Alert>
)}
</>
{state === "error" && (
<Alert variant="error" className="mt-4" role="alert">
Noe gikk galt. Hvis feilen oppstår flere ganger eller du har lyst til
å sende tilbakemeldingen direkte finner du oss under{" "}
<Link inlineText href="https://nav-it.slack.com/archives/C7NE7A8UF">
#aksel-designsystemet
</Link>{" "}
på slack.
<BodyShort
textColor="subtle"
size="small"
className="mt-3"
>{`Feil-id: ${APIError}`}</BodyShort>
</Alert>
)}
</form>
);
};
@@ -21,6 +21,7 @@ export const IconSidebar = ({
const wrapperRef = useRef<HTMLDivElement>(null);
const [blob, setBlob]: any = useState();
const router = useRouter();
const prevNameRef = useRef<string | undefined>();
const currentIcon = useMemo(
() => Object.values(meta).find((x) => x.name === name),
@@ -44,12 +45,13 @@ export const IconSidebar = ({
}
useEffect(() => {
if (wrapperRef.current) {
if (prevNameRef.current !== name && wrapperRef.current) {
wrapperRef.current.focus({ preventScroll: true });
}
prevNameRef.current = name;
}, [name]);
const logDownload = (icon, format) => {
const logDownload = (icon: string, format: "svg") => {
amplitude.track(AmplitudeEvents.ikonnedlastning, {
icon,
format,
@@ -42,7 +42,8 @@ export function MenuUl({ children, id, className }: MenuListProps) {
<ul
className={cl(styles.menuList, className)}
id={id}
role="list" // WebKit browsers remove list semantics when list-style-type is none
// biome-ignore lint/a11y/noRedundantRoles: WebKit browsers remove list semantics when list-style-type is none
role="list"
>
{children}
</ul>
+14 -13
View File
@@ -13,22 +13,23 @@
"cp:static": "copyfiles -E -u 1 '.next/static/**' .next/standalone/aksel.nav.no/website/.next/",
"cp:public": "copyfiles -E 'public/**' .next/standalone/aksel.nav.no/website/",
"cp:assets": "yarn cp:playroom && yarn cp:public && yarn cp:static",
"update:searchindex": "ts-node --project tsconfig.json ./scripts/create-index",
"update:searchindex": "yarn tsx --env-file=.env ./scripts/create-index",
"e2e": "yarn build && yarn start-server-and-test 'yarn start' http://localhost:3000 'yarn playwright'",
"e2e:full": "yarn build && ts-node ./e2e/genUrls.ts && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"e2e:ci-full": "ts-node ./e2e/genUrls.ts && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"update:rss": "ts-node --project tsconfig.tsnode.json ./scripts/generate-rss-feed",
"update:props": "ts-node --project tsconfig.tsnode.json ./scripts/update-props",
"update:tokens": "ts-node --project tsconfig.tsnode.json ./scripts/update-tokens",
"update:eksempler": "ts-node --project tsconfig.tsnode.json ./scripts/update-examples/update-sanity",
"validate:eksempler": "ts-node --project tsconfig.tsnode.json ./scripts/update-examples/validate",
"update:changelog": "ts-node --project tsconfig.tsnode.json ./scripts/changelog",
"e2e:full": "yarn build && yarn generate-testpaths && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"e2e:ci-full": "yarn generate-testpaths && FULL_TEST=true yarn start-server-and-test start http://localhost:3000 playwright",
"update:rss": "yarn tsx --env-file=.env ./scripts/generate-rss-feed",
"update:props": "yarn tsx --env-file=.env ./scripts/update-props",
"update:tokens": "yarn tsx --env-file=.env ./scripts/update-tokens",
"update:eksempler": "yarn tsx --env-file=.env ./scripts/update-examples/update-sanity",
"validate:eksempler": "yarn tsx ./scripts/update-examples/validate",
"update:changelog": "yarn tsx --env-file=.env ./scripts/changelog",
"update:sanity": "yarn update:tokens && yarn update:props && yarn update:eksempler && yarn update:changelog",
"analyze": "cross-env ANALYZE=true yarn build",
"test": "vitest run -c config/vitest.config.ts",
"test:watch": "vitest watch -c config/vitest.config.ts",
"playwright": "playwright test",
"backup": "ts-node scripts/backup.js",
"backup": "yarn tsx --env-file=.env scripts/backup.ts",
"generate-testpaths": "yarn tsx --env-file=.env ./scripts/generate-testpaths.ts",
"watch:tw": "yarn tailwindcss -i ./components/styles/index.css -o ./dist/tw.css --watch"
},
"dependencies": {
@@ -67,6 +68,7 @@
"react-rx": "^2.1.3",
"remark-gfm": "^4.0.0",
"sanity": "3.59.0",
"sanity-plugin-iframe-pane": "^3.1.6",
"sanity-plugin-media": "^2.3.2",
"sharp": "0.32.6",
"styled-components": "^6.0.0",
@@ -86,11 +88,10 @@
"@types/jscodeshift": "^0.11.11",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"autoprefixer": "^10.4.0",
"autoprefixer": "^10.4.20",
"babel-loader": "^8.2.0",
"copyfiles": "^2.4.1",
"cross-env": "^7.0.0",
"dotenv": "^10.0.0",
"jsdom": "^22.1.0",
"json5": "2.2.1",
"pino-pretty": "^10.0.0",
@@ -100,7 +101,7 @@
"rss": "^1.2.2",
"showdown": "^2.1.0",
"start-server-and-test": "^2.0.3",
"ts-node": "^10.9.1",
"tsx": "^4.19.1",
"typescript": "^5.1.6",
"vitest": "^1.2.2"
}
@@ -7,7 +7,9 @@ const Example = () => {
const [hasError, setHasError] = useState(false);
useEffect(() => {
errorRef.current && errorRef.current.focus();
if (hasError && errorRef.current) {
errorRef.current.focus();
}
}, [hasError]);
return (
@@ -3,8 +3,8 @@ import {
FileObject,
FileRejected,
FileRejectionReason,
FileUpload,
Heading,
UNSAFE_FileUpload,
VStack,
} from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
@@ -21,7 +21,7 @@ const Example = () => {
return (
<VStack gap="6">
<UNSAFE_FileUpload.Dropzone
<FileUpload.Dropzone
label="Last opp filer til søknaden"
description={`Du kan laste opp Word- og PDF-filer. Maks 3 filer. Maks størrelse ${MAX_SIZE_MB} MB.`}
accept=".doc,.docx,.pdf"
@@ -37,7 +37,7 @@ const Example = () => {
</Heading>
<VStack as="ul" gap="3">
{acceptedFiles.map((file, index) => (
<UNSAFE_FileUpload.Item
<FileUpload.Item
as="li"
key={index}
file={file.file}
@@ -57,7 +57,7 @@ const Example = () => {
</Heading>
<VStack as="ul" gap="3">
{rejectedFiles.map((rejected, index) => (
<UNSAFE_FileUpload.Item
<FileUpload.Item
as="li"
key={index}
file={rejected.file}
@@ -1,15 +1,15 @@
import { UNSAFE_FileUpload, VStack } from "@navikt/ds-react";
import { FileUpload, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => (
<VStack gap="12">
<UNSAFE_FileUpload.Dropzone
<FileUpload.Dropzone
label="Last opp filer"
onSelect={console.info}
disabled
/>
<UNSAFE_FileUpload.Dropzone
<FileUpload.Dropzone
label="Last opp filer"
onSelect={console.info}
fileLimit={{ max: 1, current: 1 }}
@@ -1,15 +1,15 @@
import { UNSAFE_FileUpload, VStack } from "@navikt/ds-react";
import { FileUpload, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => (
<VStack gap="12">
<UNSAFE_FileUpload.Dropzone
<FileUpload.Dropzone
label="Last opp filer"
onSelect={console.info}
error="Du må laste opp en fil"
/>
<UNSAFE_FileUpload.Item
<FileUpload.Item
file={{ name: "eksempel.png", size: 200000 }}
error="Filformatet støttes ikke"
/>
@@ -1,11 +1,11 @@
import { Heading, UNSAFE_FileUpload, VStack } from "@navikt/ds-react";
import { FileUpload, Heading, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => (
<VStack gap="6">
<div>
<Heading size="small">retry</Heading>
<UNSAFE_FileUpload.Item
<FileUpload.Item
file={new File(["abc".repeat(100000)], "eksempel1.pdf")}
button={{
action: "retry",
@@ -15,7 +15,7 @@ const Example = () => (
</div>
<div>
<Heading size="small">delete</Heading>
<UNSAFE_FileUpload.Item
<FileUpload.Item
file={{ name: "eksempel2.pdf", size: 200000 }}
href="#"
button={{
@@ -1,5 +1,5 @@
import { useState } from "react";
import { FileObject, UNSAFE_FileUpload, VStack } from "@navikt/ds-react";
import { FileObject, FileUpload, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => {
@@ -7,14 +7,14 @@ const Example = () => {
return (
<VStack gap="6">
<UNSAFE_FileUpload.Dropzone
<FileUpload.Dropzone
label="Last opp fødselsattest"
fileLimit={{ max: 1, current: files.length }}
multiple={false}
onSelect={setFiles}
/>
{files.map((file) => (
<UNSAFE_FileUpload.Item
<FileUpload.Item
key={file.file.name}
file={file.file}
button={{
@@ -1,8 +1,8 @@
import { Heading, UNSAFE_FileUpload, VStack } from "@navikt/ds-react";
import { FileUpload, Heading, VStack } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => (
<UNSAFE_FileUpload
<FileUpload
translations={{
dropzone: {
dragAndDropMultiple: "Dra og slipp bilder i format .png",
@@ -14,21 +14,18 @@ const Example = () => (
}}
>
<VStack gap="6">
<UNSAFE_FileUpload.Dropzone
label="Last opp bilder"
onSelect={console.info}
/>
<FileUpload.Dropzone label="Last opp bilder" onSelect={console.info} />
<div>
<Heading level="3" size="xsmall" spacing>
Vedlegg
</Heading>
<VStack as="ul" gap="2">
<UNSAFE_FileUpload.Item
<FileUpload.Item
as="li"
file={{ name: "eksempel.png", size: 200000 }}
status="uploading"
/>
<UNSAFE_FileUpload.Item
<FileUpload.Item
as="li"
file={{ name: "eksempel.png", size: 200000 }}
status="uploading"
@@ -37,7 +34,7 @@ const Example = () => (
</VStack>
</div>
</VStack>
</UNSAFE_FileUpload>
</FileUpload>
);
// EXAMPLES DO NOT INCLUDE CONTENT BELOW THIS LINE
@@ -1,14 +1,14 @@
import { UploadIcon } from "@navikt/aksel-icons";
import { Button, UNSAFE_FileUpload } from "@navikt/ds-react";
import { Button, FileUpload } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";
const Example = () => {
return (
<UNSAFE_FileUpload.Trigger multiple={false} onSelect={console.info}>
<FileUpload.Trigger multiple={false} onSelect={console.info}>
<Button variant="secondary" icon={<UploadIcon aria-hidden />}>
Last opp filer
</Button>
</UNSAFE_FileUpload.Trigger>
</FileUpload.Trigger>
);
};
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more