Skip to content

Commit

Permalink
feat(DatePicker): supports date selection by using keyboard (#3515)
Browse files Browse the repository at this point in the history
* fix(DateInput): fix AMPM not changing with the hour

* fix: fix not being able to assign controlled null

* feat(DatePicker): improve the interactive experience of DatePicker

* test(Calendar): fix failing tests due to styling

* fix: fix no focus issue

* refactor: use useEventCallback instead of useCallback

* refactor: use usePickerRef instead of usePublicMethods

* fix: fix style issues caused by loading state

* feat: add support for `label` on DatePicker

* fix: improve accessibility

* fix(DateInput): fix selection range error when deleting full month

* fix(DateInput): fix unable to update selected field value

* fix(DateInput): fix date format not working under controlled

* feat(DatePicker): add a highlighting style for invalid dates

* docs: fix document-nav incomplete display

* fix: adjust the style of invalid dates

* docs(DatePicker): update docs
  • Loading branch information
simonguo committed Dec 25, 2023
1 parent 2ab7921 commit bc56cb3
Show file tree
Hide file tree
Showing 55 changed files with 1,790 additions and 1,450 deletions.
2 changes: 1 addition & 1 deletion docs/components/PageContainer/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
}

.document-nav {
overflow: hidden;
overflow: auto;
z-index: 5;

.nav-item {
Expand Down
15 changes: 15 additions & 0 deletions docs/pages/components/date-input/en-US/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ The DateInput component lets users select a date with the keyboard.

<!--{include:`controlled.md`}-->

## Accessibility

### ARIA properties

- The DateInput component is the `<input type="text">` element.
- When the DateInput component is disabled, the `disabled` property is added to the `<input>` element.
- When the DateInput component is read only, the `readonly` property is added to the `<input>` element.

### Keyboard interactions

- Use <kbd>→</kbd> <kbd>←</kbd> keyboard to switch to select year/month/day/hour/minute/second.
- Use <kbd>↓</kbd> <kbd>↑</kbd> keys to increase and decrease values.
- Use <kbd>Backspace</kbd> key to delete selected value.
- Use numeric key to update selected value.

## Props

### `<DateInput>`
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/components/date-input/fragments/controlled.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!--start-code-->

```js
import { DateInput } from 'rsuite';
import { DateInput, Stack } from 'rsuite';

const App = () => {
const [value, setValue] = React.useState(new Date());
Expand Down
1 change: 1 addition & 0 deletions docs/pages/components/date-input/fragments/format.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const App = () => (
<DateInput format="MM/dd/yyyy HH:mm" />
<DateInput format="MM/dd/yyyy hh:mm aa" />
<DateInput format="HH:mm:ss" />
<DateInput format="dd MMM yyyy hh:mm:ss aa" style={{ width: 220 }} />
</Stack>
);
ReactDOM.render(<App />, document.getElementById('root'));
Expand Down
15 changes: 15 additions & 0 deletions docs/pages/components/date-input/zh-CN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ DateInput 组件允许用户使用键盘选择日期。

<!--{include:`controlled.md`}-->

## 可访问性

### ARIA 属性

- DateInput 组件是 `<input type="text">`元素。
- 当 DateInput 组件被禁用时,`disabled` 属性被添加到 `<input>` 元素。
- 当 DateInput 组件为只读时,`readonly` 属性被添加到 `<input>` 元素。

### 键盘交互

- 使用 <kbd>→</kbd> <kbd>←</kbd> 键切换到选择年/月/日/时/分/秒。
- 使用 <kbd>↓</kbd> <kbd>↑</kbd> 键增加和减少值。
- 使用 <kbd>Backspace</kbd> 键删除选中的值。
- 使用数字健更新选中的值。

## Props

<!-- prettier-sort-markdown-table -->
Expand Down
36 changes: 29 additions & 7 deletions docs/pages/components/date-picker/en-US/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ To select or input a date or time

<!--{include:`format-time.md`}-->

### Meridian format
### Meridian format (AM/PM)

Display hours in 12 format.

Expand All @@ -64,9 +64,17 @@ The calendar panel can be displayed in ISO standard via the ʻisoWeek` property

<!--{include:`disabled.md`}-->

### Loading state

<!--{include:`loading.md`}-->

### With a label

<!--{include:`with-label.md`}-->

### Disable input

`DatePicker` allows date and time input via keyboard by default, if you wish to disable it, you can disable editing by setting `editable={false}`.
`DatePicker` allows date and time input via keyboard by default, if you wish to disable it, you can disable input by setting `editable={false}`.

<!--{include:`editable.md`}-->

Expand All @@ -84,13 +92,13 @@ The calendar panel can be displayed in ISO standard via the ʻisoWeek` property
### Custom short options

<!--{include:`custom.md`}-->
Clicking "Prev Day" in the example does not close the picker layer because the `closeOverlay=false` property is configured. This property is used to set whether to close the picker layer after clicking the shortcut item. The default value is `true`.

Clicking "The day before" in the example does not close the picker layer because the `closeOverlay:boolean` property is configured. This property is used to set whether to close the picker layer after clicking the shortcut item. The default value is `true`.
<!--{include:`custom.md`}-->

### Controlled
### Controlled vs. uncontrolled value

<!--{include:`control.md`}-->
<!--{include:`controlled.md`}-->

### Selection range

Expand All @@ -108,7 +116,21 @@ If you only need to meet the simple date selection function, you can use the nat

## Accessibility

Learn more in [Accessibility](/guide/accessibility).
### ARIA properties

Has all ARIA properties of the DateInput component by default.

- The `aria-invalid="true"` attribute is added to the `<input>` element when the value is invalid.
- When `label` is set, the `aria-labelledby` attribute is added to the `<input>` element and the `dialog` element and is set to the value of the `id` attribute of `label`.
- Has the `aria-haspopup="dialog"` attribute to indicate that the component has an interactive dialog.

### Keyboard interactions

Has keyboard interaction for the DateInput component by default.

- When the focus is on the calendar, use the <kbd>→</kbd> <kbd>←</kbd> <kbd>↓</kbd> <kbd>↑</kbd> keys to switch dates.
- When the focus is on the calendar, use the <kbd>Enter</kbd> key to select a date.
- When the DatePicker component has `editable={false}` set to disable input, use <kbd>↓</kbd> to move focus to the calendar.

## Props

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/components/date-picker/fragments/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { DatePicker, Stack } from 'rsuite';

const App = () => (
<Stack direction="column" alignItems="flex-start" spacing={6}>
<DatePicker format="MM/yyyy" ranges={[]} />
<DatePicker format="MM/dd/yyyy" />
<DatePicker format="MM/dd/yyyy HH:mm" />
<DatePicker format="MM/dd/yyyy HH:mm:ss" />
<DatePicker format="MM/yyyy" ranges={[]} />
<DatePicker format="HH:mm:ss" ranges={[]} />
</Stack>
);
Expand Down
14 changes: 0 additions & 14 deletions docs/pages/components/date-picker/fragments/control.md

This file was deleted.

28 changes: 28 additions & 0 deletions docs/pages/components/date-picker/fragments/controlled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!--start-code-->

```js
import { DatePicker, Stack } from 'rsuite';

const App = () => {
const [value, setValue] = React.useState(new Date());

const handleChange = (value, event) => {
setValue(value);
console.log('Controlled Change', value);
};

return (
<Stack spacing={10} direction="column" alignItems="flex-start">
<label>Controlled Value:</label>
<DatePicker value={value} onChange={handleChange} />

<label>Uncontrolled Value:</label>
<DatePicker defaultValue={new Date()} />
</Stack>
);
};

ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
16 changes: 5 additions & 11 deletions docs/pages/components/date-picker/fragments/disabled.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ const Label = props => {
const App = () => (
<div className="field">
<Label>Disabled: </Label>
<DatePicker disabled style={{ width: 200 }} />
<DatePicker disabled />
<br />
<Label>Disabled date: </Label>
<DatePicker shouldDisableDate={date => isBefore(date, new Date())} style={{ width: 200 }} />
<DatePicker defaultValue={new Date()} shouldDisableDate={date => isBefore(date, new Date())} />
<br />
<Label>Disabled month: </Label>
<DatePicker
shouldDisableDate={date => isBefore(date, new Date())}
format="yyyy-MM"
style={{ width: 200 }}
/>
<DatePicker shouldDisableDate={date => isBefore(date, new Date())} format="yyyy-MM" />
<br />
<Label>Disabled time: </Label>
<DatePicker
Expand All @@ -31,7 +27,6 @@ const App = () => (
shouldDisableHour={hour => hour < 8 || hour > 18}
shouldDisableMinute={minute => minute % 15 !== 0}
shouldDisableSecond={second => second % 30 !== 0}
style={{ width: 200 }}
/>
<br />
<Label>Hidden time: </Label>
Expand All @@ -42,15 +37,14 @@ const App = () => (
hideHours={hour => hour < 8 || hour > 18}
hideMinutes={minute => minute % 15 !== 0}
hideSeconds={second => second % 30 !== 0}
style={{ width: 200 }}
/>
<hr />
<Label>Read only: </Label>
<DatePicker readOnly defaultValue={new Date()} style={{ width: 200 }} />
<DatePicker readOnly defaultValue={new Date()} />

<hr />
<Label>Plaintext: </Label>
<DatePicker plaintext defaultValue={new Date()} style={{ width: 200 }} />
<DatePicker plaintext defaultValue={new Date()} />
</div>
);

Expand Down
17 changes: 17 additions & 0 deletions docs/pages/components/date-picker/fragments/loading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!--start-code-->

```js
import { DatePicker, Stack } from 'rsuite';

const App = () => (
<Stack direction="column" spacing={8} alignItems="flex-start">
<DatePicker size="lg" loading placeholder="Large" />
<DatePicker size="md" loading placeholder="Medium" />
<DatePicker size="sm" loading placeholder="Small" />
<DatePicker size="xs" loading placeholder="Xsmall" />
</Stack>
);
ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
2 changes: 1 addition & 1 deletion docs/pages/components/date-picker/fragments/range.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DatePicker, InputGroup } from 'rsuite';
const App = () => (
<InputGroup style={{ width: 428 }}>
<DatePicker format="yyyy-MM-dd HH:mm:ss" block appearance="subtle" style={{ width: 230 }} />
<InputGroup.Addon></InputGroup.Addon>
<InputGroup.Addon>to</InputGroup.Addon>
<DatePicker format="yyyy-MM-dd HH:mm:ss" block appearance="subtle" style={{ width: 230 }} />
</InputGroup>
);
Expand Down
15 changes: 7 additions & 8 deletions docs/pages/components/date-picker/fragments/size.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
<!--start-code-->

```js
import { DatePicker } from 'rsuite';
import { DatePicker, Stack } from 'rsuite';

const styles = { width: 200, display: 'block', marginBottom: 10 };
const App = () => (
<>
<DatePicker size="lg" placeholder="Large" style={styles} />
<DatePicker size="md" placeholder="Medium" style={styles} />
<DatePicker size="sm" placeholder="Small" style={styles} />
<DatePicker size="xs" placeholder="Xsmall" style={styles} />
</>
<Stack direction="column" spacing={8} alignItems="flex-start">
<DatePicker size="lg" placeholder="Large" />
<DatePicker size="md" placeholder="Medium" />
<DatePicker size="sm" placeholder="Small" />
<DatePicker size="xs" placeholder="Xsmall" />
</Stack>
);

ReactDOM.render(<App />, document.getElementById('root'));
Expand Down
14 changes: 14 additions & 0 deletions docs/pages/components/date-picker/fragments/with-label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!--start-code-->

```js
import { DatePicker } from 'rsuite';

const App = () => (
<>
<DatePicker label="Birthday:" />
</>
);
ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
38 changes: 30 additions & 8 deletions docs/pages/components/date-picker/zh-CN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

<!--{include:`format-time.md`}-->

### 以 12 小时制的格式显示
### 以 12 小时制的格式显示 (AM/PM)

<!--{include:`format-time-meridian.md`}-->

Expand All @@ -62,9 +62,17 @@

<!--{include:`disabled.md`}-->

### 加载中状态

<!--{include:`loading.md`}-->

### 具有标签

<!--{include:`with-label.md`}-->

### 禁用输入

`DatePicker` 默认是可以通过键盘输入日期和时间的,如果您希望禁用它,可以通过设置 `editable={false}` 来禁用编辑
`DatePicker` 默认是可以通过键盘输入日期和时间的,如果您希望禁用它,可以通过设置 `editable={false}` 来禁用输入

<!--{include:`editable.md`}-->

Expand All @@ -82,13 +90,13 @@
### 自定义快捷项

<!--{include:`custom.md`}-->
示例中点击“Prev Day”,不会关闭浮层,是因为配置 `closeOverlay=false` 参数,该参数用于设置点击快捷项以后是否关闭浮层,默认为 `true`

示例中点击“前一天”,不会关闭浮层,是因为配置 `closeOverlay:boolean` 参数,该参数用于设置点击快捷项以后是否关闭浮层,默认为 `true`
<!--{include:`custom.md`}-->

### 受控
### 受控与非受控的值

<!--{include:`control.md`}-->
<!--{include:`controlled.md`}-->

### 选择范围

Expand All @@ -104,9 +112,23 @@

<!--{include:`native-pickers.md`}-->

## 无障碍设计
## 可访问性

### ARIA 属性

默认拥有 DateInput 组件的 ARIA 属性。

- 当值无效时,`aria-invalid="true"` 属性被添加到 `<input>` 元素。
- 当设置了 `label`, `aria-labelledby` 属性被添加到 `<input>` 元素和 `dialog` 元素上,并将值设置为 `label``id` 属性值。
- 拥有 `aria-haspopup="dialog"` 属性,用于指示组件拥有一个可交互的弹出层。

### 键盘交互

默认拥有 DateInput 组件的键盘交互。

了解更多有关[无障碍设计](/zh/guide/accessibility)的信息。
- 当焦点在日历面板上时,使用 <kbd>→</kbd> <kbd>←</kbd> <kbd>↓</kbd> <kbd>↑</kbd> 键切切换日期。
- 当焦点在日历面板上时,使用 <kbd>Enter</kbd> 键选中日期。
- 当 DatePicker 组件设置了 `editable={false}` 来禁用输入,使用 <kbd>↓</kbd> 将焦点移到日历面板。

## Props

Expand Down

1 comment on commit bc56cb3

@vercel
Copy link

@vercel vercel bot commented on bc56cb3 Dec 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

rsuite-nextjs – ./docs

rsuite.vercel.app
rsuite-nextjs-git-main-rsuite.vercel.app
rsuite-nextjs-rsuite.vercel.app
rsuitejs.com

Please sign in to comment.