Skip to content

Commit ead2276

Browse files
committed
Merge remote-tracking branch 'origin/master' into next
2 parents 0b88f6d + 6201e3b commit ead2276

File tree

6 files changed

+178
-2
lines changed

6 files changed

+178
-2
lines changed

docs/DateInput.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Here is how to set up the pickers to use the `fr` locale:
106106
import { Admin, Resource } from 'react-admin';
107107
import { LocalizationProvider } from '@mui/x-date-pickers';
108108
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
109-
import { fr } from 'date-fns/locale';
109+
import { fr } from 'date-fns/locale/fr';
110110
import { EventEdit } from './events';
111111

112112
export const App = () => (

docs/DateRangeInput.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
layout: default
3+
title: "The DateRangeInput Component"
4+
---
5+
6+
# `<DateRangeInput>`
7+
8+
This [Enterprise Edition](https://react-admin-ee.marmelab.com)<img class="icon" src="./img/premium.svg" /> component `<DateRangeInput>` is a date range picker, allowing users to pick an interval by selecting a start and an end date. It is ideal for filtering records based on a date range. It is designed to work with various locales and date formats.
9+
10+
![DateRangeInput](./img/DateRangeInput.png)
11+
12+
**Note**: `<DateRangeInput>` is a wrapper around the [Material UI X Date Range Picker](https://mui.com/x/react-date-pickers/date-range-picker/), which is a MUI X Pro package. This means that you need to own a [MUI X Pro license](https://mui.com/x/introduction/licensing/#pro-plan) to use it.
13+
14+
## Usage
15+
16+
Use `<DateRangeInput>` inside a form component (`<SimpleForm>`, `<TabbedForm>`, `<LongForm>`, etc.) to allow users to pick a start and an end date.
17+
18+
```tsx
19+
import { DateRangeInput } from '@react-admin/ra-form-layout';
20+
import { Edit, SimpleForm } from 'react-admin';
21+
22+
export const EventEdit = () => (
23+
<Edit>
24+
<SimpleForm>
25+
<DateRangeInput source="subscription_period" />
26+
</SimpleForm>
27+
</Edit>
28+
);
29+
```
30+
31+
`<DateRangeInput>` reads and writes date ranges as arrays of `Date` objects. It also accepts arrays of strings that can be parsed into `Date` values. It will return `null` if any of the dates is invalid.
32+
33+
```js
34+
// example valid date range values
35+
['2024-01-01', '2024-01-31']
36+
['2024-01-01T00:00:00.000Z', '2024-01-31T23:59:59.999Z']
37+
[new Date('2024-01-01T00:00:00.000Z'), new Date('2024-01-31T23:59:59.999Z')]
38+
```
39+
40+
**Tip:** You can use the `parse` prop to change the format of the returned value. See [Parsing the date/time as an ISO string](#parse-and-format) for an example.
41+
42+
## Props
43+
44+
| Prop | Required | Type | Default | Description |
45+
| ------------- | -------- | ---------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46+
| `source` | Required | string | - | The name of the field in the record. |
47+
| `defaultValue`| - | Array | - | The default value of the input. |
48+
| `disabled` | - | boolean | - | If `true`, the input will be disabled. |
49+
| `format` | - | function | - | Callback taking the value from the form state, and returning the input value. |
50+
| `fullWidth` | - | boolean | - | If `false`, the input will not expand to fill the form width |
51+
| `helperText` | - | string | - | Text to be displayed under the input |
52+
| `label` | - | string | - | Input label. In i18n apps, the label is passed to the `translate` function. When omitted, the `source` property is humanized and used as a label. Set `label={false}` to hide the label. |
53+
| `mask` | - | string | - | Alias for the MUI [`format`](https://mui.com/x/api/date-pickers/date-picker/#DatePicker-prop-format) prop. Format of the date/time when rendered in the input. Defaults to localized format. |
54+
| `parse` | - | Function | - | Callback taking the input values, and returning the values you want stored in the form state. |
55+
| `readOnly` | - | boolean | - | If `true`, the input will be read-only. |
56+
| `sx` | - | `SxProps` | - | The style to apply to the component. |
57+
| `validate` | - | `function|Array` | - | Validation rules for the input. See the [Validation Documentation](./Validation.md#per-input-validation-built-in-field-validators) for details. |
58+
59+
`<DateRangeInput>` also accept the same props as [MUI X's `<DateRangePicker>`](https://mui.com/x/api/date-pickers/date-range-picker/), except for the `format` prop (renamed `mask`),
60+
61+
**Tip:** Since `<DateRangeInput>` stores its value as a date array, [react-admin's validators](./Validation.md#per-input-validation-built-in-field-validators) like `minValue` or `maxValue` won't work out of the box.
62+
63+
## `parse` and `format`
64+
65+
By default, `<DateRangeInput>` stores the dates as an array of `Date` objects in the form state. When sent to the API, these dates will be stringified using the ISO 8601 format via [`Date.prototype.toISOString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString).
66+
67+
If you wish to store the dates in any other format, you can use the `parse` prop to change the `Date` objects into the desired format.
68+
69+
```tsx
70+
<DateRangeInput
71+
source="subscription_period"
72+
parse={(dates: Date[]) => (
73+
dates
74+
? dates.map(date => (date ? date.toUTCString()() : null))
75+
: null
76+
)}
77+
/>
78+
```
79+
80+
Similarly, if your database stores your dates in a format that can't be interpreted by [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse), you can use the `format` prop.
81+
82+
```tsx
83+
import { parse } from 'date-fns';
84+
// ...
85+
<DateRangeInput
86+
source="subscription_period"
87+
format={(dates: Date[]) => (
88+
dates
89+
? dates.map(date => date ? parse(date, 'dd/MM/yyyy', new Date()) : null)
90+
: null
91+
)}
92+
/>
93+
```
94+
95+
## `validate`
96+
97+
The value of the `validate` prop must be a function taking the record as input, and returning an object with error messages indexed by fields. The record could be null or an array of objects that could be null or a `Date` object. So the [react-admin's built-in field validators](./Validation.md#per-input-validation-built-in-field-validators) will not be useful for `<DateRageInput>`, you will need to build your own.
98+
99+
Here is an example of custom validators for a `<DateRangeInput>`:
100+
101+
```tsx
102+
import {
103+
Edit,
104+
isEmpty,
105+
required,
106+
SimpleForm,
107+
TextInput,
108+
} from "react-admin";
109+
import { DateRangeInput } from '@react-admin/ra-form-layout';
110+
111+
const requiredValues = dates =>
112+
!dates || isEmpty(dates[0]) || isEmpty(dates[1])
113+
? 'ra.validation.required'
114+
: null;
115+
116+
const thisMonth = dates => {
117+
if (!dates || !dates[0] || !dates[1]) {
118+
return
119+
}
120+
const firstOfTheMonth = new Date();
121+
firstOfTheMonth.setDate(1);
122+
firstOfTheMonth.setHours(0, 0, 0, 0);
123+
const lastOfTheMonth = new Date();
124+
lastOfTheMonth.setMonth(lastOfTheMonth.getMonth() + 1);
125+
lastOfTheMonth.setDate(0);
126+
lastOfTheMonth.setHours(23, 59, 59, 999);
127+
return dates[0] < firstOfTheMonth || dates[1] > lastOfTheMonth
128+
? 'ra.validation.dateRange.invalid'
129+
: null;
130+
}
131+
132+
const EventEdit = () => {
133+
return (
134+
<Edit>
135+
<SimpleForm>
136+
<TextInput source="title" validate={required} />
137+
<DateRangeInput source="communication_period" validate={requiredValues} />
138+
<DateRangeInput source="subscription_period" validate={[requiredValues(), tothisMonthay()]} />
139+
</SimpleForm>
140+
</Edit>
141+
);
142+
};
143+
```
144+
145+
## Providing your own `LocalizationProvider`
146+
147+
MUI X Pickers need to be wrapped in a [LocalizationProvider](https://mui.com/components/pickers/#localization) to work properly. `<DateRangeInput>` already includes a default `<LocalizationProvider>` using the `date-fns` adapter and the `enUS` locale.
148+
149+
You can change the locale and the date format for the entire app by wrapping the `<Admin>` with your own `<LocalizationProvider>`.
150+
151+
Here is how to set up the pickers to use the `fr` locale:
152+
153+
```tsx
154+
import { Admin, Resource } from 'react-admin';
155+
import { fr } from 'date-fns/locale/fr'
156+
import { EventEdit } from './events';
157+
158+
import { LocalizationProvider } from '@mui/x-date-pickers-pro';
159+
import { AdapterDateFns } from '@mui/x-date-pickers-pro/AdapterDateFnsV3';
160+
161+
export const App = () => (
162+
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={fr}>
163+
<Admin>
164+
<Resource name="events" edit={EventEdit} />
165+
</Admin>
166+
</LocalizationProvider>
167+
);
168+
```
169+
170+
**Note**: To wrap your admin using a `<DateInput>`, a `<DateTimeInput>` or a `<TimeInput>`, you need to import `LocalizationProvider` from `@mui/x-date-pickers` and `AdapterDateFns` from `@mui/x-date-pickers/AdapterDateFnsV3`. But, to wrap your admin using a `<DateRangeInput>`, you need to import `LocalizationProvider` from `@mui/x-date-pickers-pro` and `AdapterDateFns` from `@mui/x-date-pickers-pro/AdapterDateFnsV3`. If you use both components, please use `@mui/x-date-pickers-pro` imports.
171+
172+
**Note:** React-admin only supports the `date-fns` adapter for now.
173+
174+
**Tip**: React-admin already depends on `date-fns` v3 but your package manager may require you to add it to your dependencies.

docs/DateTimeInput.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Here is how to set up the pickers to use the `fr` locale:
9393
import { Admin, Resource } from 'react-admin';
9494
import { LocalizationProvider } from '@mui/x-date-pickers';
9595
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
96-
import { fr } from 'date-fns/locale';
96+
import { fr } from 'date-fns/locale/fr';
9797
import { EventEdit } from './events';
9898

9999
export const App = () => (

docs/Reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ title: "Index"
5353
* [`<DatagridHeader>`](./Datagrid.md#header)
5454
* [`<DateField>`](./DateField.md)
5555
* [`<DateInput>`](./DateInput.md)
56+
* [`<DateRangeInput>`](./DateRangeInput.md)
5657
* [`<DateTimeInput>`](./DateTimeInput.md)
5758
* `<DeleteButton>`
5859
* [`<DualListInput>`](./DualListInput.md)<img class="icon" src="./img/premium.svg" />

docs/img/DateRangeInput.png

119 KB
Loading

docs/navigation.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@
190190
<li {% if page.path == 'BooleanInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./BooleanInput.html"><code>&lt;BooleanInput&gt;</code></a></li>
191191
<li {% if page.path == 'CheckboxGroupInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./CheckboxGroupInput.html"><code>&lt;CheckboxGroupInput&gt;</code></a></li>
192192
<li {% if page.path == 'DateInput.md' %} class="active beginner" {% else %} class="beginner" {% endif %}><a class="nav-link" href="./DateInput.html"><code>&lt;DateInput></code></a></li>
193+
<li {% if page.path == 'DateRangeInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DateRangeInput.html"><code>&lt;DateRangeInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
193194
<li {% if page.path == 'DateTimeInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DateTimeInput.html"><code>&lt;DateTimeInput&gt;</code></a></li>
194195
<li {% if page.path == 'DualListInput.md' %} class="active" {% endif %}><a class="nav-link" href="./DualListInput.html"><code>&lt;DualListInput&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
195196
<li {% if page.path == 'FileInput.md' %} class="active" {% endif %}><a class="nav-link" href="./FileInput.html"><code>&lt;FileInput&gt;</code></a></li>

0 commit comments

Comments
 (0)