Skip to content

Commit

Permalink
Accessors (uber#601)
Browse files Browse the repository at this point in the history
* Accessors

* rebase and fix tests

* shift from xAccessor to getX pattern

* forgot that this pr introduced accessors on parallel coords oto
  • Loading branch information
James Sayer committed Nov 30, 2017
1 parent 63c86d2 commit 519e6ec
Show file tree
Hide file tree
Showing 35 changed files with 605 additions and 435 deletions.
6 changes: 4 additions & 2 deletions docs/parallel-coordinates.md
Expand Up @@ -13,15 +13,15 @@ Just like every other chart and series ParallelCoordinates expects an array of d
```javascript
const PARALLEL_COORDINATES_PROPS = {
data: [{
explosions: 7,
neatExplosions: 7,
wow: 10,
dog: 8,
sickMoves: 9,
nice: 7
}],
domains: [
{name: 'nice', domain: [0, 100]},
{name: 'explosions', domain: [6.9, 7.1]},
{name: 'explosions', domain: [6.9, 7.1], getValue: d => d.neatExplosions},
{name: 'wow', domain: [0, 11]},
{name: 'sickMoves', domain: [0, 20]}
],
Expand All @@ -44,6 +44,7 @@ The domains allow the user to specify the nature of the variables being plotted.
```javascript
PropTypes.shape({
name: PropTypes.string.isRequired,
getValue: PropTypes.func,
domain: PropTypes.arrayOf([PropTypes.number]).isRequired,
tickFormat: PropTypes.func
})
Expand All @@ -52,6 +53,7 @@ PropTypes.shape({
Let's looks at each member of the object

- name: generates a member of a labelSeries that shows at the end of the corresponding axis
- getValue: an accessor function that grabs a value from the row being accessed, if this is not provided a default one that uses the name property is used.
- domain: a pair of numbers that are interpolated between. Setting these values correctly is essential for making your graphic legible! Because it is often the case that there will only be one or two data rows in a parallel coordinates, react-vis requires the user to specify the exact domain for each variable. Without which we would be unable to plot the variables well.
- tickFormat: allows the user to provide a formatting function for prettifiying the the way that axis interpolates between the domain values.

Expand Down
6 changes: 4 additions & 2 deletions docs/radar-chart.md
Expand Up @@ -13,15 +13,15 @@ Just like every other chart and series RadarChart expects an array of data, each
```javascript
const RADAR_PROPS = {
data: [{
explosions: 7,
neatExplosions: 7,
wow: 10,
dog: 8,
sickMoves: 9,
nice: 7
}],
domains: [
{name: 'nice', domain: [0, 100]},
{name: 'explosions', domain: [6.9, 7.1]},
{name: 'explosions', domain: [6.9, 7.1], getValue: d => d.neatExplosions},
{name: 'wow', domain: [0, 11]},
{name: 'sickMoves', domain: [0, 20]}
],
Expand All @@ -46,6 +46,7 @@ The domains allow the user to specify the nature of the variables being plotted.
```javascript
PropTypes.shape({
name: PropTypes.string.isRequired,
getValue: PropTypes.func,
domain: PropTypes.arrayOf([PropTypes.number]).isRequired,
tickFormat: PropTypes.func
})
Expand All @@ -54,6 +55,7 @@ PropTypes.shape({
Let's looks at each member of the object

- name: generates a member of a labelSeries that shows at the end of the corresponding axis
- getValue: an accessor function that grabs a value from the row being accessed, if this is not provided a default one that uses the name property is used.
- domain: a pair of numbers that are interpolated between. Setting these values correctly is essential for making your graphic legible! Because it is often the case that there will only be one or two data rows in a radar chart, react-vis requires the user to specify the exact domain for each variable. Without which we would be unable to plot the variables well.
- tickFormat: allows the user to provide a formatting function for prettifiying the the way that axis interpolates between the domain values.

Expand Down
3 changes: 3 additions & 0 deletions docs/scales-and-data.md
Expand Up @@ -93,6 +93,9 @@ This table is also meant to be used for derived series. Canvas series have the s

To redefine a scale, you must pass a prop to the series that uses that scale. The prop names are based on the name of the attribute: name + Domain, name + Range, name + Type, name + Padding (for instance: yDomain, colorType, xRange).

* `get[name]` (optional)
Type: `function`
A accessir function that gets the value to be compute from the data. For instance if you were keeping your data as rows like {a: 1, b: 2, c: 3}, you could define an x accessor like `getX: d => d.a`.
* `[name]Domain` (optional)
Type: `Array`
Array of values to visualize from. If domain is not passed, it will be calculated from the values which are passed to component.
Expand Down
2 changes: 1 addition & 1 deletion showcase/misc/time-chart.js
Expand Up @@ -33,7 +33,7 @@ const MSEC_DAILY = 86400000;

export default class Example extends React.Component {
render() {
const timestamp = Date.now();
const timestamp = new Date('September 9 2017').getTime();
return (
<XYPlot
xType="time"
Expand Down
10 changes: 5 additions & 5 deletions showcase/parallel-coordinates/basic-parallel-coordinates.js
Expand Up @@ -43,11 +43,11 @@ export default class BasicParallelCoordinates extends Component {
colorRange={['#172d47', '#911116', '#998965']}
domains={[
{name: 'mileage', domain: [0, 10]},
{name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`},
{name: 'safety', domain: [5, 10]},
{name: 'performance', domain: [0, 10]},
{name: 'interior', domain: [0, 7]},
{name: 'warranty', domain: [10, 2]}
{name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`, getValue: d => d.price},
{name: 'safety', domain: [5, 10], getValue: d => d.safety},
{name: 'performance', domain: [0, 10], getValue: d => d.performance},
{name: 'interior', domain: [0, 7], getValue: d => d.interior},
{name: 'warranty', domain: [10, 2], getValue: d => d.warranty}
]}
showMarks
width={400}
Expand Down
12 changes: 7 additions & 5 deletions showcase/plot/complex-chart.js
Expand Up @@ -46,8 +46,8 @@ function getRandomSeriesData(total) {
for (let i = 0; i < Math.max(total, 3); i++) {
y = Math.random() * firstY - firstY / 2 + lastY;
result.push({
x: i,
y
left: i,
top: y
});
lastY = y;
}
Expand Down Expand Up @@ -121,7 +121,7 @@ export default class Example extends React.Component {
_formatCrosshairTitle(values) {
return {
title: 'X',
value: values[0].x
value: values[0].left
};
}

Expand All @@ -136,7 +136,7 @@ export default class Example extends React.Component {
return values.map((v, i) => {
return {
title: series[i].title,
value: v.y
value: v.top
};
});
}
Expand Down Expand Up @@ -167,6 +167,8 @@ export default class Example extends React.Component {
<div className="chart">
<FlexibleWidthXYPlot
animation
getX={d => d.left}
getY={d => d.top}
onMouseLeave={this._mouseLeaveHandler}
xDomain={[0, series[0].data.length - 1]}
height={300}>
Expand All @@ -182,7 +184,7 @@ export default class Example extends React.Component {
tickSizeOuter={8}
/>
<VerticalRectSeries
data={series[0].data.map(({x, y}) => ({x0: x - 0.5, x: x + 0.5, y}))}
data={series[0].data.map(({left, top}) => ({x0: left - 0.5, left: left + 0.5, top}))}
stroke="white"
onNearestX={this._nearestXHandler}
{...(series[0].disabled ? {opacity: 0.2} : null)}/>
Expand Down
14 changes: 6 additions & 8 deletions showcase/plot/contour-series-example.js
Expand Up @@ -25,20 +25,16 @@ import ShowcaseButton from '../showcase-components/showcase-button';
import {XYPlot, XAxis, YAxis, ContourSeries, MarkSeriesCanvas, Borders} from 'index';

import DATA from './old-faithful.json';
const MAPPED_DATA = DATA.map(row => ({
x: row.waiting,
y: row.eruptions
}));

function updateData() {
return MAPPED_DATA.map(row => ({
x: row.x + (Math.random() - 0.5) * 10,
y: row.y + (Math.random() - 0.5) * 2
return DATA.map(row => ({
waiting: row.waiting + (Math.random() - 0.5) * 10,
eruptions: row.eruptions + (Math.random() - 0.5) * 2
}));
}
export default class ContourSeriesExample extends Component {
state = {
data: MAPPED_DATA
data: DATA
}
render() {
const {data} = this.state;
Expand All @@ -48,6 +44,8 @@ export default class ContourSeriesExample extends Component {
xDomain={[40, 100]}
yDomain={[1.5, 8]}
width={600}
getX={d => d.waiting}
getY={d => d.eruptions}
height={300}>
<ContourSeries
animation
Expand Down
10 changes: 5 additions & 5 deletions showcase/radar-chart/basic-radar-chart.js
Expand Up @@ -41,11 +41,11 @@ export default class BasicRadarChart extends Component {
startingAngle={0}
domains={[
{name: 'mileage', domain: [0, 10]},
{name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`},
{name: 'safety', domain: [5, 10]},
{name: 'performance', domain: [0, 10]},
{name: 'interior', domain: [0, 7]},
{name: 'warranty', domain: [10, 2]}
{name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`, getValue: d => d.price},
{name: 'safety', domain: [5, 10], getValue: d => d.safety},
{name: 'performance', domain: [0, 10], getValue: d => d.performance},
{name: 'interior', domain: [0, 7], getValue: d => d.interior},
{name: 'warranty', domain: [10, 2], getValue: d => d.warranty}
]}
width={400}
height={300} />
Expand Down
11 changes: 6 additions & 5 deletions showcase/radial-chart/donut-chart.js
Expand Up @@ -36,12 +36,13 @@ export default class SimpleRadialChart extends Component {
className={'donut-chart-example'}
innerRadius={100}
radius={140}
getAngle={d => d.theta}
data={[
{angle: 2, className: 'custom-class'},
{angle: 6},
{angle: 2},
{angle: 3},
{angle: 1}
{theta: 2, className: 'custom-class'},
{theta: 6},
{theta: 2},
{theta: 3},
{theta: 1}
]}
onValueMouseOver={v => this.setState({value: v})}
onSeriesMouseOut={v => this.setState({value: false})}
Expand Down
11 changes: 6 additions & 5 deletions showcase/radial-chart/simple-radial-chart.js
Expand Up @@ -30,12 +30,13 @@ export default class SimpleRadialChart extends React.Component {
colorDomain={[0, 100]}
colorRange={[0, 10]}
margin={{top: 100}}
getLabel={d => d.name}
data={[
{angle: 1, color: '#89DAC1', label: 'green', opacity: 0.2},
{angle: 2, color: '#F6D18A', label: 'yellow'},
{angle: 5, color: '#1E96BE', label: 'cyan'},
{angle: 3, color: '#DA70BF', label: 'magenta'},
{angle: 5, color: '#F6D18A', label: 'yellow again'}
{angle: 1, color: '#89DAC1', name: 'green', opacity: 0.2},
{angle: 2, color: '#F6D18A', name: 'yellow'},
{angle: 5, color: '#1E96BE', name: 'cyan'},
{angle: 3, color: '#DA70BF', name: 'magenta'},
{angle: 5, color: '#F6D18A', name: 'yellow again'}
]}
showLabels
width={400}
Expand Down
4 changes: 3 additions & 1 deletion showcase/showcase-sections/plots-showcase.js
Expand Up @@ -145,7 +145,9 @@ class PlotsShowcase extends Component {
<h2>Series Types</h2>
{PLOTS.map(mapSection)}
<h2>Basic Components</h2>
{BASIC_COMPONENTS.map(mapSection)}
{
BASIC_COMPONENTS.map(mapSection)
}
</article>
);
}
Expand Down
4 changes: 3 additions & 1 deletion showcase/sunbursts/basic-sunburst.js
Expand Up @@ -56,7 +56,7 @@ function updateData(data, keyPath) {
data.children.map(child => updateData(child, keyPath));
}
// add a fill to all the uncolored cells
if (!data.color) {
if (!data.hex) {
data.style = {
fill: EXTENDED_DISCRETE_COLOR_RANGE[5]
};
Expand Down Expand Up @@ -115,6 +115,8 @@ export default class BasicSunburst extends React.Component {
strokeWidth: '0.5'
}}
colorType="literal"
getSize={d => d.value}
getColor={d => d.hex}
data={data}
height={300}
width={350}>
Expand Down
8 changes: 5 additions & 3 deletions showcase/sunbursts/clock-example.js
Expand Up @@ -57,6 +57,8 @@ export default class ClockExample extends React.Component {
xDomain={[-3, 3]}
yDomain={[-3, 3]}
width={300}
getAngle={d => d.time}
getAngle0={d => 0}
height={300}>
<ArcSeries
animation={{
Expand All @@ -65,9 +67,9 @@ export default class ClockExample extends React.Component {
}}
radiusDomain={[0, 3]}
data={[
{angle0: 0, angle: seconds / 60 * 2 * PI, radius0: 1, radius: 1.5, color: 0},
{angle0: 0, angle: minutes / 60 * 2 * PI, radius0: 1.6, radius: 2.1, color: 1},
{angle0: 0, angle: hours / 24 * 2 * PI, radius0: 2.2, radius: 2.7, color: 2}
{time: seconds / 60 * 2 * PI, radius0: 1, radius: 1.5, color: 0},
{time: minutes / 60 * 2 * PI, radius0: 1.6, radius: 2.1, color: 1},
{time: hours / 24 * 2 * PI, radius0: 2.2, radius: 2.7, color: 2}
]}
colorRange={EXTENDED_DISCRETE_COLOR_RANGE} />
</XYPlot>
Expand Down
29 changes: 16 additions & 13 deletions showcase/sunbursts/sunburst-with-tooltips.js
Expand Up @@ -32,25 +32,25 @@ import {
const DATA = {
children: [
{children: [
{size: 1, children: [], color: COLORS[1], label: 'excellent'},
{size: 1, children: [], color: COLORS[2], label: 'chart'}
], color: COLORS[3]},
{bigness: 1, children: [], clr: COLORS[1], name: 'excellent'},
{bigness: 1, children: [], clr: COLORS[2], name: 'chart'}
], clr: COLORS[3]},
{
size: 1,
bigness: 1,
children: [],
color: COLORS[4],
label: 'cool',
clr: COLORS[4],
name: 'cool',
labelStyle: {
fontSize: 15,
fontWeight: 'bold'
}
},
{size: 1, children: [], color: COLORS[5], label: 'dogs'},
{size: 1, children: [], color: COLORS[6], label: 'sunglasses'},
{bigness: 1, children: [], clr: COLORS[5], name: 'dogs'},
{bigness: 1, children: [], clr: COLORS[6], name: 'sunglasses'},
{children: [
{size: 1, children: [], color: COLORS[7], label: 'great'},
{size: 1, children: [], color: COLORS[8], label: 'label'}
], color: COLORS[9]}
{bigness: 1, children: [], clr: COLORS[7], name: 'great'},
{bigness: 1, children: [], clr: COLORS[8], name: 'label'}
], clr: COLORS[9]}
]
};

Expand Down Expand Up @@ -86,11 +86,14 @@ export default class SunburstWithTooltips extends React.Component {
onValueMouseOut={v => this.setState({hoveredCell: false})}
height={300}
margin={{top: 50, bottom: 50, left: 50, right: 50}}
getLabel={d => d.name}
getSize={d => d.bigness}
getColor={d => d.clr}
width={350}>
{hoveredCell ? <Hint value={buildValue(hoveredCell)}>
<div style={tipStyle}>
<div style={{...boxStyle, background: hoveredCell.color}}/>
{hoveredCell.color}
<div style={{...boxStyle, background: hoveredCell.clr}}/>
{hoveredCell.clr}
</div>
</ Hint> : null}
</Sunburst>
Expand Down

0 comments on commit 519e6ec

Please sign in to comment.