Skip to content

Commit

Permalink
Merge branch 'dp/list-component-form-values' of github.com:theopensys…
Browse files Browse the repository at this point in the history
…temslab/planx-new into dp/list-component-form-values
  • Loading branch information
jessicamcinchak committed Jun 5, 2024
2 parents dede865 + ba8757c commit e117227
Show file tree
Hide file tree
Showing 12 changed files with 690 additions and 318 deletions.
5 changes: 3 additions & 2 deletions editor.planx.uk/src/@planx/components/List/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<TYPES.List, List>;

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)
];

Expand Down
145 changes: 117 additions & 28 deletions editor.planx.uk/src/@planx/components/List/Public/Context.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,162 @@
import React, { createContext, ReactNode, useContext, useState } from "react";
import {
getPreviouslySubmittedData,
makeData,
} from "@planx/components/shared/utils";
import { PublicProps } from "@planx/components/ui";
import { FormikProps, useFormik } from "formik";
import React, {
createContext,
PropsWithChildren,
useContext,
useState,
} from "react";

import { generateNewItem, Schema, UserData } from "../model";
import {
generateInitialValues,
generateValidationSchema,
List,
Schema,
UserData,
} from "../model";

interface ListContextValue {
schema: Schema;
activeIndex: number | undefined;
userData: UserData;
activeIndex: number;
addNewItem: () => void;
saveItem: (index: number, updatedItem: UserData[0]) => void;
saveItem: () => Promise<void>;
removeItem: (index: number) => void;
editItem: (index: number) => void;
cancelEditItem: () => void;
formik: FormikProps<UserData>;
validateAndSubmitForm: () => void;
listProps: PublicProps<List>;
errors: {
addItem: boolean;
unsavedItem: boolean;
min: boolean;
max: boolean;
};
}

interface ListProviderProps {
children: ReactNode;
schema: Schema;
}
type ListProviderProps = PropsWithChildren<PublicProps<List>>;

const ListContext = createContext<ListContextValue | undefined>(undefined);

export const ListProvider: React.FC<ListProviderProps> = ({
children,
schema,
}) => {
const [activeIndex, setActiveIndex] = useState<number | undefined>(0);
const [userData, setUserData] = useState<UserData>(
schema.min === 0 ? [] : [generateNewItem(schema)],
export const ListProvider: React.FC<ListProviderProps> = (props) => {
const { schema, children, handleSubmit } = props;

const [activeIndex, setActiveIndex] = useState<number>(
props.previouslySubmittedData ? -1 : 0,
);

const addNewItem = () => {
setUserData([...userData, generateNewItem(schema)]);
setActiveIndex((prev) => (prev === undefined ? 0 : prev + 1));
const [addItemError, setAddItemError] = useState<boolean>(false);
const [unsavedItemError, setUnsavedItemError] = useState<boolean>(false);
const [minError, setMinError] = useState<boolean>(false);
const [maxError, setMaxError] = useState<boolean>(false);

const resetErrors = () => {
setMinError(false);
setMaxError(false);
setUnsavedItemError(false);
};

const saveItem = (index: number, updatedItem: UserData[0]) => {
setUserData((prev) =>
prev.map((item, i) => (i === index ? updatedItem : item)),
);
const addNewItem = async () => {
resetErrors();

// Do not allow a new item to be added if there's still an active item
if (activeIndex !== -1) return setAddItemError(true);

// Add new item, and set to active
setAddItemError(false);
formik.values.userData.push(generateInitialValues(schema));
setActiveIndex(formik.values.userData.length - 1);
};

const editItem = (index: number) => setActiveIndex(index);
const saveItem = async () => {
resetErrors();

const errors = await formik.validateForm();
const isValid = !errors.userData?.length;
if (isValid) {
setActiveIndex(-1);
setAddItemError(false);
}
};

const removeItem = (index: number) => {
resetErrors();

if (activeIndex && index < activeIndex) {
// If item is before currently active card, retain active card
setActiveIndex((prev) => (prev === undefined ? 0 : prev - 1));
setActiveIndex((prev) => (prev === -1 ? 0 : prev - 1));
} else if (index === activeIndex || index === 0) {
// If item is currently in Edit mode, exit Edit mode
cancelEditItem();
}

// Remove item from userData
setUserData((prev) => prev.filter((_, i) => i !== index));
formik.setFieldValue(
"userData",
formik.values.userData.filter((_, i) => i !== index),
);
};

const validateAndSubmitForm = () => {
// Do not allow submissions with an unsaved item
if (activeIndex !== -1) return setUnsavedItemError(true);

// Manually validate min/max
if (formik.values.userData.length < schema.min) {
return setMinError(true);
}
if (schema.max && formik.values.userData.length > schema.max) {
return setMaxError(true);
}
formik.handleSubmit();
};

const cancelEditItem = () => setActiveIndex(-1);

const editItem = (index: number) => setActiveIndex(index);

const getInitialValues = () => {
const previousValues = getPreviouslySubmittedData(props);
if (previousValues) return previousValues;

return schema.min ? [generateInitialValues(schema)] : [];
};

const cancelEditItem = () => setActiveIndex(undefined);
const formik = useFormik<UserData>({
initialValues: {
userData: getInitialValues(),
},
onSubmit: (values) => {
handleSubmit?.(makeData(props, values.userData));
},
validateOnBlur: false,
validateOnChange: false,
validationSchema: generateValidationSchema(schema),
});

return (
<ListContext.Provider
value={{
activeIndex,
userData,
addNewItem,
saveItem,
schema,
listProps: props,
editItem,
removeItem,
cancelEditItem,
formik,
validateAndSubmitForm,
errors: {
addItem: addItemError,
unsavedItem: unsavedItemError,
min: minError,
max: maxError,
},
}}
>
{children}
Expand Down
Loading

0 comments on commit e117227

Please sign in to comment.