@navikt/ds-react 4.5.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_docs.json +285 -0
- package/cjs/date/DateInput.js +10 -4
- package/cjs/date/hooks/useDatepicker.js +3 -0
- package/cjs/date/hooks/useMonthPicker.js +3 -0
- package/cjs/date/hooks/useRangeDatepicker.js +3 -0
- package/cjs/form/Fieldset/Fieldset.js +11 -4
- package/cjs/form/ReadOnlyIcon.js +15 -0
- package/cjs/form/Select.js +24 -3
- package/cjs/form/Switch.js +21 -8
- package/cjs/form/TextField.js +8 -3
- package/cjs/form/Textarea.js +8 -3
- package/cjs/form/checkbox/Checkbox.js +7 -2
- package/cjs/form/checkbox/CheckboxGroup.js +1 -1
- package/cjs/form/checkbox/useCheckbox.js +12 -2
- package/cjs/form/radio/Radio.js +3 -2
- package/cjs/form/radio/RadioGroup.js +2 -2
- package/cjs/form/radio/useRadio.js +12 -2
- package/cjs/form/search/Search.js +1 -1
- package/cjs/form/useFormField.js +11 -9
- package/esm/date/DateInput.js +10 -4
- package/esm/date/DateInput.js.map +1 -1
- package/esm/date/hooks/useDatepicker.js +3 -0
- package/esm/date/hooks/useDatepicker.js.map +1 -1
- package/esm/date/hooks/useMonthPicker.js +3 -0
- package/esm/date/hooks/useMonthPicker.js.map +1 -1
- package/esm/date/hooks/useRangeDatepicker.js +3 -0
- package/esm/date/hooks/useRangeDatepicker.js.map +1 -1
- package/esm/form/ConfirmationPanel.d.ts +1 -1
- package/esm/form/Fieldset/Fieldset.d.ts +5 -0
- package/esm/form/Fieldset/Fieldset.js +11 -4
- package/esm/form/Fieldset/Fieldset.js.map +1 -1
- package/esm/form/Fieldset/useFieldset.d.ts +2 -1
- package/esm/form/ReadOnlyIcon.d.ts +5 -0
- package/esm/form/ReadOnlyIcon.js +9 -0
- package/esm/form/ReadOnlyIcon.js.map +1 -0
- package/esm/form/Select.js +24 -3
- package/esm/form/Select.js.map +1 -1
- package/esm/form/Switch.d.ts +1 -1
- package/esm/form/Switch.js +21 -8
- package/esm/form/Switch.js.map +1 -1
- package/esm/form/TextField.js +8 -3
- package/esm/form/TextField.js.map +1 -1
- package/esm/form/Textarea.js +8 -3
- package/esm/form/Textarea.js.map +1 -1
- package/esm/form/checkbox/Checkbox.js +7 -2
- package/esm/form/checkbox/Checkbox.js.map +1 -1
- package/esm/form/checkbox/CheckboxGroup.js +1 -1
- package/esm/form/checkbox/CheckboxGroup.js.map +1 -1
- package/esm/form/checkbox/useCheckbox.d.ts +5 -2
- package/esm/form/checkbox/useCheckbox.js +12 -2
- package/esm/form/checkbox/useCheckbox.js.map +1 -1
- package/esm/form/radio/Radio.d.ts +1 -1
- package/esm/form/radio/Radio.js +3 -2
- package/esm/form/radio/Radio.js.map +1 -1
- package/esm/form/radio/RadioGroup.js +2 -2
- package/esm/form/radio/RadioGroup.js.map +1 -1
- package/esm/form/radio/useRadio.d.ts +4 -2
- package/esm/form/radio/useRadio.js +12 -2
- package/esm/form/radio/useRadio.js.map +1 -1
- package/esm/form/search/Search.d.ts +1 -1
- package/esm/form/search/Search.js +1 -1
- package/esm/form/search/Search.js.map +1 -1
- package/esm/form/useFormField.d.ts +7 -2
- package/esm/form/useFormField.js +11 -9
- package/esm/form/useFormField.js.map +1 -1
- package/package.json +2 -2
- package/src/date/DateInput.tsx +9 -2
- package/src/date/datepicker/datepicker.stories.tsx +22 -0
- package/src/date/hooks/useDatepicker.tsx +3 -0
- package/src/date/hooks/useMonthPicker.tsx +3 -0
- package/src/date/hooks/useRangeDatepicker.tsx +3 -0
- package/src/form/ConfirmationPanel.tsx +1 -1
- package/src/form/Fieldset/Fieldset.tsx +15 -2
- package/src/form/ReadOnlyIcon.tsx +20 -0
- package/src/form/Select.tsx +29 -2
- package/src/form/Switch.tsx +20 -9
- package/src/form/TextField.tsx +7 -0
- package/src/form/Textarea.tsx +6 -0
- package/src/form/checkbox/Checkbox.tsx +12 -2
- package/src/form/checkbox/CheckboxGroup.tsx +1 -0
- package/src/form/checkbox/checkbox.stories.tsx +35 -1
- package/src/form/checkbox/useCheckbox.ts +13 -1
- package/src/form/radio/Radio.tsx +4 -3
- package/src/form/radio/RadioGroup.tsx +3 -0
- package/src/form/radio/radio.stories.tsx +27 -0
- package/src/form/radio/useRadio.ts +12 -1
- package/src/form/search/Search.tsx +2 -2
- package/src/form/stories/select.stories.tsx +17 -0
- package/src/form/stories/switch.stories.tsx +14 -0
- package/src/form/stories/text-field.stories.tsx +14 -0
- package/src/form/stories/textarea.stories.tsx +19 -0
- package/src/form/useFormField.ts +25 -3
|
@@ -25,6 +25,10 @@ export interface FormFieldProps {
|
|
|
25
25
|
* Override internal id
|
|
26
26
|
*/
|
|
27
27
|
id?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Read only-state
|
|
30
|
+
*/
|
|
31
|
+
readOnly?: boolean;
|
|
28
32
|
}
|
|
29
33
|
/**
|
|
30
34
|
* Handles props and their state for various form-fields in context with Fieldset
|
|
@@ -35,10 +39,11 @@ export declare const useFormField: (props: FormFieldProps, prefix: string) => {
|
|
|
35
39
|
errorId: string;
|
|
36
40
|
inputDescriptionId: string;
|
|
37
41
|
size: "medium" | "small";
|
|
42
|
+
readOnly: true | undefined;
|
|
38
43
|
inputProps: {
|
|
39
|
-
id: string;
|
|
40
|
-
"aria-invalid": boolean;
|
|
41
44
|
"aria-describedby": string | undefined;
|
|
42
45
|
disabled: boolean | undefined;
|
|
46
|
+
"aria-invalid"?: boolean | undefined;
|
|
47
|
+
id: string;
|
|
43
48
|
};
|
|
44
49
|
};
|
package/esm/form/useFormField.js
CHANGED
|
@@ -14,24 +14,26 @@ export const useFormField = (props, prefix) => {
|
|
|
14
14
|
const errorId = propErrorId !== null && propErrorId !== void 0 ? propErrorId : `${prefix}-error-${genId}`;
|
|
15
15
|
const inputDescriptionId = `${prefix}-description-${genId}`;
|
|
16
16
|
const disabled = (fieldset === null || fieldset === void 0 ? void 0 : fieldset.disabled) || props.disabled;
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const readOnly = (((fieldset === null || fieldset === void 0 ? void 0 : fieldset.readOnly) || props.readOnly) && !disabled) || undefined;
|
|
18
|
+
const hasError = !disabled && !readOnly && !!(error || (fieldset === null || fieldset === void 0 ? void 0 : fieldset.error));
|
|
19
|
+
const showErrorMsg = !disabled && !readOnly && !!error && typeof error !== "boolean";
|
|
20
|
+
const ariaInvalid = Object.assign({}, (hasError ? { "aria-invalid": true } : {}));
|
|
21
|
+
if ((props === null || props === void 0 ? void 0 : props.required) && process.env.NODE_ENV !== "production") {
|
|
22
|
+
console.warn("Aksel: Use of 'required' in form-elements is heavily discuouraged. Docs about why here:");
|
|
23
|
+
console.warn("https://aksel.nav.no/god-praksis/artikler/obligatoriske-og-valgfrie-skjemafelter#h3bfe00453471");
|
|
24
|
+
}
|
|
19
25
|
return {
|
|
20
26
|
showErrorMsg,
|
|
21
27
|
hasError,
|
|
22
28
|
errorId,
|
|
23
29
|
inputDescriptionId,
|
|
24
30
|
size: (_b = size !== null && size !== void 0 ? size : fieldset === null || fieldset === void 0 ? void 0 : fieldset.size) !== null && _b !== void 0 ? _b : "medium",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"aria-invalid": hasError,
|
|
28
|
-
"aria-describedby": cl(props["aria-describedby"], {
|
|
31
|
+
readOnly,
|
|
32
|
+
inputProps: Object.assign(Object.assign({ id }, ariaInvalid), { "aria-describedby": cl(props["aria-describedby"], {
|
|
29
33
|
[inputDescriptionId]: !!(props === null || props === void 0 ? void 0 : props.description) && typeof (props === null || props === void 0 ? void 0 : props.description) === "string",
|
|
30
34
|
[errorId]: showErrorMsg,
|
|
31
35
|
[(_c = fieldset === null || fieldset === void 0 ? void 0 : fieldset.errorId) !== null && _c !== void 0 ? _c : ""]: hasError && !!(fieldset === null || fieldset === void 0 ? void 0 : fieldset.error),
|
|
32
|
-
}) || undefined,
|
|
33
|
-
disabled,
|
|
34
|
-
},
|
|
36
|
+
}) || undefined, disabled }),
|
|
35
37
|
};
|
|
36
38
|
};
|
|
37
39
|
//# sourceMappingURL=useFormField.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFormField.js","sourceRoot":"","sources":["../../src/form/useFormField.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"useFormField.js","sourceRoot":"","sources":["../../src/form/useFormField.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAkCjC;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAqB,EAAE,MAAc,EAAE,EAAE;;IACpE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAEpD,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;IAEtB,MAAM,EAAE,GAAG,MAAA,KAAK,CAAC,EAAE,mCAAI,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,GAAG,MAAM,UAAU,KAAK,EAAE,CAAC;IAC1D,MAAM,kBAAkB,GAAG,GAAG,MAAM,gBAAgB,KAAK,EAAE,CAAC;IAE5D,MAAM,QAAQ,GAAG,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,QAAQ,KAAI,KAAK,CAAC,QAAQ,CAAC;IACtD,MAAM,QAAQ,GACZ,CAAC,CAAC,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,QAAQ,KAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;IAErE,MAAM,QAAQ,GACZ,CAAC,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,KAAK,KAAI,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,KAAK,CAAA,CAAC,CAAC;IACzD,MAAM,YAAY,GAChB,CAAC,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,SAAS,CAAC;IAElE,MAAM,WAAW,qBAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAE,CAAC;IAEtE,IAAI,CAAC,KAAa,aAAb,KAAK,uBAAL,KAAK,CAAU,QAAQ,KAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;QACrE,OAAO,CAAC,IAAI,CACV,yFAAyF,CAC1F,CAAC;QACF,OAAO,CAAC,IAAI,CACV,gGAAgG,CACjG,CAAC;KACH;IAED,OAAO;QACL,YAAY;QACZ,QAAQ;QACR,OAAO;QACP,kBAAkB;QAClB,IAAI,EAAE,MAAA,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,mCAAI,QAAQ;QACxC,QAAQ;QACR,UAAU,gCACR,EAAE,IACC,WAAW,KACd,kBAAkB,EAChB,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE;gBAC5B,CAAC,kBAAkB,CAAC,EAClB,CAAC,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,WAAW,CAAA,IAAI,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,WAAW,CAAA,KAAK,QAAQ;gBAChE,CAAC,OAAO,CAAC,EAAE,YAAY;gBACvB,CAAC,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,OAAO,mCAAI,EAAE,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,KAAK,CAAA;aACzD,CAAC,IAAI,SAAS,EAEjB,QAAQ,GACT;KACF,CAAC;AACJ,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navikt/ds-react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.1",
|
|
4
4
|
"description": "Aksel react-components for NAV designsystem",
|
|
5
5
|
"author": "Aksel | NAV designsystem team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@floating-ui/react": "0.24.1",
|
|
41
|
-
"@navikt/aksel-icons": "^4.
|
|
41
|
+
"@navikt/aksel-icons": "^4.6.1",
|
|
42
42
|
"@radix-ui/react-tabs": "1.0.0",
|
|
43
43
|
"@radix-ui/react-toggle-group": "1.0.0",
|
|
44
44
|
"clsx": "^1.2.1",
|
package/src/date/DateInput.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import React, { forwardRef, InputHTMLAttributes } from "react";
|
|
|
4
4
|
import { BodyShort, ErrorMessage, Label, omit } from "..";
|
|
5
5
|
import { FormFieldProps, useFormField } from "../form/useFormField";
|
|
6
6
|
import { useDateInputContext } from "./context";
|
|
7
|
+
import { ReadOnlyIcon } from "../form/ReadOnlyIcon";
|
|
7
8
|
|
|
8
9
|
export interface DateInputProps
|
|
9
10
|
extends FormFieldProps,
|
|
@@ -57,6 +58,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
57
58
|
errorId,
|
|
58
59
|
showErrorMsg,
|
|
59
60
|
hasError,
|
|
61
|
+
readOnly,
|
|
60
62
|
} = useFormField(props, conditionalVariables.prefix);
|
|
61
63
|
|
|
62
64
|
return (
|
|
@@ -71,6 +73,9 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
71
73
|
"navds-date__field--error": hasError,
|
|
72
74
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
73
75
|
"navds-text-field--disabled": !!inputProps.disabled,
|
|
76
|
+
"navds-form-field--readonly": readOnly,
|
|
77
|
+
"navds-text-field--readonly": readOnly,
|
|
78
|
+
"navds-date__field--readonly": readOnly,
|
|
74
79
|
}
|
|
75
80
|
)}
|
|
76
81
|
>
|
|
@@ -81,6 +86,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
81
86
|
"navds-sr-only": hideLabel,
|
|
82
87
|
})}
|
|
83
88
|
>
|
|
89
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
84
90
|
{label}
|
|
85
91
|
</Label>
|
|
86
92
|
{!!description && (
|
|
@@ -102,6 +108,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
102
108
|
{...inputProps}
|
|
103
109
|
autoComplete="off"
|
|
104
110
|
aria-controls={ariaId}
|
|
111
|
+
readOnly={readOnly}
|
|
105
112
|
className={cl(
|
|
106
113
|
"navds-date__field-input",
|
|
107
114
|
"navds-text-field__input",
|
|
@@ -111,8 +118,8 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
111
118
|
size={14}
|
|
112
119
|
/>
|
|
113
120
|
<button
|
|
114
|
-
disabled={inputProps.disabled}
|
|
115
|
-
tabIndex={open ? -1 : 0}
|
|
121
|
+
disabled={inputProps.disabled || readOnly}
|
|
122
|
+
tabIndex={readOnly ? -1 : open ? -1 : 0}
|
|
116
123
|
onClick={() => onOpen()}
|
|
117
124
|
type="button"
|
|
118
125
|
className="navds-date__field-button"
|
|
@@ -341,3 +341,25 @@ export const Size = () => {
|
|
|
341
341
|
</div>
|
|
342
342
|
);
|
|
343
343
|
};
|
|
344
|
+
|
|
345
|
+
export const Readonly = () => {
|
|
346
|
+
const { datepickerProps, inputProps } = useDatepicker({
|
|
347
|
+
fromDate: new Date("Aug 23 2019"),
|
|
348
|
+
toDate: new Date("Feb 23 2024"),
|
|
349
|
+
onDateChange: console.log,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<div style={{ display: "flex", gap: "1rem" }}>
|
|
354
|
+
<DatePicker {...datepickerProps} dropdownCaption>
|
|
355
|
+
<DatePicker.Input
|
|
356
|
+
size="medium"
|
|
357
|
+
{...inputProps}
|
|
358
|
+
value="01.02.2021"
|
|
359
|
+
label="Velg dato"
|
|
360
|
+
readOnly
|
|
361
|
+
/>
|
|
362
|
+
</DatePicker>
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
365
|
+
};
|
|
@@ -204,6 +204,9 @@ export const useDatepicker = (
|
|
|
204
204
|
};
|
|
205
205
|
|
|
206
206
|
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
|
207
|
+
if (e.target.readOnly) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
207
210
|
!open && openOnFocus && handleOpen(true);
|
|
208
211
|
let day = parseDate(
|
|
209
212
|
e.target.value,
|
|
@@ -201,6 +201,9 @@ export const useMonthpicker = (
|
|
|
201
201
|
};
|
|
202
202
|
|
|
203
203
|
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
|
|
204
|
+
if (e.target.readOnly) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
204
207
|
!open && openOnFocus && handleOpen(true);
|
|
205
208
|
let day = parseDate(
|
|
206
209
|
e.target.value,
|
|
@@ -6,7 +6,7 @@ import { useFormField } from "./useFormField";
|
|
|
6
6
|
export interface ConfirmationPanelProps
|
|
7
7
|
extends Omit<
|
|
8
8
|
CheckboxProps,
|
|
9
|
-
"children" | "indeterminate" | "hideLabel" | "error"
|
|
9
|
+
"children" | "indeterminate" | "hideLabel" | "error" | "readOnly"
|
|
10
10
|
> {
|
|
11
11
|
/**
|
|
12
12
|
* Additional information on panel
|
|
@@ -3,6 +3,7 @@ import React, { FieldsetHTMLAttributes, forwardRef, useContext } from "react";
|
|
|
3
3
|
import { BodyShort, ErrorMessage, Label, omit } from "../..";
|
|
4
4
|
import { FormFieldProps } from "../useFormField";
|
|
5
5
|
import { useFieldset } from "./useFieldset";
|
|
6
|
+
import { ReadOnlyIcon } from "../ReadOnlyIcon";
|
|
6
7
|
|
|
7
8
|
export type FieldsetContextProps = {
|
|
8
9
|
/**
|
|
@@ -21,6 +22,10 @@ export type FieldsetContextProps = {
|
|
|
21
22
|
* Sets fieldset and all form-children to disabled
|
|
22
23
|
*/
|
|
23
24
|
disabled: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Read only-state
|
|
27
|
+
*/
|
|
28
|
+
readOnly?: boolean;
|
|
24
29
|
};
|
|
25
30
|
|
|
26
31
|
export const FieldsetContext = React.createContext<FieldsetContextProps | null>(
|
|
@@ -47,6 +52,7 @@ export interface FieldsetProps
|
|
|
47
52
|
* @default true
|
|
48
53
|
*/
|
|
49
54
|
errorPropagation?: boolean;
|
|
55
|
+
nativeReadOnly?: boolean;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
@@ -57,6 +63,7 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
57
63
|
showErrorMsg,
|
|
58
64
|
hasError,
|
|
59
65
|
size,
|
|
66
|
+
readOnly,
|
|
60
67
|
inputDescriptionId,
|
|
61
68
|
} = useFieldset(props);
|
|
62
69
|
|
|
@@ -69,6 +76,7 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
69
76
|
legend,
|
|
70
77
|
description,
|
|
71
78
|
hideLegend,
|
|
79
|
+
nativeReadOnly = true,
|
|
72
80
|
...rest
|
|
73
81
|
} = props;
|
|
74
82
|
|
|
@@ -82,17 +90,21 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
82
90
|
}),
|
|
83
91
|
size,
|
|
84
92
|
disabled: props.disabled ?? false,
|
|
93
|
+
readOnly,
|
|
85
94
|
}}
|
|
86
95
|
>
|
|
87
96
|
<fieldset
|
|
88
|
-
{...omit(rest, ["errorId", "error", "size"])}
|
|
97
|
+
{...omit(rest, ["errorId", "error", "size", "readOnly"])}
|
|
89
98
|
{...inputProps}
|
|
90
99
|
ref={ref}
|
|
91
100
|
className={cl(
|
|
92
101
|
className,
|
|
93
102
|
"navds-fieldset",
|
|
94
103
|
`navds-fieldset--${size}`,
|
|
95
|
-
{
|
|
104
|
+
{
|
|
105
|
+
"navds-fieldset--error": hasError,
|
|
106
|
+
"navds-fieldset--readonly": readOnly,
|
|
107
|
+
}
|
|
96
108
|
)}
|
|
97
109
|
>
|
|
98
110
|
<Label
|
|
@@ -102,6 +114,7 @@ export const Fieldset = forwardRef<HTMLFieldSetElement, FieldsetProps>(
|
|
|
102
114
|
"navds-sr-only": !!hideLegend,
|
|
103
115
|
})}
|
|
104
116
|
>
|
|
117
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={nativeReadOnly} />
|
|
105
118
|
{legend}
|
|
106
119
|
</Label>
|
|
107
120
|
{!!description && (
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PadlockLockedFillIcon } from "@navikt/aksel-icons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export const ReadOnlyIcon = ({
|
|
5
|
+
readOnly,
|
|
6
|
+
nativeReadOnly = true,
|
|
7
|
+
}: {
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
nativeReadOnly?: boolean;
|
|
10
|
+
}) => {
|
|
11
|
+
if (readOnly) {
|
|
12
|
+
return (
|
|
13
|
+
<PadlockLockedFillIcon
|
|
14
|
+
{...(nativeReadOnly ? { "aria-hidden": true } : { title: "readonly" })}
|
|
15
|
+
className="navds-form-field__readonly-icon"
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
package/src/form/Select.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import React, { forwardRef, SelectHTMLAttributes } from "react";
|
|
|
4
4
|
import { ChevronDownIcon } from "@navikt/aksel-icons";
|
|
5
5
|
import { BodyShort, ErrorMessage, Label, omit } from "..";
|
|
6
6
|
import { FormFieldProps, useFormField } from "./useFormField";
|
|
7
|
+
import { ReadOnlyIcon } from "./ReadOnlyIcon";
|
|
7
8
|
|
|
8
9
|
export interface SelectProps
|
|
9
10
|
extends FormFieldProps,
|
|
@@ -55,7 +56,8 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
55
56
|
hasError,
|
|
56
57
|
size,
|
|
57
58
|
inputDescriptionId,
|
|
58
|
-
|
|
59
|
+
readOnly,
|
|
60
|
+
} = useFormField(props, "select");
|
|
59
61
|
|
|
60
62
|
const {
|
|
61
63
|
children,
|
|
@@ -68,6 +70,27 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
68
70
|
...rest
|
|
69
71
|
} = props;
|
|
70
72
|
|
|
73
|
+
const readOnlyEventHandlers = {
|
|
74
|
+
onMouseDown: (evt) => {
|
|
75
|
+
// NOTE: does not prevent click
|
|
76
|
+
if (readOnly) {
|
|
77
|
+
evt.preventDefault();
|
|
78
|
+
// focus on the element as per readonly input behavior
|
|
79
|
+
evt.target.focus();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
onKeyDown: (evt) => {
|
|
83
|
+
if (
|
|
84
|
+
readOnly &&
|
|
85
|
+
["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft", " "].includes(
|
|
86
|
+
evt.key
|
|
87
|
+
)
|
|
88
|
+
) {
|
|
89
|
+
evt.preventDefault();
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
71
94
|
return (
|
|
72
95
|
<div
|
|
73
96
|
className={cl(
|
|
@@ -76,7 +99,9 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
76
99
|
`navds-form-field--${size}`,
|
|
77
100
|
{
|
|
78
101
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
102
|
+
"navds-form-field--readonly": readOnly,
|
|
79
103
|
"navds-select--error": hasError,
|
|
104
|
+
"navds-select--readonly": readOnly,
|
|
80
105
|
}
|
|
81
106
|
)}
|
|
82
107
|
>
|
|
@@ -87,6 +112,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
87
112
|
"navds-sr-only": hideLabel,
|
|
88
113
|
})}
|
|
89
114
|
>
|
|
115
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
|
|
90
116
|
{label}
|
|
91
117
|
</Label>
|
|
92
118
|
{!!description && (
|
|
@@ -103,8 +129,9 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
103
129
|
)}
|
|
104
130
|
<div className="navds-select__container" style={style}>
|
|
105
131
|
<select
|
|
106
|
-
{...omit(rest, ["error", "errorId", "size"])}
|
|
132
|
+
{...omit(rest, ["error", "errorId", "size", "readOnly"])}
|
|
107
133
|
{...inputProps}
|
|
134
|
+
{...readOnlyEventHandlers}
|
|
108
135
|
ref={ref}
|
|
109
136
|
className={cl(
|
|
110
137
|
"navds-select__input",
|
package/src/form/Switch.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import React, {
|
|
|
7
7
|
} from "react";
|
|
8
8
|
import { BodyShort, Loader, omit } from "..";
|
|
9
9
|
import { FormFieldProps, useFormField } from "./useFormField";
|
|
10
|
+
import { ReadOnlyIcon } from "./ReadOnlyIcon";
|
|
10
11
|
|
|
11
12
|
const SelectedIcon = () => (
|
|
12
13
|
<svg
|
|
@@ -63,12 +64,12 @@ export interface SwitchProps
|
|
|
63
64
|
*
|
|
64
65
|
* @example
|
|
65
66
|
* ```jsx
|
|
66
|
-
* <Switch>
|
|
67
|
+
* <Switch>Varsle med SMS</Switch>
|
|
67
68
|
* ```
|
|
68
69
|
*/
|
|
69
70
|
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
70
71
|
(props, ref) => {
|
|
71
|
-
const { inputProps, size } = useFormField(props, "switch");
|
|
72
|
+
const { inputProps, size, readOnly } = useFormField(props, "switch");
|
|
72
73
|
|
|
73
74
|
const {
|
|
74
75
|
children,
|
|
@@ -90,11 +91,6 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
90
91
|
checkedProp !== undefined && setChecked(checkedProp);
|
|
91
92
|
}, [checkedProp]);
|
|
92
93
|
|
|
93
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
94
|
-
setChecked(e.target.checked);
|
|
95
|
-
props.onChange && props.onChange(e);
|
|
96
|
-
};
|
|
97
|
-
|
|
98
94
|
return (
|
|
99
95
|
<div
|
|
100
96
|
className={cl(
|
|
@@ -105,18 +101,32 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
105
101
|
{
|
|
106
102
|
"navds-switch--loading": loading,
|
|
107
103
|
"navds-switch--disabled": inputProps.disabled ?? loading,
|
|
104
|
+
"navds-switch--readonly": readOnly,
|
|
108
105
|
}
|
|
109
106
|
)}
|
|
110
107
|
>
|
|
111
108
|
<input
|
|
112
|
-
{...omit(rest, ["size"])}
|
|
109
|
+
{...omit(rest, ["size", "readOnly"])}
|
|
113
110
|
{...omit(inputProps, ["aria-invalid", "aria-describedby"])}
|
|
114
111
|
disabled={inputProps.disabled ?? loading}
|
|
115
112
|
checked={checkedProp}
|
|
116
113
|
defaultChecked={defaultChecked}
|
|
117
114
|
ref={ref}
|
|
118
115
|
type="checkbox"
|
|
119
|
-
onChange={(e) =>
|
|
116
|
+
onChange={(e) => {
|
|
117
|
+
if (readOnly) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
setChecked(e.target.checked);
|
|
121
|
+
props.onChange && props.onChange(e);
|
|
122
|
+
}}
|
|
123
|
+
onClick={(e) => {
|
|
124
|
+
if (readOnly) {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
props?.onClick?.(e);
|
|
129
|
+
}}
|
|
120
130
|
className={cl(className, "navds-switch__input")}
|
|
121
131
|
/>
|
|
122
132
|
<span className="navds-switch__track">
|
|
@@ -136,6 +146,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
136
146
|
})}
|
|
137
147
|
>
|
|
138
148
|
<BodyShort as="div" size={size} className="navds-switch__label">
|
|
149
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
|
|
139
150
|
{children}
|
|
140
151
|
</BodyShort>
|
|
141
152
|
{description && (
|
package/src/form/TextField.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import cl from "clsx";
|
|
2
2
|
import React, { forwardRef, InputHTMLAttributes } from "react";
|
|
3
3
|
import { BodyShort, ErrorMessage, Label, omit } from "..";
|
|
4
|
+
import { ReadOnlyIcon } from "./ReadOnlyIcon";
|
|
4
5
|
import { FormFieldProps, useFormField } from "./useFormField";
|
|
5
6
|
|
|
6
7
|
export interface TextFieldProps
|
|
@@ -62,6 +63,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
62
63
|
htmlSize,
|
|
63
64
|
hideLabel = false,
|
|
64
65
|
type = "text",
|
|
66
|
+
readOnly,
|
|
65
67
|
...rest
|
|
66
68
|
} = props;
|
|
67
69
|
|
|
@@ -71,10 +73,13 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
71
73
|
className,
|
|
72
74
|
"navds-form-field",
|
|
73
75
|
`navds-form-field--${size}`,
|
|
76
|
+
|
|
74
77
|
{
|
|
75
78
|
"navds-text-field--error": hasError,
|
|
76
79
|
"navds-text-field--disabled": !!inputProps.disabled,
|
|
77
80
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
81
|
+
"navds-form-field--readonly": readOnly,
|
|
82
|
+
"navds-text-field--readonly": readOnly,
|
|
78
83
|
}
|
|
79
84
|
)}
|
|
80
85
|
>
|
|
@@ -85,6 +90,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
85
90
|
"navds-sr-only": hideLabel,
|
|
86
91
|
})}
|
|
87
92
|
>
|
|
93
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
88
94
|
{label}
|
|
89
95
|
</Label>
|
|
90
96
|
|
|
@@ -105,6 +111,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
105
111
|
{...inputProps}
|
|
106
112
|
ref={ref}
|
|
107
113
|
type={type}
|
|
114
|
+
readOnly={readOnly}
|
|
108
115
|
className={cl(
|
|
109
116
|
"navds-text-field__input",
|
|
110
117
|
"navds-body-short",
|
package/src/form/Textarea.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import React, { forwardRef, useState } from "react";
|
|
|
3
3
|
import { BodyShort, ErrorMessage, Label, omit, useId } from "..";
|
|
4
4
|
import TextareaAutosize from "../util/TextareaAutoSize";
|
|
5
5
|
import { FormFieldProps, useFormField } from "./useFormField";
|
|
6
|
+
import { ReadOnlyIcon } from "./ReadOnlyIcon";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* TODO: Mulighet for lokalisering av sr-only/counter text
|
|
@@ -85,6 +86,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
85
86
|
hideLabel = false,
|
|
86
87
|
resize,
|
|
87
88
|
i18n,
|
|
89
|
+
readOnly,
|
|
88
90
|
...rest
|
|
89
91
|
} = props;
|
|
90
92
|
|
|
@@ -115,6 +117,8 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
115
117
|
`navds-form-field--${size}`,
|
|
116
118
|
{
|
|
117
119
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
120
|
+
"navds-form-field--readonly": readOnly,
|
|
121
|
+
"navds-textarea--readonly": readOnly,
|
|
118
122
|
"navds-textarea--error": hasError,
|
|
119
123
|
"navds-textarea--resize": resize,
|
|
120
124
|
}
|
|
@@ -127,6 +131,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
127
131
|
"navds-sr-only": hideLabel,
|
|
128
132
|
})}
|
|
129
133
|
>
|
|
134
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
130
135
|
{label}
|
|
131
136
|
</Label>
|
|
132
137
|
{!!description && (
|
|
@@ -152,6 +157,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
152
157
|
}
|
|
153
158
|
minRows={getMinRows()}
|
|
154
159
|
ref={ref}
|
|
160
|
+
readOnly={readOnly}
|
|
155
161
|
className={cl(
|
|
156
162
|
"navds-textarea__input",
|
|
157
163
|
"navds-body-short",
|
|
@@ -4,6 +4,7 @@ import { BodyShort } from "../../typography";
|
|
|
4
4
|
import { omit } from "../../util";
|
|
5
5
|
import { FormFieldProps } from "../useFormField";
|
|
6
6
|
import useCheckbox from "./useCheckbox";
|
|
7
|
+
import { ReadOnlyIcon } from "../ReadOnlyIcon";
|
|
7
8
|
|
|
8
9
|
export interface CheckboxProps
|
|
9
10
|
extends FormFieldProps,
|
|
@@ -42,7 +43,7 @@ export interface CheckboxProps
|
|
|
42
43
|
|
|
43
44
|
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
44
45
|
(props, ref) => {
|
|
45
|
-
const { inputProps, hasError, size } = useCheckbox(props);
|
|
46
|
+
const { inputProps, hasError, size, readOnly, nested } = useCheckbox(props);
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
49
|
<div
|
|
@@ -53,6 +54,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
53
54
|
{
|
|
54
55
|
"navds-checkbox--error": hasError,
|
|
55
56
|
"navds-checkbox--disabled": inputProps.disabled,
|
|
57
|
+
"navds-checkbox--readonly": readOnly,
|
|
56
58
|
}
|
|
57
59
|
)}
|
|
58
60
|
>
|
|
@@ -65,6 +67,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
65
67
|
"hideLabel",
|
|
66
68
|
"indeterminate",
|
|
67
69
|
"errorId",
|
|
70
|
+
"readOnly",
|
|
68
71
|
])}
|
|
69
72
|
{...inputProps}
|
|
70
73
|
type="checkbox"
|
|
@@ -88,7 +91,14 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
88
91
|
"navds-sr-only": props.hideLabel,
|
|
89
92
|
})}
|
|
90
93
|
>
|
|
91
|
-
<BodyShort
|
|
94
|
+
<BodyShort
|
|
95
|
+
as="span"
|
|
96
|
+
size={size}
|
|
97
|
+
className="navds-checkbox__label-text"
|
|
98
|
+
>
|
|
99
|
+
{!nested && (
|
|
100
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
|
|
101
|
+
)}
|
|
92
102
|
{props.children}
|
|
93
103
|
</BodyShort>
|
|
94
104
|
{props.description && (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
import { Meta } from "@storybook/react";
|
|
2
3
|
import React, { useState } from "react";
|
|
3
4
|
import { Checkbox, CheckboxGroup } from "../../index";
|
|
4
|
-
import { Meta } from "@storybook/react";
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: "ds-react/Checkbox",
|
|
@@ -190,3 +190,37 @@ export const Indeterminate = () => {
|
|
|
190
190
|
</>
|
|
191
191
|
);
|
|
192
192
|
};
|
|
193
|
+
|
|
194
|
+
export const Readonly = () => (
|
|
195
|
+
<div className="colgap">
|
|
196
|
+
<CheckboxGroup
|
|
197
|
+
legend="Hvilken frukt liker du?"
|
|
198
|
+
defaultValue={["banan"]}
|
|
199
|
+
readOnly
|
|
200
|
+
>
|
|
201
|
+
<Checkbox value="banan">Banan</Checkbox>
|
|
202
|
+
<Checkbox value="eple">Eple</Checkbox>
|
|
203
|
+
<Checkbox value="druer" indeterminate>
|
|
204
|
+
Druer
|
|
205
|
+
</Checkbox>
|
|
206
|
+
</CheckboxGroup>
|
|
207
|
+
<CheckboxGroup
|
|
208
|
+
legend="Hvilken frukt liker du?"
|
|
209
|
+
error="feilmelding"
|
|
210
|
+
defaultValue={["Eple"]}
|
|
211
|
+
readOnly
|
|
212
|
+
>
|
|
213
|
+
<Checkbox value="eple" description="Epler kommer i 4 varianter">
|
|
214
|
+
Eple
|
|
215
|
+
</Checkbox>
|
|
216
|
+
<Checkbox value="banan">Banan</Checkbox>
|
|
217
|
+
</CheckboxGroup>
|
|
218
|
+
<hr />
|
|
219
|
+
<Checkbox value="tekst1" readOnly>
|
|
220
|
+
Eple single
|
|
221
|
+
</Checkbox>
|
|
222
|
+
<Checkbox value="tekst1" checked readOnly>
|
|
223
|
+
Banan single
|
|
224
|
+
</Checkbox>
|
|
225
|
+
</div>
|
|
226
|
+
);
|