Skip to content

Commit 32e9e91

Browse files
authored
Merge pull request #74 from williaster/chris--series-onclick
[xy-chart] add <StackedAreaSeries /> and `onClick` support to all series
2 parents 3e2273b + 2ff57f1 commit 32e9e91

39 files changed

+1299
-286
lines changed

packages/data-table/README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,3 @@ See the <a href="https://williaster.github.io/data-ui" target="_blank">@data-ui/
2424
npm install
2525
npm run dev # or 'npm run prod'
2626
```
27-
28-
## @data-ui packages
29-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/xy-chart" target="_blank">@data-ui/xy-chart</a>[![Version](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat)
30-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/histogram" target="_blank">@data-ui/histogram</a>[![Version](https://img.shields.io/npm/v/@data-ui/histogram.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/histogram.svg?style=flat)
31-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/radial-chart" target="_blank">@data-ui/radial-chart</a> [![Version](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat)
32-
- @data-ui/data-table [![Version](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat)
33-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/data-ui-theme" target="_blank">@data-ui/theme</a> [![Version](https://img.shields.io/npm/v/@data-ui/theme.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/theme.svg?style=flat)
34-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/demo" target="_blank">@data-ui/demo</a>

packages/data-ui-theme/README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,3 @@ This package exports theme variables that can be used by other packages. On the
66
```js
77
import { font, svgFont, label, size, color, chartTheme } from `@data-ui/theme`;
88
```
9-
10-
## @data-ui packages
11-
- [@data-ui/xy-chart](https://github.com/williaster/data-ui/tree/master/packages/xy-chart) [![Version](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat)
12-
- <a href="https://github.com/williaster/data-ui/tree/master/packages/histogram" target="_blank">@data-ui/histogram</a> [![Version](https://img.shields.io/npm/v/@data-ui/histogram.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/histogram.svg?style=flat)
13-
- [@data-ui/radial-chart](https://github.com/williaster/data-ui/tree/master/packages/radial-chart) [![Version](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat)
14-
- [@data-ui/data-table](https://github.com/williaster/data-ui/tree/master/packages/data-table) [![Version](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat)
15-
- @data-ui/theme [![Version](https://img.shields.io/npm/v/@data-ui/theme.svg?style=flat)](https://img.shields.io/npm/v/@data-ui/theme.svg?style=flat)
16-
- [@data-ui/demo](https://github.com/williaster/data-ui/tree/master/packages/demo)

packages/demo/README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,2 @@
11
# @data-ui/demo
22
Storybook of @data-ui examples. See it live at [williaster.github.io/data-ui](https://williaster.github.io/data-ui).
3-
4-
## @data-ui packages
5-
- [@data-ui/xy-chart](https://github.com/williaster/data-ui/tree/master/packages/xy-chart) [![Version](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat-square)](https://img.shields.io/npm/v/@data-ui/xy-chart.svg?style=flat-square)
6-
- [@data-ui/radial-chart](https://github.com/williaster/data-ui/tree/master/packages/radial-chart) [![Version](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat-square)](https://img.shields.io/npm/v/@data-ui/radial-chart.svg?style=flat-square)
7-
- [@data-ui/data-table](https://github.com/williaster/data-ui/tree/master/packages/data-table) [![Version](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat-square)](https://img.shields.io/npm/v/@data-ui/data-table.svg?style=flat-square)
8-
- [@data-ui/theme](https://github.com/williaster/data-ui/tree/master/packages/theme)
9-
- @data-ui/demo

packages/demo/examples/01-xy-chart/CirclePackWithCallback.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export default class CirclePackWithCallback extends React.Component {
4848

4949
<CirclePackSeries
5050
data={circlePackData.concat(circlePackData)}
51-
label="Circle time pack"
5251
size={d => d.r}
5352
pointComponent={RectPointComponent}
5453
layoutCallback={this.resizeCallback}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import React from 'react';
2+
import { utcFormat } from 'd3-time-format';
3+
4+
import {
5+
AreaSeries,
6+
StackedBarSeries,
7+
CrossHair,
8+
IntervalSeries,
9+
PatternLines,
10+
XAxis,
11+
XYChart,
12+
theme,
13+
withScreenSize,
14+
} from '@data-ui/xy-chart';
15+
16+
const PATTERN_ID = 'linked_pattern';
17+
const formatDate = utcFormat('%Y');
18+
const generateY = () => Math.max(10, Math.random() * 50);
19+
20+
const data = [
21+
{ x: new Date('2010-01-01 UTC'), y: generateY() },
22+
{ x: new Date('2011-01-01 UTC'), y: generateY() },
23+
{ x: new Date('2012-01-01 UTC'), y: generateY() },
24+
{ x: new Date('2013-01-01 UTC'), y: generateY() },
25+
{ x: new Date('2014-01-01 UTC'), y: generateY() },
26+
{ x: new Date('2015-01-01 UTC'), y: generateY() },
27+
{ x: new Date('2016-01-01 UTC'), y: generateY() },
28+
{ x: new Date('2017-01-01 UTC'), y: generateY() },
29+
{ x: new Date('2018-01-01 UTC'), y: generateY() },
30+
{ x: new Date('2019-01-01 UTC'), y: generateY() },
31+
{ x: new Date('2020-01-01 UTC'), y: generateY() },
32+
];
33+
34+
const tickValues = data.map(d => d.x);
35+
36+
const stackKeys = ['a', 'b', 'c'];
37+
const stackFills = theme.colors.categories.slice(2);
38+
39+
const stackedData = data.map((d) => {
40+
let total = 1;
41+
42+
return stackKeys.reduce((ret, key, i) => {
43+
const fraction = i === stackKeys.length - 1 ? total : 0.33;
44+
total -= fraction;
45+
return { ...ret, [key]: fraction * ret.y };
46+
}, d);
47+
});
48+
49+
const getYForKey = (d, key) => {
50+
const index = stackKeys.indexOf(key);
51+
return stackKeys.slice(0, index + 1).reduce((sum, currKey) => sum + d[currKey], 0);
52+
};
53+
54+
const areaChartProps = {
55+
xScale: { type: 'time' },
56+
yScale: { type: 'linear' },
57+
margin: { top: 6, left: 16, right: 16, bottom: 24 },
58+
};
59+
60+
const stackedChartProps = {
61+
xScale: { type: 'band', paddingInner: 0.1 },
62+
yScale: { type: 'linear' },
63+
margin: { top: 6, left: 16, right: 16, bottom: 24 },
64+
};
65+
66+
class LinkedXYCharts extends React.Component {
67+
constructor(props) {
68+
super(props);
69+
this.onClick = this.onClick.bind(this);
70+
this.onMouseMove = this.onMouseMove.bind(this);
71+
this.onMouseLeave = this.onMouseLeave.bind(this);
72+
73+
this.state = {
74+
selectedDatum: null,
75+
mousedOverDatum: null,
76+
mousedOverKey: null,
77+
};
78+
}
79+
80+
onClick({ datum }) {
81+
this.setState(({ selectedDatum }) => ({
82+
selectedDatum: datum === selectedDatum ? null : datum,
83+
}));
84+
}
85+
86+
onMouseMove({ datum, seriesKey }) {
87+
if (this.state.mousedOverDatum !== datum || this.state.mousedOverKey !== seriesKey) {
88+
this.setState(() => ({ mousedOverDatum: datum, mousedOverKey: seriesKey }));
89+
}
90+
}
91+
92+
onMouseLeave() {
93+
this.setState(() => ({ mousedOverDatum: null, mousedOverKey: null }));
94+
}
95+
96+
render() {
97+
const { screenWidth } = this.props; // eslint-disable-line react/prop-types
98+
const { mousedOverDatum, mousedOverKey, selectedDatum } = this.state;
99+
const width = Math.max(400, Math.min(700, screenWidth / 1.5));
100+
const height = 100;
101+
102+
const intervalData = selectedDatum ? [
103+
{
104+
x0: selectedDatum.x,
105+
x1: new Date(
106+
new Date(selectedDatum.x).setFullYear(new Date(selectedDatum.x).getFullYear() + 1),
107+
),
108+
},
109+
] : null;
110+
111+
const crossHairData = mousedOverDatum
112+
? { datum: { ...mousedOverDatum, y: mousedOverDatum[mousedOverKey] } }
113+
: null;
114+
115+
const stackCrossHairData = mousedOverDatum
116+
? { datum: { ...mousedOverDatum, y: getYForKey(mousedOverDatum, mousedOverKey) } }
117+
: null;
118+
119+
return (
120+
<div
121+
style={{
122+
fontFamily: theme.labelStyles.fontFamily,
123+
fontSize: 12,
124+
color: theme.colors.grays[6],
125+
display: 'flex',
126+
flexDirection: 'column',
127+
}}
128+
>
129+
<svg width={0} height={0}>
130+
<PatternLines
131+
id={PATTERN_ID}
132+
height={6}
133+
width={6}
134+
stroke={theme.colors.darkGray}
135+
strokeWidth={1}
136+
orientation={['diagonal']}
137+
/>
138+
</svg>
139+
140+
<XYChart
141+
ariaLabel="test"
142+
{...stackedChartProps}
143+
width={width}
144+
height={height * 3}
145+
theme={theme}
146+
tooltipData={stackCrossHairData}
147+
>
148+
<StackedBarSeries
149+
fillOpacity={0.9}
150+
data={stackedData}
151+
stackKeys={stackKeys}
152+
stackFills={stackFills}
153+
stroke={theme.colors.darkGray}
154+
strokeWidth={0.5}
155+
onMouseMove={this.onMouseMove}
156+
onMouseLeave={this.onMouseLeave}
157+
onClick={this.onClick}
158+
/>
159+
{selectedDatum &&
160+
<StackedBarSeries
161+
data={[selectedDatum]}
162+
stackKeys={['y']}
163+
stackFills={[`url(#${PATTERN_ID})`]}
164+
disableMouseEvents
165+
/>}
166+
{stackCrossHairData &&
167+
<CrossHair
168+
fullHeight
169+
showHorizontalLine={false}
170+
circleFill={stackFills[stackKeys.indexOf(mousedOverKey)]}
171+
/>}
172+
<XAxis tickFormat={formatDate} />
173+
</XYChart>
174+
175+
{stackKeys.map((seriesKey, stackIndex) => (
176+
<XYChart
177+
key={seriesKey}
178+
ariaLabel={seriesKey}
179+
{...areaChartProps}
180+
width={width}
181+
height={height}
182+
theme={theme}
183+
tooltipData={crossHairData}
184+
>
185+
<XAxis tickFormat={formatDate} tickValues={tickValues} />
186+
<AreaSeries
187+
data={stackedData.map(d => ({ ...d, y: d[seriesKey] }))}
188+
fill={stackFills[stackIndex]}
189+
fillOpacity={0.9}
190+
strokeWidth={1}
191+
stroke={stackFills[stackIndex]}
192+
onMouseMove={({ datum }) => { this.onMouseMove({ datum, seriesKey }); }}
193+
onMouseLeave={this.onMouseLeave}
194+
/>
195+
{intervalData &&
196+
<IntervalSeries
197+
fill={`url(#${PATTERN_ID})`}
198+
opacity={0.3}
199+
data={intervalData}
200+
/>}
201+
{mousedOverDatum &&
202+
<CrossHair
203+
fullHeight
204+
showHorizontalLine={false}
205+
showCircle={seriesKey === mousedOverKey}
206+
circleFill={stackFills[stackIndex]}
207+
/>}
208+
</XYChart>
209+
))}
210+
</div>
211+
);
212+
}
213+
}
214+
215+
216+
export default withScreenSize(LinkedXYCharts);

packages/demo/examples/01-xy-chart/ScatterWithHistograms.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ class ScatterWithHistogram extends React.PureComponent {
9191
{datasets.map((dataset, i) => (
9292
<PointSeries
9393
key={i}
94-
label={String(i)}
9594
data={dataset}
9695
fill={datasetColors[i]}
9796
opacity={0.7}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import React from 'react';
2+
import { timeParse, timeFormat } from 'd3-time-format';
3+
4+
import LegendOrdinal from '@vx/legend/build/legends/Ordinal';
5+
import scaleOrdinal from '@vx/scale/build/scales/ordinal';
6+
7+
import {
8+
CrossHair,
9+
StackedAreaSeries,
10+
PatternCircles,
11+
theme,
12+
XAxis,
13+
} from '@data-ui/xy-chart';
14+
15+
import {
16+
stackedData as initialStackedData,
17+
groupKeys as stackKeys,
18+
} from './data';
19+
20+
import ResponsiveXYChart from './ResponsiveXYChart';
21+
import WithToggle from '../shared/WithToggle';
22+
23+
const PATTERN_ID_1 = 'stackedarea_1';
24+
const PATTERN_ID_2 = 'stackedarea_2';
25+
const PATTERN_ID_3 = 'stackedarea_3';
26+
const PATTERN_COLOR = theme.colors.categories[4];
27+
28+
export const parseDate = timeParse('%Y%m%d');
29+
export const formatDate = timeFormat('%b %d');
30+
31+
const stackedData = initialStackedData.map(d => ({
32+
x: parseDate(d.x),
33+
...stackKeys.reduce((obj, key) => {
34+
// multple by a random fraction bc there isn't much variation in temp
35+
const value = d[key] * Math.max(0.1, Math.random());
36+
return { ...obj, y: obj.y + value, [key]: value };
37+
}, { y: 0 }),
38+
}));
39+
40+
const percentStackedData = stackedData.map(d => ({
41+
...d,
42+
y: 1,
43+
...stackKeys.reduce((obj, key) => ({ ...obj, [key]: d[key] / d.y }), {}),
44+
}));
45+
46+
const patternIds = [PATTERN_ID_1, PATTERN_ID_2, PATTERN_ID_3];
47+
const stackFills = patternIds.map(id => `url(#${id})`);
48+
const legendScale = scaleOrdinal({ range: stackFills, domain: stackKeys });
49+
50+
export default function StackedAreaExample() {
51+
return (
52+
<WithToggle id="lineseries_toggle" label="As percent" initialChecked>
53+
{asPercent => (
54+
<div
55+
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}
56+
>
57+
<LegendOrdinal
58+
key="legend"
59+
direction="row"
60+
scale={legendScale}
61+
shape={({ fill, width, height }) => (
62+
<svg width={width} height={height}>
63+
<rect
64+
width={width}
65+
height={height}
66+
fill={fill}
67+
/>
68+
</svg>
69+
)}
70+
fill={({ datum }) => legendScale(datum)}
71+
labelFormat={label => label}
72+
/>
73+
74+
<ResponsiveXYChart
75+
ariaLabel="Stacked area chart of temperatures"
76+
key="chart"
77+
xScale={{ type: 'time' }}
78+
yScale={{ type: 'linear' }}
79+
margin={{ top: 8, left: 24, right: 24 }}
80+
>
81+
<PatternCircles
82+
id={PATTERN_ID_1}
83+
width={8}
84+
height={8}
85+
radius={2}
86+
stroke={PATTERN_COLOR}
87+
fill={'#fff'}
88+
/>
89+
<PatternCircles
90+
id={PATTERN_ID_2}
91+
width={2}
92+
height={2}
93+
radius={2}
94+
stroke={PATTERN_COLOR}
95+
fill={PATTERN_COLOR}
96+
/>
97+
<PatternCircles
98+
id={PATTERN_ID_3}
99+
width={4}
100+
height={4}
101+
radius={2}
102+
stroke={PATTERN_COLOR}
103+
fill={'#fff'}
104+
/>
105+
<StackedAreaSeries
106+
data={asPercent ? percentStackedData : stackedData}
107+
strokeWidth={2}
108+
stackKeys={stackKeys}
109+
stackFills={stackFills}
110+
fillOpacity={1}
111+
/>
112+
<CrossHair
113+
stroke={PATTERN_COLOR}
114+
strokeWidth={2}
115+
showHorizontalLine={false}
116+
showCircle={false}
117+
strokeDasharray=""
118+
/>
119+
<CrossHair
120+
stroke="#fff"
121+
strokeWidth={1}
122+
showHorizontalLine={false}
123+
showCircle={false}
124+
strokeDasharray=""
125+
/>
126+
<XAxis tickFormat={formatDate} />
127+
</ResponsiveXYChart>
128+
</div>
129+
)}
130+
</WithToggle>
131+
);
132+
}

0 commit comments

Comments
 (0)