Skip to content

Commit fadef43

Browse files
authored
fix(DateTimeControl): consume data prop if present to populate edit forms (#105)
1 parent 7f1994c commit fadef43

3 files changed

Lines changed: 97 additions & 12 deletions

File tree

src/controls/DateTimeControl.test.tsx

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ import { render } from "../common/test-render"
66
import { dateTimeSchema } from "../testSchemas/dateTimeSchema"
77
import { UISchema } from "../ui-schema"
88

9-
const EXAMPLE_DATESTRING = "2021-08-09 12:34:56"
10-
const USER_DATESTRING = EXAMPLE_DATESTRING.replace(" ", "")
9+
const EMPTY_DATESTRING = ""
10+
const INPUT_MASK = "YYYY-MM-DD HH:mm:ss"
11+
const USER_NOTADATESTRING = "not a date"
12+
const EXAMPLE_DATESTRING = "2021-08-09T12:34:56"
13+
const RENDERED_DATESTRING = EXAMPLE_DATESTRING.replace("T", " ")
14+
const USER_DATESTRING = EXAMPLE_DATESTRING.replace("T", "")
1115
const TITLE = dateTimeSchema.properties.dateTime.title
16+
const REQUIRED_TEXT = `${TITLE} is required`
1217

1318
test("renders the date that the user selects", async () => {
1419
render({
@@ -17,7 +22,7 @@ test("renders the date that the user selects", async () => {
1722
const input = await screen.findByLabelText(TITLE)
1823
await userEvent.type(input, USER_DATESTRING)
1924

20-
await waitFor(() => expect(input).toHaveValue(EXAMPLE_DATESTRING))
25+
await waitFor(() => expect(input).toHaveValue(RENDERED_DATESTRING))
2126
})
2227

2328
test("renders default date when present", async () => {
@@ -33,7 +38,7 @@ test("renders default date when present", async () => {
3338
},
3439
})
3540
const input = await screen.findByLabelText(TITLE)
36-
expect(input).toHaveValue(EXAMPLE_DATESTRING)
41+
expect(input).toHaveValue(RENDERED_DATESTRING)
3742
})
3843

3944
test("updates jsonforms data as expected", async () => {
@@ -50,7 +55,7 @@ test("updates jsonforms data as expected", async () => {
5055
await userEvent.click(screen.getByText("Submit"))
5156
await waitFor(() => {
5257
expect(data).toEqual({
53-
dateTime: EXAMPLE_DATESTRING,
58+
dateTime: RENDERED_DATESTRING,
5459
})
5560
})
5661
})
@@ -65,7 +70,7 @@ test("renders required message if no value and interaction", async () => {
6570
const input = await screen.findByLabelText(TITLE)
6671
await userEvent.clear(input)
6772
await userEvent.tab()
68-
await screen.findByText(`${TITLE} is required`)
73+
await screen.findByText(REQUIRED_TEXT)
6974
})
7075

7176
test(" does not show required message if not requried", async () => {
@@ -76,7 +81,7 @@ test(" does not show required message if not requried", async () => {
7681
await userEvent.clear(input)
7782
await userEvent.tab()
7883
await waitFor(() => {
79-
expect(screen.queryByText(`${TITLE} is required`)).toBeNull()
84+
expect(screen.queryByText(REQUIRED_TEXT)).toBeNull()
8085
})
8186
})
8287

@@ -131,3 +136,47 @@ test("renders default props if invalid props are submitted", async () => {
131136
// since our default doesn't show time
132137
await screen.findByText("Today")
133138
})
139+
140+
test("it does not error on failure to parse date", async () => {
141+
render({
142+
schema: {
143+
...dateTimeSchema,
144+
properties: {
145+
dateTime: {
146+
...dateTimeSchema.properties.dateTime,
147+
format: "date-time",
148+
default: EMPTY_DATESTRING,
149+
},
150+
},
151+
},
152+
})
153+
const input = await screen.findByLabelText(TITLE)
154+
await userEvent.type(input, USER_NOTADATESTRING)
155+
await userEvent.tab()
156+
await waitFor(() => {
157+
expect(input).toHaveValue(EMPTY_DATESTRING)
158+
})
159+
})
160+
161+
test("it renders an input mask by default as user types", async () => {
162+
render({
163+
schema: dateTimeSchema,
164+
})
165+
const input = await screen.findByLabelText(TITLE)
166+
await userEvent.click(input)
167+
await waitFor(() => {
168+
expect(input).toHaveValue(INPUT_MASK)
169+
})
170+
})
171+
172+
test("it renders form data for forms with existing values (edit)", async () => {
173+
const data: Record<string, unknown> = {
174+
dateTime: EXAMPLE_DATESTRING,
175+
}
176+
render({
177+
schema: dateTimeSchema,
178+
data,
179+
})
180+
const input = await screen.findByLabelText(TITLE)
181+
expect(input).toHaveValue(RENDERED_DATESTRING)
182+
})

src/controls/DateTimeControl.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { memo } from "react"
1+
import { memo, useCallback, useEffect } from "react"
22
import type { ControlProps as JSFControlProps } from "@jsonforms/core"
33
import { withJsonFormsControlProps } from "@jsonforms/react"
44
import { DatePicker, type DatePickerProps, Form } from "antd"
@@ -26,6 +26,19 @@ function getProps(options: unknown): DateTimeControlOptions {
2626
return DEFAULT_PROPS
2727
}
2828

29+
function getInitialValue(
30+
data: unknown,
31+
schemaDefault: unknown,
32+
): string | undefined {
33+
if (typeof data === "string" && data !== "") {
34+
return data
35+
}
36+
if (typeof schemaDefault === "string" && schemaDefault !== "") {
37+
return schemaDefault
38+
}
39+
return undefined
40+
}
41+
2942
export function DateTimeControl({
3043
handleChange,
3144
path,
@@ -35,12 +48,25 @@ export function DateTimeControl({
3548
schema,
3649
uischema,
3750
visible,
51+
data,
3852
}: ControlProps) {
53+
const setInitialValue = useCallback(
54+
(value: string | undefined) => {
55+
const coercedValue = value ? dayjs(value) : value
56+
handleChange(path, value)
57+
return coercedValue
58+
},
59+
[handleChange, path],
60+
)
61+
const form = Form.useFormInstance()
62+
useEffect(() => {
63+
form.setFieldValue(
64+
path,
65+
setInitialValue(getInitialValue(data, schema.default)),
66+
)
67+
}, [data, form, path, schema.default, setInitialValue])
3968
if (!visible) return null
4069

41-
const initialValue =
42-
typeof schema.default === "string" ? dayjs(schema.default) : undefined
43-
4470
const rules: Rule[] = [{ required, message: `${label} is required` }]
4571

4672
const formItemProps =
@@ -60,7 +86,6 @@ export function DateTimeControl({
6086
required={required}
6187
validateTrigger={["onBlur"]}
6288
rules={rules}
63-
initialValue={initialValue}
6489
{...formItemProps}
6590
>
6691
<DatePicker

src/stories/controls/DateTimeControl.stories.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,14 @@ export const DefaultValue: Story = {
6363
uiSchema: dateTimeUISchema,
6464
},
6565
}
66+
67+
export const ExistingValue: Story = {
68+
tags: ["autodocs"],
69+
args: {
70+
jsonSchema: dateTimeSchema,
71+
uiSchema: dateTimeUISchema,
72+
data: {
73+
dateTime: "1999-12-31T23:59:59.999Z",
74+
},
75+
},
76+
}

0 commit comments

Comments
 (0)