@navikt/ds-react 1.4.1 → 1.4.3

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 (35) hide show
  1. package/_docs.json +120 -0
  2. package/cjs/date/hooks/useDatepicker.js +36 -11
  3. package/cjs/date/hooks/useMonthPicker.js +34 -10
  4. package/cjs/date/hooks/useRangeDatepicker.js +181 -65
  5. package/cjs/date/utils/format-date.js +3 -3
  6. package/cjs/date/utils/parse-date.js +14 -8
  7. package/esm/date/hooks/index.d.ts +3 -0
  8. package/esm/date/hooks/index.js.map +1 -1
  9. package/esm/date/hooks/useDatepicker.d.ts +18 -0
  10. package/esm/date/hooks/useDatepicker.js +36 -11
  11. package/esm/date/hooks/useDatepicker.js.map +1 -1
  12. package/esm/date/hooks/useMonthPicker.d.ts +17 -0
  13. package/esm/date/hooks/useMonthPicker.js +34 -10
  14. package/esm/date/hooks/useMonthPicker.js.map +1 -1
  15. package/esm/date/hooks/useRangeDatepicker.d.ts +12 -2
  16. package/esm/date/hooks/useRangeDatepicker.js +181 -65
  17. package/esm/date/hooks/useRangeDatepicker.js.map +1 -1
  18. package/esm/date/utils/format-date.d.ts +1 -1
  19. package/esm/date/utils/format-date.js +3 -3
  20. package/esm/date/utils/format-date.js.map +1 -1
  21. package/esm/date/utils/parse-date.d.ts +1 -1
  22. package/esm/date/utils/parse-date.js +14 -8
  23. package/esm/date/utils/parse-date.js.map +1 -1
  24. package/package.json +2 -2
  25. package/src/date/datepicker/datepicker.stories.tsx +30 -5
  26. package/src/date/hooks/index.ts +3 -0
  27. package/src/date/hooks/useDatepicker.tsx +73 -10
  28. package/src/date/hooks/useMonthPicker.tsx +68 -10
  29. package/src/date/hooks/useRangeDatepicker.test.tsx +59 -0
  30. package/src/date/hooks/useRangeDatepicker.tsx +285 -92
  31. package/src/date/monthpicker/monthpicker.stories.tsx +20 -0
  32. package/src/date/utils/__tests__/format-dates.test.ts +4 -4
  33. package/src/date/utils/__tests__/parse-dates.test.ts +0 -40
  34. package/src/date/utils/format-date.ts +5 -3
  35. package/src/date/utils/parse-date.ts +18 -9
@@ -3,16 +3,10 @@ import { isValidDate } from ".";
3
3
  export const INPUT_DATE_STRING_FORMAT_DATE = "dd.MM.yyyy";
4
4
  export const INPUT_DATE_STRING_FORMAT_MONTH = "MMMM yyyy";
5
5
  const ALLOWED_INPUT_FORMATS_DATE = [
6
- "ddMMyy",
7
- "d.M.yy",
8
- "dd.MM.yy",
9
- "dd/MM/yy",
10
- "dd-MM-yy",
11
6
  INPUT_DATE_STRING_FORMAT_DATE,
12
7
  "ddMMyyyy",
13
8
  "dd/MM/yyyy",
14
9
  "dd-MM-yyyy",
15
- "d.M.yyyy",
16
10
  ];
17
11
  const ALLOWED_INPUT_FORMATS_MONTH = [
18
12
  "M/yyyy",
@@ -23,15 +17,27 @@ const ALLOWED_INPUT_FORMATS_MONTH = [
23
17
  INPUT_DATE_STRING_FORMAT_MONTH,
24
18
  ...ALLOWED_INPUT_FORMATS_DATE,
25
19
  ];
20
+ const isTwoDigitYear = (dateString, today, locale, formats) => {
21
+ let parsed;
22
+ const newFormat = formats.map((x) => x.replace("yyyy", "yy"));
23
+ for (const format of newFormat) {
24
+ parsed = parse(dateString, format, today, { locale });
25
+ if (isValidDate(parsed)) {
26
+ return true;
27
+ }
28
+ }
29
+ return false;
30
+ };
26
31
  export const parseDate = (date, today, locale, type) => {
27
32
  let parsed;
28
33
  const ALLOWED_FORMATS = type === "date" ? ALLOWED_INPUT_FORMATS_DATE : ALLOWED_INPUT_FORMATS_MONTH;
29
34
  for (const format of ALLOWED_FORMATS) {
30
35
  parsed = parse(date, format, today, { locale });
31
- if (isValidDate(parsed)) {
36
+ if (isValidDate(parsed) &&
37
+ !isTwoDigitYear(date, today, locale, ALLOWED_FORMATS)) {
32
38
  return parsed;
33
39
  }
34
40
  }
35
- return parsed;
41
+ return new Date("Invalid date");
36
42
  };
37
43
  //# sourceMappingURL=parse-date.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"parse-date.js","sourceRoot":"","sources":["../../../src/date/utils/parse-date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,CAAC;AAEhC,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAE1D,MAAM,CAAC,MAAM,8BAA8B,GAAG,WAAW,CAAC;AAE1D,MAAM,0BAA0B,GAAG;IACjC,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,UAAU;IACV,UAAU;IACV,6BAA6B;IAC7B,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,UAAU;CACX,CAAC;AAEF,MAAM,2BAA2B,GAAG;IAClC,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,SAAS;IACT,8BAA8B;IAC9B,GAAG,0BAA0B;CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,IAAY,EACZ,KAAW,EACX,MAAc,EACd,IAAsB,EACtB,EAAE;IACF,IAAI,MAAM,CAAC;IACX,MAAM,eAAe,GACnB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,2BAA2B,CAAC;IAC7E,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE;QACpC,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE;YACvB,OAAO,MAAM,CAAC;SACf;KACF;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
1
+ {"version":3,"file":"parse-date.js","sourceRoot":"","sources":["../../../src/date/utils/parse-date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,CAAC;AAEhC,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAE1D,MAAM,CAAC,MAAM,8BAA8B,GAAG,WAAW,CAAC;AAE1D,MAAM,0BAA0B,GAAG;IACjC,6BAA6B;IAC7B,UAAU;IACV,YAAY;IACZ,YAAY;CACb,CAAC;AAEF,MAAM,2BAA2B,GAAG;IAClC,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,SAAS;IACT,8BAA8B;IAC9B,GAAG,0BAA0B;CAC9B,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;IAC5D,IAAI,MAAM,CAAC;IACX,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9D,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE;QAC9B,MAAM,GAAG,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE;YACvB,OAAO,IAAI,CAAC;SACb;KACF;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,IAAY,EACZ,KAAW,EACX,MAAc,EACd,IAAsB,EAChB,EAAE;IACR,IAAI,MAAM,CAAC;IACX,MAAM,eAAe,GACnB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,2BAA2B,CAAC;IAC7E,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE;QACpC,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,IACE,WAAW,CAAC,MAAM,CAAC;YACnB,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,EACrD;YACA,OAAO,MAAM,CAAC;SACf;KACF;IACD,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC;AAClC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navikt/ds-react",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "NAV designsystem react components",
5
5
  "author": "NAV Designsystem team",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@floating-ui/react-dom-interactions": "0.9.2",
40
- "@navikt/ds-icons": "^1.4.1",
40
+ "@navikt/ds-icons": "^1.4.3",
41
41
  "@radix-ui/react-tabs": "1.0.0",
42
42
  "@radix-ui/react-toggle-group": "1.0.0",
43
43
  "clsx": "^1.2.1",
@@ -1,4 +1,3 @@
1
- import isSaturday from "date-fns/isSaturday";
2
1
  import React, { useId, useState } from "react";
3
2
  import { UNSAFE_useDatepicker, UNSAFE_useRangeDatepicker } from "..";
4
3
  import { Button } from "../..";
@@ -221,8 +220,13 @@ export const UserControlled = () => {
221
220
  };
222
221
 
223
222
  export const Validering = () => {
224
- const { datepickerProps, selectedDay, inputProps } = UNSAFE_useDatepicker({
225
- fromDate: new Date("Aug 23 2019"),
223
+ const [error, setError] = useState(false);
224
+ const { datepickerProps, inputProps } = UNSAFE_useDatepicker({
225
+ fromDate: new Date("Aug 2 2019"),
226
+ onValidate: (val) => setError(val.isWeekend),
227
+ defaultSelected: new Date("Nov 26 2022"),
228
+ disableWeekends: true,
229
+ onDateChange: console.log,
226
230
  });
227
231
 
228
232
  return (
@@ -230,8 +234,8 @@ export const Validering = () => {
230
234
  <DatePicker {...datepickerProps}>
231
235
  <DatePicker.Input
232
236
  error={
233
- selectedDay && isSaturday(selectedDay)
234
- ? "NAV-kontoret er ikke åpent lørdager. Velg en annen dag."
237
+ error
238
+ ? "NAV-kontoret er ikke åpent i helger. Velg en annen dag."
235
239
  : undefined
236
240
  }
237
241
  {...inputProps}
@@ -261,3 +265,24 @@ export const ErrorInput = () => {
261
265
  </div>
262
266
  );
263
267
  };
268
+
269
+ export const UseRangedDatepickerValidation = () => {
270
+ const { datepickerProps, fromInputProps, toInputProps } =
271
+ UNSAFE_useRangeDatepicker({
272
+ fromDate: new Date("Aug 23 2019"),
273
+ disableWeekends: true,
274
+ disabled: [new Date("Oct 10 2022")],
275
+ onValidate: console.table,
276
+ });
277
+
278
+ return (
279
+ <div style={{ display: "flex", gap: "1rem" }}>
280
+ <DatePicker {...datepickerProps}>
281
+ <div style={{ display: "flex", gap: "1rem" }}>
282
+ <DatePicker.Input {...fromInputProps} label="Fra" />
283
+ <DatePicker.Input {...toInputProps} label="Til" />
284
+ </div>
285
+ </DatePicker>
286
+ </div>
287
+ );
288
+ };
@@ -1,6 +1,9 @@
1
1
  export { useDatepicker as UNSAFE_useDatepicker } from "./useDatepicker";
2
+ export type { DateValidationT } from "./useDatepicker";
2
3
  export { useRangeDatepicker as UNSAFE_useRangeDatepicker } from "./useRangeDatepicker";
4
+ export type { RangeValidationT } from "./useRangeDatepicker";
3
5
  export { useMonthpicker as UNSAFE_useMonthpicker } from "./useMonthPicker";
6
+ export type { MonthValidationT } from "./useMonthPicker";
4
7
  export { useDateInputContext, DateContext } from "./useDateInputContext";
5
8
  export {
6
9
  useSharedMonthContext,
@@ -36,6 +36,15 @@ export interface UseDatepickerOptions
36
36
  * Callback for changed state
37
37
  */
38
38
  onDateChange?: (val?: Date) => void;
39
+ /**
40
+ * Input-format
41
+ * @default "dd.MM.yyyy"
42
+ */
43
+ inputFormat?: string;
44
+ /**
45
+ * validation-callback
46
+ */
47
+ onValidate?: (val: DateValidationT) => void;
39
48
  }
40
49
 
41
50
  interface UseDatepickerValue {
@@ -62,6 +71,27 @@ interface UseDatepickerValue {
62
71
  setSelected: (date?: Date) => void;
63
72
  }
64
73
 
74
+ export type DateValidationT = {
75
+ isDisabled: boolean;
76
+ isWeekend: boolean;
77
+ isEmpty: boolean;
78
+ isInvalid: boolean;
79
+ isValidDate: boolean;
80
+ isBefore: boolean;
81
+ isAfter: boolean;
82
+ };
83
+
84
+ const getValidationMessage = (val = {}): DateValidationT => ({
85
+ isDisabled: false,
86
+ isWeekend: false,
87
+ isEmpty: false,
88
+ isInvalid: false,
89
+ isBefore: false,
90
+ isAfter: false,
91
+ isValidDate: true,
92
+ ...val,
93
+ });
94
+
65
95
  export const useDatepicker = (
66
96
  opt: UseDatepickerOptions = {}
67
97
  ): UseDatepickerValue => {
@@ -75,6 +105,8 @@ export const useDatepicker = (
75
105
  disabled,
76
106
  disableWeekends,
77
107
  onDateChange,
108
+ inputFormat,
109
+ onValidate,
78
110
  } = opt;
79
111
 
80
112
  const locale = getLocaleFromString(_locale);
@@ -90,7 +122,7 @@ export const useDatepicker = (
90
122
  const [open, setOpen] = useState(false);
91
123
 
92
124
  const defaultInputValue = defaultSelected
93
- ? formatDateForInput(defaultSelected, locale, "date")
125
+ ? formatDateForInput(defaultSelected, locale, "date", inputFormat)
94
126
  : "";
95
127
  const [inputValue, setInputValue] = useState(defaultInputValue);
96
128
 
@@ -99,6 +131,11 @@ export const useDatepicker = (
99
131
  setSelectedDay(date);
100
132
  };
101
133
 
134
+ const updateValidation = (val: Partial<DateValidationT> = {}) => {
135
+ const msg = getValidationMessage(val);
136
+ onValidate?.(msg);
137
+ };
138
+
102
139
  const handleFocusIn = useCallback(
103
140
  (e) => {
104
141
  if (!e?.target || !e?.target?.nodeType) {
@@ -134,7 +171,9 @@ export const useDatepicker = (
134
171
  const setSelected = (date: Date | undefined) => {
135
172
  updateDate(date);
136
173
  setMonth(date ?? today);
137
- setInputValue(date ? formatDateForInput(date, locale, "date") : "");
174
+ setInputValue(
175
+ date ? formatDateForInput(date, locale, "date", inputFormat) : ""
176
+ );
138
177
  };
139
178
 
140
179
  const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
@@ -143,13 +182,14 @@ export const useDatepicker = (
143
182
  let day = parseDate(e.target.value, today, locale, "date");
144
183
  if (isValidDate(day)) {
145
184
  setMonth(day);
146
- setInputValue(formatDateForInput(day, locale, "date"));
185
+ setInputValue(formatDateForInput(day, locale, "date", inputFormat));
147
186
  }
148
187
  };
149
188
 
150
189
  const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
151
190
  let day = parseDate(e.target.value, today, locale, "date");
152
- isValidDate(day) && setInputValue(formatDateForInput(day, locale, "date"));
191
+ isValidDate(day) &&
192
+ setInputValue(formatDateForInput(day, locale, "date", inputFormat));
153
193
  };
154
194
 
155
195
  /* Only allow de-selecting if not required */
@@ -162,11 +202,15 @@ export const useDatepicker = (
162
202
  if (!required && selected) {
163
203
  updateDate(undefined);
164
204
  setInputValue("");
205
+ updateValidation({ isValidDate: false, isEmpty: true });
165
206
  return;
166
207
  }
167
208
  updateDate(day);
209
+ updateValidation();
168
210
  setMonth(day);
169
- setInputValue(day ? formatDateForInput(day, locale, "date") : "");
211
+ setInputValue(
212
+ day ? formatDateForInput(day, locale, "date", inputFormat) : ""
213
+ );
170
214
  };
171
215
 
172
216
  // When changing the input field, save its value in state and check if the
@@ -176,22 +220,39 @@ export const useDatepicker = (
176
220
  setInputValue(e.target.value);
177
221
  const day = parseDate(e.target.value, today, locale, "date");
178
222
 
223
+ const isBefore =
224
+ fromDate && day && differenceInCalendarDays(fromDate, day) > 0;
225
+ const isAfter = toDate && day && differenceInCalendarDays(day, toDate) > 0;
226
+
179
227
  if (
180
228
  !isValidDate(day) ||
181
- (disabled &&
182
- ((disableWeekends && isWeekend(day)) || isMatch(day, disabled)))
229
+ (disableWeekends && isWeekend(day)) ||
230
+ (disabled && isMatch(day, disabled))
183
231
  ) {
184
232
  updateDate(undefined);
233
+ updateValidation({
234
+ isInvalid: isValidDate(day),
235
+ isWeekend: disableWeekends && isWeekend(day),
236
+ isDisabled: disabled && isMatch(day, disabled),
237
+ isValidDate: false,
238
+ isEmpty: !e.target.value,
239
+ isBefore: isBefore ?? false,
240
+ isAfter: isAfter ?? false,
241
+ });
185
242
  return;
186
243
  }
187
244
 
188
- const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
189
- const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
190
245
  if (isBefore || isAfter) {
191
246
  updateDate(undefined);
247
+ updateValidation({
248
+ isValidDate: false,
249
+ isBefore: isBefore ?? false,
250
+ isAfter: isAfter ?? false,
251
+ });
192
252
  return;
193
253
  }
194
254
  updateDate(day);
255
+ updateValidation();
195
256
  setMonth(day);
196
257
  };
197
258
 
@@ -217,13 +278,15 @@ export const useDatepicker = (
217
278
  month,
218
279
  onMonthChange: (month) => setMonth(month),
219
280
  onDayClick: handleDayClick,
220
- selected: selectedDay,
281
+ selected: selectedDay ?? new Date("Invalid date"),
221
282
  locale: _locale,
222
283
  fromDate,
223
284
  toDate,
224
285
  today,
225
286
  open,
226
287
  onOpenToggle: () => setOpen((x) => !x),
288
+ disabled,
289
+ disableWeekends,
227
290
  ref: daypickerRef,
228
291
  };
229
292
 
@@ -22,6 +22,15 @@ export interface UseMonthPickerOptions
22
22
  * Callback for month-change
23
23
  */
24
24
  onMonthChange?: (date?: Date) => void;
25
+ /**
26
+ * Input-format
27
+ * @default "MMMM yyyy"
28
+ */
29
+ inputFormat?: string;
30
+ /**
31
+ * validation-callback
32
+ */
33
+ onValidate?: (val: MonthValidationT) => void;
25
34
  }
26
35
 
27
36
  interface UseMonthPickerValue {
@@ -48,6 +57,25 @@ interface UseMonthPickerValue {
48
57
  reset: () => void;
49
58
  }
50
59
 
60
+ export type MonthValidationT = {
61
+ isDisabled: boolean;
62
+ isEmpty: boolean;
63
+ isInvalid: boolean;
64
+ isValidMonth: boolean;
65
+ isBefore: boolean;
66
+ isAfter: boolean;
67
+ };
68
+
69
+ const getValidationMessage = (val = {}): MonthValidationT => ({
70
+ isDisabled: false,
71
+ isEmpty: false,
72
+ isInvalid: false,
73
+ isBefore: false,
74
+ isAfter: false,
75
+ isValidMonth: true,
76
+ ...val,
77
+ });
78
+
51
79
  export const useMonthpicker = (
52
80
  opt: UseMonthPickerOptions = {}
53
81
  ): UseMonthPickerValue => {
@@ -59,6 +87,8 @@ export const useMonthpicker = (
59
87
  disabled,
60
88
  required,
61
89
  onMonthChange,
90
+ inputFormat,
91
+ onValidate,
62
92
  } = opt;
63
93
 
64
94
  const [defaultSelected, setDefaultSelected] = useState(_defaultSelected);
@@ -75,7 +105,7 @@ export const useMonthpicker = (
75
105
  const [open, setOpen] = useState(false);
76
106
 
77
107
  const defaultInputValue = defaultSelected
78
- ? formatDateForInput(defaultSelected, locale, "month")
108
+ ? formatDateForInput(defaultSelected, locale, "month", inputFormat)
79
109
  : "";
80
110
 
81
111
  const [inputValue, setInputValue] = useState(defaultInputValue);
@@ -85,6 +115,11 @@ export const useMonthpicker = (
85
115
  setSelectedMonth(date);
86
116
  };
87
117
 
118
+ const updateValidation = (val: Partial<MonthValidationT> = {}) => {
119
+ const msg = getValidationMessage(val);
120
+ onValidate?.(msg);
121
+ };
122
+
88
123
  const handleFocusIn = useCallback(
89
124
  (e) => {
90
125
  if (!e?.target || !e?.target?.nodeType) {
@@ -120,7 +155,9 @@ export const useMonthpicker = (
120
155
  const setSelected = (date: Date | undefined) => {
121
156
  updateMonth(date);
122
157
  setYear(date ?? today);
123
- setInputValue(date ? formatDateForInput(date, locale, "month") : "");
158
+ setInputValue(
159
+ date ? formatDateForInput(date, locale, "month", inputFormat) : ""
160
+ );
124
161
  };
125
162
 
126
163
  const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
@@ -128,13 +165,14 @@ export const useMonthpicker = (
128
165
  let day = parseDate(e.target.value, today, locale, "month");
129
166
  if (isValidDate(day)) {
130
167
  setYear(day);
131
- setInputValue(formatDateForInput(day, locale, "month"));
168
+ setInputValue(formatDateForInput(day, locale, "month", inputFormat));
132
169
  }
133
170
  };
134
171
 
135
172
  const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
136
173
  let day = parseDate(e.target.value, today, locale, "month");
137
- isValidDate(day) && setInputValue(formatDateForInput(day, locale, "month"));
174
+ isValidDate(day) &&
175
+ setInputValue(formatDateForInput(day, locale, "month", inputFormat));
138
176
  };
139
177
 
140
178
  /* Only allow de-selecting if not required */
@@ -146,11 +184,15 @@ export const useMonthpicker = (
146
184
 
147
185
  if (!required && !month) {
148
186
  updateMonth(undefined);
187
+ updateValidation({ isValidMonth: false, isEmpty: true });
149
188
  setInputValue("");
150
189
  return;
151
190
  }
152
191
  updateMonth(month);
153
- setInputValue(month ? formatDateForInput(month, locale, "month") : "");
192
+ updateValidation();
193
+ setInputValue(
194
+ month ? formatDateForInput(month, locale, "month", inputFormat) : ""
195
+ );
154
196
  };
155
197
 
156
198
  // When changing the input field, save its value in state and check if the
@@ -160,32 +202,48 @@ export const useMonthpicker = (
160
202
  setInputValue(e.target.value);
161
203
  const month = parseDate(e.target.value, today, locale, "month");
162
204
 
163
- if (!isValidDate(month) || (disabled && isMatch(month, disabled))) {
164
- updateMonth(undefined);
165
- return;
166
- }
167
-
168
205
  const isBefore =
169
206
  fromDate &&
207
+ month &&
170
208
  (fromDate.getFullYear() > month.getFullYear() ||
171
209
  (fromDate.getFullYear() === month.getFullYear() &&
172
210
  fromDate.getMonth() > month.getMonth()));
173
211
 
174
212
  const isAfter =
175
213
  toDate &&
214
+ month &&
176
215
  (toDate.getFullYear() < month.getFullYear() ||
177
216
  (toDate.getFullYear() === month.getFullYear() &&
178
217
  toDate.getMonth() < month.getMonth()));
179
218
 
219
+ if (!isValidDate(month) || (disabled && isMatch(month, disabled))) {
220
+ updateMonth(undefined);
221
+ updateValidation({
222
+ isInvalid: isValidDate(month),
223
+ isDisabled: disabled && isMatch(month, disabled),
224
+ isValidMonth: false,
225
+ isEmpty: !e.target.value,
226
+ isBefore: isBefore ?? false,
227
+ isAfter: isAfter ?? false,
228
+ });
229
+ return;
230
+ }
231
+
180
232
  if (
181
233
  isAfter ||
182
234
  isBefore ||
183
235
  (fromDate && toDate && !isMatch(month, [{ from: fromDate, to: toDate }]))
184
236
  ) {
185
237
  updateMonth(undefined);
238
+ updateValidation({
239
+ isValidMonth: false,
240
+ isBefore: isBefore ?? false,
241
+ isAfter: isAfter ?? false,
242
+ });
186
243
  return;
187
244
  }
188
245
  updateMonth(month);
246
+ updateValidation();
189
247
  setYear(month);
190
248
  };
191
249
 
@@ -0,0 +1,59 @@
1
+ /* eslint-disable react/jsx-pascal-case */
2
+ import { act, render } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import React from "react";
5
+ import { UNSAFE_DatePicker, UNSAFE_useRangeDatepicker } from "..";
6
+
7
+ const RangeDemo = () => {
8
+ const { datepickerProps, fromInputProps, selectedRange, toInputProps } =
9
+ UNSAFE_useRangeDatepicker({
10
+ fromDate: new Date("Aug 23 2019"),
11
+ });
12
+
13
+ return (
14
+ <div style={{ display: "flex", gap: "1rem" }}>
15
+ <UNSAFE_DatePicker {...datepickerProps}>
16
+ <UNSAFE_DatePicker.Input {...fromInputProps} label="Fra" />
17
+ <UNSAFE_DatePicker.Input {...toInputProps} label="Til" />
18
+ </UNSAFE_DatePicker>
19
+ <div title="res">{JSON.stringify(selectedRange)}</div>
20
+ </div>
21
+ );
22
+ };
23
+
24
+ describe("Writing in input sets correct values", () => {
25
+ it("useRangeDatepicker same date", async () => {
26
+ const utils = render(<RangeDemo />);
27
+
28
+ const fraInput = utils.getByRole("textbox", { name: "Fra" });
29
+ const tilInput = utils.getByRole("textbox", { name: "Til" });
30
+ await act(async () => {
31
+ await userEvent.type(fraInput, "03.08.2022");
32
+ await userEvent.type(tilInput, "03.08.2022");
33
+ });
34
+ const res = utils.getByTitle("res");
35
+ expect(res.innerHTML).toEqual(
36
+ JSON.stringify({
37
+ from: "2022-08-02T22:00:00.000Z",
38
+ to: "2022-08-02T22:00:00.000Z",
39
+ })
40
+ );
41
+ });
42
+
43
+ it("useRangeDatepicker before after to ", async () => {
44
+ const utils = render(<RangeDemo />);
45
+
46
+ const fraInput = utils.getByRole("textbox", { name: "Fra" });
47
+ const tilInput = utils.getByRole("textbox", { name: "Til" });
48
+ await act(async () => {
49
+ await userEvent.type(fraInput, "03.08.2022");
50
+ await userEvent.type(tilInput, "02.08.2022");
51
+ });
52
+ const res = utils.getByTitle("res");
53
+ expect(res.innerHTML).toEqual(
54
+ JSON.stringify({
55
+ from: "2022-08-02T22:00:00.000Z",
56
+ })
57
+ );
58
+ });
59
+ });