diff --git a/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx b/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx index ba91b881b..c7bf6ad70 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx +++ b/packages/core/client/src/schema-component/antd/date-picker/__tests__/date-picker.test.tsx @@ -1,11 +1,14 @@ import React from 'react'; -import { render, screen, sleep, userEvent } from 'testUtils'; +import { render, screen, userEvent, waitFor } from 'testUtils'; import App1 from '../demos/demo1'; import App2 from '../demos/demo2'; import App3 from '../demos/demo3'; import App4 from '../demos/demo4'; import App5 from '../demos/demo5'; import App6 from '../demos/demo6'; +import App7 from '../demos/demo7'; +import App8 from '../demos/demo8'; +import App9 from '../demos/demo9'; describe('DatePicker', () => { it('basic', async () => { @@ -17,15 +20,17 @@ describe('DatePicker', () => { await userEvent.type(input, '2023/05/01 00:00:00'); await userEvent.click(getByText('OK')); - await sleep(100); + await waitFor(() => { + expect(input).toHaveValue('2023/05/01 00:00:00'); + // Read pretty + expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - expect(input).toHaveValue('2023/05/01 00:00:00'); - // Read pretty - expect(screen.getByText('2023/05/01 00:00:00', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); - - // TODO: 本地没有错,但是 CI 上会报错,暂时注释掉 - // Value - // expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument(); + // TODO: 需要有个方法来固定测试环境的时区 + if (!process.env.GITHUB_ACTIONS) { + // Value + expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument(); + } + }); }); it('GMT', async () => { @@ -95,16 +100,17 @@ describe('RangePicker', () => { await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); await userEvent.click(document.querySelector('[title="2023-05-02"]') as HTMLElement); - await sleep(100); + await waitFor(() => { + expect(startInput).toHaveValue('2023-05-01'); + expect(endInput).toHaveValue('2023-05-02'); + // Read pretty + expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument(); - expect(startInput).toHaveValue('2023-05-01'); - expect(endInput).toHaveValue('2023-05-02'); - // Read pretty - expect(screen.getByText('2023-05-01~2023-05-02', { selector: '.ant-description-text' })).toBeInTheDocument(); - - // TODO: 本地没有错,但是 CI 上会报错,暂时注释掉 - // Value - // expect(screen.getByText(/2023-04-30t16:00:00\.000z ~ 2023-05-02t15:59:59\.999z/i)).toBeInTheDocument(); + if (!process.env.GITHUB_ACTIONS) { + // Value + expect(screen.getByText(/2023-04-30t16:00:00\.000z ~ 2023-05-02t15:59:59\.999z/i)).toBeInTheDocument(); + } + }); }); it('non-UTC', async () => { @@ -124,4 +130,66 @@ describe('RangePicker', () => { // Value expect(screen.getByText('2023-05-01 ~ 2023-05-02')).toBeInTheDocument(); }); + + it('showTime=false,gmt=true,utc=true', async () => { + const { container } = render(); + const picker = container.querySelector('.ant-picker') as HTMLElement; + const input = container.querySelector('input') as HTMLElement; + + await userEvent.click(picker); + await userEvent.type(input, '2023/05/01'); + await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); + + await waitFor(() => { + expect(input).toHaveValue('2023/05/01'); + // Read pretty + expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); + + // Value + expect(screen.getByText('2023-05-01T00:00:00.000Z')).toBeInTheDocument(); + }); + }); + + it('showTime=false,gmt=false,utc=true', async () => { + const { container } = render(); + const picker = container.querySelector('.ant-picker') as HTMLElement; + const input = container.querySelector('input') as HTMLElement; + + await userEvent.click(picker); + await userEvent.type(input, '2023/05/01'); + await userEvent.click(document.querySelector('[title="2023-05-01"]') as HTMLElement); + + await waitFor(() => { + expect(input).toHaveValue('2023/05/01'); + // Read pretty + expect(screen.getByText('2023/05/01', { selector: '.ant-description-date-picker' })).toBeInTheDocument(); + + if (!process.env.GITHUB_ACTIONS) { + // Value + // 当 gmt 为 false 时是按照客户端本地时区进行计算的,但是这里的测试环境是 UTC+8,所以会有 8 小时的误差 + expect(screen.getByText('2023-04-30T16:00:00.000Z')).toBeInTheDocument(); + } + }); + }); + + it('showTime=false,gmt=true,utc=true & not input', async () => { + const currentDateString = new Date().toISOString().split('T')[0]; + const { container } = render(); + const picker = container.querySelector('.ant-picker') as HTMLElement; + + await userEvent.click(picker); + const btn = document.querySelector(`[title="${currentDateString}"]`); + expect(btn).toBeInTheDocument(); + await userEvent.click(btn as HTMLElement); + + await waitFor(() => { + // Read pretty + expect( + screen.getByText(currentDateString.replace(/-/g, '/'), { selector: '.ant-description-date-picker' }), + ).toBeInTheDocument(); + + // Value + expect(screen.getByText(`${currentDateString}T00:00:00.000Z`)).toBeInTheDocument(); + }); + }); }); diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx new file mode 100644 index 000000000..aa84bf68a --- /dev/null +++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo10.tsx @@ -0,0 +1,61 @@ +/** + * title: DatePicker + */ +import { FormItem } from '@formily/antd'; +import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; +import React from 'react'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'boolean', + title: `Editable`, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: false, + utc: true, + }, + 'x-reactions': { + target: '*(read1,read2)', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read1: { + type: 'boolean', + title: `Read pretty`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: false, + utc: true, + }, + }, + read2: { + type: 'string', + title: `Value`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': {}, + }, + }, +}; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx new file mode 100644 index 000000000..aa7832297 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo7.tsx @@ -0,0 +1,61 @@ +/** + * title: DatePicker + */ +import { FormItem } from '@formily/antd'; +import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; +import React from 'react'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'boolean', + title: `Editable`, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: true, + utc: true, + }, + 'x-reactions': { + target: '*(read1,read2)', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read1: { + type: 'boolean', + title: `Read pretty`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: true, + utc: true, + }, + }, + read2: { + type: 'string', + title: `Value`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': {}, + }, + }, +}; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx new file mode 100644 index 000000000..aa84bf68a --- /dev/null +++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo8.tsx @@ -0,0 +1,61 @@ +/** + * title: DatePicker + */ +import { FormItem } from '@formily/antd'; +import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; +import React from 'react'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'boolean', + title: `Editable`, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: false, + utc: true, + }, + 'x-reactions': { + target: '*(read1,read2)', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read1: { + type: 'boolean', + title: `Read pretty`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: false, + utc: true, + }, + }, + read2: { + type: 'string', + title: `Value`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': {}, + }, + }, +}; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx b/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx new file mode 100644 index 000000000..aa7832297 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/date-picker/demos/demo9.tsx @@ -0,0 +1,61 @@ +/** + * title: DatePicker + */ +import { FormItem } from '@formily/antd'; +import { DatePicker, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; +import React from 'react'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'boolean', + title: `Editable`, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: true, + utc: true, + }, + 'x-reactions': { + target: '*(read1,read2)', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read1: { + type: 'boolean', + title: `Read pretty`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'DatePicker', + 'x-component-props': { + dateFormat: 'YYYY/MM/DD', + showTime: false, + gmt: true, + utc: true, + }, + }, + read2: { + type: 'string', + title: `Value`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': {}, + }, + }, +}; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/core/client/src/schema-component/antd/date-picker/index.md b/packages/core/client/src/schema-component/antd/date-picker/index.md index 924f70922..223f14afa 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/index.md +++ b/packages/core/client/src/schema-component/antd/date-picker/index.md @@ -16,6 +16,16 @@ group: +### DatePicker (showTime=false,gmt=true,utc=true) + + + + +### DatePicker (showTime=false,gmt=false,utc=true) + + + + ### DatePicker (non-UTC) @@ -32,6 +42,7 @@ group: + ## API 基于 antd 的 [DatePicker](https://ant.design/components/date-picker/#API),新增了以下扩展属性,用于支持 NocoBase 的日期字段设置。 diff --git a/packages/core/client/src/schema-component/antd/date-picker/util.ts b/packages/core/client/src/schema-component/antd/date-picker/util.ts index e106ca70c..9f9191b64 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/util.ts +++ b/packages/core/client/src/schema-component/antd/date-picker/util.ts @@ -73,6 +73,9 @@ export const mapDatePicker = function () { value: str2moment(props.value, props), onChange: (value: moment.Moment) => { if (onChange) { + if (!props.showTime) { + value.startOf('day'); + } onChange(moment2str(value, props)); } }, diff --git a/scripts/setupVitest.ts b/scripts/setupVitest.ts index 5a635c485..31f220c83 100644 --- a/scripts/setupVitest.ts +++ b/scripts/setupVitest.ts @@ -9,6 +9,9 @@ import 'jsdom-worker'; import { vi } from 'vitest'; import '../packages/core/client/src/i18n'; +// 设置 node 环境下的默认时区为中国标准时间, 即东八区 +process.env.TZ = 'Asia/Shanghai'; + // 解决 TypeError: window.matchMedia is not a function // 参见: https://github.com/vitest-dev/vitest/issues/821#issuecomment-1046954558 Object.defineProperty(window, 'matchMedia', {