From 70508686382a254073fbfecefbf4046d31828953 Mon Sep 17 00:00:00 2001 From: Robert Elliott Date: Tue, 22 May 2018 15:00:06 +0200 Subject: [PATCH 1/2] d3 base component --- .../client/components/d3/base/README.md | 0 .../client/components/d3/base/index.js | 80 +++++++++++++++++++ .../client/components/d3/base/style.scss | 3 + .../client/components/d3/base/test/index.js | 45 +++++++++++ .../client/components/header/index.js | 2 +- plugins/woocommerce-admin/package.json | 1 + 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce-admin/client/components/d3/base/README.md create mode 100644 plugins/woocommerce-admin/client/components/d3/base/index.js create mode 100644 plugins/woocommerce-admin/client/components/d3/base/style.scss create mode 100644 plugins/woocommerce-admin/client/components/d3/base/test/index.js diff --git a/plugins/woocommerce-admin/client/components/d3/base/README.md b/plugins/woocommerce-admin/client/components/d3/base/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/woocommerce-admin/client/components/d3/base/index.js b/plugins/woocommerce-admin/client/components/d3/base/index.js new file mode 100644 index 00000000000..1a93ff40b9e --- /dev/null +++ b/plugins/woocommerce-admin/client/components/d3/base/index.js @@ -0,0 +1,80 @@ +/** @format */ + +/** + * External dependencies + */ + +import { Component } from '@wordpress/element'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { select as d3Select } from 'd3-selection'; + +class D3Base extends Component { + constructor() { + super( ...arguments ); + this.updateParams = this.updateParams.bind( this ); + this.setNodeRef = this.setNodeRef.bind( this ); + this.state = {}; + } + + componentDidMount() { + window.addEventListener( 'resize', this.updateParams ); + this.updateParams(); + } + + componentWillReceiveProps( nextProps ) { + this.updateParams( nextProps ); + } + + componentDidUpdate() { + this.draw(); + } + + componentWillUnmount() { + window.removeEventListener( 'resize', this.updateParams ); + delete this.node; + } + + updateParams( nextProps ) { + const getParams = ( nextProps && nextProps.getParams ) || this.props.getParams; + this.setState( getParams( this.node ), this.draw ); + } + + draw() { + this.props.drawChart( this.createNewContext(), this.state ); + } + + createNewContext() { + const { className } = this.props; + const { width, height } = this.state; + + d3Select( this.node ) + .selectAll( 'svg' ) + .remove(); + const newNode = d3Select( this.node ) + .append( 'svg' ) + .attr( 'class', `${ className }__viewbox` ) + .attr( 'viewBox', `0 0 ${ width } ${ height }` ) + .attr( 'preserveAspectRatio', 'xMidYMid meet' ) + .append( 'g' ); + return newNode; + } + + setNodeRef( node ) { + this.node = node; + } + + render() { + return ( +
+ ); + } +} + +D3Base.propTypes = { + className: PropTypes.string, + drawChart: PropTypes.func.isRequired, + getParams: PropTypes.func.isRequired, +}; + +export default D3Base; diff --git a/plugins/woocommerce-admin/client/components/d3/base/style.scss b/plugins/woocommerce-admin/client/components/d3/base/style.scss new file mode 100644 index 00000000000..6d2f519d07c --- /dev/null +++ b/plugins/woocommerce-admin/client/components/d3/base/style.scss @@ -0,0 +1,3 @@ +.d3-base { + width:100%; +} diff --git a/plugins/woocommerce-admin/client/components/d3/base/test/index.js b/plugins/woocommerce-admin/client/components/d3/base/test/index.js new file mode 100644 index 00000000000..5b88312041e --- /dev/null +++ b/plugins/woocommerce-admin/client/components/d3/base/test/index.js @@ -0,0 +1,45 @@ +/** + * External dependencies + * + * @format + */ +import { shallow, mount } from 'enzyme'; +import { noop } from 'lodash'; + +/** + * Internal dependencies + */ +import D3Base from '../index'; + +describe( 'D3base', () => { + const shallowWithoutLifecycle = arg => shallow( arg, { disableLifecycleMethods: true } ); + + test( 'should have d3Base class', () => { + const base = shallowWithoutLifecycle( ); + expect( base.find( '.d3-base' ) ).toHaveLength( 1 ); + } ); + + test( 'should render an svg', () => { + const base = mount( ); + expect( base.render().find( 'svg' ) ).toHaveLength( 1 ); + } ); + + test( 'should render a result of the drawChart prop', () => { + const drawChart = svg => { + return svg.append( 'circle' ); + }; + const base = mount( ); + expect( base.render().find( 'circle' ) ).toHaveLength( 1 ); + } ); + + test( 'should pass a property of getParams output to drawChart function', () => { + const getParams = () => ( { + tagName: 'circle', + } ); + const drawChart = ( svg, params ) => { + return svg.append( params.tagName ); + }; + const base = mount( ); + expect( base.render().find( 'circle' ) ).toHaveLength( 1 ); + } ); +} ); diff --git a/plugins/woocommerce-admin/client/components/header/index.js b/plugins/woocommerce-admin/client/components/header/index.js index 8733e62a2ee..ea5bffc9fef 100644 --- a/plugins/woocommerce-admin/client/components/header/index.js +++ b/plugins/woocommerce-admin/client/components/header/index.js @@ -46,7 +46,7 @@ Header.propTypes = { Header.defaultProps = { onToggle: noop, -} +}; export default function( props ) { return ( diff --git a/plugins/woocommerce-admin/package.json b/plugins/woocommerce-admin/package.json index 1b1052af1b3..8012cfb962e 100755 --- a/plugins/woocommerce-admin/package.json +++ b/plugins/woocommerce-admin/package.json @@ -63,6 +63,7 @@ }, "dependencies": { "classnames": "^2.2.5", + "d3-selection": "^1.3.0", "lodash": "^4.17.10", "prop-types": "^15.6.1", "react-slot-fill": "^2.0.1" From 8216841490f2a7503fb039625229bdb42758f0c1 Mon Sep 17 00:00:00 2001 From: Robert Elliott Date: Fri, 1 Jun 2018 16:35:51 +0200 Subject: [PATCH 2/2] adding README from Calypso --- .../client/components/d3/base/README.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plugins/woocommerce-admin/client/components/d3/base/README.md b/plugins/woocommerce-admin/client/components/d3/base/README.md index e69de29bb2d..7c6a227cdc1 100644 --- a/plugins/woocommerce-admin/client/components/d3/base/README.md +++ b/plugins/woocommerce-admin/client/components/d3/base/README.md @@ -0,0 +1,23 @@ +# D3 Base Component + +Integrate React Lifecyle methods with d3.js charts. + +### Base Component Responsibilities + +* Create and manage mounting and unmounting parent `div` and `svg` +* Handle resize events, resulting re-renders, and event listeners +* Handle re-renders as a result of new props + +## Props + +### className +{ string } A class to be applied to the parent `div` + +### getParams( node ) +{ function } A function returning an object containing required properties for drawing a chart. This object is created before re-render, making it an ideal place for calculating scales and other props or user input based properties. +* `svg` { node } The parent `div`. Useful for calculating available widths + +### drawChart( svg, params ) +{ function } draw the chart +* `svg` { node } Base element +* `params` { Object } Properties created by the `getParams` function \ No newline at end of file