@sproutsocial/seeds-react-datepicker 1.0.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.
@@ -0,0 +1,125 @@
1
+ import React, { useState } from "react";
2
+ import moment, { type Moment } from "moment";
3
+ import Box from "@sproutsocial/seeds-react-box";
4
+ import Button from "@sproutsocial/seeds-react-button";
5
+ import Popout from "@sproutsocial/seeds-react-popout";
6
+ import DateRangePicker from "./DateRangePicker";
7
+ import type { EnumFocusedInput } from "./DateRangePickerTypes";
8
+ import { StatefulDateRangePicker } from "./StatefulDateRangePicker";
9
+ import { formatDateAsCalendarHeader } from "../common";
10
+
11
+ const START_DATE = "startDate";
12
+ const END_DATE = "endDate";
13
+
14
+ const today = moment("2023-01-06");
15
+
16
+ const dateProps = {
17
+ startDate: today,
18
+ endDate: today.clone().add(3, "days"),
19
+ initialVisibleMonth: () => today,
20
+ setStatusText: (dates: moment.Moment[]) =>
21
+ `Now showing: ${dates.map(formatDateAsCalendarHeader).join(" and ")}`,
22
+ };
23
+
24
+ const meta = {
25
+ title: "Components/DatePickers/DateRangePicker",
26
+ component: StatefulDateRangePicker,
27
+ };
28
+ export default meta;
29
+
30
+ export const Default = {
31
+ args: dateProps,
32
+ };
33
+
34
+ export const NoStartingDate = {
35
+ args: {
36
+ ...dateProps,
37
+ startDate: null,
38
+ endDate: null,
39
+ },
40
+ };
41
+
42
+ export const StartOnPreviousMonth = {
43
+ args: {
44
+ ...dateProps,
45
+ initialVisibleMonth: () => dateProps.endDate.clone().subtract(1, "month"),
46
+ },
47
+ };
48
+
49
+ export const BlockedRanges = {
50
+ render: () => {
51
+ const endOfPreviousMonth = today
52
+ .clone()
53
+ .subtract(1, "month")
54
+ .endOf("month");
55
+ const startOfNextMonth = today.clone().add(1, "month").startOf("month");
56
+ return (
57
+ <StatefulDateRangePicker
58
+ {...dateProps}
59
+ isOutsideRange={(day) =>
60
+ day.isSameOrBefore(endOfPreviousMonth) ||
61
+ day.isSameOrAfter(startOfNextMonth)
62
+ }
63
+ />
64
+ );
65
+ },
66
+ };
67
+
68
+ export const FocusControlledPopoutDatePicker = {
69
+ render: () => {
70
+ const [focusedInput, setFocusedInput] = useState<EnumFocusedInput>(null);
71
+ const [dates, pickDate] = useState<{
72
+ startDate: Moment | null;
73
+ endDate: Moment | null;
74
+ }>({
75
+ startDate: null,
76
+ endDate: null,
77
+ });
78
+
79
+ const onFocusChange = (nextFocusedInput: typeof focusedInput) => {
80
+ setFocusedInput(nextFocusedInput);
81
+ };
82
+
83
+ return (
84
+ <Popout
85
+ isOpen={focusedInput !== null}
86
+ content={
87
+ <Popout.Content pt={400} p={0}>
88
+ <DateRangePicker
89
+ {...dates}
90
+ onDatesChange={pickDate}
91
+ focusedInput={focusedInput}
92
+ onFocusChange={onFocusChange}
93
+ setStatusText={dateProps.setStatusText}
94
+ />
95
+ <Box display="flex" justifyContent="space-between" p={400}>
96
+ <Button
97
+ appearance="secondary"
98
+ onClick={() => setFocusedInput(START_DATE)}
99
+ >
100
+ {dates.startDate
101
+ ? dates.startDate.format("DD MMM YYYY")
102
+ : "Choose Start Date"}
103
+ </Button>
104
+ <Button
105
+ appearance="secondary"
106
+ onClick={() => setFocusedInput(END_DATE)}
107
+ >
108
+ {dates.endDate
109
+ ? dates.endDate.format("DD MMM YYYY")
110
+ : "Choose End Date"}
111
+ </Button>
112
+ </Box>
113
+ </Popout.Content>
114
+ }
115
+ >
116
+ <Button
117
+ appearance="secondary"
118
+ onClick={() => setFocusedInput(START_DATE)}
119
+ >
120
+ Open Date Picker
121
+ </Button>
122
+ </Popout>
123
+ );
124
+ },
125
+ };
@@ -0,0 +1,83 @@
1
+ import React, { useCallback, useState, type KeyboardEvent } from "react";
2
+ import moment from "moment";
3
+ import DayPickerRangeController from "react-dates/lib/components/DayPickerRangeController";
4
+ import { ReactDatesCssOverrides } from "../styles";
5
+ import {
6
+ commonDatePickerProps,
7
+ DefaultSetStatusText,
8
+ getVisibleMonths,
9
+ } from "../common";
10
+ import { VisuallyHidden } from "@sproutsocial/seeds-react-visually-hidden";
11
+ import type { TypeDateRangePickerProps } from "./DateRangePickerTypes";
12
+
13
+ const DateRangePicker = ({
14
+ startDate = null,
15
+ endDate = null,
16
+ onDatesChange,
17
+ setStatusText = DefaultSetStatusText,
18
+ initialVisibleMonth,
19
+ numberOfMonths = 2,
20
+ onFocusChange,
21
+ onBlur,
22
+ focusedInput,
23
+ ...rest
24
+ }: TypeDateRangePickerProps) => {
25
+ const [statusText, updateStatusText] = useState(() =>
26
+ setStatusText(
27
+ getVisibleMonths(
28
+ moment(initialVisibleMonth?.() ?? startDate ?? undefined),
29
+ numberOfMonths
30
+ )
31
+ )
32
+ );
33
+ interface HandleMonthClick {
34
+ (month: moment.Moment): void;
35
+ }
36
+
37
+ const handleMonthClick: HandleMonthClick = useCallback(
38
+ (month) => {
39
+ updateStatusText(setStatusText(getVisibleMonths(month, numberOfMonths)));
40
+ },
41
+ [numberOfMonths, setStatusText]
42
+ );
43
+ const wrappedOnBlur = useCallback<
44
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
+ (event: undefined | KeyboardEvent<HTMLDivElement>) => void
46
+ >(
47
+ (event) => {
48
+ // for some reason onBlur is called with no event on day selection 🤷
49
+ if (!event) {
50
+ return;
51
+ }
52
+ onFocusChange?.(null);
53
+ onBlur?.(event);
54
+ },
55
+ [onBlur, onFocusChange]
56
+ );
57
+
58
+ return (
59
+ <>
60
+ <ReactDatesCssOverrides />
61
+ <VisuallyHidden>
62
+ <div role="status">{statusText}</div>
63
+ </VisuallyHidden>
64
+ <DayPickerRangeController
65
+ {...commonDatePickerProps}
66
+ startDate={startDate}
67
+ endDate={endDate}
68
+ onDatesChange={onDatesChange}
69
+ numberOfMonths={numberOfMonths}
70
+ initialVisibleMonth={initialVisibleMonth || null}
71
+ focusedInput={focusedInput}
72
+ isFocused={focusedInput !== null}
73
+ onBlur={wrappedOnBlur}
74
+ onFocusChange={onFocusChange}
75
+ onPrevMonthClick={handleMonthClick}
76
+ onNextMonthClick={handleMonthClick}
77
+ {...rest}
78
+ />
79
+ </>
80
+ );
81
+ };
82
+
83
+ export default DateRangePicker;
@@ -0,0 +1,35 @@
1
+ import type { Moment } from "moment";
2
+ import type { TypeCommonDatePickerProps } from "../types";
3
+
4
+ export type EnumFocusedInput = null | "startDate" | "endDate";
5
+
6
+ export interface TypeStatefulDateRangePickerProps
7
+ extends TypeCommonDatePickerProps {
8
+ startDate?: Moment | null;
9
+ endDate?: Moment | null;
10
+ focusedInput?: EnumFocusedInput;
11
+ onDatesChange?: (arg0: {
12
+ startDate: Moment | null;
13
+ endDate: Moment | null;
14
+ }) => void;
15
+ onFocusChange?: (arg0: EnumFocusedInput) => void;
16
+ setStatusText?: (dates: Moment[]) => string;
17
+ }
18
+
19
+ export interface TypeDateRangePickerProps
20
+ extends Required<
21
+ Pick<
22
+ TypeStatefulDateRangePickerProps,
23
+ "focusedInput" | "onDatesChange" | "onFocusChange"
24
+ >
25
+ >,
26
+ Pick<
27
+ TypeStatefulDateRangePickerProps,
28
+ | "startDate"
29
+ | "endDate"
30
+ | "setStatusText"
31
+ | "initialVisibleMonth"
32
+ | "numberOfMonths"
33
+ > {
34
+ onBlur?: React.KeyboardEventHandler<HTMLDivElement> | undefined;
35
+ }
@@ -0,0 +1,47 @@
1
+ import React, { useState } from "react";
2
+ // @ts-ignore unknown types
3
+ import { START_DATE } from "react-dates/constants";
4
+ import type { TypeStatefulDateRangePickerProps } from "./DateRangePickerTypes";
5
+ import DateRangePicker from "./DateRangePicker";
6
+
7
+ export const StatefulDateRangePicker = ({
8
+ startDate,
9
+ endDate,
10
+ onDatesChange,
11
+ onFocusChange,
12
+ ...rest
13
+ }: TypeStatefulDateRangePickerProps) => {
14
+ const [dates, setDate] = useState({
15
+ startDate,
16
+ endDate,
17
+ });
18
+ const [focusedInput, setFocusedInput] = React.useState(START_DATE);
19
+
20
+ // @ts-ignore unknown types
21
+ const handleDatesChange = (nextDates) => {
22
+ onDatesChange && onDatesChange(nextDates);
23
+ setDate(nextDates);
24
+ };
25
+
26
+ // @ts-ignore unknown types
27
+ const handleFocusChange = (nextFocusedInput) => {
28
+ onFocusChange && onFocusChange(nextFocusedInput);
29
+ setFocusedInput(
30
+ // null means that we've selected an end date. we want to go back to START_DATE
31
+ // so the user can modify their selection. if focusedInput === null then it won't
32
+ // respond to click or keyboard events
33
+ nextFocusedInput === null ? START_DATE : nextFocusedInput
34
+ );
35
+ };
36
+
37
+ return (
38
+ <DateRangePicker
39
+ startDate={dates.startDate}
40
+ endDate={dates.endDate}
41
+ focusedInput={focusedInput}
42
+ onDatesChange={handleDatesChange}
43
+ onFocusChange={handleFocusChange}
44
+ {...rest}
45
+ />
46
+ );
47
+ };
@@ -0,0 +1,257 @@
1
+ import React, { useState } from "react";
2
+ import moment, { type Moment } from "moment";
3
+ import {
4
+ render,
5
+ fireEvent,
6
+ screen,
7
+ waitFor,
8
+ act,
9
+ } from "@sproutsocial/seeds-react-testing-library";
10
+ import { formatDateAsCalendarDay } from "../../common";
11
+ import {
12
+ DateRangePicker,
13
+ type EnumFocusedInput,
14
+ StatefulDateRangePicker,
15
+ } from "../index";
16
+ import Popout from "@sproutsocial/seeds-react-popout";
17
+ import Button from "@sproutsocial/seeds-react-button";
18
+
19
+ // @ts-ignore Unknown types
20
+ const getStartDate = (options, date) =>
21
+ // eslint-disable-next-line testing-library/prefer-screen-queries
22
+ options.queryByLabelText(
23
+ `Selected as start date. ${formatDateAsCalendarDay(date)}`
24
+ );
25
+
26
+ // @ts-ignore Unknown types
27
+ const getEndDate = (options, date) =>
28
+ // eslint-disable-next-line testing-library/prefer-screen-queries
29
+ options.queryByLabelText(
30
+ `Selected as end date. ${formatDateAsCalendarDay(date)}`
31
+ );
32
+
33
+ // @ts-ignore Unknown types
34
+ const clickOnStartDate = (options, date) =>
35
+ fireEvent.click(
36
+ // eslint-disable-next-line testing-library/prefer-screen-queries
37
+ options.getByLabelText(
38
+ `Choose ${formatDateAsCalendarDay(
39
+ date
40
+ )} as your check-in date. It’s available.`
41
+ )
42
+ );
43
+
44
+ // @ts-ignore Unknown types
45
+ const clickOnEndDate = (options, date) =>
46
+ fireEvent.click(
47
+ // eslint-disable-next-line testing-library/prefer-screen-queries
48
+ options.getByLabelText(
49
+ `Choose ${formatDateAsCalendarDay(
50
+ date
51
+ )} as your check-out date. It’s available.`
52
+ )
53
+ );
54
+
55
+ describe("date range picker", () => {
56
+ beforeEach(() => {
57
+ jest.useFakeTimers();
58
+ });
59
+
60
+ test("should start and end on a date range", async () => {
61
+ const today = moment();
62
+ const oneWeekFromToday = today.clone().add(1, "week");
63
+ render(
64
+ <StatefulDateRangePicker
65
+ startDate={today}
66
+ endDate={oneWeekFromToday}
67
+ setStatusText={() => ""}
68
+ />
69
+ );
70
+ await waitFor(() => {
71
+ expect(getStartDate(screen, today)).toBeInTheDocument();
72
+ expect(getEndDate(screen, oneWeekFromToday)).toBeInTheDocument();
73
+ });
74
+ });
75
+
76
+ test("should select a new start and end date", async () => {
77
+ const today = moment();
78
+ const oneWeekFromToday = today.clone().add(1, "week");
79
+ const oneMonthFromNow = today.clone().add(1, "month");
80
+ const oneMonthPlusOneWeekFromNow = oneMonthFromNow.clone().add(1, "week");
81
+ render(
82
+ <StatefulDateRangePicker
83
+ startDate={today}
84
+ endDate={oneWeekFromToday}
85
+ setStatusText={() => ""}
86
+ />
87
+ );
88
+ await waitFor(() => {
89
+ expect(getStartDate(screen, today)).toBeInTheDocument();
90
+ expect(getEndDate(screen, oneWeekFromToday)).toBeInTheDocument();
91
+ });
92
+ clickOnStartDate(screen, oneMonthFromNow);
93
+ clickOnEndDate(screen, oneMonthPlusOneWeekFromNow);
94
+ await waitFor(() => {
95
+ expect(getStartDate(screen, today)).not.toBeInTheDocument();
96
+ expect(getEndDate(screen, oneWeekFromToday)).not.toBeInTheDocument();
97
+ expect(getStartDate(screen, oneMonthFromNow)).toBeInTheDocument();
98
+ expect(
99
+ getEndDate(screen, oneMonthPlusOneWeekFromNow)
100
+ ).toBeInTheDocument();
101
+ });
102
+ });
103
+
104
+ test("should call onDateChange", async () => {
105
+ const onDatesChange = jest.fn();
106
+ const today = moment();
107
+ const oneWeekFromToday = today.clone().add(1, "week");
108
+ const twoWeeksFromToday = today.clone().add(2, "week");
109
+ const threeWeeksFromToday = today.clone().add(3, "week");
110
+ render(
111
+ <StatefulDateRangePicker
112
+ startDate={today}
113
+ endDate={oneWeekFromToday}
114
+ onDatesChange={onDatesChange}
115
+ setStatusText={() => ""}
116
+ />
117
+ );
118
+ clickOnStartDate(screen, twoWeeksFromToday);
119
+ await waitFor(() => {
120
+ expect(onDatesChange).toHaveBeenCalledTimes(1);
121
+ expect(onDatesChange.mock.calls[0][0].startDate.isValid()).toEqual(true);
122
+ });
123
+ clickOnEndDate(screen, threeWeeksFromToday);
124
+ await waitFor(() => {
125
+ expect(onDatesChange).toHaveBeenCalledTimes(2);
126
+ expect(onDatesChange.mock.calls[1][0].endDate.isValid()).toEqual(true);
127
+ });
128
+ });
129
+
130
+ test("should call onFocusChange", async () => {
131
+ const onFocusChange = jest.fn();
132
+ const today = moment();
133
+ const oneWeekFromToday = today.clone().add(1, "week");
134
+ const oneMonthFromNow = today.clone().add(1, "month");
135
+ const oneMonthPlusOneWeekFromNow = oneMonthFromNow.clone().add(1, "week");
136
+ render(
137
+ <StatefulDateRangePicker
138
+ startDate={today}
139
+ endDate={oneWeekFromToday}
140
+ onFocusChange={onFocusChange}
141
+ setStatusText={() => ""}
142
+ />
143
+ );
144
+ clickOnStartDate(screen, oneMonthFromNow);
145
+ await waitFor(() => {
146
+ expect(onFocusChange).toHaveBeenCalledTimes(1);
147
+ expect(onFocusChange).toHaveBeenCalledWith("endDate");
148
+ });
149
+ clickOnEndDate(screen, oneMonthPlusOneWeekFromNow);
150
+ await waitFor(() => {
151
+ expect(onFocusChange).toHaveBeenCalledTimes(2);
152
+ expect(onFocusChange).toHaveBeenCalledWith(null);
153
+ });
154
+ });
155
+
156
+ // TODO: This test times out. We'll come back to fix later
157
+ xtest("should close on escape key press", async () => {
158
+ const buttonText = "Date Picker";
159
+ const initialDate = moment("2020-01-01");
160
+
161
+ const FocusControlledPopoutDatePicker = () => {
162
+ const [isOpen, setIsOpen] = useState(false);
163
+ const [focusedInput, setFocusedInput] = useState<EnumFocusedInput>(null);
164
+ const [dates, pickDate] = useState<{
165
+ startDate: Moment | null;
166
+ endDate: Moment | null;
167
+ }>({
168
+ startDate: null,
169
+ endDate: null,
170
+ });
171
+
172
+ const onFocusChange = (nextFocusedInput: typeof focusedInput) => {
173
+ if (nextFocusedInput) {
174
+ setIsOpen(true);
175
+ } else {
176
+ setIsOpen(false);
177
+ }
178
+ setFocusedInput(nextFocusedInput);
179
+ };
180
+
181
+ return (
182
+ <Popout
183
+ isOpen={isOpen}
184
+ setIsOpen={setIsOpen}
185
+ content={
186
+ <Popout.Content pt={400} p={0}>
187
+ <DateRangePicker
188
+ {...dates}
189
+ onDatesChange={pickDate}
190
+ focusedInput={focusedInput}
191
+ onFocusChange={onFocusChange}
192
+ setStatusText={() => ""}
193
+ initialVisibleMonth={() => initialDate}
194
+ />
195
+ <Button
196
+ appearance="secondary"
197
+ onClick={() => {
198
+ setIsOpen(true);
199
+ setFocusedInput("startDate");
200
+ }}
201
+ >
202
+ {dates.startDate
203
+ ? dates.startDate.format("DD MMM YYYY")
204
+ : "Choose Start Date"}
205
+ </Button>
206
+ <Button
207
+ appearance="secondary"
208
+ onClick={() => {
209
+ setIsOpen(true);
210
+ setFocusedInput("endDate");
211
+ }}
212
+ >
213
+ {dates.endDate
214
+ ? dates.endDate.format("DD MMM YYYY")
215
+ : "Choose End Date"}
216
+ </Button>
217
+ </Popout.Content>
218
+ }
219
+ >
220
+ <Button
221
+ appearance="secondary"
222
+ onClick={() => {
223
+ setIsOpen(true);
224
+ setFocusedInput("startDate");
225
+ }}
226
+ >
227
+ {buttonText}
228
+ </Button>
229
+ </Popout>
230
+ );
231
+ };
232
+
233
+ render(<FocusControlledPopoutDatePicker />);
234
+
235
+ expect(screen.getByText(buttonText)).toBeInTheDocument();
236
+
237
+ act(() => {
238
+ fireEvent.click(screen.getByText(buttonText));
239
+ jest.runAllTimers();
240
+ });
241
+
242
+ await waitFor(() => {
243
+ expect(screen.getByText("January 2020")).toBeInTheDocument();
244
+ expect(screen.getByText("February 2020")).toBeInTheDocument();
245
+ });
246
+
247
+ act(() => {
248
+ fireEvent.keyDown(screen.getByText("January 2020"), { key: "Escape" });
249
+ jest.runAllTimers();
250
+ });
251
+
252
+ await waitFor(() => {
253
+ expect(screen.queryByText("January 2020")).not.toBeInTheDocument();
254
+ expect(screen.queryByText("February 2020")).not.toBeInTheDocument();
255
+ });
256
+ });
257
+ });
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { DateRangePicker, StatefulDateRangePicker } from "../../index";
3
+
4
+ const mockedRequiredProps = {
5
+ focusedInput: null,
6
+ onDatesChange: jest.fn(),
7
+ onFocusChange: jest.fn(),
8
+ setStatusText: jest.fn(),
9
+ };
10
+
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+ function DateRangePickerTypes() {
13
+ return (
14
+ <>
15
+ <StatefulDateRangePicker setStatusText={() => ""} />
16
+ <StatefulDateRangePicker
17
+ startDate={null}
18
+ endDate={null}
19
+ setStatusText={() => ""}
20
+ />
21
+ {/* @ts-expect-error - test that invalid type is rejected */}
22
+ <StatefulDateRangePicker startDate="invalid" />
23
+
24
+ <DateRangePicker {...mockedRequiredProps} />
25
+ <DateRangePicker
26
+ {...mockedRequiredProps}
27
+ startDate={null}
28
+ endDate={null}
29
+ />
30
+ {/* @ts-expect-error - test missing required props is rejected */}
31
+ <DateRangePicker />
32
+ </>
33
+ );
34
+ }
@@ -0,0 +1,6 @@
1
+ import DateRangePicker from "./DateRangePicker";
2
+
3
+ export default DateRangePicker;
4
+ export { DateRangePicker };
5
+ export * from "./StatefulDateRangePicker";
6
+ export * from "./DateRangePickerTypes";
@@ -0,0 +1,92 @@
1
+ import React, { useState } from "react";
2
+ import moment, { type Moment } from "moment";
3
+ import Button from "@sproutsocial/seeds-react-button";
4
+ import Popout from "@sproutsocial/seeds-react-popout";
5
+ import SingleDatePicker from "./SingleDatePicker";
6
+ import { StatefulSingleDatePicker } from "./StatefulSingleDatePicker";
7
+ import { formatDateAsCalendarHeader } from "../common";
8
+
9
+ const today = moment("2023-01-06");
10
+ interface DateProps {
11
+ date: Moment | null;
12
+ focused: boolean;
13
+ initialVisibleMonth: () => Moment;
14
+ setStatusText: (dates: Moment[]) => string;
15
+ }
16
+
17
+ const dateProps: DateProps = {
18
+ date: today,
19
+ focused: true,
20
+ initialVisibleMonth: () => today,
21
+ setStatusText: (dates: Moment[]) =>
22
+ `Now showing: ${dates.map(formatDateAsCalendarHeader).join(" and ")}`,
23
+ };
24
+
25
+ const meta = {
26
+ title: "Components/DatePickers/SingleDatePicker",
27
+ component: StatefulSingleDatePicker,
28
+ };
29
+ export default meta;
30
+
31
+ export const Default = {
32
+ args: dateProps,
33
+ };
34
+
35
+ export const NoStartingDate = {
36
+ args: {
37
+ ...dateProps,
38
+ date: null,
39
+ },
40
+ };
41
+
42
+ export const TwoMonthDatePicker = {
43
+ args: {
44
+ ...dateProps,
45
+ numberOfMonths: 2,
46
+ },
47
+ };
48
+
49
+ export const DatePickerInPopout = {
50
+ render: () => (
51
+ <Popout
52
+ content={
53
+ <Popout.Content p={0} py={400}>
54
+ <StatefulSingleDatePicker {...dateProps} />
55
+ </Popout.Content>
56
+ }
57
+ >
58
+ <Button appearance="secondary">Open Date Picker</Button>
59
+ </Popout>
60
+ ),
61
+ };
62
+
63
+ export const FocusControlledPopoutDatePicker = {
64
+ render: () => {
65
+ const [focused, setIsFocused] = useState(false);
66
+ const [date, pickDate] = useState<Moment | null>(moment());
67
+
68
+ const onFocusChange = ({ focused }: { focused: boolean }) =>
69
+ setIsFocused(focused);
70
+
71
+ return (
72
+ <Popout
73
+ isOpen={focused}
74
+ content={
75
+ <Popout.Content p={0} py={400}>
76
+ <SingleDatePicker
77
+ date={date}
78
+ onDateChange={pickDate}
79
+ focused={focused}
80
+ onFocusChange={onFocusChange}
81
+ setStatusText={dateProps.setStatusText}
82
+ />
83
+ </Popout.Content>
84
+ }
85
+ >
86
+ <Button appearance="secondary" onClick={() => setIsFocused(true)}>
87
+ Open Date Picker
88
+ </Button>
89
+ </Popout>
90
+ );
91
+ },
92
+ };