diff --git a/app/(gcforms)/[locale]/(form administration)/form-builder/[id]/edit/components/ElementPanel.tsx b/app/(gcforms)/[locale]/(form administration)/form-builder/[id]/edit/components/ElementPanel.tsx index 1a51838b94..4855666ecd 100644 --- a/app/(gcforms)/[locale]/(form administration)/form-builder/[id]/edit/components/ElementPanel.tsx +++ b/app/(gcforms)/[locale]/(form administration)/form-builder/[id]/edit/components/ElementPanel.tsx @@ -22,16 +22,25 @@ export const ElementPanel = ({ elements: FormElement[]; formId: string; }) => { - const { getFocusInput, setChangeKey, setFocusInput, remove, moveUp, moveDown, duplicateElement } = - useTemplateStore((s) => ({ - getFocusInput: s.getFocusInput, - setChangeKey: s.setChangeKey, - setFocusInput: s.setFocusInput, - remove: s.remove, - moveUp: s.moveUp, - moveDown: s.moveDown, - duplicateElement: s.duplicateElement, - })); + const { + getFocusInput, + setChangeKey, + setFocusInput, + remove, + moveUp, + moveDown, + duplicateElement, + incrementNextElementId, + } = useTemplateStore((s) => ({ + getFocusInput: s.getFocusInput, + setChangeKey: s.setChangeKey, + setFocusInput: s.setFocusInput, + remove: s.remove, + moveUp: s.moveUp, + moveDown: s.moveDown, + duplicateElement: s.duplicateElement, + incrementNextElementId: s.incrementNextElementId, + })); const [className, setClassName] = useState(""); const [ifFocus, setIfFocus] = useState(false); @@ -74,7 +83,8 @@ export const ElementPanel = ({ setFocusInput(true); const { en, fr } = await getTranslatedProperties("copy"); duplicateElement(item.id, groupId, en, fr); - }, [duplicateElement, groupId, item.id, setFocusInput]); + incrementNextElementId(); + }, [duplicateElement, incrementNextElementId, groupId, item.id, setFocusInput]); return (
{ - const { add, addSubItem, setChangeKey } = useTemplateStore((s) => ({ + const { add, incrementNextElementId, addSubItem, setChangeKey } = useTemplateStore((s) => ({ add: s.add, addSubItem: s.addSubItem, setChangeKey: s.setChangeKey, + incrementNextElementId: s.incrementNextElementId, })); const { treeView } = useTreeRef(); @@ -61,6 +62,7 @@ export const useHandleAdd = () => { blockLoader(type as LoaderType, index, async (data, position) => { // Note add() returns the element id -- we're not using it yet id = await add(position, data.type, data, groupId); + incrementNextElementId(); }); return id; } @@ -70,6 +72,7 @@ export const useHandleAdd = () => { item.properties.dynamicRow = await getTranslatedDynamicRowProperties(); } id = await add(index, item.type, item, groupId); + incrementNextElementId(); treeView?.current?.addItem(String(id)); const el = document.getElementById(`item-${id}`); @@ -82,7 +85,7 @@ export const useHandleAdd = () => { el?.focus(); }, - [add, create, groupId, treeView] + [add, incrementNextElementId, create, groupId, treeView] ); const handleAddSubElement = useCallback( diff --git a/lib/store/defaults.ts b/lib/store/defaults.ts index c75746e5cc..760366a09b 100644 --- a/lib/store/defaults.ts +++ b/lib/store/defaults.ts @@ -38,5 +38,6 @@ export const defaultForm = { }, layout: [], elements: [], + nextElementId: 1, groups: {}, }; diff --git a/lib/store/types.ts b/lib/store/types.ts index 41739511ff..42eb37f1a7 100644 --- a/lib/store/types.ts +++ b/lib/store/types.ts @@ -76,6 +76,7 @@ export interface TemplateStoreState extends TemplateStoreProps { setChangeKey: (key: string) => void; getGroupsEnabled: () => boolean; setGroupsLayout: (layout: string[]) => void; + incrementNextElementId: () => void; } export interface InitialTemplateStoreProps extends TemplateStoreProps { diff --git a/lib/store/useTemplateStore.tsx b/lib/store/useTemplateStore.tsx index 66366b9918..a8b62c4a03 100644 --- a/lib/store/useTemplateStore.tsx +++ b/lib/store/useTemplateStore.tsx @@ -29,7 +29,6 @@ import { moveElementUp, removeElementById, removeById, - incrementElementId, getSchemaFromState, incrementSubElementId, cleanInput, @@ -80,6 +79,11 @@ const createTemplateStore = (initProps?: Partial) => } else { initProps.form.groups = orderGroups(initProps.form.groups, initProps.form.groupsLayout); } + + // Handle legacy forms to ensure nextElementId is set correctly + if (initProps.form.nextElementId === 1 && initProps.form.elements.length == 1) { + initProps.form.nextElementId = initProps.form.elements.length + 1; + } } return createStore()( @@ -216,7 +220,9 @@ const createTemplateStore = (initProps?: Partial) => set((state) => { const allowGroups = state.allowGroupsFlag; - const id = incrementElementId(state.form.elements); + const id = state.form.nextElementId; + state.incrementNextElementId(); + const item = { ...defaultField, ...data, @@ -366,7 +372,7 @@ const createTemplateStore = (initProps?: Partial) => duplicateElement: (itemId, groupId = "", copyEn = "", copyFr = "") => { const elIndex = get().form.elements.findIndex((el) => el.id === itemId); set((state) => { - const id = incrementElementId(state.form.elements); + const id = state.form.nextElementId; // deep copy the element const element = JSON.parse(JSON.stringify(state.form.elements[elIndex])); element.id = id; @@ -497,6 +503,13 @@ const createTemplateStore = (initProps?: Partial) => state.form.groupsLayout = layout; }); }, + incrementNextElementId: () => { + set((state) => { + // Increment nextElementId + const nextElementId = state.form.nextElementId; + state.form.nextElementId = nextElementId + 1; + }); + }, }), { name: "form-storage", diff --git a/lib/types/form-types.ts b/lib/types/form-types.ts index cb9382f652..1c6b3b795c 100644 --- a/lib/types/form-types.ts +++ b/lib/types/form-types.ts @@ -143,6 +143,7 @@ export interface FormProperties { groups?: GroupsType; groupsLayout?: string[]; elements: FormElement[]; + nextElementId: number; brand?: BrandProperties; formPurpose?: string; [key: string]: diff --git a/lib/utils/form-builder/__tests__/util.test.js b/lib/utils/form-builder/__tests__/util.test.js index 145172509d..655e59a30f 100644 --- a/lib/utils/form-builder/__tests__/util.test.js +++ b/lib/utils/form-builder/__tests__/util.test.js @@ -1,6 +1,5 @@ import { sortByLayout, - incrementElementId, swap, moveUp, moveDown, @@ -37,13 +36,6 @@ describe("Util", () => { expect(sorted4).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]); }); - it("increments element id", () => { - expect(incrementElementId([])).toBe(1); - expect(incrementElementId([{ id: 3 }])).toBe(4); - expect(incrementElementId([{ id: 1 }, { id: 3 }, { id: 2 }])).toBe(4); - expect(incrementElementId([{ id: 6 }, { id: 8 }, { id: 4 }])).toBe(9); - }); - it("increments subElement id", () => { expect(incrementSubElementId([], 4)).toBe(401); expect(incrementSubElementId([], 5)).toBe(501); diff --git a/lib/utils/form-builder/index.ts b/lib/utils/form-builder/index.ts index fbcc3dbf86..abfcadf561 100644 --- a/lib/utils/form-builder/index.ts +++ b/lib/utils/form-builder/index.ts @@ -68,14 +68,6 @@ export const swapElement = (arr: FormElement[], index1: number, index2: number) }); }; -export const incrementElementId = (elements: FormElement[]) => { - if (!elements || !elements.length) { - return 1; - } - const ids = elements.map((element) => element.id).sort((a, b) => a - b); - return ids[ids.length - 1] + 1; -}; - export const incrementSubElementId = (subElements: FormElement[], elId: number) => { if (!subElements || !subElements.length) { return Number(elId.toString() + "01"); @@ -126,6 +118,7 @@ export const getSchemaFromState = (state: TemplateStoreState, allowGroups = fals layout, groups, groupsLayout, + nextElementId, }, } = state; @@ -141,6 +134,7 @@ export const getSchemaFromState = (state: TemplateStoreState, allowGroups = fals brand, groups, groupsLayout, + nextElementId, }; // Force this is off until a enable in a follow-up PR