Merge branch 'add/chart-second-x' of github.com:woocommerce/wc-admin into add/chart-second-x
This commit is contained in:
commit
dbca60a660
|
@ -338,18 +338,16 @@ export class RevenueReport extends Component {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="">
|
<Chart
|
||||||
<Chart
|
data={ chartData }
|
||||||
data={ chartData }
|
title={ selectedChart.label }
|
||||||
title={ selectedChart.label }
|
interval={ currentInterval }
|
||||||
interval={ currentInterval }
|
allowedIntervals={ allowedIntervals }
|
||||||
allowedIntervals={ allowedIntervals }
|
tooltipFormat={ formats.tooltipFormat }
|
||||||
tooltipFormat={ formats.tooltipFormat }
|
xFormat={ formats.xFormat }
|
||||||
xFormat={ formats.xFormat }
|
x2Format={ formats.x2Format }
|
||||||
x2Format={ formats.x2Format }
|
dateParser={ '%Y-%m-%dT%H:%M:%S' }
|
||||||
dateParser={ '%Y-%m-%dT%H:%M:%S' }
|
/>
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
grid-column-start: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-calendar__input {
|
.woocommerce-calendar__input {
|
||||||
|
@ -95,6 +96,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
grid-column-start: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
grid-column-start: 3;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-empty {
|
&.is-empty {
|
||||||
.dashicons-calendar path {
|
.dashicons-calendar path {
|
||||||
fill: $core-grey-dark-300;
|
fill: $core-grey-dark-300;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
getOrderedKeys,
|
getOrderedKeys,
|
||||||
getLine,
|
getLine,
|
||||||
getLineData,
|
getLineData,
|
||||||
|
getXTicks,
|
||||||
getUniqueKeys,
|
getUniqueKeys,
|
||||||
getUniqueDates,
|
getUniqueDates,
|
||||||
getXScale,
|
getXScale,
|
||||||
|
@ -97,6 +98,7 @@ class D3Chart extends Component {
|
||||||
data,
|
data,
|
||||||
dateParser,
|
dateParser,
|
||||||
height,
|
height,
|
||||||
|
layout,
|
||||||
margin,
|
margin,
|
||||||
orderedKeys,
|
orderedKeys,
|
||||||
tooltipFormat,
|
tooltipFormat,
|
||||||
|
@ -120,6 +122,7 @@ class D3Chart extends Component {
|
||||||
const uniqueDates = getUniqueDates( lineData, parseDate );
|
const uniqueDates = getUniqueDates( lineData, parseDate );
|
||||||
const xLineScale = getXLineScale( uniqueDates, adjWidth );
|
const xLineScale = getXLineScale( uniqueDates, adjWidth );
|
||||||
const xScale = getXScale( uniqueDates, adjWidth );
|
const xScale = getXScale( uniqueDates, adjWidth );
|
||||||
|
const xTicks = getXTicks( uniqueDates, adjWidth, layout );
|
||||||
return {
|
return {
|
||||||
colorScheme,
|
colorScheme,
|
||||||
dateSpaces: getDateSpaces( uniqueDates, adjWidth, xLineScale ),
|
dateSpaces: getDateSpaces( uniqueDates, adjWidth, xLineScale ),
|
||||||
|
@ -139,6 +142,7 @@ class D3Chart extends Component {
|
||||||
x2Format: d3TimeFormat( x2Format ),
|
x2Format: d3TimeFormat( x2Format ),
|
||||||
xGroupScale: getXGroupScale( orderedKeys, xScale ),
|
xGroupScale: getXGroupScale( orderedKeys, xScale ),
|
||||||
xLineScale,
|
xLineScale,
|
||||||
|
xTicks,
|
||||||
xScale,
|
xScale,
|
||||||
yMax,
|
yMax,
|
||||||
yScale,
|
yScale,
|
||||||
|
@ -195,6 +199,11 @@ D3Chart.propTypes = {
|
||||||
* Interval specification (hourly, daily, weekly etc.)
|
* Interval specification (hourly, daily, weekly etc.)
|
||||||
*/
|
*/
|
||||||
interval: PropTypes.oneOf( [ 'hour', 'day', 'week', 'month', 'quarter', 'year' ] ),
|
interval: PropTypes.oneOf( [ 'hour', 'day', 'week', 'month', 'quarter', 'year' ] ),
|
||||||
|
/**
|
||||||
|
* `standard` (default) legend layout in the header or `comparison` moves legend layout
|
||||||
|
* to the left or 'compact' has the legend below
|
||||||
|
*/
|
||||||
|
layout: PropTypes.oneOf( [ 'standard', 'comparison', 'compact' ] ),
|
||||||
/**
|
/**
|
||||||
* Margins for axis and chart padding.
|
* Margins for axis and chart padding.
|
||||||
*/
|
*/
|
||||||
|
@ -244,6 +253,7 @@ D3Chart.defaultProps = {
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 20,
|
top: 20,
|
||||||
},
|
},
|
||||||
|
layout: 'standard',
|
||||||
tooltipFormat: '%Y-%m-%d',
|
tooltipFormat: '%Y-%m-%d',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
width: 600,
|
width: 600,
|
||||||
|
|
|
@ -291,9 +291,10 @@ Chart.propTypes = {
|
||||||
*/
|
*/
|
||||||
yFormat: PropTypes.string,
|
yFormat: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* `standard` (default) legend layout in the header or `comparison` moves legend layout to the left
|
* `standard` (default) legend layout in the header or `comparison` moves legend layout
|
||||||
|
* to the left or 'compact' has the legend below
|
||||||
*/
|
*/
|
||||||
layout: PropTypes.oneOf( [ 'standard', 'comparison' ] ),
|
layout: PropTypes.oneOf( [ 'standard', 'comparison', 'compact' ] ),
|
||||||
/**
|
/**
|
||||||
* A title describing this chart.
|
* A title describing this chart.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
.woocommerce-chart {
|
.woocommerce-chart {
|
||||||
display: flex;
|
margin-top: -$gap;
|
||||||
flex-direction: column;
|
margin-bottom: $gap-large;
|
||||||
justify-content: flex-start;
|
background: white;
|
||||||
align-items: flex-start;
|
border: 1px solid $core-grey-light-700;
|
||||||
margin: -$gap;
|
border-top: 0;
|
||||||
border-top: 1px solid $core-grey-light-700;
|
|
||||||
|
@include breakpoint( '<782px' ) {
|
||||||
|
margin-left: -16px;
|
||||||
|
margin-right: -16px;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-chart__header {
|
.woocommerce-chart__header {
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
border-top: 1px solid $core-grey-light-700;
|
|
||||||
border-bottom: 1px solid $core-grey-light-700;
|
border-bottom: 1px solid $core-grey-light-700;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
@ -181,13 +188,9 @@
|
||||||
|
|
||||||
&.is-placeholder {
|
&.is-placeholder {
|
||||||
@include placeholder();
|
@include placeholder();
|
||||||
display: inline-block;
|
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: $gap;
|
|
||||||
border: 1px solid $core-grey-light-700;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,30 @@ import {
|
||||||
scaleLinear as d3ScaleLinear,
|
scaleLinear as d3ScaleLinear,
|
||||||
scaleTime as d3ScaleTime,
|
scaleTime as d3ScaleTime,
|
||||||
} from 'd3-scale';
|
} from 'd3-scale';
|
||||||
import { mouse as d3Mouse, select as d3Select } from 'd3-selection';
|
import { event as d3Event, mouse as d3Mouse, select as d3Select } from 'd3-selection';
|
||||||
import { line as d3Line } from 'd3-shape';
|
import { line as d3Line } from 'd3-shape';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { formatCurrency } from 'lib/currency';
|
import { formatCurrency } from 'lib/currency';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes `smallestFactor`
|
||||||
|
* @param {number} inputNum - any double or integer
|
||||||
|
* @returns {integer} smallest factor of num
|
||||||
|
*/
|
||||||
|
export const getFactors = inputNum => {
|
||||||
|
const num_factors = [];
|
||||||
|
for ( let i = 1; i <= Math.floor( Math.sqrt( inputNum ) ); i += 1 ) {
|
||||||
|
if ( inputNum % i === 0 ) {
|
||||||
|
num_factors.push( i );
|
||||||
|
inputNum / i !== i && num_factors.push( inputNum / i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num_factors.sort( ( x, y ) => x - y ); // numeric sort
|
||||||
|
return num_factors;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes `getUniqueKeys`
|
* Describes `getUniqueKeys`
|
||||||
* @param {array} data - The chart component's `data` prop.
|
* @param {array} data - The chart component's `data` prop.
|
||||||
|
@ -187,6 +204,74 @@ export const getLine = ( xLineScale, yScale ) =>
|
||||||
.x( d => xLineScale( new Date( d.date ) ) )
|
.x( d => xLineScale( new Date( d.date ) ) )
|
||||||
.y( d => yScale( d.value ) );
|
.y( d => yScale( d.value ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes getXTicks
|
||||||
|
* @param {array} uniqueDates - all the unique dates from the input data for the chart
|
||||||
|
* @param {integer} width - calculated page width
|
||||||
|
* @param {string} layout - standard, comparison or compact chart types
|
||||||
|
* @returns {integer} number of x-axis ticks based on width and chart layout
|
||||||
|
*/
|
||||||
|
export const getXTicks = ( uniqueDates, width, layout ) => {
|
||||||
|
// caluclate the maximum number of ticks allowed in the x-axis based on the width
|
||||||
|
// and layout of the chart
|
||||||
|
let ticks = 16;
|
||||||
|
if ( width < 783 ) {
|
||||||
|
ticks = 7;
|
||||||
|
} else if ( width >= 783 && width < 1129 ) {
|
||||||
|
ticks = 12;
|
||||||
|
} else if ( width >= 1130 && width < 1365 ) {
|
||||||
|
if ( layout === 'standard' ) {
|
||||||
|
ticks = 16;
|
||||||
|
} else if ( layout === 'comparison' ) {
|
||||||
|
ticks = 12;
|
||||||
|
} else if ( layout === 'compact' ) {
|
||||||
|
ticks = 7;
|
||||||
|
}
|
||||||
|
} else if ( width >= 1365 ) {
|
||||||
|
if ( layout === 'standard' ) {
|
||||||
|
ticks = 31;
|
||||||
|
} else if ( layout === 'comparison' ) {
|
||||||
|
ticks = 16;
|
||||||
|
} else if ( layout === 'compact' ) {
|
||||||
|
ticks = 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( uniqueDates.length <= ticks ) {
|
||||||
|
return uniqueDates;
|
||||||
|
}
|
||||||
|
let factors = [];
|
||||||
|
let i = 0;
|
||||||
|
// first we get all the factors of the length of the uniqieDates 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 - ( 1 + i ) );
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let newTicks = [];
|
||||||
|
let factorIndex = 0;
|
||||||
|
// newTicks is the first tick plus the smallest factor (initiallY) etc.
|
||||||
|
// however, if we still end up with too many ticks we look at the next factor
|
||||||
|
// and try again unttil we have fewer ticks than the max
|
||||||
|
while ( newTicks.length > ticks || newTicks.length === 0 ) {
|
||||||
|
if ( newTicks.length > ticks ) {
|
||||||
|
factorIndex += 1;
|
||||||
|
newTicks = [];
|
||||||
|
}
|
||||||
|
for ( let idx = 0; idx < uniqueDates.length; idx = idx + factors[ factorIndex ] ) {
|
||||||
|
newTicks.push( uniqueDates[ idx ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if, for some reason, the first or last date is missing from the newTicks array, add it back in
|
||||||
|
if ( newTicks[ 0 ] !== uniqueDates[ 0 ] ) {
|
||||||
|
newTicks.unshift( uniqueDates[ 0 ] );
|
||||||
|
}
|
||||||
|
if ( newTicks[ newTicks.length - 1 ] !== uniqueDates[ uniqueDates.length - 1 ] ) {
|
||||||
|
newTicks.push( uniqueDates[ uniqueDates.length - 1 ] );
|
||||||
|
}
|
||||||
|
return newTicks;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes getDateSpaces
|
* Describes getDateSpaces
|
||||||
* @param {array} uniqueDates - from `getUniqueDates`
|
* @param {array} uniqueDates - from `getUniqueDates`
|
||||||
|
@ -223,13 +308,15 @@ export const drawAxis = ( node, params ) => {
|
||||||
yGrids.push( i / 3 * params.yMax );
|
yGrids.push( i / 3 * params.yMax );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ticks = params.xTicks.map( d => ( params.type === 'line' ? new Date( d ) : d ) );
|
||||||
|
|
||||||
node
|
node
|
||||||
.append( 'g' )
|
.append( 'g' )
|
||||||
.attr( 'class', 'axis' )
|
.attr( 'class', 'axis' )
|
||||||
.attr( 'transform', `translate(0,${ params.height })` )
|
.attr( 'transform', `translate(0,${ params.height })` )
|
||||||
.call(
|
.call(
|
||||||
d3AxisBottom( xScale )
|
d3AxisBottom( xScale )
|
||||||
.tickValues( params.uniqueDates.map( d => ( params.type === 'line' ? new Date( d ) : d ) ) )
|
.tickValues( ticks )
|
||||||
.tickFormat( d => params.xFormat( d instanceof Date ? d : new Date( d ) ) )
|
.tickFormat( d => params.xFormat( d instanceof Date ? d : new Date( d ) ) )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -239,7 +326,7 @@ export const drawAxis = ( node, params ) => {
|
||||||
.attr( 'transform', `translate(3, ${ params.height + 20 })` )
|
.attr( 'transform', `translate(3, ${ params.height + 20 })` )
|
||||||
.call(
|
.call(
|
||||||
d3AxisBottom( xScale )
|
d3AxisBottom( xScale )
|
||||||
.tickValues( params.uniqueDates.map( d => ( params.type === 'line' ? new Date( d ) : d ) ) )
|
.tickValues( ticks )
|
||||||
.tickFormat( ( d, i ) => {
|
.tickFormat( ( d, i ) => {
|
||||||
const monthDate = d instanceof Date ? d : new Date( d );
|
const monthDate = d instanceof Date ? d : new Date( d );
|
||||||
let prevMonth = i !== 0 ? params.uniqueDates[ i - 1 ] : params.uniqueDates[ i ];
|
let prevMonth = i !== 0 ? params.uniqueDates[ i - 1 ] : params.uniqueDates[ i ];
|
||||||
|
@ -263,7 +350,7 @@ export const drawAxis = ( node, params ) => {
|
||||||
.attr( 'transform', `translate(0, ${ params.height })` )
|
.attr( 'transform', `translate(0, ${ params.height })` )
|
||||||
.call(
|
.call(
|
||||||
d3AxisBottom( xScale )
|
d3AxisBottom( xScale )
|
||||||
.tickValues( params.uniqueDates.map( d => ( params.type === 'line' ? new Date( d ) : d ) ) )
|
.tickValues( ticks )
|
||||||
.tickSize( 5 )
|
.tickSize( 5 )
|
||||||
.tickFormat( '' )
|
.tickFormat( '' )
|
||||||
);
|
);
|
||||||
|
@ -303,9 +390,9 @@ export const drawAxis = ( node, params ) => {
|
||||||
.remove();
|
.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
const showTooltip = ( node, params, d ) => {
|
const showTooltip = ( node, params, d, position ) => {
|
||||||
const chartCoords = node.node().getBoundingClientRect();
|
const chartCoords = node.node().getBoundingClientRect();
|
||||||
let [ xPosition, yPosition ] = d3Mouse( node.node() );
|
let [ xPosition, yPosition ] = position ? position : d3Mouse( node.node() );
|
||||||
xPosition = xPosition > chartCoords.width - 340 ? xPosition - 340 : xPosition + 100;
|
xPosition = xPosition > chartCoords.width - 340 ? xPosition - 340 : xPosition + 100;
|
||||||
yPosition = yPosition > chartCoords.height - 150 ? yPosition - 200 : yPosition + 20;
|
yPosition = yPosition > chartCoords.height - 150 ? yPosition - 200 : yPosition + 20;
|
||||||
const keys = params.orderedKeys.filter( row => row.visible ).map(
|
const keys = params.orderedKeys.filter( row => row.visible ).map(
|
||||||
|
@ -333,25 +420,25 @@ const showTooltip = ( node, params, d ) => {
|
||||||
` );
|
` );
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseOverBarChart = ( d, i, nodes, node, data, params ) => {
|
const handleMouseOverBarChart = ( d, i, nodes, node, data, params, position ) => {
|
||||||
d3Select( nodes[ i ].parentNode )
|
d3Select( nodes[ i ].parentNode )
|
||||||
.select( '.barfocus' )
|
.select( '.barfocus' )
|
||||||
.attr( 'opacity', '0.1' );
|
.attr( 'opacity', '0.1' );
|
||||||
showTooltip( node, params, d );
|
showTooltip( node, params, d, position );
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseOutBarChart = ( d, i, nodes, params ) => {
|
const handleMouseOutBarChart = ( d, i, nodes, params ) => {
|
||||||
d3Select( nodes[ i ].parentNode )
|
d3Select( nodes[ i ].parentNode )
|
||||||
.select( '.barfocus' )
|
.select( '.barfocus' )
|
||||||
.attr( 'opacity', '0' );
|
.attr( 'opacity', '0' );
|
||||||
params.tooltip.style( 'display', 'flex' );
|
params.tooltip.style( 'display', 'none' );
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseOverLineChart = ( d, i, nodes, node, data, params ) => {
|
const handleMouseOverLineChart = ( d, i, nodes, node, data, params, position ) => {
|
||||||
d3Select( nodes[ i ].parentNode )
|
d3Select( nodes[ i ].parentNode )
|
||||||
.select( '.focus-grid' )
|
.select( '.focus-grid' )
|
||||||
.attr( 'opacity', '1' );
|
.attr( 'opacity', '1' );
|
||||||
showTooltip( node, params, data.find( e => e.date === d.date ) );
|
showTooltip( node, params, data.find( e => e.date === d.date ), position );
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseOutLineChart = ( d, i, nodes, params ) => {
|
const handleMouseOutLineChart = ( d, i, nodes, params ) => {
|
||||||
|
@ -361,6 +448,12 @@ const handleMouseOutLineChart = ( d, i, nodes, params ) => {
|
||||||
params.tooltip.style( 'display', 'none' );
|
params.tooltip.style( 'display', 'none' );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const calculatePositionInChart = ( element, chart ) => {
|
||||||
|
const elementCoords = element.getBoundingClientRect();
|
||||||
|
const chartCoords = chart.getBoundingClientRect();
|
||||||
|
return [ elementCoords.x - chartCoords.x, elementCoords.y - chartCoords.y ];
|
||||||
|
};
|
||||||
|
|
||||||
export const drawLines = ( node, data, params ) => {
|
export const drawLines = ( node, data, params ) => {
|
||||||
const series = node
|
const series = node
|
||||||
.append( 'g' )
|
.append( 'g' )
|
||||||
|
@ -384,25 +477,26 @@ export const drawLines = ( node, data, params ) => {
|
||||||
} )
|
} )
|
||||||
.attr( 'd', d => params.line( d.values ) );
|
.attr( 'd', d => params.line( d.values ) );
|
||||||
|
|
||||||
series
|
params.uniqueDates.length < 50 &&
|
||||||
.selectAll( 'circle' )
|
series
|
||||||
.data( ( d, i ) => d.values.map( row => ( { ...row, i, visible: d.visible, key: d.key } ) ) )
|
.selectAll( 'circle' )
|
||||||
.enter()
|
.data( ( d, i ) => d.values.map( row => ( { ...row, i, visible: d.visible, key: d.key } ) ) )
|
||||||
.append( 'circle' )
|
.enter()
|
||||||
.attr( 'r', 6 )
|
.append( 'circle' )
|
||||||
.attr( 'fill', d => getColor( d.key, params ) )
|
.attr( 'r', 6 )
|
||||||
.attr( 'stroke', '#fff' )
|
.attr( 'fill', d => getColor( d.key, params ) )
|
||||||
.attr( 'stroke-width', 3 )
|
.attr( 'stroke', '#fff' )
|
||||||
.style( 'opacity', d => {
|
.attr( 'stroke-width', 3 )
|
||||||
const opacity = d.focus ? 1 : 0.1;
|
.style( 'opacity', d => {
|
||||||
return d.visible ? opacity : 0;
|
const opacity = d.focus ? 1 : 0.1;
|
||||||
} )
|
return d.visible ? opacity : 0;
|
||||||
.attr( 'cx', d => params.xLineScale( new Date( d.date ) ) )
|
} )
|
||||||
.attr( 'cy', d => params.yScale( d.value ) )
|
.attr( 'cx', d => params.xLineScale( new Date( d.date ) ) )
|
||||||
.on( 'mouseover', ( d, i, nodes ) =>
|
.attr( 'cy', d => params.yScale( d.value ) )
|
||||||
handleMouseOverLineChart( d, i, nodes, node, data, params )
|
.on( 'mouseover', ( d, i, nodes ) =>
|
||||||
)
|
handleMouseOverLineChart( d, i, nodes, node, data, params )
|
||||||
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutLineChart( d, i, nodes, params ) );
|
)
|
||||||
|
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutLineChart( d, i, nodes, params ) );
|
||||||
|
|
||||||
const focus = node
|
const focus = node
|
||||||
.append( 'g' )
|
.append( 'g' )
|
||||||
|
@ -430,10 +524,15 @@ export const drawLines = ( node, data, params ) => {
|
||||||
.attr( 'width', d => d.width )
|
.attr( 'width', d => d.width )
|
||||||
.attr( 'height', params.height )
|
.attr( 'height', params.height )
|
||||||
.attr( 'opacity', 0 )
|
.attr( 'opacity', 0 )
|
||||||
|
.attr( 'tabindex', '0' )
|
||||||
.on( 'mouseover', ( d, i, nodes ) =>
|
.on( 'mouseover', ( d, i, nodes ) =>
|
||||||
handleMouseOverLineChart( d, i, nodes, node, data, params )
|
handleMouseOverLineChart( d, i, nodes, node, data, params )
|
||||||
)
|
)
|
||||||
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutLineChart( d, i, nodes, params ) );
|
.on( 'focus', ( d, i, nodes ) => {
|
||||||
|
const position = calculatePositionInChart( d3Event.target, node.node() );
|
||||||
|
handleMouseOverLineChart( d, i, nodes, node, data, params, position );
|
||||||
|
} )
|
||||||
|
.on( 'mouseout blur', ( d, i, nodes ) => handleMouseOutLineChart( d, i, nodes, params ) );
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawBars = ( node, data, params ) => {
|
export const drawBars = ( node, data, params ) => {
|
||||||
|
@ -491,8 +590,13 @@ export const drawBars = ( node, data, params ) => {
|
||||||
.attr( 'width', params.xGroupScale.range()[ 1 ] )
|
.attr( 'width', params.xGroupScale.range()[ 1 ] )
|
||||||
.attr( 'height', params.height )
|
.attr( 'height', params.height )
|
||||||
.attr( 'opacity', '0' )
|
.attr( 'opacity', '0' )
|
||||||
|
.attr( 'tabindex', '0' )
|
||||||
.on( 'mouseover', ( d, i, nodes ) =>
|
.on( 'mouseover', ( d, i, nodes ) =>
|
||||||
handleMouseOverBarChart( d, i, nodes, node, data, params )
|
handleMouseOverBarChart( d, i, nodes, node, data, params )
|
||||||
)
|
)
|
||||||
.on( 'mouseout', ( d, i, nodes ) => handleMouseOutBarChart( d, i, nodes, params ) );
|
.on( 'focus', ( d, i, nodes ) => {
|
||||||
|
const position = calculatePositionInChart( d3Event.target, node.node() );
|
||||||
|
handleMouseOverBarChart( d, i, nodes, node, data, params, position );
|
||||||
|
} )
|
||||||
|
.on( 'mouseout blur', ( d, i, nodes ) => handleMouseOutBarChart( d, i, nodes, params ) );
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,8 @@ And as such will require data layer logic for products to fully build the table
|
||||||
"product_id": 20,
|
"product_id": 20,
|
||||||
"items_sold": 100,
|
"items_sold": 100,
|
||||||
"gross_revenue": 999.99,
|
"gross_revenue": 999.99,
|
||||||
"orders_count": 54,
|
"orders_count": 54,
|
||||||
|
"name": 'Product name',
|
||||||
"_links": {
|
"_links": {
|
||||||
"product": [
|
"product": [
|
||||||
{
|
{
|
||||||
|
@ -27,6 +28,7 @@ export default [
|
||||||
items_sold: 1000,
|
items_sold: 1000,
|
||||||
gross_revenue: 999.99,
|
gross_revenue: 999.99,
|
||||||
orders_count: 54,
|
orders_count: 54,
|
||||||
|
name: 'awesome shirt',
|
||||||
_links: {
|
_links: {
|
||||||
product: [
|
product: [
|
||||||
{
|
{
|
||||||
|
@ -40,6 +42,7 @@ export default [
|
||||||
items_sold: 90,
|
items_sold: 90,
|
||||||
gross_revenue: 875,
|
gross_revenue: 875,
|
||||||
orders_count: 41,
|
orders_count: 41,
|
||||||
|
name: 'awesome pants',
|
||||||
_links: {
|
_links: {
|
||||||
product: [
|
product: [
|
||||||
{
|
{
|
||||||
|
@ -53,6 +56,7 @@ export default [
|
||||||
items_sold: 55,
|
items_sold: 55,
|
||||||
gross_revenue: 75.75,
|
gross_revenue: 75.75,
|
||||||
orders_count: 28,
|
orders_count: 28,
|
||||||
|
name: 'awesome hat',
|
||||||
_links: {
|
_links: {
|
||||||
product: [
|
product: [
|
||||||
{
|
{
|
||||||
|
@ -66,6 +70,7 @@ export default [
|
||||||
items_sold: 10,
|
items_sold: 10,
|
||||||
gross_revenue: 24.5,
|
gross_revenue: 24.5,
|
||||||
orders_count: 14,
|
orders_count: 14,
|
||||||
|
name: 'awesome sticker',
|
||||||
_links: {
|
_links: {
|
||||||
product: [
|
product: [
|
||||||
{
|
{
|
||||||
|
@ -79,6 +84,7 @@ export default [
|
||||||
items_sold: 1,
|
items_sold: 1,
|
||||||
gross_revenue: 0.99,
|
gross_revenue: 0.99,
|
||||||
orders_count: 1,
|
orders_count: 1,
|
||||||
|
name: 'awesome button',
|
||||||
_links: {
|
_links: {
|
||||||
product: [
|
product: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,17 +53,15 @@ export class TopSellingProducts extends Component {
|
||||||
|
|
||||||
getRowsContent( data ) {
|
getRowsContent( data ) {
|
||||||
return map( data, row => {
|
return map( data, row => {
|
||||||
const { product_id, items_sold, gross_revenue, orders_count } = row;
|
const { product_id, items_sold, gross_revenue, orders_count, name } = row;
|
||||||
|
|
||||||
// @TODO We also will need to have product data to properly display the product name here
|
|
||||||
const productName = `Product ${ product_id }`;
|
|
||||||
const productLink = (
|
const productLink = (
|
||||||
<a href={ getAdminLink( `/post.php?post=${ product_id }&action=edit` ) }>{ productName }</a>
|
<a href={ getAdminLink( `/post.php?post=${ product_id }&action=edit` ) }>{ name }</a>
|
||||||
);
|
);
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
display: productLink,
|
display: productLink,
|
||||||
value: productName,
|
value: name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
display: numberFormat( items_sold ),
|
display: numberFormat( items_sold ),
|
||||||
|
@ -123,7 +121,7 @@ export default compose(
|
||||||
const endpoint = NAMESPACE + 'reports/products';
|
const endpoint = NAMESPACE + 'reports/products';
|
||||||
// @TODO We will need to add the date parameters from the Date Picker
|
// @TODO We will need to add the date parameters from the Date Picker
|
||||||
// { after: '2018-04-22', before: '2018-05-06' }
|
// { after: '2018-04-22', before: '2018-05-06' }
|
||||||
const query = { orderby: 'items_sold', per_page: 5 };
|
const query = { orderby: 'items_sold', per_page: 5, extended_product_info: 1 };
|
||||||
|
|
||||||
const stats = getReportStats( endpoint, query );
|
const stats = getReportStats( endpoint, query );
|
||||||
const isRequesting = isReportStatsRequesting( endpoint, query );
|
const isRequesting = isReportStatsRequesting( endpoint, query );
|
||||||
|
|
|
@ -43,7 +43,7 @@ describe( 'TopSellingProducts', () => {
|
||||||
const table = topSellingProducts.find( 'Table' );
|
const table = topSellingProducts.find( 'Table' );
|
||||||
const firstRow = table.props().rows[ 0 ];
|
const firstRow = table.props().rows[ 0 ];
|
||||||
|
|
||||||
expect( firstRow[ 0 ].value ).toBe( `Product ${ mockData[ 0 ].product_id }` );
|
expect( firstRow[ 0 ].value ).toBe( mockData[ 0 ].name );
|
||||||
expect( firstRow[ 1 ].display ).toBe( numberFormat( mockData[ 0 ].items_sold ) );
|
expect( firstRow[ 1 ].display ).toBe( numberFormat( mockData[ 0 ].items_sold ) );
|
||||||
expect( firstRow[ 1 ].value ).toBe( mockData[ 0 ].items_sold );
|
expect( firstRow[ 1 ].value ).toBe( mockData[ 0 ].items_sold );
|
||||||
expect( firstRow[ 2 ].display ).toBe( numberFormat( mockData[ 0 ].orders_count ) );
|
expect( firstRow[ 2 ].display ).toBe( numberFormat( mockData[ 0 ].orders_count ) );
|
||||||
|
@ -73,7 +73,7 @@ describe( 'TopSellingProducts', () => {
|
||||||
const topSellingProducts = topSellingProductsWrapper.root.findByType( TopSellingProducts );
|
const topSellingProducts = topSellingProductsWrapper.root.findByType( TopSellingProducts );
|
||||||
|
|
||||||
const endpoint = '/wc/v3/reports/products';
|
const endpoint = '/wc/v3/reports/products';
|
||||||
const query = { orderby: 'items_sold', per_page: 5 };
|
const query = { orderby: 'items_sold', per_page: 5, extended_product_info: 1 };
|
||||||
|
|
||||||
expect( getReportStatsMock.mock.calls[ 0 ][ 1 ] ).toBe( endpoint );
|
expect( getReportStatsMock.mock.calls[ 0 ][ 1 ] ).toBe( endpoint );
|
||||||
expect( getReportStatsMock.mock.calls[ 0 ][ 2 ] ).toEqual( query );
|
expect( getReportStatsMock.mock.calls[ 0 ][ 2 ] ).toEqual( query );
|
||||||
|
|
Loading…
Reference in New Issue