Compare commits

...
Author SHA1 Message Date
github-actions[bot]andgithub-actions[bot] bc8761295b Version Packages (#3314)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-04 10:22:20 +01:00
Halvor Haugan 6d9f9001fe Table.ColumnHeader: Inherit text alignment in button (#3319) 2024-11-01 16:07:39 +01:00
KenandJulian Nymark 039332b4f8 [Darkside] Updated Alert CSS (#3301)
* change: Alert variants

* feat: Updated alert css

* change: use correct spacing

* Update @navikt/core/css/darkside/alert.darkside.css

Co-authored-by: Julian Nymark <julian.nymark@nav.no>

---------

Co-authored-by: Julian Nymark <julian.nymark@nav.no>
2024-11-01 16:05:32 +01:00
Halvor Haugan 8cadfd0979 Pagination: More precise type for renderItem (#3275) 2024-11-01 08:49:08 +01:00
Halvor Haugan c200f580c1 Translations for ReadOnlyIcon (#3311) 2024-10-31 16:28:48 +01:00
Halvor Haugan 5ce8ec620c Include number of failed stories in Chromatic comment (#3308) 2024-10-31 15:21:23 +01:00
github-actions[bot]andgithub-actions[bot] f7789522c6 Version Packages (#3307)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-31 12:27:02 +01:00
Julian Nymark a9a1182eff Website: 🐛 code example chip to same on first page (#3310)
* 🐛 fix bug still appearing on first page load

* Website: ♻️
2024-10-31 11:50:32 +01:00
Ken 35f7355233 bug: Checks if autocomplete should actually be used when pressing enter (#3306) 2024-10-31 09:56:16 +01:00
Julian NymarkandHalvor Haugan bbc06a3eab ErrorSummary: add translation support (#3245)
* ErrorSummary: add translation support

* Update @navikt/core/react/src/form/error-summary/ErrorSummary.tsx

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>

* Update @navikt/core/react/src/util/i18n/locales/nn.ts

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>

* ErrorSummary: fix broken object notation (thanks github conflict webGUI! 😂)

* ErrorSummary: use proper casing for translations object

---------

Co-authored-by: Halvor Haugan <83693529+HalvorHaugan@users.noreply.github.com>
2024-10-30 16:07:47 +01:00
Halvor Haugan 6e49de805f Textarea: translations (#3302) 2024-10-30 15:31:37 +01:00
43 changed files with 357 additions and 224 deletions

No files matched your search

+6 -12
View File
@@ -61,15 +61,6 @@ jobs:
env:
STORYBOOK_GITHUB_SHA: ${{ github.sha }}
- name: replace storybook url
if: "!contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'"
uses: frabert/replace-string-action@v2
id: storybook_url
with:
pattern: "(iframe.html)"
string: ${{ steps.chromatic_tests.outputs.storybookUrl }}
replace-with: ""
- name: Get commit sha
if: "!contains(github.event.head_commit.message, '[ci skip]') && github.event_name != 'push'"
id: vars
@@ -93,7 +84,7 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## [Storybook demo](${{steps.storybook_url.outputs.replaced}})
## [Storybook demo](${{steps.chromatic_tests.outputs.storybookUrl}})
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
edit-mode: replace
@@ -105,9 +96,12 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## [Storybook demo](${{steps.storybook_url.outputs.replaced}})
## [Storybook demo](${{steps.chromatic_tests.outputs.storybookUrl}}) / [Chromatic](${{steps.chromatic_tests.outputs.url}})
### [Endringer til review](${{steps.chromatic_tests.outputs.url}}): ${{steps.chromatic_tests.outputs.changeCount}}
### 📝 Endringer til review: ${{steps.chromatic_tests.outputs.changeCount}}
${{ steps.chromatic_tests.outputs.errorCount > 0 && format('### ❌ Stories med feil: {0}', steps.chromatic_tests.outputs.errorCount) || '' }}
${{ steps.chromatic_tests.outputs.interactionTestFailuresCount > 0 && format('### ❌ Feilede interaksjonstester: {0}', steps.chromatic_tests.outputs.interactionTestFailuresCount) || '' }}
[${{ steps.vars.outputs.sha_short }}](https://github.com/navikt/aksel/commit/${{ steps.vars.outputs.sha_short }}) | ${{steps.chromatic_tests.outputs.componentCount}} komponenter | ${{steps.chromatic_tests.outputs.specCount}} stories
edit-mode: replace
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-icons
## 7.4.3
## 7.4.2
## 7.4.1
## 7.4.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-icons",
"version": "7.4.1",
"version": "7.4.3",
"author": "Aksel | NAV",
"homepage": "https://aksel.nav.no/ikoner",
"repository": {
+4
View File
@@ -1,5 +1,9 @@
# @navikt/aksel-stylelint
## 7.4.3
## 7.4.2
## 7.4.1
## 7.4.0
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel-stylelint",
"version": "7.4.1",
"version": "7.4.3",
"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.4.1",
"@navikt/ds-tokens": "^7.4.1",
"@navikt/ds-css": "^7.4.3",
"@navikt/ds-tokens": "^7.4.3",
"concurrently": "9.0.1",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0",
+14
View File
@@ -1,5 +1,19 @@
# @navikt/aksel
## 7.4.3
### Patch Changes
- Updated dependencies [[`6d9f900`](https://github.com/navikt/aksel/commit/6d9f9001fed4ccfa4245ab97399b24012ceb9edf)]:
- @navikt/ds-css@7.4.3
## 7.4.2
### Patch Changes
- Updated dependencies []:
- @navikt/ds-css@7.4.2
## 7.4.1
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/aksel",
"version": "7.4.1",
"version": "7.4.3",
"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.4.1",
"@navikt/ds-css": "7.4.3",
"axios": "1.7.4",
"chalk": "4.1.0",
"clipboardy": "^2.3.0",
+8
View File
@@ -1,5 +1,13 @@
# @navikt/ds-css
## 7.4.3
### Patch Changes
- Table.ColumnHeader: Inherit text alignment in button ([#3319](https://github.com/navikt/aksel/pull/3319))
## 7.4.2
## 7.4.1
### Patch Changes
+79 -77
View File
@@ -1,101 +1,89 @@
.navds-alert {
border-radius: var(--a-border-radius-medium);
border-radius: var(--ax-border-radius-xlarge);
border: 1px solid;
padding: var(--a-spacing-4);
padding: var(--ax-spacing-4);
display: flex;
gap: var(--a-spacing-3);
gap: var(--ax-spacing-3);
align-items: center;
}
.navds-alert--small.navds-alert--close-button {
align-items: flex-start;
@media (forced-colors: active) {
border: 1px solid canvastext;
background-color: canvas;
color: canvastext;
}
}
.navds-alert__wrapper--maxwidth {
max-width: 43.5rem;
}
.navds-alert--close-button > .navds-alert__wrapper {
margin-block-start: var(--a-spacing-05);
}
.navds-alert--small.navds-alert--close-button > .navds-alert__wrapper {
margin-block-start: var(--a-spacing-1);
.navds-alert__icon {
flex-shrink: 0;
font-size: 1.5rem;
align-self: flex-start;
height: var(--ax-font-line-height-xlarge);
}
.navds-alert--small {
padding: var(--a-spacing-2) var(--a-spacing-4);
gap: var(--a-spacing-2);
padding: var(--ax-spacing-2) var(--ax-spacing-4);
gap: var(--ax-spacing-2);
> .navds-alert__icon {
margin-block-start: 0;
height: var(--ax-font-line-height-large);
}
}
/* ----------------------------- Alert variants ----------------------------- */
.navds-alert--info {
border-color: var(--ax-border-info);
background-color: var(--ax-bg-info-moderate);
> .navds-alert__icon {
color: var(--ax-text-info-icon);
}
}
.navds-alert--success {
border-color: var(--ax-border-success);
background-color: var(--ax-bg-success-moderate);
> .navds-alert__icon {
color: var(--ax-text-success-icon);
}
}
.navds-alert--warning {
border-color: var(--ax-border-warning);
background-color: var(--ax-bg-warning-moderate);
> .navds-alert__icon {
color: var(--ax-text-warning-icon);
}
}
.navds-alert--error {
border-color: var(--ax-border-danger);
background-color: var(--ax-bg-danger-moderate);
> .navds-alert__icon {
color: var(--ax-text-danger-icon);
}
}
/* ----------------------------- Alert fullwidth ---------------------------- */
.navds-alert--full-width {
border-radius: 0;
}
.navds-alert > .navds-alert__icon {
flex-shrink: 0;
font-size: 1.5rem;
align-self: flex-start;
height: var(--a-font-line-height-xlarge);
background: radial-gradient(circle, var(--a-surface-default) 47%, 0, transparent);
}
.navds-alert--small > .navds-alert__icon {
margin-block-start: 0;
height: var(--a-font-line-height-large);
}
.navds-alert--close-button > .navds-alert__icon {
margin-block-start: var(--a-spacing-05);
}
.navds-alert--small.navds-alert--close-button > .navds-alert__icon {
margin-block-start: var(--a-spacing-1);
}
.navds-alert--error {
border-color: var(--ac-alert-error-border, var(--a-border-danger));
background-color: var(--ac-alert-error-bg, var(--a-surface-danger-subtle));
}
.navds-alert--error > .navds-alert__icon {
color: var(--ac-alert-icon-error-color, var(--a-icon-danger));
}
.navds-alert--warning {
border-color: var(--ac-alert-warning-border, var(--a-border-warning));
background-color: var(--ac-alert-warning-bg, var(--a-surface-warning-subtle));
}
.navds-alert--warning > .navds-alert__icon {
background: radial-gradient(circle at 50% 57%, var(--a-surface-default) 30%, 0, transparent);
color: var(--ac-alert-icon-warning-color, var(--a-icon-warning));
}
.navds-alert--info {
border-color: var(--ac-alert-info-border, var(--a-border-info));
background-color: var(--ac-alert-info-bg, var(--a-surface-info-subtle));
}
.navds-alert--info > .navds-alert__icon {
color: var(--ac-alert-icon-info-color, var(--a-icon-info));
}
.navds-alert--success {
border-color: var(--ac-alert-success-border, var(--a-border-success));
background-color: var(--ac-alert-success-bg, var(--a-surface-success-subtle));
}
.navds-alert--success > .navds-alert__icon {
color: var(--ac-alert-icon-success-color, var(--a-icon-success));
}
/* ------------------------------ Alert inline ------------------------------ */
.navds-alert--inline {
background-color: transparent;
border: none;
padding: 0;
}
/* ------------------------- Alert with close button ------------------------ */
.navds-alert__button-wrapper {
align-self: flex-start;
flex: 1;
@@ -104,10 +92,24 @@
justify-content: flex-end;
}
@media (forced-colors: active) {
.navds-alert {
border: 1px solid canvastext;
background-color: canvas;
color: canvastext;
.navds-alert--close-button {
> .navds-alert__wrapper {
margin-block-start: var(--ax-spacing-05);
}
> .navds-alert__icon {
margin-block-start: var(--ax-spacing-05);
}
&.navds-alert--small {
align-items: flex-start;
> .navds-alert__wrapper {
margin-block-start: var(--ax-spacing-1);
}
> .navds-alert__icon {
margin-block-start: var(--ax-spacing-1);
}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "7.4.1",
"version": "7.4.3",
"description": "CSS for NAV Designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -27,7 +27,7 @@
"css:get-version": "node config/get-version.js"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.4.1",
"@navikt/ds-tokens": "^7.4.3",
"autoprefixer": "^10.4.20",
"cssnano": "6.0.0",
"fast-glob": "3.2.11",
+1
View File
@@ -114,6 +114,7 @@
align-items: center;
line-height: inherit;
font-weight: inherit;
text-align: inherit;
}
.navds-table--small .navds-table__sort-button {
+20
View File
@@ -1,5 +1,25 @@
# @navikt/ds-react
## 7.4.3
### Patch Changes
- Pagination: More precise type for renderItem ([#3275](https://github.com/navikt/aksel/pull/3275))
- Updated dependencies []:
- @navikt/ds-tokens@7.4.3
- @navikt/aksel-icons@7.4.3
## 7.4.2
### Patch Changes
- Combobox: Pressing 'Enter' after canceling autocomplete could result in using matching options in list. ([#3306](https://github.com/navikt/aksel/pull/3306))
- Updated dependencies []:
- @navikt/ds-tokens@7.4.2
- @navikt/aksel-icons@7.4.2
## 7.4.1
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "7.4.1",
"version": "7.4.3",
"description": "React components from the Norwegian Labour and Welfare Administration.",
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
"license": "MIT",
@@ -604,8 +604,8 @@
"dependencies": {
"@floating-ui/react": "0.25.4",
"@floating-ui/react-dom": "^2.0.9",
"@navikt/aksel-icons": "^7.4.1",
"@navikt/ds-tokens": "^7.4.1",
"@navikt/aksel-icons": "^7.4.3",
"@navikt/ds-tokens": "^7.4.3",
"clsx": "^2.1.0",
"date-fns": "^3.0.0",
"react-day-picker": "8.10.1"
@@ -94,7 +94,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon readOnly={readOnly} />
{readOnly && <ReadOnlyIcon />}
{label}
</Label>
{!!description && (
+14 -17
View File
@@ -1,20 +1,17 @@
import React from "react";
import { PadlockLockedFillIcon } from "@navikt/aksel-icons";
import { useI18n } from "../util/i18n/i18n.context";
export const ReadOnlyIcon = ({
readOnly,
nativeReadOnly = true,
}: {
readOnly?: boolean;
nativeReadOnly?: boolean;
}) => {
if (readOnly) {
return (
<PadlockLockedFillIcon
{...(nativeReadOnly ? { "aria-hidden": true } : { title: "readonly" })}
className="navds-form-field__readonly-icon"
/>
);
}
return null;
};
export const ReadOnlyIcon = () => (
<PadlockLockedFillIcon
aria-hidden
className="navds-form-field__readonly-icon"
/>
);
export const ReadOnlyIconWithTitle = () => (
<PadlockLockedFillIcon
title={useI18n("global")("readOnly")}
className="navds-form-field__readonly-icon"
/>
);
@@ -3,7 +3,7 @@ import React, { forwardRef } from "react";
import { BodyShort } from "../../typography";
import { omit } from "../../util";
import { useId } from "../../util/hooks";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
import { CheckboxProps } from "./types";
import useCheckbox from "./useCheckbox";
@@ -90,9 +90,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
className="navds-checkbox__label-text"
aria-hidden
>
{!nested && (
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
)}
{!nested && readOnly && <ReadOnlyIconWithTitle />}
{props.children}
</BodyShort>
{props.description && (
@@ -1,7 +1,7 @@
import cl from "clsx";
import React, { forwardRef } from "react";
import { BodyShort, ErrorMessage, Label } from "../../typography";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
import ComboboxWrapper from "./ComboboxWrapper";
import FilteredOptions from "./FilteredOptions/FilteredOptions";
import { useFilteredOptionsContext } from "./FilteredOptions/filteredOptionsContext";
@@ -46,7 +46,7 @@ export const Combobox = forwardRef<
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon nativeReadOnly={false} readOnly={readOnly} />
{readOnly && <ReadOnlyIconWithTitle />}
{label}
</Label>
{!!description && (
@@ -100,9 +100,22 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
value,
filteredOptions,
);
/*
* User can have matching results, while not using the autocomplete result
* E.g. User types "Oslo", list has is "Oslo kommune", but user hits backspace, canceling autocomplete.
*/
const autoCompleteMatchesValue =
filteredOptionsUtil.normalizeText(value) ===
filteredOptionsUtil.normalizeText(autoCompletedOption?.label ?? "");
let selectedValue: ComboboxOption | undefined;
if (shouldAutocomplete && autoCompletedOption) {
if (
shouldAutocomplete &&
autoCompletedOption &&
autoCompleteMatchesValue
) {
selectedValue = autoCompletedOption;
} else if (allowNewValues && isValueNew) {
selectedValue = { label: value, value };
@@ -3,6 +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 ErrorSummaryItem from "./ErrorSummaryItem";
export interface ErrorSummaryProps
@@ -72,11 +73,12 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
className,
size = "medium",
headingTag = "h2",
heading = "Du må rette disse feilene før du kan fortsette:",
heading,
...rest
},
ref,
) => {
const translate = useI18n("ErrorSummary");
const wrapperRef = useRef<HTMLDivElement>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
@@ -105,7 +107,7 @@ export const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(
ref={headingRef}
tabIndex={-1}
>
{heading}
{heading ?? translate("heading")}
</Heading>
<BodyShort as="ul" size={size} className="navds-error-summary__list">
{children}
@@ -2,6 +2,8 @@ import { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import React, { useRef } from "react";
import { VStack } from "../../layout/stack";
import UNSAFE_AkselLanguageProvider from "../../provider/i18n/LanguageProvider";
import nn from "../../util/i18n/locales/nn";
import { ErrorSummary } from "./ErrorSummary";
export default {
@@ -134,3 +136,18 @@ export const Chromatic: Story = {
chromatic: { disable: false },
},
};
export const ProvidedTranslations: Story = {
render: () => {
return (
<UNSAFE_AkselLanguageProvider translations={nn}>
<ErrorSummary size="small">
<ErrorSummary.Item href="#1">Checkbox må fylles ut</ErrorSummary.Item>
<ErrorSummary.Item href="#2">
Tekstfeltet må ha en godkjent e-mail
</ErrorSummary.Item>
</ErrorSummary>
</UNSAFE_AkselLanguageProvider>
);
},
};
@@ -2,7 +2,7 @@ import cl from "clsx";
import React, { FieldsetHTMLAttributes, forwardRef, useContext } from "react";
import { BodyShort, ErrorMessage, Label } from "../../typography";
import { omit } from "../../util";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { ReadOnlyIcon, ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
import { FormFieldProps } from "../useFormField";
import { FieldsetContext } from "./context";
import { useFieldset } from "./useFieldset";
@@ -89,7 +89,8 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
"navds-sr-only": !!hideLegend,
})}
>
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={nativeReadOnly} />
{readOnly &&
(nativeReadOnly ? <ReadOnlyIcon /> : <ReadOnlyIconWithTitle />)}
{legend}
</Label>
{!!description && (
@@ -105,9 +105,7 @@ export const FormProgress = forwardRef<HTMLDivElement, FormProgressProps>(
<Collapsible lazy open={open} onOpenChange={onOpenChange}>
<HStack justify="space-between" align="center">
<BodyShort as="span">
{translate("step", {
replacements: { activeStep, totalSteps },
})}
{translate("step", { activeStep, totalSteps })}
</BodyShort>
<Collapsible.Trigger asChild aria-expanded={undefined}>
<Button
@@ -3,7 +3,7 @@ import React, { SelectHTMLAttributes, forwardRef } from "react";
import { ChevronDownIcon } from "@navikt/aksel-icons";
import { BodyShort, ErrorMessage, Label } from "../../typography";
import { omit } from "../../util";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
import { FormFieldProps, useFormField } from "../useFormField";
export interface SelectProps
@@ -112,7 +112,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
{readOnly && <ReadOnlyIconWithTitle />}
{label}
</Label>
{!!description && (
+9 -10
View File
@@ -8,7 +8,7 @@ import React, {
import { Loader } from "../../loader";
import { BodyShort } from "../../typography";
import { omit } from "../../util";
import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon";
import { FormFieldProps, useFormField } from "../useFormField";
const SelectedIcon = () => (
@@ -21,7 +21,6 @@ const SelectedIcon = () => (
focusable={false}
role="img"
aria-hidden
aria-label="Deaktiver valg"
>
<path
fillRule="evenodd"
@@ -117,19 +116,19 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
defaultChecked={defaultChecked}
ref={ref}
type="checkbox"
onChange={(e) => {
onChange={(event) => {
if (readOnly) {
return;
}
setChecked(e.target.checked);
props.onChange?.(e);
setChecked(event.target.checked);
props.onChange?.(event);
}}
onClick={(e) => {
onClick={(event) => {
if (readOnly) {
e.preventDefault();
event.preventDefault();
return;
}
props?.onClick?.(e);
props.onClick?.(event);
}}
className={cl(className, "navds-switch__input")}
/>
@@ -150,11 +149,11 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
<div
className={cl("navds-switch__content", {
"navds-sr-only": hideLabel,
"navds-switch--with-description": !!description && !hideLabel,
"navds-switch--with-description": description && !hideLabel,
})}
>
<BodyShort as="div" size={size} className="navds-switch__label">
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
{readOnly && <ReadOnlyIconWithTitle />}
{children}
</BodyShort>
{description && (
@@ -9,9 +9,6 @@ import { ReadOnlyIcon } from "../ReadOnlyIcon";
import { FormFieldProps, useFormField } from "./../useFormField";
import Counter from "./TextareaCounter";
/**
* TODO: Mulighet for lokalisering av sr-only/counter text
*/
export interface TextareaProps
extends FormFieldProps,
React.TextareaHTMLAttributes<HTMLTextAreaElement> {
@@ -144,7 +141,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon readOnly={readOnly} />
{readOnly && <ReadOnlyIcon />}
{label}
</Label>
{!!description && (
@@ -180,17 +177,13 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
{...(describedBy ? { "aria-describedby": describedBy } : {})}
/>
{hasMaxLength && !readOnly && !inputProps.disabled && (
<>
<span id={maxLengthId} className="navds-sr-only">
{`Tekstområde med plass til ${maxLength} tegn.`}
</span>
<Counter
maxLength={maxLength}
currentLength={props.value?.length ?? uncontrolledValue.length}
size={size}
i18n={i18n}
/>
</>
<Counter
maxLengthId={maxLengthId}
maxLength={maxLength}
currentLength={props.value?.length ?? uncontrolledValue.length}
size={size}
i18n={i18n}
/>
)}
<div
className="navds-form-field__error"
@@ -2,18 +2,32 @@ 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 type { TextareaProps } from "./Textarea";
interface Props {
maxLengthId: string;
maxLength: number;
currentLength: number;
size: TextareaProps["size"];
i18n: TextareaProps["i18n"];
}
const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
const difference = maxLength - currentLength;
const TextareaCounter = ({
maxLengthId,
maxLength,
currentLength,
size,
i18n,
}: Props) => {
const translate = useI18n("Textarea", {
charsLeft: i18n?.counterLeft ? `{chars} ${i18n.counterLeft}` : undefined,
charsTooMany: i18n?.counterTooMuch
? `{chars} ${i18n.counterTooMuch}`
: undefined,
});
const difference = maxLength - currentLength;
const [debouncedDiff, setDebouncedDiff] = useState(difference);
useEffect(() => {
@@ -29,12 +43,16 @@ const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
return (
<>
<span id={maxLengthId} className="navds-sr-only">
{translate("maxLength", { maxLength })}
</span>
{difference < 20 && (
<span
role="status"
className="navds-textarea__sr-counter navds-sr-only"
>
{getCounterText(debouncedDiff, i18n)}
{getCounterText(debouncedDiff, translate)}
</span>
)}
@@ -44,15 +62,21 @@ const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
})}
size={size}
>
{getCounterText(difference, i18n)}
{getCounterText(difference, translate)}
</BodyShort>
</>
);
};
const getCounterText = (difference: number, i18n: TextareaProps["i18n"]) =>
const getCounterText = (
difference: number,
translate: (
key: "charsTooMany" | "charsLeft",
replacements?: { chars: number },
) => string,
) =>
difference < 0
? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}`
: `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`;
? translate("charsTooMany", { chars: Math.abs(difference) })
: translate("charsLeft", { chars: difference });
export default TextareaCounter;
@@ -16,16 +16,11 @@ export default meta;
type Story = StoryObj<typeof Textarea>;
export const Default: StoryObj<typeof Textarea> = {
render: (props) => {
return <Textarea {...props} />;
},
export const Default: Story = {
args: {
maxLength: 0,
label: "Ipsum enim quis culpa",
},
argTypes: {
resize: {
control: { type: "radio" },
@@ -91,7 +91,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
"navds-sr-only": hideLabel,
})}
>
<ReadOnlyIcon readOnly={readOnly} />
{readOnly && <ReadOnlyIcon />}
{label}
</Label>
@@ -9,6 +9,17 @@ import PaginationItem, {
PaginationItemType,
} from "./PaginationItem";
interface RenderItemProps
extends Pick<
PaginationItemProps,
"className" | "disabled" | "selected" | "icon" | "iconPosition"
> {
children: React.ReactNode;
onClick: React.MouseEventHandler<HTMLButtonElement>;
page: number;
size: Exclude<PaginationProps["size"], undefined>;
}
export interface PaginationProps extends React.HTMLAttributes<HTMLElement> {
/**
* Current page.
@@ -46,9 +57,9 @@ export interface PaginationProps extends React.HTMLAttributes<HTMLElement> {
prevNextTexts?: boolean;
/**
* Override pagination item rendering.
* @default (item: PaginationItemProps) => <PaginationItem {...item} />
* @default PaginationItem
*/
renderItem?: (item: PaginationItemProps) => ReturnType<React.FC>;
renderItem?: (item: RenderItemProps) => ReturnType<React.FC>;
/**
* Pagination heading. We recommend adding heading instead of `aria-label` to help assistive technologies with an extra navigation-stop.
*/
@@ -111,7 +122,7 @@ export const getSteps = ({
* ```jsx
* <Pagination
* page={pageState}
* onPageChange={(x) => setPageState(x)}
* onPageChange={setPageState}
* count={9}
* boundaryCount={1}
* siblingCount={1}
@@ -131,9 +142,7 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
prevNextTexts = false,
srHeading,
"aria-labelledby": ariaLabelledBy,
renderItem: Item = (item: PaginationItemProps) => (
<PaginationItem {...item} />
),
renderItem: Item = PaginationItem,
...rest
},
ref,
@@ -218,6 +227,7 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
) : (
<li key={step}>
<Item
/* Remember to update RenderItemProps if you make changes to props sent into Item */
onClick={() => onPageChange?.(n)}
selected={page === n}
page={n}
@@ -122,13 +122,11 @@ export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
aria-valuetext={
simulated?.seconds
? translate("progressUnknown", {
replacements: { seconds: Math.round(simulated?.seconds) },
seconds: Math.round(simulated?.seconds),
})
: translate("progress", {
replacements: {
current: Math.round(value),
max: Math.round(valueMax),
},
current: Math.round(value),
max: Math.round(valueMax),
})
}
// biome-ignore lint/a11y/useAriaPropsForRole: We found that adding valueMin was not needed
@@ -79,17 +79,15 @@ describe("useI18n", () => {
};
const { result } = renderHook(() => useI18n("FileUpload", i18n));
const translate = result.current;
expect(
translate("item.uploading", { replacements: { name: "John", cnt: 3 } }),
).toBe("Hello, John. You have 3 messages.");
expect(translate("item.uploading", { name: "John", cnt: 3 })).toBe(
"Hello, John. You have 3 messages.",
);
});
test("should throw an error if replacement key is not found", () => {
const i18n = { item: { uploading: "Hello, {name}" } };
const { result } = renderHook(() => useI18n("FileUpload", i18n));
const translate = result.current;
expect(() =>
translate("item.uploading", { replacements: { other: "John" } }),
).toThrowError();
expect(() => translate("item.uploading", { other: "John" })).toThrowError();
});
});
@@ -27,7 +27,7 @@ export function useI18n<T extends Component>(
*/
const translate = (
keypath: NestedKeyOf<Translations[T]>,
options?: { replacements: Record<string, string | number> },
replacements?: Record<string, string | number>,
) => {
const text = get(
keypath,
@@ -37,19 +37,19 @@ export function useI18n<T extends Component>(
: [i18n[componentName]]),
);
if (options?.replacements) {
if (replacements) {
return text.replace(REPLACE_REGEX, (match) => {
const replacement = match.substring(1, match.length - 1);
if (options.replacements[replacement] === undefined) {
const replacementData = JSON.stringify(options.replacements);
if (replacements[replacement] === undefined) {
const replacementData = JSON.stringify(replacements);
throw new Error(
`Error translating key '${keypath}'. No replacement syntax ({}) found for key '${replacement}'. The following replacements were passed: '${replacementData}'`,
);
}
return options.replacements[replacement] as string; // can also be a number, but JS doesn't mind...
return replacements[replacement] as string; // can also be a number, but JS doesn't mind...
});
}
@@ -4,6 +4,7 @@ export default {
global: {
showMore: "Show more",
showLess: "Show less",
readOnly: "Read-only",
},
FileUpload: {
@@ -42,6 +43,9 @@ export default {
labelSuffix: "delete",
},
},
ErrorSummary: {
heading: "You must correct the following errors before you can continue:",
},
Loader: {
title: "Waiting…",
},
@@ -61,4 +65,9 @@ export default {
clear: "Clear",
search: "Search",
},
Textarea: {
maxLength: "Text area with a {maxLength} character limit.",
charsTooMany: "{chars} characters too many",
charsLeft: "{chars} characters left",
},
} satisfies Translations;
@@ -10,6 +10,7 @@ export default {
global: {
showMore: "Vis mer",
showLess: "Vis mindre",
readOnly: "Skrivebeskyttet",
},
FileUpload: {
@@ -49,6 +50,9 @@ export default {
labelSuffix: "slett",
},
},
ErrorSummary: {
heading: "Du må rette disse feilene før du kan fortsette:",
},
Loader: {
title: "Venter…",
},
@@ -68,4 +72,10 @@ export default {
clear: "Tøm",
search: "Søk",
},
Textarea: {
/** Screen readers only */
maxLength: "Tekstområde med plass til {maxLength} tegn.",
charsTooMany: "{chars} tegn for mye",
charsLeft: "{chars} tegn igjen",
},
} satisfies TranslationMap;
@@ -4,6 +4,7 @@ export default {
global: {
showMore: "Vis meir",
showLess: "Vis mindre",
readOnly: "Skrivebeskytta",
},
FileUpload: {
@@ -42,6 +43,9 @@ export default {
labelSuffix: "slett",
},
},
ErrorSummary: {
heading: "Du må rette desse feila før du kan halde fram:",
},
Loader: {
title: "Ventar…",
},
@@ -61,4 +65,9 @@ export default {
clear: "Tøm",
search: "Søk",
},
Textarea: {
maxLength: "Tekstområde med plass til {maxLength} teikn.",
charsTooMany: "{chars} teikn for mykje",
charsLeft: "{chars} teikn igjen",
},
} satisfies Translations;
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tailwind
## 7.4.3
## 7.4.2
## 7.4.1
## 7.4.0
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "7.4.1",
"version": "7.4.3",
"description": "Tailwind config based on @navikt/ds-tokens",
"author": "Aksel | NAV designsystem team",
"keywords": [
@@ -25,7 +25,7 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^7.4.1",
"@navikt/ds-tokens": "^7.4.3",
"color": "4.2.3",
"lodash": "^4.17.21",
"tailwindcss": "^3.3.3",
+4
View File
@@ -1,5 +1,9 @@
# @navikt/ds-tokens
## 7.4.3
## 7.4.2
## 7.4.1
## 7.4.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "7.4.1",
"version": "7.4.3",
"description": "Design-tokens for NAV designsystem",
"author": "Aksel | NAV designsystem team",
"keywords": [
+7 -1
View File
@@ -4,6 +4,10 @@
### @navikt/ds-react
- Pagination: More precise type for renderItem ([#3275](https://github.com/navikt/aksel/pull/3275))
* Combobox: Pressing 'Enter' after canceling autocomplete could result in using matching options in list. ([#3306](https://github.com/navikt/aksel/pull/3306))
- Tabs.List: Remove redundant icon titles ([#3282](https://github.com/navikt/aksel/pull/3282))
- Combobox: onToggleSelected is now called with correct value when autocomplete and new values are allowed. ([#3287](https://github.com/navikt/aksel/pull/3287))
@@ -26,7 +30,9 @@
### @navikt/ds-css
- Combobox: :bug: Fix scroll issue when using arrow keys to navigate list ([#3269](https://github.com/navikt/aksel/pull/3269))
- Table.ColumnHeader: Inherit text alignment in button ([#3319](https://github.com/navikt/aksel/pull/3319))
* Combobox: :bug: Fix scroll issue when using arrow keys to navigate list ([#3269](https://github.com/navikt/aksel/pull/3269))
## 7.3
+5 -5
View File
@@ -15,10 +15,10 @@
"playroom"
],
"dependencies": {
"@navikt/aksel-icons": "^7.4.1",
"@navikt/ds-css": "^7.4.1",
"@navikt/ds-react": "^7.4.1",
"@navikt/ds-tailwind": "^7.4.1",
"@navikt/ds-tokens": "^7.4.1"
"@navikt/aksel-icons": "^7.4.3",
"@navikt/ds-css": "^7.4.3",
"@navikt/ds-react": "^7.4.3",
"@navikt/ds-tailwind": "^7.4.3",
"@navikt/ds-tokens": "^7.4.3"
}
}
@@ -101,17 +101,18 @@ const ComponentExamples = ({ node }: CodeExamplesProps) => {
<Chips>
{node.dir.filer.map((fil) => {
const id = `${node.dir.title.toLowerCase()}demo-${fil.navn}`;
const isSelected = active === fil.navn;
return (
<Chips.Toggle
checkmark={false}
key={fil._key}
value={fil.navn}
selected={active === fil.navn}
selected={isSelected}
id={id}
onClick={() => {
const newPath = `${router.asPath.split("#")[0]}#${id}`;
if (newPath === router.asPath) return;
if (isSelected) return;
setUnloaded(true);
const newPath = `${router.asPath.split("#")[0]}#${id}`;
router.replace(newPath);
}}
>
+17 -17
View File
@@ -3711,7 +3711,7 @@ __metadata:
languageName: node
linkType: hard
"@navikt/aksel-icons@npm:^7.4.0, @navikt/aksel-icons@workspace:@navikt/aksel-icons":
"@navikt/aksel-icons@npm:^7.4.1, @navikt/aksel-icons@workspace:@navikt/aksel-icons":
version: 0.0.0-use.local
resolution: "@navikt/aksel-icons@workspace:@navikt/aksel-icons"
dependencies:
@@ -3742,8 +3742,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@navikt/aksel-stylelint@workspace:@navikt/aksel-stylelint"
dependencies:
"@navikt/ds-css": "npm:^7.4.0"
"@navikt/ds-tokens": "npm:^7.4.0"
"@navikt/ds-css": "npm:^7.4.1"
"@navikt/ds-tokens": "npm:^7.4.1"
concurrently: "npm:9.0.1"
postcss-selector-parser: "npm:^6.0.13"
postcss-value-parser: "npm:^4.2.0"
@@ -3758,7 +3758,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@navikt/aksel@workspace:@navikt/aksel"
dependencies:
"@navikt/ds-css": "npm:7.4.0"
"@navikt/ds-css": "npm:7.4.1"
axios: "npm:1.7.4"
chalk: "npm:4.1.0"
clipboardy: "npm:^2.3.0"
@@ -3779,11 +3779,11 @@ __metadata:
languageName: unknown
linkType: soft
"@navikt/ds-css@npm:*, @navikt/ds-css@npm:7.4.0, @navikt/ds-css@npm:^7.4.0, @navikt/ds-css@workspace:@navikt/core/css":
"@navikt/ds-css@npm:*, @navikt/ds-css@npm:7.4.1, @navikt/ds-css@npm:^7.4.1, @navikt/ds-css@workspace:@navikt/core/css":
version: 0.0.0-use.local
resolution: "@navikt/ds-css@workspace:@navikt/core/css"
dependencies:
"@navikt/ds-tokens": "npm:^7.4.0"
"@navikt/ds-tokens": "npm:^7.4.1"
autoprefixer: "npm:^10.4.20"
cssnano: "npm:6.0.0"
fast-glob: "npm:3.2.11"
@@ -3797,14 +3797,14 @@ __metadata:
languageName: unknown
linkType: soft
"@navikt/ds-react@npm:*, @navikt/ds-react@npm:^7.4.0, @navikt/ds-react@workspace:@navikt/core/react":
"@navikt/ds-react@npm:*, @navikt/ds-react@npm:^7.4.1, @navikt/ds-react@workspace:@navikt/core/react":
version: 0.0.0-use.local
resolution: "@navikt/ds-react@workspace:@navikt/core/react"
dependencies:
"@floating-ui/react": "npm:0.25.4"
"@floating-ui/react-dom": "npm:^2.0.9"
"@navikt/aksel-icons": "npm:^7.4.0"
"@navikt/ds-tokens": "npm:^7.4.0"
"@navikt/aksel-icons": "npm:^7.4.1"
"@navikt/ds-tokens": "npm:^7.4.1"
"@testing-library/dom": "npm:9.3.4"
"@testing-library/jest-dom": "npm:^5.16.0"
"@testing-library/react": "npm:^15.0.7"
@@ -3832,11 +3832,11 @@ __metadata:
languageName: unknown
linkType: soft
"@navikt/ds-tailwind@npm:^7.4.0, @navikt/ds-tailwind@workspace:@navikt/core/tailwind":
"@navikt/ds-tailwind@npm:^7.4.1, @navikt/ds-tailwind@workspace:@navikt/core/tailwind":
version: 0.0.0-use.local
resolution: "@navikt/ds-tailwind@workspace:@navikt/core/tailwind"
dependencies:
"@navikt/ds-tokens": "npm:^7.4.0"
"@navikt/ds-tokens": "npm:^7.4.1"
color: "npm:4.2.3"
lodash: "npm:^4.17.21"
tailwindcss: "npm:^3.3.3"
@@ -3846,7 +3846,7 @@ __metadata:
languageName: unknown
linkType: soft
"@navikt/ds-tokens@npm:^7.4.0, @navikt/ds-tokens@workspace:@navikt/core/tokens":
"@navikt/ds-tokens@npm:^7.4.1, @navikt/ds-tokens@workspace:@navikt/core/tokens":
version: 0.0.0-use.local
resolution: "@navikt/ds-tokens@workspace:@navikt/core/tokens"
dependencies:
@@ -7729,11 +7729,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "aksel.nav.no@workspace:aksel.nav.no"
dependencies:
"@navikt/aksel-icons": "npm:^7.4.0"
"@navikt/ds-css": "npm:^7.4.0"
"@navikt/ds-react": "npm:^7.4.0"
"@navikt/ds-tailwind": "npm:^7.4.0"
"@navikt/ds-tokens": "npm:^7.4.0"
"@navikt/aksel-icons": "npm:^7.4.1"
"@navikt/ds-css": "npm:^7.4.1"
"@navikt/ds-react": "npm:^7.4.1"
"@navikt/ds-tailwind": "npm:^7.4.1"
"@navikt/ds-tokens": "npm:^7.4.1"
languageName: unknown
linkType: soft