Skip to content

Commit

Permalink
Download individual metric data (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Mar 2, 2021
1 parent 06fe86a commit 2eaa46a
Show file tree
Hide file tree
Showing 21 changed files with 525 additions and 471 deletions.
6 changes: 6 additions & 0 deletions spotlight-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
"@types/classnames": "^2.2.11",
"@types/d3-array": "^2.8.0",
"@types/d3-color": "^2.0.1",
"@types/d3-dsv": "^2.0.1",
"@types/d3-force": "^2.1.0",
"@types/d3-format": "^2.0.0",
"@types/d3-geo": "^2.0.0",
"@types/d3-interpolate": "^2.0.0",
"@types/d3-scale": "^3.2.2",
"@types/date-fns": "^2.6.0",
"@types/downloadjs": "^1.4.2",
"@types/lodash.isempty": "^4.4.6",
"@types/lodash.xor": "^4.5.6",
"@types/qs": "^6.9.5",
Expand All @@ -43,16 +45,19 @@
"classnames": "^2.2.6",
"d3-array": "^2.9.1",
"d3-color": "^2.0.0",
"d3-dsv": "^2.0.0",
"d3-force": "^2.1.1",
"d3-force-limit": "^1.1.3",
"d3-format": "^2.0.0",
"d3-geo": "^2.0.1",
"d3-interpolate": "^2.0.1",
"d3-scale": "^3.2.3",
"date-fns": "^2.16.1",
"downloadjs": "^1.4.7",
"downshift": "^6.1.0",
"html-react-parser": "^1.1.1",
"intersection-observer": "^0.12.0",
"jszip": "^3.6.0",
"lodash.isempty": "^4.4.0",
"lodash.xor": "^4.5.0",
"mobx": "^6.0.4",
Expand All @@ -75,6 +80,7 @@
"react-spring": "^8.0.27",
"react-stickyfill": "^0.2.5",
"semiotic": "^1.20.6",
"string-strip-html": "^8.2.2",
"styled-components": "^5.2.1",
"styled-reset": "^4.3.3",
"topojson": "^3.0.2",
Expand Down
63 changes: 0 additions & 63 deletions spotlight-client/src/FiltersWrapper/FiltersWrapper.tsx

This file was deleted.

98 changes: 98 additions & 0 deletions spotlight-client/src/MetricVizControls/MetricVizControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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 { rem } from "polished";
import React from "react";
import styled from "styled-components/macro";
import downloadPath from "../assets/cloud-download.svg";
import Metric from "../contentModels/Metric";
import { MetricRecord } from "../contentModels/types";
import { colors, zIndex } from "../UiLibrary";

const Wrapper = styled.div`
background: ${colors.background};
display: flex;
flex-wrap: wrap;
justify-content: space-between;
z-index: ${zIndex.control};
`;

const ControlsGroup = styled.div`
display: flex;
flex-wrap: wrap;
margin-bottom: ${rem(16)};
padding-bottom: ${rem(16)};
`;

const FilterWrapper = styled.div`
margin: 0 ${rem(16)};
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
`;

const DownloadButton = styled.button`
background: none;
border: none;
color: ${colors.link};
cursor: pointer;
font-size: ${rem(14)};
font-weight: 500;
padding: none;
`;

const DownloadIcon = styled.img.attrs({ src: downloadPath })`
height: ${rem(16)};
/* icon needs minor adjustment to align with text */
margin-bottom: ${rem(-2)};
width: ${rem(16)};
`;

type MetricVizControlsProps = {
filters: React.ReactElement[];
metric: Metric<MetricRecord>;
};

const MetricVizControls = ({
filters,
metric,
}: MetricVizControlsProps): React.ReactElement => {
return (
<Wrapper>
<ControlsGroup>
{filters.map((filter, index) => (
// there's nothing else to use as a key, but these should be pretty static
// so there isn't any real performance concern
// eslint-disable-next-line react/no-array-index-key
<FilterWrapper key={index}>{filter}</FilterWrapper>
))}
</ControlsGroup>
<ControlsGroup>
<DownloadButton onClick={() => metric.download()}>
<DownloadIcon /> Download Data
</DownloadButton>
</ControlsGroup>
</Wrapper>
);
};

export default MetricVizControls;
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

export { default } from "./FiltersWrapper";
export { default } from "./MetricVizControls";
6 changes: 0 additions & 6 deletions spotlight-client/src/SystemNarrativePage/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ const Container = styled(PageSection)`
flex-direction: row;
justify-content: flex-start;
}
@media screen and (min-width: ${breakpoints.desktop[0]}px) {
align-items: flex-start;
flex-direction: row;
justify-content: flex-start;
}
`;

const SectionCopy = styled(CopyBlock)<{ $isSticky: boolean }>`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { BubbleChart, ProportionalBar } from "../charts";
import { useHighlightedItem } from "../charts/utils";
import DemographicsByCategoryMetric from "../contentModels/DemographicsByCategoryMetric";
import DemographicFilterSelect from "../DemographicFilterSelect";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import NoMetricData from "../NoMetricData";
import { animation, zIndex } from "../UiLibrary";

Expand Down Expand Up @@ -82,8 +82,9 @@ const VizDemographicsByCategory: React.FC<VizDemographicsByCategoryProps> = ({
>
{({ measureRef }) => (
<>
<FiltersWrapper
<MetricVizControls
filters={[<DemographicFilterSelect metric={metric} />]}
metric={metric}
/>
<animated.div style={chartContainerStyles}>
{chartTransitions.map(({ item, key, props }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import React, { useState } from "react";
import { isWindowSizeId, WindowedTimeSeries, WindowSizeId } from "../charts";
import type HistoricalPopulationBreakdownMetric from "../contentModels/HistoricalPopulationBreakdownMetric";
import DemographicFilterSelect from "../DemographicFilterSelect";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import NoMetricData from "../NoMetricData";
import { Dropdown } from "../UiLibrary";

Expand All @@ -47,7 +47,7 @@ const VizHistoricalPopulationBreakdown: React.FC<{
if (metric.dataSeries)
return (
<>
<FiltersWrapper
<MetricVizControls
filters={[
<Dropdown
label="Range"
Expand All @@ -65,6 +65,7 @@ const VizHistoricalPopulationBreakdown: React.FC<{
/>,
<DemographicFilterSelect metric={metric} />,
]}
metric={metric}
/>
<WindowedTimeSeries
data={metric.dataSeries}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import React from "react";
import styled from "styled-components/macro";
import { ProportionalBar } from "../charts";
import PopulationBreakdownByLocationMetric from "../contentModels/PopulationBreakdownByLocationMetric";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import LocalityFilterSelect from "../LocalityFilterSelect";
import NoMetricData from "../NoMetricData";
import Statistic from "../Statistic";
Expand All @@ -45,7 +45,10 @@ const VizPopulationBreakdownByLocation: React.FC<VizPopulationBreakdownByLocatio
if (metric.dataSeries) {
return (
<>
<FiltersWrapper filters={[<LocalityFilterSelect metric={metric} />]} />
<MetricVizControls
filters={[<LocalityFilterSelect metric={metric} />]}
metric={metric}
/>
{metric.dataSeries.map(({ label: viewName, records }) => (
<ChartWrapper key={viewName}>
<ProportionalBar title={viewName} data={records} height={88} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
} from "../charts";
import DemographicsByCategoryMetric from "../contentModels/DemographicsByCategoryMetric";
import DemographicFilterSelect from "../DemographicFilterSelect";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import { prisonStayLengthFields } from "../metricsApi";
import NoMetricData from "../NoMetricData";
import { animation } from "../UiLibrary";
Expand Down Expand Up @@ -100,8 +100,9 @@ const VizPrisonStayLengths: React.FC<VizPrisonStayLengthsProps> = ({
>
{({ measureRef }) => (
<>
<FiltersWrapper
<MetricVizControls
filters={[<DemographicFilterSelect metric={metric} />]}
metric={metric}
/>
<animated.div style={chartContainerStyles}>
<ChartsWrapper ref={measureRef}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import React from "react";
import styled from "styled-components/macro";
import { TopologicalMap } from "../charts";
import ProgramParticipationCurrentMetric from "../contentModels/ProgramParticipationCurrentMetric";
import MetricVizControls from "../MetricVizControls";
import NoMetricData from "../NoMetricData";

const MapWrapper = styled.figure``;
Expand All @@ -35,13 +36,16 @@ const VizProgramParticipationCurrent: React.FC<VizProgramParticipationCurrentPro

if (dataMapping) {
return (
<MapWrapper aria-label={`${metric.localityLabels.label} map chart`}>
<TopologicalMap
aspectRatio={metric.mapData.aspectRatio}
localityData={dataMapping}
topology={metric.mapData.topology}
/>
</MapWrapper>
<>
<MetricVizControls filters={[]} metric={metric} />
<MapWrapper aria-label={`${metric.localityLabels.label} map chart`}>
<TopologicalMap
aspectRatio={metric.mapData.aspectRatio}
localityData={dataMapping}
topology={metric.mapData.topology}
/>
</MapWrapper>
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import RateTrend, {
} from "../charts/RateTrend";
import RecidivismRateMetric from "../contentModels/RecidivismRateMetric";
import DemographicFilterSelect from "../DemographicFilterSelect";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import NoMetricData from "../NoMetricData";
import { animation } from "../UiLibrary";
import CohortFilterSelect from "./CohortFilterSelect";
Expand Down Expand Up @@ -59,14 +59,15 @@ const VizRecidivismRateCumulative: React.FC<VizRecidivismRateCumulativeProps> =
if (cohortDataSeries && selectedCohorts) {
return (
<>
<FiltersWrapper
<MetricVizControls
filters={[
<CohortFilterSelect metric={metric} />,
<DemographicFilterSelect
disabled={selectedCohorts.length !== 1}
metric={metric}
/>,
]}
metric={metric}
/>
<ChartWrapper>
{chartTransitions.map(({ item, props, key }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
} from "../charts";
import RecidivismRateMetric from "../contentModels/RecidivismRateMetric";
import DemographicFilterSelect from "../DemographicFilterSelect";
import FiltersWrapper from "../FiltersWrapper";
import MetricVizControls from "../MetricVizControls";
import NoMetricData from "../NoMetricData";
import { animation } from "../UiLibrary";
import FollowupPeriodFilterSelect from "./FollowupPeriodFilterSelect";
Expand Down Expand Up @@ -98,11 +98,12 @@ const VizRecidivismRateSingleFollowup: React.FC<VizRecidivismRateSingleFollowupP
>
{({ measureRef }) => (
<>
<FiltersWrapper
<MetricVizControls
filters={[
<FollowupPeriodFilterSelect metric={metric} />,
<DemographicFilterSelect metric={metric} />,
]}
metric={metric}
/>
<animated.div style={chartContainerStyles}>
<ChartsWrapper ref={measureRef}>
Expand Down

0 comments on commit 2eaa46a

Please sign in to comment.