diff --git a/.changeset/shy-cameras-tickle-duplicate.md b/.changeset/shy-cameras-tickle-duplicate.md new file mode 100644 index 00000000000..5a786bcae36 --- /dev/null +++ b/.changeset/shy-cameras-tickle-duplicate.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-css": minor +--- + +Skjema: De fleste skjemakomponenter har nå styling for `readOnly`-state diff --git a/.changeset/shy-cameras-tickle.md b/.changeset/shy-cameras-tickle.md new file mode 100644 index 00000000000..8463171d1a8 --- /dev/null +++ b/.changeset/shy-cameras-tickle.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-react": minor +--- + +Skjema: De fleste skjemakomponenter støtter nå `readOnly`-state diff --git a/.storybook/preview.jsx b/.storybook/preview.jsx index 58a1183893b..47c3fb6c8f2 100644 --- a/.storybook/preview.jsx +++ b/.storybook/preview.jsx @@ -4,7 +4,7 @@ import "./layout.css"; export const parameters = { options: { storySort: { - method: "", + method: "alphabetical", order: ["Intro", "ds-react", ["form"], ["Default"], "ds-icons"], locales: "", }, diff --git a/@navikt/core/css/date.css b/@navikt/core/css/date.css index 33343edb516..15d3000fb5f 100644 --- a/@navikt/core/css/date.css +++ b/@navikt/core/css/date.css @@ -282,3 +282,9 @@ .navds-date__popover:where(.navds-popover) { border: none; } + +/* Readonly */ + +.navds-date__field--readonly .navds-date__field-button { + cursor: default; +} diff --git a/@navikt/core/css/form/form.css b/@navikt/core/css/form/form.css index 6c19bf25774..31001f882cc 100644 --- a/@navikt/core/css/form/form.css +++ b/@navikt/core/css/form/form.css @@ -36,3 +36,9 @@ .navds-form-field__subdescription { color: var(--ac-form-subdescription, var(--a-text-subtle)); } + +.navds-form-field__readonly-icon { + margin-right: var(--a-spacing-2); + vertical-align: middle; + margin-bottom: 3px; +} diff --git a/@navikt/core/css/form/radio-checkbox.css b/@navikt/core/css/form/radio-checkbox.css index b6cacca1c96..435d6dec887 100644 --- a/@navikt/core/css/form/radio-checkbox.css +++ b/@navikt/core/css/form/radio-checkbox.css @@ -22,6 +22,9 @@ cursor: pointer; display: flex; gap: var(--a-spacing-2); + + --__ac-radio-checkbox-readonly-bg: var(--a-surface-subtle); + --__ac-radio-checkbox-readonly-border: var(--a-border-subtle); } .navds-checkbox__label::before, @@ -102,7 +105,7 @@ background-color: var(--ac-radio-checkbox-bg, var(--a-surface-default)); width: 0.75rem; height: 0.25rem; - border-radius: 1px; /* Custom value OK */ + border-radius: 1px; flex-shrink: 0; } @@ -244,3 +247,53 @@ .navds-radio--disabled > .navds-radio__label { cursor: not-allowed; } + +/* Readonly */ +.navds-checkbox--readonly > :where(.navds-checkbox__input, .navds-checkbox__label), +.navds-radio--readonly > :where(.navds-radio__input, .navds-radio__label) { + cursor: default; +} + +.navds-checkbox--readonly > .navds-checkbox__input:hover + .navds-checkbox__label, +.navds-radio--readonly > .navds-radio__input:hover + .navds-radio__label { + color: var(--a-text-default); +} + +.navds-checkbox--readonly + > .navds-checkbox__input:not(:disabled):not(:checked):not(:indeterminate) + + .navds-checkbox__label::before, +.navds-checkbox--readonly + > .navds-checkbox__input:hover:not(:checked):not(:indeterminate):not(:focus-visible) + + .navds-checkbox__label::before, +.navds-radio--readonly > .navds-radio__input:not(:disabled):not(:checked):not(:indeterminate) + .navds-radio__label::before, +.navds-radio--readonly + > .navds-radio__input:hover:not(:checked):not(:indeterminate):not(:focus-visible) + + .navds-radio__label::before { + background-color: var(--__ac-radio-checkbox-readonly-bg); + box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border); +} + +.navds-checkbox--readonly > .navds-checkbox__input:focus-visible + .navds-checkbox__label::before, +.navds-radio--readonly > .navds-radio__input:focus-visible + .navds-radio__label::before { + --__ac-radio-checkbox-readonly-border: var(--a-border-subtle), var(--a-shadow-focus); +} + +.navds-checkbox--readonly > .navds-checkbox__input:checked + .navds-checkbox__label::before { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMyAxMCI+ICAgIDxnPiAgICA8cGF0aCBmaWxsPSJyZ2JhKDAsIDAsIDAsIDAuNjUpIiBkPSJNNCwxMGMtMC40LDAtMC44LTAuMS0xLjEtMC40TDAuNCw3LjFDMC4xLDYuOCwwLDYuNCwwLDZzMC4yLTAuOCwwLjUtMS4xQzEsNC40LDIsNC40LDIuNSw0LjlMNCw2LjRsNi40LTYgICAgQzEwLjgsMC4xLDExLjEsMCwxMS41LDBjMC40LDAsMC44LDAuMiwxLDAuNWMwLjYsMC42LDAuNSwxLjYtMC4xLDIuMXYwTDUsOS42QzQuNyw5LjksNC40LDEwLDQsMTB6IE0xMS44LDEuOUwxMS44LDEuOSAgICBDMTEuOCwxLjksMTEuOCwxLjksMTEuOCwxLjl6IE0xMS4yLDEuMUMxMS4yLDEuMSwxMS4yLDEuMSwxMS4yLDEuMUwxMS4yLDEuMXoiLz4gICAgPC9nPjwvc3ZnPg==); + box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border); + background-color: var(--__ac-radio-checkbox-readonly-bg); +} + +.navds-radio--readonly > .navds-radio__input:checked + .navds-radio__label::before { + box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border), inset 0 0 0 4px var(--__ac-radio-checkbox-readonly-bg); + background-color: var(--a-icon-subtle); +} + +.navds-checkbox--readonly > .navds-checkbox__input:indeterminate + .navds-checkbox__label::before { + box-shadow: inset 0 0 0 2px var(--__ac-radio-checkbox-readonly-border); + background-color: var(--__ac-radio-checkbox-readonly-bg); +} + +.navds-checkbox--readonly > .navds-checkbox__input:indeterminate + .navds-checkbox__label::after { + background-color: var(--a-icon-subtle); +} diff --git a/@navikt/core/css/form/select.css b/@navikt/core/css/form/select.css index d56f5665ee8..f0230b52ab2 100644 --- a/@navikt/core/css/form/select.css +++ b/@navikt/core/css/form/select.css @@ -79,3 +79,9 @@ .navds-select__input:disabled > option { color: var(--a-text-default); } + +.navds-select--readonly .navds-select__input { + background-color: var(--a-surface-subtle); + border-color: var(--a-border-subtle); + cursor: default; +} diff --git a/@navikt/core/css/form/switch.css b/@navikt/core/css/form/switch.css index f36c5c036dc..d62157d7180 100644 --- a/@navikt/core/css/form/switch.css +++ b/@navikt/core/css/form/switch.css @@ -178,3 +178,40 @@ .navds-switch__input:disabled ~ .navds-switch__label-wrapper { cursor: not-allowed; } + +/* Readonly */ +.navds-switch--readonly > .navds-switch__track, +.navds-switch--readonly > .navds-switch__input:hover ~ .navds-switch__track, +.navds-switch--readonly > .navds-switch__input:checked ~ .navds-switch__track, +.navds-switch--readonly > .navds-switch__input:checked:hover ~ .navds-switch__track { + background-color: var(--a-surface-neutral-moderate); +} + +.navds-switch--readonly > .navds-switch__label-wrapper, +.navds-switch--readonly > .navds-switch__input { + cursor: default; +} + +.navds-switch--readonly > .navds-switch__input:hover ~ .navds-switch__label-wrapper, +.navds-switch--readonly .navds-switch__label-wrapper:hover { + color: var(--a-text-default); +} + +.navds-switch--readonly .navds-switch__thumb { + background-color: var(--a-surface-subtle); + box-shadow: 0 0 0 2px var(--a-border-default); +} + +.navds-switch--readonly > .navds-switch__input:checked ~ .navds-switch__track > .navds-switch__thumb { + color: var(--a-icon-subtle); +} + +@media (hover: hover) and (pointer: fine) { + .navds-switch--readonly > .navds-switch__input:hover ~ .navds-switch__track > .navds-switch__thumb { + transform: translateX(0); + } + + .navds-switch--readonly > .navds-switch__input:checked:hover ~ .navds-switch__track > .navds-switch__thumb { + transform: translateX(20px); + } +} diff --git a/@navikt/core/css/form/text-field.css b/@navikt/core/css/form/text-field.css index 1822f9aef2d..8effbba6ac8 100644 --- a/@navikt/core/css/form/text-field.css +++ b/@navikt/core/css/form/text-field.css @@ -73,11 +73,10 @@ cursor: not-allowed; } -.navds-text-field__input[readonly] { - background-color: var(--ac-textfield-bg, var(--a-surface-default)); - border-color: var(--ac-textfield-border, var(--a-border-default)); - box-shadow: none; - cursor: not-allowed; +.navds-text-field--readonly .navds-text-field__input { + background-color: var(--a-surface-subtle); + border-color: var(--a-border-subtle); + cursor: default; } /** diff --git a/@navikt/core/css/form/textarea.css b/@navikt/core/css/form/textarea.css index 60d3a28e8c3..af7eddb6769 100644 --- a/@navikt/core/css/form/textarea.css +++ b/@navikt/core/css/form/textarea.css @@ -99,9 +99,8 @@ cursor: not-allowed; } -.navds-textarea__input[readonly] { - background-color: var(--ac-textarea-bg, var(--a-surface-default)); - border-color: var(--ac-textarea-border, var(--a-border-default)); - box-shadow: none; - cursor: not-allowed; +.navds-textarea--readonly .navds-textarea__input { + background-color: var(--a-surface-subtle); + border-color: var(--a-border-subtle); + cursor: default; } diff --git a/@navikt/core/react/src/date/DateInput.tsx b/@navikt/core/react/src/date/DateInput.tsx index 871ca97a288..f76edc17d92 100644 --- a/@navikt/core/react/src/date/DateInput.tsx +++ b/@navikt/core/react/src/date/DateInput.tsx @@ -4,6 +4,7 @@ import React, { forwardRef, InputHTMLAttributes } from "react"; import { BodyShort, ErrorMessage, Label, omit } from ".."; import { FormFieldProps, useFormField } from "../form/useFormField"; import { useDateInputContext } from "./context"; +import { ReadOnlyIcon } from "../form/ReadOnlyIcon"; export interface DateInputProps extends FormFieldProps, @@ -57,6 +58,7 @@ const DateInput = forwardRef((props, ref) => { errorId, showErrorMsg, hasError, + readOnly, } = useFormField(props, conditionalVariables.prefix); return ( @@ -71,6 +73,8 @@ const DateInput = forwardRef((props, ref) => { "navds-date__field--error": hasError, "navds-form-field--disabled": !!inputProps.disabled, "navds-text-field--disabled": !!inputProps.disabled, + "navds-text-field--readonly": readOnly, + "navds-date__field--readonly": readOnly, } )} > @@ -81,6 +85,7 @@ const DateInput = forwardRef((props, ref) => { "navds-sr-only": hideLabel, })} > + {label} {!!description && ( @@ -102,6 +107,7 @@ const DateInput = forwardRef((props, ref) => { {...inputProps} autoComplete="off" aria-controls={ariaId} + readOnly={readOnly} className={cl( "navds-date__field-input", "navds-text-field__input", @@ -111,8 +117,8 @@ const DateInput = forwardRef((props, ref) => { size={14} />