diff --git a/editor.planx.uk/src/@planx/components/List/Editor.tsx b/editor.planx.uk/src/@planx/components/List/Editor.tsx index 0901d78732..c55f3c50d7 100644 --- a/editor.planx.uk/src/@planx/components/List/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/List/Editor.tsx @@ -13,49 +13,38 @@ import InputRowLabel from "ui/shared/InputRowLabel"; import { EditorProps, ICONS, InternalNotes, MoreInformation } from "../ui"; import { List, parseContent } from "./model"; +import { ProposedAdvertisements } from "./schemas/Adverts"; +import { BuildingDetailsGLA } from "./schemas/GLA/BuildingDetails"; +import { CommunalSpaceGLA } from "./schemas/GLA/CommunalSpace"; +import { ExistingAndProposedUsesGLA } from "./schemas/GLA/ExistingAndProposedUses"; +import { OpenSpaceGLA } from "./schemas/GLA/OpenSpace"; +import { ProtectedSpaceGLA } from "./schemas/GLA/ProtectedSpace"; import { ResidentialUnitsExisting } from "./schemas/ResidentialUnits/Existing"; -import { ResidentialUnitsProposed } from "./schemas/ResidentialUnits/Proposed"; -// import { ResidentialUnitsGLANew } from "./schemas/ResidentialUnits/GLA/New"; -// import { ResidentialUnitsGLARebuilt } from "./schemas/ResidentialUnits/GLA/Rebuilt"; -// import { ResidentialUnitsGLARemoved } from "./schemas/ResidentialUnits/GLA/Removed"; -// import { ResidentialUnitsGLARetained } from "./schemas/ResidentialUnits/GLA/Retained"; import { ResidentialUnitsGLAGained } from "./schemas/ResidentialUnits/GLA/Gained"; import { ResidentialUnitsGLALost } from "./schemas/ResidentialUnits/GLA/Lost"; -import { ExistingAndProposedUsesGLA } from "./schemas/GLA/ExistingAndProposedUses"; -import { CommunalSpaceGLA } from "./schemas/GLA/CommunalSpace"; -import { BuildingDetailsGLA } from "./schemas/GLA/BuildingDetails"; -import { ProtectedSpaceGLA } from "./schemas/GLA/ProtectedSpace"; -import { OpenSpaceGLA } from "./schemas/GLA/OpenSpace"; -import { Zoo } from "./schemas/Zoo"; -import { ProposedAdvertisements } from "./schemas/Adverts"; +import { ResidentialUnitsProposed } from "./schemas/ResidentialUnits/Proposed"; +import { Zoo } from "./schemas/Tests/Zoo"; type Props = EditorProps; export const SCHEMAS = [ { name: "Residential units - Existing", schema: ResidentialUnitsExisting }, { name: "Residential units - Proposed", schema: ResidentialUnitsProposed }, - { name: "Residential units (GLA) - Gained", schema: ResidentialUnitsGLAGained }, + { + name: "Residential units (GLA) - Gained", + schema: ResidentialUnitsGLAGained, + }, { name: "Residential units (GLA) - Lost", schema: ResidentialUnitsGLALost }, - { name: "Existing and proposed uses (GLA)", schema: ExistingAndProposedUsesGLA }, - { name: "Communal spaces (GLA)", schema: CommunalSpaceGLA }, + { + name: "Existing and proposed uses (GLA)", + schema: ExistingAndProposedUsesGLA, + }, { name: "Building details (GLA)", schema: BuildingDetailsGLA }, + { name: "Communal spaces (GLA)", schema: CommunalSpaceGLA }, { name: "Protected spaces (GLA)", schema: ProtectedSpaceGLA }, { name: "Open spaces (GLA)", schema: OpenSpaceGLA }, -// { name: "Residential units (GLA) - New", schema: ResidentialUnitsGLANew }, -// { -// name: "Residential units (GLA) - Rebuilt", -// schema: ResidentialUnitsGLARebuilt, -// }, -// { -// name: "Residentail units (GLA) - Removed", -// schema: ResidentialUnitsGLARemoved, -// }, -// { -// name: "Residential units (GLA) - Retained", -// schema: ResidentialUnitsGLARetained, -// }, - { name: "Zoo (test)", schema: Zoo }, { name: "Proposed advertisements", schema: ProposedAdvertisements }, + { name: "(Test only) Zoo", schema: Zoo }, ]; function ListComponent(props: Props) { 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 2f72a1e8f9..ddb2f88b0f 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 @@ -1,106 +1,22 @@ -import { screen, within } from "@testing-library/react"; +import { getByText, screen, within } from "@testing-library/react"; import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup"; import { cloneDeep, merge } from "lodash"; import React from "react"; import { axe, setup } from "testUtils"; -import { UserResponse } from "../model"; -import ListComponent, { Props } from "../Public"; -import { GenericUnitsTest } from "../schemas/GenericUnitsTest"; -import { Zoo } from "../schemas/Zoo"; +import ListComponent from "../Public"; import { - flatten, - sumIdenticalUnits, - sumIdenticalUnitsByDevelopmentType, -} from "../utils"; - -const mockProps: Props = { - fn: "mockFn", - schema: Zoo, - schemaName: "Zoo", - title: "Mock Title", - description: "Mock description", -}; - -const mockPayload = { - data: { - mockFn: [ - { - age: 10, - cuteness: "Very", - email: "richard.parker@pi.com", - name: "Richard Parker", - size: "Medium", - }, - { - age: 10, - cuteness: "Very", - email: "richard.parker@pi.com", - name: "Richard Parker", - size: "Medium", - }, - ], - "mockFn.one.age": 10, - "mockFn.one.cuteness": "Very", - "mockFn.one.email": "richard.parker@pi.com", - "mockFn.one.name": "Richard Parker", - "mockFn.one.size": "Medium", - "mockFn.two.age": 10, - "mockFn.two.cuteness": "Very", - "mockFn.two.email": "richard.parker@pi.com", - "mockFn.two.name": "Richard Parker", - "mockFn.two.size": "Medium", - "mockFn.total.listItems": 2, - }, -}; - -const mockPropsUnits: Props = { - fn: "proposal.units.residential", - schema: GenericUnitsTest, - schemaName: "Generic residential units", - title: "Describe residential units", -}; - -const mockPayloadUnits = { - data: { - "proposal.units.residential": [ - { - development: "newBuild", - garden: "Yes", - identicalUnits: 1, - }, - { - development: "newBuild", - garden: "No", - identicalUnits: 2, - }, - { - development: "changeOfUseTo", - garden: "No", - identicalUnits: 2, - }, - ], - "proposal.units.residential.one.development": "newBuild", - "proposal.units.residential.one.garden": "Yes", - "proposal.units.residential.one.identicalUnits": 1, - "proposal.units.residential.two.development": "newBuild", - "proposal.units.residential.two.garden": "No", - "proposal.units.residential.two.identicalUnits": 2, - "proposal.units.residential.three.development": "changeOfUseTo", - "proposal.units.residential.three.garden": "No", - "proposal.units.residential.three.identicalUnits": 2, - "proposal.units.residential.total.listItems": 3, - "proposal.units.residential.total.units": 5, - "proposal.units.residential.total.units.newBuid": 3, - "proposal.units.residential.total.units.changeOfUseTo": 2, - }, -}; + mockUnitsPayload, + mockUnitsProps, +} from "../schemas/Tests/GenericUnits"; +import { mockMaxOneProps } from "../schemas/Tests/MaxOne"; +import { mockZooPayload, mockZooProps } from "../schemas/Tests/Zoo"; jest.setTimeout(20_000); describe("Basic UI", () => { it("renders correctly", () => { - const { getByText } = setup(); + const { getByText } = setup(); expect(getByText(/Mock Title/)).toBeInTheDocument(); expect(getByText(/Mock description/)).toBeInTheDocument(); @@ -108,7 +24,7 @@ describe("Basic UI", () => { it("parses provided schema to render expected form", async () => { const { getByLabelText, getByText, user, getByRole, queryAllByRole } = - setup(); + setup(); // Text inputs are generated from schema... const textInput = getByLabelText(/What's their name?/) as HTMLInputElement; @@ -174,7 +90,7 @@ describe("Basic UI", () => { }); it("should not have any accessibility violations", async () => { - const { container } = setup(); + const { container } = setup(); const results = await axe(container); expect(results).toHaveNoViolations(); }); @@ -182,7 +98,9 @@ describe("Basic UI", () => { describe("Building a list", () => { it("does not display a default item if the schema has no required minimum", () => { - const mockWithMinZero = merge(cloneDeep(mockProps), { schema: { min: 0 } }); + const mockWithMinZero = merge(cloneDeep(mockZooProps), { + schema: { min: 0 }, + }); const { queryByRole, getByTestId } = setup( , ); @@ -202,7 +120,7 @@ describe("Building a list", () => { it("displays a default item if the schema has a required minimum", () => { const { getByRole, queryByLabelText } = setup( - , + , ); // Card present... @@ -218,9 +136,22 @@ describe("Building a list", () => { expect(inputField).not.toBeDisabled(); }); + it("hides the index number in the card header and the 'add another' button if the schema has a max of 1", () => { + const { getAllByTestId, queryByTestId } = setup( + , + ); + + const cards = getAllByTestId(/list-card/); + expect(cards).toHaveLength(1); + expect(cards[0]).toHaveTextContent("Parking spaces"); + + const addItemButton = queryByTestId("list-add-button"); + expect(addItemButton).not.toBeInTheDocument(); + }); + test("Adding an item", async () => { const { getAllByTestId, getByTestId, user } = setup( - , + , ); let cards = getAllByTestId(/list-card/); @@ -252,7 +183,7 @@ describe("Building a list", () => { test("Editing an item", async () => { // Setup three cards const { getAllByTestId, getByTestId, user } = setup( - , + , ); await fillInResponse(user); @@ -308,7 +239,7 @@ describe("Building a list", () => { user, getByLabelText, queryAllByTestId, - } = setup(); + } = setup(); await fillInResponse(user); @@ -378,7 +309,7 @@ describe("Building a list", () => { test("Removing an item when another card is active", async () => { // Setup two cards const { getAllByTestId, getByTestId, user } = setup( - , + , ); await fillInResponse(user); @@ -430,7 +361,7 @@ describe("Payload generation", () => { it("generates a valid payload on submission (Zoo)", async () => { const handleSubmit = jest.fn(); const { getByTestId, user } = setup( - , + , ); const addItemButton = getByTestId("list-add-button"); @@ -442,13 +373,13 @@ describe("Payload generation", () => { await user.click(screen.getByTestId("continue-button")); expect(handleSubmit).toHaveBeenCalled(); - expect(handleSubmit.mock.calls[0][0]).toMatchObject(mockPayload); + expect(handleSubmit.mock.calls[0][0]).toMatchObject(mockZooPayload); }); it.skip("generates a valid payload with summary stats on submission (Units)", async () => { const handleSubmit = jest.fn(); const { getByTestId, user } = setup( - , + , ); const saveButton = screen.getByRole("button", { name: /Save/ }); @@ -486,14 +417,17 @@ describe("Payload generation", () => { await user.click(screen.getByTestId("continue-button")); expect(handleSubmit).toHaveBeenCalled(); - expect(handleSubmit.mock.calls[0][0]).toMatchObject(mockPayloadUnits); + expect(handleSubmit.mock.calls[0][0]).toMatchObject(mockUnitsPayload); }); }); describe("Navigating back", () => { test("it pre-populates list correctly", async () => { const { getAllByText, queryByLabelText, getAllByTestId } = setup( - , + , ); const cards = getAllByTestId(/list-card/); diff --git a/editor.planx.uk/src/@planx/components/List/Public/index.tsx b/editor.planx.uk/src/@planx/components/List/Public/index.tsx index 6466cfa70a..50d49140cb 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.tsx @@ -64,16 +64,20 @@ const InputField: React.FC = (props) => { const ActiveListCard: React.FC<{ index: number; -}> = ({ index }) => { +}> = ({ index: i }) => { const { schema, saveItem, cancelEditItem, errors } = useListContext(); + // Hide the index number in the card title if the schema has a max length of 1 + const shouldShowIndexTitle = schema.max !== 1; + return ( - + - {schema.type} {index + 1} + {schema.type} + {shouldShowIndexTitle && ` ${i + 1}`} {schema.fields.map((field, i) => ( @@ -100,10 +104,14 @@ const InactiveListCard: React.FC<{ }> = ({ index: i }) => { const { schema, formik, removeItem, editItem } = useListContext(); + // Hide the index number in the card title if the schema has a max length of 1 + const shouldShowIndexTitle = schema.max !== 1; + return ( - {schema.type} {i + 1} + {schema.type} + {shouldShowIndexTitle && ` ${i + 1}`} @@ -155,6 +163,10 @@ const Root = () => { (errors.max && `You can provide at most ${schema.max} response(s)`) || ""; + // Hide the "+ Add another" button if the schema has a max length of 1, unless the only item has been cancelled/removed (userData = []) + const shouldShowAddAnotherButton = + schema.max !== 1 || formik.values.userData.length < 1; + return ( { ), )} - - - + + + )} diff --git a/editor.planx.uk/src/@planx/components/List/schemas/GLA/BuildingDetails.ts b/editor.planx.uk/src/@planx/components/List/schemas/GLA/BuildingDetails.ts index d77ac140b3..76844adf59 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/GLA/BuildingDetails.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/GLA/BuildingDetails.ts @@ -1,5 +1,5 @@ -import { TextInputType } from "@planx/components/TextInput/model"; import { Schema } from "@planx/components/List/model"; +import { TextInputType } from "@planx/components/TextInput/model"; export const BuildingDetailsGLA: Schema = { type: "Building detail", diff --git a/editor.planx.uk/src/@planx/components/List/schemas/GLA/ExistingAndProposedUses.ts b/editor.planx.uk/src/@planx/components/List/schemas/GLA/ExistingAndProposedUses.ts index 417fe6b65a..7a7cc91985 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/GLA/ExistingAndProposedUses.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/GLA/ExistingAndProposedUses.ts @@ -10,15 +10,45 @@ export const ExistingAndProposedUsesGLA: Schema = { fn: "useClass", options: [ { id: "bTwo", data: { text: "B2 - General industry", val: "bTwo" } }, - { id: "bEight", data: { text: "B8 - Storage and distribution", val: "bEight" } }, + { + id: "bEight", + data: { text: "B8 - Storage and distribution", val: "bEight" }, + }, { id: "cOne", data: { text: "C1 - Hotels", val: "cOne" } }, - { id: "cTwo", data: { text: "C2 - Residential institutions", val: "cTwo" } }, - { id: "cTwoA", data: { text: "C2a - Secure residential institutions", val: "cTwoA" } }, - { id: "cThree", data: { text: "C3 - Dwellinghouses", val: "cThree" } }, - { id: "cFour", data: { text: "C4 - Houses in multiple occupation", val: "cFour" } }, - { id: "e", data: { text: "E - Commercial, business and service", val: "e" } }, - { id: "fOne", data: { text: "F1 - Learning and non-residential institutions", val: "fOne" } }, - { id: "fTwo", data: { text: "F2 - Local community uses", val: "fTwo" } }, + { + id: "cTwo", + data: { text: "C2 - Residential institutions", val: "cTwo" }, + }, + { + id: "cTwoA", + data: { + text: "C2a - Secure residential institutions", + val: "cTwoA", + }, + }, + { + id: "cThree", + data: { text: "C3 - Dwellinghouses", val: "cThree" }, + }, + { + id: "cFour", + data: { text: "C4 - Houses in multiple occupation", val: "cFour" }, + }, + { + id: "e", + data: { text: "E - Commercial, business and service", val: "e" }, + }, + { + id: "fOne", + data: { + text: "F1 - Learning and non-residential institutions", + val: "fOne", + }, + }, + { + id: "fTwo", + data: { text: "F2 - Local community uses", val: "fTwo" }, + }, { id: "SG", data: { text: "Sui generis", val: "SG" } }, { id: "unknown", data: { text: "Unknown", val: "unknown" } }, ], diff --git a/editor.planx.uk/src/@planx/components/List/schemas/GLA/OpenSpace.ts b/editor.planx.uk/src/@planx/components/List/schemas/GLA/OpenSpace.ts index 7bf23705dc..9e167b256a 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/GLA/OpenSpace.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/GLA/OpenSpace.ts @@ -1,5 +1,5 @@ -import { TextInputType } from "@planx/components/TextInput/model"; import { Schema } from "@planx/components/List/model"; +import { TextInputType } from "@planx/components/TextInput/model"; export const OpenSpaceGLA: Schema = { type: "Open space details", @@ -12,7 +12,10 @@ export const OpenSpaceGLA: Schema = { options: [ { id: "loss", data: { text: "Loss", val: "loss" } }, { id: "gain", data: { text: "Gain", val: "gain" } }, - { id: "changeOfUse", data: { text: "Change of use", val: "changeOfUse" } }, + { + id: "changeOfUse", + data: { text: "Change of use", val: "changeOfUse" }, + }, ], }, }, @@ -23,18 +26,60 @@ export const OpenSpaceGLA: Schema = { fn: "type", options: [ { id: "park", data: { text: "Parks and gardens", val: "park" } }, - { id: "natural", data: { text: "Natural and semi-natural", val: "natural" } }, - { id: "greenCorridor", data: { text: "Green corridors", val: "greenCorridor" } }, - { id: "sport", data: { text: "Outdoor sports facilities", val: "sport" } }, + { + id: "natural", + data: { text: "Natural and semi-natural", val: "natural" }, + }, + { + id: "greenCorridor", + data: { text: "Green corridors", val: "greenCorridor" }, + }, + { + id: "sport", + data: { text: "Outdoor sports facilities", val: "sport" }, + }, { id: "amenity", data: { text: "Amenity", val: "amenity" } }, - { id: "children", data: { text: "Provision for children and young people", val: "children" } }, - { id: "allotment", data: { text: "Allotments, community gardens and city farms", val: "allotment" } }, - { id: "burial", data: { text: "Cemeteries, churchyards and other burial grounds", val: "burial" } }, - { id: "fringe", data: { text: "Countryside in urban fringe areas", val: "fringe" } }, + { + id: "children", + data: { + text: "Provision for children and young people", + val: "children", + }, + }, + { + id: "allotment", + data: { + text: "Allotments, community gardens and city farms", + val: "allotment", + }, + }, + { + id: "burial", + data: { + text: "Cemeteries, churchyards and other burial grounds", + val: "burial", + }, + }, + { + id: "fringe", + data: { text: "Countryside in urban fringe areas", val: "fringe" }, + }, { id: "civic", data: { text: "Civic spaces", val: "civic" } }, - { id: "brownfield", data: { text: "Brownfield land", val: "brownfield" } }, - { id: "nonResidential", data: { text: "Non-residential institution grounds or garden", val: "nonResidential" } }, - { id: "residential", data: { text: "Residential garden", val: "residential" } }, + { + id: "brownfield", + data: { text: "Brownfield land", val: "brownfield" }, + }, + { + id: "nonResidential", + data: { + text: "Non-residential institution grounds or garden", + val: "nonResidential", + }, + }, + { + id: "residential", + data: { text: "Residential garden", val: "residential" }, + }, ], }, }, @@ -45,7 +90,10 @@ export const OpenSpaceGLA: Schema = { fn: "designation", options: [ { id: "greenBelt", data: { text: "Green Belt", val: "greenBelt" } }, - { id: "metropolitan", data: { text: "Metropolitan Open Land", val: "metropolitan" } }, + { + id: "metropolitan", + data: { text: "Metropolitan Open Land", val: "metropolitan" }, + }, { id: "local", data: { text: "Local Open Spaces", val: "local" } }, { id: "other", data: { text: "Other designation", val: "other" } }, { id: "none", data: { text: "Not designated", val: "none" } }, @@ -67,7 +115,10 @@ export const OpenSpaceGLA: Schema = { fn: "access", options: [ { id: "restricted", data: { text: "Restricted", val: "restricted" } }, - { id: "unrestricted", data: { text: "Unrestricted", val: "unrestricted" } }, + { + id: "unrestricted", + data: { text: "Unrestricted", val: "unrestricted" }, + }, ], }, }, diff --git a/editor.planx.uk/src/@planx/components/List/schemas/GLA/ProtectedSpace.ts b/editor.planx.uk/src/@planx/components/List/schemas/GLA/ProtectedSpace.ts index 5bb608bb75..8118290c96 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/GLA/ProtectedSpace.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/GLA/ProtectedSpace.ts @@ -1,5 +1,5 @@ -import { TextInputType } from "@planx/components/TextInput/model"; import { Schema } from "@planx/components/List/model"; +import { TextInputType } from "@planx/components/TextInput/model"; export const ProtectedSpaceGLA: Schema = { type: "Protected space details", @@ -12,7 +12,10 @@ export const ProtectedSpaceGLA: Schema = { options: [ { id: "loss", data: { text: "Loss", val: "loss" } }, { id: "gain", data: { text: "Gain", val: "gain" } }, - { id: "changeOfUse", data: { text: "Change of use", val: "changeOfUse" } }, + { + id: "changeOfUse", + data: { text: "Change of use", val: "changeOfUse" }, + }, ], }, }, @@ -22,12 +25,39 @@ export const ProtectedSpaceGLA: Schema = { title: "What best describes the designation of this protected space?", fn: "designation", options: [ - { id: "SSSI", data: { text: "Sites of Special Scientific Interest", val: "SSSI" } }, - { id: "localReserve", data: { text: "Local Nature Reserve", val: "localReserve" } }, - { id: "metropolitan", data: { text: "Site of Metropolitan Importance", val: "metropolitan" } }, - { id: "boroughGradeOne", data: { text: "Site of Borough Grade 1 Importance", val: "boroughGradeOne" } }, - { id: "boroughGradeTwo", data: { text: "Site of Borough Grade 2 Importance", val: "boroughGradeTwo" } }, - { id: "local", data: { text: "Site of Local Importance", val: "local" } }, + { + id: "SSSI", + data: { text: "Sites of Special Scientific Interest", val: "SSSI" }, + }, + { + id: "localReserve", + data: { text: "Local Nature Reserve", val: "localReserve" }, + }, + { + id: "metropolitan", + data: { + text: "Site of Metropolitan Importance", + val: "metropolitan", + }, + }, + { + id: "boroughGradeOne", + data: { + text: "Site of Borough Grade 1 Importance", + val: "boroughGradeOne", + }, + }, + { + id: "boroughGradeTwo", + data: { + text: "Site of Borough Grade 2 Importance", + val: "boroughGradeTwo", + }, + }, + { + id: "local", + data: { text: "Site of Local Importance", val: "local" }, + }, { id: "none", data: { text: "Not designated", val: "none" } }, ], }, @@ -47,7 +77,10 @@ export const ProtectedSpaceGLA: Schema = { fn: "access", options: [ { id: "restricted", data: { text: "Restricted", val: "restricted" } }, - { id: "unrestricted", data: { text: "Unrestricted", val: "unrestricted" } }, + { + id: "unrestricted", + data: { text: "Unrestricted", val: "unrestricted" }, + }, ], }, }, diff --git a/editor.planx.uk/src/@planx/components/List/schemas/GenericUnitsTest.ts b/editor.planx.uk/src/@planx/components/List/schemas/GenericUnitsTest.ts deleted file mode 100644 index f8f7d45076..0000000000 --- a/editor.planx.uk/src/@planx/components/List/schemas/GenericUnitsTest.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Schema } from "@planx/components/List/model"; - -export const GenericUnitsTest: Schema = { - type: "Unit", - fields: [ - // fn = "development" triggers summary stat and options set "val" - { - type: "question", - data: { - title: "What development does this unit result from?", - fn: "development", - options: [ - { id: "newBuild", data: { text: "New build", val: "newBuild" } }, - { - id: "changeOfUseFrom", - data: { - text: "Change of use of existing single home", - val: "changeOfUseFrom", - }, - }, - { - id: "changeOfUseTo", - data: { text: "Change of use to a home", val: "changeOfUseTo" }, - }, - ], - }, - }, - // options set "text" only - { - type: "question", - data: { - title: "Is this unit built on garden land?", - fn: "garden", - options: [ - { id: "true", data: { text: "Yes" } }, - { id: "false", data: { text: "No" } }, - ], - }, - }, - // fn = "identicalUnits" triggers summary stat - { - type: "number", - data: { - title: "How many identical units does the description above apply to?", - fn: "identicalUnits", - allowNegatives: false, - }, - }, - ], - min: 1, -} as const; diff --git a/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/GLA/Gained.ts b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/GLA/Gained.ts index 2ec4565243..8382dacfb2 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/GLA/Gained.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/GLA/Gained.ts @@ -11,9 +11,12 @@ export const ResidentialUnitsGLAGained: Schema = { options: [ { id: "newBuild", data: { text: "New build", val: "newBuild" } }, { id: "conversion", data: { text: "Conversion", val: "conversion" } }, - { id: "changeOfUse", data: { text: "Change of use", val: "changeOfUse" } }, + { + id: "changeOfUse", + data: { text: "Change of use", val: "changeOfUse" }, + }, { id: "extension", data: { text: "Extension", val: "extension" } }, - { id: "notKnown", data: { text: "Not known", val: "notKnown" } }, + { id: "notKnown", data: { text: "Not known", val: "notKnown" } }, ], }, }, diff --git a/editor.planx.uk/src/@planx/components/List/schemas/Tests/GenericUnits.ts b/editor.planx.uk/src/@planx/components/List/schemas/Tests/GenericUnits.ts new file mode 100644 index 0000000000..808da9a921 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/List/schemas/Tests/GenericUnits.ts @@ -0,0 +1,95 @@ +import { Schema } from "@planx/components/List/model"; + +import { Props } from "../../Public"; + +export const GenericUnitsTest: Schema = { + type: "Unit", + fields: [ + // fn = "development" triggers summary stat and options set "val" + { + type: "question", + data: { + title: "What development does this unit result from?", + fn: "development", + options: [ + { id: "newBuild", data: { text: "New build", val: "newBuild" } }, + { + id: "changeOfUseFrom", + data: { + text: "Change of use of existing single home", + val: "changeOfUseFrom", + }, + }, + { + id: "changeOfUseTo", + data: { text: "Change of use to a home", val: "changeOfUseTo" }, + }, + ], + }, + }, + // options set "text" only + { + type: "question", + data: { + title: "Is this unit built on garden land?", + fn: "garden", + options: [ + { id: "true", data: { text: "Yes" } }, + { id: "false", data: { text: "No" } }, + ], + }, + }, + // fn = "identicalUnits" triggers summary stat + { + type: "number", + data: { + title: "How many identical units does the description above apply to?", + fn: "identicalUnits", + allowNegatives: false, + }, + }, + ], + min: 1, +} as const; + +export const mockUnitsProps: Props = { + fn: "proposal.units.residential", + schema: GenericUnitsTest, + schemaName: "Generic residential units", + title: "Describe residential units", +}; + +export const mockUnitsPayload = { + data: { + "proposal.units.residential": [ + { + development: "newBuild", + garden: "Yes", + identicalUnits: 1, + }, + { + development: "newBuild", + garden: "No", + identicalUnits: 2, + }, + { + development: "changeOfUseTo", + garden: "No", + identicalUnits: 2, + }, + ], + "proposal.units.residential.one.development": "newBuild", + "proposal.units.residential.one.garden": "Yes", + "proposal.units.residential.one.identicalUnits": 1, + "proposal.units.residential.two.development": "newBuild", + "proposal.units.residential.two.garden": "No", + "proposal.units.residential.two.identicalUnits": 2, + "proposal.units.residential.three.development": "changeOfUseTo", + "proposal.units.residential.three.garden": "No", + "proposal.units.residential.three.identicalUnits": 2, + "proposal.units.residential.total.listItems": 3, + "proposal.units.residential.total.units": 5, + "proposal.units.residential.total.units.newBuid": 3, + "proposal.units.residential.total.units.changeOfUseTo": 2, + }, +}; diff --git a/editor.planx.uk/src/@planx/components/List/schemas/Tests/MaxOne.ts b/editor.planx.uk/src/@planx/components/List/schemas/Tests/MaxOne.ts new file mode 100644 index 0000000000..bdbddaef59 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/List/schemas/Tests/MaxOne.ts @@ -0,0 +1,58 @@ +import { Schema } from "@planx/components/List/model"; + +import { Props } from "../../Public"; + +export const MaxOneTest: Schema = { + type: "Parking spaces", + fields: [ + { + type: "number", + data: { + title: "How many parking spaces for cars?", + fn: "cars", + allowNegatives: false, + }, + }, + { + type: "number", + data: { + title: "How many parking spaces for bicycles?", + fn: "bikes", + allowNegatives: false, + }, + }, + { + type: "number", + data: { + title: "How many parking spaces for caravans?", + fn: "caravans", + allowNegatives: false, + }, + }, + ], + min: 1, + max: 1, +} as const; + +export const mockMaxOneProps: Props = { + fn: "proposal.parking", + schema: MaxOneTest, + schemaName: "Parking spaces", + title: "Describe parking spaces", +}; + +export const mockMaxOnePayload = { + data: { + "proposal.parking": [ + { + cars: 2, + bicycles: 4, + caravans: 0, + }, + ], + "proposal.parking.one.cars": 2, + "proposal.parking.one.bicycles": 4, + "proposal.parking.one.caravans": 0, + "proposal.parking.total.listItems": 1, + }, +}; diff --git a/editor.planx.uk/src/@planx/components/List/schemas/Zoo.ts b/editor.planx.uk/src/@planx/components/List/schemas/Tests/Zoo.ts similarity index 58% rename from editor.planx.uk/src/@planx/components/List/schemas/Zoo.ts rename to editor.planx.uk/src/@planx/components/List/schemas/Tests/Zoo.ts index 655512f182..6debeddea7 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/Zoo.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/Tests/Zoo.ts @@ -1,6 +1,7 @@ import { TextInputType } from "@planx/components/TextInput/model"; -import { Schema } from "../model"; +import { Schema } from "../../model"; +import { Props } from "../../Public"; /** * Temp simple example to build out UI @@ -67,3 +68,43 @@ export const Zoo: Schema = { min: 1, max: 10, } as const; + +export const mockZooProps: Props = { + fn: "mockFn", + schema: Zoo, + schemaName: "Zoo", + title: "Mock Title", + description: "Mock description", +}; + +export const mockZooPayload = { + data: { + mockFn: [ + { + age: 10, + cuteness: "Very", + email: "richard.parker@pi.com", + name: "Richard Parker", + size: "Medium", + }, + { + age: 10, + cuteness: "Very", + email: "richard.parker@pi.com", + name: "Richard Parker", + size: "Medium", + }, + ], + "mockFn.one.age": 10, + "mockFn.one.cuteness": "Very", + "mockFn.one.email": "richard.parker@pi.com", + "mockFn.one.name": "Richard Parker", + "mockFn.one.size": "Medium", + "mockFn.two.age": 10, + "mockFn.two.cuteness": "Very", + "mockFn.two.email": "richard.parker@pi.com", + "mockFn.two.name": "Richard Parker", + "mockFn.two.size": "Medium", + "mockFn.total.listItems": 2, + }, +}; diff --git a/editor.planx.uk/src/@planx/components/List/utils.ts b/editor.planx.uk/src/@planx/components/List/utils.ts index b5c8226afa..0e0d0dcdc3 100644 --- a/editor.planx.uk/src/@planx/components/List/utils.ts +++ b/editor.planx.uk/src/@planx/components/List/utils.ts @@ -46,11 +46,17 @@ export function sumIdenticalUnitsByDevelopmentType( fn: string, passportData: Record, ): Record { - // Sum identical units by development type (read option `val` from Schema in future?) + // Sum identical units by development type (@todo read all possible option `val` from Schema in future) const baseSums: Record = { - newBuild: 0, + changeOfUse: 0, changeOfUseFrom: 0, changeOfUseTo: 0, + conversion: 0, + gain: 0, + extension: 0, + loss: 0, + newBuild: 0, + notKnown: 0, }; passportData[`${fn}`].map( (item) =>