@navikt/ds-react 4.5.0 → 4.6.0
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 +9 -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 +23 -3
- package/cjs/form/Switch.js +21 -8
- package/cjs/form/TextField.js +7 -3
- package/cjs/form/Textarea.js +7 -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 +9 -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 +23 -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 +7 -3
- package/esm/form/TextField.js.map +1 -1
- package/esm/form/Textarea.js +7 -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 +8 -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 +28 -2
- package/src/form/Switch.tsx +20 -9
- package/src/form/TextField.tsx +5 -0
- package/src/form/Textarea.tsx +5 -0
- package/src/form/checkbox/Checkbox.tsx +7 -1
- 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.0",
|
|
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.0",
|
|
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,8 @@ 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-text-field--readonly": readOnly,
|
|
77
|
+
"navds-date__field--readonly": readOnly,
|
|
74
78
|
}
|
|
75
79
|
)}
|
|
76
80
|
>
|
|
@@ -81,6 +85,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
81
85
|
"navds-sr-only": hideLabel,
|
|
82
86
|
})}
|
|
83
87
|
>
|
|
88
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
84
89
|
{label}
|
|
85
90
|
</Label>
|
|
86
91
|
{!!description && (
|
|
@@ -102,6 +107,7 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
102
107
|
{...inputProps}
|
|
103
108
|
autoComplete="off"
|
|
104
109
|
aria-controls={ariaId}
|
|
110
|
+
readOnly={readOnly}
|
|
105
111
|
className={cl(
|
|
106
112
|
"navds-date__field-input",
|
|
107
113
|
"navds-text-field__input",
|
|
@@ -111,8 +117,8 @@ const DateInput = forwardRef<HTMLInputElement, DateInputProps>((props, ref) => {
|
|
|
111
117
|
size={14}
|
|
112
118
|
/>
|
|
113
119
|
<button
|
|
114
|
-
disabled={inputProps.disabled}
|
|
115
|
-
tabIndex={open ? -1 : 0}
|
|
120
|
+
disabled={inputProps.disabled || readOnly}
|
|
121
|
+
tabIndex={readOnly ? -1 : open ? -1 : 0}
|
|
116
122
|
onClick={() => onOpen()}
|
|
117
123
|
type="button"
|
|
118
124
|
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(
|
|
@@ -77,6 +100,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
77
100
|
{
|
|
78
101
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
79
102
|
"navds-select--error": hasError,
|
|
103
|
+
"navds-select--readonly": readOnly,
|
|
80
104
|
}
|
|
81
105
|
)}
|
|
82
106
|
>
|
|
@@ -87,6 +111,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
87
111
|
"navds-sr-only": hideLabel,
|
|
88
112
|
})}
|
|
89
113
|
>
|
|
114
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
|
|
90
115
|
{label}
|
|
91
116
|
</Label>
|
|
92
117
|
{!!description && (
|
|
@@ -103,8 +128,9 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
103
128
|
)}
|
|
104
129
|
<div className="navds-select__container" style={style}>
|
|
105
130
|
<select
|
|
106
|
-
{...omit(rest, ["error", "errorId", "size"])}
|
|
131
|
+
{...omit(rest, ["error", "errorId", "size", "readOnly"])}
|
|
107
132
|
{...inputProps}
|
|
133
|
+
{...readOnlyEventHandlers}
|
|
108
134
|
ref={ref}
|
|
109
135
|
className={cl(
|
|
110
136
|
"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
|
|
|
@@ -75,6 +77,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
75
77
|
"navds-text-field--error": hasError,
|
|
76
78
|
"navds-text-field--disabled": !!inputProps.disabled,
|
|
77
79
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
80
|
+
"navds-text-field--readonly": readOnly,
|
|
78
81
|
}
|
|
79
82
|
)}
|
|
80
83
|
>
|
|
@@ -85,6 +88,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
85
88
|
"navds-sr-only": hideLabel,
|
|
86
89
|
})}
|
|
87
90
|
>
|
|
91
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
88
92
|
{label}
|
|
89
93
|
</Label>
|
|
90
94
|
|
|
@@ -105,6 +109,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
105
109
|
{...inputProps}
|
|
106
110
|
ref={ref}
|
|
107
111
|
type={type}
|
|
112
|
+
readOnly={readOnly}
|
|
108
113
|
className={cl(
|
|
109
114
|
"navds-text-field__input",
|
|
110
115
|
"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,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
115
117
|
`navds-form-field--${size}`,
|
|
116
118
|
{
|
|
117
119
|
"navds-form-field--disabled": !!inputProps.disabled,
|
|
120
|
+
"navds-textarea--readonly": readOnly,
|
|
118
121
|
"navds-textarea--error": hasError,
|
|
119
122
|
"navds-textarea--resize": resize,
|
|
120
123
|
}
|
|
@@ -127,6 +130,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
127
130
|
"navds-sr-only": hideLabel,
|
|
128
131
|
})}
|
|
129
132
|
>
|
|
133
|
+
<ReadOnlyIcon readOnly={readOnly} />
|
|
130
134
|
{label}
|
|
131
135
|
</Label>
|
|
132
136
|
{!!description && (
|
|
@@ -152,6 +156,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
152
156
|
}
|
|
153
157
|
minRows={getMinRows()}
|
|
154
158
|
ref={ref}
|
|
159
|
+
readOnly={readOnly}
|
|
155
160
|
className={cl(
|
|
156
161
|
"navds-textarea__input",
|
|
157
162
|
"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"
|
|
@@ -89,6 +92,9 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
89
92
|
})}
|
|
90
93
|
>
|
|
91
94
|
<BodyShort as="span" size={size}>
|
|
95
|
+
{!nested && (
|
|
96
|
+
<ReadOnlyIcon readOnly={readOnly} nativeReadOnly={false} />
|
|
97
|
+
)}
|
|
92
98
|
{props.children}
|
|
93
99
|
</BodyShort>
|
|
94
100
|
{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
|
+
);
|
|
@@ -10,7 +10,7 @@ import { omit } from "../..";
|
|
|
10
10
|
const useCheckbox = ({ children, ...props }: CheckboxProps) => {
|
|
11
11
|
const checkboxGroup = useContext(CheckboxGroupContext);
|
|
12
12
|
|
|
13
|
-
const { inputProps, ...rest } = useFormField(
|
|
13
|
+
const { inputProps, readOnly, ...rest } = useFormField(
|
|
14
14
|
omit(props, ["description"]),
|
|
15
15
|
"checkbox"
|
|
16
16
|
);
|
|
@@ -30,6 +30,8 @@ const useCheckbox = ({ children, ...props }: CheckboxProps) => {
|
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
...rest,
|
|
33
|
+
readOnly,
|
|
34
|
+
nested: !!checkboxGroup,
|
|
33
35
|
inputProps: {
|
|
34
36
|
...inputProps,
|
|
35
37
|
checked: checkboxGroup?.value
|
|
@@ -39,9 +41,19 @@ const useCheckbox = ({ children, ...props }: CheckboxProps) => {
|
|
|
39
41
|
? checkboxGroup.defaultValue.includes(props.value)
|
|
40
42
|
: props.defaultChecked,
|
|
41
43
|
onChange: (e) => {
|
|
44
|
+
if (readOnly) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
42
47
|
props.onChange && props.onChange(e);
|
|
43
48
|
checkboxGroup && checkboxGroup.toggleValue(props.value);
|
|
44
49
|
},
|
|
50
|
+
onClick: (e) => {
|
|
51
|
+
if (readOnly) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
props?.onClick?.(e);
|
|
56
|
+
},
|
|
45
57
|
},
|
|
46
58
|
};
|
|
47
59
|
};
|