Skip to content

Commit 0d607a6

Browse files
committed
feat: migrate scatter plot
1 parent 3aebda9 commit 0d607a6

File tree

3 files changed

+70
-112
lines changed

3 files changed

+70
-112
lines changed

packages/superset-ui-preset-chart-xy/src/ScatterPlot/DefaultTooltipRenderer.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,50 @@
11
import React from 'react';
22
import { TooltipFrame, TooltipTable } from '@superset-ui/chart-composition';
3-
import { isFieldDef } from '../encodeable/types/ChannelDef';
3+
import { isFieldDef } from 'encodable/lib/typeGuards/ChannelDef';
44
import { TooltipProps } from './ScatterPlot';
55

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

1010
const tooltipRows = [
11-
{ key: 'x', keyColumn: x.getTitle(), valueColumn: x.format(datum.data) },
12-
{ key: 'y', keyColumn: y.getTitle(), valueColumn: y.format(datum.data) },
11+
{ key: 'x', keyColumn: x.getTitle(), valueColumn: x.formatDatum(datum.data) },
12+
{ key: 'y', keyColumn: y.getTitle(), valueColumn: y.formatDatum(datum.data) },
1313
];
1414

1515
if (isFieldDef(fill.definition)) {
1616
tooltipRows.push({
1717
key: 'fill',
1818
keyColumn: fill.getTitle(),
19-
valueColumn: fill.format(datum.data),
19+
valueColumn: fill.formatDatum(datum.data),
2020
});
2121
}
2222
if (isFieldDef(stroke.definition)) {
2323
tooltipRows.push({
2424
key: 'stroke',
2525
keyColumn: stroke.getTitle(),
26-
valueColumn: stroke.format(datum.data),
26+
valueColumn: stroke.formatDatum(datum.data),
2727
});
2828
}
2929
if (isFieldDef(size.definition)) {
3030
tooltipRows.push({
3131
key: 'size',
3232
keyColumn: size.getTitle(),
33-
valueColumn: size.format(datum.data),
33+
valueColumn: size.formatDatum(datum.data),
3434
});
3535
}
3636
channels.group.forEach(g => {
3737
tooltipRows.push({
3838
key: `${g.name}`,
3939
keyColumn: g.getTitle(),
40-
valueColumn: g.format(datum.data),
40+
valueColumn: g.formatDatum(datum.data),
4141
});
4242
});
4343
channels.tooltip.forEach(g => {
4444
tooltipRows.push({
4545
key: `${g.name}`,
4646
keyColumn: g.getTitle(),
47-
valueColumn: g.format(datum.data),
47+
valueColumn: g.formatDatum(datum.data),
4848
});
4949
});
5050

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,39 @@
1-
import { Value } from 'vega-lite/build/src/channeldef';
2-
import { ChannelTypeToDefMap } from '../encodeable/types/Channel';
3-
import { ExtractChannelOutput } from '../encodeable/types/ChannelDef';
4-
import createEncoderClass from '../encodeable/createEncoderClass';
1+
import { createEncoderFactory, Encoder } from 'encodable';
2+
import { DeriveEncoding, DeriveChannelOutputs } from 'encodable/lib/types/Encoding';
53

6-
/**
7-
* Define channel types
8-
*/
9-
const channelTypes = {
10-
fill: 'Color',
11-
group: 'Text',
12-
size: 'Numeric',
13-
stroke: 'Color',
14-
tooltip: 'Text',
15-
x: 'X',
16-
y: 'Y',
17-
} as const; // "as const" is mandatory
18-
19-
export type ChannelTypes = typeof channelTypes;
20-
21-
/**
22-
* TEMPLATE:
23-
* Helper for defining encoding
24-
*/
25-
type CreateChannelDef<
26-
ChannelName extends keyof ChannelTypes,
27-
Output extends Value
28-
> = ChannelTypeToDefMap<Output>[ChannelTypes[ChannelName]];
29-
30-
/**
31-
* Encoding definition
32-
*/
33-
export type Encoding = {
34-
fill: CreateChannelDef<'fill', string>;
35-
group: CreateChannelDef<'group', string>[];
36-
size: CreateChannelDef<'size', number>;
37-
stroke: CreateChannelDef<'stroke', string>;
38-
tooltip: CreateChannelDef<'tooltip', string>[];
39-
x: CreateChannelDef<'x', number>;
40-
y: CreateChannelDef<'y', number>;
4+
export type ScatterPlotEncodingConfig = {
5+
x: ['X', number];
6+
y: ['Y', number];
7+
fill: ['Color', string];
8+
group: ['Category', string, 'multiple'];
9+
size: ['Numeric', number];
10+
stroke: ['Color', string];
11+
tooltip: ['Text', string, 'multiple'];
4112
};
4213

43-
/**
44-
* TEMPLATE:
45-
* Can use this to get returned type of a Channel
46-
* example usage: ChannelOutput<'x'>
47-
*/
48-
export type ChannelOutput<ChannelName extends keyof Encoding> = ExtractChannelOutput<
49-
Encoding[ChannelName]
50-
>;
51-
52-
export default class Encoder extends createEncoderClass<ChannelTypes, Encoding>({
53-
channelTypes,
14+
export const scatterPlotEncoderFactory = createEncoderFactory<ScatterPlotEncodingConfig>({
15+
channelTypes: {
16+
x: 'X',
17+
y: 'Y',
18+
fill: 'Color',
19+
group: 'Category',
20+
size: 'Numeric',
21+
stroke: 'Color',
22+
tooltip: 'Text',
23+
},
5424
defaultEncoding: {
25+
x: { field: 'x', type: 'quantitative' },
26+
y: { field: 'y', type: 'quantitative' },
5527
fill: { value: '#222' },
5628
group: [],
5729
size: { value: 5 },
5830
stroke: { value: 'none' },
5931
tooltip: [],
60-
x: { field: 'x', type: 'quantitative' },
61-
y: { field: 'y', type: 'quantitative' },
6232
},
63-
}) {}
33+
});
34+
35+
export type ScatterPlotEncoding = DeriveEncoding<ScatterPlotEncodingConfig>;
36+
37+
export type ScatterPlotEncoder = Encoder<ScatterPlotEncodingConfig>;
38+
39+
export type ScatterPlotChannelOutputs = DeriveChannelOutputs<ScatterPlotEncodingConfig>;

packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,56 @@ import { XYChart, PointSeries } from '@data-ui/xy-chart';
33
import { chartTheme, ChartTheme } from '@data-ui/theme';
44
import { Margin, Dimension } from '@superset-ui/dimension';
55
import { WithLegend } from '@superset-ui/chart-composition';
6-
import Encoder, { Encoding, ChannelOutput } from './Encoder';
7-
import { Dataset, PlainObject } from '../encodeable/types/Data';
8-
import { PartialSpec } from '../encodeable/types/Specification';
6+
import { isFieldDef } from 'encodable/lib/typeGuards/ChannelDef';
7+
import { Dataset, PlainObject } from 'encodable/lib/types/Data';
8+
import {
9+
scatterPlotEncoderFactory,
10+
ScatterPlotEncoder,
11+
ScatterPlotChannelOutputs,
12+
ScatterPlotEncodingConfig,
13+
ScatterPlotEncoding,
14+
} from './Encoder';
915
import createMarginSelector, { DEFAULT_MARGIN } from '../utils2/createMarginSelector';
1016
import DefaultTooltipRenderer from './DefaultTooltipRenderer';
1117
import convertScaleToDataUIScale from '../utils/convertScaleToDataUIScaleShape';
12-
import { isScaleFieldDef } from '../encodeable/types/ChannelDef';
13-
import createXYChartLayoutWithTheme from '../utils/createXYChartLayoutWithTheme';
14-
import createEncoderSelector from '../encodeable/createEncoderSelector';
15-
import createRenderLegend from '../components/legend/createRenderLegend';
16-
import { LegendHooks } from '../components/legend/types';
18+
import createXYChartLayoutWithTheme from '../utils2/createXYChartLayoutWithTheme';
19+
import createRenderLegend from '../components/legend2/createRenderLegend';
20+
import { LegendHooks } from '../components/legend2/types';
1721

1822
export interface TooltipProps {
1923
datum: EncodedPoint;
20-
encoder: Encoder;
24+
encoder: ScatterPlotEncoder;
2125
}
2226

2327
const defaultProps = {
2428
className: '',
2529
margin: DEFAULT_MARGIN,
30+
encoding: {},
2631
theme: chartTheme,
2732
TooltipRenderer: DefaultTooltipRenderer,
2833
} as const;
2934

3035
export type HookProps = {
3136
TooltipRenderer?: React.ComponentType<TooltipProps>;
32-
} & LegendHooks<Encoder>;
37+
} & LegendHooks<ScatterPlotEncodingConfig>;
3338

3439
type Props = {
3540
className?: string;
3641
width: string | number;
3742
height: string | number;
3843
margin?: Margin;
3944
data: Dataset;
45+
encoding?: Partial<ScatterPlotEncoding>;
4046
theme?: ChartTheme;
41-
} & PartialSpec<Encoding> &
42-
HookProps &
47+
} & HookProps &
4348
Readonly<typeof defaultProps>;
4449

45-
export interface EncodedPoint {
46-
x: ChannelOutput<'x'>;
47-
y: ChannelOutput<'y'>;
48-
size: ChannelOutput<'size'>;
49-
fill: ChannelOutput<'fill'>;
50-
stroke: ChannelOutput<'stroke'>;
51-
group: ChannelOutput<'group'>[];
52-
tooltip: ChannelOutput<'tooltip'>[];
50+
export type EncodedPoint = ScatterPlotChannelOutputs & {
5351
data: PlainObject;
54-
}
52+
};
5553

5654
export default class ScatterPlot extends PureComponent<Props> {
57-
private createEncoder = createEncoderSelector(Encoder);
55+
private createEncoder = scatterPlotEncoderFactory.createSelector();
5856

5957
private createMargin = createMarginSelector();
6058

@@ -68,34 +66,18 @@ export default class ScatterPlot extends PureComponent<Props> {
6866

6967
renderChart(dim: Dimension) {
7068
const { width, height } = dim;
71-
const { data, margin, theme, TooltipRenderer } = this.props;
72-
const encoder = this.createEncoder(this.props);
69+
const { data, margin, theme, TooltipRenderer, encoding } = this.props;
70+
const encoder = this.createEncoder(encoding);
7371
const { channels } = encoder;
7472

75-
if (typeof channels.x.scale !== 'undefined') {
76-
const xDomain = channels.x.getDomain(data);
77-
channels.x.scale.setDomain(xDomain);
78-
}
79-
if (typeof channels.y.scale !== 'undefined') {
80-
const yDomain = channels.y.getDomain(data);
81-
channels.y.scale.setDomain(yDomain);
82-
}
83-
if (
84-
isScaleFieldDef(channels.size.definition) &&
85-
channels.size.definition.type === 'quantitative'
86-
) {
87-
const domain = channels.size.getDomain(data) as number[];
88-
const [min, max] = domain;
89-
const adjustedDomain = [Math.min(min || 0, 0), Math.max(max || 1, 1)];
90-
channels.size.scale!.setDomain(adjustedDomain);
91-
}
73+
encoder.setDomainFromDataset(data);
9274

9375
const encodedData = data.map(d => ({
94-
x: channels.x.get(d),
95-
y: channels.y.get(d),
96-
size: channels.size.encode(d),
97-
fill: channels.fill.encode(d),
98-
stroke: channels.stroke.encode(d),
76+
x: channels.x.encodeDatum(d),
77+
y: channels.y.encodeDatum(d),
78+
size: channels.size.encodeDatum(d),
79+
fill: channels.fill.encodeDatum(d),
80+
stroke: channels.stroke.encodeDatum(d),
9981
data: d,
10082
}));
10183

@@ -119,13 +101,13 @@ export default class ScatterPlot extends PureComponent<Props> {
119101
<TooltipRenderer datum={datum} encoder={encoder} />
120102
)}
121103
theme={theme}
122-
xScale={convertScaleToDataUIScale(channels.x.scale!.config)}
123-
yScale={convertScaleToDataUIScale(channels.y.scale!.config)}
104+
xScale={convertScaleToDataUIScale(channels.x.definition.scale as any)}
105+
yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)}
124106
>
125107
{layout.renderXAxis()}
126108
{layout.renderYAxis()}
127109
<PointSeries
128-
key={channels.x.definition.field}
110+
key={isFieldDef(channels.x.definition) ? channels.x.definition.field : ''}
129111
data={encodedData}
130112
fill={(d: EncodedPoint) => d.fill}
131113
fillOpacity={0.5}
@@ -137,9 +119,9 @@ export default class ScatterPlot extends PureComponent<Props> {
137119
}
138120

139121
render() {
140-
const { className, data, width, height } = this.props;
122+
const { className, data, width, height, encoding } = this.props;
141123

142-
const encoder = this.createEncoder(this.props);
124+
const encoder = this.createEncoder(encoding);
143125

144126
return (
145127
<WithLegend

0 commit comments

Comments
 (0)