diff --git a/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx b/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx index e3edda687..76f7b18fe 100644 --- a/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx +++ b/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx @@ -4,7 +4,6 @@ import { BoxPlotSeries, XYChart } 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 { createSelector } from 'reselect'; import DefaultTooltipRenderer from './DefaultTooltipRenderer'; import ChartLegend from '../components/legend/ChartLegend'; import Encoder, { Encoding } from './Encoder'; @@ -14,6 +13,7 @@ import createMarginSelector, { DEFAULT_MARGIN } from '../utils/selectors/createM import { BoxPlotDataRow } from './types'; import convertScaleToDataUIScale from '../utils/convertScaleToDataUIScaleShape'; import createXYChartLayoutWithTheme from '../utils/createXYChartLayoutWithTheme'; +import createEncoderSelector from '../encodeable/createEncoderSelector'; export interface TooltipProps { datum: BoxPlotDataRow; @@ -46,32 +46,21 @@ type Props = { export default class BoxPlot extends React.PureComponent { static defaultProps = defaultProps; - encoder: Encoder; - private createEncoder: () => void; + private createEncoder = createEncoderSelector(Encoder); private createMargin = createMarginSelector(); constructor(props: Props) { super(props); - const createEncoder = createSelector( - (p: PartialSpec) => p.encoding, - p => p.options, - (encoding, options) => new Encoder({ encoding, options }), - ); - - this.createEncoder = () => { - this.encoder = createEncoder(this.props); - }; - - this.encoder = createEncoder(this.props); this.renderChart = this.renderChart.bind(this); } renderChart(dim: Dimension) { const { width, height } = dim; const { data, margin, theme, TooltipRenderer } = this.props; - const { channels } = this.encoder; + const encoder = this.createEncoder(this.props); + const { channels } = encoder; const isHorizontal = channels.y.definition.type === 'nominal'; @@ -100,7 +89,7 @@ export default class BoxPlot extends React.PureComponent { ariaLabel="BoxPlot" margin={layout.margin} renderTooltip={({ datum, color }: { datum: BoxPlotDataRow; color: string }) => ( - + )} showYGrid theme={theme} @@ -131,10 +120,10 @@ export default class BoxPlot extends React.PureComponent { render() { const { className, data, width, height } = this.props; - this.createEncoder(); - const renderLegend = this.encoder.hasLegend() + const encoder = this.createEncoder(this.props); + const renderLegend = encoder.hasLegend() ? // eslint-disable-next-line react/jsx-props-no-multi-spaces - () => data={data} encoder={this.encoder} /> + () => data={data} encoder={encoder} /> : undefined; return ( diff --git a/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx b/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx index c2ebb3947..d7ecf68fc 100644 --- a/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx +++ b/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx @@ -25,6 +25,7 @@ import DefaultTooltipRenderer from './DefaultTooltipRenderer'; import createMarginSelector, { DEFAULT_MARGIN } from '../utils/selectors/createMarginSelector'; import convertScaleToDataUIScale from '../utils/convertScaleToDataUIScaleShape'; import createXYChartLayoutWithTheme from '../utils/createXYChartLayoutWithTheme'; +import createEncoderSelector from '../encodeable/createEncoderSelector'; export interface TooltipProps { encoder: Encoder; @@ -87,11 +88,7 @@ const CIRCLE_STYLE = { strokeWidth: 1.5 }; export default class LineChart extends PureComponent { static defaultProps = defaultProps; - private createEncoder = createSelector( - (p: Props) => p.encoding, - p => p.options, - (encoding, options) => new Encoder({ encoding, options }), - ); + private createEncoder = createEncoderSelector(Encoder); private createAllSeries = createSelector( (input: { encoder: Encoder; data: Dataset }) => input.encoder, 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 b25e87ec4..08b8d6042 100644 --- a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx @@ -4,7 +4,6 @@ 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 { createSelector } from 'reselect'; import Encoder, { Encoding, ChannelOutput } from './Encoder'; import { Dataset, PlainObject } from '../encodeable/types/Data'; import ChartLegend from '../components/legend/ChartLegend'; @@ -14,6 +13,7 @@ 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'; export interface TooltipProps { datum: EncodedPoint; @@ -56,32 +56,21 @@ export interface EncodedPoint { export default class ScatterPlot extends PureComponent { static defaultProps = defaultProps; - encoder: Encoder; - private createEncoder: () => void; + private createEncoder = createEncoderSelector(Encoder); private createMargin = createMarginSelector(); constructor(props: Props) { super(props); - const createEncoder = createSelector( - (p: PartialSpec) => p.encoding, - p => p.options, - (encoding, options) => new Encoder({ encoding, options }), - ); - - this.createEncoder = () => { - this.encoder = createEncoder(this.props); - }; - - this.encoder = createEncoder(this.props); this.renderChart = this.renderChart.bind(this); } renderChart(dim: Dimension) { const { width, height } = dim; const { data, margin, theme, TooltipRenderer } = this.props; - const { channels } = this.encoder; + const encoder = this.createEncoder(this.props); + const { channels } = encoder; if (typeof channels.x.scale !== 'undefined') { const xDomain = channels.x.getDomain(data); @@ -126,7 +115,7 @@ export default class ScatterPlot extends PureComponent { ariaLabel="BoxPlot" margin={layout.margin} renderTooltip={({ datum }: { datum: EncodedPoint }) => ( - + )} showYGrid theme={theme} @@ -150,10 +139,10 @@ export default class ScatterPlot extends PureComponent { render() { const { className, data, width, height } = this.props; - this.createEncoder(); - const renderLegend = this.encoder.hasLegend() + const encoder = this.createEncoder(this.props); + const renderLegend = encoder.hasLegend() ? // eslint-disable-next-line react/jsx-props-no-multi-spaces - () => data={data} encoder={this.encoder} /> + () => data={data} encoder={encoder} /> : undefined; return ( diff --git a/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts b/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts index 8b4ae761d..522082696 100644 --- a/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts +++ b/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts @@ -3,9 +3,9 @@ import { Value } from 'vega-lite/build/src/channeldef'; import { ChannelType, ChannelInput, AllChannelOptions } from './types/Channel'; import { FullSpec, BaseOptions, PartialSpec } from './types/Specification'; import { isFieldDef, isTypedFieldDef, ChannelDef } from './types/ChannelDef'; -import ChannelEncoder from './ChannelEncoder'; import { Dataset } from './types/Data'; import { Unarray, MayBeArray, isArray, isNotArray } from './types/Base'; +import ChannelEncoder from './ChannelEncoder'; type AllChannelEncoders>> = { readonly [k in keyof Encoding]: Encoding[k] extends any[] diff --git a/packages/superset-ui-preset-chart-xy/src/encodeable/createEncoderSelector.ts b/packages/superset-ui-preset-chart-xy/src/encodeable/createEncoderSelector.ts new file mode 100644 index 000000000..6685a715b --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/encodeable/createEncoderSelector.ts @@ -0,0 +1,19 @@ +import { createSelector } from 'reselect'; +import { PartialSpec, BaseOptions } from './types/Specification'; +import AbstractEncoder from './AbstractEncoder'; +import { ChannelType } from './types/Channel'; +import { ChannelDef } from './types/ChannelDef'; +import { MayBeArray } from './types/Base'; + +export default function createEncoderSelector< + EncoderConstructor extends AbstractEncoder, + ChannelTypes extends Record, + Encoding extends Record>, + Options extends BaseOptions = BaseOptions +>(EncoderConstructor: new (spec: PartialSpec) => EncoderConstructor) { + return createSelector( + (spec: PartialSpec) => spec.encoding, + spec => spec.options, + (encoding, options) => new EncoderConstructor({ encoding, options }), + ); +}