@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.
- package/_docs.json +1119 -319
- package/cjs/datepicker/DatePicker.js +122 -0
- package/cjs/datepicker/DatePickerInput.js +68 -0
- package/cjs/datepicker/DatePickerStandalone.js +80 -0
- package/cjs/datepicker/caption/Caption.js +23 -0
- package/cjs/datepicker/caption/DropdownCaption.js +36 -0
- package/cjs/datepicker/caption/index.js +10 -0
- package/cjs/datepicker/caption/package.json +6 -0
- package/cjs/datepicker/hooks/index.js +7 -0
- package/cjs/datepicker/hooks/package.json +6 -0
- package/cjs/datepicker/hooks/useDatepicker.js +101 -0
- package/cjs/datepicker/hooks/useRangeDatepicker.js +174 -0
- package/cjs/datepicker/index.js +11 -0
- package/cjs/datepicker/package.json +6 -0
- package/cjs/datepicker/utils/dates-disabled.js +29 -0
- package/cjs/datepicker/utils/format-date.js +7 -0
- package/cjs/datepicker/utils/get-dates.js +43 -0
- package/cjs/datepicker/utils/index.js +19 -0
- package/cjs/datepicker/utils/labels.js +59 -0
- package/cjs/datepicker/utils/locale.js +21 -0
- package/cjs/datepicker/utils/package.json +6 -0
- package/cjs/datepicker/utils/parse-date.js +29 -0
- package/cjs/datepicker/utils/valid-date.js +8 -0
- package/cjs/index.js +1 -0
- package/cjs/monthpicker/MonthPicker.js +112 -0
- package/cjs/monthpicker/index.js +8 -0
- package/cjs/monthpicker/package.json +6 -0
- package/cjs/monthpicker/utils/check-dates.js +12 -0
- package/cjs/monthpicker/utils/handle-selected.js +17 -0
- package/esm/chat/Chat.d.ts +1 -1
- package/esm/datepicker/DatePicker.d.ts +107 -0
- package/esm/datepicker/DatePicker.js +94 -0
- package/esm/datepicker/DatePicker.js.map +1 -0
- package/esm/datepicker/DatePickerInput.d.ts +25 -0
- package/esm/datepicker/DatePickerInput.js +40 -0
- package/esm/datepicker/DatePickerInput.js.map +1 -0
- package/esm/datepicker/DatePickerStandalone.d.ts +12 -0
- package/esm/datepicker/DatePickerStandalone.js +52 -0
- package/esm/datepicker/DatePickerStandalone.js.map +1 -0
- package/esm/datepicker/caption/Caption.d.ts +4 -0
- package/esm/datepicker/caption/Caption.js +17 -0
- package/esm/datepicker/caption/Caption.js.map +1 -0
- package/esm/datepicker/caption/DropdownCaption.d.ts +4 -0
- package/esm/datepicker/caption/DropdownCaption.js +30 -0
- package/esm/datepicker/caption/DropdownCaption.js.map +1 -0
- package/esm/datepicker/caption/index.d.ts +2 -0
- package/esm/datepicker/caption/index.js +3 -0
- package/esm/datepicker/caption/index.js.map +1 -0
- package/esm/datepicker/hooks/index.d.ts +2 -0
- package/esm/datepicker/hooks/index.js +3 -0
- package/esm/datepicker/hooks/index.js.map +1 -0
- package/esm/datepicker/hooks/useDatepicker.d.ts +37 -0
- package/esm/datepicker/hooks/useDatepicker.js +98 -0
- package/esm/datepicker/hooks/useDatepicker.js.map +1 -0
- package/esm/datepicker/hooks/useRangeDatepicker.d.ts +36 -0
- package/esm/datepicker/hooks/useRangeDatepicker.js +171 -0
- package/esm/datepicker/hooks/useRangeDatepicker.js.map +1 -0
- package/esm/datepicker/index.d.ts +5 -0
- package/esm/datepicker/index.js +3 -0
- package/esm/datepicker/index.js.map +1 -0
- package/esm/datepicker/utils/dates-disabled.d.ts +1 -0
- package/esm/datepicker/utils/dates-disabled.js +26 -0
- package/esm/datepicker/utils/dates-disabled.js.map +1 -0
- package/esm/datepicker/utils/format-date.d.ts +1 -0
- package/esm/datepicker/utils/format-date.js +4 -0
- package/esm/datepicker/utils/format-date.js.map +1 -0
- package/esm/datepicker/utils/get-dates.d.ts +2 -0
- package/esm/datepicker/utils/get-dates.js +39 -0
- package/esm/datepicker/utils/get-dates.js.map +1 -0
- package/esm/datepicker/utils/index.d.ts +6 -0
- package/esm/datepicker/utils/index.js +7 -0
- package/esm/datepicker/utils/index.js.map +1 -0
- package/esm/datepicker/utils/labels.d.ts +4 -0
- package/esm/datepicker/utils/labels.js +55 -0
- package/esm/datepicker/utils/labels.js.map +1 -0
- package/esm/datepicker/utils/locale.d.ts +2 -0
- package/esm/datepicker/utils/locale.js +15 -0
- package/esm/datepicker/utils/locale.js.map +1 -0
- package/esm/datepicker/utils/parse-date.d.ts +2 -0
- package/esm/datepicker/utils/parse-date.js +26 -0
- package/esm/datepicker/utils/parse-date.js.map +1 -0
- package/esm/datepicker/utils/valid-date.d.ts +2 -0
- package/esm/datepicker/utils/valid-date.js +5 -0
- package/esm/datepicker/utils/valid-date.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/monthpicker/MonthPicker.d.ts +27 -0
- package/esm/monthpicker/MonthPicker.js +84 -0
- package/esm/monthpicker/MonthPicker.js.map +1 -0
- package/esm/monthpicker/index.d.ts +2 -0
- package/esm/monthpicker/index.js +2 -0
- package/esm/monthpicker/index.js.map +1 -0
- package/esm/monthpicker/utils/check-dates.d.ts +2 -0
- package/esm/monthpicker/utils/check-dates.js +8 -0
- package/esm/monthpicker/utils/check-dates.js.map +1 -0
- package/esm/monthpicker/utils/handle-selected.d.ts +3 -0
- package/esm/monthpicker/utils/handle-selected.js +12 -0
- package/esm/monthpicker/utils/handle-selected.js.map +1 -0
- package/package.json +7 -3
- package/src/chat/Chat.tsx +1 -1
- package/src/chat/chat.stories.tsx +1 -5
- package/src/datepicker/DatePicker.tsx +281 -0
- package/src/datepicker/DatePickerInput.tsx +131 -0
- package/src/datepicker/DatePickerStandalone.tsx +121 -0
- package/src/datepicker/caption/Caption.tsx +51 -0
- package/src/datepicker/caption/DropdownCaption.tsx +94 -0
- package/src/datepicker/caption/index.ts +2 -0
- package/src/datepicker/datepicker.stories.mdx +467 -0
- package/src/datepicker/datepicker.stories.tsx +257 -0
- package/src/datepicker/hooks/index.ts +2 -0
- package/src/datepicker/hooks/useDatepicker.tsx +181 -0
- package/src/datepicker/hooks/useRangeDatepicker.tsx +285 -0
- package/src/datepicker/index.ts +5 -0
- package/src/datepicker/utils/__tests__/dates-disabled.test.ts +48 -0
- package/src/datepicker/utils/__tests__/format-dates.test.ts +14 -0
- package/src/datepicker/utils/__tests__/get-dates.test.ts +79 -0
- package/src/datepicker/utils/__tests__/parse-dates.test.ts +81 -0
- package/src/datepicker/utils/dates-disabled.ts +26 -0
- package/src/datepicker/utils/format-date.ts +5 -0
- package/src/datepicker/utils/get-dates.ts +44 -0
- package/src/datepicker/utils/index.ts +6 -0
- package/src/datepicker/utils/labels.ts +58 -0
- package/src/datepicker/utils/locale.ts +15 -0
- package/src/datepicker/utils/parse-date.ts +28 -0
- package/src/datepicker/utils/valid-date.ts +4 -0
- package/src/index.ts +1 -0
- package/src/monthpicker/MonthPicker.tsx +238 -0
- package/src/monthpicker/index.ts +2 -0
- package/src/monthpicker/monthpicker.stories.tsx +35 -0
- package/src/monthpicker/utils/__tests__/check-dates.test.ts +34 -0
- package/src/monthpicker/utils/__tests__/handle-selected.test.ts +87 -0
- package/src/monthpicker/utils/check-dates.ts +15 -0
- package/src/monthpicker/utils/handle-selected.ts +26 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isSameDay } from "date-fns";
|
|
2
|
+
import { isDateRange } from "react-day-picker";
|
|
3
|
+
|
|
4
|
+
// TODO: ((date: Date) => boolean)
|
|
5
|
+
export const disableDate = (
|
|
6
|
+
disabledSelection: Date | Array<any>,
|
|
7
|
+
date: Date
|
|
8
|
+
): boolean => {
|
|
9
|
+
let result: boolean = false;
|
|
10
|
+
if (disabledSelection instanceof Date) {
|
|
11
|
+
return isSameDay(disabledSelection, date);
|
|
12
|
+
} else if (disabledSelection instanceof Array) {
|
|
13
|
+
for (let i = 0; i < disabledSelection.length; i++) {
|
|
14
|
+
const selection = disabledSelection[i];
|
|
15
|
+
if (isDateRange(selection)) {
|
|
16
|
+
if (selection.from && selection.to) {
|
|
17
|
+
result = date >= selection.from && date <= selection.to;
|
|
18
|
+
}
|
|
19
|
+
} else if (selection instanceof Date) {
|
|
20
|
+
result = isSameDay(selection, date);
|
|
21
|
+
}
|
|
22
|
+
if (result) break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isSameYear,
|
|
3
|
+
setMonth,
|
|
4
|
+
setYear,
|
|
5
|
+
startOfMonth,
|
|
6
|
+
startOfYear,
|
|
7
|
+
} from "date-fns";
|
|
8
|
+
|
|
9
|
+
export const getMonths = (start: Date, end: Date, current: Date): Date[] => {
|
|
10
|
+
const dropdownMonths: Date[] = [];
|
|
11
|
+
|
|
12
|
+
if (isSameYear(start, end)) {
|
|
13
|
+
const date = startOfMonth(start);
|
|
14
|
+
for (let month = start.getMonth(); month <= end.getMonth(); month++) {
|
|
15
|
+
dropdownMonths.push(setMonth(date, month));
|
|
16
|
+
}
|
|
17
|
+
} else if (isSameYear(current, end)) {
|
|
18
|
+
const date = startOfMonth(new Date());
|
|
19
|
+
for (let month = 0; month <= end.getMonth(); month++) {
|
|
20
|
+
dropdownMonths.push(setMonth(date, month));
|
|
21
|
+
}
|
|
22
|
+
} else if (isSameYear(current, start)) {
|
|
23
|
+
const date = startOfMonth(start);
|
|
24
|
+
for (let month = date.getMonth(); month <= 11; month++) {
|
|
25
|
+
dropdownMonths.push(setMonth(date, month));
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
const date = startOfMonth(new Date());
|
|
29
|
+
for (let month = 0; month <= 11; month++) {
|
|
30
|
+
dropdownMonths.push(setMonth(date, month));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return dropdownMonths;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getYears = (start: Date, end: Date): Date[] => {
|
|
37
|
+
const years: Date[] = [];
|
|
38
|
+
const fromYear = start.getFullYear();
|
|
39
|
+
const toYear = end.getFullYear();
|
|
40
|
+
for (let year = fromYear; year <= toYear; year++) {
|
|
41
|
+
years.push(setYear(startOfYear(new Date()), year));
|
|
42
|
+
}
|
|
43
|
+
return years;
|
|
44
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { formatDateForInput } from "./format-date";
|
|
2
|
+
export { getMonths, getYears } from "./get-dates";
|
|
3
|
+
export { labelMonthDropdown, labelYearDropdown, labels } from "./labels";
|
|
4
|
+
export { INPUT_DATE_STRING_FORMAT, parseDate } from "./parse-date";
|
|
5
|
+
export { getLocaleFromString } from "./locale";
|
|
6
|
+
export { isValidDate } from "./valid-date";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Labels, NavButtonLabel } from "react-day-picker";
|
|
2
|
+
|
|
3
|
+
const labelNext: NavButtonLabel = (date, options) => {
|
|
4
|
+
switch (options?.locale?.code) {
|
|
5
|
+
case "nb":
|
|
6
|
+
return "Gå til neste månede";
|
|
7
|
+
case "nn":
|
|
8
|
+
return "Gå til neste månede";
|
|
9
|
+
case "en":
|
|
10
|
+
return "Go to next month";
|
|
11
|
+
default:
|
|
12
|
+
return "Gå til neste månede";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const labelPrevious: NavButtonLabel = (date, options) => {
|
|
17
|
+
switch (options?.locale?.code) {
|
|
18
|
+
case "nb":
|
|
19
|
+
return "Gå til forrige månede";
|
|
20
|
+
case "nn":
|
|
21
|
+
return "Gå til forrige månede";
|
|
22
|
+
case "en":
|
|
23
|
+
return "Go to previous month";
|
|
24
|
+
default:
|
|
25
|
+
return "Gå til forrige månede";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const labelYearDropdown = (locale: Locale) => {
|
|
30
|
+
switch (locale?.code) {
|
|
31
|
+
case "nb":
|
|
32
|
+
return "År";
|
|
33
|
+
case "nn":
|
|
34
|
+
return "År";
|
|
35
|
+
case "en":
|
|
36
|
+
return "Year";
|
|
37
|
+
default:
|
|
38
|
+
return "År";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const labelMonthDropdown = (locale: Locale) => {
|
|
43
|
+
switch (locale?.code) {
|
|
44
|
+
case "nb":
|
|
45
|
+
return "Månede";
|
|
46
|
+
case "nn":
|
|
47
|
+
return "Månede";
|
|
48
|
+
case "en":
|
|
49
|
+
return "Month";
|
|
50
|
+
default:
|
|
51
|
+
return "Månede";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const labels: Partial<Labels> = {
|
|
56
|
+
labelNext,
|
|
57
|
+
labelPrevious,
|
|
58
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import enGB from "date-fns/locale/en-GB";
|
|
2
|
+
import nb from "date-fns/locale/nb";
|
|
3
|
+
import nn from "date-fns/locale/nn";
|
|
4
|
+
|
|
5
|
+
/** @private */
|
|
6
|
+
export const getLocaleFromString = (locale: "nb" | "nn" | "en" = "nb") => {
|
|
7
|
+
switch (locale) {
|
|
8
|
+
case "nn":
|
|
9
|
+
return nn;
|
|
10
|
+
case "en":
|
|
11
|
+
return enGB;
|
|
12
|
+
default:
|
|
13
|
+
return nb;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parse } from "date-fns";
|
|
2
|
+
import { isValidDate } from "./valid-date";
|
|
3
|
+
|
|
4
|
+
export const INPUT_DATE_STRING_FORMAT = "dd.MM.yyyy";
|
|
5
|
+
|
|
6
|
+
const ALLOWED_INPUT_FORMATS = [
|
|
7
|
+
"ddMMyy",
|
|
8
|
+
"d.M.yy",
|
|
9
|
+
"dd.MM.yy",
|
|
10
|
+
"dd/MM/yy",
|
|
11
|
+
"dd-MM-yy",
|
|
12
|
+
INPUT_DATE_STRING_FORMAT,
|
|
13
|
+
"ddMMyyyy",
|
|
14
|
+
"dd/MM/yyyy",
|
|
15
|
+
"dd-MM-yyyy",
|
|
16
|
+
"d.M.yyyy",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export const parseDate = (date: string, today: Date, locale: Locale) => {
|
|
20
|
+
let parsed;
|
|
21
|
+
for (const format of ALLOWED_INPUT_FORMATS) {
|
|
22
|
+
parsed = parse(date, format, today, { locale });
|
|
23
|
+
if (isValidDate(parsed)) {
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Left, Right } from "@navikt/ds-icons";
|
|
2
|
+
import cl from "clsx";
|
|
3
|
+
import {
|
|
4
|
+
compareAsc,
|
|
5
|
+
format,
|
|
6
|
+
isSameYear,
|
|
7
|
+
setMonth,
|
|
8
|
+
setYear,
|
|
9
|
+
startOfMonth,
|
|
10
|
+
startOfYear,
|
|
11
|
+
} from "date-fns";
|
|
12
|
+
import NB from "date-fns/locale/nb";
|
|
13
|
+
import React, { forwardRef, useState } from "react";
|
|
14
|
+
import { RootProvider, useDayPicker, useNavigation } from "react-day-picker";
|
|
15
|
+
import { BodyShort, Select } from "..";
|
|
16
|
+
import { dateIsInCurrentMonth, dateIsSelected } from "./utils/check-dates";
|
|
17
|
+
|
|
18
|
+
export interface MonthPickerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
mode?: "month";
|
|
21
|
+
/**
|
|
22
|
+
* The earliest day to start the month navigation.
|
|
23
|
+
*/
|
|
24
|
+
fromDate?: Date;
|
|
25
|
+
/**
|
|
26
|
+
* The latest day to end the month navigation.
|
|
27
|
+
*/
|
|
28
|
+
toDate?: Date;
|
|
29
|
+
/**
|
|
30
|
+
* Changes monthpicker locale
|
|
31
|
+
* @default "nb" (norsk bokmål)
|
|
32
|
+
*/
|
|
33
|
+
locale?: "nb" | "nn" | "en";
|
|
34
|
+
/**
|
|
35
|
+
* Adds a `Select` for picking Year
|
|
36
|
+
* Needs `fromDate` + `toDate` to be set!
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
yearSelector?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const TestCaption = ({
|
|
43
|
+
selected,
|
|
44
|
+
onSelect,
|
|
45
|
+
isValidYearSelector,
|
|
46
|
+
yearSelector,
|
|
47
|
+
}: {
|
|
48
|
+
selected: Date;
|
|
49
|
+
onSelect: (m: Date) => void;
|
|
50
|
+
isValidYearSelector: boolean;
|
|
51
|
+
yearSelector: boolean;
|
|
52
|
+
}) => {
|
|
53
|
+
const { nextMonth, previousMonth } = useNavigation();
|
|
54
|
+
const {
|
|
55
|
+
fromDate,
|
|
56
|
+
toDate,
|
|
57
|
+
formatters: { formatYearCaption },
|
|
58
|
+
locale,
|
|
59
|
+
} = useDayPicker();
|
|
60
|
+
|
|
61
|
+
const [yearState, setYearState] = useState<Date>(selected);
|
|
62
|
+
const years: Date[] = [];
|
|
63
|
+
|
|
64
|
+
if (yearSelector && fromDate && toDate) {
|
|
65
|
+
const fromYear = fromDate.getFullYear();
|
|
66
|
+
const toYear = toDate.getFullYear();
|
|
67
|
+
for (let year = fromYear; year <= toYear; year++) {
|
|
68
|
+
years.push(setYear(startOfYear(new Date()), year));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handleYearChange = (e) => {
|
|
73
|
+
const newMonth = setYear(startOfMonth(selected), Number(e.target.value));
|
|
74
|
+
setYearState(newMonth);
|
|
75
|
+
onSelect(newMonth);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleButtonClick = (val) => {
|
|
79
|
+
const newMonth = setYear(
|
|
80
|
+
startOfMonth(selected),
|
|
81
|
+
yearState.getFullYear() + val
|
|
82
|
+
);
|
|
83
|
+
setYearState(newMonth);
|
|
84
|
+
onSelect(newMonth);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="navds-monthpicker__caption">
|
|
89
|
+
<button
|
|
90
|
+
className="navds-monthpicker__caption-button"
|
|
91
|
+
disabled={!previousMonth}
|
|
92
|
+
onClick={() =>
|
|
93
|
+
years.some((x) => yearState.getFullYear() - 1 === x.getFullYear()) &&
|
|
94
|
+
handleButtonClick(-1)
|
|
95
|
+
}
|
|
96
|
+
>
|
|
97
|
+
<Left aria-hidden />
|
|
98
|
+
</button>
|
|
99
|
+
|
|
100
|
+
{isValidYearSelector ? (
|
|
101
|
+
<Select
|
|
102
|
+
label="velg år"
|
|
103
|
+
hideLabel
|
|
104
|
+
value={selected?.getFullYear()}
|
|
105
|
+
onChange={handleYearChange}
|
|
106
|
+
style={{ width: "79px" }}
|
|
107
|
+
>
|
|
108
|
+
{years.map((year) => (
|
|
109
|
+
<option key={year.getFullYear()} value={year.getFullYear()}>
|
|
110
|
+
{formatYearCaption(year, { locale })}
|
|
111
|
+
</option>
|
|
112
|
+
))}
|
|
113
|
+
</Select>
|
|
114
|
+
) : (
|
|
115
|
+
<span className="navds-monthpicker__year-label" aria-live="polite">
|
|
116
|
+
{yearState.getFullYear()}
|
|
117
|
+
</span>
|
|
118
|
+
)}
|
|
119
|
+
<button
|
|
120
|
+
className="navds-monthpicker__caption-button"
|
|
121
|
+
disabled={!nextMonth}
|
|
122
|
+
onClick={() =>
|
|
123
|
+
years.some((x) => yearState.getFullYear() + 1 === x.getFullYear()) &&
|
|
124
|
+
handleButtonClick(1)
|
|
125
|
+
}
|
|
126
|
+
>
|
|
127
|
+
<Right aria-hidden />
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const MonthSelector = ({
|
|
134
|
+
onSelect,
|
|
135
|
+
selected,
|
|
136
|
+
}: {
|
|
137
|
+
onSelect: (m: Date) => void;
|
|
138
|
+
selected: Date;
|
|
139
|
+
}) => {
|
|
140
|
+
const months: Date[] = [];
|
|
141
|
+
const {
|
|
142
|
+
fromDate,
|
|
143
|
+
toDate,
|
|
144
|
+
formatters: { formatYearCaption, formatMonthCaption },
|
|
145
|
+
locale,
|
|
146
|
+
} = useDayPicker();
|
|
147
|
+
|
|
148
|
+
if (!fromDate) return <></>;
|
|
149
|
+
if (!toDate) return <></>;
|
|
150
|
+
|
|
151
|
+
if (isSameYear(fromDate, toDate)) {
|
|
152
|
+
const date = startOfMonth(fromDate);
|
|
153
|
+
for (let month = fromDate.getMonth(); month <= toDate.getMonth(); month++) {
|
|
154
|
+
months.push(setMonth(date, month));
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
const date = startOfMonth(new Date());
|
|
158
|
+
for (let month = 0; month <= 11; month++) {
|
|
159
|
+
months.push(setMonth(date, month));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const hideMonth = (month: Date) => {
|
|
164
|
+
return compareAsc(month, fromDate) === -1;
|
|
165
|
+
};
|
|
166
|
+
return (
|
|
167
|
+
<BodyShort as="div" className="navds-monthpicker__months">
|
|
168
|
+
{months.map((x: Date, y) => {
|
|
169
|
+
return (
|
|
170
|
+
<button
|
|
171
|
+
key={x.toDateString()}
|
|
172
|
+
onClick={() =>
|
|
173
|
+
onSelect(setYear(startOfMonth(x), Number(selected.getFullYear())))
|
|
174
|
+
}
|
|
175
|
+
className={cl("navds-monthpicker__month", {
|
|
176
|
+
"navds-monthpicker__month--hidden": hideMonth(x),
|
|
177
|
+
"navds-monthpicker__month--current": dateIsInCurrentMonth(
|
|
178
|
+
x,
|
|
179
|
+
selected
|
|
180
|
+
),
|
|
181
|
+
"navds-monthpicker__month--selected": dateIsSelected(x, selected),
|
|
182
|
+
})}
|
|
183
|
+
>
|
|
184
|
+
<span aria-hidden="true">
|
|
185
|
+
{format(new Date(x), "LLL", { locale })
|
|
186
|
+
.replace(".", "")
|
|
187
|
+
.substring(0, 3)}
|
|
188
|
+
</span>
|
|
189
|
+
<span className="navds-sr-only">
|
|
190
|
+
{format(new Date(x), "LLLL", { locale })}
|
|
191
|
+
</span>
|
|
192
|
+
</button>
|
|
193
|
+
);
|
|
194
|
+
})}
|
|
195
|
+
</BodyShort>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
|
200
|
+
(
|
|
201
|
+
{
|
|
202
|
+
children,
|
|
203
|
+
yearSelector = false,
|
|
204
|
+
fromDate = new Date(),
|
|
205
|
+
toDate = new Date("Sep 27 2032"),
|
|
206
|
+
},
|
|
207
|
+
ref
|
|
208
|
+
) => {
|
|
209
|
+
const [selected, setSelected] = React.useState<Date>(new Date());
|
|
210
|
+
|
|
211
|
+
const isValidYearSelector =
|
|
212
|
+
yearSelector && fromDate && toDate ? true : false;
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<RootProvider
|
|
216
|
+
locale={NB}
|
|
217
|
+
selected={selected}
|
|
218
|
+
className="navds-monthpicker-month"
|
|
219
|
+
toDate={toDate}
|
|
220
|
+
fromDate={fromDate}
|
|
221
|
+
>
|
|
222
|
+
<div className="navds-monthpicker__wrapper">
|
|
223
|
+
<TestCaption
|
|
224
|
+
selected={selected}
|
|
225
|
+
onSelect={setSelected}
|
|
226
|
+
yearSelector={yearSelector}
|
|
227
|
+
isValidYearSelector={isValidYearSelector}
|
|
228
|
+
/>
|
|
229
|
+
<MonthSelector onSelect={setSelected} selected={selected} />
|
|
230
|
+
</div>
|
|
231
|
+
</RootProvider>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
export default MonthPicker;
|
|
237
|
+
|
|
238
|
+
export const F = "";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import MonthPicker from "./MonthPicker";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ds-react/Monthpicker",
|
|
6
|
+
component: MonthPicker,
|
|
7
|
+
argTypes: {
|
|
8
|
+
size: {
|
|
9
|
+
control: {
|
|
10
|
+
type: "radio",
|
|
11
|
+
options: ["medium", "small"],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Default = (props) => {
|
|
18
|
+
return (
|
|
19
|
+
<div style={{ height: "20rem" }}>
|
|
20
|
+
<MonthPicker />
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const YearSelector = (props) => {
|
|
26
|
+
return (
|
|
27
|
+
<div style={{ height: "20rem" }}>
|
|
28
|
+
<MonthPicker
|
|
29
|
+
yearSelector
|
|
30
|
+
fromDate={new Date()}
|
|
31
|
+
toDate={new Date("Sep 27 2032")}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { dateIsInCurrentMonth, dateIsSelected } from "../check-dates";
|
|
2
|
+
|
|
3
|
+
const selectedDate = new Date("Feb 1 1994");
|
|
4
|
+
|
|
5
|
+
describe("Returns if date is in current month", () => {
|
|
6
|
+
test("Date should be within current month (true)", () => {
|
|
7
|
+
expect(dateIsInCurrentMonth(new Date(), new Date())).toBeTruthy();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("Date should not be within current month (false)", () => {
|
|
11
|
+
expect(
|
|
12
|
+
dateIsInCurrentMonth(new Date("Sep 2 2021"), selectedDate)
|
|
13
|
+
).toBeFalsy();
|
|
14
|
+
expect(
|
|
15
|
+
dateIsInCurrentMonth(new Date("Sep 1 2021"), selectedDate)
|
|
16
|
+
).toBeFalsy();
|
|
17
|
+
expect(
|
|
18
|
+
dateIsInCurrentMonth(new Date("Sep 30 2021"), selectedDate)
|
|
19
|
+
).toBeFalsy();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("Returns if date is selected", () => {
|
|
24
|
+
test("Date should be selected (true)", () => {
|
|
25
|
+
expect(dateIsSelected(new Date("Feb 1 1994"), selectedDate)).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
test("Date should not be selected (false)", () => {
|
|
28
|
+
expect(dateIsSelected(new Date("Mar 1 1994"), selectedDate)).toBeFalsy();
|
|
29
|
+
expect(
|
|
30
|
+
dateIsInCurrentMonth(new Date("Des 15 2018"), selectedDate)
|
|
31
|
+
).toBeFalsy();
|
|
32
|
+
expect(dateIsSelected(new Date("Jul 9 2033"), selectedDate)).toBeFalsy();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
updateWithoutYearSelector,
|
|
3
|
+
updateWithYearSelector,
|
|
4
|
+
hasNextYear,
|
|
5
|
+
} from "../handle-selected";
|
|
6
|
+
|
|
7
|
+
const years = [
|
|
8
|
+
new Date(),
|
|
9
|
+
new Date("Aug 5 2023"),
|
|
10
|
+
new Date("Aug 5 2024"),
|
|
11
|
+
new Date("Aug 5 2025"),
|
|
12
|
+
new Date("Aug 5 2026"),
|
|
13
|
+
new Date("Aug 5 2027"),
|
|
14
|
+
new Date("Aug 5 2028"),
|
|
15
|
+
new Date("Aug 5 2029"),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
describe("Returns date with updated year without selector", () => {
|
|
19
|
+
test("Date should be incremented with 1 year (Date)", () => {
|
|
20
|
+
expect(updateWithoutYearSelector(new Date(), 1).getFullYear()).toEqual(
|
|
21
|
+
2023
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
test("Date should be decremented with 1 year (Date)", () => {
|
|
25
|
+
expect(updateWithoutYearSelector(new Date(), -1).getFullYear()).toEqual(
|
|
26
|
+
2021
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("Returns updated sekected date with selector", () => {
|
|
32
|
+
test("Date should be incremented with 1 years (2024)", () => {
|
|
33
|
+
expect(
|
|
34
|
+
updateWithYearSelector(
|
|
35
|
+
new Date("Aug 5 2023"),
|
|
36
|
+
new Date("Aug 5 2023"),
|
|
37
|
+
years,
|
|
38
|
+
1
|
|
39
|
+
).getFullYear()
|
|
40
|
+
).toEqual(2024);
|
|
41
|
+
});
|
|
42
|
+
test("Date should be incremented with 5 years (2028)", () => {
|
|
43
|
+
expect(
|
|
44
|
+
updateWithYearSelector(
|
|
45
|
+
new Date("Aug 5 2023"),
|
|
46
|
+
new Date("Aug 5 2023"),
|
|
47
|
+
years,
|
|
48
|
+
5
|
|
49
|
+
).getFullYear()
|
|
50
|
+
).toEqual(2028);
|
|
51
|
+
});
|
|
52
|
+
test("Date should be decremented with 5 years (Date)", () => {
|
|
53
|
+
expect(
|
|
54
|
+
updateWithYearSelector(
|
|
55
|
+
new Date("Aug 5 2028"),
|
|
56
|
+
new Date("Aug 5 2023"),
|
|
57
|
+
years,
|
|
58
|
+
-5
|
|
59
|
+
).getFullYear()
|
|
60
|
+
).toEqual(2023);
|
|
61
|
+
});
|
|
62
|
+
test("Date should be incremented with 1 years (Date)", () => {
|
|
63
|
+
expect(
|
|
64
|
+
updateWithYearSelector(
|
|
65
|
+
new Date("Aug 5 2023"),
|
|
66
|
+
new Date("Aug 5 2023"),
|
|
67
|
+
years,
|
|
68
|
+
-1
|
|
69
|
+
).getFullYear()
|
|
70
|
+
).toEqual(2022);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("Returns if year is at start or end of range", () => {
|
|
75
|
+
test("Should have next year (true)", () => {
|
|
76
|
+
expect(hasNextYear(new Date(), years, 1)).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
test("Should have previous year (true)", () => {
|
|
79
|
+
expect(hasNextYear(new Date("Aug 3 2023"), years, -1)).toBeTruthy();
|
|
80
|
+
});
|
|
81
|
+
test("Should not have next year (false)", () => {
|
|
82
|
+
expect(hasNextYear(new Date("Aug 3 2029"), years, 1)).toBeFalsy();
|
|
83
|
+
});
|
|
84
|
+
test("Should not have previous year (false)", () => {
|
|
85
|
+
expect(hasNextYear(new Date(), years, -1)).toBeFalsy();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isThisMonth, setYear, isSameMonth } from "date-fns";
|
|
2
|
+
|
|
3
|
+
export const dateIsInCurrentMonth = (
|
|
4
|
+
date: Date,
|
|
5
|
+
dateToCompare: Date
|
|
6
|
+
): boolean => {
|
|
7
|
+
return isThisMonth(setYear(date, Number(dateToCompare.getFullYear())));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const dateIsSelected = (date: Date, selectedDate: Date): boolean => {
|
|
11
|
+
return isSameMonth(
|
|
12
|
+
setYear(date, Number(selectedDate.getFullYear())),
|
|
13
|
+
selectedDate
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { startOfMonth, setYear } from "date-fns";
|
|
2
|
+
|
|
3
|
+
export const updateWithoutYearSelector = (
|
|
4
|
+
yearState: Date,
|
|
5
|
+
val: number
|
|
6
|
+
): Date => {
|
|
7
|
+
const newYear = Number(yearState.getFullYear() + val);
|
|
8
|
+
return setYear(yearState, newYear);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const updateWithYearSelector = (
|
|
12
|
+
yearState: Date,
|
|
13
|
+
selected: Date,
|
|
14
|
+
years: Date[],
|
|
15
|
+
val: number
|
|
16
|
+
): Date => {
|
|
17
|
+
return setYear(startOfMonth(selected), yearState.getFullYear() + val);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const hasNextYear = (
|
|
21
|
+
yearState: Date,
|
|
22
|
+
years: Date[],
|
|
23
|
+
val: any
|
|
24
|
+
): boolean => {
|
|
25
|
+
return years.some((x) => yearState.getFullYear() + val === x.getFullYear());
|
|
26
|
+
};
|