mirror of
https://github.com/navikt/aksel.git
synced 2026-11-05 12:24:06 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8397677049 | ||
|
|
2bbf17e8b0 |
No files matched your search
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
"@navikt/aksel-icons": patch
|
||||
---
|
||||
|
||||
ESM: Mark ESM output as side-effect free, so bundlers like webpack, Turbopack and esbuild can tree-shake unused components and icons.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
---
|
||||
|
||||
Date utilities: Import date-fns functions and locales from their own paths, so unbundled Node imports (SSR, Vitest/Jest) no longer load every date-fns locale.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
---
|
||||
|
||||
DataGrid.Table: Keyboard navigation no longer rebuilds the internal grid map on every arrow key press, improving responsiveness in large tables.
|
||||
@@ -61,14 +61,8 @@ runs:
|
||||
SANITY_AUTH_TOKEN: ${{ inputs.sanity_deploy_studio }}
|
||||
shell: bash
|
||||
|
||||
- name: Generate RSS feed
|
||||
run: yarn workspace website update:rss
|
||||
env:
|
||||
SANITY_READ_NO_DRAFTS: ${{ inputs.sanity_read_no_drafts_token }}
|
||||
shell: bash
|
||||
|
||||
- name: Build Next.js website
|
||||
run: yarn workspace website build:with-playroom
|
||||
run: yarn build:next
|
||||
env:
|
||||
SANITY_READ: ${{ inputs.sanity_read_token }}
|
||||
SANITY_READ_NO_DRAFTS: ${{ inputs.sanity_read_no_drafts_token }}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
## Repo summary
|
||||
|
||||
- Nav's design system. Yarn 4 workspaces monorepo: published packages under `@navikt/` + docs site `aksel.nav.no/` + `apps/` + `tooling/`.
|
||||
- Stack: TypeScript 6, React 19 (dev), Next.js 16, Storybook 10, Vitest 4, Oxlint 1 + ESLint 9, Stylelint 17, Biome 2 (lint only), Prettier 3 (formatting). Node 22+, Yarn 4.18.0.
|
||||
- Stack: TypeScript 6, React 19 (dev), Next.js 16, Storybook 10, Vitest 4, ESLint 9, Stylelint 17, Biome 2 (lint only), Prettier 3 (formatting). Node 22+, Yarn 4.18.0.
|
||||
- Published `@navikt/*` packages must stay React 17 compatible (peer `react ^17 || ^18 || ^19`).
|
||||
|
||||
## Start and validate
|
||||
@@ -44,12 +44,12 @@
|
||||
|
||||
## Commands
|
||||
|
||||
- `corepack yarn oxlint --deny-warnings <files...>` / `corepack yarn eslint --max-warnings=0 --no-warn-ignored <files...>` (both: oxlint runs most rules, ESLint the rest) / `corepack yarn stylelint <files...>` / `corepack yarn biome lint --no-errors-on-unmatched <files...>`
|
||||
- `corepack yarn eslint --max-warnings=0 --no-warn-ignored <files...>` / `corepack yarn stylelint <files...>` / `corepack yarn biome lint --no-errors-on-unmatched <files...>`
|
||||
- `corepack yarn tsc --noEmit --incremental false --project <tsconfig>` - targeted type-check
|
||||
- `corepack yarn workspace <name> <script>` - e.g. `@navikt/ds-react test` (Vitest + tsc), `website test`
|
||||
- `corepack yarn lint` / `corepack yarn test` / `corepack yarn boot` - root suites (slow; cross-workspace changes only)
|
||||
- `corepack yarn storybook` (:6006) / `corepack yarn storybook:aksel` (website examples, :6007, no secrets) / `corepack yarn storybook:test` (play tests, Playwright Firefox; Chromium when `COPILOT_CLI` is set) / `corepack yarn dev` (website, :3000, needs Sanity token)
|
||||
- Targeted play tests: `corepack yarn storybook:test <path/to/X.tests.stories.tsx>` (repo root). Run after changing play tests. Under Copilot CLI this runs Chromium; if the browser can't launch, report tests as not run with the exact command.
|
||||
- Targeted play tests: `corepack yarn vitest run --config ./vitest.config.ts --project=storybook <path/to/X.tests.stories.tsx>` (repo root). Run after changing play tests. Under Copilot CLI this runs Chromium; if the browser can't launch, report tests as not run with the exact command.
|
||||
|
||||
## Where to edit
|
||||
|
||||
@@ -63,7 +63,7 @@ Path-specific rules load automatically from `.github/instructions/` (see each `a
|
||||
- `@navikt/aksel/src/` - CLI and codemods
|
||||
- `aksel.nav.no/website/` - Next.js docs site · `aksel.nav.no/sanity-studio/` - CMS schema
|
||||
- `apps/playroom/`, `apps/mcp-server/`, `apps/figma-icon-plugin/` - apps · `tooling/` - ESLint plugin, scripts, changelog, analyzer
|
||||
- `.changeset/` - release notes · Root config: `.oxlintrc.json`, `eslint.config.js`, `stylelint.config.mjs`, `biome.json`, `vitest.config.mts`, `.storybook/`
|
||||
- `.changeset/` - release notes · Root config: `eslint.config.js`, `stylelint.config.mjs`, `biome.json`, `vitest.config.ts`, `.storybook/`
|
||||
|
||||
## Skills and agents
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ updates:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "@next/eslint-plugin-next"
|
||||
- "@next/bundle-analyzer"
|
||||
- "next"
|
||||
sanity:
|
||||
applies-to: version-updates
|
||||
@@ -52,6 +53,7 @@ updates:
|
||||
applies-to: security-updates
|
||||
patterns:
|
||||
- "@next/eslint-plugin-next"
|
||||
- "@next/bundle-analyzer"
|
||||
- "next"
|
||||
sanity-security:
|
||||
applies-to: security-updates
|
||||
|
||||
@@ -6,7 +6,7 @@ applyTo: "**/*.test.ts,**/*.test.tsx,**/*.stories.tsx"
|
||||
# Tests and stories
|
||||
|
||||
- User flows: use a story `play` function in `<Component>.tests.stories.tsx`. Pure logic and hooks: use Vitest `*.test.ts(x)` next to the source file.
|
||||
- `corepack yarn storybook:test` runs every story that has a `play` function (Storybook auto-tags them `play-fn`, see root `vitest.config.mts`). To opt out, use `tags: ["skip-test"]` with a reason.
|
||||
- `corepack yarn storybook:test` runs every story that has a `play` function (Storybook auto-tags them `play-fn`, see root `vitest.config.ts`). To opt out, use `tags: ["!play-fn", "skip-test"]` with a reason.
|
||||
- Play-test files set `parameters: { chromatic: { disable: true } }`. Visual stories feed Chromatic; use `renderStoriesForChromatic` from `utils/` when adding visual variants.
|
||||
- Import from `@storybook/react-vite` and `storybook/test`, not `@storybook/react` or `@storybook/test`.
|
||||
- Query by role and accessible name (`getByRole("button", { name })`). Avoid snapshots unless nearby tests already use them.
|
||||
@@ -17,4 +17,4 @@ applyTo: "**/*.test.ts,**/*.test.tsx,**/*.stories.tsx"
|
||||
- Before removing or consolidating tests, map each meaningful old scenario/assertion to its replacement story or test. Keep coverage that still represents a supported behavior; explain when a scenario is intentionally obsolete.
|
||||
- For state, accessibility, keyboard or focus behavior, use the [component-contract skill](../skills/component-contract/SKILL.md) to identify the contract and evidence to preserve.
|
||||
- ds-react unit tests run with `TZ=UTC`. Date logic must not depend on the local timezone.
|
||||
- Browser tests need Playwright Firefox (Chromium under Copilot CLI, see `vitest.config.mts`). Yarn has `enableScripts: false`, so if the browser is missing, report it instead of installing globally.
|
||||
- Browser tests need Playwright Firefox (Chromium under Copilot CLI, see `vitest.config.ts`). Yarn has `enableScripts: false`, so if the browser is missing, report it instead of installing globally.
|
||||
@@ -57,7 +57,6 @@ Read the current manifest before running a table command. A manifest is source o
|
||||
Group only matching files:
|
||||
|
||||
```sh
|
||||
corepack yarn oxlint --deny-warnings <changed .ts/.tsx/.js/.jsx files>
|
||||
corepack yarn eslint --max-warnings=0 --no-warn-ignored <changed .ts/.tsx/.js/.jsx files>
|
||||
corepack yarn stylelint <changed .css files>
|
||||
corepack yarn biome lint --no-errors-on-unmatched <changed supported source/config files>
|
||||
|
||||
@@ -54,7 +54,6 @@ Hard rules (enforced by `tooling/eslint` rules `aksel-local/*`):
|
||||
## 3. Validate
|
||||
|
||||
```sh
|
||||
corepack yarn oxlint --deny-warnings <file>
|
||||
corepack yarn eslint --max-warnings=0 --no-warn-ignored <file>
|
||||
corepack yarn workspace website validate:eksempler
|
||||
corepack yarn storybook:aksel # visual check at :6007, needs no secrets
|
||||
|
||||
@@ -17,7 +17,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (Oxlint, ESLint, Stylelint etc.)
|
||||
name: Lint (ESLint, Stylelint etc.)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
-467
@@ -1,467 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["react", "import"],
|
||||
"categories": {
|
||||
"correctness": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "19.3.1"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"builtin": true,
|
||||
"es2026": true,
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"**/public",
|
||||
"@navikt/aksel/**/*.input.*",
|
||||
"@navikt/aksel/**/*.output.*",
|
||||
"**/codemod/**/*.js",
|
||||
"**/tokens/**/plugin.js",
|
||||
"**/query-types.ts",
|
||||
".yarn",
|
||||
"**/next-env.d.ts",
|
||||
"@navikt/aksel-icons/src/**",
|
||||
"@navikt/aksel/src/version.ts",
|
||||
"@navikt/core/tailwind/{src/tokens.ts,tailwind.config.js,tailwind.darkside.config.js}",
|
||||
"@navikt/core/tokens/token_docs.js",
|
||||
"apps/playroom/{playroom.config.js,src/auto-generated-imports.ts}",
|
||||
"**/.build",
|
||||
"**/storybook-static",
|
||||
"**/test-results",
|
||||
"tooling/analyzer/temp",
|
||||
"apps/figma-icon-plugin/src/ui/vite-env.d.ts"
|
||||
],
|
||||
"rules": {
|
||||
"constructor-super": "error",
|
||||
"for-direction": "error",
|
||||
"getter-return": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-case-declarations": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-compare-neg-zero": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-constant-binary-expression": "error",
|
||||
"no-constant-condition": "error",
|
||||
"no-control-regex": "error",
|
||||
"no-debugger": "error",
|
||||
"no-delete-var": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-dupe-else-if": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-empty": "error",
|
||||
"no-empty-character-class": "error",
|
||||
"no-empty-pattern": "error",
|
||||
"no-empty-static-block": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-global-assign": "error",
|
||||
"no-import-assign": "error",
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-loss-of-precision": "error",
|
||||
"no-misleading-character-class": "error",
|
||||
"no-new-native-nonconstructor": "error",
|
||||
"no-nonoctal-decimal-escape": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-prototype-builtins": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-setter-return": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-unassigned-vars": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unsafe-negation": "error",
|
||||
"no-unsafe-optional-chaining": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-unused-private-class-members": "error",
|
||||
"no-unused-vars": "error",
|
||||
"no-useless-backreference": "error",
|
||||
"no-useless-catch": "error",
|
||||
"no-useless-escape": "error",
|
||||
"no-with": "error",
|
||||
"preserve-caught-error": "error",
|
||||
"require-yield": "error",
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "error",
|
||||
"react/jsx-key": "error",
|
||||
"react/jsx-no-comment-textnodes": "error",
|
||||
"react/jsx-no-duplicate-props": "error",
|
||||
"react/jsx-no-target-blank": "error",
|
||||
"react/jsx-no-undef": "error",
|
||||
"react/no-children-prop": "error",
|
||||
"react/no-danger-with-children": "error",
|
||||
"react/no-direct-mutation-state": "error",
|
||||
"react/no-find-dom-node": "error",
|
||||
"react/no-is-mounted": "error",
|
||||
"react/no-render-return-value": "error",
|
||||
"react/no-string-refs": "error",
|
||||
"react/no-unescaped-entities": "error",
|
||||
"react/no-unknown-property": "error",
|
||||
"react/react-in-jsx-scope": "error",
|
||||
"import/default": "error",
|
||||
"import/no-named-as-default-member": "warn",
|
||||
"import/no-duplicates": "warn",
|
||||
"react/jsx-curly-brace-presence": [
|
||||
"error",
|
||||
{
|
||||
"propElementValues": "always"
|
||||
}
|
||||
],
|
||||
"react/rules-of-hooks": "error",
|
||||
"react/exhaustive-deps": [
|
||||
"warn",
|
||||
{
|
||||
"additionalHooks": "(useClientLayoutEffect)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["aksel.nav.no/sanity-studio/**"],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/*.stories.{ts,tsx,js,jsx,mjs,cjs}",
|
||||
"**/*.story.{ts,tsx,js,jsx,mjs,cjs}"
|
||||
],
|
||||
"rules": {
|
||||
"storybook/await-interactions": "error",
|
||||
"storybook/context-in-play-function": "error",
|
||||
"storybook/default-exports": "error",
|
||||
"storybook/hierarchy-separator": "warn",
|
||||
"storybook/no-redundant-story-name": "warn",
|
||||
"storybook/no-renderer-packages": "error",
|
||||
"storybook/prefer-pascal-case": "warn",
|
||||
"storybook/story-exports": "error",
|
||||
"storybook/use-storybook-expect": "error",
|
||||
"storybook/use-storybook-testing-library": "error",
|
||||
"import/no-anonymous-default-export": "off",
|
||||
"react/rules-of-hooks": "off"
|
||||
},
|
||||
"jsPlugins": ["eslint-plugin-storybook"]
|
||||
},
|
||||
{
|
||||
"files": ["**/.storybook/main.{js,cjs,mjs,ts}"],
|
||||
"rules": {
|
||||
"storybook/no-uninstalled-addons": "error"
|
||||
},
|
||||
"jsPlugins": ["eslint-plugin-storybook"]
|
||||
},
|
||||
{
|
||||
"files": ["**/*.{ts,tsx}"],
|
||||
"rules": {
|
||||
"constructor-super": "off",
|
||||
"getter-return": "off",
|
||||
"no-class-assign": "off",
|
||||
"no-const-assign": "off",
|
||||
"no-dupe-class-members": "off",
|
||||
"no-dupe-keys": "off",
|
||||
"no-func-assign": "off",
|
||||
"no-import-assign": "off",
|
||||
"no-new-native-nonconstructor": "off",
|
||||
"no-obj-calls": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-setter-return": "off",
|
||||
"no-this-before-super": "off",
|
||||
"no-unreachable": "off",
|
||||
"no-unsafe-negation": "off",
|
||||
"no-with": "off",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-unused-expressions": [
|
||||
"error",
|
||||
{
|
||||
"allowShortCircuit": true,
|
||||
"allowTernary": true
|
||||
}
|
||||
],
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/no-duplicate-enum-values": "error",
|
||||
"typescript/no-empty-object-type": "error",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-extra-non-null-assertion": "error",
|
||||
"typescript/no-misused-new": "error",
|
||||
"typescript/no-namespace": "error",
|
||||
"typescript/no-non-null-asserted-optional-chain": "error",
|
||||
"typescript/no-require-imports": "error",
|
||||
"typescript/no-this-alias": "error",
|
||||
"typescript/no-unnecessary-type-constraint": "error",
|
||||
"typescript/no-unsafe-declaration-merging": "error",
|
||||
"typescript/no-unsafe-function-type": "error",
|
||||
"typescript/no-wrapper-object-types": "error",
|
||||
"typescript/prefer-as-const": "error",
|
||||
"typescript/prefer-namespace-keyword": "error",
|
||||
"typescript/triple-slash-reference": "error",
|
||||
"import/default": "off",
|
||||
"import/no-named-as-default-member": "off"
|
||||
},
|
||||
"plugins": ["typescript"]
|
||||
},
|
||||
{
|
||||
"files": ["**/*.test.*", "**/__tests__/*"],
|
||||
"rules": {
|
||||
"testing-library/await-async-events": [
|
||||
"error",
|
||||
{
|
||||
"eventModule": "userEvent"
|
||||
}
|
||||
],
|
||||
"testing-library/await-async-queries": "error",
|
||||
"testing-library/await-async-utils": "error",
|
||||
"testing-library/no-await-sync-events": [
|
||||
"error",
|
||||
{
|
||||
"eventModules": ["fire-event"]
|
||||
}
|
||||
],
|
||||
"testing-library/no-await-sync-queries": "error",
|
||||
"testing-library/no-container": "error",
|
||||
"testing-library/no-debugging-utils": "warn",
|
||||
"testing-library/no-dom-import": ["error", "react"],
|
||||
"testing-library/no-global-regexp-flag-in-query": "error",
|
||||
"testing-library/no-manual-cleanup": "error",
|
||||
"testing-library/no-node-access": "error",
|
||||
"testing-library/no-promise-in-fire-event": "error",
|
||||
"testing-library/no-render-in-lifecycle": "error",
|
||||
"testing-library/no-unnecessary-act": "error",
|
||||
"testing-library/no-wait-for-multiple-assertions": "error",
|
||||
"testing-library/no-wait-for-side-effects": "error",
|
||||
"testing-library/no-wait-for-snapshot": "error",
|
||||
"testing-library/prefer-find-by": "error",
|
||||
"testing-library/prefer-presence-queries": "error",
|
||||
"testing-library/prefer-query-by-disappearance": "error",
|
||||
"testing-library/prefer-screen-queries": "error",
|
||||
"testing-library/render-result-naming-convention": "error",
|
||||
"vitest/expect-expect": "error",
|
||||
"vitest/no-commented-out-tests": "error",
|
||||
"vitest/no-disabled-tests": "warn",
|
||||
"vitest/no-focused-tests": "error",
|
||||
"vitest/no-identical-title": "error",
|
||||
"vitest/no-import-node-test": "error",
|
||||
"vitest/no-interpolation-in-snapshots": "error",
|
||||
"vitest/no-mocks-import": "error",
|
||||
"vitest/no-standalone-expect": "error",
|
||||
"vitest/no-unneeded-async-expect-function": "error",
|
||||
"vitest/prefer-called-exactly-once-with": "error",
|
||||
"vitest/require-local-test-context-for-concurrent-snapshots": "error",
|
||||
"vitest/valid-describe-callback": "error",
|
||||
"vitest/valid-expect": "error",
|
||||
"vitest/valid-expect-in-promise": "error",
|
||||
"vitest/valid-title": "error"
|
||||
},
|
||||
"jsPlugins": ["eslint-plugin-testing-library"],
|
||||
"plugins": ["vitest"]
|
||||
},
|
||||
{
|
||||
"files": ["aksel.nav.no/website/**"],
|
||||
"rules": {
|
||||
"react/no-unknown-property": [
|
||||
"error",
|
||||
{
|
||||
"ignore": ["jsx", "global"]
|
||||
}
|
||||
],
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"nextjs/google-font-display": "warn",
|
||||
"nextjs/google-font-preconnect": "warn",
|
||||
"nextjs/next-script-for-ga": "warn",
|
||||
"nextjs/no-async-client-component": "warn",
|
||||
"nextjs/no-before-interactive-script-outside-document": "warn",
|
||||
"nextjs/no-css-tags": "warn",
|
||||
"nextjs/no-head-element": "warn",
|
||||
"nextjs/no-img-element": "warn",
|
||||
"nextjs/no-page-custom-font": "warn",
|
||||
"nextjs/no-styled-jsx-in-document": "warn",
|
||||
"nextjs/no-sync-scripts": "warn",
|
||||
"nextjs/no-title-in-document-head": "warn",
|
||||
"nextjs/no-typos": "warn",
|
||||
"nextjs/no-unwanted-polyfillio": "warn",
|
||||
"nextjs/inline-script-id": "error",
|
||||
"nextjs/no-assign-module-variable": "error",
|
||||
"nextjs/no-document-import-in-page": "error",
|
||||
"nextjs/no-duplicate-head": "error",
|
||||
"nextjs/no-head-import-in-document": "error",
|
||||
"nextjs/no-script-component-in-head": "error"
|
||||
},
|
||||
"plugins": ["nextjs"]
|
||||
},
|
||||
{
|
||||
"files": ["aksel.nav.no/website/pages/eksempler/**"],
|
||||
"rules": {
|
||||
"nextjs/no-img-element": "off"
|
||||
},
|
||||
"plugins": ["nextjs"]
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"aksel.nav.no/website/pages/eksempler/**/*.tsx",
|
||||
"aksel.nav.no/website/pages/templates/**/*.tsx"
|
||||
],
|
||||
"rules": {
|
||||
"aksel-local/args-check": ["error"],
|
||||
"aksel-local/comment-check": ["error"]
|
||||
},
|
||||
"jsPlugins": ["eslint-plugin-aksel-local"]
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/examples/__parts*/*.tsx",
|
||||
"**/pages/templates/**/*.tsx",
|
||||
"**/pages/eksempler/**/*.tsx"
|
||||
],
|
||||
"rules": {
|
||||
"aksel-local/import-check": ["error"]
|
||||
},
|
||||
"jsPlugins": ["eslint-plugin-aksel-local"]
|
||||
},
|
||||
{
|
||||
"files": ["**/examples/__parts-inline/*.tsx"],
|
||||
"rules": {
|
||||
"arrow-body-style": ["error", "never"],
|
||||
"func-style": ["error", "expression"],
|
||||
"import/no-named-export": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["@navikt/**"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "react",
|
||||
"importNames": [
|
||||
"useId",
|
||||
"useInsertionEffect",
|
||||
"useSyncExternalStore",
|
||||
"useDeferredValue",
|
||||
"useTransition",
|
||||
"startTransition"
|
||||
],
|
||||
"message": "React 18+ API not allowed. We currently only support React-features accesible in React 17. To add new features, we will need to update peerDependencies (breaking change)."
|
||||
},
|
||||
{
|
||||
"name": "react",
|
||||
"importNames": [
|
||||
"useOptimistic",
|
||||
"useActionState",
|
||||
"use",
|
||||
"cache",
|
||||
"useEffectEvent",
|
||||
"ViewTransition",
|
||||
"addTransitionType"
|
||||
],
|
||||
"message": "React 19+ API not allowed. We currently only support React-features accesible in React 17. To add new features, we will need to update peerDependencies (breaking change)."
|
||||
},
|
||||
{
|
||||
"name": "react-dom/client",
|
||||
"importNames": ["createRoot", "hydrateRoot"],
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"name": "react-dom",
|
||||
"importNames": ["browser"],
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
}
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["fs", "path"],
|
||||
"message": "Use `node:` prefix when importing native node modules, e.g. `import path from \"node:path\"`."
|
||||
},
|
||||
{
|
||||
"group": ["**/esm/**", "**/cjs/**"],
|
||||
"message": "Do not import from build output directories. Import from source instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-restricted-properties": [
|
||||
"error",
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useId",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useInsertionEffect",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useSyncExternalStore",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useDeferredValue",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useTransition",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "startTransition",
|
||||
"message": "React 18+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useOptimistic",
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useActionState",
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "use",
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "cache",
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
},
|
||||
{
|
||||
"object": "React",
|
||||
"property": "useEffectEvent",
|
||||
"message": "React 19+ API not allowed (targeting React 17)."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["@navikt/**/*.stories.tsx"],
|
||||
"rules": {
|
||||
"no-restricted-imports": "off",
|
||||
"no-restricted-properties": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
-1
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"oxc.oxc-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"biomejs.biome",
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Icons: New icons `Gift`, `ArrowsPointingIn` and `ArrowsPointingOut`. ([#5484](https://github.com/navikt/aksel/pull/5484))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ESM: Mark ESM output as side-effect free, so bundlers like webpack, Turbopack and esbuild can tree-shake unused components and icons. ([#5472](https://github.com/navikt/aksel/pull/5472))
|
||||
|
||||
## 8.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0001 13.2501C10.4142 13.2501 10.7501 13.5859 10.7501 14.0001V18.0001C10.75 18.4142 10.4142 18.75 10.0001 18.7501C9.58586 18.7501 9.25009 18.4142 9.25006 18.0001V15.8106L5.03033 20.0303C4.73746 20.3232 4.26268 20.3232 3.96978 20.0303C3.67689 19.7374 3.67689 19.2627 3.96978 18.9698L8.18951 14.7501H6.00006C5.58586 14.7501 5.25009 14.4142 5.25006 14.0001C5.25006 13.5858 5.58584 13.2501 6.00006 13.2501H10.0001ZM18.0001 13.2501C18.4142 13.2501 18.7501 13.5859 18.7501 14.0001C18.75 14.4142 18.4142 14.75 18.0001 14.7501H15.8106L20.0303 18.9698C20.3232 19.2627 20.3232 19.7375 20.0303 20.0303C19.7375 20.3232 19.2627 20.3232 18.9698 20.0303L14.7501 15.8106V18.0001C14.75 18.4142 14.4142 18.75 14.0001 18.7501C13.5859 18.7501 13.2501 18.4142 13.2501 18.0001V14.0001C13.2501 13.8011 13.3291 13.6104 13.4698 13.4698C13.6104 13.3291 13.8011 13.2501 14.0001 13.2501H18.0001ZM3.96978 3.96978C4.26268 3.67689 4.73744 3.67689 5.03033 3.96978L9.25006 8.18951V6.00005C9.25006 5.58584 9.58584 5.25005 10.0001 5.25005C10.4142 5.25008 10.7501 5.58586 10.7501 6.00005V10.0001C10.75 10.4142 10.4142 10.75 10.0001 10.7501H6.00006C5.58586 10.7501 5.25009 10.4142 5.25006 10.0001C5.25006 9.58584 5.58584 9.25005 6.00006 9.25005H8.18951L3.96978 5.03033C3.67689 4.73743 3.67689 4.26267 3.96978 3.96978ZM18.9698 3.96978C19.2627 3.67689 19.7374 3.67689 20.0303 3.96978C20.3232 4.26268 20.3232 4.73745 20.0303 5.03033L15.8106 9.25005H18.0001C18.4142 9.25008 18.7501 9.58586 18.7501 10.0001C18.75 10.4142 18.4142 10.75 18.0001 10.7501H14.0001C13.5859 10.7501 13.2501 10.4142 13.2501 10.0001V6.00005C13.2501 5.58584 13.5858 5.25005 14.0001 5.25005C14.4142 5.25008 14.7501 5.58586 14.7501 6.00005V8.18951L18.9698 3.96978Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,12 +0,0 @@
|
||||
name: ArrowsPointingIn
|
||||
category: Arrows
|
||||
sub_category: Arrow
|
||||
keywords:
|
||||
- arrows
|
||||
- piler
|
||||
- fullscreen
|
||||
- fullskjerm
|
||||
- lukk
|
||||
- close
|
||||
variant: Stroke
|
||||
created_at: 31.10.2026
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.96973 13.9697C9.26262 13.6768 9.73738 13.6768 10.0303 13.9697C10.3232 14.2626 10.3232 14.7374 10.0303 15.0303L5.81055 19.25H8C8.41421 19.25 8.75 19.5858 8.75 20C8.75 20.4142 8.41421 20.75 8 20.75H4C3.80109 20.75 3.61038 20.6709 3.46973 20.5303C3.32907 20.3896 3.25 20.1989 3.25 20V16C3.25 15.5858 3.58579 15.25 4 15.25C4.41421 15.25 4.75 15.5858 4.75 16V18.1895L8.96973 13.9697ZM13.9697 13.9697C14.2626 13.6768 14.7374 13.6768 15.0303 13.9697L19.25 18.1895V16C19.25 15.5858 19.5858 15.25 20 15.25C20.4142 15.25 20.75 15.5858 20.75 16V20C20.75 20.1989 20.6709 20.3896 20.5303 20.5303C20.3896 20.6709 20.1989 20.75 20 20.75H16C15.5858 20.75 15.25 20.4142 15.25 20C15.25 19.5858 15.5858 19.25 16 19.25H18.1895L13.9697 15.0303C13.6768 14.7374 13.6768 14.2626 13.9697 13.9697ZM8 3.25C8.41421 3.25 8.75 3.58579 8.75 4C8.75 4.41421 8.41421 4.75 8 4.75H5.81055L10.0303 8.96973C10.3232 9.26262 10.3232 9.73738 10.0303 10.0303C9.73738 10.3232 9.26262 10.3232 8.96973 10.0303L4.75 5.81055V8C4.75 8.41421 4.41421 8.75 4 8.75C3.58579 8.75 3.25 8.41421 3.25 8V4C3.25 3.58579 3.58579 3.25 4 3.25H8ZM20 3.25C20.4142 3.25 20.75 3.58579 20.75 4V8C20.75 8.41421 20.4142 8.75 20 8.75C19.5858 8.75 19.25 8.41421 19.25 8V5.81055L15.0303 10.0303C14.7374 10.3232 14.2626 10.3232 13.9697 10.0303C13.6768 9.73738 13.6768 9.26262 13.9697 8.96973L18.1895 4.75H16C15.5858 4.75 15.25 4.41421 15.25 4C15.25 3.58579 15.5858 3.25 16 3.25H20Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,12 +0,0 @@
|
||||
name: ArrowsPointingOut
|
||||
category: Arrows
|
||||
sub_category: Arrow
|
||||
keywords:
|
||||
- arrows
|
||||
- piler
|
||||
- fullscreen
|
||||
- fullskjerm
|
||||
- open
|
||||
- åpne
|
||||
variant: Stroke
|
||||
created_at: 31.10.2026
|
||||
@@ -6,7 +6,5 @@ keywords:
|
||||
- søkeresultater
|
||||
- tabell
|
||||
- trakt
|
||||
- fjern
|
||||
- slett
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -6,7 +6,5 @@ keywords:
|
||||
- søkeresultater
|
||||
- tabell
|
||||
- trakt
|
||||
- fjern
|
||||
- slett
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 3.25C15.5188 3.25 16.75 4.48122 16.75 6C16.75 6.45058 16.6394 6.87468 16.4473 7.25H19C19.9665 7.25 20.75 8.0335 20.75 9V11C20.75 11.6977 20.3407 12.298 19.75 12.5791V19C19.75 19.9665 18.9665 20.75 18 20.75H6C5.0335 20.75 4.25 19.9665 4.25 19V12.5791C3.65928 12.298 3.25 11.6977 3.25 11V9C3.25 8.0335 4.0335 7.25 5 7.25H7.55273C7.36065 6.87468 7.25 6.45058 7.25 6C7.25 4.48122 8.48122 3.25 10 3.25C10.789 3.25 11.4985 3.58399 12 4.11621C12.5015 3.58399 13.211 3.25 14 3.25ZM5.75 19C5.75 19.1381 5.86193 19.25 6 19.25H11.25V12.75H5.75V19ZM12.75 19.25H18C18.1381 19.25 18.25 19.1381 18.25 19V12.75H12.75V19.25ZM5 8.75C4.86193 8.75 4.75 8.86193 4.75 9V11C4.75 11.1381 4.86193 11.25 5 11.25H11.25V8.75H5ZM12.75 11.25H19C19.1381 11.25 19.25 11.1381 19.25 11V9C19.25 8.86193 19.1381 8.75 19 8.75H12.75V11.25ZM10 4.75C9.30964 4.75 8.75 5.30964 8.75 6C8.75 6.69036 9.30964 7.25 10 7.25H11.25V6C11.25 5.30964 10.6904 4.75 10 4.75ZM14 4.75C13.3096 4.75 12.75 5.30964 12.75 6V7.25H14C14.6904 7.25 15.25 6.69036 15.25 6C15.25 5.30964 14.6904 4.75 14 4.75Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,8 +0,0 @@
|
||||
name: Gift
|
||||
category: Home
|
||||
sub_category: Toys
|
||||
keywords:
|
||||
- gave
|
||||
- presang
|
||||
variant: Stroke
|
||||
created_at: 31.10.2026
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.56055 13.2197C4.70421 13.2397 4.85088 13.25 5 13.25H11C11.1381 13.25 11.25 13.3619 11.25 13.5V20.5C11.25 20.6381 11.1381 20.75 11 20.75H6C5.0335 20.75 4.25 19.9665 4.25 19V13.4775C4.25 13.3162 4.40077 13.1975 4.56055 13.2197ZM19.4395 13.2197C19.5992 13.1975 19.75 13.3162 19.75 13.4775V19C19.75 19.9665 18.9665 20.75 18 20.75H13C12.8619 20.75 12.75 20.6381 12.75 20.5V13.5C12.75 13.3619 12.8619 13.25 13 13.25H19C19.1491 13.25 19.2958 13.2397 19.4395 13.2197ZM14 3.25C15.5188 3.25 16.75 4.48122 16.75 6C16.75 6.45058 16.6394 6.87468 16.4473 7.25H19C19.9665 7.25 20.75 8.0335 20.75 9V10C20.75 10.9665 19.9665 11.75 19 11.75H13C12.8619 11.75 12.75 11.6381 12.75 11.5V7.25H14C14.6904 7.25 15.25 6.69036 15.25 6C15.25 5.30964 14.6904 4.75 14 4.75C13.3096 4.75 12.75 5.30964 12.75 6V7.24707H11.25V6C11.25 5.30964 10.6904 4.75 10 4.75C9.30964 4.75 8.75 5.30964 8.75 6C8.75 6.69036 9.30964 7.25 10 7.25H11.25V11.5C11.25 11.6381 11.1381 11.75 11 11.75H5C4.0335 11.75 3.25 10.9665 3.25 10V9C3.25 8.0335 4.0335 7.25 5 7.25H7.55273C7.36065 6.87468 7.25 6.45058 7.25 6C7.25 4.48122 8.48122 3.25 10 3.25C10.789 3.25 11.4985 3.58399 12 4.11621C12.5015 3.58399 13.211 3.25 14 3.25Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,8 +0,0 @@
|
||||
name: GiftFill
|
||||
category: Home
|
||||
sub_category: Toys
|
||||
keywords:
|
||||
- gave
|
||||
- presang
|
||||
variant: Fill
|
||||
created_at: 31.10.2026
|
||||
@@ -2,6 +2,6 @@ name: Keyboard
|
||||
category: Interface
|
||||
sub_category: Devices
|
||||
keywords:
|
||||
- Tastatur
|
||||
- Keyboard
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,6 +2,6 @@ name: KeyboardFill
|
||||
category: Interface
|
||||
sub_category: Devices
|
||||
keywords:
|
||||
- Tastatur
|
||||
- KeyboardFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCellsMerge
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- celle
|
||||
- slå sammen
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableCellsMerge
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCellsMergeFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- celle
|
||||
- slå sammen
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableCellsMergeFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCellsSplit
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- celle
|
||||
- splitt
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableCellsSplit
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCellsSplitFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- celle
|
||||
- splitt
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableCellsSplitFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCross
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- slett
|
||||
- TableCross
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableCrossFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- slett
|
||||
- TableCrossFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableDeleteColumn
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- slett kolonne
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- TableDeleteColumn
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableDeleteColumnFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- slett kolonne
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- TableDeleteColumnFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableDeleteRow
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- slett rad
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- TableDeleteRow
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableDeleteRowFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- slett rad
|
||||
- tabell
|
||||
- datagrid
|
||||
- fjern
|
||||
- TableDeleteRowFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,7 +2,6 @@ name: TableGrid
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableGrid
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,7 +2,6 @@ name: TableGridFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableGridFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertColumnLeft
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny kolonne
|
||||
- venstre
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertColumnLeft
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertColumnLeftFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny kolonne
|
||||
- venstre
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertColumnLeftFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertColumnRight
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny kolonne
|
||||
- høyre
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertColumnRight
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertColumnRightFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny kolonne
|
||||
- høyre
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertColumnRightFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertRowAbove
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny rad
|
||||
- over
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertRowAbove
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertRowAboveFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny rad
|
||||
- over
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertRowAboveFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertRowBelow
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny rad
|
||||
- under
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertRowBelow
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TableInsertRowBelowFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- ny rad
|
||||
- under
|
||||
- tabell
|
||||
- datagrid
|
||||
- TableInsertRowBelowFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TablePlus
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- legg til
|
||||
- ny
|
||||
- datagrid
|
||||
- TablePlus
|
||||
variant: Stroke
|
||||
created_at: 26.10.2026
|
||||
@@ -2,9 +2,6 @@ name: TablePlusFill
|
||||
category: Interface
|
||||
sub_category: Table
|
||||
keywords:
|
||||
- tabell
|
||||
- legg til
|
||||
- ny
|
||||
- datagrid
|
||||
- TablePlusFill
|
||||
variant: Fill
|
||||
created_at: 26.10.2026
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
@@ -91,6 +91,6 @@
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"unified": "11.0.5",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -1,9 +1,5 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
## 8.20.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
@@ -43,13 +43,13 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^8.22.1",
|
||||
"@navikt/ds-tokens": "^8.22.1",
|
||||
"@navikt/ds-css": "^8.21.0",
|
||||
"@navikt/ds-tokens": "^8.21.0",
|
||||
"concurrently": "10.0.6",
|
||||
"postcss-selector-parser": "^7.1.7",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"stylelint": "^17.15.0",
|
||||
"stylelint-test-rule-node": "^1.3.0",
|
||||
"stylelint-test-rule-node": "^1.2.0",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
## 8.20.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"description": "Aksel command line interface. Codemods and other utilities for Aksel users.",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -58,7 +58,7 @@
|
||||
"@types/lodash": "4.17.25",
|
||||
"rimraf": "6.1.3",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
|
||||
File renamed without changes.
@@ -1,13 +1,5 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 8.22.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- DataGrid: Column dividers in the table header are now hidden when column dividers are disabled, and only shown on hover, focus or while resizing. ([#5505](https://github.com/navikt/aksel/pull/5505))
|
||||
|
||||
## 8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"description": "CSS for Nav Designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -32,7 +32,7 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.22.1",
|
||||
"@navikt/ds-tokens": "^8.21.0",
|
||||
"browserslist": "^4.28.7",
|
||||
"esbuild": "^0.28.2",
|
||||
"fast-glob": "3.3.3",
|
||||
|
||||
@@ -208,20 +208,11 @@
|
||||
padding: 0;
|
||||
font-size: var(--__axc-data-table-text-size);
|
||||
|
||||
.aksel-data-table__thead &,
|
||||
.aksel-data-table[data-column-dividers="true"] & {
|
||||
box-shadow: inset 1px 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
}
|
||||
|
||||
.aksel-data-table[data-column-dividers="false"] & {
|
||||
transition: box-shadow 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.aksel-data-table[data-column-dividers="false"]
|
||||
.aksel-data-table__thead:where(:hover, :has(:focus-visible, .aksel-data-table__th-resize-handle:active))
|
||||
& {
|
||||
box-shadow: inset 1px 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
}
|
||||
|
||||
&:not(.aksel-data-table__column-header, [data-sticky]) {
|
||||
overflow: hidden; /* Needed for cases where left margin is added to cell-content (nested rows) */
|
||||
}
|
||||
|
||||
@@ -1,35 +1,5 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 8.22.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.22.1
|
||||
- @navikt/aksel-icons@8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Combobox: The list is no longer focusable ([#5490](https://github.com/navikt/aksel/pull/5490))
|
||||
|
||||
- Popover: Closed popovers no longer calculate their position on mount. This makes pages with many Dropdowns or HelpTexts render faster. ([#5474](https://github.com/navikt/aksel/pull/5474))
|
||||
|
||||
- ESM: Mark ESM output as side-effect free, so bundlers like webpack, Turbopack and esbuild can tree-shake unused components and icons. ([#5472](https://github.com/navikt/aksel/pull/5472))
|
||||
|
||||
- MonthPicker: Fixed issue where navigating to a year without any enabled months could lead to re-render loop in React ([#5503](https://github.com/navikt/aksel/pull/5503))
|
||||
|
||||
- MonthPicker: Arrow-key navigation no longer freezes the page when no later or earlier month is enabled. Keyboard search now respects `fromDate`/`toDate` with both caption types and looks at most 100 years ahead or back. ([#5477](https://github.com/navikt/aksel/pull/5477))
|
||||
|
||||
- Date utilities: Import date-fns functions and locales from their own paths, so unbundled Node imports (SSR, Vitest/Jest) no longer load every date-fns locale. ([#5476](https://github.com/navikt/aksel/pull/5476))
|
||||
|
||||
- DataGrid: Fixed text selection inside details panels when keyboard navigation is enabled. ([`0bdad46`](https://github.com/navikt/aksel/commit/0bdad462664e04abd89c4121de515f6ae6d9aad1))
|
||||
|
||||
- Updated dependencies [[`16da3ce`](https://github.com/navikt/aksel/commit/16da3ce770041acc41589fcdd087717fab027126), [`b69c5fc`](https://github.com/navikt/aksel/commit/b69c5fca305f28ad99b48dd74e4f736fe4257d48)]:
|
||||
- @navikt/aksel-icons@8.22.0
|
||||
- @navikt/ds-tokens@8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"description": "React components from the Norwegian Labour and Welfare Administration.",
|
||||
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
|
||||
"license": "MIT",
|
||||
@@ -779,17 +779,17 @@
|
||||
"clean": "rimraf cjs esm",
|
||||
"build": "yarn i18n-jsdoc && yarn metadata:generate-names && concurrently \"tsc -p tsconfig.build.json --module commonjs --outDir cjs --tsBuildInfoFile cjs/.tsbuildinfo\" \"tsc -p tsconfig.build.json --module esnext --outDir esm --tsBuildInfoFile esm/.tsbuildinfo && tsc-alias -p tsconfig.build.json --dir esm -f && yarn write-packagejson\" && yarn i18n-jsdoc --cleanup && yarn copy-types",
|
||||
"watch": "tsc --watch -p tsconfig.build.json --module esnext --outDir esm --tsBuildInfoFile esm/.tsbuildinfo",
|
||||
"test": "yarn metadata:generate-names && concurrently \"cross-env TZ=UTC vitest run -c tests/vitest.config.mts\" \"yarn test-sb-build\"",
|
||||
"test:bench": "cross-env TZ=UTC vitest bench -c tests/vitest.config.mts",
|
||||
"test": "yarn metadata:generate-names && concurrently \"cross-env TZ=UTC vitest run -c tests/vitest.config.ts\" \"yarn test-sb-build\"",
|
||||
"test:bench": "cross-env TZ=UTC vitest bench -c tests/vitest.config.ts",
|
||||
"test-sb-build": "tsc -p tsconfig.json",
|
||||
"test:watch": "TZ=UTC vitest watch -c tests/vitest.config.mts",
|
||||
"test:watch": "TZ=UTC vitest watch -c tests/vitest.config.ts",
|
||||
"copy-types": "copyfiles -f ./src/types/theme.d.ts ./esm/types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.27.20",
|
||||
"@floating-ui/react-dom": "^2.1.9",
|
||||
"@navikt/aksel-icons": "^8.22.1",
|
||||
"@navikt/ds-tokens": "^8.22.1",
|
||||
"@navikt/aksel-icons": "^8.21.0",
|
||||
"@navikt/ds-tokens": "^8.21.0",
|
||||
"date-fns": "^4.0.0",
|
||||
"react-day-picker": "10.0.2"
|
||||
},
|
||||
@@ -806,7 +806,7 @@
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"jscodeshift": "17.4.0",
|
||||
"jsdom": "30.1.3",
|
||||
"jsdom": "29.1.1",
|
||||
"react": "19.3.1",
|
||||
"react-dom": "19.3.1",
|
||||
"rimraf": "6.1.3",
|
||||
@@ -814,7 +814,7 @@
|
||||
"tsc-alias": "1.9.7",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.30 || ^18 || ^19",
|
||||
|
||||
@@ -82,6 +82,8 @@ interface DataGridComponent {
|
||||
/**
|
||||
* Component for displaying tabular data.
|
||||
*
|
||||
* **WARNING: This component is in active development and may receive breaking changes outside major releases!**
|
||||
*
|
||||
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/datagrid)
|
||||
* @see 🏷️ {@link DataGridProps}
|
||||
*
|
||||
|
||||
@@ -84,9 +84,6 @@ export const Attachments: Story = {
|
||||
options: ["none", "x", "y", "both"],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
overflow: "none",
|
||||
},
|
||||
};
|
||||
|
||||
export const Chromatic = renderStoriesForChromatic({
|
||||
|
||||
@@ -12,7 +12,7 @@ function DataTableDetailsPanelRow<T>({
|
||||
rowId: string;
|
||||
rowData: T;
|
||||
}) {
|
||||
const { tableId, totalColSpan, withKeyboardNav } = useDataTableContext();
|
||||
const { tableId, totalColSpan } = useDataTableContext();
|
||||
const {
|
||||
enableDetailsPanel,
|
||||
isExpanded,
|
||||
@@ -46,7 +46,6 @@ function DataTableDetailsPanelRow<T>({
|
||||
<td
|
||||
id={expansionId}
|
||||
colSpan={totalColSpan}
|
||||
tabIndex={withKeyboardNav ? -1 : undefined}
|
||||
className="aksel-data-table__details-panel-row-cell"
|
||||
>
|
||||
<div style={style}>{content}</div>
|
||||
|
||||
@@ -5,7 +5,9 @@ import { type GridCache, buildTableGridMap } from "../helpers/table-grid-nav";
|
||||
/**
|
||||
* Manages the table grid cache and observes DOM changes that require grid rebuilding.
|
||||
* Watches for structural changes (rows/cells added/removed) and attribute changes
|
||||
* (colspan, rowspan, hidden, style) that affect grid layout and focusability.
|
||||
* (colspan, rowspan, hidden) that affect grid layout.
|
||||
* Inline `style` is intentionally not observed: focus navigation writes
|
||||
* scroll-margin on every move, and the grid never reads styles.
|
||||
*/
|
||||
function useGridCache(tableRef: HTMLTableElement | null, enabled: boolean) {
|
||||
const gridCacheRef = useRef<GridCache>({
|
||||
@@ -34,7 +36,7 @@ function useGridCache(tableRef: HTMLTableElement | null, enabled: boolean) {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
attributeFilter: ["colspan", "rowspan", "hidden", "style"],
|
||||
attributeFilter: ["colspan", "rowspan", "hidden"],
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
chromatic: { disable: true },
|
||||
},
|
||||
/* TODO: Temp disabled CI-tests */
|
||||
tags: ["skip-test"],
|
||||
tags: ["!play-fn", "skip-test"],
|
||||
} satisfies Meta<typeof DatePicker>;
|
||||
|
||||
type Story = StoryObj<typeof DatePicker>;
|
||||
@@ -104,7 +104,7 @@ export const DropdownCaptionNavigation: Story = {
|
||||
};
|
||||
|
||||
export const ControlledMonthPreservesFocus: Story = {
|
||||
tags: ["!skip-test"],
|
||||
tags: ["play-fn", "!skip-test"],
|
||||
render: () => {
|
||||
const [month, setMonth] = React.useState(new Date(2026, 2, 15));
|
||||
const [selected, setSelected] = React.useState<Date | undefined>(
|
||||
|
||||
@@ -11,7 +11,7 @@ export default {
|
||||
chromatic: { disable: false },
|
||||
},
|
||||
/* TODO: Temp disabled CI-tests */
|
||||
tags: ["skip-test"],
|
||||
tags: ["!play-fn", "skip-test"],
|
||||
} satisfies Meta<typeof UNSAFE_Combobox>;
|
||||
|
||||
type StoryObject = StoryObj<typeof UNSAFE_Combobox>;
|
||||
@@ -343,21 +343,18 @@ export const EnterNotSubmittingForm: StoryObj<{
|
||||
args: {
|
||||
onSubmit: fn(),
|
||||
},
|
||||
render: ({ onSubmit }) => (
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
onSubmit(event);
|
||||
}}
|
||||
>
|
||||
<UNSAFE_Combobox
|
||||
options={options}
|
||||
label="Hva er dine favorittfrukter?"
|
||||
isMultiSelect
|
||||
allowNewValues
|
||||
/>
|
||||
</form>
|
||||
),
|
||||
render: ({ onSubmit }) => {
|
||||
return (
|
||||
<form action="https://www.aksel.nav.no" method="get" onSubmit={onSubmit}>
|
||||
<UNSAFE_Combobox
|
||||
options={options}
|
||||
label="Hva er dine favorittfrukter?"
|
||||
isMultiSelect
|
||||
allowNewValues
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
args.onSubmit.mockClear();
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
@@ -115,7 +115,6 @@ const FilteredOptions = () => {
|
||||
ref={setFilteredOptionsRef}
|
||||
// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: The <ul> element acts as a listbox in the combobox.
|
||||
role="listbox"
|
||||
tabIndex={-1}
|
||||
className="aksel-combobox__list-options"
|
||||
>
|
||||
{isValueNew && !maxSelected.isLimitReached && allowNewValues && (
|
||||
|
||||
@@ -48,7 +48,7 @@ export const Default: Story = {
|
||||
|
||||
args: {
|
||||
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
|
||||
title: "GlobalAlert title",
|
||||
title: "GlobalAlert Title",
|
||||
size: "medium",
|
||||
status: "announcement",
|
||||
centered: true,
|
||||
|
||||
@@ -41,7 +41,7 @@ export const Default: Story = {
|
||||
|
||||
args: {
|
||||
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
|
||||
title: "InfoCard title",
|
||||
title: "InfoCard Title",
|
||||
size: "medium",
|
||||
"data-color": "info",
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@ export const Default: Story = {
|
||||
|
||||
args: {
|
||||
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
|
||||
title: "LocalAlert title",
|
||||
title: "LocalAlert Title",
|
||||
size: "medium",
|
||||
status: "announcement",
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
parameters: {
|
||||
chromatic: { disable: true },
|
||||
},
|
||||
tags: ["play-fn"],
|
||||
} satisfies Meta<typeof Lookup>;
|
||||
|
||||
type Story = StoryObj<typeof Lookup>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import React, { useState } from "react";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { Button } from "../../button";
|
||||
import { Modal } from "../index";
|
||||
|
||||
@@ -18,10 +18,6 @@ const Test = () => {
|
||||
};
|
||||
|
||||
describe("Modal", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test("should be visible", async () => {
|
||||
render(<Test />);
|
||||
expect(await screen.findByText("Foobar")).toBeVisible();
|
||||
@@ -33,37 +29,25 @@ describe("Modal", () => {
|
||||
expect(screen.getByText("Foobar")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test.each([false, true])(
|
||||
"should toggle scroll lock (scrollbar-gutter support: %s)",
|
||||
async (supportsScrollbarGutter) => {
|
||||
vi.spyOn(CSS, "supports").mockReturnValue(supportsScrollbarGutter);
|
||||
render(<Test />);
|
||||
test("should toggle scroll lock", async () => {
|
||||
render(<Test />);
|
||||
|
||||
const lockedElement = supportsScrollbarGutter
|
||||
? document.body
|
||||
: document.documentElement;
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.overflowX).toBe("hidden");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.overflowY).toBe("hidden");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.scrollBehavior).toBe("unset");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.scrollbarGutter).toBe("stable");
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lockedElement.style.overflowX).toBe("hidden");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(lockedElement.style.overflowY).toBe("hidden");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.scrollBehavior).toBe(
|
||||
supportsScrollbarGutter ? "" : "unset",
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.scrollbarGutter).toBe("stable");
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Close"));
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.cssText).toBe("");
|
||||
expect(lockedElement.style.overflowX).toBe("");
|
||||
expect(lockedElement.style.overflowY).toBe("");
|
||||
});
|
||||
},
|
||||
);
|
||||
fireEvent.click(screen.getByText("Close"));
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.cssText).toBe("");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -83,36 +83,6 @@ export const DisabledMonths = () => (
|
||||
/>
|
||||
);
|
||||
|
||||
export const EverythingDisabledBesidesOneMonth = () => (
|
||||
<div>
|
||||
<MonthPicker.Standalone
|
||||
defaultSelected={new Date(2026, 5, 1)}
|
||||
disabled={[
|
||||
(date) => !(date.getFullYear() === year && date.getMonth() === 5),
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
/**
|
||||
* Only June is enabled. ArrowRight/ArrowLeft on June keeps focus on June instead
|
||||
* of searching forever. Keyboard search stops at the
|
||||
* fromDate/toDate years, or after 100 years when no bounds are set.
|
||||
*/
|
||||
export const KeyboardNoEnabledMonth = () => (
|
||||
<div>
|
||||
<p>Focus June and press ArrowRight or ArrowLeft. Focus stays on June.</p>
|
||||
<MonthPicker.Standalone
|
||||
defaultSelected={new Date(year, 5, 1)}
|
||||
disabled={[
|
||||
(date) => !(date.getFullYear() === year && date.getMonth() === 5),
|
||||
]}
|
||||
fromDate={new Date(year, 0, 1)}
|
||||
toDate={new Date(year, 11, 1)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Standalone = () => {
|
||||
return <MonthPicker.Standalone defaultSelected={new Date(`Jan 1 ${year}`)} />;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
chromatic: { disable: true },
|
||||
},
|
||||
/* TODO: Temp disabled CI-tests */
|
||||
tags: ["skip-test"],
|
||||
tags: ["!play-fn", "skip-test"],
|
||||
} satisfies Meta<typeof MonthPicker>;
|
||||
|
||||
type Story = StoryObj<typeof MonthPicker>;
|
||||
@@ -246,45 +246,6 @@ export const DropdownCaptionView: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that the month grid keeps a tab stop after a month is focused with the mouse
|
||||
* and focus then moves to an outside element.
|
||||
*/
|
||||
export const TabStopAfterMouseFocus: Story = {
|
||||
// Overrides the file-level skip so this regression test runs in CI
|
||||
tags: ["!skip-test"],
|
||||
render: () => (
|
||||
<>
|
||||
<MonthPicker.Standalone year={new Date("2025")} />
|
||||
<button type="button">Outside</button>
|
||||
</>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const monthButtons = () =>
|
||||
canvas
|
||||
.getAllByRole("button")
|
||||
.filter((b) => b.classList.contains("aksel-date__month-button"));
|
||||
|
||||
expect(monthButtons().filter((b) => b.tabIndex === 0)).toHaveLength(1);
|
||||
|
||||
const febButton = canvas.getByRole("button", {
|
||||
name: format(new Date("Feb 01 2025"), "LLLL", { locale: nb }),
|
||||
});
|
||||
await userEvent.click(febButton);
|
||||
expect(febButton).toHaveFocus();
|
||||
|
||||
await userEvent.click(canvas.getByRole("button", { name: "Outside" }));
|
||||
|
||||
const tabStops = monthButtons().filter((b) => b.tabIndex === 0);
|
||||
expect(tabStops).toHaveLength(1);
|
||||
expect(tabStops[0]).toBe(febButton);
|
||||
|
||||
await userEvent.tab({ shift: true });
|
||||
expect(febButton).toHaveFocus();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that the monthpicker handles `dropdownCaption` prop correctly
|
||||
*/
|
||||
|
||||
@@ -106,7 +106,7 @@ const MonthPickerButtonInternal = ({
|
||||
setTabRoot(next);
|
||||
}}
|
||||
onFocus={() => {
|
||||
setTabRoot(month);
|
||||
setTabRoot(focus);
|
||||
}}
|
||||
>
|
||||
<div className="rdp-day__inner">
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import { isSameMonth } from "date-fns/isSameMonth";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { nextEnabled } from "./MonthPicker.util";
|
||||
|
||||
const months = Array.from({ length: 12 }, (_, i) => new Date(2024, i, 1));
|
||||
const june2024 = new Date(2024, 5, 1);
|
||||
|
||||
/* Disables every month except `enabled`. Throws instead of hanging if the search becomes unbounded. */
|
||||
const onlyEnabled = (...enabled: Date[]) => {
|
||||
let calls = 0;
|
||||
return (date: Date) => {
|
||||
calls += 1;
|
||||
if (calls > 5000) {
|
||||
throw new Error("Month search did not terminate");
|
||||
}
|
||||
return !enabled.some((month) => isSameMonth(month, date));
|
||||
};
|
||||
};
|
||||
|
||||
const search = (
|
||||
key: string,
|
||||
disabled: (date: Date) => boolean,
|
||||
options: { dropdownCaption?: boolean; fromDate?: Date; toDate?: Date } = {},
|
||||
) => {
|
||||
const setYearState = vi.fn();
|
||||
const result = nextEnabled(
|
||||
months,
|
||||
key,
|
||||
[disabled],
|
||||
june2024,
|
||||
setYearState,
|
||||
june2024,
|
||||
options.dropdownCaption ?? false,
|
||||
options.fromDate,
|
||||
options.toDate,
|
||||
);
|
||||
return { result, setYearState };
|
||||
};
|
||||
|
||||
describe("nextEnabled", () => {
|
||||
test.each(["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp"])(
|
||||
"keeps focus when no month is enabled in direction (%s, no bounds)",
|
||||
(key) => {
|
||||
const { result, setYearState } = search(key, onlyEnabled(june2024));
|
||||
|
||||
expect(result).toEqual(june2024);
|
||||
expect(setYearState).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
test.each([false, true])(
|
||||
"stops at fromDate/toDate years (dropdownCaption: %s)",
|
||||
(dropdownCaption) => {
|
||||
const bounds = {
|
||||
dropdownCaption,
|
||||
fromDate: new Date(2023, 0, 1),
|
||||
toDate: new Date(2025, 11, 1),
|
||||
};
|
||||
const disabled = onlyEnabled(
|
||||
june2024,
|
||||
new Date(2022, 5, 1),
|
||||
new Date(2026, 5, 1),
|
||||
);
|
||||
|
||||
for (const key of ["ArrowRight", "ArrowLeft"]) {
|
||||
const { result, setYearState } = search(key, disabled, bounds);
|
||||
expect(result).toEqual(june2024);
|
||||
expect(setYearState).not.toHaveBeenCalled();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test("skips disabled months/years and moves to the next enabled year", () => {
|
||||
const target = new Date(2027, 2, 1);
|
||||
const { result, setYearState } = search(
|
||||
"ArrowRight",
|
||||
onlyEnabled(june2024, target),
|
||||
);
|
||||
|
||||
expect(result).toEqual(target);
|
||||
expect(setYearState).toHaveBeenCalledTimes(1);
|
||||
expect(setYearState.mock.calls[0][0].getFullYear()).toBe(2027);
|
||||
});
|
||||
|
||||
test("finds previous enabled month within bounds", () => {
|
||||
const target = new Date(2023, 10, 1);
|
||||
const { result, setYearState } = search(
|
||||
"ArrowLeft",
|
||||
onlyEnabled(june2024, target),
|
||||
{ fromDate: new Date(2023, 0, 1) },
|
||||
);
|
||||
|
||||
expect(result).toEqual(target);
|
||||
expect(setYearState.mock.calls[0][0].getFullYear()).toBe(2023);
|
||||
});
|
||||
|
||||
test("searches at most 100 years without bounds", () => {
|
||||
const within = new Date(2124, 0, 1);
|
||||
expect(search("ArrowRight", onlyEnabled(june2024, within)).result).toEqual(
|
||||
within,
|
||||
);
|
||||
|
||||
const beyond = new Date(2125, 0, 1);
|
||||
expect(search("ArrowRight", onlyEnabled(june2024, beyond)).result).toEqual(
|
||||
june2024,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -56,14 +56,14 @@ export const nextEnabled = (
|
||||
}
|
||||
}
|
||||
if (key === "ArrowRight") {
|
||||
const nextMonth = searchEnabledMonth(
|
||||
1,
|
||||
const nextMonth = loopForward(
|
||||
currentIndex,
|
||||
months,
|
||||
disabled,
|
||||
yearState,
|
||||
setYearState,
|
||||
disabled,
|
||||
false,
|
||||
dropdownCaption,
|
||||
fromDate,
|
||||
toDate,
|
||||
);
|
||||
@@ -72,14 +72,14 @@ export const nextEnabled = (
|
||||
}
|
||||
}
|
||||
if (key === "ArrowLeft") {
|
||||
const prevMonth = searchEnabledMonth(
|
||||
-1,
|
||||
currentIndex,
|
||||
const prevMonth = loopBack(
|
||||
currentIndex - 1,
|
||||
months,
|
||||
disabled,
|
||||
yearState,
|
||||
setYearState,
|
||||
false,
|
||||
dropdownCaption,
|
||||
fromDate,
|
||||
toDate,
|
||||
);
|
||||
@@ -98,14 +98,14 @@ export const nextEnabled = (
|
||||
return setYear(months[currentIndex + 4], yearState.getFullYear());
|
||||
}
|
||||
|
||||
const fallbackNext = searchEnabledMonth(
|
||||
1,
|
||||
const fallbackNext = loopForward(
|
||||
currentIndex,
|
||||
months,
|
||||
disabled,
|
||||
yearState,
|
||||
setYearState,
|
||||
disabled,
|
||||
true,
|
||||
dropdownCaption,
|
||||
fromDate,
|
||||
toDate,
|
||||
);
|
||||
@@ -127,14 +127,14 @@ export const nextEnabled = (
|
||||
)
|
||||
)
|
||||
return setYear(months[currentIndex - 4], Number(yearState.getFullYear()));
|
||||
const fallbackPrev = searchEnabledMonth(
|
||||
-1,
|
||||
const fallbackPrev = loopBack(
|
||||
currentIndex,
|
||||
months,
|
||||
disabled,
|
||||
yearState,
|
||||
setYearState,
|
||||
true,
|
||||
dropdownCaption,
|
||||
fromDate,
|
||||
toDate,
|
||||
);
|
||||
@@ -144,59 +144,90 @@ export const nextEnabled = (
|
||||
return currentMonth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Keyboard search for an enabled month never crosses fromDate/toDate years and
|
||||
* gives up after this many years when no bounds are set, so a matcher that
|
||||
* disables every remaining month cannot hang the page.
|
||||
*/
|
||||
const MAX_SEARCH_YEARS = 100;
|
||||
|
||||
const searchEnabledMonth = (
|
||||
direction: 1 | -1,
|
||||
const loopBack = (
|
||||
currentIndex: number,
|
||||
months: Date[],
|
||||
disabled: Matcher[],
|
||||
yearState: Date,
|
||||
setYearState: (date: Date) => void,
|
||||
rowCheck: boolean,
|
||||
dropdownCaption: boolean,
|
||||
fromDate?: Date,
|
||||
toDate?: Date,
|
||||
): { index: number; year: number } | undefined => {
|
||||
const currentYear = yearState.getFullYear();
|
||||
if (Number.isNaN(currentYear)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const boundYear =
|
||||
direction === 1 ? toDate?.getFullYear() : fromDate?.getFullYear();
|
||||
const horizonYear = currentYear + direction * MAX_SEARCH_YEARS;
|
||||
const lastYear = !boundYear
|
||||
? horizonYear
|
||||
: direction === 1
|
||||
? Math.min(boundYear, horizonYear)
|
||||
: Math.max(boundYear, horizonYear);
|
||||
|
||||
let nextYear = currentYear;
|
||||
let monthIndex = currentIndex;
|
||||
while (true) {
|
||||
monthIndex += direction;
|
||||
if (monthIndex < 0 || monthIndex > 11) {
|
||||
if (nextYear === lastYear || (nextYear - lastYear) * direction > 0) {
|
||||
let currentYear = setYear(yearState, Number(yearState.getFullYear()));
|
||||
for (let i = currentIndex; i >= -1; i--) {
|
||||
if (i === -1) {
|
||||
if (
|
||||
isOutOfRange(
|
||||
dropdownCaption,
|
||||
setYear(currentYear, Number(currentYear.getFullYear() - 1)),
|
||||
fromDate,
|
||||
toDate,
|
||||
)
|
||||
)
|
||||
return;
|
||||
}
|
||||
nextYear += direction;
|
||||
monthIndex = direction === 1 ? 0 : 11;
|
||||
currentYear = setYear(currentYear, Number(currentYear.getFullYear() - 1));
|
||||
setYearState(currentYear);
|
||||
i = 11;
|
||||
}
|
||||
const month = months[i];
|
||||
const isDisabled = !isMatch(
|
||||
setYear(month, Number(currentYear.getFullYear())),
|
||||
disabled,
|
||||
);
|
||||
if (rowCheck) {
|
||||
if (isDisabled && getRow(i) !== getRow(currentIndex)) {
|
||||
return { index: i, year: Number(currentYear.getFullYear()) };
|
||||
}
|
||||
} else {
|
||||
if (isDisabled) {
|
||||
return { index: i, year: Number(currentYear.getFullYear()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isEnabled = !isMatch(setYear(months[monthIndex], nextYear), disabled);
|
||||
if (
|
||||
isEnabled &&
|
||||
(!rowCheck || getRow(monthIndex) !== getRow(currentIndex))
|
||||
) {
|
||||
if (nextYear !== currentYear) {
|
||||
setYearState(setYear(yearState, nextYear));
|
||||
const loopForward = (
|
||||
currentIndex: number,
|
||||
months: Date[],
|
||||
yearState: Date,
|
||||
setYearState: (date: Date) => void,
|
||||
disabled: Matcher[],
|
||||
rowCheck: boolean,
|
||||
dropdownCaption: boolean,
|
||||
fromDate?: Date,
|
||||
toDate?: Date,
|
||||
): { index: number; year: number } | undefined => {
|
||||
let currentYear = setYear(yearState, Number(yearState.getFullYear()));
|
||||
for (let i = currentIndex + 1; i < months.length + 1; i++) {
|
||||
if (i === 12) {
|
||||
if (
|
||||
isOutOfRange(
|
||||
dropdownCaption,
|
||||
setYear(currentYear, Number(currentYear.getFullYear() + 1)),
|
||||
fromDate,
|
||||
toDate,
|
||||
)
|
||||
)
|
||||
return;
|
||||
currentYear = setYear(currentYear, Number(currentYear.getFullYear() + 1));
|
||||
setYearState(currentYear);
|
||||
i = 0;
|
||||
}
|
||||
const month = months[i];
|
||||
const isDisabled = !isMatch(
|
||||
setYear(month, Number(currentYear.getFullYear())),
|
||||
disabled,
|
||||
);
|
||||
if (rowCheck) {
|
||||
if (isDisabled && getRow(i) !== getRow(currentIndex)) {
|
||||
return { index: i, year: Number(currentYear.getFullYear()) };
|
||||
}
|
||||
} else {
|
||||
if (isDisabled) {
|
||||
return { index: i, year: Number(currentYear.getFullYear()) };
|
||||
}
|
||||
return { index: monthIndex, year: nextYear };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -207,6 +238,25 @@ const getRow = (index: number): number => {
|
||||
return 3;
|
||||
};
|
||||
|
||||
const isOutOfRange = (
|
||||
dropdownCaption: boolean,
|
||||
year: Date,
|
||||
fromDate?: Date,
|
||||
toDate?: Date,
|
||||
): boolean => {
|
||||
if (
|
||||
dropdownCaption &&
|
||||
fromDate &&
|
||||
toDate &&
|
||||
(year.getFullYear() < fromDate?.getFullYear() ||
|
||||
year.getFullYear() > toDate?.getFullYear())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const nextOnRow = (
|
||||
currentIndex: number,
|
||||
months: Date[],
|
||||
|
||||
@@ -49,15 +49,8 @@ const MonthPickerTable = () => {
|
||||
hasSelected ? selected : getRootFallback(),
|
||||
);
|
||||
|
||||
// Track the year instead of comparing tabRoot, since tabRoot is undefined
|
||||
// when every month in the year is disabled and would re-trigger forever.
|
||||
const [tabRootYear, setTabRootYear] = useState(year.getFullYear());
|
||||
|
||||
if (tabRootYear !== year.getFullYear()) {
|
||||
setTabRootYear(year.getFullYear());
|
||||
if (tabRoot?.getFullYear() !== year.getFullYear()) {
|
||||
setTabRoot(hasSelected ? selected : getRootFallback());
|
||||
}
|
||||
if (tabRoot?.getFullYear() !== year.getFullYear()) {
|
||||
setTabRoot(hasSelected ? selected : getRootFallback());
|
||||
}
|
||||
|
||||
const tableMonths = [
|
||||
|
||||
@@ -179,3 +179,4 @@ export const Chromatic = renderStoriesForChromatic({
|
||||
AsLink,
|
||||
ColorRole,
|
||||
});
|
||||
Chromatic.args = { count: 8, siblingCount: 1, boundaryCount: 1 };
|
||||
@@ -123,13 +123,9 @@ const PopoverRoot = forwardRef<HTMLDivElement, PopoverProps>(
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* Only give Floating UI a reference while open. Otherwise every closed instance
|
||||
* computes (and commits) a position
|
||||
*/
|
||||
useClientLayoutEffect(() => {
|
||||
refs.setReference(open ? anchorEl : null);
|
||||
}, [anchorEl, open, refs]);
|
||||
refs.setReference(anchorEl);
|
||||
}, [anchorEl, refs]);
|
||||
|
||||
const floatingRef = useMergeRefs(refs.setFloating, ref);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, test } from "vitest";
|
||||
import { bench, describe } from "vitest";
|
||||
import { HStack, VStack } from "../stack";
|
||||
import { Box } from "./Box";
|
||||
|
||||
@@ -15,86 +15,82 @@ import { Box } from "./Box";
|
||||
*/
|
||||
|
||||
describe("Box – single element", () => {
|
||||
test("render throughput", async ({ bench }) => {
|
||||
await bench("bare (no props)", () => {
|
||||
renderToStaticMarkup(<Box>Hei</Box>);
|
||||
}).run();
|
||||
bench("bare (no props)", () => {
|
||||
renderToStaticMarkup(<Box>Hei</Box>);
|
||||
});
|
||||
|
||||
await bench("styling props only (background/border/radius/shadow)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
background="neutral-soft"
|
||||
borderColor="neutral-subtle"
|
||||
borderWidth="1"
|
||||
borderRadius="12"
|
||||
shadow="dialog"
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
}).run();
|
||||
bench("styling props only (background/border/radius/shadow)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
background="neutral-soft"
|
||||
borderColor="neutral-subtle"
|
||||
borderWidth="1"
|
||||
borderRadius="12"
|
||||
shadow="dialog"
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
});
|
||||
|
||||
await bench("single responsive/primitive prop (padding)", () => {
|
||||
renderToStaticMarkup(<Box padding="space-16">Hei</Box>);
|
||||
}).run();
|
||||
bench("single responsive/primitive prop (padding)", () => {
|
||||
renderToStaticMarkup(<Box padding="space-16">Hei</Box>);
|
||||
});
|
||||
|
||||
await bench("many primitive props (padding/margin/width/position/inset)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
padding="space-16"
|
||||
margin="space-8"
|
||||
width="200px"
|
||||
maxWidth="400px"
|
||||
position="relative"
|
||||
inset="space-4"
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
}).run();
|
||||
bench("many primitive props (padding/margin/width/position/inset)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
padding="space-16"
|
||||
margin="space-8"
|
||||
width="200px"
|
||||
maxWidth="400px"
|
||||
position="relative"
|
||||
inset="space-4"
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
});
|
||||
|
||||
await bench("responsive object prop (padding per breakpoint)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
padding={{
|
||||
xs: "space-8",
|
||||
sm: "space-12",
|
||||
md: "space-16",
|
||||
lg: "space-20",
|
||||
xl: "space-24",
|
||||
}}
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
}).run();
|
||||
bench("responsive object prop (padding per breakpoint)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box
|
||||
padding={{
|
||||
xs: "space-8",
|
||||
sm: "space-12",
|
||||
md: "space-16",
|
||||
lg: "space-20",
|
||||
xl: "space-24",
|
||||
}}
|
||||
>
|
||||
Hei
|
||||
</Box>,
|
||||
);
|
||||
});
|
||||
|
||||
await bench("asChild (Slot merge path)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box padding="space-16" asChild>
|
||||
<section>Hei</section>
|
||||
</Box>,
|
||||
);
|
||||
}).run();
|
||||
bench("asChild (Slot merge path)", () => {
|
||||
renderToStaticMarkup(
|
||||
<Box padding="space-16" asChild>
|
||||
<section>Hei</section>
|
||||
</Box>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Box – realistic tree (24 children)", () => {
|
||||
const children = Array.from({ length: 24 }, (_, i) => i);
|
||||
|
||||
test("render throughput", async ({ bench }) => {
|
||||
await bench("VStack > Box grid of cards", () => {
|
||||
renderToStaticMarkup(
|
||||
<VStack gap="space-16">
|
||||
{children.map((i) => (
|
||||
<HStack key={i} gap="space-8" align="center">
|
||||
<Box padding="space-16" background="neutral-soft" borderRadius="8">
|
||||
Card {i}
|
||||
</Box>
|
||||
</HStack>
|
||||
))}
|
||||
</VStack>,
|
||||
);
|
||||
}).run();
|
||||
bench("VStack > Box grid of cards", () => {
|
||||
renderToStaticMarkup(
|
||||
<VStack gap="space-16">
|
||||
{children.map((i) => (
|
||||
<HStack key={i} gap="space-8" align="center">
|
||||
<Box padding="space-16" background="neutral-soft" borderRadius="8">
|
||||
Card {i}
|
||||
</Box>
|
||||
</HStack>
|
||||
))}
|
||||
</VStack>,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ export default {
|
||||
title: "ds-react/Timeline/Tests",
|
||||
component: Timeline,
|
||||
parameters: { chromatic: { disable: true } },
|
||||
tags: ["play-fn"],
|
||||
};
|
||||
|
||||
type Story = StoryObj<typeof Timeline>;
|
||||
|
||||
@@ -8,9 +8,6 @@ interface ListboxInputSlotProps {
|
||||
virtuallyFocusedOptionId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember that role=combobox requires aria-expanded
|
||||
*/
|
||||
const ListboxInputSlot = ({
|
||||
children,
|
||||
virtuallyFocusedOptionId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { describe, test } from "vitest";
|
||||
import { bench, describe } from "vitest";
|
||||
import { Provider } from "../../provider";
|
||||
import { get } from "./get";
|
||||
import { useI18n } from "./i18n.hooks";
|
||||
@@ -8,16 +8,14 @@ import type { ComponentTranslation } from "./i18n.types";
|
||||
import { nb } from "./locales";
|
||||
|
||||
describe("translate function throughput", () => {
|
||||
test("translation lookups", async ({ bench }) => {
|
||||
const { result } = renderHook(() => useI18n("FileUpload"));
|
||||
const { result } = renderHook(() => useI18n("FileUpload"));
|
||||
|
||||
await bench("simple key lookup (no replacements)", () => {
|
||||
result.current("item.uploading");
|
||||
}).run();
|
||||
bench("simple key lookup (no replacements)", () => {
|
||||
result.current("item.uploading");
|
||||
});
|
||||
|
||||
await bench("key lookup with replacements", () => {
|
||||
result.current("item.uploading", { name: "Test", count: 5 } as never);
|
||||
}).run();
|
||||
bench("key lookup with replacements", () => {
|
||||
result.current("item.uploading", { name: "Test", count: 5 } as never);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,13 +23,10 @@ describe("translate with local overrides", () => {
|
||||
const local = {
|
||||
item: { uploading: "Hello {name}, you have {count} files" },
|
||||
};
|
||||
const { result } = renderHook(() => useI18n("FileUpload", local));
|
||||
|
||||
test("local override lookup", async ({ bench }) => {
|
||||
const { result } = renderHook(() => useI18n("FileUpload", local));
|
||||
|
||||
await bench("local override with replacements", () => {
|
||||
result.current("item.uploading", { name: "Test", count: 5 });
|
||||
}).run();
|
||||
bench("local override with replacements", () => {
|
||||
result.current("item.uploading", { name: "Test", count: 5 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,21 +38,18 @@ describe("translate with multiple context objects", () => {
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
const { result } = renderHook(() => useI18n("FileUpload"), { wrapper });
|
||||
|
||||
test("context lookups", async ({ bench }) => {
|
||||
const { result } = renderHook(() => useI18n("FileUpload"), { wrapper });
|
||||
bench("hit in first context object", () => {
|
||||
result.current("dropzone.button");
|
||||
});
|
||||
|
||||
await bench("hit in first context object", () => {
|
||||
result.current("dropzone.button");
|
||||
}).run();
|
||||
bench("hit in second context object", () => {
|
||||
result.current("item.uploading");
|
||||
});
|
||||
|
||||
await bench("hit in second context object", () => {
|
||||
result.current("item.uploading");
|
||||
}).run();
|
||||
|
||||
await bench("fallback to locale default", () => {
|
||||
result.current("item.downloading");
|
||||
}).run();
|
||||
bench("fallback to locale default", () => {
|
||||
result.current("item.downloading");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,21 +61,19 @@ describe("get() function throughput", () => {
|
||||
nb.FileUpload,
|
||||
];
|
||||
|
||||
test("translation lookup", async ({ bench }) => {
|
||||
await bench("shallow key hit in first object", () => {
|
||||
get("item.uploading", objects);
|
||||
}).run();
|
||||
bench("shallow key hit in first object", () => {
|
||||
get("item.uploading", objects);
|
||||
});
|
||||
|
||||
await bench("key hit in second object (skip first)", () => {
|
||||
get("item.downloading", objects);
|
||||
}).run();
|
||||
bench("key hit in second object (skip first)", () => {
|
||||
get("item.downloading", objects);
|
||||
});
|
||||
|
||||
await bench("deep fallback to last object", () => {
|
||||
get("dropzone.button", objects);
|
||||
}).run();
|
||||
bench("deep fallback to last object", () => {
|
||||
get("dropzone.button", objects);
|
||||
});
|
||||
|
||||
await bench("long key dot notation parsing", () => {
|
||||
get("dropzone.dragAndDropMultiple", [nb.FileUpload]);
|
||||
}).run();
|
||||
bench("long key dot notation parsing", () => {
|
||||
get("dropzone.dragAndDropMultiple", [nb.FileUpload]);
|
||||
});
|
||||
});
|
||||
@@ -1,21 +1,22 @@
|
||||
import type { Args, StoryObj } from "@storybook/react-vite";
|
||||
import React from "react";
|
||||
import type { Renderer, StoryContext } from "storybook/internal/types";
|
||||
|
||||
type StoryFnOrObj =
|
||||
| { render?: (...args: any[]) => React.ReactNode; args?: Args }
|
||||
| (React.FunctionComponent & { args?: Args });
|
||||
| React.FunctionComponent;
|
||||
|
||||
export function renderStoriesForChromatic(
|
||||
stories: Record<string, StoryFnOrObj>,
|
||||
Component?: React.FunctionComponent<any>,
|
||||
): StoryObj {
|
||||
return {
|
||||
render: () => (
|
||||
render: (...args: [Args, StoryContext<Renderer, Args>]) => (
|
||||
<>
|
||||
{Object.entries(stories).map(([storyName, Story]) => (
|
||||
<div key={storyName}>
|
||||
<h2 className="storyheading">{storyName}</h2>
|
||||
{resolveComponent(Story, Component)}
|
||||
{resolveComponent(Story, args, Component)}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
@@ -29,13 +30,14 @@ export function renderStoriesForChromatic(
|
||||
|
||||
function resolveComponent(
|
||||
Story: StoryFnOrObj,
|
||||
args: [Args, StoryContext<Renderer, Args>],
|
||||
Component?: React.FunctionComponent,
|
||||
) {
|
||||
if (typeof Story === "function") {
|
||||
return <Story {...(Story.args || {})} />;
|
||||
return <Story />;
|
||||
}
|
||||
if (Story.render) {
|
||||
return Story.render(Story.args || {});
|
||||
return Story.render(...args);
|
||||
}
|
||||
if (Story.args && Component) {
|
||||
return <Component {...Story.args} />;
|
||||
|
||||
@@ -6,7 +6,6 @@ export default defineConfig({
|
||||
globals: true,
|
||||
setupFiles: "./tests/setup.ts",
|
||||
environment: "jsdom",
|
||||
pool: "vmThreads",
|
||||
typecheck: {
|
||||
enabled: true,
|
||||
},
|
||||
@@ -1,9 +1,5 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
## 8.20.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"description": "Tailwind config based on @navikt/ds-tokens",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -28,12 +28,12 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/tailwind/",
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.22.1",
|
||||
"@navikt/ds-tokens": "^8.21.0",
|
||||
"@types/node": "^24.10.9",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
File renamed without changes.
@@ -1,9 +1,5 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 8.22.1
|
||||
|
||||
## 8.22.0
|
||||
|
||||
## 8.21.0
|
||||
|
||||
## 8.20.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "8.22.1",
|
||||
"version": "8.21.0",
|
||||
"description": "Design-tokens for Nav designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/styling/design-tokens/",
|
||||
"devDependencies": {
|
||||
"@figma/plugin-typings": "^1.142.0",
|
||||
"@figma/plugin-typings": "^1.131.0",
|
||||
"@types/lodash": "4.17.25",
|
||||
"colorjs.io": "^0.7.1",
|
||||
"esbuild": "^0.28.2",
|
||||
@@ -43,7 +43,7 @@
|
||||
"style-dictionary": "^5.5.5",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
File renamed without changes.
@@ -1,35 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 8.22.1
|
||||
|
||||
### @navikt/ds-css
|
||||
|
||||
- DataGrid: Column dividers in the table header are now hidden when column dividers are disabled, and only shown on hover, focus or while resizing. ([#5505](https://github.com/navikt/aksel/pull/5505))
|
||||
|
||||
## 8.22.0
|
||||
|
||||
### @navikt/aksel-icons
|
||||
|
||||
- Icons: New icons `Gift`, `ArrowsPointingIn` and `ArrowsPointingOut`. ([#5484](https://github.com/navikt/aksel/pull/5484))
|
||||
|
||||
* ESM: Mark ESM output as side-effect free, so bundlers like webpack, Turbopack and esbuild can tree-shake unused components and icons. ([#5472](https://github.com/navikt/aksel/pull/5472))
|
||||
|
||||
### @navikt/ds-react
|
||||
|
||||
- Combobox: The list is no longer focusable ([#5490](https://github.com/navikt/aksel/pull/5490))
|
||||
|
||||
- Popover: Closed popovers no longer calculate their position on mount. This makes pages with many Dropdowns or HelpTexts render faster. ([#5474](https://github.com/navikt/aksel/pull/5474))
|
||||
|
||||
- ESM: Mark ESM output as side-effect free, so bundlers like webpack, Turbopack and esbuild can tree-shake unused components and icons. ([#5472](https://github.com/navikt/aksel/pull/5472))
|
||||
|
||||
- MonthPicker: Fixed issue where navigating to a year without any enabled months could lead to re-render loop in React ([#5503](https://github.com/navikt/aksel/pull/5503))
|
||||
|
||||
- MonthPicker: Arrow-key navigation no longer freezes the page when no later or earlier month is enabled. Keyboard search now respects `fromDate`/`toDate` with both caption types and looks at most 100 years ahead or back. ([#5477](https://github.com/navikt/aksel/pull/5477))
|
||||
|
||||
- Date utilities: Import date-fns functions and locales from their own paths, so unbundled Node imports (SSR, Vitest/Jest) no longer load every date-fns locale. ([#5476](https://github.com/navikt/aksel/pull/5476))
|
||||
|
||||
- DataGrid: Fixed text selection inside details panels when keyboard navigation is enabled. ([`0bdad46`](https://github.com/navikt/aksel/commit/0bdad462664e04abd89c4121de515f6ae6d9aad1))
|
||||
|
||||
## 8.21.0
|
||||
|
||||
### @navikt/aksel-icons
|
||||
|
||||
@@ -37,14 +37,6 @@ yarn build:next
|
||||
yarn serve:next
|
||||
```
|
||||
|
||||
`yarn build:next` genererer ikke RSS-feeden og bygger ikke Playroom. Kjør dette for å bygge alt:
|
||||
|
||||
```bash
|
||||
yarn workspace website update:rss
|
||||
yarn workspace website build:with-playroom
|
||||
yarn serve:next
|
||||
```
|
||||
|
||||
**Rettigheter**
|
||||
|
||||
Sanity-datasettet er privat, noe som betyr du må ha tilgang til Sanity-applikasjonen og følgende tokens:
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
"sanity:update": "yarn workspace website update:sanity"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/aksel": "^8.22.1",
|
||||
"@navikt/aksel-icons": "^8.22.1",
|
||||
"@navikt/ds-css": "^8.22.1",
|
||||
"@navikt/ds-react": "^8.22.1",
|
||||
"@navikt/ds-tailwind": "^8.22.1",
|
||||
"@navikt/ds-tokens": "^8.22.1"
|
||||
"@navikt/aksel": "^8.21.0",
|
||||
"@navikt/aksel-icons": "^8.21.0",
|
||||
"@navikt/ds-css": "^8.21.0",
|
||||
"@navikt/ds-react": "^8.21.0",
|
||||
"@navikt/ds-tailwind": "^8.21.0",
|
||||
"@navikt/ds-tokens": "^8.21.0"
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
"@types/react": "19.2.14",
|
||||
"tsx": "^4.23.15",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "5.0.5"
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"exports": {
|
||||
"./schema": {
|
||||
|
||||
File renamed without changes.
+5
-4
@@ -52,7 +52,7 @@ function IntroInformation() {
|
||||
return (
|
||||
<div className={styles.iconDetails}>
|
||||
<Heading level="2" size="small" spacing>
|
||||
React-komponenter
|
||||
React komponenter
|
||||
</Heading>
|
||||
<VStack gap="space-24">
|
||||
<div>
|
||||
@@ -71,17 +71,18 @@ function IntroInformation() {
|
||||
data-block-margin="space-0"
|
||||
tabs={[
|
||||
{
|
||||
code: `import { ChevronDownIcon, TrashIcon } from "@navikt/aksel-icons";
|
||||
code: `import { ChevronDownIcon, TrashIcon, FilterIcon } from "@navikt/aksel-icons";
|
||||
|
||||
function MyComponent() {
|
||||
function MyComponent () {
|
||||
return (
|
||||
<div>
|
||||
<FilterIcon />
|
||||
<TrashIcon aria-hidden />
|
||||
<ChevronDownIcon
|
||||
title="Åpne panel"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}`,
|
||||
lang: "tsx",
|
||||
text: "React",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Masonry } from "react-responsive-masonry";
|
||||
import Masonry from "react-responsive-masonry";
|
||||
import { Box, HGrid, Heading } from "@navikt/ds-react";
|
||||
import { useMedia } from "@/app/_ui/utils/hooks/useMedia";
|
||||
import Card, { type ArticleT } from "./FrontpageMasonryCard";
|
||||
|
||||
File renamed without changes.
@@ -1,3 +1,4 @@
|
||||
import BundleAnalyzer from "@next/bundle-analyzer";
|
||||
import type { NextConfig } from "next";
|
||||
import { sanity } from "next-sanity/live/cache-life";
|
||||
import path from "node:path";
|
||||
@@ -172,4 +173,5 @@ const nextConfig: NextConfig = {
|
||||
serverExternalPackages: ["@navikt/next-logger", "next-logger", "pino"],
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
module.exports =
|
||||
process.env.ANALYZE === "true" ? BundleAnalyzer()(nextConfig) : nextConfig;
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user