Skip to content
This repository has been archived by the owner on Jun 25, 2020. It is now read-only.

Commit

Permalink
feat: support custom tooltip for scatterplot and box plot
Browse files Browse the repository at this point in the history
  • Loading branch information
kristw committed May 21, 2019
1 parent dd053bd commit fbf6be3
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 108 deletions.
21 changes: 18 additions & 3 deletions packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,32 @@ import { chartTheme, ChartTheme } from '@data-ui/theme';
import { Margin, Dimension } from '@superset-ui/dimension';
import { WithLegend } from '@superset-ui/chart-composition';
import { createSelector } from 'reselect';
import createTooltip from './createTooltip';
import DefaultTooltipRenderer from './DefaultTooltipRenderer';
import ChartLegend from '../components/legend/ChartLegend';
import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder';
import { Dataset, PlainObject } from '../encodeable/types/Data';
import { PartialSpec } from '../encodeable/types/Specification';
import createMarginSelector, { DEFAULT_MARGIN } from '../utils/selectors/createMarginSelector';
import createXYChartLayoutSelector from '../utils/selectors/createXYChartLayoutSelector';
import { BoxPlotDataRow } from './types';

export interface TooltipProps {
datum: BoxPlotDataRow;
color: string;
encoder: Encoder;
}

const defaultProps = {
className: '',
margin: DEFAULT_MARGIN,
theme: chartTheme,
TooltipRenderer: DefaultTooltipRenderer,
} as const;

export type HookProps = {
TooltipRenderer?: React.ComponentType<TooltipProps>;
};

type Props = {
className?: string;
width: string | number;
Expand All @@ -27,6 +39,7 @@ type Props = {
data: Dataset;
theme?: ChartTheme;
} & PartialSpec<Encoding> &
HookProps &
Readonly<typeof defaultProps>;

export default class BoxPlot extends React.PureComponent<Props> {
Expand Down Expand Up @@ -59,7 +72,7 @@ export default class BoxPlot extends React.PureComponent<Props> {

renderChart(dim: Dimension) {
const { width, height } = dim;
const { data, margin, theme } = this.props;
const { data, margin, theme, TooltipRenderer } = this.props;
const { channels } = this.encoder;

const isHorizontal = channels.y.definition.type === 'nominal';
Expand Down Expand Up @@ -98,7 +111,9 @@ export default class BoxPlot extends React.PureComponent<Props> {
height={chartDim.height}
ariaLabel="BoxPlot"
margin={layout.margin}
renderTooltip={createTooltip(this.encoder)}
renderTooltip={({ datum, color }: { datum: BoxPlotDataRow; color: string }) => (
<TooltipRenderer datum={datum} color={color} encoder={this.encoder} />
)}
showYGrid
theme={theme}
xScale={channels.x.definition.scale}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { isDefined } from '@superset-ui/core';
import { TooltipFrame, TooltipTable } from '@superset-ui/chart-composition';
import Encoder from './Encoder';
import { BoxPlotDataRow } from './types';

export default function DefaultTooltipRenderer({
datum,
color,
encoder,
}: {
datum: BoxPlotDataRow;
color: string;
encoder: Encoder;
}) {
const { label, min, max, median, firstQuartile, thirdQuartile, outliers } = datum;
const { channels } = encoder;

const formatValue =
channels.y.definition.type === 'nominal' ? channels.x.formatValue : channels.y.formatValue;

const data = [];
if (isDefined(min)) {
data.push({ key: 'Min', valueColumn: formatValue(min) });
}
if (isDefined(max)) {
data.push({ key: 'Max', valueColumn: formatValue(max) });
}
if (isDefined(median)) {
data.push({ key: 'Median', valueColumn: formatValue(median) });
}
if (isDefined(firstQuartile)) {
data.push({ key: '1st Quartile', valueColumn: formatValue(firstQuartile) });
}
if (isDefined(thirdQuartile)) {
data.push({ key: '3rd Quartile', valueColumn: formatValue(thirdQuartile) });
}
if (isDefined(outliers) && outliers.length > 0) {
data.push({ key: '# Outliers', valueColumn: outliers.length });
}

return (
<TooltipFrame>
<div>
<strong style={{ color }}>{label}</strong>
</div>
{data.length > 0 && <br />}
<TooltipTable data={data} />
</TooltipFrame>
);
}
46 changes: 0 additions & 46 deletions packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable no-magic-numbers */

import React from 'react';
import { TooltipFrame, TooltipTable } from '@superset-ui/chart-composition';
import { isFieldDef } from '../encodeable/types/ChannelDef';
import { TooltipProps } from './ScatterPlot';

export default function DefaultTooltipRenderer({ datum, encoder }: TooltipProps) {
const { channels, commonChannels } = encoder;
const { x, y, size, fill, stroke } = channels;

const tooltipRows = [
{ key: 'x', keyColumn: x.getTitle(), valueColumn: x.format(datum.data) },
{ key: 'y', keyColumn: y.getTitle(), valueColumn: y.format(datum.data) },
];

if (isFieldDef(fill.definition)) {
tooltipRows.push({
key: 'fill',
keyColumn: fill.getTitle(),
valueColumn: fill.format(datum.data),
});
}
if (isFieldDef(stroke.definition)) {
tooltipRows.push({
key: 'stroke',
keyColumn: stroke.getTitle(),
valueColumn: stroke.format(datum.data),
});
}
if (isFieldDef(size.definition)) {
tooltipRows.push({
key: 'size',
keyColumn: size.getTitle(),
valueColumn: size.format(datum.data),
});
}
commonChannels.group.forEach(g => {
tooltipRows.push({
key: `${g.name}`,
keyColumn: g.getTitle(),
valueColumn: g.format(datum.data),
});
});

return (
<TooltipFrame>
<TooltipTable data={tooltipRows} />
</TooltipFrame>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ import { Margin, Dimension } from '@superset-ui/dimension';
import { WithLegend } from '@superset-ui/chart-composition';
import { extent as d3Extent } from 'd3-array';
import { createSelector } from 'reselect';
import createTooltip from './createTooltip';
import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder';
import { Dataset, PlainObject } from '../encodeable/types/Data';
import ChartLegend from '../components/legend/ChartLegend';
import { PartialSpec } from '../encodeable/types/Specification';
import createMarginSelector, { DEFAULT_MARGIN } from '../utils/selectors/createMarginSelector';
import createXYChartLayoutSelector from '../utils/selectors/createXYChartLayoutSelector';
import DefaultTooltipRenderer from './DefaultTooltipRenderer';

export interface TooltipProps {
datum: EncodedPoint;
encoder: Encoder;
}

const defaultProps = {
className: '',
margin: DEFAULT_MARGIN,
theme: chartTheme,
TooltipRenderer: DefaultTooltipRenderer,
} as const;

export type HookProps = {
TooltipRenderer?: React.ComponentType<TooltipProps>;
};

type Props = {
className?: string;
width: string | number;
Expand All @@ -28,6 +38,7 @@ type Props = {
data: Dataset;
theme?: ChartTheme;
} & PartialSpec<Encoding> &
HookProps &
Readonly<typeof defaultProps>;

export interface EncodedPoint {
Expand Down Expand Up @@ -69,7 +80,7 @@ export default class ScatterPlot extends PureComponent<Props> {

renderChart(dim: Dimension) {
const { width, height } = dim;
const { data, margin, theme } = this.props;
const { data, margin, theme, TooltipRenderer } = this.props;
const { channels } = this.encoder;

if (typeof channels.size.scale !== 'undefined') {
Expand Down Expand Up @@ -115,7 +126,9 @@ export default class ScatterPlot extends PureComponent<Props> {
height={chartDim.height}
ariaLabel="BoxPlot"
margin={layout.margin}
renderTooltip={createTooltip(this.encoder)}
renderTooltip={({ datum }: { datum: EncodedPoint }) => (
<TooltipRenderer datum={datum} encoder={this.encoder} />
)}
showYGrid
theme={theme}
xScale={channels.x.definition.scale}
Expand Down

This file was deleted.

0 comments on commit fbf6be3

Please sign in to comment.