diff --git a/src/controls/DateTimeControl.test.tsx b/src/controls/DateTimeControl.test.tsx index 2b1afca..6c0e03b 100644 --- a/src/controls/DateTimeControl.test.tsx +++ b/src/controls/DateTimeControl.test.tsx @@ -6,9 +6,14 @@ import { render } from "../common/test-render" import { dateTimeSchema } from "../testSchemas/dateTimeSchema" import { UISchema } from "../ui-schema" -const EXAMPLE_DATESTRING = "2021-08-09 12:34:56" -const USER_DATESTRING = EXAMPLE_DATESTRING.replace(" ", "") +const EMPTY_DATESTRING = "" +const INPUT_MASK = "YYYY-MM-DD HH:mm:ss" +const USER_NOTADATESTRING = "not a date" +const EXAMPLE_DATESTRING = "2021-08-09T12:34:56" +const RENDERED_DATESTRING = EXAMPLE_DATESTRING.replace("T", " ") +const USER_DATESTRING = EXAMPLE_DATESTRING.replace("T", "") const TITLE = dateTimeSchema.properties.dateTime.title +const REQUIRED_TEXT = `${TITLE} is required` test("renders the date that the user selects", async () => { render({ @@ -17,7 +22,7 @@ test("renders the date that the user selects", async () => { const input = await screen.findByLabelText(TITLE) await userEvent.type(input, USER_DATESTRING) - await waitFor(() => expect(input).toHaveValue(EXAMPLE_DATESTRING)) + await waitFor(() => expect(input).toHaveValue(RENDERED_DATESTRING)) }) test("renders default date when present", async () => { @@ -33,7 +38,7 @@ test("renders default date when present", async () => { }, }) const input = await screen.findByLabelText(TITLE) - expect(input).toHaveValue(EXAMPLE_DATESTRING) + expect(input).toHaveValue(RENDERED_DATESTRING) }) test("updates jsonforms data as expected", async () => { @@ -50,7 +55,7 @@ test("updates jsonforms data as expected", async () => { await userEvent.click(screen.getByText("Submit")) await waitFor(() => { expect(data).toEqual({ - dateTime: EXAMPLE_DATESTRING, + dateTime: RENDERED_DATESTRING, }) }) }) @@ -65,7 +70,7 @@ test("renders required message if no value and interaction", async () => { const input = await screen.findByLabelText(TITLE) await userEvent.clear(input) await userEvent.tab() - await screen.findByText(`${TITLE} is required`) + await screen.findByText(REQUIRED_TEXT) }) test(" does not show required message if not requried", async () => { @@ -76,7 +81,7 @@ test(" does not show required message if not requried", async () => { await userEvent.clear(input) await userEvent.tab() await waitFor(() => { - expect(screen.queryByText(`${TITLE} is required`)).toBeNull() + expect(screen.queryByText(REQUIRED_TEXT)).toBeNull() }) }) @@ -131,3 +136,47 @@ test("renders default props if invalid props are submitted", async () => { // since our default doesn't show time await screen.findByText("Today") }) + +test("it does not error on failure to parse date", async () => { + render({ + schema: { + ...dateTimeSchema, + properties: { + dateTime: { + ...dateTimeSchema.properties.dateTime, + format: "date-time", + default: EMPTY_DATESTRING, + }, + }, + }, + }) + const input = await screen.findByLabelText(TITLE) + await userEvent.type(input, USER_NOTADATESTRING) + await userEvent.tab() + await waitFor(() => { + expect(input).toHaveValue(EMPTY_DATESTRING) + }) +}) + +test("it renders an input mask by default as user types", async () => { + render({ + schema: dateTimeSchema, + }) + const input = await screen.findByLabelText(TITLE) + await userEvent.click(input) + await waitFor(() => { + expect(input).toHaveValue(INPUT_MASK) + }) +}) + +test("it renders form data for forms with existing values (edit)", async () => { + const data: Record = { + dateTime: EXAMPLE_DATESTRING, + } + render({ + schema: dateTimeSchema, + data, + }) + const input = await screen.findByLabelText(TITLE) + expect(input).toHaveValue(RENDERED_DATESTRING) +}) diff --git a/src/controls/DateTimeControl.tsx b/src/controls/DateTimeControl.tsx index b1571d9..15f1e31 100644 --- a/src/controls/DateTimeControl.tsx +++ b/src/controls/DateTimeControl.tsx @@ -1,4 +1,4 @@ -import { memo } from "react" +import { memo, useCallback, useEffect } from "react" import type { ControlProps as JSFControlProps } from "@jsonforms/core" import { withJsonFormsControlProps } from "@jsonforms/react" import { DatePicker, type DatePickerProps, Form } from "antd" @@ -26,6 +26,19 @@ function getProps(options: unknown): DateTimeControlOptions { return DEFAULT_PROPS } +function getInitialValue( + data: unknown, + schemaDefault: unknown, +): string | undefined { + if (typeof data === "string" && data !== "") { + return data + } + if (typeof schemaDefault === "string" && schemaDefault !== "") { + return schemaDefault + } + return undefined +} + export function DateTimeControl({ handleChange, path, @@ -35,12 +48,25 @@ export function DateTimeControl({ schema, uischema, visible, + data, }: ControlProps) { + const setInitialValue = useCallback( + (value: string | undefined) => { + const coercedValue = value ? dayjs(value) : value + handleChange(path, value) + return coercedValue + }, + [handleChange, path], + ) + const form = Form.useFormInstance() + useEffect(() => { + form.setFieldValue( + path, + setInitialValue(getInitialValue(data, schema.default)), + ) + }, [data, form, path, schema.default, setInitialValue]) if (!visible) return null - const initialValue = - typeof schema.default === "string" ? dayjs(schema.default) : undefined - const rules: Rule[] = [{ required, message: `${label} is required` }] const formItemProps = @@ -60,7 +86,6 @@ export function DateTimeControl({ required={required} validateTrigger={["onBlur"]} rules={rules} - initialValue={initialValue} {...formItemProps} >