Merge pull request woocommerce/woocommerce-admin#818 from woocommerce/try/chart-as-package

Try: chart component as a package part 1
This commit is contained in:
Robert Elliott 2018-11-21 16:55:04 +02:00 committed by GitHub
commit 616bf6e434
25 changed files with 305 additions and 461 deletions

View File

@ -27,7 +27,7 @@ import {
* Internal dependencies
*/
import { Chart, ChartPlaceholder } from 'components';
import { getReportChartData } from 'store/reports/utils';
import { getReportChartData, getTooltipValueFormat } from 'store/reports/utils';
import ReportError from 'analytics/components/report-error';
export const DEFAULT_FILTER = 'all';
@ -107,13 +107,11 @@ export class ReportChart extends Component {
value: interval.subtotals[ selectedChart.key ] || 0,
},
[ secondaryKey ]: {
labelDate: secondaryDate,
labelDate: secondaryDate.format( 'YYYY-MM-DD HH:mm:ss' ),
value: ( secondaryInterval && secondaryInterval.subtotals[ selectedChart.key ] ) || 0,
},
};
} );
const mode = this.getChartMode();
const layout = mode === 'item-comparison' ? 'comparison' : 'standard';
return (
<Chart
@ -125,9 +123,9 @@ export class ReportChart extends Component {
type={ getChartTypeForQuery( query ) }
allowedIntervals={ allowedIntervals }
itemsLabel={ itemsLabel }
layout={ layout }
mode={ mode }
pointLabelFormat={ formats.pointLabelFormat }
mode={ this.getChartMode() }
tooltipLabelFormat={ formats.tooltipLabelFormat }
tooltipValueFormat={ getTooltipValueFormat( selectedChart.type ) }
tooltipTitle={ selectedChart.label }
xFormat={ formats.xFormat }
x2Format={ formats.x2Format }

View File

@ -43,7 +43,6 @@ describe( 'ReportChart', () => {
const chart = reportChart.find( 'Chart' );
expect( chart.props().mode ).toEqual( null );
expect( chart.props().layout ).toEqual( 'standard' );
} );
test( 'should set the mode prop depending on the active filter', () => {
@ -77,6 +76,5 @@ describe( 'ReportChart', () => {
const chart = reportChart.find( 'Chart' );
expect( chart.props().mode ).toEqual( 'item-comparison' );
expect( chart.props().layout ).toEqual( 'comparison' );
} );
} );

View File

@ -1,34 +0,0 @@
/** @format */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
/**
* WooCommerce dependencies
*/
import { Card } from '@woocommerce/components';
/**
* Internal dependencies
*/
import Chart from './index';
import dummyOrders from './test/fixtures/dummy-hour';
class WidgetCharts extends Component {
render() {
return (
<Card title={ __( 'Test Categories', 'wc-admin' ) }>
<Chart
data={ dummyOrders }
tooltipFormat={ 'Hour of %H' }
type={ 'bar' }
xFormat={ '%H' }
/>
</Card>
);
}
}
export default WidgetCharts;

View File

@ -1,107 +0,0 @@
/** @format */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
/**
* WooCommerce dependencies
*/
import { Card } from '@woocommerce/components';
/**
* Internal dependencies
*/
import Chart from './index';
import dummyOrders from './test/fixtures/dummy';
class WidgetCharts extends Component {
constructor() {
super( ...arguments );
this.handleChange = this.handleChange.bind( this );
this.getSomeOrders = this.getSomeOrders.bind( this );
const products = [
{
key: 'date',
selected: true,
},
{
key: 'Cap',
selected: true,
},
{
key: 'T-Shirt',
selected: true,
},
{
key: 'Sunglasses',
selected: true,
},
{
key: 'Polo',
selected: true,
},
{
key: 'Hoodie',
selected: true,
},
];
const someOrders = this.getSomeOrders( products );
this.state = {
products,
someOrders,
};
}
getSomeOrders( products ) {
return dummyOrders.map( d => {
return Object.keys( d )
.filter( key =>
products
.filter( k => k.selected )
.map( k => k.key )
.includes( key )
)
.reduce( ( accum, current ) => ( { ...accum, [ current ]: d[ current ] } ), {} );
} );
}
handleChange( event ) {
const products = this.state.products.map( d => ( {
...d,
selected: d.key === event.target.id ? ! d.selected : d.selected,
} ) );
const someOrders = this.getSomeOrders( products );
this.setState( { products, someOrders } );
}
render() {
return (
<Fragment>
<Card title={ __( 'Test Categories', 'wc-admin' ) }>
<ul>
{ this.state.products.map( d => (
<li key={ d.key } style={ { display: 'inline', marginRight: '12px' } }>
<label htmlFor={ d.key }>
<input
id={ d.key }
type="checkbox"
onChange={ this.handleChange }
checked={ d.selected }
/>{' '}
{ d.key }
</label>
</li>
) ) }
</ul>
</Card>
<Card title={ __( 'Store Charts', 'wc-admin' ) }>
<Chart data={ this.state.someOrders } title="Example Chart" layout="comparison" />
</Card>
</Fragment>
);
}
}
export default WidgetCharts;

View File

@ -23,8 +23,9 @@ import { H, Section } from '@woocommerce/components';
/**
* Internal dependencies
*/
import D3Chart from './charts';
import Legend from './legend';
import './style.scss';
import D3Chart from 'components/d3chart';
import Legend from 'components/d3chart/legend';
d3FormatDefaultLocale( {
decimal: '.',
@ -47,7 +48,7 @@ function getOrderedKeys( props ) {
visible: true,
focus: true,
} ) );
if ( props.layout === 'comparison' ) {
if ( props.mode === 'item-comparison' ) {
updatedKeys.sort( ( a, b ) => b.total - a.total );
}
return updatedKeys;
@ -204,13 +205,12 @@ class Chart extends Component {
const {
dateParser,
itemsLabel,
layout,
mode,
pointLabelFormat,
isViewportLarge,
isViewportWide,
title,
tooltipFormat,
tooltipLabelFormat,
tooltipValueFormat,
tooltipTitle,
xFormat,
x2Format,
@ -219,8 +219,9 @@ class Chart extends Component {
type,
} = this.props;
let { yFormat } = this.props;
const legendDirection = layout === 'standard' && isViewportWide ? 'row' : 'column';
const chartDirection = layout === 'comparison' && isViewportWide ? 'row' : 'column';
const legendDirection = mode === 'time-comparison' && isViewportWide ? 'row' : 'column';
const chartDirection = mode === 'item-comparison' && isViewportWide ? 'row' : 'column';
const chartHeight = this.getChartHeight();
const legend = (
<Legend
@ -301,15 +302,15 @@ class Chart extends Component {
data={ visibleData }
dateParser={ dateParser }
height={ chartHeight }
interval={ interval }
margin={ margin }
mode={ mode }
orderedKeys={ orderedKeys }
pointLabelFormat={ pointLabelFormat }
tooltipFormat={ tooltipFormat }
tooltipLabelFormat={ tooltipLabelFormat }
tooltipValueFormat={ tooltipValueFormat }
tooltipPosition={ isViewportLarge ? 'over' : 'below' }
tooltipTitle={ tooltipTitle }
type={ type }
interval={ interval }
width={ chartDirection === 'row' ? width - 320 : width }
xFormat={ xFormat }
x2Format={ x2Format }
@ -338,21 +339,20 @@ Chart.propTypes = {
* Current path
*/
path: PropTypes.string,
/**
* Date format of the point labels (might be used in tooltips and ARIA properties).
*/
pointLabelFormat: PropTypes.string,
/**
* The query string represented in object form
*/
query: PropTypes.object,
/**
* A datetime formatting string to format the date displayed as the title of the toolip
* if `tooltipTitle` is missing, passed to d3TimeFormat.
* A datetime formatting string or overriding function to format the tooltip label.
*/
tooltipFormat: PropTypes.string,
tooltipLabelFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* A string to use as a title for the tooltip. Takes preference over `tooltipFormat`.
* A number formatting string or function to format the value displayed in the tooltips.
*/
tooltipValueFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* A string to use as a title for the tooltip. Takes preference over `tooltipLabelFormat`.
*/
tooltipTitle: PropTypes.string,
/**
@ -367,11 +367,6 @@ Chart.propTypes = {
* A number formatting string, passed to d3Format.
*/
yFormat: PropTypes.string,
/**
* `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' ] ),
/**
* `item-comparison` (default) or `time-comparison`, this is used to generate correct
* ARIA properties.
@ -406,11 +401,11 @@ Chart.propTypes = {
Chart.defaultProps = {
data: [],
dateParser: '%Y-%m-%dT%H:%M:%S',
tooltipFormat: '%B %d, %Y',
tooltipLabelFormat: '%B %d, %Y',
tooltipValueFormat: ',',
xFormat: '%d',
x2Format: '%b %Y',
yFormat: '$.3s',
layout: 'standard',
mode: 'item-comparison',
type: 'line',
interval: 'day',

View File

@ -63,133 +63,6 @@
width: 100%;
}
svg {
overflow: visible;
}
.tooltip {
border: 1px solid $core-grey-light-700;
position: absolute;
display: flex;
min-width: 324px;
height: auto;
background-color: $white;
text-align: left;
padding: 17px;
box-shadow: 0 3px 20px 0 rgba(18, 24, 30, 0.1), 0 1px 3px 0 rgba(18, 24, 30, 0.1);
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
pointer-events: none;
visibility: hidden;
z-index: 1;
@include breakpoint( '<600px' ) {
min-width: auto;
width: calc(100% - #{$gap-large * 2});
}
h4 {
text-align: left;
line-height: 18px;
width: 100%;
text-transform: uppercase;
font-size: 11px;
color: $core-grey-dark-300;
margin-top: 0;
}
ul {
list-style: none;
margin-bottom: 2px;
margin-top: 2px;
font-size: 14px;
li {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
&.key-row {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
.key-container {
width: 100%;
min-width: 100px;
.key-color {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 8px;
}
.key-key {
margin-right: 6px;
}
}
.key-value {
font-weight: 600;
}
}
}
}
}
.bargroup {
&rect {
shape-rendering: crispEdges;
}
}
.grid {
.tick {
line {
stroke: $core-grey-light-500;
stroke-width: 1;
shape-rendering: crispEdges;
}
&:first-child {
line {
stroke: $core-grey-dark-500;
}
}
&:last-child {
line {
opacity: 0;
}
}
}
}
.tick {
padding-top: 10px;
stroke-width: 1;
}
.y-axis {
text-anchor: start;
&.tick {
&text {
fill: $core-grey-dark-500;
}
}
}
.y-axis,
.axis-month {
.tick text {
font-size: 10px;
}
}
.focus-grid {
line {
stroke: $core-grey-light-700;
stroke-width: 1px;
}
}
&.is-placeholder {
@include placeholder();
height: 200px;
@ -240,12 +113,3 @@
.woocommerce-chart__types {
padding: 0 8px;
}
.woocommerce-chart__container {
position: relative;
width: 100%;
.tooltip {
position: absolute;
}
}

View File

@ -32,6 +32,7 @@ import {
getYMax,
getYScale,
getYTickOffset,
getFormatter,
} from './utils';
/**
@ -99,14 +100,13 @@ class D3Chart extends Component {
data,
dateParser,
height,
layout,
interval,
margin,
mode,
orderedKeys,
pointLabelFormat,
tooltipPosition,
tooltipFormat,
tooltipLabelFormat,
tooltipValueFormat,
tooltipTitle,
type,
xFormat,
@ -115,7 +115,7 @@ class D3Chart extends Component {
valueType,
} = this.props;
const { width } = this.state;
const calculatedWidth = width || node.offsetWidth;
const calculatedWidth = width && width > 0 ? width : node.offsetWidth;
const calculatedHeight = height || node.offsetHeight;
const adjHeight = calculatedHeight - margin.top - margin.bottom;
const adjWidth = calculatedWidth - margin.left - margin.right;
@ -128,7 +128,7 @@ class D3Chart extends Component {
const uniqueDates = getUniqueDates( lineData, parseDate );
const xLineScale = getXLineScale( uniqueDates, adjWidth );
const xScale = getXScale( uniqueDates, adjWidth );
const xTicks = getXTicks( uniqueDates, adjWidth, layout, interval );
const xTicks = getXTicks( uniqueDates, adjWidth, mode, interval );
return {
colorScheme,
dateSpaces: getDateSpaces( data, uniqueDates, adjWidth, xLineScale ),
@ -138,17 +138,17 @@ class D3Chart extends Component {
margin,
mode,
orderedKeys: newOrderedKeys,
pointLabelFormat,
parseDate,
tooltipPosition,
tooltipFormat: d3TimeFormat( tooltipFormat ),
tooltipLabelFormat: getFormatter( tooltipLabelFormat, d3TimeFormat ),
tooltipValueFormat: getFormatter( tooltipValueFormat ),
tooltipTitle,
type,
uniqueDates,
uniqueKeys,
width: calculatedWidth,
xFormat: d3TimeFormat( xFormat ),
x2Format: d3TimeFormat( x2Format ),
xFormat: getFormatter( xFormat, d3TimeFormat ),
x2Format: getFormatter( x2Format, d3TimeFormat ),
xGroupScale: getXGroupScale( orderedKeys, xScale ),
xLineScale,
xTicks,
@ -156,7 +156,7 @@ class D3Chart extends Component {
yMax,
yScale,
yTickOffset: getYTickOffset( adjHeight, yMax ),
yFormat,
yFormat: getFormatter( yFormat ),
valueType,
};
}
@ -167,7 +167,7 @@ class D3Chart extends Component {
}
return (
<div
className={ classNames( 'woocommerce-chart__container', this.props.className ) }
className={ classNames( 'd3-chart__container', this.props.className ) }
style={ { height: this.props.height } }
>
<D3Base
@ -178,7 +178,7 @@ class D3Chart extends Component {
type={ this.state.type }
width={ this.state.width }
/>
<div className="tooltip" ref={ this.tooltipRef } />
<div className="d3-chart__tooltip" ref={ this.tooltipRef } />
</div>
);
}
@ -209,15 +209,6 @@ D3Chart.propTypes = {
* Interval specification (hourly, daily, weekly etc.)
*/
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' ] ),
/**
* Date format of the point labels (might be used in tooltips and ARIA properties).
*/
pointLabelFormat: PropTypes.string,
/**
* Margins for axis and chart padding.
*/
@ -237,10 +228,13 @@ D3Chart.propTypes = {
*/
orderedKeys: PropTypes.array,
/**
* A datetime formatting string to format the date displayed as the title of the toolip
* if `tooltipTitle` is missing, passed to d3TimeFormat.
* A datetime formatting string or overriding function to format the tooltip label.
*/
tooltipFormat: PropTypes.string,
tooltipLabelFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* A number formatting string or function to format the value displayed in the tooltips.
*/
tooltipValueFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* The position where to render the tooltip can be `over` the chart or `below` the chart.
*/
@ -258,17 +252,17 @@ D3Chart.propTypes = {
*/
width: PropTypes.number,
/**
* A datetime formatting string, passed to d3TimeFormat.
* A datetime formatting string or function, passed to d3TimeFormat.
*/
xFormat: PropTypes.string,
xFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* A datetime formatting string, passed to d3TimeFormat.
* A datetime formatting string or function, passed to d3TimeFormat.
*/
x2Format: PropTypes.string,
x2Format: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
/**
* A number formatting string, passed to d3Format.
* A number formatting string or function, passed to d3Format.
*/
yFormat: PropTypes.string,
yFormat: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func ] ),
};
D3Chart.defaultProps = {
@ -281,10 +275,10 @@ D3Chart.defaultProps = {
right: 0,
top: 20,
},
layout: 'standard',
mode: 'item-comparison',
tooltipFormat: '%B %d, %Y',
tooltipPosition: 'over',
tooltipLabelFormat: '%B %d, %Y',
tooltipValueFormat: ',',
type: 'line',
width: 600,
xFormat: '%Y-%m-%d',

View File

@ -0,0 +1,134 @@
/** @format */
.d3-chart__container {
position: relative;
width: 100%;
svg {
overflow: visible;
}
.d3-chart__tooltip {
border: 1px solid $core-grey-light-700;
position: absolute;
display: flex;
min-width: 324px;
height: auto;
background-color: $white;
text-align: left;
padding: 17px;
box-shadow: 0 3px 20px 0 rgba(18, 24, 30, 0.1), 0 1px 3px 0 rgba(18, 24, 30, 0.1);
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
pointer-events: none;
visibility: hidden;
z-index: 1;
@include breakpoint( '<600px' ) {
min-width: auto;
width: calc(100% - #{$gap-large * 2});
}
h4 {
text-align: left;
line-height: 18px;
width: 100%;
text-transform: uppercase;
font-size: 11px;
color: $core-grey-dark-300;
margin-top: 0;
}
ul {
list-style: none;
margin-bottom: 2px;
margin-top: 2px;
font-size: 14px;
li {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
&.key-row {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
.key-container {
width: 100%;
min-width: 100px;
.key-color {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 8px;
}
.key-key {
margin-right: 6px;
}
}
.key-value {
font-weight: 600;
}
}
}
}
}
.bargroup {
&rect {
shape-rendering: crispEdges;
}
}
.grid {
.tick {
line {
stroke: $core-grey-light-500;
stroke-width: 1;
shape-rendering: crispEdges;
}
&:first-child {
line {
stroke: $core-grey-dark-500;
}
}
&:last-child {
line {
opacity: 0;
}
}
}
}
.tick {
padding-top: 10px;
stroke-width: 1;
}
.y-axis {
text-anchor: start;
&.tick {
&text {
fill: $core-grey-dark-500;
}
}
}
.y-axis,
.axis-month {
.tick text {
font-size: 10px;
}
}
.focus-grid {
line {
stroke: $core-grey-light-700;
stroke-width: 1px;
}
}
}

View File

@ -14,13 +14,25 @@ import {
} from 'd3-scale';
import { event as d3Event, select as d3Select } from 'd3-selection';
import { line as d3Line } from 'd3-shape';
import { format as formatDate } from '@wordpress/date';
const dayTicksThreshold = 63;
const weekTicksThreshold = 9;
const smallBreak = 783;
const mediumBreak = 1130;
const wideBreak = 1365;
const smallPoints = 7;
const mediumPoints = 12;
const largePoints = 16;
const mostPoints = 31;
/**
* WooCommerce dependencies
* Allows an overriding formatter or defaults to d3Format or d3TimeFormat
* @param {string|function} format - either a format string for the D3 formatters or an overriding fomatting method
* @param {function} formatter - default d3Format or another formatting method, which accepts the string `format`
* @returns {function} to be used to format an input given the format and formatter
*/
import { dayTicksThreshold, weekTicksThreshold } from '@woocommerce/date';
import { formatCurrency } from '@woocommerce/currency';
export const getFormatter = ( format, formatter = d3Format ) =>
typeof format === 'function' ? format : formatter( format );
/**
* Describes `smallestFactor`
@ -208,35 +220,31 @@ export const getLine = ( xLineScale, yScale ) =>
.y( d => yScale( d.value ) );
/**
* Calculate the maximum number of ticks allowed in the x-axis based on the width and layout of the chart
* 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} layout - standard, comparison or compact chart types
* @returns {integer} number of x-axis ticks based on width and chart layout
* @param {string} mode - item-comparison or time-comparison
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
const calculateMaxXTicks = ( width, layout ) => {
if ( width < 783 ) {
return 7;
} else if ( width >= 783 && width < 1129 ) {
return 12;
} else if ( width >= 1130 && width < 1365 ) {
if ( layout === 'standard' ) {
return 16;
} else if ( layout === 'comparison' ) {
return 12;
} else if ( layout === 'compact' ) {
return 7;
export 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 >= 1365 ) {
if ( layout === 'standard' ) {
return 31;
} else if ( layout === 'comparison' ) {
return 16;
} else if ( layout === 'compact' ) {
return 12;
} else if ( width > wideBreak ) {
if ( mode === 'time-comparison' ) {
return mostPoints;
} else if ( mode === 'item-comparison' ) {
return largePoints;
}
}
return 16;
return largePoints;
};
/**
@ -244,7 +252,7 @@ const calculateMaxXTicks = ( width, layout ) => {
* @param {array} dates - string dates.
* @returns {array} Filtered dates.
*/
const getFirstDatePerMonth = dates => {
export const getFirstDatePerMonth = dates => {
return dates.filter(
( date, i ) => i === 0 || new Date( date ).getMonth() !== new Date( dates[ i - 1 ] ).getMonth()
);
@ -256,7 +264,7 @@ const getFirstDatePerMonth = dates => {
* @param {integer} incrementFactor - increment factor for the visible ticks.
* @returns {array} Ticks for the x-axis.
*/
const getXTicksFromIncrementFactor = ( uniqueDates, incrementFactor ) => {
export const getXTicksFromIncrementFactor = ( uniqueDates, incrementFactor ) => {
const ticks = [];
for ( let idx = 0; idx < uniqueDates.length; idx = idx + incrementFactor ) {
@ -277,7 +285,7 @@ const getXTicksFromIncrementFactor = ( uniqueDates, incrementFactor ) => {
* @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 ) => {
export const calculateXTicksIncrementFactor = ( uniqueDates, maxTicks ) => {
let factors = [];
let i = 1;
// First we get all the factors of the length of the uniqueDates array
@ -295,12 +303,12 @@ const calculateXTicksIncrementFactor = ( uniqueDates, maxTicks ) => {
* 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} layout - standard, comparison or compact chart types
* @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 layout
* @returns {integer} number of x-axis ticks based on width and chart mode
*/
export const getXTicks = ( uniqueDates, width, layout, interval ) => {
const maxTicks = calculateMaxXTicks( width, layout );
export const getXTicks = ( uniqueDates, width, mode, interval ) => {
const maxTicks = calculateMaxXTicks( width, mode );
if (
( uniqueDates.length >= dayTicksThreshold && interval === 'day' ) ||
@ -323,7 +331,7 @@ export const getXTicks = ( uniqueDates, width, layout, interval ) => {
* @param {array} uniqueDates - from `getUniqueDates`
* @param {number} width - calculated width of the charting space
* @param {function} xLineScale - from `getXLineScale`
* @returns {array} that icnludes the date, start (x position) and width to layout the mouseover rectangles
* @returns {array} that icnludes the date, start (x position) and width to mode the mouseover rectangles
*/
export const getDateSpaces = ( data, uniqueDates, width, xLineScale ) =>
uniqueDates.map( ( d, i ) => {
@ -447,7 +455,7 @@ export const drawAxis = ( node, params ) => {
.call(
d3AxisLeft( params.yTickOffset )
.tickValues( yGrids )
.tickFormat( d => d3Format( params.yFormat )( d !== 0 ? d : 0 ) )
.tickFormat( d => params.yFormat( d !== 0 ? d : 0 ) )
);
node.selectAll( '.domain' ).remove();
@ -458,22 +466,15 @@ export const drawAxis = ( node, params ) => {
.remove();
};
const getTooltipRowLabel = ( d, row, params ) =>
d[ row.key ].labelDate ? formatDate( params.pointLabelFormat, d[ row.key ].labelDate ) : row.key;
const getFormatedTotal = ( total, valueType ) => {
let rowTotal = total;
switch ( valueType ) {
case 'average':
rowTotal = Math.round( total );
break;
case 'currency':
rowTotal = formatCurrency( total );
break;
case 'number':
break;
const getTooltipRowLabel = ( d, row, params ) => {
if ( d[ row.key ].labelDate ) {
return params.tooltipLabelFormat(
d[ row.key ].labelDate instanceof Date
? d[ row.key ].labelDate
: new Date( d[ row.key ].labelDate )
);
}
return rowTotal;
return row.key;
};
const showTooltip = ( params, d, position ) => {
@ -484,14 +485,14 @@ const showTooltip = ( params, d, position ) => {
<span class="key-color" style="background-color:${ getColor( row.key, params ) }"></span>
<span class="key-key">${ getTooltipRowLabel( d, row, params ) }</span>
</div>
<span class="key-value">${ getFormatedTotal( d[ row.key ].value, params.valueType ) }</span>
<span class="key-value">${ params.tooltipValueFormat( d[ row.key ].value ) }</span>
</li>
`
);
const tooltipTitle = params.tooltipTitle
? params.tooltipTitle
: params.tooltipFormat( d.date instanceof Date ? d.date : new Date( d.date ) );
: params.tooltipLabelFormat( d.date instanceof Date ? d.date : new Date( d.date ) );
params.tooltip
.style( 'left', position.x + 'px' )
@ -591,7 +592,7 @@ const calculateTooltipYPosition = (
const calculateTooltipPosition = ( element, chart, tooltipPosition, elementWidthRatio = 1 ) => {
const elementCoords = element.getBoundingClientRect();
const chartCoords = chart.getBoundingClientRect();
const tooltipSize = d3Select( '.tooltip' )
const tooltipSize = d3Select( '.d3-chart__tooltip' )
.node()
.getBoundingClientRect();
const tooltipMargin = 24;
@ -631,9 +632,9 @@ export const drawLines = ( node, data, params ) => {
.attr( 'role', 'region' )
.attr( 'aria-label', d => d.key );
let lineStroke = params.width <= 1329 || params.uniqueDates.length > 50 ? 2 : 3;
lineStroke = params.width <= 783 ? 1.25 : lineStroke;
const dotRadius = params.width <= 1329 ? 4 : 6;
let lineStroke = params.width <= wideBreak || params.uniqueDates.length > 50 ? 2 : 3;
lineStroke = params.width <= smallBreak ? 1.25 : lineStroke;
const dotRadius = params.width <= wideBreak ? 4 : 6;
series
.append( 'path' )
@ -648,7 +649,9 @@ export const drawLines = ( node, data, params ) => {
} )
.attr( 'd', d => params.line( d.values ) );
params.width / params.uniqueDates.length > 36 &&
const minDataPointSpacing = 36;
params.width / params.uniqueDates.length > minDataPointSpacing &&
series
.selectAll( 'circle' )
.data( ( d, i ) => d.values.map( row => ( { ...row, i, visible: d.visible, key: d.key } ) ) )
@ -668,8 +671,8 @@ export const drawLines = ( node, data, params ) => {
.attr( 'aria-label', d => {
const label = d.label
? d.label
: params.tooltipFormat( d.date instanceof Date ? d.date : new Date( d.date ) );
return `${ label } ${ formatCurrency( d.value ) }`;
: params.tooltipLabelFormat( d.date instanceof Date ? d.date : new Date( d.date ) );
return `${ label } ${ params.tooltipValueFormat( d.value ) }`;
} )
.on( 'focus', ( d, i, nodes ) => {
const position = calculateTooltipPosition(
@ -750,7 +753,7 @@ export const drawBars = ( node, data, params ) => {
'aria-label',
d =>
params.mode === 'item-comparison'
? params.tooltipFormat( d.date instanceof Date ? d.date : new Date( d.date ) )
? params.tooltipLabelFormat( d.date instanceof Date ? d.date : new Date( d.date ) )
: null
);
@ -786,7 +789,7 @@ export const drawBars = ( node, data, params ) => {
.attr( 'tabindex', '0' )
.attr( 'aria-label', d => {
const label = params.mode === 'time-comparison' && d.label ? d.label : d.key;
return `${ label } ${ formatCurrency( d.value ) }`;
return `${ label } ${ params.tooltipValueFormat( d.value ) }`;
} )
.style( 'opacity', d => {
const opacity = d.focus ? 1 : 0.1;

View File

@ -1,6 +1,6 @@
/** @format */
export { default as Chart } from './chart';
export { default as ChartLegend } from './chart/legend';
export { default as ChartLegend } from './d3chart/legend';
export { default as ChartPlaceholder } from './chart/placeholder';
export { default as D3Chart } from './chart/charts';
export { default as D3Chart } from './d3chart';

View File

@ -10,6 +10,7 @@ import { find, forEach, isNull } from 'lodash';
*/
import { appendTimestamp, getCurrentDates, getIntervalForQuery } from '@woocommerce/date';
import { flattenFilters, getActiveFiltersFromQuery, getUrlKey } from '@woocommerce/navigation';
import { formatCurrency } from '@woocommerce/currency';
/**
* Internal dependencies
@ -247,3 +248,24 @@ export function getReportChartData( endpoint, dataType, query, select ) {
return { ...response, data: { totals, intervals } };
}
/**
* Returns a formatting function or string to be used by d3-format
*
* @param {String} type Type of number, 'currency', 'number', 'percent', 'average'
* @return {String|Function} returns a number format based on the type or an overriding formatting function
*/
export function getTooltipValueFormat( type ) {
switch ( type ) {
case 'currency':
return formatCurrency;
case 'percent':
return '.0%';
case 'number':
return ',';
case 'average':
return ',.2r';
default:
return ',';
}
}

View File

@ -27,13 +27,6 @@ Format to parse dates into d3 time format
Current path
### `pointLabelFormat`
- Type: String
- Default: null
Date format of the point labels (might be used in tooltips and ARIA properties).
### `query`
- Type: Object
@ -41,7 +34,7 @@ Date format of the point labels (might be used in tooltips and ARIA properties).
The query string represented in object form
### `tooltipFormat`
### `tooltipLabelFormat`
- Type: String
- Default: `'%B %d, %Y'`
@ -49,12 +42,19 @@ The query string represented in object form
A datetime formatting string to format the date displayed as the title of the toolip
if `tooltipTitle` is missing, passed to d3TimeFormat.
### `tooltipValueFormat`
- Type: String | Function
- Default: `','`
A number formatting string or function to format the value displayed in the tooltips.
### `tooltipTitle`
- Type: String
- Default: null
A string to use as a title for the tooltip. Takes preference over `tooltipFormat`.
A string to use as a title for the tooltip. Takes preference over `tooltipLabelFormat`.
### `xFormat`
@ -231,7 +231,7 @@ ARIA properties.
The list of labels for this chart.
### `tooltipFormat`
### `tooltipLabelFormat`
- Type: String
- Default: `'%B %d, %Y'`
@ -251,7 +251,7 @@ The position where to render the tooltip can be `over` the chart or `below` the
- Type: String
- Default: null
A string to use as a title for the tooltip. Takes preference over `tooltipFormat`.
A string to use as a title for the tooltip. Takes preference over `tooltipLabelFormat`.
### `type`

View File

@ -8637,8 +8637,7 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"aproba": {
"version": "1.2.0",
@ -8656,13 +8655,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -8675,18 +8672,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@ -8789,8 +8783,7 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@ -8800,7 +8793,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -8813,20 +8805,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@ -8843,7 +8832,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -8916,8 +8904,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@ -8927,7 +8914,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -9003,8 +8989,7 @@
},
"safe-buffer": {
"version": "5.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@ -9033,8 +9018,8 @@
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -9052,7 +9037,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -9091,13 +9075,11 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"optional": true
"bundled": true
},
"yallist": {
"version": "3.0.2",
"bundled": true,
"optional": true
"bundled": true
}
}
},

View File

@ -437,16 +437,14 @@ export const weekTicksThreshold = 9;
* @return {String} Current interval.
*/
export function getDateFormatsForInterval( interval, ticks = 0 ) {
let pointLabelFormat = 'F j, Y';
let tooltipFormat = '%B %d %Y';
let tooltipLabelFormat = '%B %d %Y';
let xFormat = '%Y-%m-%d';
let x2Format = '%b %Y';
let tableFormat = 'm/d/Y';
switch ( interval ) {
case 'hour':
pointLabelFormat = 'h A';
tooltipFormat = '%I %p';
tooltipLabelFormat = '%I %p';
xFormat = '%I %p';
tableFormat = 'h A';
break;
@ -466,25 +464,22 @@ export function getDateFormatsForInterval( interval, ticks = 0 ) {
xFormat = '%b';
x2Format = '%Y';
}
tooltipFormat = __( 'Week of %B %d %Y', 'wc-admin' );
tooltipLabelFormat = __( 'Week of %B %d %Y', 'wc-admin' );
break;
case 'quarter':
case 'month':
pointLabelFormat = 'F Y';
tooltipFormat = '%B %Y';
tooltipLabelFormat = '%B %Y';
xFormat = '%b';
x2Format = '%Y';
break;
case 'year':
pointLabelFormat = 'Y';
tooltipFormat = '%Y';
tooltipLabelFormat = '%Y';
xFormat = '%Y';
break;
}
return {
pointLabelFormat,
tooltipFormat,
tooltipLabelFormat,
xFormat,
x2Format,
tableFormat,