@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,281 @@
1
+ import { FloatingPortal } from "@floating-ui/react-dom-interactions";
2
+ import cl from "clsx";
3
+ import { isWeekend } from "date-fns";
4
+ import React, { createContext, forwardRef, useRef, useState } from "react";
5
+ import {
6
+ DateRange,
7
+ DayPicker,
8
+ DayPickerBase,
9
+ isMatch,
10
+ Matcher,
11
+ SelectMultipleEventHandler,
12
+ SelectRangeEventHandler,
13
+ SelectSingleEventHandler,
14
+ } from "react-day-picker";
15
+ import { Popover, useId } from "..";
16
+ import { omit } from "../util";
17
+ import { Caption, DropdownCaption } from "./caption";
18
+ import DatePickerInput, { DatePickerInputType } from "./DatePickerInput";
19
+ import DatePickerStandalone, {
20
+ DatePickerStandaloneType,
21
+ } from "./DatePickerStandalone";
22
+ import { getLocaleFromString, labels } from "./utils";
23
+
24
+ export type ConditionalModeProps =
25
+ | {
26
+ mode?: "single";
27
+ onSelect?: (val?: Date) => void;
28
+ selected?: Date;
29
+ defaultSelected?: Date;
30
+ }
31
+ | {
32
+ mode?: "multiple";
33
+ onSelect?: (val?: Date[]) => void;
34
+ selected?: Date[];
35
+ defaultSelected?: Date[];
36
+ min?: number;
37
+ max?: number;
38
+ }
39
+ | {
40
+ mode?: "range";
41
+ onSelect?: (val?: DateRange) => void;
42
+ selected?: DateRange;
43
+ defaultSelected?: DateRange;
44
+ min?: number;
45
+ max?: number;
46
+ };
47
+
48
+ //github.com/gpbl/react-day-picker/blob/50b6dba/packages/react-day-picker/src/types/DayPickerBase.ts#L139
49
+ export interface DatePickerDefaultProps
50
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">,
51
+ Pick<DayPickerBase, "month" | "onMonthChange" | "today" | "onDayClick"> {
52
+ /**
53
+ * Element datepicker anchors to. Use <DatePicker.Input /> for built-in toggle,
54
+ * or make your own with the open/onClose props
55
+ */
56
+ children?: React.ReactNode;
57
+ /**
58
+ * Changes datepicker locale
59
+ * @default "nb" (norsk bokmål)
60
+ */
61
+ locale?: "nb" | "nn" | "en";
62
+ /**
63
+ * The earliest Date available for the user to pick from
64
+ */
65
+ fromDate?: Date;
66
+ /**
67
+ * The latest Date available for the user to pick from
68
+ */
69
+ toDate?: Date;
70
+ /**
71
+ * Adds a `Select` for picking Year and Month
72
+ * Needs `fromDate` + `toDate` to be set!
73
+ * @default false
74
+ */
75
+ yearSelector?: boolean;
76
+ /**
77
+ * Apply the disabled modifier to the matching days.
78
+ * {@link https://react-day-picker.js.org/api/types/Matcher | Matcher type-definition}
79
+ */
80
+ disabled?: Matcher[];
81
+ /**
82
+ * Disable saturday and sunday.
83
+ * @default false
84
+ */
85
+ disableWeekends?: boolean;
86
+ /**
87
+ * Shows week numbers in left-column
88
+ * Use with caution, takes up valuable screenspace in small screens!
89
+ * @default false
90
+ */
91
+ showWeekNumber?: boolean;
92
+ /**
93
+ * Open state for user-controlled state
94
+ * @remark Controlled by component by default
95
+ */
96
+ open?: boolean;
97
+ /**
98
+ * onClose callback for user-controlled state
99
+ */
100
+ onClose?: () => void;
101
+ /**
102
+ * onOpenToggle callback for user-controlled-state
103
+ * @remark only called if `<DatePicker.Input />` is used
104
+ */
105
+ onOpenToggle?: () => void;
106
+ /**
107
+ * Classnames for adding classes
108
+ */
109
+ classNames?: {
110
+ /**
111
+ * Children wrapper
112
+ */
113
+ wrapper?: string;
114
+ /**
115
+ * DatePicker-wrapper
116
+ */
117
+ datepicker?: string;
118
+ };
119
+ }
120
+
121
+ export type DatePickerProps = DatePickerDefaultProps & ConditionalModeProps;
122
+
123
+ interface DatePickerComponent
124
+ extends React.ForwardRefExoticComponent<DatePickerProps> {
125
+ Input: DatePickerInputType;
126
+ Standalone: DatePickerStandaloneType;
127
+ }
128
+
129
+ interface DatePickerContextProps {
130
+ open: boolean;
131
+ onOpen: () => void;
132
+ buttonRef: React.MutableRefObject<HTMLButtonElement | null> | null;
133
+ ariaId?: string;
134
+ }
135
+
136
+ export const DatePickerContext = createContext<DatePickerContextProps>({
137
+ open: false,
138
+ onOpen: () => null,
139
+ buttonRef: null,
140
+ ariaId: undefined,
141
+ });
142
+
143
+ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
144
+ (
145
+ {
146
+ children,
147
+ locale = "nb",
148
+ yearSelector,
149
+ disabled = [],
150
+ disableWeekends = false,
151
+ showWeekNumber = false,
152
+ mode = "single",
153
+ selected,
154
+ id,
155
+ defaultSelected,
156
+ classNames,
157
+ open: _open,
158
+ onClose,
159
+ onOpenToggle,
160
+ ...rest
161
+ },
162
+ ref
163
+ ) => {
164
+ const ariaId = useId(id);
165
+ const [open, setOpen] = useState(_open ?? false);
166
+
167
+ const wrapperRef = useRef<HTMLDivElement | null>(null);
168
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
169
+
170
+ const [selectedDates, setSelectedDates] = React.useState<
171
+ Date | Date[] | DateRange | undefined
172
+ >(defaultSelected);
173
+
174
+ const handleSingleSelect: SelectSingleEventHandler = (selectedDay) => {
175
+ setSelectedDates(selectedDay);
176
+ selectedDay && (onClose?.() ?? setOpen(false));
177
+ selectedDay && buttonRef && buttonRef?.current?.focus();
178
+ rest?.onSelect && (rest?.onSelect as (val?: Date) => void)(selectedDay);
179
+ };
180
+
181
+ const handleMultipleSelect: SelectMultipleEventHandler = (selectedDays) => {
182
+ setSelectedDates(selectedDays);
183
+ rest?.onSelect &&
184
+ (rest?.onSelect as (val?: Date[]) => void)(selectedDays);
185
+ };
186
+
187
+ const handleRangeSelect: SelectRangeEventHandler = (selectedDays) => {
188
+ setSelectedDates(selectedDays);
189
+ selectedDays?.from && selectedDays?.to && (onClose?.() ?? setOpen(false));
190
+ selectedDays?.from &&
191
+ selectedDays?.to &&
192
+ buttonRef &&
193
+ buttonRef?.current?.focus();
194
+ rest?.onSelect &&
195
+ (rest?.onSelect as (val?: DateRange) => void)(selectedDays);
196
+ };
197
+
198
+ const overrideProps = {
199
+ onSelect:
200
+ mode === "single"
201
+ ? handleSingleSelect
202
+ : mode === "multiple"
203
+ ? handleMultipleSelect
204
+ : handleRangeSelect,
205
+ };
206
+
207
+ return (
208
+ <DatePickerContext.Provider
209
+ value={{
210
+ open: _open ?? open,
211
+ onOpen: () => {
212
+ setOpen((x) => !x);
213
+ onOpenToggle?.();
214
+ },
215
+ buttonRef,
216
+ ariaId,
217
+ }}
218
+ >
219
+ <div
220
+ ref={wrapperRef}
221
+ className={cl("navds-date__wrapper", classNames?.wrapper)}
222
+ >
223
+ {children}
224
+ <FloatingPortal>
225
+ {(_open ?? open) && (
226
+ <Popover
227
+ arrow={false}
228
+ anchorEl={wrapperRef.current}
229
+ open={_open ?? open}
230
+ onClose={() => onClose?.() ?? setOpen(false)}
231
+ placement="bottom-start"
232
+ id={ariaId}
233
+ aria-roledescription={
234
+ locale === "en" ? "datepicker" : "datovelger"
235
+ }
236
+ role="dialog"
237
+ ref={ref}
238
+ >
239
+ <DayPicker
240
+ locale={getLocaleFromString(locale)}
241
+ mode={mode}
242
+ {...overrideProps}
243
+ selected={selected ?? selectedDates}
244
+ components={{
245
+ Caption: yearSelector ? DropdownCaption : Caption,
246
+ }}
247
+ className={cl("navds-date", classNames?.datepicker)}
248
+ classNames={{
249
+ vhidden: "navds-sr-only",
250
+ }}
251
+ disabled={(day) => {
252
+ return (
253
+ (disableWeekends && isWeekend(day)) ||
254
+ isMatch(day, disabled)
255
+ );
256
+ }}
257
+ weekStartsOn={1}
258
+ initialFocus={false}
259
+ labels={labels as any}
260
+ modifiers={{
261
+ weekend: (day) => disableWeekends && isWeekend(day),
262
+ }}
263
+ modifiersClassNames={{
264
+ weekend: "rdp-day__weekend",
265
+ }}
266
+ showWeekNumber={showWeekNumber}
267
+ {...omit(rest, ["onSelect"])}
268
+ />
269
+ </Popover>
270
+ )}
271
+ </FloatingPortal>
272
+ </div>
273
+ </DatePickerContext.Provider>
274
+ );
275
+ }
276
+ ) as DatePickerComponent;
277
+
278
+ DatePicker.Input = DatePickerInput;
279
+ DatePicker.Standalone = DatePickerStandalone;
280
+
281
+ export default DatePicker;
@@ -0,0 +1,131 @@
1
+ import { Calender } from "@navikt/ds-icons";
2
+ import cl from "clsx";
3
+ import React, { forwardRef, InputHTMLAttributes, useContext } from "react";
4
+ import { Button } from "../button";
5
+ import { FormFieldProps, useFormField } from "../form/useFormField";
6
+ import { BodyShort, ErrorMessage, Label } from "../typography";
7
+ import { omit } from "../util";
8
+ import { DatePickerContext } from "./DatePicker";
9
+
10
+ export interface DatePickerInputProps
11
+ extends FormFieldProps,
12
+ Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
13
+ /**
14
+ * Input label
15
+ */
16
+ label: React.ReactNode;
17
+ /**
18
+ * Shows label and description for screenreaders-only
19
+ * @default false
20
+ */
21
+ hideLabel?: boolean;
22
+ /**
23
+ * Changes padding and font-sizes
24
+ * @default medium
25
+ */
26
+ size?: "medium" | "small";
27
+ /**
28
+ * @private
29
+ */
30
+ wrapperRef?: React.RefObject<HTMLDivElement>;
31
+ }
32
+
33
+ export type DatePickerInputType = React.ForwardRefExoticComponent<
34
+ DatePickerInputProps & React.RefAttributes<HTMLInputElement>
35
+ >;
36
+
37
+ export const DatePickerInput: DatePickerInputType = forwardRef<
38
+ HTMLInputElement,
39
+ DatePickerInputProps
40
+ >((props, ref) => {
41
+ const { onOpen, buttonRef, ariaId, open } = useContext(DatePickerContext);
42
+
43
+ const {
44
+ inputProps,
45
+ size = "medium",
46
+ inputDescriptionId,
47
+ errorId,
48
+ showErrorMsg,
49
+ hasError,
50
+ } = useFormField(props, "datepicker-input");
51
+
52
+ const { className, hideLabel = false, label, description, ...rest } = props;
53
+
54
+ return (
55
+ <div
56
+ className={cl(
57
+ className,
58
+ "navds-form-field",
59
+ `navds-form-field--${size}`,
60
+ "navds-date__field",
61
+ {
62
+ "navds-date__field--error": hasError,
63
+ "navds-date__field--disabled": !!inputProps.disabled,
64
+ }
65
+ )}
66
+ ref={props?.wrapperRef}
67
+ >
68
+ <Label
69
+ htmlFor={inputProps.id}
70
+ size={size}
71
+ className={cl("navds-form-field__label", {
72
+ "navds-sr-only": hideLabel,
73
+ })}
74
+ >
75
+ {label}
76
+ </Label>
77
+ {!!description && (
78
+ <BodyShort
79
+ as="div"
80
+ className={cl("navds-form-field__description", {
81
+ "navds-sr-only": hideLabel,
82
+ })}
83
+ id={inputDescriptionId}
84
+ size={size}
85
+ >
86
+ {description}
87
+ </BodyShort>
88
+ )}
89
+ <div className="navds-date__field-wrapper">
90
+ <input
91
+ ref={ref}
92
+ {...omit(rest, ["error", "errorId", "size", "wrapperRef"])}
93
+ {...inputProps}
94
+ autoComplete="off"
95
+ aria-controls={ariaId}
96
+ className={cl(
97
+ className,
98
+ "navds-date__field-input",
99
+ "navds-text-field__input",
100
+ "navds-body-short",
101
+ `navds-body-${size}`
102
+ )}
103
+ size={14}
104
+ aria-haspopup="grid"
105
+ />
106
+ <Button
107
+ ref={buttonRef}
108
+ variant="tertiary"
109
+ type="button"
110
+ size="small"
111
+ onClick={() => onOpen()}
112
+ className="navds-date__field-button"
113
+ icon={
114
+ <Calender title={open ? "Lukk datovelger" : "Åpne datovelger"} />
115
+ }
116
+ aria-haspopup="grid"
117
+ />
118
+ </div>
119
+ <div
120
+ className="navds-form-field__error"
121
+ id={errorId}
122
+ aria-relevant="additions removals"
123
+ aria-live="polite"
124
+ >
125
+ {showErrorMsg && <ErrorMessage size={size}>{props.error}</ErrorMessage>}
126
+ </div>
127
+ </div>
128
+ );
129
+ });
130
+
131
+ export default DatePickerInput;
@@ -0,0 +1,121 @@
1
+ import cl from "clsx";
2
+ import { isWeekend } from "date-fns";
3
+ import React, { forwardRef } from "react";
4
+ import {
5
+ DateRange,
6
+ DayPicker,
7
+ isMatch,
8
+ SelectMultipleEventHandler,
9
+ SelectRangeEventHandler,
10
+ SelectSingleEventHandler,
11
+ } from "react-day-picker";
12
+ import { Caption, DropdownCaption } from "./caption";
13
+ import { ConditionalModeProps, DatePickerDefaultProps } from "./DatePicker";
14
+ import { getLocaleFromString, labels } from "./utils";
15
+
16
+ interface DatePickerStandaloneDefaultProps
17
+ extends Omit<
18
+ DatePickerDefaultProps,
19
+ "focusOnOpen" | "open" | "onClose" | "classNames" | "onOpenToggle"
20
+ > {
21
+ /**
22
+ * Wrapper className
23
+ */
24
+ className?: string;
25
+ }
26
+
27
+ export type DatePickerStandaloneProps = DatePickerStandaloneDefaultProps &
28
+ ConditionalModeProps;
29
+
30
+ export type DatePickerStandaloneType = React.ForwardRefExoticComponent<
31
+ DatePickerStandaloneProps & React.RefAttributes<HTMLDivElement>
32
+ >;
33
+
34
+ export const DatePickerStandalone: DatePickerStandaloneType = forwardRef<
35
+ HTMLDivElement,
36
+ DatePickerStandaloneProps
37
+ >(
38
+ (
39
+ {
40
+ children,
41
+ className,
42
+ locale = "nb",
43
+ yearSelector,
44
+ disabled = [],
45
+ disableWeekends = false,
46
+ showWeekNumber = false,
47
+ mode = "single",
48
+ selected,
49
+ id,
50
+ defaultSelected,
51
+ onSelect,
52
+ ...rest
53
+ },
54
+ ref
55
+ ) => {
56
+ const [selectedDates, setSelectedDates] = React.useState<
57
+ Date | Date[] | DateRange | undefined
58
+ >(defaultSelected);
59
+
60
+ const handleSingleSelect: SelectSingleEventHandler = (selectedDay) => {
61
+ setSelectedDates(selectedDay);
62
+ onSelect && (onSelect as (val?: Date) => void)(selectedDay);
63
+ };
64
+
65
+ const handleMultipleSelect: SelectMultipleEventHandler = (selectedDays) => {
66
+ setSelectedDates(selectedDays);
67
+ onSelect && (onSelect as (val?: Date[]) => void)(selectedDays);
68
+ };
69
+
70
+ const handleRangeSelect: SelectRangeEventHandler = (selectedDays) => {
71
+ setSelectedDates(selectedDays);
72
+ onSelect && (onSelect as (val?: DateRange) => void)(selectedDays);
73
+ };
74
+
75
+ const overrideProps = {
76
+ onSelect:
77
+ mode === "single"
78
+ ? handleSingleSelect
79
+ : mode === "multiple"
80
+ ? handleMultipleSelect
81
+ : handleRangeSelect,
82
+ };
83
+
84
+ return (
85
+ <div
86
+ ref={ref}
87
+ className={cl("navds-date__standalone-wrapper", className)}
88
+ >
89
+ <DayPicker
90
+ locale={getLocaleFromString(locale)}
91
+ mode={mode}
92
+ {...overrideProps}
93
+ selected={selected ?? selectedDates}
94
+ components={{
95
+ Caption: yearSelector ? DropdownCaption : Caption,
96
+ }}
97
+ className="navds-date"
98
+ classNames={{ vhidden: "navds-sr-only" }}
99
+ disabled={(day) => {
100
+ return (
101
+ (disableWeekends && isWeekend(day)) || isMatch(day, disabled)
102
+ );
103
+ }}
104
+ weekStartsOn={1}
105
+ initialFocus={false}
106
+ labels={labels as any}
107
+ modifiers={{
108
+ weekend: (day) => disableWeekends && isWeekend(day),
109
+ }}
110
+ modifiersClassNames={{
111
+ weekend: "rdp-day__weekend",
112
+ }}
113
+ showWeekNumber={showWeekNumber}
114
+ {...rest}
115
+ />
116
+ </div>
117
+ );
118
+ }
119
+ );
120
+
121
+ export default DatePickerStandalone;
@@ -0,0 +1,51 @@
1
+ import { Left, Right } from "@navikt/ds-icons";
2
+ import React from "react";
3
+ import { CaptionProps, useDayPicker, useNavigation } from "react-day-picker";
4
+ import { Button, Label } from "../..";
5
+
6
+ export const DatePickerCaption = ({ displayMonth, id }: CaptionProps) => {
7
+ const { goToMonth, nextMonth, previousMonth } = useNavigation();
8
+ const {
9
+ labels: { labelPrevious, labelNext },
10
+ formatters: { formatCaption },
11
+ locale,
12
+ } = useDayPicker();
13
+
14
+ const previousLabel = labelPrevious(previousMonth, { locale });
15
+ const nextLabel = labelNext(nextMonth, { locale });
16
+
17
+ return (
18
+ <div className="navds-date__caption">
19
+ <Label
20
+ as="span"
21
+ aria-live="polite"
22
+ aria-atomic="true"
23
+ id={id}
24
+ className="navds-date__caption-label"
25
+ >
26
+ {formatCaption(displayMonth, { locale })}
27
+ </Label>
28
+
29
+ <div className="navds-date__caption__month-wrapper">
30
+ <Button
31
+ aria-label={previousLabel}
32
+ variant="tertiary"
33
+ disabled={!previousMonth}
34
+ onClick={() => previousMonth && goToMonth(previousMonth)}
35
+ icon={<Left title="velg forrige månede" />}
36
+ className="navds-date__caption-button"
37
+ />
38
+ <Button
39
+ aria-label={nextLabel}
40
+ icon={<Right title="velg neste månede" />}
41
+ onClick={() => nextMonth && goToMonth(nextMonth)}
42
+ disabled={!nextMonth}
43
+ variant="tertiary"
44
+ className="navds-date__caption-button"
45
+ />
46
+ </div>
47
+ </div>
48
+ );
49
+ };
50
+
51
+ export default DatePickerCaption;
@@ -0,0 +1,94 @@
1
+ import { Left, Right } from "@navikt/ds-icons";
2
+ import { setMonth, setYear, startOfMonth } from "date-fns";
3
+ import React from "react";
4
+ import { CaptionProps, useDayPicker, useNavigation } from "react-day-picker";
5
+ import { Button, Select } from "../..";
6
+ import { getMonths, getYears } from "../utils/get-dates";
7
+ import { labelMonthDropdown, labelYearDropdown } from "../utils/labels";
8
+
9
+ export const DropdownCaption = ({ displayMonth, id }: CaptionProps) => {
10
+ const { goToMonth, nextMonth, previousMonth } = useNavigation();
11
+ const {
12
+ fromDate,
13
+ toDate,
14
+ formatters: { formatYearCaption, formatMonthCaption, formatCaption },
15
+ labels: { labelPrevious, labelNext },
16
+ locale,
17
+ } = useDayPicker();
18
+
19
+ if (!fromDate || !toDate) return <></>;
20
+
21
+ const handleYearChange: React.ChangeEventHandler<HTMLSelectElement> = (e) =>
22
+ goToMonth(setYear(startOfMonth(displayMonth), Number(e.target.value)));
23
+
24
+ const handleMonthChange: React.ChangeEventHandler<HTMLSelectElement> = (e) =>
25
+ goToMonth(setMonth(startOfMonth(displayMonth), Number(e.target.value)));
26
+
27
+ const years = getYears(fromDate, toDate);
28
+ const months = getMonths(fromDate, toDate, displayMonth);
29
+
30
+ const previousLabel = labelPrevious(previousMonth, { locale });
31
+ const nextLabel = labelNext(nextMonth, { locale });
32
+ const yearDropdownLabel = labelYearDropdown(locale);
33
+ const MonthDropdownLabel = labelMonthDropdown(locale);
34
+
35
+ return (
36
+ <div className="navds-date__caption-dropdown">
37
+ <span
38
+ aria-live="polite"
39
+ aria-atomic="true"
40
+ id={id}
41
+ className="navds-sr-only"
42
+ >
43
+ {formatCaption(displayMonth, { locale })}
44
+ </span>
45
+ <Button
46
+ aria-label={previousLabel}
47
+ variant={"tertiary"}
48
+ disabled={!previousMonth}
49
+ onClick={() => previousMonth && goToMonth(previousMonth)}
50
+ icon={<Left title="velg forrige månede" />}
51
+ className="navds-date__caption-button"
52
+ />
53
+
54
+ <div className="navds-date__caption__month-wrapper">
55
+ <Select
56
+ label={MonthDropdownLabel}
57
+ hideLabel
58
+ className="navds-date__caption__month"
59
+ value={displayMonth.getMonth()}
60
+ onChange={handleMonthChange}
61
+ >
62
+ {months.map((m) => (
63
+ <option key={m.getMonth()} value={m.getMonth()}>
64
+ {formatMonthCaption(m, { locale })}
65
+ </option>
66
+ ))}
67
+ </Select>
68
+ <Select
69
+ label={yearDropdownLabel}
70
+ hideLabel
71
+ value={displayMonth.getFullYear()}
72
+ onChange={handleYearChange}
73
+ >
74
+ {years.map((year) => (
75
+ <option key={year.getFullYear()} value={year.getFullYear()}>
76
+ {formatYearCaption(year, { locale })}
77
+ </option>
78
+ ))}
79
+ </Select>
80
+ </div>
81
+
82
+ <Button
83
+ aria-label={nextLabel}
84
+ icon={<Right title="velg neste månede" />}
85
+ onClick={() => nextMonth && goToMonth(nextMonth)}
86
+ disabled={!nextMonth}
87
+ variant={"tertiary"}
88
+ className="navds-date__caption-button"
89
+ />
90
+ </div>
91
+ );
92
+ };
93
+
94
+ export default DropdownCaption;
@@ -0,0 +1,2 @@
1
+ export { default as Caption } from "./Caption";
2
+ export { default as DropdownCaption } from "./DropdownCaption";