Skip to content

Commit 21ebd31

Browse files
cdmh219cdhenley219
and
cdhenley219
authored
DATAP-1533 stacked area chart class to functional (#537)
* initial implementation * Fixed issue with updateTooltip, so that it doesn't need (and render) with tooltip date * Removed old files for StackedAreaChart class component * Added unit test for basic code coverage * added comment to explain addition of dataClone variable --------- Co-authored-by: cdhenley219 <[email protected]>
1 parent bd75464 commit 21ebd31

File tree

10 files changed

+212
-519
lines changed

10 files changed

+212
-519
lines changed

src/components/Charts/LineChart/LineChart.spec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ describe('component: LineChart', () => {
6969
lens: 'Overview',
7070
};
7171

72-
const view = {};
72+
const view = {
73+
isPrintMode: false,
74+
};
7375

7476
renderComponent(query, trends, view);
7577

src/components/Charts/StackedAreaChart.js

-168
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import './StackedAreaChart.less';
2+
import * as d3 from 'd3';
3+
import { stackedArea } from 'britecharts';
4+
import { useEffect, useMemo } from 'react';
5+
import { useSelector, useDispatch } from 'react-redux';
6+
import * as colors from '../../../constants/colors';
7+
import {
8+
getLastDate,
9+
pruneIncompleteStackedAreaInterval,
10+
isStackedAreaDataEmpty,
11+
} from '../../../utils/chart';
12+
import { updateTrendsTooltip } from '../../../actions/trends';
13+
import { debounce } from '../../../utils';
14+
import {
15+
selectTrendsResultsDateRangeArea,
16+
selectTrendsColorMap,
17+
} from '../../../reducers/trends/selectors';
18+
import {
19+
selectQueryDateReceivedMin,
20+
selectQueryDateReceivedMax,
21+
selectQueryDateInterval,
22+
selectQueryLens,
23+
} from '../../../reducers/query/selectors';
24+
import { selectViewIsPrintMode } from '../../../reducers/view/selectors';
25+
import { ChartWrapper } from '../ChartWrapper/ChartWrapper';
26+
27+
export const StackedAreaChart = () => {
28+
const dispatch = useDispatch();
29+
30+
const colorMap = useSelector(selectTrendsColorMap);
31+
const data = useSelector(selectTrendsResultsDateRangeArea);
32+
const from = useSelector(selectQueryDateReceivedMin);
33+
const to = useSelector(selectQueryDateReceivedMax);
34+
const lens = useSelector(selectQueryLens);
35+
const interval = useSelector(selectQueryDateInterval);
36+
const isPrintMode = useSelector(selectViewIsPrintMode);
37+
38+
const showTooltip = lens !== 'Overview';
39+
40+
const filteredData = useMemo(() => {
41+
const dateRange = { from, to };
42+
return pruneIncompleteStackedAreaInterval(data, dateRange, interval);
43+
}, [data, from, to, interval]);
44+
45+
const isDataEmpty = isStackedAreaDataEmpty(filteredData);
46+
47+
useEffect(() => {
48+
const dateRange = { from, to };
49+
const chartID = '#stacked-area-chart';
50+
const chartSelector = chartID + ' .stacked-area';
51+
const container = d3.select(chartID);
52+
53+
if (!container.node() || isDataEmpty) {
54+
return;
55+
}
56+
57+
const tooltipUpdated = (selectedState) => {
58+
dispatch(updateTrendsTooltip(selectedState));
59+
};
60+
61+
const updateTooltip = (point) => {
62+
tooltipUpdated({
63+
date: point.date,
64+
dateRange,
65+
interval,
66+
values: point.values,
67+
});
68+
};
69+
70+
d3.select(chartSelector).remove();
71+
72+
const width = isPrintMode
73+
? 550
74+
: container.node().getBoundingClientRect().width;
75+
76+
const colorData = filteredData.filter((item) => item.name !== 'Other');
77+
const colorScheme = [...new Set(colorData.map((item) => item.name))].map(
78+
(obj) => colorMap[obj],
79+
);
80+
colorScheme.push(colors.DataLens[10]);
81+
82+
const stackedAreaChart = stackedArea();
83+
84+
stackedAreaChart
85+
.margin({ left: 70, right: 10, top: 10, bottom: 40 })
86+
.areaCurve('linear')
87+
.initializeVerticalMarker(true)
88+
.isAnimated(false)
89+
.tooltipThreshold(1)
90+
.grid('horizontal')
91+
.aspectRatio(0.5)
92+
.width(width)
93+
.dateLabel('date')
94+
.colorSchema(colorScheme)
95+
.on('customMouseMove', debounce(updateTooltip, 200));
96+
97+
container.datum(filteredData).call(stackedAreaChart);
98+
99+
const config = {
100+
dateRange,
101+
interval,
102+
};
103+
104+
tooltipUpdated(getLastDate(filteredData, config));
105+
106+
return () => {
107+
d3.select(chartSelector).remove();
108+
container.datum([]);
109+
};
110+
}, [
111+
colorMap,
112+
from,
113+
to,
114+
dispatch,
115+
filteredData,
116+
interval,
117+
isPrintMode,
118+
isDataEmpty,
119+
]);
120+
121+
return (
122+
<ChartWrapper
123+
hasKey={showTooltip}
124+
domId="stacked-area-chart"
125+
isEmpty={isDataEmpty}
126+
/>
127+
);
128+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { testRender as render, screen } from '../../../testUtils/test-utils';
2+
import * as d3 from 'd3';
3+
import { merge } from '../../../testUtils/functionHelpers';
4+
import { buildFluentMock } from '../__fixtures__/buildFluentMock';
5+
import { StackedAreaChart } from './StackedAreaChart';
6+
import { defaultTrends } from '../../../reducers/trends/trends';
7+
import { defaultQuery } from '../../../reducers/query/query';
8+
import { defaultView } from '../../../reducers/view/view';
9+
10+
const renderComponent = (queryState, trendsState, viewState) => {
11+
merge(queryState, defaultQuery);
12+
merge(trendsState, defaultTrends);
13+
merge(viewState, defaultView);
14+
15+
const data = {
16+
query: queryState,
17+
trends: trendsState,
18+
view: viewState,
19+
};
20+
21+
render(<StackedAreaChart />, {
22+
preloadedState: data,
23+
});
24+
};
25+
26+
jest.mock('d3');
27+
describe('component::StackedAreaChart', () => {
28+
beforeEach(() => {
29+
const fakeD3 = buildFluentMock({ height: 50 });
30+
// how we add our own implementation of d3
31+
// override this so it doesn't crash. we test implementation elsewhere.
32+
jest.spyOn(d3, 'select').mockImplementation(fakeD3);
33+
jest.spyOn(d3, 'selectAll').mockImplementation(fakeD3);
34+
});
35+
36+
afterEach(() => {
37+
jest.restoreAllMocks();
38+
});
39+
40+
it('should render chart with data as expected', () => {
41+
const trends = {
42+
colorMap: {
43+
Other: '#345',
44+
foo: '#fff',
45+
bar: '#eee',
46+
},
47+
results: {
48+
dateRangeArea: [
49+
{ date: '2024-05-01T00:00:00.000Z', value: 225171, name: 'Other' },
50+
{ date: '2024-06-01T00:00:00.000Z', value: 225171, name: 'Other' },
51+
{ date: '2024-07-01T00:00:00.000Z', value: 225171, name: 'Other' },
52+
{ date: '2024-08-01T00:00:00.000Z', value: 198483, name: 'Other' },
53+
],
54+
},
55+
tooltip: false,
56+
};
57+
58+
const query = {
59+
dateInterval: 'Month',
60+
date_received_max: new Date('2024-01-02T07:00:00.000Z'),
61+
date_received_min: new Date('2024-12-02T08:00:00.000Z'),
62+
lens: 'Overview',
63+
};
64+
65+
const view = {
66+
isPrintMode: false,
67+
width: 1000,
68+
};
69+
70+
renderComponent(query, trends, view);
71+
72+
expect(screen.getByText('Complaints')).toBeInTheDocument();
73+
expect(screen.getByText('Date received by the CFPB')).toBeInTheDocument();
74+
});
75+
});

src/components/Trends/TrendsPanel.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import React from 'react';
2424
import { RowChart } from '../Charts/RowChart/RowChart';
2525
import Select from '../RefineBar/Select';
2626
import { Separator } from '../RefineBar/Separator';
27-
import StackedAreaChart from '../Charts/StackedAreaChart';
27+
import { StackedAreaChart } from '../Charts/StackedAreaChart/StackedAreaChart';
2828
import { TabbedNavigation } from '../TabbedNavigation';
2929
import { TrendDepthToggle } from './TrendDepthToggle';
3030
import { trendsDateWarningDismissed } from '../../actions/view';

0 commit comments

Comments
 (0)