Skip to content

Commit cf26758

Browse files
authored
Merge pull request #11976 from nanaya/chart-stack-normalize
Use d3 builtin stack normalisation
2 parents e24e89e + 4bf8743 commit cf26758

File tree

1 file changed

+12
-43
lines changed

1 file changed

+12
-43
lines changed

resources/js/charts/changelog-chart.ts

+12-43
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
import * as d3 from 'd3';
5-
import { first, groupBy, kebabCase, last, map, sumBy } from 'lodash';
5+
import { first, groupBy, kebabCase, keyBy, last, map } from 'lodash';
66
import moment from 'moment';
77
import { fadeIn, fadeOut } from 'utils/fade';
88
import { formatNumber } from 'utils/html';
@@ -11,7 +11,6 @@ import { parseJson } from 'utils/json';
1111
interface BuildHistory {
1212
created_at: string;
1313
label: string;
14-
normalized?: number;
1514
user_count: number;
1615
}
1716

@@ -36,7 +35,6 @@ export default class ChangelogChart {
3635
private autoHideTooltip?: number;
3736
private chartData?: ChartData;
3837
private data: d3.Series<DataObj, string>[] = [];
39-
private hasData = false;
4038
private height = 0;
4139
private readonly hoverArea;
4240
private readonly scales;
@@ -53,6 +51,10 @@ export default class ChangelogChart {
5351
private x?: Date;
5452
private y?: number;
5553

54+
private get hasData() {
55+
return this.data.length > 0;
56+
}
57+
5658
constructor(area: HTMLElement) {
5759
this.scales = {
5860
class: d3.scaleOrdinal<string>(),
@@ -118,15 +120,15 @@ export default class ChangelogChart {
118120
loadData() {
119121
this.chartData = parseJson('json-chart-config');
120122

121-
const { data, hasData } = this.normalizeData(this.chartData.build_history);
123+
const data = this.normalizeData(this.chartData.build_history);
122124

123125
const stack = d3
124126
.stack<DataObj, string>()
125127
.keys(this.chartData.order)
126-
.value((d, val) => (d.builds[val]?.normalized ?? 0));
128+
.offset(d3.stackOffsetExpand)
129+
.value((d, val) => (d.builds[val]?.user_count ?? 0));
127130

128131
this.data = stack(data);
129-
this.hasData = this.chartData.build_history.length > 0 && hasData;
130132

131133
this.resize();
132134
}
@@ -157,53 +159,20 @@ export default class ChangelogChart {
157159
private normalizeData(rawData: BuildHistory[]) {
158160
// normalize the user count values
159161
// and parse data into a form digestible by d3.stack()
160-
161-
let resetLabel: string | null = null;
162-
let hasData: boolean | null = null;
163-
164162
const sortedData = groupBy(rawData, 'created_at');
165163

166-
const data = map(sortedData, (values, timestamp) => {
167-
let sum = sumBy(values, 'user_count');
168-
169-
if (sum === 0) {
170-
let fakedVal = resetLabel != null
171-
? values.find((val) => val.label === resetLabel)
172-
: null;
173-
174-
if (fakedVal == null) {
175-
fakedVal = last(values);
176-
resetLabel = fakedVal?.label ?? null;
177-
}
178-
179-
if (fakedVal) fakedVal.user_count = 1;
180-
sum = 1;
181-
} else {
182-
hasData ??= true;
183-
}
184-
164+
return map(sortedData, (values, timestamp): DataObj => {
185165
// parse date stored in strings to JS Date object for use by
186166
// d3 domains, and format it into a string shown on the tooltip
187-
const m = moment(values[0].created_at);
167+
const m = moment(timestamp);
188168

189-
const obj: DataObj = {
190-
builds: {},
169+
return {
170+
builds: keyBy(values, 'label'),
191171
created_at: timestamp,
192172
date: m.toDate(),
193173
date_formatted: m.format('YYYY/MM/DD'),
194174
};
195-
196-
for (const val of values) {
197-
obj.builds[val.label] = val;
198-
obj.builds[val.label].normalized = val.user_count / sum;
199-
}
200-
201-
return obj;
202175
});
203-
204-
hasData ??= false;
205-
206-
return { data, hasData };
207176
}
208177

209178
private setDimensions() {

0 commit comments

Comments
 (0)