mirror of
https://github.com/navikt/aksel.git
synced 2026-10-16 02:32:02 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d566298e4 | ||
|
|
b5dc86785d | ||
|
|
58e564fed0 | ||
|
|
c4c51dcd37 | ||
|
|
5c913ae3d6 |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
---
|
||||
|
||||
Dialog: Improved scrollbar handling when opening dialog-popup.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
---
|
||||
|
||||
useDatepicker, useMonthpicker: Add JsDoc to validation object
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@navikt/ds-react": patch
|
||||
---
|
||||
|
||||
DatePicker, MonthPicker: Removed controlled tabindex on Input-button based on open-state.
|
||||
@@ -13,4 +13,4 @@
|
||||
- [ ] Exports (`@navikt/core/react/src/index.ts` and `@navikt/core/react/package.json`)
|
||||
- [ ] New component? CSS import (`@navikt/core/css/index.css`)
|
||||
- [ ] Breaking change? Update migration guide. Consider codemod.
|
||||
- [ ] Changeset (Format: `<Component>: <gitmoji?> <Text>` E.g. "Button: :sparkles: Add feature xyz")
|
||||
- [ ] Changeset (Format: `<Component>: <gitmoji?> <Text>.` E.g. "Button: :sparkles: Add feature xyz.")
|
||||
+203
-102
@@ -1,145 +1,246 @@
|
||||
# Aksel - Copilot Instructions
|
||||
# Aksel – GitHub Copilot Instructions
|
||||
|
||||
## Use this file first
|
||||
This file tells Copilot how to work inside the Aksel monorepo. Prefer existing patterns and symbols, keep changes minimal, and reference real files in this repo.
|
||||
|
||||
- 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.
|
||||
## How to act in this monorepo (quick)
|
||||
|
||||
## How to use this file
|
||||
- Edit only what’s asked. Don’t invent files/APIs or refactors.
|
||||
- One file at a time. Provide a single coherent diff per file.
|
||||
- Use existing exports, tokens, CSS layers, and patterns before adding new ones.
|
||||
- When touching packages under `@navikt/*`, also update exports and stories nearby.
|
||||
- Add or adjust tests and stories when behavior/visuals change.
|
||||
- Link files with repo paths like `@navikt/react/src/...` or `aksel.nav.no/website/...`.
|
||||
- Keep changes focused and minimal; avoid drive-by rewrites or formatting churn.
|
||||
- Preserve public APIs unless the request explicitly calls for a breaking change (add a changeset if user-facing).
|
||||
|
||||
- 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
|
||||
## General code guidelines (Copilot)
|
||||
|
||||
- Verify before you claim. Don’t speculate.
|
||||
- Preserve unrelated code and existing structure.
|
||||
- No apologies, no “I think”, no change summaries.
|
||||
- Don’t ask to confirm info that’s already in context.
|
||||
- Prefer explicit, descriptive names and follow current style.
|
||||
- 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.
|
||||
- Keep functions small and focused; remove duplication.
|
||||
- Replace hardcoded values with named constants when reasonable.
|
||||
- Prefer composition over deep inheritance; keep modules cohesive.
|
||||
|
||||
## Inline chat and editor completions
|
||||
|
||||
- Verify before you claim. Don’t speculate.
|
||||
- Keep functions small and focused; remove duplication (rule of three).
|
||||
- Default tone: short and answer-first. Aim for 1–3 concise sentences before any code.
|
||||
- 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.
|
||||
- Use existing APIs, utilities, tokens, and patterns. Don’t add new deps unless requested.
|
||||
- Preserve style: imports, naming, types, JSX patterns, token usage, CSS layers.
|
||||
- Reference files and symbols with repo-relative paths and backticks, e.g. `@navikt/ds-react/src/...`.
|
||||
- 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 tests/visual changes: add/update the closest test/story next to the changed file.
|
||||
- For Next.js: don’t mix `app/` and `pages/` in the same hierarchy; prefer server components where used; add "use client" only when required.
|
||||
- For CSS/tokens: never hard-code colors/spacing; use tokens and existing layers.
|
||||
- For performance/security: avoid unnecessary re-renders, side effects at module top-level, unsafe HTML, and leaking env vars.
|
||||
|
||||
## Repo summary
|
||||
## Repository overview
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
Aksel is NAV’s design system: React components, CSS, design tokens, icons, tooling, and the documentation site. It’s a Yarn 4 workspaces monorepo.
|
||||
|
||||
## Start and validate
|
||||
- Languages: TypeScript, CSS, React, Next.js
|
||||
- Package manager: Yarn v4 (packageManager: `yarn@4.4.0`)
|
||||
- Build: Per-package TypeScript builds (run via root scripts)
|
||||
- Tests: Vitest + React Testing Library
|
||||
|
||||
1. `corepack enable`
|
||||
2. `yarn install`
|
||||
3. `yarn boot`
|
||||
Related files: `package.json`, `eslint.config.js`, `biome.json`, `tsconfig.json`.
|
||||
|
||||
- Run `yarn boot` after first install and after `yarn clean`.
|
||||
- Full install may need `NPM_AUTH_TOKEN` for GitHub Packages.
|
||||
## Prerequisites
|
||||
|
||||
Public-package-only install:
|
||||
- Yarn 4 via Corepack is expected by the repo (`packageManager` field).
|
||||
- Some maintenance scripts use Deno (for example `scripts/deno/changelog/createMainChangelog.ts`). Install Deno if you run those.
|
||||
- Most packages are public. If you encounter 401s from GitHub Packages in a custom setup, set `NPM_AUTH_TOKEN` with `read:packages`.
|
||||
|
||||
For limited installs (no website), you can focus core packages:
|
||||
|
||||
```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
|
||||
## Repository structure (short)
|
||||
|
||||
- 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.
|
||||
```
|
||||
@navikt/
|
||||
├─ core/react # @navikt/ds-react – React components
|
||||
├─ core/css # @navikt/ds-css – CSS
|
||||
├─ core/tokens # @navikt/ds-tokens – tokens
|
||||
├─ core/tailwind # @navikt/ds-tailwind – Tailwind preset
|
||||
├─ aksel-icons # @navikt/aksel-icons – icons
|
||||
├─ aksel-stylelint # @navikt/aksel-stylelint – stylelint rules
|
||||
└─ aksel # @navikt/aksel – CLI/tooling
|
||||
|
||||
### Common local commands
|
||||
aksel.nav.no/
|
||||
├─ website # Next.js documentation site
|
||||
└─ playroom # Component playground
|
||||
|
||||
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:
|
||||
examples/ # Example apps
|
||||
scripts/ # Build/maintenance scripts
|
||||
```
|
||||
|
||||
- `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 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`
|
||||
- `yarn workspace @navikt/ds-react test`
|
||||
- `yarn workspace website test`
|
||||
- `yarn workspace playroom sync-imports`
|
||||
## Build and development
|
||||
|
||||
### Validated locally
|
||||
Setup:
|
||||
|
||||
- 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`.
|
||||
```bash
|
||||
yarn && yarn boot
|
||||
```
|
||||
|
||||
## Where to edit
|
||||
Useful scripts (root `package.json`):
|
||||
|
||||
- `@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 token source
|
||||
- `@navikt/core/tailwind/` - Tailwind preset
|
||||
- `@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 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`
|
||||
```bash
|
||||
yarn boot # Build all workspaces (topological)
|
||||
yarn storybook # Storybook dev (port 6006)
|
||||
yarn storybook:aksel # Storybook with website config (port 6007)
|
||||
yarn dev # Run documentation website
|
||||
yarn test # Run workspace tests
|
||||
yarn lint # ESLint + Stylelint (full install recommended)
|
||||
yarn clean # Clean generated files
|
||||
yarn changeset # Create a changeset
|
||||
yarn changelog # Generate main changelog (Deno)
|
||||
yarn biome:lint # Optional extra linting with Biome
|
||||
```
|
||||
|
||||
## Coding defaults
|
||||
Note: `yarn lint` runs across the repo and may require full install (website ESLint plugins).
|
||||
|
||||
- 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.
|
||||
## CI and validation
|
||||
|
||||
## React and ds-react specifics
|
||||
- Pre-commit (Husky): `lint-staged`, plus internal version sync checks.
|
||||
- CI typically runs: lint, unit tests, example/website checks, and Chromatic when applicable.
|
||||
|
||||
- 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`.
|
||||
Common failure causes:
|
||||
|
||||
## CSS and tokens
|
||||
- Missing dependencies in a focused install when running repo-wide scripts
|
||||
- TypeScript errors in dependent workspaces
|
||||
- Stylelint token checks failing due to missing token imports
|
||||
|
||||
- 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.
|
||||
Quality gates to check locally before opening a PR:
|
||||
|
||||
## Stories, tests, website
|
||||
- Build: `yarn boot` succeeds without TS errors.
|
||||
- Lint/format: `yarn lint` (and optional `yarn biome:lint`) passes.
|
||||
- Tests: `yarn test` for changed workspaces are green.
|
||||
- Storybook/website (when UI changes): boots locally without errors.
|
||||
|
||||
- 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`.
|
||||
## Tests
|
||||
|
||||
- Framework: Vitest (jsdom where needed)
|
||||
- Website config: `aksel.nav.no/website/config/vitest.config.ts`
|
||||
- Test locations: `**/__tests__/**` and `**/*.test.*`
|
||||
- Add tests for new behavior and keep existing tests passing
|
||||
- Prefer small, focused tests. Cover the happy path and 1–2 edge cases.
|
||||
- Use Testing Library queries that reflect user intent (role/name), avoid brittle selectors.
|
||||
|
||||
## Linting and formatting
|
||||
|
||||
- ESLint config lives at root: `eslint.config.js` (React, Next.js, Storybook, Testing Library, custom rules)
|
||||
- Stylelint is configured in root `package.json` with `@navikt/aksel-stylelint`
|
||||
- Tokens are validated via Stylelint and `csstools/value-no-unknown-custom-properties`
|
||||
- Biome config: `biome.json` (optional extra lint/format)
|
||||
- Prettier uses import-sort; website adds Tailwind plugin via override
|
||||
- Avoid disabling lint rules unless necessary; if you must, comment why.
|
||||
|
||||
## Packages and exports
|
||||
|
||||
- Synchronized versions: `@navikt/ds-react`, `@navikt/ds-css`, `@navikt/aksel-icons`, `@navikt/ds-tokens`, `@navikt/ds-tailwind`
|
||||
- Typical package layout:
|
||||
|
||||
```
|
||||
src/
|
||||
├─ index.ts # Package exports
|
||||
└─ <component>/
|
||||
├─ index.ts # Component exports
|
||||
├─ <component>.tsx # Implementation
|
||||
└─ <component>.stories.tsx
|
||||
```
|
||||
|
||||
When adding/editing components in `@navikt/react`:
|
||||
|
||||
- Update exports in `src/index.ts` and per-component `index.ts`
|
||||
- Add/maintain stories and tests next to the component
|
||||
- Follow existing props, theming, and token patterns
|
||||
- Keep props stable; prefer additive changes. Deprecate before removal when possible.
|
||||
- Ensure tree-shakeability; avoid side effects at module top-level.
|
||||
|
||||
## Common workflows
|
||||
|
||||
Add a new component
|
||||
|
||||
1. Implement under `@navikt/react/src/...`
|
||||
2. Add stories and tests
|
||||
3. Export from the package `index.ts`
|
||||
4. Run `yarn boot` and fix types/lint
|
||||
5. Create a changeset (`yarn changeset`)
|
||||
|
||||
Component change checklist (quick):
|
||||
|
||||
- [ ] Implementation updated under `@navikt/react/src/...`
|
||||
- [ ] Exports updated (`src/index.ts` and component `index.ts`)
|
||||
- [ ] Stories updated/added and render without errors
|
||||
- [ ] Tests updated/added and passing (Vitest)
|
||||
- [ ] Tokens/CSS use existing variables and layers (no hard-coded values)
|
||||
- [ ] Changeset added if user-facing (version bump and notes)
|
||||
|
||||
Modify an existing component
|
||||
|
||||
1. Keep the public API stable unless explicitly requested
|
||||
2. Update tests if behavior changes; update stories if visuals change
|
||||
3. Add a changeset for user-facing changes
|
||||
|
||||
Troubleshooting
|
||||
|
||||
1. `yarn clean && yarn boot`
|
||||
2. Check workspace dependency build order/issues
|
||||
3. If focusing workspaces, avoid running repo-wide scripts that depend on the website
|
||||
|
||||
## Theming and tokens
|
||||
|
||||
- Use design tokens and CSS layers; avoid hard-coded colors/spacing.
|
||||
- Stylelint enforces token usage; unknown/missing custom properties will fail lint.
|
||||
|
||||
## Website (Next.js)
|
||||
|
||||
- The website uses Next.js 15 and contains both `app/` and `pages/`. Place new files in the router that matches the surrounding code.
|
||||
- Prefer server components where already used; mark client components with `"use client"` only when needed.
|
||||
- Use Next Image, metadata APIs, and established utilities already in `aksel.nav.no/website`.
|
||||
- Logging uses Next logger/pino; follow patterns in `aksel.nav.no/website` (see `next-logger.config.js`).
|
||||
- Use existing route conventions and file organization; don’t mix `app/` and `pages/` in the same hierarchy.
|
||||
- For Tailwind, prefer the shared preset `@navikt/ds-tailwind` and follow website `tailwind.config.js` patterns.
|
||||
|
||||
## Versioning and releases
|
||||
|
||||
- Use Changesets: `yarn changeset` and the root scripts:
|
||||
- `yarn create-version` (applies versions, updates changelog, reinstalls)
|
||||
- `yarn release` (boots, docgen, publish, git release helper)
|
||||
- Main changelog script: `scripts/deno/changelog/createMainChangelog.ts`
|
||||
|
||||
## Answer snippets (for Copilot)
|
||||
|
||||
- Build: “Run yarn boot” (see root `package.json`)
|
||||
- Storybook: “Run yarn storybook” (or `yarn storybook:aksel` for the website config)
|
||||
- Lint/format: “Run yarn lint” (full install recommended) and optional `yarn biome:lint`
|
||||
- Versioning: “Run yarn changeset, then yarn create-version”
|
||||
- Theming: “Import @navikt/ds-css in global CSS”
|
||||
|
||||
## Code reviews (for Copilot)
|
||||
|
||||
- Summarize the pull-request on maximum two sentences
|
||||
- Avoid listing all changes, focus on the most important ones
|
||||
- Check the description against the actual changes
|
||||
- Ensure the PR title is descriptive and follows the conventional commit format
|
||||
- Look for: correctness, style, performance, security, tests, documentation
|
||||
- Check: existing patterns, exports, tokens, CSS layers
|
||||
- Make sure import paths are correct, updated and use existing aliases where possible
|
||||
- When components are changed, ensure stories and tests are updated accordingly
|
||||
- When components are changed, make sure related examples for website and playroom are updated
|
||||
- Make sure changes made to packages under `@navikt/` has a related changeset where relevant
|
||||
|
||||
### Extra review tips
|
||||
|
||||
- Watch for unnecessary bundle impact (large deps, dynamic imports, side effects).
|
||||
- Confirm a11y basics: semantic elements, labels, focus order, keyboard support.
|
||||
- Check security footguns: unsafe HTML, unescaped data, leaking env vars, weak CSP assumptions.
|
||||
+12
-2
@@ -8,8 +8,7 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
exclude-paths:
|
||||
- "examples/astro/package.json"
|
||||
- "examples/shadow-dom/package.json"
|
||||
- "examples/**"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
@@ -40,6 +39,17 @@ updates:
|
||||
- "vitest"
|
||||
- "@vitest/*"
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /examples
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "04:00"
|
||||
groups:
|
||||
all-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
description: "Use for JS/TS code in this repo."
|
||||
applyTo: "**/\*.tsx", "**/_.jsx", "\*\*/_.ts", "\*_/_.js"
|
||||
|
||||
---
|
||||
|
||||
# JS/TS instructions
|
||||
|
||||
- Prefer `for...of` over `Array.forEach`.
|
||||
- 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,13 +0,0 @@
|
||||
description: "Use for ds-react components and component CSS."
|
||||
applyTo: "@navikt/core/react/src/**", "@navikt/core/css/src/**"
|
||||
|
||||
---
|
||||
|
||||
# ds-react and component instructions
|
||||
|
||||
- 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,11 +0,0 @@
|
||||
description: "Use for tests and stories."
|
||||
applyTo: "**/_.test._", "**/\*.stories.tsx"
|
||||
|
||||
---
|
||||
|
||||
# Testing and Storybook instructions
|
||||
|
||||
- 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,14 +0,0 @@
|
||||
description: "Use for website and playroom work."
|
||||
applyTo:
|
||||
|
||||
- "aksel.nav.no/website/\*\*"
|
||||
- "aksel.nav.no/playroom/\*\*"
|
||||
|
||||
---
|
||||
|
||||
# Website and playroom instructions
|
||||
|
||||
- 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,15 +1,10 @@
|
||||
name: Deploy dev (aksel.ansatt.dev.nav.no)
|
||||
|
||||
name: Deploy aksel.ansatt.dev.nav.no
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
name: Build and deploy to dev-gcp
|
||||
@@ -18,7 +13,6 @@ jobs:
|
||||
packages: "write"
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
@@ -35,8 +29,10 @@ jobs:
|
||||
sanity_read_token: ${{ secrets.SANITY_READ }}
|
||||
sanity_read_no_drafts_token: ${{ secrets.SANITY_READ_NO_DRAFTS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: nais/docker-build-push@v0
|
||||
- name: Get complete tag
|
||||
run: echo "TAG=DEV-$( date +%s )" >> $GITHUB_ENV
|
||||
|
||||
- uses: nais/docker-build-push@v0
|
||||
id: docker-push
|
||||
with:
|
||||
team: designsystem
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Deploy prod (aksel.nav.no)
|
||||
|
||||
name: Deploy aksel.nav.no
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -7,20 +6,15 @@ on:
|
||||
paths:
|
||||
- "aksel.nav.no/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
IMAGE: ghcr.io/navikt/aksel
|
||||
|
||||
jobs:
|
||||
sync_data_with_sanity:
|
||||
name: Sync data to Sanity
|
||||
name: Update component data
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup project
|
||||
uses: ./.github/actions/setup
|
||||
@@ -30,7 +24,7 @@ jobs:
|
||||
- name: Run ts-docgen
|
||||
run: yarn docgen
|
||||
|
||||
- name: Update Sanity
|
||||
- name: Update sanity sync
|
||||
env:
|
||||
SANITY_WRITE: ${{ secrets.SANITY_WRITE }}
|
||||
run: yarn workspace aksel.nav.no sanity:update
|
||||
@@ -42,7 +36,6 @@ jobs:
|
||||
packages: "write"
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
deployments: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
@@ -62,8 +55,10 @@ jobs:
|
||||
sanity_deploy_studio: ${{ secrets.SANITY_DEPLOY_STUDIO }}
|
||||
next_encryption_key: ${{ secrets.NEXT_ENCRYPTION_KEY }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: nais/docker-build-push@v0
|
||||
- name: Get complete tag
|
||||
run: echo "TAG=PROD-$( date +%s )" >> $GITHUB_ENV
|
||||
|
||||
- uses: nais/docker-build-push@v0
|
||||
id: docker-push
|
||||
with:
|
||||
team: designsystem
|
||||
@@ -1,15 +1,10 @@
|
||||
name: Deploy prod (aksel-v7.nav.no)
|
||||
|
||||
name: Deploy aksel-v7.nav.no
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- v7-aksel.nav.no
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
name: Build and deploy to prod-gcp
|
||||
@@ -43,8 +38,10 @@ jobs:
|
||||
run: cd v7-aksel.nav.no && yarn build
|
||||
shell: bash
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: nais/docker-build-push@v0
|
||||
- name: Get complete tag
|
||||
run: echo "TAG=PROD-V7-$( date +%s )" >> $GITHUB_ENV
|
||||
|
||||
- uses: nais/docker-build-push@v0
|
||||
id: docker-push
|
||||
with:
|
||||
team: designsystem
|
||||
@@ -1,25 +1,18 @@
|
||||
name: Backup Sanity dataset
|
||||
|
||||
name: Backup sanity dateset to gcp
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# 12.00 every 3 days
|
||||
- cron: "0 0 */3 * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
execute:
|
||||
name: Export and upload Sanity backup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-depth: 0 # Required to retrieve git history
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
@@ -35,13 +28,11 @@ jobs:
|
||||
env:
|
||||
SANITY_READ: ${{ secrets.SANITY_READ }}
|
||||
|
||||
- name: Authenticate with GCP
|
||||
- name: Upload GCP bucket
|
||||
uses: "google-github-actions/auth@v3"
|
||||
with:
|
||||
credentials_json: "${{ secrets.gcp_credentials_prod }}"
|
||||
|
||||
- name: Upload backup to GCS bucket
|
||||
uses: "google-github-actions/upload-cloud-storage@v3"
|
||||
- uses: "google-github-actions/upload-cloud-storage@v3"
|
||||
with:
|
||||
path: ./aksel.nav.no/website
|
||||
destination: "aksel-website-prod/backups"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Bundle check (npm)
|
||||
name: Bundle check against npm
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -24,12 +24,12 @@ jobs:
|
||||
run: echo "should-run=${{ contains(github.event.pull_request.title, 'Version Packages') }}" >> $GITHUB_OUTPUT
|
||||
|
||||
analyze:
|
||||
name: Analyze npm packages
|
||||
name: "Analyzing npm packages"
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-title
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
- name: Checkout main-branch
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
update-comment:
|
||||
name: Post PR comment with results
|
||||
name: "Create or update PR comment with results"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [analyze]
|
||||
permissions:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Bundle check (main branch)
|
||||
name: Bundle check against main branch
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -24,12 +24,12 @@ jobs:
|
||||
run: echo "should-run=${{ !contains(github.event.pull_request.title, 'Version Packages') }}" >> $GITHUB_OUTPUT
|
||||
|
||||
analyze-trunk:
|
||||
name: Analyze main branch
|
||||
name: "Analyzing main branch"
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-title
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout main branch
|
||||
- name: Checkout main-branch
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
@@ -52,12 +52,12 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
analyze-local:
|
||||
name: Analyze PR branch
|
||||
name: "Analyzing PR branch"
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-title
|
||||
if: ${{ needs.check-title.outputs.should-run == 'true' }}
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
- name: Checkout main-branch
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
compare-against-trunk:
|
||||
name: Compare bundles (PR vs main)
|
||||
name: "Analyzing PR branch and comparing against main"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [analyze-trunk, analyze-local]
|
||||
steps:
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
update-comment:
|
||||
name: Post PR comment with results
|
||||
name: "Create or update PR comment with results"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [compare-against-trunk]
|
||||
permissions:
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Visual regression (Chromatic)
|
||||
name: Visual Regression Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,7 +19,6 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
chromatic:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
@@ -48,7 +47,7 @@ jobs:
|
||||
buildScriptName: "build:storybook-chromatic"
|
||||
onlyChanged: true
|
||||
untraced: "**/package.json|yarn.lock|**/*.md"
|
||||
externals: "**/core/css/src/**/*.css"
|
||||
externals: "**/core/css/*.css|**/core/css/baseline/*.css|**/core/css/form/*.css|**/core/css/primitives/*.css"
|
||||
traceChanged: true
|
||||
env:
|
||||
STORYBOOK_GITHUB_SHA: ${{ github.sha }}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Validation (Lint, Tests)
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -8,16 +8,9 @@ on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (ESLint, Stylelint etc.)
|
||||
name: "Linter: eslint, stylelint"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -34,7 +27,7 @@ jobs:
|
||||
run: yarn lint
|
||||
|
||||
validate-examples:
|
||||
name: Validate component examples
|
||||
name: "Validate Aksel.nav.no component examples"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -51,7 +44,7 @@ jobs:
|
||||
run: yarn validate:eksempler
|
||||
|
||||
test:
|
||||
name: Unit and integration tests
|
||||
name: "Tests: Unit and integration"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -68,7 +61,7 @@ jobs:
|
||||
run: yarn test
|
||||
|
||||
ui:
|
||||
name: UI tests (Storybook)
|
||||
name: "Tests: UI"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
container:
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Cleanup E2E Docker images
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 4 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package-name:
|
||||
- aksel-e2e
|
||||
steps:
|
||||
- name: Delete old container images
|
||||
uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # actions/delete-package-versions@v5
|
||||
with:
|
||||
package-name: ${{ matrix.package-name }}
|
||||
package-type: container
|
||||
delete-only-untagged-versions: false
|
||||
min-versions-to-keep: 1
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: E2E tests (Playwright)
|
||||
name: E2E / Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [main, next]
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
prebuild-app-for-playwright:
|
||||
name: Build app for Playwright
|
||||
name: "Prebuild app for Playwright tests"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
IMAGE_NAME: ${{ github.repository }}-e2e
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
run-playwright:
|
||||
needs: [prebuild-app-for-playwright]
|
||||
name: Playwright shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
name: Playwright shard ${{ matrix.shardIndex }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
@@ -111,7 +111,6 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
merge-reports:
|
||||
name: Merge Playwright reports
|
||||
if: ${{ !cancelled() }}
|
||||
needs: [run-playwright]
|
||||
runs-on: ubuntu-latest
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Upload Figma token config to CDN
|
||||
name: "Update remote token config for figma variables plugin"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -10,7 +10,6 @@ on:
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Build and upload token config
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
@@ -18,13 +17,11 @@ jobs:
|
||||
id-token: "write"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
@@ -38,8 +35,7 @@ jobs:
|
||||
- name: Build tokens
|
||||
run: yarn workspace @navikt/ds-tokens build
|
||||
|
||||
- name: Upload config to CDN
|
||||
id: upload
|
||||
- id: upload
|
||||
uses: nais/deploy/actions/cdn-upload/v2@master
|
||||
with:
|
||||
team: designsystem
|
||||
@@ -50,6 +46,5 @@ jobs:
|
||||
source_keep_parent_name: false
|
||||
no_cache_paths: "@navikt/tokens/figma-config.json"
|
||||
|
||||
- name: Log uploaded file
|
||||
run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
|
||||
- run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
|
||||
shell: bash
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Upload fonts to CDN
|
||||
name: "push-to-cdn"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -10,19 +10,16 @@ on:
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload fonts to CDN
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: "actions/checkout@v6"
|
||||
- uses: "actions/checkout@v6"
|
||||
|
||||
- name: Upload fonts to CDN
|
||||
id: upload
|
||||
- id: upload
|
||||
uses: nais/deploy/actions/cdn-upload/v2@master
|
||||
with:
|
||||
team: aksel
|
||||
@@ -31,6 +28,5 @@ jobs:
|
||||
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Log uploaded file
|
||||
run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
|
||||
- run: echo uploaded file ${{ steps.upload.outputs.uploaded }}
|
||||
shell: bash
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release packages
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,8 +13,8 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Release packages
|
||||
if: ${{ !contains(github.event.head_commit.message, '[ci skip]') }}
|
||||
name: "Release packages"
|
||||
if: ${{!contains(github.event.head_commit.message, '[ci skip]')}}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: "write"
|
||||
@@ -22,20 +22,17 @@ jobs:
|
||||
packages: "write"
|
||||
pull-requests: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
cache-dependency-path: "yarn.lock"
|
||||
|
||||
- name: Setup Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
|
||||
@@ -58,7 +55,7 @@ jobs:
|
||||
# Needed for push to main
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create .npmrc for GPR
|
||||
- name: Creating .npmrc
|
||||
run: |
|
||||
cat << EOF > "$HOME/.npmrc"
|
||||
@navikt:registry=https://npm.pkg.github.com
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "31 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-issue-stale: 120
|
||||
days-before-pr-stale: -1
|
||||
days-before-close: 30
|
||||
any-of-labels: "Besvart"
|
||||
stale-issue-message: |-
|
||||
Hei! Det ser ut som det ikke har vært aktivitet her på en stund. Etter 30 dager vil saken bli lukket automatisk.
|
||||
|
||||
Om saken fortsatt er relevant eller du har oppdateringer, vil en kommentar hindre at den lukkes. Du kan alltids åpne den igjen hvis det skulle være nødvendig!
|
||||
stale-issue-label: "no-issue-activity"
|
||||
ascending: true
|
||||
operations-per-run: 200
|
||||
@@ -1,22 +1,16 @@
|
||||
name: Update article views (Naisjob)
|
||||
|
||||
name: Redeploy Naisjob (update-article-views)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
execute:
|
||||
name: Build and deploy article views job
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
id-token: "write"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Build and push Docker image
|
||||
uses: nais/docker-build-push@v0
|
||||
- uses: nais/docker-build-push@v0
|
||||
id: docker-push
|
||||
with:
|
||||
team: designsystem
|
||||
@@ -25,7 +19,7 @@ jobs:
|
||||
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
|
||||
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
|
||||
- name: Deploy Naisjob to prod-gcp
|
||||
- name: start naisjob prod-gcp
|
||||
uses: nais/deploy/actions/deploy@master
|
||||
env:
|
||||
APIKEY: ${{ secrets.NAIS_DEPLOY_APIKEY }}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname $0)/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
||||
yarn lint-staged && yarn tsx ./scripts/in-sync-versions.ts
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname $0)/_/husky.sh"
|
||||
|
||||
# Only run tests when pushing to main branch
|
||||
while read local_ref local_sha remote_ref remote_sha; do
|
||||
if [ "$remote_ref" = "refs/heads/main" ]; then
|
||||
echo "Pushing to main branch - running tests...\n"
|
||||
yarn test
|
||||
exit $?
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import { defineMain } from "@storybook/react-vite/node";
|
||||
import { StorybookConfig } from "@storybook/react-vite";
|
||||
import FG from "fast-glob";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
@@ -17,7 +17,7 @@ const includeWebsiteStories = process.env.WITH_WEBSITE === "true";
|
||||
|
||||
const indexRegex = /export const args = {\s+index: (\d+),/;
|
||||
|
||||
export default defineMain({
|
||||
export default {
|
||||
experimental_indexers: (indexers) => {
|
||||
// Changes here might need to be reflected in aksel.nav.no/website/.storybook/main.ts
|
||||
const customIndexer = async (fileName: string, opts: any) => {
|
||||
@@ -126,9 +126,9 @@ export default defineMain({
|
||||
features: {
|
||||
actions: false,
|
||||
},
|
||||
});
|
||||
} satisfies StorybookConfig;
|
||||
|
||||
function getAbsolutePath(value: string) {
|
||||
function getAbsolutePath(value: string): any {
|
||||
return dirname(require.resolve(join(value, "package.json")));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
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 { withThemeByClassName } from "@storybook/addon-themes";
|
||||
import { Preview } 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";
|
||||
@@ -58,8 +55,7 @@ const TypoDecorator = ({
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default definePreview({
|
||||
addons: [addonA11y(), addonThemes(), addonDocs(), addonVitest()],
|
||||
export default {
|
||||
parameters: {
|
||||
options: {
|
||||
storySort: {
|
||||
@@ -136,4 +132,4 @@ export default definePreview({
|
||||
defaultTheme: "light",
|
||||
}),
|
||||
],
|
||||
});
|
||||
} satisfies Preview;
|
||||
Vendored
+4
-4
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"biomejs.biome",
|
||||
"esbenp.prettier-vscode",
|
||||
"denoland.vscode-deno",
|
||||
"esbenp.prettier-vscode",
|
||||
"github.vscode-github-actions",
|
||||
"ms-playwright.playwright",
|
||||
"sanity-io.vscode-sanity",
|
||||
"vunguyentuan.vscode-css-variables"
|
||||
"stylelint.vscode-stylelint",
|
||||
"vunguyentuan.vscode-css-variables",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
Vendored
+5
-2
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"cssVariables.lookupFiles": ["@navikt/core/tokens/dist/tokens.css"],
|
||||
"cssVariables.lookupFiles": [
|
||||
"@navikt/core/tokens/dist/tokens.css",
|
||||
"@navikt/core/tokens/dist/darkside/tokens.css"
|
||||
],
|
||||
"eslint.problems.shortenToSingleLine": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"files.watcherExclude": {
|
||||
@@ -9,6 +12,6 @@
|
||||
"**/cjs": true
|
||||
},
|
||||
"playwright.reuseBrowser": true,
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"deno.enablePaths": ["./scripts/deno"]
|
||||
}
|
||||
@@ -12,10 +12,8 @@ npmScopes:
|
||||
npmAuthToken: "${NPM_AUTH_TOKEN:-}"
|
||||
npmRegistryServer: "https://npm.pkg.github.com"
|
||||
|
||||
enableScripts: false
|
||||
defaultSemverRangePrefix: ""
|
||||
npmMinimalAgeGate: "7d"
|
||||
npmPreapprovedPackages: ["@navikt/*"]
|
||||
enableTelemetry: false
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
@@ -1,17 +1,5 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 8.10.2
|
||||
|
||||
## 8.10.1
|
||||
|
||||
## 8.10.0
|
||||
|
||||
## 8.9.1
|
||||
|
||||
## 8.9.0
|
||||
|
||||
## 8.8.0
|
||||
|
||||
## 8.7.0
|
||||
|
||||
## 8.6.0
|
||||
|
||||
@@ -2,11 +2,44 @@
|
||||
|
||||
## Foreslå nye ikoner
|
||||
|
||||
Om du ikke finner ikonet du leter etter kan du [lage et issue på GitHub](https://github.com/navikt/aksel/issues/new?labels=nytt+✨%2Cikoner+🖼%2Cforespørsel+🥰&template=new-icon.yaml&title=%5BNytt+ikon%5D%3A+).
|
||||
Om du ikke finner et ikonet du leter etter kan du [åpne et issue på github](https://github.com/navikt/aksel/issues/new?labels=nytt+✨%2Cikoner+🖼%2Cforespørsel+🥰&template&template=new-icon.yaml&title=%5BNytt+ikon%5D%3A+).
|
||||
|
||||
## Oppdatere ikon
|
||||
|
||||
Hvis du tenker et ikon fortjener en oppdatering eller er utdatert kan [du foreslå det her](https://github.com/navikt/aksel/issues/new?labels=forespørsel+🥰&template=update-icon.yml&title=%5BInnspill+til+ikon%5D%3A+).
|
||||
Hvis du tenker et ikon fortjener en oppdatering eller er utdatert kan [du foreslå det her.](https://github.com/navikt/aksel/issues/new?labels=forespørsel+🥰&template=update-icon.yml&title=%5BInnspill+til+ikon%5D%3A+)
|
||||
|
||||
## Legge til nye ikoner selv
|
||||
|
||||
Hvis du selv ønsker å legge til nye ikoner kan du kopiere filenes fra `/template` og legge de inn i `/icons` med egen data. Alle ikonene har naming-format `PascalCase`. Alle ikonene har 1 SVG-fil og 1 tilhørende YML-fil.
|
||||
|
||||
### Svg
|
||||
|
||||
- Sizing, width og height må være 24x24: `width="24" height="24" `
|
||||
- Viewbox: `viewBox="0 0 24 24"`
|
||||
- Fill på path: `fill="#202733"`
|
||||
|
||||
### Yml
|
||||
|
||||
```yml
|
||||
name: Template # Samme som filnavn
|
||||
category: Template category # Hovedkategori
|
||||
sub_category: Sub category # Underkategori
|
||||
keywords: # Samling med søkeord
|
||||
- template1
|
||||
- template2
|
||||
- template3
|
||||
variant: Stroke # Stroke eller Fill
|
||||
created_at: 01.01.2023 # Dato for opprettelse
|
||||
```
|
||||
|
||||
### Publisere oppdatering
|
||||
|
||||
Når du har lagt til nye ikoner kan du opprette en pull request. Denne vil bli automatisk testet og så gått gjennom av Aksel-teamet.
|
||||
Legg gjerne til en `changeset` ved å kjøre `yarn changeset` i terminalen. Dette vil gi oss bedre oversikt over hva som er endret i endringslogger og automatisk bumpe pakke-versjonen ved merge.
|
||||
|
||||
#### Teste nytt ikon
|
||||
|
||||
Hvis du ønsker å teste om alt stemmer, både i `svg` og `yml` før du lager en PR kan du kjøre `yarn && cd navikt/aksel-icons && yarn test` i terminalen lokalt.
|
||||
|
||||
## Design av nye ikoner
|
||||
|
||||
@@ -17,9 +50,9 @@ Hvis du tenker et ikon fortjener en oppdatering eller er utdatert kan [du foresl
|
||||
|
||||
### Stroke (linje)
|
||||
|
||||
- Ikonet må bestå av kun linjer.
|
||||
- Linjene skal være 1.5px, center-aligned.
|
||||
- Ikonet må bestå av kun linjer
|
||||
- Linjene skal være 1.5px, center-aligned
|
||||
|
||||
### Fill (fylt)
|
||||
|
||||
- Ikoner hvor shapes kan fylles, bør fylles.
|
||||
- Ikoner hvor shapes kan fylles, bør fylles
|
||||
@@ -14,8 +14,8 @@
|
||||
"@navikt/ds-css": "*",
|
||||
"@navikt/ds-react": "*",
|
||||
"fuse.js": "^7.0.0",
|
||||
"react": "19.2.5",
|
||||
"react-dom": "19.2.5"
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@figma/plugin-typings": "^1.100.2",
|
||||
@@ -24,8 +24,8 @@
|
||||
"@types/react": "19.2.14",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"typescript": "6.0.2",
|
||||
"vite": "^7.3.2",
|
||||
"typescript": "5.9.3",
|
||||
"vite": "^7.2.4",
|
||||
"vite-plugin-singlefile": "^2.3.0",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ const App = () => {
|
||||
<div className="icons-wrapper">
|
||||
{sub.icons.map((i) => {
|
||||
// biome-ignore lint/performance/noDynamicNamespaceImportAccess: We do not know which icon will be rendered at build time
|
||||
const T = Icons[`${i.id}Icon`];
|
||||
const T = Icons[`${i.id}Icon`]; // eslint-disable-line import/namespace
|
||||
if (T === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "8.10.2",
|
||||
"version": "8.7.0",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
@@ -82,14 +82,14 @@
|
||||
"fast-glob": "3.3.3",
|
||||
"hast-util-select": "6.0.4",
|
||||
"js-yaml": "^4.1.1",
|
||||
"lodash": "4.18.1",
|
||||
"lodash": "4.17.23",
|
||||
"prettier": "3.8.1",
|
||||
"react": "19.2.5",
|
||||
"react": "19.2.4",
|
||||
"rehype-parse": "9.0.1",
|
||||
"tsc-alias": "1.8.16",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "6.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"unified": "11.0.5",
|
||||
"vitest": "4.1.4"
|
||||
"vitest": "4.0.18"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"ignoreDeprecations": "6.0",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist/react/cjs",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
|
||||
@@ -14,7 +14,7 @@ function useGlobalId(idOverride?: string): string | undefined {
|
||||
// We can't use it server-side.
|
||||
// If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
|
||||
globalId += 1;
|
||||
setDefaultId(`aksel-icon-${globalId}`); // eslint-disable-line react-hooks/set-state-in-effect
|
||||
setDefaultId(`aksel-icon-${globalId}`);
|
||||
}
|
||||
}, [defaultId]);
|
||||
return id;
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 8.10.2
|
||||
|
||||
## 8.10.1
|
||||
|
||||
## 8.10.0
|
||||
|
||||
## 8.9.1
|
||||
|
||||
## 8.9.0
|
||||
|
||||
## 8.8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ReadMore: Deprecated classes `aksel-read-more--open` and `aksel-read-more__content--closed`. Use `data-state` instead to access state. ([#4723](https://github.com/navikt/aksel/pull/4723))
|
||||
|
||||
## 8.7.0
|
||||
|
||||
## 8.6.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "8.10.2",
|
||||
"version": "8.7.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
@@ -42,14 +42,14 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"@navikt/ds-css": "^8.7.0",
|
||||
"@navikt/ds-tokens": "^8.7.0",
|
||||
"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.2"
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,4 @@ export const deprecations: DeprecatedList = [
|
||||
"Removed in vX.X.X, see documentation [link] for more information.",
|
||||
deprecatePrefix: true,
|
||||
},
|
||||
{
|
||||
classes: ["aksel-read-more__content--closed", "aksel-read-more--open"],
|
||||
message:
|
||||
"Removed in version ^8.8. Use data-state='open' or 'closed' instead.",
|
||||
},
|
||||
];
|
||||
@@ -8,25 +8,22 @@ const __dirname = dirname(__filename);
|
||||
|
||||
const flattenObject = (
|
||||
obj: Record<string, string | Record<string, string>>,
|
||||
): string[] => {
|
||||
const flattened = Object.keys(obj).reduce<(string | string[])[]>(
|
||||
(acc, key) => {
|
||||
if (typeof obj[key] === "string") {
|
||||
acc.push(key);
|
||||
} else {
|
||||
acc.push(flattenObject(obj[key]));
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[],
|
||||
);
|
||||
) => {
|
||||
const flattened = Object.keys(obj).reduce((acc, key) => {
|
||||
if (typeof obj[key] === "string") {
|
||||
acc.push(key);
|
||||
} else {
|
||||
acc.push(flattenObject(obj[key]));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return flattened.flat();
|
||||
};
|
||||
|
||||
const tokenCSSFile = "./global-tokens.css";
|
||||
const tokenJsonFile = "./component-tokens.json";
|
||||
|
||||
const allowedTokenNames: string[] = [];
|
||||
const allowedTokenNames = [];
|
||||
|
||||
export const tokenExists = (
|
||||
controlledPrefixes: string[],
|
||||
|
||||
@@ -1,53 +1,5 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 8.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`90194d7`](https://github.com/navikt/aksel/commit/90194d7d95edd2ad69156528ec7e1e0600daef8a)]:
|
||||
- @navikt/ds-css@8.10.2
|
||||
- @navikt/ds-tokens@8.10.2
|
||||
|
||||
## 8.10.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`f66236c`](https://github.com/navikt/aksel/commit/f66236c3710d132f4a2bbe043282a98136b089d9)]:
|
||||
- @navikt/ds-css@8.10.1
|
||||
- @navikt/ds-tokens@8.10.1
|
||||
|
||||
## 8.10.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`63f5aaf`](https://github.com/navikt/aksel/commit/63f5aaf2f4bfdc3402afe9617c806309e0169420), [`139805d`](https://github.com/navikt/aksel/commit/139805d5b4b6422e99ee04c1dcde1bdd15c8ddaa), [`2eab5b9`](https://github.com/navikt/aksel/commit/2eab5b956420848ca9fc785bc63263161ac79f85), [`f491aae`](https://github.com/navikt/aksel/commit/f491aaec704314fe97b77d92cc2ed1564454c0e8), [`2eab5b9`](https://github.com/navikt/aksel/commit/2eab5b956420848ca9fc785bc63263161ac79f85), [`c096f46`](https://github.com/navikt/aksel/commit/c096f46beb07f48e2869f7bcc722732e4f7ac31e), [`c096f46`](https://github.com/navikt/aksel/commit/c096f46beb07f48e2869f7bcc722732e4f7ac31e)]:
|
||||
- @navikt/ds-css@8.10.0
|
||||
- @navikt/ds-tokens@8.10.0
|
||||
|
||||
## 8.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`f7393f7`](https://github.com/navikt/aksel/commit/f7393f75159d3e4cff5fd438fd8e275325f98f1c), [`9346578`](https://github.com/navikt/aksel/commit/93465781c13e91ec41520e1284cba42c452d1386), [`47d74be`](https://github.com/navikt/aksel/commit/47d74be1a0db844d7116e46b0608c42146dcbc01)]:
|
||||
- @navikt/ds-css@8.9.1
|
||||
- @navikt/ds-tokens@8.9.1
|
||||
|
||||
## 8.9.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`86f86d3`](https://github.com/navikt/aksel/commit/86f86d37608432160333ed275f76553d936bf63a), [`72a9131`](https://github.com/navikt/aksel/commit/72a9131aeeac41d0242869f5684999e6bc731285)]:
|
||||
- @navikt/ds-css@8.9.0
|
||||
- @navikt/ds-tokens@8.9.0
|
||||
|
||||
## 8.8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`befae1e`](https://github.com/navikt/aksel/commit/befae1e0a27e32b46c45335cf438aa65f4333b9f), [`7388130`](https://github.com/navikt/aksel/commit/738813046ffece51d4670a33297f4333c6bae8d0), [`5756986`](https://github.com/navikt/aksel/commit/57569860700bf81d6eef06ddbc39270933eff528), [`232ed45`](https://github.com/navikt/aksel/commit/232ed4569414e70f5a6f8e7bab584226f191a889)]:
|
||||
- @navikt/ds-css@8.8.0
|
||||
- @navikt/ds-tokens@8.8.0
|
||||
|
||||
## 8.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "8.10.2",
|
||||
"version": "8.7.0",
|
||||
"description": "Aksel command line interface. Codemods and other utilities for Aksel users.",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -23,13 +23,6 @@
|
||||
"test:watch": "vitest watch"
|
||||
},
|
||||
"bin": "./dist/index.js",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./migrations": {
|
||||
"import": "./dist/migrations-config.js",
|
||||
"types": "./dist/migrations-config.d.ts"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/navikt/aksel.git",
|
||||
@@ -40,8 +33,8 @@
|
||||
"url": "https://github.com/navikt/aksel/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"@navikt/ds-css": "^8.7.0",
|
||||
"@navikt/ds-tokens": "^8.7.0",
|
||||
"chalk": "5.6.2",
|
||||
"cli-progress": "3.12.0",
|
||||
"clipboardy": "5.0.1",
|
||||
@@ -52,15 +45,13 @@
|
||||
"is-git-clean": "1.1.0",
|
||||
"jscodeshift": "17.3.0",
|
||||
"jscodeshift-add-imports": "1.0.11",
|
||||
"lodash": "4.18.1"
|
||||
"lodash": "4.17.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cli-progress": "3.11.6",
|
||||
"@types/is-git-clean": "1.1.2",
|
||||
"@types/jscodeshift": "17.3.0",
|
||||
"rimraf": "6.1.3",
|
||||
"typescript": "6.0.2",
|
||||
"vitest": "4.1.4"
|
||||
"typescript": "5.9.3",
|
||||
"vitest": "4.0.18"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,216 @@
|
||||
import chalk from "chalk";
|
||||
import { migrations } from "../migrations-config";
|
||||
import { SupportedCodemodExtensions } from "./codeshift.utils";
|
||||
|
||||
const JS_EXTENSIONS = [
|
||||
"js",
|
||||
"jsx",
|
||||
"ts",
|
||||
"tsx",
|
||||
] satisfies SupportedCodemodExtensions[];
|
||||
|
||||
const CSS_EXTENSIONS = [
|
||||
"css",
|
||||
"scss",
|
||||
"less",
|
||||
] satisfies SupportedCodemodExtensions[];
|
||||
|
||||
type MigrationT = Record<
|
||||
string,
|
||||
{
|
||||
description: string;
|
||||
value: string;
|
||||
path: string;
|
||||
warning?: string;
|
||||
/**
|
||||
* Some transforms are only meant to run on specific file extensions.
|
||||
* This is used to filter out files that should not be transformed.
|
||||
*/
|
||||
ignoredExtensions: SupportedCodemodExtensions[];
|
||||
}[]
|
||||
>;
|
||||
|
||||
export const migrations: MigrationT = {
|
||||
v1: [
|
||||
{
|
||||
description: "Runs all codemods for beta -> v1 migration",
|
||||
value: "v1-preset",
|
||||
path: "v1.0.0/preset/preset",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Fixes breaking API-changes for <Pagination /> component",
|
||||
value: "v1-pagination",
|
||||
path: "v1.0.0/pagination/pagination",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Fixes breaking API-changes for <Tabs /> component",
|
||||
value: "v1-tabs",
|
||||
path: "v1.0.0/tabs/tabs",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Fixes breaking API-changes for <SpeechBubble /> (now <Chat/>) component",
|
||||
value: "v1-chat",
|
||||
path: "v1.0.0/chat/chat",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v2: [
|
||||
{
|
||||
description: "Patches changed css-variables",
|
||||
value: "v2-css",
|
||||
path: "v2.0.0/update-css-tokens/update-css-tokens",
|
||||
ignoredExtensions: [],
|
||||
},
|
||||
{
|
||||
description: "Patches changed js-variables",
|
||||
value: "v2-js",
|
||||
path: "v2.0.0/update-js-tokens/update-js-tokens",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Patches changed sass-variables",
|
||||
value: "v2-sass",
|
||||
path: "v2.0.0/update-sass-tokens/update-sass-tokens",
|
||||
ignoredExtensions: JS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Patches changed less-variables",
|
||||
value: "v2-less",
|
||||
path: "v2.0.0/update-less-tokens/update-less-tokens",
|
||||
ignoredExtensions: JS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v3: [
|
||||
{
|
||||
description:
|
||||
"Replaces deprecated <CopyToClipboard /> with <CopyButton />",
|
||||
value: "v3-copybutton",
|
||||
path: "v3.0.0/copybutton/copybutton",
|
||||
warning:
|
||||
"Remember to remove css-import from '@navikt/ds-css-internal' if no longer needed\nIf non-text was used as children, or different locales were handled, you need to manually fix this",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v4: [
|
||||
{
|
||||
description:
|
||||
"Replaced deprecated 'internal'-component import to 'core'-imports",
|
||||
value: "v4-internal-react",
|
||||
path: "v4.0.0/internal-react/internal-react",
|
||||
warning:
|
||||
"Remember to also run 'v4-internal-css' if you have overwritten any css-classes!",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Replaced 'navdsi'-prefixes used to overwrite internal-css to 'navds'-prefixes",
|
||||
value: "v4-internal-css",
|
||||
path: "v4.0.0/internal-css/internal-css",
|
||||
warning:
|
||||
"Remember to remove '@navikt/ds-css-internal' imports if no longer needed! Also run 'v4-internal-react' to convert internal-react imports to core-imports",
|
||||
ignoredExtensions: [],
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Removes all instances of UNSAFE_ for datepicker/monthpicker.",
|
||||
value: "v4-date",
|
||||
path: "v4.0.0/date/date",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v6: [
|
||||
{
|
||||
description:
|
||||
"Removes `backgroundColor` and `avatarBgColor` properties from `Chat` and `Chat.Bubble`",
|
||||
value: "v6-chat",
|
||||
path: "v6.0.0/chat/chat",
|
||||
warning:
|
||||
"Remember to update use of `variant`-prop to match previous use of colors. If needed the component exposes css-variables for custom overrides",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v8: [
|
||||
{
|
||||
description:
|
||||
"Updates Box with legacy-tokens to Box using the new token system, and renames already migrated BoxNew/Box.New instances to Box.",
|
||||
value: "v8-box",
|
||||
path: "v8.0.0/box/box",
|
||||
warning:
|
||||
"Remember to check if 'TODO: Aksel Box migration'-comment was added to any files after migration. This comment will help you find and update Box-instances we couldn't update for you.",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Renames already migrated BoxNew/Box.New instances to Box.",
|
||||
value: "v8-box-new",
|
||||
path: "v8.0.0/box-new/box-new",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Removes deprecated props from components.",
|
||||
value: "v8-prop-deprecate",
|
||||
path: "v8.0.0/prop-deprecate/prop-deprecate",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Tries to migrate List component by wrapping it with 'Box' and moving 'title' and 'description' props to 'Heading' and 'BodyShort'.",
|
||||
value: "v8-list",
|
||||
path: "v8.0.0/list/list",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Updates all Primitives to use new `space`-tokens.",
|
||||
value: "v8-primitive-spacing",
|
||||
path: "v8.0.0/primitives-spacing/spacing",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Tag based on current variant prop.",
|
||||
value: "v8-tag-variant",
|
||||
path: "v8.0.0/tag-variant/tag-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on ToggleGroup based on current variant prop.",
|
||||
value: "v8-toggle-group-variant",
|
||||
path: "v8.0.0/toggle-group-variant/toggle-group-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Accordion based on current variant prop.",
|
||||
value: "v8-accordion-variant",
|
||||
path: "v8.0.0/accordion-variant/accordion-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Chips based on current variant prop.",
|
||||
value: "v8-chips-variant",
|
||||
path: "v8.0.0/chips-variant/chips-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Button based on current variant prop.",
|
||||
value: "v8-button-variant",
|
||||
path: "v8.0.0/button-variant/button-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Link based on current variant prop.",
|
||||
value: "v8-link-variant",
|
||||
path: "v8.0.0/link-variant/link-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Extracts `path` field for a given migration.
|
||||
@@ -53,14 +264,8 @@ export function getMigrationsForVersion(version: string) {
|
||||
/**
|
||||
* Returns the override migrations available for a specific version.
|
||||
*/
|
||||
function isOverrideVersion(
|
||||
version: string,
|
||||
): version is keyof typeof migrationStringOverride {
|
||||
return version in migrationStringOverride;
|
||||
}
|
||||
|
||||
export function getOverridesForVersion(version: string) {
|
||||
return isOverrideVersion(version) ? migrationStringOverride[version] : [];
|
||||
return migrationStringOverride[version] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +292,7 @@ export function getMigrationString() {
|
||||
Object.entries(migrations).forEach(([version, vMigrations]) => {
|
||||
str += `\n${chalk.underline(version)}\n`;
|
||||
|
||||
const overrideMigrations = getOverridesForVersion(version);
|
||||
const overrideMigrations = migrationStringOverride[version] || [];
|
||||
overrideMigrations.forEach((migration) => {
|
||||
str += `${chalk.blue(migration.value)}: ${migration.description}\n`;
|
||||
});
|
||||
|
||||
@@ -80,11 +80,6 @@ export async function runCodeshift(
|
||||
|
||||
warning && console.info(`\n${chalk.yellow(warning)}\n`);
|
||||
} catch (error) {
|
||||
program.error(
|
||||
chalk.red(
|
||||
"Error:",
|
||||
error instanceof Error ? error.message : String(error),
|
||||
),
|
||||
);
|
||||
program.error(chalk.red("Error:", error.message));
|
||||
}
|
||||
}
|
||||
@@ -50,9 +50,7 @@ async function runVersionMigration(
|
||||
chalk.greenBright.bold(`\nAvailable migrations for ${version}:\n`),
|
||||
);
|
||||
|
||||
const overrideValues = new Set<string>(
|
||||
overrideMigrations.map((m) => m.value),
|
||||
);
|
||||
const overrideValues = new Set(overrideMigrations.map((m) => m.value));
|
||||
|
||||
const choices = [
|
||||
...overrideMigrations.map((migration) => ({
|
||||
|
||||
@@ -363,6 +363,6 @@ const tokens = [
|
||||
["--navdsi-header-color-hover", "--a-surface-inverted-hover"],
|
||||
["--navdsi-header-color-active", "--a-surface-inverted-active"],
|
||||
["--navds-button-icon-adjustment", "--ac-button-icon-margin"]
|
||||
] as const
|
||||
]
|
||||
|
||||
export { tokens };
|
||||
module.exports = { tokens };
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { API, FileInfo, Options } from "jscodeshift";
|
||||
import renameProps from "../../utils/rename-props";
|
||||
|
||||
export default function transformer(
|
||||
file: FileInfo,
|
||||
api: API,
|
||||
options: Options,
|
||||
) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api, options) {
|
||||
const j = api.jscodeshift;
|
||||
const root = j(file.source);
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { API, FileInfo } from "jscodeshift";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
import renameProps from "../../../utils/rename-props";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
let localName = "SpeechBubble";
|
||||
|
||||
@@ -13,17 +16,14 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
.find(j.ImportDeclaration)
|
||||
.filter((path) => path.node.source.value === "@navikt/ds-react")
|
||||
.forEach((imp) => {
|
||||
imp.value.specifiers?.forEach((x) => {
|
||||
if (x.type !== "ImportSpecifier") return;
|
||||
imp.value.specifiers.forEach((x) => {
|
||||
if (x.imported.name === "SpeechBubble") {
|
||||
if (x.local && x.local.name !== x.imported.name) {
|
||||
localName = String(x.local.name);
|
||||
if (x.local.name !== x.imported.name) {
|
||||
localName = x.local.name;
|
||||
x.imported.name = "Chat";
|
||||
} else {
|
||||
x.imported.name = "Chat";
|
||||
if (x.local) {
|
||||
x.local.name = "Chat";
|
||||
}
|
||||
x.local.name = "Chat";
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -47,34 +47,20 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
|
||||
compRoot.forEach((x) => {
|
||||
if (localName !== "SpeechBubble") return;
|
||||
const opening = x.node.openingElement.name;
|
||||
if (opening.type === "JSXIdentifier") {
|
||||
opening.name = "Chat";
|
||||
}
|
||||
const closing = x.node.closingElement?.name;
|
||||
if (closing?.type === "JSXIdentifier") {
|
||||
closing.name = "Chat";
|
||||
}
|
||||
x.node.openingElement.name.name = "Chat";
|
||||
x.node.closingElement.name.name = "Chat";
|
||||
});
|
||||
|
||||
/* Need to handle dot-notations differently */
|
||||
const child = root.find(j.JSXElement);
|
||||
|
||||
child.forEach((x) => {
|
||||
const openingName = x.value.openingElement.name;
|
||||
if (
|
||||
openingName.type === "JSXMemberExpression" &&
|
||||
openingName.object.type === "JSXIdentifier" &&
|
||||
openingName.object.name === "SpeechBubble"
|
||||
x.value.openingElement.name.type === "JSXMemberExpression" &&
|
||||
x.value.openingElement.name.object.name === "SpeechBubble"
|
||||
) {
|
||||
openingName.object.name = "Chat";
|
||||
const closingName = x.value.closingElement?.name;
|
||||
if (
|
||||
closingName?.type === "JSXMemberExpression" &&
|
||||
closingName.object.type === "JSXIdentifier"
|
||||
) {
|
||||
closingName.object.name = "Chat";
|
||||
}
|
||||
x.value.openingElement.name.object.name = "Chat";
|
||||
x.value.closingElement.name.object.name = "Chat";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import type {
|
||||
API,
|
||||
FileInfo,
|
||||
JSXAttribute,
|
||||
JSXElement,
|
||||
JSXSpreadAttribute,
|
||||
} from "jscodeshift";
|
||||
import { getJSXLiteralValue } from "../../../utils/jsx-value";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
let localName = "Pagination";
|
||||
|
||||
const root = j(file.source);
|
||||
|
||||
function addMigrationTag(node: JSXElement) {
|
||||
function addMigrationTag(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
const isMigrated = attributes?.find(
|
||||
(attr: JSXAttribute | JSXSpreadAttribute) =>
|
||||
const isMigrated = attributes.find(
|
||||
(attr) =>
|
||||
attr.type === "JSXAttribute" &&
|
||||
attr.name.name === "data-version" &&
|
||||
getJSXLiteralValue(attr.value) === "v1",
|
||||
attr.value.value === "v1",
|
||||
);
|
||||
|
||||
if (!isMigrated && attributes) {
|
||||
if (!isMigrated) {
|
||||
attributes.push(
|
||||
j.jsxAttribute(j.jsxIdentifier("data-version"), j.literal("v1")),
|
||||
);
|
||||
@@ -31,14 +27,13 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
}
|
||||
|
||||
/* https://github.com/mui/material-ui/blob/master/packages/mui-codemod/src/v5.0.0/variant-prop.js */
|
||||
function addExplicitStandardProp(node: JSXElement) {
|
||||
function addExplicitStandardProp(node) {
|
||||
const attributes = node.openingElement.attributes;
|
||||
const variant = attributes?.find(
|
||||
(attr: JSXAttribute | JSXSpreadAttribute) =>
|
||||
attr.type === "JSXAttribute" && attr.name.name === "size",
|
||||
const variant = attributes.find(
|
||||
(attr) => attr.type === "JSXAttribute" && attr.name.name === "size",
|
||||
);
|
||||
|
||||
if (!variant && attributes) {
|
||||
if (!variant) {
|
||||
attributes.unshift(
|
||||
j.jsxAttribute(j.jsxIdentifier("size"), j.literal("small")),
|
||||
);
|
||||
@@ -51,45 +46,37 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
.find(j.ImportDeclaration)
|
||||
.filter((path) => path.node.source.value === "@navikt/ds-react")
|
||||
.forEach((imp) => {
|
||||
imp.value.specifiers?.forEach((x) => {
|
||||
imp.value.specifiers.forEach((x) => {
|
||||
if (
|
||||
x.type === "ImportSpecifier" &&
|
||||
x.imported.type === "Identifier" &&
|
||||
x.imported.name === "Pagination" &&
|
||||
x.local?.name !== x.imported.name &&
|
||||
x.local?.name
|
||||
x.local.name !== x.imported.name
|
||||
) {
|
||||
localName = String(x.local.name);
|
||||
localName = x.local.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (j(file.source).findJSXElements(localName)) {
|
||||
root.findJSXElements(`${localName}`).forEach((parent) => {
|
||||
const skip = !!parent.value.openingElement?.attributes?.find(
|
||||
(attr: JSXAttribute | JSXSpreadAttribute) =>
|
||||
attr.type === "JSXAttribute" &&
|
||||
attr.name.name === "data-version" &&
|
||||
getJSXLiteralValue(attr.value) === "v1",
|
||||
const skip = !!parent.value.openingElement?.attributes.find(
|
||||
(x) => x.name.name === "data-version" && x.value.value === "v1",
|
||||
);
|
||||
|
||||
parent.value.openingElement?.attributes?.forEach(
|
||||
(x: JSXAttribute | JSXSpreadAttribute) => {
|
||||
let didUpdate = false;
|
||||
if (x.type === "JSXAttribute" && x.name?.name === "size" && !skip) {
|
||||
const value = getJSXLiteralValue(x.value);
|
||||
if (value === "medium") {
|
||||
x.value = j.literal("small");
|
||||
didUpdate = true;
|
||||
} else if (value === "small") {
|
||||
x.value = j.literal("xsmall");
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
didUpdate && addMigrationTag(parent.value);
|
||||
parent.value.openingElement?.attributes.forEach((x) => {
|
||||
let didUpdate = false;
|
||||
if (x.name?.name === "size" && x.type === "JSXAttribute" && !skip) {
|
||||
/* addExplicitStandardProp */
|
||||
if (x.value.value === "medium") {
|
||||
x.value = j.literal("small");
|
||||
didUpdate = true;
|
||||
} else if (x.value.value === "small") {
|
||||
x.value = j.literal("xsmall");
|
||||
didUpdate = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
didUpdate && addMigrationTag(parent.value);
|
||||
}
|
||||
});
|
||||
addExplicitStandardProp(parent.value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Pagination as DsPagination, Button } from "@navikt/ds-react";
|
||||
|
||||
export const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<DsPagination onPageChange={setPage} />
|
||||
<DsPagination size={"medium"} onPageChange={setPage} />
|
||||
<DsPagination size={"small"} onPageChange={setPage} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Pagination as DsPagination, Button } from "@navikt/ds-react";
|
||||
|
||||
export const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<DsPagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<DsPagination size="small" onPageChange={setPage} data-version="v1" />
|
||||
<DsPagination size="xsmall" onPageChange={setPage} data-version="v1" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { check } from "../../../../utils/check";
|
||||
|
||||
const migration = "pagination";
|
||||
const fixtures = ["size", "idempotent", "import", "literal"];
|
||||
const fixtures = ["size", "idempotent", "import"];
|
||||
|
||||
for (const fixture of fixtures) {
|
||||
check(__dirname, {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { API, FileInfo } from "jscodeshift";
|
||||
import transformChat from "../chat/chat";
|
||||
import transformPagination from "../pagination/pagination";
|
||||
import transformTabs from "../tabs/tabs";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
file.source = transformTabs(file, api);
|
||||
file.source = transformChat(file, api);
|
||||
file.source = transformPagination(file, api);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { API, FileInfo, JSXAttribute } from "jscodeshift";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
import renameProps from "../../../utils/rename-props";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
let localName = "Tabs";
|
||||
let iconPositionProp: JSXAttribute | null = null;
|
||||
let iconPositionProp = null;
|
||||
|
||||
const root = j(file.source);
|
||||
|
||||
@@ -14,14 +17,9 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
.find(j.ImportDeclaration)
|
||||
.filter((path) => path.node.source.value === "@navikt/ds-react")
|
||||
.forEach((imp) => {
|
||||
imp.value.specifiers?.forEach((x) => {
|
||||
if (
|
||||
x.type === "ImportSpecifier" &&
|
||||
x.imported.name === "Tabs" &&
|
||||
x.local &&
|
||||
x.local.name !== x.imported.name
|
||||
) {
|
||||
localName = String(x.local.name);
|
||||
imp.value.specifiers.forEach((x) => {
|
||||
if (x.imported.name === "Tabs" && x.local.name !== x.imported.name) {
|
||||
localName = x.local.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -36,52 +34,48 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
});
|
||||
|
||||
root.findJSXElements(`${localName}`).forEach((parent) => {
|
||||
parent.value.children?.forEach((el) => {
|
||||
if (el.type !== "JSXElement") return;
|
||||
parent.value.children.forEach((el) => {
|
||||
const openingEl = el.openingElement;
|
||||
if (openingEl.name.type === "JSXMemberExpression") {
|
||||
if (openingEl?.name && openingEl.name.type === "JSXMemberExpression") {
|
||||
if (
|
||||
openingEl.name.object.type === "JSXIdentifier" &&
|
||||
openingEl.name.object.name === localName &&
|
||||
openingEl.name.property.name === "List"
|
||||
) {
|
||||
/* Move loop-prop */
|
||||
openingEl.attributes?.forEach((x, index) => {
|
||||
if (x.type === "JSXAttribute" && x.name.name === "loop") {
|
||||
parent.value.openingElement.attributes?.push(x);
|
||||
openingEl.attributes?.splice(index, 1);
|
||||
el.openingElement.attributes.forEach((x, index) => {
|
||||
if (x.name?.name === "loop" && x.type === "JSXAttribute") {
|
||||
parent.value.openingElement.attributes.push(x);
|
||||
delete el.openingElement.attributes[index];
|
||||
}
|
||||
});
|
||||
|
||||
/* Find and move iconPosition-prop to <Tabs/> */
|
||||
el.children?.forEach((tab) => {
|
||||
if (tab.type !== "JSXElement") return;
|
||||
el.children.forEach((tab) => {
|
||||
const tabEl = tab.openingElement;
|
||||
if (
|
||||
tabEl.name.type === "JSXMemberExpression" &&
|
||||
tabEl.name.object.type === "JSXIdentifier" &&
|
||||
tabEl.name.object.name === localName &&
|
||||
tabEl.name.property.name === "Tab"
|
||||
) {
|
||||
tabEl.attributes?.forEach((x, index) => {
|
||||
if (
|
||||
x.type === "JSXAttribute" &&
|
||||
x.name.name === "iconPosition"
|
||||
) {
|
||||
if (!iconPositionProp) {
|
||||
iconPositionProp = x;
|
||||
if (tabEl?.name && tabEl.name.type === "JSXMemberExpression") {
|
||||
if (
|
||||
tabEl.name.object.name === localName &&
|
||||
tabEl.name.property.name === "Tab"
|
||||
) {
|
||||
tabEl?.attributes.forEach((x, index) => {
|
||||
if (
|
||||
x.name?.name === "iconPosition" &&
|
||||
x.type === "JSXAttribute"
|
||||
) {
|
||||
if (!iconPositionProp) {
|
||||
iconPositionProp = x;
|
||||
}
|
||||
delete tabEl.attributes[index];
|
||||
}
|
||||
tabEl.attributes?.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (iconPositionProp) {
|
||||
parent.value.openingElement.attributes?.push(iconPositionProp);
|
||||
}
|
||||
iconPositionProp &&
|
||||
parent.value.openingElement.attributes.push(iconPositionProp);
|
||||
iconPositionProp = null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { FileInfo } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.js";
|
||||
|
||||
export default function transformer(file: FileInfo) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file) {
|
||||
let src = file.source;
|
||||
|
||||
tokens.forEach((tok) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { API, JSCodeshift } from "jscodeshift";
|
||||
import type { JSCodeshift } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.js";
|
||||
import {
|
||||
getImportSpecifier,
|
||||
@@ -12,7 +12,7 @@ import { translateToken } from "../../../utils/translate-token";
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file: JSCodeshift, api: API) {
|
||||
export default function transformer(file: JSCodeshift, api) {
|
||||
const src = file.source;
|
||||
|
||||
const j = api.jscodeshift as JSCodeshift;
|
||||
@@ -37,7 +37,7 @@ export default function transformer(file: JSCodeshift, api: API) {
|
||||
|
||||
getImportSpecifier(j, root, name, "@navikt/ds-tokens/dist/tokens").forEach(
|
||||
(x) => {
|
||||
foundName = String(x.node.imported.name);
|
||||
foundName = x.node.imported.name;
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
import type { FileInfo } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.js";
|
||||
import { translateToken } from "../../../utils/translate-token";
|
||||
|
||||
export default function transformer(file: FileInfo) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file) {
|
||||
let src = file.source;
|
||||
|
||||
tokens.forEach((tok) => {
|
||||
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
import type { FileInfo } from "jscodeshift";
|
||||
import { tokens } from "../../../tokens-map.js";
|
||||
import { translateToken } from "../../../utils/translate-token";
|
||||
|
||||
export default function transformer(file: FileInfo) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file) {
|
||||
let src = file.source;
|
||||
|
||||
tokens.forEach((tok) => {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import chalk from "chalk";
|
||||
import type { API, Collection, FileInfo } from "jscodeshift";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
import moveAndRenameImport from "../../../utils/packageImports";
|
||||
import removePropsFromComponent from "../../../utils/removeProps";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
|
||||
let root: Collection<any>;
|
||||
let root: any;
|
||||
try {
|
||||
root = j(file.source);
|
||||
} catch {
|
||||
@@ -43,14 +46,13 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
const children = node.node.children;
|
||||
let flagged = false;
|
||||
if (
|
||||
children &&
|
||||
children.length > 0 &&
|
||||
!node.node.openingElement.attributes?.some(
|
||||
(attr) => attr.type === "JSXAttribute" && attr.name.name === "text",
|
||||
!node.node.openingElement.attributes.some(
|
||||
(attr) => attr.name.name === "text",
|
||||
)
|
||||
) {
|
||||
if (children.length === 1 && children[0].type === "JSXText") {
|
||||
node.node.openingElement.attributes?.push(
|
||||
node.node.openingElement.attributes.push(
|
||||
j.jsxAttribute(
|
||||
j.jsxIdentifier("text"),
|
||||
j.literal(children[0].value.trim()),
|
||||
@@ -78,15 +80,9 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
});
|
||||
|
||||
compRoot.forEach((x) => {
|
||||
const openingName = x.node.openingElement.name;
|
||||
if (openingName.type === "JSXIdentifier") {
|
||||
openingName.name = "CopyButton";
|
||||
}
|
||||
if (x.node.children && x.node.children.length > 0) {
|
||||
const closingName = x.node.closingElement?.name;
|
||||
if (closingName?.type === "JSXIdentifier") {
|
||||
closingName.name = "CopyButton";
|
||||
}
|
||||
x.node.openingElement.name.name = "CopyButton";
|
||||
if (x.node.children.length > 0) {
|
||||
x.node.closingElement.name.name = "CopyButton";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { FileInfo } from "jscodeshift";
|
||||
|
||||
export default function transformer(file: FileInfo) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file) {
|
||||
const names = [
|
||||
"UNSAFE_useDatepicker",
|
||||
"UNSAFE_DatePicker",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { FileInfo } from "jscodeshift";
|
||||
|
||||
const cssConversions = {
|
||||
".navdsi-dropdown": ".navds-dropdown",
|
||||
".navdsi-header": ".navds-internalheader",
|
||||
@@ -10,8 +8,11 @@ const cssConversions = {
|
||||
"--ac-header-hover-bg": "--ac-internalheader-hover-bg",
|
||||
"--ac-header-active-bg": "--ac-internalheader-active-bg",
|
||||
};
|
||||
|
||||
export default function transformer(file: FileInfo) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file) {
|
||||
let src = file.source;
|
||||
|
||||
Object.entries(cssConversions).forEach(([key, value]) => {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { API, FileInfo } from "jscodeshift";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
import moveAndRenameImport from "../../../utils/packageImports";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
|
||||
let root: any;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { API, FileInfo } from "jscodeshift";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
|
||||
export default function transformer(file: FileInfo, api: API) {
|
||||
/**
|
||||
* @param {import('jscodeshift').FileInfo} file
|
||||
* @param {import('jscodeshift').API} api
|
||||
*/
|
||||
export default function transformer(file, api) {
|
||||
const j = api.jscodeshift;
|
||||
let localName = "Chat";
|
||||
|
||||
@@ -12,14 +15,9 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
.find(j.ImportDeclaration)
|
||||
.filter((path) => path.node.source.value === "@navikt/ds-react")
|
||||
.forEach((imp) => {
|
||||
imp.value.specifiers?.forEach((x) => {
|
||||
if (x.type !== "ImportSpecifier") return;
|
||||
if (
|
||||
x.imported.name === "Chat" &&
|
||||
x.local &&
|
||||
x.local.name !== x.imported.name
|
||||
) {
|
||||
localName = String(x.local.name);
|
||||
imp.value.specifiers.forEach((x) => {
|
||||
if (x.imported.name === "Chat" && x.local.name !== x.imported.name) {
|
||||
localName = x.local.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
for (const prop of propsAffected) {
|
||||
findProps({ j, path: astElement, name: prop }).forEach((attr) => {
|
||||
const attrvalue = attr.value.value;
|
||||
if (attrvalue?.type === "StringLiteral") {
|
||||
if (attrvalue.type === "StringLiteral") {
|
||||
/**
|
||||
* Skips if the replacement token already set
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
tokenComments.push(tokenComment);
|
||||
}
|
||||
} else if (
|
||||
attrvalue?.type === "JSXExpressionContainer" &&
|
||||
attrvalue.type === "JSXExpressionContainer" &&
|
||||
attrvalue.expression.type === "StringLiteral"
|
||||
) {
|
||||
const literal = attrvalue.expression;
|
||||
@@ -96,10 +96,10 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
findProps({ j, path: astElement, name: "borderRadius" }).forEach((attr) => {
|
||||
const attrValue = attr.value.value;
|
||||
|
||||
if (attrValue?.type === "StringLiteral") {
|
||||
if (attrValue.type === "StringLiteral") {
|
||||
/* borderRadius="xlarge" */
|
||||
attrValue.value = convertBorderRadiusToRadius(attrValue.value);
|
||||
} else if (attrValue?.type === "JSXExpressionContainer") {
|
||||
} else if (attrValue.type === "JSXExpressionContainer") {
|
||||
/* borderRadius={{xs: "xlarge", sm: "large"}} */
|
||||
const expression = attrValue.expression;
|
||||
if (expression.type === "ObjectExpression") {
|
||||
@@ -189,9 +189,7 @@ function convertBorderRadiusToRadius(oldValue: string): string {
|
||||
console.warn(`Possibly invalid radius token found: ${radiusToken}\n`);
|
||||
newRadius.push(radiusToken);
|
||||
} else {
|
||||
newRadius.push(
|
||||
`${legacyBorderRadiusNameTokenLookup[radiusToken as keyof typeof legacyBorderRadiusNameTokenLookup]}`,
|
||||
);
|
||||
newRadius.push(`${legacyBorderRadiusNameTokenLookup[radiusToken]}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,23 @@ import type {
|
||||
FileInfo,
|
||||
ImportDeclaration,
|
||||
JSXAttribute,
|
||||
JSXElement,
|
||||
JSXSpreadAttribute,
|
||||
} from "jscodeshift";
|
||||
import { getJSXStringValue } from "../../../utils/jsx-value";
|
||||
import { getLineTerminator } from "../../../utils/lineterminator";
|
||||
|
||||
const headingSizeMap: Record<string, string> = {
|
||||
const headingSizeMap = {
|
||||
small: "xsmall",
|
||||
medium: "small",
|
||||
large: "medium",
|
||||
};
|
||||
|
||||
const bodySizeMap: Record<string, string> = {
|
||||
const bodySizeMap = {
|
||||
small: "small",
|
||||
medium: "medium",
|
||||
large: "large",
|
||||
};
|
||||
|
||||
const boxMarginMap: Record<string, string> = {
|
||||
const boxMarginMap = {
|
||||
small: "space-12",
|
||||
medium: "space-16",
|
||||
large: "space-16",
|
||||
@@ -34,7 +32,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
|
||||
let localListName = "List";
|
||||
let hasListImport = false;
|
||||
let dsReactImportPath: ASTPath<ImportDeclaration> | undefined;
|
||||
let dsReactImportPath: ASTPath<ImportDeclaration> | null = null;
|
||||
|
||||
let localHeadingName = "Heading";
|
||||
let hasHeadingImport = false;
|
||||
@@ -52,19 +50,19 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
path.node.specifiers?.forEach((specifier) => {
|
||||
if (specifier.type === "ImportSpecifier") {
|
||||
if (specifier.imported.name === "List") {
|
||||
localListName = String(specifier.local?.name || "List");
|
||||
localListName = specifier.local?.name || "List";
|
||||
hasListImport = true;
|
||||
}
|
||||
if (specifier.imported.name === "Heading") {
|
||||
localHeadingName = String(specifier.local?.name || "Heading");
|
||||
localHeadingName = specifier.local?.name || "Heading";
|
||||
hasHeadingImport = true;
|
||||
}
|
||||
if (specifier.imported.name === "BodyShort") {
|
||||
localBodyShortName = String(specifier.local?.name || "BodyShort");
|
||||
localBodyShortName = specifier.local?.name || "BodyShort";
|
||||
hasBodyShortImport = true;
|
||||
}
|
||||
if (specifier.imported.name === "Box") {
|
||||
localBoxName = String(specifier.local?.name || "Box");
|
||||
localBoxName = specifier.local?.name || "Box";
|
||||
hasBoxImport = true;
|
||||
}
|
||||
}
|
||||
@@ -73,7 +71,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
path.node.specifiers?.forEach((specifier) => {
|
||||
if (specifier.type === "ImportSpecifier") {
|
||||
if (specifier.imported.name === "List") {
|
||||
localListName = String(specifier.local?.name || "List");
|
||||
localListName = specifier.local?.name || "List";
|
||||
hasListImport = true;
|
||||
}
|
||||
}
|
||||
@@ -94,10 +92,6 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
.forEach((path) => {
|
||||
const attributes = path.node.openingElement.attributes;
|
||||
|
||||
if (!attributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const titleAttr = attributes.find(
|
||||
(attr) => attr.type === "JSXAttribute" && attr.name.name === "title",
|
||||
) as JSXAttribute | undefined;
|
||||
@@ -128,11 +122,15 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
const titleValue = titleAttr?.value;
|
||||
const descValue = descAttr?.value;
|
||||
|
||||
const extractedSize = getJSXStringValue(sizeAttr?.value);
|
||||
const sizeValue = extractedSize || "medium";
|
||||
let sizeValue = "medium";
|
||||
if (sizeAttr && sizeAttr.value?.type === "StringLiteral") {
|
||||
sizeValue = sizeAttr.value.value;
|
||||
}
|
||||
|
||||
const extractedHeadingAs = getJSXStringValue(headingTagAttr?.value);
|
||||
const headingAs = extractedHeadingAs || "h3";
|
||||
let headingAs = "h3";
|
||||
if (headingTagAttr && headingTagAttr.value?.type === "StringLiteral") {
|
||||
headingAs = headingTagAttr.value.value;
|
||||
}
|
||||
|
||||
// Separate attributes
|
||||
const listAttributes: JSXAttribute[] = [
|
||||
@@ -170,7 +168,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
}
|
||||
});
|
||||
|
||||
const newNodes: JSXElement[] = [];
|
||||
const newNodes = [];
|
||||
|
||||
// Create Heading
|
||||
if (titleValue) {
|
||||
@@ -191,13 +189,11 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
);
|
||||
}
|
||||
|
||||
let children: (ReturnType<typeof j.jsxText> | typeof titleValue)[];
|
||||
let children = [];
|
||||
if (titleValue.type === "StringLiteral") {
|
||||
children = [j.jsxText(titleValue.value)];
|
||||
} else if (titleValue.type === "JSXExpressionContainer") {
|
||||
children = [titleValue];
|
||||
} else {
|
||||
children = [];
|
||||
}
|
||||
|
||||
const heading = j.jsxElement(
|
||||
@@ -213,7 +209,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
if (!hasBodyShortImport) {
|
||||
newImports.add("BodyShort");
|
||||
}
|
||||
const bodyAttrs: JSXAttribute[] = [];
|
||||
const bodyAttrs = [];
|
||||
const mappedSize = bodySizeMap[sizeValue];
|
||||
if (mappedSize && mappedSize !== "medium") {
|
||||
bodyAttrs.push(
|
||||
@@ -224,13 +220,11 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
);
|
||||
}
|
||||
|
||||
let children: (ReturnType<typeof j.jsxText> | typeof descValue)[];
|
||||
let children = [];
|
||||
if (descValue.type === "StringLiteral") {
|
||||
children = [j.jsxText(descValue.value)];
|
||||
} else if (descValue.type === "JSXExpressionContainer") {
|
||||
children = [descValue];
|
||||
} else {
|
||||
children = [];
|
||||
}
|
||||
|
||||
const body = j.jsxElement(
|
||||
@@ -291,8 +285,8 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
);
|
||||
|
||||
newImports.forEach((imp) => {
|
||||
if (!existingSpecifiers.has(imp) && dsReactImportPath) {
|
||||
dsReactImportPath.node.specifiers?.push(
|
||||
if (!existingSpecifiers.has(imp)) {
|
||||
dsReactImportPath!.node.specifiers?.push(
|
||||
j.importSpecifier(j.identifier(imp)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,17 +56,17 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
findProps({ j, path, name: prop }).forEach((attr) => {
|
||||
const attrValue = attr.value.value;
|
||||
|
||||
if (attrValue?.type === "StringLiteral") {
|
||||
if (attrValue.type === "StringLiteral") {
|
||||
/* padding="32" */
|
||||
attrValue.value = convertSpacingToSpace(attrValue.value);
|
||||
} else if (
|
||||
attrValue?.type === "JSXExpressionContainer" &&
|
||||
attrValue.type === "JSXExpressionContainer" &&
|
||||
attrValue.expression.type === "StringLiteral"
|
||||
) {
|
||||
attrValue.expression.value = convertSpacingToSpace(
|
||||
attrValue.expression.value,
|
||||
);
|
||||
} else if (attrValue?.type === "JSXExpressionContainer") {
|
||||
} else if (attrValue.type === "JSXExpressionContainer") {
|
||||
/* padding={{xs: "16", sm: "32"}} */
|
||||
const expression = attrValue.expression;
|
||||
if (expression.type === "ObjectExpression") {
|
||||
|
||||
@@ -33,14 +33,14 @@ function findComponentImport(input: {
|
||||
return;
|
||||
}
|
||||
|
||||
path.node.specifiers?.forEach((specifier) => {
|
||||
path.node.specifiers.forEach((specifier) => {
|
||||
if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported.name === name
|
||||
) {
|
||||
foundName = String(
|
||||
specifier.local ? specifier.local.name : specifier.imported.name,
|
||||
);
|
||||
foundName = specifier.local
|
||||
? specifier.local.name
|
||||
: specifier.imported.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* @ts-expect-error No avaliable types for testUtils */
|
||||
import { applyTransform } from "jscodeshift/dist/testUtils";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import type { JSXAttribute, JSXExpressionContainer } from "jscodeshift";
|
||||
|
||||
type AttributeValue =
|
||||
| JSXAttribute["value"]
|
||||
| JSXExpressionContainer["expression"];
|
||||
|
||||
type LiteralValue = string | number | boolean;
|
||||
|
||||
/**
|
||||
* Extracts a literal value (string, number, or boolean) from a JSX attribute value.
|
||||
* Handles the following patterns:
|
||||
* - `prop="value"` (StringLiteral)
|
||||
* - `prop={"value"}` (JSXExpressionContainer with StringLiteral)
|
||||
* - `prop={`value`}` (TemplateLiteral with no expressions)
|
||||
* - `prop={42}` (NumericLiteral)
|
||||
* - `prop={true}` / `prop={false}` (BooleanLiteral)
|
||||
*
|
||||
* @param node - The JSX attribute value node to extract the value from
|
||||
* @returns The literal value if it can be extracted, otherwise null
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* getJSXLiteralValue(attr.value) // where attr is prop="hello" => "hello"
|
||||
* getJSXLiteralValue(attr.value) // where attr is prop={42} => 42
|
||||
* getJSXLiteralValue(attr.value) // where attr is prop={true} => true
|
||||
* ```
|
||||
*/
|
||||
export function getJSXLiteralValue(node: AttributeValue): LiteralValue | null {
|
||||
if (!node) return null;
|
||||
|
||||
if (node.type === "StringLiteral") {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
if (node.type === "NumericLiteral") {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
if (node.type === "UnaryExpression") {
|
||||
const unary = node;
|
||||
if (unary.operator === "-" && unary.argument.type === "NumericLiteral") {
|
||||
return -unary.argument.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.type === "BooleanLiteral") {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
if (node.type === "Literal") {
|
||||
const value = node.value;
|
||||
if (
|
||||
typeof value === "string" ||
|
||||
typeof value === "number" ||
|
||||
typeof value === "boolean"
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.type === "TemplateLiteral") {
|
||||
const template = node;
|
||||
if (template.expressions.length === 0 && template.quasis.length === 1) {
|
||||
return template.quasis[0].value.cooked ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.type === "JSXExpressionContainer") {
|
||||
return getJSXLiteralValue(node.expression);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the string value from a JSX attribute value.
|
||||
* Convenience wrapper around `getJSXLiteralValue` that only returns strings.
|
||||
*
|
||||
* @param node - The JSX attribute value node to extract the string from
|
||||
* @returns The string value if it can be extracted, otherwise null
|
||||
*/
|
||||
export function getJSXStringValue(node: AttributeValue): string | null {
|
||||
const value = getJSXLiteralValue(node);
|
||||
return typeof value === "string" ? value : null;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type {
|
||||
API,
|
||||
FileInfo,
|
||||
JSXAttribute,
|
||||
JSXExpressionContainer,
|
||||
Literal,
|
||||
} from "jscodeshift";
|
||||
import { getJSXStringValue } from "./jsx-value";
|
||||
import { getLineTerminator } from "./lineterminator";
|
||||
|
||||
export interface MigrationConfig {
|
||||
@@ -44,7 +44,7 @@ export function moveVariantToDataColor(
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported.name === rootComponent
|
||||
) {
|
||||
localName = String(specifier.local?.name || specifier.imported.name);
|
||||
localName = specifier.local?.name || specifier.imported.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -77,7 +77,7 @@ export function moveVariantToDataColor(
|
||||
if (attr.type !== "JSXAttribute") return;
|
||||
if (attr.name.name === config.prop) {
|
||||
variantPropIndex = index;
|
||||
const value = getJSXStringValue(attr.value);
|
||||
const value = getStringValue(attr.value);
|
||||
if (value) {
|
||||
variantValue = value;
|
||||
}
|
||||
@@ -134,3 +134,25 @@ export function moveVariantToDataColor(
|
||||
const toSourceOptions = getLineTerminator(file.source);
|
||||
return root.toSource(toSourceOptions);
|
||||
}
|
||||
|
||||
type AttributeValue =
|
||||
| JSXAttribute["value"]
|
||||
| JSXExpressionContainer["expression"];
|
||||
|
||||
function getStringValue(node: AttributeValue): string | null {
|
||||
if (!node) return null;
|
||||
if (node.type === "StringLiteral" || node.type === "Literal") {
|
||||
return node.value as string;
|
||||
}
|
||||
if (
|
||||
node.type === "TemplateLiteral" &&
|
||||
node.expressions.length === 0 &&
|
||||
node.quasis.length === 1
|
||||
) {
|
||||
return node.quasis[0].value.cooked ?? null;
|
||||
}
|
||||
if (node.type === "JSXExpressionContainer") {
|
||||
return getStringValue(node.expression);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -59,8 +59,8 @@ export default function moveAndRenameImport(
|
||||
const existingFromImportSpecifier = existingFromImport?.find(
|
||||
j.ImportSpecifier,
|
||||
(node) => {
|
||||
if (node.imported.name === fromName && node.local?.name) {
|
||||
localname = String(node.local.name);
|
||||
if (node.imported.name === fromName) {
|
||||
localname = node.local.name;
|
||||
}
|
||||
return node.imported.name === fromName;
|
||||
},
|
||||
|
||||
@@ -11,10 +11,6 @@ export default function removePropsFromComponent(
|
||||
component.forEach((node) => {
|
||||
const attributes = node.node.openingElement.attributes;
|
||||
|
||||
if (!attributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = attributes.length - 1; i >= 0; i--) {
|
||||
const attribute = attributes[i];
|
||||
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
import type { Collection } from "jscodeshift";
|
||||
|
||||
interface RenamePropsOptions {
|
||||
root: Collection;
|
||||
componentName: string;
|
||||
props: Record<string, string>;
|
||||
}
|
||||
|
||||
export default function renameProps({
|
||||
root,
|
||||
componentName,
|
||||
props,
|
||||
}: RenamePropsOptions) {
|
||||
export default function renameProps({ root, componentName, props }) {
|
||||
return root.findJSXElements(componentName).forEach((path) => {
|
||||
path.node.openingElement.attributes?.forEach((node) => {
|
||||
path.node.openingElement.attributes.forEach((node) => {
|
||||
if (
|
||||
node.type === "JSXAttribute" &&
|
||||
typeof node.name.name === "string" &&
|
||||
Object.keys(props).includes(node.name.name)
|
||||
) {
|
||||
node.name.name = props[node.name.name];
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import jscodeshift from "jscodeshift";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { getJSXLiteralValue, getJSXStringValue } from "../jsx-value";
|
||||
|
||||
const j = jscodeshift.withParser("tsx");
|
||||
|
||||
function getAttrValue(source: string) {
|
||||
const root = j(source);
|
||||
const attr = root.find(j.JSXAttribute, { name: { name: "prop" } });
|
||||
return attr.paths()[0]?.value.value ?? null;
|
||||
}
|
||||
|
||||
describe("getJSXLiteralValue", () => {
|
||||
test("returns null for null/undefined input", () => {
|
||||
expect(getJSXLiteralValue(null)).toBe(null);
|
||||
expect(getJSXLiteralValue(undefined)).toBe(null);
|
||||
});
|
||||
|
||||
test('handles StringLiteral: prop="value"', () => {
|
||||
const node = getAttrValue('<Component prop="hello" />');
|
||||
expect(getJSXLiteralValue(node)).toBe("hello");
|
||||
});
|
||||
|
||||
test("handles empty StringLiteral", () => {
|
||||
const node = getAttrValue('<Component prop="" />');
|
||||
expect(getJSXLiteralValue(node)).toBe("");
|
||||
});
|
||||
|
||||
test('handles JSXExpressionContainer with StringLiteral: prop={"value"}', () => {
|
||||
const node = getAttrValue('<Component prop={"hello"} />');
|
||||
expect(getJSXLiteralValue(node)).toBe("hello");
|
||||
});
|
||||
|
||||
test("handles TemplateLiteral without expressions: prop={`value`}", () => {
|
||||
const node = getAttrValue("<Component prop={`hello`} />");
|
||||
expect(getJSXLiteralValue(node)).toBe("hello");
|
||||
});
|
||||
|
||||
test("returns null for TemplateLiteral with expressions", () => {
|
||||
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing only
|
||||
const node = getAttrValue("<Component prop={`hello ${name}`} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(null);
|
||||
});
|
||||
|
||||
test("handles numeric values: prop={42}", () => {
|
||||
const node = getAttrValue("<Component prop={42} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(42);
|
||||
});
|
||||
|
||||
test("handles negative numbers: prop={-5}", () => {
|
||||
const node = getAttrValue("<Component prop={-5} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(-5);
|
||||
});
|
||||
|
||||
test("handles floating point numbers: prop={3.14}", () => {
|
||||
const node = getAttrValue("<Component prop={3.14} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(3.14);
|
||||
});
|
||||
|
||||
test("handles boolean true: prop={true}", () => {
|
||||
const node = getAttrValue("<Component prop={true} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(true);
|
||||
});
|
||||
|
||||
test("handles boolean false: prop={false}", () => {
|
||||
const node = getAttrValue("<Component prop={false} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns null for variable references", () => {
|
||||
const node = getAttrValue("<Component prop={someVar} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for function calls", () => {
|
||||
const node = getAttrValue("<Component prop={getValue()} />");
|
||||
expect(getJSXLiteralValue(node)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for object expressions", () => {
|
||||
const node = getAttrValue('<Component prop={{ key: "value" }} />');
|
||||
expect(getJSXLiteralValue(node)).toBe(null);
|
||||
});
|
||||
|
||||
test("handles strings with special characters", () => {
|
||||
const node = getAttrValue('<Component prop={"hello, world! 👋"} />');
|
||||
expect(getJSXLiteralValue(node)).toBe("hello, world! 👋");
|
||||
});
|
||||
|
||||
test("handles multiline template literal without expressions", () => {
|
||||
const node = getAttrValue(`<Component prop={\`hello
|
||||
world\`} />`);
|
||||
expect(getJSXLiteralValue(node)).toBe("hello\nworld");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getJSXStringValue", () => {
|
||||
test("returns string for StringLiteral", () => {
|
||||
const node = getAttrValue('<Component prop="hello" />');
|
||||
expect(getJSXStringValue(node)).toBe("hello");
|
||||
});
|
||||
|
||||
test("returns null for numeric values", () => {
|
||||
const node = getAttrValue("<Component prop={42} />");
|
||||
expect(getJSXStringValue(node)).toBe(null);
|
||||
});
|
||||
|
||||
test("returns null for boolean values", () => {
|
||||
const node = getAttrValue("<Component prop={true} />");
|
||||
expect(getJSXStringValue(node)).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -29,11 +29,11 @@ class TokenStatus {
|
||||
status: StatusT;
|
||||
|
||||
constructor() {
|
||||
this.status = this.initStatus();
|
||||
this.initStatus();
|
||||
}
|
||||
|
||||
initStatus() {
|
||||
return {
|
||||
this.status = {
|
||||
css: { legacy: [], updated: [] },
|
||||
scss: { legacy: [], updated: [] },
|
||||
less: { legacy: [], updated: [] },
|
||||
@@ -44,10 +44,6 @@ class TokenStatus {
|
||||
};
|
||||
}
|
||||
|
||||
resetStatus() {
|
||||
this.status = this.initStatus();
|
||||
}
|
||||
|
||||
add({
|
||||
isLegacy,
|
||||
type,
|
||||
@@ -100,9 +96,11 @@ class TokenStatus {
|
||||
let statusDataObj: StatusDataT;
|
||||
if (type === "summary") {
|
||||
statusDataObj = {
|
||||
legacy: Object.values(this.status).flatMap((_status) => _status.legacy),
|
||||
updated: Object.values(this.status).flatMap(
|
||||
(_status) => _status.updated,
|
||||
legacy: [].concat(
|
||||
...Object.values(this.status).map((_status) => _status.legacy),
|
||||
),
|
||||
updated: [].concat(
|
||||
...Object.values(this.status).map((_status) => _status.updated),
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -1230,7 +1230,7 @@ const colors = {
|
||||
|
||||
type ColorObjWithTwType = ColorObjType & {
|
||||
twOld: string;
|
||||
twNew?: string;
|
||||
twNew: string;
|
||||
};
|
||||
|
||||
const colorWithTailwindConversion = Object.entries(colors).reduce<
|
||||
|
||||
@@ -9,7 +9,7 @@ function getFrameworkRegexes({
|
||||
legacy = false,
|
||||
}: {
|
||||
token: string;
|
||||
twString?: string | null;
|
||||
twString: string | null;
|
||||
legacy: boolean;
|
||||
}): Record<"css" | "scss" | "less" | "js" | "tailwind", RegExp | null> {
|
||||
const regexes: ReturnType<typeof getFrameworkRegexes> = {
|
||||
|
||||
@@ -453,7 +453,7 @@ const newTokensWithoutRegex: TokensWithTailwindTag = {
|
||||
|
||||
type TokensWithRegexes = Record<
|
||||
string,
|
||||
{ tw: string | null; regexes: ReturnType<typeof getFrameworkRegexes> }
|
||||
{ tw: string; regexes: ReturnType<typeof getFrameworkRegexes> }
|
||||
>;
|
||||
|
||||
const v8TokenConfig: TokensWithRegexes = Object.entries(
|
||||
|
||||
@@ -107,12 +107,7 @@ export async function runTooling(
|
||||
() => getStatus(filepaths, "no-print"),
|
||||
);
|
||||
} catch (error) {
|
||||
program.error(
|
||||
chalk.red(
|
||||
"Error:",
|
||||
error instanceof Error ? error.message : String(error),
|
||||
),
|
||||
);
|
||||
program.error(chalk.red("Error:", error.message));
|
||||
}
|
||||
|
||||
task = await getNextTask(currentStatus.status);
|
||||
@@ -266,21 +261,15 @@ function getScopedFilesForTask(
|
||||
return safeFilepaths.filter((f) => {
|
||||
switch (task) {
|
||||
case "css-tokens":
|
||||
return !!status.css.legacy.find((config: any) => config.fileName === f);
|
||||
return !!status.css.legacy.find((config) => config.fileName === f);
|
||||
case "scss-tokens":
|
||||
return !!status.scss.legacy.find(
|
||||
(config: any) => config.fileName === f,
|
||||
);
|
||||
return !!status.scss.legacy.find((config) => config.fileName === f);
|
||||
case "less-tokens":
|
||||
return !!status.less.legacy.find(
|
||||
(config: any) => config.fileName === f,
|
||||
);
|
||||
return !!status.less.legacy.find((config) => config.fileName === f);
|
||||
case "js-tokens":
|
||||
return !!status.js.legacy.find((config: any) => config.fileName === f);
|
||||
return !!status.js.legacy.find((config) => config.fileName === f);
|
||||
case "tailwind-tokens":
|
||||
return !!status.tailwind.legacy.find(
|
||||
(config: any) => config.fileName === f,
|
||||
);
|
||||
return !!status.tailwind.legacy.find((config) => config.fileName === f);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -372,12 +361,7 @@ async function getNextTask(status?: any): Promise<TaskName> {
|
||||
} as any);
|
||||
return response.task;
|
||||
} catch (error) {
|
||||
if (
|
||||
error != null &&
|
||||
typeof error === "object" &&
|
||||
"isTtyError" in error &&
|
||||
error.isTtyError
|
||||
) {
|
||||
if (error.isTtyError) {
|
||||
console.info(
|
||||
"Oops, something went wrong! Looks like @navikt/aksel can't run in this terminal. " +
|
||||
"Contact Aksel for support if this persists, or try another terminal.",
|
||||
|
||||
@@ -190,10 +190,7 @@ async function printRemaining(files: string[], status?: TokenStatus["status"]) {
|
||||
clipboardy.writeSync(JSON.stringify(jsonOutput, null, 2));
|
||||
console.info("✅ Report (JSON) copied to clipboard!");
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"❌ Failed to copy to clipboard:",
|
||||
error instanceof Error ? error.message : String(error),
|
||||
);
|
||||
console.error("❌ Failed to copy to clipboard:", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ function getStatus(
|
||||
progressBar.start(files.length, 0);
|
||||
}
|
||||
|
||||
StatusStore.resetStatus();
|
||||
StatusStore.initStatus();
|
||||
|
||||
/**
|
||||
* Prepare search terms for legacy and v8 tokens.
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function transformer(file: FileInfo, api: API) {
|
||||
oldJsVar,
|
||||
"@navikt/ds-tokens/dist/tokens",
|
||||
).forEach((x) => {
|
||||
foundName = x.node.imported.name as string;
|
||||
foundName = x.node.imported.name;
|
||||
});
|
||||
|
||||
if (!foundName) {
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function transformer(file: FileInfo) {
|
||||
let src = file.source;
|
||||
|
||||
for (const config of Object.values(legacyTokenConfig)) {
|
||||
if (config.replacement.length > 0 && config.regexes.less) {
|
||||
if (config.replacement.length > 0) {
|
||||
src = src.replace(
|
||||
config.regexes.less,
|
||||
translateToken(`--ax-${config.replacement}`, "less"),
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function transformer(file: FileInfo) {
|
||||
let src = file.source;
|
||||
|
||||
for (const config of Object.values(legacyTokenConfig)) {
|
||||
if (config.replacement.length > 0 && config.regexes.scss) {
|
||||
if (config.replacement.length > 0) {
|
||||
src = src.replace(
|
||||
config.regexes.scss,
|
||||
translateToken(`--ax-${config.replacement}`, "scss"),
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
import type { SupportedCodemodExtensions } from "./codemod/codeshift.utils";
|
||||
|
||||
const JS_EXTENSIONS = [
|
||||
"js",
|
||||
"jsx",
|
||||
"ts",
|
||||
"tsx",
|
||||
] satisfies SupportedCodemodExtensions[];
|
||||
|
||||
const CSS_EXTENSIONS = [
|
||||
"css",
|
||||
"scss",
|
||||
"less",
|
||||
] satisfies SupportedCodemodExtensions[];
|
||||
|
||||
type MigrationT = Record<
|
||||
string,
|
||||
{
|
||||
description: string;
|
||||
value: string;
|
||||
path: string;
|
||||
warning?: string;
|
||||
/**
|
||||
* Some transforms are only meant to run on specific file extensions.
|
||||
* This is used to filter out files that should not be transformed.
|
||||
*/
|
||||
ignoredExtensions: SupportedCodemodExtensions[];
|
||||
}[]
|
||||
>;
|
||||
|
||||
const migrations: MigrationT = {
|
||||
v1: [
|
||||
{
|
||||
description: "Runs all codemods for beta -> v1 migration",
|
||||
value: "v1-preset",
|
||||
path: "v1.0.0/preset/preset",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Fixes breaking API-changes for <Pagination /> component",
|
||||
value: "v1-pagination",
|
||||
path: "v1.0.0/pagination/pagination",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Fixes breaking API-changes for <Tabs /> component",
|
||||
value: "v1-tabs",
|
||||
path: "v1.0.0/tabs/tabs",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Fixes breaking API-changes for <SpeechBubble /> (now <Chat/>) component",
|
||||
value: "v1-chat",
|
||||
path: "v1.0.0/chat/chat",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v2: [
|
||||
{
|
||||
description: "Patches changed css-variables",
|
||||
value: "v2-css",
|
||||
path: "v2.0.0/update-css-tokens/update-css-tokens",
|
||||
ignoredExtensions: [],
|
||||
},
|
||||
{
|
||||
description: "Patches changed js-variables",
|
||||
value: "v2-js",
|
||||
path: "v2.0.0/update-js-tokens/update-js-tokens",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Patches changed sass-variables",
|
||||
value: "v2-sass",
|
||||
path: "v2.0.0/update-sass-tokens/update-sass-tokens",
|
||||
ignoredExtensions: JS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Patches changed less-variables",
|
||||
value: "v2-less",
|
||||
path: "v2.0.0/update-less-tokens/update-less-tokens",
|
||||
ignoredExtensions: JS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v3: [
|
||||
{
|
||||
description:
|
||||
"Replaces deprecated <CopyToClipboard /> with <CopyButton />",
|
||||
value: "v3-copybutton",
|
||||
path: "v3.0.0/copybutton/copybutton",
|
||||
warning:
|
||||
"Remember to remove css-import from '@navikt/ds-css-internal' if no longer needed\nIf non-text was used as children, or different locales were handled, you need to manually fix this",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v4: [
|
||||
{
|
||||
description:
|
||||
"Replaced deprecated 'internal'-component import to 'core'-imports",
|
||||
value: "v4-internal-react",
|
||||
path: "v4.0.0/internal-react/internal-react",
|
||||
warning:
|
||||
"Remember to also run 'v4-internal-css' if you have overwritten any css-classes!",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Replaced 'navdsi'-prefixes used to overwrite internal-css to 'navds'-prefixes",
|
||||
value: "v4-internal-css",
|
||||
path: "v4.0.0/internal-css/internal-css",
|
||||
warning:
|
||||
"Remember to remove '@navikt/ds-css-internal' imports if no longer needed! Also run 'v4-internal-react' to convert internal-react imports to core-imports",
|
||||
ignoredExtensions: [],
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Removes all instances of UNSAFE_ for datepicker/monthpicker.",
|
||||
value: "v4-date",
|
||||
path: "v4.0.0/date/date",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v6: [
|
||||
{
|
||||
description:
|
||||
"Removes `backgroundColor` and `avatarBgColor` properties from `Chat` and `Chat.Bubble`",
|
||||
value: "v6-chat",
|
||||
path: "v6.0.0/chat/chat",
|
||||
warning:
|
||||
"Remember to update use of `variant`-prop to match previous use of colors. If needed the component exposes css-variables for custom overrides",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
v8: [
|
||||
{
|
||||
description:
|
||||
"Updates Box with legacy-tokens to Box using the new token system, and renames already migrated BoxNew/Box.New instances to Box.",
|
||||
value: "v8-box",
|
||||
path: "v8.0.0/box/box",
|
||||
warning:
|
||||
"Remember to check if 'TODO: Aksel Box migration'-comment was added to any files after migration. This comment will help you find and update Box-instances we couldn't update for you.",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Renames already migrated BoxNew/Box.New instances to Box.",
|
||||
value: "v8-box-new",
|
||||
path: "v8.0.0/box-new/box-new",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Removes deprecated props from components.",
|
||||
value: "v8-prop-deprecate",
|
||||
path: "v8.0.0/prop-deprecate/prop-deprecate",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Tries to migrate List component by wrapping it with 'Box' and moving 'title' and 'description' props to 'Heading' and 'BodyShort'.",
|
||||
value: "v8-list",
|
||||
path: "v8.0.0/list/list",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description: "Updates all Primitives to use new `space`-tokens.",
|
||||
value: "v8-primitive-spacing",
|
||||
path: "v8.0.0/primitives-spacing/spacing",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Tag based on current variant prop.",
|
||||
value: "v8-tag-variant",
|
||||
path: "v8.0.0/tag-variant/tag-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on ToggleGroup based on current variant prop.",
|
||||
value: "v8-toggle-group-variant",
|
||||
path: "v8.0.0/toggle-group-variant/toggle-group-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Accordion based on current variant prop.",
|
||||
value: "v8-accordion-variant",
|
||||
path: "v8.0.0/accordion-variant/accordion-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Chips based on current variant prop.",
|
||||
value: "v8-chips-variant",
|
||||
path: "v8.0.0/chips-variant/chips-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Button based on current variant prop.",
|
||||
value: "v8-button-variant",
|
||||
path: "v8.0.0/button-variant/button-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Updates variant + data-color props on Link based on current variant prop.",
|
||||
value: "v8-link-variant",
|
||||
path: "v8.0.0/link-variant/link-variant",
|
||||
ignoredExtensions: CSS_EXTENSIONS,
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
export { migrations };
|
||||
@@ -4,9 +4,8 @@
|
||||
"sourceMap": false,
|
||||
"esModuleInterop": true,
|
||||
"target": "es2015",
|
||||
"lib": ["ES2021"],
|
||||
"preserveWatchOutput": true,
|
||||
"declaration": true,
|
||||
"declaration": false,
|
||||
"types": ["node"],
|
||||
"outDir": "dist",
|
||||
"rootDir": "./src",
|
||||
|
||||
@@ -1,69 +1,5 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 8.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Dialog: Now aligns to bottom when centered at 480px instead of 768px screen width ([#4831](https://github.com/navikt/aksel/pull/4831))
|
||||
|
||||
## 8.10.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Modal: Changing `size` had no effect before ([#4823](https://github.com/navikt/aksel/pull/4823))
|
||||
|
||||
## 8.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- DatePicker: Highlight days between start and hovered day ([#4801](https://github.com/navikt/aksel/pull/4801))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Skeleton: Text-variant now has properly rounded edges ([#4808](https://github.com/navikt/aksel/pull/4808))
|
||||
|
||||
- ExpandableRow: Fix selected row top border not showing when row above was collapsed ([#4810](https://github.com/navikt/aksel/pull/4810))
|
||||
|
||||
- Timeline: Icons in periods now scale based on avaliable space. No longer clips on smaller periods ([#4809](https://github.com/navikt/aksel/pull/4809))
|
||||
|
||||
- DatePicker, MonthPicker: Change styling on disabled days/months ([#4801](https://github.com/navikt/aksel/pull/4801))
|
||||
|
||||
- InfoCard: Align header with content when no icon is present for small version. ([#4815](https://github.com/navikt/aksel/pull/4815))
|
||||
|
||||
- InfoCard, LocalAlert, GlobalAlert: Update content typography use and sizing. ([#4815](https://github.com/navikt/aksel/pull/4815))
|
||||
|
||||
## 8.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Button: Use correct text color when inherited `data-color` is "neutral" ([#4772](https://github.com/navikt/aksel/pull/4772))
|
||||
|
||||
- Checkbox, Radio: Internal updates. ([#4760](https://github.com/navikt/aksel/pull/4760))
|
||||
|
||||
- Datepicker, Monthpicker: Sync background on DateInput button to Input. ([#4781](https://github.com/navikt/aksel/pull/4781))
|
||||
|
||||
## 8.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- InternalHeader: Added `isActive`-prop to `InternalHeader.Button`. ([#4761](https://github.com/navikt/aksel/pull/4761))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- DatePicker, MonthPicker: Make open button more prominent ([#4751](https://github.com/navikt/aksel/pull/4751))
|
||||
|
||||
## 8.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- ReadMore: Added variant `moderate`. ([#4723](https://github.com/navikt/aksel/pull/4723))
|
||||
|
||||
- InfoCard: New sub-component InfoCard.Message. ([#4717](https://github.com/navikt/aksel/pull/4717))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- GlobalAlert, LocalAlert: Content outline changed from 4px to 2px. ([#4745](https://github.com/navikt/aksel/pull/4745))
|
||||
|
||||
## 8.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "8.10.2",
|
||||
"version": "8.7.0",
|
||||
"description": "CSS for Nav Designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -30,9 +30,9 @@
|
||||
"css:get-version": "node config/get-version.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^8.10.2",
|
||||
"@navikt/ds-tokens": "^8.7.0",
|
||||
"browserslist": "^4.25.0",
|
||||
"esbuild": "^0.27.4",
|
||||
"esbuild": "^0.27.2",
|
||||
"fast-glob": "3.3.3",
|
||||
"lightningcss": "^1.30.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
||||
@@ -114,16 +114,11 @@
|
||||
&[data-variant="moderate"] {
|
||||
outline: 1px solid var(--ax-border-default);
|
||||
outline-offset: -1px;
|
||||
|
||||
/* Adjust horizontal padding for small moderate alerts */
|
||||
&[data-size="small"] {
|
||||
--__axc-base-alert-pi: var(--ax-space-16);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant="strong"] {
|
||||
outline: 2px solid var(--ax-border-default);
|
||||
outline-offset: -2px;
|
||||
outline: 4px solid var(--ax-border-default);
|
||||
outline-offset: -4px;
|
||||
|
||||
&[data-color="neutral"] {
|
||||
outline-color: var(--ax-border-strong);
|
||||
@@ -150,9 +145,6 @@
|
||||
background: var(--ax-bg-moderate);
|
||||
color: var(--ax-text-default);
|
||||
border-color: var(--ax-border-subtleA);
|
||||
|
||||
/* Optical alignment */
|
||||
padding-top: var(--ax-space-1);
|
||||
}
|
||||
|
||||
.aksel-base-alert[data-variant="strong"] & {
|
||||
@@ -180,10 +172,6 @@
|
||||
padding: var(--__axc-base-alert-content-p);
|
||||
background-color: var(--ax-bg-default);
|
||||
|
||||
&[data-color] {
|
||||
color: var(--ax-text-neutral);
|
||||
}
|
||||
|
||||
.aksel-base-alert[data-global="true"] & {
|
||||
padding-inline: var(--__axc-global-alert-alignment);
|
||||
}
|
||||
@@ -204,24 +192,3 @@
|
||||
color: var(--ax-text-neutral-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-base-alert__message {
|
||||
border-radius: var(--ax-radius-12);
|
||||
height: fit-content;
|
||||
outline: 1px solid var(--ax-border-default);
|
||||
outline-offset: -1px;
|
||||
background: var(--ax-bg-moderate);
|
||||
color: var(--ax-text-default);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--ax-space-8);
|
||||
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"] & > .aksel-base-alert__icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,7 @@
|
||||
background-color: transparent;
|
||||
color: var(--ax-text-subtle);
|
||||
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] > &:not([data-color]),
|
||||
[data-color="neutral"] :not([data-color]) &:not([data-color]) {
|
||||
&[data-color="neutral"] {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
|
||||
@@ -64,8 +62,7 @@
|
||||
color: var(--ax-text-subtle);
|
||||
background-color: transparent;
|
||||
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] > &:not([data-color]) {
|
||||
&[data-color="neutral"] {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
}
|
||||
@@ -75,9 +72,7 @@
|
||||
background-color: transparent;
|
||||
color: var(--ax-text-subtle);
|
||||
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] > &:not([data-color], [data-pressed="true"]),
|
||||
[data-color="neutral"] :not([data-color]) &:not([data-color], [data-pressed="true"]) {
|
||||
&[data-color="neutral"] {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
|
||||
@@ -98,8 +93,7 @@
|
||||
color: var(--ax-text-subtle);
|
||||
background-color: transparent;
|
||||
|
||||
&[data-color="neutral"],
|
||||
[data-color="neutral"] > &:not([data-color]) {
|
||||
&[data-color="neutral"] {
|
||||
color: var(--ax-text-default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
padding-left: var(--ax-space-6);
|
||||
}
|
||||
|
||||
/* -------------------------- start old CSS -------------------------- */
|
||||
|
||||
/* ----------------------------- Chips removable ---------------------------- */
|
||||
.aksel-chips__removable {
|
||||
gap: 0;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* stylelint-disable csstools/value-no-unknown-custom-properties */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
|
||||
.aksel-data-table__border-wrapper {
|
||||
@@ -49,70 +48,41 @@
|
||||
background-color: var(--ax-bg-neutral-softA);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-truncate-content="true"] {
|
||||
.aksel-data-table__td > div {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__thead {
|
||||
background-color: var(--ax-bg-raised);
|
||||
|
||||
.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;
|
||||
}
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.aksel-data-table__tbody {
|
||||
background-color: var(--ax-bg-raised);
|
||||
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 {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--ax-bg-overlay);
|
||||
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"] {
|
||||
background-color: var(--ax-bg-softA);
|
||||
.aksel-data-table__tr--selected {
|
||||
background-color: var(--ax-bg-softA);
|
||||
box-shadow:
|
||||
inset 1px 0 0 0 var(--ax-border-default),
|
||||
inset -1px 0 0 0 var(--ax-border-default);
|
||||
|
||||
&:first-of-type {
|
||||
box-shadow:
|
||||
inset 1px 0 0 0 var(--ax-border-default),
|
||||
inset -1px 0 0 0 var(--ax-border-default),
|
||||
inset 0 1px 0 0 var(--ax-border-default);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
& :is(.aksel-data-table__td, .aksel-data-table__th) {
|
||||
border-color: var(--ax-border-default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +100,6 @@
|
||||
pointer-events: none;
|
||||
border: 1px solid var(--ax-border-neutral-subtle);
|
||||
border-radius: var(--ax-radius-12);
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,32 +111,16 @@
|
||||
}
|
||||
|
||||
.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 {
|
||||
.aksel-data-table__th,
|
||||
.aksel-data-table__td {
|
||||
/* border doesn't work on sticky thead when we have collapsed borders, so using box-shadow instead */
|
||||
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;
|
||||
|
||||
&[data-nested="true"] {
|
||||
position: relative;
|
||||
|
||||
& > div {
|
||||
margin-inline-start: calc(32px + 8px + calc(var(--__axc-data-table-nested-depth) * var(--ax-space-16)));
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__thead & {
|
||||
background-color: var(--ax-bg-neutral-soft);
|
||||
}
|
||||
|
||||
&[data-align="left"] {
|
||||
text-align: start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&[data-align="center"] {
|
||||
@@ -175,90 +128,18 @@
|
||||
}
|
||||
|
||||
&[data-align="right"] {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
&[data-sticky] {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-block: 0;
|
||||
pointer-events: none;
|
||||
border-color: var(--ax-border-neutral-subtle);
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
&[data-sticky="start"] {
|
||||
left: 0;
|
||||
|
||||
&::after {
|
||||
inset-inline: 0 -1px;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-sticky="end"] {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
background-color: var(--ax-bg-raised);
|
||||
|
||||
.aksel-data-table__thead & {
|
||||
background-color: var(--ax-bg-neutral-soft);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible,
|
||||
/* Focus instead of focus-visible so that effect of sort-icon staying isnt strange */
|
||||
/* TODO: Update from th to generic */
|
||||
&:has(.aksel-data-table__th-sort-button:focus) {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
&[data-selectable="true"] {
|
||||
padding: 0;
|
||||
|
||||
& > div {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
--__axc-data-table-pi-cell: var(--ax-space-16);
|
||||
|
||||
&:not([data-selectable="true"]) > div {
|
||||
padding-block: var(--__axc-data-table-density);
|
||||
padding-inline: var(--__axc-data-table-pi-cell);
|
||||
|
||||
.aksel-data-table[data-truncate-content="true"] & {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
/* BORDER STYLING END */
|
||||
|
||||
.aksel-data-table__column-header {
|
||||
/* TODO: Might want this to be dynamic */
|
||||
--__axc-data-table-density: var(--ax-space-8);
|
||||
|
||||
.aksel-data-table__th {
|
||||
position: relative;
|
||||
font-weight: var(--ax-font-weight-bold);
|
||||
|
||||
&:has(.aksel-data-table__th-sort-button:hover) {
|
||||
background-color: var(--ax-bg-neutral-moderate-hover);
|
||||
}
|
||||
|
||||
&:has(.aksel-data-table__th-sort-button:active) {
|
||||
background-color: var(--ax-bg-neutral-moderate-pressed);
|
||||
}
|
||||
background-color: var(--ax-bg-neutral-soft);
|
||||
color: var(--ax-text-neutral);
|
||||
padding: var(--ax-space-8) var(--ax-space-16);
|
||||
text-align: start;
|
||||
|
||||
&[data-sortable="true"] {
|
||||
padding: 0;
|
||||
@@ -270,14 +151,21 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ax-space-2);
|
||||
padding-inline-start: var(--ax-space-16);
|
||||
block-size: calc(var(--__axc-data-table-density) + 1.5rem);
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
text-align: start;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-weight: inherit;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ax-bg-neutral-moderate-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--ax-bg-neutral-moderate-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
@@ -312,7 +200,7 @@
|
||||
}
|
||||
|
||||
.aksel-data-table__th-resize-handle {
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
margin: 0;
|
||||
@@ -343,13 +231,6 @@
|
||||
transition: background 50ms linear;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible,
|
||||
&:active {
|
||||
/* Avoids clipping when having sticky columns */
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
/* TODO: Consider if the resize-handle should be visible already when hovering the column header */
|
||||
background: var(--ax-bg-strong);
|
||||
@@ -360,7 +241,7 @@
|
||||
background: var(--ax-bg-strong-pressed);
|
||||
}
|
||||
|
||||
.aksel-data-table__column-header:last-of-type & {
|
||||
.aksel-data-table__th:last-of-type & {
|
||||
inset-inline-end: 0;
|
||||
width: 15px;
|
||||
|
||||
@@ -418,6 +299,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__td {
|
||||
text-align: start;
|
||||
vertical-align: middle;
|
||||
color: var(--ax-text-neutral-subtle);
|
||||
|
||||
> div {
|
||||
padding-block: var(--__axc-data-table-density);
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__td,
|
||||
.aksel-data-table__th {
|
||||
scroll-margin-block: var(--ax-space-0);
|
||||
scroll-margin-inline: var(--ax-space-0);
|
||||
@@ -428,15 +321,6 @@
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
&[data-selectable="true"] {
|
||||
padding: 0;
|
||||
|
||||
& > div {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__tfoot {
|
||||
@@ -449,35 +333,15 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__button {
|
||||
height: 1.5rem;
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
&:focus-visible,
|
||||
&[data-drag-handler-active="true"] {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -1px;
|
||||
border-radius: var(--ax-radius-8);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__drag-and-drop-item {
|
||||
background-color: var(--ax-bg-default);
|
||||
&[data-dragging="true"] {
|
||||
background: var(--ax-bg-default);
|
||||
}
|
||||
|
||||
&[data-overlay="true"] {
|
||||
width: 100%;
|
||||
&[data-mouse-dragging="true"]:not(:active) {
|
||||
opacity: 0.8;
|
||||
box-shadow: var(--ax-shadow-dialog);
|
||||
}
|
||||
|
||||
/* TODO - might be removable */
|
||||
&[data-keyboard-dragging="true"] {
|
||||
.aksel-data-drag-and-drop__drag-handler__button {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
@@ -486,110 +350,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
&[data-drop-target="true"]:not([data-overlay="true"]) {
|
||||
&[data-drop-target="true"] {
|
||||
background: var(--ax-bg-neutral-moderate);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__alt,
|
||||
.aksel-data-drag-and-drop__drag-handler {
|
||||
position: relative;
|
||||
.aksel-data-drag-and-drop__drag-handler__button {
|
||||
cursor: grab;
|
||||
height: 1.5rem;
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--ax-border-focus);
|
||||
outline-offset: -1px;
|
||||
border-radius: var(--ax-radius-8);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__arrow {
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__alt {
|
||||
position: relative;
|
||||
|
||||
.aksel-data-drag-and-drop__drag-handler__arrow {
|
||||
height: 1.8rem;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: var(--ax-bg-default);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
|
||||
&[data-direction="up"] {
|
||||
top: -18px;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
&[data-direction="down"] {
|
||||
bottom: calc(-100% - 22px);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.8;
|
||||
cursor: not-allowed;
|
||||
bottom: calc(-100% - 6px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-table__empty-state {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
padding-block: var(--ax-space-32);
|
||||
padding-inline: var(--ax-space-16);
|
||||
text-align: center;
|
||||
gap: var(--ax-space-8);
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.aksel-data-table__loading-state {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
padding-block: var(--ax-space-32);
|
||||
padding-inline: var(--ax-space-16);
|
||||
text-align: center;
|
||||
gap: var(--ax-space-8);
|
||||
justify-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes animateDataTableLoading {
|
||||
0% {
|
||||
inset-inline-start: 0;
|
||||
inline-size: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
inset-inline-start: 0;
|
||||
inline-size: 40%;
|
||||
}
|
||||
|
||||
80% {
|
||||
inset-inline-start: 60%;
|
||||
inline-size: 40%;
|
||||
}
|
||||
|
||||
100% {
|
||||
inset-inline-start: 100%;
|
||||
inline-size: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Look to handle "inline"-checkbox better than custom CSS-overrides in the future. */
|
||||
.aksel-data-table tr .aksel-checkbox {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.aksel-data-table tr .aksel-checkbox:focus-within::after {
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
/* TODO -
|
||||
* This is needed to prevent the cursor from changing back to default when dragging over interactive elements in the row.
|
||||
* Not sure if this is the best approach or breaks styling elsewhere */
|
||||
html[data-dragging-cursor="true"],
|
||||
html[data-dragging-cursor="true"] *,
|
||||
html[data-dragging-cursor="true"] *::before,
|
||||
html[data-dragging-cursor="true"] *::after {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
/* TODO: alignment not optimal when resizing column with nesting */
|
||||
.aksel-data-table__nested-toggle {
|
||||
position: absolute;
|
||||
inset-block: 0;
|
||||
left: calc(var(--__axc-data-table-pi-cell) + var(--__axc-data-table-nested-depth) * var(--ax-space-16));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -1,105 +1,36 @@
|
||||
/* .aksel-property-filter {
|
||||
flex: 1 1 fit-content;
|
||||
max-inline-size: 100%;
|
||||
} */
|
||||
|
||||
.aksel-property-filter__popup {
|
||||
overflow: hidden;
|
||||
border-radius: var(--ax-radius-8);
|
||||
border-radius: var(--ax-radius-12);
|
||||
border: 1px solid var(--ax-border-neutral-subtleA);
|
||||
box-shadow: var(--ax-shadow-dialog);
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
transform-origin: var(--__axc-floating-transform-origin);
|
||||
transition: opacity 150ms ease allow-discrete;
|
||||
transition:
|
||||
transform 150ms ease allow-discrete,
|
||||
opacity 150ms ease allow-discrete;
|
||||
|
||||
@starting-style {
|
||||
opacity: 0.6;
|
||||
opacity: 0.8;
|
||||
transform: scale3d(0.94, 0.94, 0.94);
|
||||
}
|
||||
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
.aksel-property-filter__popup-inner {
|
||||
--__axc-token-filter-content-p: var(--ax-space-8);
|
||||
--__axc-token-filter-item-pr: var(--ax-space-8);
|
||||
--__axc-token-filter-item-pl: var(--ax-space-8);
|
||||
--__axc-token-filter-item-height: 2rem;
|
||||
|
||||
border-radius: var(--ax-radius-8);
|
||||
border-radius: var(--ax-radius-12);
|
||||
background-color: var(--ax-bg-raised);
|
||||
min-width: 128px;
|
||||
max-width: min(95vw, 640px);
|
||||
animation-duration: 160ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
padding: var(--__axc-token-filter-content-p);
|
||||
overflow: auto;
|
||||
/* stylelint-disable-next-line csstools/value-no-unknown-custom-properties */
|
||||
max-height: calc(var(--__axc-floating-available-height) - 4px);
|
||||
}
|
||||
|
||||
.aksel-property-filter__input {
|
||||
max-inline-size: 640px;
|
||||
}
|
||||
|
||||
.aksel-property-filter__chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--ax-space-8);
|
||||
margin: 0;
|
||||
margin-block-start: var(--ax-space-12);
|
||||
padding: 0;
|
||||
font-size: var(--ax-font-size-medium);
|
||||
}
|
||||
|
||||
.aksel-property-filter__chip {
|
||||
display: flex;
|
||||
gap: var(--ax-space-1);
|
||||
}
|
||||
|
||||
.aksel-property-filter__chip-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ax-space-4);
|
||||
padding: var(--ax-space-4) var(--ax-space-8);
|
||||
background-color: var(--ax-bg-strong);
|
||||
color: var(--ax-text-contrast);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ax-bg-strong-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--ax-bg-strong-pressed);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 3px solid var(--ax-border-focus);
|
||||
outline-offset: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-start-start-radius: var(--ax-radius-8);
|
||||
border-end-start-radius: var(--ax-radius-8);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-start-end-radius: var(--ax-radius-8);
|
||||
border-end-end-radius: var(--ax-radius-8);
|
||||
}
|
||||
|
||||
&[data-type="operation"] {
|
||||
/* background-color: var(--ax-bg-default);
|
||||
box-shadow: inset 0 0 0 1px var(--ax-bg-strong);
|
||||
color: var(--ax-text-default); */
|
||||
color: var(--ax-text-neutral);
|
||||
background-color: var(--ax-bg-neutral-moderate-pressed);
|
||||
}
|
||||
|
||||
/* &[data-type="value"] {
|
||||
} */
|
||||
|
||||
&[data-type="remove"] {
|
||||
padding-inline: var(--ax-space-4);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,18 @@
|
||||
.aksel-data-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: var(--ax-space-8);
|
||||
}
|
||||
|
||||
.aksel-data-toolbar__actions {
|
||||
margin-inline-start: auto;
|
||||
padding: var(--ax-space-12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ax-space-8);
|
||||
border: 1px solid var(--ax-border-neutral);
|
||||
border-radius: var(--ax-radius-12);
|
||||
background-color: var(--ax-bg-raised);
|
||||
}
|
||||
|
||||
> :not(:last-child) {
|
||||
border-right: 1px solid var(--ax-border-neutral-subtle);
|
||||
margin-inline-end: var(--ax-space-4);
|
||||
padding-inline-end: var(--ax-space-4);
|
||||
.aksel-data-toolbar__toggle-button {
|
||||
&[aria-pressed="true"] {
|
||||
--__axc-button-border-width: 0px; /* ??? */
|
||||
|
||||
color: var(--ax-text-contrast);
|
||||
background-color: var(--ax-bg-strong-pressed);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-data-toolbar__input {
|
||||
flex: 1 1 fit-content;
|
||||
max-inline-size: 100%;
|
||||
}
|
||||
|
||||
.aksel-data-toolbar__pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ax-space-8);
|
||||
}
|
||||
@@ -4,11 +4,11 @@
|
||||
padding: var(--ax-space-16);
|
||||
|
||||
.rdp-table {
|
||||
border-collapse: collapse;
|
||||
border-collapse: separate; /* Some CSS-resets sets border-collapse:collapse on tables */
|
||||
}
|
||||
|
||||
.rdp-cell {
|
||||
padding: 0;
|
||||
padding: 1px; /* Some CSS-resets sets padding:0 on all items */
|
||||
|
||||
&[data-outside="true"] {
|
||||
width: var(--__axc-date-button-size);
|
||||
@@ -23,22 +23,18 @@
|
||||
}
|
||||
|
||||
.rdp-day_range_middle {
|
||||
&.rdp-day_disabled .rdp-day__inner {
|
||||
&.rdp-day_disabled {
|
||||
color: var(--ax-text-neutral);
|
||||
background: var(--ax-bg-neutral-moderateA);
|
||||
}
|
||||
|
||||
&.rdp-day_selected .rdp-day__inner {
|
||||
&.rdp-day_selected {
|
||||
background-color: var(--ax-bg-moderate-pressedA);
|
||||
box-shadow: inset 0 0 0 1px var(--ax-border-subtleA);
|
||||
color: var(--ax-text-neutral);
|
||||
}
|
||||
}
|
||||
|
||||
.rdp-button.rdp-day__range-hover-middle .rdp-day__inner {
|
||||
background-color: var(--ax-bg-moderate-hoverA);
|
||||
}
|
||||
|
||||
.rdp-month,
|
||||
&.rdp-month {
|
||||
display: grid;
|
||||
@@ -56,30 +52,22 @@
|
||||
|
||||
.rdp-button {
|
||||
all: unset;
|
||||
padding: var(--ax-space-2);
|
||||
cursor: pointer;
|
||||
|
||||
& .rdp-day__inner {
|
||||
width: var(--__axc-date-button-size);
|
||||
height: var(--__axc-date-button-size);
|
||||
}
|
||||
}
|
||||
|
||||
.rdp-day__inner {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
display: block;
|
||||
width: var(--__axc-date-button-size);
|
||||
height: var(--__axc-date-button-size);
|
||||
text-align: center;
|
||||
border-radius: var(--ax-radius-8);
|
||||
}
|
||||
|
||||
.rdp-day_range_start .rdp-day__inner {
|
||||
.rdp-day_range_start {
|
||||
border-radius: 100% var(--ax-radius-12) var(--ax-radius-12) 100%;
|
||||
}
|
||||
|
||||
.rdp-day_range_end:not(.rdp-day_range_start) .rdp-day__inner {
|
||||
.rdp-day_range_end:not(.rdp-day_range_start) {
|
||||
border-radius: var(--ax-radius-8) 100% 100% var(--ax-radius-8);
|
||||
}
|
||||
|
||||
.rdp-day_range_start.rdp-day_range_end .rdp-day__inner {
|
||||
.rdp-day_range_start.rdp-day_range_end {
|
||||
border-radius: var(--ax-radius-8);
|
||||
}
|
||||
|
||||
@@ -90,23 +78,24 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rdp-day_selected .rdp-day__inner {
|
||||
.rdp-day_selected,
|
||||
.aksel-monthpicker__month--selected {
|
||||
color: var(--ax-text-contrast);
|
||||
background: var(--ax-bg-strong-pressed);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rdp-day_disabled .rdp-day__inner {
|
||||
.rdp-day_disabled {
|
||||
cursor: not-allowed;
|
||||
text-decoration: line-through;
|
||||
background-color: var(--ax-bg-neutral-moderateA);
|
||||
color: var(--ax-text-neutral-subtle);
|
||||
opacity: var(--ax-opacity-disabled);
|
||||
}
|
||||
|
||||
.rdp-button:where(:not(.rdp-day_selected, [disabled])):hover,
|
||||
.aksel-date__month-button:where(:not(.rdp-day_selected, [disabled])):hover {
|
||||
.rdp-day__inner {
|
||||
background: var(--ax-bg-moderate-hoverA);
|
||||
}
|
||||
background: var(--ax-bg-moderate-hoverA);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rdp-day_today {
|
||||
@@ -165,7 +154,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--ax-space-4);
|
||||
padding-inline: var(--ax-space-2);
|
||||
padding-inline: var(--ax-space-4);
|
||||
}
|
||||
|
||||
.aksel-date__caption-button {
|
||||
@@ -178,17 +167,15 @@
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
/* MonthPicker */
|
||||
/* Monthpicker */
|
||||
.aksel-date__month-button {
|
||||
all: unset;
|
||||
padding: var(--ax-space-2);
|
||||
text-align: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
text-transform: capitalize;
|
||||
border-radius: var(--ax-radius-8);
|
||||
cursor: pointer;
|
||||
|
||||
& .rdp-day__inner {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-date__year-label {
|
||||
@@ -203,12 +190,12 @@
|
||||
|
||||
.rdp-cell > button.rdp-day,
|
||||
button.aksel-date__month-button {
|
||||
&:focus-visible .rdp-day__inner {
|
||||
&:focus-visible {
|
||||
outline: 3px solid var(--ax-border-focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) .rdp-day__inner {
|
||||
&:active:not(:disabled) {
|
||||
color: var(--ax-text-contrast);
|
||||
background-color: var(--ax-bg-strong-pressed);
|
||||
}
|
||||
@@ -216,41 +203,35 @@
|
||||
}
|
||||
|
||||
.aksel-date__field-input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
&.aksel-text-field__input {
|
||||
padding-right: var(--ax-space-48);
|
||||
}
|
||||
|
||||
.aksel-date__field--error &:not(:disabled) {
|
||||
box-shadow:
|
||||
inset -2px 0 0 0 var(--ax-border-danger-strong),
|
||||
inset 0 0 0 1px var(--ax-border-danger-strong);
|
||||
.aksel-form-field--small & {
|
||||
padding-right: var(--ax-space-32);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-date__field-button {
|
||||
--__axc-button-border-width: 1px;
|
||||
--__axc-button-border-color: var(--ax-border-neutral);
|
||||
|
||||
position: absolute;
|
||||
right: 0.0625rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--ax-text-neutral);
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: none;
|
||||
border-radius: calc(var(--ax-radius-8) - 1px);
|
||||
padding: var(--ax-space-12);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
height: calc(100% - 0.125rem);
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
position: relative;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 3px solid var(--ax-border-focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.aksel-date__field:not(.aksel-date__field--readonly):has(.aksel-date__field-input:not(:disabled)) &:hover,
|
||||
.aksel-date__field:not(.aksel-date__field--readonly, .aksel-date__field--error):has(
|
||||
.aksel-date__field-input:is(:focus-visible, :hover):not(:disabled)
|
||||
)
|
||||
& {
|
||||
--__axc-button-border-color: var(--ax-border-strong);
|
||||
}
|
||||
|
||||
.aksel-form-field--small & {
|
||||
padding: var(--ax-space-4);
|
||||
@@ -263,37 +244,19 @@
|
||||
|
||||
/* Readonly */
|
||||
.aksel-date__field--readonly & {
|
||||
--__axc-button-border-color: var(--ax-border-neutral-subtleA);
|
||||
|
||||
cursor: default;
|
||||
opacity: 1;
|
||||
background-color: var(--ax-bg-neutral-moderate);
|
||||
color: var(--ax-text-neutral-subtle);
|
||||
}
|
||||
|
||||
/* Syncs button-bg with input while respecting hover/active states */
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background-color: var(--ax-bg-input);
|
||||
z-index: -1;
|
||||
&:focus-visible {
|
||||
outline-color: var(--ax-border-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-date__field-wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
border-radius: var(--ax-radius-8);
|
||||
|
||||
/* Initializes stacking context to allow z-index: -1 on button to work. */
|
||||
z-index: 0;
|
||||
|
||||
&:has(.aksel-date__field-input:focus-visible) {
|
||||
outline: 3px solid var(--ax-border-focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.aksel-date__week-row {
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
transform: translateY(calc(1rem * var(--__axc-nested-level) * -1)) scale(calc(1 - 0.02 * var(--__axc-nested-level)));
|
||||
}
|
||||
|
||||
@media (max-width: 480px) or (max-height: 479px) {
|
||||
@media (max-width: 768px) or (max-height: 479px) {
|
||||
--__axc-dialog-transform: translateY(35%);
|
||||
|
||||
max-height: calc(100dvh - max(16px, 10dvh));
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
}
|
||||
|
||||
/* Applied when hideLegend is applied to fieldset */
|
||||
.aksel-fieldset > .aksel-sr-only + :not(:first-child, :empty),
|
||||
.aksel-fieldset > .aksel-typo--visually-hidden + :not(:first-child, :empty) {
|
||||
.aksel-fieldset > .aksel-sr-only + :not(:first-child, :empty) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 388 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user