Skip to content

Commit

Permalink
feat(page): Handle submit and "back" functionality (#3678)
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr authored Sep 16, 2024
1 parent 8be0bb9 commit 76c4f9d
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 24 deletions.
2 changes: 1 addition & 1 deletion api.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@airbrake/node": "^2.1.8",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e",
"@types/isomorphic-fetch": "^0.0.36",
"adm-zip": "^0.5.10",
"aws-sdk": "^2.1467.0",
Expand Down
8 changes: 4 additions & 4 deletions api.planx.uk/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/tests/api-driven/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"packageManager": "[email protected]",
"dependencies": {
"@cucumber/cucumber": "^9.3.0",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e",
"axios": "^1.7.4",
"dotenv": "^16.3.1",
"dotenv-expand": "^10.0.0",
Expand Down
8 changes: 4 additions & 4 deletions e2e/tests/api-driven/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/tests/ui-driven/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"postinstall": "./install-dependencies.sh"
},
"dependencies": {
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e",
"axios": "^1.7.4",
"dotenv": "^16.3.1",
"eslint": "^8.56.0",
Expand Down
8 changes: 4 additions & 4 deletions e2e/tests/ui-driven/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion editor.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@mui/material": "^5.15.10",
"@mui/utils": "^5.15.11",
"@opensystemslab/map": "1.0.0-alpha.3",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e6040d9",
"@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e",
"@tiptap/core": "^2.4.0",
"@tiptap/extension-bold": "^2.0.3",
"@tiptap/extension-bubble-menu": "^2.1.13",
Expand Down
10 changes: 5 additions & 5 deletions editor.planx.uk/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

192 changes: 191 additions & 1 deletion editor.planx.uk/src/@planx/components/Page/Public.test.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,191 @@
test.todo("coming soon!");
import React from "react";
import { setup } from "testUtils";
import { vi } from "vitest";
import { axe } from "vitest-axe";

import PageComponent from "./Public";
import { ProposedAdvertisements } from "./schema/AdvertConsent";

it("renders correctly", async () => {
const { getByRole } = setup(
<PageComponent
handleSubmit={vi.fn()}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
/>,
);

const title = getByRole("heading", {
level: 1,
name: "Tell us about your proposed advertisements",
});
expect(title).toBeVisible();
});

it("has no accessibility violations", async () => {
const { container } = setup(
<PageComponent
handleSubmit={vi.fn()}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
/>,
);

const results = await axe(container);
expect(results).toHaveNoViolations();
});

it("displays the supplied Page schema", () => {
const { getByLabelText } = setup(
<PageComponent
handleSubmit={vi.fn()}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
/>,
);

// Each PageSchema field is displayed
ProposedAdvertisements.fields.forEach(({ data }) => {
const input = getByLabelText(data.title);
expect(input).toBeVisible();
});
});

it("handles PageSchema errors", async () => {
const handleSubmit = vi.fn();
const {
user,
queryAllByTestId,
getAllByTestId,
getByTestId,
queryByText,
getAllByText,
} = setup(
<PageComponent
handleSubmit={handleSubmit}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
/>,
);

// No error messages present initially
let errorMessages = queryAllByTestId(/error-message-input-number/);
expect(queryByText("Enter your answer before continuing")).toBeNull();
errorMessages.forEach((message) => expect(message).toBeEmptyDOMElement());

// User hits "Continue" without populating fields
const continueButton = getByTestId("continue-button");
await user.click(continueButton);

// Error messages present, per field
errorMessages = getAllByTestId(/error-message-input-number/);
errorMessages.forEach((message) => expect(message).not.toBeEmptyDOMElement());
expect(getAllByText("Enter your answer before continuing")).toHaveLength(
ProposedAdvertisements.fields.length,
);

// Due to errors, component has not submitted data
expect(handleSubmit).not.toHaveBeenCalled();
});

it("submits a valid payload", async () => {
const handleSubmit = vi.fn();
const { user, getAllByLabelText, getByTestId } = setup(
<PageComponent
handleSubmit={handleSubmit}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
/>,
);

const inputs = getAllByLabelText(/How many/);
expect(inputs).toHaveLength(ProposedAdvertisements.fields.length);

// Populate each field
for (const input of inputs) {
await user.type(input, "1");
}

const continueButton = getByTestId("continue-button");
await user.click(continueButton);

expect(handleSubmit).toHaveBeenCalled();

const expectedFlattenedData = expect.objectContaining({
data: expect.objectContaining({
"testFn.fascia": 1,
"testFn.hoarding": 1,
"testFn.other": 1,
"testFn.projecting": 1,
}),
});
expect(handleSubmit).toHaveBeenCalledWith(expectedFlattenedData);

const expectedDefaultData = expect.objectContaining({
data: expect.objectContaining({
testFn: [
{
fascia: 1,
hoarding: 1,
other: 1,
projecting: 1,
},
],
}),
});
expect(handleSubmit).toHaveBeenCalledWith(expectedDefaultData);
});

it("handles back navigation", () => {
const previousData = {
data: {
testFn: [
{
fascia: 1,
hoarding: 2,
other: 3,
projecting: 4,
},
],
"testFn.fascia": 1,
"testFn.hoarding": 2,
"testFn.other": 3,
"testFn.projecting": 4,
},
};

const handleSubmit = vi.fn();
const { getByLabelText } = setup(
<PageComponent
handleSubmit={handleSubmit}
schema={ProposedAdvertisements}
fn="testFn"
schemaName="Proposed advertisements"
title="Tell us about your proposed advertisements"
previouslySubmittedData={previousData}
/>,
);

// Each input is correctly pre-populated
const fasciaInput = getByLabelText(/fascia/);
expect(fasciaInput).toHaveValue(1);

const hoardingInput = getByLabelText(/hoarding/);
expect(hoardingInput).toHaveValue(2);

const otherInput = getByLabelText(/other/);
expect(otherInput).toHaveValue(3);

const projectingInput = getByLabelText(/projecting/);
expect(projectingInput).toHaveValue(4);
});
25 changes: 23 additions & 2 deletions editor.planx.uk/src/@planx/components/Page/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { PublicProps } from "@planx/components/ui";
import { useFormik } from "formik";
import { FormikConfig, useFormik } from "formik";
import React from "react";

import { flatten } from "../List/utils";
import Card from "../shared/Preview/Card";
import CardHeader from "../shared/Preview/CardHeader";
import { useSchema } from "../shared/Schema/hook";
import { SchemaUserData } from "../shared/Schema/model";
import { SchemaFields } from "../shared/Schema/SchemaFields";
import { getPreviouslySubmittedData } from "../shared/utils";
import { Page } from "./model";
Expand All @@ -17,9 +19,28 @@ function PageComponent(props: Props) {
previousValues: getPreviouslySubmittedData(props),
});

const onSubmit: FormikConfig<SchemaUserData>["onSubmit"] = (data) => {
if (!props.handleSubmit) return;

// Default data - used for "back", to maintain a copy of the data as generated by the component
const defaultPassportData = { [props.fn]: data.schemaData };

// Flattened data - used for access via automation, SetValue etc
const flattenedData = flatten({ [props.fn]: data.schemaData[0] });

const userData = {
data: {
...defaultPassportData,
...flattenedData,
},
};

props.handleSubmit(userData);
};

const formik = useFormik({
...formikConfig,
onSubmit: (data) => console.log({ data }),
onSubmit,
});

return (
Expand Down

0 comments on commit 76c4f9d

Please sign in to comment.