@availity/mui-datepicker 0.6.1 → 0.6.2
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/CHANGELOG.md +5 -0
- package/DateRangePicker.stories.mdx +8 -0
- package/DateRangePickerDocs.md +294 -0
- package/package.json +2 -2
- package/src/lib/Datepicker.stories.tsx +61 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [0.6.2](https://github.com/Availity/element/compare/@availity/mui-datepicker@0.6.1...@availity/mui-datepicker@0.6.2) (2025-02-18)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `mui-textfield` updated to version `0.6.1`
|
|
5
10
|
## [0.6.1](https://github.com/Availity/element/compare/@availity/mui-datepicker@0.6.0...@availity/mui-datepicker@0.6.1) (2025-02-05)
|
|
6
11
|
|
|
7
12
|
### Dependency Updates
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Markdown, Source } from '@storybook/blocks';
|
|
2
|
+
import { Meta } from '@storybook/addon-docs';
|
|
3
|
+
|
|
4
|
+
import Docs from './DateRangePickerDocs.md?raw';
|
|
5
|
+
|
|
6
|
+
<Meta title="Form Components/Datepicker/Datepicker/Date Range Picker Docs" />
|
|
7
|
+
|
|
8
|
+
<Markdown>{Docs}</Markdown>
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# DateRangePicker Docs
|
|
2
|
+
|
|
3
|
+
The `element` library has a `Datepicker` component that can be used to create your own `DateRangePicker`. There is not a `DateRangePicker` exported from the library.
|
|
4
|
+
|
|
5
|
+
## Example
|
|
6
|
+
|
|
7
|
+
The following code block shows an example `DateRangePicker` using the `Datepicker` and `Grid` components.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Datepicker, Grid } from '@availity/element';
|
|
11
|
+
import type { Dayjs } from 'dayjs';
|
|
12
|
+
|
|
13
|
+
type DateRangePickerProps = {
|
|
14
|
+
startDate: Dayjs | null;
|
|
15
|
+
endDate: Dayjs | null;
|
|
16
|
+
onStartDateChange: (date: Dayjs | null) => void;
|
|
17
|
+
onEndDateChange: (date: Dayjs | null) => void;
|
|
18
|
+
startFieldProps?: {
|
|
19
|
+
label?: string;
|
|
20
|
+
helperText?: string;
|
|
21
|
+
helpTopicId?: string;
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
};
|
|
24
|
+
endFieldProps?: {
|
|
25
|
+
label?: string;
|
|
26
|
+
helperText?: string;
|
|
27
|
+
helpTopicId?: string;
|
|
28
|
+
fullWidth?: boolean;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const DateRangePicker = ({
|
|
33
|
+
startDate,
|
|
34
|
+
endDate,
|
|
35
|
+
onStartDateChange,
|
|
36
|
+
onEndDateChange,
|
|
37
|
+
startFieldProps = {
|
|
38
|
+
label: 'Start Date',
|
|
39
|
+
helperText: 'Select start date',
|
|
40
|
+
},
|
|
41
|
+
endFieldProps = {
|
|
42
|
+
label: 'End Date',
|
|
43
|
+
helperText: 'Select end date',
|
|
44
|
+
},
|
|
45
|
+
}: DateRangePickerProps) => {
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<Grid container spacing={2}>
|
|
49
|
+
<Grid xs={12} sm={6}>
|
|
50
|
+
<Datepicker
|
|
51
|
+
value={startDate}
|
|
52
|
+
onChange={onStartDateChange}
|
|
53
|
+
FieldProps={startFieldProps}
|
|
54
|
+
maxDate={endDate ?? undefined} // Prevent selecting start date after end date
|
|
55
|
+
views={['day', 'month', 'year']}
|
|
56
|
+
/>
|
|
57
|
+
</Grid>
|
|
58
|
+
<Grid xs={12} sm={6}>
|
|
59
|
+
<Datepicker
|
|
60
|
+
value={endDate}
|
|
61
|
+
onChange={onEndDateChange}
|
|
62
|
+
FieldProps={endFieldProps}
|
|
63
|
+
minDate={startDate ?? undefined} // Prevent selecting end date before start date
|
|
64
|
+
views={['day', 'month', 'year']}
|
|
65
|
+
/>
|
|
66
|
+
</Grid>
|
|
67
|
+
</Grid>
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Simple Usage
|
|
74
|
+
|
|
75
|
+
Below is an example of the `DateRangePicker` component we made above.
|
|
76
|
+
|
|
77
|
+
<Source />
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { useState } from 'react';
|
|
81
|
+
import { Dayjs } from 'dayjs';
|
|
82
|
+
|
|
83
|
+
import { DateRangePicker } from './DateRangePicker';
|
|
84
|
+
|
|
85
|
+
const MyComponent = () => {
|
|
86
|
+
const [startDate, setStartDate] = useState<Dayjs | null>(null);
|
|
87
|
+
const [endDate, setEndDate] = useState<Dayjs | null>(null);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<DateRangePicker
|
|
91
|
+
// Required props
|
|
92
|
+
startDate={startDate}
|
|
93
|
+
endDate={endDate}
|
|
94
|
+
onStartDateChange={setStartDate}
|
|
95
|
+
onEndDateChange={setEndDate}
|
|
96
|
+
// Optional props with default values shown
|
|
97
|
+
startFieldProps={{
|
|
98
|
+
label: 'Start Date',
|
|
99
|
+
helperText: 'Select start date',
|
|
100
|
+
helpTopicId: '1234', // Optional help topic ID
|
|
101
|
+
fullWidth: false, // Optional width control
|
|
102
|
+
}}
|
|
103
|
+
endFieldProps={{
|
|
104
|
+
label: 'End Date',
|
|
105
|
+
helperText: 'Select end date',
|
|
106
|
+
helpTopicId: '1234', // Optional help topic ID
|
|
107
|
+
fullWidth: false, // Optional width control
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Validation
|
|
115
|
+
|
|
116
|
+
The recommended way to validate the `DateRangePicker` is to use `yup`. Below we have an example of validating that the `endDate` is after the `startDate`.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import * as yup from 'yup';
|
|
120
|
+
import { Dayjs } from 'dayjs';
|
|
121
|
+
|
|
122
|
+
const dateRangeSchema = yup.object().shape({
|
|
123
|
+
startDate: yup.mixed<Dayjs | null>().nullable().required('Start date is required'),
|
|
124
|
+
|
|
125
|
+
endDate: yup
|
|
126
|
+
.mixed<Dayjs | null>()
|
|
127
|
+
.nullable()
|
|
128
|
+
.required('End date is required')
|
|
129
|
+
.test('is-after-start', 'End date must be after start date', function (endDate, context) {
|
|
130
|
+
const { startDate } = context.parent;
|
|
131
|
+
|
|
132
|
+
if (!startDate || !endDate) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return endDate.isAfter(startDate) || endDate.isSame(startDate);
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You can then use the created component and validation schema in a form with `react-hook-form`.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
145
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
146
|
+
import * as yup from 'yup';
|
|
147
|
+
import { Dayjs } from 'dayjs';
|
|
148
|
+
|
|
149
|
+
import { DateRangePicker } from './DateRangePicker';
|
|
150
|
+
|
|
151
|
+
interface DateRangeForm {
|
|
152
|
+
startDate: Dayjs | null;
|
|
153
|
+
endDate: Dayjs | null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const dateRangeSchema = yup.object().shape({
|
|
157
|
+
startDate: yup.mixed<Dayjs | null>().nullable().required('Start date is required'),
|
|
158
|
+
|
|
159
|
+
endDate: yup
|
|
160
|
+
.mixed<Dayjs | null>()
|
|
161
|
+
.nullable()
|
|
162
|
+
.required('End date is required')
|
|
163
|
+
.test('is-after-start', 'End date must be after start date', function (endDate, context) {
|
|
164
|
+
const { startDate } = context.parent;
|
|
165
|
+
|
|
166
|
+
if (!startDate || !endDate) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return endDate.isAfter(startDate) || endDate.isSame(startDate);
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const DateRangeForm = () => {
|
|
175
|
+
const {
|
|
176
|
+
control,
|
|
177
|
+
handleSubmit,
|
|
178
|
+
formState: { errors },
|
|
179
|
+
} = useForm<DateRangeForm>({
|
|
180
|
+
resolver: yupResolver(dateRangeSchema),
|
|
181
|
+
defaultValues: {
|
|
182
|
+
startDate: null,
|
|
183
|
+
endDate: null,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
const DateRangeForm = () => {
|
|
187
|
+
const {
|
|
188
|
+
control,
|
|
189
|
+
handleSubmit,
|
|
190
|
+
formState: { errors },
|
|
191
|
+
watch,
|
|
192
|
+
} = useForm<DateRangeForm>({
|
|
193
|
+
resolver: yupResolver(dateRangeSchema),
|
|
194
|
+
defaultValues: {
|
|
195
|
+
startDate: null,
|
|
196
|
+
endDate: null,
|
|
197
|
+
},
|
|
198
|
+
mode: 'onChange', // Enable real-time validation
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Watch for changes in dates
|
|
202
|
+
const startDate = watch('startDate');
|
|
203
|
+
const endDate = watch('endDate');
|
|
204
|
+
|
|
205
|
+
// Optional: Add side effects when dates change
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (startDate && endDate) {
|
|
208
|
+
// Perform any additional validation or business logic
|
|
209
|
+
console.log('Date range changed:', { startDate, endDate });
|
|
210
|
+
}
|
|
211
|
+
}, [startDate, endDate]);
|
|
212
|
+
|
|
213
|
+
const onSubmit = (data: DateRangeForm) => {
|
|
214
|
+
console.log('Form submitted:', data);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
219
|
+
<Controller
|
|
220
|
+
name="startDate"
|
|
221
|
+
control={control}
|
|
222
|
+
render={({ field: { onChange, value } }) => (
|
|
223
|
+
<Controller
|
|
224
|
+
name="endDate"
|
|
225
|
+
control={control}
|
|
226
|
+
render={({ field: { onChange: onEndChange, value: endValue } }) => (
|
|
227
|
+
<DateRangePicker
|
|
228
|
+
startDate={value}
|
|
229
|
+
endDate={endValue}
|
|
230
|
+
onStartDateChange={onChange}
|
|
231
|
+
onEndDateChange={onEndChange}
|
|
232
|
+
startFieldProps={{
|
|
233
|
+
helperText: errors.startDate?.message,
|
|
234
|
+
error: Boolean(errors.startDate),
|
|
235
|
+
label: 'Start Date',
|
|
236
|
+
fullWidth: true,
|
|
237
|
+
}}
|
|
238
|
+
endFieldProps={{
|
|
239
|
+
helperText: errors.endDate?.message,
|
|
240
|
+
error: Boolean(errors.endDate),
|
|
241
|
+
label: 'End Date',
|
|
242
|
+
fullWidth: true,
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
)}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
/>
|
|
249
|
+
<button type="submit" style={{ marginTop: '16px' }}>
|
|
250
|
+
Submit
|
|
251
|
+
</button>
|
|
252
|
+
</form>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const onSubmit = (data: DateRangeForm) => {
|
|
257
|
+
console.log(data);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
262
|
+
<Controller
|
|
263
|
+
name="startDate"
|
|
264
|
+
control={control}
|
|
265
|
+
render={({ field: { onChange, value } }) => (
|
|
266
|
+
<Controller
|
|
267
|
+
name="endDate"
|
|
268
|
+
control={control}
|
|
269
|
+
render={({ field: { onChange: onEndChange, value: endValue } }) => (
|
|
270
|
+
<DateRangePicker
|
|
271
|
+
startDate={value}
|
|
272
|
+
endDate={endValue}
|
|
273
|
+
onStartDateChange={onChange}
|
|
274
|
+
onEndDateChange={onEndChange}
|
|
275
|
+
startFieldProps={{
|
|
276
|
+
helperText: errors.startDate?.message,
|
|
277
|
+
error: Boolean(errors.startDate),
|
|
278
|
+
label: 'Start Date',
|
|
279
|
+
}}
|
|
280
|
+
endFieldProps={{
|
|
281
|
+
helperText: errors.endDate?.message,
|
|
282
|
+
error: Boolean(errors.endDate),
|
|
283
|
+
label: 'End Date',
|
|
284
|
+
}}
|
|
285
|
+
/>
|
|
286
|
+
)}
|
|
287
|
+
/>
|
|
288
|
+
)}
|
|
289
|
+
/>
|
|
290
|
+
<button type="submit">Submit</button>
|
|
291
|
+
</form>
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@availity/mui-datepicker",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Availity MUI Datepicker Component - part of the @availity/element design system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@availity/mui-icon": "0.14.0",
|
|
44
|
-
"@availity/mui-textfield": "0.7.
|
|
44
|
+
"@availity/mui-textfield": "0.7.1",
|
|
45
45
|
"@mui/x-date-pickers": "^7.2.0",
|
|
46
46
|
"dayjs": "^1.11.13"
|
|
47
47
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Each exported component in the package should have its own stories file
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
-
import Grid from '@mui/material/Unstable_Grid2';
|
|
5
|
-
import Paper from '@mui/material/Paper';
|
|
6
4
|
import Typography from '@mui/material/Typography';
|
|
7
5
|
import { MonthCalendar } from '@mui/x-date-pickers/MonthCalendar';
|
|
8
6
|
import { YearCalendar } from '@mui/x-date-pickers/YearCalendar';
|
|
9
7
|
import dayjs, { Dayjs } from 'dayjs';
|
|
8
|
+
import { Box, Grid } from '@availity/mui-layout';
|
|
9
|
+
import { Paper } from '@availity/mui-paper';
|
|
10
10
|
import { Datepicker, DatepickerProps } from './Datepicker';
|
|
11
11
|
import { DateCalendar } from './DateCalendar';
|
|
12
12
|
|
|
@@ -42,6 +42,65 @@ export const _Datepicker: StoryObj<typeof Datepicker> = {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
type DateRangePickerProps = {
|
|
46
|
+
startDate: Dayjs | null;
|
|
47
|
+
endDate: Dayjs | null;
|
|
48
|
+
onStartDateChange: (date: Dayjs | null) => void;
|
|
49
|
+
onEndDateChange: (date: Dayjs | null) => void;
|
|
50
|
+
startFieldProps?: {
|
|
51
|
+
label?: string;
|
|
52
|
+
helperText?: string;
|
|
53
|
+
helpTopicId?: string;
|
|
54
|
+
fullWidth?: boolean;
|
|
55
|
+
};
|
|
56
|
+
endFieldProps?: {
|
|
57
|
+
label?: string;
|
|
58
|
+
helperText?: string;
|
|
59
|
+
helpTopicId?: string;
|
|
60
|
+
fullWidth?: boolean;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type DateRangePicker = (props: DateRangePickerProps) => JSX.Element;
|
|
65
|
+
|
|
66
|
+
export const _DateRangePicker: StoryObj<DateRangePicker> = {
|
|
67
|
+
render: () => {
|
|
68
|
+
const [startDate, setStartDate] = useState<Dayjs | null>(null);
|
|
69
|
+
const [endDate, setEndDate] = useState<Dayjs | null>(null);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Box sx={{ backgroundColor: 'background.paper', padding: '1.25rem' }}>
|
|
73
|
+
<Grid container spacing={2}>
|
|
74
|
+
<Grid xs={12} sm={6}>
|
|
75
|
+
<Datepicker
|
|
76
|
+
value={startDate}
|
|
77
|
+
onChange={setStartDate}
|
|
78
|
+
FieldProps={{
|
|
79
|
+
label: 'Start Date',
|
|
80
|
+
helperText: 'Select start date',
|
|
81
|
+
}}
|
|
82
|
+
maxDate={endDate ?? undefined} // Prevent selecting start date after end date
|
|
83
|
+
views={['day', 'month', 'year']}
|
|
84
|
+
/>
|
|
85
|
+
</Grid>
|
|
86
|
+
<Grid xs={12} sm={6}>
|
|
87
|
+
<Datepicker
|
|
88
|
+
value={endDate}
|
|
89
|
+
onChange={setEndDate}
|
|
90
|
+
FieldProps={{
|
|
91
|
+
label: 'End Date',
|
|
92
|
+
helperText: 'Select end date',
|
|
93
|
+
}}
|
|
94
|
+
minDate={startDate ?? undefined} // Prevent selecting end date before start date
|
|
95
|
+
views={['day', 'month', 'year']}
|
|
96
|
+
/>
|
|
97
|
+
</Grid>
|
|
98
|
+
</Grid>
|
|
99
|
+
</Box>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
45
104
|
export const _PickerViews: StoryObj<typeof DateCalendar> = {
|
|
46
105
|
render: () => {
|
|
47
106
|
const minDate = dayjs('2020-01-01T00:00:00.000');
|