From f5bd3af19c91076917139440a71320d102cfe2a8 Mon Sep 17 00:00:00 2001 From: Harrison Shoff Date: Mon, 22 May 2017 11:08:31 -0700 Subject: [PATCH 1/4] [shape] start BarGroup, add rest prop support to Bar --- packages/vx-shape/build/index.js | 18 ++++++++++++++ packages/vx-shape/package.json | 2 ++ packages/vx-shape/src/index.js | 2 ++ packages/vx-shape/src/shapes/AreaStack.js | 9 +------ packages/vx-shape/src/shapes/Bar.js | 6 +++++ packages/vx-shape/src/shapes/BarGroup.js | 29 ++++++++++++++++++++++ packages/vx-shape/src/util/callOrValue.js | 6 +++++ packages/vx-shape/test/BarGroup.test.js | 30 +++++++++++++++++++++++ 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 packages/vx-shape/src/shapes/BarGroup.js create mode 100644 packages/vx-shape/src/util/callOrValue.js create mode 100644 packages/vx-shape/test/BarGroup.test.js diff --git a/packages/vx-shape/build/index.js b/packages/vx-shape/build/index.js index 2ea0e1b04..3eef7c303 100644 --- a/packages/vx-shape/build/index.js +++ b/packages/vx-shape/build/index.js @@ -49,4 +49,22 @@ Object.defineProperty(exports, 'Bar', { } }); +var _BarGroup = require('./shapes/BarGroup'); + +Object.defineProperty(exports, 'BarGroup', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_BarGroup).default; + } +}); + +var _callOrValue = require('./util/callOrValue'); + +Object.defineProperty(exports, 'callOrValue', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_callOrValue).default; + } +}); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/packages/vx-shape/package.json b/packages/vx-shape/package.json index e0a726e29..ce107661f 100644 --- a/packages/vx-shape/package.json +++ b/packages/vx-shape/package.json @@ -23,9 +23,11 @@ "license": "MIT", "dependencies": { "@vx/curve": "0.0.112", + "@vx/group": "0.0.113", "@vx/point": "0.0.112", "classnames": "^2.2.5", "d3-shape": "^1.0.6", + "prop-types": "^15.5.10", "react": "^15.4.2" }, "devDependencies": { diff --git a/packages/vx-shape/src/index.js b/packages/vx-shape/src/index.js index a7564a0d3..6c64b663a 100644 --- a/packages/vx-shape/src/index.js +++ b/packages/vx-shape/src/index.js @@ -3,3 +3,5 @@ export { default as LinePath } from './shapes/LinePath'; export { default as AreaClosed } from './shapes/AreaClosed'; export { default as AreaStack } from './shapes/AreaStack'; export { default as Bar } from './shapes/Bar'; +export { default as BarGroup } from './shapes/BarGroup'; +export { default as callOrValue } from './util/callOrValue'; diff --git a/packages/vx-shape/src/shapes/AreaStack.js b/packages/vx-shape/src/shapes/AreaStack.js index f18a5705b..c49f415f4 100644 --- a/packages/vx-shape/src/shapes/AreaStack.js +++ b/packages/vx-shape/src/shapes/AreaStack.js @@ -1,15 +1,8 @@ import React from 'react'; import cx from 'classnames'; +import { callOrValue } from '../util/callOrValue'; import { area, stack as d3stack } from 'd3-shape'; -function callOrValue(maybeFn, d, i) { - if (typeof maybeFn === 'function') { - return maybeFn(d,i); - } - return maybeFn; -} - - export default ({ className, top = 0, diff --git a/packages/vx-shape/src/shapes/Bar.js b/packages/vx-shape/src/shapes/Bar.js index 27d93edd0..eb794a784 100644 --- a/packages/vx-shape/src/shapes/Bar.js +++ b/packages/vx-shape/src/shapes/Bar.js @@ -3,6 +3,7 @@ import cx from 'classnames'; export default ({ className, + data, x = 0, y = 0, width, @@ -18,6 +19,7 @@ export default ({ strokeLinejoin, strokeMiterlimit, strokeOpacity, + ...restProps, }) => { return ( { + ret[cur] = callOrValue(restProps[cur], data); + return ret; + }, {})} /> ); } diff --git a/packages/vx-shape/src/shapes/BarGroup.js b/packages/vx-shape/src/shapes/BarGroup.js new file mode 100644 index 000000000..6d0619b5a --- /dev/null +++ b/packages/vx-shape/src/shapes/BarGroup.js @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cx from 'classnames'; +import { Group } from '@vx/group'; +import Bar from './Bar'; + +BarGroup.propTypes = { + data: PropTypes.array.isRequired, + className: PropTypes.string, + top: PropTypes.number, + left: PropTypes.number, +}; + +export default function BarGroup({ + data, + className, + top, + left, +}) { + return ( + + + + ); +} diff --git a/packages/vx-shape/src/util/callOrValue.js b/packages/vx-shape/src/util/callOrValue.js new file mode 100644 index 000000000..b72965089 --- /dev/null +++ b/packages/vx-shape/src/util/callOrValue.js @@ -0,0 +1,6 @@ +export default function callOrValue(maybeFn, d, i) { + if (typeof maybeFn === 'function') { + return maybeFn(d,i); + } + return maybeFn; +} diff --git a/packages/vx-shape/test/BarGroup.test.js b/packages/vx-shape/test/BarGroup.test.js new file mode 100644 index 000000000..ea43b3b9b --- /dev/null +++ b/packages/vx-shape/test/BarGroup.test.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { BarGroup } from '../src'; +import { shallow, mount } from 'enzyme'; + +describe('', () => { + beforeEach(() => { + global.console.error = jest.fn() + }) + + test('it should be defined', () => { + expect(BarGroup).toBeDefined() + }) + + test('it should have className .vx-bar-group', () => { + const wrapper = shallow() + expect(wrapper.prop('className')).toEqual('vx-bar-group') + }) + + test('it should set className prop', () => { + const wrapper = shallow() + expect(wrapper.prop('className')).toEqual('vx-bar-group test') + }) + + test('it should require a data prop', () => { + const wrapper = mount() + console.log(wrapper) + expect(console.error).toBeCalled() + expect(console.error.mock.calls[0][0]).toEqual("Warning: Failed prop type: The prop `data` is marked as required in `BarGroup`, but its value is `undefined`.\n in BarGroup") + }) +}) From 6d250eac99eb881a7f6797e380bc13e8693e8ed2 Mon Sep 17 00:00:00 2001 From: Harry Shoff Date: Mon, 22 May 2017 14:42:29 -0700 Subject: [PATCH 2/4] [shape][scale] add , add tickFormat to scaleBand --- packages/vx-demo/components/tiles/bargroup.js | 94 ++++++++++++++++ packages/vx-demo/pages/bargroup.js | 104 ++++++++++++++++++ packages/vx-demo/pages/gallery.js | 35 +++++- packages/vx-scale/src/scales/band.js | 2 + packages/vx-shape/src/shapes/AreaStack.js | 2 +- packages/vx-shape/src/shapes/Bar.js | 1 + packages/vx-shape/src/shapes/BarGroup.js | 54 +++++++-- packages/vx-shape/test/BarGroup.test.js | 7 +- 8 files changed, 280 insertions(+), 19 deletions(-) create mode 100644 packages/vx-demo/components/tiles/bargroup.js create mode 100644 packages/vx-demo/pages/bargroup.js diff --git a/packages/vx-demo/components/tiles/bargroup.js b/packages/vx-demo/components/tiles/bargroup.js new file mode 100644 index 000000000..8dea5001b --- /dev/null +++ b/packages/vx-demo/components/tiles/bargroup.js @@ -0,0 +1,94 @@ +import React from 'react'; +import { BarGroup } from '@vx/shape'; +import { Group } from '@vx/group'; +import { AxisBottom } from '@vx/axis'; +import { cityTemperature } from '@vx/mock-data'; +import { scaleBand, scaleLinear, scaleOrdinal } from '@vx/scale'; +import { timeParse, timeFormat } from 'd3-time-format'; +import { extent, max } from 'd3-array'; + +const data = cityTemperature.slice(0, 8); +const keys = Object.keys(data[0]).filter(d => d !== 'date'); +const parseDate = timeParse("%Y%m%d"); +const format = timeFormat("%b %d"); +const formatDate = (date) => format(parseDate(date)); + +// accessors +const x0 = d => d.date; +const y = d => d.value; + +export default ({ + width, + height, + margin = { + top: 40 + } +}) => { + if (width < 10) return null; + + // bounds + const xMax = width; + const yMax = height - margin.top - 100; + + // // scales + const x0Scale = scaleBand({ + rangeRound: [0, xMax], + domain: data.map(x0), + padding: 0.2, + tickFormat: () => (val) => formatDate(val) + }); + const x1Scale = scaleBand({ + rangeRound: [0, x0Scale.bandwidth()], + domain: keys, + padding: .1 + }); + const yScale = scaleLinear({ + rangeRound: [yMax, 0], + domain: [0, max(data, (d) => { + return max(keys, (key) => d[key]) + })], + }); + const zScale = scaleOrdinal({ + domain: keys, + range: ['#aeeef8', '#e5fd3d', '#9caff6'] + }) + + return ( + + + + + )} + /> + + ); +} diff --git a/packages/vx-demo/pages/bargroup.js b/packages/vx-demo/pages/bargroup.js new file mode 100644 index 000000000..0defb2d76 --- /dev/null +++ b/packages/vx-demo/pages/bargroup.js @@ -0,0 +1,104 @@ +import React from 'react'; +import Show from '../components/show'; +import BarGroup from '../components/tiles/bargroup'; + +export default () => { + return ( + +{`import React from 'react'; +import { BarGroup } from '@vx/shape'; +import { Group } from '@vx/group'; +import { AxisBottom } from '@vx/axis'; +import { cityTemperature } from '@vx/mock-data'; +import { scaleBand, scaleLinear, scaleOrdinal } from '@vx/scale'; +import { timeParse, timeFormat } from 'd3-time-format'; +import { extent, max } from 'd3-array'; + +const data = cityTemperature.slice(0, 4); +const keys = Object.keys(data[0]).filter(d => d !== 'date'); +const parseDate = timeParse("%Y%m%d"); +const format = timeFormat("%b %d"); +const formatDate = (date) => format(parseDate(date)); + +// accessors +const x0 = d => d.date; +const y = d => d.value; + +export default ({ + width, + height, + margin = { + top: 40 + } +}) => { + if (width < 10) return null; + + // bounds + const xMax = width; + const yMax = height - margin.top - 100; + + // // scales + const x0Scale = scaleBand({ + rangeRound: [0, xMax], + domain: data.map(x0), + padding: 0.2, + tickFormat: () => (val) => formatDate(val) + }); + const x1Scale = scaleBand({ + rangeRound: [0, x0Scale.bandwidth()], + domain: keys, + padding: .1 + }); + const yScale = scaleLinear({ + rangeRound: [yMax, 0], + domain: [0, max(data, (d) => { + return max(keys, (key) => d[key]) + })], + }); + const zScale = scaleOrdinal({ + domain: keys, + range: ['#aeeef8', '#e5fd3d', '#9caff6'] + }) + + return ( + + + + + )} + /> + + ); +}`} + + ); +} diff --git a/packages/vx-demo/pages/gallery.js b/packages/vx-demo/pages/gallery.js index 402702608..3d097e309 100644 --- a/packages/vx-demo/pages/gallery.js +++ b/packages/vx-demo/pages/gallery.js @@ -15,6 +15,7 @@ import Area from '../components/tiles/area'; import Stacked from '../components/tiles/stacked'; import MultiLine from '../components/tiles/multiline'; import Axis from '../components/tiles/axis'; +import BarGroup from '../components/tiles/bargroup'; const items = [ "#242424", @@ -69,6 +70,7 @@ export default class Gallery extends React.Component { const t7 = this.state.dimensions[6] || [8, 300]; const t8 = this.state.dimensions[7] || [8, 300]; const t9 = this.state.dimensions[8] || [8, 300]; + const t10 = this.state.dimensions[9] || [8, 300]; return ( @@ -310,12 +312,33 @@ export default class Gallery extends React.Component { -
-
-

- More on the way! -

-
+ + +
this.nodes.add(d)}> +
+ +
+
+
Bar Group
+
+
{``}
+
+
+
+ +
+ + +
+

+ More on the way! +