Skip to content

Commit

Permalink
feat: add screenshot functions to partition/goal (opensearch-project#697
Browse files Browse the repository at this point in the history
)

This commit adds the screenshot function to partition chart and goal charts
  • Loading branch information
markov00 authored Jun 11, 2020
1 parent 323b325 commit 6d2ff64
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import React, { MouseEvent } from 'react';
import React, { MouseEvent, RefObject } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';

Expand All @@ -39,18 +39,20 @@ interface ReactiveChartDispatchProps {
onChartRendered: typeof onChartRendered;
}

type Props = ReactiveChartStateProps & ReactiveChartDispatchProps;
interface ReactiveChartOwnProps {
forwardStageRef: RefObject<HTMLCanvasElement>;
}

type Props = ReactiveChartStateProps & ReactiveChartDispatchProps & ReactiveChartOwnProps;
class Component extends React.Component<Props> {
static displayName = 'Goal';
// firstRender = true; // this'll be useful for stable resizing of treemaps
private readonly canvasRef: React.RefObject<HTMLCanvasElement>;
private ctx: CanvasRenderingContext2D | null;
// see example https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Example
private readonly devicePixelRatio: number; // fixme this be no constant: multi-monitor window drag may necessitate modifying the `<canvas>` dimensions

constructor(props: Readonly<Props>) {
super(props);
this.canvasRef = React.createRef();
this.ctx = null;
this.devicePixelRatio = window.devicePixelRatio;
}
Expand Down Expand Up @@ -78,7 +80,7 @@ class Component extends React.Component<Props> {
}

private tryCanvasContext() {
const canvas = this.canvasRef.current;
const canvas = this.props.forwardStageRef.current;
this.ctx = canvas && canvas.getContext('2d');
}

Expand All @@ -96,13 +98,15 @@ class Component extends React.Component<Props> {
const {
initialized,
chartContainerDimensions: { width, height },
forwardStageRef,
geometries,
} = this.props;
if (!this.canvasRef.current || !this.ctx || !initialized || width === 0 || height === 0) {
if (!forwardStageRef.current || !this.ctx || !initialized || width === 0 || height === 0) {
return;
}
const picker = this.props.geometries.pickQuads;
const box = this.canvasRef.current.getBoundingClientRect();
const { chartCenter } = this.props.geometries;
const picker = geometries.pickQuads;
const box = forwardStageRef.current.getBoundingClientRect();
const { chartCenter } = geometries;
const x = e.clientX - box.left - chartCenter.x;
const y = e.clientY - box.top - chartCenter.y;
const pickedShapes: Array<BulletViewModel> = picker(x, y);
Expand All @@ -113,14 +117,15 @@ class Component extends React.Component<Props> {
const {
initialized,
chartContainerDimensions: { width, height },
forwardStageRef,
} = this.props;
if (!initialized || width === 0 || height === 0) {
return null;
}

return (
<canvas
ref={this.canvasRef}
ref={forwardStageRef}
className="echCanvasRenderer"
width={width * this.devicePixelRatio}
height={height * this.devicePixelRatio}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import React from 'react';
import React, { RefObject } from 'react';

import { ChartTypes } from '../..';
import { LegendItem } from '../../../commons/legend';
Expand Down Expand Up @@ -78,11 +78,11 @@ export class GoalState implements InternalChartState {
return EMPTY_MAP;
}

chartRenderer(containerRef: BackwardRef) {
chartRenderer(containerRef: BackwardRef, forwardStageRef: RefObject<HTMLCanvasElement>) {
return (
<>
<Tooltip getChartContainerRef={containerRef} />
<Goal />
<Goal forwardStageRef={forwardStageRef} />
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import React, { MouseEvent } from 'react';
import React, { MouseEvent, RefObject } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';

Expand All @@ -40,20 +40,21 @@ interface ReactiveChartStateProps {
interface ReactiveChartDispatchProps {
onChartRendered: typeof onChartRendered;
}
interface ReactiveChartOwnProps {
forwardStageRef: RefObject<HTMLCanvasElement>;
}

type PartitionProps = ReactiveChartStateProps & ReactiveChartDispatchProps;
type PartitionProps = ReactiveChartStateProps & ReactiveChartDispatchProps & ReactiveChartOwnProps;
class PartitionComponent extends React.Component<PartitionProps> {
static displayName = 'Partition';

// firstRender = true; // this'll be useful for stable resizing of treemaps
private readonly canvasRef: React.RefObject<HTMLCanvasElement>;
private ctx: CanvasRenderingContext2D | null;
// see example https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Example
private readonly devicePixelRatio: number; // fixme this be no constant: multi-monitor window drag may necessitate modifying the `<canvas>` dimensions

constructor(props: Readonly<PartitionProps>) {
super(props);
this.canvasRef = React.createRef();
this.ctx = null;
this.devicePixelRatio = window.devicePixelRatio;
}
Expand Down Expand Up @@ -91,20 +92,21 @@ class PartitionComponent extends React.Component<PartitionProps> {
}

private tryCanvasContext() {
const canvas = this.canvasRef.current;
const canvas = this.props.forwardStageRef.current;
this.ctx = canvas && canvas.getContext('2d');
}

handleMouseMove(e: MouseEvent<HTMLCanvasElement>) {
const {
initialized,
chartContainerDimensions: { width, height },
forwardStageRef,
} = this.props;
if (!this.canvasRef.current || !this.ctx || !initialized || width === 0 || height === 0) {
if (!forwardStageRef.current || !this.ctx || !initialized || width === 0 || height === 0) {
return;
}
const picker = this.props.geometries.pickQuads;
const box = this.canvasRef.current.getBoundingClientRect();
const box = forwardStageRef.current.getBoundingClientRect();
const { diskCenter } = this.props.geometries;
const x = e.clientX - box.left - diskCenter.x;
const y = e.clientY - box.top - diskCenter.y;
Expand All @@ -129,6 +131,7 @@ class PartitionComponent extends React.Component<PartitionProps> {

render() {
const {
forwardStageRef,
initialized,
chartContainerDimensions: { width, height },
} = this.props;
Expand All @@ -138,7 +141,7 @@ class PartitionComponent extends React.Component<PartitionProps> {

return (
<canvas
ref={this.canvasRef}
ref={forwardStageRef}
className="echCanvasRenderer"
width={width * this.devicePixelRatio}
height={height * this.devicePixelRatio}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import React from 'react';
import React, { RefObject } from 'react';

import { ChartTypes } from '../..';
import { Tooltip } from '../../../components/tooltip';
Expand Down Expand Up @@ -78,11 +78,11 @@ export class PartitionState implements InternalChartState {
return EMPTY_MAP;
}

chartRenderer(containerRef: BackwardRef) {
chartRenderer(containerRef: BackwardRef, forwardStageRef: RefObject<HTMLCanvasElement>) {
return (
<>
<Tooltip getChartContainerRef={containerRef} />
<Partition />
<Partition forwardStageRef={forwardStageRef} />
<HighlighterFromHover />
<HighlighterFromLegend />
</>
Expand Down
108 changes: 100 additions & 8 deletions packages/osd-charts/stories/interactions/17_png_export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,34 @@
* under the License.
*/

import { button } from '@storybook/addon-knobs';
import React from 'react';
import { button, select } from '@storybook/addon-knobs';
import React, { RefObject } from 'react';

import { Axis, BarSeries, Chart, niceTimeFormatter, Position, ScaleType, Settings } from '../../src';
import {
Axis,
BarSeries,
Chart,
niceTimeFormatter,
Position,
ScaleType,
Settings,
Partition,
Datum,
Goal,
ChartTypes,
} from '../../src';
import { GOAL_SUBTYPES, BandFillColorAccessorInput } from '../../src/chart_types/goal_chart/specs';
import { config } from '../../src/chart_types/partition_chart/layout/config/config';
import { mocks } from '../../src/mocks/hierarchical';
import { Color } from '../../src/utils/commons';
import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana';
import { SB_KNOBS_PANEL } from '../utils/storybook';
import { productLookup, indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils';

export const Example = () => {
/**
* The handler section of this story demonstrates the PNG export functionality
*/
const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 100);
const label = 'Export PNG';
const chartRef: React.RefObject<Chart> = React.createRef();
const handler = () => {
if (!chartRef.current) {
Expand Down Expand Up @@ -57,8 +72,49 @@ export const Example = () => {
document.body.removeChild(link);
}
};
const groupId = '';
button(label, handler, groupId);
button('Export PNG', handler);
const selectedChart = select(
'chart type',
[ChartTypes.XYAxis, ChartTypes.Partition, ChartTypes.Goal],
ChartTypes.XYAxis,
);

switch (selectedChart) {
case ChartTypes.Partition:
return renderPartitionChart(chartRef);
case ChartTypes.Goal:
return renderGoalchart(chartRef);
case ChartTypes.XYAxis:
default:
return renderXYAxisChart(chartRef);
}
};

function renderPartitionChart(chartRef: RefObject<Chart>) {
return (
<Chart ref={chartRef}>
<Partition
id="spec_1"
data={mocks.pie}
valueAccessor={(d: Datum) => d.exportVal as number}
valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\u00A0Bn`}
layers={[
{
groupByRollup: (d: Datum) => d.sitc1,
nodeLabel: (d: Datum) => productLookup[d].name,
fillLabel: { textInvertible: true },
shape: {
fillColor: indexInterpolatedFillColor(interpolatorCET2s),
},
},
]}
/>
</Chart>
);
}

function renderXYAxisChart(chartRef: RefObject<Chart>) {
const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 100);
return (
<Chart className="story-chart" ref={chartRef}>
<Settings showLegend showLegendExtra />
Expand All @@ -80,7 +136,43 @@ export const Example = () => {
/>
</Chart>
);
};
}

function renderGoalchart(chartRef: RefObject<Chart>) {
const subtype = GOAL_SUBTYPES[0];

const colorMap: { [k: number]: Color } = {
200: '#fc8d62',
250: 'lightgrey',
300: '#66c2a5',
};

const bandFillColor = (x: number): Color => colorMap[x];

return (
<Chart className="story-chart" ref={chartRef}>
<Goal
id="spec_1"
subtype={subtype}
base={0}
target={260}
actual={280}
bands={[200, 250, 300]}
ticks={[0, 50, 100, 150, 200, 250, 300]}
tickValueFormatter={({ value }: BandFillColorAccessorInput) => String(value)}
bandFillColor={({ value }: BandFillColorAccessorInput) => bandFillColor(value)}
labelMajor=""
labelMinor=""
centralMajor="280 MB/s"
centralMinor=""
config={{
angleStart: Math.PI + (Math.PI - (2 * Math.PI) / 3) / 2,
angleEnd: -(Math.PI - (2 * Math.PI) / 3) / 2,
}}
/>
</Chart>
);
}

// storybook configuration
Example.story = {
Expand Down

0 comments on commit 6d2ff64

Please sign in to comment.