Skip to content

Commit

Permalink
[Publisher] Add "Other" textbox to race breakdown (#1557)
Browse files Browse the repository at this point in the history
* Add Other textbox to race breakdown

* Update other textbox logic

* Handle Other race checking behavior

* Use contexts for other description

* Use disaggregations contexts for other description

* Fixes

* Fix undefined BE contexts case
  • Loading branch information
nasaownsky authored Jan 16, 2025
1 parent a61100e commit fa7313a
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
7 changes: 7 additions & 0 deletions common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -53,6 +57,7 @@ function RaceEthnicitiesModalForm({
const [settingsSearchParams] = useSettingsSearchParams();
const { metricConfigStore } = useStore();
const {
disaggregations,
getEthnicitiesByRace,
updateRacesDimensions,
updateAllRaceEthnicitiesToDefaultState,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -160,7 +204,8 @@ function RaceEthnicitiesModalForm({
currentState,
raceEthnicityGridStates,
systemSearchParam,
metricSearchParam
metricSearchParam,
otherDescription
);
saveMetricSettings(updatedDimensions, agencyId);
};
Expand Down Expand Up @@ -210,10 +255,24 @@ function RaceEthnicitiesModalForm({
<CheckboxOptions
options={raceEthnicityOptions}
onChange={({ key, checked }) => {
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,
});
}
}}
/>
<Tooltip
Expand Down
6 changes: 6 additions & 0 deletions publisher/src/components/MetricsConfiguration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
MetricConfigurationSettings,
MetricConfigurationSettingsOptions,
MetricContext,
MetricDimensionContext,
MetricDisaggregationContext,
ReportFrequency,
} from "@justice-counts/common/types";

Expand All @@ -37,6 +39,7 @@ export type MetricSettings = {
disaggregations?: {
key: string;
enabled?: boolean;
contexts?: MetricDisaggregationContext[];
dimensions?: {
key: string;
enabled?: boolean;
Expand Down Expand Up @@ -81,6 +84,7 @@ export type Dimensions = {
enabled?: boolean | null;
label?: string;
description?: string;
contexts?: MetricDimensionContext[];
key?: string;
race?: Races;
ethnicity?: Ethnicities;
Expand All @@ -102,12 +106,14 @@ export type UpdatedDimension = {
enabled: boolean;
race: Races;
ethnicity: Ethnicities;
contexts?: MetricDimensionContext[];
};

export type UpdatedDisaggregation = {
key: string;
disaggregations: {
key: string;
contexts?: MetricDisaggregationContext[];
dimensions: UpdatedDimension[];
}[];
};
Expand Down
26 changes: 24 additions & 2 deletions publisher/src/stores/MetricConfigStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
MetricConfigurationSettings,
MetricConfigurationSettingsOptions,
MetricContext,
MetricDimensionContext,
MetricDisaggregationContext,
MetricDisaggregationDimensions,
MetricDisaggregations,
} from "@justice-counts/common/types";
Expand Down Expand Up @@ -88,6 +90,7 @@ class MetricConfigStore {
enabled?: boolean;
display_name?: string;
is_breakdown_configured?: ConfigurationStatus | null;
contexts?: MetricDisaggregationContext[];
};
};
};
Expand All @@ -102,6 +105,7 @@ class MetricConfigStore {
key?: string;
race?: Races;
ethnicity?: Ethnicities;
contexts?: MetricDimensionContext[];
is_dimension_includes_excludes_configured?: ConfigurationStatus | null;
};
};
Expand Down Expand Up @@ -277,6 +281,7 @@ class MetricConfigStore {
display_name: disaggregation.display_name,
enabled: disaggregation.enabled,
is_breakdown_configured: disaggregation.is_breakdown_configured,
contexts: disaggregation.contexts,
}
);

Expand Down Expand Up @@ -408,7 +413,7 @@ class MetricConfigStore {
disaggregationKey: string,
disaggregationData: Pick<
MetricDisaggregations,
"display_name" | "enabled" | "is_breakdown_configured"
"display_name" | "enabled" | "is_breakdown_configured" | "contexts"
>
) => {
const systemMetricKey = MetricConfigStore.getSystemMetricKey(
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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,
},
],
Expand Down

0 comments on commit fa7313a

Please sign in to comment.