Skip to content

Commit 7c4c3ab

Browse files
committed
converted interactive examples
1 parent f6a18a5 commit 7c4c3ab

2 files changed

Lines changed: 247 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import {
2+
Chart,
3+
ChartArea,
4+
ChartAxis,
5+
ChartGroup,
6+
ChartLegend,
7+
ChartLegendTooltip,
8+
ChartScatter,
9+
ChartThemeColor,
10+
createContainer,
11+
getInteractiveLegendEvents,
12+
getInteractiveLegendItemStyles
13+
} from '@patternfly/react-charts/victory';
14+
import { getResizeObserver } from '@patternfly/react-core';
15+
16+
export const ChartLegendInteractive: React.FunctionComponent = () => {
17+
const containerRef = React.useRef(null);
18+
const [hiddenSeries, setHiddenSeries] = React.useState(new Set());
19+
const [width, setWidth] = React.useState(0);
20+
21+
const series = [
22+
{
23+
datapoints: [
24+
{ x: '2015', y: 3 },
25+
{ x: '2016', y: 4 },
26+
{ x: '2017', y: 8 },
27+
{ x: '2018', y: 6 }
28+
],
29+
legendItem: { name: 'Cats' }
30+
},
31+
{
32+
datapoints: [
33+
{ x: '2015', y: 2 },
34+
{ x: '2016', y: 3 },
35+
{ x: '2017', y: 4 },
36+
{ x: '2018', y: 5 },
37+
{ x: '2019', y: 6 }
38+
],
39+
legendItem: { name: 'Dogs' }
40+
},
41+
{
42+
datapoints: [
43+
{ x: '2015', y: 1 },
44+
{ x: '2016', y: 2 },
45+
{ x: '2017', y: 3 },
46+
{ x: '2018', y: 2 },
47+
{ x: '2019', y: 4 }
48+
],
49+
legendItem: { name: 'Birds' }
50+
}
51+
];
52+
53+
// Returns groups of chart names associated with each data series
54+
const getChartNames = () => series.map((_, index) => [`area-${index}`, `scatter-${index}`]);
55+
56+
// Handles legend click to toggle visibility of data series
57+
const handleLegendClick = (props) => {
58+
setHiddenSeries((prev) => {
59+
const newHidden = new Set(prev);
60+
if (!newHidden.delete(props.index)) {
61+
newHidden.add(props.index);
62+
}
63+
return newHidden;
64+
});
65+
};
66+
67+
// Returns legend data styled per hiddenSeries
68+
const getLegendData = () =>
69+
series.map((s, index) => ({
70+
childName: `area-${index}`,
71+
...s.legendItem,
72+
...getInteractiveLegendItemStyles(hiddenSeries.has(index))
73+
}));
74+
75+
// Returns true if data series is hidden
76+
const isHidden = (index) => hiddenSeries.has(index);
77+
78+
// Checks if any data series is visible
79+
const isDataAvailable = () => hiddenSeries.size !== series.length;
80+
81+
// Set chart width per current window size
82+
useEffect(() => {
83+
const observer = getResizeObserver(containerRef.current, () => {
84+
if (containerRef.current?.clientWidth) {
85+
setWidth(containerRef.current.clientWidth);
86+
}
87+
});
88+
return () => observer();
89+
}, []);
90+
91+
// Note: Container order is important
92+
const CursorVoronoiContainer = createContainer('voronoi', 'cursor');
93+
const container = (
94+
<CursorVoronoiContainer
95+
cursorDimension="x"
96+
labels={({ datum }) => (datum.childName.includes('area-') && datum.y !== null ? `${datum.y}` : null)}
97+
labelComponent={<ChartLegendTooltip legendData={getLegendData()} title={(datum) => datum.x} />}
98+
mouseFollowTooltips
99+
voronoiDimension="x"
100+
voronoiPadding={50}
101+
disable={!isDataAvailable()}
102+
/>
103+
);
104+
105+
return (
106+
<div ref={containerRef}>
107+
<div className="area-chart-legend-bottom-responsive">
108+
<Chart
109+
ariaDesc="Average number of pets"
110+
ariaTitle="Area chart example"
111+
containerComponent={container}
112+
events={getInteractiveLegendEvents({
113+
chartNames: getChartNames(),
114+
isHidden,
115+
legendName: 'chart5-ChartLegend',
116+
onLegendClick: handleLegendClick
117+
})}
118+
height={225}
119+
legendComponent={<ChartLegend name={'chart5-ChartLegend'} data={getLegendData()} />}
120+
legendPosition="bottom-left"
121+
name="chart5"
122+
padding={{ bottom: 75, left: 50, right: 50, top: 50 }}
123+
maxDomain={{ y: 9 }}
124+
themeColor={ChartThemeColor.multiUnordered}
125+
width={width}
126+
>
127+
<ChartAxis tickValues={['2015', '2016', '2017', '2018']} />
128+
<ChartAxis dependentAxis showGrid />
129+
<ChartGroup>
130+
{series.map((s, index) => (
131+
<ChartScatter
132+
key={`scatter-${index}`}
133+
name={`scatter-${index}`}
134+
data={!isHidden(index) ? s.datapoints : [{ y: null }]}
135+
size={({ active }) => (active ? 5 : 3)}
136+
/>
137+
))}
138+
</ChartGroup>
139+
<ChartGroup>
140+
{series.map((s, index) => (
141+
<ChartArea
142+
key={`area-${index}`}
143+
name={`area-${index}`}
144+
data={!isHidden(index) ? s.datapoints : [{ y: null }]}
145+
interpolation="monotoneX"
146+
/>
147+
))}
148+
</ChartGroup>
149+
</Chart>
150+
</div>
151+
</div>
152+
);
153+
};
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import {
2+
Chart,
3+
ChartLegend,
4+
ChartThemeColor,
5+
ChartPie,
6+
getInteractiveLegendEvents,
7+
getInteractiveLegendItemStyles
8+
} from '@patternfly/react-charts/victory';
9+
10+
export const ChartLegendInteractivePieChart: React.FunctionComponent = () => {
11+
const [hiddenSeries, setHiddenSeries] = React.useState<Set<number>>(new Set());
12+
13+
const series = [
14+
{
15+
datapoints: { x: 'Cats', y: 35 },
16+
legendItem: { name: 'Cats: 35' }
17+
},
18+
{
19+
datapoints: { x: 'Dogs', y: 55 },
20+
legendItem: { name: 'Dogs: 55' }
21+
},
22+
{
23+
datapoints: { x: 'Birds', y: 10 },
24+
legendItem: { name: 'Birds: 10' }
25+
}
26+
];
27+
28+
// Returns groups of chart names associated with each data series
29+
const getChartNames = () => {
30+
const result = [];
31+
series.map((_, _index) => {
32+
// Provide names for each series hidden / shown -- use the same name for a pie chart
33+
result.push(['pie']);
34+
});
35+
return result;
36+
};
37+
38+
// Returns legend data styled per hiddenSeries
39+
const getLegendData = () =>
40+
series.map((s, index) => ({
41+
...s.legendItem, // name property
42+
...getInteractiveLegendItemStyles(hiddenSeries.has(index)) // hidden styles
43+
}));
44+
45+
// Hide each data series individually
46+
const handleLegendClick = (props: { index: number }) => {
47+
const newHiddenSeries = new Set(hiddenSeries);
48+
if (newHiddenSeries.delete(props.index)) {
49+
newHiddenSeries.add(props.index);
50+
}
51+
setHiddenSeries(newHiddenSeries);
52+
};
53+
54+
// Returns true if data series is hidden
55+
const isHidden = (index: number) => hiddenSeries.has(index);
56+
// Returns onMouseOver, onMouseOut, and onClick events for the interactive legend
57+
const getEvents = () =>
58+
getInteractiveLegendEvents({
59+
chartNames: getChartNames(),
60+
isHidden,
61+
legendName: 'chart6-ChartLegend',
62+
onLegendClick: handleLegendClick
63+
});
64+
65+
const data = [];
66+
series.map((s, index) => {
67+
data.push(!hiddenSeries.has(index) ? s.datapoints : { y: null });
68+
});
69+
70+
return (
71+
<div style={{ height: '275px', width: '300px' }}>
72+
<Chart
73+
ariaDesc="Average number of pets"
74+
ariaTitle="Pie chart example"
75+
events={getEvents()}
76+
height={275}
77+
legendComponent={<ChartLegend name={'chart6-ChartLegend'} data={getLegendData()} />}
78+
legendPosition="bottom"
79+
name="chart6"
80+
padding={{
81+
bottom: 65,
82+
left: 20,
83+
right: 20,
84+
top: 20
85+
}}
86+
showAxis={false}
87+
themeColor={ChartThemeColor.multiUnordered}
88+
width={300}
89+
>
90+
<ChartPie constrainToVisibleArea data={data} labels={({ datum }) => `${datum.x}: ${datum.y}`} name="pie" />
91+
</Chart>
92+
</div>
93+
);
94+
};

0 commit comments

Comments
 (0)