Skip to content

Commit

Permalink
chore: Add dynamic row dialog (#4261)
Browse files Browse the repository at this point in the history
* add customize button text dialog
* update element schema to handle dynamic row properties
  • Loading branch information
timarney authored Sep 10, 2024
1 parent 6dddcd3 commit bdb9821
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const SelectedElement = ({
element = <ShortAnswer data-testid="number">0123456789</ShortAnswer>;
break;
case "dynamicRow":
element = <SubElement elIndex={item.index} formId={formId} />;
element = <SubElement item={item} elIndex={item.index} formId={formId} />;
break;
case "attestation":
element = <Options item={item} renderIcon={() => <CheckBoxEmptyIcon />} formId={formId} />;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";
import React, { useState } from "react";
import { DynamicRowDialog } from "@formBuilder/components/shared/DynamicRowDialog";
import { Button } from "@clientComponents/globals";
import { useTranslation } from "@i18n/client";
import { MoreIcon } from "@serverComponents/icons/MoreIcon";

export const CustomizeSetButton = ({
itemId,
itemIndex,
}: {
itemId: number;
itemIndex: number;
}) => {
const { t } = useTranslation("form-builder");
const [showCustomizeSetDialog, setShowCustomizeSetDialog] = useState(false);
return (
<>
<div className="mb-4">
<Button
onClick={() => {
setShowCustomizeSetDialog(true);
}}
theme="link"
className="group/button mb-2 !px-4 !py-2 text-sm leading-6"
>
<>
<MoreIcon className="mr-2 size-[24px] rounded-sm border-1 border-black group-focus/button:border-white group-focus/button:fill-white" />
{t("dynamicRow.dialog.customizeElement")}
</>
</Button>
</div>
{showCustomizeSetDialog && (
<DynamicRowDialog
itemId={itemId}
itemIndex={itemIndex}
handleClose={() => setShowCustomizeSetDialog(false)}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import React from "react";
import { useTemplateStore } from "@lib/store/useTemplateStore";
import { PanelBodySub } from "../../PanelBodySub";
import { FormElementTypes } from "@lib/types";
import { AddToSetButton } from "../AddToSetButton";
import {
LocalizedElementProperties,
Language,
Expand All @@ -14,8 +13,20 @@ import { SubElementModal } from "./SubElementModal";
import { PanelHightLight } from "./PanelHightlight";
import { PanelActions } from "../../PanelActions";
import { useHandleAdd } from "@lib/hooks/form-builder/useHandleAdd";
import { CustomizeSetButton } from "../CustomizeSetButton";
import { AddToSetButton } from "../AddToSetButton";
import { FormElementWithIndex } from "@lib/types/form-builder-types";

export const SubElement = ({ elIndex, formId, ...props }: { elIndex: number; formId: string }) => {
export const SubElement = ({
item,
elIndex,
formId,
...props
}: {
item: FormElementWithIndex;
elIndex: number;
formId: string;
}) => {
const {
updateField,
subMoveUp,
Expand Down Expand Up @@ -59,6 +70,7 @@ export const SubElement = ({ elIndex, formId, ...props }: { elIndex: number; for
}}
filterElements={elementFilter}
/>
<CustomizeSetButton itemId={item.id} itemIndex={item.index} />
</div>
);

Expand Down Expand Up @@ -119,13 +131,14 @@ export const SubElement = ({ elIndex, formId, ...props }: { elIndex: number; for
})}

{subElements.length >= 1 && (
<div className="m-4">
<div className="mb-2 ml-4 mt-4">
<AddToSetButton
handleAdd={(type?: FormElementTypes) => {
handleAddSubElement(elIndex, subElements.length, type);
}}
filterElements={elementFilter}
/>
<CustomizeSetButton itemId={item.id} itemIndex={item.index} />
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,18 @@ export const getTranslatedProperties = async (type: string) => {
};
};

export const getTranslatedDynamicRowProperties = async () => {
const { t: en } = await serverTranslation("form-builder", { lang: "en" });
const { t: fr } = await serverTranslation("form-builder", { lang: "fr" });

return {
addButtonTextEn: en("dynamicRow.addButtonText"),
removeButtonTextEn: en("dynamicRow.removeButtonText"),
addButtonTextFr: fr("dynamicRow.addButtonText"),
removeButtonTextFr: fr("dynamicRow.removeButtonText"),
};
};

export async function checkFlag(id: string) {
return checkOne(id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const Dialog = ({

return (
<dialog
className="h-full w-full bg-transparent bg-clip-padding p-0"
className="size-full bg-transparent bg-clip-padding p-0"
aria-labelledby="modal-title"
ref={dialogRef}
data-testid="dialog"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"use client";
import React, { useState } from "react";
import { useTranslation } from "@i18n/client";
import { Button } from "@clientComponents/globals";
import { useDialogRef, Dialog } from "./Dialog";
import { useTemplateStore } from "@lib/store/useTemplateStore";

export const TextInput = ({ label, children }: { label: string; children: React.ReactElement }) => {
return (
<div className="mb-4 flex rounded-md border-1 border-black">
<label className="block rounded-l-md border-r-1 border-black bg-slate-50 p-4 text-sm">
{label}
</label>
{React.cloneElement(children, {
className: "block w-full rounded-r-md p-2 outline-offset-[-5px]",
})}
</div>
);
};

export const DynamicRowDialog = ({
itemId,
itemIndex,
handleClose,
}: {
itemId: number;
itemIndex: number;
handleClose: () => void;
}) => {
const dialog = useDialogRef();
const { t } = useTranslation("form-builder");

const { updateField, elements } = useTemplateStore((s) => ({
elements: s.form.elements,
updateField: s.updateField,
}));

const item = elements.find((el) => el.id === itemId);
const rowProps = item?.properties?.dynamicRow;

const [addButtonValueEn, setAddButtonValueEn] = useState(rowProps?.addButtonTextEn || "");
const [addButtonValueFr, setAddButtonValueFr] = useState(rowProps?.addButtonTextFr || "");

const [removeButtonValueEn, setRemoveButtonValueEn] = useState(
rowProps?.removeButtonTextEn || ""
);
const [removeButtonValueFr, setRemoveButtonValueFr] = useState(
rowProps?.removeButtonTextFr || ""
);

const addButtonTextA11yEn = t("dynamicRow.addButtonTextA11yEn");
const addButtonTextA11yFr = t("dynamicRow.addButtonTextA11yFr");

const removeButtonTextA11yEn = t("dynamicRow.removeButtonTextA11yEn");
const removeButtonTextA11yFr = t("dynamicRow.removeButtonTextA11yFr");

const actions = (
<>
<Button
theme="secondary"
onClick={() => {
dialog.current?.close();
handleClose && handleClose();
}}
>
{t("dynamicRow.dialog.cancel")}
</Button>
<Button
className="ml-5"
theme="primary"
onClick={() => {
dialog.current?.close();

if (!item || !item.properties) return;

const properties = {
...item.properties,
dynamicRow: {
addButtonTextEn: addButtonValueEn,
addButtonTextFr: addButtonValueFr,
removeButtonTextEn: removeButtonValueEn,
removeButtonTextFr: removeButtonValueFr,
},
};
updateField(`form.elements[${itemIndex}].properties`, properties);
handleClose && handleClose();
}}
dataTestId="confirm-delete"
>
{t("dynamicRow.dialog.save")}
</Button>
</>
);

return (
<Dialog
handleClose={handleClose}
dialogRef={dialog}
actions={actions}
title={t("dynamicRow.dialog.title")}
>
<div className="p-5">
{/* Add button */}
<div className="mb-8">
<h4 className="mb-4 block font-bold">{t("dynamicRow.dialog.addButton.title")}</h4>
<p className="mb-4 text-sm">{t("dynamicRow.dialog.addButton.description")}</p>
<TextInput label={t("dynamicRow.dialog.english")}>
<input
aria-label={addButtonTextA11yEn}
value={addButtonValueEn}
onChange={(e) => setAddButtonValueEn(e.target.value)}
/>
</TextInput>
<TextInput label={t("dynamicRow.dialog.french")}>
<input
aria-label={addButtonTextA11yFr}
value={addButtonValueFr}
onChange={(e) => setAddButtonValueFr(e.target.value)}
/>
</TextInput>
</div>

{/* Remove button */}
<div>
<label className="mb-4 block font-bold">
{t("dynamicRow.dialog.removeButton.title")}
</label>
<p className="mb-4 text-sm">{t("dynamicRow.dialog.removeButton.description")}</p>
<TextInput label={t("dynamicRow.dialog.english")}>
<input
aria-label={removeButtonTextA11yEn}
value={removeButtonValueEn}
onChange={(e) => setRemoveButtonValueEn(e.target.value)}
/>
</TextInput>
<TextInput label={t("dynamicRow.dialog.french")}>
<input
aria-label={removeButtonTextA11yFr}
value={removeButtonValueFr}
onChange={(e) => setRemoveButtonValueFr(e.target.value)}
/>
</TextInput>
</div>
</div>
</Dialog>
);
};
27 changes: 26 additions & 1 deletion i18n/translations/en/form-builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -1068,5 +1068,30 @@
"logicSetup": "Skip to Branching set-up"
},
"goBack": "Go back",
"disabled": "disabled"
"disabled": "disabled",
"dynamicRow": {
"addButtonText": "Add",
"addButtonTextA11yEn": "Add button Text (english)",
"addButtonTextA11yFr": "Add button Text (french)",
"removeButtonText": "Remove",
"removeButtonTextA11yEn": "Remove button text (english)",
"removeButtonTextA11yFr": "Remove button text (french)",
"dialog": {
"customizeElement": "Customize element",
"title": "Customize set",
"english": "English",
"french": "French",
"cancel": "Cancel",
"save": "Save",
"addButton": {
"title": "Button to add to the set",
"description": "Create a descriptive button title to allow those filling out your form to add another set. Example: “Add another address” "
},
"removeButton": {
"title": "Button to remove from the set",
"label": "Add a dynamic row",
"description": "Create a descriptive button title to allow those filling out your form to remove a set. Example: “Remove address” "
}
}
}
}
27 changes: 26 additions & 1 deletion i18n/translations/fr/form-builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -1068,5 +1068,30 @@
"logicSetup": "Passer à la configuration d'embranchements'"
},
"goBack": "Retourner",
"disabled": "désactivé"
"disabled": "désactivé",
"dynamicRow": {
"addButtonText": "Add [FR]",
"addButtonTextA11yEn": "Add button text (english) [FR]",
"addButtonTextA11yFr": "Add button text (french) [FR]",
"removeButtonText": "Remove [FR]",
"removeButtonTextA11yEn": "Remove button text (english) [FR] ",
"removeButtonTextA11yFr": "Remove button text (french) [FR] ",
"dialog": {
"customizeElement": "Customize element [FR]",
"title": "Customize set [FR]",
"english": "English [FR]",
"french": "French [FR]",
"cancel": "Cancel [FR]",
"save": "Save [FR]",
"addButton": {
"title": "Button to add to the set [FR]",
"description": "Create a descriptive button title to allow those filling out your form to add another set. Example: “Add another address” [FR]"
},
"removeButton": {
"title": "Button to remove from the set [FR]",
"label": "Add a dynamic row [FR]",
"description": "Create a descriptive button title to allow those filling out your form to remove a set. Example: “Remove address” [FR]"
}
}
}
}
9 changes: 7 additions & 2 deletions lib/hooks/form-builder/useHandleAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { blockLoader, LoaderType } from "../../utils/form-builder/blockLoader";
import { allowedTemplates } from "@lib/utils/form-builder";
import { defaultField, createElement, setDescription } from "@lib/utils/form-builder/itemHelper";
import { useGroupStore } from "@formBuilder/components/shared/right-panel/treeview/store/useGroupStore";
import { getTranslatedElementProperties } from "@formBuilder/actions";
import {
getTranslatedElementProperties,
getTranslatedDynamicRowProperties,
} from "@formBuilder/actions";
import { useTreeRef } from "@formBuilder/components/shared/right-panel/treeview/provider/TreeRefProvider";

export const useHandleAdd = () => {
Expand Down Expand Up @@ -44,7 +47,9 @@ export const useHandleAdd = () => {
}

const item = await create(type as FormElementTypes);
// Note add() returns the element id -- we're not using it yet
if (item.type === "dynamicRow") {
item.properties.dynamicRow = await getTranslatedDynamicRowProperties();
}
const id = await add(index, item.type, item, groupId);
treeView?.current?.addItem(String(id));

Expand Down
21 changes: 21 additions & 0 deletions lib/middleware/schemas/templates.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,27 @@
"description": "Secondary paragraph/text of a question or element that provides additional context beyond the label in French",
"type": "string"
},
"dynamicRow": {
"type": "object",
"properties": {
"addButtonTextEn": {
"type": "string",
"description": "Text for the add button in English"
},
"removeButtonTextEn": {
"type": "string",
"description": "Text for the remove button in English"
},
"addButtonTextFr": {
"type": "string",
"description": "Text for the add button in French"
},
"removeButtonTextFr": {
"type": "string",
"description": "Text for the remove button in French"
}
}
},
"choices": {
"type": "array",
"items": {
Expand Down
Loading

0 comments on commit bdb9821

Please sign in to comment.