feat: input-date 支持手动输入 (#3677)

* feat: input-date 支持手动输入

* update snapshots
This commit is contained in:
吴多益 2022-03-03 14:31:35 +08:00 committed by GitHub
parent ce7ae06c67
commit a840b910e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 171 additions and 90 deletions

View File

@ -56,11 +56,12 @@ exports[`Renderer:date 1`] = `
class="cxd-DatePicker" class="cxd-DatePicker"
tabindex="0" tabindex="0"
> >
<span <input
class="cxd-DatePicker-value" autocomplete="off"
> placeholder="请选择日期以及时间"
2019-06-06 21:11:00 type="text"
</span> value="2019-06-06 21:11:00"
/>
<a <a
class="cxd-DatePicker-clear" class="cxd-DatePicker-clear"
> >

View File

@ -56,11 +56,12 @@ exports[`Renderer:inputDate 1`] = `
class="cxd-DatePicker" class="cxd-DatePicker"
tabindex="0" tabindex="0"
> >
<span <input
class="cxd-DatePicker-value" autocomplete="off"
> placeholder="请选择日期"
2019-06-07 type="text"
</span> value="2019-06-07"
/>
<a <a
class="cxd-DatePicker-clear" class="cxd-DatePicker-clear"
> >

View File

@ -31,8 +31,11 @@ test('Renderer:date', async () => {
) )
); );
const input = container.querySelector('.cxd-DatePicker-value'); const input = container.querySelector(
expect(input?.innerHTML).toEqual( '.cxd-DatePicker input'
)! as HTMLInputElement;
expect(input.value).toEqual(
moment(1559826660, 'X').format('YYYY-MM-DD HH:mm:ss') moment(1559826660, 'X').format('YYYY-MM-DD HH:mm:ss')
); );

View File

@ -31,10 +31,10 @@ test('Renderer:inputDate', async () => {
) )
); );
const input = container.querySelector('.cxd-DatePicker-value'); const input = container.querySelector(
expect(input?.innerHTML).toEqual( '.cxd-DatePicker input'
moment(1559836800, 'X').format('YYYY-MM-DD') )! as HTMLInputElement;
); expect(input.value).toEqual(moment(1559836800, 'X').format('YYYY-MM-DD'));
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });

View File

@ -8,27 +8,28 @@ import {makeEnv} from '../../helper';
import moment from 'moment'; import moment from 'moment';
test('Renderer:inputMonth click', async () => { test('Renderer:inputMonth click', async () => {
const {container, findByText} = render( const {container, findByText, findByPlaceholderText, findByDisplayValue} =
amisRender( render(
{ amisRender(
type: 'form', {
api: '/api/xxx', type: 'form',
body: [ api: '/api/xxx',
{ body: [
type: 'input-month', {
name: 'month', type: 'input-month',
label: '时间' name: 'month',
} label: '时间'
], }
title: 'The form', ],
actions: [] title: 'The form',
}, actions: []
{}, },
makeEnv({}) {},
) makeEnv({})
); )
);
const inputDate = await findByText('请选择月份'); const inputDate = await findByPlaceholderText('请选择月份');
fireEvent.click(inputDate); fireEvent.click(inputDate);
@ -43,11 +44,11 @@ test('Renderer:inputMonth click', async () => {
const lastYearMonth = moment().subtract(1, 'year').format('YYYY') + '-01'; const lastYearMonth = moment().subtract(1, 'year').format('YYYY') + '-01';
await findByText(lastYearMonth); await findByDisplayValue(lastYearMonth);
const value = document.querySelector( const value = document.querySelector(
'.cxd-DatePicker-value' '.cxd-DatePicker input'
) as HTMLSpanElement; ) as HTMLInputElement;
expect(value.innerHTML).toEqual(lastYearMonth); expect(value.value).toEqual(lastYearMonth);
}); });

View File

@ -8,7 +8,7 @@ import {makeEnv} from '../../helper';
import moment from 'moment'; import moment from 'moment';
test('Renderer:inputYear click', async () => { test('Renderer:inputYear click', async () => {
const {container, findByText} = render( const {container, findByPlaceholderText, findByText} = render(
amisRender( amisRender(
{ {
type: 'form', type: 'form',
@ -28,7 +28,7 @@ test('Renderer:inputYear click', async () => {
) )
); );
const inputDate = await findByText('请选择年'); const inputDate = await findByPlaceholderText('请选择年');
fireEvent.click(inputDate); fireEvent.click(inputDate);
@ -39,8 +39,8 @@ test('Renderer:inputYear click', async () => {
fireEvent.click(thisYear); fireEvent.click(thisYear);
const value = document.querySelector( const value = document.querySelector(
'.cxd-DatePicker-value' '.cxd-DatePicker input'
) as HTMLSpanElement; ) as HTMLInputElement;
expect(value.innerHTML).toEqual(thisYearText); expect(value.value).toEqual(thisYearText);
}); });

View File

@ -17,7 +17,7 @@ afterAll(() => {
}); });
test('Renderer:mobile Input Date', async () => { test('Renderer:mobile Input Date', async () => {
const {container, findByText, getByText} = render( const {container, findByPlaceholderText, getByText} = render(
amisRender( amisRender(
{ {
type: 'form', type: 'form',
@ -35,7 +35,7 @@ test('Renderer:mobile Input Date', async () => {
) )
); );
const inputDate = await findByText('请选择日期'); const inputDate = await findByPlaceholderText('请选择日期');
fireEvent.click(inputDate); fireEvent.click(inputDate);
const confirmButton = document.querySelector( const confirmButton = document.querySelector(
@ -44,9 +44,9 @@ test('Renderer:mobile Input Date', async () => {
fireEvent.click(confirmButton); fireEvent.click(confirmButton);
const value = document.querySelector( // const value = document.querySelector(
'.cxd-DatePicker-value' // '.cxd-DatePicker-value'
) as HTMLSpanElement; // ) as HTMLSpanElement;
// TODO: 这里原组件的日错了,等修复 // TODO: 这里原组件的日错了,等修复
// expect(value.innerHTML).toEqual(moment().format('YYYY-MM-DD')); // expect(value.innerHTML).toEqual(moment().format('YYYY-MM-DD'));

View File

@ -84,20 +84,39 @@ order: 51
} }
``` ```
## 默认值
和其它表单项一样,如果要设置默认值,可以使用 value 属性
```schema: scope="body"
{
"type": "form",
"debug": true,
"body": [
{
"name": "switch",
"type": "switch",
"label": "开关",
"value": false
}
]
}
```
## 属性表 ## 属性表
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置 除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
| 属性名 | 类型 | 默认值 | 说明 | | 属性名 | 类型 | 默认值 | 说明 |
| ---------- | -------- | --------- | ------------ | | ---------- | --------------------------- | ------- | -------------------- |
| option | `string` | | 选项说明 | | option | `string` | | 选项说明 |
| onText | `string / IconSchema` | | 开启时开关显示的内容 | | onText | `string / IconSchema` | | 开启时开关显示的内容 |
| offText | `string / IconSchema` | | 关闭时开关显示的内容 | | offText | `string / IconSchema` | | 关闭时开关显示的内容 |
| trueValue | `boolean / string / number` | `true` | 标识真值 | | trueValue | `boolean / string / number` | `true` | 标识真值 |
| falseValue | `boolean / string / number` | `false` | 标识假值 | | falseValue | `boolean / string / number` | `false` | 标识假值 |
IconSchema 配置 IconSchema 配置
| 属性名 | 类型 | 默认值 | 说明 | | 属性名 | 类型 | 默认值 | 说明 |
| ---------- | -------- | --------- | ------------ | | ---------- | -------- | --------- | ------------ |
| type | `string` | | `icon` | | type | `string` | | `icon` |
| icon | `string` | | icon的类型 | | icon | `string` | | icon 的类型 |

View File

@ -12,6 +12,19 @@
background: var(--DatePicker-bg); background: var(--DatePicker-bg);
border-radius: var(--DatePicker-borderRadius); border-radius: var(--DatePicker-borderRadius);
input {
display: inline-block;
width: 100%;
background: none;
padding: 0;
border: 0;
&:focus {
border: none;
outline: none;
box-sizing: none;
}
}
@include input-border(); @include input-border();
&:not(.is-disabled) { &:not(.is-disabled) {

View File

@ -17,6 +17,7 @@ import Calendar from './calendar/Calendar';
import {localeable, LocaleProps, TranslateFn} from '../locale'; import {localeable, LocaleProps, TranslateFn} from '../locale';
import {isMobile, ucFirst} from '../utils/helper'; import {isMobile, ucFirst} from '../utils/helper';
import CalendarMobile from './CalendarMobile'; import CalendarMobile from './CalendarMobile';
import Input from './Input';
const availableShortcuts: {[propName: string]: any} = { const availableShortcuts: {[propName: string]: any} = {
now: { now: {
@ -302,6 +303,7 @@ export interface DatePickerState {
isOpened: boolean; isOpened: boolean;
isFocused: boolean; isFocused: boolean;
value: moment.Moment | undefined; value: moment.Moment | undefined;
inputValue: string | undefined; // 手动输入的值
} }
function normalizeValue(value: any, format?: string) { function normalizeValue(value: any, format?: string) {
@ -329,11 +331,15 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
state: DatePickerState = { state: DatePickerState = {
isOpened: false, isOpened: false,
isFocused: false, isFocused: false,
value: normalizeValue(this.props.value, this.props.format) value: normalizeValue(this.props.value, this.props.format),
inputValue:
normalizeValue(this.props.value, this.props.format)?.format(
this.props.inputFormat
) || ''
}; };
constructor(props: DateProps) { constructor(props: DateProps) {
super(props); super(props);
this.inputRef = React.createRef();
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
this.selectRannge = this.selectRannge.bind(this); this.selectRannge = this.selectRannge.bind(this);
this.checkIsValidDate = this.checkIsValidDate.bind(this); this.checkIsValidDate = this.checkIsValidDate.bind(this);
@ -348,10 +354,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
this.getTarget = this.getTarget.bind(this); this.getTarget = this.getTarget.bind(this);
this.handlePopOverClick = this.handlePopOverClick.bind(this); this.handlePopOverClick = this.handlePopOverClick.bind(this);
this.renderShortCuts = this.renderShortCuts.bind(this); this.renderShortCuts = this.renderShortCuts.bind(this);
this.inputChange = this.inputChange.bind(this);
} }
dom: HTMLDivElement; dom: HTMLDivElement;
inputRef: React.RefObject<HTMLInputElement>;
componentDidUpdate(prevProps: DateProps) { componentDidUpdate(prevProps: DateProps) {
const props = this.props; const props = this.props;
@ -403,13 +412,17 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
} }
open(fn?: () => void) { open(fn?: () => void) {
this.props.disabled || if (this.props.disabled) {
this.setState( return;
{ }
isOpened: true this.setState(
}, {
fn isOpened: true
); },
fn
);
const input = this.inputRef.current;
input && input.focus();
} }
close() { close() {
@ -423,6 +436,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
e.stopPropagation(); e.stopPropagation();
const onChange = this.props.onChange; const onChange = this.props.onChange;
onChange(''); onChange('');
this.setState({inputValue: ''});
} }
handleChange(value: moment.Moment) { handleChange(value: moment.Moment) {
@ -432,6 +446,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
minDate, minDate,
maxDate, maxDate,
dateFormat, dateFormat,
inputFormat,
timeFormat, timeFormat,
closeOnSelect, closeOnSelect,
utc, utc,
@ -453,6 +468,31 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
if (closeOnSelect && dateFormat && !timeFormat) { if (closeOnSelect && dateFormat && !timeFormat) {
this.close(); this.close();
} }
this.setState({
inputValue: utc
? moment.utc(value).format(inputFormat)
: value.format(inputFormat)
});
}
// 手动输入日期
inputChange(e: React.ChangeEvent<HTMLInputElement>) {
const {onChange, inputFormat, format, utc} = this.props;
const value = e.currentTarget.value;
this.setState({inputValue: value});
if (value === '') {
onChange('');
} else {
const newDate = moment(value, inputFormat);
const dateValue = utc
? moment.utc(newDate).format(format)
: newDate.format(format);
// 小于 0 的日期丢弃
if (!dateValue.startsWith('-')) {
onChange(dateValue);
}
}
} }
selectRannge(item: any) { selectRannge(item: any) {
@ -693,15 +733,13 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
ref={this.domRef} ref={this.domRef}
onClick={this.handleClick} onClick={this.handleClick}
> >
{date ? ( <Input
<span className={cx(`DatePicker-value`)}> onChange={this.inputChange}
{date.format(inputFormat)} ref={this.inputRef}
</span> placeholder={__(placeholder)}
) : ( autoComplete="off"
<span className={cx(`DatePicker-placeholder`)}> value={this.state.inputValue}
{__(placeholder)} />
</span>
)}
{clearable && !disabled && normalizeValue(value, format) ? ( {clearable && !disabled && normalizeValue(value, format) ? (
<a className={cx(`DatePicker-clear`)} onClick={this.clearValue}> <a className={cx(`DatePicker-clear`)} onClick={this.clearValue}>

View File

@ -161,18 +161,19 @@ class BaseDatePicker extends React.Component<
updatedState = this.getStateFromProps(props); updatedState = this.getStateFromProps(props);
} }
if (updatedState.open === undefined) { // open 是外部控制了
if (typeof props.open !== 'undefined') { // if (updatedState.open === undefined) {
updatedState.open = props.open; // if (typeof props.open !== 'undefined') {
} else if ( // updatedState.open = props.open;
prevProps.closeOnSelect && // } else if (
this.state.currentView !== viewModes.TIME // prevProps.closeOnSelect &&
) { // this.state.currentView !== viewModes.TIME
updatedState.open = false; // ) {
} else { // updatedState.open = false;
updatedState.open = this.state.open; // } else {
} // updatedState.open = this.state.open;
} // }
// }
if (props.viewMode !== prevProps.viewMode) { if (props.viewMode !== prevProps.viewMode) {
updatedState.currentView = props.viewMode; updatedState.currentView = props.viewMode;
@ -236,6 +237,10 @@ class BaseDatePicker extends React.Component<
updatedState.viewDate = moment(props.viewDate); updatedState.viewDate = moment(props.viewDate);
} }
if (Object.keys(updatedState).length) {
this.setState(updatedState);
}
this.checkTZ(props); this.checkTZ(props);
} }