diff --git a/pages/components/docs/Donut.md b/pages/components/docs/Donut.md
new file mode 100644
index 00000000000..3f15650a2d0
--- /dev/null
+++ b/pages/components/docs/Donut.md
@@ -0,0 +1,43 @@
+# Donut
+The Donut component is a circular chart that shows the relative share of commit status states for a pull request.
+
+## The `data` prop
+The `data` prop is the simplest way to define the share of states. It takes an object literal with states as keys and the number of statuses with that state as values. Slices are always rendered clockwise in descending order by size.
+
+```.jsx
+
+```
+
+When using the `data` prop, the fill of each slice comes from the corresponding value in the theme's `colors.state` object. In other words, if `theme.colors.state.error = "red"`, then the `error` slice will get `fill="red"`. You can customize the slice colors by either passing a custom `theme` prop or using the `Donut.Slice` component described below.
+
+## System props
+
+Donut components get `space` system props. Read our [System Props](/system-props) doc page for a full list of available props.
+
+## Component props
+
+| Prop name | Type | Description |
+| :- | :- | :- |
+| data | Object | Use the keys `error`, `pending`, and `success` to set values used to generate slices in the chart |
+| size | Number | Used to set the width and height of the component |
+
+# Donut.Slice
+If you need to customize the color of your slices, you can use the `Donut.Slice` component as a child of `Donut`.
+
+```.jsx
+
+
+
+
+
+```
+
+## `Donut.Slice` component props
+
+| Prop name | Type | Description |
+| :- | :- | :- |
+| state | String | The commit status state which this slice represents |
+| value | Number | The number of statuses with this slice's state |
+| fill | String | The fill color of the slice, which overrides the color determined by the `state` prop |
+
+export const meta = {displayName: 'Donut'}
diff --git a/pages/components/docs/DonutChart.md b/pages/components/docs/DonutChart.md
deleted file mode 100644
index 30fffcc18f8..00000000000
--- a/pages/components/docs/DonutChart.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# DonutChart
-
-The DonutChart component is used to represent build status in a pull request.
-
-## Default example
-```.jsx
-
-
-
-
-
-
-```
-
-## System props
-
-DonutChart components get `space` system props. Read our [System Props](/system-props) doc page for a full list of available props.
-
-## Component props
-
-| Name | Type | Default | Description |
-| :- | :- | :-: | :- |
-| data | Object | | Use the keys `error`, `pending`, and `success` to set values used to generate slices in the chart |
-| size | Number | 30 | Used to set the width and height of the component
-
-export const meta = {displayName: 'DonutChart'}
diff --git a/pages/components/docs/index.js b/pages/components/docs/index.js
index 83681033338..a20e2cff3eb 100644
--- a/pages/components/docs/index.js
+++ b/pages/components/docs/index.js
@@ -7,7 +7,7 @@ export {meta as CircleBadge} from './CircleBadge.md'
export {meta as CircleOcticon} from './CircleOcticon.md'
export {meta as CounterLabel} from './CounterLabel.md'
export {meta as Details} from './Details.md'
-export {meta as DonutChart} from './DonutChart.md'
+export {meta as Donut} from './Donut.md'
export {meta as Dropdown} from './Dropdown.md'
export {meta as FilterList} from './FilterList.md'
export {meta as Flash} from './Flash.md'
diff --git a/src/Donut.js b/src/Donut.js
new file mode 100644
index 00000000000..27d2ade0cab
--- /dev/null
+++ b/src/Donut.js
@@ -0,0 +1,80 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import {arc as Arc, pie as Pie} from 'd3-shape'
+import {themeGet} from 'styled-system'
+import {withSystemProps, withDefaultTheme} from './system-props'
+
+const defaultColor = '#666'
+const getStateColors = themeGet('colors.state', {})
+
+function Donut(props) {
+ const {className, data, children = mapData(data), size} = props
+
+ const radius = size / 2
+ const innerRadius = radius - 6
+
+ const pie = Pie().value(child => child.props.value)
+
+ // coerce the children into an array
+ const childList = React.Children.toArray(children)
+ const arcData = pie(childList)
+ const arc = Arc()
+ .innerRadius(innerRadius)
+ .outerRadius(radius)
+
+ const slices = childList.map((child, i) => {
+ return React.cloneElement(child, {d: arc(arcData[i])})
+ })
+
+ return (
+
+ )
+}
+
+function mapData(data) {
+ return Object.keys(data).map(key => )
+}
+
+Donut.defaultProps = {
+ size: 30
+}
+
+Donut.propTypes = {
+ // require elements, not mixed content: , , etc.
+ children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
+ data: PropTypes.objectOf(PropTypes.number),
+ size: PropTypes.number
+}
+
+const Slice = withDefaultTheme(props => {
+ const {children, d, fill, state, value} = props
+ const stateColors = getStateColors(props)
+ const color = fill || stateColors[state] || stateColors.unknown || defaultColor
+ return (
+
+ {children}
+
+ )
+})
+
+Slice.propTypes = {
+ // is really the only thing that should be acceptable here
+ children: PropTypes.shape({type: 'title'}),
+ d: PropTypes.string,
+ fill: PropTypes.string,
+ state: PropTypes.string,
+ /* eslint-disable react/no-unused-prop-types */
+ theme: PropTypes.shape({
+ colors: PropTypes.shape({
+ state: PropTypes.objectOf(PropTypes.string)
+ })
+ }),
+ /* eslint-enable */
+ value: PropTypes.number
+}
+
+Donut.Slice = Slice
+
+export default withSystemProps(Donut, ['space'])
diff --git a/src/DonutChart.js b/src/DonutChart.js
deleted file mode 100644
index 3933d85071d..00000000000
--- a/src/DonutChart.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {arc as Arc, pie as Pie} from 'd3-shape'
-import DonutSlice from './DonutSlice'
-import {withSystemProps} from './system-props'
-
-function DonutChart(props) {
- const {className, data, children = mapData(data), size} = props
-
- const radius = size / 2
- const innerRadius = radius - 6
-
- const pie = Pie().value(child => child.props.value)
-
- // coerce the children into an array
- const childList = React.Children.toArray(children)
- const arcData = pie(childList)
- const arc = Arc()
- .innerRadius(innerRadius)
- .outerRadius(radius)
-
- const slices = childList.map((child, i) => {
- return React.cloneElement(child, {d: arc(arcData[i])})
- })
-
- return (
-
- )
-}
-
-function mapData(data) {
- return Object.keys(data).map(key => )
-}
-
-DonutChart.defaultProps = {
- size: 30
-}
-
-DonutChart.propTypes = {
- // require elements, not mixed content: , , etc.
- children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
- data: PropTypes.objectOf(PropTypes.number),
- size: PropTypes.number
-}
-
-export default withSystemProps(DonutChart, ['space'])
diff --git a/src/DonutSlice.js b/src/DonutSlice.js
deleted file mode 100644
index a06b6a5c0c8..00000000000
--- a/src/DonutSlice.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {themeGet} from 'styled-system'
-import {withDefaultTheme} from './system-props'
-
-const defaultColor = '#666'
-const getStateColors = themeGet('colors.state', {})
-
-function DonutSlice(props) {
- const {children, d, fill, state, value} = props
- const stateColors = getStateColors(props)
- const color = fill || stateColors[state] || stateColors.unknown || defaultColor
- return (
-
- {children}
-
- )
-}
-
-DonutSlice.propTypes = {
- // is really the only thing that should be acceptable here
- children: PropTypes.shape({type: 'title'}),
- d: PropTypes.string,
- fill: PropTypes.string,
- state: PropTypes.string,
- /* eslint-disable react/no-unused-prop-types */
- theme: PropTypes.shape({
- colors: PropTypes.shape({
- state: PropTypes.objectOf(PropTypes.string)
- })
- }),
- /* eslint-enable */
- value: PropTypes.number
-}
-
-export default withDefaultTheme(DonutSlice)
diff --git a/src/__tests__/Donut.js b/src/__tests__/Donut.js
new file mode 100644
index 00000000000..e93731bea87
--- /dev/null
+++ b/src/__tests__/Donut.js
@@ -0,0 +1,85 @@
+import React from 'react'
+import Donut from '../Donut'
+import theme, {colors} from '../theme'
+import {render} from '../utils/testing'
+
+const {state} = colors
+
+describe('Donut', () => {
+ it('is a system component', () => {
+ expect(Donut.systemComponent).toEqual(true)
+ })
+
+ it('renders the data prop', () => {
+ const donut = render()
+ expect(donut).toMatchSnapshot()
+
+ expect(donut.type).toEqual('svg')
+ expect(donut.props.width).toEqual(30)
+ expect(donut.props.height).toEqual(30)
+ expect(donut.children).toHaveLength(1)
+
+ const [g] = donut.children
+ expect(g.type).toEqual('g')
+ expect(g.children).toHaveLength(1)
+
+ const [slice] = g.children
+ expect(slice.type).toEqual('path')
+ // expect(slice.props.fill).toEqual(colors.state.error)
+ })
+
+ it('renders Donut.Slice children', () => {
+ const donut = render(
+
+
+
+
+ )
+ expect(donut).toMatchSnapshot()
+ expect(donut.children).toHaveLength(1)
+ const slices = donut.children[0].children
+ expect(slices).toHaveLength(2)
+ expect(slices.map(slice => slice.type)).toEqual(['path', 'path'])
+ expect(slices[0].props.fill).toEqual(colors.state.success)
+ expect(slices[1].props.fill).toEqual(colors.state.failure)
+ })
+
+ it('renders a single Donut.Slice child', () => {
+ const donut = render(
+
+
+
+ )
+ expect(donut).toMatchSnapshot()
+ expect(donut.type).toEqual('svg')
+ })
+
+ it('respects margin utility prop', () => {
+ expect(render()).toHaveStyleRule('margin', `${theme.space[4]}px`)
+ })
+
+ it('respects padding utility prop', () => {
+ expect(render()).toHaveStyleRule('padding', `${theme.space[4]}px`)
+ })
+
+ describe('Donut.Slice', () => {
+ it('renders known states as colors', () => {
+ expect(render().props.fill).toEqual(state.error)
+ expect(render().props.fill).toEqual(state.pending)
+ expect(render().props.fill).toEqual(state.success)
+ expect(render().props.fill).toEqual(state.unknown)
+ })
+
+ it('renders unknown states with theme.colors.state.unknown', () => {
+ expect(render().props.fill).toEqual(state.unknown)
+ })
+
+ it('renders the fallback color when no state color is found in the theme', () => {
+ expect(render().props.fill).toEqual('#666')
+ })
+
+ it('respects the fill attribute', () => {
+ expect(render().props.fill).toEqual('pink')
+ })
+ })
+})
diff --git a/src/__tests__/DonutChart.js b/src/__tests__/DonutChart.js
deleted file mode 100644
index d98af748820..00000000000
--- a/src/__tests__/DonutChart.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react'
-import DonutChart from '../DonutChart'
-import DonutSlice from '../DonutSlice'
-import theme, {colors} from '../theme'
-import {render} from '../utils/testing'
-
-describe('DonutChart', () => {
- it('is a system component', () => {
- expect(DonutChart.systemComponent).toEqual(true)
- })
-
- it('renders the data prop', () => {
- const donut = render()
- expect(donut).toMatchSnapshot()
-
- expect(donut.type).toEqual('svg')
- expect(donut.props.width).toEqual(30)
- expect(donut.props.height).toEqual(30)
- expect(donut.children).toHaveLength(1)
-
- const [g] = donut.children
- expect(g.type).toEqual('g')
- expect(g.children).toHaveLength(1)
-
- const [slice] = g.children
- expect(slice.type).toEqual('path')
- // expect(slice.props.fill).toEqual(colors.state.error)
- })
-
- it('renders DonutSlice children', () => {
- const donut = render(
-
-
-
-
- )
- expect(donut).toMatchSnapshot()
- expect(donut.children).toHaveLength(1)
- const slices = donut.children[0].children
- expect(slices).toHaveLength(2)
- expect(slices.map(slice => slice.type)).toEqual(['path', 'path'])
- expect(slices[0].props.fill).toEqual(colors.state.success)
- expect(slices[1].props.fill).toEqual(colors.state.failure)
- })
-
- it('renders a single DonutSlice child', () => {
- const donut = render(
-
-
-
- )
- expect(donut).toMatchSnapshot()
- expect(donut.type).toEqual('svg')
- })
-
- it('respects margin utility prop', () => {
- expect(render()).toHaveStyleRule('margin', `${theme.space[4]}px`)
- })
-
- it('respects padding utility prop', () => {
- expect(render()).toHaveStyleRule('padding', `${theme.space[4]}px`)
- })
-})
diff --git a/src/__tests__/DonutSlice.js b/src/__tests__/DonutSlice.js
deleted file mode 100644
index 46ee60e4607..00000000000
--- a/src/__tests__/DonutSlice.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-import DonutSlice from '../DonutSlice'
-import {colors} from '../theme'
-import {renderWithTheme as render} from '../utils/testing'
-
-const {state} = colors
-
-describe('DonutSlice', () => {
- it('renders known states as colors', () => {
- expect(render().props.fill).toEqual(state.error)
- expect(render().props.fill).toEqual(state.pending)
- expect(render().props.fill).toEqual(state.success)
- expect(render().props.fill).toEqual(state.unknown)
- })
-
- it('renders unknown states with theme.colors.state.unknown', () => {
- expect(render().props.fill).toEqual(state.unknown)
- })
-
- it('renders the fallback color when no state color is found in the theme', () => {
- expect(render().props.fill).toEqual('#666')
- })
-
- it('respects the fill attribute', () => {
- expect(render().props.fill).toEqual('pink')
- })
-})
diff --git a/src/__tests__/UtilitySystemProps.js b/src/__tests__/UtilitySystemProps.js
index 5b1c02c1066..c293b3896dc 100644
--- a/src/__tests__/UtilitySystemProps.js
+++ b/src/__tests__/UtilitySystemProps.js
@@ -6,7 +6,8 @@ import {renderStyles} from '../utils/testing'
const testProps = {
OcticonButton: {icon: X, label: 'button'},
- DonutChart: {data: {pending: 1}}
+ Donut: {data: {pending: 1}},
+ Avatar: {alt: ''}
}
describe('UtilitySystemProps', () => {
diff --git a/src/__tests__/__snapshots__/DonutChart.js.snap b/src/__tests__/__snapshots__/Donut.js.snap
similarity index 88%
rename from src/__tests__/__snapshots__/DonutChart.js.snap
rename to src/__tests__/__snapshots__/Donut.js.snap
index 4f8a874a51e..bc430a1166e 100644
--- a/src/__tests__/__snapshots__/DonutChart.js.snap
+++ b/src/__tests__/__snapshots__/Donut.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`DonutChart renders DonutSlice children 1`] = `
+exports[`Donut renders Donut.Slice children 1`] = `