Skip to content

Commit 556773f

Browse files
devongovettdannify
andauthored
DatePicker docs (#2732)
* Add isWeekend and isWeekday functions * Initial DatePicker docs * Updates * Add DateField and DateRangePicker docs * Add section on international calendars * Calendar docs * RangeCalendar docs * TimeField docs * Updates * More updates * Improve calendar description Co-authored-by: Danni <[email protected]>
1 parent df9eb32 commit 556773f

File tree

23 files changed

+1746
-13
lines changed

23 files changed

+1746
-13
lines changed

packages/@adobe/react-spectrum/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ export {Well} from '@react-spectrum/well';
4949
export {Item, Section} from '@react-stately/collections';
5050
export {useAsyncList, useListData, useTreeData} from '@react-stately/data';
5151
export {VisuallyHidden} from '@react-aria/visually-hidden';
52-
export {useCollator, useDateFormatter, useFilter, useMessageFormatter, useNumberFormatter} from '@react-aria/i18n';
52+
export {useCollator, useDateFormatter, useFilter, useLocale, useMessageFormatter, useNumberFormatter} from '@react-aria/i18n';
5353
export {SSRProvider} from '@react-aria/ssr';

packages/@internationalized/date/src/queries.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,44 @@ export function minDate<A extends DateValue, B extends DateValue>(a: A, b: B): A
215215
export function maxDate<A extends DateValue, B extends DateValue>(a: A, b: B): A | B {
216216
return a.compare(b) >= 0 ? a : b;
217217
}
218+
219+
const WEEKEND_DATA = {
220+
AF: [4, 5],
221+
AE: [5, 6],
222+
BH: [5, 6],
223+
DZ: [5, 6],
224+
EG: [5, 6],
225+
IL: [5, 6],
226+
IQ: [5, 6],
227+
IR: [5, 5],
228+
JO: [5, 6],
229+
KW: [5, 6],
230+
LY: [5, 6],
231+
OM: [5, 6],
232+
QA: [5, 6],
233+
SA: [5, 6],
234+
SD: [5, 6],
235+
SY: [5, 6],
236+
YE: [5, 6]
237+
};
238+
239+
export function isWeekend(date: DateValue, locale: string) {
240+
let julian = date.calendar.toJulianDay(date);
241+
242+
// If julian is negative, then julian % 7 will be negative, so we adjust
243+
// accordingly. Julian day 0 is Monday.
244+
let dayOfWeek = Math.ceil(julian + 1) % 7;
245+
if (dayOfWeek < 0) {
246+
dayOfWeek += 7;
247+
}
248+
249+
let region = getRegion(locale);
250+
// Use Intl.Locale for this once weekInfo is supported.
251+
// https://github.com/tc39/proposal-intl-locale-info
252+
let [start, end] = WEEKEND_DATA[region] || [6, 0];
253+
return dayOfWeek === start || dayOfWeek === end;
254+
}
255+
256+
export function isWeekday(date: DateValue, locale: string) {
257+
return !isWeekend(date, locale);
258+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
{/* Copyright 2022 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. */}
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-spectrum/calendar';
14+
import i18nDocs from 'docs:@internationalized/date';
15+
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
16+
import packageData from '@react-spectrum/calendar/package.json';
17+
18+
```jsx import
19+
import {Calendar} from '@react-spectrum/calendar';
20+
import {Flex} from '@react-spectrum/layout';
21+
```
22+
23+
---
24+
category: Date and Time
25+
keywords: [date]
26+
after_version: 3.0.0
27+
---
28+
29+
# Calendar
30+
31+
<PageDescription>{docs.exports.Calendar.description}</PageDescription>
32+
33+
<HeaderInfo
34+
packageData={packageData}
35+
componentNames={['Calendar']}
36+
sourceData={[]} />
37+
38+
## Example
39+
40+
```tsx example
41+
<Calendar aria-label="Event date" />
42+
```
43+
44+
## Value
45+
46+
A `Calendar` has no selection by default. An initial, uncontrolled value can be provided to the `Calendar` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop.
47+
48+
Date values are provided using objects in the `@internationalized/date` package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns.
49+
50+
`Calendar` supports values with both date and time components, but only allows users to modify the date. By default, `Calendar` will emit <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDate} /> objects in the `onChange` event, but if a <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDateTime} /> or <TypeLink links={i18nDocs.links} type={i18nDocs.exports.ZonedDateTime} /> object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the date and preserving the time components.
51+
52+
```tsx example
53+
import {parseDate} from '@internationalized/date';
54+
55+
function Example() {
56+
let [value, setValue] = React.useState(parseDate('2020-02-03'));
57+
58+
return (
59+
<Flex gap="size-300" wrap>
60+
<Calendar
61+
aria-label="Date (uncontrolled)"
62+
defaultValue={parseDate('2020-02-03')} />
63+
<Calendar
64+
aria-label="Date (controlled)"
65+
value={value}
66+
onChange={setValue} />
67+
</Flex>
68+
);
69+
}
70+
```
71+
72+
### International calendars
73+
74+
`Calendar` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the `Provider` component.
75+
76+
Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.
77+
78+
The below example displays a `Calendar` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar.
79+
80+
```tsx example
81+
import {Provider} from '@adobe/react-spectrum';
82+
83+
function Example() {
84+
let [date, setDate] = React.useState(null);
85+
return (
86+
<Provider locale="hi-IN-u-ca-indian">
87+
<Calendar aria-label="Date" value={date} onChange={setDate} />
88+
<p>Selected date: {date?.toString()}</p>
89+
</Provider>
90+
);
91+
}
92+
```
93+
94+
## Labeling
95+
96+
An aria-label must be provided to the `Calendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the `id` of the labeling element instead.
97+
98+
### Internationalization
99+
100+
In order to internationalize a `Calendar`, a localized string should be passed to the `aria-label` prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the `Calendar` is automatically flipped. Dates are automatically formatted using the current locale.
101+
102+
## Events
103+
104+
`Calendar` accepts an `onChange` prop which is triggered whenever a date is selected by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter.
105+
106+
```tsx example
107+
import {getLocalTimeZone} from '@internationalized/date';
108+
import {useDateFormatter} from '@adobe/react-spectrum';
109+
110+
function Example() {
111+
let [date, setDate] = React.useState(parseDate('2022-07-04'));
112+
let formatter = useDateFormatter({dateStyle: 'full'});
113+
114+
return (
115+
<>
116+
<Calendar aria-label="Event date" value={date} onChange={setDate} />
117+
<p>Selected date: {formatter.format(date.toDate(getLocalTimeZone()))}</p>
118+
</>
119+
);
120+
}
121+
```
122+
123+
## Validation
124+
125+
By default, `Calendar` allows selecting any date. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range.
126+
127+
This example only accepts dates after today.
128+
129+
```tsx example
130+
import {today} from '@internationalized/date';
131+
132+
<Calendar aria-label="Appointment date" minValue={today(getLocalTimeZone())} />
133+
```
134+
135+
## Props
136+
137+
<PropTable component={docs.exports.Calendar} links={docs.links} />
138+
139+
## Visual options
140+
141+
### Disabled
142+
143+
```tsx example
144+
<Calendar aria-label="Event date" isDisabled />
145+
```
146+
147+
### Read only
148+
149+
The `isReadOnly` boolean prop makes the Calendar's value immutable. Unlike `isDisabled`, the Calendar remains focusable.
150+
151+
```tsx example
152+
<Calendar aria-label="Event date" value={today(getLocalTimeZone())} isReadOnly />
153+
```
154+
155+
### Visible months
156+
157+
By default, the `Calendar` displays a single month. The `visibleMonths` prop allows displaying up to 3 months at a time.
158+
159+
```tsx example
160+
<Calendar aria-label="Event date" visibleMonths={3} />
161+
```
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
{/* Copyright 2022 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. */}
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-spectrum/calendar';
14+
import i18nDocs from 'docs:@internationalized/date';
15+
import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs';
16+
import packageData from '@react-spectrum/calendar/package.json';
17+
18+
```jsx import
19+
import {RangeCalendar} from '@react-spectrum/calendar';
20+
import {Flex} from '@react-spectrum/layout';
21+
```
22+
23+
---
24+
category: Date and Time
25+
keywords: [date]
26+
after_version: 3.0.0
27+
---
28+
29+
# RangeCalendar
30+
31+
<PageDescription>{docs.exports.RangeCalendar.description}</PageDescription>
32+
33+
<HeaderInfo
34+
packageData={packageData}
35+
componentNames={['RangeCalendar']}
36+
sourceData={[]} />
37+
38+
## Example
39+
40+
```tsx example
41+
<RangeCalendar aria-label="Trip dates" />
42+
```
43+
44+
## Value
45+
46+
A `RangeCalendar` has no selection by default. An initial, uncontrolled value can be provided to the `RangeCalendar` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop.
47+
48+
Date ranges are objects with `start` and `end` properties containing date values, which are provided using objects in the `@internationalized/date` package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns.
49+
50+
`RangeCalendar` supports values with both date and time components, but only allows users to modify the dates. By default, `RangeCalendar` will emit <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDate} /> objects in the `onChange` event, but if a <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDateTime} /> or <TypeLink links={i18nDocs.links} type={i18nDocs.exports.ZonedDateTime} /> object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the date and preserving the time components.
51+
52+
```tsx example
53+
import {parseDate} from '@internationalized/date';
54+
55+
function Example() {
56+
let [value, setValue] = React.useState({
57+
start: parseDate('2020-02-03'),
58+
end: parseDate('2020-02-12')
59+
});
60+
61+
return (
62+
<Flex gap="size-300" wrap>
63+
<RangeCalendar
64+
aria-label="Date range (uncontrolled)"
65+
defaultValue={{
66+
start: parseDate('2020-02-03'),
67+
end: parseDate('2020-02-12')
68+
}} />
69+
<RangeCalendar
70+
aria-label="Date range (controlled)"
71+
value={value}
72+
onChange={setValue} />
73+
</Flex>
74+
);
75+
}
76+
```
77+
78+
### International calendars
79+
80+
`RangeCalendar` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the `Provider` component.
81+
82+
Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently.
83+
84+
The below example displays a `RangeCalendar` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar.
85+
86+
```tsx example
87+
import {Provider} from '@adobe/react-spectrum';
88+
89+
function Example() {
90+
let [range, setRange] = React.useState(null);
91+
return (
92+
<Provider locale="hi-IN-u-ca-indian">
93+
<RangeCalendar aria-label="Date range" value={range} onChange={setRange} />
94+
<p>Start date: {range?.start.toString()}</p>
95+
<p>End date: {range?.end.toString()}</p>
96+
</Provider>
97+
);
98+
}
99+
```
100+
101+
## Labeling
102+
103+
An aria-label must be provided to the `RangeCalendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the `id` of the labeling element instead.
104+
105+
### Internationalization
106+
107+
In order to internationalize a `RangeCalendar`, a localized string should be passed to the `aria-label` prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the `RangeCalendar` is automatically flipped. Dates are automatically formatted using the current locale.
108+
109+
## Events
110+
111+
`RangeCalendar` accepts an `onChange` prop which is triggered whenever a date is selected by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter.
112+
113+
```tsx example
114+
import {getLocalTimeZone} from '@internationalized/date';
115+
import {useDateFormatter} from '@adobe/react-spectrum';
116+
117+
function Example() {
118+
let [range, setRange] = React.useState({
119+
start: parseDate('2020-07-03'),
120+
end: parseDate('2020-07-10')
121+
});
122+
let formatter = useDateFormatter({dateStyle: 'long'});
123+
124+
return (
125+
<>
126+
<RangeCalendar aria-label="Date range" value={range} onChange={setRange} />
127+
<p>
128+
Selected date:{' '}
129+
{formatter.formatRange(
130+
range.start.toDate(getLocalTimeZone()),
131+
range.end.toDate(getLocalTimeZone())
132+
)}
133+
</p>
134+
</>
135+
);
136+
}
137+
```
138+
139+
## Validation
140+
141+
By default, `RangeCalendar` allows selecting any date range. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range.
142+
143+
This example only accepts dates after today.
144+
145+
```tsx example
146+
import {today} from '@internationalized/date';
147+
148+
<RangeCalendar aria-label="Trip dates" minValue={today(getLocalTimeZone())} />
149+
```
150+
151+
## Props
152+
153+
<PropTable component={docs.exports.RangeCalendar} links={docs.links} />
154+
155+
## Visual options
156+
157+
### Disabled
158+
159+
```tsx example
160+
<RangeCalendar aria-label="Trip dates" isDisabled />
161+
```
162+
163+
### Read only
164+
165+
The `isReadOnly` boolean prop makes the Calendar's value immutable. Unlike `isDisabled`, the RangeCalendar remains focusable.
166+
167+
```tsx example
168+
<RangeCalendar aria-label="Trip dates" value={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({ weeks: 1 })}} isReadOnly />
169+
```
170+
171+
### Visible months
172+
173+
By default, a `RangeCalendar` displays a single month. The `visibleMonths` prop allows displaying up to 3 months at a time.
174+
175+
```tsx example
176+
<RangeCalendar aria-label="Trip dates" visibleMonths={3} />
177+
```

0 commit comments

Comments
 (0)