Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82656da435 | ||
|
|
ab9932ed8f | ||
|
|
180ca25861 | ||
|
|
cbac535fcb | ||
|
|
296049b8c7 | ||
|
|
073d811b3f | ||
|
|
da1374e833 |
No files matched your search
@@ -25,8 +25,7 @@
|
||||
"aksel-icons-figma-plugin",
|
||||
"eslint-plugin-aksel-local",
|
||||
"scripts",
|
||||
"aksel-analyzer",
|
||||
"aksel-mcp"
|
||||
"aksel-analyzer"
|
||||
],
|
||||
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
|
||||
@@ -1,27 +1,43 @@
|
||||
# Aksel - Copilot Instructions
|
||||
|
||||
## Approach
|
||||
## Use this file first
|
||||
|
||||
- Narrow search space; don't replace reading code.
|
||||
- Before editing: read target file + nearest related files (`index.ts`, story, test, exports).
|
||||
- Prefer known paths/scripts over broad searches.
|
||||
- Ignore release/deploy/changelog/ops scripts unless asked.
|
||||
- Trust these instructions to narrow the search space, but still read the target code and nearest related files before editing.
|
||||
- Optimize for local work. Prefer known paths, nearby patterns, and existing scripts over broad repo searches.
|
||||
- Ignore release, deploy, changelog, CMS sync, and other remote/ops scripts unless the user asks.
|
||||
|
||||
## Behavior
|
||||
## How to use this file
|
||||
|
||||
- No apologies, no "I think", no change summaries.
|
||||
- Answer-first. 0-2 sentences max.
|
||||
- No thinking aloud; do next edit. Explain only when asked or non-obvious.
|
||||
- Show only what changes. Minimal diffs; don't paste whole files.
|
||||
- Edits scoped to one file unless asked. Ask only when truly blocked.
|
||||
- Favor perf/security; avoid re-renders, top-level side effects, unsafe HTML, env var leaks.
|
||||
- Use this file to narrow the search space, not to replace reading code.
|
||||
- Before editing, read the target file and the nearest related files in the same folder such as `index.ts`, story, test, and package/root export files.
|
||||
- Search wider only when nearby files do not answer the question.
|
||||
|
||||
## Agent mode
|
||||
|
||||
- No apologies, no “I think”, no change summaries.
|
||||
- Don’t ask to confirm info that’s already in context.
|
||||
- Favor performance and security; add robust error handling and safe logging.
|
||||
- Default tone: short and answer-first. Aim for max 0–2 concise sentences where needed.
|
||||
- Thinking and explanation process: Keep it short, omit whats not strictly necessary, and focus on the next edit.
|
||||
- Avoid long explanations or multiple steps at once.
|
||||
- Don't explain thinking process for each step, just do the next edit. Only add explanations when the user explicitly asks for it or when it's necessary to clarify a non-obvious change.
|
||||
|
||||
## Inline chat and editor completions
|
||||
|
||||
- Verify before you claim. Don’t speculate.
|
||||
- Keep functions small and focused; remove duplication (rule of three).
|
||||
- Show only what changes. Provide minimal diffs/snippets; don’t paste whole files or unrelated lines.
|
||||
- Keep edits scoped to a single file unless explicitly asked to touch more.
|
||||
- Extract requirements into a tiny checklist and proceed. Ask a question only when truly blocked.
|
||||
- Avoid heavy formatting: bullets are OK; skip tables; use code fences only for code.
|
||||
- For performance/security: avoid unnecessary re-renders, side effects at module top-level, unsafe HTML, and leaking env vars.
|
||||
|
||||
## Repo summary
|
||||
|
||||
- Yarn 4 workspaces monorepo: public React/CSS/tokens/icons/stylelint/CLI packages + docs site + playroom.
|
||||
- Large Yarn 4 workspaces monorepo for NAV's design system: public React/CSS/tokens/icons/stylelint/CLI packages plus docs site and playroom.
|
||||
- Stack: TypeScript 6, React 19, Next.js 16, Storybook 10, Vitest 4, ESLint 9, Stylelint 17, Biome 2, Prettier 3.
|
||||
- Node 22+, Yarn 4.12.0.
|
||||
- `@navikt/*` must stay React 17 compatible.
|
||||
- Runtime: Node 22+ recommended, Yarn 4.12.0.
|
||||
- Important constraint: code under `@navikt/*` must stay React 17 API compatible. Do not use React 18/19-only APIs there.
|
||||
|
||||
## Start and validate
|
||||
|
||||
@@ -29,54 +45,101 @@
|
||||
2. `yarn install`
|
||||
3. `yarn boot`
|
||||
|
||||
- `yarn boot` after first install + after `yarn clean`.
|
||||
- Full install needs `NPM_AUTH_TOKEN` for GitHub Packages.
|
||||
- Run `yarn boot` after first install and after `yarn clean`.
|
||||
- Full install may need `NPM_AUTH_TOKEN` for GitHub Packages.
|
||||
|
||||
Public-package-only: `yarn workspaces focus @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint`
|
||||
Public-package-only install:
|
||||
|
||||
```bash
|
||||
yarn workspaces focus @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint
|
||||
```
|
||||
|
||||
## Definition of done
|
||||
|
||||
- Behavior/visual change: update impl + closest story + test.
|
||||
- New public export: sync component `index.ts`, `src/index.ts`, `package.json` exports.
|
||||
- User-facing change: add changeset unless told otherwise.
|
||||
- Validate with narrowest workspace command first.
|
||||
- Behavior or visual change: update the implementation, closest story, and closest test.
|
||||
- New public export: update the component `index.ts`, package `src/index.ts`, and `package.json` exports.
|
||||
- User-facing package change: add a changeset unless the task says otherwise.
|
||||
- Validate with the narrowest workspace command first; use root commands only for cross-workspace changes.
|
||||
|
||||
### Commands
|
||||
### Common local commands
|
||||
|
||||
- `yarn boot` - build all
|
||||
- `yarn test` / `yarn lint` - all tests / all linting
|
||||
Always prefer using these commands that already exists in workspaces before running custom commands or doing manual steps. These are optimized for local development and validation. For example:
|
||||
|
||||
- `yarn boot` - build all workspaces
|
||||
- `yarn test` - run all workspace tests
|
||||
- `yarn lint` - eslint + stylelint + biome + cycle + version checks
|
||||
- `yarn lint:eslint` / `yarn lint:css` / `yarn lint:biome`
|
||||
- `yarn storybook` - root `:6006`
|
||||
- `yarn storybook:aksel` - website examples `:6007`
|
||||
- `yarn storybook:test` - browser tests (Playwright/Firefox)
|
||||
- `yarn dev` - website `:3000`
|
||||
- `yarn storybook` - root Storybook on `localhost:6006`
|
||||
- `yarn storybook:aksel` - website/example Storybook on `localhost:6007`
|
||||
- `yarn storybook:test` - Storybook browser tests, slow, uses Playwright/Firefox
|
||||
- `yarn dev` - website dev server on `localhost:3000`
|
||||
- `yarn clean` - remove build artifacts
|
||||
- `yarn workspace @navikt/ds-react build|test`
|
||||
- `yarn workspace @navikt/ds-react build`
|
||||
- `yarn workspace @navikt/ds-react test`
|
||||
- `yarn workspace website test`
|
||||
- `yarn workspace playroom sync-imports`
|
||||
|
||||
`yarn lint` fails if playroom imports stale → `yarn workspace playroom sync-imports`.
|
||||
### Validated locally
|
||||
|
||||
- Works: `yarn boot`, `yarn test`, `yarn storybook --ci --smoke-test`, `yarn storybook:aksel --ci --smoke-test`
|
||||
- Caveat: `yarn lint` and `yarn lint:eslint` can fail if playroom imports are stale. Fix with `yarn workspace playroom sync-imports`.
|
||||
|
||||
## Where to edit
|
||||
|
||||
- `@navikt/core/react/src/<component>/` - component source, index, stories
|
||||
- `@navikt/core/react/src/index.ts` + `package.json` exports - public API surface
|
||||
- `@navikt/core/react/` - `@navikt/ds-react`
|
||||
- `src/<component>/<Component>.tsx`
|
||||
- `src/<component>/index.ts`
|
||||
- `src/<component>/*.stories.tsx`
|
||||
- `src/index.ts`
|
||||
- `package.json` exports map for new public components
|
||||
- `@navikt/core/css/src/` - component CSS
|
||||
- `@navikt/core/tokens/src/` - design tokens
|
||||
- `@navikt/core/tokens/src/` - design token source
|
||||
- `@navikt/core/tailwind/` - Tailwind preset
|
||||
- `@navikt/aksel-icons/icons/` - source SVGs (`src/` generated)
|
||||
- `@navikt/aksel-stylelint/src/` - Stylelint rules
|
||||
- `@navikt/aksel-icons/icons/` - source SVGs; `src/` is generated
|
||||
- `@navikt/aksel-stylelint/src/` - Stylelint rules/config
|
||||
- `@navikt/aksel/src/` - CLI and codemods
|
||||
- `aksel.nav.no/website/` - Next.js docs site
|
||||
- `aksel.nav.no/playroom/` - playroom
|
||||
- Root: `eslint.config.js`, `stylelint.config.mjs`, `biome.json`, `tsconfig.json`, `.storybook/`
|
||||
- `aksel.nav.no/playroom/` - playroom and generated imports
|
||||
- `examples/` - example apps
|
||||
- `scripts/` - shared repo scripts
|
||||
- Root references: `README.md`, `CONTRIBUTING.md`, `aksel.nav.no/README.md`, `package.json`, `eslint.config.js`, `stylelint.config.mjs`, `biome.json`, `tsconfig.json`, `tsconfig.build.json`, `.storybook/`, `.github/workflows/build-validation.yml`
|
||||
|
||||
## Coding defaults
|
||||
|
||||
- Small diffs. No drive-by refactors or new deps unless asked.
|
||||
- Copy nearest existing pattern before writing new code.
|
||||
- Stable public APIs. Prefer additive props/exports.
|
||||
- `node:` imports for Node builtins. Never import from `esm/`/`cjs/` output.
|
||||
- Import order: `@navikt/*`, `@/*`, relative. (Prettier sorts)
|
||||
- Preserve JSDoc on public props/components when changing public APIs.
|
||||
- Reuse helpers: `cl`, `composeEventHandlers`, `useId`, `omit`, `useClientLayoutEffect`, `useEventListener`.
|
||||
- Keep diffs small. No drive-by refactors, formatting churn, or new dependencies unless asked.
|
||||
- Read the full file and copy the nearest existing pattern before changing code.
|
||||
- Keep public APIs stable. Prefer additive props and exports.
|
||||
- In `@navikt/*`, import React in TSX files; root JSX runtime is classic.
|
||||
- Use named exports with explicit type exports.
|
||||
- Keep `forwardRef`, `className`, `...rest`, and `as`/`OverridableComponent` patterns when the surrounding component uses them.
|
||||
- Reuse internal helpers instead of re-implementing them: `cl`, `composeEventHandlers`, `useId`, `omit`, `useClientLayoutEffect`, `useEventListener`.
|
||||
- Use `node:` imports for Node builtins.
|
||||
- Never import from `esm/` or `cjs/` build output.
|
||||
- Keep import order consistent: `@navikt/*`, `@/*`, then relative. Let Prettier handle sorting.
|
||||
- Preserve or add JSDoc for public props/components when changing public APIs.
|
||||
|
||||
## React and ds-react specifics
|
||||
|
||||
- Component `index.ts` files in `@navikt/core/react/src/**` start with `"use client"`.
|
||||
- When adding or exposing a public component, keep these in sync:
|
||||
- component `index.ts`
|
||||
- `@navikt/core/react/src/index.ts`
|
||||
- `@navikt/core/react/package.json` exports
|
||||
- Do not use React 18/19-only APIs under `@navikt/*`. Use internal helpers like `useId`.
|
||||
|
||||
## CSS and tokens
|
||||
|
||||
- Never hardcode colors, spacing, radius, or shadows. Use tokens only.
|
||||
- Public tokens use `--ax-*`. Internal component variables use `--__axc-*`.
|
||||
- Follow existing CSS nesting, `data-*` selectors, `focus-visible`, `forced-colors`, and reduced-motion patterns.
|
||||
- Prettier formats; Biome is lint-only.
|
||||
|
||||
## Stories, tests, website
|
||||
|
||||
- Update the closest story and test whenever behavior or visuals change.
|
||||
- Use Testing Library queries by role and name.
|
||||
- Stories live next to components and use `@storybook/react-vite`.
|
||||
- For component work, prefer Storybook over `yarn dev`.
|
||||
- `aksel.nav.no/website/pages/eksempler/**` and `pages/templates/**` feed the website and Storybook. In these files, only import from `@navikt/*` and `react`.
|
||||
- The website uses both `app/` and `pages/`; stay inside the router already used nearby.
|
||||
- `yarn storybook:aksel` works for website examples/templates without Sanity secrets. `yarn dev` may need website env values such as `SANITY_READ_NO_DRAFTS`.
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
description: >
|
||||
Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman
|
||||
while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra.
|
||||
Always use caveman for code-related questions unless spesificed otherwise. For non-code topics, use caveman if user seems to want quick, concise answers.
|
||||
applyTo: "**"
|
||||
---
|
||||
|
||||
# caveman
|
||||
|
||||
ACTIVE EVERY RESPONSE.
|
||||
|
||||
default: full
|
||||
|
||||
switch mode:
|
||||
|
||||
- "/caveman lite" → lite
|
||||
- "/caveman full" → full
|
||||
- "/caveman ultra" → ultra
|
||||
- "stop caveman" | "normal mode" → off
|
||||
|
||||
persist: keep last state
|
||||
|
||||
## intensity
|
||||
|
||||
| Level | What change |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| **lite** | short sentences, no filler, keep grammar |
|
||||
| **full** | fragments ok, drop articles, short words. Classic caveman |
|
||||
| **ultra** | Abbreviate (DB/auth/config/req/res/fn/impl), fragments, abbrev (db/api/req/res/fn), arrows (→), minimal words |
|
||||
|
||||
Example - "Why React component re-render?"
|
||||
|
||||
- lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
|
||||
- full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
|
||||
- ultra: "Inline obj prop → new ref → re-render. `useMemo`."
|
||||
|
||||
## global rules
|
||||
|
||||
- no filler (just/really/basically/etc)
|
||||
- no pleasantries
|
||||
- no hedging
|
||||
- keep tech exact
|
||||
- no long sentences
|
||||
- prefer symbols (→, =)
|
||||
|
||||
## patterns
|
||||
|
||||
Patterns: `[thing] [action] [reason]. [next step].`
|
||||
|
||||
## examples
|
||||
|
||||
lite:
|
||||
"Component re-renders because you create a new object each render. Use useMemo."
|
||||
|
||||
full:
|
||||
"New object each render → new ref → re-render. useMemo."
|
||||
|
||||
ultra:
|
||||
"inline obj → new ref → re-render. fix: useMemo."
|
||||
|
||||
## auto-Clarity
|
||||
|
||||
Drop caveman for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
|
||||
|
||||
Example - destructive op:
|
||||
|
||||
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
||||
>
|
||||
> ```sql
|
||||
> DROP TABLE users;
|
||||
> ```
|
||||
>
|
||||
> Caveman resume. Verify backup exist first.
|
||||
|
||||
## boundaries
|
||||
|
||||
Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
description: "Code review instructions used when reviewing pull requests."
|
||||
applyTo: "**"
|
||||
excludeAgent: ["coding-agent"]
|
||||
---
|
||||
|
||||
# Code review focus
|
||||
|
||||
Review for issues that could block release, surprise users, or create maintenance risk. Prefer high-signal findings over style-only comments.
|
||||
|
||||
## What to check
|
||||
|
||||
### 1. Public API and behavior
|
||||
|
||||
- New or changed APIs should match existing naming, params, defaults, and behavior patterns.
|
||||
- Watch for breaking changes, regressions, and accidental behavior changes in public components, hooks, CSS, tokens, and CLI output.
|
||||
- Avoid introducing new patterns unless the change clearly justifies them.
|
||||
|
||||
### 2. Documentation and examples
|
||||
|
||||
- Public components and exported APIs should have accurate JSDoc, including `@default` where it adds clarity.
|
||||
- Check whether Storybook stories, website examples, and decision records need updates to match the new behavior.
|
||||
|
||||
### 3. Tests
|
||||
|
||||
- Flag missing or insufficient tests for changed behavior, edge cases, and regressions.
|
||||
- Prefer tests that prove the contract users rely on, not just implementation details.
|
||||
|
||||
### 4. CSS and styling
|
||||
|
||||
- If CSS class names are removed, verify deprecation handling in `@navikt/aksel-stylelint/src/deprecations.ts`.
|
||||
- If new CSS files or selectors are added, confirm they follow the repo's import and naming conventions.
|
||||
|
||||
### 5. Exports and packaging
|
||||
|
||||
- New public exports must be wired through the correct `index.ts` files and `package.json` exports when relevant.
|
||||
- Check that release-facing entry points stay consistent across packages.
|
||||
|
||||
### 6. Changesets and migrations
|
||||
|
||||
- User-facing changes should include a changeset with a clear summary and impact.
|
||||
- Breaking changes should also include a codemod when needed.
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
description: "JS/TS code conventions."
|
||||
applyTo: "**/*.tsx", "**/*.jsx", "**/*.ts", "**/*.js"
|
||||
description: "Use for JS/TS code in this repo."
|
||||
applyTo: "**/\*.tsx", "**/_.jsx", "\*\*/_.ts", "\*_/_.js"
|
||||
|
||||
---
|
||||
|
||||
# JS/TS instructions
|
||||
|
||||
- Prefer `for...of` over `Array.forEach`.
|
||||
- Named exports; type exports separate from value exports.
|
||||
- Group exports at bottom: values first, then types.
|
||||
- In `*.stories.*`, keep default export at top.
|
||||
- Prefer named exports; keep type exports separate from value exports.
|
||||
- Group exports at the bottom: values first, then types.
|
||||
- In `*.stories.*`, keep the default export at the top.
|
||||
@@ -1,17 +1,13 @@
|
||||
---
|
||||
description: "ds-react components and component CSS."
|
||||
description: "Use for ds-react components and component CSS."
|
||||
applyTo: "@navikt/core/react/src/**", "@navikt/core/css/src/**"
|
||||
|
||||
---
|
||||
|
||||
# ds-react component instructions
|
||||
# ds-react and component instructions
|
||||
|
||||
- Read full component folder before editing.
|
||||
- Preserve `forwardRef`, `className`, `...rest`, `as`/`OverridableComponent` patterns.
|
||||
- `@navikt/*` must stay React 17 compatible. No React 18/19-only APIs; use internal `useId` etc.
|
||||
- `@navikt/*` TSX: import React explicitly (classic JSX).
|
||||
- Component `index.ts` starts with `"use client"`.
|
||||
- Use tokens, not hardcoded values. Token names: `--ax-*` (public), `--__axc-*` (internal).
|
||||
- Follow CSS nesting, `data-*` selectors, `focus-visible`, `forced-colors`, reduced-motion.
|
||||
- Prettier formats CSS; Biome lint-only.
|
||||
- Behavior/visual change: update nearest story + test.
|
||||
- Public component change: sync component `index.ts`, `src/index.ts`, `package.json` exports.
|
||||
- Read the full component folder before editing.
|
||||
- Preserve the surrounding `forwardRef`, `className`, `...rest`, and `as`/`OverridableComponent` patterns.
|
||||
- Keep `@navikt/*` code React 17 API compatible.
|
||||
- Use tokens, not hardcoded component values.
|
||||
- If behavior or visuals change, update the nearest story and test.
|
||||
- If a public component changes, keep component exports, package exports, and root exports in sync.
|
||||
@@ -1,14 +1,11 @@
|
||||
---
|
||||
description: "Tests and stories."
|
||||
applyTo: "**/*.test.*", "**/*.stories.tsx"
|
||||
description: "Use for tests and stories."
|
||||
applyTo: "**/_.test._", "**/\*.stories.tsx"
|
||||
|
||||
---
|
||||
|
||||
# Testing and Storybook instructions
|
||||
|
||||
- Prefer story `play` functions + `yarn storybook:test` for user flows; Vitest for logic/non-browser.
|
||||
- Prefer focused assertions over snapshots unless nearby tests use them.
|
||||
- Cover main path + 1-2 meaningful edge cases.
|
||||
- UI behavior change: update nearest story + test in same change.
|
||||
- Testing Library: query by role and name.
|
||||
- Stories use `@storybook/react-vite`, live next to components.
|
||||
- Prefer `yarn storybook` for component work over `yarn dev`.
|
||||
- Prefer story `play` functions and `yarn storybook:test` for real user flows; use Vitest for logic and non-browser cases.
|
||||
- Prefer focused assertions over snapshots unless nearby tests already use snapshots.
|
||||
- Cover the main path and one or two meaningful edge cases.
|
||||
- If UI behavior changes, update the nearest story and test in the same change.
|
||||
@@ -1,12 +1,14 @@
|
||||
---
|
||||
description: "Website and playroom."
|
||||
applyTo: "aksel.nav.no/website/**, aksel.nav.no/playroom/**"
|
||||
description: "Use for website and playroom work."
|
||||
applyTo:
|
||||
|
||||
- "aksel.nav.no/website/\*\*"
|
||||
- "aksel.nav.no/playroom/\*\*"
|
||||
|
||||
---
|
||||
|
||||
# Website and playroom instructions
|
||||
|
||||
- Stay in router already used nearby; don't mix `app/`/`pages/`.
|
||||
- `pages/eksempler/**` and `pages/templates/**`: only import from `@navikt/*` and `react`.
|
||||
- Reuse nearby utils/patterns before adding abstractions.
|
||||
- Prefer `yarn storybook:aksel` for examples/templates; `yarn dev` only for app-specific behavior.
|
||||
- `yarn storybook:aksel` needs no secrets. `yarn dev` needs `SANITY_READ_NO_DRAFTS`.
|
||||
- Stay inside the router already used nearby; do not mix `app/` and `pages/` patterns in the same area.
|
||||
- In `aksel.nav.no/website/pages/eksempler/**` and `aksel.nav.no/website/pages/templates/**`, only import from `@navikt/*` and `react`.
|
||||
- Reuse nearby website utilities and patterns before adding abstractions.
|
||||
- Prefer `yarn storybook:aksel` for examples/templates; use `yarn dev` only for app-only behavior.
|
||||
@@ -1,203 +0,0 @@
|
||||
---
|
||||
name: ds-component-restructure
|
||||
description: "Restructure a ds-react component to follow canonical folder/file conventions. Use when: restructuring a component, reorganizing component files, migrating component structure, refactoring component layout, splitting monolithic component files, renaming component files to match domain."
|
||||
argument-hint: "Path to component folder, e.g. @navikt/core/react/src/accordion"
|
||||
---
|
||||
|
||||
# ds-component-restructure
|
||||
|
||||
Restructure a `ds-react` component folder to follow canonical conventions — without breaking the public API.
|
||||
|
||||
## Canonical Structure
|
||||
|
||||
```
|
||||
<component>/
|
||||
├── index.ts # "use client" + re-exports from root
|
||||
├── <Component>.stories.tsx # Storybook stories
|
||||
├── <Component>.tests.stories.tsx # Storybook play-tests (only if play tests exist)
|
||||
├── root/
|
||||
│ ├── <Component>Root.tsx # Main compound component + sub-component namespace
|
||||
│ ├── <Component>Root.context.ts # Context (if shared across sub-components)
|
||||
│ ├── <Component>Root.test.ts # Unit tests for root (if needed)
|
||||
│ └── use<Hook>.ts # Files for root-level hooks (if needed)
|
||||
├── <subcomponent>/ # One dir per sub-component
|
||||
│ ├── <Component><Subcomponent>.tsx
|
||||
│ ├── <Component><Subcomponent>.test.ts # Unit tests for sub-component (if needed)
|
||||
│ └── use<Hook>.ts # Files for sub-component-level hooks (if needed)
|
||||
├── helpers/
|
||||
│ ├── <helper-name>.ts
|
||||
│ └── <helper-name>.test.ts # Unit tests co-located with helper
|
||||
└── <Component>.types.ts # Shared types (if needed across multiple files)
|
||||
```
|
||||
|
||||
### Naming Rules
|
||||
|
||||
| What | Pattern | Example |
|
||||
| ---------------------- | --------------------------------------- | ----------------------------- |
|
||||
| Root component file | `<Component>Root.tsx` | `AccordionRoot.tsx` |
|
||||
| Sub-component file | `<Component><Subcomponent>.tsx` | `AccordionItem.tsx` |
|
||||
| Internal sub-component | `<Component><Subcomponent>Internal.tsx` | `DialogBackdropInternal.tsx` |
|
||||
| Sub-component dir | kebab-case sub-component name | `item/` |
|
||||
| Context file | `<Component>Root.context.ts` | `AccordionRoot.context.ts` |
|
||||
| Shared types file | `<Component>.types.ts` | `Accordion.types.ts` |
|
||||
| Stories | `<Component>.stories.tsx` | `Accordion.stories.tsx` |
|
||||
| Play-test stories | `<Component>.tests.stories.tsx` | `Accordion.tests.stories.tsx` |
|
||||
| Unit test file | `<filename>.test.ts(x)` | `useAccordion.test.ts` |
|
||||
| `index.ts` | always `index.ts` | `index.ts` |
|
||||
|
||||
## Procedure
|
||||
|
||||
### 1. Read the Component
|
||||
|
||||
Read every file in the component folder:
|
||||
|
||||
- `index.ts` — current exports
|
||||
- All `.tsx` / `.ts` source files
|
||||
- Stories and test stories
|
||||
- Note what is currently exported publicly
|
||||
- Note: Ignore inconsistencies in file naming and structure — the goal is to fix those, not preserve them. Focus on understanding the component's public API and internal structure.
|
||||
|
||||
### 2. Map Files to Target Structure
|
||||
|
||||
Build this table before touching any files:
|
||||
|
||||
| Old path | New path | Renamed? | Publicly exported? | Importers to update |
|
||||
| ------------------- | --------------------------- | -------- | ------------------ | --------------------- |
|
||||
| `DialogTrigger.tsx` | `trigger/DialogTrigger.tsx` | No | Yes | `root/DialogRoot.tsx` |
|
||||
| ... | | | | |
|
||||
|
||||
For each file:
|
||||
|
||||
- Determine new path and whether it moves/renames
|
||||
- Check whether content needs splitting (e.g. types → `<Component>.types.ts`)
|
||||
- List every file that imports it (grep for the filename or exported symbol)
|
||||
- Flag circular dep risks: context importing sub-components, helpers importing component files, types importing non-types
|
||||
|
||||
Resolve circular deps before proceeding: move shared pieces to a lower-level file (types → `.types.ts`, utilities → `helpers/`).
|
||||
|
||||
### 3. Create New Files
|
||||
|
||||
For each row in the mapping table where the path changes:
|
||||
|
||||
1. Create the file at the new path with all import paths updated to reflect new locations
|
||||
2. Update every file in the "Importers to update" column — use the mapping table, don't rely on memory
|
||||
3. Do NOT delete old files yet
|
||||
|
||||
### 4. Update Stories
|
||||
|
||||
Move stories to component root dir. If stories import from internal paths, update those paths.
|
||||
|
||||
**Exception:** A `stories/` subdirectory is acceptable if it already exists — do not flatten it.
|
||||
|
||||
### 5. Update `index.ts`
|
||||
|
||||
The repo uses a mix of default and named exports because `react-docgen-typescript` (used by `scripts/docgen.ts`) has inconsistent pickup depending on export style. Follow the existing pattern in the component's original `index.ts` — do NOT change export style during restructure.
|
||||
|
||||
Typical pattern (main component as default re-export, sub-components as named):
|
||||
|
||||
```ts
|
||||
"use client";
|
||||
export {
|
||||
default as <Component>, // main component — default export from Root file
|
||||
<Component>Trigger, // sub-components — named exports
|
||||
// ...
|
||||
} from "./root/<Component>Root";
|
||||
export type {
|
||||
<Component>Props,
|
||||
<Component>TriggerProps,
|
||||
// ...
|
||||
} from "./root/<Component>Root";
|
||||
```
|
||||
|
||||
Some components export everything as named (no `default`). If the original had no default export, keep it that way. If types are in `<Component>.types.ts`, re-export them from there directly or via the root.
|
||||
|
||||
### 6. Validate No Breaking Changes
|
||||
|
||||
**Step 1 — Export diff.** Before deleting old files, extract all exported names from the original `index.ts` (recorded in step 1) and compare against the new `index.ts`. Every name must be present:
|
||||
|
||||
- Exports in new index.ts that are missing from original → regression
|
||||
- Exports in original that are missing from new index.ts → missing export (breaking change)
|
||||
|
||||
Concretely: grep for `export` lines in both versions and diff them. All component names and type names must match exactly.
|
||||
|
||||
**Step 2 — Build.** Run `yarn workspace @navikt/ds-react build` — must succeed with no TypeScript errors.
|
||||
|
||||
**Step 3 — Package root.** Verify `@navikt/core/react/src/index.ts` still re-exports the component. No changes needed there unless the component's `index.ts` path changed (it shouldn't).
|
||||
|
||||
### 7. Remove Old Files
|
||||
|
||||
Only after verifying new files are correct and `index.ts` exports match the original, delete the old files.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Unit tests live next to the file they test — same directory, same base name with `.test.ts` / `.test.tsx` suffix.
|
||||
|
||||
| What to test | File location |
|
||||
| ---------------------- | --------------------------------------------------- |
|
||||
| Helper utility | `helpers/<helper-name>.test.ts` |
|
||||
| Sub-component logic | `<subcomponent>/<Component><Subcomponent>.test.tsx` |
|
||||
| Root component / hooks | `root/<Component>Root.test.tsx` |
|
||||
| Context helpers | `root/<Component>Root.context.test.ts` |
|
||||
|
||||
Do NOT move existing test files unless their source file moves. When moving a source file, move its test file with it and update import paths.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
### Flat components without sub-components
|
||||
|
||||
Leave flat components flat - do NOT introduce subdirs unless the component contains internal utility-components (files that render JSX but are not public sub-components). If such a utility-component exists, move it to its own subdir following the canonical naming.
|
||||
|
||||
Example: `button/Button.tsx` has no other sub-components, so should stay in root.
|
||||
|
||||
### `parts/` directory
|
||||
|
||||
If the component uses a `parts/` dir, break it up into separate sub-component dirs.
|
||||
|
||||
### Internal sub-components
|
||||
|
||||
Sub-components that are implementation details (not exported publicly) follow the same dir convention as public ones, but the filename gets an `Internal` suffix:
|
||||
|
||||
```
|
||||
backdrop/
|
||||
└── DialogBackdropInternal.tsx ← internal, NOT exported from index.ts
|
||||
```
|
||||
|
||||
- **Dir name**: same pattern as public sub-components
|
||||
- **File name**: `<Component><Subcomponent>Internal.tsx`
|
||||
- **`index.ts`**: do NOT export it
|
||||
|
||||
### Nested subcomponents
|
||||
|
||||
Do NOT nest subcomponent dirs inside other subcomponent dirs. All sub-component dirs must be direct children of the component root:
|
||||
|
||||
```
|
||||
# Wrong
|
||||
dialog/
|
||||
└── header/
|
||||
├── DialogHeader.tsx
|
||||
└── title/
|
||||
└── DialogTitle.tsx
|
||||
|
||||
# Correct
|
||||
dialog/
|
||||
├── header/
|
||||
│ └── DialogHeader.tsx
|
||||
└── title/
|
||||
└── DialogTitle.tsx
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- **No breaking changes.** Public export names and prop shapes must be identical after restructuring.
|
||||
- **React 17 compatible.** No React 18/19-only APIs. Import React explicitly in `.tsx` files.
|
||||
- `index.ts` must start with `"use client"`.
|
||||
- Use tokens, not hardcoded values.
|
||||
- Preserve `forwardRef`, `className`, `...rest`, `as`/`OverridableComponent` patterns.
|
||||
- Keep JSDoc on public props and components.
|
||||
|
||||
## Reference Files
|
||||
|
||||
- Canonical example: `@navikt/core/react/src/dialog/`
|
||||
- Package public API: `@navikt/core/react/src/index.ts`
|
||||
- Component `index.ts` pattern: `@navikt/core/react/src/dialog/index.ts`
|
||||
- Context pattern: `@navikt/core/react/src/dialog/root/DialogRoot.context.ts`
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: E2E tests
|
||||
name: E2E tests (Playwright)
|
||||
on:
|
||||
push:
|
||||
branches: [main, next]
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
REGISTRY: ghcr.io
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
name: Playwright shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.60.0-noble
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
services:
|
||||
testapp:
|
||||
image: ${{ needs.prebuild-app-for-playwright.outputs.image }}
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
shardTotal: [5]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Validation
|
||||
name: Validation (Lint, Tests)
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -72,10 +72,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.60.0-noble
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Visual regression
|
||||
name: Visual regression (Chromatic)
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -37,7 +37,6 @@ jobs:
|
||||
npm_auth_token: ${{ secrets.READER_TOKEN }}
|
||||
|
||||
- name: Chromatic
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
uses: chromaui/action@latest
|
||||
id: chromatic_tests
|
||||
continue-on-error: false
|
||||
@@ -55,14 +54,14 @@ jobs:
|
||||
STORYBOOK_GITHUB_SHA: ${{ github.sha }}
|
||||
|
||||
- name: Get commit sha
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]') && github.actor != 'dependabot[bot]'
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Find Comment
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]') && github.actor != 'dependabot[bot]'
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
|
||||
uses: peter-evans/find-comment@v4
|
||||
id: fc
|
||||
with:
|
||||
@@ -71,7 +70,7 @@ jobs:
|
||||
body-includes: Storybook demo
|
||||
|
||||
- name: Create or update comment
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]') && github.actor != 'dependabot[bot]'
|
||||
if: github.event_name != 'push' && !contains(github.event.head_commit.message, '[ci skip]')
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout main branch
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: main
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
needs: [analyze-trunk, analyze-local]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: "actions/checkout@v7"
|
||||
uses: "actions/checkout@v6"
|
||||
|
||||
- name: Upload fonts to CDN
|
||||
id: upload
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
name: Deploy Aksel MCP server
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "mcp-server/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
IMAGE: ghcr.io/navikt/aksel
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
name: Build and deploy to prod-gcp
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
npm_auth_token: ${{ secrets.READER_TOKEN }}
|
||||
|
||||
- name: Test mcp-server
|
||||
run: cd mcp-server && yarn test
|
||||
|
||||
- name: Build mcp-server
|
||||
run: cd mcp-server && yarn build
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: nais/docker-build-push@v0
|
||||
id: docker-push
|
||||
with:
|
||||
team: designsystem
|
||||
dockerfile: mcp-server/Dockerfile
|
||||
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy to prod-gcp
|
||||
uses: nais/deploy/actions/deploy@master
|
||||
env:
|
||||
APIKEY: ${{ secrets.NAIS_DEPLOY_APIKEY }}
|
||||
RESOURCE: mcp-server/.nais/app.yaml
|
||||
CLUSTER: prod-gcp
|
||||
IMAGE: ${{ steps.docker-push.outputs.image }}
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -33,7 +33,6 @@ jobs:
|
||||
- name: Update Sanity
|
||||
env:
|
||||
SANITY_WRITE: ${{ secrets.SANITY_WRITE }}
|
||||
GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS_BQ_READ }}
|
||||
run: yarn workspace aksel.nav.no sanity:update
|
||||
|
||||
build_and_deploy:
|
||||
@@ -46,7 +45,7 @@ jobs:
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
id-token: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
pull-requests: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Build and push Docker image
|
||||
|
||||
@@ -98,6 +98,7 @@ examples/webpack/out
|
||||
.iml
|
||||
.DS_Store
|
||||
|
||||
*.d.ts
|
||||
*-exports.less
|
||||
*/figma-api/icons
|
||||
*/ds-icons/png
|
||||
@@ -135,6 +136,7 @@ _docs.json
|
||||
.next
|
||||
!*/website/lib/
|
||||
!*/website/public/
|
||||
!aksel.nav.no/**/*.d.ts
|
||||
!*/sanity/lib/
|
||||
!*/sanity/static/
|
||||
out
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint --edit $1
|
||||
@@ -17,17 +17,6 @@ const includeWebsiteStories = process.env.WITH_WEBSITE === "true";
|
||||
|
||||
const indexRegex = /export const args = {\s+index: (\d+),/;
|
||||
|
||||
const addons = [
|
||||
getAbsolutePath("@storybook/addon-a11y"),
|
||||
getAbsolutePath("@storybook/addon-themes"),
|
||||
getAbsolutePath("@storybook/addon-docs"),
|
||||
getAbsolutePath("@storybook/addon-vitest"),
|
||||
];
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
addons.push(getAbsolutePath("@github-ui/storybook-addon-performance-panel"));
|
||||
}
|
||||
|
||||
export default defineMain({
|
||||
experimental_indexers: (indexers) => {
|
||||
// Changes here might need to be reflected in aksel.nav.no/website/.storybook/main.ts
|
||||
@@ -90,7 +79,12 @@ export default defineMain({
|
||||
|
||||
stories: resolveStoriesPaths(),
|
||||
|
||||
addons,
|
||||
addons: [
|
||||
getAbsolutePath("@storybook/addon-a11y"),
|
||||
getAbsolutePath("@storybook/addon-themes"),
|
||||
getAbsolutePath("@storybook/addon-docs"),
|
||||
getAbsolutePath("@storybook/addon-vitest"),
|
||||
],
|
||||
|
||||
framework: {
|
||||
name: getAbsolutePath("@storybook/react-vite"),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import addonPerformancePanel from "@github-ui/storybook-addon-performance-panel";
|
||||
import addonA11y from "@storybook/addon-a11y";
|
||||
import addonDocs from "@storybook/addon-docs";
|
||||
import addonThemes, { withThemeByClassName } from "@storybook/addon-themes";
|
||||
import addonVitest from "@storybook/addon-vitest";
|
||||
import { definePreview } from "@storybook/react-vite";
|
||||
import React, { useEffect } from "react";
|
||||
import "../@navikt/core/css/src/data-table.css";
|
||||
import "../@navikt/core/css/src/data-token-filter.css";
|
||||
import "../@navikt/core/css/src/data-toolbar.css";
|
||||
import "../@navikt/core/css/src/index.css";
|
||||
@@ -58,17 +58,8 @@ const TypoDecorator = ({
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const addons = [addonA11y(), addonThemes(), addonDocs(), addonVitest()];
|
||||
|
||||
/**
|
||||
* Fixes flaky interaction tests
|
||||
*/
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
addons.push(addonPerformancePanel());
|
||||
}
|
||||
|
||||
export default definePreview({
|
||||
addons,
|
||||
addons: [addonA11y(), addonThemes(), addonDocs(), addonVitest()],
|
||||
parameters: {
|
||||
options: {
|
||||
storySort: {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
||||
import { setProjectAnnotations } from "@storybook/react-vite";
|
||||
import * as projectAnnotations from "./preview";
|
||||
|
||||
// This is an important step to apply the right configuration when testing your stories.
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
||||
@@ -1,33 +1,5 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 8.13.1
|
||||
|
||||
## 8.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Icons: New icons Armchair, FocusRoom, PhoneRoom and Sleep ([#4979](https://github.com/navikt/aksel/pull/4979))
|
||||
|
||||
## 8.12.1
|
||||
|
||||
## 8.12.0
|
||||
|
||||
## 8.11.1
|
||||
|
||||
## 8.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Icons: New icon `Newsletter` and `NewsletterFill` ([#4887](https://github.com/navikt/aksel/pull/4887))
|
||||
|
||||
## 8.10.6
|
||||
|
||||
## 8.10.5
|
||||
|
||||
## 8.10.4
|
||||
|
||||
## 8.10.3
|
||||
|
||||
## 8.10.2
|
||||
|
||||
## 8.10.1
|
||||
|
||||
@@ -28,7 +28,7 @@ describe(`SVG should be correctly parsed`, () => {
|
||||
</svg>
|
||||
`;
|
||||
|
||||
test("should replace width and height with 1em, and fill with currentColor", () => {
|
||||
test("a", () => {
|
||||
expect(parseIcon(simpleSvg)).toEqual(simpleSvgResult);
|
||||
expect(parseIcon(advancedSvg)).toEqual(advancedSvgResult);
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ fs.writeFileSync(
|
||||
id: string;
|
||||
name: string;
|
||||
category: string;
|
||||
sub_category: string;
|
||||
sub_category: "Time",
|
||||
keywords: string[];
|
||||
variant: "stroke" | "fill";
|
||||
created_at: Date;
|
||||
|
||||
@@ -22,4 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
!src
|
||||
!src
|
||||
!*.d.ts
|
||||
@@ -13,20 +13,20 @@
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "*",
|
||||
"@navikt/ds-react": "*",
|
||||
"fuse.js": "^7.4.0",
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6"
|
||||
"fuse.js": "^7.0.0",
|
||||
"react": "19.2.5",
|
||||
"react-dom": "19.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@figma/plugin-typings": "^1.128.0",
|
||||
"@figma/plugin-typings": "^1.100.2",
|
||||
"@svgr/core": "^8.1.0",
|
||||
"@types/node": "^24.10.9",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"typescript": "6.0.3",
|
||||
"vite": "^7.3.5",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"typescript": "6.0.2",
|
||||
"vite": "^7.3.2",
|
||||
"vite-plugin-singlefile": "^2.3.0",
|
||||
"vite-plugin-svgr": "^5.2.0"
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
}
|
||||
@@ -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="M15.8398 3.25C16.7677 3.25 17.5195 4.0335 17.5195 5V8.25H18.7197C19.6476 8.25 20.4004 9.0335 20.4004 10V16C20.4004 16.9665 19.6476 17.75 18.7197 17.75H16.8398L17.4834 19.7627C17.6091 20.1557 17.4046 20.5809 17.0273 20.7119C16.6502 20.8426 16.2419 20.6301 16.1162 20.2373L15.3203 17.75H8.67969L7.88379 20.2373C7.75809 20.6301 7.34978 20.8426 6.97266 20.7119C6.59542 20.5809 6.39086 20.1557 6.5166 19.7627L7.16016 17.75H5.28027C4.35244 17.75 3.59961 16.9665 3.59961 16V10C3.59961 9.0335 4.35244 8.25 5.28027 8.25H6.48047V5C6.48047 4.0335 7.23232 3.25 8.16016 3.25H15.8398ZM5.28027 9.75C5.14773 9.75 5.04004 9.86193 5.04004 10V16C5.04004 16.1381 5.14773 16.25 5.28027 16.25H18.7197C18.8523 16.25 18.96 16.1381 18.96 16V10C18.96 9.86193 18.8523 9.75 18.7197 9.75H16.7998C16.6673 9.75011 16.5596 9.862 16.5596 10V13C16.5596 13.4142 16.2375 13.75 15.8398 13.75H8.16016C7.76251 13.75 7.44043 13.4142 7.44043 13V10C7.44043 9.862 7.33265 9.75011 7.2002 9.75H5.28027ZM8.16016 4.75C8.02761 4.75 7.91992 4.86193 7.91992 5V8.41992C8.48722 8.7009 8.87988 9.30212 8.87988 10V12.25H15.1201V10C15.1201 9.30212 15.5128 8.7009 16.0801 8.41992V5C16.0801 4.86193 15.9724 4.75 15.8398 4.75H8.16016Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,7 +0,0 @@
|
||||
name: Armchair
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- Armchair
|
||||
variant: Stroke
|
||||
created_at: 15.06.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="M6.95508 9.45605C7.22008 9.45617 7.43457 9.67999 7.43457 9.95605V12.6582C7.43457 13.1 7.77897 13.458 8.20312 13.458H15.7812C16.2054 13.458 16.5498 13.1 16.5498 12.6582V9.95605C16.5498 9.67996 16.7643 9.45613 17.0293 9.45605H19.9209C20.186 9.45605 20.4014 9.67991 20.4014 9.95605V16.0596C20.4014 16.9432 19.7135 17.66 18.8652 17.6602H16.8262L17.4707 19.6738C17.5964 20.0666 17.3926 20.4919 17.0156 20.623C16.6384 20.754 16.2293 20.5414 16.1035 20.1484L15.3076 17.6602H8.66699L7.87109 20.1484C7.74537 20.5413 7.33716 20.7539 6.95996 20.623C6.58272 20.4921 6.37816 20.0668 6.50391 19.6738L7.14844 17.6602H5.11719C4.26891 17.6601 3.59961 16.9432 3.59961 16.0596V9.95605C3.59961 9.67991 3.81498 9.45605 4.08008 9.45605H6.95508ZM15.8271 3.33984C16.3572 3.33984 16.7869 3.83751 16.7871 4.38965V7.61523C16.787 7.80915 16.6364 7.96679 16.4502 7.9668C15.7053 7.9668 15.1017 8.59523 15.1016 9.37109V11.4541C15.1014 11.7301 14.8861 11.9541 14.6211 11.9541H9.36816C9.1032 11.9541 8.88791 11.7301 8.8877 11.4541V9.38379C8.8877 8.60107 8.27875 7.9668 7.52734 7.9668C7.33967 7.96669 7.18765 7.80778 7.1875 7.6123V4.38965C7.18769 3.83751 7.61738 3.33984 8.14746 3.33984H15.8271Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,7 +0,0 @@
|
||||
name: ArmchairFill
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- ArmchairFill
|
||||
variant: Fill
|
||||
created_at: 15.06.2026
|
||||
@@ -1,4 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.4695 11.0302C16.7624 11.3231 17.2372 11.3231 17.5301 11.0302L21.0301 7.53018C21.1707 7.38953 21.2498 7.19882 21.2498 6.9999C21.2498 6.80099 21.1707 6.61028 21.0301 6.46963L17.5301 2.96963C17.2372 2.67674 16.7624 2.67674 16.4695 2.96963C16.1766 3.26252 16.1766 3.73728 16.4695 4.03018L18.6892 6.2499H9.49979C9.08558 6.2499 8.74979 6.58569 8.74979 6.9999C8.74979 7.41412 9.08558 7.7499 9.49979 7.7499H18.6892L16.4695 9.96963C16.1766 10.2625 16.1766 10.7373 16.4695 11.0302Z" fill="#202733"/>
|
||||
<path d="M6.46952 21.0302C6.76241 21.3231 7.23717 21.3231 7.53006 21.0302C7.82296 20.7373 7.82296 20.2625 7.53006 19.9696L5.31034 17.7499H14.4998C14.914 17.7499 15.2498 17.4141 15.2498 16.9999C15.2498 16.5857 14.914 16.2499 14.4998 16.2499H5.31034L7.53006 14.0302C7.82296 13.7373 7.82296 13.2625 7.53006 12.9696C7.23717 12.6767 6.76241 12.6767 6.46952 12.9696L2.96952 16.4696C2.67662 16.7625 2.67662 17.2373 2.96952 17.5302L6.46952 21.0302Z" fill="#202733"/>
|
||||
<path d="M16.4695 11.0304C16.7624 11.3233 17.2371 11.3233 17.53 11.0304L21.03 7.53042C21.1707 7.38977 21.2498 7.19906 21.2498 7.00015C21.2498 6.80124 21.1707 6.61053 21.03 6.46988L17.53 2.96988C17.2371 2.67698 16.7624 2.67698 16.4695 2.96988C16.1766 3.26277 16.1766 3.73753 16.4695 4.03042L18.6892 6.25015L9.49976 6.25015C9.08555 6.25015 8.74976 6.58593 8.74976 7.00015C8.74976 7.41436 9.08555 7.75015 9.49976 7.75015L18.6892 7.75015L16.4695 9.96988C16.1766 10.2628 16.1766 10.7375 16.4695 11.0304ZM6.46949 21.0304C6.76238 21.3233 7.23714 21.3233 7.53003 21.0304C7.82293 20.7375 7.82293 20.2628 7.53003 19.9699L5.31031 17.7501H14.4998C14.914 17.7501 15.2498 17.4144 15.2498 17.0001C15.2498 16.5859 14.914 16.2501 14.4998 16.2501H5.31031L7.53003 14.0304C7.82293 13.7375 7.82293 13.2628 7.53003 12.9699C7.23714 12.677 6.76238 12.677 6.46949 12.9699L2.96949 16.4699C2.67659 16.7628 2.67659 17.2375 2.96949 17.5304L6.46949 21.0304Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,4 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 15.25C7.41422 15.25 7.75 14.9142 7.75 14.5V5.31055L9.96973 7.53028C10.2626 7.82317 10.7374 7.82317 11.0303 7.53028C11.3232 7.23738 11.3232 6.76262 11.0303 6.46973L7.53028 2.96973C7.23738 2.67684 6.76262 2.67684 6.46973 2.96973L2.96973 6.46973C2.67684 6.76262 2.67684 7.23738 2.96973 7.53028C3.26262 7.82317 3.73738 7.82317 4.03028 7.53028L6.25 5.31055V14.5C6.25 14.9142 6.58579 15.25 7 15.25Z" fill="#202733"/>
|
||||
<path d="M16.4697 21.0303C16.7626 21.3232 17.2374 21.3232 17.5303 21.0303L21.0303 17.5303C21.3232 17.2374 21.3232 16.7626 21.0303 16.4697C20.7374 16.1768 20.2626 16.1768 19.9697 16.4697L17.75 18.6895V9.5C17.75 9.08579 17.4142 8.75 17 8.75C16.5858 8.75 16.25 9.08579 16.25 9.5V18.6895L14.0303 16.4697C13.7374 16.1768 13.2626 16.1768 12.9697 16.4697C12.6768 16.7626 12.6768 17.2374 12.9697 17.5303L16.4697 21.0303Z" fill="#202733"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4697 21.0303C16.7626 21.3232 17.2374 21.3232 17.5303 21.0303L21.0303 17.5303C21.3232 17.2374 21.3232 16.7626 21.0303 16.4697C20.7374 16.1768 20.2626 16.1768 19.9697 16.4697L17.75 18.6893L17.75 9.5C17.75 9.08579 17.4142 8.75 17 8.75C16.5858 8.75 16.25 9.08579 16.25 9.5L16.25 18.6893L14.0303 16.4697C13.7374 16.1768 13.2626 16.1768 12.9697 16.4697C12.6768 16.7626 12.6768 17.2374 12.9697 17.5303L16.4697 21.0303ZM4.03033 7.53033L6.25 5.31066L6.25 14.5C6.25 14.9142 6.58579 15.25 7 15.25C7.41421 15.25 7.75 14.9142 7.75 14.5L7.75 5.31066L9.96967 7.53033C10.2626 7.82322 10.7374 7.82322 11.0303 7.53033C11.3232 7.23744 11.3232 6.76256 11.0303 6.46967L7.53033 2.96967C7.23744 2.67678 6.76256 2.67678 6.46967 2.96967L2.96967 6.46967C2.67678 6.76256 2.67678 7.23744 2.96967 7.53033C3.26256 7.82322 3.73744 7.82322 4.03033 7.53033Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 999 B |
@@ -1,4 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5 11.25C19.9142 11.25 20.25 10.9142 20.25 10.5V4.5C20.25 4.30109 20.1709 4.11038 20.0303 3.96973C19.8896 3.82907 19.6989 3.75 19.5 3.75H13.5C13.0858 3.75002 12.75 4.0858 12.75 4.5C12.75 4.9142 13.0858 5.24998 13.5 5.25H17.6895L12.9697 9.96973C12.6769 10.2626 12.6769 10.7374 12.9697 11.0303C13.2626 11.3232 13.7374 11.3232 14.0303 11.0303L18.75 6.31055V10.5C18.75 10.9142 19.0858 11.25 19.5 11.25Z" fill="#202733"/>
|
||||
<path d="M10.5 20.25C10.9142 20.25 11.25 19.9142 11.25 19.5C11.25 19.0858 10.9142 18.75 10.5 18.75H6.31055L11.0303 14.0303C11.3232 13.7374 11.3232 13.2626 11.0303 12.9697C10.7374 12.6768 10.2626 12.6768 9.96973 12.9697L5.25 17.6895V13.5C5.25 13.0858 4.91421 12.75 4.5 12.75C4.08579 12.75 3.75 13.0858 3.75 13.5V19.5C3.75 19.6989 3.82907 19.8896 3.96973 20.0303C4.11038 20.1709 4.30109 20.25 4.5 20.25H10.5Z" fill="#202733"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 19.5C11.25 19.9142 10.9143 20.25 10.5 20.25H4.50003C4.30112 20.25 4.11035 20.171 3.9697 20.0303C3.82905 19.8897 3.75003 19.6989 3.75003 19.5L3.75003 13.5C3.75003 13.0858 4.08582 12.75 4.50003 12.75C4.91424 12.75 5.25003 13.0858 5.25003 13.5L5.25003 17.6894L9.96971 12.9697C10.2626 12.6768 10.7375 12.6768 11.0304 12.9697C11.3233 13.2626 11.3233 13.7374 11.0304 14.0303L6.3107 18.75L10.5 18.75C10.9143 18.75 11.25 19.0858 11.25 19.5ZM19.5 11.25C19.0858 11.25 18.75 10.9142 18.75 10.5L18.75 6.31066L14.0304 11.0303C13.7375 11.3232 13.2626 11.3232 12.9697 11.0303C12.6768 10.7374 12.6768 10.2626 12.9697 9.96967L17.6894 5.24998L13.5 5.24998C13.0858 5.24998 12.75 4.9142 12.75 4.49998C12.75 4.08577 13.0858 3.74998 13.5 3.74998L19.5 3.74998C19.6989 3.74998 19.8897 3.829 20.0304 3.96965C20.171 4.11031 20.25 4.30107 20.25 4.49998L20.25 10.5C20.25 10.9142 19.9142 11.25 19.5 11.25Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 1.0 KiB |
@@ -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="M6.71973 10.25C7.11737 10.25 7.44043 10.5858 7.44043 11V15C7.44043 15.138 7.54728 15.2498 7.67969 15.25H10.0801C11.0079 15.25 11.7598 16.0335 11.7598 17V21C11.7598 21.4142 11.4377 21.75 11.04 21.75C10.6424 21.75 10.3203 21.4142 10.3203 21V17C10.3203 16.862 10.2126 16.75 10.0801 16.75H7.67969C6.75199 16.7498 6 15.9664 6 15V11C6 10.5859 6.32221 10.2502 6.71973 10.25ZM18.7197 11.25C19.1174 11.25 19.4404 11.5858 19.4404 12C19.4404 12.4142 19.1174 12.75 18.7197 12.75H16.0801V21C16.0801 21.4141 15.7578 21.7498 15.3604 21.75C14.9627 21.75 14.6396 21.4142 14.6396 21V12.75H11.5195C11.1221 12.7497 10.7998 12.4141 10.7998 12C10.7998 11.5859 11.1221 11.2503 11.5195 11.25H18.7197ZM18.5742 5.62793C18.7715 5.26833 19.2124 5.14321 19.5576 5.34863C19.9026 5.55421 20.0223 6.01256 19.8252 6.37207L17.6982 10.25H13.9199C13.5223 10.25 13.2002 9.91419 13.2002 9.5C13.2002 9.08581 13.5223 8.75004 13.9199 8.75H16.8623L18.5742 5.62793ZM6.71973 3C8.31031 3 9.59961 4.34315 9.59961 6C9.59961 7.65685 8.31031 9 6.71973 9C5.12927 8.99985 3.83984 7.65676 3.83984 6C3.83984 4.34324 5.12927 3.00015 6.71973 3ZM6.71973 4.5C5.92456 4.50015 5.28027 5.17167 5.28027 6C5.28027 6.82833 5.92456 7.49985 6.71973 7.5C7.51502 7.5 8.16016 6.82843 8.16016 6C8.16016 5.17157 7.51502 4.5 6.71973 4.5Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,7 +0,0 @@
|
||||
name: FocusRoom
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- FocusRoom
|
||||
variant: Stroke
|
||||
created_at: 15.06.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="M6.71973 10.25C7.11737 10.25 7.44043 10.5858 7.44043 11V15C7.44043 15.138 7.54728 15.2498 7.67969 15.25H10.0801C11.0079 15.25 11.7598 16.0335 11.7598 17V21C11.7598 21.4142 11.4377 21.75 11.04 21.75C10.6424 21.75 10.3203 21.4142 10.3203 21V17C10.3203 16.862 10.2126 16.75 10.0801 16.75H7.67969C6.75199 16.7498 6 15.9664 6 15V11C6 10.5859 6.32221 10.2502 6.71973 10.25ZM18.7197 11.25C19.1174 11.25 19.4404 11.5858 19.4404 12C19.4404 12.4142 19.1174 12.75 18.7197 12.75H16.0801V21C16.0801 21.4141 15.7578 21.7498 15.3604 21.75C14.9627 21.75 14.6396 21.4142 14.6396 21V12.75H11.5195C11.1221 12.7497 10.7998 12.4141 10.7998 12C10.7998 11.5859 11.1221 11.2503 11.5195 11.25H18.7197ZM18.5742 5.62793C18.7715 5.26833 19.2124 5.14321 19.5576 5.34863C19.9026 5.55421 20.0223 6.01256 19.8252 6.37207L17.6982 10.25H13.9199C13.5223 10.25 13.2002 9.91419 13.2002 9.5C13.2002 9.08581 13.5223 8.75004 13.9199 8.75H16.8623L18.5742 5.62793ZM6.71973 3C8.31031 3 9.59961 4.34315 9.59961 6C9.59961 7.65685 8.31031 9 6.71973 9C5.12927 8.99985 3.83984 7.65676 3.83984 6C3.83984 4.34324 5.12927 3.00015 6.71973 3Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,7 +0,0 @@
|
||||
name: FocusRoomFill
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- FocusRoomFill
|
||||
variant: Fill
|
||||
created_at: 15.06.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="M18.25 3C18.4489 3 18.6396 3.07907 18.7803 3.21973C18.9209 3.36038 19 3.55109 19 3.75V7.18848L21.0303 8.91309C21.1709 9.05362 21.2499 9.2446 21.25 9.44336L21.2012 20.2461C21.2009 20.6601 20.8652 20.9961 20.4512 20.9961L3.5 20.9824C3.08579 20.9824 2.75 20.6466 2.75 20.2324V9.43066C2.75007 9.23181 2.82908 9.04099 2.96973 8.90039L5 7.18848V3.75C5 3.33579 5.33579 3 5.75 3H18.25ZM5.38086 19.5508H18.6191L13.6553 14.249H10.3447L5.38086 19.5508ZM15.1201 13.6533L19.75 18.3428L19.7012 10.9424L15.1201 13.6533ZM4.29883 18.2324L8.87891 13.6533L4.25 10.832L4.29883 18.2324ZM6.5 10.4307L10.2266 12.749H13.7734L17.5 10.4307V4.5H6.5V10.4307ZM13 9C13.4142 9 13.75 9.33579 13.75 9.75C13.75 10.1642 13.4142 10.5 13 10.5H9C8.58579 10.5 8.25 10.1642 8.25 9.75C8.25 9.33579 8.58579 9 9 9H13ZM15 6C15.4142 6 15.75 6.33579 15.75 6.75C15.75 7.16421 15.4142 7.5 15 7.5H9C8.58579 7.5 8.25 7.16421 8.25 6.75C8.25 6.33579 8.58579 6 9 6H15Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,16 +0,0 @@
|
||||
name: Newsletter
|
||||
category: Interface
|
||||
sub_category: Communication
|
||||
keywords:
|
||||
- email
|
||||
- message
|
||||
- communication
|
||||
- send
|
||||
- e-post
|
||||
- melding
|
||||
- kommunikasjon
|
||||
- sende
|
||||
- lest
|
||||
- read
|
||||
variant: Stroke
|
||||
created_at: 28.04.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="M13.0068 14.5024C13.2715 14.5026 13.5255 14.6075 13.7129 14.7944L19.5017 20.5706C19.6595 20.728 19.548 20.9975 19.3251 20.9975L4.65308 20.9966C4.43081 20.9966 4.31908 20.7283 4.47566 20.5705L10.2051 14.7983C10.3927 14.6093 10.6477 14.5026 10.9141 14.5024H13.0068ZM20.998 19.3451C20.998 19.5675 20.7293 19.6792 20.5717 19.5222L15.1631 14.1372C15.0485 14.0231 15.0712 13.8313 15.209 13.7466L20.6171 10.4212C20.7837 10.3188 20.998 10.4387 20.998 10.6342V19.3451ZM8.71191 13.7466C8.84841 13.8314 8.8701 14.0213 8.75684 14.1353L3.4293 19.4971C3.27204 19.6554 3.00195 19.5441 3.00195 19.3209V10.6475C3.00195 10.4514 3.21739 10.3316 3.38392 10.4352L8.71191 13.7466ZM18.248 3.00244C18.4468 3.00256 18.6378 3.08162 18.7783 3.22217C18.9188 3.3628 18.998 3.55363 18.998 3.75244V7.35922C18.998 7.42875 19.027 7.49514 19.078 7.54245L20.1735 8.55951C20.2948 8.67212 20.2752 8.86939 20.1341 8.95588L13.7725 12.855C13.6152 12.9513 13.4344 13.0024 13.25 13.0024H10.6699C10.4841 13.0024 10.3016 12.9508 10.1436 12.853L3.85591 8.96112C3.71561 8.87428 3.69644 8.67773 3.81731 8.56541L4.91822 7.54244C4.96913 7.49514 4.99805 7.42878 4.99805 7.3593V3.75244C4.99805 3.33837 5.33403 3.00268 5.74805 3.00244H18.248ZM8.99609 9.00244C8.58188 9.00244 8.24609 9.33823 8.24609 9.75244C8.24609 10.1667 8.58188 10.5024 8.99609 10.5024H12.9961C13.4103 10.5024 13.7461 10.1667 13.7461 9.75244C13.7461 9.33823 13.4103 9.00244 12.9961 9.00244H8.99609ZM8.99609 6.00244C8.58188 6.00244 8.24609 6.33823 8.24609 6.75244C8.24609 7.16666 8.58188 7.50244 8.99609 7.50244H14.9961C15.4103 7.50244 15.7461 7.16666 15.7461 6.75244C15.7461 6.33823 15.4103 6.00244 14.9961 6.00244H8.99609Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,16 +0,0 @@
|
||||
name: NewsletterFill
|
||||
category: Interface
|
||||
sub_category: Communication
|
||||
keywords:
|
||||
- email
|
||||
- message
|
||||
- communication
|
||||
- send
|
||||
- e-post
|
||||
- melding
|
||||
- kommunikasjon
|
||||
- sende
|
||||
- lest
|
||||
- read
|
||||
variant: Fill
|
||||
created_at: 28.04.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="M5.20338 4.77244C5.69995 4.27551 6.50465 4.27552 7.00123 4.77244L10.4817 8.25486C10.978 8.75174 10.978 9.55681 10.4817 10.0537L9.011 11.5254L12.8313 15.3476L14.302 13.8769C14.7986 13.3802 15.6033 13.3802 16.0999 13.8769L19.2297 17.0088C19.7263 17.5057 19.7263 18.3116 19.2297 18.8086L16.7717 21.2695C15.9968 22.045 14.8057 22.223 13.8381 21.708L11.3635 20.3896C7.92226 18.5574 5.12439 15.7139 3.34694 12.2422L2.27467 10.1484C1.78142 9.18501 1.96495 8.01254 2.72975 7.24705L5.20338 4.77244ZM3.80885 8.32712C3.5125 8.62391 3.44099 9.07867 3.63209 9.45212L4.70436 11.5459C6.33963 14.7399 8.91432 17.3563 12.0803 19.042L14.554 20.3594C14.9292 20.5591 15.3921 20.4902 15.6926 20.1894L17.971 17.9092L15.2005 15.1367L13.3704 16.9677C13.0725 17.2656 12.5901 17.2656 12.2922 16.9677L7.39284 12.0654C7.09489 11.7672 7.09489 11.2835 7.39284 10.9853L9.22291 9.15427L6.1028 6.0322L3.80885 8.32712ZM12.5198 2.03123C15.1966 1.833 17.7522 2.57398 19.5891 4.41209C21.4261 6.25041 22.1672 8.80829 21.969 11.4873C21.9376 11.9074 21.5715 12.2222 21.1516 12.1914C20.7318 12.16 20.4167 11.7941 20.4475 11.374C20.62 9.04017 19.9703 6.95274 18.511 5.49216C17.0515 4.03157 14.9654 3.38104 12.6331 3.55369C12.2132 3.58471 11.8472 3.26967 11.8157 2.84959C11.7846 2.42926 12.0999 2.06265 12.5198 2.03123ZM13.6438 5.98826C14.8638 5.96973 15.9043 6.4827 16.7122 7.29099C17.5201 8.09947 18.0334 9.14124 18.0149 10.3623C18.0083 10.7836 17.6615 11.1204 17.2405 11.1142C16.8195 11.1077 16.4825 10.7611 16.4885 10.3398C16.4999 9.59681 16.1987 8.93711 15.6331 8.37107C15.0675 7.80517 14.4086 7.50328 13.6663 7.51462C13.2453 7.52069 12.8993 7.18406 12.8928 6.76267C12.8867 6.34138 13.2228 5.99483 13.6438 5.98826Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,16 +0,0 @@
|
||||
name: PhoneRoom
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- veiledning
|
||||
- mobil
|
||||
- telefon
|
||||
- call
|
||||
- ringe
|
||||
- ringer
|
||||
- hjelp
|
||||
- snakke
|
||||
- talk
|
||||
- help
|
||||
variant: Stroke
|
||||
created_at: 15.06.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="M5.20338 4.77244C5.69995 4.27551 6.50465 4.27552 7.00123 4.77244L10.4817 8.25486C10.978 8.75174 10.978 9.55681 10.4817 10.0537L9.011 11.5254L12.8313 15.3476L14.302 13.8769C14.7986 13.3802 15.6033 13.3802 16.0999 13.8769L19.2297 17.0088C19.7263 17.5057 19.7263 18.3116 19.2297 18.8086L16.7717 21.2695C15.9968 22.045 14.8057 22.223 13.8381 21.708L11.3635 20.3896C7.92226 18.5574 5.12439 15.7139 3.34694 12.2422L2.27467 10.1484C1.78142 9.18501 1.96495 8.01254 2.72975 7.24705L5.20338 4.77244ZM12.5198 2.03123C15.1966 1.833 17.7522 2.57398 19.5891 4.41209C21.4261 6.25041 22.1672 8.80829 21.969 11.4873C21.9376 11.9074 21.5715 12.2222 21.1516 12.1914C20.7318 12.16 20.4167 11.7941 20.4475 11.374C20.62 9.04017 19.9703 6.95274 18.511 5.49216C17.0515 4.03157 14.9654 3.38104 12.6331 3.55369C12.2132 3.58471 11.8472 3.26967 11.8157 2.84959C11.7846 2.42926 12.0999 2.06265 12.5198 2.03123ZM13.6438 5.98826C14.8638 5.96973 15.9043 6.4827 16.7122 7.29099C17.5201 8.09947 18.0334 9.14124 18.0149 10.3623C18.0083 10.7836 17.6615 11.1204 17.2405 11.1142C16.8195 11.1077 16.4825 10.7611 16.4885 10.3398C16.4999 9.59681 16.1987 8.93711 15.6331 8.37107C15.0675 7.80517 14.4086 7.50328 13.6663 7.51462C13.2453 7.52069 12.8993 7.18406 12.8928 6.76267C12.8867 6.34138 13.2228 5.99483 13.6438 5.98826Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,16 +0,0 @@
|
||||
name: PhoneRoomFill
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- veiledning
|
||||
- mobil
|
||||
- telefon
|
||||
- call
|
||||
- ringe
|
||||
- ringer
|
||||
- hjelp
|
||||
- snakke
|
||||
- talk
|
||||
- help
|
||||
variant: Fill
|
||||
created_at: 15.06.2026
|
||||
@@ -1,4 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5 11.25C19.9142 11.25 20.25 10.9142 20.25 10.5C20.25 10.0858 19.9142 9.75 19.5 9.75H15.3105L20.0303 5.03027C20.3232 4.73738 20.3232 4.26262 20.0303 3.96973C19.7374 3.67685 19.2626 3.67684 18.9697 3.96973L14.25 8.68945V4.5C14.25 4.08579 13.9142 3.75 13.5 3.75C13.0858 3.75001 12.75 4.08579 12.75 4.5V10.5C12.75 10.9142 13.0858 11.25 13.5 11.25H19.5Z" fill="#202733"/>
|
||||
<path d="M10.5 20.25C10.9142 20.25 11.25 19.9142 11.25 19.5V13.5C11.25 13.3011 11.1709 13.1104 11.0303 12.9697C10.8896 12.8291 10.6989 12.75 10.5 12.75H4.5C4.08579 12.75 3.75 13.0858 3.75 13.5C3.75001 13.9142 4.0858 14.25 4.5 14.25H8.68945L3.96973 18.9697C3.67685 19.2626 3.67685 19.7374 3.96973 20.0303C4.26261 20.3232 4.73738 20.3232 5.03027 20.0303L9.75 15.3105V19.5C9.75 19.9142 10.0858 20.25 10.5 20.25Z" fill="#202733"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 20.25C10.0858 20.25 9.74998 19.9142 9.74998 19.5L9.74999 15.3107L5.03035 20.0303C4.73745 20.3232 4.26258 20.3232 3.96969 20.0303C3.67679 19.7374 3.67679 19.2626 3.96968 18.9697L8.68935 14.25L4.5 14.25C4.08579 14.25 3.75 13.9142 3.75 13.5C3.75 13.0858 4.08579 12.75 4.5 12.75L10.5 12.75C10.6989 12.75 10.8897 12.829 11.0303 12.9697C11.171 13.1103 11.25 13.3011 11.25 13.5L11.25 19.5C11.25 19.9142 10.9142 20.25 10.5 20.25ZM20.25 10.5C20.25 10.9142 19.9142 11.25 19.5 11.25H13.5C13.0858 11.25 12.75 10.9142 12.75 10.5V4.5C12.75 4.08579 13.0858 3.75 13.5 3.75C13.9142 3.75 14.25 4.08579 14.25 4.5V8.68935L18.9697 3.96967C19.2626 3.67678 19.7375 3.67678 20.0303 3.96967C20.3232 4.26256 20.3232 4.73744 20.0303 5.03033L15.3107 9.74999H19.5C19.9142 9.74999 20.25 10.0858 20.25 10.5Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 910 B After Width: | Height: | Size: 953 B |
@@ -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="M12.0303 3.35547C12.4444 3.35547 12.7801 3.69137 12.7803 4.10547C12.7803 4.51968 12.4445 4.85547 12.0303 4.85547C7.73347 4.85555 4.25023 8.33897 4.25 12.6357C4.25 16.9327 7.73333 20.4159 12.0303 20.416C16.3273 20.416 19.8105 16.9328 19.8105 12.6357C19.8105 11.671 19.6352 10.7489 19.3154 9.89844C19.1698 9.51078 19.3663 9.07739 19.7539 8.93164C20.1415 8.78614 20.5739 8.98267 20.7197 9.37012C21.102 10.3868 21.3105 11.4879 21.3105 12.6357C21.3105 17.7612 17.1557 21.916 12.0303 21.916C6.90491 21.9159 2.75 17.7611 2.75 12.6357C2.75023 7.51055 6.90504 3.35555 12.0303 3.35547ZM9.87988 12.79C10.1727 12.4972 10.6475 12.4973 10.9404 12.79C11.2333 13.0829 11.2333 13.5577 10.9404 13.8506C9.62972 15.1608 7.50487 15.1611 6.19434 13.8506C5.9016 13.5578 5.90181 13.0829 6.19434 12.79C6.48722 12.4972 6.96199 12.4972 7.25488 12.79C7.97963 13.5148 9.15496 13.5145 9.87988 12.79ZM16.791 12.79C17.0839 12.4972 17.5587 12.4972 17.8516 12.79C18.1443 13.0829 18.1444 13.5577 17.8516 13.8506C16.5409 15.161 14.4161 15.161 13.1055 13.8506C12.8126 13.5577 12.8127 13.0829 13.1055 12.79C13.3984 12.4971 13.8731 12.4971 14.166 12.79C14.8908 13.5147 16.0662 13.5146 16.791 12.79ZM15.6631 5.19824C15.9395 5.19839 16.1938 5.35102 16.3242 5.59473C16.4545 5.83841 16.4402 6.13424 16.2871 6.36426L15.2217 7.96289H15.6631C16.077 7.96311 16.4129 8.29896 16.4131 8.71289C16.4129 9.1268 16.077 9.46267 15.6631 9.46289H13.8203C13.5437 9.46289 13.2897 9.31025 13.1592 9.06641C13.0288 8.82264 13.043 8.52696 13.1963 8.29688L14.2617 6.69824H13.8203C13.4061 6.69824 13.0703 6.36246 13.0703 5.94824C13.0705 5.53418 13.4062 5.19824 13.8203 5.19824H15.6631ZM20.0859 2.25C20.3668 2.2501 20.6244 2.4075 20.7529 2.65723C20.8811 2.90679 20.8592 3.20713 20.6963 3.43555L19.2402 5.47461H20.0859C20.4999 5.47475 20.8357 5.81066 20.8359 6.22461C20.8359 6.63874 20.5 6.97447 20.0859 6.97461H17.7822C17.5013 6.97461 17.2438 6.81808 17.1152 6.56836C16.9867 6.31856 17.0086 6.01767 17.1719 5.78906L18.6289 3.75H17.7822C17.368 3.75 17.0322 3.41421 17.0322 3C17.0322 2.58579 17.368 2.25 17.7822 2.25H20.0859Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,12 +0,0 @@
|
||||
name: Sleep
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- waiting room
|
||||
- venterom
|
||||
- vente
|
||||
- venting
|
||||
- tid
|
||||
- pause
|
||||
variant: Stroke
|
||||
created_at: 15.06.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="M12.0303 3.08301C12.7306 3.08301 13.4132 3.15958 14.0703 3.30371C14.2228 3.33716 14.1962 3.53125 14.04 3.53125C12.8746 3.53125 11.9173 4.41718 11.8018 5.55078L11.79 5.78027C11.79 6.23766 11.9282 6.6626 12.1631 7.01758C12.2192 7.10242 12.2245 7.21328 12.168 7.29785C11.7082 7.98803 11.6653 8.87606 12.0566 9.60742C12.4474 10.3372 13.2086 10.7959 14.04 10.7959H15.8838C17.1252 10.7951 18.1321 9.78853 18.1328 8.54688V8.54492C18.1327 8.418 18.2306 8.30788 18.3574 8.30762H20.3066C20.4283 8.30755 20.544 8.36793 20.5967 8.47754C21.1936 9.72036 21.5303 11.1121 21.5303 12.583C21.5303 17.8297 17.277 22.083 12.0303 22.083C6.78368 22.0829 2.53027 17.8296 2.53027 12.583C2.53027 7.33638 6.78368 3.08314 12.0303 3.08301ZM10.9482 12.623C10.6554 12.3303 10.1806 12.3304 9.8877 12.623C9.16282 13.3474 7.98745 13.3476 7.2627 12.623C6.96985 12.3302 6.49504 12.3303 6.20215 12.623C5.90962 12.916 5.90941 13.3908 6.20215 13.6836C7.51269 14.994 9.63758 14.9938 10.9482 13.6836C11.2409 13.3907 11.2409 12.9159 10.9482 12.623ZM17.8594 12.623C17.5665 12.3303 17.0917 12.3303 16.7988 12.623C16.074 13.3476 14.8987 13.3475 14.1738 12.623C13.881 12.3302 13.4062 12.3303 13.1133 12.623C12.8206 12.916 12.8204 13.3908 13.1133 13.6836C14.4239 14.9939 16.5488 14.9939 17.8594 13.6836C18.152 13.3908 18.1519 12.9159 17.8594 12.623ZM15.8828 5.03125C16.1592 5.03142 16.4135 5.18405 16.5439 5.42773C16.6742 5.6714 16.6599 5.96726 16.5068 6.19727L15.4414 7.7959H15.8828C16.2967 7.79615 16.6326 8.13199 16.6328 8.5459C16.6328 8.64938 16.6122 8.74819 16.5742 8.83789C16.5362 8.92758 16.4809 9.00831 16.4131 9.07617C16.2942 9.19503 16.1355 9.27406 15.959 9.29199C15.9339 9.29454 15.9085 9.29588 15.8828 9.2959H14.04C13.798 9.2959 13.5734 9.1789 13.4336 8.98633C13.4136 8.95882 13.3952 8.92989 13.3789 8.89941C13.3626 8.86893 13.3487 8.83755 13.3369 8.80566C13.2662 8.61441 13.2763 8.40227 13.3652 8.21875C13.38 8.18827 13.3969 8.15855 13.416 8.12988L14.4814 6.53125H14.04C13.6258 6.53125 13.29 6.19546 13.29 5.78125C13.2902 5.36718 13.6259 5.03125 14.04 5.03125H15.8828ZM20.3057 2.08301C20.5865 2.08313 20.8442 2.24053 20.9727 2.49023C21.1008 2.73978 21.0789 3.04015 20.916 3.26855L19.46 5.30762H20.3057C20.7196 5.30779 21.0555 5.64369 21.0557 6.05762C21.0557 6.47172 20.7197 6.80744 20.3057 6.80762H18.002C17.8966 6.80762 17.7944 6.78583 17.7012 6.74512C17.6081 6.70447 17.524 6.6449 17.4541 6.57031C17.4306 6.54527 17.4087 6.5186 17.3887 6.49023C17.3689 6.46216 17.351 6.43245 17.335 6.40137C17.2868 6.30774 17.2593 6.20696 17.2529 6.10547C17.2444 5.97005 17.273 5.83335 17.3369 5.71094C17.3529 5.68034 17.3712 5.65065 17.3916 5.62207L18.8486 3.58301H18.002C17.5877 3.58301 17.252 3.24722 17.252 2.83301C17.252 2.41879 17.5877 2.08301 18.002 2.08301H20.3057Z" fill="#202733"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,12 +0,0 @@
|
||||
name: SleepFill
|
||||
category: Workplace
|
||||
sub_category: Workplace
|
||||
keywords:
|
||||
- waiting room
|
||||
- venterom
|
||||
- vente
|
||||
- venting
|
||||
- tid
|
||||
- pause
|
||||
variant: Fill
|
||||
created_at: 15.06.2026
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "8.13.1",
|
||||
"version": "8.10.2",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
@@ -78,17 +78,18 @@
|
||||
"adm-zip": "^0.5.10",
|
||||
"concurrently": "9.2.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-zip-cli": "1.0.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"hast-util-select": "6.0.4",
|
||||
"js-yaml": "^4.1.1",
|
||||
"lodash": "4.18.1",
|
||||
"prettier": "3.8.3",
|
||||
"react": "19.2.6",
|
||||
"prettier": "3.8.1",
|
||||
"react": "19.2.5",
|
||||
"rehype-parse": "9.0.1",
|
||||
"tsc-alias": "1.8.16",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "6.0.3",
|
||||
"typescript": "6.0.2",
|
||||
"unified": "11.0.5",
|
||||
"vitest": "4.1.8"
|
||||
"vitest": "4.1.4"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,5 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 8.13.1
|
||||
|
||||
## 8.13.0
|
||||
|
||||
## 8.12.1
|
||||
|
||||
## 8.12.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Stylelint: Fixed bug in aksel-no-deprecated-classes rule skipping some classes ([#4920](https://github.com/navikt/aksel/pull/4920))
|
||||
|
||||
## 8.11.1
|
||||
|
||||
## 8.11.0
|
||||
|
||||
## 8.10.6
|
||||
|
||||
## 8.10.5
|
||||
|
||||
## 8.10.4
|
||||
|
||||
## 8.10.3
|
||||
|
||||
## 8.10.2
|
||||
|
||||
## 8.10.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "8.13.1",
|
||||
"version": "8.10.2",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
@@ -30,8 +30,7 @@
|
||||
"./recommended": "./dist/recommended.js"
|
||||
},
|
||||
"files": [
|
||||
"/dist",
|
||||
"!**/*.test.js"
|
||||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "yarn build && node --import tsx --test ./dist/**/*.test.js",
|
||||
@@ -43,14 +42,14 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^8.13.1",
|
||||
"@navikt/ds-tokens": "^8.13.1",
|
||||
"@navikt/ds-css": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"concurrently": "9.2.1",
|
||||
"postcss-selector-parser": "^7.1.0",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"stylelint": "^17.2.0",
|
||||
"stylelint-test-rule-node": "^1.0.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "6.0.3"
|
||||
"typescript": "6.0.2"
|
||||
}
|
||||
}
|
||||
@@ -51,17 +51,5 @@ testRule({
|
||||
column: 3,
|
||||
endColumn: 38,
|
||||
},
|
||||
{
|
||||
code: ".foo .aksel-read-more__content--closed {}",
|
||||
description: "selector with deprecated read-more closed class",
|
||||
message: messages.unexpected(
|
||||
"aksel-read-more__content--closed",
|
||||
deprecations[2].message,
|
||||
),
|
||||
line: 1,
|
||||
endLine: 1,
|
||||
column: 7,
|
||||
endColumn: 39,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -26,7 +26,7 @@ const ruleFunction: stylelint.Rule = () => {
|
||||
deprecation.deprecatePrefix &&
|
||||
!deprecation.classes.some((x) => className.value.startsWith(x))
|
||||
) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
stylelint.utils.report({
|
||||
message: messages.unexpected(
|
||||
|
||||
@@ -25,7 +25,7 @@ testRule({
|
||||
line: 1,
|
||||
endLine: 1,
|
||||
column: 8,
|
||||
endColumn: 19,
|
||||
endColumn: 25,
|
||||
},
|
||||
{
|
||||
code: ".foo { color: var(--__axc-bar) }",
|
||||
|
||||
@@ -47,7 +47,6 @@ const ruleFunction: stylelint.Rule = () => {
|
||||
node,
|
||||
result: postcssResult,
|
||||
ruleName,
|
||||
word: node.prop,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,85 +1,5 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 8.13.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@8.13.1
|
||||
- @navikt/ds-tokens@8.13.1
|
||||
|
||||
## 8.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`386cbcb`](https://github.com/navikt/aksel/commit/386cbcbd1df687b44a7d2013c4673b3dafcc645f), [`7acd2a4`](https://github.com/navikt/aksel/commit/7acd2a4416ca30f9053502ab3b9461b4e919f0c1)]:
|
||||
- @navikt/ds-css@8.13.0
|
||||
- @navikt/ds-tokens@8.13.0
|
||||
|
||||
## 8.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0e9547e`](https://github.com/navikt/aksel/commit/0e9547ecab76ea7548c0525b33a68e5733b2a73a), [`0e9547e`](https://github.com/navikt/aksel/commit/0e9547ecab76ea7548c0525b33a68e5733b2a73a)]:
|
||||
- @navikt/ds-css@8.12.1
|
||||
- @navikt/ds-tokens@8.12.1
|
||||
|
||||
## 8.12.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`366d72f`](https://github.com/navikt/aksel/commit/366d72feebc4aa6d297eaa0051144b4b60b853a6)]:
|
||||
- @navikt/ds-css@8.12.0
|
||||
- @navikt/ds-tokens@8.12.0
|
||||
|
||||
## 8.11.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`54634ff`](https://github.com/navikt/aksel/commit/54634ffed97000a96b375820a4693413ddb78066), [`a375059`](https://github.com/navikt/aksel/commit/a375059cdb983c95c0ab6004cc81e5eee3516833)]:
|
||||
- @navikt/ds-tokens@8.11.1
|
||||
- @navikt/ds-css@8.11.1
|
||||
|
||||
## 8.11.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`dc5299b`](https://github.com/navikt/aksel/commit/dc5299b1f95bd53ae0b0cd9c48aa20c87eb1438f)]:
|
||||
- @navikt/ds-css@8.11.0
|
||||
- @navikt/ds-tokens@8.11.0
|
||||
|
||||
## 8.10.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1f39de4`](https://github.com/navikt/aksel/commit/1f39de488fa3b1942c5e81cf22da3cf4c93bde26)]:
|
||||
- @navikt/ds-css@8.10.6
|
||||
- @navikt/ds-tokens@8.10.6
|
||||
|
||||
## 8.10.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`35295e7`](https://github.com/navikt/aksel/commit/35295e7c010bd4866c3cd4ae9ff72641a234b089), [`0522dfa`](https://github.com/navikt/aksel/commit/0522dfae59320477b10cab2941d0b004d280d79a)]:
|
||||
- @navikt/ds-css@8.10.5
|
||||
- @navikt/ds-tokens@8.10.5
|
||||
|
||||
## 8.10.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-css@8.10.4
|
||||
- @navikt/ds-tokens@8.10.4
|
||||
|
||||
## 8.10.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`92f6d9e`](https://github.com/navikt/aksel/commit/92f6d9e93b879962df8415f7fad22203174dd3a8)]:
|
||||
- @navikt/ds-css@8.10.3
|
||||
- @navikt/ds-tokens@8.10.3
|
||||
|
||||
## 8.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "8.13.1",
|
||||
"version": "8.10.2",
|
||||
"description": "Aksel command line interface. Codemods and other utilities for Aksel users.",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -40,15 +40,15 @@
|
||||
"url": "https://github.com/navikt/aksel/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "^8.13.1",
|
||||
"@navikt/ds-tokens": "^8.13.1",
|
||||
"@navikt/ds-css": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"chalk": "5.6.2",
|
||||
"cli-progress": "3.12.0",
|
||||
"clipboardy": "5.3.1",
|
||||
"clipboardy": "5.0.1",
|
||||
"commander": "14.0.3",
|
||||
"enquirer": "2.4.1",
|
||||
"fast-glob": "3.3.3",
|
||||
"figlet": "1.11.0",
|
||||
"figlet": "1.10.0",
|
||||
"is-git-clean": "1.1.0",
|
||||
"jscodeshift": "17.3.0",
|
||||
"jscodeshift-add-imports": "1.0.11",
|
||||
@@ -59,8 +59,8 @@
|
||||
"@types/is-git-clean": "1.1.2",
|
||||
"@types/jscodeshift": "17.3.0",
|
||||
"rimraf": "6.1.3",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.8"
|
||||
"typescript": "6.0.2",
|
||||
"vitest": "4.1.4"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!*.d.ts
|
||||
@@ -1,65 +1,5 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 8.13.1
|
||||
|
||||
## 8.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- DataGrid: Added `columnDividers`-setting to DataGrid.Settings ([#4963](https://github.com/navikt/aksel/pull/4963))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Timeline: Update AxisLabel width calculation ([#4968](https://github.com/navikt/aksel/pull/4968))
|
||||
|
||||
## 8.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- DataGrid: Fixed sticky columns horizontal shadows ([#4960](https://github.com/navikt/aksel/pull/4960))
|
||||
|
||||
- DataGrid: Fixed zebra-stripes with sticky columns ([#4960](https://github.com/navikt/aksel/pull/4960))
|
||||
|
||||
## 8.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 🎨 ActionMenu: New medium content size and a prop to add indent to items without left marker to align items ([#4911](https://github.com/navikt/aksel/pull/4911))
|
||||
|
||||
## 8.11.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- DataGrid: Loading-bar now stays stuck to sticky-header. ([#4897](https://github.com/navikt/aksel/pull/4897))
|
||||
|
||||
## 8.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- DataGrid: New `preview` of DataGrid and DataGrid.Table-components ([#4885](https://github.com/navikt/aksel/pull/4885))
|
||||
|
||||
## 8.10.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 🎨 InfoCard: Added bigger padding for medium sized InfoCard.Message ([#4861](https://github.com/navikt/aksel/pull/4861))
|
||||
|
||||
## 8.10.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Pagination: Fix selected-styling in forced-colors mode ([#4858](https://github.com/navikt/aksel/pull/4858))
|
||||
|
||||
- Button: Fix issue where secondary and tertiary variants did not have correct text color is certain cases ([#4857](https://github.com/navikt/aksel/pull/4857))
|
||||
|
||||
## 8.10.4
|
||||
|
||||
## 8.10.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Add module declaration to avoid TypeScript error when importing ([`92f6d9e`](https://github.com/navikt/aksel/commit/92f6d9e93b879962df8415f7fad22203174dd3a8))
|
||||
|
||||
## 8.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "8.13.1",
|
||||
"version": "8.10.2",
|
||||
"description": "CSS for Nav Designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -14,9 +14,7 @@
|
||||
"files": [
|
||||
"*",
|
||||
"!dist/version",
|
||||
"!config",
|
||||
"!.gitignore",
|
||||
"!CHANGELOG.md"
|
||||
"!config/bundle.js"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -32,9 +30,9 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.13.1",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"browserslist": "^4.25.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"esbuild": "^0.27.4",
|
||||
"fast-glob": "3.3.3",
|
||||
"lightningcss": "^1.30.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
@@ -48,13 +46,9 @@
|
||||
],
|
||||
"main": "./dist/index.css",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types.d.ts",
|
||||
"default": "./dist/index.css"
|
||||
},
|
||||
"./dist/*": {
|
||||
"types": "./types.d.ts",
|
||||
"default": "./dist/*"
|
||||
}
|
||||
".": "./dist/index.css",
|
||||
"./dist/*": "./dist/*",
|
||||
"./tokens.json": "./tokens.json",
|
||||
"./config/_mappings": "./config/_mappings.js"
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,5 @@
|
||||
/* --------------------------- ActionMenu content --------------------------- */
|
||||
.aksel-action-menu__content {
|
||||
--__axc-action-menu-content-p-block: var(--ax-space-6);
|
||||
--__axc-action-menu-content-p-inline: var(--ax-space-8);
|
||||
--__axc-action-menu-item-height: 2rem;
|
||||
--__axc-action-menu-item-font-size: var(--ax-font-size-medium);
|
||||
--__axc-action-menu-content-group-label-pt: var(--ax-space-2);
|
||||
|
||||
&[data-size="medium"] {
|
||||
--__axc-action-menu-content-p-block: var(--ax-space-8);
|
||||
--__axc-action-menu-content-p-inline: var(--ax-space-12);
|
||||
--__axc-action-menu-item-height: var(--ax-space-40);
|
||||
--__axc-action-menu-item-font-size: var(--ax-font-size-large);
|
||||
--__axc-action-menu-content-group-label-pt: var(--ax-space-4);
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
border-radius: var(--ax-radius-12);
|
||||
border: 1px solid var(--ax-border-neutral-subtleA);
|
||||
@@ -33,8 +19,10 @@
|
||||
}
|
||||
|
||||
.aksel-action-menu__content > .aksel-action-menu__content-inner {
|
||||
--__axc-action-menu-content-p: var(--ax-space-8);
|
||||
--__axc-action-menu-item-pr: var(--ax-space-8);
|
||||
--__axc-action-menu-item-pl: var(--ax-space-8);
|
||||
--__axc-action-menu-item-height: 2rem;
|
||||
|
||||
border-radius: var(--ax-radius-12);
|
||||
background-color: var(--ax-bg-raised);
|
||||
@@ -42,8 +30,7 @@
|
||||
max-width: min(95vw, 640px);
|
||||
animation-duration: 160ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
padding-block: var(--__axc-action-menu-content-p-block);
|
||||
padding-inline: var(--__axc-action-menu-content-p-inline);
|
||||
padding: var(--__axc-action-menu-content-p);
|
||||
overflow: auto;
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
max-height: var(--__axc-action-menu-content-available-height);
|
||||
@@ -60,8 +47,8 @@
|
||||
position: relative;
|
||||
padding-left: var(--__axc-action-menu-item-pl);
|
||||
padding-right: var(--__axc-action-menu-item-pr);
|
||||
font-size: var(--__axc-action-menu-item-font-size);
|
||||
scroll-margin-block: var(--__axc-action-menu-content-p-block);
|
||||
font-size: var(--ax-font-size-medium);
|
||||
scroll-margin-block: var(--__axc-action-menu-content-p);
|
||||
line-height: 1.5;
|
||||
color: var(--ax-text-neutral);
|
||||
text-decoration: none;
|
||||
@@ -81,14 +68,8 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Indent items with left marker or if data-indent is set to true */
|
||||
&[data-indent="true"],
|
||||
&[data-marker="left"] {
|
||||
--__axc-action-menu-item-pl: var(--ax-space-28); /* 28px */
|
||||
|
||||
.aksel-action-menu__content[data-size="medium"] & {
|
||||
--__axc-action-menu-item-pl: var(--ax-space-32); /* 32px */
|
||||
}
|
||||
--__axc-action-menu-item-pl: var(--ax-space-24);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,23 +152,19 @@
|
||||
.aksel-action-menu__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: var(--__axc-action-menu-item-height);
|
||||
min-height: calc(var(--__axc-action-menu-item-height) - 6px);
|
||||
padding-right: var(--__axc-action-menu-item-pr);
|
||||
padding-left: var(--__axc-action-menu-item-pl);
|
||||
color: var(--ax-text-neutral-subtle);
|
||||
border-radius: var(--ax-radius-4);
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
font-size: var(--ax-font-size-medium);
|
||||
font-weight: var(--ax-font-weight-bold);
|
||||
}
|
||||
|
||||
.aksel-action-menu__group:not(:first-child) .aksel-action-menu__label {
|
||||
padding-top: var(--__axc-action-menu-content-group-label-pt);
|
||||
font-size: var(--ax-font-size-small);
|
||||
}
|
||||
|
||||
.aksel-action-menu__divider {
|
||||
height: 1px;
|
||||
margin-block: var(--ax-space-4);
|
||||
margin-block: var(--ax-space-8);
|
||||
background-color: var(--ax-border-neutral-subtleA);
|
||||
}
|
||||
|
||||
|
||||
@@ -215,17 +215,13 @@
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--ax-space-8);
|
||||
padding: var(--ax-space-16) var(--ax-space-20);
|
||||
padding: var(--ax-space-12) var(--ax-space-16);
|
||||
|
||||
& > .aksel-base-alert__icon {
|
||||
margin-top: var(--ax-space-2);
|
||||
}
|
||||
|
||||
.aksel-base-alert[data-size="small"] & {
|
||||
padding: var(--ax-space-12) var(--ax-space-16);
|
||||
|
||||
& > .aksel-base-alert__icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
.aksel-base-alert[data-size="small"] & > .aksel-base-alert__icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -42,22 +42,14 @@
|
||||
--__axc-button-border-color: var(--ax-border-default);
|
||||
|
||||
background-color: transparent;
|
||||
color: var(--ax-text-subtle);
|
||||
|
||||
/* secondary + neutral should use "default" text color.
|
||||
Check for data-color=neutral on both the button itself and the parents. */
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] &:not([data-color]) {
|
||||
[data-color="neutral"] > &:not([data-color]),
|
||||
[data-color="neutral"] :not([data-color]) &:not([data-color]) {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
|
||||
/* secondary + NOT neutral should use "subtle" text color.
|
||||
We need to explicitly target cases where a parent has data-color=neutral but
|
||||
a closer parent has a different data-color (overriding the selector above). */
|
||||
&,
|
||||
[data-color="neutral"] [data-color]:not([data-color="neutral"]) & {
|
||||
color: var(--ax-text-subtle);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
--__axc-button-border-color: var(--ax-border-strong);
|
||||
|
||||
@@ -81,22 +73,14 @@
|
||||
|
||||
&[data-variant="tertiary"] {
|
||||
background-color: transparent;
|
||||
color: var(--ax-text-subtle);
|
||||
|
||||
/* tertiary + neutral should use "default" text color.
|
||||
Check for data-color=neutral on both the button itself and the parents. */
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] &:not([data-color], [data-pressed="true"]) {
|
||||
[data-color="neutral"] > &:not([data-color], [data-pressed="true"]),
|
||||
[data-color="neutral"] :not([data-color]) &:not([data-color], [data-pressed="true"]) {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
|
||||
/* tertiary + NOT neutral should use "subtle" text color.
|
||||
We need to explicitly target cases where a parent has data-color=neutral but
|
||||
a closer parent has a different data-color (overriding the selector above). */
|
||||
&,
|
||||
[data-color="neutral"] [data-color]:not([data-color="neutral"]) &:not([data-pressed="true"]) {
|
||||
color: var(--ax-text-subtle);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ax-bg-moderate-hoverA);
|
||||
}
|
||||
@@ -247,15 +231,6 @@
|
||||
color: highlight;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&[data-variant="tertiary"][data-pressed="true"] {
|
||||
background-color: selecteditem;
|
||||
color: selecteditemtext;
|
||||
|
||||
& span {
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-button:not(:disabled):hover span {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
/* stylelint-disable csstools/value-no-unknown-custom-properties */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
|
||||
.aksel-data-grid {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.aksel-data-table__border-wrapper {
|
||||
/* display:flex and max-height:100% is needed for a "no page scroll" layout */
|
||||
display: flex;
|
||||
@@ -19,7 +11,6 @@
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
scroll-timeline: --horizontal-table-scroll inline;
|
||||
}
|
||||
|
||||
.aksel-data-table {
|
||||
@@ -43,56 +34,35 @@
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* Default font size is prop "medium", which is variable --ax-font-size-large */
|
||||
--__axc-data-table-text-size: var(--ax-font-size-large);
|
||||
--__axc-data-table-density: var(--ax-space-12);
|
||||
--__axc-data-table-density: var(--ax-space-16);
|
||||
|
||||
&[data-text-size="small"] {
|
||||
--__axc-data-table-text-size: var(--ax-font-size-medium);
|
||||
&[data-density="condensed"] {
|
||||
--__axc-data-table-density: var(--ax-space-8);
|
||||
}
|
||||
|
||||
&[data-density="tight"] {
|
||||
--__axc-data-table-density: var(--ax-space-6);
|
||||
}
|
||||
|
||||
&[data-density="loose"] {
|
||||
--__axc-data-table-density: var(--ax-space-20);
|
||||
&[data-density="spacious"] {
|
||||
--__axc-data-table-density: var(--ax-space-32);
|
||||
}
|
||||
|
||||
&[data-zebra-stripes="true"] {
|
||||
& .aksel-data-table__tbody :where(.aksel-data-table__tr:nth-child(even)) {
|
||||
--__axc-data-table-zebra-color: var(--ax-bg-neutral-soft);
|
||||
|
||||
background-color: var(--__axc-data-table-zebra-color);
|
||||
|
||||
&[data-selected="true"] {
|
||||
--__axc-data-table-zebra-color: var(--ax-bg-soft);
|
||||
|
||||
background-color: var(--__axc-data-table-zebra-color);
|
||||
}
|
||||
& .aksel-data-table__tbody :where(.aksel-data-table__tr:nth-child(odd)) {
|
||||
background-color: var(--ax-bg-neutral-softA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__thead {
|
||||
background-color: var(--ax-bg-raised);
|
||||
position: relative; /* Shadow ends up behind loading/empty-state without, consider after-element instead like with sticky */
|
||||
box-shadow: 0 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
|
||||
.aksel-data-table[data-loading="true"] &::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
block-size: 2px;
|
||||
inset-block-end: -2px;
|
||||
background-color: var(--ax-bg-strong);
|
||||
animation: animateDataTableLoading 1s linear infinite;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
&[data-sticky="true"] {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__tbody {
|
||||
@@ -100,7 +70,12 @@
|
||||
position: relative;
|
||||
|
||||
&[inert] {
|
||||
transition: opacity 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
opacity: var(--ax-opacity-disabled);
|
||||
|
||||
@starting-style {
|
||||
opacity: 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
&[inert]::after {
|
||||
@@ -108,16 +83,36 @@
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--ax-bg-overlay);
|
||||
z-index: 2;
|
||||
transition: opacity 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
z-index: 10;
|
||||
opacity: 0.2;
|
||||
|
||||
/* iOS 26+: Ensure the backdrop covers the entire visible viewport. */
|
||||
|
||||
@starting-style {
|
||||
opacity: 0.001;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__tr {
|
||||
&[data-selected="true"] {
|
||||
--__axc-data-table-zebra-color: var(--ax-bg-soft);
|
||||
background-color: var(--ax-bg-softA);
|
||||
}
|
||||
|
||||
background-color: var(--__axc-data-table-zebra-color);
|
||||
.aksel-data-table__thead &[data-sticky="true"] {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
bottom: -1px;
|
||||
pointer-events: none;
|
||||
border-bottom: 1px solid var(--ax-border-neutral-subtle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,49 +141,23 @@
|
||||
border-bottom: 1px solid var(--ax-border-neutral-subtle);
|
||||
}
|
||||
|
||||
@keyframes scroll-shadow-fade-start {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
10%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scroll-shadow-fade-end {
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
.aksel-data-table__thead {
|
||||
position: relative; /* Shadow ends up behind loading/empty-state without, consider after-element instead like with sticky */
|
||||
box-shadow: 0 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
}
|
||||
|
||||
.aksel-data-table__cell {
|
||||
/* border doesn't work on sticky thead when we have collapsed borders, so using box-shadow instead */
|
||||
box-shadow: inset 0 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
box-shadow: inset 1px 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
text-align: start;
|
||||
vertical-align: middle;
|
||||
scroll-margin: var(--ax-space-0);
|
||||
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);
|
||||
}
|
||||
|
||||
&:not(.aksel-data-table__column-header, [data-sticky]) {
|
||||
overflow: hidden; /* Needed for cases where left margin is added to cell-content (nested rows) */
|
||||
}
|
||||
|
||||
&[data-nested="true"] {
|
||||
position: relative;
|
||||
|
||||
& > .aksel-data-table__cell-content {
|
||||
& > div {
|
||||
margin-inline-start: calc(32px + 8px + calc(var(--__axc-data-table-nested-depth) * var(--ax-space-16)));
|
||||
}
|
||||
}
|
||||
@@ -213,13 +182,6 @@
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
|
||||
--__axc-data-table-shadow-color: var(--ax-border-neutral-subtleA);
|
||||
|
||||
/* Avoids "light" color shadows in dark mode */
|
||||
.dark & {
|
||||
--__axc-data-table-shadow-color: #0f141b;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -233,52 +195,17 @@
|
||||
&[data-sticky="start"] {
|
||||
left: 0;
|
||||
|
||||
&[data-sticky-last]::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
box-shadow: 4px 0 8px 1px var(--__axc-data-table-shadow-color);
|
||||
clip-path: inset(0 -24px 0 0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aksel-data-table[data-scroll] &[data-sticky-last]::before {
|
||||
opacity: 1;
|
||||
animation: scroll-shadow-fade-start 1s linear;
|
||||
animation-timeline: --horizontal-table-scroll;
|
||||
&::after {
|
||||
inset-inline: 0 -1px;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-sticky="end"] {
|
||||
right: 0;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
box-shadow: -4px 0 8px 1px var(--__axc-data-table-shadow-color);
|
||||
clip-path: inset(0 0 0 -24px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.aksel-data-table[data-scroll] &::before {
|
||||
opacity: 1;
|
||||
animation: scroll-shadow-fade-end 1s linear;
|
||||
animation-timeline: --horizontal-table-scroll;
|
||||
}
|
||||
}
|
||||
|
||||
background-color: var(--__axc-data-table-zebra-color, var(--ax-bg-raised));
|
||||
|
||||
.aksel-data-table__tr[data-selected="true"] & {
|
||||
background-color: var(--ax-bg-soft);
|
||||
}
|
||||
background-color: var(--ax-bg-raised);
|
||||
|
||||
.aksel-data-table__thead & {
|
||||
background-color: var(--ax-bg-neutral-soft);
|
||||
@@ -293,10 +220,10 @@
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
&[data-cell-type="action"] {
|
||||
&[data-selectable="true"] {
|
||||
padding: 0;
|
||||
|
||||
& > .aksel-data-table__cell-content {
|
||||
& > div {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
@@ -304,14 +231,14 @@
|
||||
|
||||
--__axc-data-table-pi-cell: var(--ax-space-16);
|
||||
|
||||
&:not([data-cell-type="action"]) > .aksel-data-table__cell-content {
|
||||
&:not([data-selectable="true"]) > div {
|
||||
padding-block: var(--__axc-data-table-density);
|
||||
padding-inline: var(--__axc-data-table-pi-cell);
|
||||
overflow: hidden;
|
||||
|
||||
.aksel-data-table[data-truncate-content="true"] & {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,11 +252,11 @@
|
||||
position: relative;
|
||||
font-weight: var(--ax-font-weight-bold);
|
||||
|
||||
&:has(.aksel-data-table__th-sort-button:hover:not(:disabled)) {
|
||||
&:has(.aksel-data-table__th-sort-button:hover) {
|
||||
background-color: var(--ax-bg-neutral-moderate-hover);
|
||||
}
|
||||
|
||||
&:has(.aksel-data-table__th-sort-button:active:not(:disabled)) {
|
||||
&:has(.aksel-data-table__th-sort-button:active) {
|
||||
background-color: var(--ax-bg-neutral-moderate-pressed);
|
||||
}
|
||||
|
||||
@@ -355,11 +282,6 @@
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: inherit;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__th-sort-icon {
|
||||
@@ -370,7 +292,7 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.aksel-data-table__th-sort-button:is(:hover, :focus-visible, :focus-within):not(:disabled) & {
|
||||
.aksel-data-table__th-sort-button:is(:hover, :focus-visible, :focus-within) & {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@@ -455,11 +377,11 @@
|
||||
transform: translateY(-50%);
|
||||
height: 1.5rem;
|
||||
|
||||
&:first-child {
|
||||
&.aksel-data-table__th-resize-handle-indicator--start {
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&.aksel-data-table__th-resize-handle-indicator--end {
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
@@ -507,10 +429,10 @@
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
&[data-cell-type="action"] {
|
||||
&[data-selectable="true"] {
|
||||
padding: 0;
|
||||
|
||||
& > .aksel-data-table__cell-content {
|
||||
& > div {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
@@ -522,14 +444,9 @@
|
||||
}
|
||||
|
||||
.aksel-data-table__filler-cell {
|
||||
width: auto;
|
||||
display: table-cell;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.aksel-data-table__drag-and-drop-root {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__button {
|
||||
@@ -552,11 +469,21 @@
|
||||
}
|
||||
|
||||
.aksel-data-table__drag-and-drop-item {
|
||||
background-color: var(--ax-bg-default);
|
||||
|
||||
&[data-overlay="true"] {
|
||||
width: 100%;
|
||||
opacity: 0.8;
|
||||
box-shadow: var(--ax-shadow-dialog);
|
||||
background-color: var(--ax-bg-default);
|
||||
}
|
||||
|
||||
/* TODO - might be removable */
|
||||
&[data-keyboard-dragging="true"] {
|
||||
.aksel-data-drag-and-drop__drag-handler__button {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -1px;
|
||||
border-radius: var(--ax-radius-8);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-drop-target="true"]:not([data-overlay="true"]) {
|
||||
@@ -564,6 +491,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__alt,
|
||||
.aksel-data-drag-and-drop__drag-handler {
|
||||
position: relative;
|
||||
height: 1.5rem;
|
||||
@@ -665,14 +593,3 @@ html[data-dragging-cursor="true"] *::after {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.aksel-data-table__details-panel-row {
|
||||
box-shadow: inset 1px 1px 0 0 var(--ax-border-neutral-subtle);
|
||||
}
|
||||
|
||||
.aksel-data-table__details-panel-row-cell {
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@
|
||||
@import "./chat.css" layer(aksel.components.modules);
|
||||
@import "./copybutton.css" layer(aksel.components.modules);
|
||||
@import "./date.css" layer(aksel.components.modules);
|
||||
@import "./data-grid-preview.css" layer(aksel.components.modules);
|
||||
@import "./dialog.css" layer(aksel.components.modules);
|
||||
@import "./dropdown.css" layer(aksel.components.modules);
|
||||
@import "./action-menu.css" layer(aksel.components.modules);
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
position: absolute;
|
||||
color: var(--ax-text-neutral-subtle);
|
||||
white-space: nowrap;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
.aksel-timeline__row {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
declare module "@navikt/ds-css" {}
|
||||
@@ -1,2 +1,3 @@
|
||||
scripts/*.backup
|
||||
!**/types/theme.d.ts
|
||||
*tsbuildinfo
|
||||
@@ -1,133 +1,5 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 8.13.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Combobox: Selected options are now persistent in dropdown-menu for single-select ([#4970](https://github.com/navikt/aksel/pull/4970))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.13.1
|
||||
- @navikt/aksel-icons@8.13.1
|
||||
|
||||
## 8.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- DataGrid: Added `columnDividers`-setting to DataGrid.Settings ([#4963](https://github.com/navikt/aksel/pull/4963))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Table: ExpandableRows now works with React.Activity ([#4972](https://github.com/navikt/aksel/pull/4972))
|
||||
|
||||
- Timeline: Update AxisLabel width calculation ([#4968](https://github.com/navikt/aksel/pull/4968))
|
||||
|
||||
- Updated dependencies [[`f3b7857`](https://github.com/navikt/aksel/commit/f3b7857df4f4bb20b8117174eb981cff5e35159a)]:
|
||||
- @navikt/aksel-icons@8.13.0
|
||||
- @navikt/ds-tokens@8.13.0
|
||||
|
||||
## 8.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Timeline: Fix bug where axis-labels could overflow-x ([#4959](https://github.com/navikt/aksel/pull/4959))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.12.1
|
||||
- @navikt/aksel-icons@8.12.1
|
||||
|
||||
## 8.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 🎨 ActionMenu: New medium content size and a prop to add indent to items without left marker to align items ([#4911](https://github.com/navikt/aksel/pull/4911))
|
||||
|
||||
- DatePicker: Updated user-flow for ranged datepicker with inputs. Now changes from/to-date based on which input user opened DatePicker with. ([#4896](https://github.com/navikt/aksel/pull/4896))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Textarea: Better internal type-guarding ([#4950](https://github.com/navikt/aksel/pull/4950))
|
||||
|
||||
- Logging: Improved console warnings ([#4922](https://github.com/navikt/aksel/pull/4922))
|
||||
|
||||
- useEventListener-hook: Inline-handlers are now stable ([#4923](https://github.com/navikt/aksel/pull/4923))
|
||||
|
||||
- ToggleGroup: No longer uses `descendant`-API internally ([#4931](https://github.com/navikt/aksel/pull/4931))
|
||||
|
||||
- ActionMenu: No longer uses `descendant`-API internally ([#4931](https://github.com/navikt/aksel/pull/4931))
|
||||
|
||||
- Tabs: No longer uses `descendant`-API internally ([#4931](https://github.com/navikt/aksel/pull/4931))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.12.0
|
||||
- @navikt/aksel-icons@8.12.0
|
||||
|
||||
## 8.11.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- DataGrid: Loading-bar now stays stuck to sticky-header. ([#4897](https://github.com/navikt/aksel/pull/4897))
|
||||
|
||||
- Popover: Fixed issue where clicking inside container sometimes could lead to Popover closing ([#4909](https://github.com/navikt/aksel/pull/4909))
|
||||
|
||||
- DataGrid: Added `columnDisplay` value to settings ([#4908](https://github.com/navikt/aksel/pull/4908))
|
||||
|
||||
- DataGrid.Table: Fix auto-width for col with "show nested rows" button ([#4906](https://github.com/navikt/aksel/pull/4906))
|
||||
|
||||
- Updated dependencies [[`54634ff`](https://github.com/navikt/aksel/commit/54634ffed97000a96b375820a4693413ddb78066)]:
|
||||
- @navikt/ds-tokens@8.11.1
|
||||
- @navikt/aksel-icons@8.11.1
|
||||
|
||||
## 8.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- DataGrid: New `preview` of DataGrid and DataGrid.Table-components ([#4885](https://github.com/navikt/aksel/pull/4885))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`76e0410`](https://github.com/navikt/aksel/commit/76e04106f792516afc986a003dd6f451f8064f84)]:
|
||||
- @navikt/aksel-icons@8.11.0
|
||||
- @navikt/ds-tokens@8.11.0
|
||||
|
||||
## 8.10.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.10.6
|
||||
- @navikt/aksel-icons@8.10.6
|
||||
|
||||
## 8.10.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Dependencies: Bumped `@floating-ui/react` from v0.27.8 -> v0.27.19 ([#4851](https://github.com/navikt/aksel/pull/4851))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.10.5
|
||||
- @navikt/aksel-icons@8.10.5
|
||||
|
||||
## 8.10.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ActionMenu: Now locked to bottom/top side placement, left/right for sub-menus. ([#4843](https://github.com/navikt/aksel/pull/4843))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.10.4
|
||||
- @navikt/aksel-icons@8.10.4
|
||||
|
||||
## 8.10.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Radio, Checkbox: Fix regression where `className` was mistakenly passed to `input`-element ([#4837](https://github.com/navikt/aksel/pull/4837))
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@8.10.3
|
||||
- @navikt/aksel-icons@8.10.3
|
||||
|
||||
## 8.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "8.13.1",
|
||||
"version": "8.10.2",
|
||||
"description": "React components from the Norwegian Labour and Welfare Administration.",
|
||||
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
|
||||
"license": "MIT",
|
||||
@@ -46,26 +46,6 @@
|
||||
"default": "./cjs/index.js"
|
||||
}
|
||||
},
|
||||
"./PREVIEW": {
|
||||
"import": {
|
||||
"types": "./esm/preview.d.ts",
|
||||
"default": "./esm/preview.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./cjs/preview.d.ts",
|
||||
"default": "./cjs/preview.js"
|
||||
}
|
||||
},
|
||||
"./PREVIEW/DataGrid": {
|
||||
"import": {
|
||||
"types": "./esm/data-grid/index.d.ts",
|
||||
"default": "./esm/data-grid/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./cjs/data-grid/index.d.ts",
|
||||
"default": "./cjs/data-grid/index.js"
|
||||
}
|
||||
},
|
||||
"./types/theme": {
|
||||
"types": "./esm/types/theme.d.ts"
|
||||
},
|
||||
@@ -718,16 +698,16 @@
|
||||
"clean": "rimraf cjs esm",
|
||||
"build": "yarn i18n-jsdoc && concurrently \"tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" && yarn i18n-jsdoc --cleanup && yarn copy-types",
|
||||
"watch": "tsc --watch -p tsconfig.esm.json",
|
||||
"test": "concurrently \"cross-env TZ=UTC vitest run -c tests/vitest.config.ts\" \"yarn test-sb-build\"",
|
||||
"test": "concurrently \"TZ=UTC vitest run -c tests/vitest.config.ts\" \"yarn test-sb-build\"",
|
||||
"test-sb-build": "tsc -p tsconfig.sb.json",
|
||||
"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.19",
|
||||
"@floating-ui/react": "0.27.8",
|
||||
"@floating-ui/react-dom": "^2.1.8",
|
||||
"@navikt/aksel-icons": "^8.13.1",
|
||||
"@navikt/ds-tokens": "^8.13.1",
|
||||
"@navikt/aksel-icons": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"date-fns": "^4.0.0",
|
||||
"react-day-picker": "9.14.0"
|
||||
},
|
||||
@@ -743,19 +723,18 @@
|
||||
"aksel": "workspace:^",
|
||||
"concurrently": "9.2.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"jscodeshift": "17.3.0",
|
||||
"jsdom": "29.1.1",
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6",
|
||||
"react-router": "^7.15.1",
|
||||
"jsdom": "27.1.0",
|
||||
"react": "19.2.5",
|
||||
"react-dom": "19.2.5",
|
||||
"react-router": "^7.13.1",
|
||||
"rimraf": "6.1.3",
|
||||
"swr": "^2.4.1",
|
||||
"tsc-alias": "1.8.16",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.8"
|
||||
"typescript": "6.0.2",
|
||||
"vitest": "4.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.30 || ^18 || ^19",
|
||||
|
||||
@@ -108,115 +108,6 @@ export const GroupedItems: Story = {
|
||||
decorators: [DemoDecorator],
|
||||
};
|
||||
|
||||
export const SizeMedium: Story = {
|
||||
render: (props) => {
|
||||
const [checkedItems, setCheckedItems] = useState({
|
||||
checkbox1: false,
|
||||
checkbox2: false,
|
||||
});
|
||||
|
||||
const handleCheckboxChange = (checkboxId: string) => {
|
||||
setCheckedItems((prevState) => ({
|
||||
...prevState,
|
||||
[checkboxId]: !prevState[checkboxId],
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionMenu open={props.open} size="medium">
|
||||
<ActionMenu.Trigger>
|
||||
<button>Open action</button>
|
||||
</ActionMenu.Trigger>
|
||||
<ActionMenu.Content>
|
||||
<ActionMenu.Group label="Group 1">
|
||||
<ActionMenu.CheckboxItem
|
||||
checked={checkedItems.checkbox1}
|
||||
onCheckedChange={() => handleCheckboxChange("checkbox1")}
|
||||
shortcut="⌘+T"
|
||||
>
|
||||
Checkbox 1
|
||||
</ActionMenu.CheckboxItem>
|
||||
<ActionMenu.CheckboxItem
|
||||
checked={checkedItems.checkbox2}
|
||||
onCheckedChange={() => handleCheckboxChange("checkbox2")}
|
||||
shortcut="⇧+⌘+N"
|
||||
>
|
||||
Checkbox 2
|
||||
</ActionMenu.CheckboxItem>
|
||||
</ActionMenu.Group>
|
||||
<ActionMenu.Divider />
|
||||
<ActionMenu.Group label="Group 2">
|
||||
<ActionMenu.Item
|
||||
shortcut="⌘+T"
|
||||
onSelect={() => console.log("Item 1 clicked")}
|
||||
icon={<StarIcon aria-hidden />}
|
||||
>
|
||||
Item 1
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item
|
||||
shortcut="⇧+⌘+N"
|
||||
onSelect={() => console.log("Item 2 clicked")}
|
||||
icon={<PencilIcon aria-hidden />}
|
||||
>
|
||||
Item 2
|
||||
</ActionMenu.Item>
|
||||
</ActionMenu.Group>
|
||||
</ActionMenu.Content>
|
||||
</ActionMenu>
|
||||
);
|
||||
},
|
||||
decorators: [DemoDecorator],
|
||||
};
|
||||
|
||||
export const EnableItemIndent: Story = {
|
||||
render: (props) => {
|
||||
return (
|
||||
<ActionMenu open={props.open}>
|
||||
<ActionMenu.Trigger>
|
||||
<button>Open action</button>
|
||||
</ActionMenu.Trigger>
|
||||
<ActionMenu.Content>
|
||||
<ActionMenu.Group label="Group 1">
|
||||
<ActionMenu.Item
|
||||
onSelect={() => console.log("Item 1 clicked")}
|
||||
icon={<StarIcon aria-hidden />}
|
||||
>
|
||||
Item 1
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item
|
||||
onSelect={() => console.log("Item 2 clicked")}
|
||||
indent
|
||||
>
|
||||
Item 2
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item
|
||||
onSelect={() => console.log("Item 3 clicked")}
|
||||
indent
|
||||
>
|
||||
Item 3
|
||||
</ActionMenu.Item>
|
||||
</ActionMenu.Group>
|
||||
<ActionMenu.Group label="Group 2">
|
||||
<ActionMenu.Item
|
||||
onSelect={() => console.log("Item 1 clicked")}
|
||||
icon={<StarIcon aria-hidden />}
|
||||
>
|
||||
Item 1
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item onSelect={() => console.log("Item 2 clicked")}>
|
||||
Item 2
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item onSelect={() => console.log("Item 3 clicked")}>
|
||||
Item 3
|
||||
</ActionMenu.Item>
|
||||
</ActionMenu.Group>
|
||||
</ActionMenu.Content>
|
||||
</ActionMenu>
|
||||
);
|
||||
},
|
||||
decorators: [DemoDecorator],
|
||||
};
|
||||
|
||||
export const ShortcutsAndIcons: Story = {
|
||||
render: (props) => {
|
||||
const [checkedItems, setCheckedItems] = useState({
|
||||
@@ -454,7 +345,7 @@ export const Submenus: Story = {
|
||||
<ActionMenu.Item onSelect={() => console.log("Item 1 clicked")}>
|
||||
Item 1
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Item onSelect={() => console.log("Item 2 clicked")}>
|
||||
<ActionMenu.Item onSelect={() => console.log("Item 1 clicked")}>
|
||||
Item 2 with a little longer name
|
||||
</ActionMenu.Item>
|
||||
<ActionMenu.Sub open={props.open}>
|
||||
@@ -771,14 +662,6 @@ export const Chromatic: Story = {
|
||||
{Disabled.render?.(newArgs, context)}
|
||||
</div>
|
||||
</HStack>
|
||||
<div>
|
||||
<h2>Medium size</h2>
|
||||
{SizeMedium.render?.(newArgs, context)}
|
||||
</div>
|
||||
<div>
|
||||
<h2>Enable item indent</h2>
|
||||
{EnableItemIndent.render?.(newArgs, context)}
|
||||
</div>
|
||||
<div style={{ marginBottom: "10rem" }}>
|
||||
<h2>Align</h2>
|
||||
{Align.render?.(newArgs, context)}
|
||||
|
||||
@@ -23,7 +23,6 @@ type ActionMenuContextValue = {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onOpenToggle: () => void;
|
||||
rootElement: MenuPortalProps["rootElement"];
|
||||
size: "small" | "medium";
|
||||
};
|
||||
|
||||
const { Provider: ActionMenuProvider, useContext: useActionMenuContext } =
|
||||
@@ -44,11 +43,6 @@ type ActionMenuProps = {
|
||||
* Callback for when the menu is opened or closed.
|
||||
*/
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
/**
|
||||
* The size of the menu.
|
||||
* @default "small"
|
||||
*/
|
||||
size?: "small" | "medium";
|
||||
} & Pick<MenuPortalProps, "rootElement">;
|
||||
|
||||
interface ActionMenuComponent extends React.FC<ActionMenuProps> {
|
||||
@@ -254,7 +248,6 @@ const ActionMenuRoot = ({
|
||||
open: openProp,
|
||||
onOpenChange,
|
||||
rootElement: rootElementProp,
|
||||
size = "small", // TODO: Default to medium in next major release
|
||||
}: ActionMenuProps) => {
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
@@ -278,7 +271,6 @@ const ActionMenuRoot = ({
|
||||
onOpenChange={setOpen}
|
||||
onOpenToggle={() => setOpen((prevOpen) => !prevOpen)}
|
||||
rootElement={rootElement}
|
||||
size={size}
|
||||
>
|
||||
<Menu open={open} onOpenChange={setOpen} modal>
|
||||
{children}
|
||||
@@ -391,9 +383,8 @@ export const ActionMenuContent = forwardRef<
|
||||
className={cl("aksel-action-menu__content", className)}
|
||||
{...rest}
|
||||
align={align}
|
||||
data-size={context.size}
|
||||
sideOffset={4}
|
||||
collisionPadding={5}
|
||||
collisionPadding={10}
|
||||
returnFocus={context.triggerRef}
|
||||
safeZone={{
|
||||
anchor: context.triggerRef.current,
|
||||
@@ -562,11 +553,6 @@ interface ActionMenuItemProps extends Omit<MenuItemProps, "asChild"> {
|
||||
* @default "left"
|
||||
*/
|
||||
iconPosition?: "left" | "right";
|
||||
/**
|
||||
* Add indent for this item even if it doesn't have a left marker. This is useful for aligning items that don't have an icon with items that do have an icon.
|
||||
* @default false
|
||||
*/
|
||||
indent?: boolean;
|
||||
}
|
||||
|
||||
export const ActionMenuItem: OverridableComponent<
|
||||
@@ -582,7 +568,6 @@ export const ActionMenuItem: OverridableComponent<
|
||||
shortcut,
|
||||
variant,
|
||||
iconPosition = "left",
|
||||
indent = false,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
@@ -594,7 +579,6 @@ export const ActionMenuItem: OverridableComponent<
|
||||
"aksel-action-menu__item--danger": variant === "danger",
|
||||
})}
|
||||
data-marker={icon ? iconPosition : undefined}
|
||||
data-indent={indent}
|
||||
aria-keyshortcuts={shortcut ?? undefined}
|
||||
asChild
|
||||
>
|
||||
@@ -919,11 +903,6 @@ interface ActionMenuSubTriggerProps extends Omit<
|
||||
* @default "left"
|
||||
*/
|
||||
iconPosition?: "left" | "right";
|
||||
/**
|
||||
* Add indent for this item even if it doesn't have a left marker. This is useful for aligning items that don't have an icon with items that do have an icon.
|
||||
* @default false
|
||||
*/
|
||||
indent?: boolean;
|
||||
}
|
||||
|
||||
export const ActionMenuSubTrigger = forwardRef<
|
||||
@@ -936,7 +915,6 @@ export const ActionMenuSubTrigger = forwardRef<
|
||||
className,
|
||||
icon,
|
||||
iconPosition = "left",
|
||||
indent = false,
|
||||
...rest
|
||||
}: ActionMenuSubTriggerProps,
|
||||
ref,
|
||||
@@ -950,7 +928,6 @@ export const ActionMenuSubTrigger = forwardRef<
|
||||
"aksel-action-menu__item aksel-action-menu__sub-trigger",
|
||||
className,
|
||||
)}
|
||||
data-indent={indent}
|
||||
data-marker={icon ? iconPosition : undefined}
|
||||
>
|
||||
{children}
|
||||
@@ -992,7 +969,6 @@ export const ActionMenuSubContent = forwardRef<
|
||||
alignOffset={-4}
|
||||
sideOffset={1}
|
||||
collisionPadding={10}
|
||||
data-size={context.size}
|
||||
{...rest}
|
||||
className={cl(
|
||||
"aksel-action-menu__content aksel-action-menu__sub-content",
|
||||
|
||||
@@ -184,66 +184,126 @@ export const ColorRole = () => (
|
||||
</HStack>
|
||||
<h3>data-color=neutral on wrapper and danger on button</h3>
|
||||
<HStack gap="space-8" data-color="neutral">
|
||||
<Button data-color="danger" variant="secondary">
|
||||
<Button data-color="danger" variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="danger" variant="tertiary">
|
||||
<Button data-color="danger" variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="danger" variant="tertiary" data-pressed>
|
||||
Pressed
|
||||
</Button>
|
||||
<Button data-color="danger" variant="secondary" disabled>
|
||||
<Button
|
||||
data-color="danger"
|
||||
variant="secondary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="danger" variant="tertiary" disabled>
|
||||
<Button
|
||||
data-color="danger"
|
||||
variant="tertiary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
<h3>data-color=neutral on parent wrapper and danger on button</h3>
|
||||
<div data-color="neutral">
|
||||
<HStack gap="space-8">
|
||||
<Button data-color="danger" variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="danger" variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button
|
||||
data-color="danger"
|
||||
variant="secondary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
<Button
|
||||
data-color="danger"
|
||||
variant="tertiary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
</div>
|
||||
<h3>data-color=neutral on button</h3>
|
||||
<HStack gap="space-8">
|
||||
<Button data-color="neutral" variant="secondary">
|
||||
<Button data-color="neutral" variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="neutral" variant="tertiary">
|
||||
<Button data-color="neutral" variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="neutral" variant="tertiary" data-pressed>
|
||||
Pressed
|
||||
</Button>
|
||||
<Button data-color="neutral" variant="secondary" disabled>
|
||||
<Button
|
||||
data-color="neutral"
|
||||
variant="secondary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
<Button data-color="neutral" variant="tertiary" disabled>
|
||||
<Button
|
||||
data-color="neutral"
|
||||
variant="tertiary"
|
||||
icon={<StarIcon />}
|
||||
disabled
|
||||
>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
<h3>data-color=neutral on wrapper</h3>
|
||||
<HStack gap="space-8" data-color="neutral">
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Button variant="tertiary">Button</Button>
|
||||
<Button variant="tertiary" data-pressed>
|
||||
Pressed
|
||||
</Button>
|
||||
<Button variant="secondary" disabled>
|
||||
<Button variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" disabled>
|
||||
<Button variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
<h3>data-color=neutral on parent wrapper</h3>
|
||||
<div data-color="neutral">
|
||||
<HStack gap="space-8">
|
||||
<Button variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
</div>
|
||||
<h3>data-color=neutral on parent wrapper and danger on wrapper</h3>
|
||||
<div data-color="neutral">
|
||||
<HStack gap="space-8" data-color="danger">
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Button variant="tertiary">Button</Button>
|
||||
<Button variant="tertiary" data-pressed>
|
||||
Pressed
|
||||
</Button>
|
||||
<Button variant="secondary" disabled>
|
||||
<Button variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" disabled>
|
||||
<Button variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
@@ -251,15 +311,16 @@ export const ColorRole = () => (
|
||||
<h3>data-color=neutral on both wrappers</h3>
|
||||
<div data-color="neutral">
|
||||
<HStack gap="space-8" data-color="neutral">
|
||||
<Button variant="secondary">Button</Button>
|
||||
<Button variant="tertiary">Button</Button>
|
||||
<Button variant="tertiary" data-pressed>
|
||||
Pressed
|
||||
</Button>
|
||||
<Button variant="secondary" disabled>
|
||||
<Button variant="secondary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" disabled>
|
||||
<Button variant="tertiary" icon={<StarIcon />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" icon={<StarIcon />} disabled>
|
||||
Button
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { HTMLAttributes, forwardRef } from "react";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import { ChipsRemovable } from "../removable/ChipsRemovable";
|
||||
import { ChipsToggle } from "../toggle/ChipsToggle";
|
||||
import { cl } from "../utils/helpers";
|
||||
import RemovableChips, { ChipsRemovableProps } from "./Removable";
|
||||
import ToggleChips, { ChipsToggleProps } from "./Toggle";
|
||||
|
||||
interface ChipsProps extends HTMLAttributes<HTMLUListElement> {
|
||||
children: React.ReactNode;
|
||||
@@ -20,13 +20,13 @@ interface ChipsComponent extends React.ForwardRefExoticComponent<
|
||||
* @see 🏷️ {@link ToggleChipsProps}
|
||||
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
|
||||
*/
|
||||
Toggle: typeof ChipsToggle;
|
||||
Toggle: typeof ToggleChips;
|
||||
/**
|
||||
* Remove filter or the likes on click.
|
||||
* @see 🏷️ {@link RemovableChipsProps}
|
||||
* @see [🤖 OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
|
||||
*/
|
||||
Removable: typeof ChipsRemovable;
|
||||
Removable: typeof RemovableChips;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ interface ChipsComponent extends React.ForwardRefExoticComponent<
|
||||
* </Chips>
|
||||
* ```
|
||||
*/
|
||||
export const Chips = forwardRef<HTMLUListElement, ChipsProps>(
|
||||
export const Chips: ChipsComponent = forwardRef<HTMLUListElement, ChipsProps>(
|
||||
({ className, size = "medium", children, ...rest }, ref) => {
|
||||
return (
|
||||
<ul
|
||||
@@ -75,8 +75,9 @@ export const Chips = forwardRef<HTMLUListElement, ChipsProps>(
|
||||
},
|
||||
) as ChipsComponent;
|
||||
|
||||
Chips.Toggle = ChipsToggle;
|
||||
Chips.Removable = ChipsRemovable;
|
||||
Chips.Toggle = ToggleChips;
|
||||
Chips.Removable = RemovableChips;
|
||||
|
||||
export default Chips;
|
||||
export type { ChipsProps };
|
||||
export { ToggleChips as ChipsToggle, RemovableChips as ChipsRemovable };
|
||||
export type { ChipsProps, ChipsToggleProps, ChipsRemovableProps };
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { XMarkIcon } from "@navikt/aksel-icons";
|
||||
import { AkselColor } from "../../types";
|
||||
import { cl, composeEventHandlers } from "../../utils/helpers";
|
||||
import { useI18n } from "../../utils/i18n/i18n.hooks";
|
||||
import { AkselColor } from "../types";
|
||||
import { cl, composeEventHandlers } from "../utils/helpers";
|
||||
import { useI18n } from "../utils/i18n/i18n.hooks";
|
||||
|
||||
export interface ChipsRemovableProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: string;
|
||||
@@ -22,7 +22,7 @@ export interface ChipsRemovableProps extends React.ButtonHTMLAttributes<HTMLButt
|
||||
"data-color"?: AkselColor;
|
||||
}
|
||||
|
||||
export const ChipsRemovable = forwardRef<
|
||||
export const RemovableChips = forwardRef<
|
||||
HTMLButtonElement,
|
||||
ChipsRemovableProps
|
||||
>(
|
||||
@@ -76,4 +76,4 @@ function variantToColor(
|
||||
}
|
||||
}
|
||||
|
||||
export default ChipsRemovable;
|
||||
export default RemovableChips;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { AkselColor } from "../../types";
|
||||
import type { OverridableComponent } from "../../utils-external";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import { AkselColor } from "../types";
|
||||
import type { OverridableComponent } from "../utils-external";
|
||||
import { cl } from "../utils/helpers";
|
||||
|
||||
export interface ChipsToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: string;
|
||||
@@ -26,7 +26,7 @@ export interface ChipsToggleProps extends React.ButtonHTMLAttributes<HTMLButtonE
|
||||
checkmark?: boolean;
|
||||
}
|
||||
|
||||
export const ChipsToggle: OverridableComponent<
|
||||
export const ToggleChips: OverridableComponent<
|
||||
ChipsToggleProps,
|
||||
HTMLButtonElement
|
||||
> = forwardRef(
|
||||
@@ -103,4 +103,4 @@ function variantToColor(
|
||||
}
|
||||
}
|
||||
|
||||
export default ChipsToggle;
|
||||
export default ToggleChips;
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
export { default as Chips, type ChipsProps } from "./root/ChipsRoot";
|
||||
export {
|
||||
ChipsRemovable,
|
||||
type ChipsRemovableProps,
|
||||
} from "./removable/ChipsRemovable";
|
||||
export { ChipsToggle, type ChipsToggleProps } from "./toggle/ChipsToggle";
|
||||
export { default as Chips, ChipsRemovable, ChipsToggle } from "./Chips";
|
||||
export type {
|
||||
ChipsProps,
|
||||
ChipsRemovableProps,
|
||||
ChipsToggleProps,
|
||||
} from "./Chips";
|
||||
@@ -1,3 +0,0 @@
|
||||
"use client";
|
||||
export { DataGrid } from "./root/DataGridRoot";
|
||||
export { DataGridTable } from "../data/table/root/DataGridTableRoot";
|
||||
@@ -1,51 +0,0 @@
|
||||
type DataGridSettings = {
|
||||
/**
|
||||
* Controls vertical cell padding.
|
||||
* @default "standard"
|
||||
*/
|
||||
rowDensity?: "tight" | "standard" | "loose";
|
||||
/**
|
||||
* Zebra striped table
|
||||
* @default false
|
||||
*/
|
||||
zebraStripes?: boolean;
|
||||
/**
|
||||
* Adds vertical borders between columns.
|
||||
* @default true
|
||||
*/
|
||||
columnDividers?: boolean;
|
||||
/**
|
||||
* Truncate content in cells and show ellipsis for overflowed text.
|
||||
*
|
||||
* **NB:** When using this together with `layout="auto"`,
|
||||
* you have to manually set a `maxWidth` on columns that should be truncated.
|
||||
* @default false if layout="auto", else true
|
||||
*/
|
||||
truncateContent?: boolean;
|
||||
/**
|
||||
* Sticky columns that remain visible when horizontally scrolling the table.
|
||||
*
|
||||
* You can specify 1 sticky column on the left and 1 on the right.
|
||||
*/
|
||||
stickyColumns?: {
|
||||
start?: 1;
|
||||
end?: 1;
|
||||
};
|
||||
/**
|
||||
* Adjusts font-size
|
||||
* @default "medium"
|
||||
*/
|
||||
textSize?: "small" | "medium";
|
||||
/**
|
||||
* Optional configuration for column order and visibility.
|
||||
*
|
||||
* If provided, only columns listed here are rendered, and the array order
|
||||
* determines the rendered column order.
|
||||
*
|
||||
* Set `visible: false` to hide a column without removing it from the list.
|
||||
* Each `id` must be unique and match a column `id` in the `columns` prop.
|
||||
*/
|
||||
columnDisplay?: { id: string; visible: boolean }[];
|
||||
};
|
||||
|
||||
export type { DataGridSettings };
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { SelectionProps } from "../../data/table/hooks/useTableSelection";
|
||||
import type { ColumnDefinitions } from "../../data/table/root/DataGridTable.types";
|
||||
import { createStrictContext } from "../../utils/helpers";
|
||||
import type { DataGridSettings } from "./DataGrid.types";
|
||||
|
||||
type DataGridContextValue<RowT = unknown> = {
|
||||
data: RowT[];
|
||||
columnDefinitions: ColumnDefinitions<RowT>;
|
||||
getRowId?: (rowData: RowT) => string;
|
||||
selection?: SelectionProps<RowT>;
|
||||
isLoading?: boolean;
|
||||
tableSettings?: DataGridSettings;
|
||||
};
|
||||
|
||||
const { Provider: DataGridContextProvider, useContext: useDataGridContext } =
|
||||
createStrictContext<DataGridContextValue>({
|
||||
name: "DataGridContext",
|
||||
errorMessage: "DataGrid hooks must be used within a <DataGrid />",
|
||||
});
|
||||
|
||||
export { DataGridContextProvider, useDataGridContext };
|
||||
@@ -1,156 +0,0 @@
|
||||
import React, { forwardRef, useMemo } from "react";
|
||||
import type { SelectionProps } from "../../data/table/hooks/useTableSelection";
|
||||
import type { ColumnDefinitions } from "../../data/table/root/DataGridTable.types";
|
||||
import { DataGridTable } from "../../data/table/root/DataGridTableRoot";
|
||||
import { cl } from "../../utils/helpers";
|
||||
import type { DataGridSettings } from "./DataGrid.types";
|
||||
import { DataGridContextProvider } from "./DataGridRoot.context";
|
||||
|
||||
interface DataGridProps<RowT> {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
/**
|
||||
* Definitions of the columns to display.
|
||||
*/
|
||||
columns: ColumnDefinitions<RowT>;
|
||||
/**
|
||||
* The data to display.
|
||||
*
|
||||
* Each object in the array represents a row, and the properties of the
|
||||
* object are used to render the cells based on `columnDefinitions`.
|
||||
*/
|
||||
data: RowT[];
|
||||
/**
|
||||
* Function to get unique row ID from row data.
|
||||
*
|
||||
* If not provided, the row index will be used as ID.
|
||||
* This can cause issues if your data changes dynamically,
|
||||
* so it's recommended to provide a stable ID if possible.
|
||||
*/
|
||||
getRowId?: (rowData: RowT) => string;
|
||||
/**
|
||||
* Object with props related to row selection.
|
||||
*/
|
||||
selection?: SelectionProps<RowT>;
|
||||
/**
|
||||
* Determines if the data grid is in a loading state.
|
||||
* See `loadingContent` prop on the `DataGrid.Table` component for display settings.
|
||||
* @default false
|
||||
*/
|
||||
isLoading?: boolean;
|
||||
/**
|
||||
* Settings for the data grid.
|
||||
*/
|
||||
settings?: DataGridSettings;
|
||||
}
|
||||
|
||||
interface DataGridComponent {
|
||||
<RowT>(
|
||||
props: DataGridProps<RowT> & React.RefAttributes<HTMLDivElement>,
|
||||
): React.ReactElement | null;
|
||||
/**
|
||||
* @see 🏷️ {@link DataGridTableProps}
|
||||
*
|
||||
* @example
|
||||
* <DataGrid columnDefinitions={columnDefs} data={rowData} getRowId={(row) => row.id}>
|
||||
* <DataGrid.Table />
|
||||
* </DataGrid>
|
||||
*/
|
||||
Table: typeof DataGridTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* <DataGrid columnDefinitions={columnDefs} data={rowData} getRowId={(row) => row.id}>
|
||||
* <DataGrid.Table />
|
||||
* </DataGrid>
|
||||
* ```
|
||||
*/
|
||||
const DataGridInternal = forwardRef<HTMLDivElement, DataGridProps<any>>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
className,
|
||||
columns,
|
||||
data,
|
||||
getRowId,
|
||||
selection,
|
||||
isLoading = false,
|
||||
settings,
|
||||
...rest
|
||||
}: DataGridProps<unknown>,
|
||||
ref,
|
||||
) => {
|
||||
const resolvedSettings = useMemo(
|
||||
() => ({
|
||||
rowDensity: settings?.rowDensity ?? "standard",
|
||||
zebraStripes: settings?.zebraStripes ?? false,
|
||||
truncateContent: settings?.truncateContent,
|
||||
stickyColumns: settings?.stickyColumns ?? {},
|
||||
textSize: settings?.textSize ?? "medium",
|
||||
columnDisplay: settings?.columnDisplay,
|
||||
columnDividers: settings?.columnDividers ?? true,
|
||||
}),
|
||||
[
|
||||
settings?.rowDensity,
|
||||
settings?.zebraStripes,
|
||||
settings?.truncateContent,
|
||||
settings?.stickyColumns,
|
||||
settings?.textSize,
|
||||
settings?.columnDisplay,
|
||||
settings?.columnDividers,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<div {...rest} ref={ref} className={cl("aksel-data-grid", className)}>
|
||||
<DataGridContextProvider
|
||||
columnDefinitions={columns}
|
||||
data={data}
|
||||
getRowId={getRowId}
|
||||
selection={selection}
|
||||
isLoading={isLoading}
|
||||
tableSettings={resolvedSettings}
|
||||
>
|
||||
{children}
|
||||
</DataGridContextProvider>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const DataGrid = DataGridInternal as unknown as DataGridComponent;
|
||||
DataGrid.Table = DataGridTable;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace, import/export
|
||||
export namespace DataGrid {
|
||||
export type Props<T = unknown> = DataGridProps<T>;
|
||||
export type Columns<T = unknown> = ColumnDefinitions<T>;
|
||||
export type Selection<T = unknown> = SelectionProps<T>;
|
||||
export type Settings = DataGridSettings;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace Table {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
export type Props<T = unknown> = DataGridTable.Props<T>;
|
||||
export type Sorting = DataGridTable.Sorting;
|
||||
export type SortEntry = DataGridTable.SortEntry;
|
||||
export type SortChangeDetail = DataGridTable.SortChangeDetail;
|
||||
export type LoadingContent = DataGridTable.LoadingContent;
|
||||
export type SubRows<T = unknown> = DataGridTable.SubRows<T>;
|
||||
export type DetailsPanel<T = unknown> = DataGridTable.DetailsPanel<T>;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/export
|
||||
export { DataGrid, DataGridInternal }; // DataGridRoot needs to be exported b.c. of docgen
|
||||
export default DataGrid;
|
||||
@@ -0,0 +1,104 @@
|
||||
import React from "react";
|
||||
import {
|
||||
CaretDownCircleFillIcon,
|
||||
CaretUpCircleFillIcon,
|
||||
DragVerticalIcon,
|
||||
} from "@navikt/aksel-icons";
|
||||
import { Floating } from "../../../utils/components/floating/Floating";
|
||||
|
||||
export interface DataDragAndDropDragHandlerProps {
|
||||
/**
|
||||
* Whether the drag handler is disabled
|
||||
*/
|
||||
// disabled?: boolean;
|
||||
/**
|
||||
* Wether dragging is done by keyboard. Used to conditionally render drag indicators.
|
||||
*/
|
||||
keyboardDragging?: boolean;
|
||||
/**
|
||||
* Handle ref is forwarded to the button element serving as drag handle.
|
||||
*/
|
||||
handleRef: React.Ref<HTMLDivElement>;
|
||||
// Just for testing purposes, to render an alternative drag handler
|
||||
alt?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* DataDragAndDropDragHandler
|
||||
*
|
||||
* A button component that serves as a drag handle for drag and drop operations.
|
||||
* Can be used to initiate dragging of elements in a data table or list.
|
||||
*/
|
||||
export const DataDragAndDropDragHandler = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
DataDragAndDropDragHandlerProps
|
||||
>(({ keyboardDragging, handleRef, alt }) => {
|
||||
if (alt) {
|
||||
return (
|
||||
<div className="aksel-data-drag-and-drop__drag-handler__alt">
|
||||
{keyboardDragging && (
|
||||
<span
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
data-direction="up"
|
||||
>
|
||||
<CaretUpCircleFillIcon aria-hidden fontSize="1.2rem" />
|
||||
</span>
|
||||
)}
|
||||
<div
|
||||
ref={handleRef}
|
||||
className="aksel-data-drag-and-drop__drag-handler__button"
|
||||
>
|
||||
<DragVerticalIcon
|
||||
aria-hidden
|
||||
title="Dra for å flytte"
|
||||
fontSize="1.5rem"
|
||||
/>
|
||||
</div>
|
||||
{keyboardDragging && (
|
||||
<span
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
data-direction="down"
|
||||
>
|
||||
<CaretDownCircleFillIcon aria-hidden fontSize="1.2rem" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Floating>
|
||||
{keyboardDragging && (
|
||||
<Floating.Content
|
||||
side="top"
|
||||
avoidCollisions={false}
|
||||
updatePositionStrategy="always"
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
>
|
||||
<CaretUpCircleFillIcon aria-hidden fontSize="1.2rem" />
|
||||
</Floating.Content>
|
||||
)}
|
||||
<Floating.Anchor asChild>
|
||||
<div
|
||||
ref={handleRef}
|
||||
className="aksel-data-drag-and-drop__drag-handler__button"
|
||||
>
|
||||
<DragVerticalIcon
|
||||
aria-hidden
|
||||
title="Dra for å flytte"
|
||||
fontSize="1.5rem"
|
||||
/>
|
||||
</div>
|
||||
</Floating.Anchor>
|
||||
{keyboardDragging && (
|
||||
<Floating.Content
|
||||
avoidCollisions={false}
|
||||
updatePositionStrategy="always"
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
>
|
||||
<CaretDownCircleFillIcon aria-hidden fontSize="1.2rem" />
|
||||
</Floating.Content>
|
||||
)}
|
||||
</Floating>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useSortable } from "@dnd-kit/react/sortable";
|
||||
import React, { useRef } from "react";
|
||||
import { HStack } from "../../../primitives/stack";
|
||||
import { cl } from "../../../utils/helpers";
|
||||
import { useMergeRefs } from "../../../utils/hooks";
|
||||
import { DataDragAndDropDragHandler } from "../drag-handler/DataDragAndDropDragHandler";
|
||||
import { DataDragAndDropContext } from "../root/DataDragAndDrop.context";
|
||||
|
||||
interface DataDragAndDropItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Unique id
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Index of the item being dragged
|
||||
*/
|
||||
index: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @see 🏷️ {@link DataDragAndDropItemProps}
|
||||
* @example
|
||||
* ```tsx
|
||||
* <DragAndDrop.Item numOfSelectedRows={selectedRows.length} onClear={handleClear}>
|
||||
* TODO
|
||||
* </DragAndDrop.Item>
|
||||
* ```
|
||||
*/
|
||||
const DataDragAndDropItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DataDragAndDropItemProps
|
||||
>(({ children, id, index, className, ...rest }, forwardedRef) => {
|
||||
const handleRef = useRef<HTMLDivElement>(null);
|
||||
const { ref, isDragging, isDropTarget } = useSortable({
|
||||
id,
|
||||
index,
|
||||
handle: handleRef,
|
||||
});
|
||||
const mergedRef = useMergeRefs(ref, forwardedRef);
|
||||
const context = React.useContext(DataDragAndDropContext);
|
||||
const mouseDragging = isDragging && context?.inputMethod === "mouse";
|
||||
const mouseDropTarget = isDropTarget && context?.inputMethod === "mouse";
|
||||
const keyboardDragging = isDragging && context?.inputMethod === "keyboard";
|
||||
|
||||
return (
|
||||
<HStack gap="space-8" align="center" wrap={false} asChild>
|
||||
{/* TODO Should this be a <li>? */}
|
||||
<div
|
||||
ref={mergedRef}
|
||||
{...rest}
|
||||
className={cl("aksel-data-table__drag-and-drop-item", className)}
|
||||
data-dragging={isDragging}
|
||||
data-mouse-dragging={mouseDragging}
|
||||
data-keyboard-dragging={keyboardDragging}
|
||||
data-drop-target={mouseDropTarget}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<DataDragAndDropDragHandler
|
||||
handleRef={handleRef}
|
||||
keyboardDragging={keyboardDragging}
|
||||
alt
|
||||
/>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</HStack>
|
||||
);
|
||||
});
|
||||
|
||||
export default DataDragAndDropItem;
|
||||
export { DataDragAndDropItem };
|
||||
export type { DataDragAndDropItemProps };
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
interface DataDragAndDropContextType {
|
||||
inputMethod: "mouse" | "keyboard" | null;
|
||||
// setInputMethod: (method: "mouse" | "keyboard" | null) => void;
|
||||
// setItems: React.Dispatch<React.SetStateAction<any[]>>;
|
||||
}
|
||||
|
||||
export const DataDragAndDropContext = createContext<
|
||||
DataDragAndDropContextType | undefined
|
||||
>(undefined);
|
||||
@@ -0,0 +1,96 @@
|
||||
import { DragDropProvider, DragOverlay } from "@dnd-kit/react";
|
||||
import { isSortable } from "@dnd-kit/react/sortable";
|
||||
import React, { forwardRef, isValidElement } from "react";
|
||||
import { VStack } from "../../../primitives/stack";
|
||||
import DataDragAndDropItem, {
|
||||
DataDragAndDropItemProps,
|
||||
} from "../item/DataDragAndDropItem";
|
||||
import { DataDragAndDropContext } from "./DataDragAndDrop.context";
|
||||
|
||||
interface DataDragAndDropProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: any[];
|
||||
setItems: React.Dispatch<React.SetStateAction<any[]>>;
|
||||
}
|
||||
|
||||
interface DataDragAndDropRootComponent extends React.ForwardRefExoticComponent<
|
||||
DataDragAndDropProps & React.RefAttributes<HTMLDivElement>
|
||||
> {
|
||||
/**
|
||||
* @see 🏷️ {@link DataDragAndDropItemProps}
|
||||
* * @example
|
||||
* ```jsx
|
||||
* <DragAndDrop>
|
||||
* <DragAndDrop.Item id="1" index={0}>
|
||||
* ...
|
||||
* </DragAndDrop.Item>
|
||||
* </DragAndDrop>
|
||||
* ```
|
||||
*/
|
||||
Item: typeof DataDragAndDropItem;
|
||||
}
|
||||
|
||||
const DataDragAndDrop = forwardRef<HTMLDivElement, DataDragAndDropProps>(
|
||||
({ setItems, children, ...rest }, forwardedRef) => {
|
||||
const [inputMethod, setInputMethod] = React.useState<
|
||||
"mouse" | "keyboard" | null
|
||||
>(null);
|
||||
|
||||
const setItemOrder = (initalIndex: number, targetIndex: number) => {
|
||||
setItems((items) => {
|
||||
const newItems = [...items];
|
||||
const [movedItem] = newItems.splice(initalIndex, 1);
|
||||
newItems.splice(targetIndex, 0, movedItem);
|
||||
return newItems;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DataDragAndDropContext.Provider value={{ inputMethod }}>
|
||||
<DragDropProvider
|
||||
// TODO Look into overriding default keybinds, might eliminate context need
|
||||
onBeforeDragStart={(event) =>
|
||||
setInputMethod(
|
||||
event.operation.activatorEvent?.type === "pointerdown"
|
||||
? "mouse"
|
||||
: "keyboard",
|
||||
)
|
||||
}
|
||||
onDragOver={(event) => {
|
||||
if (event.operation.activatorEvent?.type === "pointerdown") {
|
||||
// Prevents items to rearrange while dragging with mouse, but allows keyboard dragging to work as intended
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
onDragEnd={(event) => {
|
||||
const { source, target } = event.operation;
|
||||
if (!isSortable(source) || !isSortable(target)) return;
|
||||
setItemOrder(source.initialIndex, target.index);
|
||||
}}
|
||||
>
|
||||
<VStack asChild gap="space-12">
|
||||
<div {...rest} ref={forwardedRef}>
|
||||
{children}
|
||||
</div>
|
||||
</VStack>
|
||||
<DragOverlay dropAnimation={null}>
|
||||
{(source) => {
|
||||
// Overlay not needed and causes glitching when using keyboard
|
||||
if (inputMethod === "keyboard") return null;
|
||||
if (!isSortable(source)) return null;
|
||||
if (isValidElement(children[source.initialIndex])) {
|
||||
return children[source.initialIndex];
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</DragOverlay>
|
||||
</DragDropProvider>
|
||||
</DataDragAndDropContext.Provider>
|
||||
);
|
||||
},
|
||||
) as DataDragAndDropRootComponent;
|
||||
|
||||
DataDragAndDrop.Item = DataDragAndDropItem;
|
||||
|
||||
export { DataDragAndDrop, DataDragAndDropItem };
|
||||
export default DataDragAndDrop;
|
||||
export type { DataDragAndDropItemProps, DataDragAndDropProps };
|
||||
@@ -49,7 +49,7 @@ export const DragAndDropDragHandler = React.forwardRef<
|
||||
<button
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
data-direction="up"
|
||||
onClick={() => context?.onKeyboardDragEnd(-1, itemLabel)}
|
||||
onClick={() => context?.onKeyboardDragEnd(-1)}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
disabled={context?.dragHandlerActive?.index === 0}
|
||||
type="button"
|
||||
@@ -58,7 +58,13 @@ export const DragAndDropDragHandler = React.forwardRef<
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
aria-label={`${itemLabel}. Plass ${item.index + 1}.`}
|
||||
// TODO - Bedre formulering av aria-label?
|
||||
//aria-label={`Flytt element ${item.index + 1}. Trykk Enter eller Mellomrom for å aktivere, deretter piltastene for å flytte elementet.`}
|
||||
aria-label={
|
||||
active
|
||||
? `Flytt element ${itemLabel || item.index + 1}. Bruk piltastene for å flytte elementet.`
|
||||
: `Flytt element ${itemLabel || item.index + 1}. Trykk Enter eller Mellomrom for å aktivere flytting.`
|
||||
}
|
||||
aria-pressed={Boolean(active)}
|
||||
aria-roledescription="draggable"
|
||||
type="button"
|
||||
@@ -71,22 +77,18 @@ export const DragAndDropDragHandler = React.forwardRef<
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (!active) {
|
||||
context?.onKeyboardDragStart(item);
|
||||
context?.setDragHandlerActive(item);
|
||||
event.currentTarget.focus();
|
||||
} else {
|
||||
context?.onKeyboardDragStart(null);
|
||||
context?.setDragHandlerActive(null);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (context?.activeItem) return;
|
||||
|
||||
context?.cancelDrag();
|
||||
}}
|
||||
onBlur={() => context?.setDragHandlerActive(null)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
// Enter or space, currently active item - end keyboard dragging
|
||||
event.preventDefault();
|
||||
context?.onKeyboardDragStart(active ? null : item);
|
||||
context?.setDragHandlerActive(active ? null : item);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,19 +96,19 @@ export const DragAndDropDragHandler = React.forwardRef<
|
||||
|
||||
if (event.key === "Escape") {
|
||||
// Cancel dragging
|
||||
// TODO Handle reset
|
||||
event.preventDefault();
|
||||
context?.setAnnouncer(`Flytting av ${itemLabel} avbrutt.`);
|
||||
context?.cancelDrag(true);
|
||||
context?.setDragHandlerActive(null);
|
||||
return;
|
||||
} else if (event.key === "ArrowUp") {
|
||||
// Move item up
|
||||
event.preventDefault();
|
||||
context?.onKeyboardDragEnd(-1, itemLabel);
|
||||
context?.onKeyboardDragEnd(-1);
|
||||
return;
|
||||
} else if (event.key === "ArrowDown") {
|
||||
// Move item down
|
||||
event.preventDefault();
|
||||
context?.onKeyboardDragEnd(1, itemLabel);
|
||||
context?.onKeyboardDragEnd(1);
|
||||
return;
|
||||
}
|
||||
}}
|
||||
@@ -118,7 +120,7 @@ export const DragAndDropDragHandler = React.forwardRef<
|
||||
className="aksel-data-drag-and-drop__drag-handler__arrow"
|
||||
data-direction="down"
|
||||
type="button"
|
||||
onClick={() => context?.onKeyboardDragEnd(1, itemLabel)}
|
||||
onClick={() => context?.onKeyboardDragEnd(1)}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
disabled={
|
||||
context?.dragHandlerActive?.index === context?.itemAmount - 1
|
||||
|
||||
@@ -7,16 +7,14 @@ interface DragAndDropContextType {
|
||||
dropTarget: DragAndDropElement | null;
|
||||
setDropTarget: (id: DragAndDropElement | null) => void;
|
||||
dragHandlerActive: DragAndDropElement | null;
|
||||
onKeyboardDragStart: (active: DragAndDropElement | null) => void;
|
||||
onKeyboardDragEnd: (diff: number, label: string) => void;
|
||||
setDragHandlerActive: (active: DragAndDropElement | null) => void;
|
||||
onKeyboardDragEnd: (diff: number) => void;
|
||||
startPendingDrag: (
|
||||
event: React.PointerEvent,
|
||||
item: DragAndDropElement,
|
||||
element?: HTMLElement | null,
|
||||
) => void;
|
||||
itemAmount: number;
|
||||
cancelDrag: (resetOrder?: boolean) => void;
|
||||
setAnnouncer: (message: string) => void;
|
||||
}
|
||||
|
||||
export const {
|
||||
|
||||
@@ -3,17 +3,14 @@ import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useId } from "../../../utils-external";
|
||||
import { Floating } from "../../../utils/components/floating/Floating";
|
||||
import { cl } from "../../../utils/helpers";
|
||||
import {
|
||||
ColumnDefinition,
|
||||
ColumnDefinitions,
|
||||
} from "../../table/root/DataGridTable.types";
|
||||
} from "../../table/root/DataTable.types";
|
||||
import DragAndDropItem, { DragAndDropItemProps } from "../item/DragAndDropItem";
|
||||
import { DragAndDropElement } from "../types";
|
||||
import { DragAndDropProvider } from "./DragAndDrop.context";
|
||||
@@ -25,50 +22,54 @@ interface DragAndDropProps<T> extends React.HTMLAttributes<HTMLUListElement> {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
*
|
||||
* Backlog:
|
||||
* [ ] Quick nav (< > samtidig) - få piltastene til å fungere - ignore?
|
||||
* [ ] Look at instructions text
|
||||
* TODO
|
||||
* [x] setItems on root
|
||||
* [x] state : active element
|
||||
* [x] pointer over listener / state, onPointerEnter, onPointerLeave
|
||||
* [x] Overlay - Use floating component
|
||||
* [x] Keyboard navigation
|
||||
* [ ] UU - announce on drag start, item moved, drag end
|
||||
* [x] Make overlay same width as the OG item, currently jumps to content width
|
||||
* [x] Look into adding a cancel listener event
|
||||
* [x] Make onClick work on drag handler button, currently blocked by pointer down/up listeners
|
||||
* [x] Talk to design about what should happen on ESC key press, currently just cancels dragging, should it also reset position?
|
||||
* [x] Make arrow icons into buttons that react to keyboard events, currently just decorative
|
||||
* [x] Keep handler focus after clicking arrows for dragging
|
||||
* [x] Look into data-based API vs component-based API
|
||||
* [ ] Should we have hidden instructions for screen readers on how to use the drag and drop, and should we announce the position of the item while dragging?
|
||||
* [x] Discuss if this component should be generic for drag and drop, or if it should be specifically for tables - just for table for now
|
||||
* [x] Discuss items type
|
||||
* [ ] Discuss how to implement label best
|
||||
* [ ] Quick nav (< > samtidig) - få piltastene til å fungere
|
||||
* [x] Implement new type for items - ColumnDefinitions<T>
|
||||
* [ ] Remove announcer div and use a live region component instead
|
||||
* [ ] Make ESC reset position, not just cancel dragging
|
||||
* [ ] Make instructions for keyboard users (visible?)
|
||||
* [ ] Ask design about visible keyboard instructions
|
||||
*
|
||||
*/
|
||||
|
||||
const DRAG_THRESHOLD = 4; // Minimum movement in pixels to start dragging
|
||||
|
||||
function DragAndDropInner<T>(
|
||||
{ items, setItems, renderItem, className, ...rest }: DragAndDropProps<T>,
|
||||
{ items, setItems, renderItem }: DragAndDropProps<T>,
|
||||
forwardedRef: React.ForwardedRef<HTMLUListElement>,
|
||||
) {
|
||||
const instructionsId = useId();
|
||||
const [activeItem, setActiveItem] = useState<DragAndDropElement | null>(null);
|
||||
const [dropTarget, setDropTarget] = useState<DragAndDropElement | null>(null);
|
||||
const [dragHandlerActive, setDragHandlerActive] =
|
||||
useState<DragAndDropElement | null>(null);
|
||||
const [overlayWidth, setOverlayWidth] = useState<number | null>(null);
|
||||
const [announcer, setAnnouncer] = useState("");
|
||||
const initialItemsRef = useRef<ColumnDefinitions<T> | null>(null);
|
||||
const virtualPositionRef = useRef({ x: 0, y: 0 });
|
||||
const itemsById = useMemo(
|
||||
() => new Map(items.map((item) => [item.id, item] as const)),
|
||||
[items],
|
||||
);
|
||||
const activeData = activeItem ? itemsById.get(activeItem.id) : undefined;
|
||||
const [announcement, setAnnouncement] = useState("");
|
||||
const activeData = items.find((item) => item.id === activeItem?.id);
|
||||
|
||||
const activeItemRef = useRef<DragAndDropElement | null>(null);
|
||||
const dropTargetRef = useRef<DragAndDropElement | null>(null);
|
||||
|
||||
const virtualRef = useMemo(
|
||||
() => ({
|
||||
getBoundingClientRect: () =>
|
||||
DOMRect.fromRect({
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: virtualPositionRef.current.x,
|
||||
y: virtualPositionRef.current.y,
|
||||
}),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const [virtualRef, setVirtualRef] = useState({
|
||||
getBoundingClientRect: () =>
|
||||
DOMRect.fromRect({ width: 0, height: 0, x: 0, y: 0 }),
|
||||
});
|
||||
|
||||
const pendingDragStartRef = useRef<{
|
||||
item: DragAndDropElement;
|
||||
@@ -78,19 +79,6 @@ function DragAndDropInner<T>(
|
||||
startY: number;
|
||||
} | null>(null);
|
||||
|
||||
const saveInitialItems = useCallback(() => {
|
||||
initialItemsRef.current = items;
|
||||
}, [items]);
|
||||
|
||||
const keyboardDragStart = (item: DragAndDropElement | null) => {
|
||||
if (item) {
|
||||
saveInitialItems();
|
||||
} else {
|
||||
initialItemsRef.current = null;
|
||||
}
|
||||
setDragHandlerActive(item);
|
||||
};
|
||||
|
||||
const startPendingDrag = (
|
||||
event: React.PointerEvent,
|
||||
item: DragAndDropElement,
|
||||
@@ -115,11 +103,6 @@ function DragAndDropInner<T>(
|
||||
|
||||
const setCombinedDropTarget = useCallback(
|
||||
(item: DragAndDropElement | null) => {
|
||||
const previous = dropTargetRef.current;
|
||||
if (previous?.id === item?.id && previous?.index === item?.index) {
|
||||
return;
|
||||
}
|
||||
|
||||
dropTargetRef.current = item;
|
||||
setDropTarget(item);
|
||||
},
|
||||
@@ -129,22 +112,8 @@ function DragAndDropInner<T>(
|
||||
const reorderItems = useCallback(
|
||||
(fromIndex: number, toIndex: number) => {
|
||||
setItems((currentItems) => {
|
||||
if (
|
||||
fromIndex === toIndex ||
|
||||
fromIndex < 0 ||
|
||||
toIndex < 0 ||
|
||||
fromIndex >= currentItems.length ||
|
||||
toIndex >= currentItems.length
|
||||
) {
|
||||
return currentItems;
|
||||
}
|
||||
|
||||
const newItems = [...currentItems];
|
||||
const [movedItem] = newItems.splice(fromIndex, 1);
|
||||
if (!movedItem) {
|
||||
return currentItems;
|
||||
}
|
||||
|
||||
newItems.splice(toIndex, 0, movedItem);
|
||||
return newItems;
|
||||
});
|
||||
@@ -152,24 +121,9 @@ function DragAndDropInner<T>(
|
||||
[setItems],
|
||||
);
|
||||
|
||||
const cancelDrag = useCallback(
|
||||
(resetOrder = false) => {
|
||||
if (resetOrder && initialItemsRef.current) {
|
||||
setItems(initialItemsRef.current);
|
||||
}
|
||||
setOverlayWidth(null);
|
||||
setDragHandlerActive(null);
|
||||
setCombinedActiveItem(null);
|
||||
setCombinedDropTarget(null);
|
||||
pendingDragStartRef.current = null;
|
||||
initialItemsRef.current = null;
|
||||
},
|
||||
[setItems, setCombinedActiveItem, setCombinedDropTarget],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
/* This useEffect is used to toggle a class on the html element when dragging,
|
||||
to prevent cursor issues when dragging over interactive elements,
|
||||
/* This useEffect is used to toggle a class on the html element when dragging,
|
||||
to prevent cursor issues when dragging over interactive elements,
|
||||
and to prevent text selection during dragging. */
|
||||
|
||||
if (activeItem) {
|
||||
@@ -196,13 +150,12 @@ function DragAndDropInner<T>(
|
||||
const deltaX = Math.abs(event.clientX - pendingStart.startX);
|
||||
const deltaY = Math.abs(event.clientY - pendingStart.startY);
|
||||
|
||||
if (deltaX >= DRAG_THRESHOLD || deltaY >= DRAG_THRESHOLD) {
|
||||
if (deltaX > DRAG_THRESHOLD || deltaY > DRAG_THRESHOLD) {
|
||||
if (element) {
|
||||
element.setPointerCapture(pendingStart.pointerId);
|
||||
}
|
||||
|
||||
setOverlayWidth(element?.getBoundingClientRect().width ?? null);
|
||||
saveInitialItems();
|
||||
setCombinedActiveItem(pendingStart.item);
|
||||
setCombinedDropTarget(pendingStart.item);
|
||||
pendingDragStartRef.current = null;
|
||||
@@ -213,7 +166,15 @@ function DragAndDropInner<T>(
|
||||
const active = activeItemRef.current;
|
||||
if (!active) return;
|
||||
|
||||
virtualPositionRef.current = { x: event.clientX, y: event.clientY };
|
||||
setVirtualRef({
|
||||
getBoundingClientRect: () =>
|
||||
DOMRect.fromRect({
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
}),
|
||||
});
|
||||
|
||||
const elements = document.elementsFromPoint(event.clientX, event.clientY);
|
||||
|
||||
@@ -260,12 +221,25 @@ function DragAndDropInner<T>(
|
||||
|
||||
if (active && target && active.id !== target.id) {
|
||||
reorderItems(active.index, target.index);
|
||||
setAnnouncement(
|
||||
`Element flyttet til posisjon ${target?.index + 1} av ${items.length}`,
|
||||
); // TODO - Bedre formulering?
|
||||
}
|
||||
|
||||
cancelDrag();
|
||||
setOverlayWidth(null);
|
||||
setDragHandlerActive(null);
|
||||
setCombinedActiveItem(null);
|
||||
setCombinedDropTarget(null);
|
||||
pendingDragStartRef.current = null;
|
||||
};
|
||||
|
||||
const handlePointerCancel = () => cancelDrag(true);
|
||||
const handlePointerCancel = () => {
|
||||
setOverlayWidth(null);
|
||||
setDragHandlerActive(null);
|
||||
setCombinedActiveItem(null);
|
||||
setCombinedDropTarget(null);
|
||||
pendingDragStartRef.current = null;
|
||||
};
|
||||
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
@@ -280,13 +254,10 @@ function DragAndDropInner<T>(
|
||||
setCombinedDropTarget,
|
||||
setCombinedActiveItem,
|
||||
reorderItems,
|
||||
saveInitialItems,
|
||||
cancelDrag,
|
||||
items.length,
|
||||
]);
|
||||
|
||||
const describedBy = cl(rest["aria-describedby"], instructionsId);
|
||||
|
||||
const onKeyboardDragEnd = (diff: number, label: string) => {
|
||||
const onKeyboardDragEnd = (diff: number) => {
|
||||
if (!dragHandlerActive) return;
|
||||
|
||||
const targetIndex = dragHandlerActive.index + diff;
|
||||
@@ -294,9 +265,11 @@ function DragAndDropInner<T>(
|
||||
return;
|
||||
}
|
||||
|
||||
setAnnouncer(`${label}. Plass ${targetIndex + 1} av ${items.length}.`);
|
||||
reorderItems(dragHandlerActive.index, targetIndex);
|
||||
setDragHandlerActive({ ...dragHandlerActive, index: targetIndex });
|
||||
setAnnouncement(
|
||||
`Element flyttet til posisjon ${targetIndex + 1} av ${items.length}`,
|
||||
); // TODO - Bedre formulering?
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -306,35 +279,19 @@ function DragAndDropInner<T>(
|
||||
dropTarget={dropTarget}
|
||||
setDropTarget={setCombinedDropTarget}
|
||||
dragHandlerActive={dragHandlerActive}
|
||||
onKeyboardDragStart={keyboardDragStart}
|
||||
setDragHandlerActive={setDragHandlerActive}
|
||||
onKeyboardDragEnd={onKeyboardDragEnd}
|
||||
startPendingDrag={startPendingDrag}
|
||||
cancelDrag={cancelDrag}
|
||||
setAnnouncer={setAnnouncer}
|
||||
itemAmount={items.length}
|
||||
>
|
||||
<span id={instructionsId} className="aksel-sr-only">
|
||||
Bruk Tab for å fokusere på en kolonne. Trykk mellomrom eller enter for å
|
||||
starte flytting, bruk piltastene for å flytte kolonnen, trykk mellomrom
|
||||
eller enter for å slippe, eller Escape for å avbryte.
|
||||
</span>
|
||||
<div aria-live="assertive" className="aksel-sr-only" aria-atomic>
|
||||
{announcer}
|
||||
</div>
|
||||
<ul
|
||||
{...rest}
|
||||
ref={forwardedRef}
|
||||
aria-label={rest["aria-label"] ?? "Kolonneinnstillinger"}
|
||||
aria-describedby={describedBy}
|
||||
className={cl("aksel-data-table__drag-and-drop-root", className)}
|
||||
>
|
||||
<ul ref={forwardedRef} aria-label="Dra og slipp elementer">
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<DragAndDropItem
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
index={index}
|
||||
itemLabel={item.header}
|
||||
itemLabel={item.label}
|
||||
>
|
||||
{renderItem(item, index)}
|
||||
</DragAndDropItem>
|
||||
@@ -349,7 +306,6 @@ function DragAndDropInner<T>(
|
||||
<Floating.Content
|
||||
align="start"
|
||||
updatePositionStrategy="always"
|
||||
aria-hidden
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
boxSizing: "border-box",
|
||||
@@ -360,13 +316,16 @@ function DragAndDropInner<T>(
|
||||
id={activeItem.id}
|
||||
index={activeItem.index}
|
||||
isOverlay
|
||||
itemLabel={activeData.header}
|
||||
itemLabel={activeData.label}
|
||||
>
|
||||
{renderItem(activeData, activeItem.index)}
|
||||
</DragAndDropItem>
|
||||
</Floating.Content>
|
||||
</Floating>
|
||||
)}
|
||||
<div aria-live="assertive" className="sr-only" aria-atomic="true">
|
||||
{announcement}
|
||||
</div>
|
||||
</DragAndDropProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,3 +2,8 @@ export interface DragAndDropElement {
|
||||
id: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface DragAndDropItemData {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||