This commit is contained in:
Albert Juhé Lluveras 2019-04-05 11:01:12 +02:00 committed by GitHub
parent 753780bf7b
commit dbf0a8d169
5 changed files with 370 additions and 348 deletions

View File

@ -0,0 +1,226 @@
/** @format */
/**
* External dependencies
*/
import { axisBottom as d3AxisBottom } from 'd3-axis';
import { smallBreak, wideBreak } from './breakpoints';
import moment from 'moment';
const dayTicksThreshold = 63;
const weekTicksThreshold = 9;
const mediumBreak = 1130;
const smallPoints = 7;
const mediumPoints = 12;
const largePoints = 16;
const mostPoints = 31;
/**
* Calculate the maximum number of ticks allowed in the x-axis based on the width and mode of the chart
* @param {integer} width - calculated page width
* @param {string} mode - item-comparison or time-comparison
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
const calculateMaxXTicks = ( width, mode ) => {
if ( width < smallBreak ) {
return smallPoints;
} else if ( width >= smallBreak && width <= mediumBreak ) {
return mediumPoints;
} else if ( width > mediumBreak && width <= wideBreak ) {
if ( mode === 'time-comparison' ) {
return largePoints;
} else if ( mode === 'item-comparison' ) {
return mediumPoints;
}
} else if ( width > wideBreak ) {
if ( mode === 'time-comparison' ) {
return mostPoints;
} else if ( mode === 'item-comparison' ) {
return largePoints;
}
}
return largePoints;
};
/**
* Filter out irrelevant dates so only the first date of each month is kept.
* @param {array} dates - string dates.
* @returns {array} Filtered dates.
*/
const getFirstDatePerMonth = dates => {
return dates.filter(
( date, i ) => i === 0 || moment( date ).toDate().getMonth() !== moment( dates[ i - 1 ] ).toDate().getMonth()
);
};
/**
* Given an array of dates, returns true if the first and last one belong to the same day.
* @param {array} dates - an array of dates
* @returns {boolean} whether the first and last date are different hours from the same date.
*/
const areDatesInTheSameDay = dates => {
const firstDate = moment( dates [ 0 ] ).toDate();
const lastDate = moment( dates [ dates.length - 1 ] ).toDate();
return (
firstDate.getDate() === lastDate.getDate() &&
firstDate.getMonth() === lastDate.getMonth() &&
firstDate.getFullYear() === lastDate.getFullYear()
);
};
/**
* Describes `smallestFactor`
* @param {number} inputNum - any double or integer
* @returns {integer} smallest factor of num
*/
const getFactors = inputNum => {
const numFactors = [];
for ( let i = 1; i <= Math.floor( Math.sqrt( inputNum ) ); i++ ) {
if ( inputNum % i === 0 ) {
numFactors.push( i );
inputNum / i !== i && numFactors.push( inputNum / i );
}
}
numFactors.sort( ( x, y ) => x - y ); // numeric sort
return numFactors;
};
/**
* Calculates the increment factor between ticks so there aren't more than maxTicks.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} maxTicks - maximum number of ticks that can be displayed in the x-axis
* @returns {integer} x-axis ticks increment factor
*/
const calculateXTicksIncrementFactor = ( uniqueDates, maxTicks ) => {
let factors = [];
let i = 1;
// First we get all the factors of the length of the uniqueDates array
// if the number is a prime number or near prime (with 3 factors) then we
// step down by 1 integer and try again.
while ( factors.length <= 3 ) {
factors = getFactors( uniqueDates.length - i );
i += 1;
}
return factors.find( f => uniqueDates.length / f < maxTicks );
};
/**
* Get x-axis ticks given the unique dates and the increment factor.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} incrementFactor - increment factor for the visible ticks.
* @returns {array} Ticks for the x-axis.
*/
const getXTicksFromIncrementFactor = ( uniqueDates, incrementFactor ) => {
const ticks = [];
for ( let idx = 0; idx < uniqueDates.length; idx = idx + incrementFactor ) {
ticks.push( uniqueDates[ idx ] );
}
// If the first date is missing from the ticks array, add it back in.
if ( ticks[ 0 ] !== uniqueDates[ 0 ] ) {
ticks.unshift( uniqueDates[ 0 ] );
}
return ticks;
};
/**
* Returns ticks for the x-axis.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} width - calculated page width
* @param {string} mode - item-comparison or time-comparison
* @param {string} interval - string of the interval used in the graph (hour, day, week...)
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
export const getXTicks = ( uniqueDates, width, mode, interval ) => {
const maxTicks = calculateMaxXTicks( width, mode );
if (
( uniqueDates.length >= dayTicksThreshold && interval === 'day' ) ||
( uniqueDates.length >= weekTicksThreshold && interval === 'week' )
) {
uniqueDates = getFirstDatePerMonth( uniqueDates );
}
if ( uniqueDates.length <= maxTicks ||
( interval === 'hour' && areDatesInTheSameDay( uniqueDates ) && width > smallBreak ) ) {
return uniqueDates;
}
const incrementFactor = calculateXTicksIncrementFactor( uniqueDates, maxTicks );
return getXTicksFromIncrementFactor( uniqueDates, incrementFactor );
};
/**
* Compares 2 strings and returns a list of words that are unique from s2
* @param {string} s1 - base string to compare against
* @param {string} s2 - string to compare against the base string
* @param {string|Object} splitChar - character or RegExp to use to deliminate words
* @returns {array} of unique words that appear in s2 but not in s1, the base string
*/
export const compareStrings = ( s1, s2, splitChar = new RegExp( [ ' |,' ], 'g' ) ) => {
const string1 = s1.split( splitChar );
const string2 = s2.split( splitChar );
const diff = new Array();
const long = s1.length > s2.length ? string1 : string2;
for ( let x = 0; x < long.length; x++ ) {
string1[ x ] !== string2[ x ] && diff.push( string2[ x ] );
}
return diff;
};
const removeDuplicateDates = ( d, i, ticks, formatter ) => {
const monthDate = moment( d ).toDate();
let prevMonth = i !== 0 ? ticks[ i - 1 ] : ticks[ i ];
prevMonth = prevMonth instanceof Date ? prevMonth : moment( prevMonth ).toDate();
return i === 0
? formatter( monthDate )
: compareStrings( formatter( prevMonth ), formatter( monthDate ) ).join( ' ' );
};
export const drawXAxis = ( node, params, scales, formats ) => {
const height = scales.yScale.range()[ 0 ];
let ticks = getXTicks( params.uniqueDates, scales.xScale.range()[ 1 ], params.mode, params.interval );
if ( params.chartType === 'line' ) {
ticks = ticks.map( d => moment( d ).toDate() );
}
node
.append( 'g' )
.attr( 'class', 'axis' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', `translate(0, ${ height })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickFormat( ( d, i ) => params.interval === 'hour'
? formats.xFormat( d instanceof Date ? d : moment( d ).toDate() )
: removeDuplicateDates( d, i, ticks, formats.xFormat ) )
);
node
.append( 'g' )
.attr( 'class', 'axis axis-month' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', `translate(0, ${ height + 14 })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickFormat( ( d, i ) => removeDuplicateDates( d, i, ticks, formats.x2Format ) )
);
node
.append( 'g' )
.attr( 'class', 'pipes' )
.attr( 'transform', `translate(0, ${ height })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickSize( 5 )
.tickFormat( '' )
);
};

View File

@ -0,0 +1,76 @@
/** @format */
/**
* External dependencies
*/
import { axisLeft as d3AxisLeft } from 'd3-axis';
const calculateYGridValues = ( numberOfTicks, limit, roundValues ) => {
const grids = [];
for ( let i = 0; i < numberOfTicks; i++ ) {
const val = ( i + 1 ) / numberOfTicks * limit;
const rVal = roundValues ? Math.round( val ) : val;
if ( grids[ grids.length - 1 ] !== rVal ) {
grids.push( rVal );
}
}
return grids;
};
const getNegativeYGrids = ( yMin, step ) => {
if ( yMin >= 0 ) {
return [];
}
const numberOfTicks = Math.ceil( -yMin / step );
return calculateYGridValues( numberOfTicks, yMin, yMin < -1 );
};
const getPositiveYGrids = ( yMax, step ) => {
if ( yMax <= 0 ) {
return [];
}
const numberOfTicks = Math.ceil( yMax / step );
return calculateYGridValues( numberOfTicks, yMax, yMax > 1 );
};
export const getYGrids = ( yMin, yMax, step ) => {
return [
0,
...getNegativeYGrids( yMin, step ),
...getPositiveYGrids( yMax, step ),
];
};
export const drawYAxis = ( node, scales, formats, margin, isRTL ) => {
const yGrids = getYGrids( scales.yScale.domain()[ 0 ], scales.yScale.domain()[ 1 ], scales.step );
const width = scales.xScale.range()[ 1 ];
const xPosition = isRTL ? width + margin.left + margin.right / 2 - 15 : -margin.left / 2 - 15;
const withPositiveValuesClass = scales.yMin >= 0 || scales.yMax > 0 ? ' with-positive-ticks' : '';
node
.append( 'g' )
.attr( 'class', 'grid' + withPositiveValuesClass )
.attr( 'transform', `translate(-${ margin.left }, 0)` )
.call(
d3AxisLeft( scales.yScale )
.tickValues( yGrids )
.tickSize( -width - margin.left - margin.right )
.tickFormat( '' )
);
node
.append( 'g' )
.attr( 'class', 'axis y-axis' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', 'translate(' + xPosition + ', 12)' )
.attr( 'text-anchor', 'start' )
.call(
d3AxisLeft( scales.yScale )
.tickValues( scales.yMax === 0 && scales.yMin === 0 ? [ yGrids[ 0 ] ] : yGrids )
.tickFormat( d => formats.yFormat( d !== 0 ? d : 0 ) )
);
};

View File

@ -3,297 +3,8 @@
/** /**
* External dependencies * External dependencies
*/ */
import { axisBottom as d3AxisBottom, axisLeft as d3AxisLeft } from 'd3-axis'; import { drawXAxis } from './axis-x';
import { smallBreak, wideBreak } from './breakpoints'; import { drawYAxis } from './axis-y';
import moment from 'moment';
const dayTicksThreshold = 63;
const weekTicksThreshold = 9;
const mediumBreak = 1130;
const smallPoints = 7;
const mediumPoints = 12;
const largePoints = 16;
const mostPoints = 31;
/**
* Describes `smallestFactor`
* @param {number} inputNum - any double or integer
* @returns {integer} smallest factor of num
*/
const getFactors = inputNum => {
const numFactors = [];
for ( let i = 1; i <= Math.floor( Math.sqrt( inputNum ) ); i++ ) {
if ( inputNum % i === 0 ) {
numFactors.push( i );
inputNum / i !== i && numFactors.push( inputNum / i );
}
}
numFactors.sort( ( x, y ) => x - y ); // numeric sort
return numFactors;
};
/**
* Calculate the maximum number of ticks allowed in the x-axis based on the width and mode of the chart
* @param {integer} width - calculated page width
* @param {string} mode - item-comparison or time-comparison
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
const calculateMaxXTicks = ( width, mode ) => {
if ( width < smallBreak ) {
return smallPoints;
} else if ( width >= smallBreak && width <= mediumBreak ) {
return mediumPoints;
} else if ( width > mediumBreak && width <= wideBreak ) {
if ( mode === 'time-comparison' ) {
return largePoints;
} else if ( mode === 'item-comparison' ) {
return mediumPoints;
}
} else if ( width > wideBreak ) {
if ( mode === 'time-comparison' ) {
return mostPoints;
} else if ( mode === 'item-comparison' ) {
return largePoints;
}
}
return largePoints;
};
/**
* Get x-axis ticks given the unique dates and the increment factor.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} incrementFactor - increment factor for the visible ticks.
* @returns {array} Ticks for the x-axis.
*/
const getXTicksFromIncrementFactor = ( uniqueDates, incrementFactor ) => {
const ticks = [];
for ( let idx = 0; idx < uniqueDates.length; idx = idx + incrementFactor ) {
ticks.push( uniqueDates[ idx ] );
}
// If the first date is missing from the ticks array, add it back in.
if ( ticks[ 0 ] !== uniqueDates[ 0 ] ) {
ticks.unshift( uniqueDates[ 0 ] );
}
return ticks;
};
/**
* Calculates the increment factor between ticks so there aren't more than maxTicks.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} maxTicks - maximum number of ticks that can be displayed in the x-axis
* @returns {integer} x-axis ticks increment factor
*/
const calculateXTicksIncrementFactor = ( uniqueDates, maxTicks ) => {
let factors = [];
let i = 1;
// First we get all the factors of the length of the uniqueDates array
// if the number is a prime number or near prime (with 3 factors) then we
// step down by 1 integer and try again.
while ( factors.length <= 3 ) {
factors = getFactors( uniqueDates.length - i );
i += 1;
}
return factors.find( f => uniqueDates.length / f < maxTicks );
};
/**
* Given an array of dates, returns true if the first and last one belong to the same day.
* @param {array} dates - an array of dates
* @returns {boolean} whether the first and last date are different hours from the same date.
*/
const areDatesInTheSameDay = dates => {
const firstDate = moment( dates [ 0 ] ).toDate();
const lastDate = moment( dates [ dates.length - 1 ] ).toDate();
return (
firstDate.getDate() === lastDate.getDate() &&
firstDate.getMonth() === lastDate.getMonth() &&
firstDate.getFullYear() === lastDate.getFullYear()
);
};
/**
* Filter out irrelevant dates so only the first date of each month is kept.
* @param {array} dates - string dates.
* @returns {array} Filtered dates.
*/
const getFirstDatePerMonth = dates => {
return dates.filter(
( date, i ) => i === 0 || moment( date ).toDate().getMonth() !== moment( dates[ i - 1 ] ).toDate().getMonth()
);
};
/**
* Returns ticks for the x-axis.
* @param {array} uniqueDates - all the unique dates from the input data for the chart
* @param {integer} width - calculated page width
* @param {string} mode - item-comparison or time-comparison
* @param {string} interval - string of the interval used in the graph (hour, day, week...)
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
export const getXTicks = ( uniqueDates, width, mode, interval ) => {
const maxTicks = calculateMaxXTicks( width, mode );
if (
( uniqueDates.length >= dayTicksThreshold && interval === 'day' ) ||
( uniqueDates.length >= weekTicksThreshold && interval === 'week' )
) {
uniqueDates = getFirstDatePerMonth( uniqueDates );
}
if ( uniqueDates.length <= maxTicks ||
( interval === 'hour' && areDatesInTheSameDay( uniqueDates ) && width > smallBreak ) ) {
return uniqueDates;
}
const incrementFactor = calculateXTicksIncrementFactor( uniqueDates, maxTicks );
return getXTicksFromIncrementFactor( uniqueDates, incrementFactor );
};
/**
* Compares 2 strings and returns a list of words that are unique from s2
* @param {string} s1 - base string to compare against
* @param {string} s2 - string to compare against the base string
* @param {string|Object} splitChar - character or RegExp to use to deliminate words
* @returns {array} of unique words that appear in s2 but not in s1, the base string
*/
export const compareStrings = ( s1, s2, splitChar = new RegExp( [ ' |,' ], 'g' ) ) => {
const string1 = s1.split( splitChar );
const string2 = s2.split( splitChar );
const diff = new Array();
const long = s1.length > s2.length ? string1 : string2;
for ( let x = 0; x < long.length; x++ ) {
string1[ x ] !== string2[ x ] && diff.push( string2[ x ] );
}
return diff;
};
const calculateYGridValues = ( numberOfTicks, limit, roundValues ) => {
const grids = [];
for ( let i = 0; i < numberOfTicks; i++ ) {
const val = ( i + 1 ) / numberOfTicks * limit;
const rVal = roundValues ? Math.round( val ) : val;
if ( grids[ grids.length - 1 ] !== rVal ) {
grids.push( rVal );
}
}
return grids;
};
const getNegativeYGrids = ( yMin, step ) => {
if ( yMin >= 0 ) {
return [];
}
const numberOfTicks = Math.ceil( -yMin / step );
return calculateYGridValues( numberOfTicks, yMin, yMin < -1 );
};
const getPositiveYGrids = ( yMax, step ) => {
if ( yMax <= 0 ) {
return [];
}
const numberOfTicks = Math.ceil( yMax / step );
return calculateYGridValues( numberOfTicks, yMax, yMax > 1 );
};
export const getYGrids = ( yMin, yMax, step ) => {
return [
0,
...getNegativeYGrids( yMin, step ),
...getPositiveYGrids( yMax, step ),
];
};
const removeDuplicateDates = ( d, i, ticks, formatter ) => {
const monthDate = moment( d ).toDate();
let prevMonth = i !== 0 ? ticks[ i - 1 ] : ticks[ i ];
prevMonth = prevMonth instanceof Date ? prevMonth : moment( prevMonth ).toDate();
return i === 0
? formatter( monthDate )
: compareStrings( formatter( prevMonth ), formatter( monthDate ) ).join( ' ' );
};
const drawXAxis = ( node, params, scales, formats ) => {
const height = scales.yScale.range()[ 0 ];
let ticks = getXTicks( params.uniqueDates, scales.xScale.range()[ 1 ], params.mode, params.interval );
if ( params.chartType === 'line' ) {
ticks = ticks.map( d => moment( d ).toDate() );
}
node
.append( 'g' )
.attr( 'class', 'axis' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', `translate(0, ${ height })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickFormat( ( d, i ) => params.interval === 'hour'
? formats.xFormat( d instanceof Date ? d : moment( d ).toDate() )
: removeDuplicateDates( d, i, ticks, formats.xFormat ) )
);
node
.append( 'g' )
.attr( 'class', 'axis axis-month' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', `translate(0, ${ height + 14 })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickFormat( ( d, i ) => removeDuplicateDates( d, i, ticks, formats.x2Format ) )
);
node
.append( 'g' )
.attr( 'class', 'pipes' )
.attr( 'transform', `translate(0, ${ height })` )
.call(
d3AxisBottom( scales.xScale )
.tickValues( ticks )
.tickSize( 5 )
.tickFormat( '' )
);
};
const drawYAxis = ( node, scales, formats, margin, isRTL ) => {
const yGrids = getYGrids( scales.yScale.domain()[ 0 ], scales.yScale.domain()[ 1 ], scales.step );
const width = scales.xScale.range()[ 1 ];
const xPosition = isRTL ? width + margin.left + margin.right / 2 - 15 : -margin.left / 2 - 15;
const withPositiveValuesClass = scales.yMin >= 0 || scales.yMax > 0 ? ' with-positive-ticks' : '';
node
.append( 'g' )
.attr( 'class', 'grid' + withPositiveValuesClass )
.attr( 'transform', `translate(-${ margin.left }, 0)` )
.call(
d3AxisLeft( scales.yScale )
.tickValues( yGrids )
.tickSize( -width - margin.left - margin.right )
.tickFormat( '' )
);
node
.append( 'g' )
.attr( 'class', 'axis y-axis' )
.attr( 'aria-hidden', 'true' )
.attr( 'transform', 'translate(' + xPosition + ', 12)' )
.attr( 'text-anchor', 'start' )
.call(
d3AxisLeft( scales.yScale )
.tickValues( scales.yMax === 0 && scales.yMin === 0 ? [ yGrids[ 0 ] ] : yGrids )
.tickFormat( d => formats.yFormat( d !== 0 ? d : 0 ) )
);
};
export const drawAxis = ( node, params, scales, formats, margin, isRTL ) => { export const drawAxis = ( node, params, scales, formats, margin, isRTL ) => {
drawXAxis( node, params, scales, formats ); drawXAxis( node, params, scales, formats );

View File

@ -6,7 +6,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { compareStrings, getXTicks, getYGrids } from '../axis'; import { compareStrings, getXTicks } from '../axis-x';
describe( 'getXTicks', () => { describe( 'getXTicks', () => {
describe( 'interval=day', () => { describe( 'interval=day', () => {
@ -238,59 +238,3 @@ describe( 'compareStrings', () => {
expect( compareStrings( 'Jul, 2018', 'Aug, 2018' ).join( ' ' ) ).toEqual( 'Aug' ); expect( compareStrings( 'Jul, 2018', 'Aug, 2018' ).join( ' ' ) ).toEqual( 'Aug' );
} ); } );
} ); } );
describe( 'getYGrids', () => {
it( 'returns a single 0 when yMax and yMin are 0', () => {
expect( getYGrids( 0, 0, 0 ) ).toEqual( [ 0 ] );
} );
describe( 'positive charts', () => {
it( 'returns decimal values when yMax is <= 1 and yMin is 0', () => {
expect( getYGrids( 0, 1, 0.3333333333333333 ) ).toEqual( [ 0, 0.3333333333333333, 0.6666666666666666, 1 ] );
} );
it( 'returns decimal values when yMax and yMin are <= 1', () => {
expect( getYGrids( 1, 1, 0.3333333333333333 ) ).toEqual( [ 0, 0.3333333333333333, 0.6666666666666666, 1 ] );
} );
it( 'doesn\'t return decimal values when yMax is > 1', () => {
expect( getYGrids( 0, 2, 1 ) ).toEqual( [ 0, 1, 2 ] );
} );
it( 'returns up to four values when yMax is a big number', () => {
expect( getYGrids( 0, 12000, 4000 ) ).toEqual( [ 0, 4000, 8000, 12000 ] );
} );
} );
describe( 'negative charts', () => {
it( 'returns decimal values when yMin is >= -1 and yMax is 0', () => {
expect( getYGrids( -1, 0, 0.3333333333333333 ) ).toEqual( [ 0, -0.3333333333333333, -0.6666666666666666, -1 ] );
} );
it( 'returns decimal values when yMax and yMin are >= -1', () => {
expect( getYGrids( -1, -1, 0.3333333333333333 ) ).toEqual( [ 0, -0.3333333333333333, -0.6666666666666666, -1 ] );
} );
it( 'doesn\'t return decimal values when yMin is < -1', () => {
expect( getYGrids( -2, 0, 1 ) ).toEqual( [ 0, -1, -2 ] );
} );
it( 'returns up to four values when yMin is a big negative number', () => {
expect( getYGrids( -12000, 0, 4000 ) ).toEqual( [ 0, -4000, -8000, -12000 ] );
} );
} );
describe( 'positive & negative charts', () => {
it( 'returns decimal values when yMax is <= 1 and yMin is 0', () => {
expect( getYGrids( -1, 1, 0.5 ) ).toEqual( [ 0, -0.5, -1, 0.5, 1 ] );
} );
it( 'doesn\'t return decimal values when yMax is > 1', () => {
expect( getYGrids( -2, 2, 1 ) ).toEqual( [ 0, -1, -2, 1, 2 ] );
} );
it( 'returns up to six values when yMax is a big number', () => {
expect( getYGrids( -12000, 12000, 6000 ) ).toEqual( [ 0, -6000, -12000, 6000, 12000 ] );
} );
} );
} );

View File

@ -0,0 +1,65 @@
/** @format */
/**
* External dependencies
*/
/**
* Internal dependencies
*/
import { getYGrids } from '../axis-y';
describe( 'getYGrids', () => {
it( 'returns a single 0 when yMax and yMin are 0', () => {
expect( getYGrids( 0, 0, 0 ) ).toEqual( [ 0 ] );
} );
describe( 'positive charts', () => {
it( 'returns decimal values when yMax is <= 1 and yMin is 0', () => {
expect( getYGrids( 0, 1, 0.3333333333333333 ) ).toEqual( [ 0, 0.3333333333333333, 0.6666666666666666, 1 ] );
} );
it( 'returns decimal values when yMax and yMin are <= 1', () => {
expect( getYGrids( 1, 1, 0.3333333333333333 ) ).toEqual( [ 0, 0.3333333333333333, 0.6666666666666666, 1 ] );
} );
it( 'doesn\'t return decimal values when yMax is > 1', () => {
expect( getYGrids( 0, 2, 1 ) ).toEqual( [ 0, 1, 2 ] );
} );
it( 'returns up to four values when yMax is a big number', () => {
expect( getYGrids( 0, 12000, 4000 ) ).toEqual( [ 0, 4000, 8000, 12000 ] );
} );
} );
describe( 'negative charts', () => {
it( 'returns decimal values when yMin is >= -1 and yMax is 0', () => {
expect( getYGrids( -1, 0, 0.3333333333333333 ) ).toEqual( [ 0, -0.3333333333333333, -0.6666666666666666, -1 ] );
} );
it( 'returns decimal values when yMax and yMin are >= -1', () => {
expect( getYGrids( -1, -1, 0.3333333333333333 ) ).toEqual( [ 0, -0.3333333333333333, -0.6666666666666666, -1 ] );
} );
it( 'doesn\'t return decimal values when yMin is < -1', () => {
expect( getYGrids( -2, 0, 1 ) ).toEqual( [ 0, -1, -2 ] );
} );
it( 'returns up to four values when yMin is a big negative number', () => {
expect( getYGrids( -12000, 0, 4000 ) ).toEqual( [ 0, -4000, -8000, -12000 ] );
} );
} );
describe( 'positive & negative charts', () => {
it( 'returns decimal values when yMax is <= 1 and yMin is 0', () => {
expect( getYGrids( -1, 1, 0.5 ) ).toEqual( [ 0, -0.5, -1, 0.5, 1 ] );
} );
it( 'doesn\'t return decimal values when yMax is > 1', () => {
expect( getYGrids( -2, 2, 1 ) ).toEqual( [ 0, -1, -2, 1, 2 ] );
} );
it( 'returns up to six values when yMax is a big number', () => {
expect( getYGrids( -12000, 12000, 6000 ) ).toEqual( [ 0, -6000, -12000, 6000, 12000 ] );
} );
} );
} );