Skip to content

Commit

Permalink
feat: create reusable selector factory for Encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
kristw authored and zhaoyongjie committed Nov 26, 2021
1 parent 52d120a commit 852b9e6
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -46,32 +46,21 @@ type Props = {
export default class BoxPlot extends React.PureComponent<Props> {
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<Encoding>) => 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';

Expand Down Expand Up @@ -100,7 +89,7 @@ export default class BoxPlot extends React.PureComponent<Props> {
ariaLabel="BoxPlot"
margin={layout.margin}
renderTooltip={({ datum, color }: { datum: BoxPlotDataRow; color: string }) => (
<TooltipRenderer datum={datum} color={color} encoder={this.encoder} />
<TooltipRenderer datum={datum} color={color} encoder={encoder} />
)}
showYGrid
theme={theme}
Expand Down Expand Up @@ -131,10 +120,10 @@ export default class BoxPlot extends React.PureComponent<Props> {
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
() => <ChartLegend<Encoder> data={data} encoder={this.encoder} />
() => <ChartLegend<Encoder> data={data} encoder={encoder} />
: undefined;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,11 +88,7 @@ const CIRCLE_STYLE = { strokeWidth: 1.5 };
export default class LineChart extends PureComponent<Props> {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -56,32 +56,21 @@ export interface EncodedPoint {
export default class ScatterPlot extends PureComponent<Props> {
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<Encoding>) => 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);
Expand Down Expand Up @@ -126,7 +115,7 @@ export default class ScatterPlot extends PureComponent<Props> {
ariaLabel="BoxPlot"
margin={layout.margin}
renderTooltip={({ datum }: { datum: EncodedPoint }) => (
<TooltipRenderer datum={datum} encoder={this.encoder} />
<TooltipRenderer datum={datum} encoder={encoder} />
)}
showYGrid
theme={theme}
Expand All @@ -150,10 +139,10 @@ export default class ScatterPlot extends PureComponent<Props> {
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
() => <ChartLegend<Encoder> data={data} encoder={this.encoder} />
() => <ChartLegend<Encoder> data={data} encoder={encoder} />
: undefined;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Encoding extends Record<string, MayBeArray<ChannelDef>>> = {
readonly [k in keyof Encoding]: Encoding[k] extends any[]
Expand Down
Original file line number Diff line number Diff line change
@@ -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, Encoding, Options>,
ChannelTypes extends Record<string, ChannelType>,
Encoding extends Record<keyof ChannelTypes, MayBeArray<ChannelDef>>,
Options extends BaseOptions = BaseOptions
>(EncoderConstructor: new (spec: PartialSpec<Encoding, Options>) => EncoderConstructor) {
return createSelector(
(spec: PartialSpec<Encoding, Options>) => spec.encoding,
spec => spec.options,
(encoding, options) => new EncoderConstructor({ encoding, options }),
);
}

0 comments on commit 852b9e6

Please sign in to comment.