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 86f146e217 feat(explore): Implement viz switcher redesign (#20248)
86f146e217 is described below

commit 86f146e217ddb7c2ebd499acddaa5e8c3b3ab560
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Tue Jun 14 15:20:54 2022 +0200

    feat(explore): Implement viz switcher redesign (#20248)
    
    * add icons
    
    * Implement fast viz switcher component
    
    * Remove unnecessary keys from ControlsPanelContainer
    
    * Rename icons
    
    * Add unit tests
    
    * Add licenses
    
    * Fix test
    
    * Change BigNumberWithTrendline to BigNumber
    
    * fix test
    
    * fix test
    
    * Add currently rendered viz tile
    
    * Move View all charts to the right side
    
    * Add license
    
    * Fix imports
    
    * Fix e2e test
---
 .../cypress/integration/explore/control.test.ts    |  10 +-
 .../src/assets/images/icons/area-chart-tile.svg    |  21 ++
 .../src/assets/images/icons/bar-chart-tile.svg     |  21 ++
 .../assets/images/icons/big-number-chart-tile.svg  |  22 ++
 .../assets/images/icons/current-rendered-tile.svg  |  21 ++
 .../src/assets/images/icons/line-chart-tile.svg    |  21 ++
 .../src/assets/images/icons/pie-chart-tile.svg     |  28 +++
 .../src/assets/images/icons/table-chart-tile.svg   |  28 +++
 superset-frontend/src/components/Icons/index.tsx   |   7 +
 .../explore/components/ControlPanelsContainer.tsx  |   2 -
 .../controls/VizTypeControl/FastVizSwitcher.tsx    | 253 +++++++++++++++++++++
 .../VizTypeControl/VizTypeControl.test.jsx         |   1 +
 .../VizTypeControl/VizTypeControl.test.tsx         | 193 +++++++++++++---
 .../components/controls/VizTypeControl/index.tsx   |  97 ++++----
 .../src/explore/controlPanels/sections.tsx         |   2 +-
 15 files changed, 644 insertions(+), 83 deletions(-)

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 e2aec6b7b9..6bc0840540 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
@@ -99,11 +99,13 @@ describe('VizType control', () => {
     cy.visitChartByName('Daily Totals');
     cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
 
-    cy.get('[data-test="visualization-type"]').contains('Table').click();
+    cy.contains('View all charts').click();
 
-    cy.get('button').contains('Evolution').click(); // change categories
-    cy.get('[role="button"]').contains('Line Chart').click();
-    cy.get('button').contains('Select').click();
+    cy.get('.ant-modal-content').within(() => {
+      cy.get('button').contains('Evolution').click(); // change categories
+      cy.get('[role="button"]').contains('Line Chart').click();
+      cy.get('button').contains('Select').click();
+    });
 
     cy.get('button[data-test="run-query-button"]').click();
     cy.verifySliceSuccess({
diff --git a/superset-frontend/src/assets/images/icons/area-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/area-chart-tile.svg
new file mode 100644
index 0000000000..dbd747d5e5
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/area-chart-tile.svg
@@ -0,0 +1,21 @@
+<!--
+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.
+-->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M18.7145 17.0006H6.42878V5.8577C6.42878 5.77913 6.3645 5.71484 
6.28592 5.71484H5.28592C5.20735 5.71484 5.14307 5.77913 5.14307 
5.8577V18.1434C5.14307 18.222 5.20735 18.2863 5.28592 18.2863H18.7145C18.7931 
18.2863 18.8574 18.222 18.8574 18.1434V17.1434C18.8574 17.0648 18.7931 17.0006 
18.7145 17.0006ZM7.7145 15.8577H17.2859C17.3645 15.8577 17.4288 15.7934 17.4288 
15.7148V7.92913C17.4288 7.80056 17.2734 7.73806 17.1841 7.82734L13.4288 
11.5827L11.1895 9.36842C11.1626 9.34183 11.1264 [...]
+</svg>
diff --git a/superset-frontend/src/assets/images/icons/bar-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/bar-chart-tile.svg
new file mode 100644
index 0000000000..a3aaa6fdd9
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/bar-chart-tile.svg
@@ -0,0 +1,21 @@
+<!--
+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.
+-->
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M19.5475 17.0006H7.26179V5.8577C7.26179 5.77913 7.1975 5.71484 
7.11893 5.71484H6.11893C6.04036 5.71484 5.97607 5.77913 5.97607 
5.8577V18.1434C5.97607 18.222 6.04036 18.2863 6.11893 18.2863H19.5475C19.6261 
18.2863 19.6904 18.222 19.6904 18.1434V17.1434C19.6904 17.0648 19.6261 17.0006 
19.5475 17.0006ZM8.83322 15.572H9.83322C9.91179 15.572 9.97608 15.5077 9.97608 
15.4291V12.8577C9.97608 12.7791 9.91179 12.7148 9.83322 12.7148H8.83322C8.75465 
12.7148 8.69036 12.7791 8.69036 12.8577V [...]
+</svg>
diff --git 
a/superset-frontend/src/assets/images/icons/big-number-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/big-number-chart-tile.svg
new file mode 100644
index 0000000000..cd4ac8f2f7
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/big-number-chart-tile.svg
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M4.5 15.0942V13.8545L8.88866 6.92188H9.86557V8.74676H9.2457L6.10669 
13.7156V13.795H12.1219V15.0942H4.5ZM9.31512 17.0778V14.7173L9.32504 
14.152V6.92188H10.778V17.0778H9.31512Z" fill="#666666"/>
+<path d="M15.2336 14.4942L15.2237 12.6842H15.4816L18.5164 
9.46085H20.2917L16.8304 13.1305H16.5973L15.2336 14.4942ZM13.8699 
17.0778V6.92188H15.3526V17.0778H13.8699ZM18.6801 17.0778L15.9527 
13.4577L16.9742 12.4213L20.5 17.0778H18.6801Z" fill="#666666"/>
+</svg>
diff --git 
a/superset-frontend/src/assets/images/icons/current-rendered-tile.svg 
b/superset-frontend/src/assets/images/icons/current-rendered-tile.svg
new file mode 100644
index 0000000000..78f63014e5
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/current-rendered-tile.svg
@@ -0,0 +1,21 @@
+<!--
+  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.
+-->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M18.5717 6.14509H15.5717V5.00223C15.5717 4.92366 15.5074 4.85938 
15.4289 4.85938H14.4289C14.3503 4.85938 14.286 4.92366 14.286 
5.00223V6.14509H9.71457V5.00223C9.71457 4.92366 9.65028 4.85938 9.57171 
4.85938H8.57171C8.49314 4.85938 8.42885 4.92366 8.42885 
5.00223V6.14509H5.42885C5.11278 6.14509 4.85742 6.40045 4.85742 
6.71652V18.5737C4.85742 18.8897 5.11278 19.1451 5.42885 19.1451H18.5717C18.8878 
19.1451 19.1431 18.8897 19.1431 18.5737V6.71652C19.1431 6.40045 18.8878 6.14509 
18.5 [...]
+</svg>
diff --git a/superset-frontend/src/assets/images/icons/line-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/line-chart-tile.svg
new file mode 100644
index 0000000000..c6b6b4e403
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/line-chart-tile.svg
@@ -0,0 +1,21 @@
+<!--
+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.
+-->
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M18.8805 17.0006H6.5948V5.8577C6.5948 5.77913 6.53051 5.71484 6.45194 
5.71484H5.45194C5.37337 5.71484 5.30908 5.77913 5.30908 5.8577V18.1434C5.30908 
18.222 5.37337 18.2863 5.45194 18.2863H18.8805C18.9591 18.2863 19.0234 18.222 
19.0234 18.1434V17.1434C19.0234 17.0648 18.9591 17.0006 18.8805 
17.0006ZM8.48408 14.2452C8.53944 14.3006 8.62873 14.3006 8.68587 
14.2452L11.1555 11.7881L13.4341 14.0809C13.4894 14.1363 13.5805 14.1363 13.6359 
14.0809L18.5537 9.16484C18.6091 9.10949 18.6091 [...]
+</svg>
diff --git a/superset-frontend/src/assets/images/icons/pie-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/pie-chart-tile.svg
new file mode 100644
index 0000000000..3bd3bf74df
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/pie-chart-tile.svg
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+    <g clip-path="url(#clip0_1714_70879)">
+        <path d="M18.952 12.1055H12.5591V5.7126C12.5591 5.63402 12.4948 
5.56974 12.4162 5.56974H11.952C11.0139 5.5682 10.0848 5.75216 9.21816 
6.11103C8.35148 6.4699 7.56431 6.99659 6.90195 7.66081C6.24965 8.3111 5.7299 
9.08193 5.37159 9.93045C4.99875 10.8105 4.80742 11.7568 4.80909 12.7126C4.80756 
13.6506 4.99151 14.5797 5.35038 15.4464C5.70925 16.3131 6.23595 17.1002 6.90016 
17.7626C7.55552 18.418 8.31981 18.934 9.16981 19.293C10.0499 19.6658 10.9962 
19.8571 11.952 19.8555C12.89 19.857  [...]
+    </g>
+    <defs>
+        <clipPath id="clip0_1714_70879">
+            <rect width="16" height="16" fill="white" 
transform="translate(4.6665 4)"/>
+        </clipPath>
+    </defs>
+</svg>
diff --git a/superset-frontend/src/assets/images/icons/table-chart-tile.svg 
b/superset-frontend/src/assets/images/icons/table-chart-tile.svg
new file mode 100644
index 0000000000..9a99419d55
--- /dev/null
+++ b/superset-frontend/src/assets/images/icons/table-chart-tile.svg
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<g clip-path="url(#clip0_1714_70889)">
+<path d="M19.7621 5.71094H4.90492C4.58885 5.71094 4.3335 5.96629 4.3335 
6.28237V17.7109C4.3335 18.027 4.58885 18.2824 4.90492 18.2824H19.7621C20.0781 
18.2824 20.3335 18.027 20.3335 17.7109V6.28237C20.3335 5.96629 20.0781 5.71094 
19.7621 5.71094ZM19.0478 9.42522H15.2621V6.99665H19.0478V9.42522ZM19.0478 
13.4252H15.2621V10.5681H19.0478V13.4252ZM10.5478 
10.5681H14.1192V13.4252H10.5478V10.5681ZM14.1192 
9.42522H10.5478V6.99665H14.1192V9.42522ZM5.61921 
10.5681H9.40492V13.4252H5.61921V10.5681ZM5 [...]
+</g>
+<defs>
+<clipPath id="clip0_1714_70889">
+<rect width="16" height="16" fill="white" transform="translate(4.3335 4)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/superset-frontend/src/components/Icons/index.tsx 
b/superset-frontend/src/components/Icons/index.tsx
index 27efbe4c2e..0761890e4c 100644
--- a/superset-frontend/src/components/Icons/index.tsx
+++ b/superset-frontend/src/components/Icons/index.tsx
@@ -27,6 +27,9 @@ const IconFileNames = [
   'alert',
   'alert_solid',
   'alert_solid_small',
+  'area-chart-tile',
+  'bar-chart-tile',
+  'big-number-chart-tile',
   'binoculars',
   'bolt',
   'bolt_small',
@@ -56,6 +59,7 @@ const IconFileNames = [
   'cog',
   'collapse',
   'color_palette',
+  'current-rendered-tile',
   'components',
   'copy',
   'cursor_target',
@@ -101,6 +105,7 @@ const IconFileNames = [
   'keyboard',
   'layers',
   'lightbulb',
+  'line-chart-tile',
   'link',
   'list',
   'list_view',
@@ -123,6 +128,7 @@ const IconFileNames = [
   'note',
   'offline',
   'paperclip',
+  'pie-chart-tile',
   'placeholder',
   'plus',
   'plus_large',
@@ -141,6 +147,7 @@ const IconFileNames = [
   'sort_desc',
   'sort',
   'table',
+  'table-chart-tile',
   'tag',
   'trash',
   'triangle_change',
diff --git 
a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx 
b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
index 6ed73f2b38..bb2be2b182 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -535,7 +535,6 @@ export const ControlPanelsContainer = (props: 
ControlPanelsContainerProps) => {
             defaultActiveKey={expandedQuerySections}
             expandIconPosition="right"
             ghost
-            
key={`query-sections-${props.form_data.datasource}-${props.form_data.viz_type}`}
           >
             {showDatasourceAlert && <DatasourceAlert />}
             {querySections.map(renderControlPanelSection)}
@@ -547,7 +546,6 @@ export const ControlPanelsContainer = (props: 
ControlPanelsContainerProps) => {
               defaultActiveKey={expandedCustomizeSections}
               expandIconPosition="right"
               ghost
-              
key={`customize-sections-${props.form_data.datasource}-${props.form_data.viz_type}`}
             >
               {customizeSections.map(renderControlPanelSection)}
             </Collapse>
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
 
b/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
new file mode 100644
index 0000000000..5b9df8021f
--- /dev/null
+++ 
b/superset-frontend/src/explore/components/controls/VizTypeControl/FastVizSwitcher.tsx
@@ -0,0 +1,253 @@
+/**
+ * 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, {
+  ReactElement,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from 'react';
+import { useSelector } from 'react-redux';
+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';
+
+export interface VizMeta {
+  icon: ReactElement;
+  name: string;
+}
+
+export interface FastVizSwitcherProps {
+  onChange: (vizName: string) => void;
+  currentSelection: string | null;
+}
+interface VizTileProps {
+  vizMeta: VizMeta;
+  isActive: boolean;
+  isRendered: boolean;
+  onTileClick: (vizType: string) => void;
+}
+
+const FEATURED_CHARTS: VizMeta[] = [
+  {
+    name: 'echarts_timeseries_line',
+    icon: <Icons.LineChartTile />,
+  },
+  { name: 'table', icon: <Icons.TableChartTile /> },
+  {
+    name: 'big_number_total',
+    icon: <Icons.BigNumberChartTile />,
+  },
+  { name: 'pie', icon: <Icons.PieChartTile /> },
+  {
+    name: 'echarts_timeseries_bar',
+    icon: <Icons.BarChartTile />,
+  },
+  { name: 'echarts_area', icon: <Icons.AreaChartTile /> },
+];
+
+const VizTile = ({
+  isActive,
+  isRendered,
+  vizMeta,
+  onTileClick,
+}: VizTileProps) => {
+  const { mountedPluginMetadata } = usePluginContext();
+  const chartNameRef = useRef<HTMLSpanElement>(null);
+  const theme = useTheme();
+  const TILE_TRANSITION_TIME = theme.transitionTiming * 2;
+  const [tooltipVisible, setTooltipVisible] = useState(false);
+  const [isTransitioning, setIsTransitioning] = useState(false);
+  const [showTooltip, setShowTooltip] = useState(false);
+  const chartName = vizMeta.name
+    ? mountedPluginMetadata[vizMeta.name]?.name || `${vizMeta.name}`
+    : t('Select Viz Type');
+
+  const handleTileClick = useCallback(() => {
+    onTileClick(vizMeta.name);
+    setIsTransitioning(true);
+    setTooltipVisible(false);
+    setTimeout(() => {
+      setIsTransitioning(false);
+    }, TILE_TRANSITION_TIME * 1000);
+  }, [onTileClick, TILE_TRANSITION_TIME, vizMeta.name]);
+
+  // Antd tooltip seems to be bugged - when elements move, the tooltip 
sometimes
+  // stays visible even when user doesn't hover over the element.
+  // Here we manually prevent it from displaying after user triggers transition
+  useEffect(() => {
+    setShowTooltip(
+      Boolean(
+        !isTransitioning &&
+          (!isActive ||
+            (chartNameRef.current &&
+              chartNameRef.current.scrollWidth >
+                chartNameRef.current.clientWidth)),
+      ),
+    );
+  }, [isActive, isTransitioning]);
+
+  const containerProps = useMemo(
+    () =>
+      !isActive
+        ? { role: 'button', tabIndex: 0, onClick: handleTileClick }
+        : {},
+    [handleTileClick, isActive],
+  );
+
+  let tooltipTitle: string | null = null;
+  if (showTooltip) {
+    tooltipTitle = isRendered
+      ? t('Currently rendered: %s', chartName)
+      : chartName;
+  }
+  return (
+    <Tooltip
+      title={tooltipTitle}
+      onVisibleChange={visible => setTooltipVisible(visible)}
+      visible={tooltipVisible && !isTransitioning}
+      placement="top"
+      mouseEnterDelay={0.4}
+    >
+      <div
+        {...containerProps}
+        css={css`
+          display: flex;
+          align-items: center;
+          text-transform: uppercase;
+
+          color: ${theme.colors.grayscale.base};
+          font-weight: ${theme.typography.weights.bold};
+          border-radius: 6px;
+          white-space: nowrap;
+          overflow: hidden;
+          max-width: fit-content;
+
+          ${!isActive &&
+          css`
+            flex-shrink: 0;
+            width: ${theme.gridUnit * 6}px;
+            background-color: transparent;
+            transition: none;
+            &:hover svg path {
+              fill: ${theme.colors.primary.base};
+              transition: fill ${theme.transitionTiming}s ease-out;
+            }
+          `}
+
+          ${isActive &&
+          css`
+            width: 100%;
+            background-color: ${theme.colors.grayscale.light4};
+            transition: width ${TILE_TRANSITION_TIME}s ease-out,
+              background-color ${TILE_TRANSITION_TIME}s ease-out;
+            cursor: default;
+            svg path {
+              fill: ${theme.colors.primary.base};
+            }
+          `}
+        `}
+      >
+        {vizMeta.icon}{' '}
+        <span
+          css={css`
+            overflow: hidden;
+            text-overflow: ellipsis;
+            min-width: 0;
+            padding-right: ${theme.gridUnit}px;
+          `}
+          ref={chartNameRef}
+        >
+          {chartName}
+        </span>
+      </div>
+    </Tooltip>
+  );
+};
+
+export const FastVizSwitcher = React.memo(
+  ({ currentSelection, onChange }: FastVizSwitcherProps) => {
+    const currentViz = useSelector<ExplorePageState, string | undefined>(
+      state =>
+        state.charts &&
+        Object.values(state.charts)[0]?.latestQueryFormData?.viz_type,
+    );
+    const vizTiles = useMemo(() => {
+      const vizTiles = [...FEATURED_CHARTS];
+      if (
+        currentSelection &&
+        FEATURED_CHARTS.every(
+          featuredVizMeta => featuredVizMeta.name !== currentSelection,
+        ) &&
+        currentSelection !== currentViz
+      ) {
+        vizTiles.unshift({
+          name: currentSelection,
+          icon: (
+            <Icons.MonitorOutlined
+              iconSize="l"
+              css={(theme: SupersetTheme) => css`
+                padding: ${theme.gridUnit}px;
+                & > * {
+                  line-height: 0;
+                }
+              `}
+            />
+          ),
+        });
+      }
+      if (
+        currentViz &&
+        FEATURED_CHARTS.every(
+          featuredVizMeta => featuredVizMeta.name !== currentViz,
+        )
+      ) {
+        vizTiles.unshift({
+          name: currentViz,
+          icon: <Icons.CurrentRenderedTile />,
+        });
+      }
+      return vizTiles;
+    }, [currentSelection, currentViz]);
+
+    return (
+      <div
+        css={(theme: SupersetTheme) => css`
+          display: flex;
+          justify-content: space-between;
+          column-gap: ${theme.gridUnit}px;
+        `}
+        data-test="fast-viz-switcher"
+      >
+        {vizTiles.map(vizMeta => (
+          <VizTile
+            vizMeta={vizMeta}
+            isActive={currentSelection === vizMeta.name}
+            isRendered={currentViz === vizMeta.name}
+            onTileClick={onChange}
+            key={vizMeta.name}
+          />
+        ))}
+      </div>
+    );
+  },
+);
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.jsx
 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.jsx
index c027fd0c3d..67a6a19333 100644
--- 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.jsx
+++ 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.jsx
@@ -67,6 +67,7 @@ describe('VizTypeControl', () => {
       <DynamicPluginProvider>
         <VizTypeControl {...defaultProps} />
       </DynamicPluginProvider>,
+      { useRedux: true },
     );
     await waitForEffects();
   });
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
index a875591620..98a7b9099e 100644
--- 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
+++ 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
@@ -17,21 +17,22 @@
  * under the License.
  */
 import { Preset } from '@superset-ui/core';
-import { render, cleanup, screen, waitFor } from 
'spec/helpers/testing-library';
-import { Provider } from 'react-redux';
-import {
-  getMockStore,
-  mockStore,
-  stateWithoutNativeFilters,
-} from 'spec/fixtures/mockStore';
+import { render, cleanup, screen, within } from 'spec/helpers/testing-library';
+import { stateWithoutNativeFilters } from 'spec/fixtures/mockStore';
 import React from 'react';
 import userEvent from '@testing-library/user-event';
 import { DynamicPluginProvider } from 'src/components/DynamicPlugins';
 import { testWithId } from 'src/utils/testUtils';
 import {
+  BigNumberTotalChartPlugin,
+  EchartsAreaChartPlugin,
   EchartsMixedTimeseriesChartPlugin,
+  EchartsPieChartPlugin,
+  EchartsTimeseriesBarChartPlugin,
   EchartsTimeseriesChartPlugin,
+  EchartsTimeseriesLineChartPlugin,
 } from '@superset-ui/plugin-chart-echarts';
+import TableChartPlugin from '@superset-ui/plugin-chart-table';
 import { LineChartPlugin } from '@superset-ui/preset-chart-xy';
 import TimeTableChartPlugin from '../../../../visualizations/TimeTable';
 import VizTypeControl, { VIZ_TYPE_CONTROL_TEST_ID } from './index';
@@ -44,6 +45,18 @@ class MainPreset extends Preset {
       name: 'Legacy charts',
       plugins: [
         new LineChartPlugin().configure({ key: 'line' }),
+        new TableChartPlugin().configure({ key: 'table' }),
+        new BigNumberTotalChartPlugin().configure({ key: 'big_number_total' }),
+        new EchartsTimeseriesLineChartPlugin().configure({
+          key: 'echarts_timeseries_line',
+        }),
+        new EchartsAreaChartPlugin().configure({
+          key: 'echarts_area',
+        }),
+        new EchartsTimeseriesBarChartPlugin().configure({
+          key: 'echarts_timeseries_bar',
+        }),
+        new EchartsPieChartPlugin().configure({ key: 'pie' }),
         new EchartsTimeseriesChartPlugin().configure({
           key: 'echarts_timeseries',
         }),
@@ -67,7 +80,7 @@ const getTestId = 
testWithId<string>(VIZ_TYPE_CONTROL_TEST_ID, true);
 
 describe('VizTypeControl', () => {
   new MainPreset().register();
-  const newVizTypeControlProps = {
+  const defaultProps = {
     description: '',
     label: '',
     name: '',
@@ -75,20 +88,17 @@ describe('VizTypeControl', () => {
     labelType: 'primary',
     onChange: jest.fn(),
     isModalOpenInit: true,
-  } as const;
+  };
 
   const renderWrapper = (
-    props = newVizTypeControlProps,
+    props: typeof defaultProps = defaultProps,
     state: object = stateWithoutNativeFilters,
   ) => {
     render(
-      <Provider
-        store={state ? getMockStore(stateWithoutNativeFilters) : mockStore}
-      >
-        <DynamicPluginProvider>
-          <VizTypeControl {...props} />
-        </DynamicPluginProvider>
-      </Provider>,
+      <DynamicPluginProvider>
+        <VizTypeControl {...props} />
+      </DynamicPluginProvider>,
+      { useRedux: true, initialState: state },
     );
   };
 
@@ -97,6 +107,119 @@ describe('VizTypeControl', () => {
     jest.clearAllMocks();
   });
 
+  it('Fast viz switcher tiles render', () => {
+    const props = {
+      ...defaultProps,
+      value: 'echarts_timeseries_line',
+      isModalOpenInit: false,
+    };
+    renderWrapper(props);
+    expect(screen.getByLabelText('line-chart-tile')).toBeVisible();
+    expect(screen.getByLabelText('table-chart-tile')).toBeVisible();
+    expect(screen.getByLabelText('big-number-chart-tile')).toBeVisible();
+    expect(screen.getByLabelText('pie-chart-tile')).toBeVisible();
+    expect(screen.getByLabelText('bar-chart-tile')).toBeVisible();
+    expect(screen.getByLabelText('area-chart-tile')).toBeVisible();
+    expect(screen.queryByLabelText('monitor')).not.toBeInTheDocument();
+    expect(
+      screen.queryByLabelText('current-rendered-tile'),
+    ).not.toBeInTheDocument();
+
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText(
+        'Time-series Line Chart',
+      ),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Table'),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Big Number'),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Pie Chart'),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText(
+        'Time-series Bar Chart v2',
+      ),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText(
+        'Time-series Area Chart',
+      ),
+    ).toBeInTheDocument();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).queryByText('Line 
Chart'),
+    ).not.toBeInTheDocument();
+  });
+
+  it('Render viz tiles when non-featured chart is selected', () => {
+    const props = {
+      ...defaultProps,
+      value: 'line',
+      isModalOpenInit: false,
+    };
+    renderWrapper(props);
+
+    expect(screen.getByLabelText('monitor')).toBeVisible();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Line Chart'),
+    ).toBeVisible();
+  });
+
+  it('Render viz tiles when non-featured is rendered', () => {
+    const props = {
+      ...defaultProps,
+      value: 'line',
+      isModalOpenInit: false,
+    };
+    const state = {
+      charts: {
+        1: {
+          latestQueryFormData: {
+            viz_type: 'line',
+          },
+        },
+      },
+    };
+    renderWrapper(props, state);
+    expect(screen.getByLabelText('current-rendered-tile')).toBeVisible();
+    expect(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Line Chart'),
+    ).toBeVisible();
+  });
+
+  it('Change viz type on click', () => {
+    const props = {
+      ...defaultProps,
+      value: 'echarts_timeseries_line',
+      isModalOpenInit: false,
+    };
+    renderWrapper(props);
+    userEvent.click(
+      within(screen.getByTestId('fast-viz-switcher')).getByText(
+        'Time-series Line Chart',
+      ),
+    );
+    expect(props.onChange).not.toHaveBeenCalled();
+    userEvent.click(
+      within(screen.getByTestId('fast-viz-switcher')).getByText('Table'),
+    );
+    expect(props.onChange).toHaveBeenCalledWith('table');
+  });
+
+  it('Open viz gallery modal on "View all charts" click', async () => {
+    renderWrapper({ ...defaultProps, isModalOpenInit: false });
+    expect(
+      screen.queryByText('Select a visualization type'),
+    ).not.toBeInTheDocument();
+    userEvent.click(screen.getByText('View all charts'));
+    expect(
+      await screen.findByText('Select a visualization type'),
+    ).toBeVisible();
+  });
+
   it('Search visualization type', async () => {
     renderWrapper();
 
@@ -104,20 +227,38 @@ describe('VizTypeControl', () => {
 
     userEvent.click(screen.getByRole('button', { name: 'ballot All charts' }));
 
-    await waitFor(() => {
-      expect(visualizations).toHaveTextContent(/Time-series Table/);
-    });
+    expect(
+      await within(visualizations).findByText('Time-series Line Chart'),
+    ).toBeVisible();
 
     // search
     userEvent.type(
       screen.getByTestId(getTestId('search-input')),
       'time series',
     );
-    await waitFor(() => {
-      expect(visualizations).toHaveTextContent(/Time-series Table/);
-      expect(visualizations).toHaveTextContent(/Time-series Chart/);
-      expect(visualizations).toHaveTextContent(/Mixed Time-Series/);
-      expect(visualizations).not.toHaveTextContent(/Line Chart/);
-    });
+    expect(
+      await within(visualizations).findByText('Time-series Table'),
+    ).toBeVisible();
+    expect(within(visualizations).getByText('Time-series 
Chart')).toBeVisible();
+    expect(within(visualizations).getByText('Mixed 
Time-Series')).toBeVisible();
+    expect(
+      within(visualizations).getByText('Time-series Area Chart'),
+    ).toBeVisible();
+    expect(
+      within(visualizations).getByText('Time-series Line Chart'),
+    ).toBeVisible();
+    expect(
+      within(visualizations).getByText('Time-series Bar Chart v2'),
+    ).toBeVisible();
+    expect(
+      within(visualizations).queryByText('Line Chart'),
+    ).not.toBeInTheDocument();
+    
expect(within(visualizations).queryByText('Table')).not.toBeInTheDocument();
+    expect(
+      within(visualizations).queryByText('Big Number'),
+    ).not.toBeInTheDocument();
+    expect(
+      within(visualizations).queryByText('Pie Chart'),
+    ).not.toBeInTheDocument();
   });
 });
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx 
b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
index 7837eb04ae..0f8de76926 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
@@ -17,25 +17,20 @@
  * under the License.
  */
 import React, { useCallback, useState } from 'react';
-import PropTypes from 'prop-types';
-import { t, getChartMetadataRegistry, styled } from '@superset-ui/core';
+import {
+  css,
+  t,
+  getChartMetadataRegistry,
+  styled,
+  SupersetTheme,
+} from '@superset-ui/core';
 import { usePluginContext } from 'src/components/DynamicPlugins';
 import Modal from 'src/components/Modal';
-import { Tooltip } from 'src/components/Tooltip';
-import Label, { Type } from 'src/components/Label';
-import ControlHeader from 'src/explore/components/ControlHeader';
+import { noOp } from 'src/utils/common';
 import VizTypeGallery, {
   MAX_ADVISABLE_VIZ_GALLERY_WIDTH,
 } from './VizTypeGallery';
-
-const propTypes = {
-  description: PropTypes.string,
-  label: PropTypes.string,
-  name: PropTypes.string.isRequired,
-  onChange: PropTypes.func,
-  value: PropTypes.string.isRequired,
-  labelType: PropTypes.string,
-};
+import { FastVizSwitcher } from './FastVizSwitcher';
 
 interface VizTypeControlProps {
   description?: string;
@@ -43,15 +38,9 @@ interface VizTypeControlProps {
   name: string;
   onChange: (vizType: string | null) => void;
   value: string | null;
-  labelType?: Type;
   isModalOpenInit?: boolean;
 }
 
-const defaultProps = {
-  onChange: () => {},
-  labelType: 'default',
-};
-
 const metadataRegistry = getChartMetadataRegistry();
 
 export const VIZ_TYPE_CONTROL_TEST_ID = 'viz-type-control';
@@ -62,7 +51,14 @@ function VizSupportValidation({ vizType }: { vizType: string 
}) {
     return null;
   }
   return (
-    <div className="text-danger">
+    <div
+      className="text-danger"
+      css={(theme: SupersetTheme) =>
+        css`
+          margin-top: ${theme.gridUnit}px;
+        `
+      }
+    >
       <i className="fa fa-exclamation-circle text-danger" />{' '}
       <small>{t('This visualization type is not supported.')}</small>
     </div>
@@ -76,9 +72,11 @@ const UnpaddedModal = styled(Modal)`
 `;
 
 /** Manages the viz type and the viz picker modal */
-const VizTypeControl = (props: VizTypeControlProps) => {
-  const { value: initialValue, onChange, isModalOpenInit, labelType } = props;
-  const { mountedPluginMetadata } = usePluginContext();
+const VizTypeControl = ({
+  value: initialValue,
+  onChange = noOp,
+  isModalOpenInit,
+}: VizTypeControlProps) => {
   const [showModal, setShowModal] = useState(!!isModalOpenInit);
   // a trick to force re-initialization of the gallery each time the modal 
opens,
   // ensuring that the modal always opens to the correct category.
@@ -101,30 +99,32 @@ const VizTypeControl = (props: VizTypeControlProps) => {
     setSelectedViz(initialValue);
   }, [initialValue]);
 
-  const labelContent = initialValue
-    ? mountedPluginMetadata[initialValue]?.name || `${initialValue}`
-    : t('Select Viz Type');
-
   return (
-    <div>
-      <ControlHeader {...props} />
-      <Tooltip
-        id="error-tooltip"
-        placement="right"
-        title={t('Click to change visualization type')}
+    <>
+      <div
+        css={(theme: SupersetTheme) => css`
+          min-width: ${theme.gridUnit * 72}px;
+          max-width: fit-content;
+        `}
       >
-        <>
-          <Label
-            onClick={openModal}
-            type={labelType}
-            data-test="visualization-type"
-          >
-            {labelContent}
-          </Label>
-          {initialValue && <VizSupportValidation vizType={initialValue} />}
-        </>
-      </Tooltip>
-
+        <FastVizSwitcher onChange={onChange} currentSelection={initialValue} />
+        {initialValue && <VizSupportValidation vizType={initialValue} />}
+      </div>
+      <div
+        css={(theme: SupersetTheme) =>
+          css`
+            display: flex;
+            justify-content: flex-end;
+            margin-top: ${theme.gridUnit * 3}px;
+            color: ${theme.colors.grayscale.base};
+            text-decoration: underline;
+          `
+        }
+      >
+        <span role="button" tabIndex={0} onClick={openModal}>
+          {t('View all charts')}
+        </span>
+      </div>
       <UnpaddedModal
         show={showModal}
         onHide={onCancel}
@@ -142,11 +142,8 @@ const VizTypeControl = (props: VizTypeControlProps) => {
           onChange={setSelectedViz}
         />
       </UnpaddedModal>
-    </div>
+    </>
   );
 };
 
-VizTypeControl.propTypes = propTypes;
-VizTypeControl.defaultProps = defaultProps;
-
 export default VizTypeControl;
diff --git a/superset-frontend/src/explore/controlPanels/sections.tsx 
b/superset-frontend/src/explore/controlPanels/sections.tsx
index be21747ed6..78815215df 100644
--- a/superset-frontend/src/explore/controlPanels/sections.tsx
+++ b/superset-frontend/src/explore/controlPanels/sections.tsx
@@ -29,7 +29,7 @@ export const druidTimeSeries: ControlPanelSectionConfig = {
 };
 
 export const datasourceAndVizType: ControlPanelSectionConfig = {
-  label: t('Chart type'),
+  label: t('Visualization type'),
   expanded: true,
   controlSetRows: [
     ['datasource'],

Reply via email to