@automattic/charts 4.0.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/SECURITY.md +1 -0
- package/TOKENS.md +7 -5
- package/dist/index.cjs +614 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +30 -0
- package/dist/index.d.cts +88 -74
- package/dist/index.d.ts +88 -74
- package/dist/index.js +615 -261
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/charts/area-chart/area-chart.tsx +2 -1
- package/src/charts/area-chart/test/area-chart.test.tsx +31 -0
- package/src/charts/heatmap-chart/build-calendar-data.ts +189 -0
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +31 -0
- package/src/charts/heatmap-chart/heatmap-chart.tsx +44 -9
- package/src/charts/heatmap-chart/index.ts +10 -3
- package/src/charts/heatmap-chart/private/civil-day.ts +174 -0
- package/src/charts/heatmap-chart/private/index.ts +0 -2
- package/src/charts/heatmap-chart/private/use-heatmap-colors.ts +5 -1
- package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +4 -1
- package/src/charts/heatmap-chart/test/civil-day.test.ts +70 -0
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +91 -0
- package/src/charts/heatmap-chart/test/use-calendar-heatmap-data.test.tsx +118 -0
- package/src/charts/heatmap-chart/test/use-heatmap-colors.test.ts +8 -0
- package/src/charts/heatmap-chart/test/zoned-calendar-data.test.ts +248 -0
- package/src/charts/heatmap-chart/types.ts +35 -0
- package/src/charts/heatmap-chart/use-calendar-heatmap-data.ts +42 -0
- package/src/charts/leaderboard-chart/types.ts +2 -2
- package/src/charts/line-chart/line-chart.tsx +41 -7
- package/src/charts/line-chart/test/crosshair-style-types.test.ts +31 -0
- package/src/charts/line-chart/test/line-chart.test.tsx +178 -38
- package/src/charts/line-chart/test/zoned-date-string.test.tsx +37 -0
- package/src/charts/line-chart/types.ts +13 -2
- package/src/charts/pie-chart/test/pie-chart.test.tsx +3 -12
- package/src/components/tooltip/accessible-tooltip.tsx +40 -12
- package/src/components/tooltip/private/bounded-tooltip.tsx +71 -16
- package/src/components/tooltip/test/accessible-tooltip.test.tsx +60 -1
- package/src/components/tooltip/test/bounded-tooltip.test.tsx +102 -6
- package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +122 -3
- package/src/components/tooltip/xy-chart-tooltip.tsx +53 -14
- package/src/hooks/test/use-chart-data-transform-zone.test.tsx +36 -0
- package/src/hooks/test/use-chart-margin.test.tsx +173 -3
- package/src/hooks/use-chart-data-transform.ts +10 -3
- package/src/hooks/use-chart-margin.tsx +54 -9
- package/src/index.ts +4 -0
- package/src/styles/chart-scope.scss +11 -3
- package/src/styles/test/chart-scope.test.ts +35 -1
- package/src/test-utils/zoned-parsing-suite.ts +90 -0
- package/src/types.ts +2 -2
- package/src/utils/date-formatting.ts +1 -18
- package/src/utils/date-parsing.ts +103 -52
- package/src/utils/get-edge-tick-widths.ts +33 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/test/date-parsing.auckland.test.ts +6 -0
- package/src/utils/test/date-parsing.santiago.test.ts +6 -0
- package/src/utils/test/date-parsing.test.ts +16 -1
- package/src/utils/test/get-edge-tick-widths.test.ts +55 -0
- package/src/utils/warn-once.ts +23 -0
- package/src/visx/types.ts +27 -3
- package/src/charts/heatmap-chart/private/build-calendar-data.ts +0 -179
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { addDays, differenceInCalendarWeeks, format, parseISO, startOfWeek } from 'date-fns';
|
|
2
|
-
import type { DataPointDate } from '../../../types';
|
|
3
|
-
import type { HeatmapCell, HeatmapColumn } from '../types';
|
|
4
|
-
|
|
5
|
-
export type CalendarHeatmapResult = {
|
|
6
|
-
data: HeatmapColumn[];
|
|
7
|
-
rowLabels: string[];
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/** Rows that get a weekday label (Mon, Wed, Fri with a Monday week start). */
|
|
11
|
-
const LABELLED_ROWS = [ 0, 2, 4 ];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Resolve one grid bound: an unparseable or narrowing value falls back to the
|
|
15
|
-
* series' own bound, so the grid can only ever be widened.
|
|
16
|
-
*
|
|
17
|
-
* @param bound - Requested bound as `yyyy-MM-dd`, if any.
|
|
18
|
-
* @param fallback - The series' own bound on this side.
|
|
19
|
-
* @param direction - Which way the bound is allowed to move.
|
|
20
|
-
* @return The bound to draw to.
|
|
21
|
-
*/
|
|
22
|
-
const widenTo = (
|
|
23
|
-
bound: string | undefined,
|
|
24
|
-
fallback: Date,
|
|
25
|
-
direction: 'earlier' | 'later'
|
|
26
|
-
): Date => {
|
|
27
|
-
if ( ! bound ) {
|
|
28
|
-
return fallback;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const parsed = parseISO( bound );
|
|
32
|
-
if ( isNaN( parsed.getTime() ) ) {
|
|
33
|
-
return fallback;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if ( direction === 'earlier' ) {
|
|
37
|
-
return parsed < fallback ? parsed : fallback;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return parsed > fallback ? parsed : fallback;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const toDate = ( point: DataPointDate ): Date | null => {
|
|
44
|
-
if ( point.date instanceof Date && ! isNaN( point.date.getTime() ) ) {
|
|
45
|
-
return point.date;
|
|
46
|
-
}
|
|
47
|
-
if ( point.dateString ) {
|
|
48
|
-
const parsed = parseISO( point.dateString );
|
|
49
|
-
if ( ! isNaN( parsed.getTime() ) ) {
|
|
50
|
-
return parsed;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Labels stay English on the browser's calendar, unlike the time axis: this is a
|
|
57
|
-
// plain function the host calls outside the provider, so `GlobalChartsProvider`'s
|
|
58
|
-
// `locale` and `timeZone` cannot reach it. CHARTS-266 has the detail.
|
|
59
|
-
export const buildCalendarHeatmapData = (
|
|
60
|
-
series: DataPointDate[],
|
|
61
|
-
options: {
|
|
62
|
-
weekStartsOn?: 0 | 1;
|
|
63
|
-
/**
|
|
64
|
-
* Mark the days completing the first/last week outside the series'
|
|
65
|
-
* date span as hidden cells (empty grid slots) instead of blank
|
|
66
|
-
* cells, giving the calendar ragged edges. Days inside the span stay
|
|
67
|
-
* blank cells even when the series has no entry for them.
|
|
68
|
-
*/
|
|
69
|
-
hideOutOfRangeDays?: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* Draw the grid over this span (`yyyy-MM-dd` bounds) instead of the
|
|
72
|
-
* series' own. Days inside the grid but outside the series become
|
|
73
|
-
* placeholder cells: painted as empty slots so a short series still
|
|
74
|
-
* fills its container, but reporting nothing, since they were never
|
|
75
|
-
* measured. A start bound is drawn from the beginning of its week, so
|
|
76
|
-
* the grid always opens on a whole column. Bounds narrower than the
|
|
77
|
-
* series are ignored — the grid never drops a day that carries data.
|
|
78
|
-
*/
|
|
79
|
-
gridSpan?: { start?: string; end?: string };
|
|
80
|
-
} = {}
|
|
81
|
-
): CalendarHeatmapResult => {
|
|
82
|
-
const weekStartsOn = options.weekStartsOn ?? 1;
|
|
83
|
-
const hideOutOfRangeDays = options.hideOutOfRangeDays ?? true;
|
|
84
|
-
|
|
85
|
-
const entries = series
|
|
86
|
-
.map( point => ( { date: toDate( point ), value: point.value } ) )
|
|
87
|
-
.filter( ( entry ): entry is { date: Date; value: number | null } => entry.date !== null );
|
|
88
|
-
|
|
89
|
-
if ( ! entries.length ) {
|
|
90
|
-
return { data: [], rowLabels: [] };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const valueByDay = new Map< string, number | null >();
|
|
94
|
-
let minDate = entries[ 0 ].date;
|
|
95
|
-
let maxDate = entries[ 0 ].date;
|
|
96
|
-
for ( const { date, value } of entries ) {
|
|
97
|
-
valueByDay.set( format( date, 'yyyy-MM-dd' ), value );
|
|
98
|
-
if ( date < minDate ) {
|
|
99
|
-
minDate = date;
|
|
100
|
-
}
|
|
101
|
-
if ( date > maxDate ) {
|
|
102
|
-
maxDate = date;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Day-key bounds for the ragged-edge option: calendar-day comparison, so
|
|
107
|
-
// entries carrying a time of day can't shift the span.
|
|
108
|
-
const minDayKey = format( minDate, 'yyyy-MM-dd' );
|
|
109
|
-
const maxDayKey = format( maxDate, 'yyyy-MM-dd' );
|
|
110
|
-
|
|
111
|
-
// The grid may run wider than the series, never narrower: a bound that would
|
|
112
|
-
// cut into the data is dropped rather than honoured.
|
|
113
|
-
const gridMinDate = widenTo( options.gridSpan?.start, minDate, 'earlier' );
|
|
114
|
-
const gridMaxDate = widenTo( options.gridSpan?.end, maxDate, 'later' );
|
|
115
|
-
|
|
116
|
-
const gridMaxDayKey = format( gridMaxDate, 'yyyy-MM-dd' );
|
|
117
|
-
|
|
118
|
-
const gridStart = startOfWeek( gridMinDate, { weekStartsOn } );
|
|
119
|
-
|
|
120
|
-
// A start bound rarely lands on a week start, and `gridStart` rounds it down.
|
|
121
|
-
// The days it rounds past were no more measured than the rest of the widened
|
|
122
|
-
// span, so they are filler too; treating them as a ragged edge instead would
|
|
123
|
-
// notch the first column by however far the bound sat into its week.
|
|
124
|
-
const gridMinDayKey = format( gridMinDate < minDate ? gridStart : gridMinDate, 'yyyy-MM-dd' );
|
|
125
|
-
const weekCount = differenceInCalendarWeeks( gridMaxDate, gridStart, { weekStartsOn } ) + 1;
|
|
126
|
-
|
|
127
|
-
const rowLabels = Array.from( { length: 7 }, ( _, row ) =>
|
|
128
|
-
LABELLED_ROWS.includes( row ) ? format( addDays( gridStart, row ), 'EEE' ) : ''
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Hide short partial first-month labels when a later month follows; compact
|
|
132
|
-
// cells make adjacent labels collide. Keep the label for single-month ranges.
|
|
133
|
-
const MIN_FIRST_MONTH_WEEKS = 2;
|
|
134
|
-
const firstMonth = gridStart.getMonth();
|
|
135
|
-
let firstMonthWeeks = 0;
|
|
136
|
-
while (
|
|
137
|
-
firstMonthWeeks < weekCount &&
|
|
138
|
-
addDays( gridStart, firstMonthWeeks * 7 ).getMonth() === firstMonth
|
|
139
|
-
) {
|
|
140
|
-
firstMonthWeeks++;
|
|
141
|
-
}
|
|
142
|
-
const spansLaterMonth = firstMonthWeeks < weekCount;
|
|
143
|
-
const showFirstMonthLabel = ! spansLaterMonth || firstMonthWeeks >= MIN_FIRST_MONTH_WEEKS;
|
|
144
|
-
|
|
145
|
-
const data: HeatmapColumn[] = [];
|
|
146
|
-
let previousMonth = -1;
|
|
147
|
-
for ( let week = 0; week < weekCount; week++ ) {
|
|
148
|
-
const columnStart = addDays( gridStart, week * 7 );
|
|
149
|
-
const month = columnStart.getMonth();
|
|
150
|
-
const isNewMonth = month !== previousMonth;
|
|
151
|
-
const label =
|
|
152
|
-
isNewMonth && ( week !== 0 || showFirstMonthLabel ) ? format( columnStart, 'MMM' ) : '';
|
|
153
|
-
previousMonth = month;
|
|
154
|
-
|
|
155
|
-
const cells: HeatmapCell[] = [];
|
|
156
|
-
for ( let row = 0; row < 7; row++ ) {
|
|
157
|
-
const day = addDays( gridStart, week * 7 + row );
|
|
158
|
-
const key = format( day, 'yyyy-MM-dd' );
|
|
159
|
-
const cell: HeatmapCell = {
|
|
160
|
-
label: format( day, 'EEE, MMM d, yyyy' ),
|
|
161
|
-
value: valueByDay.has( key ) ? ( valueByDay.get( key ) as number | null ) : null,
|
|
162
|
-
};
|
|
163
|
-
if ( key < gridMinDayKey || key > gridMaxDayKey ) {
|
|
164
|
-
// The days completing the grid's first/last week: the calendar's
|
|
165
|
-
// ragged edge.
|
|
166
|
-
if ( hideOutOfRangeDays ) {
|
|
167
|
-
cell.hidden = true;
|
|
168
|
-
}
|
|
169
|
-
} else if ( key < minDayKey || key > maxDayKey ) {
|
|
170
|
-
// Inside the grid the caller asked for, outside the series: filler.
|
|
171
|
-
cell.placeholder = true;
|
|
172
|
-
}
|
|
173
|
-
cells.push( cell );
|
|
174
|
-
}
|
|
175
|
-
data.push( { label, data: cells } );
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return { data, rowLabels };
|
|
179
|
-
};
|