Merge pull request woocommerce/woocommerce-admin#184 from woocommerce/add/chart-readme-docs
D3 Chart: README and JSDocs
This commit is contained in:
commit
8883966ff0
|
@ -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`
|
|
@ -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',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @format
|
||||
*/
|
||||
|
||||
export const dummyOrders = [
|
||||
export default [
|
||||
{
|
||||
date: '2018-05-30',
|
||||
Polo: 2704659,
|
|
@ -8,7 +8,7 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { dummyOrders } from '../dummy';
|
||||
import dummyOrders from './fixtures/dummy';
|
||||
import {
|
||||
getColorScale,
|
||||
getDateSpaces,
|
||||
|
|
|
@ -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 ) );
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
"client/**/*.js",
|
||||
"!**/node_modules/**",
|
||||
"!**/vendor/**",
|
||||
"!**/test/**",
|
||||
"!client/**/dummy.js"
|
||||
"!**/test/**"
|
||||
],
|
||||
"moduleDirectories": ["node_modules", "<rootDir>/client"],
|
||||
"moduleNameMapper": {
|
||||
|
|
Loading…
Reference in New Issue