Skip to content

Commit

Permalink
chore: Refactor MoreModal to Dialog component with Event trigger (#4467)
Browse files Browse the repository at this point in the history
* Add unique constraint on invitations to ensure no duplicate invitations get created

* Just starting

* Get new dialog basically wired up and rendering content

* Update custom event details type

* Update most onChange handlers to update the item

* Unused import and console statement

* extract FormattedDateOptions

* Update remaining onChange handlers

* Extract TextInputOptions

* Extract remaining sections, eliminate ModalForm

* Cleanup

* More cleanup

* Replace Modal with Button and Event based trigger

* reorder some styles

* Fix test

* Add component test

* Add component test

* Add testid

* Add some component test stubs

* Use esc to close

* Restore Dialog title

* Use testid

* missed one

* force refresh

* Use testid

* Fix dropdown styling

* Retrieve item from templateStore when MoreDialog opens so it's always fresh

* Return item with index

* Rename function for clarity

* Use type without index

* Fix all required label / disable button

* Fix getFormElementById to search/find subItems

* Fire handle close when exiting by Esc

* Cleanup

* Remove old code

* Cleanup old MoreModal

* cleanup

* remove todo

* cleanup

* cleanup

* cleanup

* Fix up PanelActions spacing on subElements

* Bit of renaming

* Switched indexes to ids

* rename didn't propagate to test

* Remove old AddressCompleteOptions

* Update label for dynamicRow label/question

---------

Co-authored-by: Tim Arney <[email protected]>
  • Loading branch information
dsamojlenko and timarney authored Oct 29, 2024
1 parent 3485777 commit 1ad4ccb
Show file tree
Hide file tree
Showing 43 changed files with 1,147 additions and 629 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
"use client";
import { useTranslation } from "@i18n/client";
import { Checkbox, Radio } from "@formBuilder/components/shared";
import { ElementProperties, AddressComponents } from "@lib/types";
import { FormElementWithIndex } from "@lib/types/form-builder-types";
import { AddressComponents, FormElement, FormElementTypes } from "@lib/types";

export const AddressCompleteOptions = ({
item,
properties,
updateModalProperties,
setItem,
}: {
item: FormElementWithIndex;
properties: ElementProperties;
updateModalProperties: (id: number, properties: ElementProperties) => void;
item: FormElement;
setItem: (item: FormElement) => void;
}) => {
const { t } = useTranslation("form-builder");

if (item.type !== FormElementTypes.addressComplete) {
return null;
}

const updateAddressComponents = (props: AddressComponents) => {
// check if the addresscomponent exists, if it doesn't make it.
if (properties.addressComponents == undefined) {
if (item.properties.addressComponents == undefined) {
const baseAddress = {} as AddressComponents;
const addressComponent = Object.assign({}, baseAddress, props);
updateModalProperties(item.id, { ...properties, addressComponents: addressComponent });
setItem({
...item,
properties: { ...item.properties, addressComponents: addressComponent },
});
} else {
// clone the existing properties so that we don't overwrite other keys in "validation"
const addressComponent = Object.assign(
{},
properties.addressComponents,
item.properties.addressComponents,
props
) as AddressComponents;
updateModalProperties(item.id, { ...properties, addressComponents: addressComponent });
setItem({
...item,
properties: { ...item.properties, addressComponents: addressComponent },
});
}
};

Expand All @@ -37,24 +44,24 @@ export const AddressCompleteOptions = ({
<h3>{t("addElementDialog.addressComplete.options")}</h3>

<Checkbox
id={`addressComponent-${item.index}-id-canadianOnly`}
id={`addressComponent-${item.id}-id-canadianOnly`}
value={
`addressComponent-${item.index}-value-canadianOnly-` +
properties.addressComponents?.canadianOnly
`addressComponent-${item.id}-value-canadianOnly-` +
item.properties.addressComponents?.canadianOnly
}
key={
`addressComponent-${item.index}-canadianOnly-` +
properties.addressComponents?.canadianOnly
`addressComponent-${item.id}-canadianOnly-` +
item.properties.addressComponents?.canadianOnly
}
defaultChecked={properties.addressComponents?.canadianOnly}
defaultChecked={item.properties.addressComponents?.canadianOnly}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateAddressComponents({ canadianOnly: e.target.checked });
}}
label={t("addElementDialog.addressComplete.canadianOnly")}
></Checkbox>

<h4 className="mt-4">{t("addElementDialog.addressComplete.fields")}</h4>
<p className="mt-2 mb-4">{t("addElementDialog.addressComplete.fieldsDesc")}</p>
<p className="mb-4 mt-2">{t("addElementDialog.addressComplete.fieldsDesc")}</p>

<Radio
className="mt-2"
Expand All @@ -63,14 +70,14 @@ export const AddressCompleteOptions = ({
label={t("addElementDialog.addressComplete.fullAddress")}
value="addressType-full"
checked={
properties.addressComponents?.splitAddress === false ||
properties.addressComponents?.splitAddress === undefined
item.properties.addressComponents?.splitAddress === false ||
item.properties.addressComponents?.splitAddress === undefined
}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateAddressComponents({ splitAddress: !e.target.checked });
}}
/>
<div className="text-sm ml-12 mb-4">
<div className="mb-4 ml-12 text-sm">
{t("addElementDialog.addressComplete.fullAddressDesc")}
</div>
<Radio
Expand All @@ -79,12 +86,12 @@ export const AddressCompleteOptions = ({
id="addressType-split"
label={t("addElementDialog.addressComplete.splitAddress")}
value="addressType-split"
checked={properties.addressComponents?.splitAddress === true}
checked={item.properties.addressComponents?.splitAddress === true}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateAddressComponents({ splitAddress: e.target.checked });
}}
/>
<div className="text-sm ml-12 mb-4">
<div className="mb-4 ml-12 text-sm">
{t("addElementDialog.addressComplete.splitAddressDesc")}
</div>
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";
import React, { ChangeEvent } from "react";

import { useAutocompleteOptions } from "@lib/hooks/useAutocompleteOptions";
import { useTranslation } from "react-i18next";

interface DropdownOptionProps {
label: string;
value: string;
selected?: boolean;
}

const DropdownOption = (props: DropdownOptionProps): React.ReactElement => {
return <option value={props.value}>{props.label}</option>;
};

export const AutocompleteOptions = ({
handleChange,
selectedValue,
}: {
handleChange: (evt: ChangeEvent<HTMLSelectElement>) => void;
selectedValue: string;
}) => {
const autocompleteOptions = useAutocompleteOptions();
const { t } = useTranslation("form-builder");

const options = autocompleteOptions.map((option, i) => {
return (
<DropdownOption
key={i}
value={option.value}
label={option.label}
selected={option.value === selectedValue}
/>
);
});

return (
<div className="gcds-select-wrapper">
<select
data-testid="autocomplete"
className="gc-dropdown mb-4 inline-block"
onChange={handleChange}
value={selectedValue}
>
<option value="">{t("selectAutocomplete")}</option>
{options}
</select>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useTranslation } from "@i18n/client";
import { InfoDetails, Input } from "@formBuilder/components/shared";
import { FormElement, FormElementTypes } from "@lib/types";
import { Label } from "./Label";
import { Hint } from "./Hint";

export const CharacterLimitOptions = ({
item,
setItem,
}: {
item: FormElement;
setItem: (item: FormElement) => void;
}) => {
const { t } = useTranslation("form-builder");

if (
![FormElementTypes.textField, FormElementTypes.textArea].includes(item.type) ||
(item.properties.validation?.type && item.properties.validation?.type !== "text")
) {
return null;
}

return (
<section className="mb-4">
<div className="mb-2">
<Label htmlFor={`characterLength--modal--${item.id}`}>{t("maximumCharacterLength")}</Label>
<Hint>{t("characterLimitDescription")}</Hint>
<Input
id={`characterLength--modal--${item.id}`}
type="number"
min="1"
className="w-1/4"
value={item.properties.validation?.maxLength || ""}
onKeyDown={(e) => {
if (["-", "+", ".", "e"].includes(e.key)) {
e.preventDefault();
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
// if value is "", unset the field
if (e.target.value === "") {
setItem({
...item,
properties: {
...item.properties,
...{
validation: {
...(item.properties.validation ?? { required: false }),
maxLength: undefined,
},
},
},
});
return;
}

const value = parseInt(e.target.value);
if (!isNaN(value) && value >= 1) {
// clone the existing properties so that we don't overwrite other keys in "validation"
const validation = Object.assign({}, item.properties.validation, {
maxLength: value,
});
setItem({
...item,
properties: {
...item.properties,
...{ validation },
},
});
}
}}
/>
</div>
<InfoDetails summary={t("characterLimitWhenToUse.title")}>
<div className="mb-8 mt-4 border-l-3 border-gray-500 pl-8">
<p className="mb-4 text-sm">{t("characterLimitWhenToUse.text1")}</p>
<p className="mb-4 text-sm">{t("characterLimitWhenToUse.text2")}</p>
<p className="text-sm">{t("characterLimitWhenToUse.text3")}</p>
</div>
</InfoDetails>
</section>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useTranslation } from "@i18n/client";
import { LocalizedElementProperties } from "@lib/types/form-builder-types";
import { Label } from "./Label";
import { Hint } from "./Hint";
import { TextArea } from "@formBuilder/components/shared";
import { useTemplateStore } from "@lib/store/useTemplateStore";
import { FormElement } from "@lib/types";

export const Description = ({
item,
setItem,
}: {
item: FormElement;
setItem: (item: FormElement) => void;
}) => {
const { t } = useTranslation("form-builder");

const { localizeField, translationLanguagePriority } = useTemplateStore((s) => ({
localizeField: s.localizeField,
translationLanguagePriority: s.translationLanguagePriority,
}));

return (
<div className="mb-2">
<Label>{t("inputDescription")}</Label>
<Hint>{t("descriptionDescription")}</Hint>
<TextArea
id={`description--modal--${item.id}`}
placeholder={t("inputDescription")}
testId="description-input"
className="w-11/12"
onChange={(e) => {
const description = e.target.value.replace(/[\r\n]/gm, "");
setItem({
...item,
properties: {
...item.properties,
...{
[localizeField(
LocalizedElementProperties.DESCRIPTION,
translationLanguagePriority
)]: description,
},
},
});
}}
value={
item.properties[
localizeField(LocalizedElementProperties.DESCRIPTION, translationLanguagePriority)
]
}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Input } from "@formBuilder/components/shared";
import { useTranslation } from "@i18n/client";
import { FormElement, FormElementTypes } from "@lib/types";
import { Label } from "./Label";
import { Hint } from "./Hint";

export const DynamicRowOptions = ({
item,
setItem,
}: {
item: FormElement;
setItem: (item: FormElement) => void;
}) => {
const { t } = useTranslation("form-builder");

if (item.type !== FormElementTypes.dynamicRow) {
return null;
}

return (
<section className="mb-4">
<Label htmlFor={`maxNumberOfRows--modal--${item.id}`}>{t("maxNumberOfRows.label")}</Label>
<Hint>{t("maxNumberOfRows.description")}</Hint>
<Input
id={`maxNumberOfRows--modal--${item.id}`}
type="number"
min="1"
className="w-1/4"
value={item.properties.maxNumberOfRows || ""}
onKeyDown={(e) => {
if (["-", "+", ".", "e"].includes(e.key)) {
e.preventDefault();
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
// if value is "", unset the field
if (e.target.value === "") {
setItem({
...item,
properties: {
...item.properties,
...{ maxNumberOfRows: undefined },
},
});
return;
}

const value = parseInt(e.target.value);
if (!isNaN(value) && value >= 1) {
setItem({
...item,
properties: {
...item.properties,
...{ maxNumberOfRows: value },
},
});
}
}}
/>
</section>
);
};
Loading

0 comments on commit 1ad4ccb

Please sign in to comment.