mirror of
https://github.com/navikt/aksel.git
synced 2026-10-15 02:02:02 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99465ddbeb | ||
|
|
25305ebba0 | ||
|
|
ef3464c96c | ||
|
|
0480d785c4 | ||
|
|
ef5e671cfe | ||
|
|
fa156773e0 | ||
|
|
137b4c49c1 | ||
|
|
1fb72241fe | ||
|
|
42470baaaf |
No files matched your search
+14
-10
@@ -1,31 +1,35 @@
|
||||
# Changesets
|
||||
|
||||
For versjonering bruker vi løsningen [Changesets](https://github.com/changesets/changesets) for [versjonering](https://semver.org/) og publisering av pakker.
|
||||
Vi bruker [Changesets](https://github.com/changesets/changesets) for [versjonering](https://semver.org/) og publisering av pakker.
|
||||
|
||||
## Howto
|
||||
## Lage ny versjon
|
||||
|
||||
- For å lage en ny versjons-entry: `yarn changeset`
|
||||
- Velg pakkene som skal versjoneres med `arrowkeys` + `Space`
|
||||
1. `yarn changeset`
|
||||
2. Svar på spørsmålene som kommer opp:
|
||||
|
||||
`🦋 Which packages would you like to include?`
|
||||
|
||||
Trykk `Space` for å velge pakkene som skal versjoneres, for så trykke `Enter` for å gå videre.
|
||||
Trykk `Space` for å velge pakkene som skal versjoneres, trykk så `Enter` for å gå videre.
|
||||
|
||||
`🦋 Which packages should have a major bump?`
|
||||
|
||||
Trykk `Enter` for å gå videre _*eller*_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`
|
||||
Trykk `Enter` for å gå videre _eller_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`.
|
||||
|
||||
`🦋 Which packages should have a minor bump?`
|
||||
|
||||
Trykk `Enter` for å gå videre _*eller*_ velg pakkene som skal marjor-bumpes med `arrowkeys` + `Space`
|
||||
Trykk `Enter` for å gå videre _eller_ velg pakkene som skal minor-bumpes med `arrowkeys` + `Space`.
|
||||
|
||||
Hvis du ikke velger major eller minor, så blir pakkene få en patch-bump
|
||||
Hvis du ikke velger major eller minor, så vil pakkene få en patch-bump.
|
||||
|
||||
- Commit og push.
|
||||
`Please enter a summary for this change`
|
||||
|
||||
Skriv inn en kort beskrivelse av endringen på formatet `<Komponent>: <gitmoji?> <Beskrivelse>`, f.eks. "Button: ✨ Add feature xyz".
|
||||
|
||||
3. Commit og push.
|
||||
|
||||
## Fixed versjonering
|
||||
|
||||
Alle base-pakkene våre har `fixed versjonering`. Dette tilsier at en bump på en av disse vil bumpe alle til samme versjon
|
||||
Alle base-pakkene våre har `fixed versjonering`. Dette betyr at en bump på en av disse vil bumpe alle til samme versjon
|
||||
|
||||
```sh
|
||||
"@navikt/ds-react",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/aksel-icons
|
||||
|
||||
## 7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
## 7.8.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Aksel icons
|
||||
|
||||
800+ open source icons made by Aksel, NAVs designsystem-team
|
||||
800+ open source icons made by Aksel, Nav's design system team.
|
||||
|
||||
- Designed for 24x24px
|
||||
- Available in React and SVG
|
||||
@@ -30,7 +30,7 @@ Tip: Use the `title` prop for accessible icons.
|
||||
|
||||
### Sizing
|
||||
|
||||
Each icons `width` and `height` is default `1em`. This allows you to use the native `fontSize`-prop to adjust sizing. We recommend using at least 1.5rem/24px for best possible visual representation.
|
||||
Each icon's `width` and `height` is default `1em`. This allows you to use the native `fontSize`-prop to adjust sizing. We recommend using at least 1.5rem/24px for the best possible visual representation.
|
||||
|
||||
```jsx
|
||||
<StarIcon fontSize="1.5rem" />
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
# Aksel Icons plugin
|
||||
|
||||
Pluginen gir deg mulighet til å se gjennom og hente ut alle ikonene i fra ikonpakken `@navikt/aksel-icons`. Du slipper da å hente nye oppdateringer fra Figma Community.
|
||||
Pluginen gir deg mulighet til å se gjennom og hente ut alle ikonene fra ikonpakken `@navikt/aksel-icons`. Du slipper da å hente nye oppdateringer fra Figma Community.
|
||||
|
||||
Referer til [ikon-dokumentasjonen](https://aksel.nav.no/ikoner) for å se alle ikonene.
|
||||
Gå til [ikon-dokumentasjonen](https://aksel.nav.no/ikoner) for å se alle ikonene.
|
||||
Har du ønsker om nye ikoner, eller ønsker å bidra med å lage nye, lag et issue på [GitHub-repoet for designsystemet](https://github.com/navikt/aksel).
|
||||
|
||||
## Quickstart
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-icons",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/ikoner",
|
||||
"repository": {
|
||||
@@ -87,6 +87,6 @@
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "5.5.4",
|
||||
"unified": "10.1.2",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/aksel-stylelint
|
||||
|
||||
## 7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
## 7.8.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel-stylelint",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"author": "Aksel | Nav",
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/stylelint",
|
||||
"repository": {
|
||||
@@ -35,8 +35,8 @@
|
||||
"dev": "yarn watch:lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-css": "^7.9.0",
|
||||
"@navikt/ds-tokens": "^7.9.0",
|
||||
"@navikt/ds-css": "^7.9.1",
|
||||
"@navikt/ds-tokens": "^7.9.1",
|
||||
"concurrently": "9.0.1",
|
||||
"postcss-selector-parser": "^6.0.13",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @navikt/aksel
|
||||
|
||||
## 7.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`25305eb`](https://github.com/navikt/aksel/commit/25305ebba04a37e6315bc37bfabb39f04f83f146)]:
|
||||
- @navikt/ds-css@7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/aksel",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"description": "Aksel command line interface. Handles css-imports, codemods and more",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"homepage": "https://aksel.nav.no/grunnleggende/kode/kommandolinje",
|
||||
"dependencies": {
|
||||
"@navikt/ds-css": "7.9.0",
|
||||
"@navikt/ds-css": "7.9.1",
|
||||
"axios": "1.7.4",
|
||||
"chalk": "4.1.0",
|
||||
"clipboardy": "^2.3.0",
|
||||
@@ -46,7 +46,7 @@
|
||||
"devDependencies": {
|
||||
"rimraf": "6.0.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @navikt/ds-css
|
||||
|
||||
## 7.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Link: :bug: Use inside Alert got wrong text-color while focused or active. ([#3489](https://github.com/navikt/aksel/pull/3489))
|
||||
|
||||
## 7.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Aksel styling
|
||||
|
||||
CSS for NAVs designsystem.
|
||||
CSS for Nav's design system.
|
||||
|
||||
- Css tokens (Colors, border, shadows etc)
|
||||
- Component-styling for `@navikt/ds-react` packages
|
||||
- Css tokens (Colors, border, shadows etc.)
|
||||
- Component styling for `@navikt/ds-react` packages
|
||||
- Normalize.css v8
|
||||
- Font-loading
|
||||
- Font loading
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -16,7 +16,7 @@ CSS for NAVs designsystem.
|
||||
|
||||
## Usage
|
||||
|
||||
Import styles once to make it accessible for your entire project. Typically you will add the following line to your root .tsx file.
|
||||
Import styles once to make it accessible for your entire project. Typically you will add the following line to your root .tsx file:
|
||||
|
||||
```javascript
|
||||
import "@navikt/ds-css";
|
||||
@@ -26,7 +26,7 @@ function App() {
|
||||
}
|
||||
```
|
||||
|
||||
But you can import it in a .css-file
|
||||
But you can import it in a .css-file as well:
|
||||
|
||||
```css
|
||||
@import "@navikt/ds-css";
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.navds-alert:not(.navds-alert--inline) .navds-link,
|
||||
.navds-alert:not(.navds-alert--inline) .navds-link:not(:active, :focus-visible),
|
||||
.navds-confirmation-panel .navds-link {
|
||||
color: var(--a-text-default);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-css",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"description": "CSS for Nav Designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -28,7 +28,7 @@
|
||||
"test": "yarn build && vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^7.9.0",
|
||||
"@navikt/ds-tokens": "^7.9.1",
|
||||
"@types/clean-css": "4.2.11",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"browserslist": "^4.24.2",
|
||||
@@ -44,7 +44,7 @@
|
||||
"postcss-combine-duplicated-selectors": "10.0.3",
|
||||
"postcss-import": "^14.1.0",
|
||||
"tsx": "^4.7.1",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
scripts/*.backup
|
||||
@@ -1,5 +1,13 @@
|
||||
# @navikt/ds-react
|
||||
|
||||
## 7.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @navikt/ds-tokens@7.9.1
|
||||
- @navikt/aksel-icons@7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Aksel React-components
|
||||
|
||||
React-components for NAVs designsystem.
|
||||
React-components for Nav's design system.
|
||||
|
||||
- Uses styling defined in `@navikt/ds-css`
|
||||
- All components implemented with React.forwardRef!
|
||||
- All components are implemented with React.forwardRef!
|
||||
- Most components extend native html-elements.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-react",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"description": "React components from the Norwegian Labour and Welfare Administration.",
|
||||
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
|
||||
"license": "MIT",
|
||||
@@ -603,10 +603,11 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"docgen": "yarn tsx ../../../scripts/docgen.ts",
|
||||
"docgen": "tsx ../../../scripts/docgen.ts",
|
||||
"i18n-jsdoc": "tsx ./scripts/addJsdocToLocales.ts",
|
||||
"write-packagejson": "echo '{\"type\": \"module\"}' > esm/package.json",
|
||||
"clean": "rimraf cjs esm",
|
||||
"build": "concurrently \"tsc -p tsconfig.build.json\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" ",
|
||||
"build": "yarn i18n-jsdoc && concurrently \"tsc -p tsconfig.build.json\" \"tsc -p tsconfig.esm.json && tsc-alias -p tsconfig.esm.json && yarn write-packagejson\" && yarn i18n-jsdoc --cleanup",
|
||||
"watch": "tsc --watch -p tsconfig.esm.json",
|
||||
"test": "TZ=UTC vitest run -c tests/vitest.config.ts",
|
||||
"test:watch": "vitest watch"
|
||||
@@ -614,20 +615,23 @@
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.25.4",
|
||||
"@floating-ui/react-dom": "^2.0.9",
|
||||
"@navikt/aksel-icons": "^7.9.0",
|
||||
"@navikt/ds-tokens": "^7.9.0",
|
||||
"@navikt/aksel-icons": "^7.9.1",
|
||||
"@navikt/ds-tokens": "^7.9.1",
|
||||
"clsx": "^2.1.0",
|
||||
"date-fns": "^3.0.0",
|
||||
"react-day-picker": "8.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "9.3.4",
|
||||
"@testing-library/jest-dom": "^5.16.0",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@testing-library/dom": "10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jscodeshift": "^0.11.11",
|
||||
"aksel": "workspace:^",
|
||||
"concurrently": "9.0.1",
|
||||
"fast-glob": "3.2.11",
|
||||
"jsdom": "24.0.0",
|
||||
"jscodeshift": "^0.15.1",
|
||||
"jsdom": "25.0.1",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"rimraf": "6.0.1",
|
||||
@@ -635,7 +639,7 @@
|
||||
"tsc-alias": "1.8.8",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=17.0.30",
|
||||
@@ -645,5 +649,6 @@
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"_comment": "The 'aksel' dependency tells yarn to install the root deps when installing deps for this package. Helps make it possible to contribute without access to GPR."
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import fs from "fs";
|
||||
import jscodeshift from "jscodeshift";
|
||||
|
||||
const j = jscodeshift.withParser("ts");
|
||||
const file = "src/util/i18n/locales/nb.ts";
|
||||
const backup = "scripts/nb.backup";
|
||||
|
||||
function addJsdoc() {
|
||||
if (fs.existsSync(backup)) {
|
||||
// If last build failed, we need to restore the file so that we don't create duplicate JSDoc
|
||||
console.info(
|
||||
`WARNING: Restoring backup of ${file} - Uncommitted changes will be lost!`,
|
||||
);
|
||||
fs.copyFileSync(backup, file);
|
||||
}
|
||||
|
||||
fs.copyFileSync(file, backup); // Create a copy of the original file that we can restore afterwards
|
||||
|
||||
const code = fs.readFileSync(file, "utf-8");
|
||||
const parsedCode = j(code);
|
||||
const defaultExport = parsedCode.find(j.ExportDefaultDeclaration);
|
||||
const keys = defaultExport.find(j.ObjectProperty);
|
||||
keys
|
||||
.filter((key) => key.value.value.type === "StringLiteral")
|
||||
.forEach((key) => {
|
||||
// @ts-expect-error It works, doesn't it?
|
||||
const value = key.value.value.value;
|
||||
const foundCommentBlock = key.value.comments?.find(
|
||||
(comment) => comment.type === "CommentBlock",
|
||||
);
|
||||
if (foundCommentBlock) {
|
||||
foundCommentBlock.value = `${foundCommentBlock.value}\n* @default "${value}" `;
|
||||
} else {
|
||||
key.value.comments = [
|
||||
...(key.value.comments || []),
|
||||
{
|
||||
type: "CommentBlock",
|
||||
value: `* @default "${value}" `,
|
||||
leading: true,
|
||||
trailing: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
const modifiedCode = parsedCode.toSource();
|
||||
fs.writeFileSync(file, modifiedCode);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
fs.copyFileSync(backup, file);
|
||||
fs.unlinkSync(backup);
|
||||
}
|
||||
|
||||
if (process.argv.includes("--cleanup")) {
|
||||
cleanup();
|
||||
} else {
|
||||
addJsdoc();
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@navikt/aksel-icons";
|
||||
import { Button } from "../button";
|
||||
import { BodyLong } from "../typography";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { XMarkIcon } from "@navikt/aksel-icons";
|
||||
import { composeEventHandlers } from "../util/composeEventHandlers";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export interface ChipsRemovableProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons";
|
||||
import { Label } from "../typography";
|
||||
import { composeEventHandlers } from "../util/composeEventHandlers";
|
||||
import copy from "../util/copy";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export interface CopyButtonProps
|
||||
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createContext } from "../../util/create-context";
|
||||
import { TFunction } from "../../util/i18n/i18n.context";
|
||||
import { TFunction } from "../../util/i18n/i18n.types";
|
||||
|
||||
interface DateTranslationContextProps {
|
||||
translate: TFunction<"DatePicker">;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DateRange, DayPicker, isMatch } from "react-day-picker";
|
||||
import { omit } from "../../util";
|
||||
import { useId } from "../../util/hooks";
|
||||
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { DateInputContext, DateTranslationContextProvider } from "../context";
|
||||
import { DatePickerInput } from "../parts/DateInput";
|
||||
import { DateWrapper } from "../parts/DateWrapper";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isWeekend } from "date-fns";
|
||||
import React, { forwardRef } from "react";
|
||||
import { DateRange, DayPicker, isMatch } from "react-day-picker";
|
||||
import { omit } from "../../util";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { DateTranslationContextProvider } from "../context";
|
||||
import { getLocaleFromString, getTranslations } from "../utils";
|
||||
import Caption from "./parts/Caption";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { describe, test } from "vitest";
|
||||
@@ -23,9 +23,6 @@ describe("Render datepicker", () => {
|
||||
test("Should not crash when e.target is window", async () => {
|
||||
render(<App />);
|
||||
|
||||
// eslint-disable-next-line testing-library/no-unnecessary-act -- https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning
|
||||
await act(async () => {
|
||||
await userEvent.click(screen.getByText("Velg dato"));
|
||||
});
|
||||
await userEvent.click(screen.getByText("Velg dato"));
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { differenceInCalendarDays, isWeekend } from "date-fns";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { DayClickEventHandler, isMatch } from "react-day-picker";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.hooks";
|
||||
import { DatePickerProps } from "../datepicker/DatePicker";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.hooks";
|
||||
import { MonthPickerProps } from "../monthpicker/types";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable testing-library/no-unnecessary-act -- https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning */
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { describe, expect, test } from "vitest";
|
||||
@@ -29,10 +28,8 @@ describe("Writing in input sets correct values", () => {
|
||||
|
||||
const fraInput = screen.getByRole("textbox", { name: "Fra" });
|
||||
const tilInput = screen.getByRole("textbox", { name: "Til" });
|
||||
await act(async () => {
|
||||
await userEvent.type(fraInput, "03.08.2022");
|
||||
await userEvent.type(tilInput, "03.08.2022");
|
||||
});
|
||||
await userEvent.type(fraInput, "03.08.2022");
|
||||
await userEvent.type(tilInput, "03.08.2022");
|
||||
const res = screen.getByTitle("res");
|
||||
expect(res.innerHTML).toEqual(
|
||||
JSON.stringify({
|
||||
@@ -47,10 +44,8 @@ describe("Writing in input sets correct values", () => {
|
||||
|
||||
const fraInput = screen.getByRole("textbox", { name: "Fra" });
|
||||
const tilInput = screen.getByRole("textbox", { name: "Til" });
|
||||
await act(async () => {
|
||||
await userEvent.type(fraInput, "03.08.2022");
|
||||
await userEvent.type(tilInput, "02.08.2022");
|
||||
});
|
||||
await userEvent.type(fraInput, "03.08.2022");
|
||||
await userEvent.type(tilInput, "02.08.2022");
|
||||
const res = screen.getByTitle("res");
|
||||
expect(res.innerHTML).toEqual(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from "date-fns";
|
||||
import React, { useState } from "react";
|
||||
import { DateRange, isMatch } from "react-day-picker";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale } from "../../util/i18n/i18n.hooks";
|
||||
import { DatePickerProps } from "../datepicker/DatePicker";
|
||||
import { DateInputProps } from "../parts/DateInput";
|
||||
import {
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { forwardRef, useState } from "react";
|
||||
import { DayPickerProvider } from "react-day-picker";
|
||||
import { useId } from "../../util/hooks";
|
||||
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import {
|
||||
DateInputContext,
|
||||
DateTranslationContextProvider,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useState } from "react";
|
||||
import { DayPickerProvider } from "react-day-picker";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useDateLocale, useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import {
|
||||
DateTranslationContextProvider,
|
||||
SharedMonthProvider,
|
||||
|
||||
@@ -5,7 +5,8 @@ import { Modal } from "../../modal";
|
||||
import { useModalContext } from "../../modal/Modal.context";
|
||||
import { Popover } from "../../popover";
|
||||
import { useMedia } from "../../util/hooks";
|
||||
import { TFunction, useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { TFunction } from "../../util/i18n/i18n.types";
|
||||
import { getGlobalTranslations } from "../utils";
|
||||
|
||||
const variantToLabel = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef, useContext } from "react";
|
||||
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { ExpansionCardContext } from "./context";
|
||||
|
||||
export interface ExpansionCardHeaderProps
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { PadlockLockedFillIcon } from "@navikt/aksel-icons";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export const ReadOnlyIcon = () => (
|
||||
<PadlockLockedFillIcon
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React from "react";
|
||||
import { PlusIcon } from "@navikt/aksel-icons";
|
||||
import { BodyShort, Label } from "../../../typography";
|
||||
import { useI18n } from "../../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../../util/i18n/i18n.hooks";
|
||||
import { useInputContext } from "../Input/Input.context";
|
||||
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
||||
import { isInList, toComboboxOption } from "../combobox-utils";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { Loader } from "../../../loader";
|
||||
import { useI18n } from "../../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../../util/i18n/i18n.hooks";
|
||||
import { useInputContext } from "../Input/Input.context";
|
||||
import filteredOptionsUtil from "./filtered-options-util";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useI18n } from "../../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../../util/i18n/i18n.hooks";
|
||||
import { useInputContext } from "../Input/Input.context";
|
||||
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
||||
import filteredOptionsUtil from "./filtered-options-util";
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { HTMLAttributes, forwardRef, useRef } from "react";
|
||||
import { BodyShort, Heading } from "../../typography";
|
||||
import { composeEventHandlers } from "../../util/composeEventHandlers";
|
||||
import { useMergeRefs } from "../../util/hooks";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import ErrorSummaryItem from "./ErrorSummaryItem";
|
||||
|
||||
export interface ErrorSummaryProps
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from "../../../../button";
|
||||
import { BodyShort, ErrorMessage, Label } from "../../../../typography";
|
||||
import { composeEventHandlers } from "../../../../util/composeEventHandlers";
|
||||
import { useId } from "../../../../util/hooks";
|
||||
import { useI18n } from "../../../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../../../util/i18n/i18n.hooks";
|
||||
import { omit } from "../../../../util/omit";
|
||||
import { useFormField } from "../../../useFormField";
|
||||
import { useFileUploadTranslation } from "../../FileUpload.context";
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { MouseEvent, forwardRef } from "react";
|
||||
import { ExclamationmarkTriangleIcon } from "@navikt/aksel-icons";
|
||||
import { BodyShort } from "../../../../typography";
|
||||
import { OverridableComponent } from "../../../../util";
|
||||
import { useI18n } from "../../../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../../../util/i18n/i18n.hooks";
|
||||
import { ComponentTranslation } from "../../../../util/i18n/i18n.types";
|
||||
import { useFileUploadTranslation } from "../../FileUpload.context";
|
||||
import { FileItem } from "./Item.types";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { HStack } from "../../layout/stack";
|
||||
import { ProgressBar } from "../../progress-bar";
|
||||
import { Stepper, StepperStepProps } from "../../stepper";
|
||||
import { BodyShort } from "../../typography";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { ComponentTranslation } from "../../util/i18n/i18n.types";
|
||||
|
||||
export interface FormProgressProps
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { Link } from "../../link";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { OverridableComponent } from "../../util/types";
|
||||
|
||||
export interface FormSummaryEditProps
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useThemeInternal } from "../../theme/Theme";
|
||||
import { BodyShort, ErrorMessage, Label } from "../../typography";
|
||||
import { omit } from "../../util";
|
||||
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { FormFieldProps, useFormField } from "../useFormField";
|
||||
import SearchButton, { SearchButtonType } from "./SearchButton";
|
||||
import { SearchContext } from "./context";
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { forwardRef, useContext } from "react";
|
||||
import { MagnifyingGlassIcon } from "@navikt/aksel-icons";
|
||||
import { Button, ButtonProps } from "../../button";
|
||||
import { composeEventHandlers } from "../../util/composeEventHandlers";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { SearchContext } from "./context";
|
||||
|
||||
export interface SearchButtonProps
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BodyShort } from "../../typography";
|
||||
import debounce from "../../util/debounce";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import type { TextareaProps } from "./Textarea";
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export const DarksideGudiepanelIllustration = () => {
|
||||
const translate = useI18n("GuidePanel");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export const DefaultIllustration = () => {
|
||||
const translate = useI18n("GuidePanel");
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Popover, PopoverProps } from "../popover";
|
||||
import { useThemeInternal } from "../theme/Theme";
|
||||
import { composeEventHandlers } from "../util/composeEventHandlers";
|
||||
import { useMergeRefs } from "../util/hooks/useMergeRefs";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { HelpTextIcon } from "./HelpTextIcon";
|
||||
|
||||
export interface HelpTextProps
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React, { SVGProps, forwardRef } from "react";
|
||||
import { omit } from "../util";
|
||||
import { useId } from "../util/hooks";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
export interface LoaderProps extends Omit<SVGProps<SVGSVGElement>, "ref"> {
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import React, { forwardRef } from "react";
|
||||
import { XMarkIcon } from "@navikt/aksel-icons";
|
||||
import { Button } from "../button";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { useModalContext } from "./Modal.context";
|
||||
|
||||
export interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { forwardRef } from "react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@navikt/aksel-icons";
|
||||
import { BodyShort, Heading } from "../typography";
|
||||
import { useId } from "../util";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import PaginationItem, {
|
||||
PaginationItemProps,
|
||||
PaginationItemType,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React, { HTMLAttributes, forwardRef, useEffect, useRef } from "react";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
|
||||
interface ProgressBarPropsBase
|
||||
extends Omit<HTMLAttributes<HTMLDivElement>, "role"> {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ChevronDownIcon } from "@navikt/aksel-icons";
|
||||
import { composeEventHandlers } from "../util/composeEventHandlers";
|
||||
import { useId } from "../util/hooks";
|
||||
import { useControllableState } from "../util/hooks/useControllableState";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import AnimateHeight from "./AnimateHeight";
|
||||
import DataCell from "./DataCell";
|
||||
import Row, { RowProps } from "./Row";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { describe, expect, test } from "vitest";
|
||||
@@ -154,19 +154,11 @@ describe("Tabs", () => {
|
||||
expect(screen.getByTestId("tab2")).toHaveFocus();
|
||||
|
||||
/* Move focus to tabPanel */
|
||||
// eslint-disable-next-line testing-library/no-unnecessary-act
|
||||
await act(async () => {
|
||||
/* Tablist handles tabbing with setTimeout, so we need to use act */
|
||||
await userEvent.tab();
|
||||
});
|
||||
|
||||
await userEvent.tab();
|
||||
expect(screen.getByTestId("tabpanel1")).toHaveFocus();
|
||||
/* Move focus back to tablist, now tab1 should have focus */
|
||||
|
||||
// eslint-disable-next-line testing-library/no-unnecessary-act
|
||||
await act(async () => {
|
||||
await userEvent.tab({ shift: true });
|
||||
});
|
||||
/* Move focus back to tablist, now tab1 should have focus */
|
||||
await userEvent.tab({ shift: true });
|
||||
expect(screen.getByTestId("tab1")).toHaveFocus();
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
} from "date-fns";
|
||||
import React from "react";
|
||||
import { Detail } from "../typography/Detail";
|
||||
import { TFunction, useDateLocale, useI18n } from "../util/i18n/i18n.context";
|
||||
import { useDateLocale, useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { TFunction } from "../util/i18n/i18n.types";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
import { isVisible } from "./utils";
|
||||
import { horizontalPositionAndWidth } from "./utils/calc";
|
||||
|
||||
@@ -17,7 +17,7 @@ import { format } from "date-fns";
|
||||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import { useThemeInternal } from "../theme/Theme";
|
||||
import { useMergeRefs } from "../util/hooks/useMergeRefs";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
import { position } from "./utils/calc";
|
||||
import { TimelineComponentTypes } from "./utils/types.internal";
|
||||
|
||||
@@ -2,7 +2,7 @@ import cl from "clsx";
|
||||
import { format } from "date-fns";
|
||||
import React, { forwardRef } from "react";
|
||||
import { BodyShort } from "../typography/BodyShort";
|
||||
import { useI18n } from "../util/i18n/i18n.context";
|
||||
import { useI18n } from "../util/i18n/i18n.hooks";
|
||||
import { PeriodContext } from "./hooks/usePeriodContext";
|
||||
import { useRowContext } from "./hooks/useRowContext";
|
||||
import { useTimelineContext } from "./hooks/useTimelineContext";
|
||||
|
||||
@@ -17,7 +17,7 @@ import cl from "clsx";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useThemeInternal } from "../../theme/Theme";
|
||||
import { useMergeRefs } from "../../util/hooks/useMergeRefs";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { usePeriodContext } from "../hooks/usePeriodContext";
|
||||
import { useRowContext } from "../hooks/useRowContext";
|
||||
import { useTimelineContext } from "../hooks/useTimelineContext";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import React from "react";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { ariaLabel, getConditionalClasses } from "../utils/period";
|
||||
import type { PeriodProps } from "./types";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import cl from "clsx";
|
||||
import { format } from "date-fns";
|
||||
import type { TFunction } from "../../util/i18n/i18n.context";
|
||||
import type { TFunction } from "../../util/i18n/i18n.types";
|
||||
import type { PeriodProps } from "../period/types";
|
||||
|
||||
export const getConditionalClasses = (
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "date-fns";
|
||||
import React, { forwardRef } from "react";
|
||||
import { Detail } from "../../typography/Detail";
|
||||
import { useI18n } from "../../util/i18n/i18n.context";
|
||||
import { useI18n } from "../../util/i18n/i18n.hooks";
|
||||
import { useTimelineContext } from "../hooks/useTimelineContext";
|
||||
|
||||
export interface TimelineZoomButtonProps {
|
||||
|
||||
+62
-3
@@ -2,9 +2,8 @@ import { renderHook } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { Provider } from "../../provider";
|
||||
import { useI18n } from "./i18n.context";
|
||||
import en from "./locales/en";
|
||||
import nb from "./locales/nb";
|
||||
import { useDateLocale, useI18n } from "./i18n.hooks";
|
||||
import { en, nb, nn } from "./locales";
|
||||
|
||||
describe("useI18n", () => {
|
||||
test("should throw error if key is not found", () => {
|
||||
@@ -112,3 +111,63 @@ describe("useI18n", () => {
|
||||
expect(() => translate("item.uploading", { other: "John" })).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe("useDateLocale", () => {
|
||||
test("should return the default NB date locale when Provider is not used", () => {
|
||||
const { result } = renderHook(() => useDateLocale());
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(nb.global.dateLocale);
|
||||
});
|
||||
|
||||
test("should return the default NB date locale when Provider is used without the locale prop", () => {
|
||||
const { result } = renderHook(() => useDateLocale(), {
|
||||
wrapper: ({ children }) => <Provider>{children}</Provider>,
|
||||
});
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(nb.global.dateLocale);
|
||||
});
|
||||
|
||||
test("should return date locale from context.locale when using Provider with just locale prop", () => {
|
||||
const { result } = renderHook(() => useDateLocale(), {
|
||||
wrapper: ({ children }) => <Provider locale={en}>{children}</Provider>,
|
||||
});
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(en.global.dateLocale);
|
||||
});
|
||||
|
||||
test("should return date locale from context.locale when it does not exist in translations", () => {
|
||||
const { result } = renderHook(() => useDateLocale(), {
|
||||
wrapper: ({ children }) => (
|
||||
<Provider locale={en} translations={{ global: {} }}>
|
||||
{children}
|
||||
</Provider>
|
||||
),
|
||||
});
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(en.global.dateLocale);
|
||||
});
|
||||
|
||||
test("should return date locale from context.translations", () => {
|
||||
const { result } = renderHook(() => useDateLocale(), {
|
||||
wrapper: ({ children }) => (
|
||||
<Provider locale={en} translations={nn}>
|
||||
{children}
|
||||
</Provider>
|
||||
),
|
||||
});
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(nn.global.dateLocale);
|
||||
});
|
||||
|
||||
test("should return date locale from second object in context.translations", () => {
|
||||
const { result } = renderHook(() => useDateLocale(), {
|
||||
wrapper: ({ children }) => (
|
||||
<Provider locale={nb} translations={[{}, nn, en]}>
|
||||
{children}
|
||||
</Provider>
|
||||
),
|
||||
});
|
||||
const dateLocale = result.current;
|
||||
expect(dateLocale).toBe(nn.global.dateLocale);
|
||||
});
|
||||
});
|
||||
+3
-19
@@ -5,26 +5,12 @@ import {
|
||||
Component,
|
||||
ComponentTranslation,
|
||||
PartialTranslations,
|
||||
Translations,
|
||||
TFunction,
|
||||
} from "./i18n.types";
|
||||
|
||||
/**
|
||||
* https://regex101.com/r/LYKWi3/1
|
||||
*/
|
||||
/* https://regex101.com/r/LYKWi3/1 */
|
||||
const REPLACE_REGEX = /{[^}]*}/g;
|
||||
|
||||
/* https://dev.to/pffigueiredo/typescript-utility-keyof-nested-object-2pa3 */
|
||||
type NestedKeyOf<ObjectType extends object> = {
|
||||
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
|
||||
? `${Key}.${NestedKeyOf<ObjectType[Key]>}`
|
||||
: `${Key}`;
|
||||
}[keyof ObjectType & (string | number)];
|
||||
|
||||
export type TFunction<T extends Component> = (
|
||||
keypath: NestedKeyOf<Translations[T]>,
|
||||
replacements?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
export function useI18n<T extends Component>(
|
||||
componentName: T,
|
||||
...localTranslations: (ComponentTranslation<T> | undefined)[]
|
||||
@@ -39,9 +25,7 @@ export function useI18n<T extends Component>(
|
||||
context.locale[componentName],
|
||||
];
|
||||
|
||||
/**
|
||||
* https://github.com/Shopify/polaris/blob/2115f9ba2f5bcbf2ad15745233501bff2db81ecf/polaris-react/src/utilities/i18n/I18n.ts#L24
|
||||
*/
|
||||
/* https://github.com/Shopify/polaris/blob/2115f9ba2f5bcbf2ad15745233501bff2db81ecf/polaris-react/src/utilities/i18n/I18n.ts#L24 */
|
||||
const translate: TFunction<T> = (keypath, replacements) => {
|
||||
const text = get(keypath, i18nObjects);
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import type nb from "./locales/nb";
|
||||
|
||||
/* https://dev.to/pffigueiredo/typescript-utility-keyof-nested-object-2pa3 */
|
||||
type NestedKeyOf<ObjectType extends object> = {
|
||||
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
|
||||
? `${Key}.${NestedKeyOf<ObjectType[Key]>}`
|
||||
: `${Key}`;
|
||||
}[keyof ObjectType & (string | number)];
|
||||
|
||||
export type TFunction<T extends Component> = (
|
||||
keypath: NestedKeyOf<Translations[T]>,
|
||||
replacements?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
/* https://stackoverflow.com/questions/47914536/use-partial-in-nested-property-with-typescripts */
|
||||
type RecursivePartial<T> = {
|
||||
[P in keyof T]?: RecursivePartial<T[P]>;
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
/**
|
||||
* Setup-file is required to avoid "chai" type errors when using testing-library with vitest
|
||||
*/
|
||||
import matchers from "@testing-library/jest-dom/matchers";
|
||||
import { expect } from "vitest";
|
||||
|
||||
expect.extend(matchers);
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
@@ -7,5 +7,11 @@
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["./src"]
|
||||
"include": ["./src"],
|
||||
"exclude": [
|
||||
"./src/**/*.test.ts",
|
||||
"./src/**/*.test.tsx",
|
||||
"./src/**/*.test-d.ts",
|
||||
"./src/**/*.stories.tsx"
|
||||
]
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"resolveJsonModule": true
|
||||
"resolveJsonModule": true,
|
||||
"types": ["vitest/globals"]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/ds-tailwind
|
||||
|
||||
## 7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
## 7.8.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tailwind",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"description": "Tailwind config based on @navikt/ds-tokens",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -24,13 +24,13 @@
|
||||
"directory": "@navikt/core/tailwind"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@navikt/ds-tokens": "^7.9.0",
|
||||
"@navikt/ds-tokens": "^7.9.1",
|
||||
"color": "4.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @navikt/ds-tokens
|
||||
|
||||
## 7.9.1
|
||||
|
||||
## 7.9.0
|
||||
|
||||
## 7.8.1
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Aksel tokens
|
||||
|
||||
Design-tokens for NAVs designsystem and used when implementing @navikt/ds-css.
|
||||
Design-tokens for Nav's design system.
|
||||
|
||||
Node: This package is consumed by `@navikt/ds-css`, so not needed separately for regular use of css-variables.
|
||||
Node: This package is consumed by `@navikt/ds-css`, so not needed separately for regular use of CSS-variables.
|
||||
|
||||
Formats
|
||||
Formats:
|
||||
|
||||
- CSS-variables <- Used in `@navikt/ds-css`
|
||||
- Commonjs
|
||||
- CommonJS
|
||||
- ESM
|
||||
- Less
|
||||
- Scss
|
||||
- Sass
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -23,7 +23,7 @@ Formats
|
||||
|
||||
```css
|
||||
color: var(--a-text-default);
|
||||
backgroundcolor: var(--a-surface-default);
|
||||
background-color: var(--a-surface-default);
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navikt/ds-tokens",
|
||||
"version": "7.9.0",
|
||||
"version": "7.9.1",
|
||||
"description": "Design-tokens for Nav designsystem",
|
||||
"author": "Aksel | Nav designsystem team",
|
||||
"keywords": [
|
||||
@@ -40,7 +40,7 @@
|
||||
"style-dictionary": "^4.1.1",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
+570
-568
File diff suppressed because it is too large.
Load diff
+18
-10
@@ -2,7 +2,7 @@
|
||||
|
||||
## TL;DR
|
||||
|
||||
Vi bruker yarn for pakkeversjonering. Har du ikke yarn? `npm i -g yarn`
|
||||
Vi bruker yarn for pakkeversjonering. Har du ikke yarn? `corepack enable`
|
||||
|
||||
```sh
|
||||
git clone git@github.com:navikt/aksel.git
|
||||
@@ -17,24 +17,32 @@ yarn storybook
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Storybook: [http://localhost:6006](http://localhost:6006)
|
||||
Storybook: http://localhost:6006
|
||||
|
||||
Nettside: [http://localhost:3000](http://localhost:3000)
|
||||
Nettside: http://localhost:3000
|
||||
|
||||
### Ikke Nav-ansatt?
|
||||
|
||||
Nettsiden avhenger av pakker som ikke er åpent tilgjengelig. Du kan likevel bidra på andre deler av repoet ved å kjøre denne kommandoen i stedet for `yarn install`:
|
||||
|
||||
```sh
|
||||
yarn workspaces focus @navikt/aksel-icons @navikt/ds-tokens @navikt/ds-css @navikt/ds-react @navikt/ds-tailwind @navikt/aksel @navikt/aksel-stylelint
|
||||
```
|
||||
|
||||
## Utviklingsmiljø
|
||||
|
||||
Vi bruker storybook [https://storybook.js.org/](https://storybook.js.org/) for utvikling av nye komponenter.
|
||||
Lag `stories` for komponentene man vil teste [(hvordan skrive storybook stories)](https://storybook.js.org/docs/react/writing-stories/introduction)
|
||||
Vi bruker [Storybook](https://storybook.js.org/) for utvikling av nye komponenter.
|
||||
Lag [stories](https://storybook.js.org/docs/writing-stories) for komponentene du vil teste.
|
||||
|
||||
## Utvikling av nye komponenter
|
||||
|
||||
Det er ikke satt opp en template for nye komponenter, men vi følger en mal.
|
||||
Det er ikke satt opp noen template for nye komponenter, men vi følger en mal.
|
||||
|
||||
```sh
|
||||
@navikt/pakkenavn
|
||||
└─ src # Komponenter
|
||||
├─ index.ts # Alle exports
|
||||
└─ pakkenavn
|
||||
└─ komponent
|
||||
├─ index.ts # Alle exports fra komponent
|
||||
├─ komponent.tsx # Komponentkode
|
||||
└─ komponent.stories.tsx # Stories
|
||||
@@ -42,7 +50,7 @@ Det er ikke satt opp en template for nye komponenter, men vi følger en mal.
|
||||
|
||||
## Dokumentasjon
|
||||
|
||||
Dokumentasjon for komponenter finner man på [aksel.nav.no](https://aksel.nav.no/). Hvis man ønsker å skrive noe dokumentasjon selv gjør vi det via CMS et Sanity. Kode-eksempler blir skrevet lokalt i [/pages/eksempler](https://github.com/navikt/aksel/tree/main/aksel.nav.no/website/pages/eksempler)
|
||||
Dokumentasjon for komponenter finner man på [aksel.nav.no](https://aksel.nav.no/). Hvis man ønsker å skrive noe dokumentasjon selv gjør vi det via CMS-et Sanity. Kode-eksempler blir skrevet lokalt i [/pages/eksempler](https://github.com/navikt/aksel/tree/main/aksel.nav.no/website/pages/eksempler)
|
||||
|
||||
## Versjonering av pakker
|
||||
|
||||
@@ -50,10 +58,10 @@ Vi bruker `changeset` for versjonering av pakker. Se [README](https://github.com
|
||||
|
||||
## Kodekvalitet og testing
|
||||
|
||||
Komponenter og løsninger blir testet med jest + react-testing-library.
|
||||
Komponenter og løsninger blir testet med vitest + react-testing-library.
|
||||
|
||||
- Commit: Kjører prettier på endrede filer + linter
|
||||
- Ved PR: Kjører tester, linter og visuell regresjonstester
|
||||
- Ved PR: Kjører tester, linter og visuelle regresjonstester
|
||||
|
||||
## Oppdatere Aksel.nav.no
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
<h1 align="center">
|
||||
<img src="/aksel.nav.no/website/public/favicon.svg" style="width:13rem"/>
|
||||
<img src="/aksel.nav.no/website/public/favicon.svg" style="width:13rem" />
|
||||
<br/><br/>Aksel
|
||||
</h1>
|
||||
|
||||
<div align="center">
|
||||
<p>Nav's design system + Aksel.nav.no</p>
|
||||
<p>
|
||||
Navs designsystem + Aksel.nav.no
|
||||
<a href="https://aksel.nav.no/storybook/"><img alt="Storybook" src="https://shields.io/badge/storybook-white?logo=storybook&style=flat" /></a>
|
||||
<a href="https://github.com/navikt/aksel/pulls"><img alt="PRs" src="https://img.shields.io/badge/PRs-welcome-green.svg?color=%23262626" /></a>
|
||||
<a href="https://www.npmjs.com/package/@navikt/ds-css"><img alt="npm" src="https://img.shields.io/npm/v/@navikt/ds-css?label=version" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://aksel.nav.no">Nettside</a> | <a href="https://github.com/navikt/aksel/issues">Issues</a>
|
||||
</p>
|
||||
<div align="center">
|
||||
<a href="https://aksel.nav.no/storybook/">
|
||||
<img src="https://shields.io/badge/storybook-white?logo=storybook&style=flat"" />
|
||||
</a>
|
||||
<a href="https://github.com/navikt/aksel/pulls">
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-green.svg?color=%23262626"" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@navikt/ds-css"><img alt="npm" src="https://img.shields.io/npm/v/@navikt/ds-css?label=version"></a>
|
||||
</div>
|
||||
<br/>
|
||||
<a href="https://aksel.nav.no">Nettside</a> | <a href="https://github.com/navikt/aksel/issues">Issues</a>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
## Innhold
|
||||
@@ -34,15 +28,14 @@
|
||||
└── aksel # @navikt/aksel, kommandolinje-verktøy
|
||||
|
||||
aksel.nav.no/
|
||||
├── website # NEXT.JS app (v13)
|
||||
├── website/sanity # Sanity CMS-app (v3)
|
||||
└── playroom # aksel.nav.no/sandbox
|
||||
|
||||
├── website # Next.js-app
|
||||
├── website/sanity # Sanity CMS-app
|
||||
└── playroom # aksel.nav.no/sandbox
|
||||
```
|
||||
|
||||
## Commands
|
||||
## Kommandoer
|
||||
|
||||
Installer dependencies og bygg workspace
|
||||
Installer dependencies og bygg workspace:
|
||||
|
||||
```sh
|
||||
yarn && yarn boot
|
||||
@@ -50,7 +43,7 @@ yarn && yarn boot
|
||||
|
||||
### Tilgang til Github Package Registry
|
||||
|
||||
Siden vi bruker avhengigheter som ligger i GPR, så må man sette opp tilgang til GPR med en PAT (personal access token) som har `read:packages`. Du kan [opprette PAT her](https://github.com/settings/tokens).
|
||||
Siden vi bruker avhengigheter som ligger i GPR, så må man sette opp tilgang til GPR med en PAT (personal access token) som har `read:packages` og er autorisert for organisasjonen "navikt". Du kan [opprette PAT her](https://github.com/settings/tokens). (Ikke Nav-ansatt? Se [CONTRIBUTING.md](https://github.com/navikt/aksel/blob/main/CONTRIBUTING.md))
|
||||
|
||||
I din `.bashrc` eller `.zshrc`, sett følgende miljøvariabel:
|
||||
|
||||
@@ -64,14 +57,14 @@ I din `.bashrc` eller `.zshrc`, sett følgende miljøvariabel:
|
||||
| `yarn storybook` | Starter storybook |
|
||||
| `yarn dev` | Starter aksel-nettsiden [(Mer info)](https://github.com/navikt/aksel/blob/main/aksel.nav.no/README.md) |
|
||||
| `yarn example:shadow-dom` | Starter demo-app med shadow-dom |
|
||||
| `yarn lint` | Linter react and css workspaces |
|
||||
| `yarn lint` | Linter kode og css |
|
||||
| `yarn test` | Kjører tester |
|
||||
| `yarn clean` | Fjerner genererte filer |
|
||||
| `yarn changeset` | Ny versjon-entry [(how-to)](https://github.com/navikt/aksel/blob/main/.changeset/README.md) |
|
||||
|
||||
## Bidra
|
||||
|
||||
Vi tar gjerne i mot PR-er! [CONTRIBUTING.md](https://github.com/navikt/aksel/blob/main/CONTRIBUTING.md)
|
||||
Vi tar gjerne imot PR-er! [CONTRIBUTING.md](https://github.com/navikt/aksel/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## Lisenser
|
||||
|
||||
@@ -95,7 +88,6 @@ Løsningen forvaltes av team Aksel
|
||||
|
||||
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||
|
||||
|
||||
## Kode generert av GitHub Copilot
|
||||
|
||||
Dette repoet bruker GitHub Copilot til å generere kode.
|
||||
+13
-15
@@ -1,17 +1,17 @@
|
||||
CMS og nettside for [aksel.nav.no](https://aksel.nav.no/)
|
||||
|
||||
```sh
|
||||
├── /website # NEXT.JS app (v13)
|
||||
└── /website/sanity # Sanity CMS-app (v3)
|
||||
├── /website # Next.js-app
|
||||
└── /website/sanity # Sanity CMS-app
|
||||
```
|
||||
|
||||
### /website
|
||||
|
||||
[Nextjs](https://nextjs.org/) applikasjon som henter data fra et [sanity-datasett](https://www.sanity.io/).
|
||||
[Next.js](https://nextjs.org/)-applikasjon som henter data fra et [Sanity](https://www.sanity.io/)-datasett.
|
||||
|
||||
### /website/sanity
|
||||
|
||||
Det publiserte studioet finner man på [aksel.nav.no/admin](https://aksel.nav.no/admin)(krever Nav SSO for innlogging).
|
||||
Det publiserte studioet finner man på [aksel.nav.no/admin](https://aksel.nav.no/admin) (krever Nav SSO for innlogging).
|
||||
|
||||
`production`-datasettet er privat, så man må ha riktige tilganger for å kunne lese/jobbe med dataen der.
|
||||
|
||||
@@ -23,9 +23,7 @@ Branch `main` pusher rett til prod -> aksel.nav.no. For å utvikle nye features
|
||||
|
||||
## Kode-eksempler
|
||||
|
||||
Alle eksemplene våre ligger under `/website/pages/eksempler` og `/website/pages/templates`. For å se endringene live kan man starte opp storybook med `yarn storybook:aksel`.
|
||||
|
||||
> Du trenger ikke secrets for å teste storybook
|
||||
Alle eksemplene våre ligger under `/website/pages/eksempler` og `/website/pages/templates`. For å se endringene live kan man starte opp storybook med `yarn storybook:aksel`. Du trenger ikke secrets for å kjøre storybook.
|
||||
|
||||
### Nettside (localhost:3000)
|
||||
|
||||
@@ -41,28 +39,28 @@ yarn serve:next
|
||||
|
||||
**Rettigheter**
|
||||
|
||||
Sanity-datasettet er privat, noe som betyr du må ha tilgang til sanity applikasjonen + følgende token for å få data lokalt:
|
||||
Sanity-datasettet er privat, noe som betyr du må ha tilgang til Sanity-applikasjonen og følgende tokens:
|
||||
|
||||
```env
|
||||
SANITY_READ
|
||||
SANITY_READ_NO_DRAFTS
|
||||
```
|
||||
|
||||
`SANITY_READ` er bare nødvendig for lokal forhåndsvisning.
|
||||
### .env
|
||||
|
||||
Blir brukt flere keys i .env under `./website`, men kun `SANITY_READ_NO_DRAFTS` trengs for å teste lokalt
|
||||
Det blir brukt flere keys i .env under `./website`, men kun `SANITY_READ_NO_DRAFTS` trengs for å teste lokalt.
|
||||
|
||||
- SANITY_WRITE:
|
||||
For å sende dokumenter til sanity, brukt til å oppdatere sandboxes/examples/farger. Trengs bare hvis kode skal synkes fra lokal branch/teste feedback-løsning
|
||||
- SANITY_READ: Gir appen tilgang til å lese draft innhold fra sanity i "preview"-mode. Trengs for å teste ikke publiserte-sider
|
||||
- SANITY_READ_NO_DRAFTS: Gir appen tilgang til å lese innhold fra Sanity da datasettet er privat
|
||||
For å sende dokumenter til Sanity. Blir brukt i lokale scripts til å synke data til nettsiden, som f.eks. kode-eksempler og changelog. Trengs normalt ikke lokalt.
|
||||
- SANITY_READ: Gir appen tilgang til å lese både publisert og upublisert innhold fra Sanity. Trengs for å teste ikke-publiserte sider i "preview"-mode.
|
||||
- SANITY_READ_NO_DRAFTS: Gir appen tilgang til å lese kun publisert innhold fra Sanity.
|
||||
|
||||
## Backups
|
||||
|
||||
Kjøres backups hver 3 dag til GCP-bucket under `designsystem-prod`
|
||||
Det kjøres backups hver 3. dag til GCP-bucket under `designsystem-prod`.
|
||||
|
||||
## Henvendelser
|
||||
|
||||
Spørsmål og kontakt kan rettes til team Aksel
|
||||
Spørsmål og kontakt kan rettes til team Aksel.
|
||||
|
||||
Aksel har også en egen Slack-kanal `#aksel-designsystemet`
|
||||
@@ -15,10 +15,10 @@
|
||||
"playroom"
|
||||
],
|
||||
"dependencies": {
|
||||
"@navikt/aksel-icons": "^7.9.0",
|
||||
"@navikt/ds-css": "^7.9.0",
|
||||
"@navikt/ds-react": "^7.9.0",
|
||||
"@navikt/ds-tailwind": "^7.9.0",
|
||||
"@navikt/ds-tokens": "^7.9.0"
|
||||
"@navikt/aksel-icons": "^7.9.1",
|
||||
"@navikt/ds-css": "^7.9.1",
|
||||
"@navikt/ds-react": "^7.9.1",
|
||||
"@navikt/ds-tailwind": "^7.9.1",
|
||||
"@navikt/ds-tokens": "^7.9.1"
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,9 @@ const batchedEvents: Parameters<Pick<Types.BrowserClient, "track">["track"]>[] =
|
||||
[];
|
||||
|
||||
export let amplitude: Pick<Types.BrowserClient, "init" | "track"> = {
|
||||
track: (...eventsData) => {
|
||||
batchedEvents.push(eventsData);
|
||||
track: (/* ...eventsData */) => {
|
||||
/* Until re-enabled in _app.tsx, we can skip storing events */
|
||||
/* batchedEvents.push(eventsData); */
|
||||
return {
|
||||
promise: new Promise<Types.Result>((resolve) =>
|
||||
resolve({
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import cl from "clsx";
|
||||
import { forwardRef } from "react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@navikt/aksel-icons";
|
||||
|
||||
interface CompareHandleProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
isDragging: boolean;
|
||||
}
|
||||
|
||||
export const CompareHandle = forwardRef<HTMLButtonElement, CompareHandleProps>(
|
||||
({ isDragging, ...rest }, ref) => {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={cl(
|
||||
"absolute left-0 top-0 z-10 h-full appearance-none border-0 bg-none p-0 outline-none transition-opacity",
|
||||
{
|
||||
"cursor-ew-resize opacity-100": isDragging,
|
||||
"opacity-40 focus:opacity-100 group-hover:opacity-100": !isDragging,
|
||||
},
|
||||
)}
|
||||
aria-label="Endre verdi for å sammenligne bilder"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
aria-valuenow={50}
|
||||
aria-valuetext="50%"
|
||||
data-rcs="handle-container"
|
||||
role="slider"
|
||||
style={{
|
||||
transform: "translate3d(-50%, 0, 0)",
|
||||
left: `var(--image-clip-2)`,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<span className="absolute top-0 h-full w-[2px] bg-surface-subtle shadow-[rgba(0,_0,_0,_0.35)_0px_0px_4px]" />
|
||||
<span className="pointer-events-none relative z-10 flex items-center rounded-full bg-surface-subtle text-3xl">
|
||||
<ChevronLeftIcon aria-hidden />
|
||||
<ChevronRightIcon aria-hidden />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,202 @@
|
||||
"use client";
|
||||
|
||||
import cl from "clsx";
|
||||
import React, {
|
||||
CSSProperties,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { BodyShort } from "@navikt/ds-react";
|
||||
import { urlFor } from "@/sanity/interface";
|
||||
import { CompareHandle } from "./CompareHandle";
|
||||
import { CompareItem } from "./CompareItem";
|
||||
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
|
||||
type CompareImagesProps = {
|
||||
node: {
|
||||
image_1: { asset: any; alt: string };
|
||||
image_2: { asset: any; alt: string };
|
||||
caption?: string;
|
||||
border?: boolean;
|
||||
background?: {
|
||||
rgb: { a: number; b: number; g: number; r: number };
|
||||
alpha: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const CompareImages = ({ node }: CompareImagesProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const handleRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const internalPosition = useRef(50);
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const syncPosition = () => {
|
||||
if (!containerRef.current || !handleRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
containerRef.current.style.setProperty(
|
||||
"--image-clip-1",
|
||||
`${100 - internalPosition.current}%`,
|
||||
);
|
||||
containerRef.current.style.setProperty(
|
||||
"--image-clip-2",
|
||||
`${internalPosition.current}%`,
|
||||
);
|
||||
|
||||
const rounded = Math.round(internalPosition.current);
|
||||
|
||||
handleRef.current.ariaValueNow = rounded.toString();
|
||||
handleRef.current.ariaValueText = `${rounded}%`;
|
||||
};
|
||||
/**
|
||||
* Update the position based on the cursor's current position within container.
|
||||
*/
|
||||
const updateOnCursorPosition = useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement> | PointerEvent) => {
|
||||
if (!containerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const elementLeft = rect.left;
|
||||
const elementWidth = rect.width;
|
||||
|
||||
// Get X coordinate relative to viewport
|
||||
const clickX = event.clientX;
|
||||
|
||||
// Calculate X coordinate relative to the div (0 = left edge)
|
||||
const relativeX = clickX - elementLeft;
|
||||
|
||||
// Get percentage
|
||||
const percentageX = (relativeX / elementWidth) * 100;
|
||||
|
||||
// Clamp between 0 and 100
|
||||
const clampedPercentageX = Math.max(-1, Math.min(100, percentageX));
|
||||
|
||||
internalPosition.current = Number(clampedPercentageX.toFixed(2));
|
||||
syncPosition();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handlePointerMove = useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement> | PointerEvent) => {
|
||||
if (!isDragging || !containerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateOnCursorPosition(event);
|
||||
},
|
||||
[isDragging, updateOnCursorPosition],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const stopDragging = () => setIsDragging(false);
|
||||
|
||||
globalThis.addEventListener("pointermove", handlePointerMove);
|
||||
globalThis.addEventListener("pointerup", stopDragging);
|
||||
|
||||
return () => {
|
||||
globalThis.removeEventListener("pointermove", handlePointerMove);
|
||||
globalThis.removeEventListener("pointerup", stopDragging);
|
||||
};
|
||||
}, [handlePointerMove]);
|
||||
|
||||
if (!node || !node.image_1 || !node.image_2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||
// Only handle left mouse button (touch events also use 0).
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsDragging(true);
|
||||
updateOnCursorPosition(event);
|
||||
};
|
||||
|
||||
const movePosition = (offset: number) => {
|
||||
internalPosition.current = Math.max(
|
||||
-1,
|
||||
Math.min(100, internalPosition.current + offset),
|
||||
);
|
||||
syncPosition();
|
||||
};
|
||||
|
||||
/* Set on CompareHandle */
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
if (event.key === "ArrowLeft" || event.key === "ArrowDown") {
|
||||
movePosition(-5);
|
||||
} else if (event.key === "ArrowRight" || event.key === "ArrowUp") {
|
||||
movePosition(5);
|
||||
} else if (event.key === "Home") {
|
||||
movePosition(-100);
|
||||
} else if (event.key === "End") {
|
||||
movePosition(100);
|
||||
}
|
||||
};
|
||||
|
||||
const appliedStyle: CSSProperties = {
|
||||
"--image-clip-2": `${internalPosition.current}%`,
|
||||
"--image-clip-1": `${100 - internalPosition.current}%`,
|
||||
backgroundColor: node.background
|
||||
? `rgba(${node.background.rgb.r},${node.background.rgb.g},${node.background.rgb.b},${node.background.rgb.a})`
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<figure className="m-0 mb-8 flex flex-col group-[.aksel-artikkel]/aksel:mx-auto">
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={appliedStyle}
|
||||
onPointerDown={handlePointerDown}
|
||||
className={cl(
|
||||
"group relative grid max-h-full max-w-fit touch-pan-y select-none overflow-hidden rounded-lg",
|
||||
"outline-2 outline-offset-4 outline-border-focus focus-within:outline",
|
||||
{
|
||||
"ring-1 ring-border-subtle": node.border,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<CompareItem order="1">
|
||||
<img
|
||||
src={urlFor(node.image_1.asset).auto("format").url()}
|
||||
alt={node.image_1.alt}
|
||||
className="object-cover object-center"
|
||||
/>
|
||||
</CompareItem>
|
||||
<CompareItem order="2">
|
||||
<img
|
||||
src={urlFor(node.image_2.asset).auto("format").url()}
|
||||
alt={node.image_2.alt}
|
||||
className="object-cover object-center"
|
||||
/>
|
||||
</CompareItem>
|
||||
<CompareHandle
|
||||
ref={handleRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
isDragging={isDragging}
|
||||
/>
|
||||
</div>
|
||||
{node.caption && (
|
||||
<figcaption className="mt-2 grid gap-1 px-4">
|
||||
<BodyShort as="span" size="small" className="self-center text-center">
|
||||
{node.caption}
|
||||
</BodyShort>
|
||||
</figcaption>
|
||||
)}
|
||||
</figure>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompareImages;
|
||||
@@ -0,0 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export const CompareItem = ({
|
||||
children,
|
||||
order,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
order: "1" | "2";
|
||||
}) => {
|
||||
const appliedStyle: CSSProperties = {
|
||||
gridArea: "1 / 1 / 2 / 2",
|
||||
willChange: "clip-path",
|
||||
clipPath:
|
||||
order === "1"
|
||||
? `inset(0 var(--image-clip-1) 0 0)`
|
||||
: `inset(0 0 0 var(--image-clip-2))`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={appliedStyle}
|
||||
className="box-border max-w-fit cursor-pointer select-none overflow-hidden"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -13,6 +13,7 @@ import Bilde from "@/cms/bilde/Bilde";
|
||||
import InnholdsKort from "@/cms/cards/InnholdsKort";
|
||||
import CodeExamples from "@/cms/code-examples/CodeExamples";
|
||||
import SnippetLazy from "@/cms/code-snippet/SnippetLazy";
|
||||
import CompareImages from "@/cms/compare-images/CompareImages";
|
||||
import DoDont from "@/cms/do-dont/DoDont";
|
||||
import ExampletextBlock from "@/cms/exampletext-block/ExampletextBlock";
|
||||
import ExpansionCard from "@/cms/expansioncard/ExpansionCard";
|
||||
@@ -53,6 +54,7 @@ const serializers: Partial<PortableTextReactComponents> = {
|
||||
language: ({ value }) => (
|
||||
<SanityBlockContent lang={value.language} blocks={value.body} />
|
||||
),
|
||||
compare_images: ({ value }) => <CompareImages node={value} />,
|
||||
},
|
||||
unknownType: () => null,
|
||||
block: {
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-responsive-masonry": "2.2.0",
|
||||
"react-rx": "^2.1.3",
|
||||
"react-snowfall": "^2.2.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sanity": "3.59.0",
|
||||
"sanity-plugin-iframe-pane": "^3.1.6",
|
||||
@@ -93,7 +94,7 @@
|
||||
"babel-loader": "^8.2.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"jsdom": "25.0.1",
|
||||
"json5": "2.2.1",
|
||||
"pino-pretty": "^10.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
@@ -104,6 +105,6 @@
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "^1.2.2"
|
||||
"vitest": "^2.1.8"
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,14 @@ import { useEffect } from "react";
|
||||
import { useCheckAuth } from "@/hooks/useCheckAuth";
|
||||
import { useHashScroll } from "@/hooks/useHashScroll";
|
||||
import { SanityDataContext } from "@/hooks/useSanityData";
|
||||
import { useAmplitudeInit } from "@/logging";
|
||||
import { BaseSEO } from "@/web/seo/BaseSEO";
|
||||
import "../components/styles/index.css";
|
||||
|
||||
function App({ Component, pageProps, router }: AppProps) {
|
||||
useHashScroll();
|
||||
useAmplitudeInit();
|
||||
|
||||
/* As of 01.01.25, removed until cookie compliance is implemented */
|
||||
/* useAmplitudeInit(); */
|
||||
|
||||
useEffect(() => {
|
||||
window.location.host === "design.nav.no" &&
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Kode eksempler
|
||||
# Kode-eksempler
|
||||
|
||||
Kode eksempler som blir vist på komponentsider @ aksel.nav.no
|
||||
Kode-eksempler som blir vist på komponentsider @ aksel.nav.no
|
||||
|
||||
## Anatomi
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import cl from "clsx";
|
||||
import { groq } from "next-sanity";
|
||||
import { GetStaticProps } from "next/types";
|
||||
import { useState } from "react";
|
||||
import Snowfall from "react-snowfall";
|
||||
import {
|
||||
CompassIcon,
|
||||
ComponentIcon,
|
||||
@@ -197,6 +198,17 @@ const Forside = ({ page, tema, blocks }: PageProps["props"]) => {
|
||||
/>
|
||||
<Header />
|
||||
<main tabIndex={-1} id="hovedinnhold" className="focus:outline-none">
|
||||
{!pause && (
|
||||
<div aria-hidden>
|
||||
<Snowfall
|
||||
style={{ zIndex: 1 }}
|
||||
color="rgba(230, 241, 248, 0.9)"
|
||||
speed={reducedMotion || pause ? [0, 0] : [0.1, 0.2]}
|
||||
wind={reducedMotion || pause ? [0, 0] : [-0.2, 0.2]}
|
||||
snowflakeCount={600}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="z-20 pb-28">
|
||||
<div className="relative mx-auto mb-12 mt-20 grid w-full place-items-center px-4 text-center sm:mt-36 sm:max-w-[632px] sm:px-6">
|
||||
<Heading
|
||||
|
||||
@@ -39,7 +39,7 @@ export const Blogg = defineType({
|
||||
description:
|
||||
"Innholdet i artikkelen er riktekst. Tips: klikk på ikon i høyre hjørne for å skrive i fullskjerm.",
|
||||
name: "content",
|
||||
type: "riktekst_standard",
|
||||
type: "riktekst_blogg",
|
||||
group: "innhold",
|
||||
}),
|
||||
BaseSEOPreset,
|
||||
|
||||
@@ -62,6 +62,7 @@ export const schema: SchemaPluginOptions = {
|
||||
object.UnikSidemodul,
|
||||
object.Attachment,
|
||||
object.Language,
|
||||
object.CompareImages,
|
||||
|
||||
/* Riktekst */
|
||||
object.RiktekstKomponent,
|
||||
@@ -72,6 +73,7 @@ export const schema: SchemaPluginOptions = {
|
||||
object.RiktekstPrinsipp,
|
||||
object.RiktekstStandalone,
|
||||
object.RiktekstAccordion,
|
||||
object.RiktekstBlogg,
|
||||
|
||||
/* Komponentsider */
|
||||
object.PropsSeksjon,
|
||||
|
||||
@@ -7,16 +7,17 @@ export * from "./prinsipper/herobilde";
|
||||
export * from "./prinsipper/innholdskort";
|
||||
export * from "./shared/accordion";
|
||||
export * from "./shared/alert";
|
||||
export * from "./shared/bilde";
|
||||
export * from "./shared/dodont";
|
||||
export * from "./shared/attachment";
|
||||
export * from "./shared/bilde";
|
||||
export * from "./shared/compare-images";
|
||||
export * from "./shared/dodont";
|
||||
export * from "./shared/expansion-card";
|
||||
export * from "./shared/kode";
|
||||
export * from "./shared/language";
|
||||
export * from "./shared/relatert-innhold";
|
||||
export * from "./shared/riktekst";
|
||||
export * from "./shared/tabell";
|
||||
export * from "./shared/tips";
|
||||
export * from "./shared/unik-sidemodul";
|
||||
export * from "./shared/video";
|
||||
export * from "./shared/language";
|
||||
export * from "./templates/ExampletextBlock";
|
||||
@@ -0,0 +1,57 @@
|
||||
import { defineField, defineType } from "sanity";
|
||||
import { MigrationIcon } from "@navikt/aksel-icons";
|
||||
|
||||
export const CompareImages = defineType({
|
||||
title: "Sammenlign bilder",
|
||||
name: "compare_images",
|
||||
type: "object",
|
||||
icon: () => <MigrationIcon aria-hidden />,
|
||||
fields: [
|
||||
defineField({
|
||||
title: "Bilde 1",
|
||||
name: "image_1",
|
||||
type: "image",
|
||||
validation: (Rule) => Rule.required(),
|
||||
fields: [
|
||||
defineField({
|
||||
name: "alt",
|
||||
title: "Alt tekst",
|
||||
type: "string",
|
||||
validation: (Rule) =>
|
||||
Rule.required().error("Bilde 1 må ha alt-tekst."),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
defineField({
|
||||
title: "Bilde 2",
|
||||
name: "image_2",
|
||||
type: "image",
|
||||
validation: (Rule) => Rule.required(),
|
||||
fields: [
|
||||
defineField({
|
||||
name: "alt",
|
||||
title: "Alt tekst",
|
||||
type: "string",
|
||||
validation: (Rule) =>
|
||||
Rule.required().error("Bilde 2 må ha alt-tekst."),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
defineField({
|
||||
name: "caption",
|
||||
title: "Bildetekst",
|
||||
type: "string",
|
||||
}),
|
||||
defineField({
|
||||
name: "border",
|
||||
title: "Border",
|
||||
type: "boolean",
|
||||
initialValue: true,
|
||||
}),
|
||||
defineField({
|
||||
name: "background",
|
||||
title: "Bakgrunnsfarge",
|
||||
type: "color",
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -143,6 +143,7 @@ export const headingStyles = [
|
||||
|
||||
const Riktekst = (
|
||||
type:
|
||||
| "blogg"
|
||||
| "god-praksis"
|
||||
| "grunnleggende"
|
||||
| "templates"
|
||||
@@ -188,8 +189,8 @@ const Riktekst = (
|
||||
];
|
||||
|
||||
const templates = ["kode_eksempler", "exampletext_block"];
|
||||
|
||||
const grunnleggende = ["spesial_seksjon", "attachment", "props_seksjon"];
|
||||
const blogg = ["compare_images"];
|
||||
|
||||
fields.push(...standard);
|
||||
|
||||
@@ -209,6 +210,9 @@ const Riktekst = (
|
||||
case "templates":
|
||||
fields.push(...templates);
|
||||
break;
|
||||
case "blogg":
|
||||
fields.push(...blogg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -287,3 +291,11 @@ export const RiktekstTemplates = {
|
||||
of: Riktekst("templates"),
|
||||
icon: () => <FileTextIcon aria-hidden />,
|
||||
};
|
||||
|
||||
export const RiktekstBlogg = {
|
||||
title: "Riktekst",
|
||||
name: "riktekst_blogg",
|
||||
type: "array",
|
||||
of: Riktekst("blogg"),
|
||||
icon: () => <FileTextIcon aria-hidden />,
|
||||
};
|
||||
+1
-1
@@ -147,7 +147,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.3",
|
||||
"@changesets/cli": "2.26.2",
|
||||
"@changesets/cli": "2.27.11",
|
||||
"@storybook/addon-a11y": "^8.3.4",
|
||||
"@storybook/addon-essentials": "^8.3.4",
|
||||
"@storybook/addon-interactions": "^8.3.4",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "eslint-plugin-aksel-local",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"module": "index.js"
|
||||
}
|
||||
Reference in new issue
Block a user