Skip to content

Commit c644dc8

Browse files
authored
WOOA7S-534: Show ticks for year. (#45529)
* WOOA7S: Show tick format for year. * changelog
1 parent 4ac0d80 commit c644dc8

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Show ticks in year format when the interval is more than a year.

projects/js-packages/charts/src/components/line-chart/line-chart.tsx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { scaleTime } from '@visx/scale';
55
import { XYChart, AreaSeries, Grid, Axis, DataContext } from '@visx/xychart';
66
import { __ } from '@wordpress/i18n';
77
import clsx from 'clsx';
8-
import { differenceInHours } from 'date-fns';
8+
import { differenceInHours, differenceInYears } from 'date-fns';
99
import { useMemo, useContext, forwardRef, useImperativeHandle, useState, useRef } from 'react';
1010
import {
1111
useXYChartTheme,
@@ -101,6 +101,13 @@ const renderDefaultTooltip = ( params: RenderTooltipParams< DataPointDate > ) =>
101101
);
102102
};
103103

104+
const formatYearTick = ( timestamp: number ) => {
105+
const date = new Date( timestamp );
106+
return date.toLocaleDateString( undefined, {
107+
year: 'numeric',
108+
} );
109+
};
110+
104111
const formatDateTick = ( timestamp: number ) => {
105112
const date = new Date( timestamp );
106113
return date.toLocaleDateString( undefined, {
@@ -117,6 +124,23 @@ const formatHourTick = ( timestamp: number ) => {
117124
} );
118125
};
119126

127+
const getFormatter = ( sortedData: ReturnType< typeof useChartDataTransform > ) => {
128+
const minX = Math.min( ...sortedData.map( datom => datom.data.at( 0 )?.date ) );
129+
const maxX = Math.max( ...sortedData.map( datom => datom.data.at( -1 )?.date ) );
130+
131+
const diffInHours = Math.abs( differenceInHours( maxX, minX ) );
132+
if ( diffInHours <= 24 ) {
133+
return formatHourTick;
134+
}
135+
136+
const diffInYears = Math.abs( differenceInYears( maxX, minX ) );
137+
if ( diffInYears <= 1 ) {
138+
return formatDateTick;
139+
}
140+
141+
return formatYearTick;
142+
};
143+
120144
const guessOptimalNumTicks = (
121145
data: ReturnType< typeof useChartDataTransform >,
122146
chartWidth: number,
@@ -146,8 +170,13 @@ const guessOptimalNumTicks = (
146170
return 1;
147171
}
148172

149-
const hasDuplicate = uniqueTicks.length < ticks.length;
150-
if ( hasDuplicate ) {
173+
// Example: OCT 1 JAN 1 APR 1 JUL 1 OCT 1
174+
// Here, the two OCTs are not duplicates as they represent October of two different years.
175+
const hasConsecutiveDuplicate = ticks.some(
176+
( tick, idx ) => idx > 0 && tick === ticks[ idx - 1 ]
177+
);
178+
179+
if ( hasConsecutiveDuplicate ) {
151180
continue;
152181
}
153182

@@ -278,12 +307,7 @@ const LineChartInternal = forwardRef< SingleChartRef, LineChartProps >(
278307
} );
279308

280309
const chartOptions = useMemo( () => {
281-
const minX = Math.min( ...dataSorted.map( datom => datom.data.at( 0 )?.date ) );
282-
const maxX = Math.max( ...dataSorted.map( datom => datom.data.at( -1 )?.date ) );
283-
const diffInHours = Math.abs( differenceInHours( maxX, minX ) );
284-
285-
// Show the difference in hours if less than 24 hours; otherwise, display the date.
286-
const formatter = diffInHours <= 24 ? formatHourTick : formatDateTick;
310+
const formatter = getFormatter( dataSorted );
287311

288312
return {
289313
axis: {

projects/js-packages/charts/src/components/line-chart/test/line-chart.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,48 @@ describe( 'LineChart', () => {
388388
expect( ticks.length ).toBeGreaterThan( 1 );
389389
} );
390390

391+
test( 'renders ticks in short date format.', () => {
392+
renderWithTheme( {
393+
width: 800,
394+
data: [
395+
{
396+
label: 'Series A',
397+
data: [
398+
{ date: new Date( '2024-01-01' ), value: 10 },
399+
{ date: new Date( '2024-04-01' ), value: 20 },
400+
{ date: new Date( '2024-07-01' ), value: 30 },
401+
{ date: new Date( '2024-10-01' ), value: 40 },
402+
{ date: new Date( '2025-03-01' ), value: 50 },
403+
],
404+
},
405+
],
406+
} );
407+
408+
const ticks = screen.getAllByText(
409+
/^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d+$/
410+
);
411+
expect( ticks.length ).toBeGreaterThan( 1 );
412+
} );
413+
414+
test( 'renders ticks in year format.', () => {
415+
renderWithTheme( {
416+
width: 800,
417+
data: [
418+
{
419+
label: 'Series A',
420+
data: [
421+
{ date: new Date( '2023-01-01' ), value: 10 },
422+
{ date: new Date( '2024-01-01' ), value: 10 },
423+
{ date: new Date( '2025-01-01' ), value: 50 },
424+
],
425+
},
426+
],
427+
} );
428+
429+
const ticks = screen.getAllByText( /^202\d$/ );
430+
expect( ticks.length ).toBeGreaterThan( 1 );
431+
} );
432+
391433
test( 'renders optimal number of ticks.', () => {
392434
renderWithTheme( {
393435
width: 400,

0 commit comments

Comments
 (0)