Merge pull request woocommerce/woocommerce-admin#184 from woocommerce/add/chart-readme-docs

D3 Chart: README and JSDocs
This commit is contained in:
Robert Elliott 2018-07-11 17:14:36 +02:00 committed by GitHub
commit 8883966ff0
7 changed files with 137 additions and 19 deletions

View File

@ -0,0 +1,54 @@
D3 Chart Component
===
A simple D3 line and bar chart component for timeseries data in React.
## Usage
```jsx
<D3Chart
className="woocommerce-dashboard__*"
data={ timeseries }
height={ 200 }
margin={ { bottom: 30, left: 40, right: 0, top: 20 } }
type={ 'bar' }
width={ 600 }
/>
```
### Expected Data Format
This component accepts timeseries `data` prop in the following format (with dates following the [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601)):
```
[
{
date: 'YYYY-mm-dd', // string
category1: value, // number
category2: value, // number
...
},
...
]
```
For example:
```
[
{
date: '2018-06-25',
category1: 1234.56,
category2: 9876,
...
},
...
]
```
### Props
Required props are marked with `*`.
Name | Type | Default | Description
--- | --- | --- | ---
`data`* | `array` | none | An array of data as specified above
`height` | `number` | `200` | Relative viewpoirt height of the `svg`
`margin` | `object` | `{ bottom: 30, left: 40, right: 0, top: 20 }` | Margins for axis and chart padding
`type`* | `string` | `line` | Chart type of either `line` or `bar`
`width` | `number` | `600` | Relative viewport width of the `svg`

View File

@ -34,17 +34,7 @@ import {
getYTickOffset,
} from './utils';
const D3Chart = ( {
className,
data,
height,
margin,
timeseries,
type,
xFormat,
yFormat,
width,
} ) => {
const D3Chart = ( { className, data, height, margin, type, xFormat, yFormat, width } ) => {
const drawChart = ( node, params ) => {
const g = node
.select( 'svg' )
@ -90,7 +80,7 @@ const D3Chart = ( {
uniqueDates,
uniqueKeys,
width: calculatedWidth,
xFormat: timeseries ? d3TimeFormat( xFormat ) : d3Format( xFormat ),
xFormat: d3TimeFormat( xFormat ),
xGroupScale: getXGroupScale( orderedKeys, xScale ),
xLineScale,
xScale,
@ -116,7 +106,6 @@ D3Chart.propTypes = {
right: PropTypes.number,
top: PropTypes.number,
} ),
timeseries: PropTypes.bool,
type: PropTypes.oneOf( [ 'bar', 'line' ] ),
width: PropTypes.number,
xFormat: PropTypes.string,
@ -131,7 +120,6 @@ D3Chart.defaultProps = {
right: 0,
top: 20,
},
timeseries: true,
type: 'line',
width: 600,
xFormat: '%Y-%m-%d',

View File

@ -4,7 +4,7 @@
* @format
*/
export const dummyOrders = [
export default [
{
date: '2018-05-30',
Polo: 2704659,

View File

@ -8,7 +8,7 @@
/**
* Internal dependencies
*/
import { dummyOrders } from '../dummy';
import dummyOrders from './fixtures/dummy';
import {
getColorScale,
getDateSpaces,

View File

@ -20,6 +20,11 @@ import { timeFormat as d3TimeFormat, utcParse as d3UTCParse } from 'd3-time-form
export const parseDate = d3UTCParse( '%Y-%m-%d' );
/**
* Describes `getUniqueKeys`
* @param {array} data - The chart component's `data` prop.
* @returns {array} of unique category keys
*/
export const getUniqueKeys = data => {
return [
...new Set(
@ -31,6 +36,12 @@ export const getUniqueKeys = data => {
];
};
/**
* Describes `getOrderedKeys`
* @param {array} data - The chart component's `data` prop.
* @param {array} uniqueKeys - from `getUniqueKeys`.
* @returns {array} of unique category keys ordered by cumulative total value
*/
export const getOrderedKeys = ( data, uniqueKeys ) =>
uniqueKeys
.map( key => ( {
@ -40,6 +51,12 @@ export const getOrderedKeys = ( data, uniqueKeys ) =>
.sort( ( a, b ) => b.total - a.total )
.map( d => d.key );
/**
* Describes `getLineData`
* @param {array} data - The chart component's `data` prop.
* @param {array} orderedKeys - from `getOrderedKeys`.
* @returns {array} an array objects with a category `key` and an array of `values` with `date` and `value` properties
*/
export const getLineData = ( data, orderedKeys ) =>
orderedKeys.map( key => ( {
key,
@ -49,6 +66,11 @@ export const getLineData = ( data, orderedKeys ) =>
} ) ),
} ) );
/**
* Describes `getUniqueDates`
* @param {array} lineData - from `GetLineData`
* @returns {array} an array of unique date values sorted from earliest to latest
*/
export const getUniqueDates = lineData => {
return [
...new Set(
@ -60,44 +82,99 @@ export const getUniqueDates = lineData => {
].sort( ( a, b ) => parseDate( a ) - parseDate( b ) );
};
/**
* Describes getXScale
* @param {array} uniqueDates - from `getUniqueDates`
* @param {number} width - calculated width of the charting space
* @returns {function} a D3 scale of the dates
*/
export const getXScale = ( uniqueDates, width ) =>
d3ScaleBand()
.domain( uniqueDates )
.rangeRound( [ 0, width ] )
.paddingInner( 0.1 );
/**
* Describes getXGroupScale
* @param {array} orderedKeys - from `getOrderedKeys`
* @param {function} xScale - from `getXScale`
* @returns {function} a D3 scale for each category within the xScale range
*/
export const getXGroupScale = ( orderedKeys, xScale ) =>
d3ScaleBand()
.domain( orderedKeys )
.rangeRound( [ 0, xScale.bandwidth() ] )
.padding( 0.07 );
/**
* Describes getXLineScale
* @param {array} uniqueDates - from `getUniqueDates`
* @param {number} width - calculated width of the charting space
* @returns {function} a D3 scaletime for each date
*/
export const getXLineScale = ( uniqueDates, width ) =>
d3ScaleTime()
.domain( [ new Date( uniqueDates[ 0 ] ), new Date( uniqueDates[ uniqueDates.length - 1 ] ) ] )
.rangeRound( [ 0, width ] );
/**
* Describes getYMax
* @param {array} lineData - from `getLineData`
* @returns {number} the maximum value in the timeseries multiplied by 4/3
*/
export const getYMax = lineData =>
Math.round( 4 / 3 * d3Max( lineData, d => d3Max( d.values.map( date => date.value ) ) ) );
/**
* Describes getYScale
* @param {number} height - calculated height of the charting space
* @param {number} yMax - from `getYMax`
* @returns {function} the D3 linear scale from 0 to the value from `getYMax`
*/
export const getYScale = ( height, yMax ) =>
d3ScaleLinear()
.domain( [ 0, yMax ] )
.rangeRound( [ height, 0 ] );
/**
* Describes getyTickOffset
* @param {number} height - calculated height of the charting space
* @param {number} scale - ratio of the expected width to calculated width (given the viewbox)
* @param {number} yMax - from `getYMax`
* @returns {function} the D3 linear scale from 0 to the value from `getYMax`, offset by 12 pixels down
*/
export const getYTickOffset = ( height, scale, yMax ) =>
d3ScaleLinear()
.domain( [ 0, yMax ] )
.rangeRound( [ height + scale * 12, scale * 12 ] );
/**
* Describes getyTickOffset
* @param {array} orderedKeys - from `getOrderedKeys`
* @returns {function} the D3 ordinal scale of the categories
*/
export const getColorScale = orderedKeys =>
d3ScaleOrdinal().range( d3Range( 0, 1.1, 100 / ( orderedKeys.length - 1 ) / 100 ) );
/**
* Describes getyTickOffset
* @param {array} data - The chart component's `data` prop.
* @param {function} xLineScale - from `getXLineScale`.
* @param {function} yScale - from `getYScale`.
* @returns {function} the D3 line function for plotting all category values
*/
export const getLine = ( data, xLineScale, yScale ) =>
d3Line()
.x( d => xLineScale( new Date( d.date ) ) )
.y( d => yScale( d.value ) );
/**
* Describes getDateSpaces
* @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
*/
export const getDateSpaces = ( uniqueDates, width, xLineScale ) =>
uniqueDates.map( ( d, i ) => {
const xNow = xLineScale( new Date( d ) );

View File

@ -10,7 +10,7 @@ import { Component } from '@wordpress/element';
*/
import Card from 'components/card';
import D3Chart from 'components/d3/charts';
import { dummyOrders } from 'components/d3/charts/dummy';
import dummyOrders from 'components/d3/charts/test/fixtures/dummy';
class WidgetCharts extends Component {
constructor() {

View File

@ -4,8 +4,7 @@
"client/**/*.js",
"!**/node_modules/**",
"!**/vendor/**",
"!**/test/**",
"!client/**/dummy.js"
"!**/test/**"
],
"moduleDirectories": ["node_modules", "<rootDir>/client"],
"moduleNameMapper": {