@navikt/ds-react 1.2.2 → 1.3.0-alpha.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 (134) hide show
  1. package/_docs.json +1119 -319
  2. package/cjs/datepicker/DatePicker.js +122 -0
  3. package/cjs/datepicker/DatePickerInput.js +68 -0
  4. package/cjs/datepicker/DatePickerStandalone.js +80 -0
  5. package/cjs/datepicker/caption/Caption.js +23 -0
  6. package/cjs/datepicker/caption/DropdownCaption.js +36 -0
  7. package/cjs/datepicker/caption/index.js +10 -0
  8. package/cjs/datepicker/caption/package.json +6 -0
  9. package/cjs/datepicker/hooks/index.js +7 -0
  10. package/cjs/datepicker/hooks/package.json +6 -0
  11. package/cjs/datepicker/hooks/useDatepicker.js +101 -0
  12. package/cjs/datepicker/hooks/useRangeDatepicker.js +174 -0
  13. package/cjs/datepicker/index.js +11 -0
  14. package/cjs/datepicker/package.json +6 -0
  15. package/cjs/datepicker/utils/dates-disabled.js +29 -0
  16. package/cjs/datepicker/utils/format-date.js +7 -0
  17. package/cjs/datepicker/utils/get-dates.js +43 -0
  18. package/cjs/datepicker/utils/index.js +19 -0
  19. package/cjs/datepicker/utils/labels.js +59 -0
  20. package/cjs/datepicker/utils/locale.js +21 -0
  21. package/cjs/datepicker/utils/package.json +6 -0
  22. package/cjs/datepicker/utils/parse-date.js +29 -0
  23. package/cjs/datepicker/utils/valid-date.js +8 -0
  24. package/cjs/index.js +1 -0
  25. package/cjs/monthpicker/MonthPicker.js +112 -0
  26. package/cjs/monthpicker/index.js +8 -0
  27. package/cjs/monthpicker/package.json +6 -0
  28. package/cjs/monthpicker/utils/check-dates.js +12 -0
  29. package/cjs/monthpicker/utils/handle-selected.js +17 -0
  30. package/esm/chat/Chat.d.ts +1 -1
  31. package/esm/datepicker/DatePicker.d.ts +107 -0
  32. package/esm/datepicker/DatePicker.js +94 -0
  33. package/esm/datepicker/DatePicker.js.map +1 -0
  34. package/esm/datepicker/DatePickerInput.d.ts +25 -0
  35. package/esm/datepicker/DatePickerInput.js +40 -0
  36. package/esm/datepicker/DatePickerInput.js.map +1 -0
  37. package/esm/datepicker/DatePickerStandalone.d.ts +12 -0
  38. package/esm/datepicker/DatePickerStandalone.js +52 -0
  39. package/esm/datepicker/DatePickerStandalone.js.map +1 -0
  40. package/esm/datepicker/caption/Caption.d.ts +4 -0
  41. package/esm/datepicker/caption/Caption.js +17 -0
  42. package/esm/datepicker/caption/Caption.js.map +1 -0
  43. package/esm/datepicker/caption/DropdownCaption.d.ts +4 -0
  44. package/esm/datepicker/caption/DropdownCaption.js +30 -0
  45. package/esm/datepicker/caption/DropdownCaption.js.map +1 -0
  46. package/esm/datepicker/caption/index.d.ts +2 -0
  47. package/esm/datepicker/caption/index.js +3 -0
  48. package/esm/datepicker/caption/index.js.map +1 -0
  49. package/esm/datepicker/hooks/index.d.ts +2 -0
  50. package/esm/datepicker/hooks/index.js +3 -0
  51. package/esm/datepicker/hooks/index.js.map +1 -0
  52. package/esm/datepicker/hooks/useDatepicker.d.ts +37 -0
  53. package/esm/datepicker/hooks/useDatepicker.js +98 -0
  54. package/esm/datepicker/hooks/useDatepicker.js.map +1 -0
  55. package/esm/datepicker/hooks/useRangeDatepicker.d.ts +36 -0
  56. package/esm/datepicker/hooks/useRangeDatepicker.js +171 -0
  57. package/esm/datepicker/hooks/useRangeDatepicker.js.map +1 -0
  58. package/esm/datepicker/index.d.ts +5 -0
  59. package/esm/datepicker/index.js +3 -0
  60. package/esm/datepicker/index.js.map +1 -0
  61. package/esm/datepicker/utils/dates-disabled.d.ts +1 -0
  62. package/esm/datepicker/utils/dates-disabled.js +26 -0
  63. package/esm/datepicker/utils/dates-disabled.js.map +1 -0
  64. package/esm/datepicker/utils/format-date.d.ts +1 -0
  65. package/esm/datepicker/utils/format-date.js +4 -0
  66. package/esm/datepicker/utils/format-date.js.map +1 -0
  67. package/esm/datepicker/utils/get-dates.d.ts +2 -0
  68. package/esm/datepicker/utils/get-dates.js +39 -0
  69. package/esm/datepicker/utils/get-dates.js.map +1 -0
  70. package/esm/datepicker/utils/index.d.ts +6 -0
  71. package/esm/datepicker/utils/index.js +7 -0
  72. package/esm/datepicker/utils/index.js.map +1 -0
  73. package/esm/datepicker/utils/labels.d.ts +4 -0
  74. package/esm/datepicker/utils/labels.js +55 -0
  75. package/esm/datepicker/utils/labels.js.map +1 -0
  76. package/esm/datepicker/utils/locale.d.ts +2 -0
  77. package/esm/datepicker/utils/locale.js +15 -0
  78. package/esm/datepicker/utils/locale.js.map +1 -0
  79. package/esm/datepicker/utils/parse-date.d.ts +2 -0
  80. package/esm/datepicker/utils/parse-date.js +26 -0
  81. package/esm/datepicker/utils/parse-date.js.map +1 -0
  82. package/esm/datepicker/utils/valid-date.d.ts +2 -0
  83. package/esm/datepicker/utils/valid-date.js +5 -0
  84. package/esm/datepicker/utils/valid-date.js.map +1 -0
  85. package/esm/index.d.ts +1 -0
  86. package/esm/index.js +1 -0
  87. package/esm/index.js.map +1 -1
  88. package/esm/monthpicker/MonthPicker.d.ts +27 -0
  89. package/esm/monthpicker/MonthPicker.js +84 -0
  90. package/esm/monthpicker/MonthPicker.js.map +1 -0
  91. package/esm/monthpicker/index.d.ts +2 -0
  92. package/esm/monthpicker/index.js +2 -0
  93. package/esm/monthpicker/index.js.map +1 -0
  94. package/esm/monthpicker/utils/check-dates.d.ts +2 -0
  95. package/esm/monthpicker/utils/check-dates.js +8 -0
  96. package/esm/monthpicker/utils/check-dates.js.map +1 -0
  97. package/esm/monthpicker/utils/handle-selected.d.ts +3 -0
  98. package/esm/monthpicker/utils/handle-selected.js +12 -0
  99. package/esm/monthpicker/utils/handle-selected.js.map +1 -0
  100. package/package.json +7 -3
  101. package/src/chat/Chat.tsx +1 -1
  102. package/src/chat/chat.stories.tsx +1 -5
  103. package/src/datepicker/DatePicker.tsx +281 -0
  104. package/src/datepicker/DatePickerInput.tsx +131 -0
  105. package/src/datepicker/DatePickerStandalone.tsx +121 -0
  106. package/src/datepicker/caption/Caption.tsx +51 -0
  107. package/src/datepicker/caption/DropdownCaption.tsx +94 -0
  108. package/src/datepicker/caption/index.ts +2 -0
  109. package/src/datepicker/datepicker.stories.mdx +467 -0
  110. package/src/datepicker/datepicker.stories.tsx +257 -0
  111. package/src/datepicker/hooks/index.ts +2 -0
  112. package/src/datepicker/hooks/useDatepicker.tsx +181 -0
  113. package/src/datepicker/hooks/useRangeDatepicker.tsx +285 -0
  114. package/src/datepicker/index.ts +5 -0
  115. package/src/datepicker/utils/__tests__/dates-disabled.test.ts +48 -0
  116. package/src/datepicker/utils/__tests__/format-dates.test.ts +14 -0
  117. package/src/datepicker/utils/__tests__/get-dates.test.ts +79 -0
  118. package/src/datepicker/utils/__tests__/parse-dates.test.ts +81 -0
  119. package/src/datepicker/utils/dates-disabled.ts +26 -0
  120. package/src/datepicker/utils/format-date.ts +5 -0
  121. package/src/datepicker/utils/get-dates.ts +44 -0
  122. package/src/datepicker/utils/index.ts +6 -0
  123. package/src/datepicker/utils/labels.ts +58 -0
  124. package/src/datepicker/utils/locale.ts +15 -0
  125. package/src/datepicker/utils/parse-date.ts +28 -0
  126. package/src/datepicker/utils/valid-date.ts +4 -0
  127. package/src/index.ts +1 -0
  128. package/src/monthpicker/MonthPicker.tsx +238 -0
  129. package/src/monthpicker/index.ts +2 -0
  130. package/src/monthpicker/monthpicker.stories.tsx +35 -0
  131. package/src/monthpicker/utils/__tests__/check-dates.test.ts +34 -0
  132. package/src/monthpicker/utils/__tests__/handle-selected.test.ts +87 -0
  133. package/src/monthpicker/utils/check-dates.ts +15 -0
  134. package/src/monthpicker/utils/handle-selected.ts +26 -0
@@ -0,0 +1,181 @@
1
+ import { differenceInCalendarDays } from "date-fns";
2
+ import React, { useCallback, useEffect, useRef, useState } from "react";
3
+ import { DayClickEventHandler } from "react-day-picker";
4
+ import { DatePickerProps } from "../DatePicker";
5
+ import { DatePickerInputProps } from "../DatePickerInput";
6
+ import { formatDateForInput, getLocaleFromString, isValidDate } from "../utils";
7
+ import { parseDate } from "../utils/parse-date";
8
+
9
+ export interface UseDatepickerOptions
10
+ extends Pick<
11
+ DatePickerProps,
12
+ | "locale"
13
+ | "fromDate"
14
+ | "toDate"
15
+ | "today"
16
+ | "toDate"
17
+ | "fromDate"
18
+ | "toDate"
19
+ > {
20
+ /** The initially selected date */
21
+ defaultSelected?: Date;
22
+ /** Make the selection required. */
23
+ required?: boolean;
24
+ /**
25
+ * Opens datepicker on input-focus
26
+ * @default true
27
+ */
28
+ openOnFocus?: boolean;
29
+ }
30
+
31
+ interface UseDatepickerValue {
32
+ /**
33
+ * Use: <DatePicker {...dayPickerProps}/>
34
+ */
35
+ dayPickerProps: DatePickerProps;
36
+ /**
37
+ * Use: <DatePicker.Input {...inputProps}/>
38
+ */
39
+ inputProps: Pick<
40
+ DatePickerInputProps,
41
+ "onChange" | "onFocus" | "onBlur" | "value" | "wrapperRef"
42
+ >;
43
+ /**
44
+ * Resets all states
45
+ */
46
+ reset: () => void;
47
+ /**
48
+ * Selected Date callback
49
+ */
50
+ selectedDay?: Date;
51
+ /**
52
+ * Manually set selected day if needed
53
+ */
54
+ setSelected: (date?: Date) => void;
55
+ }
56
+
57
+ export const useDatepicker = (
58
+ opt: UseDatepickerOptions = {}
59
+ ): UseDatepickerValue => {
60
+ const {
61
+ locale: _locale = "nb",
62
+ required,
63
+ defaultSelected,
64
+ today = new Date(),
65
+ fromDate,
66
+ toDate,
67
+ openOnFocus = true,
68
+ } = opt;
69
+
70
+ const locale = getLocaleFromString(_locale);
71
+
72
+ const inputRef = useRef<HTMLDivElement>(null);
73
+ const daypickerRef = useRef<HTMLDivElement>(null);
74
+
75
+ // Initialize states
76
+ const [month, setMonth] = useState(defaultSelected ?? today);
77
+ const [selectedDay, setSelectedDay] = useState(defaultSelected);
78
+ const [open, setOpen] = useState(false);
79
+
80
+ const defaultInputValue = defaultSelected
81
+ ? formatDateForInput(defaultSelected, locale)
82
+ : "";
83
+ const [inputValue, setInputValue] = useState(defaultInputValue);
84
+
85
+ const handleFocusOut = useCallback(
86
+ (e) =>
87
+ ![daypickerRef.current, inputRef.current].some((element) =>
88
+ element?.contains(e.relatedTarget)
89
+ ) && setOpen(false),
90
+ []
91
+ );
92
+
93
+ useEffect(() => {
94
+ const el = inputRef.current;
95
+ el?.addEventListener("focusout", handleFocusOut);
96
+ return () => el?.removeEventListener?.("focusout", handleFocusOut);
97
+ }, [handleFocusOut]);
98
+
99
+ const reset = () => {
100
+ setSelectedDay(defaultSelected);
101
+ setMonth(defaultSelected ?? today);
102
+ setInputValue(defaultInputValue ?? "");
103
+ };
104
+
105
+ const setSelected = (date: Date | undefined) => {
106
+ setSelectedDay(date);
107
+ setMonth(date ?? today);
108
+ setInputValue(date ? formatDateForInput(date, locale) : "");
109
+ };
110
+
111
+ const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
112
+ !open && openOnFocus && setOpen(true);
113
+ if (!e.target.value) {
114
+ reset();
115
+ return;
116
+ }
117
+ let day = parseDate(e.target.value, today, locale);
118
+ if (isValidDate(day)) {
119
+ setMonth(day);
120
+ setInputValue(formatDateForInput(day, locale));
121
+ }
122
+ };
123
+
124
+ const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
125
+ let day = parseDate(e.target.value, today, locale);
126
+ isValidDate(day) && setInputValue(formatDateForInput(day, locale));
127
+ };
128
+
129
+ /* Only allow de-selecting if not required */
130
+ const handleDayClick: DayClickEventHandler = (day, { selected }) => {
131
+ if (!required && selected) {
132
+ setSelectedDay(undefined);
133
+ setInputValue("");
134
+ return;
135
+ }
136
+ setSelectedDay(day);
137
+ setInputValue(day ? formatDateForInput(day, locale) : "");
138
+ };
139
+
140
+ // When changing the input field, save its value in state and check if the
141
+ // string is a valid date. If it is a valid day, set it as selected and update
142
+ // the calendar’s month.
143
+ const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
144
+ setInputValue(e.target.value);
145
+ const day = parseDate(e.target.value, today, locale);
146
+
147
+ const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
148
+ const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
149
+ if (!isValidDate(day) || isBefore || isAfter) {
150
+ setSelectedDay(undefined);
151
+ return;
152
+ }
153
+ setSelectedDay(day);
154
+ setMonth(day);
155
+ };
156
+
157
+ const dayPickerProps = {
158
+ month,
159
+ onMonthChange: (month) => setMonth(month),
160
+ onDayClick: handleDayClick,
161
+ selected: selectedDay,
162
+ locale: _locale,
163
+ fromDate,
164
+ toDate,
165
+ today,
166
+ open,
167
+ onClose: () => setOpen(false),
168
+ onOpenToggle: () => setOpen((x) => !x),
169
+ ref: daypickerRef,
170
+ };
171
+
172
+ const inputProps = {
173
+ onChange: handleChange,
174
+ onFocus: handleFocus,
175
+ onBlur: handleBlur,
176
+ value: inputValue,
177
+ wrapperRef: inputRef,
178
+ };
179
+
180
+ return { dayPickerProps, inputProps, reset, selectedDay, setSelected };
181
+ };
@@ -0,0 +1,285 @@
1
+ import { differenceInCalendarDays } from "date-fns";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { DateRange } from "react-day-picker";
4
+ import { DatePickerProps } from "../DatePicker";
5
+ import { DatePickerInputProps } from "../DatePickerInput";
6
+ import {
7
+ formatDateForInput,
8
+ getLocaleFromString,
9
+ isValidDate,
10
+ parseDate,
11
+ } from "../utils";
12
+ import { UseDatepickerOptions } from "./useDatepicker";
13
+
14
+ interface UseRangeDatepickerOptions
15
+ extends Omit<UseDatepickerOptions, "defaultSelected"> {
16
+ /** The initially selected date-range */
17
+ defaultSelected?: DateRange;
18
+ }
19
+
20
+ interface UseRangeDatepickerValue {
21
+ /**
22
+ * Use: <DatePicker {...dayPickerProps}/>
23
+ */
24
+ dayPickerProps: DatePickerProps;
25
+ /**
26
+ * Use: <DatePicker.Input label="from" {...fromInputProps}/>
27
+ */
28
+ fromInputProps: Pick<
29
+ DatePickerInputProps,
30
+ "onChange" | "onFocus" | "onBlur" | "value"
31
+ >;
32
+ /**
33
+ * Use: <DatePicker.Input label="to" {...toInputProps}/>
34
+ */
35
+ toInputProps: Pick<
36
+ DatePickerInputProps,
37
+ "onChange" | "onFocus" | "onBlur" | "value"
38
+ >;
39
+ /**
40
+ * Resets all states
41
+ */
42
+ reset: () => void;
43
+ /**
44
+ * Selected range-callback
45
+ */
46
+ selectedRange?: DateRange;
47
+ /**
48
+ * Manually set selected range if needed
49
+ */
50
+ setSelected: (date?: DateRange) => void;
51
+ }
52
+
53
+ const RANGE = {
54
+ FROM: "FROM",
55
+ TO: "TO",
56
+ } as const;
57
+
58
+ type RangeT = typeof RANGE[keyof typeof RANGE];
59
+
60
+ export const useRangeDatepicker = (
61
+ opt: UseRangeDatepickerOptions = {}
62
+ ): UseRangeDatepickerValue => {
63
+ const {
64
+ locale: _locale = "nb",
65
+ defaultSelected,
66
+ today = new Date(),
67
+ fromDate,
68
+ toDate,
69
+ openOnFocus = true,
70
+ } = opt;
71
+
72
+ const locale = getLocaleFromString(_locale);
73
+
74
+ const inputRefTo = useRef<HTMLDivElement>(null);
75
+ const inputRefFrom = useRef<HTMLDivElement>(null);
76
+ const datePickerRef = useRef<HTMLDivElement | null>(null);
77
+
78
+ // Initialize states
79
+ const [month, setMonth] = useState(
80
+ defaultSelected ? defaultSelected.from : today
81
+ );
82
+ const [selectedRange, setSelectedRange] = useState(defaultSelected);
83
+
84
+ const [fromInputValue, setFromInputValue] = useState(
85
+ defaultSelected?.from
86
+ ? formatDateForInput(defaultSelected.from, locale)
87
+ : ""
88
+ );
89
+
90
+ const [toInputValue, setToInputValue] = useState(
91
+ defaultSelected?.to ? formatDateForInput(defaultSelected.to, locale) : ""
92
+ );
93
+ const [open, setOpen] = useState(false);
94
+
95
+ const handleFocusOut = useCallback(
96
+ (e) =>
97
+ ![datePickerRef.current, inputRefTo.current, inputRefFrom.current].some(
98
+ (element) => element?.contains(e.relatedTarget)
99
+ ) && setOpen(false),
100
+ []
101
+ );
102
+
103
+ useEffect(() => {
104
+ const from = inputRefFrom.current;
105
+ const to = inputRefTo.current;
106
+ from?.addEventListener("focusout", handleFocusOut);
107
+ to?.addEventListener("focusout", handleFocusOut);
108
+ return () => {
109
+ from?.removeEventListener?.("focusout", handleFocusOut);
110
+ to?.removeEventListener?.("focusout", handleFocusOut);
111
+ };
112
+ }, [handleFocusOut]);
113
+
114
+ const reset = () => {
115
+ setSelectedRange(defaultSelected);
116
+ setMonth(defaultSelected ? defaultSelected.from : today);
117
+ setFromInputValue(
118
+ defaultSelected?.from
119
+ ? formatDateForInput(defaultSelected.from, locale)
120
+ : ""
121
+ );
122
+ setToInputValue(
123
+ defaultSelected?.to ? formatDateForInput(defaultSelected.to, locale) : ""
124
+ );
125
+ };
126
+
127
+ const setSelected = (range?: DateRange) => {
128
+ setSelectedRange(range);
129
+ setFromInputValue(
130
+ range?.from ? formatDateForInput(range.from, locale) : ""
131
+ );
132
+ setToInputValue(range?.to ? formatDateForInput(range?.to, locale) : "");
133
+ };
134
+
135
+ const handleFocus = (e, src: RangeT) => {
136
+ !open && openOnFocus && setOpen(true);
137
+ let day = parseDate(e.target.value, today, locale);
138
+ if (isValidDate(day)) {
139
+ setMonth(day);
140
+ src === RANGE.FROM
141
+ ? setFromInputValue(formatDateForInput(day, locale))
142
+ : setToInputValue(formatDateForInput(day, locale));
143
+ }
144
+ };
145
+
146
+ const handleInputs = (day: Date, src: RangeT) => {
147
+ if (src === RANGE.FROM) {
148
+ const isAfter =
149
+ toInputValue &&
150
+ differenceInCalendarDays(day, parseDate(toInputValue, today, locale)) >
151
+ 0;
152
+
153
+ if (isAfter) {
154
+ setFromInputValue(
155
+ formatDateForInput(parseDate(toInputValue, today, locale), locale)
156
+ );
157
+ setToInputValue(formatDateForInput(day, locale));
158
+ } else {
159
+ setFromInputValue(formatDateForInput(day, locale));
160
+ }
161
+ } else if (src === RANGE.TO) {
162
+ const isBefore =
163
+ fromInputValue &&
164
+ differenceInCalendarDays(
165
+ parseDate(fromInputValue, today, locale),
166
+ day
167
+ ) > 0;
168
+
169
+ if (isBefore) {
170
+ setToInputValue(
171
+ formatDateForInput(parseDate(fromInputValue, today, locale), locale)
172
+ );
173
+ setFromInputValue(formatDateForInput(day, locale));
174
+ } else {
175
+ setToInputValue(formatDateForInput(day, locale));
176
+ }
177
+ }
178
+ };
179
+
180
+ const handleBlur = (e, src: RangeT) => {
181
+ let day = parseDate(e.target.value, today, locale);
182
+ if (!isValidDate(day)) {
183
+ return;
184
+ }
185
+
186
+ handleInputs(day, src);
187
+ };
188
+
189
+ const handleSelect = (range) => {
190
+ const prevToRange =
191
+ !selectedRange?.from && selectedRange?.to ? selectedRange?.to : range?.to;
192
+
193
+ range?.from
194
+ ? setFromInputValue(formatDateForInput(range?.from, locale))
195
+ : setFromInputValue("");
196
+ prevToRange
197
+ ? setToInputValue(formatDateForInput(prevToRange, locale))
198
+ : setToInputValue("");
199
+ setSelectedRange({ from: range?.from, to: prevToRange });
200
+ };
201
+
202
+ /* live-update datepicker based on changes in inputfields */
203
+ const handleChange = (e, src: RangeT) => {
204
+ src === RANGE.FROM
205
+ ? setFromInputValue(e.target.value)
206
+ : setToInputValue(e.target.value);
207
+ const day = parseDate(e.target.value, today, locale);
208
+
209
+ const isBefore = fromDate && differenceInCalendarDays(fromDate, day) > 0;
210
+ const isAfter = toDate && differenceInCalendarDays(day, toDate) > 0;
211
+ if (!isValidDate(day) || isBefore || isAfter) {
212
+ src === RANGE.FROM
213
+ ? setSelectedRange((x) => ({ ...x, from: undefined }))
214
+ : setSelectedRange((x) => ({ from: x?.from, to: undefined }));
215
+ return;
216
+ }
217
+
218
+ /* If to-value < from-value, switch places in state */
219
+ if (
220
+ src === RANGE.TO &&
221
+ selectedRange?.from &&
222
+ differenceInCalendarDays(selectedRange?.from, day) >= 0
223
+ ) {
224
+ setSelectedRange({ from: day, to: selectedRange?.from });
225
+ setMonth(day);
226
+ return;
227
+ }
228
+
229
+ /* If from-value > to-value , switch places in state */
230
+ if (
231
+ src === RANGE.FROM &&
232
+ selectedRange?.to &&
233
+ differenceInCalendarDays(day, selectedRange?.to) >= 0
234
+ ) {
235
+ setSelectedRange({ to: day, from: selectedRange?.to });
236
+ setMonth(day);
237
+ return;
238
+ }
239
+
240
+ src === RANGE.FROM && setSelectedRange((x) => ({ ...x, from: day }));
241
+ src === RANGE.TO && setSelectedRange((x) => ({ from: x?.from, to: day }));
242
+ setMonth(day);
243
+ };
244
+
245
+ const dayPickerProps = {
246
+ month: month,
247
+ onMonthChange: (month) => setMonth(month),
248
+ onSelect: handleSelect,
249
+ selected: selectedRange,
250
+ locale: _locale,
251
+ fromDate,
252
+ toDate,
253
+ today,
254
+ mode: "range" as const,
255
+ open,
256
+ onClose: () => setOpen(false),
257
+ onOpenToggle: () => setOpen((x) => !x),
258
+ ref: datePickerRef,
259
+ };
260
+
261
+ const fromInputProps = {
262
+ onChange: (e) => handleChange(e, RANGE.FROM),
263
+ onFocus: (e) => handleFocus(e, RANGE.FROM),
264
+ onBlur: (e) => handleBlur(e, RANGE.FROM),
265
+ value: fromInputValue,
266
+ wrapperRef: inputRefFrom,
267
+ };
268
+
269
+ const toInputProps = {
270
+ onChange: (e) => handleChange(e, RANGE.TO),
271
+ onFocus: (e) => handleFocus(e, RANGE.TO),
272
+ onBlur: (e) => handleBlur(e, RANGE.TO),
273
+ value: toInputValue,
274
+ wrapperRef: inputRefTo,
275
+ };
276
+
277
+ return {
278
+ dayPickerProps,
279
+ fromInputProps,
280
+ toInputProps,
281
+ reset,
282
+ selectedRange,
283
+ setSelected,
284
+ };
285
+ };
@@ -0,0 +1,5 @@
1
+ export { default as DatePicker } from "./DatePicker";
2
+ export { DatePickerProps } from "./DatePicker";
3
+ export { DatePickerStandaloneProps } from "./DatePickerStandalone";
4
+ export { DatePickerInputProps } from "./DatePickerInput";
5
+ export { useDatepicker, useRangeDatepicker } from "./hooks";
@@ -0,0 +1,48 @@
1
+ import { disableDate } from "../dates-disabled";
2
+
3
+ describe("Returns if date should be disabled", () => {
4
+ test("Should be disabled using Date (true)", () => {
5
+ const dateToDisable = new Date("Aug 3 2022");
6
+ expect(disableDate(dateToDisable, new Date("Aug 3 2022"))).toBe(true);
7
+ });
8
+
9
+ test("Should not be disabled using Date (false)", () => {
10
+ const dateToDisable = new Date("Aug 3 2022");
11
+ expect(disableDate(dateToDisable, new Date("Okt 11 2019"))).toBe(false);
12
+ });
13
+
14
+ test("Should be disabled using Array (true)", () => {
15
+ const dateToDisable = [
16
+ new Date("Nov 2 2019"),
17
+ new Date("Aug 13 2021"),
18
+ new Date("Jul 17 2018"),
19
+ ];
20
+ expect(disableDate(dateToDisable, new Date("Jul 17 2018"))).toBe(true);
21
+ });
22
+
23
+ test("Should not be disabled using Array (false)", () => {
24
+ const dateToDisable = [
25
+ new Date("Nov 2 2019"),
26
+ new Date("Aug 13 2021"),
27
+ new Date("Jul 17 2018"),
28
+ ];
29
+ expect(disableDate(dateToDisable, new Date("Jul 18 2018"))).toBe(false);
30
+ });
31
+
32
+ test("Should be disabled using Array with range (true)", () => {
33
+ const dateToDisable = [
34
+ { from: new Date("Jul 05 2018"), to: new Date("Sept 09 2020") },
35
+ { from: new Date("Sep 18 2020"), to: new Date("Des 09 2020") },
36
+ new Date("Nov 2 2019"),
37
+ ];
38
+ expect(disableDate(dateToDisable, new Date("Jul 18 2018"))).toBe(true);
39
+ });
40
+
41
+ test("Should not be disabled using Array with range (false)", () => {
42
+ const dateToDisable = [
43
+ { from: new Date("Jul 05 2018"), to: new Date("Sept 09 2020") },
44
+ new Date("Nov 2 2019"),
45
+ ];
46
+ expect(disableDate(dateToDisable, new Date("Jul 4 2018"))).toBe(false);
47
+ });
48
+ });
@@ -0,0 +1,14 @@
1
+ import { parseDate } from "../parse-date";
2
+ import nb from "date-fns/locale/nb";
3
+ import { formatDateForInput } from "../format-date";
4
+
5
+ const parse = (inp: string) => parseDate(inp, new Date(), nb);
6
+
7
+ describe("Format date to correct output", () => {
8
+ test("formatDateForInput", () => {
9
+ expect(formatDateForInput(parse("15/05/22"), nb)).toEqual("15.05.2022");
10
+ expect(formatDateForInput(parse("1/5/22"), nb)).toEqual("01.05.2022");
11
+ expect(formatDateForInput(parse("1/05/22"), nb)).toEqual("01.05.2022");
12
+ expect(formatDateForInput(parse("15/5/22"), nb)).toEqual("15.05.2022");
13
+ });
14
+ });
@@ -0,0 +1,79 @@
1
+ import { getMonths, getYears } from "../get-dates";
2
+
3
+ describe("Extracts correct months", () => {
4
+ test("March - October (8)", () => {
5
+ const t = {
6
+ start: new Date(2019, 2, 22),
7
+ end: new Date(2019, 9, 22),
8
+ current: new Date(2019, 6, 22),
9
+ res: 8,
10
+ };
11
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
12
+ });
13
+
14
+ test("Only first 7 months", () => {
15
+ const t = {
16
+ start: new Date(2019, 0, 22),
17
+ end: new Date(2022, 7, 22),
18
+ current: new Date(2022, 6, 22),
19
+ res: 8,
20
+ };
21
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
22
+ });
23
+
24
+ test("All of 2019 when only 2019 avaliable (12)", () => {
25
+ const t = {
26
+ start: new Date(2019, 0, 22),
27
+ end: new Date(2019, 11, 22),
28
+ current: new Date(2019, 6, 22),
29
+ res: 12,
30
+ };
31
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
32
+ });
33
+ test("All of 2019 when start + current is 2019 and end is later(12)", () => {
34
+ const t = {
35
+ start: new Date(2019, 0, 22),
36
+ end: new Date(2022, 7, 22),
37
+ current: new Date(2019, 6, 22),
38
+ res: 12,
39
+ };
40
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
41
+ });
42
+ test("All of 2020 when between 2019 and 2022 (12)", () => {
43
+ const t = {
44
+ start: new Date(2019, 0, 22),
45
+ end: new Date(2022, 7, 22),
46
+ current: new Date(2020, 6, 22),
47
+ res: 12,
48
+ };
49
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
50
+ });
51
+ test("Tail part of 2019, including starting month (12)", () => {
52
+ const t = {
53
+ start: new Date(2019, 5, 22),
54
+ end: new Date(2022, 7, 22),
55
+ current: new Date(2019, 6, 22),
56
+ res: 7,
57
+ };
58
+ expect(getMonths(t.start, t.end, t.current).length).toEqual(t.res);
59
+ });
60
+ });
61
+
62
+ describe("Extracts correct years", () => {
63
+ test("Same year (1)", () => {
64
+ const t = {
65
+ start: new Date(2019, 2, 22),
66
+ end: new Date(2019, 9, 22),
67
+ res: 1,
68
+ };
69
+ expect(getYears(t.start, t.end).length).toEqual(t.res);
70
+ });
71
+ test("Multiple years (11)", () => {
72
+ const t = {
73
+ start: new Date(2019, 2, 22),
74
+ end: new Date(2029, 9, 22),
75
+ res: 11,
76
+ };
77
+ expect(getYears(t.start, t.end).length).toEqual(t.res);
78
+ });
79
+ });
@@ -0,0 +1,81 @@
1
+ import { parseDate } from "../parse-date";
2
+ import { isValidDate } from "../valid-date";
3
+ import nb from "date-fns/locale/nb";
4
+ import { getMonth, getYear } from "date-fns";
5
+
6
+ const check = (inp: string) =>
7
+ expect(isValidDate(parseDate(inp, new Date(), nb)));
8
+
9
+ const parse = (inp: string) => parseDate(inp, new Date(), nb);
10
+
11
+ describe("Parse date-inputs", () => {
12
+ test("No spaces", () => {
13
+ check("150522").toBeTruthy();
14
+ check("11052022").toBeTruthy();
15
+ check("15052022").toBeTruthy();
16
+ });
17
+
18
+ test(". divider", () => {
19
+ check("1.5.22").toBeTruthy();
20
+ check("1.5.2022").toBeTruthy();
21
+ check("11.05.22").toBeTruthy();
22
+ check("11.05.2022").toBeTruthy();
23
+ });
24
+
25
+ test("/ divider", () => {
26
+ check("1/5/22").toBeTruthy();
27
+ check("10/5/22").toBeTruthy();
28
+ check("1/5/2022").toBeTruthy();
29
+ check("10/5/2022").toBeTruthy();
30
+ check("1/05/22").toBeTruthy();
31
+ check("10/05/22").toBeTruthy();
32
+ check("1/05/2022").toBeTruthy();
33
+ check("10/05/2022").toBeTruthy();
34
+ });
35
+
36
+ test("- divider", () => {
37
+ check("1-5-22").toBeTruthy();
38
+ check("10-5-22").toBeTruthy();
39
+ check("1-5-2022").toBeTruthy();
40
+ check("10-5-2022").toBeTruthy();
41
+ check("1-05-22").toBeTruthy();
42
+ check("10-05-22").toBeTruthy();
43
+ check("1-05-2022").toBeTruthy();
44
+ check("10-05-2022").toBeTruthy();
45
+ });
46
+ });
47
+
48
+ describe("Parse date-inputs to correct year", () => {
49
+ test("No spaces", () => {
50
+ expect(getYear(parse("150522"))).toEqual(2022);
51
+ });
52
+
53
+ test(". divider", () => {
54
+ expect(getYear(parse("1.05.22"))).toEqual(2022);
55
+ expect(getYear(parse("11.05.22"))).toEqual(2022);
56
+ expect(getYear(parse("1.5.22"))).toEqual(2022);
57
+ expect(getYear(parse("11.5.22"))).toEqual(2022);
58
+ });
59
+
60
+ test("/ divider", () => {
61
+ expect(getYear(parse("1/5/22"))).toEqual(2022);
62
+ expect(getYear(parse("10/5/22"))).toEqual(2022);
63
+ expect(getYear(parse("1/05/22"))).toEqual(2022);
64
+ expect(getYear(parse("10/05/22"))).toEqual(2022);
65
+ });
66
+
67
+ test("- divider", () => {
68
+ expect(getYear(parse("1-5-22"))).toEqual(2022);
69
+ expect(getYear(parse("10-5-22"))).toEqual(2022);
70
+ expect(getYear(parse("1-05-22"))).toEqual(2022);
71
+ expect(getYear(parse("10-05-22"))).toEqual(2022);
72
+ });
73
+ });
74
+
75
+ describe("Parse date-inputs to correct month", () => {
76
+ test("No spaces", () => {
77
+ expect(getMonth(parse("150522"))).toEqual(4);
78
+ expect(getMonth(parse("11052022"))).toEqual(4);
79
+ expect(getMonth(parse("15052022"))).toEqual(4);
80
+ });
81
+ });