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

kgabryje 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 b30f6a5db1 chore(explore): Get Explore data from endpoint instead of 
bootstrap_data (#20519)
b30f6a5db1 is described below

commit b30f6a5db1c9d79d4837529b671090f4a908c056
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Thu Jun 30 21:06:51 2022 +0200

    chore(explore): Get Explore data from endpoint instead of bootstrap_data 
(#20519)
    
    * feat(explore): Use v1/explore endpoint data instead of bootstrapData
    
    * Add tests
    
    * Fix ci
    
    * Remove redundant dependency
    
    * Use form_data_key in cypress tests
    
    * Add auth headers to for data request
    
    * Address comments
    
    * Remove displaying danger toast
    
    * Conditionally add auth headers
    
    * Address comments
    
    * Fix typing bug
    
    * fix
    
    * Fix opening dataset
    
    * Fix sqllab chart create
    
    * Run queries in parallel
    
    * Fix dashboard id autofill
    
    * Fix lint
    
    * Fix test
---
 .../cypress/integration/explore/chart.test.js      |   2 +-
 .../cypress/integration/explore/control.test.ts    |  10 +-
 .../integration/explore/explore.applitools.test.ts |   2 +-
 .../cypress/integration/explore/filter_box.test.js |   2 +-
 .../cypress/integration/explore/link.test.ts       |   2 +-
 .../explore/visualizations/area.test.js            |  34 +++--
 .../explore/visualizations/big_number.test.js      |   6 +-
 .../explore/visualizations/box_plot.test.js        |   2 +-
 .../explore/visualizations/bubble.test.js          |   8 +-
 .../explore/visualizations/compare.test.js         |   2 +-
 .../explore/visualizations/dist_bar.test.js        |   8 +-
 .../explore/visualizations/download_chart.test.js  |   2 +-
 .../explore/visualizations/dual_line.test.js       |   4 +-
 .../explore/visualizations/gauge.test.js           |   2 +-
 .../explore/visualizations/graph.test.ts           |   2 +-
 .../explore/visualizations/histogram.test.ts       |   2 +-
 .../explore/visualizations/line.test.ts            |  38 +++---
 .../integration/explore/visualizations/pie.test.js |   2 +-
 .../explore/visualizations/pivot_table.test.js     |   4 +-
 .../explore/visualizations/sankey.test.js          |   6 +-
 .../explore/visualizations/shared.helper.js        |   4 +-
 .../explore/visualizations/sunburst.test.js        |   2 +-
 .../explore/visualizations/table.test.ts           |   8 +-
 .../explore/visualizations/time_table.js           |  10 +-
 .../explore/visualizations/treemap.test.js         |   2 +-
 .../explore/visualizations/world_map.test.js       |   2 +-
 .../cypress-base/cypress/support/index.ts          |  46 ++++++-
 .../superset-ui-chart-controls/src/types.ts        |  10 ++
 .../SqlLab/components/SaveDatasetModal/index.tsx   |  75 +++++++----
 superset-frontend/src/SqlLab/types.ts              |  10 +-
 .../src/addSlice/AddSliceContainer.test.tsx        |   9 +-
 .../src/addSlice/AddSliceContainer.tsx             |  31 ++---
 .../src/components/Chart/chartReducer.ts           |   3 +-
 superset-frontend/src/constants.ts                 |   4 +
 superset-frontend/src/explore/App.jsx              |   6 +-
 superset-frontend/src/explore/ExplorePage.tsx      |  68 ++++++++++
 .../src/explore/actions/datasourcesActions.ts      |   2 +-
 .../src/explore/actions/hydrateExplore.test.ts     |  92 +++++++++++++
 .../src/explore/actions/hydrateExplore.ts          | 146 +++++++++++++++++++++
 .../explore/components/ControlPanelsContainer.tsx  |   3 +-
 .../DatasourcePanel/DatasourcePanel.test.tsx       |   4 +-
 .../components/ExploreViewContainer/index.jsx      |  16 +--
 .../controls/DatasourceControl/index.jsx           |   2 +-
 .../controls/VizTypeControl/FastVizSwitcher.tsx    |   2 +-
 .../exploreUtils/getParsedExploreURLParams.test.ts |  62 +++++++++
 .../exploreUtils/getParsedExploreURLParams.ts      | 117 +++++++++++++++++
 .../src/explore/exploreUtils/index.js              |   3 +-
 superset-frontend/src/explore/fixtures.tsx         |  59 ++++++++-
 superset-frontend/src/explore/index.jsx            |  22 +++-
 .../src/explore/reducers/datasourcesReducer.ts     |   6 +-
 .../src/explore/reducers/exploreReducer.js         |   7 +-
 .../src/explore/reducers/getInitialState.ts        | 146 ---------------------
 .../src/explore/reducers/saveModalReducer.js       |   4 +
 superset-frontend/src/explore/store.js             |   2 +-
 superset-frontend/src/explore/types.ts             |  47 ++++++-
 55 files changed, 845 insertions(+), 327 deletions(-)

diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js 
b/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
index c9f4a1c9f5..ca37bf9690 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
@@ -41,7 +41,7 @@ describe('No Results', () => {
       ],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.wait('@getJson').its('response.statusCode').should('eq', 200);
     cy.get('div.chart-container').contains(
       'No results were returned for this query',
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts 
b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
index 18bf8859a8..95bdd514bf 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
@@ -148,7 +148,7 @@ describe('Time range filter', () => {
       metrics: [NUM_METRIC],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData' });
 
     cy.get('[data-test=time-range-trigger]')
@@ -172,7 +172,7 @@ describe('Time range filter', () => {
       time_range: 'Last year',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData' });
 
     cy.get('[data-test=time-range-trigger]')
@@ -192,7 +192,7 @@ describe('Time range filter', () => {
       time_range: 'previous calendar month',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData' });
 
     cy.get('[data-test=time-range-trigger]')
@@ -212,7 +212,7 @@ describe('Time range filter', () => {
       time_range: 'DATEADD(DATETIME("today"), -7, day) : today',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData' });
 
     cy.get('[data-test=time-range-trigger]')
@@ -235,7 +235,7 @@ describe('Time range filter', () => {
       time_range: 'No filter',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData' });
 
     cy.get('[data-test=time-range-trigger]')
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/explore.applitools.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/explore/explore.applitools.test.ts
index 96b0d66847..64d77b4d68 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/explore.applitools.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/explore.applitools.test.ts
@@ -31,7 +31,7 @@ describe('explore view', () => {
   it('should load Explore', () => {
     const LINE_CHART_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'line' };
     const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
     cy.eyesOpen({
       testName: 'Explore page',
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/filter_box.test.js 
b/superset-frontend/cypress-base/cypress/integration/explore/filter_box.test.js
index 921377c45f..b9844274e2 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/filter_box.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/filter_box.test.js
@@ -22,7 +22,7 @@ describe('Edit FilterBox Chart', () => {
   const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'filter_box' };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/link.test.ts 
b/superset-frontend/cypress-base/cypress/integration/explore/link.test.ts
index 9f07e9c10b..fb3445fc63 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/link.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/link.test.ts
@@ -74,7 +74,7 @@ describe('Test explore links', () => {
     };
     const newChartName = `Test chart [${shortid.generate()}]`;
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
     cy.url().then(() => {
       cy.get('[data-test="query-save-button"]').click();
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/area.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/area.test.js
index 86b5a789c2..59b8beabe3 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/area.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/area.test.js
@@ -51,7 +51,7 @@ describe('Visualization > Area', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
@@ -75,23 +75,21 @@ describe('Visualization > Area', () => {
   });
 
   it('should work with groupby and filter', () => {
-    cy.visitChartByParams(
-      JSON.stringify({
-        ...AREA_FORM_DATA,
-        groupby: ['region'],
-        adhoc_filters: [
-          {
-            expressionType: 'SIMPLE',
-            subject: 'region',
-            operator: 'IN',
-            comparator: ['South Asia', 'North America'],
-            clause: 'WHERE',
-            sqlExpression: null,
-            filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo',
-          },
-        ],
-      }),
-    );
+    cy.visitChartByParams({
+      ...AREA_FORM_DATA,
+      groupby: ['region'],
+      adhoc_filters: [
+        {
+          expressionType: 'SIMPLE',
+          subject: 'region',
+          operator: 'IN',
+          comparator: ['South Asia', 'North America'],
+          clause: 'WHERE',
+          sqlExpression: null,
+          filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo',
+        },
+      ],
+    });
 
     cy.wait('@getJson').then(async ({ response }) => {
       const responseBody = response?.body;
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/big_number.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/big_number.test.js
index 30e7716b73..ede7ed47f9 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/big_number.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/big_number.test.js
@@ -25,11 +25,11 @@ describe('Visualization > Big Number with Trendline', () => 
{
     slice_id: 42,
     granularity_sqla: 'year',
     time_grain_sqla: 'P1D',
-    time_range: '2000+:+2014-01-02',
+    time_range: '2000 : 2014-01-02',
     metric: 'sum__SP_POP_TOTL',
     adhoc_filters: [],
     compare_lag: '10',
-    compare_suffix: 'over+10Y',
+    compare_suffix: 'over 10Y',
     y_axis_format: '.3s',
     show_trend_line: true,
     start_y_axis_at_zero: true,
@@ -42,7 +42,7 @@ describe('Visualization > Big Number with Trendline', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@chartData',
       chartSelector: '.superset-legacy-chart-big-number',
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/box_plot.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/box_plot.test.js
index 432815b869..6a4afba97a 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/box_plot.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/box_plot.test.js
@@ -33,7 +33,7 @@ describe('Visualization > Box Plot', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/bubble.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/bubble.test.js
index 9bd91f37c5..9c824e5d9f 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/bubble.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/bubble.test.js
@@ -23,7 +23,7 @@ describe('Visualization > Bubble', () => {
     slice_id: 46,
     granularity_sqla: 'year',
     time_grain_sqla: 'P1D',
-    time_range: '2011-01-01+:+2011-01-02',
+    time_range: '2011-01-01 : 2011-01-02',
     series: 'region',
     entity: 'country_name',
     x: 'sum__SP_RUR_TOTL_ZS',
@@ -47,7 +47,7 @@ describe('Visualization > Bubble', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
@@ -60,7 +60,7 @@ describe('Visualization > Bubble', () => {
   // Since main functionality is already covered in filter test below,
   // skip this test until we find a solution.
   it.skip('should work', () => {
-    cy.visitChartByParams(JSON.stringify(BUBBLE_FORM_DATA)).then(() => {
+    cy.visitChartByParams(BUBBLE_FORM_DATA).then(() => {
       cy.wait('@getJson').then(xhr => {
         let expectedBubblesNumber = 0;
         xhr.responseBody.data.forEach(element => {
@@ -86,7 +86,7 @@ describe('Visualization > Bubble', () => {
           expressionType: 'SIMPLE',
           subject: 'region',
           operator: '==',
-          comparator: 'South+Asia',
+          comparator: 'South Asia',
           clause: 'WHERE',
           sqlExpression: null,
           filterOptionName: 'filter_b2tfg1rs8y_8kmrcyxvsqd',
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/compare.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/compare.test.js
index 83b37f889f..35f56754a8 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/compare.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/compare.test.js
@@ -47,7 +47,7 @@ describe('Visualization > Compare', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dist_bar.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dist_bar.test.js
index bec718367e..6bd7c82f43 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dist_bar.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dist_bar.test.js
@@ -33,7 +33,7 @@ describe('Visualization > Distribution bar chart', () => {
       groupby: ['state'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@getJson',
       querySubstring: NUM_METRIC.label,
@@ -49,7 +49,7 @@ describe('Visualization > Distribution bar chart', () => {
       columns: ['gender'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -61,7 +61,7 @@ describe('Visualization > Distribution bar chart', () => {
       row_limit: 10,
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -74,7 +74,7 @@ describe('Visualization > Distribution bar chart', () => {
       contribution: true,
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 });
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/download_chart.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/download_chart.test.js
index ce4a871f8e..029ead3110 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/download_chart.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/download_chart.test.js
@@ -33,7 +33,7 @@ describe('Download Chart > Distribution bar chart', () => {
       groupby: ['state'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.get('.header-with-actions .ant-dropdown-trigger').click();
     cy.get(':nth-child(1) > .ant-dropdown-menu-submenu-title').click();
     cy.get(
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dual_line.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dual_line.test.js
index 641b2925d7..3a3eb334fb 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dual_line.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/dual_line.test.js
@@ -23,7 +23,7 @@ describe('Visualization > Dual Line', () => {
     slice_id: 58,
     granularity_sqla: 'ds',
     time_grain_sqla: 'P1D',
-    time_range: '100+years+ago+:+now',
+    time_range: '100 years ago : now',
     color_scheme: 'bnbColors',
     x_axis_format: 'smart_date',
     metric: 'sum__num',
@@ -35,7 +35,7 @@ describe('Visualization > Dual Line', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js
index 8b5b2ffd0b..c4735b7b8a 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js
@@ -27,7 +27,7 @@ describe('Visualization > Gauge', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/graph.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/graph.test.ts
index 47adb075bd..c01d9c5099 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/graph.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/graph.test.ts
@@ -46,7 +46,7 @@ describe('Visualization > Graph', () => {
   function verify(formData: {
     [name: string]: string | boolean | number | Array<adhocFilter>;
   }): void {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/histogram.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/histogram.test.ts
index 67cbba3f96..ff6355319a 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/histogram.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/histogram.test.ts
@@ -39,7 +39,7 @@ describe('Visualization > Histogram', () => {
   };
 
   function verify(formData: QueryFormData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
index e8998b4bef..da20cfab85 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
@@ -28,7 +28,7 @@ describe('Visualization > Line', () => {
 
   it('should show validator error when no metric', () => {
     const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.get('.panel-body').contains(
       `Add required control values to preview chart`,
     );
@@ -36,7 +36,7 @@ describe('Visualization > Line', () => {
 
   it('should not show validator error when metric added', () => {
     const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.get('.panel-body').contains(
       `Add required control values to preview chart`,
     );
@@ -61,7 +61,7 @@ describe('Visualization > Line', () => {
 
   it('should allow negative values in Y bounds', () => {
     const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.get('#controlSections-tab-display').click();
     cy.get('span').contains('Y Axis Bounds').scrollIntoView();
     cy.get('input[placeholder="Min"]').type('-0.1', { delay: 100 });
@@ -81,7 +81,7 @@ describe('Visualization > Line', () => {
 
   it('should work with adhoc metric', () => {
     const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -89,7 +89,7 @@ describe('Visualization > Line', () => {
     const metrics = ['count'];
     const groupby = ['gender'];
     const formData = { ...LINE_CHART_DEFAULTS, metrics, groupby };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -101,7 +101,7 @@ describe('Visualization > Line', () => {
       metrics,
       adhoc_filters: filters,
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -113,7 +113,7 @@ describe('Visualization > Line', () => {
       groupby: ['name'],
       timeseries_limit_metric: NUM_METRIC,
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -126,7 +126,7 @@ describe('Visualization > Line', () => {
       timeseries_limit_metric: NUM_METRIC,
       order_desc: true,
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -138,7 +138,7 @@ describe('Visualization > Line', () => {
       rolling_type: 'mean',
       rolling_periods: 10,
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -147,12 +147,12 @@ describe('Visualization > Line', () => {
     const formData = {
       ...LINE_CHART_DEFAULTS,
       metrics,
-      time_compare: ['1+year'],
+      time_compare: ['1 year'],
       comparison_type: 'values',
       groupby: ['gender'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
 
     // Offset color should match original line color
@@ -190,10 +190,10 @@ describe('Visualization > Line', () => {
     const formData = {
       ...LINE_CHART_DEFAULTS,
       metrics,
-      time_compare: ['1+year'],
+      time_compare: ['1 year'],
       comparison_type: 'ratio',
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -202,10 +202,10 @@ describe('Visualization > Line', () => {
     const formData = {
       ...LINE_CHART_DEFAULTS,
       metrics,
-      time_compare: ['1+year'],
+      time_compare: ['1 year'],
       comparison_type: 'percentage',
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   });
 
@@ -214,7 +214,7 @@ describe('Visualization > Line', () => {
       ...LINE_CHART_DEFAULTS,
       metrics: ['count'],
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
     cy.get('text.nv-legend-text').contains('COUNT(*)');
   });
@@ -225,7 +225,7 @@ describe('Visualization > Line', () => {
       metrics: ['count'],
       annotation_layers: [
         {
-          name: 'Goal+line',
+          name: 'Goal line',
           annotationType: 'FORMULA',
           sourceType: '',
           value: 'y=140000',
@@ -245,7 +245,7 @@ describe('Visualization > Line', () => {
         },
       ],
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
     cy.get('.slice_container').within(() => {
       // Goal line annotation doesn't show up in legend
@@ -281,7 +281,7 @@ describe('Visualization > Line', () => {
             },
           ],
         };
-        cy.visitChartByParams(JSON.stringify(formData));
+        cy.visitChartByParams(formData);
       },
     );
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pie.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pie.test.js
index fb083de615..3b28128e68 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pie.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pie.test.js
@@ -37,7 +37,7 @@ describe('Visualization > Pie', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pivot_table.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pivot_table.test.js
index 14de08da79..ef62beb381 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pivot_table.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/pivot_table.test.js
@@ -23,7 +23,7 @@ describe('Visualization > Pivot Table', () => {
     slice_id: 61,
     granularity_sqla: 'ds',
     time_grain_sqla: 'P1D',
-    time_range: '100+years+ago+:+now',
+    time_range: '100 years ago : now',
     metrics: ['sum__num'],
     adhoc_filters: [],
     groupby: ['name'],
@@ -54,7 +54,7 @@ describe('Visualization > Pivot Table', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sankey.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sankey.test.js
index 257ec00c1f..747e8a06f2 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sankey.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sankey.test.js
@@ -24,7 +24,7 @@ describe('Visualization > Sankey', () => {
     url_params: {},
     granularity_sqla: null,
     time_grain_sqla: 'P1D',
-    time_range: 'Last+week',
+    time_range: 'Last week',
     groupby: ['source', 'target'],
     metric: 'sum__value',
     adhoc_filters: [],
@@ -33,7 +33,7 @@ describe('Visualization > Sankey', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
@@ -53,7 +53,7 @@ describe('Visualization > Sankey', () => {
       adhoc_filters: [
         {
           expressionType: 'SQL',
-          sqlExpression: 'SUM(value)+>+0',
+          sqlExpression: 'SUM(value) > 0',
           clause: 'HAVING',
           subject: null,
           operator: null,
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/shared.helper.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/shared.helper.js
index 78a659fc91..bfd50e66d3 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/shared.helper.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/shared.helper.js
@@ -24,7 +24,7 @@ export const FORM_DATA_DEFAULTS = {
   datasource: '3__table',
   granularity_sqla: 'ds',
   time_grain_sqla: null,
-  time_range: '100+years+ago+:+now',
+  time_range: '100 years ago : now',
   adhoc_filters: [],
   groupby: [],
   limit: null,
@@ -37,7 +37,7 @@ export const HEALTH_POP_FORM_DATA_DEFAULTS = {
   datasource: '2__table',
   granularity_sqla: 'ds',
   time_grain_sqla: 'P1D',
-  time_range: '1960-01-01+:+2014-01-02',
+  time_range: '1960-01-01 : 2014-01-02',
 };
 
 export const NUM_METRIC = {
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sunburst.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sunburst.test.js
index 99cbb1e407..e7ccacac03 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sunburst.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/sunburst.test.js
@@ -32,7 +32,7 @@ describe('Visualization > Sunburst', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
index 6361d93d18..f11b23d595 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
@@ -174,7 +174,7 @@ describe('Visualization > Table', () => {
       groupby: ['name'],
       row_limit: limit,
     };
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.wait('@chartData').then(({ response }) => {
       cy.verifySliceContainer('table');
       expect(response?.body.result[0].data.length).to.eq(limit);
@@ -219,7 +219,7 @@ describe('Visualization > Table', () => {
       order_by_cols: ['["num", false]'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.wait('@chartData').then(({ response }) => {
       cy.verifySliceContainer('table');
       const records = response?.body.result[0].data;
@@ -233,7 +233,7 @@ describe('Visualization > Table', () => {
 
     const formData = { ...VIZ_DEFAULTS, metrics, adhoc_filters: filters };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'table' });
   });
 
@@ -244,7 +244,7 @@ describe('Visualization > Table', () => {
       groupby: ['state'],
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@chartData',
       querySubstring: /group by.*state/i,
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/time_table.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/time_table.js
index 7da9002785..eb81d17a3b 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/time_table.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/time_table.js
@@ -33,7 +33,7 @@ describe('Visualization > Time TableViz', () => {
       column_collection: [
         {
           key: '9g4K-B-YL',
-          label: 'Last+Year',
+          label: 'Last Year',
           colType: 'time',
           timeLag: '1',
           comparisonType: 'value',
@@ -42,7 +42,7 @@ describe('Visualization > Time TableViz', () => {
       url: '',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@getJson',
       querySubstring: NUM_METRIC.label,
@@ -61,7 +61,7 @@ describe('Visualization > Time TableViz', () => {
       column_collection: [
         {
           key: '9g4K-B-YL',
-          label: 'Last+Year',
+          label: 'Last Year',
           colType: 'time',
           timeLag: '1',
           comparisonType: 'value',
@@ -70,7 +70,7 @@ describe('Visualization > Time TableViz', () => {
       url: '',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@getJson',
       querySubstring: NUM_METRIC.label,
@@ -107,7 +107,7 @@ describe('Visualization > Time TableViz', () => {
       url: '',
     };
 
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({
       waitAlias: '@getJson',
       querySubstring: NUM_METRIC.label,
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/treemap.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/treemap.test.js
index 6ebe06274f..1be85e9e4c 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/treemap.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/treemap.test.js
@@ -38,7 +38,7 @@ describe('Visualization > Treemap', () => {
   const level2 = '.chart-container rect[style="fill: rgb(0, 122, 135);"]';
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/world_map.test.js
 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/world_map.test.js
index ed9d3e4214..0fab65519d 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/world_map.test.js
+++ 
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/world_map.test.js
@@ -35,7 +35,7 @@ describe('Visualization > World Map', () => {
   };
 
   function verify(formData) {
-    cy.visitChartByParams(JSON.stringify(formData));
+    cy.visitChartByParams(formData);
     cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
   }
 
diff --git a/superset-frontend/cypress-base/cypress/support/index.ts 
b/superset-frontend/cypress-base/cypress/support/index.ts
index 38e1ad5f6c..9d77f98acc 100644
--- a/superset-frontend/cypress-base/cypress/support/index.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.ts
@@ -55,12 +55,46 @@ Cypress.Commands.add('visitChartById', chartId =>
   cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${chartId}}`),
 );
 
-Cypress.Commands.add('visitChartByParams', params => {
-  const queryString =
-    typeof params === 'string' ? params : JSON.stringify(params);
-  const url = `${BASE_EXPLORE_URL}${queryString}`;
-  return cy.visit(url);
-});
+Cypress.Commands.add(
+  'visitChartByParams',
+  (formData: {
+    datasource?: string;
+    datasource_id?: number;
+    datasource_type?: string;
+    [key: string]: unknown;
+  }) => {
+    let datasource_id;
+    let datasource_type;
+    if (formData.datasource_id && formData.datasource_type) {
+      ({ datasource_id, datasource_type } = formData);
+    } else {
+      [datasource_id, datasource_type] = formData.datasource?.split('__') || 
[];
+    }
+    const accessToken = window.localStorage.getItem('access_token');
+    cy.request({
+      method: 'POST',
+      url: 'api/v1/explore/form_data',
+      body: {
+        datasource_id,
+        datasource_type,
+        form_data: JSON.stringify(formData),
+      },
+      headers: {
+        ...(accessToken && {
+          Cookie: `csrf_access_token=${accessToken}`,
+          'X-CSRFToken': accessToken,
+        }),
+        ...(TokenName && { Authorization: `Bearer ${TokenName}` }),
+        'Content-Type': 'application/json',
+        Referer: `${Cypress.config().baseUrl}/`,
+      },
+    }).then(response => {
+      const formDataKey = response.body.key;
+      const url = `/superset/explore/?form_data_key=${formDataKey}`;
+      cy.visit(url);
+    });
+  },
+);
 
 Cypress.Commands.add('verifySliceContainer', chartSelector => {
   // After a wait response check for valid slice container
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts 
b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
index 1de492d518..5ae2f26c4f 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
@@ -50,6 +50,14 @@ export type SharedControlComponents = typeof 
sharedControlComponents;
 /** ----------------------------------------------
  * Input data/props while rendering
  * ---------------------------------------------*/
+export interface Owner {
+  first_name: string;
+  id: number;
+  last_name: string;
+  username: string;
+  email?: string;
+}
+
 export type ColumnMeta = Omit<Column, 'id'> & {
   id?: number;
 } & AnyDict;
@@ -67,8 +75,10 @@ export interface Dataset {
   time_grain_sqla?: string;
   granularity_sqla?: string;
   datasource_name: string | null;
+  name?: string;
   description: string | null;
   uid?: string;
+  owners?: Owner[];
 }
 
 export interface ControlPanelState {
diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx 
b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx
index 9a8366ba56..4a87d52c18 100644
--- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx
@@ -46,8 +46,11 @@ import {
   SqlLabExploreRootState,
   getInitialState,
   ExploreDatasource,
+  SqlLabRootState,
 } from 'src/SqlLab/types';
-import { exploreChart } from 'src/explore/exploreUtils';
+import { mountExploreUrl } from 'src/explore/exploreUtils';
+import { postFormData } from 'src/explore/exploreUtils/formData';
+import { URL_PARAMS } from 'src/constants';
 
 interface SaveDatasetModalProps {
   visible: boolean;
@@ -115,6 +118,9 @@ export const SaveDatasetModal: 
FunctionComponent<SaveDatasetModalProps> = ({
   modalDescription,
   datasource,
 }) => {
+  const defaultVizType = useSelector<SqlLabRootState, string>(
+    state => state.common?.conf?.DEFAULT_VIZ_TYPE || 'table',
+  );
   const query = datasource as QueryResponse;
   const getDefaultDatasetName = () =>
     `${query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`;
@@ -137,30 +143,40 @@ export const SaveDatasetModal: 
FunctionComponent<SaveDatasetModalProps> = ({
   const dispatch = useDispatch<(dispatch: any) => Promise<JsonObject>>();
 
   const handleOverwriteDataset = async () => {
-    await updateDataset(
-      query.dbId,
-      datasetToOverwrite.datasetId,
-      query.sql,
-      query.results.selected_columns.map(
-        (d: { name: string; type: string; is_dttm: boolean }) => ({
-          column_name: d.name,
-          type: d.type,
-          is_dttm: d.is_dttm,
-        }),
+    const [, key] = await Promise.all([
+      updateDataset(
+        query.dbId,
+        datasetToOverwrite.datasetId,
+        query.sql,
+        query.results.selected_columns.map(
+          (d: { name: string; type: string; is_dttm: boolean }) => ({
+            column_name: d.name,
+            type: d.type,
+            is_dttm: d.is_dttm,
+          }),
+        ),
+        datasetToOverwrite.owners.map((o: DatasetOwner) => o.id),
+        true,
       ),
-      datasetToOverwrite.owners.map((o: DatasetOwner) => o.id),
-      true,
-    );
+      postFormData(datasetToOverwrite.datasetId, 'table', {
+        ...EXPLORE_CHART_DEFAULT,
+        datasource: `${datasetToOverwrite.datasetId}__table`,
+        ...(defaultVizType === 'table' && {
+          all_columns: query.results.selected_columns.map(
+            column => column.name,
+          ),
+        }),
+      }),
+    ]);
+
+    const url = mountExploreUrl(null, {
+      [URL_PARAMS.formDataKey.name]: key,
+    });
+    window.open(url, '_blank', 'noreferrer');
 
     setShouldOverwriteDataset(false);
     setDatasetToOverwrite({});
     setDatasetName(getDefaultDatasetName());
-
-    exploreChart({
-      ...EXPLORE_CHART_DEFAULT,
-      datasource: `${datasetToOverwrite.datasetId}__table`,
-      selected_columns: query.results.selected_columns,
-    });
   };
 
   const getUserDatasets = async (searchText = '') => {
@@ -235,15 +251,20 @@ export const SaveDatasetModal: 
FunctionComponent<SaveDatasetModalProps> = ({
         columns: selectedColumns,
       }),
     )
-      .then((data: { table_id: number }) => {
-        exploreChart({
+      .then((data: { table_id: number }) =>
+        postFormData(data.table_id, 'table', {
+          ...EXPLORE_CHART_DEFAULT,
           datasource: `${data.table_id}__table`,
-          metrics: [],
-          groupby: [],
-          time_range: 'No filter',
-          selectedColumns,
-          row_limit: 1000,
+          ...(defaultVizType === 'table' && {
+            all_columns: selectedColumns.map(column => column.name),
+          }),
+        }),
+      )
+      .then((key: string) => {
+        const url = mountExploreUrl(null, {
+          [URL_PARAMS.formDataKey.name]: key,
         });
+        window.open(url, '_blank', 'noreferrer');
       })
       .catch(() => {
         addDangerToast(t('An error occurred saving dataset'));
diff --git a/superset-frontend/src/SqlLab/types.ts 
b/superset-frontend/src/SqlLab/types.ts
index 9a6198b864..1b7b1d495c 100644
--- a/superset-frontend/src/SqlLab/types.ts
+++ b/superset-frontend/src/SqlLab/types.ts
@@ -16,11 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { Dataset } from '@superset-ui/chart-controls';
+import { JsonObject, Query, QueryResponse } from '@superset-ui/core';
 import { SupersetError } from 'src/components/ErrorMessage/types';
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import { ToastType } from 'src/components/MessageToasts/types';
-import { Dataset } from '@superset-ui/chart-controls';
-import { Query, QueryResponse } from '@superset-ui/core';
 import { ExploreRootState } from 'src/explore/types';
 
 export type ExploreDatasource = Dataset | QueryResponse;
@@ -68,7 +68,10 @@ export type SqlLabRootState = {
   };
   localStorageUsageInKilobytes: number;
   messageToasts: toastState[];
-  common: {};
+  common: {
+    flash_messages: string[];
+    conf: JsonObject;
+  };
 };
 
 export type SqlLabExploreRootState = SqlLabRootState | ExploreRootState;
@@ -96,6 +99,7 @@ export const EXPLORE_CHART_DEFAULT = {
   metrics: [],
   groupby: [],
   time_range: 'No filter',
+  row_limit: 1000,
 };
 
 export interface DatasetOwner {
diff --git a/superset-frontend/src/addSlice/AddSliceContainer.test.tsx 
b/superset-frontend/src/addSlice/AddSliceContainer.test.tsx
index f05a349ad7..bc58e925e4 100644
--- a/superset-frontend/src/addSlice/AddSliceContainer.test.tsx
+++ b/superset-frontend/src/addSlice/AddSliceContainer.test.tsx
@@ -104,7 +104,7 @@ test('renders an enabled button if datasource and viz type 
are selected', async
   const wrapper = await getWrapper();
   wrapper.setState({
     datasource,
-    visType: 'table',
+    vizType: 'table',
   });
   expect(
     wrapper.find(Button).find({ disabled: true }).hostNodes(),
@@ -125,7 +125,7 @@ test('double-click viz type submits if datasource is 
selected', async () => {
   wrapper.update();
   wrapper.setState({
     datasource,
-    visType: 'table',
+    vizType: 'table',
   });
 
   wrapper.instance().onVizTypeDoubleClick();
@@ -136,9 +136,8 @@ test('formats Explore url', async () => {
   const wrapper = await getWrapper();
   wrapper.setState({
     datasource,
-    visType: 'table',
+    vizType: 'table',
   });
-  const formattedUrl =
-    
'/superset/explore/?form_data=%7B%22viz_type%22%3A%22table%22%2C%22datasource%22%3A%221%22%7D';
+  const formattedUrl = '/superset/explore/?viz_type=table&datasource=1';
   expect(wrapper.instance().exploreUrl()).toBe(formattedUrl);
 });
diff --git a/superset-frontend/src/addSlice/AddSliceContainer.tsx 
b/superset-frontend/src/addSlice/AddSliceContainer.tsx
index a99c75e2af..71981fdef9 100644
--- a/superset-frontend/src/addSlice/AddSliceContainer.tsx
+++ b/superset-frontend/src/addSlice/AddSliceContainer.tsx
@@ -45,7 +45,7 @@ export type AddSliceContainerProps = {
 
 export type AddSliceContainerState = {
   datasource?: { label: string; value: string };
-  visType: string | null;
+  vizType: string | null;
   canCreateDataset: boolean;
 };
 
@@ -208,7 +208,7 @@ export default class AddSliceContainer extends 
React.PureComponent<
   constructor(props: AddSliceContainerProps) {
     super(props);
     this.state = {
-      visType: null,
+      vizType: null,
       canCreateDataset: findPermission(
         'can_write',
         'Dataset',
@@ -217,7 +217,7 @@ export default class AddSliceContainer extends 
React.PureComponent<
     };
 
     this.changeDatasource = this.changeDatasource.bind(this);
-    this.changeVisType = this.changeVisType.bind(this);
+    this.changeVizType = this.changeVizType.bind(this);
     this.gotoSlice = this.gotoSlice.bind(this);
     this.newLabel = this.newLabel.bind(this);
     this.loadDatasources = this.loadDatasources.bind(this);
@@ -226,14 +226,11 @@ export default class AddSliceContainer extends 
React.PureComponent<
 
   exploreUrl() {
     const dashboardId = getUrlParam(URL_PARAMS.dashboardId);
-    const formData = encodeURIComponent(
-      JSON.stringify({
-        viz_type: this.state.visType,
-        datasource: this.state.datasource?.value,
-        ...(!isNullish(dashboardId) && { dashboardId }),
-      }),
-    );
-    return `/superset/explore/?form_data=${formData}`;
+    let url = 
`/superset/explore/?viz_type=${this.state.vizType}&datasource=${this.state.datasource?.value}`;
+    if (!isNullish(dashboardId)) {
+      url += `&dashboard_id=${dashboardId}`;
+    }
+    return url;
   }
 
   gotoSlice() {
@@ -244,12 +241,12 @@ export default class AddSliceContainer extends 
React.PureComponent<
     this.setState({ datasource });
   }
 
-  changeVisType(visType: string | null) {
-    this.setState({ visType });
+  changeVizType(vizType: string | null) {
+    this.setState({ vizType });
   }
 
   isBtnDisabled() {
-    return !(this.state.datasource?.value && this.state.visType);
+    return !(this.state.datasource?.value && this.state.vizType);
   }
 
   onVizTypeDoubleClick() {
@@ -369,14 +366,14 @@ export default class AddSliceContainer extends 
React.PureComponent<
           />
           <Steps.Step
             title={<StyledStepTitle>{t('Choose chart type')}</StyledStepTitle>}
-            status={this.state.visType ? 'finish' : 'process'}
+            status={this.state.vizType ? 'finish' : 'process'}
             description={
               <StyledStepDescription>
                 <VizTypeGallery
                   className="viz-gallery"
-                  onChange={this.changeVisType}
+                  onChange={this.changeVizType}
                   onDoubleClick={this.onVizTypeDoubleClick}
-                  selectedViz={this.state.visType}
+                  selectedViz={this.state.vizType}
                 />
               </StyledStepDescription>
             }
diff --git a/superset-frontend/src/components/Chart/chartReducer.ts 
b/superset-frontend/src/components/Chart/chartReducer.ts
index 010140584c..11b498290f 100644
--- a/superset-frontend/src/components/Chart/chartReducer.ts
+++ b/superset-frontend/src/components/Chart/chartReducer.ts
@@ -22,6 +22,7 @@ import { HYDRATE_DASHBOARD } from 
'src/dashboard/actions/hydrate';
 import { DatasourcesAction } from 'src/dashboard/actions/datasources';
 import { ChartState } from 'src/explore/types';
 import { getFormDataFromControls } from 'src/explore/controlUtils';
+import { HYDRATE_EXPLORE } from 'src/explore/actions/hydrateExplore';
 import { now } from 'src/utils/dates';
 import * as actions from './chartAction';
 
@@ -194,7 +195,7 @@ export default function chartReducer(
     delete charts[key];
     return charts;
   }
-  if (action.type === HYDRATE_DASHBOARD) {
+  if (action.type === HYDRATE_DASHBOARD || action.type === HYDRATE_EXPLORE) {
     return { ...action.data.charts };
   }
   if (action.type === DatasourcesAction.SET_DATASOURCES) {
diff --git a/superset-frontend/src/constants.ts 
b/superset-frontend/src/constants.ts
index 60668ddcb8..84e809532e 100644
--- a/superset-frontend/src/constants.ts
+++ b/superset-frontend/src/constants.ts
@@ -91,6 +91,10 @@ export const URL_PARAMS = {
     name: 'permalink_key',
     type: 'string',
   },
+  vizType: {
+    name: 'viz_type',
+    type: 'string',
+  },
 } as const;
 
 export const RESERVED_CHART_URL_PARAMS: string[] = [
diff --git a/superset-frontend/src/explore/App.jsx 
b/superset-frontend/src/explore/App.jsx
index c440b784e7..995cf3d9e9 100644
--- a/superset-frontend/src/explore/App.jsx
+++ b/superset-frontend/src/explore/App.jsx
@@ -27,10 +27,10 @@ import { DynamicPluginProvider } from 
'src/components/DynamicPlugins';
 import ToastContainer from 'src/components/MessageToasts/ToastContainer';
 import setupApp from 'src/setup/setupApp';
 import setupPlugins from 'src/setup/setupPlugins';
+import { theme } from 'src/preamble';
+import { ExplorePage } from './ExplorePage';
 import './main.less';
 import '../assets/stylesheets/reactable-pagination.less';
-import { theme } from 'src/preamble';
-import ExploreViewContainer from './components/ExploreViewContainer';
 
 setupApp();
 setupPlugins();
@@ -41,7 +41,7 @@ const App = ({ store }) => (
       <ThemeProvider theme={theme}>
         <GlobalStyles />
         <DynamicPluginProvider>
-          <ExploreViewContainer />
+          <ExplorePage />
           <ToastContainer />
         </DynamicPluginProvider>
       </ThemeProvider>
diff --git a/superset-frontend/src/explore/ExplorePage.tsx 
b/superset-frontend/src/explore/ExplorePage.tsx
new file mode 100644
index 0000000000..50982924ee
--- /dev/null
+++ b/superset-frontend/src/explore/ExplorePage.tsx
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { useEffect, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { makeApi, t } from '@superset-ui/core';
+import Loading from 'src/components/Loading';
+import { getParsedExploreURLParams } from 
'./exploreUtils/getParsedExploreURLParams';
+import { hydrateExplore } from './actions/hydrateExplore';
+import ExploreViewContainer from './components/ExploreViewContainer';
+import { ExploreResponsePayload } from './types';
+import { fallbackExploreInitialData } from './fixtures';
+import { addDangerToast } from '../components/MessageToasts/actions';
+import { isNullish } from '../utils/common';
+
+const loadErrorMessage = t('Failed to load chart data.');
+
+const fetchExploreData = () => {
+  const exploreUrlParams = getParsedExploreURLParams();
+  return makeApi<{}, ExploreResponsePayload>({
+    method: 'GET',
+    endpoint: 'api/v1/explore/',
+  })(exploreUrlParams);
+};
+
+export const ExplorePage = () => {
+  const [isLoaded, setIsLoaded] = useState(false);
+  const dispatch = useDispatch();
+
+  useEffect(() => {
+    fetchExploreData()
+      .then(({ result }) => {
+        if (isNullish(result.dataset?.id) && isNullish(result.dataset?.uid)) {
+          dispatch(hydrateExplore(fallbackExploreInitialData));
+          dispatch(addDangerToast(loadErrorMessage));
+        } else {
+          dispatch(hydrateExplore(result));
+        }
+      })
+      .catch(() => {
+        dispatch(hydrateExplore(fallbackExploreInitialData));
+        dispatch(addDangerToast(loadErrorMessage));
+      })
+      .finally(() => {
+        setIsLoaded(true);
+      });
+  }, [dispatch]);
+
+  if (!isLoaded) {
+    return <Loading />;
+  }
+  return <ExploreViewContainer />;
+};
diff --git a/superset-frontend/src/explore/actions/datasourcesActions.ts 
b/superset-frontend/src/explore/actions/datasourcesActions.ts
index baf6f75ccc..4fc3bce96a 100644
--- a/superset-frontend/src/explore/actions/datasourcesActions.ts
+++ b/superset-frontend/src/explore/actions/datasourcesActions.ts
@@ -20,7 +20,7 @@
 import { Dispatch } from 'redux';
 import { Dataset } from '@superset-ui/chart-controls';
 import { updateFormDataByDatasource } from './exploreActions';
-import { ExplorePageState } from '../reducers/getInitialState';
+import { ExplorePageState } from '../types';
 
 export const SET_DATASOURCE = 'SET_DATASOURCE';
 export interface SetDatasource {
diff --git a/superset-frontend/src/explore/actions/hydrateExplore.test.ts 
b/superset-frontend/src/explore/actions/hydrateExplore.test.ts
new file mode 100644
index 0000000000..9cc7b883e9
--- /dev/null
+++ b/superset-frontend/src/explore/actions/hydrateExplore.test.ts
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { hydrateExplore, HYDRATE_EXPLORE } from './hydrateExplore';
+import { exploreInitialData } from '../fixtures';
+
+test('creates hydrate action from initial data', () => {
+  const dispatch = jest.fn();
+  const getState = jest.fn(() => ({
+    user: {},
+    charts: {},
+    datasources: {},
+    common: {},
+    explore: {},
+  }));
+  // ignore type check - we dont need exact explore state for this test
+  // @ts-ignore
+  hydrateExplore(exploreInitialData)(dispatch, getState);
+  expect(dispatch).toHaveBeenCalledWith(
+    expect.objectContaining({
+      type: HYDRATE_EXPLORE,
+      data: {
+        charts: {
+          371: {
+            id: 371,
+            chartAlert: null,
+            chartStatus: null,
+            chartStackTrace: null,
+            chartUpdateEndTime: null,
+            chartUpdateStartTime: 0,
+            latestQueryFormData: {
+              cache_timeout: undefined,
+              datasource: '8__table',
+              slice_id: 371,
+              url_params: undefined,
+              viz_type: 'table',
+            },
+            sliceFormData: {
+              cache_timeout: undefined,
+              datasource: '8__table',
+              slice_id: 371,
+              url_params: undefined,
+              viz_type: 'table',
+            },
+            queryController: null,
+            queriesResponse: null,
+            triggerQuery: false,
+            lastRendered: 0,
+          },
+        },
+        datasources: {
+          '8__table': exploreInitialData.dataset,
+        },
+        saveModal: {
+          dashboards: [],
+          saveModalAlert: null,
+        },
+        explore: {
+          can_add: false,
+          can_download: false,
+          can_overwrite: false,
+          isDatasourceMetaLoading: false,
+          isStarred: false,
+          triggerRender: false,
+          datasource: exploreInitialData.dataset,
+          controls: expect.any(Object),
+          form_data: exploreInitialData.form_data,
+          slice: exploreInitialData.slice,
+          controlsTransferred: [],
+          standalone: null,
+          force: null,
+        },
+      },
+    }),
+  );
+});
diff --git a/superset-frontend/src/explore/actions/hydrateExplore.ts 
b/superset-frontend/src/explore/actions/hydrateExplore.ts
new file mode 100644
index 0000000000..ec49c0cdd5
--- /dev/null
+++ b/superset-frontend/src/explore/actions/hydrateExplore.ts
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { ControlStateMapping } from '@superset-ui/chart-controls';
+
+import {
+  ChartState,
+  ExplorePageInitialData,
+  ExplorePageState,
+} from 'src/explore/types';
+import { getChartKey } from 'src/explore/exploreUtils';
+import { getControlsState } from 'src/explore/store';
+import { Dispatch } from 'redux';
+import { ensureIsArray } from '@superset-ui/core';
+import {
+  getFormDataFromControls,
+  applyMapStateToPropsToControl,
+} from 'src/explore/controlUtils';
+import { getDatasourceUid } from 'src/utils/getDatasourceUid';
+import { getUrlParam } from 'src/utils/urlUtils';
+import { URL_PARAMS } from 'src/constants';
+import { findPermission } from 'src/utils/findPermission';
+
+export const HYDRATE_EXPLORE = 'HYDRATE_EXPLORE';
+export const hydrateExplore =
+  ({ form_data, slice, dataset }: ExplorePageInitialData) =>
+  (dispatch: Dispatch, getState: () => ExplorePageState) => {
+    const { user, datasources, charts, sliceEntities, common } = getState();
+
+    const sliceId = getUrlParam(URL_PARAMS.sliceId);
+    const dashboardId = getUrlParam(URL_PARAMS.dashboardId);
+    const fallbackSlice = sliceId ? sliceEntities?.slices?.[sliceId] : null;
+    const initialSlice = slice ?? fallbackSlice;
+    const initialFormData = form_data ?? initialSlice?.form_data;
+    if (!initialFormData.viz_type) {
+      const defaultVizType = common?.conf.DEFAULT_VIZ_TYPE || 'table';
+      initialFormData.viz_type =
+        getUrlParam(URL_PARAMS.vizType) || defaultVizType;
+    }
+    if (dashboardId) {
+      initialFormData.dashboardId = dashboardId;
+    }
+    const initialDatasource =
+      datasources?.[initialFormData.datasource] ?? dataset;
+
+    const initialExploreState = {
+      form_data: initialFormData,
+      slice: initialSlice,
+      datasource: initialDatasource,
+    };
+    const initialControls = getControlsState(
+      initialExploreState,
+      initialFormData,
+    ) as ControlStateMapping;
+
+    const exploreState = {
+      // note this will add `form_data` to state,
+      // which will be manipulable by future reducers.
+      can_add: findPermission('can_write', 'Chart', user?.roles),
+      can_download: findPermission('can_csv', 'Superset', user?.roles),
+      can_overwrite: ensureIsArray(slice?.owners).includes(
+        user?.userId as number,
+      ),
+      isDatasourceMetaLoading: false,
+      isStarred: false,
+      triggerRender: false,
+      // duplicate datasource in exploreState - it's needed by getControlsState
+      datasource: initialDatasource,
+      // Initial control state will skip `control.mapStateToProps`
+      // because `bootstrapData.controls` is undefined.
+      controls: initialControls,
+      form_data: initialFormData,
+      slice: initialSlice,
+      controlsTransferred: [],
+      standalone: getUrlParam(URL_PARAMS.standalone),
+      force: getUrlParam(URL_PARAMS.force),
+    };
+
+    // apply initial mapStateToProps for all controls, must execute AFTER
+    // bootstrapState has initialized `controls`. Order of execution is not
+    // guaranteed, so controls shouldn't rely on each other's mapped state.
+    Object.entries(exploreState.controls).forEach(([key, controlState]) => {
+      exploreState.controls[key] = applyMapStateToPropsToControl(
+        controlState,
+        exploreState,
+      );
+    });
+    const sliceFormData = initialSlice
+      ? getFormDataFromControls(initialControls)
+      : null;
+
+    const chartKey: number = getChartKey(initialExploreState);
+    const chart: ChartState = {
+      id: chartKey,
+      chartAlert: null,
+      chartStatus: null,
+      chartStackTrace: null,
+      chartUpdateEndTime: null,
+      chartUpdateStartTime: 0,
+      latestQueryFormData: getFormDataFromControls(exploreState.controls),
+      sliceFormData,
+      queryController: null,
+      queriesResponse: null,
+      triggerQuery: false,
+      lastRendered: 0,
+    };
+
+    return dispatch({
+      type: HYDRATE_EXPLORE,
+      data: {
+        charts: {
+          ...charts,
+          [chartKey]: chart,
+        },
+        datasources: {
+          ...datasources,
+          [getDatasourceUid(initialDatasource)]: initialDatasource,
+        },
+        saveModal: {
+          dashboards: [],
+          saveModalAlert: null,
+        },
+        explore: exploreState,
+      },
+    });
+  };
+
+export type HydrateExplore = {
+  type: typeof HYDRATE_EXPLORE;
+  data: ExplorePageState;
+};
diff --git 
a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx 
b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
index a02917e2fc..4160fbbd89 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -54,8 +54,7 @@ import Loading from 'src/components/Loading';
 import { usePrevious } from 'src/hooks/usePrevious';
 import { getSectionsToRender } from 'src/explore/controlUtils';
 import { ExploreActions } from 'src/explore/actions/exploreActions';
-import { ExplorePageState } from 'src/explore/reducers/getInitialState';
-import { ChartState } from 'src/explore/types';
+import { ChartState, ExplorePageState } from 'src/explore/types';
 import { Tooltip } from 'src/components/Tooltip';
 
 import { rgba } from 'emotion-rgba';
diff --git 
a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
 
b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
index 4b19c5b2f3..a9495175d9 100644
--- 
a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
+++ 
b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx
@@ -47,7 +47,9 @@ const datasource = {
   main_dttm_col: 'None',
   datasource_name: 'table1',
   description: 'desc',
-  owners: [{ username: 'admin', userId: 1 }],
+  owners: [
+    { first_name: 'admin', last_name: 'admin', username: 'admin', id: 1 },
+  ],
 };
 
 const mockUser = {
diff --git 
a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx 
b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
index d8d2662371..0155e3c378 100644
--- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
+++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
@@ -694,17 +694,9 @@ function ExploreViewContainer(props) {
 ExploreViewContainer.propTypes = propTypes;
 
 function mapStateToProps(state) {
-  const {
-    explore,
-    charts,
-    common,
-    impressionId,
-    dataMask,
-    reports,
-    datasources,
-    user,
-  } = state;
-  const { controls, slice } = explore;
+  const { explore, charts, common, impressionId, dataMask, reports, user } =
+    state;
+  const { controls, slice, datasource } = explore;
   const form_data = getFormDataFromControls(controls);
   const slice_id = form_data.slice_id ?? slice?.slice_id ?? 0; // 0 - unsaved 
chart
   form_data.extra_form_data = mergeExtraFormData(
@@ -720,8 +712,6 @@ function mapStateToProps(state) {
     dashboardId = undefined;
   }
 
-  const datasource = datasources[form_data.datasource];
-
   return {
     isDatasourceMetaLoading: explore.isDatasourceMetaLoading,
     datasource,
diff --git 
a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx 
b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
index 3e559c4bbf..254ebe0902 100644
--- 
a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
+++ 
b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
@@ -237,7 +237,7 @@ class DatasourceControl extends React.PureComponent {
     const isSqlSupported = datasource.type === 'table';
     const { user } = this.props;
     const allowEdit = datasource.owners
-      .map(o => o.id || o.value)
+      ?.map(o => o.id || o.value)
       .includes(user.userId);
     isUserAdmin(user);
 
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
 
b/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
index ce19ba2fa4..c31b632b5f 100644
--- 
a/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
+++ 
b/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
@@ -29,8 +29,8 @@ import { css, SupersetTheme, t, useTheme } from 
'@superset-ui/core';
 import { usePluginContext } from 'src/components/DynamicPlugins';
 import { Tooltip } from 'src/components/Tooltip';
 import Icons from 'src/components/Icons';
-import { ExplorePageState } from 'src/explore/reducers/getInitialState';
 import { getChartKey } from 'src/explore/exploreUtils';
+import { ExplorePageState } from 'src/explore/types';
 
 export interface VizMeta {
   icon: ReactElement;
diff --git 
a/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.test.ts 
b/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.test.ts
new file mode 100644
index 0000000000..8d5a8ee09c
--- /dev/null
+++ 
b/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.test.ts
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { getParsedExploreURLParams } from './getParsedExploreURLParams';
+
+const EXPLORE_BASE_URL = 'http://localhost:9000/superset/explore/';
+const setupLocation = (newUrl: string) => {
+  delete (window as any).location;
+  // @ts-ignore
+  window.location = new URL(newUrl);
+};
+
+test('get form_data_key and slice_id from search params - url when moving from 
dashboard to explore', () => {
+  setupLocation(
+    
`${EXPLORE_BASE_URL}?form_data_key=yrLXmyE9fmhQ11lM1KgaD1PoPSBpuLZIJfqdyIdw9GoBwhPFRZHeIgeFiNZljbpd&slice_id=56`,
+  );
+  expect(getParsedExploreURLParams().toString()).toEqual(
+    
'slice_id=56&form_data_key=yrLXmyE9fmhQ11lM1KgaD1PoPSBpuLZIJfqdyIdw9GoBwhPFRZHeIgeFiNZljbpd',
+  );
+});
+
+test('get slice_id from form_data search param - url on Chart List', () => {
+  setupLocation(`${EXPLORE_BASE_URL}?form_data=%7B%22slice_id%22%3A%2056%7D`);
+  expect(getParsedExploreURLParams().toString()).toEqual('slice_id=56');
+});
+
+test('get datasource and viz type from form_data search param - url when 
creating new chart', () => {
+  setupLocation(
+    
`${EXPLORE_BASE_URL}?form_data=%7B%22viz_type%22%3A%22big_number%22%2C%22datasource%22%3A%222__table%22%7D`,
+  );
+  expect(getParsedExploreURLParams().toString()).toEqual(
+    'viz_type=big_number&dataset_id=2&dataset_type=table',
+  );
+});
+
+test('get permalink key from path params', () => {
+  setupLocation(`${EXPLORE_BASE_URL}p/kpOqweaMY9R/`);
+  expect(getParsedExploreURLParams().toString()).toEqual(
+    'permalink_key=kpOqweaMY9R',
+  );
+});
+
+test('get dataset id from path params', () => {
+  setupLocation(`${EXPLORE_BASE_URL}table/42/`);
+  expect(getParsedExploreURLParams().toString()).toEqual('dataset_id=42');
+});
diff --git 
a/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.ts 
b/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.ts
new file mode 100644
index 0000000000..042b8f7f88
--- /dev/null
+++ b/superset-frontend/src/explore/exploreUtils/getParsedExploreURLParams.ts
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// mapping { url_param: v1_explore_request_param }
+const EXPLORE_URL_SEARCH_PARAMS = {
+  form_data: {
+    name: 'form_data',
+    parser: (formData: string) => {
+      const formDataObject = JSON.parse(formData);
+      if (formDataObject.datasource) {
+        const [dataset_id, dataset_type] =
+          formDataObject.datasource.split('__');
+        formDataObject.dataset_id = dataset_id;
+        formDataObject.dataset_type = dataset_type;
+        delete formDataObject.datasource;
+      }
+      return formDataObject;
+    },
+  },
+  slice_id: {
+    name: 'slice_id',
+  },
+  dataset_id: {
+    name: 'dataset_id',
+  },
+  dataset_type: {
+    name: 'dataset_type',
+  },
+  datasource: {
+    name: 'datasource',
+    parser: (datasource: string) => {
+      const [dataset_id, dataset_type] = datasource.split('__');
+      return { dataset_id, dataset_type };
+    },
+  },
+  form_data_key: {
+    name: 'form_data_key',
+  },
+  permalink_key: {
+    name: 'permalink_key',
+  },
+  viz_type: {
+    name: 'viz_type',
+  },
+  dashboard_id: {
+    name: 'dashboard_id',
+  },
+};
+
+const EXPLORE_URL_PATH_PARAMS = {
+  p: 'permalink_key', // permalink
+  table: 'dataset_id',
+};
+
+// search params can be placed in form_data object
+// we need to "flatten" the search params to use them with /v1/explore endpoint
+const getParsedExploreURLSearchParams = () => {
+  const urlSearchParams = new URLSearchParams(window.location.search);
+  return Object.keys(EXPLORE_URL_SEARCH_PARAMS).reduce((acc, currentParam) => {
+    const paramValue = urlSearchParams.get(currentParam);
+    if (paramValue === null) {
+      return acc;
+    }
+    let parsedParamValue;
+    try {
+      parsedParamValue =
+        EXPLORE_URL_SEARCH_PARAMS[currentParam].parser?.(paramValue) ??
+        paramValue;
+    } catch {
+      parsedParamValue = paramValue;
+    }
+    if (typeof parsedParamValue === 'object') {
+      return { ...acc, ...parsedParamValue };
+    }
+    return {
+      ...acc,
+      [EXPLORE_URL_SEARCH_PARAMS[currentParam].name]: parsedParamValue,
+    };
+  }, {});
+};
+
+// path params need to be transformed to search params to use them with 
/v1/explore endpoint
+const getParsedExploreURLPathParams = () =>
+  Object.keys(EXPLORE_URL_PATH_PARAMS).reduce((acc, currentParam) => {
+    const re = new RegExp(`/(${currentParam})/(\\w+)`);
+    const pathGroups = window.location.pathname.match(re);
+    if (pathGroups && pathGroups[2]) {
+      return { ...acc, [EXPLORE_URL_PATH_PARAMS[currentParam]]: pathGroups[2] 
};
+    }
+    return acc;
+  }, {});
+
+export const getParsedExploreURLParams = () =>
+  new URLSearchParams(
+    Object.entries({
+      ...getParsedExploreURLSearchParams(),
+      ...getParsedExploreURLPathParams(),
+    })
+      .map(entry => entry.join('='))
+      .join('&'),
+  );
diff --git a/superset-frontend/src/explore/exploreUtils/index.js 
b/superset-frontend/src/explore/exploreUtils/index.js
index 73d1fe088b..506de032e5 100644
--- a/superset-frontend/src/explore/exploreUtils/index.js
+++ b/superset-frontend/src/explore/exploreUtils/index.js
@@ -267,11 +267,12 @@ export const exportChart = ({
   SupersetClient.postForm(url, { form_data: safeStringify(payload) });
 };
 
-export const exploreChart = formData => {
+export const exploreChart = (formData, requestParams) => {
   const url = getExploreUrl({
     formData,
     endpointType: 'base',
     allowDomainSharding: false,
+    requestParams,
   });
   SupersetClient.postForm(url, { form_data: safeStringify(formData) });
 };
diff --git a/superset-frontend/src/explore/fixtures.tsx 
b/superset-frontend/src/explore/fixtures.tsx
index 78579b82e4..755985be2b 100644
--- a/superset-frontend/src/explore/fixtures.tsx
+++ b/superset-frontend/src/explore/fixtures.tsx
@@ -18,13 +18,14 @@
  */
 
 import React from 'react';
-import { t } from '@superset-ui/core';
+import { DatasourceType, t } from '@superset-ui/core';
 import {
   ColumnMeta,
   ColumnOption,
   ControlConfig,
   ControlPanelSectionConfig,
 } from '@superset-ui/chart-controls';
+import { ExplorePageInitialData } from './types';
 
 export const controlPanelSectionsChartOptions: (ControlPanelSectionConfig | 
null)[] =
   [
@@ -108,3 +109,59 @@ export const controlPanelSectionsChartOptionsTable: 
ControlPanelSectionConfig[]
       ],
     },
   ];
+
+export const exploreInitialData: ExplorePageInitialData = {
+  form_data: {
+    datasource: '8__table',
+    metric: 'count',
+    slice_id: 371,
+    time_range: 'No filter',
+    viz_type: 'table',
+  },
+  slice: {
+    cache_timeout: null,
+    description: null,
+    slice_id: 371,
+    slice_name: 'Age distribution of respondents',
+    is_managed_externally: false,
+    form_data: {
+      datasource: '8__table',
+      metric: 'count',
+      slice_id: 371,
+      time_range: 'No filter',
+      viz_type: 'table',
+    },
+  },
+  dataset: {
+    id: 8,
+    type: DatasourceType.Table,
+    columns: [{ column_name: 'a' }],
+    metrics: [{ metric_name: 'first' }, { metric_name: 'second' }],
+    column_format: {},
+    verbose_map: {},
+    main_dttm_col: '',
+    datasource_name: '8__table',
+    description: null,
+  },
+};
+
+export const fallbackExploreInitialData: ExplorePageInitialData = {
+  form_data: {
+    datasource: '0__table',
+    viz_type: 'table',
+  },
+  dataset: {
+    id: 0,
+    type: DatasourceType.Table,
+    columns: [],
+    metrics: [],
+    column_format: {},
+    verbose_map: {},
+    main_dttm_col: '',
+    owners: [],
+    datasource_name: 'missing_datasource',
+    name: 'missing_datasource',
+    description: null,
+  },
+  slice: null,
+};
diff --git a/superset-frontend/src/explore/index.jsx 
b/superset-frontend/src/explore/index.jsx
index cc99666e02..0af6b02747 100644
--- a/superset-frontend/src/explore/index.jsx
+++ b/superset-frontend/src/explore/index.jsx
@@ -20,10 +20,11 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import { createStore, applyMiddleware, compose } from 'redux';
 import thunk from 'redux-thunk';
-import logger from '../middleware/loggerMiddleware';
-import { initFeatureFlags } from '../featureFlags';
-import { initEnhancer } from '../reduxUtils';
-import getInitialState from './reducers/getInitialState';
+import shortid from 'shortid';
+import getToastsFromPyFlashMessages from 
'src/components/MessageToasts/getToastsFromPyFlashMessages';
+import logger from 'src/middleware/loggerMiddleware';
+import { initFeatureFlags } from 'src/featureFlags';
+import { initEnhancer } from 'src/reduxUtils';
 import rootReducer from './reducers/index';
 import App from './App';
 
@@ -31,11 +32,18 @@ const exploreViewContainer = document.getElementById('app');
 const bootstrapData = JSON.parse(
   exploreViewContainer.getAttribute('data-bootstrap'),
 );
-initFeatureFlags(bootstrapData.common.feature_flags);
-const initState = getInitialState(bootstrapData);
+
+const user = { ...bootstrapData.user };
+const common = { ...bootstrapData.common };
+initFeatureFlags(common.feature_flags);
 const store = createStore(
   rootReducer,
-  initState,
+  {
+    user,
+    common,
+    impressionId: shortid.generate(),
+    messageToasts: getToastsFromPyFlashMessages(common?.flash_messages || []),
+  },
   compose(applyMiddleware(thunk, logger), initEnhancer(false)),
 );
 
diff --git a/superset-frontend/src/explore/reducers/datasourcesReducer.ts 
b/superset-frontend/src/explore/reducers/datasourcesReducer.ts
index e3050feea0..50393dbd24 100644
--- a/superset-frontend/src/explore/reducers/datasourcesReducer.ts
+++ b/superset-frontend/src/explore/reducers/datasourcesReducer.ts
@@ -22,11 +22,12 @@ import {
   AnyDatasourcesAction,
   SET_DATASOURCE,
 } from '../actions/datasourcesActions';
+import { HYDRATE_EXPLORE, HydrateExplore } from '../actions/hydrateExplore';
 
 export default function datasourcesReducer(
   // TODO: change type to include other datasource types
   datasources: { [key: string]: Dataset },
-  action: AnyDatasourcesAction,
+  action: AnyDatasourcesAction | HydrateExplore,
 ) {
   if (action.type === SET_DATASOURCE) {
     return {
@@ -34,5 +35,8 @@ export default function datasourcesReducer(
       [getDatasourceUid(action.datasource)]: action.datasource,
     };
   }
+  if (action.type === HYDRATE_EXPLORE) {
+    return { ...(action as HydrateExplore).data.datasources };
+  }
   return datasources || {};
 }
diff --git a/superset-frontend/src/explore/reducers/exploreReducer.js 
b/superset-frontend/src/explore/reducers/exploreReducer.js
index 706104b765..f99ab9437c 100644
--- a/superset-frontend/src/explore/reducers/exploreReducer.js
+++ b/superset-frontend/src/explore/reducers/exploreReducer.js
@@ -28,6 +28,7 @@ import {
   StandardizedFormData,
 } from 'src/explore/controlUtils';
 import * as actions from 'src/explore/actions/exploreActions';
+import { HYDRATE_EXPLORE } from '../actions/hydrateExplore';
 
 export default function exploreReducer(state = {}, action) {
   const actionHandlers = {
@@ -247,8 +248,12 @@ export default function exploreReducer(state = {}, action) 
{
         force: action.force,
       };
     },
+    [HYDRATE_EXPLORE]() {
+      return {
+        ...action.data.explore,
+      };
+    },
   };
-
   if (action.type in actionHandlers) {
     return actionHandlers[action.type]();
   }
diff --git a/superset-frontend/src/explore/reducers/getInitialState.ts 
b/superset-frontend/src/explore/reducers/getInitialState.ts
deleted file mode 100644
index 4d4970237e..0000000000
--- a/superset-frontend/src/explore/reducers/getInitialState.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import shortid from 'shortid';
-import {
-  DatasourceType,
-  ensureIsArray,
-  JsonObject,
-  QueryFormData,
-} from '@superset-ui/core';
-import { ControlStateMapping, Dataset } from '@superset-ui/chart-controls';
-import {
-  CommonBootstrapData,
-  UserWithPermissionsAndRoles,
-} from 'src/types/bootstrapTypes';
-import getToastsFromPyFlashMessages from 
'src/components/MessageToasts/getToastsFromPyFlashMessages';
-
-import { ChartState, Slice } from 'src/explore/types';
-import { getChartKey } from 'src/explore/exploreUtils';
-import { getControlsState } from 'src/explore/store';
-import {
-  getFormDataFromControls,
-  applyMapStateToPropsToControl,
-} from 'src/explore/controlUtils';
-import { findPermission } from 'src/utils/findPermission';
-import { getDatasourceUid } from 'src/utils/getDatasourceUid';
-import { getUrlParam } from 'src/utils/urlUtils';
-import { URL_PARAMS } from 'src/constants';
-
-export interface ExplorePageBootstrapData extends JsonObject {
-  can_add: boolean;
-  can_download: boolean;
-  can_overwrite: boolean;
-  common: CommonBootstrapData;
-  datasource: Dataset;
-  datasource_id: number;
-  datasource_type: DatasourceType;
-  forced_height: string | null;
-  form_data: QueryFormData;
-  slice: Slice | null;
-  standalone: boolean;
-  force: boolean;
-  user: UserWithPermissionsAndRoles;
-}
-
-export default function getInitialState(
-  bootstrapData: ExplorePageBootstrapData,
-) {
-  const {
-    form_data: initialFormData,
-    common,
-    user,
-    datasource,
-    slice,
-  } = bootstrapData;
-
-  const exploreState = {
-    // note this will add `form_data` to state,
-    // which will be manipulatable by future reducers.
-    can_add: findPermission('can_write', 'Chart', user?.roles),
-    can_download: findPermission('can_csv', 'Superset', user?.roles),
-    can_overwrite: ensureIsArray(slice?.owners).includes(
-      user?.userId as number,
-    ),
-    isDatasourceMetaLoading: false,
-    isStarred: false,
-    triggerRender: false,
-    // duplicate datasource in exploreState - it's needed by getControlsState
-    datasource,
-    // Initial control state will skip `control.mapStateToProps`
-    // because `bootstrapData.controls` is undefined.
-    controls: getControlsState(
-      bootstrapData,
-      initialFormData,
-    ) as ControlStateMapping,
-    form_data: initialFormData,
-    slice,
-    controlsTransferred: [],
-    standalone: getUrlParam(URL_PARAMS.standalone),
-    force: getUrlParam(URL_PARAMS.force),
-  };
-
-  // apply initial mapStateToProps for all controls, must execute AFTER
-  // bootstrapState has initialized `controls`. Order of execution is not
-  // guaranteed, so controls shouldn't rely on each other's mapped state.
-  Object.entries(exploreState.controls).forEach(([key, controlState]) => {
-    exploreState.controls[key] = applyMapStateToPropsToControl(
-      controlState,
-      exploreState,
-    );
-  });
-  const sliceFormData = slice
-    ? getFormDataFromControls(getControlsState(bootstrapData, slice.form_data))
-    : null;
-
-  const chartKey: number = getChartKey(bootstrapData);
-  const chart: ChartState = {
-    id: chartKey,
-    chartAlert: null,
-    chartStatus: null,
-    chartStackTrace: null,
-    chartUpdateEndTime: null,
-    chartUpdateStartTime: 0,
-    latestQueryFormData: getFormDataFromControls(exploreState.controls),
-    sliceFormData,
-    queryController: null,
-    queriesResponse: null,
-    triggerQuery: false,
-    lastRendered: 0,
-  };
-
-  return {
-    common: common || {},
-    user: user || {},
-    charts: {
-      [chartKey]: chart,
-    },
-    datasources: { [getDatasourceUid(datasource)]: datasource },
-    saveModal: {
-      dashboards: [],
-      saveModalAlert: null,
-    },
-    explore: exploreState,
-    impressionId: shortid.generate(),
-    messageToasts: getToastsFromPyFlashMessages(
-      (bootstrapData.common || {}).flash_messages || [],
-    ),
-  };
-}
-
-export type ExplorePageState = ReturnType<typeof getInitialState>;
diff --git a/superset-frontend/src/explore/reducers/saveModalReducer.js 
b/superset-frontend/src/explore/reducers/saveModalReducer.js
index eee4197991..85d4cf0e32 100644
--- a/superset-frontend/src/explore/reducers/saveModalReducer.js
+++ b/superset-frontend/src/explore/reducers/saveModalReducer.js
@@ -18,6 +18,7 @@
  */
 /* eslint camelcase: 0 */
 import * as actions from '../actions/saveModalActions';
+import { HYDRATE_EXPLORE } from '../actions/hydrateExplore';
 
 export default function saveModalReducer(state = {}, action) {
   const actionHandlers = {
@@ -39,6 +40,9 @@ export default function saveModalReducer(state = {}, action) {
     [actions.REMOVE_SAVE_MODAL_ALERT]() {
       return { ...state, saveModalAlert: null };
     },
+    [HYDRATE_EXPLORE]() {
+      return { ...action.data.saveModal };
+    },
   };
 
   if (action.type in actionHandlers) {
diff --git a/superset-frontend/src/explore/store.js 
b/superset-frontend/src/explore/store.js
index 80ad75e3e5..8bd0477087 100644
--- a/superset-frontend/src/explore/store.js
+++ b/superset-frontend/src/explore/store.js
@@ -42,7 +42,7 @@ export function getControlsState(state, inputFormData) {
   // Getting a list of active control names for the current viz
   const formData = { ...inputFormData };
   const vizType =
-    formData.viz_type || state.common.conf.DEFAULT_VIZ_TYPE || 'table';
+    formData.viz_type || state.common?.conf.DEFAULT_VIZ_TYPE || 'table';
 
   handleDeprecatedControls(formData);
 
diff --git a/superset-frontend/src/explore/types.ts 
b/superset-frontend/src/explore/types.ts
index 4d50b449c5..85b161058a 100644
--- a/superset-frontend/src/explore/types.ts
+++ b/superset-frontend/src/explore/types.ts
@@ -21,13 +21,17 @@ import {
   QueryFormData,
   AnnotationData,
   AdhocMetric,
+  JsonObject,
 } from '@superset-ui/core';
-import { ColumnMeta, Dataset } from '@superset-ui/chart-controls';
+import {
+  ColumnMeta,
+  ControlStateMapping,
+  Dataset,
+} from '@superset-ui/chart-controls';
 import { DatabaseObject } from 'src/views/CRUD/types';
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import { toastState } from 'src/SqlLab/types';
-
-export { Slice, Chart } from 'src/types/Chart';
+import { Slice } from 'src/types/Chart';
 
 export type ChartStatus =
   | 'loading'
@@ -90,3 +94,40 @@ export type ExploreRootState = {
   messageToasts: toastState[];
   common: {};
 };
+
+export interface ExplorePageInitialData {
+  dataset: Dataset;
+  form_data: QueryFormData;
+  slice: Slice | null;
+}
+
+export interface ExploreResponsePayload {
+  result: ExplorePageInitialData & { message: string };
+}
+
+export interface ExplorePageState {
+  user: UserWithPermissionsAndRoles;
+  common: {
+    flash_messages: string[];
+    conf: JsonObject;
+  };
+  charts: { [key: number]: ChartState };
+  datasources: { [key: string]: Dataset };
+  explore: {
+    can_add: boolean;
+    can_download: boolean;
+    can_overwrite: boolean;
+    isDatasourceMetaLoading: boolean;
+    isStarred: boolean;
+    triggerRender: boolean;
+    // duplicate datasource in exploreState - it's needed by getControlsState
+    datasource: Dataset;
+    controls: ControlStateMapping;
+    form_data: QueryFormData;
+    slice: Slice;
+    controlsTransferred: string[];
+    standalone: boolean;
+    force: boolean;
+  };
+  sliceEntities?: JsonObject; // propagated from Dashboard view
+}

Reply via email to