Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Categorical breakdown sections #332

Merged
merged 18 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions spotlight-client/@types/d3-force-limit.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

declare module "d3-force-limit";
3 changes: 3 additions & 0 deletions spotlight-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@types/classnames": "^2.2.11",
"@types/d3-array": "^2.8.0",
"@types/d3-color": "^2.0.1",
"@types/d3-force": "^2.1.0",
"@types/d3-format": "^2.0.0",
"@types/d3-interpolate": "^2.0.0",
"@types/d3-scale": "^3.2.2",
Expand All @@ -37,6 +38,8 @@
"classnames": "^2.2.6",
"d3-array": "^2.9.1",
"d3-color": "^2.0.0",
"d3-force": "^2.1.1",
"d3-force-limit": "^1.1.3",
"d3-format": "^2.0.0",
"d3-interpolate": "^2.0.1",
"d3-scale": "^3.2.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import { action } from "mobx";
import { observer } from "mobx-react-lite";
import React from "react";
import HistoricalPopulationBreakdownMetric from "../contentModels/HistoricalPopulationBreakdownMetric";
import type DemographicsByCategoryMetric from "../contentModels/DemographicsByCategoryMetric";
import type HistoricalPopulationBreakdownMetric from "../contentModels/HistoricalPopulationBreakdownMetric";
import {
DemographicView,
DemographicViewList,
Expand All @@ -30,7 +31,7 @@ import { Dropdown } from "../UiLibrary";
type DemographicFilterOption = { id: DemographicView; label: string };

type DemographicFilterSelectProps = {
metric: HistoricalPopulationBreakdownMetric;
metric: HistoricalPopulationBreakdownMetric | DemographicsByCategoryMetric;
};

const DemographicFilterSelect: React.FC<DemographicFilterSelectProps> = ({
Expand Down
2 changes: 1 addition & 1 deletion spotlight-client/src/MeasureWidth/MeasureWidth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import React from "react";
import Measure from "react-measure";

type MeasureWidthProps = {
export type MeasureWidthProps = {
children: (props: {
measureRef: (ref: Element | null) => void;
width: number;
Expand Down
27 changes: 27 additions & 0 deletions spotlight-client/src/MeasureWidth/__mocks__/MeasureWidth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import React from "react";
import { MeasureWidthProps } from "../MeasureWidth";

const MockMeasureWidth = ({
children,
}: MeasureWidthProps): React.ReactElement => {
return children({ measureRef: () => undefined, width: 600 });
};

export default MockMeasureWidth;
8 changes: 8 additions & 0 deletions spotlight-client/src/MetricVizMapper/MetricVizMapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import VizHistoricalPopulationBreakdown from "../VizHistoricalPopulationBreakdow
import { MetricRecord } from "../contentModels/types";
import PopulationBreakdownByLocationMetric from "../contentModels/PopulationBreakdownByLocationMetric";
import VizPopulationBreakdownByLocation from "../VizPopulationBreakdownByLocation";
import DemographicsByCategoryMetric from "../contentModels/DemographicsByCategoryMetric";
import VizDemographicsByCategory from "../VizDemographicsByCategory";

type MetricVizMapperProps = {
metric: Metric<MetricRecord>;
Expand All @@ -34,6 +36,12 @@ const MetricVizMapper: React.FC<MetricVizMapperProps> = ({ metric }) => {
if (metric instanceof PopulationBreakdownByLocationMetric) {
return <VizPopulationBreakdownByLocation metric={metric} />;
}
if (
metric instanceof DemographicsByCategoryMetric &&
metric.id !== "PrisonStayLengthAggregate"
) {
return <VizDemographicsByCategory metric={metric} />;
}
return <h3>Placeholder for {metric.name}</h3>;
};

Expand Down
33 changes: 33 additions & 0 deletions spotlight-client/src/NoMetricData/NoMetricData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import React from "react";
import Metric from "../contentModels/Metric";
import { MetricRecord } from "../contentModels/types";
import Loading from "../Loading";

type NoMetricDataProps = {
metric: Metric<MetricRecord>;
};

const NoMetricData: React.FC<NoMetricDataProps> = ({ metric }) => {
if (metric.error) throw metric.error;

return <Loading />;
};

export default NoMetricData;
18 changes: 18 additions & 0 deletions spotlight-client/src/NoMetricData/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

export { default } from "./NoMetricData";
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import { fireEvent, screen, waitFor, within } from "@testing-library/react";
import { runInAction, when } from "mobx";
import React from "react";
import DemographicsByCategoryMetric from "../contentModels/DemographicsByCategoryMetric";
import DataStore from "../DataStore";
import { reactImmediately, renderWithStore } from "../testUtils";
import VizDemographicsByCategory from "./VizDemographicsByCategory";

jest.mock("../MeasureWidth/MeasureWidth");

let metric: DemographicsByCategoryMetric;

beforeEach(() => {
runInAction(() => {
DataStore.tenantStore.currentTenantId = "US_ND";
});
reactImmediately(() => {
const metricToTest = DataStore.tenant?.metrics.get(
"ProbationRevocationsAggregate"
);
// it will be
if (metricToTest instanceof DemographicsByCategoryMetric) {
metric = metricToTest;
}
});
});

afterEach(() => {
runInAction(() => {
DataStore.tenantStore.currentTenantId = undefined;
});
});

test("loading", () => {
renderWithStore(<VizDemographicsByCategory metric={metric} />);
expect(screen.getByText(/loading/i)).toBeVisible();
});

test("total chart", async () => {
renderWithStore(<VizDemographicsByCategory metric={metric} />);

await when(() => !metric.isLoading);

const bubbles = screen.getByRole("group", { name: "nodes" });
expect(bubbles).toBeVisible();
expect(within(bubbles).getAllByRole("img", { name: /Node/ }).length).toBe(4);
});

test("demographic charts", async () => {
renderWithStore(<VizDemographicsByCategory metric={metric} />);

await when(() => !metric.isLoading);

const menuButton = screen.getByRole("button", {
name: "View Total",
});
fireEvent.click(menuButton);
fireEvent.click(screen.getByRole("option", { name: "Race or Ethnicity" }));

// pause for animated transition
await waitFor(() => {
expect(
screen.getByRole("figure", { name: "Native American" })
).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "Black" })).toBeInTheDocument();
expect(
screen.getByRole("figure", { name: "Hispanic" })
).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "White" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "Other" })).toBeInTheDocument();

expect(
screen.getAllByRole("group", { name: "4 bars in a bar chart" }).length
).toBe(5);
});

fireEvent.click(menuButton);
fireEvent.click(screen.getByRole("option", { name: "Gender" }));

// pause for animated transition
await waitFor(() => {
expect(screen.getByRole("figure", { name: "Male" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "Female" })).toBeInTheDocument();

expect(
screen.getAllByRole("group", { name: "4 bars in a bar chart" }).length
).toBe(2);
});

fireEvent.click(menuButton);
fireEvent.click(screen.getByRole("option", { name: "Age Group" }));

// pause for animated transition
await waitFor(() => {
expect(screen.getByRole("figure", { name: "<25" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "25-29" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "30-34" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "35-39" })).toBeInTheDocument();
expect(screen.getByRole("figure", { name: "40<" })).toBeInTheDocument();

expect(
screen.getAllByRole("group", { name: "4 bars in a bar chart" }).length
).toBe(5);
});
});
Loading