From bba44f9944fef3cec0dcaa02f523017ec96e6c01 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 25 Apr 2019 21:50:05 -0700 Subject: [PATCH] feat: integrate line chart with build query and update typings (#73) * feat: integrate line chart with buildQuery * fix: typings * fix: minor * feat: derive groupbys * fix: tooltip for single series --- .../preset-chart-xy/Line/stories/query.tsx | 31 +++++++++++++++++-- .../src/Line/Line.tsx | 31 ++++++++++++------- .../src/Line/buildQuery.ts | 6 +++- .../src/components/ChartLegend.tsx | 8 +++-- .../src/encodeable/ChannelEncoder.ts | 27 ++++++++++------ .../src/encodeable/types/Channel.ts | 2 ++ 6 files changed, 77 insertions(+), 28 deletions(-) diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/query.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/query.tsx index b7d3315884918..77d456cde9ec8 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/query.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/query.tsx @@ -26,15 +26,40 @@ export default [ formData: { viz_type: LINE_PLUGIN_TYPE, datasource: '3__table', - url_params: {}, granularity_sqla: 'ds', time_grain_sqla: 'P1D', time_range: '100 years ago : now', metrics: ['sum__num'], - adhoc_filters: [], - groupby: [], limit: 25, row_limit: 50000, + encoding: { + x: { + field: '__timestamp', + type: 'temporal', + format: '%Y', + scale: { + type: 'time', + }, + axis: { + title: 'Time', + }, + }, + y: { + field: 'sum__num', + type: 'quantitative', + scale: { + type: 'linear', + }, + axis: { + title: 'Number of Babies', + }, + }, + color: { + field: 'gender', + type: 'nominal', + legend: true, + }, + }, }, }, }, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx index 2b7f845fcd0ed..5df7de674e903 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx @@ -49,8 +49,8 @@ export interface Series { } export interface SeriesValue { - x: Outputs['x']; - y: Outputs['y']; + x: number | Date; + y: number; data: PlainObject; parent: Series; } @@ -90,21 +90,28 @@ class LineChart extends PureComponent { const allSeries = values(groups).map(seriesData => { const firstDatum = seriesData[0]; - + const key = fieldNames.map(f => firstDatum[f]).join(','); const series: Series = { - key: fieldNames.map(f => firstDatum[f]).join(','), - color: channels.color.encode(firstDatum), + key: key.length === 0 ? channels.y.getTitle() : key, + color: channels.color.encode(firstDatum, '#222'), fill: channels.fill.encode(firstDatum, false), - strokeDasharray: channels.strokeDasharray.encode(firstDatum), + strokeDasharray: channels.strokeDasharray.encode(firstDatum, ''), values: [], }; - series.values = seriesData.map(v => ({ - x: channels.x.get(v), - y: channels.y.get(v), - data: v, - parent: series, - })); + series.values = seriesData + .map(v => ({ + x: channels.x.get(v), + y: channels.y.get(v), + data: v, + parent: series, + })) + .sort((a: SeriesValue, b: SeriesValue) => { + const aTime = a.x instanceof Date ? a.x.getTime() : a.x; + const bTime = b.x instanceof Date ? b.x.getTime() : b.x; + + return aTime - bTime; + }); return series; }); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/buildQuery.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/buildQuery.ts index cef6f32490cdb..d7bc98d22d89c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/buildQuery.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/buildQuery.ts @@ -1,12 +1,16 @@ import { buildQueryContext } from '@superset-ui/chart'; import ChartFormData from './ChartFormData'; +import Encoder from './Encoder'; export default function buildQuery(formData: ChartFormData) { const queryContext = buildQueryContext(formData); + const { encoding } = formData; + const encoder = new Encoder({ encoding }); - // Enforce time-series mode queryContext.queries.forEach(query => { const q = query; + q.groupby = encoder.getGroupBys(); + // Enforce time-series mode q.is_timeseries = true; }); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx index 01f4d4326624c..7419efddf847f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx @@ -5,7 +5,11 @@ import { Value } from 'vega-lite/build/src/channeldef'; import AbstractEncoder from '../encodeable/AbstractEncoder'; import { Dataset } from '../encodeable/types/Data'; import { ObjectWithKeysFromAndValueType } from '../encodeable/types/Base'; -import { ChannelType, EncodingFromChannelsAndOutputs } from '../encodeable/types/Channel'; +import { + ChannelType, + EncodingFromChannelsAndOutputs, + ChannelInput, +} from '../encodeable/types/Channel'; import { BaseOptions } from '../encodeable/types/Specification'; type Props = { @@ -50,7 +54,7 @@ export default class ChartLegend< const domain = Array.from(new Set(data.map(channelEncoder.get))); const scale = scaleOrdinal({ domain, - range: domain.map((key: string) => channelEncoder.encodeValue(key)), + range: domain.map((key: ChannelInput) => channelEncoder.encodeValue(key)), }); return ( diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts index 43b2be13e973c..0d56918a92fcf 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts @@ -2,7 +2,7 @@ import { Value } from 'vega-lite/build/src/channeldef'; import { extractFormatFromChannelDef } from './parsers/extractFormat'; import extractScale, { ScaleAgent } from './parsers/extractScale'; import extractGetter from './parsers/extractGetter'; -import { ChannelOptions, ChannelType } from './types/Channel'; +import { ChannelOptions, ChannelType, ChannelInput } from './types/Channel'; import { PlainObject } from './types/Data'; import { ChannelDef, @@ -24,9 +24,9 @@ export default class ChannelEncoder, Output exten readonly definition: Def; readonly options: ChannelOptions; - protected readonly getValue: (datum: PlainObject) => Value | undefined; - readonly encodeValue: (value: any) => Output | null | undefined; - readonly formatValue: (value: any) => string; + protected readonly getValue: (datum: PlainObject) => ChannelInput; + readonly encodeValue: (value: ChannelInput) => Output | null | undefined; + readonly formatValue: (value: ChannelInput | { toString(): string }) => string; readonly scale?: ScaleAgent; readonly axis?: AxisAgent; @@ -66,10 +66,14 @@ export default class ChannelEncoder, Output exten this.get = this.get.bind(this); } + encode(datum: PlainObject): Output | null | undefined; + // eslint-disable-next-line no-dupe-class-members + encode(datum: PlainObject, otherwise: Output): Output; + // eslint-disable-next-line no-dupe-class-members encode(datum: PlainObject, otherwise?: Output) { const value = this.get(datum); if (value === null || value === undefined) { - return value; + return otherwise; } const output = this.encodeValue(value); @@ -83,10 +87,12 @@ export default class ChannelEncoder, Output exten return this.formatValue(this.get(datum)); } - get(datum: PlainObject, otherwise?: any) { + get(datum: PlainObject, otherwise?: T) { const value = this.getValue(datum); - return otherwise !== undefined && (value === null || value === undefined) ? otherwise : value; + return otherwise !== undefined && (value === null || value === undefined) + ? otherwise + : (value as T); } getTitle() { @@ -110,12 +116,13 @@ export default class ChannelEncoder, Output exten isGroupBy() { if (isTypedFieldDef(this.definition)) { + const { type: dataType } = this.definition; + return ( - this.type === 'XBand' || - this.type === 'YBand' || this.type === 'Category' || this.type === 'Text' || - (this.type === 'Color' && this.definition.type === 'nominal') + (this.type === 'Color' && (dataType === 'nominal' || dataType === 'ordinal')) || + (this.isXY() && (dataType === 'nominal' || dataType === 'ordinal')) ); } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts index 1c5d79d3f0b9f..d45f4ed094e33 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts @@ -2,6 +2,8 @@ import { Value } from 'vega-lite/build/src/channeldef'; import { XFieldDef, YFieldDef, ChannelDef, MarkPropChannelDef, TextChannelDef } from './ChannelDef'; import { ObjectWithKeysFromAndValueType } from './Base'; +export type ChannelInput = number | string | boolean | null | Date | undefined; + // eslint-disable-next-line import/prefer-default-export export interface ChannelOptions { namespace?: string;