Skip to content

Commit

Permalink
feat(works-to-trees): Add DateInputField to List component (#3519)
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr authored Aug 16, 2024
1 parent 04ed5fb commit 110d062
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 21 deletions.
2 changes: 1 addition & 1 deletion editor.planx.uk/src/@planx/components/DateInput/Public.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const DateInputPublic: React.FC<Props> = (props) => {
validateOnBlur: false,
validateOnChange: false,
validationSchema: object({
date: dateRangeSchema({ min: props.min, max: props.max }),
date: dateRangeSchema(props),
}),
});

Expand Down
29 changes: 19 additions & 10 deletions editor.planx.uk/src/@planx/components/DateInput/model.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { dateRangeSchema, dateSchema, paddedDate, parseDate } from "./model";
import {
DateInput,
dateRangeSchema,
dateSchema,
paddedDate,
parseDate,
} from "./model";

describe("parseDate helper function", () => {
it("returns a day value", () => {
Expand Down Expand Up @@ -101,19 +107,22 @@ describe("dateSchema", () => {
describe("dateRangeSchema", () => {
test("basic validation", async () => {
expect(
await dateRangeSchema({ min: "1990-01-01", max: "1999-12-31" }).isValid(
"1995-06-15",
),
await dateRangeSchema({
min: "1990-01-01",
max: "1999-12-31",
} as DateInput).isValid("1995-06-15")
).toBe(true);
expect(
await dateRangeSchema({ min: "1990-01-01", max: "1999-12-31" }).isValid(
"2021-06-15",
),
await dateRangeSchema({
min: "1990-01-01",
max: "1999-12-31",
} as DateInput).isValid("2021-06-15")
).toBe(false);
expect(
await dateRangeSchema({ min: "1990-01-01", max: "1999-12-31" }).isValid(
"1980-06-15",
),
await dateRangeSchema({
min: "1990-01-01",
max: "1999-12-31",
} as DateInput).isValid("1980-06-15")
).toBe(false);
});
});
Expand Down
5 changes: 1 addition & 4 deletions editor.planx.uk/src/@planx/components/DateInput/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@ export const dateSchema = () => {
);
};

export const dateRangeSchema: (params: {
min?: string;
max?: string;
}) => SchemaOf<string> = (params) =>
export const dateRangeSchema: (input: DateInput) => SchemaOf<string> = (params) =>
dateSchema()
.required("Enter a valid date in DD.MM.YYYY format")
.test({
Expand Down
26 changes: 26 additions & 0 deletions editor.planx.uk/src/@planx/components/List/Public/Fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import Grid from "@mui/material/Grid";
import MenuItem from "@mui/material/MenuItem";
import RadioGroup from "@mui/material/RadioGroup";
import { visuallyHidden } from "@mui/utils";
import { paddedDate } from "@planx/components/DateInput/model";
import { getIn } from "formik";
import { get } from "lodash";
import React from "react";
import SelectInput from "ui/editor/SelectInput";
import InputLabel from "ui/public/InputLabel";
import ChecklistItem from "ui/shared/ChecklistItem";
import DateInput from "ui/shared/DateInput";
import ErrorWrapper from "ui/shared/ErrorWrapper";
import Input from "ui/shared/Input";
import InputRowLabel from "ui/shared/InputRowLabel";
Expand All @@ -19,6 +21,7 @@ import { DESCRIPTION_TEXT, ERROR_MESSAGE } from "../../shared/constants";
import BasicRadio from "../../shared/Radio/BasicRadio";
import type {
ChecklistField,
DateField,
NumberField,
QuestionField,
TextField,
Expand Down Expand Up @@ -226,3 +229,26 @@ export const ChecklistFieldInput: React.FC<Props<ChecklistField>> = (props) => {
</InputLabel>
);
};

export const DateFieldInput: React.FC<Props<DateField>> = ({
id,
data,
}) => {
const { formik, activeIndex } = useListContext();

return (
<InputLabel label={data.title} htmlFor={id}>
<Box sx={{ display: "flex", alignItems: "baseline" }}>
<DateInput
value={formik.values.userData[activeIndex][data.fn] as string}
bordered
onChange={(newDate: string, eventType: string) => {
formik.setFieldValue(`userData[${activeIndex}]['${data.fn}']`, paddedDate(newDate, eventType));
}}
error={get(formik.errors, ["userData", activeIndex, data.fn])}
id={id}
/>
</Box>
</InputLabel>
);
};
35 changes: 31 additions & 4 deletions editor.planx.uk/src/@planx/components/List/Public/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,11 @@ describe("Form validation and error handling", () => {
);

let errorMessages = getAllByTestId(/error-message-input/);
// One error per field, plus 3 for a date input (one per input)
const numberOfErrors = mockZooProps.schema.fields.length + 3;

// Each field has an ErrorWrapper
expect(errorMessages).toHaveLength(mockZooProps.schema.fields.length);
expect(errorMessages).toHaveLength(numberOfErrors);

// All are empty initially
errorMessages.forEach((message) => {
Expand All @@ -398,10 +400,12 @@ describe("Form validation and error handling", () => {

// Error wrappers persist
errorMessages = getAllByTestId(/error-message-input/);
expect(errorMessages).toHaveLength(mockZooProps.schema.fields.length);
expect(errorMessages).toHaveLength(numberOfErrors);

// Each field is in an error state
errorMessages.forEach((message) => {
// Each field is in an error state, ignoring individual date input fields
const fieldErrors = errorMessages.slice(0, mockZooProps.schema.fields.length);

fieldErrors.forEach((message) => {
expect(message).not.toBeEmptyDOMElement();
});
});
Expand Down Expand Up @@ -495,6 +499,22 @@ describe("Form validation and error handling", () => {
/Select at least one option/,
);
});

test("date fields", async () => {
const { user, getByRole, getAllByTestId } = setup(
<ListComponent {...mockZooProps} />,
);

await user.click(getByRole("button", { name: /Save/ }));

const dateInputErrorMessage = getAllByTestId(
/error-message-input-date-birthday/,
)[0];

expect(dateInputErrorMessage).toHaveTextContent(
/Date must include a day/,
);
});
});

test("an error displays if the minimum number of items is not met", async () => {
Expand Down Expand Up @@ -742,6 +762,13 @@ const fillInResponse = async (user: UserEvent) => {
await user.click(eatCheckboxes[1]);
await user.click(eatCheckboxes[2]);

const dayInput = screen.getByLabelText("Day");
const monthInput = screen.getByLabelText("Month");
const yearInput = screen.getByLabelText("Year");
await user.type(dayInput, "14");
await user.type(monthInput, "7");
await user.type(yearInput, "1988");

const saveButton = screen.getByRole("button", {
name: /Save/,
});
Expand Down
3 changes: 3 additions & 0 deletions editor.planx.uk/src/@planx/components/List/Public/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { formatSchemaDisplayValue } from "../utils";
import { ListProvider, useListContext } from "./Context";
import {
ChecklistFieldInput,
DateFieldInput,
NumberFieldInput,
RadioFieldInput,
SelectFieldInput,
Expand Down Expand Up @@ -62,6 +63,8 @@ const InputField: React.FC<Field> = (props) => {
return <SelectFieldInput id={inputFieldId} {...props} />;
case "checklist":
return <ChecklistFieldInput id={inputFieldId} {...props} />;
case "date":
return <DateFieldInput id={inputFieldId} {...props} />;
}
};

Expand Down
15 changes: 14 additions & 1 deletion editor.planx.uk/src/@planx/components/List/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { cloneDeep } from "lodash";
import { array, BaseSchema, object, ObjectSchema, string } from "yup";

import { checklistValidationSchema } from "../Checklist/model";
import {
DateInput,
dateRangeSchema as dateValidationSchema,
} from "../DateInput/model";
import { NumberInput, numberInputValidationSchema } from "../NumberInput/model";
import { MoreInformation, Option, parseMoreInformation } from "../shared";
import {
Expand Down Expand Up @@ -49,17 +53,23 @@ export type QuestionField = {
type: "question";
data: QuestionInput & { fn: string };
};

export type ChecklistField = {
type: "checklist";
required?: true;
data: ChecklistInput & { fn: string };
};

export type DateField = {
type: "date";
data: DateInput & { fn: string };
};

/**
* Represents the input types available in the List component
* Existing models are used to allow to us to re-use existing components, maintaining consistend UX/UI
*/
export type Field = TextField | NumberField | QuestionField | ChecklistField;
export type Field = TextField | NumberField | QuestionField | ChecklistField | DateField;

/**
* Models the form displayed to the user
Expand Down Expand Up @@ -115,6 +125,9 @@ const generateValidationSchemaForFields = (
case "checklist":
fieldSchemas[data.fn] = checklistValidationSchema(data);
break;
case "date":
fieldSchemas[data.fn] = dateValidationSchema(data);
break;
}
});

Expand Down
16 changes: 15 additions & 1 deletion editor.planx.uk/src/@planx/components/List/schemas/mocks/Zoo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,20 @@ export const Zoo: Schema = {
{ id: "meat", data: { text: "Meat" } },
{ id: "leaves", data: { text: "Leaves" } },
{ id: "bamboo", data: { text: "Bamboo" } },
{ id: "fruit", data: { text: "fruit" } },
{ id: "fruit", data: { text: "Fruit" } },
],
},
},
// Date
{
type: "date",
data: {
title: "What's their birthday?",
fn: "birthday",
min: "1970-01-01",
max: "2999-12-31"
},
},
],
min: 1,
max: 3,
Expand All @@ -97,6 +107,7 @@ export const mockZooPayload = {
name: "Richard Parker",
size: "Medium",
food: ["meat", "leaves", "bamboo"],
birthday: "1988-07-14",
},
{
age: 10,
Expand All @@ -105,6 +116,7 @@ export const mockZooPayload = {
name: "Richard Parker",
size: "Medium",
food: ["meat", "leaves", "bamboo"],
birthday: "1988-07-14",
},
],
"mockFn.one.age": 10,
Expand All @@ -113,12 +125,14 @@ export const mockZooPayload = {
"mockFn.one.name": "Richard Parker",
"mockFn.one.size": "Medium",
"mockFn.one.food": ["meat", "leaves", "bamboo"],
"mockFn.one.birthday": "1988-07-14",
"mockFn.two.age": 10,
"mockFn.two.cuteness.amount": "Very",
"mockFn.two.email.address": "[email protected]",
"mockFn.two.name": "Richard Parker",
"mockFn.two.size": "Medium",
"mockFn.two.food": ["meat", "leaves", "bamboo"],
"mockFn.two.birthday": "1988-07-14",
"mockFn.total.listItems": 2,
},
};
1 change: 1 addition & 0 deletions editor.planx.uk/src/@planx/components/List/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function formatSchemaDisplayValue(
case "number":
return field.data.units ? `${value} ${field.data.units}` : value;
case "text":
case "date":
return value;
case "checklist": {
const matchingOptions = field.data.options.filter((option) =>
Expand Down

0 comments on commit 110d062

Please sign in to comment.