Skip to content

Commit

Permalink
Merge 5e478e1 into 09577ff
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Apr 20, 2021
2 parents 09577ff + 5e478e1 commit 3d726ca
Show file tree
Hide file tree
Showing 31 changed files with 452 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ beforeEach(() => {
narrative = RacialDisparitiesNarrative.build({
tenantId: "US_ND",
content: contentFixture.racialDisparitiesNarrative,
categoryFilter: contentFixture.demographicCategories.raceOrEthnicity,
});

render(<RaceOrEthnicityFilterSelect narrative={narrative} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,9 @@ import { action } from "mobx";
import { observer } from "mobx-react-lite";
import React from "react";
import RacialDisparitiesNarrative from "../contentModels/RacialDisparitiesNarrative";
import {
getDemographicCategories,
getDemographicViewLabel,
RaceIdentifier,
} from "../demographics";
import { getDemographicViewLabel, RaceIdentifier } from "../demographics";
import { Dropdown } from "../UiLibrary";

const DROPDOWN_OPTIONS = getDemographicCategories(
"raceOrEthnicity"
).map(({ identifier, label }) => ({ label, id: identifier }));

type RaceOrEthnicityFilterSelectProps = {
narrative: RacialDisparitiesNarrative;
};
Expand All @@ -46,11 +38,15 @@ const RaceOrEthnicityFilterSelect: React.FC<RaceOrEthnicityFilterSelectProps> =
}
);

const dropdownOptions = narrative.allCategories.map(
({ identifier, label }) => ({ label, id: identifier })
);

return (
<Dropdown
label={getDemographicViewLabel("raceOrEthnicity")}
onChange={onChange}
options={DROPDOWN_OPTIONS}
options={dropdownOptions}
selectedId={narrative.selectedCategory}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { runInAction, when } from "mobx";
import React from "react";
import SentenceTypeByLocationMetric from "../contentModels/SentenceTypeByLocationMetric";
import DataStore from "../DataStore";
import { getDemographicCategories } from "../demographics";
import { reactImmediately, renderWithStore } from "../testUtils";
import VizSentenceTypeByLocation from "./VizSentenceTypeByLocation";

Expand Down Expand Up @@ -107,10 +106,10 @@ test("total chart", async () => {
});

test.each([
["Race or Ethnicity", getDemographicCategories("raceOrEthnicity")],
["Gender", getDemographicCategories("gender")],
["Age Group", getDemographicCategories("ageBucket")],
])("%s charts", async (demographicLabel, categories) => {
["Race or Ethnicity", "raceOrEthnicity"],
["Gender", "gender"],
["Age Group", "ageBucket"],
])("%s charts", async (demographicLabel, demographicView) => {
renderWithStore(<VizSentenceTypeByLocation metric={metric} />);

await when(() => !metric.isLoading);
Expand All @@ -122,7 +121,10 @@ test.each([
fireEvent.click(screen.getByRole("option", { name: demographicLabel }));

verifySankey(
categories.map(({ label }) => label),
metric
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.getDemographicCategories(demographicView as any)
.map(({ label }) => label),
["6,193", "3,399", "2,056"]
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import { runInAction } from "mobx";
import React from "react";
import SupervisionSuccessRateMetric from "../contentModels/SupervisionSuccessRateMetric";
import DataStore from "../DataStore";
import {
DemographicView,
getDemographicCategories,
getDemographicViewLabel,
} from "../demographics";
import { DemographicView, getDemographicViewLabel } from "../demographics";
import { reactImmediately, renderWithStore } from "../testUtils";
import VizSupervisionSuccessRate from "./VizSupervisionSuccessRate";

Expand Down Expand Up @@ -142,7 +138,7 @@ test("demographic filter", async () => {
})
);

getDemographicCategories(demographicView).forEach(({ label }) => {
metric.getDemographicCategories(demographicView).forEach(({ label }) => {
const stat = screen.getByRole("figure", { name: label });
expect(within(stat).getByText(/\d+%/)).toBeInTheDocument();
});
Expand Down
9 changes: 9 additions & 0 deletions spotlight-client/src/contentApi/sources/us_nd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ const content: TenantContent = {
'<a href="https://www.docr.nd.gov">The North Dakota Department of Corrections and Rehabilitation (DOCR)</a> provides correctional services for the state of North Dakota. Our mission is to transform lives, influence change, and strengthen community. Transparency is a critical element of our mission; sharing information builds greater accountability between the DOCR and the communities we serve.',
coBrandingCopy:
'Produced in collaboration with <a href="https://www.docr.nd.gov">the North Dakota Department of Corrections and Rehabilitation</a>.',
demographicCategories: {
raceOrEthnicity: [
"AMERICAN_INDIAN_ALASKAN_NATIVE",
"BLACK",
"HISPANIC",
"WHITE",
"OTHER",
],
},
metrics: {
SentencePopulationCurrent: {
name: "Sentenced Population",
Expand Down
10 changes: 10 additions & 0 deletions spotlight-client/src/contentApi/sources/us_pa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ const content: TenantContent = {
description: "",
coBrandingCopy:
'Produced in collaboration with <a href="https://www.cor.pa.gov">the Pennsylvania Department of Corrections</a>.',
demographicCategories: {
raceOrEthnicity: [
"BLACK",
"HISPANIC",
"WHITE",
"ASIAN",
"AMERICAN_INDIAN_ALASKAN_NATIVE",
"OTHER",
],
},
systemNarratives: {
Prison: {
title: "Prison",
Expand Down
10 changes: 10 additions & 0 deletions spotlight-client/src/contentApi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// =============================================================================

import type { Topology } from "topojson-specification";
import { AgeValue, GenderValue, RaceOrEthnicityValue } from "../demographics";

export type LocalityLabels = {
label: string;
Expand All @@ -31,6 +32,12 @@ export function isTenantId(x: string): x is TenantId {
return TenantIdList.includes(x as TenantId);
}

export type DemographicCategoryFilter = {
raceOrEthnicity?: RaceOrEthnicityValue[];
gender?: GenderValue[];
ageBucket?: AgeValue[];
};

export type TenantContent = {
name: string;
description: string;
Expand All @@ -57,6 +64,9 @@ export type TenantContent = {
ProgramRegions: MapData;
};
racialDisparitiesNarrative?: RacialDisparitiesNarrativeContent;
// if categories are enumerated for any of the keys here, they will be the only ones used;
// otherwise categories default to including all values in the associated unions
demographicCategories?: DemographicCategoryFilter;
};

// ============================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function getTestMetric() {
localityLabelMapping: contentFixture.localities,
metadataMapping: testMetadataMapping,
tenantId: testTenantId,
demographicFilter: contentFixture.demographicCategories,
}).get(testMetricId) as DemographicsByCategoryMetric;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@

import { sum } from "d3-array";
import { computed, makeObservable } from "mobx";
import {
getDemographicCategories,
recordIsTotalByDimension,
} from "../demographics";
import { recordIsTotalByDimension } from "../demographics";
import { DemographicsByCategoryRecord } from "../metricsApi";
import { colors } from "../UiLibrary";
import calculatePct from "./calculatePct";
Expand Down Expand Up @@ -55,7 +52,7 @@ export default class DemographicsByCategoryMetric extends Metric<DemographicsByC
}

get dataSeries(): DemographicCategoryRecords[] | null {
const { color, demographicView, records } = this;
const { color, demographicView, records, getDemographicCategories } = this;
if (!records || demographicView === "nofilter") return null;

const categories = getDemographicCategories(demographicView);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
import { isEqual } from "date-fns";
import { advanceTo, clear } from "jest-date-mock";
import { runInAction, when } from "mobx";
import { DemographicViewList, getDemographicCategories } from "../demographics";
import {
createDemographicCategories,
DemographicViewList,
getDemographicCategoriesForView,
} from "../demographics";
import {
fetchAndTransformMetric,
HistoricalPopulationBreakdownRecord,
Expand Down Expand Up @@ -104,7 +108,10 @@ test("fills in missing data", async () => {
reactImmediately(() => {
const data = metric.dataSeries;
if (data) {
const categories = getDemographicCategories(demographicView);
const categories = getDemographicCategoriesForView(
demographicView,
createDemographicCategories()
);
categories.forEach(({ identifier }, index) => {
const series = data[index].coordinates;
expect(series.length).toBe(240);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { DataSeries } from "../charts";
import {
DemographicViewList,
recordIsTotalByDimension,
getDemographicCategories,
RaceIdentifier,
GenderIdentifier,
AgeIdentifier,
Expand Down Expand Up @@ -115,7 +114,7 @@ export default class HistoricalPopulationBreakdownMetric extends Metric<Historic
recordIsTotalByDimension(demographicView)
);

const categories = getDemographicCategories(demographicView);
const categories = this.getDemographicCategories(demographicView);
categories.forEach(({ identifier }) => {
let recordsForCategory;
if (demographicView !== "total") {
Expand Down Expand Up @@ -166,7 +165,7 @@ export default class HistoricalPopulationBreakdownMetric extends Metric<Historic
}

get dataSeries(): DataSeries<HistoricalPopulationBreakdownRecord>[] | null {
const { records, demographicView } = this;
const { records, demographicView, getDemographicCategories } = this;
if (!records || demographicView === "nofilter") return null;

const categories = getDemographicCategories(demographicView);
Expand Down
36 changes: 36 additions & 0 deletions spotlight-client/src/contentModels/Metric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,40 @@ describe("data download", () => {
}
});
});

describe("demographic categories", () => {
test("default", () => {
testMetricMapping = getTestMapping();

// arbitrary choice, they should all be the same
const metric = getTestMetric("PrisonPopulationCurrent");

expect(
metric.getDemographicCategories("raceOrEthnicity")
).toMatchSnapshot();
});

test("customized", () => {
createMetricMapping({
localityLabelMapping: allTestContent.localities,
metadataMapping: testMetadataMapping,
topologyMapping: allTestContent.topologies,
demographicFilter: allTestContent.demographicCategories,
tenantId: testTenantId,
});

const metric = createMetricMapping({
localityLabelMapping: allTestContent.localities,
metadataMapping: testMetadataMapping,
topologyMapping: allTestContent.topologies,
demographicFilter: allTestContent.demographicCategories,
tenantId: testTenantId,
// arbitrary choice, they should all be the same
}).get("PrisonPopulationCurrent");

expect(
metric?.getDemographicCategories("raceOrEthnicity")
).toMatchSnapshot();
});
});
});
26 changes: 24 additions & 2 deletions spotlight-client/src/contentModels/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,18 @@ import {
runInAction,
when,
} from "mobx";
import { LocalityLabels, MetricTypeId, TenantId } from "../contentApi/types";
import { DemographicView } from "../demographics";
import {
DemographicCategoryFilter,
LocalityLabels,
MetricTypeId,
TenantId,
} from "../contentApi/types";
import {
createDemographicCategories,
DemographicCategories,
DemographicView,
getDemographicCategoriesForView,
} from "../demographics";
import {
RawMetricData,
DemographicFields,
Expand All @@ -42,6 +52,7 @@ export type BaseMetricConstructorOptions<RecordFormat extends MetricRecord> = {
tenantId: TenantId;
sourceFileName: string;
dataTransformer: (d: RawMetricData) => RecordFormat[];
demographicFilter?: DemographicCategoryFilter;
defaultDemographicView: RecordFormat extends DemographicFields
? DemographicView
: // special case: this metric supports demographics for an alternative record format
Expand Down Expand Up @@ -89,6 +100,8 @@ export default abstract class Metric<RecordFormat extends MetricRecord>
error?: Error;

// filter properties
private readonly demographicCategories: DemographicCategories;

localityId: RecordFormat extends LocalityFields ? string : undefined;

localityLabels: RecordFormat extends LocalityFields
Expand All @@ -109,6 +122,7 @@ export default abstract class Metric<RecordFormat extends MetricRecord>
tenantId,
sourceFileName,
dataTransformer,
demographicFilter,
defaultDemographicView,
defaultLocalityId,
localityLabels,
Expand All @@ -134,6 +148,8 @@ export default abstract class Metric<RecordFormat extends MetricRecord>
this.dataTransformer = dataTransformer;

// initialize filters
this.demographicCategories = createDemographicCategories(demographicFilter);
this.getDemographicCategories = this.getDemographicCategories.bind(this);
this.localityId = defaultLocalityId;
this.localityLabels = localityLabels;
this.demographicView = defaultDemographicView;
Expand Down Expand Up @@ -195,4 +211,10 @@ export default abstract class Metric<RecordFormat extends MetricRecord>
})
);
}

getDemographicCategories(
view: Exclude<DemographicView, "nofilter">
): ReturnType<typeof getDemographicCategoriesForView> {
return getDemographicCategoriesForView(view, this.demographicCategories);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function getTestMetric() {
localityLabelMapping: contentFixture.localities,
metadataMapping: testMetadataMapping,
tenantId: testTenantId,
demographicFilter: contentFixture.demographicCategories,
}).get(testMetricId) as PopulationBreakdownByLocationMetric;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { computed, makeObservable } from "mobx";
import {
DemographicView,
DemographicViewList,
getDemographicCategories,
getDemographicViewLabel,
recordIsTotalByDimension,
} from "../demographics";
Expand Down Expand Up @@ -65,7 +64,7 @@ export default class PopulationBreakdownByLocationMetric extends Metric<Populati
}

get dataSeries(): DemographicCategoryRecords[] | null {
const { records } = this;
const { records, getDemographicCategories } = this;
if (!records) return null;

return DemographicViewList.filter(
Expand Down
Loading

0 comments on commit 3d726ca

Please sign in to comment.