@navikt/ds-react 1.3.7 → 1.3.9

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 (185) hide show
  1. package/_docs.json +4956 -2568
  2. package/cjs/date/DateInput.js +79 -0
  3. package/cjs/date/datepicker/DatePicker.js +113 -0
  4. package/cjs/date/datepicker/DatePickerStandalone.js +81 -0
  5. package/cjs/date/datepicker/DayButton.js +43 -0
  6. package/cjs/date/datepicker/caption/Caption.js +23 -0
  7. package/cjs/date/datepicker/caption/DropdownCaption.js +38 -0
  8. package/cjs/date/datepicker/caption/index.js +10 -0
  9. package/cjs/date/datepicker/caption/package.json +6 -0
  10. package/cjs/date/hooks/index.js +15 -0
  11. package/cjs/date/hooks/package.json +6 -0
  12. package/cjs/date/hooks/useDateInputContext.js +17 -0
  13. package/cjs/date/hooks/useDatepicker.js +135 -0
  14. package/cjs/date/hooks/useMonthPicker.js +139 -0
  15. package/cjs/date/hooks/useRangeDatepicker.js +215 -0
  16. package/cjs/date/hooks/useSharedMonthContext.js +63 -0
  17. package/cjs/date/index.js +14 -0
  18. package/cjs/date/monthpicker/MonthButton.js +80 -0
  19. package/cjs/date/monthpicker/MonthCaption.js +47 -0
  20. package/cjs/date/monthpicker/MonthPicker.js +74 -0
  21. package/cjs/date/monthpicker/MonthPickerStandalone.js +54 -0
  22. package/cjs/date/monthpicker/MonthSelector.js +79 -0
  23. package/cjs/date/package.json +6 -0
  24. package/cjs/date/utils/check-dates.js +17 -0
  25. package/cjs/date/utils/dates-disabled.js +29 -0
  26. package/cjs/date/utils/format-date.js +12 -0
  27. package/cjs/date/utils/get-dates.js +43 -0
  28. package/cjs/date/utils/get-initial-year.js +21 -0
  29. package/cjs/date/utils/index.js +33 -0
  30. package/cjs/date/utils/is-match.js +61 -0
  31. package/cjs/date/utils/labels.js +85 -0
  32. package/cjs/date/utils/locale.js +21 -0
  33. package/cjs/date/utils/navigation.js +155 -0
  34. package/cjs/date/utils/package.json +6 -0
  35. package/cjs/date/utils/parse-date.js +39 -0
  36. package/cjs/form/ConfirmationPanel.js +4 -3
  37. package/cjs/index.js +1 -0
  38. package/cjs/util/AnimateHeight.js +2 -2
  39. package/esm/date/DateInput.d.ts +30 -0
  40. package/esm/date/DateInput.js +51 -0
  41. package/esm/date/DateInput.js.map +1 -0
  42. package/esm/date/datepicker/DatePicker.d.ts +95 -0
  43. package/esm/date/datepicker/DatePicker.js +85 -0
  44. package/esm/date/datepicker/DatePicker.js.map +1 -0
  45. package/esm/date/datepicker/DatePickerStandalone.d.ts +12 -0
  46. package/esm/date/datepicker/DatePickerStandalone.js +53 -0
  47. package/esm/date/datepicker/DatePickerStandalone.js.map +1 -0
  48. package/esm/date/datepicker/DayButton.d.ts +3 -0
  49. package/esm/date/datepicker/DayButton.js +17 -0
  50. package/esm/date/datepicker/DayButton.js.map +1 -0
  51. package/esm/date/datepicker/caption/Caption.d.ts +4 -0
  52. package/esm/date/datepicker/caption/Caption.js +17 -0
  53. package/esm/date/datepicker/caption/Caption.js.map +1 -0
  54. package/esm/date/datepicker/caption/DropdownCaption.d.ts +4 -0
  55. package/esm/date/datepicker/caption/DropdownCaption.js +32 -0
  56. package/esm/date/datepicker/caption/DropdownCaption.js.map +1 -0
  57. package/esm/date/datepicker/caption/index.d.ts +2 -0
  58. package/esm/date/datepicker/caption/index.js +3 -0
  59. package/esm/date/datepicker/caption/index.js.map +1 -0
  60. package/esm/date/hooks/index.d.ts +5 -0
  61. package/esm/date/hooks/index.js +6 -0
  62. package/esm/date/hooks/index.js.map +1 -0
  63. package/esm/date/hooks/useDateInputContext.d.ts +18 -0
  64. package/esm/date/hooks/useDateInputContext.js +14 -0
  65. package/esm/date/hooks/useDateInputContext.js.map +1 -0
  66. package/esm/date/hooks/useDatepicker.d.ts +37 -0
  67. package/esm/date/hooks/useDatepicker.js +132 -0
  68. package/esm/date/hooks/useDatepicker.js.map +1 -0
  69. package/esm/date/hooks/useMonthPicker.d.ts +33 -0
  70. package/esm/date/hooks/useMonthPicker.js +136 -0
  71. package/esm/date/hooks/useMonthPicker.js.map +1 -0
  72. package/esm/date/hooks/useRangeDatepicker.d.ts +39 -0
  73. package/esm/date/hooks/useRangeDatepicker.js +212 -0
  74. package/esm/date/hooks/useRangeDatepicker.js.map +1 -0
  75. package/esm/date/hooks/useSharedMonthContext.d.ts +21 -0
  76. package/esm/date/hooks/useSharedMonthContext.js +36 -0
  77. package/esm/date/hooks/useSharedMonthContext.js.map +1 -0
  78. package/esm/date/index.d.ts +6 -0
  79. package/esm/date/index.js +4 -0
  80. package/esm/date/index.js.map +1 -0
  81. package/esm/date/monthpicker/MonthButton.d.ts +11 -0
  82. package/esm/date/monthpicker/MonthButton.js +51 -0
  83. package/esm/date/monthpicker/MonthButton.js.map +1 -0
  84. package/esm/date/monthpicker/MonthCaption.d.ts +3 -0
  85. package/esm/date/monthpicker/MonthCaption.js +41 -0
  86. package/esm/date/monthpicker/MonthCaption.js.map +1 -0
  87. package/esm/date/monthpicker/MonthPicker.d.ts +90 -0
  88. package/esm/date/monthpicker/MonthPicker.js +46 -0
  89. package/esm/date/monthpicker/MonthPicker.js.map +1 -0
  90. package/esm/date/monthpicker/MonthPickerStandalone.d.ts +11 -0
  91. package/esm/date/monthpicker/MonthPickerStandalone.js +26 -0
  92. package/esm/date/monthpicker/MonthPickerStandalone.js.map +1 -0
  93. package/esm/date/monthpicker/MonthSelector.d.ts +3 -0
  94. package/esm/date/monthpicker/MonthSelector.js +50 -0
  95. package/esm/date/monthpicker/MonthSelector.js.map +1 -0
  96. package/esm/date/utils/check-dates.d.ts +4 -0
  97. package/esm/date/utils/check-dates.js +12 -0
  98. package/esm/date/utils/check-dates.js.map +1 -0
  99. package/esm/date/utils/dates-disabled.d.ts +1 -0
  100. package/esm/date/utils/dates-disabled.js +26 -0
  101. package/esm/date/utils/dates-disabled.js.map +1 -0
  102. package/esm/date/utils/format-date.d.ts +1 -0
  103. package/esm/date/utils/format-date.js +9 -0
  104. package/esm/date/utils/format-date.js.map +1 -0
  105. package/esm/date/utils/get-dates.d.ts +2 -0
  106. package/esm/date/utils/get-dates.js +39 -0
  107. package/esm/date/utils/get-dates.js.map +1 -0
  108. package/esm/date/utils/get-initial-year.d.ts +5 -0
  109. package/esm/date/utils/get-initial-year.js +18 -0
  110. package/esm/date/utils/get-initial-year.js.map +1 -0
  111. package/esm/date/utils/index.d.ts +10 -0
  112. package/esm/date/utils/index.js +11 -0
  113. package/esm/date/utils/index.js.map +1 -0
  114. package/esm/date/utils/is-match.d.ts +4 -0
  115. package/esm/date/utils/is-match.js +57 -0
  116. package/esm/date/utils/is-match.js.map +1 -0
  117. package/esm/date/utils/labels.d.ts +6 -0
  118. package/esm/date/utils/labels.js +79 -0
  119. package/esm/date/utils/labels.js.map +1 -0
  120. package/esm/date/utils/locale.d.ts +2 -0
  121. package/esm/date/utils/locale.js +15 -0
  122. package/esm/date/utils/locale.js.map +1 -0
  123. package/esm/date/utils/navigation.d.ts +2 -0
  124. package/esm/date/utils/navigation.js +152 -0
  125. package/esm/date/utils/navigation.js.map +1 -0
  126. package/esm/date/utils/parse-date.d.ts +3 -0
  127. package/esm/date/utils/parse-date.js +36 -0
  128. package/esm/date/utils/parse-date.js.map +1 -0
  129. package/esm/form/ConfirmationPanel.js +5 -4
  130. package/esm/form/ConfirmationPanel.js.map +1 -1
  131. package/esm/index.d.ts +1 -0
  132. package/esm/index.js +1 -0
  133. package/esm/index.js.map +1 -1
  134. package/esm/typography/Detail.d.ts +1 -1
  135. package/esm/util/AnimateHeight.js +2 -2
  136. package/esm/util/AnimateHeight.js.map +1 -1
  137. package/package.json +6 -4
  138. package/src/chat/chat.stories.tsx +15 -15
  139. package/src/date/DateInput.tsx +167 -0
  140. package/src/date/datepicker/DatePicker.tsx +252 -0
  141. package/src/date/datepicker/DatePickerStandalone.tsx +120 -0
  142. package/src/date/datepicker/DayButton.tsx +26 -0
  143. package/src/date/datepicker/caption/Caption.tsx +51 -0
  144. package/src/date/datepicker/caption/DropdownCaption.tsx +98 -0
  145. package/src/date/datepicker/caption/index.ts +2 -0
  146. package/src/date/datepicker/datepicker.stories.tsx +235 -0
  147. package/src/date/hooks/index.ts +8 -0
  148. package/src/date/hooks/useDateInputContext.tsx +32 -0
  149. package/src/date/hooks/useDatepicker.tsx +225 -0
  150. package/src/date/hooks/useMonthPicker.tsx +223 -0
  151. package/src/date/hooks/useRangeDatepicker.tsx +348 -0
  152. package/src/date/hooks/useSharedMonthContext.tsx +68 -0
  153. package/src/date/index.ts +16 -0
  154. package/src/date/monthpicker/MonthButton.tsx +109 -0
  155. package/src/date/monthpicker/MonthCaption.tsx +94 -0
  156. package/src/date/monthpicker/MonthPicker.tsx +204 -0
  157. package/src/date/monthpicker/MonthPickerStandalone.tsx +87 -0
  158. package/src/date/monthpicker/MonthSelector.tsx +85 -0
  159. package/src/date/monthpicker/monthpicker.stories.tsx +128 -0
  160. package/src/date/utils/__tests__/check-dates.test.ts +47 -0
  161. package/src/date/utils/__tests__/dates-disabled.test.ts +48 -0
  162. package/src/date/utils/__tests__/format-dates.test.ts +22 -0
  163. package/src/date/utils/__tests__/get-dates.test.ts +79 -0
  164. package/src/date/utils/__tests__/get-initial-year.test.ts +94 -0
  165. package/src/date/utils/__tests__/is-match.test.ts +42 -0
  166. package/src/date/utils/__tests__/parse-dates.test.ts +81 -0
  167. package/src/date/utils/check-dates.ts +17 -0
  168. package/src/date/utils/dates-disabled.ts +26 -0
  169. package/src/date/utils/format-date.ts +17 -0
  170. package/src/date/utils/get-dates.ts +44 -0
  171. package/src/date/utils/get-initial-year.ts +25 -0
  172. package/src/date/utils/index.ts +21 -0
  173. package/src/date/utils/is-match.ts +88 -0
  174. package/src/date/utils/labels.ts +84 -0
  175. package/src/date/utils/locale.ts +15 -0
  176. package/src/date/utils/navigation.ts +292 -0
  177. package/src/date/utils/parse-date.ts +46 -0
  178. package/src/form/ConfirmationPanel.tsx +9 -2
  179. package/src/form/checkbox/Checkbox.test.tsx +2 -2
  180. package/src/form/radio/radio.stories.tsx +4 -4
  181. package/src/form/stories/textarea.stories.tsx +1 -3
  182. package/src/help-text/help-text.stories.tsx +3 -0
  183. package/src/index.ts +1 -0
  184. package/src/typography/Detail.tsx +1 -1
  185. package/src/util/AnimateHeight.tsx +6 -7
@@ -0,0 +1,167 @@
1
+ import { Calender } from "@navikt/ds-icons";
2
+ import cl from "clsx";
3
+ import React, { forwardRef, InputHTMLAttributes } from "react";
4
+ import { BodyShort, Button, ErrorMessage, Label, omit } from "..";
5
+ import { FormFieldProps, useFormField } from "../form/useFormField";
6
+ import { useDateInputContext } from "./hooks";
7
+
8
+ export interface DateInputProps
9
+ extends FormFieldProps,
10
+ Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
11
+ /**
12
+ * Input label
13
+ */
14
+ label: React.ReactNode;
15
+ /**
16
+ * Shows label and description for screenreaders-only
17
+ * @default false
18
+ */
19
+ hideLabel?: boolean;
20
+ /**
21
+ * Changes padding and font-sizes
22
+ * @default medium
23
+ */
24
+ size?: "medium" | "small";
25
+ /**
26
+ * @private
27
+ */
28
+ wrapperRef?: React.RefObject<HTMLDivElement>;
29
+ /**
30
+ * Defines usage for date or month picker.
31
+ * @default "datepicker"
32
+ */
33
+ variant?: "datepicker" | "monthpicker";
34
+ }
35
+
36
+ export type DateInputType = React.ForwardRefExoticComponent<
37
+ DateInputProps & React.RefAttributes<HTMLInputElement>
38
+ >;
39
+
40
+ const DateInput: DateInputType = forwardRef<HTMLInputElement, DateInputProps>(
41
+ (props, ref) => {
42
+ const {
43
+ className,
44
+ hideLabel = false,
45
+ label,
46
+ description,
47
+ variant = "datepicker",
48
+ ...rest
49
+ } = props;
50
+
51
+ const isDatepickerVariant = variant === "datepicker";
52
+
53
+ const conditionalVariables = {
54
+ prefix: isDatepickerVariant ? "datepicker-input" : "monthpicker-input",
55
+ iconTitle: {
56
+ open: isDatepickerVariant ? "Åpne datovelger" : "Åpne månedsvelger",
57
+ close: isDatepickerVariant ? "Lukk datovelger" : "Lukk månedsvelger",
58
+ },
59
+ };
60
+
61
+ const { onOpen, ariaId, open } = useDateInputContext();
62
+
63
+ const {
64
+ inputProps,
65
+ size = "medium",
66
+ inputDescriptionId,
67
+ errorId,
68
+ showErrorMsg,
69
+ hasError,
70
+ } = useFormField(props, conditionalVariables.prefix);
71
+
72
+ return (
73
+ <div
74
+ className={cl(
75
+ className,
76
+ "navds-form-field",
77
+ `navds-form-field--${size}`,
78
+ "navds-date__field",
79
+ {
80
+ "navds-text-field--error": hasError,
81
+ "navds-date__field--error": hasError,
82
+ "navds-form-field--disabled": !!inputProps.disabled,
83
+ "navds-text-field--disabled": !!inputProps.disabled,
84
+ }
85
+ )}
86
+ ref={props?.wrapperRef}
87
+ >
88
+ <Label
89
+ htmlFor={inputProps.id}
90
+ size={size}
91
+ className={cl("navds-form-field__label", {
92
+ "navds-sr-only": hideLabel,
93
+ })}
94
+ >
95
+ {label}
96
+ </Label>
97
+ {!!description && (
98
+ <BodyShort
99
+ as="div"
100
+ className={cl("navds-form-field__description", {
101
+ "navds-sr-only": hideLabel,
102
+ })}
103
+ id={inputDescriptionId}
104
+ size={size}
105
+ >
106
+ {description}
107
+ </BodyShort>
108
+ )}
109
+ <div className="navds-date__field-wrapper">
110
+ <input
111
+ ref={ref}
112
+ {...omit(rest, ["error", "errorId", "size", "wrapperRef"])}
113
+ {...inputProps}
114
+ autoComplete="off"
115
+ aria-controls={ariaId}
116
+ className={cl(
117
+ className,
118
+ "navds-date__field-input",
119
+ "navds-text-field__input",
120
+ "navds-body-short",
121
+ `navds-body-${size}`
122
+ )}
123
+ size={14}
124
+ />
125
+ <Button
126
+ variant="tertiary"
127
+ type="button"
128
+ size="small"
129
+ onClick={() => onOpen()}
130
+ className="navds-date__field-button"
131
+ tabIndex={open ? -1 : 0}
132
+ disabled={inputProps.disabled}
133
+ icon={
134
+ <Calender
135
+ title={
136
+ open
137
+ ? conditionalVariables.iconTitle.close
138
+ : conditionalVariables.iconTitle.open
139
+ }
140
+ />
141
+ }
142
+ />
143
+ </div>
144
+ <div
145
+ className="navds-form-field__error"
146
+ id={errorId}
147
+ aria-relevant="additions removals"
148
+ aria-live="polite"
149
+ >
150
+ {showErrorMsg && (
151
+ <ErrorMessage size={size}>{props.error}</ErrorMessage>
152
+ )}
153
+ </div>
154
+ </div>
155
+ );
156
+ }
157
+ );
158
+
159
+ export const DatePickerInput: DateInputType = forwardRef<
160
+ HTMLInputElement,
161
+ DateInputProps
162
+ >((props, ref) => <DateInput {...props} ref={ref} />);
163
+
164
+ export const MonthPickerInput: DateInputType = forwardRef<
165
+ HTMLInputElement,
166
+ DateInputProps
167
+ >((props, ref) => <DateInput {...props} variant="monthpicker" ref={ref} />);
@@ -0,0 +1,252 @@
1
+ import cl from "clsx";
2
+ import { isWeekend } from "date-fns";
3
+ import React, { forwardRef, useRef, useState } from "react";
4
+ import {
5
+ DateRange,
6
+ DayPicker,
7
+ DayPickerBase,
8
+ isMatch,
9
+ Matcher,
10
+ SelectMultipleEventHandler,
11
+ SelectRangeEventHandler,
12
+ SelectSingleEventHandler,
13
+ } from "react-day-picker";
14
+ import { omit, Popover, useId } from "../..";
15
+ import { DateInputType, DatePickerInput } from "../DateInput";
16
+ import { DateContext } from "../hooks";
17
+ import { getLocaleFromString, labels } from "../utils";
18
+ import { Caption, DropdownCaption } from "./caption";
19
+ import DatePickerStandalone, {
20
+ DatePickerStandaloneType,
21
+ } from "./DatePickerStandalone";
22
+ import { DayButton } from "./DayButton";
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
+ * Classname for datepicker in popover
59
+ */
60
+ className?: string;
61
+ /**
62
+ * Classname for wrapper
63
+ */
64
+ wrapperClassName?: string;
65
+ /**
66
+ * Changes datepicker locale
67
+ * @default "nb" (norsk bokmål)
68
+ */
69
+ locale?: "nb" | "nn" | "en";
70
+ /**
71
+ * The earliest day to start navigation.
72
+ */
73
+ fromDate?: Date;
74
+ /**
75
+ * The latests day to end navigation.
76
+ */
77
+ toDate?: Date;
78
+ /**
79
+ * Display dropdown for choosing the month and the year.
80
+ * Needs `fromDate` + `toDate` to work.
81
+ * @default false
82
+ */
83
+ dropdownCaption?: boolean;
84
+ /**
85
+ * Apply the disabled modifier to the matching days.
86
+ * {@link https://react-day-picker.js.org/api/types/Matcher | Matcher type-definition}
87
+ */
88
+ disabled?: Matcher[];
89
+ /**
90
+ * Disable saturday and sunday.
91
+ * @default false
92
+ */
93
+ disableWeekends?: boolean;
94
+ /**
95
+ * Shows week numbers in left-column
96
+ * Use with caution, takes up valuable screenspace for small screens.
97
+ * @default false
98
+ */
99
+ showWeekNumber?: boolean;
100
+ /**
101
+ * Open state for user-controlled state
102
+ * Component controlled by default
103
+ */
104
+ open?: boolean;
105
+ /**
106
+ * onClose callback for user-controlled state
107
+ */
108
+ onClose?: () => void;
109
+ /**
110
+ * onOpenToggle callback for user-controlled state
111
+ * only called if `<DatePicker.Input />` is used
112
+ */
113
+ onOpenToggle?: () => void;
114
+ }
115
+
116
+ export type DatePickerProps = DatePickerDefaultProps & ConditionalModeProps;
117
+
118
+ interface DatePickerComponent
119
+ extends React.ForwardRefExoticComponent<DatePickerProps> {
120
+ Standalone: DatePickerStandaloneType;
121
+ Input: DateInputType;
122
+ }
123
+
124
+ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
125
+ (
126
+ {
127
+ children,
128
+ locale = "nb",
129
+ dropdownCaption,
130
+ disabled = [],
131
+ disableWeekends = false,
132
+ showWeekNumber = false,
133
+ selected,
134
+ id,
135
+ defaultSelected,
136
+ className,
137
+ wrapperClassName,
138
+ open: _open,
139
+ onClose,
140
+ onOpenToggle,
141
+ ...rest
142
+ },
143
+ ref
144
+ ) => {
145
+ const ariaId = useId(id);
146
+ const [open, setOpen] = useState(_open ?? false);
147
+
148
+ const wrapperRef = useRef<HTMLDivElement | null>(null);
149
+
150
+ const [selectedDates, setSelectedDates] = React.useState<
151
+ Date | Date[] | DateRange | undefined
152
+ >(defaultSelected);
153
+
154
+ const handleSingleSelect: SelectSingleEventHandler = (selectedDay) => {
155
+ setSelectedDates(selectedDay);
156
+ selectedDay && (onClose?.() ?? setOpen(false));
157
+ rest?.onSelect && (rest?.onSelect as (val?: Date) => void)(selectedDay);
158
+ };
159
+
160
+ const handleMultipleSelect: SelectMultipleEventHandler = (selectedDays) => {
161
+ setSelectedDates(selectedDays);
162
+ rest?.onSelect &&
163
+ (rest?.onSelect as (val?: Date[]) => void)(selectedDays);
164
+ };
165
+
166
+ const handleRangeSelect: SelectRangeEventHandler = (selectedDays) => {
167
+ setSelectedDates(selectedDays);
168
+ selectedDays?.from && selectedDays?.to && (onClose?.() ?? setOpen(false));
169
+ rest?.onSelect &&
170
+ (rest?.onSelect as (val?: DateRange) => void)(selectedDays);
171
+ };
172
+
173
+ const overrideProps = {
174
+ onSelect:
175
+ rest?.mode === "single"
176
+ ? handleSingleSelect
177
+ : rest?.mode === "multiple"
178
+ ? handleMultipleSelect
179
+ : handleRangeSelect,
180
+ };
181
+
182
+ return (
183
+ <DateContext.Provider
184
+ value={{
185
+ open: _open ?? open,
186
+ onOpen: () => {
187
+ setOpen((x) => !x);
188
+ onOpenToggle?.();
189
+ },
190
+ ariaId,
191
+ }}
192
+ >
193
+ <div
194
+ ref={wrapperRef}
195
+ className={cl("navds-date__wrapper", wrapperClassName)}
196
+ >
197
+ {children}
198
+ {(_open ?? open) && (
199
+ <Popover
200
+ arrow={false}
201
+ anchorEl={wrapperRef.current}
202
+ open={_open ?? open}
203
+ onClose={() => {
204
+ onClose?.() ?? setOpen(false);
205
+ }}
206
+ placement="bottom-start"
207
+ id={ariaId}
208
+ role="dialog"
209
+ ref={ref}
210
+ >
211
+ <DayPicker
212
+ locale={getLocaleFromString(locale)}
213
+ {...overrideProps}
214
+ selected={selected ?? selectedDates}
215
+ components={{
216
+ Caption: dropdownCaption ? DropdownCaption : Caption,
217
+ Day: DayButton,
218
+ }}
219
+ className={cl("navds-date", className)}
220
+ classNames={{
221
+ vhidden: "navds-sr-only",
222
+ }}
223
+ disabled={(day) => {
224
+ return (
225
+ (disableWeekends && isWeekend(day)) ||
226
+ isMatch(day, disabled)
227
+ );
228
+ }}
229
+ weekStartsOn={1}
230
+ initialFocus={false}
231
+ labels={labels as any}
232
+ modifiers={{
233
+ weekend: (day) => disableWeekends && isWeekend(day),
234
+ }}
235
+ modifiersClassNames={{
236
+ weekend: "rdp-day__weekend",
237
+ }}
238
+ showWeekNumber={showWeekNumber}
239
+ {...omit(rest, ["onSelect"])}
240
+ />
241
+ </Popover>
242
+ )}
243
+ </div>
244
+ </DateContext.Provider>
245
+ );
246
+ }
247
+ ) as DatePickerComponent;
248
+
249
+ DatePicker.Standalone = DatePickerStandalone;
250
+ DatePicker.Input = DatePickerInput;
251
+
252
+ export default DatePicker;
@@ -0,0 +1,120 @@
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 { omit } from "../..";
13
+ import { getLocaleFromString, labels } from "../utils";
14
+ import { Caption, DropdownCaption } from "./caption";
15
+ import { ConditionalModeProps, DatePickerDefaultProps } from "./DatePicker";
16
+
17
+ interface DatePickerStandaloneDefaultProps
18
+ extends Omit<
19
+ DatePickerDefaultProps,
20
+ "open" | "onClose" | "onOpenToggle" | "wrapperClassName"
21
+ > {
22
+ /**
23
+ * Datepicker classname
24
+ */
25
+ className?: string;
26
+ }
27
+
28
+ export type DatePickerStandaloneProps = DatePickerStandaloneDefaultProps &
29
+ ConditionalModeProps;
30
+
31
+ export type DatePickerStandaloneType = React.ForwardRefExoticComponent<
32
+ DatePickerStandaloneProps & React.RefAttributes<HTMLDivElement>
33
+ >;
34
+
35
+ export const DatePickerStandalone: DatePickerStandaloneType = forwardRef<
36
+ HTMLDivElement,
37
+ DatePickerStandaloneProps
38
+ >(
39
+ (
40
+ {
41
+ children,
42
+ className,
43
+ locale = "nb",
44
+ dropdownCaption,
45
+ disabled = [],
46
+ disableWeekends = false,
47
+ showWeekNumber = false,
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
+ rest?.mode === "single"
78
+ ? handleSingleSelect
79
+ : rest?.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
+ {...overrideProps}
92
+ selected={selected ?? selectedDates}
93
+ components={{
94
+ Caption: dropdownCaption ? DropdownCaption : Caption,
95
+ }}
96
+ className="navds-date"
97
+ classNames={{ vhidden: "navds-sr-only" }}
98
+ disabled={(day) => {
99
+ return (
100
+ (disableWeekends && isWeekend(day)) || isMatch(day, disabled)
101
+ );
102
+ }}
103
+ weekStartsOn={1}
104
+ initialFocus={false}
105
+ labels={labels as any}
106
+ modifiers={{
107
+ weekend: (day) => disableWeekends && isWeekend(day),
108
+ }}
109
+ modifiersClassNames={{
110
+ weekend: "rdp-day__weekend",
111
+ }}
112
+ showWeekNumber={showWeekNumber}
113
+ {...omit(rest, ["onSelect"])}
114
+ />
115
+ </div>
116
+ );
117
+ }
118
+ );
119
+
120
+ export default DatePickerStandalone;
@@ -0,0 +1,26 @@
1
+ import { format } from "date-fns";
2
+ import React, { useRef } from "react";
3
+ import { Button, DayProps, useDayPicker, useDayRender } from "react-day-picker";
4
+
5
+ export const DayButton = (props: DayProps) => {
6
+ const buttonRef = useRef<HTMLButtonElement>(null);
7
+ const dayRender = useDayRender(props.date, props.displayMonth, buttonRef);
8
+ const { locale } = useDayPicker();
9
+ const dateTime = format(props.date, "cccc d", { locale });
10
+
11
+ if (dayRender.isHidden) {
12
+ return <></>;
13
+ }
14
+ if (!dayRender.isButton) {
15
+ return <div {...dayRender.divProps} />;
16
+ }
17
+
18
+ return (
19
+ <Button
20
+ name="day"
21
+ ref={buttonRef}
22
+ {...dayRender.buttonProps}
23
+ aria-label={dateTime}
24
+ />
25
+ );
26
+ };
@@ -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åned" />}
36
+ className="navds-date__caption-button"
37
+ />
38
+ <Button
39
+ aria-label={nextLabel}
40
+ icon={<Right title="velg neste måned" />}
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;