@data-ui/sparkline
Version:
React + d3 library for creating sparklines
259 lines (218 loc) • 22.1 kB
Markdown
# @data-ui/sparkline
A React + d3 library for creating sparklines 📈 implemented with <a href="vx-demo.now.sh" target="_blank">vx</a>.
`npm install --save @data-ui/sparkline`
<a title="package version" href="https://img.shields.io/npm/v/@data-ui/sparkline.svg?style=flat-square">
<img src="https://img.shields.io/npm/v/@data-ui/sparkline.svg?style=flat-square" />
</a>
<p align="center">
<img width="400px" src="https://user-images.githubusercontent.com/4496521/29400148-001a942c-82e1-11e7-85e5-a77b0db97b79.png" />
</p>
Demo it live at <a href="https://williaster.github.io/data-ui" target="_blank">williaster.github.io/data-ui</a>.
## Example usage
Sparklines are composable using the container `<Sparkline />` component and different types of children including one or more `<*Series />` components and reference lines or bands. Additionally, you can customize aesthetics using the exported `<PatternLines />` and `<LinearGradient/>` components.
Note that the order of children passed to `<Sparkline />` determines their rendering order, for example a `<HorizontalReferenceLine />` passed after a `<BarSeries />` will overlay the line on the bars.
```javascript
import {
Sparkline,
LineSeries,
HorizontalReferenceLine,
BandLine,
PatternLines,
PointSeries } from '@data-ui/sparkline';
import { allColors } from '@data-ui/theme'; // open-color colors
const data = Array(25).fill().map(Math.random);
<Sparkline
ariaLabel="A line graph of randomly-generated data"
margin={{ top: 24, right: 64, bottom: 24, left: 64,}}
width={500}
height={100}
data={data}
valueAccessor={datum => datum}
>
{/* this creates a <defs> referenced for fill */}
<PatternLines
id="unique_pattern_id"
height={6}
width={6}
stroke={allColors.grape[6]}
strokeWidth={1}
orientation={['diagonal']}
/>
{/* display innerquartiles of the data */}
<BandLine
band="innerquartiles"
fill="url(#unique_pattern_id)"
/>
{/* display the median */}
<HorizontalReferenceLine
stroke={allColors.grape[8]}
strokeWidth={1}
strokeDasharray="4 4"
reference="median"
/>
{/* Series children are passed the data from the parent Sparkline */}
<LineSeries
showArea={false}
stroke={allColors.grape[7]}
/>
<PointSeries
points={['min', 'max']}
fill={allColors.grape[3]}
size={5}
stroke="#fff"
renderLabel={val => val.toFixed(2)}
/>
</Sparkline>
```
## Components
Check out the example source code and PropTable tabs in the Storybook <a href="https://williaster.github.io/data-ui" target="_blank">williaster.github.io/data-ui</a> for more!
#### `<Sparkline />`
The `Sparkline` component renders an `<svg />` and coordinates scales across all of it's child series and reference lines. It takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
ariaLabel | PropTypes.string.isRequired | - | Accessibility label for the svg
children | PropTypes.node.isRequired | - | Child series, reference lines, defs, or other valid svg children
className | PropTypes.string | - | Optional className to add to the svg
data | PropTypes.array | [] | an array of data that is shared across, items can be any shape or number
height | PropTypes.number.isRequired | - | Height of the svg including top/bottom margin
margin | PropTypes.shape({ top: PropTypes.number, right: PropTypes.number, bottom: PropTypes.number, left: PropTypes.number }) | { top: 16, right: 16, bottom: 16, left: 16 } | chart margin, leave room for labels! note 0 may clip LineSeries and PointSeries. a partial { top/right/bottom/ left } object is filled with the other default values
max | PropTypes.number | - | Optionally set the maximum y-value of the chart (e.g., to coordinate axes across multiple Sparklines)
min | PropTypes.number | - | Optionally set the minimum y-value of the chart (e.g., to coordinate axes across multiple Sparklines)
onMouseMove | PropTypes.func | - | `func({ data, datum, event, index, color })`, passed to an invisible `BarSeries` that intercepts all mouse events (can pass to individual series for more control)
onMouseLeave | PropTypes.func | - | `func()`, passed to an invisible `BarSeries` that intercepts all mouse events
styles | PropTypes.object | - | Optional styles to apply to the svg
width | PropTypes.number.isRequired | - | Width of the svg including left/right margin
valueAccessor | PropTypes.func | d => d | Optional accessor function that takes an item from the data array as input and returns the y value of the datum. This value is passed back in e.g., `renderLabel` functions.
### Series
The following series components are available, they are passed the data and scales from the parent `Sparkline` component, and you may compose multiple to create the sparkline you want 😍:
* `<LineSeries />`
* `<PointSeries />`
* `<BarSeries />`
`<PointSeries />` and `<BarSeries />` support labeling of specific data points.
#### `<LineSeries />`
This component can be used to create lines and or area sparklines with various `curve` types, and takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
fill | PropTypes.string | `@data-ui/theme`s color.default | If `showArea=true`, this sets the `fill` of the area path.
fillOpacity | PropTypes.number | 0.3 | If `showArea=true`, this sets the `fillOpacity` of the `area` shape.
curve | PropTypes.oneOf(['linear', 'cardinal', 'basis', 'monotoneX']) | 'cardinal' | The type of curve interpolator to use.
onMouseMove | PropTypes.func | - | `func({ data, datum, event, index, color })` called on line mouse move for the closest datum
onMouseLeave | PropTypes.func | - | `func()` called on line mouse leave
showArea | PropTypes.bool | false | Boolean indicating whether to render an Area path.
showLine | PropTypes.bool | true | Boolean indicating whether to render a Line path.
stroke | PropTypes.string | `@data-ui/theme`s color.default | If `showLine=true`, this sets the `stroke` of the line path.
strokeDasharray | PropTypes.string | - | If `showLine=true`, this sets the `strokeDasharray` attribute of the line path.
strokeLinecap | PropTypes.oneOf(['butt', 'square', 'round', 'inherit']) | 'round' | If `showLine=true`, this sets the `strokeLinecap` attribute of the line path.
strokeWidth | PropTypes.number | 2 | If `showLine=true`, this sets the `strokeWidth` attribute of the line path.
#### `<BarSeries />`
This component can be used to bar-graph sparklines and takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
fill | PropTypes.oneOfType([PropTypes.func, PropTypes.string]) | `@data-ui/theme`s color.default | A single `fill` to use for all `Bar`s or a function with the following signature `(yVal, i) => fill` called for each data point. If data objects have a `fill` property, it overrides this value.
fillOpacity | PropTypes.oneOfType([PropTypes.func, PropTypes.number]) | `0.7` | A single `fillOpacity` value (0 - 1) to use for all `Bar`s or a function with the following signature `(yVal, i) => opacity` called for each data point. If data objects have a `fillOpacity` property, it overrides this value.
LabelComponent | PropTypes.element | `@data-ui/sparkline`'s `<Label />` component | Component to use for labels, if relevant. This component is cloned with appropriate x, y, dx, and dy values for positioning.
labelOffset | PropTypes.number | `8` | (Absolute) pixel offset to use for positioning a label relative to a `Bar`. `labelPosition` is used to determine direction.
labelPosition | PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf(['top', 'right', 'bottom', 'left']), ]) | 'top' | A single string indicating how to position a label relative to the top point of a bar, or a function with the following signature `(yVal, i) => position` called for each data point. If the return value is not one of top, right, bottom, left, it is spread on the `LabelComponent` directly (e.g., `{ dx: -100, dy: 100 }`)
onMouseMove | PropTypes.func | - | `func({ data, datum, event, index, color })` called on bar mouse move
onMouseLeave | PropTypes.func | - | `func()` called on bar mouse leave
renderLabel | PropTypes.func | - | Optional function called for each datum, with the following signature `(yVal, i) => node`. If this is passed to the Series and returns a value, a label will be rendered for the passed point. This is used as the child of `LabelComponent` so any valid child of svg `<text>` elements can be returned.
stroke | PropTypes.oneOfType([PropTypes.func, PropTypes.string]) | `white` | A single `stroke` to use for all `Bar`s or a function with the following signature `(yVal, i) => stroke`. If data objects have a `stroke` property, it overrides this value.
strokeWidth | PropTypes.oneOfType([PropTypes.func, PropTypes.number]) | `1` | A single `strokeWidth` to use for all `Bar`s or a function with the following signature `(yVal, i) => stroke`. If data objects have a `strokeWidth` property, it overrides this value.
#### `<PointSeries />`
This component can be used to render all or a subset of points for a sparkline and takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
fill | PropTypes.oneOfType([PropTypes.func, PropTypes.string]) | `@data-ui/theme`s color.default | A single `fill` to use for all `Point`s or a function with the following signature `(yVal, i) => fill` called for each data point. If data objects have a `fill` property, it overrides this value.
fillOpacity | PropTypes.oneOfType([PropTypes.func, PropTypes.number]) | `1` | A single `fillOpacity` value (0 - 1) to use for all `Point`s or a function with the following signature `(yVal, i) => opacity` called for each data point. If data objects have a `fillOpacity` property, it overrides this value.
LabelComponent | PropTypes.element | `@data-ui/sparkline`'s `<Label />` component | Component to use for labels, if relevant. This component is cloned with appropriate x, y, dx, and dy values for positioning.
labelOffset | PropTypes.number | `12` | (Absolute) pixel offset to use for positioning a label relative to a `Point`. `labelPosition` is used to determine direction.
labelPosition | PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf(['auto', 'top', 'right', 'bottom', 'left']), ]) | 'auto' | A single string indicating how to position a label relative to the center of a point, or a function with the following signature `(yVal, i) => position` called for each data point. 'auto' attempts to position the label on top or bottom of a point depending on the surrounding data points. If the return value is not one of auto, top, right, bottom, left, it is spread on the `LabelComponent` directly (e.g., `{ dx: -100, dy: 100 }`)
onMouseMove | PropTypes.func | - | `func({ data, datum, event, index, color })` called on point mouse move
onMouseLeave | PropTypes.func | - | `func()` called on point mouse leave
points | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['all', 'min', 'max', 'first', 'last']), ])) | `['min', 'max']` | String(s) or index(s) indicating which point(s) to render. e.g., If `all`, all points are rendered, if `['min', 'max']`, only the minimum and maximum points are rendered.
size | PropTypes.oneOfType([PropTypes.func, PropTypes.number]) | `3` | A single `size` to use as the radius of all `Points`s or a function with the following signature `(yVal, i) => size` called for each data point. If data objects have a `size` property, it overrides this value.
renderLabel | PropTypes.func | - | Optional function called for each datum, with the following signature `(yVal, i) => node`. If this is passed to the Series and returns a value, a label will be rendered for the passed point. This is used as the child of `LabelComponent` so any valid child of svg `<text>` elements can be returned.
stroke | PropTypes.oneOfType([PropTypes.func, PropTypes.string]) | `white` | A single `stroke` to use for all `Point`s or a function with the following signature `(yVal, i) => stroke`. If data objects have a `stroke` property, it overrides this value.
strokeWidth | PropTypes.oneOfType([PropTypes.func, PropTypes.number]) | `1` | A single `strokeWidth` to use for all `Point`s or a function with the following signature `(yVal, i) => stroke`. If data objects have a `strokeWidth` property, it overrides this value.
### Tooltips
You can add tooltips to `<Sparkline />` components by wrapping them with the higher-order `<WithTooltip />` component. This component accepts a `renderTooltip` function whose output is rendered into a boundary-aware (html-based) tooltip. `<WithTooltip />` handles tooltip visibility state and passes `onMouseMove` `onMouseLeave` and `tooltipData` props to its child. If these are passed to `<Sparkline />`, it will render a series of invisible `Bar`s to intercept mouse events. If they are passed to individual series, mouse events will be handled on the series level.
See the <a href="https://williaster.github.io/data-ui" target="_blank">storybook</a> for example usage!
Name | Type | Default | Description
------------ | ------------- | ------- | ----
children | PropTypes.func or PropTypes.object | - | Child function (to call) or element (to clone) with onMouseMove, onMouseLeave, and tooltipData props/keys
className | PropTypes.string | - | Class name to add to the `<div>` container wrapper
renderTooltip | PropTypes.func.isRequired | - | Renders the _contents_ of the tooltip, signature of `({ event, data, datum, color }) => node`. If this function returns a `falsy` value, a tooltip will not be rendered.
styles | PropTypes.object | {} | Styles to add to the `<div>` container wrapper
TooltipComponent | PropTypes.func or PropTypes.object | `@vx`'s `TooltipWithBounds` | Component (not instance) to use as the tooltip container component. It is passed `top` and `left` numbers for positioning
tooltipProps | PropTypes.object | - | Props that are passed to `TooltipComponent`
tooltipTimeout | PropTypes.number | 200 | Timeout in ms for the tooltip to hide upon calling `onMouseLeave`
### Reference lines and bands
The following reference line components are exported to support different types of annotations you may want:
* `<HorizontalReferenceLine />`
* `<VerticalReferenceLine />`
* `<BandLine />`.
#### `<HorizontalReferenceLine />`
This component can be used to render a single horizontal reference line to call out a point of interest. It takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
reference | PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['mean', 'median', 'min', 'max'] ])) | `mean` | What reference to create a line for. This may be a raw number to call out, or one of `'mean', 'median', 'min', 'max'` which will compute the relevant y value.
LabelComponent | PropTypes.element | `@data-ui/sparkline`'s `<Label />` component | Component to use for labels, if relevant. This component is cloned with appropriate x, y, dx, and dy values for positioning.
labelOffset | PropTypes.number | `8` | (Absolute) pixel offset to use for positioning a label relative to a `Point`. `labelPosition` is used to determine direction.
labelPosition | PropTypes.oneOf(['top', 'right', 'bottom', 'left']) | 'right' | A single string indicating how to position a label relative to the end of a reference line
renderLabel | PropTypes.func | - | Optional function called for each datum, with the following signature `(yVal) => node`. If this is passed and returns a value, a label will be rendered. This is used as the child of `LabelComponent` so any valid child of svg `<text>` elements can be returned.
stroke | PropTypes.string | `@data-ui/theme`s color.default | Sets the `stroke` of the line path.
strokeDasharray | PropTypes.string | - | Sets the `strokeDasharray` attribute of the line path.
strokeLinecap | PropTypes.oneOf(['butt', 'square', 'round', 'inherit']) | 'round' | Sets the `strokeLinecap` attribute of the line path.
strokeWidth | PropTypes.number | 2 | Sets the `strokeWidth` attribute of the line path.
#### `<VerticalReferenceLine />`
@TODO picture
This component can be used to render a single vertical reference line to call out a point of interest. It takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
reference | PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['first', 'last', 'min', 'max'] ])) | `last` | What reference to create a line for. This may be a raw number (x index) to call out, or one of `'first', 'last', 'min', 'max'` which will compute the relevant x index value.
LabelComponent | PropTypes.element | `@data-ui/sparkline`'s `<Label />` component | Component to use for labels, if relevant. This component is cloned with appropriate x, y, dx, and dy values for positioning.
labelOffset | PropTypes.number | `10` | (Absolute) pixel offset to use for positioning a label relative to a `Point`. `labelPosition` is used to determine direction.
labelPosition | PropTypes.oneOf(['top', 'right', 'bottom', 'left']) | 'top' | A single string indicating how to position a label relative to the top of a reference line
renderLabel | PropTypes.func | - | Optional function called for each datum, with the following signature `(xVal) => node`. If this is passed and returns a value, a label will be rendered. This is used as the child of `LabelComponent` so any valid child of svg `<text>` elements can be returned.
stroke | PropTypes.string | `@data-ui/theme`s color.default | Sets the `stroke` of the line path.
strokeDasharray | PropTypes.string | - | Sets the `strokeDasharray` attribute of the line path.
strokeLinecap | PropTypes.oneOf(['butt', 'square', 'round', 'inherit']) | 'round' | Sets the `strokeLinecap` attribute of the line path.
strokeWidth | PropTypes.number | 2 | Sets the `strokeWidth` attribute of the line path.
#### `<BandLine />`
This component can be used to render _ranges_ of interest as opposed to single values. It may be used to create vertical or horizontal bands and takes the following props:
Name | Type | Default | Description
------------ | ------------- | ------- | ----
band | PropTypes.oneOfType([PropTypes.oneOf([ 'innerQuartiles' ]), PropTypes.shape({ from: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }).isRequired, to: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }) ]).isRequired }) | 'innerQuartiles' | Specifies the band to render. May be `innerQuartiles` which computes the midspread of the data values, or an object with the keys `{ from: coords, to: coords }` specifying a custom band. If an `x` or `y` value is missing in either the `from` or `to` key, the bounds (min, max) of the chart are used.
fill | PropTypes.string | `@data-ui/theme`s color.lightGray | Sets the `fill` of the bar shape.
fillOpacity | PropTypes.number | `0.5` | Sets the `fillOpacity` of the bar shape.
stroke | PropTypes.string | `transparent` | Sets the `stroke` of the bar shape.
strokeWidth | PropTypes.number | `0` | Sets the `strokeWidth` of the bar shape.
#### `<PatternLines />` and `<LinearGradient/>`s
These components are exported for convenience from `@vx` to support customization of the aesthetics of your sparklines. They create `<defs/>` elements with the specified `id`, which are then referenced for fills or strokes in other components via `url(#my_id)`. See example usage above and the <a href="https://williaster.github.io/data-ui" target="_blank">Storybook</a> for examples!
They take the following props:
#### `<PatternLines />`
Name | Type | Default | Description
------------ | ------------- | ------- | ----
id | PropTypes.string.isRequired | - | `id` for the `<defs>` that is created. When used as a fill in another component it can be referenced via `url(#my_id)`.
width | PropTypes.number.isRequired | - | Width of the pattern (which is then repeated).
height | PropTypes.number.isRequired | - | Height of the pattern (which is then repeated).
stroke | PropTypes.string | - | Sets the `stroke` attribute of the pattern line.
strokeWidth | PropTypes.number | - | Sets the `strokeWidth` attribute of the pattern line.
strokeDasharray | PropTypes.string | - | Sets the `strokeDasharray` attribute of the pattern line.
strokeLinecap | PropTypes.string | 'square' | Sets the `strokeLinecap` attribute of the pattern line.
shapeRendering | PropTypes.string | 'auto' | Sets the `shapeRendering` attribute of the pattern line.
orientation | PropTypes.arrayOf(PropTypes.oneOf(['vertical', 'horizontal', 'diagonal'])) | ['vertical'] | Array of orientations for lines. If multiple are passed, one path is rendered for each.
background | PropTypes.string | - | Optional background fill for the pattern.
className | PropTypes.string | - | Optional className added to the pattern `path`'s.
#### `<LinearGradient />`
Name | Type | Default | Description
------------ | ------------- | ------- | ----
id | PropTypes.string.isRequired | - | `id` for the `<defs>` that is created. When used as a fill in another component it can be referenced via `url(#my_id)`.
from | PropTypes.string | - | String used for the start color in the gradient.
to | PropTypes.string | - | String used for the end color in the gradient.
fromOffset | PropTypes.string | `0%` | Sets the offset (as a %) of the `from` color.
fromOpacity | PropTypes.number | 1 | Sets the opacity of the `from` color.
toOffset | PropTypes.string | `100%` | Sets the offset (as a %) of the `to` color
toOpacity | PropTypes.number | 1 | Sets the opacity of the `to` color.
rotate | PropTypes.oneOfType([PropTypes.string, PropTypes.number]) | - | Sets the `transform` attribute of the gradient to `rotate(<rotate>)`
transform | PropTypes.string | - | Sets the `gradientTransform` of the `linearGradient` element, overridden by `rotate`