From 30b4ed77a1ea877a75b65b69c6ce897a231660f7 Mon Sep 17 00:00:00 2001 From: katherinehhh Date: Thu, 7 Mar 2024 10:48:49 +0800 Subject: [PATCH] fix: dateTime format configured in table is invalid (#3630) * fix: datetime format configured in the table is invalid * test: date display format * refactor: default timeformat * test: datetime display format --- .../DatePicker/__e2e__/schemaSettings.test.ts | 54 +++ .../component/DatePicker/__e2e__/utils.ts | 331 ++++++++++++++++++ .../datePickerComponentFieldSettings.tsx | 5 +- .../DateFormat/ExpiresRadio.tsx | 6 +- .../SchemaSettingsDateFormat.tsx | 8 +- .../fields/datetime/schemaSettings.test.ts | 16 +- 6 files changed, 407 insertions(+), 13 deletions(-) create mode 100644 packages/core/client/src/modules/fields/component/DatePicker/__e2e__/schemaSettings.test.ts create mode 100644 packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts diff --git a/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/schemaSettings.test.ts new file mode 100644 index 0000000000..a3b1c28228 --- /dev/null +++ b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/schemaSettings.test.ts @@ -0,0 +1,54 @@ +import { expect, expectSettingsMenu, test } from '@nocobase/test/e2e'; +import dayjs from 'dayjs'; +import { oneTableBlockWithDatetimeFields, oneFormBlockWithDatetimeFields } from './utils'; + +test('Date display format in form', async ({ page, mockPage }) => { + await mockPage(oneFormBlockWithDatetimeFields).goto(); + + await expectSettingsMenu({ + page, + showMenu: async () => { + await page.getByPlaceholder('Select date').hover(); + await page.getByLabel('block-item-CollectionField').hover(); + await page.getByLabel('designer-schema-settings-CollectionField-fieldSettings:FormItem-general').hover(); + }, + supportedOptions: [ + 'Edit field title', + 'Display title', + 'Edit description', + 'Edit tooltip', + 'Required', + 'Set default value', + 'Pattern', + 'Date display format', + 'Delete', + ], + }); + await page.getByText('Date display format').click(); + await page.getByLabel('Show time').check(); + await page.getByRole('button', { name: 'HH:mm:ss', exact: true }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await page.getByPlaceholder('Select date').click(); + await page.getByText('Now').click(); + const value = await page.getByPlaceholder('Select date').inputValue(); + expect(value).toBe(dayjs(value).format('YYYY-MM-DD HH:mm:ss')); +}); + +test('Date display format in table', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneTableBlockWithDatetimeFields).goto(); + const date = new Date().toDateString(); + await mockRecord('general', { datetime: date }); + await expectSettingsMenu({ + page, + showMenu: async () => { + await page.getByRole('button', { name: 'datetime' }).hover(); + await page.getByLabel('designer-schema-settings-TableV2.Column-fieldSettings:TableColumn-general').hover(); + }, + supportedOptions: ['Custom column title', 'Column width', 'Sortable', 'Date display format', 'Delete'], + }); + await page.getByText('Date display format').click(); + await page.getByLabel('Show time').check(); + await page.getByRole('button', { name: 'hh:mm:ss a' }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect(page.getByRole('button', { name: dayjs(date).format('YYYY-MM-DD hh:mm:ss a') })).toBeVisible(); +}); diff --git a/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts new file mode 100644 index 0000000000..e583bac73b --- /dev/null +++ b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts @@ -0,0 +1,331 @@ +import { generalWithDatetime, PageConfig } from '@nocobase/test/e2e'; +/** + * 1. 一个 Table 区块 + * 5. 所有字段都是 datetime 字段 + */ +export const oneTableBlockWithDatetimeFields: PageConfig = { + collections: generalWithDatetime, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + rzvrcrafmff: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + irc4mhtog83: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + bv65e6wkcef: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + gq797u6o8ti: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'general:list', + 'x-decorator-props': { + collection: 'general', + dataSource: 'main', + resource: 'general', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + disableTemplate: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'TableActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-uid': 'nd5jqapjy3w', + 'x-async': false, + 'x-index': 1, + }, + pkdf64ojs4s: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + gk4b4e4wp8b: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': 'cj0u0c1ajp7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bqjujfh7zdw', + 'x-async': false, + 'x-index': 1, + }, + nlf077nijaa: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + properties: { + datetime: { + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'general.datetime', + 'x-component': 'CollectionField', + 'x-component-props': {}, + 'x-read-pretty': true, + 'x-decorator': null, + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-uid': 'uv26aslikjn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1eten1jj0gt', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 't47ph1tmsa0', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '7zqek220iua', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'jh19krqxftf', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lbfsv3pz0qi', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xlc7hd8s8az', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ml7rypfikcm', + 'x-async': true, + 'x-index': 1, + }, +}; + +/** + * 1. 一个 Form 区块 + * 5. 所有字段都是 datetime 字段 + */ +export const oneFormBlockWithDatetimeFields: PageConfig = { + collections: generalWithDatetime, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + rzvrcrafmff: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + annx4g65mtu: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + ov02gf15cw5: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + qx7c4utyugy: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'general:create', + 'x-decorator': 'FormBlockProvider', + 'x-decorator-props': { + dataSource: 'main', + resource: 'general', + collection: 'general', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + 'x-component-props': {}, + properties: { + '00mj93xz46e': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'FormItemInitializers', + properties: { + '269bdhc35j2': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '7nkbge7b0bo': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + datetime: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'general.datetime', + 'x-component-props': {}, + 'x-uid': 'lpcbskripdi', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ykx953ujfur', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bnz69w2wylm', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rf04idbxdeo', + 'x-async': false, + 'x-index': 1, + }, + '0vnfo3li57v': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'FormActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + marginTop: 24, + }, + }, + 'x-uid': '0m3zbok6yhn', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'gusa79tzjit', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ntdpcoeszfh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4qk0nzqrmtn', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hoxpqzqrumd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xlc7hd8s8az', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ml7rypfikcm', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/modules/fields/component/DatePicker/datePickerComponentFieldSettings.tsx b/packages/core/client/src/modules/fields/component/DatePicker/datePickerComponentFieldSettings.tsx index bea3df021e..62488de03d 100644 --- a/packages/core/client/src/modules/fields/component/DatePicker/datePickerComponentFieldSettings.tsx +++ b/packages/core/client/src/modules/fields/component/DatePicker/datePickerComponentFieldSettings.tsx @@ -1,6 +1,7 @@ import { useFieldSchema } from '@formily/react'; import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings'; import { SchemaSettingsDateFormat } from '../../../../schema-settings/SchemaSettingsDateFormat'; +import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; export const datePickerComponentFieldSettings = new SchemaSettings({ name: 'fieldSettings:component:DatePicker', @@ -9,7 +10,9 @@ export const datePickerComponentFieldSettings = new SchemaSettings({ name: 'dateDisplayFormat', Component: SchemaSettingsDateFormat as any, useComponentProps() { - const fieldSchema = useFieldSchema(); + const schema = useFieldSchema(); + const { fieldSchema: tableColumnSchema } = useColumnSchema(); + const fieldSchema = tableColumnSchema || schema; return { fieldSchema, }; diff --git a/packages/core/client/src/schema-settings/DateFormat/ExpiresRadio.tsx b/packages/core/client/src/schema-settings/DateFormat/ExpiresRadio.tsx index 2823db613c..ffa5120d3a 100644 --- a/packages/core/client/src/schema-settings/DateFormat/ExpiresRadio.tsx +++ b/packages/core/client/src/schema-settings/DateFormat/ExpiresRadio.tsx @@ -84,8 +84,10 @@ const InternalExpiresRadio = (props) => { ); } return ( - - {v.label} + + + {v.label} + ); })} diff --git a/packages/core/client/src/schema-settings/SchemaSettingsDateFormat.tsx b/packages/core/client/src/schema-settings/SchemaSettingsDateFormat.tsx index 8e74a6bfad..89ff1f31f2 100644 --- a/packages/core/client/src/schema-settings/SchemaSettingsDateFormat.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettingsDateFormat.tsx @@ -19,7 +19,9 @@ export const SchemaSettingsDateFormat = function DateFormatConfig(props: { field collectionField?.uiSchema?.['x-component-props']?.dateFormat || 'YYYY-MM-DD'; const timeFormatDefaultValue = - fieldSchema?.['x-component-props']?.timeFormat || collectionField?.uiSchema?.['x-component-props']?.timeFormat; + fieldSchema?.['x-component-props']?.timeFormat || + collectionField?.uiSchema?.['x-component-props']?.timeFormat || + 'HH:mm:ss'; return ( { - f.setComponentProps({ ...data }); + if (f.props.name === fieldSchema.name) { + f.setComponentProps({ ...data }); + } }); dn.emit('patch', { schema, diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/datetime/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/datetime/schemaSettings.test.ts index 3657e74c96..03ab2303c6 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/datetime/schemaSettings.test.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/datetime/schemaSettings.test.ts @@ -126,7 +126,7 @@ test.describe('form item & create form', () => { .hover(); })(page, 'datetime'); await page.getByRole('menuitem', { name: 'Date display format' }).click(); - await page.getByLabel('YYYY/MM/DD').click(); + await page.getByRole('button', { name: 'DD/MM/YYYY' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); // 输入一个值,然后验证格式是否正确 @@ -140,7 +140,7 @@ test.describe('form item & create form', () => { page .getByLabel('block-item-CollectionField-general-form-general.datetime-datetime') .getByPlaceholder('Select date'), - ).toHaveValue(dayjs().format('YYYY/MM/DD')); + ).toHaveValue(dayjs().format('DD/MM/YYYY')); }); }); @@ -236,14 +236,14 @@ test.describe('form item & edit form', () => { .hover(); })(page, 'datetime'); await page.getByRole('menuitem', { name: 'Date display format' }).click(); - await page.getByLabel('YYYY/MM/DD').click(); + await page.getByRole('button', { name: 'DD/MM/YYYY' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( page .getByLabel('block-item-CollectionField-general-form-general.datetime-datetime') .getByPlaceholder('Select date'), - ).toHaveValue(dayjs(record.datetime).format('YYYY/MM/DD')); + ).toHaveValue(dayjs(record.datetime).format('DD/MM/YYYY')); }); }); @@ -285,11 +285,11 @@ test.describe('form item & view form', () => { .hover(); })(page, 'datetime'); await page.getByRole('menuitem', { name: 'Date display format' }).click(); - await page.getByLabel('YYYY/MM/DD').click(); + await page.getByRole('button', { name: 'DD/MM/YYYY' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect(page.getByLabel('block-item-CollectionField-general-form-general.datetime-datetime')).toHaveText( - `datetime:${dayjs(record.datetime).format('YYYY/MM/DD')}`, + `datetime:${dayjs(record.datetime).format('DD/MM/YYYY')}`, ); }); }); @@ -318,10 +318,10 @@ test.describe('table column & table', () => { await createColumnItem(page, 'datetime'); await showSettingsMenu(page, 'datetime'); await page.getByRole('menuitem', { name: 'Date display format' }).click(); - await page.getByLabel('MM/DD/YY').click(); + await page.getByRole('button', { name: 'DD/MM/YYYY' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( - page.getByRole('cell', { name: dayjs(records[0].datetime).format('MM/DD/YY'), exact: true }), + page.getByRole('cell', { name: dayjs(records[0].datetime).format('DD/MM/YYYY'), exact: true }), ).toBeVisible(); }); });