diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.ts similarity index 100% rename from packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js rename to packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.ts diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/DefaultTooltipRenderer.tsx b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/DefaultTooltipRenderer.tsx index fa3bf48d0..a94adf316 100644 --- a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/DefaultTooltipRenderer.tsx +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/DefaultTooltipRenderer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { TooltipFrame, TooltipTable } from '@superset-ui/chart-composition'; -import { isFieldDef } from '../encodeable/types/ChannelDef'; +import { isFieldDef } from 'encodable/lib/typeGuards/ChannelDef'; import { TooltipProps } from './ScatterPlot'; export default function DefaultTooltipRenderer({ datum, encoder }: TooltipProps) { @@ -8,43 +8,43 @@ export default function DefaultTooltipRenderer({ datum, encoder }: TooltipProps) 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) }, + { key: 'x', keyColumn: x.getTitle(), valueColumn: x.formatDatum(datum.data) }, + { key: 'y', keyColumn: y.getTitle(), valueColumn: y.formatDatum(datum.data) }, ]; if (isFieldDef(fill.definition)) { tooltipRows.push({ key: 'fill', keyColumn: fill.getTitle(), - valueColumn: fill.format(datum.data), + valueColumn: fill.formatDatum(datum.data), }); } if (isFieldDef(stroke.definition)) { tooltipRows.push({ key: 'stroke', keyColumn: stroke.getTitle(), - valueColumn: stroke.format(datum.data), + valueColumn: stroke.formatDatum(datum.data), }); } if (isFieldDef(size.definition)) { tooltipRows.push({ key: 'size', keyColumn: size.getTitle(), - valueColumn: size.format(datum.data), + valueColumn: size.formatDatum(datum.data), }); } channels.group.forEach(g => { tooltipRows.push({ key: `${g.name}`, keyColumn: g.getTitle(), - valueColumn: g.format(datum.data), + valueColumn: g.formatDatum(datum.data), }); }); channels.tooltip.forEach(g => { tooltipRows.push({ key: `${g.name}`, keyColumn: g.getTitle(), - valueColumn: g.format(datum.data), + valueColumn: g.formatDatum(datum.data), }); }); diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts index d16d07984..aa4fe0e5c 100644 --- a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts @@ -1,63 +1,39 @@ -import { Value } from 'vega-lite/build/src/channeldef'; -import { ChannelTypeToDefMap } from '../encodeable/types/Channel'; -import { ExtractChannelOutput } from '../encodeable/types/ChannelDef'; -import createEncoderClass from '../encodeable/createEncoderClass'; +import { createEncoderFactory, Encoder } from 'encodable'; +import { DeriveEncoding, DeriveChannelOutputs } from 'encodable/lib/types/Encoding'; -/** - * Define channel types - */ -const channelTypes = { - fill: 'Color', - group: 'Text', - size: 'Numeric', - stroke: 'Color', - tooltip: 'Text', - x: 'X', - y: 'Y', -} as const; // "as const" is mandatory - -export type ChannelTypes = typeof channelTypes; - -/** - * TEMPLATE: - * Helper for defining encoding - */ -type CreateChannelDef< - ChannelName extends keyof ChannelTypes, - Output extends Value -> = ChannelTypeToDefMap[ChannelTypes[ChannelName]]; - -/** - * Encoding definition - */ -export type Encoding = { - fill: CreateChannelDef<'fill', string>; - group: CreateChannelDef<'group', string>[]; - size: CreateChannelDef<'size', number>; - stroke: CreateChannelDef<'stroke', string>; - tooltip: CreateChannelDef<'tooltip', string>[]; - x: CreateChannelDef<'x', number>; - y: CreateChannelDef<'y', number>; +export type ScatterPlotEncodingConfig = { + x: ['X', number]; + y: ['Y', number]; + fill: ['Color', string]; + group: ['Category', string, 'multiple']; + size: ['Numeric', number]; + stroke: ['Color', string]; + tooltip: ['Text', string, 'multiple']; }; -/** - * TEMPLATE: - * Can use this to get returned type of a Channel - * example usage: ChannelOutput<'x'> - */ -export type ChannelOutput = ExtractChannelOutput< - Encoding[ChannelName] ->; - -export default class Encoder extends createEncoderClass({ - channelTypes, +export const scatterPlotEncoderFactory = createEncoderFactory({ + channelTypes: { + x: 'X', + y: 'Y', + fill: 'Color', + group: 'Category', + size: 'Numeric', + stroke: 'Color', + tooltip: 'Text', + }, defaultEncoding: { + x: { field: 'x', type: 'quantitative' }, + y: { field: 'y', type: 'quantitative' }, fill: { value: '#222' }, group: [], size: { value: 5 }, stroke: { value: 'none' }, tooltip: [], - x: { field: 'x', type: 'quantitative' }, - y: { field: 'y', type: 'quantitative' }, }, -}) {} +}); + +export type ScatterPlotEncoding = DeriveEncoding; + +export type ScatterPlotEncoder = Encoder; + +export type ScatterPlotChannelOutputs = DeriveChannelOutputs; diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx index b652b22a5..c857e0351 100644 --- a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx @@ -3,33 +3,38 @@ import { XYChart, PointSeries } from '@data-ui/xy-chart'; import { chartTheme, ChartTheme } from '@data-ui/theme'; import { Margin, Dimension } from '@superset-ui/dimension'; import { WithLegend } from '@superset-ui/chart-composition'; -import Encoder, { Encoding, ChannelOutput } from './Encoder'; -import { Dataset, PlainObject } from '../encodeable/types/Data'; -import { PartialSpec } from '../encodeable/types/Specification'; +import { isFieldDef } from 'encodable/lib/typeGuards/ChannelDef'; +import { Dataset, PlainObject } from 'encodable/lib/types/Data'; +import { + scatterPlotEncoderFactory, + ScatterPlotEncoder, + ScatterPlotChannelOutputs, + ScatterPlotEncodingConfig, + ScatterPlotEncoding, +} from './Encoder'; import createMarginSelector, { DEFAULT_MARGIN } from '../utils2/createMarginSelector'; import DefaultTooltipRenderer from './DefaultTooltipRenderer'; import convertScaleToDataUIScale from '../utils/convertScaleToDataUIScaleShape'; -import { isScaleFieldDef } from '../encodeable/types/ChannelDef'; -import createXYChartLayoutWithTheme from '../utils/createXYChartLayoutWithTheme'; -import createEncoderSelector from '../encodeable/createEncoderSelector'; -import createRenderLegend from '../components/legend/createRenderLegend'; -import { LegendHooks } from '../components/legend/types'; +import createXYChartLayoutWithTheme from '../utils2/createXYChartLayoutWithTheme'; +import createRenderLegend from '../components/legend2/createRenderLegend'; +import { LegendHooks } from '../components/legend2/types'; export interface TooltipProps { datum: EncodedPoint; - encoder: Encoder; + encoder: ScatterPlotEncoder; } const defaultProps = { className: '', margin: DEFAULT_MARGIN, + encoding: {}, theme: chartTheme, TooltipRenderer: DefaultTooltipRenderer, } as const; export type HookProps = { TooltipRenderer?: React.ComponentType; -} & LegendHooks; +} & LegendHooks; type Props = { className?: string; @@ -37,24 +42,17 @@ type Props = { height: string | number; margin?: Margin; data: Dataset; + encoding?: Partial; theme?: ChartTheme; -} & PartialSpec & - HookProps & +} & HookProps & Readonly; -export interface EncodedPoint { - x: ChannelOutput<'x'>; - y: ChannelOutput<'y'>; - size: ChannelOutput<'size'>; - fill: ChannelOutput<'fill'>; - stroke: ChannelOutput<'stroke'>; - group: ChannelOutput<'group'>[]; - tooltip: ChannelOutput<'tooltip'>[]; +export type EncodedPoint = ScatterPlotChannelOutputs & { data: PlainObject; -} +}; export default class ScatterPlot extends PureComponent { - private createEncoder = createEncoderSelector(Encoder); + private createEncoder = scatterPlotEncoderFactory.createSelector(); private createMargin = createMarginSelector(); @@ -68,34 +66,18 @@ export default class ScatterPlot extends PureComponent { renderChart(dim: Dimension) { const { width, height } = dim; - const { data, margin, theme, TooltipRenderer } = this.props; - const encoder = this.createEncoder(this.props); + const { data, margin, theme, TooltipRenderer, encoding } = this.props; + const encoder = this.createEncoder(encoding); const { channels } = encoder; - if (typeof channels.x.scale !== 'undefined') { - const xDomain = channels.x.getDomain(data); - channels.x.scale.setDomain(xDomain); - } - if (typeof channels.y.scale !== 'undefined') { - const yDomain = channels.y.getDomain(data); - channels.y.scale.setDomain(yDomain); - } - if ( - isScaleFieldDef(channels.size.definition) && - channels.size.definition.type === 'quantitative' - ) { - const domain = channels.size.getDomain(data) as number[]; - const [min, max] = domain; - const adjustedDomain = [Math.min(min || 0, 0), Math.max(max || 1, 1)]; - channels.size.scale!.setDomain(adjustedDomain); - } + encoder.setDomainFromDataset(data); const encodedData = data.map(d => ({ - x: channels.x.get(d), - y: channels.y.get(d), - size: channels.size.encode(d), - fill: channels.fill.encode(d), - stroke: channels.stroke.encode(d), + x: channels.x.encodeDatum(d), + y: channels.y.encodeDatum(d), + size: channels.size.encodeDatum(d), + fill: channels.fill.encodeDatum(d), + stroke: channels.stroke.encodeDatum(d), data: d, })); @@ -119,13 +101,13 @@ export default class ScatterPlot extends PureComponent { )} theme={theme} - xScale={convertScaleToDataUIScale(channels.x.scale!.config)} - yScale={convertScaleToDataUIScale(channels.y.scale!.config)} + xScale={convertScaleToDataUIScale(channels.x.definition.scale as any)} + yScale={convertScaleToDataUIScale(channels.y.definition.scale as any)} > {layout.renderXAxis()} {layout.renderYAxis()} d.fill} fillOpacity={0.5} @@ -137,9 +119,9 @@ export default class ScatterPlot extends PureComponent { } render() { - const { className, data, width, height } = this.props; + const { className, data, width, height, encoding } = this.props; - const encoder = this.createEncoder(this.props); + const encoder = this.createEncoder(encoding); return ( ({ fill={ // @ts-ignore (item.output.color ?? - // @ts-ignore - item.output.stroke ?? // @ts-ignore item.output.fill ?? + // @ts-ignore + item.output.stroke ?? '#ccc') as string } stroke={