@navikt/ds-react 4.4.2 → 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.
Files changed (98) hide show
  1. package/_docs.json +325 -0
  2. package/cjs/alert/Alert.js +9 -3
  3. package/cjs/date/DateInput.js +9 -4
  4. package/cjs/date/hooks/useDatepicker.js +3 -0
  5. package/cjs/date/hooks/useMonthPicker.js +3 -0
  6. package/cjs/date/hooks/useRangeDatepicker.js +3 -0
  7. package/cjs/form/Fieldset/Fieldset.js +11 -4
  8. package/cjs/form/ReadOnlyIcon.js +15 -0
  9. package/cjs/form/Select.js +23 -3
  10. package/cjs/form/Switch.js +21 -8
  11. package/cjs/form/TextField.js +7 -3
  12. package/cjs/form/Textarea.js +7 -3
  13. package/cjs/form/checkbox/Checkbox.js +7 -2
  14. package/cjs/form/checkbox/CheckboxGroup.js +1 -1
  15. package/cjs/form/checkbox/useCheckbox.js +12 -2
  16. package/cjs/form/radio/Radio.js +3 -2
  17. package/cjs/form/radio/RadioGroup.js +2 -2
  18. package/cjs/form/radio/useRadio.js +12 -2
  19. package/cjs/form/search/Search.js +1 -1
  20. package/cjs/form/useFormField.js +11 -9
  21. package/esm/alert/Alert.d.ts +11 -0
  22. package/esm/alert/Alert.js +10 -4
  23. package/esm/alert/Alert.js.map +1 -1
  24. package/esm/date/DateInput.js +9 -4
  25. package/esm/date/DateInput.js.map +1 -1
  26. package/esm/date/hooks/useDatepicker.js +3 -0
  27. package/esm/date/hooks/useDatepicker.js.map +1 -1
  28. package/esm/date/hooks/useMonthPicker.js +3 -0
  29. package/esm/date/hooks/useMonthPicker.js.map +1 -1
  30. package/esm/date/hooks/useRangeDatepicker.js +3 -0
  31. package/esm/date/hooks/useRangeDatepicker.js.map +1 -1
  32. package/esm/form/ConfirmationPanel.d.ts +1 -1
  33. package/esm/form/Fieldset/Fieldset.d.ts +5 -0
  34. package/esm/form/Fieldset/Fieldset.js +11 -4
  35. package/esm/form/Fieldset/Fieldset.js.map +1 -1
  36. package/esm/form/Fieldset/useFieldset.d.ts +2 -1
  37. package/esm/form/ReadOnlyIcon.d.ts +5 -0
  38. package/esm/form/ReadOnlyIcon.js +9 -0
  39. package/esm/form/ReadOnlyIcon.js.map +1 -0
  40. package/esm/form/Select.js +23 -3
  41. package/esm/form/Select.js.map +1 -1
  42. package/esm/form/Switch.d.ts +1 -1
  43. package/esm/form/Switch.js +21 -8
  44. package/esm/form/Switch.js.map +1 -1
  45. package/esm/form/TextField.js +7 -3
  46. package/esm/form/TextField.js.map +1 -1
  47. package/esm/form/Textarea.js +7 -3
  48. package/esm/form/Textarea.js.map +1 -1
  49. package/esm/form/checkbox/Checkbox.js +7 -2
  50. package/esm/form/checkbox/Checkbox.js.map +1 -1
  51. package/esm/form/checkbox/CheckboxGroup.js +1 -1
  52. package/esm/form/checkbox/CheckboxGroup.js.map +1 -1
  53. package/esm/form/checkbox/useCheckbox.d.ts +5 -2
  54. package/esm/form/checkbox/useCheckbox.js +12 -2
  55. package/esm/form/checkbox/useCheckbox.js.map +1 -1
  56. package/esm/form/radio/Radio.d.ts +1 -1
  57. package/esm/form/radio/Radio.js +3 -2
  58. package/esm/form/radio/Radio.js.map +1 -1
  59. package/esm/form/radio/RadioGroup.js +2 -2
  60. package/esm/form/radio/RadioGroup.js.map +1 -1
  61. package/esm/form/radio/useRadio.d.ts +4 -2
  62. package/esm/form/radio/useRadio.js +12 -2
  63. package/esm/form/radio/useRadio.js.map +1 -1
  64. package/esm/form/search/Search.d.ts +1 -1
  65. package/esm/form/search/Search.js +1 -1
  66. package/esm/form/search/Search.js.map +1 -1
  67. package/esm/form/useFormField.d.ts +7 -2
  68. package/esm/form/useFormField.js +11 -9
  69. package/esm/form/useFormField.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/alert/Alert.tsx +49 -18
  72. package/src/alert/alert.stories.tsx +75 -22
  73. package/src/date/DateInput.tsx +8 -2
  74. package/src/date/datepicker/datepicker.stories.tsx +22 -0
  75. package/src/date/hooks/useDatepicker.tsx +3 -0
  76. package/src/date/hooks/useMonthPicker.tsx +3 -0
  77. package/src/date/hooks/useRangeDatepicker.tsx +3 -0
  78. package/src/form/ConfirmationPanel.tsx +1 -1
  79. package/src/form/Fieldset/Fieldset.tsx +15 -2
  80. package/src/form/ReadOnlyIcon.tsx +20 -0
  81. package/src/form/Select.tsx +28 -2
  82. package/src/form/Switch.tsx +20 -9
  83. package/src/form/TextField.tsx +5 -0
  84. package/src/form/Textarea.tsx +5 -0
  85. package/src/form/checkbox/Checkbox.tsx +7 -1
  86. package/src/form/checkbox/CheckboxGroup.tsx +1 -0
  87. package/src/form/checkbox/checkbox.stories.tsx +35 -1
  88. package/src/form/checkbox/useCheckbox.ts +13 -1
  89. package/src/form/radio/Radio.tsx +4 -3
  90. package/src/form/radio/RadioGroup.tsx +3 -0
  91. package/src/form/radio/radio.stories.tsx +27 -0
  92. package/src/form/radio/useRadio.ts +12 -1
  93. package/src/form/search/Search.tsx +2 -2
  94. package/src/form/stories/select.stories.tsx +17 -0
  95. package/src/form/stories/switch.stories.tsx +14 -0
  96. package/src/form/stories/text-field.stories.tsx +14 -0
  97. package/src/form/stories/textarea.stories.tsx +19 -0
  98. package/src/form/useFormField.ts +25 -3
@@ -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
- { "navds-fieldset--error": hasError }
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
+ };
@@ -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
- } = useFormField(props, "textField");
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",
@@ -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>Slå notifikasjoner</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) => handleChange(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 && (
@@ -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",
@@ -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 && (
@@ -81,6 +81,7 @@ export const CheckboxGroup = forwardRef<
81
81
  "navds-checkbox-group",
82
82
  `navds-checkbox-group--${rest.size ?? fieldset?.size ?? "medium"}`
83
83
  )}
84
+ nativeReadOnly={false}
84
85
  >
85
86
  <CheckboxGroupContext.Provider
86
87
  value={{
@@ -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
  };
@@ -6,7 +6,7 @@ import { FormFieldProps } from "../useFormField";
6
6
  import { useRadio } from "./useRadio";
7
7
 
8
8
  export interface RadioProps
9
- extends Omit<FormFieldProps, "error" | "errorId">,
9
+ extends Omit<FormFieldProps, "error" | "errorId" | "readOnly">,
10
10
  Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "value"> {
11
11
  /**
12
12
  * Radio label
@@ -23,17 +23,18 @@ export interface RadioProps
23
23
  }
24
24
 
25
25
  export const Radio = forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
26
- const { inputProps, size, hasError } = useRadio(props);
26
+ const { inputProps, size, hasError, readOnly } = useRadio(props);
27
27
 
28
28
  return (
29
29
  <div
30
30
  className={cl(props.className, "navds-radio", `navds-radio--${size}`, {
31
31
  "navds-radio--error": hasError,
32
32
  "navds-radio--disabled": inputProps.disabled,
33
+ "navds-radio--readonly": readOnly,
33
34
  })}
34
35
  >
35
36
  <input
36
- {...omit(props, ["children", "size", "description"])}
37
+ {...omit(props, ["children", "size", "description", "readOnly"])}
37
38
  {...inputProps}
38
39
  className="navds-radio__input"
39
40
  ref={ref}
@@ -65,6 +65,7 @@ export const RadioGroup = forwardRef<HTMLFieldSetElement, RadioGroupProps>(
65
65
  value,
66
66
  onChange = () => {},
67
67
  required,
68
+ readOnly,
68
69
  ...rest
69
70
  },
70
71
  ref
@@ -76,12 +77,14 @@ export const RadioGroup = forwardRef<HTMLFieldSetElement, RadioGroupProps>(
76
77
  return (
77
78
  <Fieldset
78
79
  {...rest}
80
+ readOnly={readOnly}
79
81
  ref={ref}
80
82
  className={cl(
81
83
  className,
82
84
  "navds-radio-group",
83
85
  `navds-radio-group--${rest.size ?? fieldset?.size ?? "medium"}`
84
86
  )}
87
+ nativeReadOnly={false}
85
88
  >
86
89
  <RadioGroupContext.Provider
87
90
  value={{
@@ -92,3 +92,30 @@ export const GroupDescription = () => (
92
92
  <Radio value="tekst2">Radiotekst</Radio>
93
93
  </RadioGroup>
94
94
  );
95
+
96
+ export const UUDemo = () => (
97
+ <div className="colgap">
98
+ <RadioGroup
99
+ legend="Hvilken frukt vil du ha?"
100
+ description="Du kan bare velge en frukt"
101
+ defaultValue="eple"
102
+ readOnly
103
+ >
104
+ <Radio value="eple">Eple</Radio>
105
+ <Radio value="banan" description="Bananer er importert fra X">
106
+ Banan
107
+ </Radio>
108
+ <Radio value="druer">Druer</Radio>
109
+ </RadioGroup>
110
+ <RadioGroup
111
+ legend="Når har du ferie?"
112
+ defaultValue="1"
113
+ readOnly
114
+ error="du må velge en ferie"
115
+ >
116
+ <Radio value="1">August</Radio>
117
+ <Radio value="2">Juli</Radio>
118
+ <Radio value="3">Juni</Radio>
119
+ </RadioGroup>
120
+ </div>
121
+ );
@@ -10,7 +10,7 @@ import { omit } from "../..";
10
10
  export const useRadio = (props: RadioProps) => {
11
11
  const radioGroup = useContext(RadioGroupContext);
12
12
 
13
- const { inputProps, ...rest } = useFormField(
13
+ const { inputProps, readOnly, ...rest } = useFormField(
14
14
  omit(props, ["description"]),
15
15
  "radio"
16
16
  );
@@ -25,6 +25,7 @@ export const useRadio = (props: RadioProps) => {
25
25
 
26
26
  return {
27
27
  ...rest,
28
+ readOnly,
28
29
  inputProps: {
29
30
  ...inputProps,
30
31
  name: radioGroup?.name,
@@ -37,9 +38,19 @@ export const useRadio = (props: RadioProps) => {
37
38
  ? undefined
38
39
  : radioGroup?.value === props.value,
39
40
  onChange: (e) => {
41
+ if (readOnly) {
42
+ return;
43
+ }
40
44
  props.onChange && props.onChange(e);
41
45
  radioGroup?.onChange && radioGroup.onChange(props.value);
42
46
  },
47
+ onClick: (e) => {
48
+ if (readOnly) {
49
+ e.preventDefault();
50
+ return;
51
+ }
52
+ props?.onClick?.(e);
53
+ },
43
54
  required: radioGroup?.required,
44
55
  type: "radio",
45
56
  },
@@ -27,7 +27,7 @@ export type SearchClearEvent =
27
27
  | { trigger: "Escape"; event: React.KeyboardEvent<HTMLDivElement> };
28
28
 
29
29
  export interface SearchProps
30
- extends FormFieldProps,
30
+ extends Omit<FormFieldProps, "readOnly">,
31
31
  Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "onChange"> {
32
32
  children?: React.ReactNode;
33
33
  /**
@@ -221,7 +221,7 @@ export const Search = forwardRef<HTMLInputElement, SearchProps>(
221
221
  )}
222
222
  <input
223
223
  ref={mergedRef}
224
- {...omit(rest, ["error", "errorId", "size"])}
224
+ {...omit(rest, ["error", "errorId", "size", "readOnly"])}
225
225
  {...inputProps}
226
226
  value={value ?? internalValue}
227
227
  onChange={(e) => handleChange(e.target.value)}
@@ -114,3 +114,20 @@ export const HideLabel = () => {
114
114
  </Select>
115
115
  );
116
116
  };
117
+
118
+ export const Readonly = () => {
119
+ return (
120
+ <div className="colgap">
121
+ <Select
122
+ label="Hvilkets land er du fra?"
123
+ description="Velg landet du bor 180 dagen i året"
124
+ readOnly
125
+ >
126
+ {content}
127
+ </Select>
128
+ <Select label="Hvilkets land er du fra?" readOnly error="feilmelding">
129
+ {content}
130
+ </Select>
131
+ </div>
132
+ );
133
+ };
@@ -116,3 +116,17 @@ export const HideLabel = () => {
116
116
  </div>
117
117
  );
118
118
  };
119
+
120
+ export const Readonly = () => {
121
+ return (
122
+ <div className="colgap">
123
+ <Switch description="Slår av alle notifikasjoner" readOnly>
124
+ Notifikasjoner
125
+ </Switch>
126
+
127
+ <Switch checked readOnly>
128
+ Notifikasjoner
129
+ </Switch>
130
+ </div>
131
+ );
132
+ };
@@ -83,3 +83,17 @@ export const Disabled = () => {
83
83
  export const HideLabel = () => {
84
84
  return <TextField label="Ipsum enim quis culpa" hideLabel />;
85
85
  };
86
+
87
+ export const Readonly = () => {
88
+ return (
89
+ <div className="colgap">
90
+ <TextField
91
+ label="Bosted"
92
+ description="Skriv bosted i Norge"
93
+ readOnly
94
+ value="Oslo"
95
+ />
96
+ <TextField label="Bosted" readOnly error="feilmelding" value="Oslo" />
97
+ </div>
98
+ );
99
+ };
@@ -120,3 +120,22 @@ export const MaxRows = () => {
120
120
  export const Resize = () => {
121
121
  return <Textarea resize label="Ipsum enim quis culpa" />;
122
122
  };
123
+
124
+ export const Readonly = () => {
125
+ return (
126
+ <div className="colgap">
127
+ <Textarea
128
+ label="På hvilket grunnlag har du tatt denne vurderingen?"
129
+ description="Beskriv i korte punkter"
130
+ value="Denne vurderingen ble gjort på grunnlag av X og Y"
131
+ readOnly
132
+ />
133
+ <Textarea
134
+ label="På hvilket grunnlag har du tatt denne vurderingen?"
135
+ readOnly
136
+ value="Denne vurderingen ble gjort på grunnlag av X og Y"
137
+ error="feilmelding"
138
+ />
139
+ </div>
140
+ );
141
+ };