@indico-data/design-system 2.41.0 → 2.41.1

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 (39) hide show
  1. package/build/generated/iconTypes.ts +5 -0
  2. package/lib/components/forms/date/datePicker/DatePicker.d.ts +1 -2
  3. package/lib/components/forms/date/datePicker/__tests__/DatePicker.test.d.ts +1 -0
  4. package/lib/components/forms/date/datePicker/contants.d.ts +1 -1
  5. package/lib/components/forms/date/datePicker/index.d.ts +1 -0
  6. package/lib/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.d.ts +3 -3
  7. package/lib/components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.d.ts +1 -0
  8. package/lib/components/forms/date/inputDatePicker/SingleInputDatePicker.d.ts +5 -6
  9. package/lib/components/forms/date/inputDatePicker/__tests__/SingleInputDatePicker.test.d.ts +1 -0
  10. package/lib/components/forms/date/inputDatePicker/helpers.d.ts +1 -0
  11. package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +5 -6
  12. package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.d.ts +1 -1
  13. package/lib/components/forms/date/inputDateRangePicker/__tests__/InputDateRangePicker.test.d.ts +1 -0
  14. package/lib/components/index.d.ts +1 -0
  15. package/lib/index.d.ts +8 -7
  16. package/lib/index.esm.js +48 -47
  17. package/lib/index.esm.js.map +1 -1
  18. package/lib/index.js +48 -47
  19. package/lib/index.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/floatUI/{FloatUI.test.tsx → __tests__/FloatUI.test.tsx} +3 -3
  22. package/src/components/forms/date/datePicker/DatePicker.tsx +2 -2
  23. package/src/components/forms/date/datePicker/__tests__/DatePicker.test.tsx +55 -0
  24. package/src/components/forms/date/datePicker/contants.ts +1 -1
  25. package/src/components/forms/date/datePicker/index.ts +1 -0
  26. package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.mdx +1 -1
  27. package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.stories.tsx +0 -10
  28. package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.tsx +9 -12
  29. package/src/components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.tsx +47 -127
  30. package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.stories.tsx +17 -3
  31. package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.tsx +31 -30
  32. package/src/components/forms/date/inputDatePicker/__tests__/SingleInputDatePicker.test.tsx +118 -0
  33. package/src/components/forms/date/inputDatePicker/helpers.ts +3 -0
  34. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.mdx +9 -3
  35. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.tsx +41 -44
  36. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +83 -49
  37. package/src/components/forms/date/inputDateRangePicker/__tests__/InputDateRangePicker.test.tsx +96 -0
  38. package/src/components/index.ts +1 -0
  39. /package/lib/components/floatUI/{FloatUI.test.d.ts → __tests__/FloatUI.test.d.ts} +0 -0
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor, within } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { SingleInputDatePicker } from '../SingleInputDatePicker';
5
+ import { format, addDays } from 'date-fns';
6
+
7
+ const mockOnSelect = jest.fn();
8
+
9
+ describe('SingleInputDatePicker', () => {
10
+ const RealDate = Date;
11
+
12
+ beforeAll(() => {
13
+ global.Date = class extends RealDate {
14
+ constructor() {
15
+ super('2024-01-01T00:00:00Z');
16
+ }
17
+ } as any;
18
+ Object.assign(global.Date, RealDate);
19
+ });
20
+
21
+ afterAll(() => {
22
+ global.Date = RealDate;
23
+ });
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ });
28
+
29
+ it('opens the date picker and selects a date', async () => {
30
+ render(
31
+ <SingleInputDatePicker
32
+ ariaLabel="Single Input Date Picker"
33
+ data-testid="datepicker-testid"
34
+ captionLayout="label"
35
+ onSelect={mockOnSelect}
36
+ />,
37
+ );
38
+
39
+ const formInput = await screen.findByTestId('form-input-Date Picker');
40
+ expect(formInput).toBeInTheDocument();
41
+
42
+ await userEvent.click(formInput);
43
+
44
+ const datepicker = await screen.findByTestId('datepicker-testid');
45
+ expect(datepicker).toBeVisible();
46
+
47
+ const dayButtons = await within(datepicker).findAllByRole('button', { name: /\d+/ });
48
+ expect(dayButtons.length).toBeGreaterThan(0);
49
+
50
+ const lastDayButton = dayButtons[dayButtons.length - 1];
51
+ await userEvent.click(lastDayButton);
52
+
53
+ const expectedDate = new Date('2024-01-01T00:00:00Z');
54
+ const alternateDate = addDays(expectedDate, -1);
55
+
56
+ await waitFor(
57
+ () => {
58
+ const inputValue = (formInput as HTMLInputElement).value;
59
+ const formattedExpectedDate = format(expectedDate, 'MM/dd/yyyy');
60
+ const formattedAlternateDate = format(alternateDate, 'MM/dd/yyyy');
61
+
62
+ expect(inputValue === formattedExpectedDate || inputValue === formattedAlternateDate).toBe(
63
+ true,
64
+ );
65
+ },
66
+ { timeout: 2000 },
67
+ );
68
+ });
69
+
70
+ it('updates the selected value in the dropdown to match user input', async () => {
71
+ render(
72
+ <SingleInputDatePicker
73
+ ariaLabel="Single Input Date Picker"
74
+ captionLayout="label"
75
+ data-testid="datepicker-testid"
76
+ onSelect={mockOnSelect}
77
+ />,
78
+ );
79
+
80
+ const formInput = await screen.findByTestId('form-input-Date Picker');
81
+ expect(formInput).toBeInTheDocument();
82
+
83
+ await userEvent.click(formInput);
84
+ await userEvent.clear(formInput);
85
+ await userEvent.type(formInput, '12/31/2023');
86
+
87
+ await waitFor(() => {
88
+ expect(formInput).toHaveValue('12/31/2023');
89
+ });
90
+
91
+ await userEvent.click(formInput);
92
+
93
+ const datepicker = await screen.findByTestId('datepicker-testid');
94
+ expect(datepicker).toBeVisible();
95
+
96
+ const dayButtons = await within(datepicker).findAllByRole('button', { name: /\d+/ });
97
+ expect(dayButtons.length).toBeGreaterThan(0);
98
+
99
+ const lastDayButton = dayButtons[dayButtons.length - 1];
100
+ await userEvent.click(lastDayButton);
101
+
102
+ const expectedDate = new Date('2024-01-01T00:00:00Z');
103
+ const alternateDate = addDays(expectedDate, -1);
104
+
105
+ await waitFor(
106
+ () => {
107
+ const inputValue = (formInput as HTMLInputElement).value;
108
+ const formattedExpectedDate = format(expectedDate, 'MM/dd/yyyy');
109
+ const formattedAlternateDate = format(alternateDate, 'MM/dd/yyyy');
110
+ // date hack to make sure pipeline timezone does not break test.
111
+ expect(inputValue === formattedExpectedDate || inputValue === formattedAlternateDate).toBe(
112
+ true,
113
+ );
114
+ },
115
+ { timeout: 2000 },
116
+ );
117
+ });
118
+ });
@@ -0,0 +1,3 @@
1
+ import { format } from 'date-fns';
2
+
3
+ export const formatDateAsString = (date: Date) => format(date, 'MM/dd/yyyy');
@@ -7,12 +7,18 @@ import { Row, Col } from '../../../grid/index';
7
7
  # Input Date Range Picker
8
8
  The Input Date Range Picker is a visual representation of an input field that allows you to enter a date value to the input or select a date from the date picker. It leverages our DatePicker component inside of a FloatingUI window.
9
9
 
10
- <Canvas of={InputDateRangePicker.SingleInput} />
10
+ <Canvas of={InputDateRangePicker.RangeInput} />
11
11
 
12
12
  ## Props
13
13
  In addition to the props listed below, it also accepts all props listed [here](story=?path=/docs/forms-datepicker--datepicker)
14
- <Controls of={InputDateRangePicker.SingleInput} />
14
+ <Controls of={InputDateRangePicker.RangeInput} />
15
15
 
16
+ # Notes
17
+ It is expected to show the same date in both input fields if only one date has been selected. This represents a start and end date on the same date. A usecase for this behavior would be a separate time on that same date. Expect the `to` input to update once a range has been selected (or the `from` input has been updated) to reflect the selected range.
16
18
 
17
19
  ## ToDo
18
- - Create single component for all dropdown date pickers that accept an react element as the trigger, such as an icon, button, component, input, etc.
20
+ - Clear on closed property
21
+ - Clear all selections (we need to think this one through, my initial thought is this clear would be handled by the parent component, not the date picker itself.)
22
+ - Better error handling and react-hook-form support
23
+ - Decide whether we want one icon prop or two.
24
+ - Decide if we want individual labels for each input or a single label for the range.
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { InputDateRangePicker } from './InputDateRangePicker';
3
3
  import { useArgs } from '@storybook/preview-api';
4
+ import { DateRange } from 'react-day-picker';
5
+ import { useEffect, useState } from 'react';
4
6
 
5
7
  const meta: Meta<typeof InputDateRangePicker> = {
6
8
  component: InputDateRangePicker,
@@ -23,19 +25,21 @@ const meta: Meta<typeof InputDateRangePicker> = {
23
25
  },
24
26
  },
25
27
  },
26
- className: {
27
- control: false,
28
- description: 'Accepts a CSS class name',
28
+ captionLayout: {
29
+ control: 'select',
30
+ options: ['dropdown', 'dropdown-months', 'dropdown-years', 'label'],
31
+ description:
32
+ 'The layout of the caption. Enables you to add or remove the dropdown navigation for months/years',
29
33
  table: {
30
34
  category: 'Props',
31
35
  type: {
32
- summary: 'string',
36
+ summary: 'dropdown | dropdown-months | dropdown-years | label',
33
37
  },
34
38
  },
35
39
  },
36
- id: {
37
- control: 'text',
38
- description: 'The id of the input field.',
40
+ className: {
41
+ control: false,
42
+ description: 'Accepts a CSS class name',
39
43
  table: {
40
44
  category: 'Props',
41
45
  type: {
@@ -43,9 +47,10 @@ const meta: Meta<typeof InputDateRangePicker> = {
43
47
  },
44
48
  },
45
49
  },
46
- label: {
50
+ id: {
47
51
  control: 'text',
48
- description: 'The label for the input field.',
52
+ description:
53
+ 'The id of the input fieldss. Each one has the id appended to it, For example an id of `holiday` would be `holiday-to` and `holiday-from`',
49
54
  table: {
50
55
  category: 'Props',
51
56
  type: {
@@ -64,28 +69,18 @@ const meta: Meta<typeof InputDateRangePicker> = {
64
69
  },
65
70
  },
66
71
  selected: {
67
- control: 'date',
68
- description: 'The selected date.',
72
+ control: false,
73
+ description: 'The selected date range.',
69
74
  table: {
70
75
  category: 'Props',
71
76
  type: {
72
- summary: 'Date',
77
+ summary: 'DateRange',
73
78
  },
74
79
  },
75
80
  },
76
81
  inputPlaceholder: {
77
82
  control: 'text',
78
- description: 'The placeholder text for the input field.',
79
- table: {
80
- category: 'Props',
81
- type: {
82
- summary: 'string',
83
- },
84
- },
85
- },
86
- inputIconName: {
87
- control: 'text',
88
- description: 'The icon to display in the input field.',
83
+ description: 'The placeholder text for the input fields.',
89
84
  table: {
90
85
  category: 'Props',
91
86
  type: {
@@ -93,9 +88,10 @@ const meta: Meta<typeof InputDateRangePicker> = {
93
88
  },
94
89
  },
95
90
  },
96
- isClearable: {
97
- control: 'boolean',
98
- description: 'Whether the input field should be clearable.',
91
+ isOpen: {
92
+ control: false,
93
+ description:
94
+ 'Whether the floatingUI component wrapped around the date picker is opened or closed.',
99
95
  table: {
100
96
  category: 'Props',
101
97
  type: {
@@ -103,29 +99,29 @@ const meta: Meta<typeof InputDateRangePicker> = {
103
99
  },
104
100
  },
105
101
  },
106
- isOpen: {
107
- control: false,
108
- description: 'Whether the date picker is open.',
102
+ inputIconName: {
103
+ control: 'text',
104
+ description: 'The icon to display in the input fields.',
109
105
  table: {
110
106
  category: 'Props',
111
107
  type: {
112
- summary: 'boolean',
108
+ summary: 'string',
113
109
  },
114
110
  },
115
111
  },
116
- clearOnClose: {
117
- control: 'boolean',
118
- description: 'Whether the input field should be cleared when the date picker is closed.',
112
+ toErrorMessage: {
113
+ control: 'text',
114
+ description: 'An error message to display on the `to` input.',
119
115
  table: {
120
116
  category: 'Props',
121
117
  type: {
122
- summary: 'boolean',
118
+ summary: 'string',
123
119
  },
124
120
  },
125
121
  },
126
- errorMessage: {
122
+ fromErrorMessage: {
127
123
  control: 'text',
128
- description: 'An error message to display.',
124
+ description: 'An error message to display on the `from` input.',
129
125
  table: {
130
126
  category: 'Props',
131
127
  type: {
@@ -155,7 +151,7 @@ const meta: Meta<typeof InputDateRangePicker> = {
155
151
  },
156
152
  isDisabled: {
157
153
  control: 'boolean',
158
- description: 'Disable the date picker.',
154
+ description: 'Disable the date picker and input fields.',
159
155
  table: {
160
156
  category: 'Props',
161
157
  type: {
@@ -165,7 +161,7 @@ const meta: Meta<typeof InputDateRangePicker> = {
165
161
  },
166
162
  month: {
167
163
  control: 'date',
168
- description: 'The month to display.',
164
+ description: 'The currently displayed month.',
169
165
  table: {
170
166
  category: 'Props',
171
167
  type: {
@@ -185,21 +181,22 @@ export default meta;
185
181
 
186
182
  type Story = StoryObj<typeof InputDateRangePicker>;
187
183
 
188
- export const SingleInput: Story = {
184
+ export const RangeInput: Story = {
189
185
  args: {
190
186
  id: 'date-picker',
191
- label: 'Pick a date:',
192
187
  inputPlaceholder: 'MM/DD/YYYY',
193
188
  inputIconName: 'calendar',
194
- isClearable: true,
195
189
  isDisabled: false,
196
- clearOnClose: false,
197
- errorMessage: '',
190
+ toErrorMessage: '',
191
+ fromErrorMessage: '',
198
192
  ariaLabel: 'Date Picker',
193
+ isOpen: false,
194
+ selected: { from: new Date(), to: new Date(new Date().getTime() + 5 * 24 * 60 * 60 * 1000) },
199
195
  },
200
196
  render: (args) => {
201
197
  const [{ selected }, updateArgs] = useArgs();
202
- const handleSelect = (date: Date | undefined) => {
198
+
199
+ const handleSelect = (date: DateRange | undefined) => {
203
200
  updateArgs({ selected: date });
204
201
  };
205
202
 
@@ -1,28 +1,29 @@
1
1
  import { useId, useState, useEffect } from 'react';
2
- import { format, isValid, parse } from 'date-fns';
2
+ import { isValid, parse } from 'date-fns';
3
3
  import { DatePicker } from '../datePicker/DatePicker';
4
4
  import { Input } from '../../input';
5
5
  import { IconName } from '@/types';
6
6
  import { DateRange } from 'react-day-picker';
7
7
  import { FloatUI } from '../../../floatUI';
8
+ import { Col, Row } from '../../../grid';
9
+ import { formatDateAsString } from '../inputDatePicker/helpers';
8
10
 
9
11
  interface InputDateRangePickerProps {
10
12
  ariaLabel: string;
11
13
  disableBeforeDate?: Date;
12
14
  disableAfterDate?: Date;
15
+ captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
13
16
  isDisabled?: boolean;
14
17
  id?: string;
15
- label?: string;
16
- onSelect?: (selected: Date | undefined) => void;
18
+ onSelect: (selected: DateRange | undefined) => void;
17
19
  month?: Date;
18
- selected?: Date | DateRange | Date[] | undefined;
20
+ selected?: DateRange | undefined;
19
21
  isOpen?: boolean;
20
- clearOnClose?: boolean;
21
22
  className?: string;
22
23
  inputIconName?: IconName;
23
- isClearable?: boolean;
24
24
  inputPlaceholder?: string;
25
- errorMessage?: string | undefined;
25
+ toErrorMessage?: string | undefined;
26
+ fromErrorMessage?: string | undefined;
26
27
  'data-testid'?: string;
27
28
  }
28
29
 
@@ -33,82 +34,115 @@ export function InputDateRangePicker(props: InputDateRangePickerProps) {
33
34
  isDisabled,
34
35
  disableBeforeDate,
35
36
  disableAfterDate,
37
+ captionLayout,
36
38
  month,
37
39
  id,
38
- label,
39
40
  onSelect,
40
41
  selected,
41
42
  isOpen,
42
43
  inputPlaceholder,
43
- clearOnClose,
44
44
  inputIconName,
45
- isClearable,
46
- errorMessage,
45
+ toErrorMessage,
46
+ fromErrorMessage,
47
47
  ...rest
48
48
  } = props;
49
49
 
50
50
  const inputId = useId();
51
51
 
52
- // Hold the month in state to control the calendar when the input changes
53
- const [localMonth, setLocalMonth] = useState(new Date());
54
-
55
- // Hold the selected date in state
56
- const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
52
+ // Hold the input values in state
53
+ const [localTextValueFrom, setLocalTextValueFrom] = useState<string>(
54
+ selected?.from ? formatDateAsString(selected.from) : '',
55
+ );
56
+ const [localTextValueTo, setLocalTextValueTo] = useState<string>(
57
+ selected?.to ? formatDateAsString(selected.to) : '',
58
+ );
57
59
 
58
- // Hold the input value in state
59
- const [inputValue, setInputValue] = useState('');
60
+ const [localMonth, setLocalMonth] = useState<Date>(selected?.from || new Date());
60
61
 
61
- const handleDayPickerSelect = (date: Date | undefined) => {
62
+ // When the day picker is selected, update the text values.
63
+ const handleDayPickerSelect = (date: DateRange | undefined) => {
62
64
  if (!date) {
63
- setInputValue('');
64
- setSelectedDate(undefined);
65
+ setLocalTextValueFrom('');
66
+ setLocalTextValueTo('');
67
+ onSelect(undefined);
65
68
  } else {
66
- setSelectedDate(date);
67
- setLocalMonth(date);
68
- setInputValue(format(date, 'MM/dd/yyyy'));
69
+ setLocalTextValueFrom(date.from ? formatDateAsString(date.from) : '');
70
+ setLocalTextValueTo(date.to ? formatDateAsString(date.to) : '');
71
+ onSelect(date);
69
72
  }
70
73
  };
71
74
 
72
- const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
73
- setInputValue(e.target.value); // keep the input value in sync
75
+ // When the text input is changed, update the selected date.
76
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>, type: 'from' | 'to') => {
77
+ const value = e.target.value;
78
+ if (type === 'from') {
79
+ setLocalTextValueFrom(value);
80
+ } else {
81
+ setLocalTextValueTo(value);
82
+ }
74
83
 
75
- const parsedDate = parse(e.target.value, 'MM/dd/yyyy', new Date());
84
+ const parsedDate = parse(value, 'MM/dd/yyyy', new Date());
76
85
 
77
86
  if (isValid(parsedDate)) {
78
- setSelectedDate(parsedDate);
79
- setLocalMonth(parsedDate);
87
+ onSelect({
88
+ from: type === 'from' ? parsedDate : selected?.from,
89
+ to: type === 'to' ? parsedDate : selected?.to,
90
+ });
80
91
  } else {
81
- setSelectedDate(undefined);
92
+ onSelect({
93
+ from: type === 'from' ? undefined : selected?.from,
94
+ to: type === 'to' ? undefined : selected?.to,
95
+ });
82
96
  }
83
97
  };
84
98
 
85
99
  // clear selection if clear on close is true
86
100
  useEffect(() => {
87
- if (!isOpen && clearOnClose) {
88
- setSelectedDate(undefined);
89
- setInputValue('');
101
+ if (!isOpen) {
102
+ onSelect(undefined);
103
+ setLocalTextValueFrom('');
104
+ setLocalTextValueTo('');
90
105
  }
91
- }, [isOpen, clearOnClose]);
106
+ }, [isOpen]);
92
107
 
93
108
  return (
94
109
  <FloatUI isOpen={isOpen} ariaLabel={ariaLabel}>
95
- <Input
96
- id={inputId}
97
- value={inputValue}
98
- placeholder={inputPlaceholder}
99
- isDisabled={isDisabled}
100
- iconName={inputIconName}
101
- isClearable={isClearable}
102
- onChange={handleInputChange}
103
- errorMessage={errorMessage}
104
- label={'Single Date Picker'}
105
- name={'Date Picker'}
106
- />
110
+ <Row>
111
+ <Col>
112
+ <Input
113
+ id={`${inputId}-from`}
114
+ value={localTextValueFrom}
115
+ placeholder={inputPlaceholder}
116
+ isDisabled={isDisabled}
117
+ iconName={inputIconName}
118
+ onChange={(e) => handleInputChange(e, 'from')}
119
+ errorMessage={fromErrorMessage}
120
+ label={'From Date'}
121
+ name={'From Date'}
122
+ data-testid="date-picker-from"
123
+ />
124
+ </Col>
125
+ <Col>
126
+ <Input
127
+ id={`${inputId}-to`}
128
+ value={localTextValueTo}
129
+ placeholder={inputPlaceholder}
130
+ isDisabled={isDisabled}
131
+ iconName={inputIconName}
132
+ onChange={(e) => handleInputChange(e, 'to')}
133
+ errorMessage={toErrorMessage}
134
+ label={'To Date'}
135
+ name={'To Date'}
136
+ data-testid="date-picker-to"
137
+ />
138
+ </Col>
139
+ </Row>
107
140
  <DatePicker
141
+ captionLayout={captionLayout}
108
142
  month={localMonth}
109
- onMonthChange={setLocalMonth}
110
- mode="single"
111
- selected={selectedDate}
143
+ onMonthChange={(date) => setLocalMonth(date)}
144
+ mode="range"
145
+ selected={selected}
112
146
  onSelect={handleDayPickerSelect}
113
147
  {...rest}
114
148
  />
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor, within } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { InputDateRangePicker } from '../InputDateRangePicker';
5
+ import { format } from 'date-fns';
6
+
7
+ const mockOnSelect = jest.fn();
8
+
9
+ describe('InputDateRangePicker', () => {
10
+ const RealDate = Date;
11
+
12
+ beforeAll(() => {
13
+ global.Date = class extends RealDate {
14
+ constructor() {
15
+ super('2024-01-01T00:00:00Z');
16
+ }
17
+ } as any;
18
+ Object.assign(global.Date, RealDate);
19
+ });
20
+
21
+ afterAll(() => {
22
+ global.Date = RealDate;
23
+ });
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ });
28
+
29
+ it('opens the date picker and selects a date', async () => {
30
+ render(
31
+ <InputDateRangePicker
32
+ ariaLabel="Input Date Range Picker"
33
+ data-testid="datepicker-testid"
34
+ captionLayout="label"
35
+ onSelect={mockOnSelect}
36
+ />,
37
+ );
38
+
39
+ const formInput = await screen.findByTestId('date-picker-from');
40
+ expect(formInput).toBeInTheDocument();
41
+
42
+ await userEvent.click(formInput);
43
+
44
+ const datepicker = await screen.findByTestId('datepicker-testid');
45
+ expect(datepicker).toBeVisible();
46
+
47
+ const dayButtons = await within(datepicker).findAllByRole('button');
48
+ expect(dayButtons.length).toBeGreaterThan(0);
49
+
50
+ const lastDayButton = dayButtons[dayButtons.length - 1];
51
+ await userEvent.click(lastDayButton);
52
+
53
+ const initialDate = format(new Date('2024-01-01T00:00:00Z'), 'MM/dd/yyyy');
54
+ await waitFor(() => {
55
+ expect(formInput).toHaveValue(initialDate);
56
+ });
57
+ });
58
+
59
+ it('updates the selected value in the dropdown to match user input', async () => {
60
+ render(
61
+ <InputDateRangePicker
62
+ ariaLabel="Input Date Range Picker"
63
+ captionLayout="label"
64
+ data-testid="datepicker-testid"
65
+ onSelect={mockOnSelect}
66
+ />,
67
+ );
68
+
69
+ const formInput = await screen.findByTestId('date-picker-from');
70
+ expect(formInput).toBeInTheDocument();
71
+
72
+ await userEvent.click(formInput);
73
+ await userEvent.clear(formInput);
74
+ await userEvent.type(formInput, '12/31/2023');
75
+
76
+ await waitFor(() => {
77
+ expect(formInput).toHaveValue('12/31/2023');
78
+ });
79
+
80
+ await userEvent.click(formInput); // Ensure the date picker is open
81
+
82
+ const datepicker = await screen.findByTestId('datepicker-testid');
83
+ expect(datepicker).toBeVisible();
84
+
85
+ const dayButtons = await within(datepicker).findAllByRole('button');
86
+ expect(dayButtons.length).toBeGreaterThan(0);
87
+
88
+ const lastDayButton = dayButtons[dayButtons.length - 1];
89
+ await userEvent.click(lastDayButton);
90
+
91
+ const initialDate = format(new Date('2024-01-01T00:00:00Z'), 'MM/dd/yyyy');
92
+ await waitFor(() => {
93
+ expect(formInput).toHaveValue(initialDate);
94
+ });
95
+ });
96
+ });
@@ -17,3 +17,4 @@ export { Menu } from './menu';
17
17
  export { DatePicker } from './forms/date/datePicker/DatePicker';
18
18
  export { IconTriggerDatePicker } from './forms/date/iconTriggerDatePicker';
19
19
  export { SingleInputDatePicker } from './forms/date/inputDatePicker';
20
+ export { InputDateRangePicker } from './forms/date/inputDateRangePicker';