From ce31c4602bdedc94fc2afd8ffbb82bc086b08926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Wed, 20 Nov 2024 13:50:30 +0000 Subject: [PATCH] refactor: Split up `Pay/Editor.tsx` into sections (#3987) --- .../src/@planx/components/Pay/Editor.tsx | 450 ------------------ .../Pay/{ => Editor}/Editor.test.tsx | 0 .../@planx/components/Pay/Editor/Editor.tsx | 185 +++++++ .../Pay/Editor/GovPayMetadataSection.tsx | 204 ++++++++ .../Pay/Editor/InviteToPaySection.tsx | 102 ++++ .../FlowEditor/components/forms/index.ts | 2 +- 6 files changed, 492 insertions(+), 451 deletions(-) delete mode 100644 editor.planx.uk/src/@planx/components/Pay/Editor.tsx rename editor.planx.uk/src/@planx/components/Pay/{ => Editor}/Editor.test.tsx (100%) create mode 100644 editor.planx.uk/src/@planx/components/Pay/Editor/Editor.tsx create mode 100644 editor.planx.uk/src/@planx/components/Pay/Editor/GovPayMetadataSection.tsx create mode 100644 editor.planx.uk/src/@planx/components/Pay/Editor/InviteToPaySection.tsx diff --git a/editor.planx.uk/src/@planx/components/Pay/Editor.tsx b/editor.planx.uk/src/@planx/components/Pay/Editor.tsx deleted file mode 100644 index b9be3cff16..0000000000 --- a/editor.planx.uk/src/@planx/components/Pay/Editor.tsx +++ /dev/null @@ -1,450 +0,0 @@ -import DataObjectIcon from "@mui/icons-material/DataObject"; -import Box from "@mui/material/Box"; -import Link from "@mui/material/Link"; -import Typography from "@mui/material/Typography"; -import { - ComponentType as TYPES, - GovPayMetadata, -} from "@opensystemslab/planx-core/types"; -import { - Pay, - REQUIRED_GOVPAY_METADATA, - validationSchema, -} from "@planx/components/Pay/model"; -import { parseBaseNodeData } from "@planx/components/shared"; -import { Form, Formik, useFormikContext } from "formik"; -import { useStore } from "pages/FlowEditor/lib/store"; -import React from "react"; -import { ComponentTagSelect } from "ui/editor/ComponentTagSelect"; -import { InternalNotes } from "ui/editor/InternalNotes"; -import ListManager, { - EditorProps as ListManagerEditorProps, -} from "ui/editor/ListManager/ListManager"; -import ModalSection from "ui/editor/ModalSection"; -import ModalSectionContent from "ui/editor/ModalSectionContent"; -import { MoreInformation } from "ui/editor/MoreInformation/MoreInformation"; -import RichTextInput from "ui/editor/RichTextInput/RichTextInput"; -import ErrorWrapper from "ui/shared/ErrorWrapper"; -import Input from "ui/shared/Input/Input"; -import InputRow from "ui/shared/InputRow"; -import { Switch } from "ui/shared/Switch"; - -import { ICONS } from "../shared/icons"; -import { EditorProps } from "../shared/types"; - -type FormikGovPayMetadata = - | Record[] - | string - | undefined; - -const GOVPAY_DOCS_URL = - "https://docs.payments.service.gov.uk/reporting/#add-more-information-to-a-payment-39-custom-metadata-39-or-39-reporting-columns-39"; - -/** - * Helper method to handle Formik errors in arrays - * Required as errors can be at array-level or field-level and the useFormikContext hook cannot correctly type infer this from the validation schema - * Docs: https://formik.org/docs/api/fieldarray#fieldarray-validation-gotchas - */ -const parseError = ( - errors: FormikGovPayMetadata, - index: number, -): string | undefined => { - // No errors - if (!errors) return; - - // Array-level error - handled at a higher level - if (typeof errors === "string") return; - - // No error for this field - if (!errors[index]) return; - - // Specific field-level error - return errors[index].key || errors[index].value; -}; - -/** - * Helper method to handle Formik "touched" in arrays - * Please see parseError() for additional context - */ -const parseTouched = ( - touched: string | undefined | FormikGovPayMetadata, - index: number, -): string | undefined => { - // No errors - if (!touched) return; - - // Array-level error - handled at a higher level - if (typeof touched === "string") return; - - // No error for this field - if (!touched[index]) return; - - // Specific field-level error - return touched[index].key && touched[index].value; -}; - -/** - * Disable required fields so they cannot be edited - * Only disable first instance, otherwise any field beginning with a required field will be disabled, and user will not be able to fix their mistake as the delete icon is also disabled - */ -const isFieldDisabled = (key: string, index: number) => - REQUIRED_GOVPAY_METADATA.includes(key) && - index === REQUIRED_GOVPAY_METADATA.indexOf(key); - -function GovPayMetadataEditor(props: ListManagerEditorProps) { - const { key: currKey, value: currVal } = props.value; - const isDisabled = isFieldDisabled(currKey, props.index); - const { errors, touched } = useFormikContext(); - const error = parseError( - errors.govPayMetadata as FormikGovPayMetadata, - props.index, - ); - const isTouched = parseTouched( - touched.govPayMetadata as FormikGovPayMetadata, - props.index, - ); - - return ( - - - - - props.onChange({ key: newKey, value: currVal }) - } - placeholder="key" - /> - - props.onChange({ key: currKey, value: newVal }) - } - placeholder="value" - /> - - - - ); -} - -export type Props = EditorProps; - -const Component: React.FC = (props: Props) => { - const [flowName] = useStore((store) => [store.flowName]); - const initialValues: Pay = { - title: props.node?.data?.title || "Pay for your application", - bannerTitle: - props.node?.data?.bannerTitle || - "The planning fee for this application is", - description: - props.node?.data?.description || - `

The planning fee covers the cost of processing your application.\ - Find out more about how planning fees are calculated (opens in new tab).

`, - fn: props.node?.data?.fn, - instructionsTitle: props.node?.data?.instructionsTitle || "How to pay", - instructionsDescription: - props.node?.data?.instructionsDescription || - `

You can pay for your application by using GOV.UK Pay.

\ -

Your application will be sent after you have paid the fee. \ - Wait until you see an application sent message before closing your browser.

`, - hidePay: props.node?.data?.hidePay || false, - allowInviteToPay: props.node?.data?.allowInviteToPay ?? true, - secondaryPageTitle: - props.node?.data?.secondaryPageTitle || - "Invite someone else to pay for this application", - nomineeTitle: - props.node?.data?.nomineeTitle || "Details of the person paying", - nomineeDescription: props.node?.data?.nomineeDescription, - yourDetailsTitle: props.node?.data?.yourDetailsTitle || "Your details", - yourDetailsDescription: props.node?.data?.yourDetailsDescription, - yourDetailsLabel: - props.node?.data?.yourDetailsLabel || "Your name or organisation name", - govPayMetadata: props.node?.data?.govPayMetadata || [ - { - key: "flow", - value: flowName, - }, - { - key: "source", - value: "PlanX", - }, - { - key: "paidViaInviteToPay", - value: "@paidViaInviteToPay", - }, - ], - ...parseBaseNodeData(props.node?.data), - }; - - const onSubmit = (newValues: Pay) => { - if (props.handleSubmit) { - props.handleSubmit({ type: TYPES.Pay, data: newValues }); - } - }; - - return ( - - initialValues={initialValues} - onSubmit={onSubmit} - validationSchema={validationSchema} - validateOnChange={true} - validateOnBlur={true} - > - {({ - values, - handleChange, - setFieldValue, - errors, - touched, - setTouched, - }) => ( -