Skip to content

Commit 9cc1f1b

Browse files
Afani97Louis-Cloud
andauthored
Production Release September 26, 2022 (#158)
* show statewide data * cleanup compare view * listen for scroll event and update * cleanup css * Fixed changing height in header (#143) * hotfix: Agency header misaligned in compare * Add 1 more year to include in range * improve scroll when comparing * hide white checkbox since it doesn't affect graph * allow filtering of year in overview section * conditinally show dropdown direction depending on show compare * fix regression for dropdown directions on mobile * Don't crash site if no data is found for a selected year * update condition for no data in traffic stops * remove migration and update csv instead * Revert "Bug fix no data for selected year" * give this another try * fix dropdown cutoff overview page in mobile view * only show years that are available * remove fixed header * hot-fix-modal-cuttoff * clarify some of the graphs Co-authored-by: Louis-Cloud <58861655+Louis-Cloud@users.noreply.github.com>
1 parent 80c7fd8 commit 9cc1f1b

28 files changed

+272
-103
lines changed

frontend/src/Components/AgencyData/AgencyData.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ function AgencyData(props) {
2424
const [sidebarOpen, setSidebarOpen] = useState(false);
2525
const [agencyHeaderOpen, setAgencyHeaderOpen] = useState(false);
2626
const [chartsOpen, setChartsOpen] = useState(false);
27-
2827
const [chartState] = useDataset(agencyId, AGENCY_DETAILS);
2928

3029
useEffect(() => {
31-
if (chartState.data[AGENCY_DETAILS] && !props?.agencyId) setSidebarOpen(true);
30+
if (chartState.data[AGENCY_DETAILS]) setSidebarOpen(true);
3231
}, [chartState.data[AGENCY_DETAILS]]);
3332

3433
useEffect(() => {
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
import styled from 'styled-components';
22

33
import MainBase from '../../styles/MainBase';
4-
import { phoneOnly, smallerThanTabletLandscape } from '../../styles/breakpoints';
4+
import { smallerThanTabletLandscape } from '../../styles/breakpoints';
55

66
export const AgencyData = styled(MainBase)``;
77

88
export const ContentWrapper = styled.div`
99
display: flex;
1010
flex-direction: ${(props) => (props.showCompare ? 'column' : 'row')};
1111
width: 100%;
12-
12+
scroll-behavior: smooth;
1313
@media (${smallerThanTabletLandscape}) {
1414
flex-direction: column;
1515
overflow-y: visible;
1616
}
17-
18-
@media (${phoneOnly}) {
19-
flex-direction: column-reverse;
20-
overflow-y: hidden;
21-
}
2217
`;

frontend/src/Components/AgencyData/AgencyHeader.js

+17-15
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function AgencyHeader({
5757
transition={{ ease: 'easeIn' }}
5858
>
5959
<S.AgencyHeader>
60-
<S.SubHeaderNavRow>{!showCloseButton && <BackButton />}</S.SubHeaderNavRow>
60+
<S.SubHeaderNavRow>{!showCompareDepartments && <BackButton />}</S.SubHeaderNavRow>
6161
<S.SubHeaderContentRow>
6262
<S.EntityDetails>
6363
{officerId ? (
@@ -138,20 +138,22 @@ function AgencyHeader({
138138
</S.ShowDepartmentsButton>
139139
)}
140140
{showCloseButton && (
141-
<Button
142-
variant="positive"
143-
border={`2px solid ${theme.colors.primary}`}
144-
{...ChartHeaderStyles.ButtonInlines}
145-
onClick={() => toggleShowCompare()}
146-
>
147-
<ChartHeaderStyles.Icon
148-
icon={ICONS.close}
149-
height={25}
150-
width={25}
151-
fill={theme.colors.white}
152-
/>
153-
Close
154-
</Button>
141+
<div style={{ display: 'block' }}>
142+
<Button
143+
variant="positive"
144+
border={`2px solid ${theme.colors.primary}`}
145+
{...ChartHeaderStyles.ButtonInlines}
146+
onClick={() => toggleShowCompare()}
147+
>
148+
<ChartHeaderStyles.Icon
149+
icon={ICONS.close}
150+
height={25}
151+
width={25}
152+
fill={theme.colors.white}
153+
/>
154+
Close
155+
</Button>
156+
</div>
155157
)}
156158
</S.AgencyHeader>
157159
</motion.div>

frontend/src/Components/App.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@ function App() {
4646
};
4747

4848
const updateAgencyId = (department) => {
49-
if (!department) {
50-
setAgencyId(null);
51-
}
52-
setAgencyId(department.id);
49+
const departmentId = department !== undefined ? department.id : null;
50+
setAgencyId(departmentId);
5351
};
5452

5553
return (
@@ -64,7 +62,7 @@ function App() {
6462
<Header />
6563
<Switch>
6664
<Route path={`${AGENCY_LIST_SLUG}/:agencyId`}>
67-
<div style={{ display: 'flex', overflowY: 'auto' }}>
65+
<div style={{ display: 'flex', height: '100%' }}>
6866
<ChartStateProvider reducer={chartReducer} initialState={initialChartState}>
6967
<AgencyData showCompare={showCompare} toggleShowCompare={toggleShowCompare} />
7068
</ChartStateProvider>
@@ -82,7 +80,7 @@ function App() {
8280
<div style={{ width: '50%', padding: 20 }}>
8381
<S.SubHeading>Search for Additional Departments to Compare</S.SubHeading>
8482
<DepartmentSearch
85-
onChange={updateAgencyId}
83+
onChange={(d) => updateAgencyId(d)}
8684
placeholder="Search for a police or sheriff's department..."
8785
/>
8886
</div>

frontend/src/Components/Charts/ChartRoutes.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,38 +28,38 @@ function Charts(props) {
2828
<FJRoute
2929
exact
3030
path={`${match.path}${slugs.OVERVIEW_SLUG}`}
31-
importComponent={<Overview agencyId={props.agencyId} showCompare={props.showCompare} />}
31+
importComponent={<Overview {...props} />}
3232
renderLoading={() => <DataLoading />}
3333
renderError={() => <ChartError chartName="Overview" />}
3434
/>
3535
<FJRoute
3636
exact
3737
path={`${match.path}${slugs.TRAFFIC_STOPS_SLUG}`}
38-
importComponent={<TrafficStops agencyId={props.agencyId} showCompare={props.showCompare} />}
38+
importComponent={<TrafficStops {...props} />}
3939
renderLoading={() => <DataLoading />}
4040
renderError={() => <ChartError chartName="Traffic Stops" />}
4141
/>
4242
<FJRoute
4343
path={`${match.path}${slugs.SEARCHES_SLUG}`}
44-
importComponent={<Searches agencyId={props.agencyId} showCompare={props.showCompare} />}
44+
importComponent={<Searches {...props} />}
4545
renderLoading={() => <DataLoading />}
4646
renderError={() => <ChartError chartName="Searches" />}
4747
/>
4848
<FJRoute
4949
path={`${match.path}${slugs.SEARCH_RATE_SLUG}`}
50-
importComponent={<SearchRate agencyId={props.agencyId} showCompare={props.showCompare} />}
50+
importComponent={<SearchRate {...props} />}
5151
renderLoading={() => <DataLoading />}
5252
renderError={() => <ChartError chartName="Search Rate" />}
5353
/>
5454
<FJRoute
5555
path={`${match.path}${slugs.CONTRABAND_SLUG}`}
56-
importComponent={<Contraband agencyId={props.agencyId} showCompare={props.showCompare} />}
56+
importComponent={<Contraband {...props} />}
5757
renderLoading={() => <DataLoading />}
5858
renderError={() => <ChartError chartName="Contraband" />}
5959
/>
6060
<FJRoute
6161
path={`${match.path}${slugs.USE_OF_FORCE_SLUG}`}
62-
importComponent={<UseOfForce agencyId={props.agencyId} showCompare={props.showCompare} />}
62+
importComponent={<UseOfForce {...props} />}
6363
renderLoading={() => <DataLoading />}
6464
renderError={() => <ChartError chartName="Use of Force" />}
6565
/>

frontend/src/Components/Charts/ChartSections/ChartPageBase.styled.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { phoneOnly, smallerThanDesktop } from '../../../styles/breakpoints';
55
export const ChartPageBase = styled(motion.article)`
66
flex: 1;
77
height: 100%;
8-
overflow-y: scroll;
8+
overflow-y: hidden;
99
width: 100%;
1010
`;
1111

frontend/src/Components/Charts/ChartSections/ChartsCommon.styled.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import styled from 'styled-components';
2-
import { phoneOnly } from '../../../styles/breakpoints';
2+
import { phoneOnly, smallerThanDesktop } from '../../../styles/breakpoints';
33
import { H2 } from '../../../styles/StyledComponents/Typography';
44

55
export const ChartSection = styled.div`
@@ -36,7 +36,7 @@ export const ChartSubsection = styled.div`
3636
flex-direction: ${(props) => (props.showCompare ? 'column' : 'row')};
3737
flex: 1;
3838
39-
@media (${phoneOnly}) {
39+
@media (${smallerThanDesktop}) {
4040
flex-direction: column;
4141
}
4242
`;

frontend/src/Components/Charts/ChartSections/DataSubsetPicker/DataSubsetPicker.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import * as S from './DataSubsetPicker.styled';
55
import Dropdown from '../../../Elements/Dropdown/Dropdown';
66
import { SIZES, COLORS, WEIGHTS } from '../../../../styles/StyledComponents/Typography';
77

8-
function DataSubsetPicker({ options, value, onChange, label, dropUp }) {
8+
function DataSubsetPicker({ options, value, onChange, label, dropUp, dropDown }) {
99
return (
1010
<S.DataSubsetPicker>
1111
{label && (
1212
<S.Label size={SIZES[0]} color={COLORS[1]} weight={WEIGHTS[1]}>
1313
{label}
1414
</S.Label>
1515
)}
16-
<Dropdown value={value} onChange={onChange} options={options} dropUp={dropUp} />
16+
<Dropdown
17+
value={value}
18+
onChange={onChange}
19+
options={options}
20+
dropUp={dropUp}
21+
dropDown={dropDown}
22+
/>
1723
</S.DataSubsetPicker>
1824
);
1925
}

frontend/src/Components/Charts/Contraband/Contraband.js

+26-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useEffect, useState } from 'react';
22
import ContrabandStyled from './Contraband.styled';
33
import * as S from '../ChartSections/ChartsCommon.styled';
44
import { useTheme } from 'styled-components';
55

66
// Util
77
import toTitleCase from '../../../util/toTitleCase';
88
import {
9-
RACES,
10-
YEARS_DEFAULT,
11-
reduceYearsToTotal,
129
calculatePercentage,
1310
getQuantityForYear,
11+
RACES,
12+
reduceYearsToTotal,
13+
YEARS_DEFAULT,
1414
} from '../chartUtils';
1515

1616
// Hooks
@@ -27,7 +27,7 @@ import ChartHeader from '../ChartSections/ChartHeader';
2727
import DataSubsetPicker from '../ChartSections/DataSubsetPicker/DataSubsetPicker';
2828

2929
function SearchRate(props) {
30-
const { agencyId } = props;
30+
const { agencyId, showCompare } = props;
3131
const theme = useTheme();
3232

3333
const [chartState] = useDataset(agencyId, CONTRABAND_HIT_RATE);
@@ -45,6 +45,20 @@ function SearchRate(props) {
4545
const data = chartState.data[CONTRABAND_HIT_RATE];
4646
if (data) {
4747
const { contraband, searches } = data;
48+
if (year && year !== YEARS_DEFAULT) {
49+
// If an agency has no data for selected year
50+
if (contraband.filter((c) => c.year === year).length === 0) {
51+
setContrabandData(
52+
RACES.map((ethnicGroup) => ({
53+
displayName: toTitleCase(ethnicGroup),
54+
color: `${theme.colors.ethnicGroup[ethnicGroup]}90`,
55+
x: toTitleCase(ethnicGroup),
56+
y: 0,
57+
}))
58+
);
59+
return;
60+
}
61+
}
4862
const mappedData = [];
4963
RACES.forEach((ethnicGroup) => {
5064
const groupBar = {};
@@ -64,7 +78,11 @@ function SearchRate(props) {
6478
}
6579
mappedData.push(groupBar);
6680
});
67-
setContrabandData(mappedData.reverse());
81+
if (mappedData) {
82+
setContrabandData(mappedData.reverse());
83+
} else {
84+
setContrabandData([]);
85+
}
6886
}
6987
}, [chartState.data[CONTRABAND_HIT_RATE], year]);
7088

@@ -90,7 +108,7 @@ function SearchRate(props) {
90108
Shows what percentage of searches discovered contraband for a given race / ethnic group
91109
</P>
92110
</S.ChartDescription>
93-
<S.ChartSubsection showCompare={props.showCompare}>
111+
<S.ChartSubsection showCompare={showCompare}>
94112
<S.LineSection>
95113
<S.LineWrapper>
96114
<Bar
@@ -128,6 +146,7 @@ function SearchRate(props) {
128146
value={year}
129147
onChange={handleYearSelect}
130148
options={[YEARS_DEFAULT].concat(chartState.yearRange)}
149+
dropUp={!!showCompare}
131150
/>
132151
</S.LegendSection>
133152
</S.ChartSubsection>

0 commit comments

Comments
 (0)