@indico-data/design-system 2.36.4 → 2.38.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.
- package/lib/index.css +40 -408
- package/lib/index.d.ts +122 -84
- package/lib/index.esm.css +40 -408
- package/lib/index.esm.js +15631 -16559
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +14165 -15092
- package/lib/index.js.map +1 -1
- package/lib/src/components/forms/date/datePicker/DatePicker.d.ts +3 -0
- package/lib/src/components/forms/date/datePicker/DatePicker.stories.d.ts +9 -0
- package/lib/src/components/forms/date/datePicker/contants.d.ts +21 -0
- package/lib/src/components/forms/date/datePicker/types.d.ts +48 -0
- package/lib/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.d.ts +22 -0
- package/lib/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.stories.d.ts +6 -0
- package/lib/src/components/forms/date/iconTriggerDatePicker/index.d.ts +1 -0
- package/lib/src/components/forms/date/inputDatePicker/SingleInputDatePicker.d.ts +23 -0
- package/lib/src/components/forms/date/inputDatePicker/SingleInputDatePicker.stories.d.ts +6 -0
- package/lib/src/components/forms/date/inputDatePicker/index.d.ts +1 -0
- package/lib/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +23 -0
- package/lib/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.d.ts +6 -0
- package/lib/src/components/forms/date/inputDateRangePicker/index.d.ts +1 -0
- package/lib/src/components/forms/input/Input.d.ts +1 -0
- package/lib/src/components/index.d.ts +3 -0
- package/lib/src/components/skeleton/Skeleton.d.ts +2 -2
- package/lib/src/components/skeleton/Skeleton.stories.d.ts +1 -0
- package/lib/src/index.d.ts +4 -1
- package/lib/src/legacy/components/index.d.ts +1 -1
- package/lib/src/legacy/components/inputs/index.d.ts +0 -2
- package/package.json +2 -2
- package/src/components/forms/date/datePicker/DatePicker.mdx +41 -0
- package/src/components/forms/date/datePicker/DatePicker.stories.tsx +307 -0
- package/src/components/forms/date/datePicker/DatePicker.tsx +68 -0
- package/src/components/forms/date/datePicker/contants.ts +22 -0
- package/src/components/forms/date/datePicker/styles/DatePicker.scss +85 -0
- package/src/components/forms/date/datePicker/types.ts +59 -0
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.mdx +17 -0
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.stories.tsx +201 -0
- package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.tsx +89 -0
- package/src/components/forms/date/iconTriggerDatePicker/index.ts +1 -0
- package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.mdx +18 -0
- package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.stories.tsx +208 -0
- package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.tsx +117 -0
- package/src/components/forms/date/inputDatePicker/index.ts +1 -0
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.mdx +18 -0
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.tsx +208 -0
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +117 -0
- package/src/components/forms/date/inputDateRangePicker/index.ts +1 -0
- package/src/components/forms/input/Input.tsx +3 -0
- package/src/components/index.ts +3 -0
- package/src/components/skeleton/Skeleton.stories.tsx +24 -4
- package/src/components/skeleton/Skeleton.tsx +4 -4
- package/src/index.ts +3 -2
- package/src/legacy/components/index.ts +0 -2
- package/src/legacy/components/inputs/index.ts +0 -2
- package/src/styles/index.scss +2 -1
- package/lib/src/legacy/components/inputs/DatePicker/DatePicker.d.ts +0 -15
- package/lib/src/legacy/components/inputs/DatePicker/DatePicker.stories.d.ts +0 -6
- package/lib/src/legacy/components/inputs/DatePicker/DatePicker.styles.d.ts +0 -1
- package/lib/src/legacy/components/inputs/DatePicker/index.d.ts +0 -1
- package/lib/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.d.ts +0 -21
- package/lib/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.stories.d.ts +0 -7
- package/lib/src/legacy/components/inputs/NoInputDatePicker/index.d.ts +0 -1
- package/src/legacy/components/inputs/DatePicker/DatePicker.stories.tsx +0 -30
- package/src/legacy/components/inputs/DatePicker/DatePicker.styles.ts +0 -437
- package/src/legacy/components/inputs/DatePicker/DatePicker.tsx +0 -165
- package/src/legacy/components/inputs/DatePicker/index.ts +0 -1
- package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.scss +0 -441
- package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.stories.tsx +0 -52
- package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.tsx +0 -245
- package/src/legacy/components/inputs/NoInputDatePicker/index.ts +0 -1
- /package/src/{legacy/components/inputs/NoInputDatePicker/__tests__/NoInputDatePicker.test.tsx → components/forms/date/iconTriggerDatePicker/__tests__/IconTriggerDatePicker.test.tsx} +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { SingleInputDatePicker } from './SingleInputDatePicker';
|
|
3
|
+
import { useArgs } from '@storybook/preview-api';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof SingleInputDatePicker> = {
|
|
6
|
+
component: SingleInputDatePicker,
|
|
7
|
+
title: 'Forms/DatePicker/SingleInputDatePicker',
|
|
8
|
+
decorators: [
|
|
9
|
+
(Story) => (
|
|
10
|
+
<div style={{ height: '400px' }}>
|
|
11
|
+
<Story />
|
|
12
|
+
</div>
|
|
13
|
+
),
|
|
14
|
+
],
|
|
15
|
+
argTypes: {
|
|
16
|
+
ariaLabel: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'A label for assistive technologies.',
|
|
19
|
+
table: {
|
|
20
|
+
category: 'Props',
|
|
21
|
+
type: {
|
|
22
|
+
summary: 'string',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
className: {
|
|
27
|
+
control: false,
|
|
28
|
+
description: 'Accepts a CSS class name',
|
|
29
|
+
table: {
|
|
30
|
+
category: 'Props',
|
|
31
|
+
type: {
|
|
32
|
+
summary: 'string',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
id: {
|
|
37
|
+
control: 'text',
|
|
38
|
+
description: 'The id of the input field.',
|
|
39
|
+
table: {
|
|
40
|
+
category: 'Props',
|
|
41
|
+
type: {
|
|
42
|
+
summary: 'string',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
label: {
|
|
47
|
+
control: 'text',
|
|
48
|
+
description: 'The label for the input field.',
|
|
49
|
+
table: {
|
|
50
|
+
category: 'Props',
|
|
51
|
+
type: {
|
|
52
|
+
summary: 'string',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
onSelect: {
|
|
57
|
+
action: 'selected',
|
|
58
|
+
description: 'Callback function that is called when a date is selected.',
|
|
59
|
+
table: {
|
|
60
|
+
category: 'Events',
|
|
61
|
+
type: {
|
|
62
|
+
summary: 'function',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
selected: {
|
|
67
|
+
control: 'date',
|
|
68
|
+
description: 'The selected date.',
|
|
69
|
+
table: {
|
|
70
|
+
category: 'Props',
|
|
71
|
+
type: {
|
|
72
|
+
summary: 'Date',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
inputPlaceholder: {
|
|
77
|
+
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.',
|
|
89
|
+
table: {
|
|
90
|
+
category: 'Props',
|
|
91
|
+
type: {
|
|
92
|
+
summary: 'string',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
isClearable: {
|
|
97
|
+
control: 'boolean',
|
|
98
|
+
description: 'Whether the input field should be clearable.',
|
|
99
|
+
table: {
|
|
100
|
+
category: 'Props',
|
|
101
|
+
type: {
|
|
102
|
+
summary: 'boolean',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
isOpen: {
|
|
107
|
+
control: false,
|
|
108
|
+
description: 'Whether the date picker is open.',
|
|
109
|
+
table: {
|
|
110
|
+
category: 'Props',
|
|
111
|
+
type: {
|
|
112
|
+
summary: 'boolean',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
clearOnClose: {
|
|
117
|
+
control: 'boolean',
|
|
118
|
+
description: 'Whether the input field should be cleared when the date picker is closed.',
|
|
119
|
+
table: {
|
|
120
|
+
category: 'Props',
|
|
121
|
+
type: {
|
|
122
|
+
summary: 'boolean',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
errorMessage: {
|
|
127
|
+
control: 'text',
|
|
128
|
+
description: 'An error message to display.',
|
|
129
|
+
table: {
|
|
130
|
+
category: 'Props',
|
|
131
|
+
type: {
|
|
132
|
+
summary: 'string',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
disableAfterDate: {
|
|
137
|
+
control: 'date',
|
|
138
|
+
description: 'Disable dates after this date.',
|
|
139
|
+
table: {
|
|
140
|
+
category: 'Props',
|
|
141
|
+
type: {
|
|
142
|
+
summary: 'Date',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
disableBeforeDate: {
|
|
147
|
+
control: 'date',
|
|
148
|
+
description: 'Disable dates before this date.',
|
|
149
|
+
table: {
|
|
150
|
+
category: 'Props',
|
|
151
|
+
type: {
|
|
152
|
+
summary: 'Date',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
isDisabled: {
|
|
157
|
+
control: 'boolean',
|
|
158
|
+
description: 'Disable the date picker.',
|
|
159
|
+
table: {
|
|
160
|
+
category: 'Props',
|
|
161
|
+
type: {
|
|
162
|
+
summary: 'boolean',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
month: {
|
|
167
|
+
control: 'date',
|
|
168
|
+
description: 'The month to display.',
|
|
169
|
+
table: {
|
|
170
|
+
category: 'Props',
|
|
171
|
+
type: {
|
|
172
|
+
summary: 'Date',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
'data-testid': {
|
|
177
|
+
table: {
|
|
178
|
+
disable: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default meta;
|
|
185
|
+
|
|
186
|
+
type Story = StoryObj<typeof SingleInputDatePicker>;
|
|
187
|
+
|
|
188
|
+
export const SingleInput: Story = {
|
|
189
|
+
args: {
|
|
190
|
+
id: 'date-picker',
|
|
191
|
+
label: 'Pick a date:',
|
|
192
|
+
inputPlaceholder: 'MM/DD/YYYY',
|
|
193
|
+
inputIconName: 'calendar',
|
|
194
|
+
isClearable: true,
|
|
195
|
+
isDisabled: false,
|
|
196
|
+
clearOnClose: false,
|
|
197
|
+
errorMessage: '',
|
|
198
|
+
ariaLabel: 'Date Picker',
|
|
199
|
+
},
|
|
200
|
+
render: (args) => {
|
|
201
|
+
const [{ selected }, updateArgs] = useArgs();
|
|
202
|
+
const handleSelect = (date: Date | undefined) => {
|
|
203
|
+
updateArgs({ selected: date });
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return <SingleInputDatePicker {...args} selected={selected} onSelect={handleSelect} />;
|
|
207
|
+
},
|
|
208
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useId, useState, useEffect } from 'react';
|
|
2
|
+
import { format, isValid, parse } from 'date-fns';
|
|
3
|
+
import { DatePicker } from '../datePicker/DatePicker';
|
|
4
|
+
import { Input } from '../../input';
|
|
5
|
+
import { IconName } from '@/types';
|
|
6
|
+
import { DateRange } from 'react-day-picker';
|
|
7
|
+
import { FloatUI } from '../../../floatUI';
|
|
8
|
+
|
|
9
|
+
interface SingleInputDatePickerProps {
|
|
10
|
+
ariaLabel: string;
|
|
11
|
+
disableBeforeDate?: Date;
|
|
12
|
+
disableAfterDate?: Date;
|
|
13
|
+
isDisabled?: boolean;
|
|
14
|
+
id?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
onSelect?: (selected: Date | undefined) => void;
|
|
17
|
+
month?: Date;
|
|
18
|
+
selected?: Date | DateRange | Date[] | undefined;
|
|
19
|
+
isOpen?: boolean;
|
|
20
|
+
clearOnClose?: boolean;
|
|
21
|
+
className?: string;
|
|
22
|
+
inputIconName?: IconName;
|
|
23
|
+
isClearable?: boolean;
|
|
24
|
+
inputPlaceholder?: string;
|
|
25
|
+
errorMessage?: string | undefined;
|
|
26
|
+
'data-testid'?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
|
|
30
|
+
const {
|
|
31
|
+
ariaLabel,
|
|
32
|
+
className,
|
|
33
|
+
isDisabled,
|
|
34
|
+
disableBeforeDate,
|
|
35
|
+
disableAfterDate,
|
|
36
|
+
month,
|
|
37
|
+
id,
|
|
38
|
+
label,
|
|
39
|
+
onSelect,
|
|
40
|
+
selected,
|
|
41
|
+
isOpen,
|
|
42
|
+
inputPlaceholder,
|
|
43
|
+
clearOnClose,
|
|
44
|
+
inputIconName,
|
|
45
|
+
isClearable,
|
|
46
|
+
errorMessage,
|
|
47
|
+
...rest
|
|
48
|
+
} = props;
|
|
49
|
+
|
|
50
|
+
const inputId = useId();
|
|
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);
|
|
57
|
+
|
|
58
|
+
// Hold the input value in state
|
|
59
|
+
const [inputValue, setInputValue] = useState('');
|
|
60
|
+
|
|
61
|
+
const handleDayPickerSelect = (date: Date | undefined) => {
|
|
62
|
+
if (!date) {
|
|
63
|
+
setInputValue('');
|
|
64
|
+
setSelectedDate(undefined);
|
|
65
|
+
} else {
|
|
66
|
+
setSelectedDate(date);
|
|
67
|
+
setLocalMonth(date);
|
|
68
|
+
setInputValue(format(date, 'MM/dd/yyyy'));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
73
|
+
setInputValue(e.target.value); // keep the input value in sync
|
|
74
|
+
|
|
75
|
+
const parsedDate = parse(e.target.value, 'MM/dd/yyyy', new Date());
|
|
76
|
+
|
|
77
|
+
if (isValid(parsedDate)) {
|
|
78
|
+
setSelectedDate(parsedDate);
|
|
79
|
+
setLocalMonth(parsedDate);
|
|
80
|
+
} else {
|
|
81
|
+
setSelectedDate(undefined);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// clear selection if clear on close is true
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!isOpen && clearOnClose) {
|
|
88
|
+
setSelectedDate(undefined);
|
|
89
|
+
setInputValue('');
|
|
90
|
+
}
|
|
91
|
+
}, [isOpen, clearOnClose]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<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
|
+
/>
|
|
107
|
+
<DatePicker
|
|
108
|
+
month={localMonth}
|
|
109
|
+
onMonthChange={setLocalMonth}
|
|
110
|
+
mode="single"
|
|
111
|
+
selected={selectedDate}
|
|
112
|
+
onSelect={handleDayPickerSelect}
|
|
113
|
+
{...rest}
|
|
114
|
+
/>
|
|
115
|
+
</FloatUI>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SingleInputDatePicker } from './SingleInputDatePicker';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as InputDateRangePicker from './InputDateRangePicker.stories';
|
|
3
|
+
import { Row, Col } from '../../../grid/index';
|
|
4
|
+
|
|
5
|
+
<Meta title="Forms/DatePicker/InputDateRangePicker" name="InputDateRangePicker" />
|
|
6
|
+
|
|
7
|
+
# Input Date Range Picker
|
|
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
|
+
|
|
10
|
+
<Canvas of={InputDateRangePicker.SingleInput} />
|
|
11
|
+
|
|
12
|
+
## Props
|
|
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} />
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 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.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { InputDateRangePicker } from './InputDateRangePicker';
|
|
3
|
+
import { useArgs } from '@storybook/preview-api';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof InputDateRangePicker> = {
|
|
6
|
+
component: InputDateRangePicker,
|
|
7
|
+
title: 'Forms/DatePicker/InputDateRangePicker',
|
|
8
|
+
decorators: [
|
|
9
|
+
(Story) => (
|
|
10
|
+
<div style={{ height: '400px' }}>
|
|
11
|
+
<Story />
|
|
12
|
+
</div>
|
|
13
|
+
),
|
|
14
|
+
],
|
|
15
|
+
argTypes: {
|
|
16
|
+
ariaLabel: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'A label for assistive technologies.',
|
|
19
|
+
table: {
|
|
20
|
+
category: 'Props',
|
|
21
|
+
type: {
|
|
22
|
+
summary: 'string',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
className: {
|
|
27
|
+
control: false,
|
|
28
|
+
description: 'Accepts a CSS class name',
|
|
29
|
+
table: {
|
|
30
|
+
category: 'Props',
|
|
31
|
+
type: {
|
|
32
|
+
summary: 'string',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
id: {
|
|
37
|
+
control: 'text',
|
|
38
|
+
description: 'The id of the input field.',
|
|
39
|
+
table: {
|
|
40
|
+
category: 'Props',
|
|
41
|
+
type: {
|
|
42
|
+
summary: 'string',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
label: {
|
|
47
|
+
control: 'text',
|
|
48
|
+
description: 'The label for the input field.',
|
|
49
|
+
table: {
|
|
50
|
+
category: 'Props',
|
|
51
|
+
type: {
|
|
52
|
+
summary: 'string',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
onSelect: {
|
|
57
|
+
action: 'selected',
|
|
58
|
+
description: 'Callback function that is called when a date is selected.',
|
|
59
|
+
table: {
|
|
60
|
+
category: 'Events',
|
|
61
|
+
type: {
|
|
62
|
+
summary: 'function',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
selected: {
|
|
67
|
+
control: 'date',
|
|
68
|
+
description: 'The selected date.',
|
|
69
|
+
table: {
|
|
70
|
+
category: 'Props',
|
|
71
|
+
type: {
|
|
72
|
+
summary: 'Date',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
inputPlaceholder: {
|
|
77
|
+
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.',
|
|
89
|
+
table: {
|
|
90
|
+
category: 'Props',
|
|
91
|
+
type: {
|
|
92
|
+
summary: 'string',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
isClearable: {
|
|
97
|
+
control: 'boolean',
|
|
98
|
+
description: 'Whether the input field should be clearable.',
|
|
99
|
+
table: {
|
|
100
|
+
category: 'Props',
|
|
101
|
+
type: {
|
|
102
|
+
summary: 'boolean',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
isOpen: {
|
|
107
|
+
control: false,
|
|
108
|
+
description: 'Whether the date picker is open.',
|
|
109
|
+
table: {
|
|
110
|
+
category: 'Props',
|
|
111
|
+
type: {
|
|
112
|
+
summary: 'boolean',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
clearOnClose: {
|
|
117
|
+
control: 'boolean',
|
|
118
|
+
description: 'Whether the input field should be cleared when the date picker is closed.',
|
|
119
|
+
table: {
|
|
120
|
+
category: 'Props',
|
|
121
|
+
type: {
|
|
122
|
+
summary: 'boolean',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
errorMessage: {
|
|
127
|
+
control: 'text',
|
|
128
|
+
description: 'An error message to display.',
|
|
129
|
+
table: {
|
|
130
|
+
category: 'Props',
|
|
131
|
+
type: {
|
|
132
|
+
summary: 'string',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
disableAfterDate: {
|
|
137
|
+
control: 'date',
|
|
138
|
+
description: 'Disable dates after this date.',
|
|
139
|
+
table: {
|
|
140
|
+
category: 'Props',
|
|
141
|
+
type: {
|
|
142
|
+
summary: 'Date',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
disableBeforeDate: {
|
|
147
|
+
control: 'date',
|
|
148
|
+
description: 'Disable dates before this date.',
|
|
149
|
+
table: {
|
|
150
|
+
category: 'Props',
|
|
151
|
+
type: {
|
|
152
|
+
summary: 'Date',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
isDisabled: {
|
|
157
|
+
control: 'boolean',
|
|
158
|
+
description: 'Disable the date picker.',
|
|
159
|
+
table: {
|
|
160
|
+
category: 'Props',
|
|
161
|
+
type: {
|
|
162
|
+
summary: 'boolean',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
month: {
|
|
167
|
+
control: 'date',
|
|
168
|
+
description: 'The month to display.',
|
|
169
|
+
table: {
|
|
170
|
+
category: 'Props',
|
|
171
|
+
type: {
|
|
172
|
+
summary: 'Date',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
'data-testid': {
|
|
177
|
+
table: {
|
|
178
|
+
disable: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default meta;
|
|
185
|
+
|
|
186
|
+
type Story = StoryObj<typeof InputDateRangePicker>;
|
|
187
|
+
|
|
188
|
+
export const SingleInput: Story = {
|
|
189
|
+
args: {
|
|
190
|
+
id: 'date-picker',
|
|
191
|
+
label: 'Pick a date:',
|
|
192
|
+
inputPlaceholder: 'MM/DD/YYYY',
|
|
193
|
+
inputIconName: 'calendar',
|
|
194
|
+
isClearable: true,
|
|
195
|
+
isDisabled: false,
|
|
196
|
+
clearOnClose: false,
|
|
197
|
+
errorMessage: '',
|
|
198
|
+
ariaLabel: 'Date Picker',
|
|
199
|
+
},
|
|
200
|
+
render: (args) => {
|
|
201
|
+
const [{ selected }, updateArgs] = useArgs();
|
|
202
|
+
const handleSelect = (date: Date | undefined) => {
|
|
203
|
+
updateArgs({ selected: date });
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return <InputDateRangePicker {...args} selected={selected} onSelect={handleSelect} />;
|
|
207
|
+
},
|
|
208
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useId, useState, useEffect } from 'react';
|
|
2
|
+
import { format, isValid, parse } from 'date-fns';
|
|
3
|
+
import { DatePicker } from '../datePicker/DatePicker';
|
|
4
|
+
import { Input } from '../../input';
|
|
5
|
+
import { IconName } from '@/types';
|
|
6
|
+
import { DateRange } from 'react-day-picker';
|
|
7
|
+
import { FloatUI } from '../../../floatUI';
|
|
8
|
+
|
|
9
|
+
interface InputDateRangePickerProps {
|
|
10
|
+
ariaLabel: string;
|
|
11
|
+
disableBeforeDate?: Date;
|
|
12
|
+
disableAfterDate?: Date;
|
|
13
|
+
isDisabled?: boolean;
|
|
14
|
+
id?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
onSelect?: (selected: Date | undefined) => void;
|
|
17
|
+
month?: Date;
|
|
18
|
+
selected?: Date | DateRange | Date[] | undefined;
|
|
19
|
+
isOpen?: boolean;
|
|
20
|
+
clearOnClose?: boolean;
|
|
21
|
+
className?: string;
|
|
22
|
+
inputIconName?: IconName;
|
|
23
|
+
isClearable?: boolean;
|
|
24
|
+
inputPlaceholder?: string;
|
|
25
|
+
errorMessage?: string | undefined;
|
|
26
|
+
'data-testid'?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function InputDateRangePicker(props: InputDateRangePickerProps) {
|
|
30
|
+
const {
|
|
31
|
+
ariaLabel,
|
|
32
|
+
className,
|
|
33
|
+
isDisabled,
|
|
34
|
+
disableBeforeDate,
|
|
35
|
+
disableAfterDate,
|
|
36
|
+
month,
|
|
37
|
+
id,
|
|
38
|
+
label,
|
|
39
|
+
onSelect,
|
|
40
|
+
selected,
|
|
41
|
+
isOpen,
|
|
42
|
+
inputPlaceholder,
|
|
43
|
+
clearOnClose,
|
|
44
|
+
inputIconName,
|
|
45
|
+
isClearable,
|
|
46
|
+
errorMessage,
|
|
47
|
+
...rest
|
|
48
|
+
} = props;
|
|
49
|
+
|
|
50
|
+
const inputId = useId();
|
|
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);
|
|
57
|
+
|
|
58
|
+
// Hold the input value in state
|
|
59
|
+
const [inputValue, setInputValue] = useState('');
|
|
60
|
+
|
|
61
|
+
const handleDayPickerSelect = (date: Date | undefined) => {
|
|
62
|
+
if (!date) {
|
|
63
|
+
setInputValue('');
|
|
64
|
+
setSelectedDate(undefined);
|
|
65
|
+
} else {
|
|
66
|
+
setSelectedDate(date);
|
|
67
|
+
setLocalMonth(date);
|
|
68
|
+
setInputValue(format(date, 'MM/dd/yyyy'));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
73
|
+
setInputValue(e.target.value); // keep the input value in sync
|
|
74
|
+
|
|
75
|
+
const parsedDate = parse(e.target.value, 'MM/dd/yyyy', new Date());
|
|
76
|
+
|
|
77
|
+
if (isValid(parsedDate)) {
|
|
78
|
+
setSelectedDate(parsedDate);
|
|
79
|
+
setLocalMonth(parsedDate);
|
|
80
|
+
} else {
|
|
81
|
+
setSelectedDate(undefined);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// clear selection if clear on close is true
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!isOpen && clearOnClose) {
|
|
88
|
+
setSelectedDate(undefined);
|
|
89
|
+
setInputValue('');
|
|
90
|
+
}
|
|
91
|
+
}, [isOpen, clearOnClose]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<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
|
+
/>
|
|
107
|
+
<DatePicker
|
|
108
|
+
month={localMonth}
|
|
109
|
+
onMonthChange={setLocalMonth}
|
|
110
|
+
mode="single"
|
|
111
|
+
selected={selectedDate}
|
|
112
|
+
onSelect={handleDayPickerSelect}
|
|
113
|
+
{...rest}
|
|
114
|
+
/>
|
|
115
|
+
</FloatUI>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { InputDateRangePicker } from './InputDateRangePicker';
|
|
@@ -7,6 +7,7 @@ import { withLabel, LabelProps } from '../subcomponents/Label';
|
|
|
7
7
|
import { DisplayFormError } from '../subcomponents/DisplayFormError';
|
|
8
8
|
|
|
9
9
|
export interface BaseInputProps {
|
|
10
|
+
id?: string;
|
|
10
11
|
value?: string | undefined;
|
|
11
12
|
placeholder?: string;
|
|
12
13
|
isDisabled?: boolean;
|
|
@@ -26,6 +27,7 @@ export interface InputProps extends BaseInputProps, LabelProps {}
|
|
|
26
27
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
27
28
|
(
|
|
28
29
|
{
|
|
30
|
+
id,
|
|
29
31
|
name,
|
|
30
32
|
placeholder,
|
|
31
33
|
isRequired,
|
|
@@ -64,6 +66,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
64
66
|
)}
|
|
65
67
|
<input
|
|
66
68
|
ref={ref}
|
|
69
|
+
id={id}
|
|
67
70
|
data-testid={`form-input-${name}`}
|
|
68
71
|
name={name}
|
|
69
72
|
type="text"
|
package/src/components/index.ts
CHANGED
|
@@ -14,3 +14,6 @@ export { Skeleton } from './skeleton';
|
|
|
14
14
|
export { Card } from './card';
|
|
15
15
|
export { FloatUI } from './floatUI';
|
|
16
16
|
export { Menu } from './menu';
|
|
17
|
+
export { DatePicker } from './forms/date/datePicker/DatePicker';
|
|
18
|
+
export { IconTriggerDatePicker } from './forms/date/iconTriggerDatePicker';
|
|
19
|
+
export { SingleInputDatePicker } from './forms/date/inputDatePicker';
|