You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2024/02/29 19:09:05 UTC

(superset) branch 3.1 updated (38bc2d3b02 -> 176f8b513c)

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

michaelsmolina pushed a change to branch 3.1
in repository https://gitbox.apache.org/repos/asf/superset.git


    from 38bc2d3b02 fix: Inoperable dashboard filter slider when range is <= 1 (#27271)
     new 254f1ae512 fix: Navigating to an invalid page index in lists (#27273)
     new e80179a056 fix: Data zoom with horizontal orientation (#27291)
     new 22e3419e36 fix: Incorrect data type on import page (#27307)
     new c39e16e624 fix(plugin-chart-echarts): calculate Gauge Chart intervals correctly when min value is set (#27285)
     new da6a25780e fix(sqllab): invalid dump sql shown after closing tab (#27295)
     new 176f8b513c chore: bump cryptography minimum to 42.0.4 (#27281)

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 requirements/base.txt                              |  2 +-
 setup.py                                           |  3 +-
 .../src/Gauge/transformProps.ts                    | 13 ++--
 .../src/Timeseries/transformProps.ts               |  2 +-
 .../src/Timeseries/transformers.ts                 |  7 +-
 .../test/Gauge/transformProps.test.ts              | 54 +++++++++++++++-
 .../SqlLab/components/SqlEditor/SqlEditor.test.tsx | 43 ++++++++++++-
 .../src/SqlLab/components/SqlEditor/index.tsx      | 28 +++++++-
 .../src/SqlLab/reducers/getInitialState.ts         |  2 +-
 superset-frontend/src/SqlLab/reducers/sqlLab.js    |  5 +-
 .../src/SqlLab/reducers/sqlLab.test.js             | 19 ++++++
 .../src/components/ListView/ListView.test.tsx      | 74 ++++++++++++++++++++++
 .../src/components/ListView/ListView.tsx           |  8 ++-
 superset/views/database/forms.py                   |  3 +-
 14 files changed, 239 insertions(+), 24 deletions(-)
 create mode 100644 superset-frontend/src/components/ListView/ListView.test.tsx


(superset) 06/06: chore: bump cryptography minimum to 42.0.4 (#27281)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 176f8b513ce187c799a15bebbd02e3ed4cb87986
Author: Joe Li <jo...@preset.io>
AuthorDate: Thu Feb 29 10:28:57 2024 -0800

    chore: bump cryptography minimum to 42.0.4 (#27281)
    
    (cherry picked from commit 371f2ab851eee46cd4ab3c5428c8061430a288ad)
---
 requirements/base.txt | 2 +-
 setup.py              | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/requirements/base.txt b/requirements/base.txt
index 09b81ec61a..2df0f87695 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -76,7 +76,7 @@ cron-descriptor==1.2.24
     # via apache-superset
 croniter==1.0.15
     # via apache-superset
-cryptography==42.0.2
+cryptography==42.0.4
     # via
     #   apache-superset
     #   paramiko
diff --git a/setup.py b/setup.py
index cb6be9ec12..7050d7b497 100644
--- a/setup.py
+++ b/setup.py
@@ -80,8 +80,7 @@ setup(
         "colorama",
         "croniter>=0.3.28",
         "cron-descriptor",
-        # snowflake-connector-python as of 3.7.0 doesn't support >=42.* therefore lowering the min to 41.0.2
-        "cryptography>=41.0.2, <43.0.0",
+        "cryptography>=42.0.4, <43.0.0",
         "deprecation>=2.1.0, <2.2.0",
         "flask>=2.2.5, <3.0.0",
         "flask-appbuilder>=4.4.1, <5.0.0",


(superset) 05/06: fix(sqllab): invalid dump sql shown after closing tab (#27295)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit da6a25780ef157bfb1e94d647f2e1c734b95ac94
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Thu Feb 29 09:57:30 2024 -0800

    fix(sqllab): invalid dump sql shown after closing tab (#27295)
---
 .../SqlLab/components/SqlEditor/SqlEditor.test.tsx | 43 +++++++++++++++++++++-
 .../src/SqlLab/components/SqlEditor/index.tsx      | 28 ++++++++++++--
 .../src/SqlLab/reducers/getInitialState.ts         |  2 +-
 superset-frontend/src/SqlLab/reducers/sqlLab.js    |  5 ++-
 .../src/SqlLab/reducers/sqlLab.test.js             | 19 ++++++++++
 5 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
index 63f67170d0..d491330443 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import React from 'react';
+import * as uiCore from '@superset-ui/core';
 import { act } from 'react-dom/test-utils';
 import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
 import fetchMock from 'fetch-mock';
@@ -31,7 +32,7 @@ import {
 import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
 import ResultSet from 'src/SqlLab/components/ResultSet';
 import { api } from 'src/hooks/apiResources/queryApi';
-import { getExtensionsRegistry } from '@superset-ui/core';
+import { getExtensionsRegistry, FeatureFlag } from '@superset-ui/core';
 import setupExtensions from 'src/setup/setupExtensions';
 import type { Action, Middleware, Store } from 'redux';
 import SqlEditor, { Props } from '.';
@@ -63,6 +64,7 @@ fetchMock.get('glob:*/api/v1/database/*/function_names/', {
 });
 fetchMock.get('glob:*/api/v1/database/*', { result: [] });
 fetchMock.get('glob:*/api/v1/database/*/tables/*', { options: [] });
+fetchMock.get('glob:*/tabstateview/*', defaultQueryEditor);
 fetchMock.post('glob:*/sqllab/execute/*', { result: [] });
 
 let store: Store;
@@ -290,4 +292,43 @@ describe('SqlEditor', () => {
       await findByText('sqleditor.extension.form extension component'),
     ).toBeInTheDocument();
   });
+
+  describe('with SqllabBackendPersistence enabled', () => {
+    let isFeatureEnabledMock: jest.MockInstance<
+      boolean,
+      [feature: FeatureFlag]
+    >;
+    beforeEach(() => {
+      isFeatureEnabledMock = jest
+        .spyOn(uiCore, 'isFeatureEnabled')
+        .mockImplementation(
+          featureFlag =>
+            featureFlag === uiCore.FeatureFlag.SqllabBackendPersistence,
+        );
+    });
+    afterEach(() => {
+      isFeatureEnabledMock.mockClear();
+    });
+
+    it('should render loading state when its Editor is not loaded', async () => {
+      const switchTabApi = `glob:*/tabstateview/${defaultQueryEditor.id}/activate`;
+      fetchMock.post(switchTabApi, {});
+      const { getByTestId } = setup(
+        {
+          ...mockedProps,
+          queryEditor: {
+            ...mockedProps.queryEditor,
+            loaded: false,
+          },
+        },
+        store,
+      );
+      const indicator = getByTestId('sqlEditor-loading');
+      expect(indicator).toBeInTheDocument();
+      await waitFor(() =>
+        expect(fetchMock.calls('glob:*/tabstateview/*').length).toBe(1),
+      );
+      expect(fetchMock.calls(switchTabApi).length).toBe(1);
+    });
+  });
 });
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
index 0881434487..3970eae513 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
@@ -51,7 +51,7 @@ import Mousetrap from 'mousetrap';
 import Button from 'src/components/Button';
 import Timer from 'src/components/Timer';
 import ResizableSidebar from 'src/components/ResizableSidebar';
-import { AntdDropdown, AntdSwitch } from 'src/components';
+import { AntdDropdown, AntdSwitch, Skeleton } from 'src/components';
 import { Input } from 'src/components/Input';
 import { Menu } from 'src/components/Menu';
 import Icons from 'src/components/Icons';
@@ -73,6 +73,7 @@ import {
   setActiveSouthPaneTab,
   updateSavedQuery,
   formatQuery,
+  switchQueryEditor,
 } from 'src/SqlLab/actions/sqlLab';
 import {
   STATE_TYPE_MAP,
@@ -489,6 +490,16 @@ const SqlEditor: React.FC<Props> = ({
     }
   });
 
+  const shouldLoadQueryEditor =
+    isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) &&
+    !queryEditor.loaded;
+
+  const loadQueryEditor = useEffectEvent(() => {
+    if (shouldLoadQueryEditor) {
+      dispatch(switchQueryEditor(queryEditor, displayLimit));
+    }
+  });
+
   useEffect(() => {
     // We need to measure the height of the sql editor post render to figure the height of
     // the south pane so it gets rendered properly
@@ -498,6 +509,7 @@ const SqlEditor: React.FC<Props> = ({
       WINDOW_RESIZE_THROTTLE_MS,
     );
 
+    loadQueryEditor();
     window.addEventListener('resize', handleWindowResizeWithThrottle);
     window.addEventListener('beforeunload', onBeforeUnload);
 
@@ -506,7 +518,7 @@ const SqlEditor: React.FC<Props> = ({
       window.removeEventListener('beforeunload', onBeforeUnload);
     };
     // TODO: Remove useEffectEvent deps once https://github.com/facebook/react/pull/25881 is released
-  }, [onBeforeUnload]);
+  }, [onBeforeUnload, loadQueryEditor]);
 
   useEffect(() => {
     if (!database || isEmpty(database)) {
@@ -841,7 +853,17 @@ const SqlEditor: React.FC<Props> = ({
           )}
         </ResizableSidebar>
       </CSSTransition>
-      {showEmptyState ? (
+      {shouldLoadQueryEditor ? (
+        <div
+          data-test="sqlEditor-loading"
+          css={css`
+            flex: 1;
+            padding: ${theme.gridUnit * 4}px;
+          `}
+        >
+          <Skeleton active />
+        </div>
+      ) : showEmptyState ? (
         <EmptyStateBig
           image="vector.svg"
           title={t('Select a database to write a query')}
diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.ts b/superset-frontend/src/SqlLab/reducers/getInitialState.ts
index 02bb5b45fc..e7c6a52c35 100644
--- a/superset-frontend/src/SqlLab/reducers/getInitialState.ts
+++ b/superset-frontend/src/SqlLab/reducers/getInitialState.ts
@@ -57,7 +57,7 @@ export default function getInitialState({
     version: LatestQueryEditorVersion,
     loaded: true,
     name: t('Untitled query'),
-    sql: 'SELECT *\nFROM\nWHERE',
+    sql: '',
     latestQueryId: null,
     autorun: false,
     dbId: common.conf.SQLLAB_DEFAULT_DBID,
diff --git a/superset-frontend/src/SqlLab/reducers/sqlLab.js b/superset-frontend/src/SqlLab/reducers/sqlLab.js
index 59bd0558a1..d74c2c815b 100644
--- a/superset-frontend/src/SqlLab/reducers/sqlLab.js
+++ b/superset-frontend/src/SqlLab/reducers/sqlLab.js
@@ -152,7 +152,10 @@ export default function sqlLabReducer(state = {}, action) {
 
       newState = {
         ...newState,
-        tabHistory,
+        tabHistory:
+          tabHistory.length === 0 && newState.queryEditors.length > 0
+            ? newState.queryEditors.slice(-1).map(qe => qe.id)
+            : tabHistory,
         tables,
         queries,
         unsavedQueryEditor: {
diff --git a/superset-frontend/src/SqlLab/reducers/sqlLab.test.js b/superset-frontend/src/SqlLab/reducers/sqlLab.test.js
index e1a234734b..041835d04b 100644
--- a/superset-frontend/src/SqlLab/reducers/sqlLab.test.js
+++ b/superset-frontend/src/SqlLab/reducers/sqlLab.test.js
@@ -75,6 +75,25 @@ describe('sqlLabReducer', () => {
         initialState.queryEditors.length,
       );
     });
+    it('should select the latest query editor when tabHistory is empty', () => {
+      const currentQE = newState.queryEditors[0];
+      newState = {
+        ...initialState,
+        tabHistory: [initialState.queryEditors[0]],
+      };
+      const action = {
+        type: actions.REMOVE_QUERY_EDITOR,
+        queryEditor: currentQE,
+      };
+      newState = sqlLabReducer(newState, action);
+      expect(newState.queryEditors).toHaveLength(
+        initialState.queryEditors.length - 1,
+      );
+      expect(newState.queryEditors.map(qe => qe.id)).not.toContainEqual(
+        currentQE.id,
+      );
+      expect(newState.tabHistory).toEqual([initialState.queryEditors[2].id]);
+    });
     it('should remove a query editor including unsaved changes', () => {
       expect(newState.queryEditors).toHaveLength(
         initialState.queryEditors.length + 1,


(superset) 02/06: fix: Data zoom with horizontal orientation (#27291)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e80179a056d3bbf4617fe2eb9b619614f39c554f
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu Feb 29 08:53:09 2024 -0500

    fix: Data zoom with horizontal orientation (#27291)
    
    (cherry picked from commit 7854b622a34c9a9674e2c916acb8acbc63714fb8)
---
 .../plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts  | 2 +-
 .../plugins/plugin-chart-echarts/src/Timeseries/transformers.ts    | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
index 451685ec0a..78245cc9d9 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -575,7 +575,6 @@ export default function transformProps(
       right: TIMESERIES_CONSTANTS.toolboxRight,
       feature: {
         dataZoom: {
-          yAxisIndex: false,
           title: {
             zoom: t('zoom area'),
             back: t('restore zoom'),
@@ -590,6 +589,7 @@ export default function transformProps(
             start: TIMESERIES_CONSTANTS.dataZoomStart,
             end: TIMESERIES_CONSTANTS.dataZoomEnd,
             bottom: TIMESERIES_CONSTANTS.zoomBottom,
+            yAxisIndex: isHorizontal ? 0 : undefined,
           },
         ]
       : [],
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
index 5b9fa43047..be89fdfc74 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
@@ -570,9 +570,10 @@ export function getPadding(
         yAxisTitlePosition && yAxisTitlePosition === 'Top'
           ? TIMESERIES_CONSTANTS.gridOffsetTop + (Number(yAxisTitleMargin) || 0)
           : TIMESERIES_CONSTANTS.gridOffsetTop + yAxisOffset,
-      bottom: zoomable
-        ? TIMESERIES_CONSTANTS.gridOffsetBottomZoomable + xAxisOffset
-        : TIMESERIES_CONSTANTS.gridOffsetBottom + xAxisOffset,
+      bottom:
+        zoomable && !isHorizontal
+          ? TIMESERIES_CONSTANTS.gridOffsetBottomZoomable + xAxisOffset
+          : TIMESERIES_CONSTANTS.gridOffsetBottom + xAxisOffset,
       left:
         yAxisTitlePosition === 'Left'
           ? TIMESERIES_CONSTANTS.gridOffsetLeft +


(superset) 03/06: fix: Incorrect data type on import page (#27307)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 22e3419e369976a4d2d98df0e1e52beaf07444d3
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu Feb 29 10:03:20 2024 -0500

    fix: Incorrect data type on import page (#27307)
    
    (cherry picked from commit fa04eec2d5cdd2698e8a3f28926ab70d17358e86)
---
 superset/views/database/forms.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/superset/views/database/forms.py b/superset/views/database/forms.py
index f8e528c4e3..6b39765bed 100644
--- a/superset/views/database/forms.py
+++ b/superset/views/database/forms.py
@@ -146,7 +146,8 @@ class CsvToDatabaseForm(UploadToDatabaseForm):
         description=_(
             "A dictionary with column names and their data types"
             " if you need to change the defaults."
-            ' Example: {"user_id":"integer"}'
+            ' Example: {"user_id":"int"}. '
+            "Check Python's Pandas library for supported data types."
         ),
         validators=[Optional()],
         widget=BS3TextFieldWidget(),


(superset) 01/06: fix: Navigating to an invalid page index in lists (#27273)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 254f1ae512b91112c74378b80199801cb2a9aeca
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Wed Feb 28 10:15:28 2024 -0500

    fix: Navigating to an invalid page index in lists (#27273)
---
 .../src/components/ListView/ListView.test.tsx      | 74 ++++++++++++++++++++++
 .../src/components/ListView/ListView.tsx           |  8 ++-
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/components/ListView/ListView.test.tsx b/superset-frontend/src/components/ListView/ListView.test.tsx
new file mode 100644
index 0000000000..9f4da16140
--- /dev/null
+++ b/superset-frontend/src/components/ListView/ListView.test.tsx
@@ -0,0 +1,74 @@
+/**
+ * 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 from 'react';
+import { render, waitFor } from 'spec/helpers/testing-library';
+import ListView from './ListView';
+
+const mockedProps = {
+  title: 'Data Table',
+  columns: [
+    {
+      accessor: 'id',
+      Header: 'ID',
+      sortable: true,
+    },
+    {
+      accessor: 'age',
+      Header: 'Age',
+    },
+    {
+      accessor: 'name',
+      Header: 'Name',
+    },
+    {
+      accessor: 'time',
+      Header: 'Time',
+    },
+  ],
+  data: [
+    { id: 1, name: 'data 1', age: 10, time: '2020-11-18T07:53:45.354Z' },
+    { id: 2, name: 'data 2', age: 1, time: '2020-11-18T07:53:45.354Z' },
+  ],
+  count: 2,
+  pageSize: 1,
+  loading: false,
+  refreshData: jest.fn(),
+  addSuccessToast: jest.fn(),
+  addDangerToast: jest.fn(),
+};
+
+test('redirects to first page when page index is invalid', async () => {
+  const fetchData = jest.fn();
+  window.history.pushState({}, '', '/?pageIndex=9');
+  render(<ListView {...mockedProps} fetchData={fetchData} />, {
+    useRouter: true,
+    useQueryParams: true,
+  });
+  await waitFor(() => {
+    expect(window.location.search).toEqual('?pageIndex=0');
+    expect(fetchData).toBeCalledTimes(2);
+    expect(fetchData).toHaveBeenCalledWith(
+      expect.objectContaining({ pageIndex: 9 }),
+    );
+    expect(fetchData).toHaveBeenCalledWith(
+      expect.objectContaining({ pageIndex: 0 }),
+    );
+  });
+  fetchData.mockClear();
+});
diff --git a/superset-frontend/src/components/ListView/ListView.tsx b/superset-frontend/src/components/ListView/ListView.tsx
index 93847ca5d8..0cdb4ba034 100644
--- a/superset-frontend/src/components/ListView/ListView.tsx
+++ b/superset-frontend/src/components/ListView/ListView.tsx
@@ -322,6 +322,12 @@ function ListView<T extends object = any>({
     if (!bulkSelectEnabled) toggleAllRowsSelected(false);
   }, [bulkSelectEnabled, toggleAllRowsSelected]);
 
+  useEffect(() => {
+    if (!loading && pageIndex > pageCount - 1 && pageCount > 0) {
+      gotoPage(0);
+    }
+  }, [gotoPage, loading, pageCount, pageIndex]);
+
   return (
     <ListViewStyles>
       {allowBulkTagActions && (
@@ -463,7 +469,7 @@ function ListView<T extends object = any>({
         <div className="pagination-container">
           <Pagination
             totalPages={pageCount || 0}
-            currentPage={pageCount ? pageIndex + 1 : 0}
+            currentPage={pageCount && pageIndex < pageCount ? pageIndex + 1 : 0}
             onChange={(p: number) => gotoPage(p - 1)}
             hideFirstAndLastPageLinks
           />


(superset) 04/06: fix(plugin-chart-echarts): calculate Gauge Chart intervals correctly when min value is set (#27285)

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c39e16e624d3156a07a57ba3611c7b6bf0df2e9a
Author: goto-loop <10...@users.noreply.github.com>
AuthorDate: Thu Feb 29 17:41:23 2024 +0100

    fix(plugin-chart-echarts): calculate Gauge Chart intervals correctly when min value is set (#27285)
    
    (cherry picked from commit d65f64d1ceacb69226fa1907343405b5571bc6a8)
---
 .../src/Gauge/transformProps.ts                    | 13 +++---
 .../test/Gauge/transformProps.test.ts              | 54 ++++++++++++++++++++--
 2 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
index f32993b1df..231b0d65da 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/transformProps.ts
@@ -48,11 +48,12 @@ import { getDefaultTooltip } from '../utils/tooltip';
 import { Refs } from '../types';
 import { getColtypesMapping } from '../utils/series';
 
-const setIntervalBoundsAndColors = (
+export const getIntervalBoundsAndColors = (
   intervals: string,
   intervalColorIndices: string,
   colorFn: CategoricalColorScale,
-  normalizer: number,
+  min: number,
+  max: number,
 ): Array<[number, string]> => {
   let intervalBoundsNonNormalized;
   let intervalColorIndicesArray;
@@ -65,7 +66,7 @@ const setIntervalBoundsAndColors = (
   }
 
   const intervalBounds = intervalBoundsNonNormalized.map(
-    bound => bound / normalizer,
+    bound => (bound - min) / (max - min),
   );
   const intervalColors = intervalColorIndicesArray.map(
     ind => colorFn.colors[(ind - 1) % colorFn.colors.length],
@@ -221,12 +222,12 @@ export default function transformProps(
   const axisLabelLength = Math.max(
     ...axisLabels.map(label => numberFormatter(label).length).concat([1]),
   );
-  const normalizer = max;
-  const intervalBoundsAndColors = setIntervalBoundsAndColors(
+  const intervalBoundsAndColors = getIntervalBoundsAndColors(
     intervals,
     intervalColorIndices,
     colorFn,
-    normalizer,
+    min,
+    max,
   );
   const splitLineDistance =
     axisLineWidth + splitLineLength + OFFSETS.ticksFromLine;
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Gauge/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Gauge/transformProps.test.ts
index 915a8b9e9d..760e3ff93c 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/Gauge/transformProps.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/Gauge/transformProps.test.ts
@@ -16,8 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { ChartProps, SqlaFormData, supersetTheme } from '@superset-ui/core';
-import transformProps from '../../src/Gauge/transformProps';
+import {
+  CategoricalColorNamespace,
+  ChartProps,
+  SqlaFormData,
+  supersetTheme,
+} from '@superset-ui/core';
+import transformProps, {
+  getIntervalBoundsAndColors,
+} from '../../src/Gauge/transformProps';
 import { EchartsGaugeChartProps } from '../../src/Gauge/types';
 
 describe('Echarts Gauge transformProps', () => {
@@ -256,8 +263,9 @@ describe('Echarts Gauge transformProps', () => {
     const formData: SqlaFormData = {
       ...baseFormData,
       groupby: ['year', 'platform'],
-      intervals: '50,100',
+      intervals: '60,100',
       intervalColorIndices: '1,2',
+      minVal: 20,
     };
     const queriesData = [
       {
@@ -342,3 +350,43 @@ describe('Echarts Gauge transformProps', () => {
     );
   });
 });
+
+describe('getIntervalBoundsAndColors', () => {
+  it('should generate correct interval bounds and colors', () => {
+    const colorFn = CategoricalColorNamespace.getScale(
+      'supersetColors' as string,
+    );
+    expect(getIntervalBoundsAndColors('', '', colorFn, 0, 10)).toEqual([]);
+    expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 0, 10)).toEqual(
+      [
+        [0.4, '#1f77b4'],
+        [1, '#ff7f0e'],
+      ],
+    );
+    expect(
+      getIntervalBoundsAndColors('4, 8, 10', '9, 8, 7', colorFn, 0, 10),
+    ).toEqual([
+      [0.4, '#bcbd22'],
+      [0.8, '#7f7f7f'],
+      [1, '#e377c2'],
+    ]);
+    expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 2, 10)).toEqual(
+      [
+        [0.25, '#1f77b4'],
+        [1, '#ff7f0e'],
+      ],
+    );
+    expect(
+      getIntervalBoundsAndColors('-4, 0', '1, 2', colorFn, -10, 0),
+    ).toEqual([
+      [0.6, '#1f77b4'],
+      [1, '#ff7f0e'],
+    ]);
+    expect(
+      getIntervalBoundsAndColors('-4, -2', '1, 2', colorFn, -10, -2),
+    ).toEqual([
+      [0.75, '#1f77b4'],
+      [1, '#ff7f0e'],
+    ]);
+  });
+});