diff --git a/shared-helpers/__tests__/views/multiselectQuestions.test.ts b/shared-helpers/__tests__/views/multiselectQuestions.test.ts new file mode 100644 index 0000000000..efd8090cc1 --- /dev/null +++ b/shared-helpers/__tests__/views/multiselectQuestions.test.ts @@ -0,0 +1,14 @@ +import { cleanup } from "@testing-library/react" +import { cleanMultiselectString } from "../../src/views/multiselectQuestions" + +afterEach(cleanup) + +describe("multiselectQuestions", () => { + describe("cleanMultiselectString", () => { + it("should remove expected characters", () => { + expect( + cleanMultiselectString("I'm a string, and I have a comma, period, and apostrophe.") + ).toBe("Im a string and I have a comma period and apostrophe") + }) + }) +}) diff --git a/shared-helpers/src/views/multiselectQuestions.tsx b/shared-helpers/src/views/multiselectQuestions.tsx index 3d9c822430..45cf7b13e4 100644 --- a/shared-helpers/src/views/multiselectQuestions.tsx +++ b/shared-helpers/src/views/multiselectQuestions.tsx @@ -21,6 +21,11 @@ import { stateKeys } from "../utilities/formKeys" import { AddressHolder } from "../utilities/constants" import { FormAddressAlternate } from "./address/FormAddressAlternate" +// Removes periods, commas, and apostrophes +export const cleanMultiselectString = (name: string | undefined) => { + return name?.replace(/\.|,|'/g, "") +} + export const listingSectionQuestions = ( listing: Listing, applicationSection: ApplicationSection @@ -37,8 +42,8 @@ export const fieldName = ( applicationSection: ApplicationSection, optionName?: string ) => { - return `application.${applicationSection}.${questionName?.replace(/'/g, "")}${ - optionName ? `.${optionName?.replace(/'/g, "")}` : "" + return `application.${applicationSection}.${cleanMultiselectString(questionName)}${ + optionName ? `.${cleanMultiselectString(optionName)}` : "" }` } @@ -343,7 +348,8 @@ export const mapCheckboxesToApi = ( question: MultiselectQuestion, applicationSection: ApplicationSection ): ApplicationMultiselectQuestion => { - const data = formData["application"][applicationSection][question.text.replace(/'/g, "")] + const data = + formData["application"][applicationSection][cleanMultiselectString(question.text) || ""] const claimed = !!Object.keys(data).filter((key) => data[key] === true).length const addressFields = Object.keys(data).filter((option) => Object.keys(data[option])) @@ -378,8 +384,17 @@ export const mapCheckboxesToApi = ( } } + const getFinalKey = () => { + const optionKey = question?.options?.find( + (elem) => cleanMultiselectString(elem.text) === key + )?.text + const cleanOptOutKey = cleanMultiselectString(question?.optOutText) + if (cleanOptOutKey === key) return question?.optOutText || key + return optionKey || key + } + return { - key, + key: getFinalKey(), mapPinPosition: data?.[`${key}-mapPinPosition`], checked: data[key] === true, extraData: extraData, @@ -433,26 +448,27 @@ export const mapApiToMultiselectForm = ( if (appQuestion.inputType === "checkbox") { options = question.options.reduce((acc, curr) => { const claimed = curr.checked + const cleanKey = cleanMultiselectString(curr.key) || "" if (appQuestion.inputType === "checkbox") { - acc[curr.key] = claimed + acc[cleanKey] = claimed if (curr.extraData?.length) { - acc[`${curr.key}-address`] = curr.extraData[0].value + acc[`${cleanKey}-address`] = curr.extraData[0].value const addressHolderName = curr.extraData?.find( (field) => field.key === AddressHolder.Name ) if (addressHolderName) { - acc[`${curr.key}-${AddressHolder.Name}`] = addressHolderName.value + acc[`${cleanKey}-${AddressHolder.Name}`] = addressHolderName.value } const addressHolderRelationship = curr.extraData?.find( (field) => field.key === AddressHolder.Relationship ) if (addressHolderRelationship) { - acc[`${curr.key}-${AddressHolder.Relationship}`] = addressHolderRelationship.value + acc[`${cleanKey}-${AddressHolder.Relationship}`] = addressHolderRelationship.value } if (curr?.mapPinPosition) { - acc[`${curr.key}-mapPinPosition`] = curr.mapPinPosition + acc[`${cleanKey}-mapPinPosition`] = curr.mapPinPosition } } } diff --git a/sites/partners/src/components/applications/PaperApplicationForm/sections/FormMultiselectQuestions.tsx b/sites/partners/src/components/applications/PaperApplicationForm/sections/FormMultiselectQuestions.tsx index cac836d64c..9a574a9458 100644 --- a/sites/partners/src/components/applications/PaperApplicationForm/sections/FormMultiselectQuestions.tsx +++ b/sites/partners/src/components/applications/PaperApplicationForm/sections/FormMultiselectQuestions.tsx @@ -2,7 +2,13 @@ import React, { useEffect, useMemo, useState } from "react" import { Field, t, FieldGroup, resolveObject } from "@bloom-housing/ui-components" import { FieldValue, Grid } from "@bloom-housing/ui-seeds" import { useFormContext } from "react-hook-form" -import { stateKeys, getInputType, fieldName, AddressHolder } from "@bloom-housing/shared-helpers" +import { + stateKeys, + getInputType, + fieldName, + AddressHolder, + cleanMultiselectString, +} from "@bloom-housing/shared-helpers" import { ApplicationSection, ListingMultiselectQuestion, @@ -41,7 +47,11 @@ const FormMultiselectQuestions = ({ questions?.forEach((listingQuestion) => listingQuestion?.multiselectQuestion.options.forEach((option) => keys.push( - fieldName(listingQuestion?.multiselectQuestion.text, applicationSection, option.text) + fieldName( + listingQuestion?.multiselectQuestion.text, + applicationSection, + cleanMultiselectString(option.text) + ) ) ) ) @@ -68,7 +78,11 @@ const FormMultiselectQuestions = ({ const watchQuestions = watch(allOptionFieldNames) const getCheckboxOption = (option: MultiselectOption, question: MultiselectQuestion) => { - const optionFieldName = fieldName(question.text, applicationSection, option.text) + const optionFieldName = fieldName( + question.text, + applicationSection, + cleanMultiselectString(option.text) + ) return ( { + const initialMultiselectQuestion = listing.listingMultiselectQuestions.find( + (elem) => + cleanMultiselectString(elem.multiselectQuestion.text) === + cleanMultiselectString(question.key) + ) + + const initialOption = initialMultiselectQuestion?.multiselectQuestion.options.find( + (elem) => cleanMultiselectString(elem.text) === option.key + ) + + const initialOptOut = initialMultiselectQuestion?.multiselectQuestion.optOutText + + const optOutOption = + option.key === cleanMultiselectString(initialOptOut) ? initialOptOut : undefined + + return initialOption?.text || optOutOption || option.key + } + const multiselectQuestionSection = ( applicationSection: ApplicationSection, appLink: string, @@ -141,7 +167,7 @@ const FormSummaryDetails = ({ testId={"app-summary-preference"} className={"pb-6 whitespace-pre-wrap"} > - {option.key} + {getOptionText(question, option)} )) )} diff --git a/sites/public/src/lib/helpers.tsx b/sites/public/src/lib/helpers.tsx index d2365920a4..c0fd8cb7c0 100644 --- a/sites/public/src/lib/helpers.tsx +++ b/sites/public/src/lib/helpers.tsx @@ -19,6 +19,7 @@ import { imageUrlFromListing, getSummariesTable, IMAGE_FALLBACK_URL, + cleanMultiselectString, } from "@bloom-housing/shared-helpers" export const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ @@ -184,7 +185,9 @@ export const untranslateMultiselectQuestion = ( data.forEach((datum) => { const question = multiselectQuestions.find( - (elem) => elem.multiselectQuestion.text === datum.key + (elem) => + elem.multiselectQuestion.text === datum.key || + elem.multiselectQuestion.untranslatedText === datum.key )?.multiselectQuestion if (question) { @@ -192,11 +195,14 @@ export const untranslateMultiselectQuestion = ( if (datum.options) { datum.options.forEach((option) => { - const selectedOption = question.options.find((elem) => elem.text === option.key) - + const selectedOption = question.options.find((elem) => { + return cleanMultiselectString(elem.text) === cleanMultiselectString(option.key) + }) if (selectedOption) { option.key = selectedOption.untranslatedText ?? selectedOption.text - } else if (question.optOutText === option.key) { + } else if ( + cleanMultiselectString(question?.optOutText) === cleanMultiselectString(option.key) + ) { option.key = question.untranslatedOptOutText ?? question.optOutText } @@ -226,6 +232,7 @@ export const downloadExternalPDF = async (fileURL: string, fileName: string) => const url = window.URL.createObjectURL(new Blob([blob])) const link = document.createElement("a") link.href = url + link.target = "_blank" link.setAttribute("download", `${fileName}.pdf`) document.body.appendChild(link)