From 8387bba672689a9a8bac719abe67a4a9cccc57b5 Mon Sep 17 00:00:00 2001 From: nasaownsky Date: Thu, 17 Oct 2024 13:19:47 +0400 Subject: [PATCH 1/3] Add Other textbox to race breakdown --- .../MetricsConfiguration/ModalForm.styled.tsx | 8 +++++++ .../RaceEthnicitiesModalForm.tsx | 23 ++++++++++++++++++- .../components/MetricsConfiguration/types.ts | 2 ++ publisher/src/stores/MetricConfigStore.tsx | 12 +++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx index 68581ddab..b51a11461 100644 --- a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx @@ -170,3 +170,11 @@ export const ContextLabel = styled.div` color: ${palette.highlight.grey8}; margin-bottom: 16px; `; + +export const InputWrapper = styled.div` + margin-bottom: 16px; + + textarea { + height: 100px; + } +`; diff --git a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx index 6b6c4eb9a..357513fb0 100644 --- a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx +++ b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx @@ -21,6 +21,7 @@ import { CheckboxOption, CheckboxOptions, } from "@justice-counts/common/components/CheckboxOptions"; +import { NewInput } from "@justice-counts/common/components/Input"; import { RadioButton, RadioButtonsWrapper, @@ -144,6 +145,9 @@ function RaceEthnicitiesModalForm({ return "NO_ETHNICITY_HISPANIC_NOT_SPECIFIED"; }; + const [isOtherChecked, setOtherChecked] = useState(false); + const [otherDescription, setOtherDescription] = useState(""); + const handleUpdateRacesDimensions = () => { if (!systemSearchParam || !metricSearchParam) return; const currentState = determineCurrentState(); @@ -160,7 +164,8 @@ function RaceEthnicitiesModalForm({ currentState, raceEthnicityGridStates, systemSearchParam, - metricSearchParam + metricSearchParam, + otherDescription ); saveMetricSettings(updatedDimensions, agencyId); }; @@ -207,9 +212,25 @@ function RaceEthnicitiesModalForm({ capture for race? + {isOtherChecked && ( + + setOtherDescription(e.target.value)} + fullWidth + /> + + )} { + if (key === "Other") { + setOtherChecked(!checked); + } setRacesStatusObject({ ...racesStatusObject, [key]: !checked, diff --git a/publisher/src/components/MetricsConfiguration/types.ts b/publisher/src/components/MetricsConfiguration/types.ts index 7d53f2664..3df6df562 100644 --- a/publisher/src/components/MetricsConfiguration/types.ts +++ b/publisher/src/components/MetricsConfiguration/types.ts @@ -80,6 +80,7 @@ export type Dimensions = { enabled?: boolean | null; label?: string; description?: string; + other_description?: string; key?: string; race?: Races; ethnicity?: Ethnicities; @@ -101,6 +102,7 @@ export type UpdatedDimension = { enabled: boolean; race: Races; ethnicity: Ethnicities; + other_description?: string; }; export type UpdatedDisaggregation = { diff --git a/publisher/src/stores/MetricConfigStore.tsx b/publisher/src/stores/MetricConfigStore.tsx index 0d6c89320..960c479c3 100644 --- a/publisher/src/stores/MetricConfigStore.tsx +++ b/publisher/src/stores/MetricConfigStore.tsx @@ -1149,7 +1149,8 @@ class MetricConfigStore { state: StateKeys, gridStates: RaceEthnicitiesGridStates, system: AgencySystem, - metricKey: string + metricKey: string, + otherDescription?: string ): UpdatedDisaggregation => { const ethnicitiesByRace = this.getEthnicitiesByRace(system, metricKey); @@ -1192,6 +1193,15 @@ class MetricConfigStore { raceEthnicitiesDimensions && (Object.values(raceEthnicitiesDimensions) as UpdatedDimension[]); + if (otherDescription) { + const otherDimension = dimensions.find( + (dimension) => dimension.race === "Other" && dimension.enabled + ); + if (otherDimension) { + otherDimension.other_description = otherDescription; + } + } + /** Return an object w/ all dimensions in the desired backend data structure for saving purposes */ return { key: metricKey, From 15664f4a5694735b42fa852b1d63c10fca0738fb Mon Sep 17 00:00:00 2001 From: nasaownsky Date: Tue, 10 Dec 2024 18:20:04 +0400 Subject: [PATCH 2/3] Update other textbox logic --- .../MetricsConfiguration/ModalForm.styled.tsx | 8 ---- .../RaceEthnicitiesModalForm.tsx | 48 +++++++++++-------- publisher/src/stores/MetricConfigStore.tsx | 16 ++++--- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx index b51a11461..68581ddab 100644 --- a/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx +++ b/publisher/src/components/MetricsConfiguration/ModalForm.styled.tsx @@ -170,11 +170,3 @@ export const ContextLabel = styled.div` color: ${palette.highlight.grey8}; margin-bottom: 16px; `; - -export const InputWrapper = styled.div` - margin-bottom: 16px; - - textarea { - height: 100px; - } -`; diff --git a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx index 357513fb0..143952101 100644 --- a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx +++ b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx @@ -21,7 +21,6 @@ import { CheckboxOption, CheckboxOptions, } from "@justice-counts/common/components/CheckboxOptions"; -import { NewInput } from "@justice-counts/common/components/Input"; import { RadioButton, RadioButtonsWrapper, @@ -119,18 +118,46 @@ function RaceEthnicitiesModalForm({ } : undefined; + const currentOtherDescription = + Object.values( + Object.entries(ethnicitiesByRace).find( + ([race]) => race === "Other" + )?.[1] || {} + ).find((ethnicity) => { + if (!canSpecifyEthnicity && !specifiesHispanicAsRace) + return ethnicity.key === "Other / Unknown Ethnicity"; + + if (!canSpecifyEthnicity && specifiesHispanicAsRace) + return ethnicity.key === "Other / Not Hispanic or Latino"; + + return ethnicity.key === "Other / Hispanic or Latino"; + })?.other_description || ""; + + const [otherDescription, setOtherDescription] = useState( + currentOtherDescription + ); + const raceEthnicityOptions: CheckboxOption[] = [ ...(hispanicOrLatinoOption ? [hispanicOrLatinoOption] : []), ...Object.entries(racesStatusObject).map(([race, enabled]) => { const disabledUnknownRace = race === "Unknown" && specifiesHispanicAsRace && !canSpecifyEthnicity; + const otherDescriptionParams = { + isEnabled: race === "Other" && Boolean(enabled), + placeholder: + "Please describe additional definition/clarification of the 'Other' selection.", + value: currentOtherDescription, + onChange: (value: string) => setOtherDescription(value), + }; + return { key: race, label: race, checked: Boolean(enabled), disabled: disabledUnknownRace, icon: disabledUnknownRace ? : undefined, + otherDescription: otherDescriptionParams, }; }), ]; @@ -145,9 +172,6 @@ function RaceEthnicitiesModalForm({ return "NO_ETHNICITY_HISPANIC_NOT_SPECIFIED"; }; - const [isOtherChecked, setOtherChecked] = useState(false); - const [otherDescription, setOtherDescription] = useState(""); - const handleUpdateRacesDimensions = () => { if (!systemSearchParam || !metricSearchParam) return; const currentState = determineCurrentState(); @@ -212,25 +236,9 @@ function RaceEthnicitiesModalForm({ capture for race? - {isOtherChecked && ( - - setOtherDescription(e.target.value)} - fullWidth - /> - - )} { - if (key === "Other") { - setOtherChecked(!checked); - } setRacesStatusObject({ ...racesStatusObject, [key]: !checked, diff --git a/publisher/src/stores/MetricConfigStore.tsx b/publisher/src/stores/MetricConfigStore.tsx index 960c479c3..de5f93569 100644 --- a/publisher/src/stores/MetricConfigStore.tsx +++ b/publisher/src/stores/MetricConfigStore.tsx @@ -1193,13 +1193,15 @@ class MetricConfigStore { raceEthnicitiesDimensions && (Object.values(raceEthnicitiesDimensions) as UpdatedDimension[]); - if (otherDescription) { - const otherDimension = dimensions.find( - (dimension) => dimension.race === "Other" && dimension.enabled - ); - if (otherDimension) { - otherDimension.other_description = otherDescription; - } + if (otherDescription !== undefined) { + dimensions + .filter((dimension) => dimension.race === "Other") + .forEach((otherDimension) => { + const updatedDimension = otherDimension; + updatedDimension.other_description = otherDimension.enabled + ? otherDescription + : ""; + }); } /** Return an object w/ all dimensions in the desired backend data structure for saving purposes */ From 915cadf0b83c4776d4a2804885a7ef3fbfe57e38 Mon Sep 17 00:00:00 2001 From: nasaownsky Date: Thu, 12 Dec 2024 17:18:59 +0400 Subject: [PATCH 3/3] Handle Other race checking behavior --- .../RaceEthnicitiesModalForm.tsx | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx index 143952101..c1ab76d75 100644 --- a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx +++ b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx @@ -27,7 +27,7 @@ import { } from "@justice-counts/common/components/RadioButton"; import { Tooltip } from "@justice-counts/common/components/Tooltip"; import { observer } from "mobx-react-lite"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { useStore } from "../../stores"; @@ -136,6 +136,14 @@ function RaceEthnicitiesModalForm({ const [otherDescription, setOtherDescription] = useState( currentOtherDescription ); + const [isOtherChecked, setOtherChecked] = useState(Boolean(otherDescription)); + + useEffect(() => { + setRacesStatusObject((prev) => ({ + ...prev, + Other: Boolean(otherDescription), + })); + }, [otherDescription]); const raceEthnicityOptions: CheckboxOption[] = [ ...(hispanicOrLatinoOption ? [hispanicOrLatinoOption] : []), @@ -143,13 +151,22 @@ function RaceEthnicitiesModalForm({ const disabledUnknownRace = race === "Unknown" && specifiesHispanicAsRace && !canSpecifyEthnicity; - const otherDescriptionParams = { - isEnabled: race === "Other" && Boolean(enabled), - placeholder: - "Please describe additional definition/clarification of the 'Other' selection.", - value: currentOtherDescription, - onChange: (value: string) => setOtherDescription(value), - }; + if (race === "Other") { + const otherDescriptionParams = { + isEnabled: race === "Other" && isOtherChecked, + placeholder: + "Please describe additional definition/clarification of the 'Other' selection.", + value: currentOtherDescription, + onChange: (value: string) => setOtherDescription(value), + }; + + return { + key: race, + label: race, + checked: Boolean(otherDescription), + otherDescription: otherDescriptionParams, + }; + } return { key: race, @@ -157,7 +174,6 @@ function RaceEthnicitiesModalForm({ checked: Boolean(enabled), disabled: disabledUnknownRace, icon: disabledUnknownRace ? : undefined, - otherDescription: otherDescriptionParams, }; }), ]; @@ -239,10 +255,24 @@ function RaceEthnicitiesModalForm({ { - setRacesStatusObject({ - ...racesStatusObject, - [key]: !checked, - }); + if (key === "Other") { + setOtherChecked(!checked); + + if (checked) { + // Clear otherDescription if "Other" is unchecked + setOtherDescription(""); + } + + setRacesStatusObject((prev) => ({ + ...prev, + Other: Boolean(otherDescription), + })); + } else { + setRacesStatusObject({ + ...racesStatusObject, + [key]: !checked, + }); + } }} />