diff --git a/common/types.ts b/common/types.ts index a0c5c2329..74051c1b3 100644 --- a/common/types.ts +++ b/common/types.ts @@ -248,9 +248,16 @@ export interface MetricDisaggregations { helper_text: string | null | undefined; enabled?: boolean; should_sum_to_total: boolean; + contexts?: MetricDisaggregationContext[]; is_breakdown_configured: ConfigurationStatus | null; } +export type MetricDisaggregationContext = { + key?: string; + value?: string; + display_name?: string; +}; + export type MetricDimensionContext = { key?: string; value?: string; diff --git a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx index 6b6c4eb9a..0a12cc24a 100644 --- a/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx +++ b/publisher/src/components/MetricsConfiguration/RaceEthnicitiesModalForm.tsx @@ -27,11 +27,15 @@ 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"; -import { useSettingsSearchParams } from "../AgencySettings"; +import { + getActiveSystemMetricKey, + useSettingsSearchParams, +} from "../AgencySettings"; +import { RACE_ETHNICITY_DISAGGREGATION_KEY } from "./constants"; import * as Styled from "./ModalForm.styled"; import { Ethnicity, @@ -53,6 +57,7 @@ function RaceEthnicitiesModalForm({ const [settingsSearchParams] = useSettingsSearchParams(); const { metricConfigStore } = useStore(); const { + disaggregations, getEthnicitiesByRace, updateRacesDimensions, updateAllRaceEthnicitiesToDefaultState, @@ -118,12 +123,51 @@ function RaceEthnicitiesModalForm({ } : undefined; + const systemMetricKey = getActiveSystemMetricKey(settingsSearchParams); + + const currentOtherDescription = + disaggregations[systemMetricKey]?.[ + RACE_ETHNICITY_DISAGGREGATION_KEY + ].contexts?.find((context) => context.key === "OTHER_RACE_DESCRIPTION") + ?.value || ""; + + const [otherDescription, setOtherDescription] = useState( + currentOtherDescription + ); + const [isOtherChecked, setOtherChecked] = useState( + Boolean(currentOtherDescription) + ); + + useEffect(() => { + setRacesStatusObject((prev) => ({ + ...prev, + Other: Boolean(otherDescription), + })); + }, [otherDescription]); + const raceEthnicityOptions: CheckboxOption[] = [ ...(hispanicOrLatinoOption ? [hispanicOrLatinoOption] : []), ...Object.entries(racesStatusObject).map(([race, enabled]) => { const disabledUnknownRace = race === "Unknown" && specifiesHispanicAsRace && !canSpecifyEthnicity; + if (race === "Other") { + const otherDescriptionParams = { + isEnabled: race === "Other" && isOtherChecked, + placeholder: + "Please describe additional definition/clarification of the 'Other' selection.", + value: otherDescription, + onChange: (value: string) => setOtherDescription(value), + }; + + return { + key: race, + label: race, + checked: Boolean(otherDescription), + otherDescription: otherDescriptionParams, + }; + } + return { key: race, label: race, @@ -160,7 +204,8 @@ function RaceEthnicitiesModalForm({ currentState, raceEthnicityGridStates, systemSearchParam, - metricSearchParam + metricSearchParam, + otherDescription ); saveMetricSettings(updatedDimensions, agencyId); }; @@ -210,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, + }); + } }} /> ) => { const systemMetricKey = MetricConfigStore.getSystemMetricKey( @@ -1157,7 +1162,8 @@ class MetricConfigStore { state: StateKeys, gridStates: RaceEthnicitiesGridStates, system: AgencySystem, - metricKey: string + metricKey: string, + otherDescription?: string ): UpdatedDisaggregation => { const ethnicitiesByRace = this.getEthnicitiesByRace(system, metricKey); @@ -1200,12 +1206,28 @@ class MetricConfigStore { raceEthnicitiesDimensions && (Object.values(raceEthnicitiesDimensions) as UpdatedDimension[]); + const defaultOtherDescriptionContext: MetricDisaggregationContext = { + key: "OTHER_RACE_DESCRIPTION", + value: "", + }; + + const otherDescriptionContext = + this.disaggregations[systemMetricKey]?.[ + RACE_ETHNICITY_DISAGGREGATION_KEY + ].contexts?.find((context) => context.key === "OTHER_RACE_DESCRIPTION") || + defaultOtherDescriptionContext; + + if (otherDescription !== undefined) { + otherDescriptionContext.value = otherDescription; + } + /** Return an object w/ all dimensions in the desired backend data structure for saving purposes */ return { key: metricKey, disaggregations: [ { key: RACE_ETHNICITY_DISAGGREGATION_KEY, + contexts: [otherDescriptionContext], dimensions, }, ],