@automattic/charts 3.1.0 → 3.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 +5 -0
- package/dist/index.cjs +15 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/area-chart/area-chart.tsx +2 -2
- package/src/charts/area-chart/test/area-chart.test.tsx +45 -0
- package/src/charts/bar-chart/private/use-bar-chart-options.ts +19 -12
- package/src/charts/bar-chart/test/bar-chart.test.tsx +22 -0
- package/src/charts/line-chart/line-chart.tsx +2 -2
- package/src/charts/line-chart/test/line-chart.test.tsx +51 -0
package/package.json
CHANGED
|
@@ -205,8 +205,8 @@ const AreaChartInternal = forwardRef< ChartInstanceRef, AreaChartProps >(
|
|
|
205
205
|
}, [ dataSorted, stacked, stackOffset, rescaleYOnVisibility ] );
|
|
206
206
|
|
|
207
207
|
const chartOptions = useMemo( () => {
|
|
208
|
-
const { tickResolution, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
209
|
-
const formatter =
|
|
208
|
+
const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
209
|
+
const formatter = tickFormat || getFormatter( dataSorted, tickResolution );
|
|
210
210
|
|
|
211
211
|
return {
|
|
212
212
|
axis: {
|
|
@@ -112,6 +112,51 @@ describe( 'AreaChart', () => {
|
|
|
112
112
|
} );
|
|
113
113
|
} );
|
|
114
114
|
|
|
115
|
+
describe( 'X-Axis Ticks', () => {
|
|
116
|
+
const monthlySeries = [
|
|
117
|
+
{
|
|
118
|
+
label: 'Series A',
|
|
119
|
+
data: [
|
|
120
|
+
{ date: new Date( '2024-01-01' ), value: 10 },
|
|
121
|
+
{ date: new Date( '2024-04-01' ), value: 20 },
|
|
122
|
+
{ date: new Date( '2024-07-01' ), value: 30 },
|
|
123
|
+
{ date: new Date( '2024-10-01' ), value: 40 },
|
|
124
|
+
{ date: new Date( '2025-03-01' ), value: 50 },
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
test( 'keeps the derived month formatter when tickFormat is passed as undefined', () => {
|
|
130
|
+
renderWithProvider( {
|
|
131
|
+
options: { axis: { x: { tickFormat: undefined } } },
|
|
132
|
+
data: monthlySeries,
|
|
133
|
+
} );
|
|
134
|
+
|
|
135
|
+
// January is absent: formatMonthOrYearTick renders it as the year instead.
|
|
136
|
+
const ticks = screen.getAllByText( /^(Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/ );
|
|
137
|
+
expect( ticks.length ).toBeGreaterThan( 1 );
|
|
138
|
+
expect(
|
|
139
|
+
screen.queryByText( /^(February|March|April|June|July|August|September|October)$/ )
|
|
140
|
+
).not.toBeInTheDocument();
|
|
141
|
+
} );
|
|
142
|
+
|
|
143
|
+
test( 'honors an explicit tickFormat over the derived formatter', () => {
|
|
144
|
+
renderWithProvider( {
|
|
145
|
+
options: {
|
|
146
|
+
axis: {
|
|
147
|
+
x: {
|
|
148
|
+
tickFormat: ( date: Date | number ) =>
|
|
149
|
+
`tick-${ new Date( Number( date ) ).getUTCMonth() }`,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
data: monthlySeries,
|
|
154
|
+
} );
|
|
155
|
+
|
|
156
|
+
expect( screen.getAllByText( /^tick-\d+$/ ).length ).toBeGreaterThan( 0 );
|
|
157
|
+
} );
|
|
158
|
+
} );
|
|
159
|
+
|
|
115
160
|
describe( 'Stacking', () => {
|
|
116
161
|
test( 'is stacked by default', () => {
|
|
117
162
|
renderWithProvider();
|
|
@@ -154,23 +154,29 @@ export function useBarChartOptions(
|
|
|
154
154
|
const stableOptions = useDeepMemo( options );
|
|
155
155
|
|
|
156
156
|
// `labelOverflow` and `tickResolution` are consumed by this hook rather than
|
|
157
|
-
// forwarded — visx has an axis prop for neither —
|
|
158
|
-
//
|
|
157
|
+
// forwarded — visx has an axis prop for neither — and `tickFormat` is merged
|
|
158
|
+
// with the derived default explicitly, so an `undefined` passed by a caller
|
|
159
|
+
// cannot clobber it through the spread. They are split off the caller's axis
|
|
160
|
+
// options once, here, and only the rest reaches visx below.
|
|
159
161
|
const axisConfig = useMemo( () => {
|
|
160
162
|
const {
|
|
161
163
|
labelOverflow: xLabelOverflow,
|
|
162
164
|
tickResolution: xTickResolution,
|
|
165
|
+
tickFormat: xTickFormat,
|
|
163
166
|
...xAxisOptions
|
|
164
167
|
} = stableOptions.axis?.x || {};
|
|
165
168
|
const {
|
|
166
169
|
labelOverflow: yLabelOverflow,
|
|
167
170
|
tickResolution: yTickResolution,
|
|
171
|
+
tickFormat: yTickFormat,
|
|
168
172
|
...yAxisOptions
|
|
169
173
|
} = stableOptions.axis?.y || {};
|
|
170
174
|
|
|
171
175
|
return {
|
|
172
176
|
xLabelOverflow,
|
|
173
177
|
yLabelOverflow,
|
|
178
|
+
xTickFormat,
|
|
179
|
+
yTickFormat,
|
|
174
180
|
xAxisOptions,
|
|
175
181
|
yAxisOptions,
|
|
176
182
|
// The dates sit on the x axis normally, and on the y axis when the
|
|
@@ -243,8 +249,8 @@ export function useBarChartOptions(
|
|
|
243
249
|
return useMemo( () => {
|
|
244
250
|
const orientationKey = horizontal ? 'horizontal' : 'vertical';
|
|
245
251
|
const {
|
|
246
|
-
xTickFormat,
|
|
247
|
-
yTickFormat,
|
|
252
|
+
xTickFormat: defaultXTickFormat,
|
|
253
|
+
yTickFormat: defaultYTickFormat,
|
|
248
254
|
tooltipLabelFormatter: defaultTooltipLabelFormatter,
|
|
249
255
|
xAccessor,
|
|
250
256
|
yAccessor,
|
|
@@ -294,10 +300,11 @@ export function useBarChartOptions(
|
|
|
294
300
|
...( stableOptions.yScale || {} ),
|
|
295
301
|
...( ! horizontal ? valueScaleDomainOverride : {} ),
|
|
296
302
|
};
|
|
297
|
-
const { xLabelOverflow, yLabelOverflow, xAxisOptions, yAxisOptions } =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
const { xLabelOverflow, yLabelOverflow, xTickFormat, yTickFormat, xAxisOptions, yAxisOptions } =
|
|
304
|
+
axisConfig;
|
|
305
|
+
// The dates sit on the y axis of a horizontal chart, so the caller's format
|
|
306
|
+
// for them moves with the orientation. It also labels the tooltip.
|
|
307
|
+
const dateAxisTickFormat = horizontal ? yTickFormat : xTickFormat;
|
|
301
308
|
|
|
302
309
|
// A band scale has no ticks of its own for visx to ask for, so it samples
|
|
303
310
|
// the domain by index and can miss the tick that dates the day or names the
|
|
@@ -306,7 +313,7 @@ export function useBarChartOptions(
|
|
|
306
313
|
const { timeAxis } = defaultOptions;
|
|
307
314
|
const dateAxisOptions = horizontal ? yAxisOptions : xAxisOptions;
|
|
308
315
|
const bandTickValues =
|
|
309
|
-
timeAxis && !
|
|
316
|
+
timeAxis && ! dateAxisTickFormat
|
|
310
317
|
? getBandTickValues(
|
|
311
318
|
timeAxis.domain,
|
|
312
319
|
timeAxis.tickFormatter,
|
|
@@ -327,7 +334,7 @@ export function useBarChartOptions(
|
|
|
327
334
|
x: {
|
|
328
335
|
orientation: 'bottom' as const,
|
|
329
336
|
numTicks: DEFAULT_NUM_TICKS,
|
|
330
|
-
tickFormat: xTickFormat,
|
|
337
|
+
tickFormat: xTickFormat || defaultXTickFormat,
|
|
331
338
|
...( horizontal ? {} : dateAxisTickValues ),
|
|
332
339
|
...( xLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedXTickComponent } : {} ),
|
|
333
340
|
...xAxisOptions,
|
|
@@ -335,7 +342,7 @@ export function useBarChartOptions(
|
|
|
335
342
|
y: {
|
|
336
343
|
orientation: 'left' as const,
|
|
337
344
|
numTicks: DEFAULT_NUM_TICKS,
|
|
338
|
-
tickFormat: yTickFormat,
|
|
345
|
+
tickFormat: yTickFormat || defaultYTickFormat,
|
|
339
346
|
...( horizontal ? dateAxisTickValues : {} ),
|
|
340
347
|
...( yLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedYTickComponent } : {} ),
|
|
341
348
|
...yAxisOptions,
|
|
@@ -345,7 +352,7 @@ export function useBarChartOptions(
|
|
|
345
352
|
padding: getGroupPadding( horizontal ? yScale : xScale ),
|
|
346
353
|
},
|
|
347
354
|
tooltip: {
|
|
348
|
-
labelFormatter:
|
|
355
|
+
labelFormatter: dateAxisTickFormat || defaultTooltipLabelFormatter,
|
|
349
356
|
},
|
|
350
357
|
};
|
|
351
358
|
}, [ defaultOptions, axisConfig, stableOptions, horizontal, data ] );
|
|
@@ -505,6 +505,28 @@ describe( 'BarChart', () => {
|
|
|
505
505
|
expect( distinctTexts( ticks ).size ).toBeGreaterThan( 1 );
|
|
506
506
|
} );
|
|
507
507
|
|
|
508
|
+
test( 'keeps the derived date formatter when tickFormat is passed as undefined', () => {
|
|
509
|
+
renderWithTheme( {
|
|
510
|
+
width: 800,
|
|
511
|
+
options: { axis: { x: { tickFormat: undefined } } },
|
|
512
|
+
data: [
|
|
513
|
+
{
|
|
514
|
+
label: 'Series A',
|
|
515
|
+
data: Array.from( { length: 14 }, ( _, i ) => ( {
|
|
516
|
+
date: new Date( 2024, 0, 1 + i ),
|
|
517
|
+
value: 10 + i,
|
|
518
|
+
} ) ),
|
|
519
|
+
options: {},
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
} );
|
|
523
|
+
|
|
524
|
+
const ticks = screen.getAllByText( /^Jan \d+$/ );
|
|
525
|
+
expect( distinctTexts( ticks ).size ).toBeGreaterThan( 1 );
|
|
526
|
+
// A clobbered formatter falls back to raw `Date.toString()` ticks.
|
|
527
|
+
expect( screen.queryByText( /GMT/ ) ).not.toBeInTheDocument();
|
|
528
|
+
} );
|
|
529
|
+
|
|
508
530
|
test( 'renders distinct hour ticks for sub-daily buckets in a single day', () => {
|
|
509
531
|
renderWithTheme( {
|
|
510
532
|
width: 800,
|
|
@@ -291,8 +291,8 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
291
291
|
} );
|
|
292
292
|
|
|
293
293
|
const chartOptions = useMemo( () => {
|
|
294
|
-
const { tickResolution, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
295
|
-
const formatter =
|
|
294
|
+
const { tickResolution, tickFormat, ...xAxisOptions } = options?.axis?.x ?? {};
|
|
295
|
+
const formatter = tickFormat || getFormatter( dataSorted, tickResolution );
|
|
296
296
|
|
|
297
297
|
return {
|
|
298
298
|
axis: {
|
|
@@ -500,6 +500,57 @@ describe( 'LineChart', () => {
|
|
|
500
500
|
expect( ticks.length ).toBeGreaterThan( 1 );
|
|
501
501
|
} );
|
|
502
502
|
|
|
503
|
+
test( 'keeps the derived month formatter when tickFormat is passed as undefined.', () => {
|
|
504
|
+
renderWithTheme( {
|
|
505
|
+
width: 800,
|
|
506
|
+
options: { axis: { x: { tickFormat: undefined } } },
|
|
507
|
+
data: [
|
|
508
|
+
{
|
|
509
|
+
label: 'Series A',
|
|
510
|
+
data: [
|
|
511
|
+
{ date: new Date( '2024-01-01' ), value: 10 },
|
|
512
|
+
{ date: new Date( '2024-04-01' ), value: 20 },
|
|
513
|
+
{ date: new Date( '2024-07-01' ), value: 30 },
|
|
514
|
+
{ date: new Date( '2024-10-01' ), value: 40 },
|
|
515
|
+
{ date: new Date( '2025-03-01' ), value: 50 },
|
|
516
|
+
],
|
|
517
|
+
},
|
|
518
|
+
],
|
|
519
|
+
} );
|
|
520
|
+
|
|
521
|
+
// January is absent: formatMonthOrYearTick renders it as the year instead.
|
|
522
|
+
const ticks = screen.getAllByText( /^(Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/ );
|
|
523
|
+
expect( ticks.length ).toBeGreaterThan( 1 );
|
|
524
|
+
expect(
|
|
525
|
+
screen.queryByText( /^(February|March|April|June|July|August|September|October)$/ )
|
|
526
|
+
).not.toBeInTheDocument();
|
|
527
|
+
} );
|
|
528
|
+
|
|
529
|
+
test( 'honors an explicit tickFormat over the derived formatter.', () => {
|
|
530
|
+
renderWithTheme( {
|
|
531
|
+
width: 800,
|
|
532
|
+
options: {
|
|
533
|
+
axis: {
|
|
534
|
+
x: {
|
|
535
|
+
tickFormat: ( date: Date | number ) =>
|
|
536
|
+
`tick-${ new Date( Number( date ) ).getUTCMonth() }`,
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
data: [
|
|
541
|
+
{
|
|
542
|
+
label: 'Series A',
|
|
543
|
+
data: [
|
|
544
|
+
{ date: new Date( '2024-01-01' ), value: 10 },
|
|
545
|
+
{ date: new Date( '2024-07-01' ), value: 30 },
|
|
546
|
+
],
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
} );
|
|
550
|
+
|
|
551
|
+
expect( screen.getAllByText( /^tick-\d+$/ ).length ).toBeGreaterThan( 0 );
|
|
552
|
+
} );
|
|
553
|
+
|
|
503
554
|
test( 'renders ticks in year format.', () => {
|
|
504
555
|
renderWithTheme( {
|
|
505
556
|
width: 800,
|