Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add _requestedFiles key to Passport #2763

Merged
merged 7 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function Component(props: any) {
</InputRow>
<InputRow>
<Input
// required
required
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this should be correct behavior - but I wonder if this will break any discretionary submission services like building control forms or something? Hopefully not / easy enough to fix forward if we find it does I guess? I don't necessarily think we should be putting as much proactive effort into find/replace etc outside of "standard" services?

format="data"
name="fn"
value={formik.values.fn}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Story = StoryObj<typeof meta>;

export const Basic = {
args: {
fn: "roofPlan",
title: "Upload roof plan",
description:
"The plan should show the roof of the building as it looks today.",
Expand Down
32 changes: 9 additions & 23 deletions editor.planx.uk/src/@planx/components/FileUpload/Public.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,32 @@ import { uniqueId } from "lodash";
import React from "react";
import { axe, setup } from "testUtils";

import { PASSPORT_REQUESTED_FILES_KEY } from "../FileUploadAndLabel/model";
import FileUpload from "./Public";

test("renders correctly and blocks submit if there are no files added", async () => {
const handleSubmit = jest.fn();

setup(<FileUpload handleSubmit={handleSubmit} />);
setup(<FileUpload fn="someKey" handleSubmit={handleSubmit} />);

expect(screen.getByRole("button", { name: "Continue" })).toBeDisabled();

expect(handleSubmit).toHaveBeenCalledTimes(0);
});

test("recovers previously submitted files when clicking the back button", async () => {
const handleSubmit = jest.fn();
const componentId = uniqueId();
const uploadedFile = {
data: {
[componentId]: [dummyFile],
},
};

const { user } = setup(
<FileUpload
id={componentId}
handleSubmit={handleSubmit}
previouslySubmittedData={uploadedFile}
/>,
);

await user.click(screen.getByTestId("continue-button"));

expect(handleSubmit).toHaveBeenCalledWith(uploadedFile);
});

test("recovers previously submitted files when clicking the back button even if a data field is set", async () => {
const handleSubmit = jest.fn();
const componentId = uniqueId();
const dataField = "data-field";
const uploadedFile = {
data: {
[dataField]: [dummyFile],
[PASSPORT_REQUESTED_FILES_KEY]: [
{
fn: dataField,
condition: "AlwaysRequired",
},
],
},
};

Expand Down Expand Up @@ -85,6 +70,7 @@ it("should not have any accessibility violations", async () => {

const { container } = setup(
<FileUpload
fn="someKey"
id={componentId}
handleSubmit={handleSubmit}
description="description"
Expand Down
62 changes: 43 additions & 19 deletions editor.planx.uk/src/@planx/components/FileUpload/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { MoreInformation } from "@planx/components/shared";
import Card from "@planx/components/shared/Preview/Card";
import QuestionHeader from "@planx/components/shared/Preview/QuestionHeader";
import { Store } from "pages/FlowEditor/lib/store";
import { Store, useStore } from "pages/FlowEditor/lib/store";
import type { handleSubmit } from "pages/Preview/Node";
import React, { useEffect, useRef, useState } from "react";
import { FileWithPath } from "react-dropzone";
import ErrorWrapper from "ui/shared/ErrorWrapper";
import { array } from "yup";

import {
Condition,
PASSPORT_REQUESTED_FILES_KEY,
RequestedFile,
} from "../FileUploadAndLabel/model";
import { PrivateFileUpload } from "../shared/PrivateFileUpload/PrivateFileUpload";
import { getPreviouslySubmittedData, makeData } from "../shared/utils";

interface Props extends MoreInformation {
id?: string;
title?: string;
fn?: string;
fn: string;
description?: string;
handleSubmit: handleSubmit;
previouslySubmittedData?: Store.userData;
Expand Down Expand Up @@ -50,27 +55,46 @@ const FileUpload: React.FC<Props> = (props) => {
const [slots, setSlots] = useState<FileUploadSlot[]>(recoveredSlots ?? []);
const [validationError, setValidationError] = useState<string>();

const existingRequestedFiles: RequestedFile[] =
useStore(
(state) => state.computePassport().data?.[PASSPORT_REQUESTED_FILES_KEY],
) || [];

const requestedFile: RequestedFile = {
fn: props.fn,
condition: Condition.AlwaysRequired,
};

const uploadedFiles = (slots: FileUploadSlot[]) =>
makeData(
props,
slots.map((slot) => ({
url: slot.url,
filename: slot.file.path,
cachedSlot: {
...slot,
file: {
path: slot.file.path,
type: slot.file.type,
size: slot.file.size,
},
},
})),
);

const handleSubmit = () => {
slotsSchema
.validate(slots)
.then(() => {
props.handleSubmit(
makeData(
props,
slots.map((slot) => ({
url: slot.url,
filename: slot.file.path,
cachedSlot: {
...slot,
file: {
path: slot.file.path,
type: slot.file.type,
size: slot.file.size,
},
},
})),
),
);
props.handleSubmit({
data: {
...uploadedFiles(slots).data,
[PASSPORT_REQUESTED_FILES_KEY]: [
...existingRequestedFiles,
requestedFile,
],
},
});
})
.catch((err) => {
setValidationError(err.message);
Expand Down
35 changes: 31 additions & 4 deletions editor.planx.uk/src/@planx/components/FileUploadAndLabel/model.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import cloneDeep from "lodash/cloneDeep";
import sortBy from "lodash/sortBy";
import uniqBy from "lodash/uniqBy";
import { Store } from "pages/FlowEditor/lib/store";
import { Store, useStore } from "pages/FlowEditor/lib/store";
import { FileWithPath } from "react-dropzone";

import { FileUploadSlot } from "../FileUpload/Public";
import { MoreInformation, parseMoreInformation } from "../shared";

export const PASSPORT_REQUESTED_FILES_KEY = "_requestedFiles" as const;

export interface RequestedFile {
fn: string;
condition: Condition;
}

/**
* Conditions which can apply to a rule
* Order is significant - these represent the hierarchy of these rules
Expand Down Expand Up @@ -220,6 +227,11 @@ const formatUserFiles = (userFile: UserFileWithSlots): FormattedUserFile[] =>
},
}));

const formatRequestedFiles = ({
fn,
rule: { condition },
}: FileType): RequestedFile => ({ fn, condition });

/**
* Type guard to coerce UserFile -> UserFileWithSlot
*/
Expand All @@ -233,17 +245,32 @@ const hasSlots = (userFile: UserFile): userFile is UserFileWithSlots =>
export const generatePayload = (fileList: FileList): Store.userData => {
const newPassportData: Store.userData["data"] = {};

const uploadedFiles = [
const requestedFiles = [
...fileList.required,
...fileList.recommended,
...fileList.optional,
].filter(hasSlots);
];

const uploadedFiles = requestedFiles.filter(hasSlots);

uploadedFiles.forEach((userFile) => {
newPassportData[userFile.fn] = formatUserFiles(userFile);
});

return { data: newPassportData };
const existingRequestedFiles: FileType[] =
useStore.getState().computePassport().data?.[
PASSPORT_REQUESTED_FILES_KEY
] || [];

return {
data: {
...newPassportData,
[PASSPORT_REQUESTED_FILES_KEY]: [
...existingRequestedFiles,
...requestedFiles.map(formatRequestedFiles),
],
},
};
};

const getCachedSlotsFromPreviousData = (
Expand Down
2 changes: 1 addition & 1 deletion editor.planx.uk/src/@planx/components/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const makeData = <T>(
props: any,
value: T,
overwriteKey?: string,
): {} | { data: Record<string, T> } => {
): Record<string, never> | { data: Record<string, T> } => {
Copy link
Contributor Author

@DafyddLlyr DafyddLlyr Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

if (isEmpty(value)) return {};
else
return {
Expand Down
Loading