From a8fac5641a3bd5971e9174b4a9ff52bd2c94764c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Sat, 1 Jun 2024 09:47:49 +0100 Subject: [PATCH] feat: List component - First iteration of a residential units schema (#3222) --- .../src/@planx/components/List/Editor.tsx | 5 +- .../@planx/components/List/Public/Fields.tsx | 42 ++++++++--- .../components/List/Public/index.test.tsx | 1 + .../src/@planx/components/List/model.ts | 1 + .../List/schemas/ResidentialUnits.ts | 73 +++++++++++++++++++ 5 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits.ts diff --git a/editor.planx.uk/src/@planx/components/List/Editor.tsx b/editor.planx.uk/src/@planx/components/List/Editor.tsx index d2975e52ed..bd1f6340f8 100644 --- a/editor.planx.uk/src/@planx/components/List/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/List/Editor.tsx @@ -13,13 +13,14 @@ import InputRowLabel from "ui/shared/InputRowLabel"; import { EditorProps, ICONS, InternalNotes, MoreInformation } from "../ui"; import { List, parseContent } from "./model"; +import { ResidentialUnits } from "./schemas/ResidentialUnits"; import { Zoo } from "./schemas/Zoo"; type Props = EditorProps; export const SCHEMAS = [ - { name: "Zoo", schema: Zoo }, - // TODO: Residential units + { name: "Residential Units (alpha)", schema: ResidentialUnits }, + { name: "Zoo (test)", schema: Zoo }, // TODO: Residential units (GLA) ]; diff --git a/editor.planx.uk/src/@planx/components/List/Public/Fields.tsx b/editor.planx.uk/src/@planx/components/List/Public/Fields.tsx index 1068ed4771..36d0812e05 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/Fields.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/Fields.tsx @@ -3,6 +3,7 @@ import FormControl from "@mui/material/FormControl"; import FormLabel from "@mui/material/FormLabel"; import MenuItem from "@mui/material/MenuItem"; import RadioGroup from "@mui/material/RadioGroup"; +import { Option } from "@planx/components/shared"; import { getIn } from "formik"; import React from "react"; import SelectInput from "ui/editor/SelectInput"; @@ -26,7 +27,10 @@ export const TextFieldInput: React.FC> = ({ const { formik, activeIndex } = useListContext(); return ( - + { if (type === "email") return "email"; @@ -70,7 +74,10 @@ export const NumberFieldInput: React.FC> = ({ const { formik, activeIndex } = useListContext(); return ( - + > = ({ export const RadioFieldInput: React.FC> = ({ id, data, + required, }) => { const { formik, activeIndex } = useListContext(); @@ -119,7 +127,7 @@ export const RadioFieldInput: React.FC> = ({ }, })} > - {data.title} + {required === false ? data.title + " (optional)" : data.title} > = ({ ); }; -export const SelectFieldInput: React.FC> = ({ - id, - data, - required, -}) => { +export const SelectFieldInput: React.FC> = (props) => { const { formik, activeIndex } = useListContext(); + const { id, data, required } = props; + + const isDisabled = (option: Option) => { + if (!props.unique) return false; + + const existingValues = formik.values.userData + .map((response) => response[data.fn]) + .filter((value) => value === option.data.text); + + return existingValues.includes(option.data.text); + }; return ( - + > = ({ name={`userData[${activeIndex}][${data.fn}]`} > {data.options.map((option) => ( - + {option.data.text} ))} diff --git a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx index 432976c523..2bae5ecf6f 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx @@ -364,6 +364,7 @@ describe("Form validation and error handling", () => { test.todo("text fields use existing validation schemas"); test.todo("number fields use existing validation schemas"); test.todo("question fields use validation schema"); + test.todo("unique constraints are enforced on question where this is set"); test.todo("an error displays if the minimum number of items is not met"); test.todo("an error displays if the maximum number of items is exceeded"); test.todo( diff --git a/editor.planx.uk/src/@planx/components/List/model.ts b/editor.planx.uk/src/@planx/components/List/model.ts index 1d5e78872f..f2734ccbe0 100644 --- a/editor.planx.uk/src/@planx/components/List/model.ts +++ b/editor.planx.uk/src/@planx/components/List/model.ts @@ -42,6 +42,7 @@ export type NumberField = { export type QuestionField = { type: "question"; required?: boolean; + unique?: boolean; data: QuestionInput & { fn: string }; }; diff --git a/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits.ts b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits.ts new file mode 100644 index 0000000000..8a8a579780 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits.ts @@ -0,0 +1,73 @@ +import { Schema } from "../model"; + +export const ResidentialUnits: Schema = { + type: "Residential Units", + fields: [ + { + type: "question", + data: { + title: "What type of change are you making?", + fn: "changeType", + options: [ + { id: "proposed", data: { text: "Proposed" } }, + { id: "existing", data: { text: "Existing" } }, + ], + }, + }, + { + type: "question", + unique: true, + data: { + title: "What is the tenure type?", + fn: "tenureType", + options: [ + { id: "marketHousing", data: { text: "Market housing" } }, + { + id: "socialAffordableRent", + data: { text: "Social and affordable rent" }, + }, + { + id: "affordableHomeOwnership", + data: { text: "Affordable home ownership" }, + }, + { id: "starterHome", data: { text: "Starter homes" } }, + { id: "selfBuild", data: { text: "Self build" } }, + ], + }, + }, + { + type: "question", + data: { + title: "What is the unit type?", + fn: "unitType", + options: [ + { id: "houses", data: { text: "Houses" } }, + { id: "flats", data: { text: "Flats" } }, + { id: "bedsits", data: { text: "Bedsits" } }, + { id: "starterHome", data: { text: "Starter homes" } }, + { id: "shelteredHousing", data: { text: "Sheltered housing" } }, + { id: "clusteredFlats", data: { text: "Clustered flats" } }, + { id: "other", data: { text: "Other" } }, + ], + }, + }, + { + type: "number", + data: { + title: "How many bedrooms are there?", + fn: "numberBedrooms", + allowNegatives: false, + }, + }, + { + type: "number", + required: false, + data: { + title: "How many identical units of this type are there?", + fn: "numberIdenticalUnits", + allowNegatives: false, + }, + }, + ], + min: 1, +} as const;