Skip to content

Commit

Permalink
[shape][scale] add <BarGroup />, add tickFormat to scaleBand
Browse files Browse the repository at this point in the history
  • Loading branch information
hshoff committed May 22, 2017
1 parent f5bd3af commit 6d250ea
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 19 deletions.
94 changes: 94 additions & 0 deletions packages/vx-demo/components/tiles/bargroup.js
Original file line number Diff line number Diff line change
@@ -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 (
<svg width={width} height={height}>
<rect
x={0}
y={0}
width={width}
height={height}
fill={`#612efb`}
rx={14}
/>
<BarGroup
top={margin.top}
data={data}
keys={keys}
height={yMax}
x0={x0}
y={y}
x0Scale={x0Scale}
x1Scale={x1Scale}
yScale={yScale}
zScale={zScale}
/>
<AxisBottom
scale={x0Scale}
top={yMax + margin.top}
stroke='#e5fd3d'
tickStroke='#e5fd3d'
hideAxisLine
tickLabelComponent={(
<text
fill='#e5fd3d'
fontSize={11}
textAnchor="middle"
/>
)}
/>
</svg>
);
}
104 changes: 104 additions & 0 deletions packages/vx-demo/pages/bargroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import Show from '../components/show';
import BarGroup from '../components/tiles/bargroup';

export default () => {
return (
<Show margin={{top: 80}} component={BarGroup} title="Bar Group">
{`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 (
<svg width={width} height={height}>
<rect
x={0}
y={0}
width={width}
height={height}
fill='#612efb'
rx={14}
/>
<BarGroup
top={margin.top}
data={data}
keys={keys}
height={yMax}
x0={x0}
y={y}
x0Scale={x0Scale}
x1Scale={x1Scale}
yScale={yScale}
zScale={zScale}
/>
<AxisBottom
scale={x0Scale}
top={yMax + margin.top}
stroke='#e5fd3d'
tickStroke='#e5fd3d'
hideAxisLine
tickLabelComponent={(
<text
fill='#e5fd3d'
fontSize={11}
textAnchor="middle"
/>
)}
/>
</svg>
);
}`}
</Show>
);
}
35 changes: 29 additions & 6 deletions packages/vx-demo/pages/gallery.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 (
<Page title="gallery">
Expand Down Expand Up @@ -310,12 +312,33 @@ export default class Gallery extends React.Component {
</div>
</Link>
</Tilt>
<div>
<br/>
<h1 style={{textAlign: 'center', lineHeight: '.8em'}}>
More on the way!
</h1>
</div>
<Tilt
className="tilt"
options={{ max: 8, scale: 1 }}
>
<Link prefetch href="/bargroup">
<div className="gallery-item" style={{ background: '#612efb' }} ref={d => this.nodes.add(d)}>
<div className="image">
<BarGroup
width={t10[0]}
height={t10[1]}
/>
</div>
<div className="details" style={{ color: '#e5fd3d'}}>
<div className="title">Bar Group</div>
<div className="description">
<pre>{`<Shape.BarGroup />`}</pre>
</div>
</div>
</div>
</Link>
</Tilt>
</div>

<div>
<h1 style={{textAlign: 'center', lineHeight: '.8em'}}>
More on the way!
</h1>
</div>

<Footer />
Expand Down
2 changes: 2 additions & 0 deletions packages/vx-scale/src/scales/band.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default ({
paddingInner,
paddingOuter,
align,
tickFormat,
}) => {
const scale = scaleBand();

Expand All @@ -18,6 +19,7 @@ export default ({
if (paddingInner) scale.paddingInner(paddingInner);
if (paddingOuter) scale.paddingOuter(paddingOuter);
if (align) scale.align(align);
if (tickFormat) scale.tickFormat = tickFormat;

return scale;
}
2 changes: 1 addition & 1 deletion packages/vx-shape/src/shapes/AreaStack.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import cx from 'classnames';
import { callOrValue } from '../util/callOrValue';
import callOrValue from '../util/callOrValue';
import { area, stack as d3stack } from 'd3-shape';

export default ({
Expand Down
1 change: 1 addition & 0 deletions packages/vx-shape/src/shapes/Bar.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import cx from 'classnames';
import callOrValue from '../util/callOrValue';

export default ({
className,
Expand Down
54 changes: 46 additions & 8 deletions packages/vx-shape/src/shapes/BarGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,64 @@ 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,
x0,
y,
z,
x0Scale,
x1Scale,
yScale,
zScale,
keys,
height,
}) {
return (
<Group
className={cx('vx-bar-group', className)}
top={top}
left={left}
>

{data && data.map((d, i) => {
return (
<Group
key={`bar-group-${i}-${x0(d)}`}
left={x0Scale(x0(d))}
>
{keys && keys.map((key, i) => {
const value = d[key];
return (
<Bar
key={`bar-group-bar-${i}-${value}-${key}`}
x={x1Scale(key)}
y={yScale(value)}
width={x1Scale.bandwidth()}
height={height - yScale(value)}
fill={zScale(key)}
/>
);
})}
</Group>
);
})}
</Group>
);
}

BarGroup.propTypes = {
data: PropTypes.array.isRequired,
className: PropTypes.string,
top: PropTypes.number,
left: PropTypes.number,
x0: PropTypes.func.isRequired,
y: PropTypes.func.isRequired,
x0Scale: PropTypes.func.isRequired,
x1Scale: PropTypes.func.isRequired,
yScale: PropTypes.func.isRequired,
z: PropTypes.func,
zScale: PropTypes.func,
keys: PropTypes.array.isRequired,
};
7 changes: 3 additions & 4 deletions packages/vx-shape/test/BarGroup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ describe('<BarGroup />', () => {
})

test('it should have className .vx-bar-group', () => {
const wrapper = shallow(<BarGroup />)
const wrapper = shallow(<BarGroup data={[]} />)
expect(wrapper.prop('className')).toEqual('vx-bar-group')
})

test('it should set className prop', () => {
const wrapper = shallow(<BarGroup className='test' />)
const wrapper = shallow(<BarGroup className='test' data={[]} />)
expect(wrapper.prop('className')).toEqual('vx-bar-group test')
})

test('it should require a data prop', () => {
const wrapper = mount(<BarGroup />)
console.log(wrapper)
const wrapper = shallow(<BarGroup />);
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")
})
Expand Down

0 comments on commit 6d250ea

Please sign in to comment.