Merge pull request woocommerce/woocommerce-admin#381 from woocommerce/add/chart-legend-order
Chart Component: remove chart legend ordering for layout=standard and color scales
This commit is contained in:
commit
e38284eb96
|
@ -305,7 +305,7 @@ export class RevenueReport extends Component {
|
|||
|
||||
return (
|
||||
<Card title="">
|
||||
<Chart data={ chartData } title={ selectedChart.label } />
|
||||
<Chart data={ chartData } title={ selectedChart.label } dateParser={ '%Y-%m-%d' } />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ A simple D3 line and bar chart component for timeseries data in React.
|
|||
```jsx
|
||||
<Chart
|
||||
data={ timeseries }
|
||||
dateParser={ '%Y-%m-%d' }
|
||||
tooltipFormat={ 'Date is %Y-%m-%d' }
|
||||
type={ 'bar' }
|
||||
xFormat={ '%d' }
|
||||
|
@ -20,7 +21,7 @@ This component accepts timeseries `data` prop in the following format (with date
|
|||
```
|
||||
[
|
||||
{
|
||||
date: '%Y-%m-%dT%H:%M:%S', // string
|
||||
date: '%Y-%m-%d', // string in `dateParser` format (see below)
|
||||
category1: value, // number
|
||||
category2: value, // number
|
||||
...
|
||||
|
@ -32,7 +33,7 @@ For example:
|
|||
```
|
||||
[
|
||||
{
|
||||
date: '2018-06-25T00:00:00',
|
||||
date: '2018-06-25',
|
||||
category1: 1234.56,
|
||||
category2: 9876,
|
||||
...
|
||||
|
@ -47,6 +48,7 @@ Required props are marked with `*`.
|
|||
Name | Type | Default | Description
|
||||
--- | --- | --- | ---
|
||||
`data`* | `array` | none | An array of data as specified above(below)
|
||||
`dateParser` | `string` | `%Y-%m-%dT%H:%M:%S` | Format to parse datetimes in the data
|
||||
`type`* | `string` | `line` | Chart type of either `line` or `bar`
|
||||
`title` | `string` | none | Chart title
|
||||
`tooltipFormat` | `string` | `%Y-%m-%d` | Title and format of the tooltip title
|
||||
|
|
|
@ -7,7 +7,7 @@ import { isEqual } from 'lodash';
|
|||
import { Component, createRef } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { timeFormat as d3TimeFormat } from 'd3-time-format';
|
||||
import { timeFormat as d3TimeFormat, utcParse as d3UTCParse } from 'd3-time-format';
|
||||
import { select as d3Select } from 'd3-selection';
|
||||
|
||||
/**
|
||||
|
@ -91,6 +91,7 @@ class D3Chart extends Component {
|
|||
const {
|
||||
colorScheme,
|
||||
data,
|
||||
dateParser,
|
||||
height,
|
||||
margin,
|
||||
orderedKeys,
|
||||
|
@ -111,7 +112,8 @@ class D3Chart extends Component {
|
|||
const lineData = getLineData( data, newOrderedKeys );
|
||||
const yMax = getYMax( lineData );
|
||||
const yScale = getYScale( adjHeight, yMax );
|
||||
const uniqueDates = getUniqueDates( lineData );
|
||||
const parseDate = d3UTCParse( dateParser );
|
||||
const uniqueDates = getUniqueDates( lineData, parseDate );
|
||||
const xLineScale = getXLineScale( uniqueDates, adjWidth );
|
||||
const xScale = getXScale( uniqueDates, adjWidth );
|
||||
return {
|
||||
|
@ -122,6 +124,7 @@ class D3Chart extends Component {
|
|||
lineData,
|
||||
margin,
|
||||
orderedKeys: newOrderedKeys,
|
||||
parseDate,
|
||||
scale,
|
||||
tooltipFormat: d3TimeFormat( tooltipFormat ),
|
||||
type,
|
||||
|
@ -175,6 +178,10 @@ D3Chart.propTypes = {
|
|||
* An array of data.
|
||||
*/
|
||||
data: PropTypes.array.isRequired,
|
||||
/**
|
||||
* Format to parse dates into d3 time format
|
||||
*/
|
||||
dateParser: PropTypes.string.isRequired,
|
||||
/**
|
||||
* Relative viewpoirt height of the `svg`.
|
||||
*/
|
||||
|
@ -224,6 +231,7 @@ D3Chart.propTypes = {
|
|||
|
||||
D3Chart.defaultProps = {
|
||||
data: [],
|
||||
dateParser: '%Y-%m-%dT%H:%M:%S',
|
||||
height: 200,
|
||||
margin: {
|
||||
bottom: 30,
|
||||
|
|
|
@ -93,7 +93,7 @@ class WidgetCharts extends Component {
|
|||
</ul>
|
||||
</Card>
|
||||
<Card title={ __( 'Store Charts', 'wc-admin' ) }>
|
||||
<Chart data={ this.state.someOrders } title="Example Chart" />
|
||||
<Chart data={ this.state.someOrders } title="Example Chart" layout="comparison" />
|
||||
</Card>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -17,22 +17,24 @@ import { gap, gaplarge } from 'stylesheets/abstracts/_variables.scss';
|
|||
|
||||
const WIDE_BREAKPOINT = 1100;
|
||||
|
||||
function getOrderedKeys( data ) {
|
||||
return [
|
||||
function getOrderedKeys( props ) {
|
||||
const updatedKeys = [
|
||||
...new Set(
|
||||
data.reduce( ( accum, curr ) => {
|
||||
props.data.reduce( ( accum, curr ) => {
|
||||
Object.keys( curr ).forEach( key => key !== 'date' && accum.push( key ) );
|
||||
return accum;
|
||||
}, [] )
|
||||
),
|
||||
]
|
||||
.map( key => ( {
|
||||
key,
|
||||
total: data.reduce( ( a, c ) => a + c[ key ], 0 ),
|
||||
visible: true,
|
||||
focus: true,
|
||||
} ) )
|
||||
.sort( ( a, b ) => b.total - a.total );
|
||||
].map( key => ( {
|
||||
key,
|
||||
total: props.data.reduce( ( a, c ) => a + c[ key ], 0 ),
|
||||
visible: true,
|
||||
focus: true,
|
||||
} ) );
|
||||
if ( props.layout === 'comparison' ) {
|
||||
updatedKeys.sort( ( a, b ) => b.total - a.total );
|
||||
}
|
||||
return updatedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +49,7 @@ class Chart extends Component {
|
|||
const calcGap = wpWrap > 782 ? gaplarge.match( /\d+/ )[ 0 ] : gap.match( /\d+/ )[ 0 ];
|
||||
this.state = {
|
||||
data: props.data,
|
||||
orderedKeys: getOrderedKeys( props.data ),
|
||||
orderedKeys: getOrderedKeys( props ),
|
||||
visibleData: [ ...props.data ],
|
||||
width: wpBody - 2 * calcGap,
|
||||
};
|
||||
|
@ -59,7 +61,7 @@ class Chart extends Component {
|
|||
|
||||
componentDidUpdate( prevProps ) {
|
||||
const { data } = this.props;
|
||||
const orderedKeys = getOrderedKeys( data );
|
||||
const orderedKeys = getOrderedKeys( this.props );
|
||||
if ( ! isEqual( [ ...data ].sort(), [ ...prevProps.data ].sort() ) ) {
|
||||
/* eslint-disable react/no-did-update-set-state */
|
||||
this.setState( {
|
||||
|
@ -129,10 +131,18 @@ class Chart extends Component {
|
|||
|
||||
render() {
|
||||
const { orderedKeys, visibleData, width } = this.state;
|
||||
const legendDirection =
|
||||
this.props.layout === 'standard' && width > WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
const chartDirection =
|
||||
this.props.layout === 'comparison' && width > WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
const {
|
||||
dateParser,
|
||||
layout,
|
||||
title,
|
||||
tooltipFormat,
|
||||
type,
|
||||
xFormat,
|
||||
x2Format,
|
||||
yFormat,
|
||||
} = this.props;
|
||||
const legendDirection = layout === 'standard' && width > WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
const chartDirection = layout === 'comparison' && width > WIDE_BREAKPOINT ? 'row' : 'column';
|
||||
const legend = (
|
||||
<Legend
|
||||
className={ 'woocommerce-chart__legend' }
|
||||
|
@ -152,7 +162,7 @@ class Chart extends Component {
|
|||
return (
|
||||
<div className="woocommerce-chart" ref={ this.chartRef }>
|
||||
<div className="woocommerce-chart__header">
|
||||
<span className="woocommerce-chart__title">{ this.props.title }</span>
|
||||
<span className="woocommerce-chart__title">{ title }</span>
|
||||
{ width > WIDE_BREAKPOINT && legendDirection === 'row' && legend }
|
||||
</div>
|
||||
<div
|
||||
|
@ -165,15 +175,16 @@ class Chart extends Component {
|
|||
<D3Chart
|
||||
colorScheme={ d3InterpolateViridis }
|
||||
data={ visibleData }
|
||||
dateParser={ dateParser }
|
||||
height={ 300 }
|
||||
margin={ margin }
|
||||
orderedKeys={ orderedKeys }
|
||||
tooltipFormat={ this.props.tooltipFormat }
|
||||
type={ this.props.type }
|
||||
tooltipFormat={ tooltipFormat }
|
||||
type={ type }
|
||||
width={ chartDirection === 'row' ? width - 320 : width }
|
||||
xFormat={ this.props.xFormat }
|
||||
x2Format={ this.props.x2Format }
|
||||
yFormat={ this.props.yFormat }
|
||||
xFormat={ xFormat }
|
||||
x2Format={ x2Format }
|
||||
yFormat={ yFormat }
|
||||
/>
|
||||
</div>
|
||||
{ width < WIDE_BREAKPOINT && <div className="woocommerce-chart__footer">{ legend }</div> }
|
||||
|
@ -187,6 +198,10 @@ Chart.propTypes = {
|
|||
* An array of data.
|
||||
*/
|
||||
data: PropTypes.array.isRequired,
|
||||
/**
|
||||
* Format to parse dates into d3 time format
|
||||
*/
|
||||
dateParser: PropTypes.string.isRequired,
|
||||
/**
|
||||
* A datetime formatting string to format the title of the toolip, passed to d3TimeFormat.
|
||||
*/
|
||||
|
@ -219,6 +234,7 @@ Chart.propTypes = {
|
|||
|
||||
Chart.defaultProps = {
|
||||
data: [],
|
||||
dateParser: '%Y-%m-%dT%H:%M:%S',
|
||||
tooltipFormat: '%Y-%m-%d',
|
||||
xFormat: '%d',
|
||||
x2Format: '%b %Y',
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* @format
|
||||
*/
|
||||
// import { noop } from 'lodash';
|
||||
import { utcParse as d3UTCParse } from 'd3-time-format';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -21,7 +22,6 @@ import {
|
|||
getYMax,
|
||||
getYScale,
|
||||
getYTickOffset,
|
||||
parseDate,
|
||||
} from '../utils';
|
||||
|
||||
const orderedKeys = [
|
||||
|
@ -64,10 +64,11 @@ const orderedDates = [
|
|||
'2018-06-03T00:00:00',
|
||||
'2018-06-04T00:00:00',
|
||||
];
|
||||
const parseDate = d3UTCParse( '%Y-%m-%dT%H:%M:%S' );
|
||||
const testUniqueKeys = getUniqueKeys( dummyOrders );
|
||||
const testOrderedKeys = getOrderedKeys( dummyOrders, testUniqueKeys );
|
||||
const testLineData = getLineData( dummyOrders, testOrderedKeys );
|
||||
const testUniqueDates = getUniqueDates( testLineData );
|
||||
const testUniqueDates = getUniqueDates( testLineData, parseDate );
|
||||
const testXScale = getXScale( testUniqueDates, 100 );
|
||||
const testXLineScale = getXLineScale( testUniqueDates, 100 );
|
||||
const testYMax = getYMax( testLineData );
|
||||
|
|
|
@ -15,14 +15,11 @@ import {
|
|||
} from 'd3-scale';
|
||||
import { mouse as d3Mouse, select as d3Select } from 'd3-selection';
|
||||
import { line as d3Line } from 'd3-shape';
|
||||
import { utcParse as d3UTCParse } from 'd3-time-format';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { formatCurrency } from 'lib/currency';
|
||||
|
||||
export const parseDate = d3UTCParse( '%Y-%m-%dT%H:%M:%S' );
|
||||
|
||||
function decodeSymbol( str ) {
|
||||
return str.replace( /&#(\d+);/g, ( match, dec ) => String.fromCharCode( dec ) );
|
||||
}
|
||||
|
@ -87,9 +84,10 @@ export const getLineData = ( data, orderedKeys ) =>
|
|||
/**
|
||||
* Describes `getUniqueDates`
|
||||
* @param {array} lineData - from `GetLineData`
|
||||
* @param {function} parseDate - D3 time format parser
|
||||
* @returns {array} an array of unique date values sorted from earliest to latest
|
||||
*/
|
||||
export const getUniqueDates = lineData => {
|
||||
export const getUniqueDates = ( lineData, parseDate ) => {
|
||||
return [
|
||||
...new Set(
|
||||
lineData.reduce( ( accum, { values } ) => {
|
||||
|
@ -101,10 +99,21 @@ export const getUniqueDates = lineData => {
|
|||
};
|
||||
|
||||
export const getColor = ( key, params ) => {
|
||||
const keyValue =
|
||||
params.orderedKeys.length > 1
|
||||
? findIndex( params.orderedKeys, d => d.key === key ) / ( params.orderedKeys.length - 1 )
|
||||
: 0;
|
||||
const smallColorScales = [
|
||||
[],
|
||||
[ 0.5 ],
|
||||
[ 0.333, 0.667 ],
|
||||
[ 0.2, 0.5, 0.8 ],
|
||||
[ 0.12, 0.375, 0.625, 0.88 ],
|
||||
];
|
||||
let keyValue = 0;
|
||||
const len = params.orderedKeys.length;
|
||||
const idx = findIndex( params.orderedKeys, d => d.key === key );
|
||||
if ( len < 5 ) {
|
||||
keyValue = smallColorScales[ len ][ idx ];
|
||||
} else {
|
||||
keyValue = idx / ( params.orderedKeys.length - 1 );
|
||||
}
|
||||
return params.colorScheme( keyValue );
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue