diff --git a/.changeset/nasty-files-know.md b/.changeset/nasty-files-know.md new file mode 100644 index 0000000000..74ecf69f78 --- /dev/null +++ b/.changeset/nasty-files-know.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-react": patch +--- + +:wheelchair: Textarea: Byttet fra `aria-live` til `role=status` på telleren for bedre semantikk diff --git a/.changeset/real-wasps-kiss.md b/.changeset/real-wasps-kiss.md new file mode 100644 index 0000000000..0c59733600 --- /dev/null +++ b/.changeset/real-wasps-kiss.md @@ -0,0 +1,6 @@ +--- +"@navikt/ds-react": patch +"@navikt/ds-css": patch +--- + +:bug: Textarea: Teller flyttet ut av tekstfeltet for å unngå overlapp og misforståelser diff --git a/@navikt/core/css/form/textarea.css b/@navikt/core/css/form/textarea.css index 7e0b8761e6..9f7df7606d 100644 --- a/@navikt/core/css/form/textarea.css +++ b/@navikt/core/css/form/textarea.css @@ -37,10 +37,6 @@ color: var(--ac-textarea-text, var(--__ac-textarea-text, var(--a-text-default))); } -.navds-textarea--counter { - padding-bottom: var(--a-spacing-8); -} - .navds-textarea__input::placeholder { color: var(--ac-textarea-placeholder, var(--__ac-textarea-placeholder, var(--a-text-subtle))); } @@ -65,19 +61,9 @@ padding: var(--a-spacing-1-alt); } -.navds-form-field--small .navds-textarea--counter.navds-textarea__input { - padding-bottom: var(--a-spacing-7); -} - .navds-textarea__counter { - pointer-events: none; + margin-top: var(--a-spacing-05); color: var(--ac-textarea-counter-text, var(--__ac-textarea-counter-text, var(--a-text-subtle))); - font-style: italic; - position: absolute; - text-align: left; - left: 0.0625rem; - bottom: 0.0625rem; - padding: var(--a-spacing-1) var(--a-spacing-2); } .navds-textarea__counter--error { diff --git a/@navikt/core/react/src/form/Textarea.tsx b/@navikt/core/react/src/form/Textarea.tsx index a6988151d7..4353744e1a 100644 --- a/@navikt/core/react/src/form/Textarea.tsx +++ b/@navikt/core/react/src/form/Textarea.tsx @@ -1,10 +1,11 @@ import cl from "clsx"; import React, { forwardRef, useState } from "react"; import { BodyShort, ErrorMessage, Label } from "../typography"; +import { omit, useId } from "../util"; import TextareaAutosize from "../util/TextareaAutoSize"; -import { FormFieldProps, useFormField } from "./useFormField"; import { ReadOnlyIcon } from "./ReadOnlyIcon"; -import { omit, useId } from "../util"; +import Counter from "./TextareaCounter"; +import { FormFieldProps, useFormField } from "./useFormField"; /** * TODO: Mulighet for lokalisering av sr-only/counter text @@ -27,7 +28,6 @@ export interface TextareaProps defaultValue?: string; /** * Maximum number of character rows to display. - * @bug Internal scrolling with `maxLength` scrolls over maxLength-text */ maxRows?: number; /** @@ -50,7 +50,7 @@ export interface TextareaProps * i18n-translations for counter-text */ i18n?: { - /** @default Antall tegn igjen */ + /** @default tegn igjen */ counterLeft?: string; /** @default tegn for mye */ counterTooMuch?: string; @@ -162,14 +162,11 @@ export const Textarea = forwardRef( className={cl( "navds-textarea__input", "navds-body-short", - `navds-body-short--${size ?? "medium"}`, - { - "navds-textarea--counter": hasMaxLength, - } + `navds-body-short--${size ?? "medium"}` )} {...(describedBy ? { "aria-describedby": describedBy } : {})} /> - {hasMaxLength && ( + {hasMaxLength && !readOnly && !inputProps.disabled && ( <> {`Tekstområde med plass til ${maxLength} tegn.`} @@ -198,22 +195,4 @@ export const Textarea = forwardRef( } ); -export const Counter = ({ maxLength, currentLength, size, i18n }) => { - const difference = maxLength - currentLength; - - return ( - - {difference < 0 - ? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}` - : `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`} - - ); -}; - export default Textarea; diff --git a/@navikt/core/react/src/form/TextareaCounter.tsx b/@navikt/core/react/src/form/TextareaCounter.tsx new file mode 100644 index 0000000000..95123a290a --- /dev/null +++ b/@navikt/core/react/src/form/TextareaCounter.tsx @@ -0,0 +1,31 @@ +import cl from "clsx"; +import React from "react"; +import { BodyShort } from "../typography"; +import type { TextareaProps } from "./Textarea"; + +interface Props { + maxLength: number; + currentLength: number; + size: TextareaProps["size"]; + i18n: TextareaProps["i18n"]; +} + +const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => { + const difference = maxLength - currentLength; + + return ( + + {difference < 0 + ? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}` + : `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`} + + ); +}; + +export default TextareaCounter; diff --git a/@navikt/core/react/src/form/index.ts b/@navikt/core/react/src/form/index.ts index 7efa4997eb..2a73063766 100644 --- a/@navikt/core/react/src/form/index.ts +++ b/@navikt/core/react/src/form/index.ts @@ -1,15 +1,20 @@ +export { + default as ConfirmationPanel, + type ConfirmationPanelProps, +} from "./ConfirmationPanel"; +export { Fieldset, FieldsetContext, type FieldsetProps } from "./Fieldset"; +export { default as Select, type SelectProps } from "./Select"; +export { default as Switch, type SwitchProps } from "./Switch"; +export { default as TextField, type TextFieldProps } from "./TextField"; +export { default as Textarea, type TextareaProps } from "./Textarea"; export { Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, } from "./checkbox"; -export { - type ConfirmationPanelProps, - default as ConfirmationPanel, -} from "./ConfirmationPanel"; +export { Combobox as UNSAFE_Combobox, type ComboboxProps } from "./combobox"; export { ErrorSummary, type ErrorSummaryProps } from "./error-summary"; -export { Fieldset, FieldsetContext, type FieldsetProps } from "./Fieldset"; export { Radio, RadioGroup, @@ -17,8 +22,3 @@ export { type RadioProps, } from "./radio"; export { Search, type SearchClearEvent, type SearchProps } from "./search"; -export { Combobox as UNSAFE_Combobox, type ComboboxProps } from "./combobox"; -export { default as Select, type SelectProps } from "./Select"; -export { default as Switch, type SwitchProps } from "./Switch"; -export { Counter, default as Textarea, type TextareaProps } from "./Textarea"; -export { default as TextField, type TextFieldProps } from "./TextField"; diff --git a/@navikt/core/react/src/form/stories/textarea.stories.tsx b/@navikt/core/react/src/form/stories/textarea.stories.tsx index 9e848e64b2..49f223dc85 100644 --- a/@navikt/core/react/src/form/stories/textarea.stories.tsx +++ b/@navikt/core/react/src/form/stories/textarea.stories.tsx @@ -14,7 +14,7 @@ export const Default: StoryObj = { }, args: { - maxLength: 100, + maxLength: 0, label: "Ipsum enim quis culpa", resize: false, }, @@ -28,6 +28,7 @@ export const Default: StoryObj = { error: { type: "string" }, hideLabel: { type: "boolean" }, disabled: { type: "boolean" }, + readOnly: { type: "boolean" }, maxRows: { type: "number" }, minRows: { type: "number" }, }, @@ -66,6 +67,20 @@ export const Error = () => { error="Consectetur labore velit eiusmod Lorem ut nostrud mollit labore ullamco laboris laboris in." size="small" /> + +