Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Show ticks in year format when the interval is more than a year.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { scaleTime } from '@visx/scale';
import { XYChart, AreaSeries, Grid, Axis, DataContext } from '@visx/xychart';
import { __ } from '@wordpress/i18n';
import clsx from 'clsx';
import { differenceInHours } from 'date-fns';
import { differenceInHours, differenceInYears } from 'date-fns';
import { useMemo, useContext, forwardRef, useImperativeHandle, useState, useRef } from 'react';
import {
useXYChartTheme,
Expand Down Expand Up @@ -101,6 +101,13 @@ const renderDefaultTooltip = ( params: RenderTooltipParams< DataPointDate > ) =>
);
};

const formatYearTick = ( timestamp: number ) => {
const date = new Date( timestamp );
return date.toLocaleDateString( undefined, {
year: 'numeric',
} );
};

const formatDateTick = ( timestamp: number ) => {
const date = new Date( timestamp );
return date.toLocaleDateString( undefined, {
Expand All @@ -117,6 +124,23 @@ const formatHourTick = ( timestamp: number ) => {
} );
};

const getFormatter = ( sortedData: ReturnType< typeof useChartDataTransform > ) => {
const minX = Math.min( ...sortedData.map( datom => datom.data.at( 0 )?.date ) );
const maxX = Math.max( ...sortedData.map( datom => datom.data.at( -1 )?.date ) );

const diffInHours = Math.abs( differenceInHours( maxX, minX ) );
if ( diffInHours <= 24 ) {
return formatHourTick;
}

const diffInYears = Math.abs( differenceInYears( maxX, minX ) );
if ( diffInYears <= 1 ) {
return formatDateTick;
}

return formatYearTick;
};

const guessOptimalNumTicks = (
data: ReturnType< typeof useChartDataTransform >,
chartWidth: number,
Expand Down Expand Up @@ -146,8 +170,13 @@ const guessOptimalNumTicks = (
return 1;
}

const hasDuplicate = uniqueTicks.length < ticks.length;
if ( hasDuplicate ) {
// Example: OCT 1 JAN 1 APR 1 JUL 1 OCT 1
// Here, the two OCTs are not duplicates as they represent October of two different years.
const hasConsecutiveDuplicate = ticks.some(
( tick, idx ) => idx > 0 && tick === ticks[ idx - 1 ]
);

if ( hasConsecutiveDuplicate ) {
continue;
}

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

const chartOptions = useMemo( () => {
const minX = Math.min( ...dataSorted.map( datom => datom.data.at( 0 )?.date ) );
const maxX = Math.max( ...dataSorted.map( datom => datom.data.at( -1 )?.date ) );
const diffInHours = Math.abs( differenceInHours( maxX, minX ) );

// Show the difference in hours if less than 24 hours; otherwise, display the date.
const formatter = diffInHours <= 24 ? formatHourTick : formatDateTick;
const formatter = getFormatter( dataSorted );

return {
axis: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,48 @@ describe( 'LineChart', () => {
expect( ticks.length ).toBeGreaterThan( 1 );
} );

test( 'renders ticks in short date format.', () => {
renderWithTheme( {
width: 800,
data: [
{
label: 'Series A',
data: [
{ date: new Date( '2024-01-01' ), value: 10 },
{ date: new Date( '2024-04-01' ), value: 20 },
{ date: new Date( '2024-07-01' ), value: 30 },
{ date: new Date( '2024-10-01' ), value: 40 },
{ date: new Date( '2025-03-01' ), value: 50 },
],
},
],
} );

const ticks = screen.getAllByText(
/^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d+$/
);
expect( ticks.length ).toBeGreaterThan( 1 );
} );

test( 'renders ticks in year format.', () => {
renderWithTheme( {
width: 800,
data: [
{
label: 'Series A',
data: [
{ date: new Date( '2023-01-01' ), value: 10 },
{ date: new Date( '2024-01-01' ), value: 10 },
{ date: new Date( '2025-01-01' ), value: 50 },
],
},
],
} );

const ticks = screen.getAllByText( /^202\d$/ );
expect( ticks.length ).toBeGreaterThan( 1 );
} );

test( 'renders optimal number of ticks.', () => {
renderWithTheme( {
width: 400,
Expand Down
Loading