@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.
Files changed (92) hide show
  1. package/_docs.json +285 -0
  2. package/cjs/date/DateInput.js +9 -4
  3. package/cjs/date/hooks/useDatepicker.js +3 -0
  4. package/cjs/date/hooks/useMonthPicker.js +3 -0
  5. package/cjs/date/hooks/useRangeDatepicker.js +3 -0
  6. package/cjs/form/Fieldset/Fieldset.js +11 -4
  7. package/cjs/form/ReadOnlyIcon.js +15 -0
  8. package/cjs/form/Select.js +23 -3
  9. package/cjs/form/Switch.js +21 -8
  10. package/cjs/form/TextField.js +7 -3
  11. package/cjs/form/Textarea.js +7 -3
  12. package/cjs/form/checkbox/Checkbox.js +7 -2
  13. package/cjs/form/checkbox/CheckboxGroup.js +1 -1
  14. package/cjs/form/checkbox/useCheckbox.js +12 -2
  15. package/cjs/form/radio/Radio.js +3 -2
  16. package/cjs/form/radio/RadioGroup.js +2 -2
  17. package/cjs/form/radio/useRadio.js +12 -2
  18. package/cjs/form/search/Search.js +1 -1
  19. package/cjs/form/useFormField.js +11 -9
  20. package/esm/date/DateInput.js +9 -4
  21. package/esm/date/DateInput.js.map +1 -1
  22. package/esm/date/hooks/useDatepicker.js +3 -0
  23. package/esm/date/hooks/useDatepicker.js.map +1 -1
  24. package/esm/date/hooks/useMonthPicker.js +3 -0
  25. package/esm/date/hooks/useMonthPicker.js.map +1 -1
  26. package/esm/date/hooks/useRangeDatepicker.js +3 -0
  27. package/esm/date/hooks/useRangeDatepicker.js.map +1 -1
  28. package/esm/form/ConfirmationPanel.d.ts +1 -1
  29. package/esm/form/Fieldset/Fieldset.d.ts +5 -0
  30. package/esm/form/Fieldset/Fieldset.js +11 -4
  31. package/esm/form/Fieldset/Fieldset.js.map +1 -1
  32. package/esm/form/Fieldset/useFieldset.d.ts +2 -1
  33. package/esm/form/ReadOnlyIcon.d.ts +5 -0
  34. package/esm/form/ReadOnlyIcon.js +9 -0
  35. package/esm/form/ReadOnlyIcon.js.map +1 -0
  36. package/esm/form/Select.js +23 -3
  37. package/esm/form/Select.js.map +1 -1
  38. package/esm/form/Switch.d.ts +1 -1
  39. package/esm/form/Switch.js +21 -8
  40. package/esm/form/Switch.js.map +1 -1
  41. package/esm/form/TextField.js +7 -3
  42. package/esm/form/TextField.js.map +1 -1
  43. package/esm/form/Textarea.js +7 -3
  44. package/esm/form/Textarea.js.map +1 -1
  45. package/esm/form/checkbox/Checkbox.js +7 -2
  46. package/esm/form/checkbox/Checkbox.js.map +1 -1
  47. package/esm/form/checkbox/CheckboxGroup.js +1 -1
  48. package/esm/form/checkbox/CheckboxGroup.js.map +1 -1
  49. package/esm/form/checkbox/useCheckbox.d.ts +5 -2
  50. package/esm/form/checkbox/useCheckbox.js +12 -2
  51. package/esm/form/checkbox/useCheckbox.js.map +1 -1
  52. package/esm/form/radio/Radio.d.ts +1 -1
  53. package/esm/form/radio/Radio.js +3 -2
  54. package/esm/form/radio/Radio.js.map +1 -1
  55. package/esm/form/radio/RadioGroup.js +2 -2
  56. package/esm/form/radio/RadioGroup.js.map +1 -1
  57. package/esm/form/radio/useRadio.d.ts +4 -2
  58. package/esm/form/radio/useRadio.js +12 -2
  59. package/esm/form/radio/useRadio.js.map +1 -1
  60. package/esm/form/search/Search.d.ts +1 -1
  61. package/esm/form/search/Search.js +1 -1
  62. package/esm/form/search/Search.js.map +1 -1
  63. package/esm/form/useFormField.d.ts +7 -2
  64. package/esm/form/useFormField.js +11 -9
  65. package/esm/form/useFormField.js.map +1 -1
  66. package/package.json +2 -2
  67. package/src/date/DateInput.tsx +8 -2
  68. package/src/date/datepicker/datepicker.stories.tsx +22 -0
  69. package/src/date/hooks/useDatepicker.tsx +3 -0
  70. package/src/date/hooks/useMonthPicker.tsx +3 -0
  71. package/src/date/hooks/useRangeDatepicker.tsx +3 -0
  72. package/src/form/ConfirmationPanel.tsx +1 -1
  73. package/src/form/Fieldset/Fieldset.tsx +15 -2
  74. package/src/form/ReadOnlyIcon.tsx +20 -0
  75. package/src/form/Select.tsx +28 -2
  76. package/src/form/Switch.tsx +20 -9
  77. package/src/form/TextField.tsx +5 -0
  78. package/src/form/Textarea.tsx +5 -0
  79. package/src/form/checkbox/Checkbox.tsx +7 -1
  80. package/src/form/checkbox/CheckboxGroup.tsx +1 -0
  81. package/src/form/checkbox/checkbox.stories.tsx +35 -1
  82. package/src/form/checkbox/useCheckbox.ts +13 -1
  83. package/src/form/radio/Radio.tsx +4 -3
  84. package/src/form/radio/RadioGroup.tsx +3 -0
  85. package/src/form/radio/radio.stories.tsx +27 -0
  86. package/src/form/radio/useRadio.ts +12 -1
  87. package/src/form/search/Search.tsx +2 -2
  88. package/src/form/stories/select.stories.tsx +17 -0
  89. package/src/form/stories/switch.stories.tsx +14 -0
  90. package/src/form/stories/text-field.stories.tsx +14 -0
  91. package/src/form/stories/textarea.stories.tsx +19 -0
  92. package/src/form/useFormField.ts +25 -3
@@ -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
+ };
@@ -29,6 +29,10 @@ export interface FormFieldProps {
29
29
  * Override internal id
30
30
  */
31
31
  id?: string;
32
+ /**
33
+ * Read only-state
34
+ */
35
+ readOnly?: boolean;
32
36
  }
33
37
 
34
38
  /**
@@ -46,8 +50,24 @@ export const useFormField = (props: FormFieldProps, prefix: string) => {
46
50
  const inputDescriptionId = `${prefix}-description-${genId}`;
47
51
 
48
52
  const disabled = fieldset?.disabled || props.disabled;
49
- const hasError: boolean = !disabled && !!(error || fieldset?.error);
50
- const showErrorMsg = !disabled && !!error && typeof error !== "boolean";
53
+ const readOnly =
54
+ ((fieldset?.readOnly || props.readOnly) && !disabled) || undefined;
55
+
56
+ const hasError: boolean =
57
+ !disabled && !readOnly && !!(error || fieldset?.error);
58
+ const showErrorMsg =
59
+ !disabled && !readOnly && !!error && typeof error !== "boolean";
60
+
61
+ const ariaInvalid = { ...(hasError ? { "aria-invalid": true } : {}) };
62
+
63
+ if ((props as any)?.required && process.env.NODE_ENV !== "production") {
64
+ console.warn(
65
+ "Aksel: Use of 'required' in form-elements is heavily discuouraged. Docs about why here:"
66
+ );
67
+ console.warn(
68
+ "https://aksel.nav.no/god-praksis/artikler/obligatoriske-og-valgfrie-skjemafelter#h3bfe00453471"
69
+ );
70
+ }
51
71
 
52
72
  return {
53
73
  showErrorMsg,
@@ -55,9 +75,10 @@ export const useFormField = (props: FormFieldProps, prefix: string) => {
55
75
  errorId,
56
76
  inputDescriptionId,
57
77
  size: size ?? fieldset?.size ?? "medium",
78
+ readOnly,
58
79
  inputProps: {
59
80
  id,
60
- "aria-invalid": hasError,
81
+ ...ariaInvalid,
61
82
  "aria-describedby":
62
83
  cl(props["aria-describedby"], {
63
84
  [inputDescriptionId]:
@@ -65,6 +86,7 @@ export const useFormField = (props: FormFieldProps, prefix: string) => {
65
86
  [errorId]: showErrorMsg,
66
87
  [fieldset?.errorId ?? ""]: hasError && !!fieldset?.error,
67
88
  }) || undefined,
89
+
68
90
  disabled,
69
91
  },
70
92
  };