-
Notifications
You must be signed in to change notification settings - Fork 646
Add DonutChart component #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
7e87871
2255eba
8288ef1
c87286b
3b6415c
3a11bda
0d3b90a
ade2745
44f5a9d
ec2e10e
d164ff6
f6bf27a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import React from 'react' | ||
| import PropTypes from 'prop-types' | ||
| import {arc as Arc, pie as Pie} from 'd3-shape' | ||
| import theme from './theme' | ||
|
|
||
| const {colors} = theme | ||
|
|
||
| const fillForState = { | ||
| 'error': colors.red[5], | ||
| 'pending': colors.yellow[5], | ||
| 'failure': colors.red[5], | ||
| 'success': colors.green[5] | ||
| } | ||
|
|
||
| function mapData(data) { | ||
| return Object.keys(data) | ||
| .map((key, i) => ( | ||
| <DonutSlice key={key} state={key} value={data[key]} /> | ||
| )) | ||
| } | ||
|
|
||
| const DonutChart = props => { | ||
| const { | ||
| data, | ||
| children = mapData(data), | ||
| size = 40, | ||
| } = props | ||
|
|
||
| const radius = size / 2 | ||
| const { | ||
| innerRadius = radius / 2 | ||
|
||
| } = props | ||
|
|
||
| const pie = Pie() | ||
| .value(child => child.props.value) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Translation: create a pie slice generator with a value accessor that gets |
||
|
|
||
| const arcData = pie(children) | ||
|
||
| const arc = Arc() | ||
| .innerRadius(innerRadius) | ||
| .outerRadius(radius) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use a custom arc generator here because we need to set the inner and outer radii to fixed values. |
||
|
|
||
| const arcs = React.Children.map(children, (child, i) => { | ||
| const { | ||
| state, | ||
| fill = fillForState[state] | ||
|
||
| } = child.props | ||
| return React.cloneElement(child, { | ||
|
||
| d: arc(arcData[i]), | ||
|
||
| fill, | ||
| key: i | ||
| }) | ||
| }) | ||
|
|
||
| return ( | ||
| <svg width={size} height={size}> | ||
| <g transform={`translate(${radius},${radius})`}> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pie paths are always centered on (0, 0), so we need to translate to the center. |
||
| {arcs} | ||
| </g> | ||
| </svg> | ||
| ) | ||
| } | ||
|
|
||
| const DonutSlice = ({children, d, fill}) => { | ||
|
||
| return <path d={d} fill={fill}>{children}</path> | ||
|
||
| } | ||
|
|
||
| DonutChart.propTypes = { | ||
| children: PropTypes.arrayOf(DonutSlice) | ||
|
||
| } | ||
|
|
||
| DonutSlice.propTypes = { | ||
| d: PropTypes.string, | ||
| fill: PropTypes.string, | ||
| state: PropTypes.oneOf(Object.keys(fillForState)), | ||
|
||
| value: PropTypes.number | ||
| } | ||
|
|
||
| export default DonutChart | ||
| export {DonutSlice} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import React from 'react' | ||
| import DonutChart, {DonutSlice} from '../DonutChart' | ||
| import theme from '../theme' | ||
| import {render} from '../utils/testing' | ||
|
|
||
| describe('DonutChart', () => { | ||
| it('renders the data prop', () => { | ||
| const donut = render(<DonutChart data={{error: 1}} />) | ||
|
|
||
| expect(donut.type).toEqual('svg') | ||
| expect(donut.props.width).toEqual(40) | ||
| expect(donut.props.height).toEqual(40) | ||
| expect(donut.children.length).toEqual(1) | ||
|
|
||
| const [g] = donut.children | ||
| expect(g.type).toEqual('g') | ||
| expect(g.children.length).toEqual(1) | ||
|
|
||
| const [slice] = g.children | ||
| expect(slice.type).toEqual('path') | ||
| expect(slice.props.fill).toEqual(theme.colors.red[5]) | ||
| }) | ||
|
|
||
| xit('renders DonutSlice children', () => { | ||
| const donut = render(( | ||
| <DonutChart> | ||
| <DonutSlice state='success' value={1} /> | ||
| </DonutChart> | ||
| )) | ||
| expect(donut.type).toEqual('svg') | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the
childrenprop isn't passed, generate slices from thedataprop.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌