@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.
- package/build/generated/iconTypes.ts +5 -0
- package/lib/components/forms/date/datePicker/DatePicker.d.ts +1 -2
- package/lib/components/forms/date/datePicker/__tests__/DatePicker.test.d.ts +1 -0
- package/lib/components/forms/date/datePicker/contants.d.ts +1 -1
- package/lib/components/forms/date/datePicker/index.d.ts +1 -0
- package/lib/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.d.ts +3 -3
- package/lib/components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.d.ts +1 -0
- package/lib/components/forms/date/inputDatePicker/SingleInputDatePicker.d.ts +5 -6
- package/lib/components/forms/date/inputDatePicker/__tests__/SingleInputDatePicker.test.d.ts +1 -0
- package/lib/components/forms/date/inputDatePicker/helpers.d.ts +1 -0
- package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +5 -6
- package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.d.ts +1 -1
- package/lib/components/forms/date/inputDateRangePicker/__tests__/InputDateRangePicker.test.d.ts +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/index.d.ts +8 -7
- package/lib/index.esm.js +48 -47
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +48 -47
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/floatUI/{FloatUI.test.tsx → __tests__/FloatUI.test.tsx} +3 -3
- package/src/components/forms/date/datePicker/DatePicker.tsx +2 -2
- package/src/components/forms/date/datePicker/__tests__/DatePicker.test.tsx +55 -0
- package/src/components/forms/date/datePicker/contants.ts +1 -1
- package/src/components/forms/date/datePicker/index.ts +1 -0
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.mdx +1 -1
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.stories.tsx +0 -10
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.tsx +9 -12
- package/src/components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.tsx +47 -127
- package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.stories.tsx +17 -3
- package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.tsx +31 -30
- package/src/components/forms/date/inputDatePicker/__tests__/SingleInputDatePicker.test.tsx +118 -0
- package/src/components/forms/date/inputDatePicker/helpers.ts +3 -0
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.mdx +9 -3
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.tsx +41 -44
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +83 -49
- package/src/components/forms/date/inputDateRangePicker/__tests__/InputDateRangePicker.test.tsx +96 -0
- package/src/components/index.ts +1 -0
- /package/lib/components/floatUI/{FloatUI.test.d.ts → __tests__/FloatUI.test.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { FloatUI } from '
|
|
3
|
-
import { Menu } from '
|
|
4
|
-
import { Button } from '
|
|
2
|
+
import { FloatUI } from '../FloatUI';
|
|
3
|
+
import { Menu } from '../../menu';
|
|
4
|
+
import { Button } from '../../button';
|
|
5
5
|
|
|
6
6
|
describe('FloatUI Component', () => {
|
|
7
7
|
it('does not display FloatUI content initially when rendered in uncontrolled mode', () => {
|
|
@@ -3,7 +3,7 @@ import { DateRange, DayPicker, Mode, OnSelectHandler } from 'react-day-picker';
|
|
|
3
3
|
import { DatePickerProps } from './types';
|
|
4
4
|
import { getCommonProps } from './contants';
|
|
5
5
|
|
|
6
|
-
export const DatePicker =
|
|
6
|
+
export const DatePicker = (props: DatePickerProps) => {
|
|
7
7
|
const {
|
|
8
8
|
mode = 'single',
|
|
9
9
|
className,
|
|
@@ -65,4 +65,4 @@ export const DatePicker = React.forwardRef<HTMLInputElement, DatePickerProps>((p
|
|
|
65
65
|
const modeProps = modeMap[mode];
|
|
66
66
|
|
|
67
67
|
return <DayPicker {...modeProps} {...commonProps} {...rest} />;
|
|
68
|
-
}
|
|
68
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { DatePicker } from '../DatePicker';
|
|
3
|
+
|
|
4
|
+
describe('DatePicker', () => {
|
|
5
|
+
describe('Single Date Picker', () => {
|
|
6
|
+
it('Renders the default mode for date picker (single)', () => {
|
|
7
|
+
render(<DatePicker data-testid="date-picker" />);
|
|
8
|
+
expect(screen.getByTestId('date-picker')).toHaveAttribute('data-mode', 'single');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('renders the date picker with a default of 2 months if no months are provided', () => {
|
|
12
|
+
render(<DatePicker data-testid="date-picker" />);
|
|
13
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders the date picker with a custom number of months', () => {
|
|
17
|
+
render(<DatePicker numberOfMonths={3} data-testid="date-picker" />);
|
|
18
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(3);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('Range Date Picker', () => {
|
|
23
|
+
it('Renders the range mode for date picker', () => {
|
|
24
|
+
render(<DatePicker mode="range" data-testid="date-picker" />);
|
|
25
|
+
expect(screen.getByTestId('date-picker')).toHaveAttribute('data-mode', 'range');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders the date range picker with a default of 2 months if no months are provided', () => {
|
|
29
|
+
render(<DatePicker mode="range" data-testid="date-picker" />);
|
|
30
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders the date range picker with a custom number of months', () => {
|
|
34
|
+
render(<DatePicker mode="range" numberOfMonths={3} data-testid="date-picker" />);
|
|
35
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(3);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Multiple Date Picker', () => {
|
|
40
|
+
it('Renders the multiple mode for date picker', () => {
|
|
41
|
+
render(<DatePicker mode="multiple" data-testid="date-picker" />);
|
|
42
|
+
expect(screen.getByTestId('date-picker')).toHaveAttribute('data-mode', 'multiple');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders the date picker with a default of 2 months if no months are provided', () => {
|
|
46
|
+
render(<DatePicker mode="multiple" data-testid="date-picker" />);
|
|
47
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('renders the date picker with a custom number of months', () => {
|
|
51
|
+
render(<DatePicker mode="multiple" numberOfMonths={3} data-testid="date-picker" />);
|
|
52
|
+
expect(document.getElementsByClassName('rdp-month')).toHaveLength(3);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -4,7 +4,7 @@ export const getCommonProps = (props: CommonProps) => ({
|
|
|
4
4
|
className: props.className,
|
|
5
5
|
id: props.id,
|
|
6
6
|
month: props.month,
|
|
7
|
-
captionLayout: props.captionLayout,
|
|
7
|
+
captionLayout: props.captionLayout ?? 'dropdown',
|
|
8
8
|
defaultMonth: props.defaultMonth,
|
|
9
9
|
startMonth: props.startMonth,
|
|
10
10
|
endMonth: props.endMonth,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DatePicker } from './DatePicker';
|
|
@@ -5,7 +5,7 @@ import { Row, Col } from '../../../grid/index';
|
|
|
5
5
|
<Meta title="Forms/DatePicker/IconTriggerDatePicker" name="IconTriggerDatePicker" />
|
|
6
6
|
|
|
7
7
|
# IconTriggerDatePicker
|
|
8
|
-
The
|
|
8
|
+
The Icon Trigger Date Picker is to be used when we need to open the datepicker with a simple icon and dont care about the user typing in a value. It leverages our DatePicker component inside of a popper window.
|
|
9
9
|
<Canvas of={IconTriggerDatePicker.Default} />
|
|
10
10
|
|
|
11
11
|
## Props
|
|
@@ -54,16 +54,6 @@ const meta: Meta<typeof IconTriggerDatePicker> = {
|
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
|
-
month: {
|
|
58
|
-
control: 'date',
|
|
59
|
-
description: 'The month to display.',
|
|
60
|
-
table: {
|
|
61
|
-
category: 'Props',
|
|
62
|
-
type: {
|
|
63
|
-
summary: 'Date',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
57
|
triggerIcon: {
|
|
68
58
|
control: 'text',
|
|
69
59
|
description: 'The icon to use as a trigger.',
|
|
@@ -12,14 +12,14 @@ interface Props {
|
|
|
12
12
|
isDisabled?: boolean;
|
|
13
13
|
id: string;
|
|
14
14
|
label?: string;
|
|
15
|
-
onSelect?: (selected: Date | DateRange |
|
|
16
|
-
|
|
17
|
-
selected?: Date | DateRange | Date[] | undefined;
|
|
15
|
+
onSelect?: (selected: Date | DateRange | undefined) => void;
|
|
16
|
+
selected?: Date | DateRange | undefined;
|
|
18
17
|
triggerIcon: IconName;
|
|
19
18
|
triggerIconSize: IconSizes;
|
|
20
19
|
isOpen?: boolean;
|
|
21
20
|
clearOnClose?: boolean;
|
|
22
21
|
className?: string;
|
|
22
|
+
initialMonth?: Date;
|
|
23
23
|
'data-testid'?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -30,7 +30,6 @@ export const IconTriggerDatePicker = (props: Props) => {
|
|
|
30
30
|
isDisabled,
|
|
31
31
|
disableBeforeDate,
|
|
32
32
|
disableAfterDate,
|
|
33
|
-
month,
|
|
34
33
|
id,
|
|
35
34
|
label,
|
|
36
35
|
onSelect,
|
|
@@ -40,27 +39,24 @@ export const IconTriggerDatePicker = (props: Props) => {
|
|
|
40
39
|
mode,
|
|
41
40
|
isOpen,
|
|
42
41
|
clearOnClose,
|
|
42
|
+
initialMonth,
|
|
43
43
|
...rest
|
|
44
44
|
} = props;
|
|
45
45
|
|
|
46
|
-
const [
|
|
47
|
-
selected || undefined,
|
|
48
|
-
);
|
|
46
|
+
const [localMonth, setLocalMonth] = useState<Date>(initialMonth ?? new Date());
|
|
49
47
|
|
|
50
48
|
const handleSelect: OnSelectHandler<Date> = (date) => {
|
|
51
49
|
if (!date) {
|
|
52
50
|
onSelect && onSelect(undefined);
|
|
53
|
-
setLocalSelected(undefined);
|
|
54
51
|
} else {
|
|
55
52
|
onSelect && onSelect(date);
|
|
56
|
-
setLocalSelected(date);
|
|
57
53
|
}
|
|
58
54
|
};
|
|
59
55
|
|
|
60
56
|
// clear selection if clear on close is true
|
|
61
57
|
useEffect(() => {
|
|
62
58
|
if (!isOpen && clearOnClose) {
|
|
63
|
-
|
|
59
|
+
onSelect && onSelect(undefined);
|
|
64
60
|
}
|
|
65
61
|
}, [isOpen, clearOnClose]);
|
|
66
62
|
|
|
@@ -77,11 +73,12 @@ export const IconTriggerDatePicker = (props: Props) => {
|
|
|
77
73
|
<DatePicker
|
|
78
74
|
isDisabled={isDisabled}
|
|
79
75
|
mode={mode}
|
|
80
|
-
|
|
81
|
-
selected={localSelected as Date | DateRange | undefined}
|
|
76
|
+
selected={selected}
|
|
82
77
|
onSelect={handleSelect}
|
|
78
|
+
month={localMonth}
|
|
83
79
|
startMonth={disableBeforeDate}
|
|
84
80
|
endMonth={disableAfterDate}
|
|
81
|
+
onMonthChange={setLocalMonth}
|
|
85
82
|
{...rest}
|
|
86
83
|
/>
|
|
87
84
|
</FloatUI>
|
package/src/components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.tsx
CHANGED
|
@@ -1,127 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// const day28 = screen.getByText('28');
|
|
50
|
-
// await userEvent.click(day28);
|
|
51
|
-
// expect(mockOnChange).toHaveBeenCalledWith(
|
|
52
|
-
// new Date(today.getFullYear(), today.getMonth(), 28),
|
|
53
|
-
// );
|
|
54
|
-
// });
|
|
55
|
-
|
|
56
|
-
// it('closes date picker on single selection', async () => {
|
|
57
|
-
// await userEvent.click(trigger);
|
|
58
|
-
// const datePickerDialogContainer = screen.getByTestId('datepicker-dialog');
|
|
59
|
-
// expect(datePickerDialogContainer).toBeVisible();
|
|
60
|
-
|
|
61
|
-
// const day28 = screen.getByText('28');
|
|
62
|
-
// await userEvent.click(day28);
|
|
63
|
-
// expect(mockOnChange).toHaveBeenCalledWith(
|
|
64
|
-
// new Date(today.getFullYear(), today.getMonth(), 28),
|
|
65
|
-
// );
|
|
66
|
-
// expect(datePickerDialogContainer).not.toBeVisible();
|
|
67
|
-
// });
|
|
68
|
-
// });
|
|
69
|
-
|
|
70
|
-
// describe('range', () => {
|
|
71
|
-
// beforeEach(() => {
|
|
72
|
-
// render(
|
|
73
|
-
// <NoInputDatePicker
|
|
74
|
-
// id={'date-picker'}
|
|
75
|
-
// label={'Pick a date'}
|
|
76
|
-
// value={today}
|
|
77
|
-
// isRangePicker={true}
|
|
78
|
-
// triggerIcon="calendar"
|
|
79
|
-
// triggerIconSize={[20]}
|
|
80
|
-
// onChange={(date: Date | DateRange | undefined) => {
|
|
81
|
-
// mockOnChange(date);
|
|
82
|
-
// }}
|
|
83
|
-
// />,
|
|
84
|
-
// );
|
|
85
|
-
|
|
86
|
-
// trigger = screen.getByTestId('datepicker-trigger-for-date-picker');
|
|
87
|
-
// });
|
|
88
|
-
|
|
89
|
-
// it('shows ranged date picker', async () => {
|
|
90
|
-
// await userEvent.click(trigger);
|
|
91
|
-
// expect(screen.getByTestId('datepicker-dialog')).toBeVisible();
|
|
92
|
-
// expect(screen.getByTestId('range-datepicker')).toBeVisible();
|
|
93
|
-
// });
|
|
94
|
-
|
|
95
|
-
// it('emits the right date picked for range', async () => {
|
|
96
|
-
// await userEvent.click(trigger);
|
|
97
|
-
// const datePickerDialogContainer = screen.getByTestId('datepicker-dialog');
|
|
98
|
-
// expect(datePickerDialogContainer).toBeVisible();
|
|
99
|
-
|
|
100
|
-
// const day27 = screen.getByText('27');
|
|
101
|
-
// await userEvent.click(day27);
|
|
102
|
-
// const day28 = screen.getByText('28');
|
|
103
|
-
// await userEvent.click(day28);
|
|
104
|
-
|
|
105
|
-
// expect(mockOnChange).toHaveBeenCalledWith({
|
|
106
|
-
// from: new Date(today.getFullYear(), today.getMonth(), 27),
|
|
107
|
-
// to: new Date(today.getFullYear(), today.getMonth(), 28),
|
|
108
|
-
// });
|
|
109
|
-
// });
|
|
110
|
-
// it('closes date picker on both ranged selection', async () => {
|
|
111
|
-
// await userEvent.click(trigger);
|
|
112
|
-
// const datePickerDialogContainer = screen.getByTestId('datepicker-dialog');
|
|
113
|
-
// expect(datePickerDialogContainer).toBeVisible();
|
|
114
|
-
|
|
115
|
-
// const day27 = screen.getByText('27');
|
|
116
|
-
// await userEvent.click(day27);
|
|
117
|
-
// const day28 = screen.getByText('28');
|
|
118
|
-
// await userEvent.click(day28);
|
|
119
|
-
|
|
120
|
-
// expect(mockOnChange).toHaveBeenCalledWith({
|
|
121
|
-
// from: new Date(today.getFullYear(), today.getMonth(), 27),
|
|
122
|
-
// to: new Date(today.getFullYear(), today.getMonth(), 28),
|
|
123
|
-
// });
|
|
124
|
-
// expect(datePickerDialogContainer).not.toBeVisible();
|
|
125
|
-
// });
|
|
126
|
-
// });
|
|
127
|
-
// });
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { IconTriggerDatePicker } from '../IconTriggerDatePicker';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
|
|
5
|
+
describe('DatePicker', () => {
|
|
6
|
+
it('The date picker opens when the icon is clicked', async () => {
|
|
7
|
+
render(
|
|
8
|
+
<IconTriggerDatePicker
|
|
9
|
+
ariaLabel={''}
|
|
10
|
+
id={'date-picker'}
|
|
11
|
+
triggerIcon={'checkbox'}
|
|
12
|
+
triggerIconSize={'xs'}
|
|
13
|
+
data-testid={'date-picker-testid'}
|
|
14
|
+
/>,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const icon = screen.getByTestId('datepicker-trigger-for-date-picker');
|
|
18
|
+
expect(icon).toBeInTheDocument();
|
|
19
|
+
await userEvent.click(icon);
|
|
20
|
+
expect(screen.getByTestId('date-picker-testid')).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('handles onSelect with the correct value', async () => {
|
|
24
|
+
const onSelect = jest.fn();
|
|
25
|
+
render(
|
|
26
|
+
<IconTriggerDatePicker
|
|
27
|
+
ariaLabel={''}
|
|
28
|
+
id={'date-picker'}
|
|
29
|
+
triggerIcon={'checkbox'}
|
|
30
|
+
triggerIconSize={'xs'}
|
|
31
|
+
data-testid={'date-picker-testid'}
|
|
32
|
+
onSelect={onSelect}
|
|
33
|
+
/>,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const icon = screen.getByTestId('datepicker-trigger-for-date-picker');
|
|
37
|
+
expect(icon).toBeInTheDocument();
|
|
38
|
+
await userEvent.click(icon);
|
|
39
|
+
expect(screen.getByTestId('date-picker-testid')).toBeInTheDocument();
|
|
40
|
+
expect(onSelect).toHaveBeenCalledTimes(0);
|
|
41
|
+
const day = screen.getAllByRole('button', { name: /day/i })[0];
|
|
42
|
+
expect(day).toBeInTheDocument();
|
|
43
|
+
await userEvent.click(day);
|
|
44
|
+
// no need to be more specific as it will cause issues with pipelines having different time zones. This was verified manually at the time of writing
|
|
45
|
+
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { SingleInputDatePicker } from './SingleInputDatePicker';
|
|
2
|
+
import { SingleInputDatePicker, SingleInputDatePickerProps } from './SingleInputDatePicker';
|
|
3
3
|
import { useArgs } from '@storybook/preview-api';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof SingleInputDatePicker> = {
|
|
@@ -23,6 +23,18 @@ const meta: Meta<typeof SingleInputDatePicker> = {
|
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
|
+
captionLayout: {
|
|
27
|
+
control: 'select',
|
|
28
|
+
options: ['dropdown', 'dropdown-months', 'dropdown-years', 'label'],
|
|
29
|
+
description:
|
|
30
|
+
'The layout of the caption. Enables you to add or remove the dropdown navigation for months/years',
|
|
31
|
+
table: {
|
|
32
|
+
category: 'Props',
|
|
33
|
+
type: {
|
|
34
|
+
summary: 'dropdown | dropdown-months | dropdown-years | label',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
26
38
|
className: {
|
|
27
39
|
control: false,
|
|
28
40
|
description: 'Accepts a CSS class name',
|
|
@@ -163,7 +175,7 @@ const meta: Meta<typeof SingleInputDatePicker> = {
|
|
|
163
175
|
},
|
|
164
176
|
},
|
|
165
177
|
},
|
|
166
|
-
|
|
178
|
+
initialMonth: {
|
|
167
179
|
control: 'date',
|
|
168
180
|
description: 'The month to display.',
|
|
169
181
|
table: {
|
|
@@ -196,9 +208,11 @@ export const SingleInput: Story = {
|
|
|
196
208
|
clearOnClose: false,
|
|
197
209
|
errorMessage: '',
|
|
198
210
|
ariaLabel: 'Date Picker',
|
|
211
|
+
selected: new Date(),
|
|
199
212
|
},
|
|
200
213
|
render: (args) => {
|
|
201
|
-
const [{ selected }, updateArgs] = useArgs();
|
|
214
|
+
const [{ selected }, updateArgs] = useArgs<SingleInputDatePickerProps>();
|
|
215
|
+
|
|
202
216
|
const handleSelect = (date: Date | undefined) => {
|
|
203
217
|
updateArgs({ selected: date });
|
|
204
218
|
};
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { useId, useState, useEffect } from 'react';
|
|
2
|
-
import {
|
|
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
|
-
import { DateRange } from 'react-day-picker';
|
|
7
6
|
import { FloatUI } from '../../../floatUI';
|
|
7
|
+
import { formatDateAsString } from './helpers';
|
|
8
8
|
|
|
9
|
-
interface SingleInputDatePickerProps {
|
|
9
|
+
export interface SingleInputDatePickerProps {
|
|
10
10
|
ariaLabel: string;
|
|
11
11
|
disableBeforeDate?: Date;
|
|
12
12
|
disableAfterDate?: Date;
|
|
13
13
|
isDisabled?: boolean;
|
|
14
|
+
captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
|
|
14
15
|
id?: string;
|
|
15
16
|
label?: string;
|
|
16
|
-
onSelect
|
|
17
|
-
|
|
18
|
-
selected?: Date
|
|
17
|
+
onSelect: (selected: Date | undefined) => void;
|
|
18
|
+
initialMonth?: Date;
|
|
19
|
+
selected?: Date;
|
|
19
20
|
isOpen?: boolean;
|
|
20
21
|
clearOnClose?: boolean;
|
|
21
22
|
className?: string;
|
|
@@ -33,10 +34,10 @@ export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
|
|
|
33
34
|
isDisabled,
|
|
34
35
|
disableBeforeDate,
|
|
35
36
|
disableAfterDate,
|
|
36
|
-
|
|
37
|
+
captionLayout,
|
|
38
|
+
initialMonth,
|
|
37
39
|
id,
|
|
38
40
|
label,
|
|
39
|
-
onSelect,
|
|
40
41
|
selected,
|
|
41
42
|
isOpen,
|
|
42
43
|
inputPlaceholder,
|
|
@@ -44,49 +45,48 @@ export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
|
|
|
44
45
|
inputIconName,
|
|
45
46
|
isClearable,
|
|
46
47
|
errorMessage,
|
|
48
|
+
onSelect,
|
|
47
49
|
...rest
|
|
48
50
|
} = props;
|
|
49
51
|
|
|
50
52
|
const inputId = useId();
|
|
51
53
|
|
|
52
|
-
//
|
|
53
|
-
const [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
|
|
54
|
+
// The text value is assumed to be unneeded by the consumer.
|
|
55
|
+
const [localTextValue, setLocalTextValue] = useState<string>(
|
|
56
|
+
selected ? formatDateAsString(selected) : '',
|
|
57
|
+
);
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
const [inputValue, setInputValue] = useState('');
|
|
59
|
+
const [localMonth, setLocalMonth] = useState<Date>(initialMonth ?? selected ?? new Date());
|
|
60
60
|
|
|
61
|
+
// When the day picker is selected, update the text value.
|
|
61
62
|
const handleDayPickerSelect = (date: Date | undefined) => {
|
|
62
63
|
if (!date) {
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
setLocalTextValue('');
|
|
65
|
+
onSelect(undefined);
|
|
65
66
|
} else {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
setInputValue(format(date, 'MM/dd/yyyy'));
|
|
67
|
+
setLocalTextValue(formatDateAsString(date));
|
|
68
|
+
onSelect(date);
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
// When the text input is changed, update the selected date.
|
|
72
73
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
73
|
-
|
|
74
|
+
setLocalTextValue(e.target.value); // keep the input value in sync
|
|
74
75
|
|
|
75
76
|
const parsedDate = parse(e.target.value, 'MM/dd/yyyy', new Date());
|
|
76
77
|
|
|
77
78
|
if (isValid(parsedDate)) {
|
|
78
|
-
|
|
79
|
-
setLocalMonth(parsedDate);
|
|
79
|
+
onSelect(parsedDate);
|
|
80
80
|
} else {
|
|
81
|
-
|
|
81
|
+
onSelect(undefined);
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
// clear selection if clear on close is true
|
|
86
86
|
useEffect(() => {
|
|
87
87
|
if (!isOpen && clearOnClose) {
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
onSelect(undefined);
|
|
89
|
+
setLocalTextValue('');
|
|
90
90
|
}
|
|
91
91
|
}, [isOpen, clearOnClose]);
|
|
92
92
|
|
|
@@ -94,7 +94,7 @@ export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
|
|
|
94
94
|
<FloatUI isOpen={isOpen} ariaLabel={ariaLabel}>
|
|
95
95
|
<Input
|
|
96
96
|
id={inputId}
|
|
97
|
-
value={
|
|
97
|
+
value={localTextValue}
|
|
98
98
|
placeholder={inputPlaceholder}
|
|
99
99
|
isDisabled={isDisabled}
|
|
100
100
|
iconName={inputIconName}
|
|
@@ -105,11 +105,12 @@ export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
|
|
|
105
105
|
name={'Date Picker'}
|
|
106
106
|
/>
|
|
107
107
|
<DatePicker
|
|
108
|
-
|
|
109
|
-
onMonthChange={setLocalMonth}
|
|
108
|
+
captionLayout={captionLayout}
|
|
110
109
|
mode="single"
|
|
111
|
-
selected={
|
|
110
|
+
selected={selected}
|
|
112
111
|
onSelect={handleDayPickerSelect}
|
|
112
|
+
month={localMonth}
|
|
113
|
+
onMonthChange={setLocalMonth}
|
|
113
114
|
{...rest}
|
|
114
115
|
/>
|
|
115
116
|
</FloatUI>
|