You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2022/01/21 19:24:40 UTC

[superset] branch master updated: feat(plugin-chart-echarts): support non-timeseries x-axis (#17917)

This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new e9651ea  feat(plugin-chart-echarts): support non-timeseries x-axis (#17917)
e9651ea is described below

commit e9651ea52fdc0edb574bfb9dc1b22c225bcc068f
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Fri Jan 21 21:23:23 2022 +0200

    feat(plugin-chart-echarts): support non-timeseries x-axis (#17917)
    
    * feat(plugin-chart-echarts): support non-timeseries x-axis
    
    * fix tests
    
    * change formula return type from Date to number
    
    * add x_axis test coverage
    
    * rename func and improve coverage
    
    * add x-axis control to bar chart
    
    * remove redundant console.log
    
    * fix description
    
    * make x-axis control mandatory
    
    * 🙃
    
    * fix x-axis formatter
    
    * fix showValues
    
    * fix implicit rDTTM_ALIAS references in postProcessing
    
    * replace TIME_COLUMN with DTTM_ALIAS
    
    * fix remaining implicit indexes
    
    * fix: Disable filtering on wide result sets (#18021)
    
    * fix: handle null values in time-series table (#18039)
    
    * cleanup column_type_mappings (#17569)
    
    Signed-off-by: Đặng Minh Dũng <du...@live.com>
    
    * important change to MakeFile (#18037)
    
    * add missing is_timeseries to pivot op
    
    Co-authored-by: Erik Ritter <er...@airbnb.com>
    Co-authored-by: Grace Guo <gr...@airbnb.com>
    Co-authored-by: Đặng Minh Dũng <du...@live.com>
    Co-authored-by: AAfghahi <48...@users.noreply.github.com>
---
 .../src/operators/pivotOperator.ts                 |  8 ++-
 .../src/operators/prophetOperator.ts               |  3 +-
 .../src/operators/resampleOperator.ts              |  4 +-
 .../src/operators/rollingWindowOperator.ts         |  8 +--
 .../src/operators/sortOperator.ts                  |  9 +--
 .../src/operators/timeComparePivotOperator.ts      |  7 +-
 .../src/operators/utils/constants.ts               |  1 -
 .../src/operators/utils/index.ts                   |  2 +-
 .../test/utils/operators/pivotOperator.test.ts     | 26 ++++++++
 .../test/utils/operators/prophetOperator.test.ts   | 34 +++++++++-
 .../test/utils/operators/resampleOperator.test.ts  | 25 +++++++-
 .../test/utils/operators/sortOperator.test.ts      | 16 +++++
 .../utils/operators/timeCompareOperator.test.ts    | 26 ++++++++
 .../src/query/types/PostProcessing.ts              | 10 ++-
 .../superset-ui-core/src/utils/featureFlags.ts     |  1 +
 .../BigNumber/BigNumberWithTrendline/buildQuery.ts |  7 +-
 .../BigNumberWithTrendline/transformProps.ts       |  6 +-
 .../src/MixedTimeseries/transformProps.ts          |  6 +-
 .../src/Timeseries/Area/controlPanel.tsx           |  4 +-
 .../src/Timeseries/Area/index.ts                   | 26 ++++----
 .../src/Timeseries/Regular/Bar/controlPanel.tsx    |  4 +-
 .../src/Timeseries/Regular/Bar/index.ts            | 20 ++++--
 .../src/Timeseries/Regular/Line/index.ts           | 22 +++++--
 .../Timeseries/Regular/Scatter/controlPanel.tsx    |  4 +-
 .../src/Timeseries/Regular/Scatter/index.ts        | 22 +++++--
 .../src/Timeseries/Regular/SmoothLine/index.ts     | 22 +++++--
 .../src/Timeseries/Regular/controlPanel.tsx        |  8 ++-
 .../src/Timeseries/Step/controlPanel.tsx           |  4 +-
 .../src/Timeseries/Step/index.ts                   | 22 +++++--
 .../src/Timeseries/buildQuery.ts                   | 11 +++-
 .../src/Timeseries/controlPanel.tsx                |  4 +-
 .../plugin-chart-echarts/src/Timeseries/index.ts   | 32 +++++----
 .../src/Timeseries/transformProps.ts               | 47 +++++++++++---
 .../plugins/plugin-chart-echarts/src/controls.tsx  | 19 +++++-
 .../plugin-chart-echarts/src/utils/annotation.ts   |  4 +-
 .../plugin-chart-echarts/src/utils/prophet.ts      | 10 ++-
 .../plugin-chart-echarts/src/utils/series.ts       | 56 ++++++++--------
 .../test/Timeseries/transformProps.test.ts         | 29 +++++----
 .../test/utils/annotation.test.ts                  | 18 ++++--
 .../plugin-chart-echarts/test/utils/series.test.ts | 75 ++++++++++++++++------
 superset/common/query_context_processor.py         | 12 ++--
 superset/utils/pandas_postprocessing.py            | 12 ++--
 42 files changed, 487 insertions(+), 199 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/pivotOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/pivotOperator.ts
index 8789efb..e1e1fde 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/pivotOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/pivotOperator.ts
@@ -17,20 +17,22 @@
  * under the License.
  */
 import {
+  DTTM_ALIAS,
   ensureIsArray,
   getColumnLabel,
   getMetricLabel,
   PostProcessingPivot,
 } from '@superset-ui/core';
 import { PostProcessingFactory } from './types';
-import { TIME_COLUMN, isValidTimeCompare } from './utils';
+import { isValidTimeCompare } from './utils';
 import { timeComparePivotOperator } from './timeComparePivotOperator';
 
 export const pivotOperator: PostProcessingFactory<
   PostProcessingPivot | undefined
 > = (formData, queryObject) => {
   const metricLabels = ensureIsArray(queryObject.metrics).map(getMetricLabel);
-  if (queryObject.is_timeseries && metricLabels.length) {
+  const { x_axis: xAxis } = formData;
+  if ((xAxis || queryObject.is_timeseries) && metricLabels.length) {
     if (isValidTimeCompare(formData, queryObject)) {
       return timeComparePivotOperator(formData, queryObject);
     }
@@ -38,7 +40,7 @@ export const pivotOperator: PostProcessingFactory<
     return {
       operation: 'pivot',
       options: {
-        index: [TIME_COLUMN],
+        index: [xAxis || DTTM_ALIAS],
         columns: ensureIsArray(queryObject.columns).map(getColumnLabel),
         // Create 'dummy' mean aggregates to assign cell values in pivot table
         // use the 'mean' aggregates to avoid drop NaN. PR: https://github.com/apache-superset/superset-ui/pull/1231
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/prophetOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/prophetOperator.ts
index 1b20803..640cb8b 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/prophetOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/prophetOperator.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitationsxw
  * under the License.
  */
-import { PostProcessingProphet } from '@superset-ui/core';
+import { DTTM_ALIAS, PostProcessingProphet } from '@superset-ui/core';
 import { PostProcessingFactory } from './types';
 
 export const prophetOperator: PostProcessingFactory<
@@ -32,6 +32,7 @@ export const prophetOperator: PostProcessingFactory<
         yearly_seasonality: formData.forecastSeasonalityYearly,
         weekly_seasonality: formData.forecastSeasonalityWeekly,
         daily_seasonality: formData.forecastSeasonalityDaily,
+        index: formData.x_axis || DTTM_ALIAS,
       },
     };
   }
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/resampleOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/resampleOperator.ts
index 1c50e93..d639e19 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/resampleOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/resampleOperator.ts
@@ -18,12 +18,12 @@
  * under the License.
  */
 import {
+  DTTM_ALIAS,
   ensureIsArray,
   isPhysicalColumn,
   PostProcessingResample,
 } from '@superset-ui/core';
 import { PostProcessingFactory } from './types';
-import { TIME_COLUMN } from './utils';
 
 export const resampleOperator: PostProcessingFactory<
   PostProcessingResample | undefined
@@ -45,7 +45,7 @@ export const resampleOperator: PostProcessingFactory<
         method: resampleMethod,
         rule: resampleRule,
         fill_value: resampleZeroFill ? 0 : null,
-        time_column: TIME_COLUMN,
+        time_column: formData.x_axis || DTTM_ALIAS,
         groupby_columns,
       },
     };
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/rollingWindowOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/rollingWindowOperator.ts
index f8dd4013..d4c04ec 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/rollingWindowOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/rollingWindowOperator.ts
@@ -18,12 +18,12 @@
  * under the License.
  */
 import {
-  ensureIsInt,
+  ComparisionType,
   ensureIsArray,
-  RollingType,
-  PostProcessingRolling,
+  ensureIsInt,
   PostProcessingCum,
-  ComparisionType,
+  PostProcessingRolling,
+  RollingType,
 } from '@superset-ui/core';
 import {
   getMetricOffsetsMap,
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/sortOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/sortOperator.ts
index 9a86f4b..9443bb7 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/sortOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/sortOperator.ts
@@ -17,22 +17,23 @@
  * specific language governing permissions and limitationsxw
  * under the License.
  */
-import { PostProcessingSort, RollingType } from '@superset-ui/core';
+import { DTTM_ALIAS, PostProcessingSort, RollingType } from '@superset-ui/core';
 import { PostProcessingFactory } from './types';
-import { TIME_COLUMN } from './utils';
 
 export const sortOperator: PostProcessingFactory<
   PostProcessingSort | undefined
 > = (formData, queryObject) => {
+  const { x_axis: xAxis } = formData;
   if (
-    queryObject.is_timeseries &&
+    (xAxis || queryObject.is_timeseries) &&
     Object.values(RollingType).includes(formData.rolling_type)
   ) {
+    const index = xAxis || DTTM_ALIAS;
     return {
       operation: 'sort',
       options: {
         columns: {
-          [TIME_COLUMN]: true,
+          [index]: true,
         },
       },
     };
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/timeComparePivotOperator.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/timeComparePivotOperator.ts
index 4b5458e..9e16d29 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/timeComparePivotOperator.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/timeComparePivotOperator.ts
@@ -19,10 +19,11 @@
  */
 import {
   ComparisionType,
-  PostProcessingPivot,
-  NumpyFunction,
+  DTTM_ALIAS,
   ensureIsArray,
   getColumnLabel,
+  NumpyFunction,
+  PostProcessingPivot,
 } from '@superset-ui/core';
 import {
   getMetricOffsetsMap,
@@ -57,7 +58,7 @@ export const timeComparePivotOperator: PostProcessingFactory<
     return {
       operation: 'pivot',
       options: {
-        index: ['__timestamp'],
+        index: [formData.x_axis || DTTM_ALIAS],
         columns: ensureIsArray(queryObject.columns).map(getColumnLabel),
         aggregates:
           comparisonType === ComparisionType.Values ? valuesAgg : changeAgg,
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/constants.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/constants.ts
index cb01a2e..6f2f59b 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/constants.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/constants.ts
@@ -18,4 +18,3 @@
  * under the License.
  */
 export const TIME_COMPARISON_SEPARATOR = '__';
-export const TIME_COLUMN = '__timestamp';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/index.ts
index acf072f..d591dbd 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/operators/utils/index.ts
@@ -19,4 +19,4 @@
  */
 export { getMetricOffsetsMap } from './getMetricOffsetsMap';
 export { isValidTimeCompare } from './isValidTimeCompare';
-export { TIME_COMPARISON_SEPARATOR, TIME_COLUMN } from './constants';
+export { TIME_COMPARISON_SEPARATOR } from './constants';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/pivotOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/pivotOperator.test.ts
index 8f522cc..5054049 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/pivotOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/pivotOperator.test.ts
@@ -105,6 +105,32 @@ test('pivot by __timestamp with groupby', () => {
   });
 });
 
+test('pivot by x_axis with groupby', () => {
+  expect(
+    pivotOperator(
+      {
+        ...formData,
+        x_axis: 'baz',
+      },
+      {
+        ...queryObject,
+        columns: ['foo', 'bar'],
+      },
+    ),
+  ).toEqual({
+    operation: 'pivot',
+    options: {
+      index: ['baz'],
+      columns: ['foo', 'bar'],
+      aggregates: {
+        'count(*)': { operator: 'mean' },
+        'sum(val)': { operator: 'mean' },
+      },
+      drop_missing_columns: false,
+    },
+  });
+});
+
 test('timecompare in formdata', () => {
   expect(
     pivotOperator(
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/prophetOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/prophetOperator.test.ts
index d79868a..78d4bc9 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/prophetOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/prophetOperator.test.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { QueryObject, SqlaFormData } from '@superset-ui/core';
+import { DTTM_ALIAS, QueryObject, SqlaFormData } from '@superset-ui/core';
 import { prophetOperator } from '../../../src';
 
 const formData: SqlaFormData = {
@@ -42,7 +42,7 @@ test('should skip prophetOperator', () => {
   expect(prophetOperator(formData, queryObject)).toEqual(undefined);
 });
 
-test('should do prophetOperator', () => {
+test('should do prophetOperator with default index', () => {
   expect(
     prophetOperator(
       {
@@ -65,6 +65,36 @@ test('should do prophetOperator', () => {
       yearly_seasonality: true,
       weekly_seasonality: false,
       daily_seasonality: false,
+      index: DTTM_ALIAS,
+    },
+  });
+});
+
+test('should do prophetOperator over named column', () => {
+  expect(
+    prophetOperator(
+      {
+        ...formData,
+        x_axis: 'ds',
+        forecastEnabled: true,
+        forecastPeriods: '3',
+        forecastInterval: '5',
+        forecastSeasonalityYearly: true,
+        forecastSeasonalityWeekly: false,
+        forecastSeasonalityDaily: false,
+      },
+      queryObject,
+    ),
+  ).toEqual({
+    operation: 'prophet',
+    options: {
+      time_grain: 'P1Y',
+      periods: 3.0,
+      confidence_interval: 5.0,
+      yearly_seasonality: true,
+      weekly_seasonality: false,
+      daily_seasonality: false,
+      index: 'ds',
     },
   });
 });
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/resampleOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/resampleOperator.test.ts
index 18116a8..a562dbb 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/resampleOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/resampleOperator.test.ts
@@ -62,7 +62,7 @@ test('should skip resampleOperator', () => {
   ).toEqual(undefined);
 });
 
-test('should do resample', () => {
+test('should do resample on implicit time column', () => {
   expect(
     resampleOperator(
       { ...formData, resample_method: 'ffill', resample_rule: '1D' },
@@ -80,6 +80,29 @@ test('should do resample', () => {
   });
 });
 
+test('should do resample on x-axis', () => {
+  expect(
+    resampleOperator(
+      {
+        ...formData,
+        x_axis: 'ds',
+        resample_method: 'ffill',
+        resample_rule: '1D',
+      },
+      queryObject,
+    ),
+  ).toEqual({
+    operation: 'resample',
+    options: {
+      fill_value: null,
+      groupby_columns: [],
+      method: 'ffill',
+      rule: '1D',
+      time_column: 'ds',
+    },
+  });
+});
+
 test('should do zerofill resample', () => {
   expect(
     resampleOperator(
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/sortOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/sortOperator.test.ts
index e8dfe3d..54cf348 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/sortOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/sortOperator.test.ts
@@ -125,3 +125,19 @@ test('sort by __timestamp', () => {
     },
   });
 });
+
+test('sort by named x-axis', () => {
+  expect(
+    sortOperator(
+      { ...formData, x_axis: 'ds', rolling_type: 'cumsum' },
+      { ...queryObject },
+    ),
+  ).toEqual({
+    operation: 'sort',
+    options: {
+      columns: {
+        ds: true,
+      },
+    },
+  });
+});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/timeCompareOperator.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/timeCompareOperator.test.ts
index 0b51fa7..c2fcb75 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/timeCompareOperator.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/operators/timeCompareOperator.test.ts
@@ -174,3 +174,29 @@ test('time compare pivot: difference/percentage/ratio', () => {
     });
   });
 });
+
+test('time compare pivot on x-axis', () => {
+  expect(
+    timeComparePivotOperator(
+      {
+        ...formData,
+        comparison_type: 'values',
+        time_compare: ['1 year ago', '1 year later'],
+        x_axis: 'ds',
+      },
+      queryObject,
+    ),
+  ).toEqual({
+    operation: 'pivot',
+    options: {
+      aggregates: {
+        'count(*)': { operator: 'mean' },
+        'count(*)__1 year ago': { operator: 'mean' },
+        'count(*)__1 year later': { operator: 'mean' },
+      },
+      drop_missing_columns: false,
+      columns: [],
+      index: ['ds'],
+    },
+  });
+});
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
index 266551b..cf2baf5 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
@@ -94,10 +94,16 @@ export interface PostProcessingContribution {
 export interface PostProcessingPivot {
   operation: 'pivot';
   options: {
-    index: string[];
-    columns: string[];
     aggregates: Aggregates;
+    column_fill_value?: string;
+    columns: string[];
+    combine_value_with_metric?: boolean;
+    drop_missing_columns?: boolean;
     flatten_columns?: boolean;
+    index: string[];
+    marginal_distribution_name?: string;
+    marginal_distributions?: boolean;
+    metric_fill_value?: any;
     reset_index?: boolean;
   };
 }
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
index a09f9b4..a55dcfd 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
@@ -52,6 +52,7 @@ export enum FeatureFlag {
   ALERTS_ATTACH_REPORTS = 'ALERTS_ATTACH_REPORTS',
   ALLOW_FULL_CSV_EXPORT = 'ALLOW_FULL_CSV_EXPORT',
   UX_BETA = 'UX_BETA',
+  GENERIC_CHART_AXES = 'GENERIC_CHART_AXES',
 }
 export type ScheduleQueriesProps = {
   JSONSCHEMA: {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.ts
index 1a9096f..b4dcd2a 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.ts
@@ -22,10 +22,7 @@ import {
   PostProcessingResample,
   QueryFormData,
 } from '@superset-ui/core';
-import {
-  rollingWindowOperator,
-  TIME_COLUMN,
-} from '@superset-ui/chart-controls';
+import { rollingWindowOperator } from '@superset-ui/chart-controls';
 
 const TIME_GRAIN_MAP: Record<string, string> = {
   PT1S: 'S',
@@ -65,7 +62,7 @@ export default function buildQuery(formData: QueryFormData) {
             method: 'asfreq',
             rule,
             fill_value: null,
-            time_column: TIME_COLUMN,
+            time_column: DTTM_ALIAS,
           },
         };
       }
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
index 5054d99..892a81e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 import {
+  DTTM_ALIAS,
   extractTimegrain,
   getNumberFormatter,
   NumberFormats,
@@ -54,7 +55,6 @@ export function renderTooltipFactory(
   };
 }
 
-const TIME_COLUMN = '__timestamp';
 const formatPercentChange = getNumberFormatter(
   NumberFormats.PERCENT_SIGNED_1_POINT,
 );
@@ -97,7 +97,7 @@ export default function transformProps(
   let trendLineData;
   let percentChange = 0;
   let bigNumber = data.length === 0 ? null : data[0][metricName];
-  let timestamp = data.length === 0 ? null : data[0][TIME_COLUMN];
+  let timestamp = data.length === 0 ? null : data[0][DTTM_ALIAS];
   let bigNumberFallback;
 
   const metricColtypeIndex = colnames.findIndex(name => name === metricName);
@@ -106,7 +106,7 @@ export default function transformProps(
 
   if (data.length > 0) {
     const sortedData = (data as BigNumberDatum[])
-      .map(d => [d[TIME_COLUMN], parseMetricValue(d[metricName])])
+      .map(d => [d[DTTM_ALIAS], parseMetricValue(d[metricName])])
       // sort in time descending order
       .sort((a, b) => (a[0] !== null && b[0] !== null ? b[0] - a[0] : 0));
 
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
index 1f39a4a..886f693 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
@@ -40,7 +40,7 @@ import { parseYAxisBound } from '../utils/controls';
 import {
   currentSeries,
   dedupSeries,
-  extractTimeseriesSeries,
+  extractSeries,
   getLegendProps,
 } from '../utils/series';
 import { extractAnnotationLabels } from '../utils/annotation';
@@ -131,11 +131,11 @@ export default function transformProps(
 
   const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
   const rebasedDataA = rebaseTimeseriesDatum(data1, verboseMap);
-  const rawSeriesA = extractTimeseriesSeries(rebasedDataA, {
+  const rawSeriesA = extractSeries(rebasedDataA, {
     fillNeighborValue: stack ? 0 : undefined,
   });
   const rebasedDataB = rebaseTimeseriesDatum(data2, verboseMap);
-  const rawSeriesB = extractTimeseriesSeries(rebasedDataB, {
+  const rawSeriesB = extractSeries(rebasedDataB, {
     fillNeighborValue: stackB ? 0 : undefined,
   });
 
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
index 0dcc76fc..720eb94 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -36,6 +36,7 @@ import {
   legendSection,
   richTooltipSection,
   showValueSection,
+  xAxisControl,
 } from '../../controls';
 
 const {
@@ -59,6 +60,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         [
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/index.ts
index e0c0af2..ee7c899 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/index.ts
@@ -22,6 +22,8 @@ import {
   ChartPlugin,
   AnnotationType,
   Behavior,
+  isFeatureEnabled,
+  FeatureFlag,
 } from '@superset-ui/core';
 import buildQuery from '../buildQuery';
 import controlPanel from './controlPanel';
@@ -43,16 +45,6 @@ export default class EchartsAreaChartPlugin extends ChartPlugin<
   EchartsTimeseriesFormData,
   EchartsTimeseriesChartProps
 > {
-  /**
-   * The constructor is used to pass relevant metadata and callbacks that get
-   * registered in respective registries that are used throughout the library
-   * and application. A more thorough description of each property is given in
-   * the respective imported file.
-   *
-   * It is worth noting that `buildQuery` and is optional, and only needed for
-   * advanced visualizations that require either post processing operations
-   * (pivoting, rolling aggregations, sorting etc) or submitting multiple queries.
-   */
   constructor() {
     super({
       buildQuery,
@@ -62,9 +54,13 @@ export default class EchartsAreaChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series Area chart are similar to line chart in that they represent variables with the same scale, but area charts stack the metrics on top of each other. An area chart in Superset can be stream, stack, or expand.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Area charts are similar to line charts in that they represent variables with the same scale, but area charts stack the metrics on top of each other.',
+            )
+          : t(
+              'Time-series Area chart are similar to line chart in that they represent variables with the same scale, but area charts stack the metrics on top of each other. An area chart in Superset can be stream, stack, or expand.',
+            ),
         exampleGallery: [{ url: example1 }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -72,7 +68,9 @@ export default class EchartsAreaChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Area Chart'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Area Chart v2')
+          : t('Time-series Area Chart'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
index 9902336..96cb0b6 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -35,6 +35,7 @@ import {
   legendSection,
   richTooltipSection,
   showValueSection,
+  xAxisControl,
 } from '../../../controls';
 
 const {
@@ -56,6 +57,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         [
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/index.ts
index 0c23a19..0ffc090 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from '../../buildQuery';
 import controlPanel from './controlPanel';
@@ -58,9 +60,11 @@ export default class EchartsTimeseriesBarChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series Bar Charts are used to show the changes in a metric over time as a series of bars.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Bar Charts are used to show metrics as a series of bars.')
+          : t(
+              'Time-series Bar Charts are used to show the changes in a metric over time as a series of bars.',
+            ),
         exampleGallery: [
           { url: example1 },
           { url: example2 },
@@ -72,7 +76,9 @@ export default class EchartsTimeseriesBarChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Bar Chart v2'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Bar Chart v2')
+          : t('Time-series Bar Chart v2'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
index 0ee78da..6f4a780 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from '../../buildQuery';
 import controlPanel from '../controlPanel';
@@ -57,9 +59,13 @@ export default class EchartsTimeseriesLineChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series line chart is used to visualize repeated measurements taken over regular time intervals. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Line chart is used to visualize measurements taken over a given category. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
+            )
+          : t(
+              'Time-series line chart is used to visualize repeated measurements taken over regular time intervals. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
+            ),
         exampleGallery: [{ url: example1 }, { url: example2 }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -67,7 +73,9 @@ export default class EchartsTimeseriesLineChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Line Chart'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Line Chart')
+          : t('Time-series Line Chart'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
index a2a3446..21110c8 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -32,6 +32,7 @@ import {
   legendSection,
   richTooltipSection,
   showValueSection,
+  xAxisControl,
 } from '../../../controls';
 
 const {
@@ -52,6 +53,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         ['adhoc_filters'],
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/index.ts
index 98a7898..7c77868 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from '../../buildQuery';
 import controlPanel from './controlPanel';
@@ -56,9 +58,13 @@ export default class EchartsTimeseriesScatterChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series Scatter Plot has time on the horizontal axis in linear units, and the points are connected in order. It shows a statistical relationship between two variables.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Scatter Plot has the horizontal axis in linear units, and the points are connected in order. It shows a statistical relationship between two variables.',
+            )
+          : t(
+              'Time-series Scatter Plot has time on the horizontal axis in linear units, and the points are connected in order. It shows a statistical relationship between two variables.',
+            ),
         exampleGallery: [{ url: example1 }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -66,7 +72,9 @@ export default class EchartsTimeseriesScatterChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Scatter Plot'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Scatter Plot')
+          : t('Time-series Scatter Plot'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/index.ts
index db592e1..ee348b2 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from '../../buildQuery';
 import controlPanel from '../controlPanel';
@@ -56,9 +58,13 @@ export default class EchartsTimeseriesSmoothLineChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series Smooth-line is a variation of line chart. Without angles and hard edges, Smooth-line looks more smarter and more professional.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Smooth-line is a variation of the line chart. Without angles and hard edges, Smooth-line sometimes looks smarter and more professional.',
+            )
+          : t(
+              'Time-series Smooth-line is a variation of the line chart. Without angles and hard edges, Smooth-line sometimes looks smarter and more professional.',
+            ),
         exampleGallery: [{ url: example1 }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -66,7 +72,9 @@ export default class EchartsTimeseriesSmoothLineChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Smooth Line'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Smooth Line')
+          : t('Time-series Smooth Line'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/controlPanel.tsx
index 3e0dc46..14edc34 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -31,7 +31,8 @@ import { DEFAULT_FORM_DATA, EchartsTimeseriesContributionType } from '../types';
 import {
   legendSection,
   richTooltipSection,
-  showValueSection,
+  showValueSectionWithoutStack,
+  xAxisControl,
 } from '../../controls';
 
 const {
@@ -53,6 +54,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         [
@@ -100,7 +102,7 @@ const config: ControlPanelConfig = {
       expanded: true,
       controlSetRows: [
         ['color_scheme'],
-        ...showValueSection,
+        ...showValueSectionWithoutStack,
         [
           {
             name: 'markerEnabled',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
index 1deb723..8178e49 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -36,6 +36,7 @@ import {
   legendSection,
   richTooltipSection,
   showValueSection,
+  xAxisControl,
 } from '../../controls';
 
 const {
@@ -59,6 +60,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         [
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/index.ts
index ddf18eb..2a24b70 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from '../buildQuery';
 import controlPanel from './controlPanel';
@@ -47,9 +49,13 @@ export default class EchartsTimeseriesStepChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Time-series Stepped-line graph (also called step chart) is a variation of line chart but with the line forming a series of steps between data points. A step chart can be useful when you want to show the changes that occur at irregular intervals.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Stepped-line graph (also called step chart) is a variation of line chart but with the line forming a series of steps between data points. A step chart can be useful when you want to show the changes that occur at irregular intervals.',
+            )
+          : t(
+              'Time-series Stepped-line graph (also called step chart) is a variation of line chart but with the line forming a series of steps between data points. A step chart can be useful when you want to show the changes that occur at irregular intervals.',
+            ),
         exampleGallery: [{ url: example1 }, { url: example2 }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -57,7 +63,9 @@ export default class EchartsTimeseriesStepChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Stepped Line'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Stepped Line')
+          : t('Time-series Stepped Line'),
         tags: [
           t('ECharts'),
           t('Predictive'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
index 5d02f2f..1571c1c 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/buildQuery.ts
@@ -18,6 +18,8 @@
  */
 import {
   buildQueryContext,
+  DTTM_ALIAS,
+  ensureIsArray,
   QueryFormData,
   normalizeOrderBy,
   RollingType,
@@ -35,11 +37,14 @@ import {
 } from '@superset-ui/chart-controls';
 
 export default function buildQuery(formData: QueryFormData) {
+  const { x_axis, groupby } = formData;
+  const is_timeseries = x_axis === DTTM_ALIAS || !x_axis;
   return buildQueryContext(formData, baseQueryObject => {
     const pivotOperatorInRuntime: PostProcessingPivot | undefined =
       pivotOperator(formData, {
         ...baseQueryObject,
-        is_timeseries: true,
+        index: x_axis,
+        is_timeseries,
       });
     if (
       pivotOperatorInRuntime &&
@@ -57,7 +62,9 @@ export default function buildQuery(formData: QueryFormData) {
     return [
       {
         ...baseQueryObject,
-        is_timeseries: true,
+        columns: [...ensureIsArray(x_axis), ...ensureIsArray(groupby)],
+        series_columns: groupby,
+        is_timeseries,
         // todo: move `normalizeOrderBy to extractQueryFields`
         orderby: normalizeOrderBy(baseQueryObject).orderby,
         time_offsets: isValidTimeCompare(formData, baseQueryObject)
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx
index 5878882..1012b2f 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/controlPanel.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
 import {
   ControlPanelConfig,
   ControlPanelsContainerProps,
@@ -36,6 +36,7 @@ import {
   legendSection,
   richTooltipSection,
   showValueSection,
+  xAxisControl,
 } from '../controls';
 
 const {
@@ -60,6 +61,7 @@ const config: ControlPanelConfig = {
       label: t('Query'),
       expanded: true,
       controlSetRows: [
+        isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? [xAxisControl] : [],
         ['metrics'],
         ['groupby'],
         [
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/index.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/index.ts
index 48856ab..062d741 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/index.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/index.ts
@@ -17,11 +17,13 @@
  * under the License.
  */
 import {
-  t,
-  ChartMetadata,
-  ChartPlugin,
   AnnotationType,
   Behavior,
+  ChartMetadata,
+  ChartPlugin,
+  FeatureFlag,
+  isFeatureEnabled,
+  t,
 } from '@superset-ui/core';
 import buildQuery from './buildQuery';
 import controlPanel from './controlPanel';
@@ -37,16 +39,6 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
   EchartsTimeseriesFormData,
   EchartsTimeseriesChartProps
 > {
-  /**
-   * The constructor is used to pass relevant metadata and callbacks that get
-   * registered in respective registries that are used throughout the library
-   * and application. A more thorough description of each property is given in
-   * the respective imported file.
-   *
-   * It is worth noting that `buildQuery` and is optional, and only needed for
-   * advanced visualizations that require either post processing operations
-   * (pivoting, rolling aggregations, sorting etc) or submitting multiple queries.
-   */
   constructor() {
     super({
       buildQuery,
@@ -56,9 +48,13 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
         behaviors: [Behavior.INTERACTIVE_CHART],
         category: t('Evolution'),
         credits: ['https://echarts.apache.org'],
-        description: t(
-          'Swiss army knife for visualizing time series data. Choose between  step, line, scatter, and bar charts. This viz type has many customization options as well.',
-        ),
+        description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t(
+              'Swiss army knife for visualizing data. Choose between  step, line, scatter, and bar charts. This viz type has many customization options as well.',
+            )
+          : t(
+              'Swiss army knife for visualizing time series data. Choose between  step, line, scatter, and bar charts. This viz type has many customization options as well.',
+            ),
         exampleGallery: [{ url: example }],
         supportedAnnotationTypes: [
           AnnotationType.Event,
@@ -66,7 +62,9 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
           AnnotationType.Interval,
           AnnotationType.Timeseries,
         ],
-        name: t('Time-series Chart'),
+        name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
+          ? t('Generic Chart')
+          : t('Time-series Chart'),
         tags: [
           t('Advanced-Analytics'),
           t('Aesthetic'),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
index 44f97e9..bb3dca7 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -20,28 +20,32 @@
 import {
   AnnotationLayer,
   CategoricalColorNamespace,
+  DataRecordValue,
+  DTTM_ALIAS,
+  GenericDataType,
   getNumberFormatter,
   isEventAnnotationLayer,
   isFormulaAnnotationLayer,
   isIntervalAnnotationLayer,
   isTimeseriesAnnotationLayer,
   TimeseriesChartDataResponseResult,
-  DataRecordValue,
 } from '@superset-ui/core';
 import { EChartsCoreOption, SeriesOption } from 'echarts';
 import {
   DEFAULT_FORM_DATA,
   EchartsTimeseriesChartProps,
   EchartsTimeseriesFormData,
+  EchartsTimeseriesSeriesType,
   TimeseriesChartTransformedProps,
 } from './types';
 import { ForecastSeriesEnum, ProphetValue } from '../types';
 import { parseYAxisBound } from '../utils/controls';
 import {
+  currentSeries,
   dedupSeries,
-  extractTimeseriesSeries,
+  extractSeries,
+  getColtypesMapping,
   getLegendProps,
-  currentSeries,
 } from '../utils/series';
 import { extractAnnotationLabels } from '../utils/annotation';
 import {
@@ -77,8 +81,10 @@ export default function transformProps(
     datasource,
   } = chartProps;
   const { verboseMap = {} } = datasource;
+  const [queryData] = queriesData;
   const { annotation_data: annotationData_, data = [] } =
-    queriesData[0] as TimeseriesChartDataResponseResult;
+    queryData as TimeseriesChartDataResponseResult;
+  const dataTypes = getColtypesMapping(queryData);
   const annotationData = annotationData_ || {};
 
   const {
@@ -106,6 +112,7 @@ export default function transformProps(
     tooltipSortByMetric,
     zoomable,
     richTooltip,
+    xAxis: xAxisOrig,
     xAxisLabelRotation,
     emitFilter,
     groupby,
@@ -120,12 +127,28 @@ export default function transformProps(
   }: EchartsTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
   const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
   const rebasedData = rebaseTimeseriesDatum(data, verboseMap);
-  const rawSeries = extractTimeseriesSeries(rebasedData, {
+  const xAxisCol = xAxisOrig || DTTM_ALIAS;
+  const rawSeries = extractSeries(rebasedData, {
     fillNeighborValue: stack && !forecastEnabled ? 0 : undefined,
+    xAxis: xAxisCol,
+    removeNulls: seriesType === EchartsTimeseriesSeriesType.Scatter,
   });
   const seriesContexts = extractForecastSeriesContexts(
     Object.values(rawSeries).map(series => series.name as string),
   );
+  const xAxisDataType = dataTypes?.[xAxisCol];
+  let xAxisType: 'time' | 'value' | 'category';
+  switch (xAxisDataType) {
+    case GenericDataType.TEMPORAL:
+      xAxisType = 'time';
+      break;
+    case GenericDataType.NUMERIC:
+      xAxisType = 'value';
+      break;
+    default:
+      xAxisType = 'category';
+      break;
+  }
   const series: SeriesOption[] = [];
   const formatter = getNumberFormatter(contributionMode ? ',.0%' : yAxisFormat);
 
@@ -135,7 +158,7 @@ export default function transformProps(
 
   rebasedData.forEach(data => {
     const values = Object.keys(data).reduce((prev, curr) => {
-      if (curr === '__timestamp') {
+      if (curr === xAxisCol) {
         return prev;
       }
       const value = data[curr] || 0;
@@ -227,8 +250,14 @@ export default function transformProps(
     if (max === undefined) max = 1;
   }
 
-  const tooltipFormatter = getTooltipTimeFormatter(tooltipTimeFormat);
-  const xAxisFormatter = getXAxisFormatter(xAxisTimeFormat);
+  const tooltipFormatter =
+    xAxisDataType === GenericDataType.TEMPORAL
+      ? getTooltipTimeFormatter(tooltipTimeFormat)
+      : String;
+  const xAxisFormatter =
+    xAxisDataType === GenericDataType.TEMPORAL
+      ? getXAxisFormatter(xAxisTimeFormat)
+      : String;
 
   const labelMap = series.reduce(
     (acc: Record<string, DataRecordValue[]>, datum) => {
@@ -273,7 +302,7 @@ export default function transformProps(
       ...padding,
     },
     xAxis: {
-      type: 'time',
+      type: xAxisType,
       name: xAxisTitle,
       nameGap: xAxisTitleMargin,
       nameLocation: 'middle',
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
index 5f8ebea..053d0db 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { t, validateNonEmpty } from '@superset-ui/core';
 import {
   ControlPanelsContainerProps,
   ControlSetItem,
@@ -138,6 +138,18 @@ const onlyTotalControl: ControlSetItem = {
   },
 };
 
+export const xAxisControl: ControlSetItem = {
+  name: 'x_axis',
+  config: {
+    ...sharedControls.groupby,
+    label: t('X-axis'),
+    default: null,
+    multi: false,
+    description: t('Dimension to use on x-axis.'),
+    validators: [validateNonEmpty],
+  },
+};
+
 const percentageThresholdControl: ControlSetItem = {
   name: 'percentage_threshold',
   config: {
@@ -163,6 +175,11 @@ export const showValueSection: ControlSetRow[] = [
   [percentageThresholdControl],
 ];
 
+export const showValueSectionWithoutStack: ControlSetRow[] = [
+  [showValueControl],
+  [onlyTotalControl],
+];
+
 const richTooltipControl: ControlSetItem = {
   name: 'rich_tooltip',
   config: {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
index 9ccd7bb..f59dbf9 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/annotation.ts
@@ -34,11 +34,11 @@ import {
 export function evalFormula(
   formula: FormulaAnnotationLayer,
   data: TimeseriesDataRecord[],
-): [Date, number][] {
+): [number, number][] {
   const { value: expression } = formula;
 
   return data.map(row => [
-    new Date(Number(row.__timestamp)),
+    Number(row.__timestamp),
     evalExpression(expression, row.__timestamp as number),
   ]);
 }
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/prophet.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/prophet.ts
index 2f8acf3..82a747e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/prophet.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/prophet.ts
@@ -16,7 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { TimeseriesDataRecord, NumberFormatter } from '@superset-ui/core';
+import {
+  TimeseriesDataRecord,
+  NumberFormatter,
+  DTTM_ALIAS,
+} from '@superset-ui/core';
 import { CallbackDataParams, OptionName } from 'echarts/types/src/util/types';
 import { TooltipMarker } from 'echarts/types/src/util/format';
 import {
@@ -117,7 +121,7 @@ export function rebaseTimeseriesDatum(
 
   // eslint-disable-next-line @typescript-eslint/no-unsafe-return
   return data.map(row => {
-    const newRow: TimeseriesDataRecord = { __timestamp: '' };
+    const newRow: TimeseriesDataRecord = { [DTTM_ALIAS]: '' };
     keys.forEach(key => {
       const forecastContext = extractForecastSeriesContext(key);
       const lowerKey = `${forecastContext.name}${ForecastSeriesEnum.ForecastLower}`;
@@ -131,7 +135,7 @@ export function rebaseTimeseriesDatum(
         value -= row[lowerKey] as number;
       }
       const newKey =
-        key !== '__timestamp' && verboseMap[key] ? verboseMap[key] : key;
+        key !== DTTM_ALIAS && verboseMap[key] ? verboseMap[key] : key;
       newRow[newKey] = value;
     });
     // eslint-disable-next-line @typescript-eslint/no-unsafe-return
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
index 324ab06..d4c53d9 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
@@ -21,11 +21,11 @@ import {
   ChartDataResponseResult,
   DataRecord,
   DataRecordValue,
+  DTTM_ALIAS,
   ensureIsArray,
   GenericDataType,
   NumberFormatter,
   TimeFormatter,
-  TimeseriesDataRecord,
 } from '@superset-ui/core';
 import { format, LegendComponentOption, SeriesOption } from 'echarts';
 import { NULL_STRING, TIMESERIES_CONSTANTS } from '../constants';
@@ -36,37 +36,40 @@ function isDefined<T>(value: T | undefined | null): boolean {
   return value !== undefined && value !== null;
 }
 
-export function extractTimeseriesSeries(
-  data: TimeseriesDataRecord[],
-  opts: { fillNeighborValue?: number } = {},
+export function extractSeries(
+  data: DataRecord[],
+  opts: {
+    fillNeighborValue?: number;
+    xAxis?: string;
+    removeNulls?: boolean;
+  } = {},
 ): SeriesOption[] {
-  const { fillNeighborValue } = opts;
+  const { fillNeighborValue, xAxis = DTTM_ALIAS, removeNulls = false } = opts;
   if (data.length === 0) return [];
-  const rows: TimeseriesDataRecord[] = data.map(datum => ({
+  const rows: DataRecord[] = data.map(datum => ({
     ...datum,
-    __timestamp:
-      datum.__timestamp || datum.__timestamp === 0
-        ? new Date(datum.__timestamp)
-        : null,
+    [xAxis]: datum[xAxis],
   }));
 
   return Object.keys(rows[0])
-    .filter(key => key !== '__timestamp')
+    .filter(key => key !== xAxis && key !== DTTM_ALIAS)
     .map(key => ({
       id: key,
       name: key,
-      data: rows.map((row, idx) => {
-        const isNextToDefinedValue =
-          isDefined(rows[idx - 1]?.[key]) || isDefined(rows[idx + 1]?.[key]);
-        return [
-          row.__timestamp,
-          !isDefined(row[key]) &&
-          isNextToDefinedValue &&
-          fillNeighborValue !== undefined
-            ? fillNeighborValue
-            : row[key],
-        ];
-      }),
+      data: rows
+        .map((row, idx) => {
+          const isNextToDefinedValue =
+            isDefined(rows[idx - 1]?.[key]) || isDefined(rows[idx + 1]?.[key]);
+          return [
+            row[xAxis],
+            !isDefined(row[key]) &&
+            isNextToDefinedValue &&
+            fillNeighborValue !== undefined
+              ? fillNeighborValue
+              : row[key],
+          ];
+        })
+        .filter(obs => !removeNulls || (obs[0] !== null && obs[1] !== null)),
     }));
 }
 
@@ -102,7 +105,10 @@ export function formatSeriesName(
 export const getColtypesMapping = ({
   coltypes = [],
   colnames = [],
-}: ChartDataResponseResult): Record<string, GenericDataType> =>
+}: Pick<ChartDataResponseResult, 'coltypes' | 'colnames'>): Record<
+  string,
+  GenericDataType
+> =>
   colnames.reduce(
     (accumulator, item, index) => ({ ...accumulator, [item]: coltypes[index] }),
     {},
@@ -119,7 +125,7 @@ export function extractGroupbyLabel({
   groupby?: string[] | null;
   numberFormatter?: NumberFormatter;
   timeFormatter?: TimeFormatter;
-  coltypeMapping: Record<string, GenericDataType>;
+  coltypeMapping?: Record<string, GenericDataType>;
 }): string {
   return ensureIsArray(groupby)
     .map(val =>
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
index ab82417..7d21524 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts
@@ -17,14 +17,14 @@
  * under the License.
  */
 import {
+  AnnotationSourceType,
+  AnnotationStyle,
+  AnnotationType,
   ChartProps,
   EventAnnotationLayer,
   FormulaAnnotationLayer,
   IntervalAnnotationLayer,
   TimeseriesAnnotationLayer,
-  AnnotationStyle,
-  AnnotationType,
-  AnnotationSourceType,
 } from '@superset-ui/core';
 import transformProps from '../../src/Timeseries/transformProps';
 
@@ -35,6 +35,7 @@ describe('EchartsTimeseries transformProps', () => {
     granularity_sqla: 'ds',
     metric: 'sum__num',
     groupby: ['foo', 'bar'],
+    viz_type: 'my_viz',
   };
   const queriesData = [
     {
@@ -64,15 +65,15 @@ describe('EchartsTimeseries transformProps', () => {
           series: expect.arrayContaining([
             expect.objectContaining({
               data: [
-                [new Date(599616000000), 1],
-                [new Date(599916000000), 3],
+                [599616000000, 1],
+                [599916000000, 3],
               ],
               name: 'San Francisco',
             }),
             expect.objectContaining({
               data: [
-                [new Date(599616000000), 2],
-                [new Date(599916000000), 4],
+                [599616000000, 2],
+                [599916000000, 4],
               ],
               name: 'New York',
             }),
@@ -88,8 +89,8 @@ describe('EchartsTimeseries transformProps', () => {
       annotationType: AnnotationType.Formula,
       value: 'x+1',
       style: AnnotationStyle.Solid,
-      showLabel: true,
       show: true,
+      showLabel: true,
     };
     const chartProps = new ChartProps({
       ...chartPropsConfig,
@@ -109,22 +110,22 @@ describe('EchartsTimeseries transformProps', () => {
           series: expect.arrayContaining([
             expect.objectContaining({
               data: [
-                [new Date(599616000000), 1],
-                [new Date(599916000000), 3],
+                [599616000000, 1],
+                [599916000000, 3],
               ],
               name: 'San Francisco',
             }),
             expect.objectContaining({
               data: [
-                [new Date(599616000000), 2],
-                [new Date(599916000000), 4],
+                [599616000000, 2],
+                [599916000000, 4],
               ],
               name: 'New York',
             }),
             expect.objectContaining({
               data: [
-                [new Date(599616000000), 599616000001],
-                [new Date(599916000000), 599916000001],
+                [599616000000, 599616000001],
+                [599916000000, 599916000001],
               ],
               name: 'My Formula',
             }),
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
index 96a75b7..4d6b164 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/annotation.test.ts
@@ -18,8 +18,8 @@
  */
 import {
   AnnotationLayer,
+  AnnotationData,
   AnnotationOpacity,
-  AnnotationResult,
   AnnotationSourceType,
   AnnotationStyle,
   AnnotationType,
@@ -88,6 +88,7 @@ describe('extractAnnotationLabels', () => {
         show: true,
         style: AnnotationStyle.Solid,
         value: 'sin(x)',
+        showLabel: true,
       },
       {
         annotationType: AnnotationType.Formula,
@@ -95,6 +96,7 @@ describe('extractAnnotationLabels', () => {
         show: false,
         style: AnnotationStyle.Solid,
         value: 'sin(2x)',
+        showLabel: true,
       },
       {
         annotationType: AnnotationType.Interval,
@@ -103,6 +105,7 @@ describe('extractAnnotationLabels', () => {
         show: true,
         style: AnnotationStyle.Solid,
         value: 1,
+        showLabel: true,
       },
       {
         annotationType: AnnotationType.Timeseries,
@@ -111,6 +114,7 @@ describe('extractAnnotationLabels', () => {
         style: AnnotationStyle.Dashed,
         sourceType: AnnotationSourceType.Line,
         value: 1,
+        showLabel: true,
       },
       {
         annotationType: AnnotationType.Timeseries,
@@ -119,9 +123,10 @@ describe('extractAnnotationLabels', () => {
         style: AnnotationStyle.Dashed,
         sourceType: AnnotationSourceType.Line,
         value: 1,
+        showLabel: true,
       },
     ];
-    const results: AnnotationResult = {
+    const results: AnnotationData = {
       'My Interval': {
         columns: ['col'],
         records: [{ col: 1 }],
@@ -147,6 +152,7 @@ describe('evalFormula', () => {
     show: true,
     style: AnnotationStyle.Solid,
     value: 'x+1',
+    showLabel: true,
   };
   it('Should evaluate a regular formula', () => {
     const data: TimeseriesDataRecord[] = [
@@ -155,8 +161,8 @@ describe('evalFormula', () => {
     ];
 
     expect(evalFormula(layer, data)).toEqual([
-      [new Date(0), 1],
-      [new Date(10), 11],
+      [0, 1],
+      [10, 11],
     ]);
   });
 
@@ -167,8 +173,8 @@ describe('evalFormula', () => {
     ];
 
     expect(evalFormula({ ...layer, value: 'y  = x* 2   -1' }, data)).toEqual([
-      [new Date(0), -1],
-      [new Date(10), 19],
+      [0, -1],
+      [10, 19],
     ]);
   });
 });
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
index 91b9872..36cb204 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
@@ -20,7 +20,7 @@ import { getNumberFormatter, getTimeFormatter } from '@superset-ui/core';
 import {
   dedupSeries,
   extractGroupbyLabel,
-  extractTimeseriesSeries,
+  extractSeries,
   formatSeriesName,
   getChartPadding,
   getLegendProps,
@@ -30,7 +30,7 @@ import { LegendOrientation, LegendType } from '../../src/types';
 import { defaultLegendPadding } from '../../src/defaults';
 import { NULL_STRING } from '../../src/constants';
 
-describe('extractTimeseriesSeries', () => {
+describe('extractSeries', () => {
   it('should generate a valid ECharts timeseries series object', () => {
     const data = [
       {
@@ -49,23 +49,58 @@ describe('extractTimeseriesSeries', () => {
         abc: 5,
       },
     ];
-    expect(extractTimeseriesSeries(data)).toEqual([
+    expect(extractSeries(data)).toEqual([
       {
         id: 'Hulk',
         name: 'Hulk',
         data: [
-          [new Date('2000-01-01'), null],
-          [new Date('2000-02-01'), 2],
-          [new Date('2000-03-01'), 1],
+          ['2000-01-01', null],
+          ['2000-02-01', 2],
+          ['2000-03-01', 1],
         ],
       },
       {
         id: 'abc',
         name: 'abc',
         data: [
-          [new Date('2000-01-01'), 2],
-          [new Date('2000-02-01'), 10],
-          [new Date('2000-03-01'), 5],
+          ['2000-01-01', 2],
+          ['2000-02-01', 10],
+          ['2000-03-01', 5],
+        ],
+      },
+    ]);
+  });
+
+  it('should remove rows that have a null x-value', () => {
+    const data = [
+      {
+        x: 1,
+        Hulk: null,
+        abc: 2,
+      },
+      {
+        x: null,
+        Hulk: 2,
+        abc: 10,
+      },
+      {
+        x: 2,
+        Hulk: 1,
+        abc: 5,
+      },
+    ];
+    expect(extractSeries(data, { xAxis: 'x', removeNulls: true })).toEqual([
+      {
+        id: 'Hulk',
+        name: 'Hulk',
+        data: [[2, 1]],
+      },
+      {
+        id: 'abc',
+        name: 'abc',
+        data: [
+          [1, 2],
+          [2, 5],
         ],
       },
     ]);
@@ -114,21 +149,21 @@ describe('extractTimeseriesSeries', () => {
         abc: null,
       },
     ];
-    expect(extractTimeseriesSeries(data, { fillNeighborValue: 0 })).toEqual([
+    expect(extractSeries(data, { fillNeighborValue: 0 })).toEqual([
       {
         id: 'abc',
         name: 'abc',
         data: [
-          [new Date('2000-01-01'), null],
-          [new Date('2000-02-01'), 0],
-          [new Date('2000-03-01'), 1],
-          [new Date('2000-04-01'), 0],
-          [new Date('2000-05-01'), null],
-          [new Date('2000-06-01'), 0],
-          [new Date('2000-07-01'), 2],
-          [new Date('2000-08-01'), 3],
-          [new Date('2000-09-01'), 0],
-          [new Date('2000-10-01'), null],
+          ['2000-01-01', null],
+          ['2000-02-01', 0],
+          ['2000-03-01', 1],
+          ['2000-04-01', 0],
+          ['2000-05-01', null],
+          ['2000-06-01', 0],
+          ['2000-07-01', 2],
+          ['2000-08-01', 3],
+          ['2000-09-01', 0],
+          ['2000-10-01', null],
         ],
       },
     ]);
diff --git a/superset/common/query_context_processor.py b/superset/common/query_context_processor.py
index 9c641cb..a313d9d 100644
--- a/superset/common/query_context_processor.py
+++ b/superset/common/query_context_processor.py
@@ -306,10 +306,14 @@ class QueryContextProcessor:
                 # 2. rename extra query columns
                 offset_metrics_df = offset_metrics_df.rename(columns=metrics_mapping)
 
-                # 3. set time offset for dttm column
-                offset_metrics_df[DTTM_ALIAS] = offset_metrics_df[
-                    DTTM_ALIAS
-                ] - DateOffset(**normalize_time_delta(offset))
+                # 3. set time offset for index
+                # TODO: add x-axis to QueryObject, potentially as an array for
+                #  multi-dimensional charts
+                granularity = query_object.granularity
+                index = granularity if granularity in df.columns else DTTM_ALIAS
+                offset_metrics_df[index] = offset_metrics_df[index] - DateOffset(
+                    **normalize_time_delta(offset)
+                )
 
             # df left join `offset_metrics_df`
             offset_df = df_utils.left_join_df(
diff --git a/superset/utils/pandas_postprocessing.py b/superset/utils/pandas_postprocessing.py
index c247a49..beef42a 100644
--- a/superset/utils/pandas_postprocessing.py
+++ b/superset/utils/pandas_postprocessing.py
@@ -14,6 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+# pylint: disable=too-many-lines
 import logging
 from decimal import Decimal
 from functools import partial
@@ -785,6 +786,7 @@ def prophet(  # pylint: disable=too-many-arguments
     yearly_seasonality: Optional[Union[bool, int]] = None,
     weekly_seasonality: Optional[Union[bool, int]] = None,
     daily_seasonality: Optional[Union[bool, int]] = None,
+    index: Optional[str] = None,
 ) -> DataFrame:
     """
     Add forecasts to each series in a timeseries dataframe, along with confidence
@@ -808,8 +810,10 @@ def prophet(  # pylint: disable=too-many-arguments
     :param daily_seasonality: Should daily seasonality be applied.
            An integer value will specify Fourier order of seasonality, `None` will
            automatically detect seasonality.
+    :param index: the name of the column containing the x-axis data
     :return: DataFrame with contributions, with temporal column at beginning if present
     """
+    index = index or DTTM_ALIAS
     # validate inputs
     if not time_grain:
         raise QueryObjectValidationError(_("Time grain missing"))
@@ -826,15 +830,15 @@ def prophet(  # pylint: disable=too-many-arguments
         raise QueryObjectValidationError(
             _("Confidence interval must be between 0 and 1 (exclusive)")
         )
-    if DTTM_ALIAS not in df.columns:
+    if index not in df.columns:
         raise QueryObjectValidationError(_("DataFrame must include temporal column"))
     if len(df.columns) < 2:
         raise QueryObjectValidationError(_("DataFrame include at least one series"))
 
     target_df = DataFrame()
-    for column in [column for column in df.columns if column != DTTM_ALIAS]:
+    for column in [column for column in df.columns if column != index]:
         fit_df = _prophet_fit_and_predict(
-            df=df[[DTTM_ALIAS, column]].rename(columns={DTTM_ALIAS: "ds", column: "y"}),
+            df=df[[index, column]].rename(columns={index: "ds", column: "y"}),
             confidence_interval=confidence_interval,
             yearly_seasonality=_prophet_parse_seasonality(yearly_seasonality),
             weekly_seasonality=_prophet_parse_seasonality(weekly_seasonality),
@@ -855,7 +859,7 @@ def prophet(  # pylint: disable=too-many-arguments
             for new_column in new_columns:
                 target_df = target_df.assign(**{new_column: fit_df[new_column]})
     target_df.reset_index(level=0, inplace=True)
-    return target_df.rename(columns={"ds": DTTM_ALIAS})
+    return target_df.rename(columns={"ds": index})
 
 
 def boxplot(