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/01/09 12:21:50 UTC
(superset) branch 3.0 updated (37ad33bf5b -> 439979c73a)
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a change to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
from 37ad33bf5b fix: Reactivates native filters E2E tests (#26362)
new f467310cea fix(dashboard): Chart menu disable is fixed on chart-fullscreen in issue #25992 (#26410)
new 3589f7ece4 fix(dashboard): narrow empty drop area (#26313)
new 26b26e46e4 fix(chart): Resolve incorrect column customization when switching metrics in table chart (#26393)
new 333b18db5a fix: In chart gallery thumbnail is rendered in case of no example in #16707 (#26415)
new 52109e5f24 fix(plugin-chart-echarts): support forced categorical x-axis (#26404)
new 2a30d59b05 fix(translations): Clear all (#26424)
new 439979c73a fix(post-processing): handle missing values in cumulative operator (#26429)
The 7 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:
.../superset-ui-chart-controls/src/constants.ts | 11 +-
.../superset-ui-chart-controls/src/fixtures.ts | 12 +-
.../src/sections/echartsTimeSeriesQuery.tsx | 2 +
.../src/shared-controls/customControls.tsx | 54 +++++--
.../superset-ui-chart-controls/src/types.ts | 6 +-
.../src/utils/checkColumnType.ts} | 50 +++---
.../src/utils/columnChoices.ts | 8 +-
.../superset-ui-chart-controls/src/utils/index.ts | 1 +
.../test/utils/checkColumnType.test.ts} | 49 ++----
.../test/utils/columnChoices.test.tsx | 16 +-
.../test/utils/getTemporalColumns.test.ts | 9 +-
.../superset-ui-core/src/query/types/Query.ts | 11 ++
.../src/MixedTimeseries/transformProps.ts | 3 +-
.../src/MixedTimeseries/types.ts | 1 +
.../src/Timeseries/constants.ts | 1 +
.../src/Timeseries/transformProps.ts | 3 +-
.../plugin-chart-echarts/src/Timeseries/types.ts | 1 +
.../plugins/plugin-chart-echarts/src/controls.tsx | 11 ++
.../plugin-chart-echarts/src/utils/series.ts | 10 +-
.../plugin-chart-echarts/test/utils/series.test.ts | 26 ++-
superset-frontend/src/SqlLab/fixtures.ts | 6 +
.../src/components/Dropdown/index.tsx | 8 +-
.../ReportModal/HeaderReportDropdown/index.tsx | 7 +
.../DashboardBuilder/DashboardBuilder.tsx | 70 +-------
.../DashboardBuilder/DashboardWrapper.test.tsx | 75 +++++++++
.../DashboardBuilder/DashboardWrapper.tsx | 128 +++++++++++++++
.../src/dashboard/components/DashboardGrid.jsx | 19 ++-
.../components/SliceHeaderControls/index.tsx | 7 +
.../src/dashboard/components/dnd/DragDroppable.jsx | 11 +-
.../dashboard/components/gridComponents/Tab.jsx | 8 +-
.../src/explore/actions/exploreActions.test.js | 180 +++++++++++++++++++++
.../controls/VizTypeControl/VizTypeGallery.tsx | 12 +-
.../src/explore/reducers/exploreReducer.js | 5 +
superset/result_set.py | 16 +-
superset/translations/zh/LC_MESSAGES/messages.po | 6 +-
superset/utils/pandas_postprocessing/cum.py | 1 +
tests/integration_tests/result_set_tests.py | 92 +++++++++--
tests/unit_tests/fixtures/dataframes.py | 5 +
tests/unit_tests/pandas_postprocessing/test_cum.py | 14 ++
39 files changed, 761 insertions(+), 194 deletions(-)
copy superset-frontend/{plugins/plugin-chart-handlebars/src/plugin/controls/groupBy.tsx => packages/superset-ui-chart-controls/src/utils/checkColumnType.ts} (52%)
copy superset-frontend/{src/utils/findPermission.test.ts => packages/superset-ui-chart-controls/test/utils/checkColumnType.test.ts} (50%)
create mode 100644 superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
create mode 100644 superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
(superset) 02/07: fix(dashboard): narrow empty drop area (#26313)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 3589f7ece4c7fde31ef7aa957298a5e27b33bd6e
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Thu Jan 4 09:44:23 2024 -0800
fix(dashboard): narrow empty drop area (#26313)
(cherry picked from commit 300ddaedf97f582a8d73bc78e02ff3c434a4934f)
---
.../DashboardBuilder/DashboardBuilder.tsx | 70 +----------
.../DashboardBuilder/DashboardWrapper.test.tsx | 75 ++++++++++++
.../DashboardBuilder/DashboardWrapper.tsx | 128 +++++++++++++++++++++
.../src/dashboard/components/DashboardGrid.jsx | 19 ++-
.../src/dashboard/components/dnd/DragDroppable.jsx | 11 +-
.../dashboard/components/gridComponents/Tab.jsx | 8 +-
6 files changed, 235 insertions(+), 76 deletions(-)
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index a17b168374..51de15c7a1 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -86,72 +86,10 @@ import {
import { getRootLevelTabsComponent, shouldFocusTabs } from './utils';
import DashboardContainer from './DashboardContainer';
import { useNativeFilters } from './state';
+import DashboardWrapper from './DashboardWrapper';
type DashboardBuilderProps = {};
-const StyledDiv = styled.div`
- ${({ theme }) => css`
- display: grid;
- grid-template-columns: auto 1fr;
- grid-template-rows: auto 1fr;
- flex: 1;
- /* Special cases */
-
- /* A row within a column has inset hover menu */
- .dragdroppable-column .dragdroppable-row .hover-menu--left {
- left: ${theme.gridUnit * -3}px;
- background: ${theme.colors.grayscale.light5};
- border: 1px solid ${theme.colors.grayscale.light2};
- }
-
- .dashboard-component-tabs {
- position: relative;
- }
-
- /* A column within a column or tabs has inset hover menu */
- .dragdroppable-column .dragdroppable-column .hover-menu--top,
- .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
- top: ${theme.gridUnit * -3}px;
- background: ${theme.colors.grayscale.light5};
- border: 1px solid ${theme.colors.grayscale.light2};
- }
-
- /* move Tabs hover menu to top near actual Tabs */
- .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
- top: 0;
- transform: unset;
- background: transparent;
- }
-
- /* push Chart actions to upper right */
- .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
- .dragdroppable .dashboard-component-header .hover-menu--top {
- right: ${theme.gridUnit * 2}px;
- top: ${theme.gridUnit * 2}px;
- background: transparent;
- border: none;
- transform: unset;
- left: unset;
- }
- div:hover > .hover-menu-container .hover-menu,
- .hover-menu-container .hover-menu:hover {
- opacity: 1;
- }
-
- p {
- margin: 0 0 ${theme.gridUnit * 2}px 0;
- }
-
- i.danger {
- color: ${theme.colors.error.base};
- }
-
- i.warning {
- color: ${theme.colors.alert.base};
- }
- `}
-`;
-
// @z-index-above-dashboard-charts + 1 = 11
const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
grid-column: 1;
@@ -317,7 +255,7 @@ const DashboardContentWrapper = styled.div`
width: 100%;
}
- & > .empty-droptarget:first-child {
+ & > .empty-droptarget:first-child:not(.empty-droptarget--full) {
height: ${theme.gridUnit * 4}px;
top: -2px;
z-index: 10;
@@ -640,7 +578,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
: theme.gridUnit * 8;
return (
- <StyledDiv>
+ <DashboardWrapper>
{showFilterBar && filterBarOrientation === FilterBarOrientation.VERTICAL && (
<>
<ResizableSidebar
@@ -749,7 +687,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
`}
/>
)}
- </StyledDiv>
+ </DashboardWrapper>
);
};
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
new file mode 100644
index 0000000000..fb913b4627
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
@@ -0,0 +1,75 @@
+/**
+ * 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 { fireEvent, render } from 'spec/helpers/testing-library';
+import { OptionControlLabel } from 'src/explore/components/controls/OptionControls';
+
+import DashboardWrapper from './DashboardWrapper';
+
+test('should render children', () => {
+ const { getByTestId } = render(
+ <DashboardWrapper>
+ <div data-test="mock-children" />
+ </DashboardWrapper>,
+ { useRedux: true, useDnd: true },
+ );
+ expect(getByTestId('mock-children')).toBeInTheDocument();
+});
+
+test('should update the style on dragging state', () => {
+ const defaultProps = {
+ label: <span>Test label</span>,
+ tooltipTitle: 'This is a tooltip title',
+ onRemove: jest.fn(),
+ onMoveLabel: jest.fn(),
+ onDropLabel: jest.fn(),
+ type: 'test',
+ index: 0,
+ };
+ const { container, getByText } = render(
+ <DashboardWrapper>
+ <OptionControlLabel
+ {...defaultProps}
+ index={1}
+ label={<span>Label 1</span>}
+ />
+ <OptionControlLabel
+ {...defaultProps}
+ index={2}
+ label={<span>Label 2</span>}
+ />
+ </DashboardWrapper>,
+ {
+ useRedux: true,
+ useDnd: true,
+ initialState: {
+ dashboardState: {
+ editMode: true,
+ },
+ },
+ },
+ );
+ expect(
+ container.getElementsByClassName('dragdroppable--dragging'),
+ ).toHaveLength(0);
+ fireEvent.dragStart(getByText('Label 1'));
+ expect(
+ container.getElementsByClassName('dragdroppable--dragging'),
+ ).toHaveLength(1);
+});
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
new file mode 100644
index 0000000000..f39c7ed630
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
@@ -0,0 +1,128 @@
+/**
+ * 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 } from 'react';
+import { css, styled } from '@superset-ui/core';
+import { RootState } from 'src/dashboard/types';
+import { useSelector } from 'react-redux';
+import { useDragDropManager } from 'react-dnd';
+import classNames from 'classnames';
+
+const StyledDiv = styled.div`
+ ${({ theme }) => css`
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: auto 1fr;
+ flex: 1;
+ /* Special cases */
+
+ &.dragdroppable--dragging
+ .dashboard-component-tabs-content
+ > .empty-droptarget.empty-droptarget--full {
+ height: 100%;
+ }
+
+ /* A row within a column has inset hover menu */
+ .dragdroppable-column .dragdroppable-row .hover-menu--left {
+ left: ${theme.gridUnit * -3}px;
+ background: ${theme.colors.grayscale.light5};
+ border: 1px solid ${theme.colors.grayscale.light2};
+ }
+
+ .dashboard-component-tabs {
+ position: relative;
+ }
+
+ /* A column within a column or tabs has inset hover menu */
+ .dragdroppable-column .dragdroppable-column .hover-menu--top,
+ .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
+ top: ${theme.gridUnit * -3}px;
+ background: ${theme.colors.grayscale.light5};
+ border: 1px solid ${theme.colors.grayscale.light2};
+ }
+
+ /* move Tabs hover menu to top near actual Tabs */
+ .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
+ top: 0;
+ transform: unset;
+ background: transparent;
+ }
+
+ /* push Chart actions to upper right */
+ .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
+ .dragdroppable .dashboard-component-header .hover-menu--top {
+ right: ${theme.gridUnit * 2}px;
+ top: ${theme.gridUnit * 2}px;
+ background: transparent;
+ border: none;
+ transform: unset;
+ left: unset;
+ }
+ div:hover > .hover-menu-container .hover-menu,
+ .hover-menu-container .hover-menu:hover {
+ opacity: 1;
+ }
+
+ p {
+ margin: 0 0 ${theme.gridUnit * 2}px 0;
+ }
+
+ i.danger {
+ color: ${theme.colors.error.base};
+ }
+
+ i.warning {
+ color: ${theme.colors.alert.base};
+ }
+ `}
+`;
+
+type Props = {};
+
+const DashboardWrapper: React.FC<Props> = ({ children }) => {
+ const editMode = useSelector<RootState, boolean>(
+ state => state.dashboardState.editMode,
+ );
+ const dragDropManager = useDragDropManager();
+ const [isDragged, setIsDragged] = React.useState(
+ dragDropManager.getMonitor().isDragging(),
+ );
+
+ useEffect(() => {
+ const monitor = dragDropManager.getMonitor();
+ const unsub = monitor.subscribeToStateChange(() => {
+ setIsDragged(monitor.isDragging());
+ });
+
+ return () => {
+ unsub();
+ };
+ }, [dragDropManager]);
+
+ return (
+ <StyledDiv
+ className={classNames({
+ 'dragdroppable--dragging': editMode && isDragged,
+ })}
+ >
+ {children}
+ </StyledDiv>
+ );
+};
+
+export default DashboardWrapper;
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
index 601dbac4a4..70cf65218f 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
import { addAlpha, css, styled, t } from '@superset-ui/core';
import { EmptyStateBig } from 'src/components/EmptyState';
import { componentShape } from '../util/propShapes';
@@ -76,10 +77,14 @@ const GridContent = styled.div`
& > .empty-droptarget:first-child {
height: ${theme.gridUnit * 12}px;
margin-top: ${theme.gridUnit * -6}px;
- margin-bottom: ${theme.gridUnit * -6}px;
}
- & > .empty-droptarget:only-child {
+ & > .empty-droptarget:last-child {
+ height: ${theme.gridUnit * 12}px;
+ margin-top: ${theme.gridUnit * -6}px;
+ }
+
+ & > .empty-droptarget.empty-droptarget--full:only-child {
height: 80vh;
}
`}
@@ -270,10 +275,14 @@ class DashboardGrid extends React.PureComponent {
index={0}
orientation="column"
onDrop={this.handleTopDropTargetDrop}
- className="empty-droptarget"
+ className={classNames({
+ 'empty-droptarget': true,
+ 'empty-droptarget--full':
+ gridComponent?.children?.length === 0,
+ })}
editMode
>
- {renderDraggableContentBottom}
+ {renderDraggableContentTop}
</DragDroppable>
)}
{gridComponent?.children?.map((id, index) => (
@@ -304,7 +313,7 @@ class DashboardGrid extends React.PureComponent {
className="empty-droptarget"
editMode
>
- {renderDraggableContentTop}
+ {renderDraggableContentBottom}
</DragDroppable>
)}
{isResizing &&
diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
index 3bc9f4d299..6a49f98875 100644
--- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
@@ -90,6 +90,11 @@ const DragDroppableStyles = styled.div`
z-index: 10;
}
+ &.empty-droptarget--full > .drop-indicator--top {
+ height: 100%;
+ opacity: 0.3;
+ }
+
& {
.drop-indicator {
display: block;
@@ -99,7 +104,7 @@ const DragDroppableStyles = styled.div`
}
.drop-indicator--top {
- top: 0;
+ top: ${-theme.gridUnit - 2}px;
left: 0;
height: ${theme.gridUnit}px;
width: 100%;
@@ -107,7 +112,7 @@ const DragDroppableStyles = styled.div`
}
.drop-indicator--bottom {
- top: 100%;
+ bottom: ${-theme.gridUnit - 2}px;
left: 0;
height: ${theme.gridUnit}px;
width: 100%;
@@ -116,7 +121,7 @@ const DragDroppableStyles = styled.div`
.drop-indicator--right {
top: 0;
- left: 100%;
+ left: calc(100% - ${theme.gridUnit}px);
height: 100%;
width: ${theme.gridUnit}px;
min-height: ${theme.gridUnit * 4}px;
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index 32ac77936c..d1d08176ba 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { styled, t } from '@superset-ui/core';
@@ -173,7 +174,10 @@ class Tab extends React.PureComponent {
depth={depth}
onDrop={this.handleTopDropTargetDrop}
editMode
- className="empty-droptarget"
+ className={classNames({
+ 'empty-droptarget': true,
+ 'empty-droptarget--full': tabComponent.children.length === 0,
+ })}
>
{renderDraggableContentTop}
</DragDroppable>
@@ -234,7 +238,7 @@ class Tab extends React.PureComponent {
/>
))}
{/* Make bottom of tab droppable */}
- {editMode && (
+ {editMode && tabComponent.children.length > 0 && (
<DragDroppable
component={tabComponent}
parentComponent={tabParentComponent}
(superset) 07/07: fix(post-processing): handle missing values in cumulative operator (#26429)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 439979c73a3839e48595ba63dc596fdd45fc75fa
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Tue Jan 9 03:56:19 2024 -0800
fix(post-processing): handle missing values in cumulative operator (#26429)
(cherry picked from commit ff025b78f35f917f9d4a959154047727d3fc46f7)
---
superset/utils/pandas_postprocessing/cum.py | 1 +
tests/unit_tests/fixtures/dataframes.py | 5 +++++
tests/unit_tests/pandas_postprocessing/test_cum.py | 14 ++++++++++++++
3 files changed, 20 insertions(+)
diff --git a/superset/utils/pandas_postprocessing/cum.py b/superset/utils/pandas_postprocessing/cum.py
index 128fa970f5..d3eb969f79 100644
--- a/superset/utils/pandas_postprocessing/cum.py
+++ b/superset/utils/pandas_postprocessing/cum.py
@@ -46,6 +46,7 @@ def cum(
"""
columns = columns or {}
df_cum = df.loc[:, columns.keys()]
+ df_cum = df_cum.fillna(0)
operation = "cum" + operator
if operation not in ALLOWLIST_CUMULATIVE_FUNCTIONS or not hasattr(
df_cum, operation
diff --git a/tests/unit_tests/fixtures/dataframes.py b/tests/unit_tests/fixtures/dataframes.py
index 31a275b735..e1499792cb 100644
--- a/tests/unit_tests/fixtures/dataframes.py
+++ b/tests/unit_tests/fixtures/dataframes.py
@@ -130,6 +130,11 @@ timeseries_df = DataFrame(
data={"label": ["x", "y", "z", "q"], "y": [1.0, 2.0, 3.0, 4.0]},
)
+timeseries_with_gap_df = DataFrame(
+ index=to_datetime(["2019-01-01", "2019-01-02", "2019-01-05", "2019-01-07"]),
+ data={"label": ["x", "y", "z", "q"], "y": [1.0, 2.0, None, 4.0]},
+)
+
timeseries_df2 = DataFrame(
index=to_datetime(["2019-01-01", "2019-01-02", "2019-01-05", "2019-01-07"]),
data={
diff --git a/tests/unit_tests/pandas_postprocessing/test_cum.py b/tests/unit_tests/pandas_postprocessing/test_cum.py
index 130e060252..25d7fd045f 100644
--- a/tests/unit_tests/pandas_postprocessing/test_cum.py
+++ b/tests/unit_tests/pandas_postprocessing/test_cum.py
@@ -24,6 +24,7 @@ from tests.unit_tests.fixtures.dataframes import (
multiple_metrics_df,
single_metric_df,
timeseries_df,
+ timeseries_with_gap_df,
)
from tests.unit_tests.pandas_postprocessing.utils import series_to_list
@@ -77,6 +78,19 @@ def test_cum():
)
+def test_cum_with_gap():
+ # create new column (cumsum)
+ post_df = pp.cum(
+ df=timeseries_with_gap_df,
+ columns={"y": "y2"},
+ operator="sum",
+ )
+ assert post_df.columns.tolist() == ["label", "y", "y2"]
+ assert series_to_list(post_df["label"]) == ["x", "y", "z", "q"]
+ assert series_to_list(post_df["y"]) == [1.0, 2.0, None, 4.0]
+ assert series_to_list(post_df["y2"]) == [1.0, 3.0, 3.0, 7.0]
+
+
def test_cum_after_pivot_with_single_metric():
pivot_df = pp.pivot(
df=single_metric_df,
(superset) 05/07: fix(plugin-chart-echarts): support forced categorical x-axis (#26404)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 52109e5f242d0fd99b5958b7901b9f28c61cfb29
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Mon Jan 8 12:04:22 2024 -0800
fix(plugin-chart-echarts): support forced categorical x-axis (#26404)
---
.../superset-ui-chart-controls/src/constants.ts | 11 +--
.../superset-ui-chart-controls/src/fixtures.ts | 12 +--
.../src/sections/echartsTimeSeriesQuery.tsx | 2 +
.../src/shared-controls/customControls.tsx | 54 ++++++++++---
.../superset-ui-chart-controls/src/types.ts | 6 +-
.../src/utils/checkColumnType.ts | 49 ++++++++++++
.../src/utils/columnChoices.ts | 8 +-
.../superset-ui-chart-controls/src/utils/index.ts | 1 +
.../test/utils/checkColumnType.test.ts | 48 +++++++++++
.../test/utils/columnChoices.test.tsx | 16 +++-
.../test/utils/getTemporalColumns.test.ts | 9 ++-
.../superset-ui-core/src/query/types/Query.ts | 11 +++
.../src/MixedTimeseries/transformProps.ts | 3 +-
.../src/MixedTimeseries/types.ts | 1 +
.../src/Timeseries/constants.ts | 1 +
.../src/Timeseries/transformProps.ts | 3 +-
.../plugin-chart-echarts/src/Timeseries/types.ts | 1 +
.../plugins/plugin-chart-echarts/src/controls.tsx | 11 +++
.../plugin-chart-echarts/src/utils/series.ts | 10 ++-
.../plugin-chart-echarts/test/utils/series.test.ts | 26 ++++--
superset-frontend/src/SqlLab/fixtures.ts | 6 ++
superset/result_set.py | 16 +++-
tests/integration_tests/result_set_tests.py | 92 +++++++++++++++++++---
23 files changed, 346 insertions(+), 51 deletions(-)
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts
index ae6dd8f894..1aefc25464 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts
@@ -17,12 +17,11 @@
* under the License.
*/
import {
- t,
- QueryMode,
DTTM_ALIAS,
GenericDataType,
QueryColumn,
- DatasourceType,
+ QueryMode,
+ t,
} from '@superset-ui/core';
import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
@@ -43,6 +42,7 @@ export const COLUMN_NAME_ALIASES: Record<string, string> = {
export const DATASET_TIME_COLUMN_OPTION: ColumnMeta = {
verbose_name: COLUMN_NAME_ALIASES[DTTM_ALIAS],
column_name: DTTM_ALIAS,
+ type: 'TIMESTAMP',
type_generic: GenericDataType.TEMPORAL,
description: t(
'A reference to the [Time] configuration, taking granularity into account',
@@ -51,8 +51,9 @@ export const DATASET_TIME_COLUMN_OPTION: ColumnMeta = {
export const QUERY_TIME_COLUMN_OPTION: QueryColumn = {
column_name: DTTM_ALIAS,
- type: DatasourceType.Query,
- is_dttm: false,
+ is_dttm: true,
+ type: 'TIMESTAMP',
+ type_generic: GenericDataType.TEMPORAL,
};
export const QueryModeLabel = {
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/fixtures.ts b/superset-frontend/packages/superset-ui-chart-controls/src/fixtures.ts
index cc0b678f6d..b12d5be048 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/fixtures.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/fixtures.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { DatasourceType } from '@superset-ui/core';
+import { DatasourceType, GenericDataType } from '@superset-ui/core';
import { Dataset } from './types';
export const TestDataset: Dataset = {
@@ -37,7 +37,7 @@ export const TestDataset: Dataset = {
is_dttm: false,
python_date_format: null,
type: 'BIGINT',
- type_generic: 0,
+ type_generic: GenericDataType.NUMERIC,
verbose_name: null,
warning_markdown: null,
},
@@ -55,7 +55,7 @@ export const TestDataset: Dataset = {
is_dttm: false,
python_date_format: null,
type: 'VARCHAR(16)',
- type_generic: 1,
+ type_generic: GenericDataType.STRING,
verbose_name: '',
warning_markdown: null,
},
@@ -73,7 +73,7 @@ export const TestDataset: Dataset = {
is_dttm: false,
python_date_format: null,
type: 'VARCHAR(10)',
- type_generic: 1,
+ type_generic: GenericDataType.STRING,
verbose_name: null,
warning_markdown: null,
},
@@ -91,7 +91,7 @@ export const TestDataset: Dataset = {
is_dttm: true,
python_date_format: null,
type: 'TIMESTAMP WITHOUT TIME ZONE',
- type_generic: 2,
+ type_generic: GenericDataType.TEMPORAL,
verbose_name: null,
warning_markdown: null,
},
@@ -109,7 +109,7 @@ export const TestDataset: Dataset = {
is_dttm: false,
python_date_format: null,
type: 'VARCHAR(255)',
- type_generic: 1,
+ type_generic: GenericDataType.STRING,
verbose_name: null,
warning_markdown: null,
},
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/sections/echartsTimeSeriesQuery.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/sections/echartsTimeSeriesQuery.tsx
index 53c9aa7447..596c6f0e55 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/sections/echartsTimeSeriesQuery.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/sections/echartsTimeSeriesQuery.tsx
@@ -20,6 +20,7 @@ import { hasGenericChartAxes, t } from '@superset-ui/core';
import { ControlPanelSectionConfig, ControlSetRow } from '../types';
import {
contributionModeControl,
+ xAxisForceCategoricalControl,
xAxisSortAscControl,
xAxisSortControl,
xAxisSortSeriesAscendingControl,
@@ -55,6 +56,7 @@ export const echartsTimeSeriesQueryWithXAxisSort: ControlPanelSectionConfig = {
controlSetRows: [
[hasGenericChartAxes ? 'x_axis' : null],
[hasGenericChartAxes ? 'time_grain_sqla' : null],
+ [hasGenericChartAxes ? xAxisForceCategoricalControl : null],
[hasGenericChartAxes ? xAxisSortControl : null],
[hasGenericChartAxes ? xAxisSortAscControl : null],
[hasGenericChartAxes ? xAxisSortSeriesControl : null],
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/customControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/customControls.tsx
index 82ba6dfeeb..b0f3006353 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/customControls.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/customControls.tsx
@@ -20,9 +20,9 @@
import {
ContributionType,
ensureIsArray,
+ GenericDataType,
getColumnLabel,
getMetricLabel,
- isDefined,
QueryFormColumn,
QueryFormMetric,
t,
@@ -38,6 +38,7 @@ import {
DEFAULT_XAXIS_SORT_SERIES_DATA,
SORT_SERIES_CHOICES,
} from '../constants';
+import { checkColumnType } from '../utils/checkColumnType';
export const contributionModeControl = {
name: 'contributionMode',
@@ -54,18 +55,29 @@ export const contributionModeControl = {
},
};
-function isTemporal(controls: ControlStateMapping): boolean {
- return !(
- isDefined(controls?.x_axis?.value) &&
- !isTemporalColumn(
+function isForcedCategorical(controls: ControlStateMapping): boolean {
+ return (
+ checkColumnType(
getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
controls?.datasource?.datasource,
+ [GenericDataType.NUMERIC],
+ ) && !!controls?.xAxisForceCategorical?.value
+ );
+}
+
+function isSortable(controls: ControlStateMapping): boolean {
+ return (
+ isForcedCategorical(controls) ||
+ checkColumnType(
+ getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
+ controls?.datasource?.datasource,
+ [GenericDataType.STRING, GenericDataType.BOOLEAN],
)
);
}
const xAxisSortVisibility = ({ controls }: { controls: ControlStateMapping }) =>
- !isTemporal(controls) &&
+ isSortable(controls) &&
ensureIsArray(controls?.groupby?.value).length === 0 &&
ensureIsArray(controls?.metrics?.value).length === 1;
@@ -74,7 +86,7 @@ const xAxisMultiSortVisibility = ({
}: {
controls: ControlStateMapping;
}) =>
- !isTemporal(controls) &&
+ isSortable(controls) &&
(!!ensureIsArray(controls?.groupby?.value).length ||
ensureIsArray(controls?.metrics?.value).length > 1);
@@ -141,7 +153,29 @@ export const xAxisSortAscControl = {
: t('X-Axis Sort Ascending'),
default: true,
description: t('Whether to sort ascending or descending on the base Axis.'),
- visibility: xAxisSortVisibility,
+ visibility: ({ controls }: { controls: ControlStateMapping }) =>
+ controls?.x_axis_sort?.value !== undefined &&
+ xAxisSortVisibility({ controls }),
+ },
+};
+
+export const xAxisForceCategoricalControl = {
+ name: 'xAxisForceCategorical',
+ config: {
+ type: 'CheckboxControl',
+ label: () => t('Force categorical'),
+ default: false,
+ description: t('Treat values as categorical.'),
+ initialValue: (control: ControlState, state: ControlPanelState | null) =>
+ state?.form_data?.x_axis_sort !== undefined || control.value,
+ renderTrigger: true,
+ visibility: ({ controls }: { controls: ControlStateMapping }) =>
+ checkColumnType(
+ getColumnLabel(controls?.x_axis?.value as QueryFormColumn),
+ controls?.datasource?.datasource,
+ [GenericDataType.NUMERIC],
+ ),
+ shouldMapStateToProps: () => true,
},
};
@@ -173,6 +207,8 @@ export const xAxisSortSeriesAscendingControl = {
default: DEFAULT_XAXIS_SORT_SERIES_DATA.sort_series_ascending,
description: t('Whether to sort ascending or descending on the base Axis.'),
renderTrigger: true,
- visibility: xAxisMultiSortVisibility,
+ visibility: ({ controls }: { controls: ControlStateMapping }) =>
+ controls?.x_axis_sort_series?.value !== undefined &&
+ xAxisMultiSortVisibility({ controls }),
},
};
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 889bc9a47c..bbfaf904fb 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts
@@ -479,13 +479,15 @@ export function isControlPanelSectionConfig(
export function isDataset(
datasource: Dataset | QueryResponse | null | undefined,
): datasource is Dataset {
- return !!datasource && 'columns' in datasource;
+ return (
+ !!datasource && 'columns' in datasource && !('sqlEditorId' in datasource)
+ );
}
export function isQueryResponse(
datasource: Dataset | QueryResponse | null | undefined,
): datasource is QueryResponse {
- return !!datasource && 'results' in datasource && 'sql' in datasource;
+ return !!datasource && 'results' in datasource && 'sqlEditorId' in datasource;
}
export enum SortSeriesType {
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/checkColumnType.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/checkColumnType.ts
new file mode 100644
index 0000000000..202b9605d5
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/checkColumnType.ts
@@ -0,0 +1,49 @@
+/**
+ * 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 { ensureIsArray, GenericDataType, ValueOf } from '@superset-ui/core';
+import {
+ ControlPanelState,
+ isDataset,
+ isQueryResponse,
+} from '@superset-ui/chart-controls';
+
+export function checkColumnType(
+ columnName: string,
+ datasource: ValueOf<Pick<ControlPanelState, 'datasource'>>,
+ columnTypes: GenericDataType[],
+): boolean {
+ if (isDataset(datasource)) {
+ return ensureIsArray(datasource.columns).some(
+ c =>
+ c.type_generic !== undefined &&
+ columnTypes.includes(c.type_generic) &&
+ columnName === c.column_name,
+ );
+ }
+ if (isQueryResponse(datasource)) {
+ return ensureIsArray(datasource.columns)
+ .filter(
+ c =>
+ c.type_generic !== undefined && columnTypes.includes(c.type_generic),
+ )
+ .map(c => c.column_name)
+ .some(c => columnName === c);
+ }
+ return false;
+}
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts
index c76cd79031..f056151750 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts
@@ -17,7 +17,7 @@
* under the License.
*/
import { QueryResponse } from '@superset-ui/core';
-import { Dataset, isColumnMeta, isDataset } from '../types';
+import { Dataset, isDataset, isQueryResponse } from '../types';
/**
* Convert Datasource columns to column choices
@@ -25,11 +25,13 @@ import { Dataset, isColumnMeta, isDataset } from '../types';
export default function columnChoices(
datasource?: Dataset | QueryResponse | null,
): [string, string][] {
- if (isDataset(datasource) && isColumnMeta(datasource.columns[0])) {
+ if (isDataset(datasource) || isQueryResponse(datasource)) {
return datasource.columns
.map((col): [string, string] => [
col.column_name,
- col.verbose_name || col.column_name,
+ 'verbose_name' in col
+ ? col.verbose_name || col.column_name
+ : col.column_name,
])
.sort((opt1, opt2) =>
opt1[1].toLowerCase() > opt2[1].toLowerCase() ? 1 : -1,
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts
index 4fa4243c1e..208d708a96 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+export * from './checkColumnType';
export * from './selectOptions';
export * from './D3Formatting';
export * from './expandControlConfig';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/checkColumnType.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/checkColumnType.test.ts
new file mode 100644
index 0000000000..44ebbf605c
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/checkColumnType.test.ts
@@ -0,0 +1,48 @@
+/**
+ * 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 { GenericDataType, testQueryResponse } from '@superset-ui/core';
+import { checkColumnType, TestDataset } from '../../src';
+
+test('checkColumnType columns from a Dataset', () => {
+ expect(
+ checkColumnType('num', TestDataset, [GenericDataType.NUMERIC]),
+ ).toEqual(true);
+ expect(checkColumnType('num', TestDataset, [GenericDataType.STRING])).toEqual(
+ false,
+ );
+ expect(
+ checkColumnType('gender', TestDataset, [GenericDataType.STRING]),
+ ).toEqual(true);
+ expect(
+ checkColumnType('gender', TestDataset, [GenericDataType.NUMERIC]),
+ ).toEqual(false);
+});
+
+test('checkColumnType from a QueryResponse', () => {
+ expect(
+ checkColumnType('Column 1', testQueryResponse, [GenericDataType.STRING]),
+ ).toEqual(true);
+ expect(
+ checkColumnType('Column 1', testQueryResponse, [GenericDataType.NUMERIC]),
+ ).toEqual(false);
+});
+
+test('checkColumnType from null', () => {
+ expect(checkColumnType('col', null, [])).toEqual(false);
+});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx
index aaaccda95d..f6a9a4b271 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx
@@ -16,7 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { DatasourceType, testQueryResponse } from '@superset-ui/core';
+import {
+ DatasourceType,
+ GenericDataType,
+ testQueryResponse,
+} from '@superset-ui/core';
import { columnChoices } from '../../src';
describe('columnChoices()', () => {
@@ -31,14 +35,20 @@ describe('columnChoices()', () => {
columns: [
{
column_name: 'fiz',
+ type: 'INT',
+ type_generic: GenericDataType.NUMERIC,
},
{
column_name: 'about',
verbose_name: 'right',
+ type: 'VARCHAR',
+ type_generic: GenericDataType.STRING,
},
{
column_name: 'foo',
- verbose_name: 'bar',
+ verbose_name: undefined,
+ type: 'TIMESTAMP',
+ type_generic: GenericDataType.TEMPORAL,
},
],
verbose_map: {},
@@ -48,8 +58,8 @@ describe('columnChoices()', () => {
description: 'this is my datasource',
}),
).toEqual([
- ['foo', 'bar'],
['fiz', 'fiz'],
+ ['foo', 'foo'],
['about', 'right'],
]);
});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/getTemporalColumns.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getTemporalColumns.test.ts
index 7227173045..f0a6529de7 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/getTemporalColumns.test.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getTemporalColumns.test.ts
@@ -16,7 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { testQueryResponse, testQueryResults } from '@superset-ui/core';
+import {
+ GenericDataType,
+ testQueryResponse,
+ testQueryResults,
+} from '@superset-ui/core';
import {
Dataset,
getTemporalColumns,
@@ -55,8 +59,9 @@ test('get temporal columns from a QueryResponse', () => {
temporalColumns: [
{
column_name: 'Column 2',
- type: 'TIMESTAMP',
is_dttm: true,
+ type: 'TIMESTAMP',
+ type_generic: GenericDataType.TEMPORAL,
},
],
defaultTemporalColumn: 'Column 2',
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
index f42b01abdc..1271be2fff 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
@@ -31,6 +31,7 @@ import { Maybe } from '../../types';
import { PostProcessingRule } from './PostProcessing';
import { JsonObject } from '../../connection';
import { TimeGranularity } from '../../time-format';
+import { GenericDataType } from './QueryResponse';
export type BaseQueryObjectFilterClause = {
col: QueryFormColumn;
@@ -250,6 +251,7 @@ export type QueryColumn = {
name?: string;
column_name: string;
type: string | null;
+ type_generic: GenericDataType;
is_dttm: boolean;
};
@@ -383,16 +385,19 @@ export const testQuery: Query = {
column_name: 'Column 1',
type: 'STRING',
is_dttm: false,
+ type_generic: GenericDataType.STRING,
},
{
column_name: 'Column 3',
type: 'STRING',
is_dttm: false,
+ type_generic: GenericDataType.STRING,
},
{
column_name: 'Column 2',
type: 'TIMESTAMP',
is_dttm: true,
+ type_generic: GenericDataType.TEMPORAL,
},
],
};
@@ -404,16 +409,19 @@ export const testQueryResults = {
{
column_name: 'Column 1',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
is_dttm: false,
},
{
column_name: 'Column 3',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
is_dttm: false,
},
{
column_name: 'Column 2',
type: 'TIMESTAMP',
+ type_generic: GenericDataType.TEMPORAL,
is_dttm: true,
},
],
@@ -425,16 +433,19 @@ export const testQueryResults = {
{
column_name: 'Column 1',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
is_dttm: false,
},
{
column_name: 'Column 3',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
is_dttm: false,
},
{
column_name: 'Column 2',
type: 'TIMESTAMP',
+ type_generic: GenericDataType.TEMPORAL,
is_dttm: true,
},
],
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
index 8709286aee..98db1fa299 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
@@ -187,6 +187,7 @@ export default function transformProps(
groupby,
groupbyB,
xAxis: xAxisOrig,
+ xAxisForceCategorical,
xAxisTitle,
yAxisTitle,
xAxisTitleMargin,
@@ -225,7 +226,7 @@ export default function transformProps(
const dataTypes = getColtypesMapping(queriesData[0]);
const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig];
- const xAxisType = getAxisType(stack, xAxisDataType);
+ const xAxisType = getAxisType(stack, xAxisForceCategorical, xAxisDataType);
const series: SeriesOption[] = [];
const formatter = contributionMode
? getNumberFormatter(',.0%')
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts
index c027ed7ac4..e79523d176 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/types.ts
@@ -106,6 +106,7 @@ export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = {
yAxisTitleSecondary: DEFAULT_TITLE_FORM_DATA.yAxisTitle,
tooltipTimeFormat: TIMESERIES_DEFAULTS.tooltipTimeFormat,
xAxisBounds: TIMESERIES_DEFAULTS.xAxisBounds,
+ xAxisForceCategorical: TIMESERIES_DEFAULTS.xAxisForceCategorical,
xAxisTimeFormat: TIMESERIES_DEFAULTS.xAxisTimeFormat,
area: TIMESERIES_DEFAULTS.area,
areaB: TIMESERIES_DEFAULTS.area,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts
index a95a14077d..0a065b178e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts
@@ -63,6 +63,7 @@ export const DEFAULT_FORM_DATA: EchartsTimeseriesFormData = {
zoomable: false,
richTooltip: true,
xAxisLabelRotation: 0,
+ xAxisForceCategorical: false,
groupby: [],
showValue: false,
onlyTotal: false,
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 63a4cb9373..519323a62e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -165,6 +165,7 @@ export default function transformProps(
truncateYAxis,
xAxis: xAxisOrig,
xAxisBounds,
+ xAxisForceCategorical,
xAxisLabelRotation,
xAxisSortSeries,
xAxisSortSeriesAscending,
@@ -246,7 +247,7 @@ export default function transformProps(
const isAreaExpand = stack === StackControlsValue.Expand;
const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig];
- const xAxisType = getAxisType(stack, xAxisDataType);
+ const xAxisType = getAxisType(stack, xAxisForceCategorical, xAxisDataType);
const series: SeriesOption[] = [];
const forcePercentFormatter = Boolean(contributionMode || isAreaExpand);
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts
index 751f262ec1..692abb2c79 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts
@@ -79,6 +79,7 @@ export type EchartsTimeseriesFormData = QueryFormData & {
truncateXAxis: boolean;
truncateYAxis: boolean;
yAxisFormat?: string;
+ xAxisForceCategorical?: boolean;
xAxisTimeFormat?: string;
timeGrainSqla?: TimeGranularity;
xAxisBounds: [number | undefined | null, number | undefined | null];
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
index e87b68d9ce..4817230b96 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/controls.tsx
@@ -290,3 +290,14 @@ export const minorTicks: ControlSetItem = {
description: t('Show minor ticks on axes.'),
},
};
+
+export const forceCategorical: ControlSetItem = {
+ name: 'forceCategorical',
+ config: {
+ type: 'CheckboxControl',
+ label: t('Force categorical'),
+ default: false,
+ renderTrigger: true,
+ description: t('Make the x-axis categorical'),
+ },
+};
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
index a294fa44c3..6a51e7cbf7 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
@@ -229,8 +229,10 @@ export function sortRows(
}
const value =
- xAxisSortSeries === SortSeriesType.Name && typeof sortKey === 'string'
- ? sortKey.toLowerCase()
+ xAxisSortSeries === SortSeriesType.Name
+ ? typeof sortKey === 'string'
+ ? sortKey.toLowerCase()
+ : sortKey
: aggregate;
return {
@@ -515,8 +517,12 @@ export function sanitizeHtml(text: string): string {
export function getAxisType(
stack: StackType,
+ forceCategorical?: boolean,
dataType?: GenericDataType,
): AxisType {
+ if (forceCategorical) {
+ return AxisType.category;
+ }
if (dataType === GenericDataType.TEMPORAL) {
return AxisType.time;
}
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
index 8e55b125b8..3d7d21c8d0 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
@@ -878,14 +878,28 @@ test('calculateLowerLogTick', () => {
expect(calculateLowerLogTick(0.005)).toEqual(0.001);
});
-test('getAxisType', () => {
- expect(getAxisType(false, GenericDataType.TEMPORAL)).toEqual(AxisType.time);
- expect(getAxisType(false, GenericDataType.NUMERIC)).toEqual(AxisType.value);
- expect(getAxisType(true, GenericDataType.NUMERIC)).toEqual(AxisType.category);
- expect(getAxisType(false, GenericDataType.BOOLEAN)).toEqual(
+test('getAxisType without forced categorical', () => {
+ expect(getAxisType(false, false, GenericDataType.TEMPORAL)).toEqual(
+ AxisType.time,
+ );
+ expect(getAxisType(false, false, GenericDataType.NUMERIC)).toEqual(
+ AxisType.value,
+ );
+ expect(getAxisType(true, false, GenericDataType.NUMERIC)).toEqual(
+ AxisType.category,
+ );
+ expect(getAxisType(false, false, GenericDataType.BOOLEAN)).toEqual(
+ AxisType.category,
+ );
+ expect(getAxisType(false, false, GenericDataType.STRING)).toEqual(
+ AxisType.category,
+ );
+});
+
+test('getAxisType with forced categorical', () => {
+ expect(getAxisType(false, true, GenericDataType.NUMERIC)).toEqual(
AxisType.category,
);
- expect(getAxisType(false, GenericDataType.STRING)).toEqual(AxisType.category);
});
test('getMinAndMaxFromBounds returns empty object when not truncating', () => {
diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts
index 0afd1c4149..b64dbefdd0 100644
--- a/superset-frontend/src/SqlLab/fixtures.ts
+++ b/superset-frontend/src/SqlLab/fixtures.ts
@@ -22,6 +22,7 @@ import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement';
import {
DatasourceType,
denormalizeTimestamp,
+ GenericDataType,
QueryResponse,
QueryState,
} from '@superset-ui/core';
@@ -579,11 +580,13 @@ const baseQuery: QueryResponse = {
is_dttm: true,
column_name: 'ds',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
},
{
is_dttm: false,
column_name: 'gender',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
},
],
selected_columns: [
@@ -591,11 +594,13 @@ const baseQuery: QueryResponse = {
is_dttm: true,
column_name: 'ds',
type: 'STRING',
+ type_generic: GenericDataType.TEMPORAL,
},
{
is_dttm: false,
column_name: 'gender',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
},
],
expanded_columns: [
@@ -603,6 +608,7 @@ const baseQuery: QueryResponse = {
is_dttm: true,
column_name: 'ds',
type: 'STRING',
+ type_generic: GenericDataType.STRING,
},
],
data: [
diff --git a/superset/result_set.py b/superset/result_set.py
index 4ca39cba2b..2c29dec3bd 100644
--- a/superset/result_set.py
+++ b/superset/result_set.py
@@ -29,6 +29,7 @@ from numpy.typing import NDArray
from superset.db_engine_specs import BaseEngineSpec
from superset.superset_typing import DbapiDescription, DbapiResult, ResultSetColumnType
from superset.utils import core as utils
+from superset.utils.core import GenericDataType
logger = logging.getLogger(__name__)
@@ -223,6 +224,18 @@ class SupersetResultSet:
return False
return column_spec.is_dttm
+ def type_generic(
+ self, db_type_str: Optional[str]
+ ) -> Optional[utils.GenericDataType]:
+ column_spec = self.db_engine_spec.get_column_spec(db_type_str)
+ if column_spec is None:
+ return None
+
+ if column_spec.is_dttm:
+ return GenericDataType.TEMPORAL
+
+ return column_spec.generic_type
+
def data_type(self, col_name: str, pa_dtype: pa.DataType) -> Optional[str]:
"""Given a pyarrow data type, Returns a generic database type"""
if set_type := self._type_dict.get(col_name):
@@ -256,7 +269,8 @@ class SupersetResultSet:
"column_name": col.name,
"name": col.name,
"type": db_type_str,
- "is_dttm": self.is_temporal(db_type_str),
+ "type_generic": self.type_generic(db_type_str),
+ "is_dttm": self.is_temporal(db_type_str) or False,
}
columns.append(column)
return columns
diff --git a/tests/integration_tests/result_set_tests.py b/tests/integration_tests/result_set_tests.py
index a39e0ac0d4..3e2b3656c2 100644
--- a/tests/integration_tests/result_set_tests.py
+++ b/tests/integration_tests/result_set_tests.py
@@ -21,6 +21,7 @@ import tests.integration_tests.test_app
from superset.dataframe import df_to_records
from superset.db_engine_specs import BaseEngineSpec
from superset.result_set import dedup, SupersetResultSet
+from superset.utils.core import GenericDataType
from .base_tests import SupersetTestCase
@@ -48,9 +49,27 @@ class TestSupersetResultSet(SupersetTestCase):
self.assertEqual(
results.columns,
[
- {"is_dttm": False, "type": "STRING", "column_name": "a", "name": "a"},
- {"is_dttm": False, "type": "STRING", "column_name": "b", "name": "b"},
- {"is_dttm": False, "type": "STRING", "column_name": "c", "name": "c"},
+ {
+ "is_dttm": False,
+ "type": "STRING",
+ "type_generic": GenericDataType.STRING,
+ "column_name": "a",
+ "name": "a",
+ },
+ {
+ "is_dttm": False,
+ "type": "STRING",
+ "type_generic": GenericDataType.STRING,
+ "column_name": "b",
+ "name": "b",
+ },
+ {
+ "is_dttm": False,
+ "type": "STRING",
+ "type_generic": GenericDataType.STRING,
+ "column_name": "c",
+ "name": "c",
+ },
],
)
@@ -61,8 +80,20 @@ class TestSupersetResultSet(SupersetTestCase):
self.assertEqual(
results.columns,
[
- {"is_dttm": False, "type": "STRING", "column_name": "a", "name": "a"},
- {"is_dttm": False, "type": "INT", "column_name": "b", "name": "b"},
+ {
+ "is_dttm": False,
+ "type": "STRING",
+ "type_generic": GenericDataType.STRING,
+ "column_name": "a",
+ "name": "a",
+ },
+ {
+ "is_dttm": False,
+ "type": "INT",
+ "type_generic": GenericDataType.NUMERIC,
+ "column_name": "b",
+ "name": "b",
+ },
],
)
@@ -76,11 +107,41 @@ class TestSupersetResultSet(SupersetTestCase):
self.assertEqual(
results.columns,
[
- {"is_dttm": False, "type": "FLOAT", "column_name": "a", "name": "a"},
- {"is_dttm": False, "type": "INT", "column_name": "b", "name": "b"},
- {"is_dttm": False, "type": "STRING", "column_name": "c", "name": "c"},
- {"is_dttm": True, "type": "DATETIME", "column_name": "d", "name": "d"},
- {"is_dttm": False, "type": "BOOL", "column_name": "e", "name": "e"},
+ {
+ "is_dttm": False,
+ "type": "FLOAT",
+ "type_generic": GenericDataType.NUMERIC,
+ "column_name": "a",
+ "name": "a",
+ },
+ {
+ "is_dttm": False,
+ "type": "INT",
+ "type_generic": GenericDataType.NUMERIC,
+ "column_name": "b",
+ "name": "b",
+ },
+ {
+ "is_dttm": False,
+ "type": "STRING",
+ "type_generic": GenericDataType.STRING,
+ "column_name": "c",
+ "name": "c",
+ },
+ {
+ "is_dttm": True,
+ "type": "DATETIME",
+ "type_generic": GenericDataType.TEMPORAL,
+ "column_name": "d",
+ "name": "d",
+ },
+ {
+ "is_dttm": False,
+ "type": "BOOL",
+ "type_generic": GenericDataType.BOOLEAN,
+ "column_name": "e",
+ "name": "e",
+ },
],
)
@@ -108,6 +169,7 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("user_id", "bigint", None, None, None, None, True)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "BIGINT")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.NUMERIC)
def test_data_as_list_of_lists(self):
data = [[1, "a"], [2, "b"]]
@@ -127,6 +189,7 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("is_test", "bool", None, None, None, None, True)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "BOOL")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.BOOLEAN)
df = results.to_pandas_df()
self.assertEqual(
df_to_records(df),
@@ -158,9 +221,13 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("id",), ("dict_arr",), ("num_arr",), ("map_col",)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "INT")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.NUMERIC)
self.assertEqual(results.columns[1]["type"], "STRING")
+ self.assertEqual(results.columns[1]["type_generic"], GenericDataType.STRING)
self.assertEqual(results.columns[2]["type"], "STRING")
+ self.assertEqual(results.columns[2]["type_generic"], GenericDataType.STRING)
self.assertEqual(results.columns[3]["type"], "STRING")
+ self.assertEqual(results.columns[3]["type_generic"], GenericDataType.STRING)
df = results.to_pandas_df()
self.assertEqual(
df_to_records(df),
@@ -204,6 +271,7 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("metadata",)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "STRING")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.STRING)
df = results.to_pandas_df()
self.assertEqual(
df_to_records(df),
@@ -219,6 +287,7 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("metadata",)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "STRING")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.STRING)
df = results.to_pandas_df()
self.assertEqual(
df_to_records(df), [{"metadata": '[{"TestKey": [123456, "foo"]}]'}]
@@ -229,6 +298,7 @@ class TestSupersetResultSet(SupersetTestCase):
cursor_descr = [("ds", "timestamp", None, None, None, None, True)]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "TIMESTAMP")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.TEMPORAL)
def test_no_type_coercion(self):
data = [("a", 1), ("b", 2)]
@@ -238,7 +308,9 @@ class TestSupersetResultSet(SupersetTestCase):
]
results = SupersetResultSet(data, cursor_descr, BaseEngineSpec)
self.assertEqual(results.columns[0]["type"], "VARCHAR")
+ self.assertEqual(results.columns[0]["type_generic"], GenericDataType.STRING)
self.assertEqual(results.columns[1]["type"], "INT")
+ self.assertEqual(results.columns[1]["type_generic"], GenericDataType.NUMERIC)
def test_empty_data(self):
data = []
(superset) 01/07: fix(dashboard): Chart menu disable is fixed on chart-fullscreen in issue #25992 (#26410)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit f467310cea1c261cfdb845314aba37ffda68e775
Author: Siva Sathyaseelan <95...@users.noreply.github.com>
AuthorDate: Thu Jan 4 20:03:07 2024 +0530
fix(dashboard): Chart menu disable is fixed on chart-fullscreen in issue #25992 (#26410)
(cherry picked from commit d0ffe9af7c802b2a996e3b2029838eedb93aa03b)
---
superset-frontend/src/components/Dropdown/index.tsx | 8 +-------
.../src/components/ReportModal/HeaderReportDropdown/index.tsx | 7 +++++++
.../src/dashboard/components/SliceHeaderControls/index.tsx | 7 +++++++
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/superset-frontend/src/components/Dropdown/index.tsx b/superset-frontend/src/components/Dropdown/index.tsx
index c40f479579..1e2e03ceb2 100644
--- a/superset-frontend/src/components/Dropdown/index.tsx
+++ b/superset-frontend/src/components/Dropdown/index.tsx
@@ -104,12 +104,6 @@ interface ExtendedDropDownProps extends DropDownProps {
ref?: RefObject<HTMLDivElement>;
}
-// @z-index-below-dashboard-header (100) - 1 = 99
export const NoAnimationDropdown = (
props: ExtendedDropDownProps & { children?: React.ReactNode },
-) => (
- <AntdDropdown
- overlayStyle={{ zIndex: 99, animationDuration: '0s' }}
- {...props}
- />
-);
+) => <AntdDropdown overlayStyle={props.overlayStyle} {...props} />;
diff --git a/superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.tsx b/superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.tsx
index 4f560a2294..2e83dc3017 100644
--- a/superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.tsx
+++ b/superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.tsx
@@ -191,6 +191,12 @@ export default function HeaderReportDropDown({
const showReportSubMenu = report && setShowReportSubMenu && canAddReports();
+ // @z-index-below-dashboard-header (100) - 1 = 99
+ const dropdownOverlayStyle = {
+ zIndex: 99,
+ animationDuration: '0s',
+ };
+
useEffect(() => {
if (showReportSubMenu) {
setShowReportSubMenu(true);
@@ -288,6 +294,7 @@ export default function HeaderReportDropDown({
<>
<NoAnimationDropdown
overlay={menu()}
+ overlayStyle={dropdownOverlayStyle}
trigger={['click']}
getPopupContainer={(triggerNode: any) =>
triggerNode.closest('.action-button')
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
index 287d83692f..17d5bdc83e 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
@@ -373,6 +373,12 @@ const SliceHeaderControls = (props: SliceHeaderControlsPropsWithRouter) => {
? t('Exit fullscreen')
: t('Enter fullscreen');
+ // @z-index-below-dashboard-header (100) - 1 = 99 for !isFullSize and 101 for isFullSize
+ const dropdownOverlayStyle = {
+ zIndex: isFullSize ? 101 : 99,
+ animationDuration: '0s',
+ };
+
const menu = (
<Menu
onClick={handleMenuClick}
@@ -541,6 +547,7 @@ const SliceHeaderControls = (props: SliceHeaderControlsPropsWithRouter) => {
)}
<NoAnimationDropdown
overlay={menu}
+ overlayStyle={dropdownOverlayStyle}
trigger={['click']}
placement="bottomRight"
>
(superset) 06/07: fix(translations): Clear all (#26424)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 2a30d59b05a7368f8d603b66ce47cad20320adcd
Author: Xuebin Zhang <z....@gmail.com>
AuthorDate: Tue Jan 9 05:18:45 2024 +0800
fix(translations): Clear all (#26424)
(cherry picked from commit 4c2e818cd37ea0fde313ddacb71bf49990bc9ac5)
---
superset/translations/zh/LC_MESSAGES/messages.po | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/superset/translations/zh/LC_MESSAGES/messages.po b/superset/translations/zh/LC_MESSAGES/messages.po
index e05a11e2e9..422499b165 100644
--- a/superset/translations/zh/LC_MESSAGES/messages.po
+++ b/superset/translations/zh/LC_MESSAGES/messages.po
@@ -3723,12 +3723,12 @@ msgstr "清除"
#: superset-frontend/src/dashboard/components/nativeFilters/FilterBar/ActionButtons/index.tsx:152
msgid "Clear all"
-msgstr "清楚所有"
+msgstr "清除所有"
#: superset-frontend/src/components/Table/index.tsx:210
#, fuzzy
msgid "Clear all data"
-msgstr "清楚所有"
+msgstr "清除所有"
#: superset-frontend/src/explore/components/ControlPanelsContainer.tsx:675
#, fuzzy
@@ -19604,7 +19604,7 @@ msgstr "无法更新您的查询"
#: superset-frontend/plugins/legacy-plugin-chart-horizon/src/controlPanel.ts:86
#, fuzzy
msgid "overall"
-msgstr "清楚所有"
+msgstr "清除所有"
#: superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/controlPanel.ts:77
msgid "p-value precision"
(superset) 03/07: fix(chart): Resolve incorrect column customization when switching metrics in table chart (#26393)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 26b26e46e477265b146da4538e66702423b95be1
Author: Sonia Gautam <96...@users.noreply.github.com>
AuthorDate: Fri Jan 5 02:31:24 2024 +0700
fix(chart): Resolve incorrect column customization when switching metrics in table chart (#26393)
Co-authored-by: Sonia <so...@agoda.com>
(cherry picked from commit dfde2adf27faef3e73c44e7280e77d9d3928dc97)
---
.../src/explore/actions/exploreActions.test.js | 180 +++++++++++++++++++++
.../src/explore/reducers/exploreReducer.js | 5 +
2 files changed, 185 insertions(+)
diff --git a/superset-frontend/src/explore/actions/exploreActions.test.js b/superset-frontend/src/explore/actions/exploreActions.test.js
index 9dd5375680..54cf8f16c5 100644
--- a/superset-frontend/src/explore/actions/exploreActions.test.js
+++ b/superset-frontend/src/explore/actions/exploreActions.test.js
@@ -21,6 +21,63 @@ import { defaultState } from 'src/explore/store';
import exploreReducer from 'src/explore/reducers/exploreReducer';
import * as actions from 'src/explore/actions/exploreActions';
+const METRICS = [
+ {
+ expressionType: 'SIMPLE',
+ column: {
+ advanced_data_type: null,
+ certification_details: null,
+ certified_by: null,
+ column_name: 'a',
+ description: null,
+ expression: null,
+ filterable: true,
+ groupby: true,
+ id: 1,
+ is_certified: false,
+ is_dttm: false,
+ python_date_format: null,
+ type: 'DOUBLE PRECISION',
+ type_generic: 0,
+ verbose_name: null,
+ warning_markdown: null,
+ },
+ aggregate: 'SUM',
+ sqlExpression: null,
+ datasourceWarning: false,
+ hasCustomLabel: false,
+ label: 'SUM(a)',
+ optionName: 'metric_1a2b3c4d5f_1a2b3c4d5f',
+ },
+ {
+ expressionType: 'SIMPLE',
+ column: {
+ advanced_data_type: null,
+ certification_details: null,
+ certified_by: null,
+ column_name: 'b',
+ description: null,
+ expression: null,
+ filterable: true,
+ groupby: true,
+ id: 2,
+ is_certified: false,
+ is_dttm: false,
+ python_date_format: null,
+ type: 'BIGINT',
+ type_generic: 0,
+ verbose_name: null,
+ warning_markdown: null,
+ },
+ aggregate: 'AVG',
+ sqlExpression: null,
+ datasourceWarning: false,
+ hasCustomLabel: false,
+ label: 'AVG(b)',
+ optionName: 'metric_6g7h8i9j0k_6g7h8i9j0k',
+ },
+];
+
describe('reducers', () => {
it('Does not set a control value if control does not exist', () => {
const newState = exploreReducer(
@@ -37,4 +94,127 @@ describe('reducers', () => {
expect(newState.controls.y_axis_format.value).toBe('$,.2f');
expect(newState.form_data.y_axis_format).toBe('$,.2f');
});
+ it('Keeps the column config when metric column positions are swapped', () => {
+ const mockedState = {
+ ...defaultState,
+ controls: {
+ ...defaultState.controls,
+ metrics: {
+ ...defaultState.controls.metrics,
+ value: METRICS,
+ },
+ column_config: {
+ ...defaultState.controls.column_config,
+ value: {
+ 'AVG(b)': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ },
+ },
+ },
+ form_data: {
+ ...defaultState.form_data,
+ metrics: METRICS,
+ column_config: {
+ 'AVG(b)': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ },
+ },
+ };
+
+ const swappedMetrics = [METRICS[1], METRICS[0]];
+ const newState = exploreReducer(
+ mockedState,
+ actions.setControlValue('metrics', swappedMetrics, []),
+ );
+
+ const expectedColumnConfig = {
+ 'AVG(b)': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ };
+
+ expect(newState.controls.metrics.value).toStrictEqual(swappedMetrics);
+ expect(newState.form_data.metrics).toStrictEqual(swappedMetrics);
+ expect(newState.controls.column_config.value).toStrictEqual(
+ expectedColumnConfig,
+ );
+ expect(newState.form_data.column_config).toStrictEqual(
+ expectedColumnConfig,
+ );
+ });
+
+ it('Keeps the column config when metric column name is updated', () => {
+ const mockedState = {
+ ...defaultState,
+ controls: {
+ ...defaultState.controls,
+ metrics: {
+ ...defaultState.controls.metrics,
+ value: METRICS,
+ },
+ column_config: {
+ ...defaultState.controls.column_config,
+ value: {
+ 'AVG(b)': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ },
+ },
+ },
+ form_data: {
+ ...defaultState.form_data,
+ metrics: METRICS,
+ column_config: {
+ 'AVG(b)': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ },
+ },
+ };
+
+ const updatedMetrics = [
+ METRICS[0],
+ {
+ ...METRICS[1],
+ hasCustomLabel: true,
+ label: 'AVG of b',
+ },
+ ];
+
+ const newState = exploreReducer(
+ mockedState,
+ actions.setControlValue('metrics', updatedMetrics, []),
+ );
+
+ const expectedColumnConfig = {
+ 'AVG of b': {
+ currencyFormat: {
+ symbolPosition: 'prefix',
+ symbol: 'USD',
+ },
+ },
+ };
+ expect(newState.controls.metrics.value).toStrictEqual(updatedMetrics);
+ expect(newState.form_data.metrics).toStrictEqual(updatedMetrics);
+ expect(newState.form_data.column_config).toStrictEqual(
+ expectedColumnConfig,
+ );
+ });
});
diff --git a/superset-frontend/src/explore/reducers/exploreReducer.js b/superset-frontend/src/explore/reducers/exploreReducer.js
index d5565a0dad..1797c57637 100644
--- a/superset-frontend/src/explore/reducers/exploreReducer.js
+++ b/superset-frontend/src/explore/reducers/exploreReducer.js
@@ -115,7 +115,12 @@ export default function exploreReducer(state = {}, action) {
// need to update column config as well to keep the previous config.
if (controlName === 'metrics' && old_metrics_data && new_column_config) {
value.forEach((item, index) => {
+ const itemExist = old_metrics_data.some(
+ oldItem => oldItem?.label === item?.label,
+ );
+
if (
+ !itemExist &&
item?.label !== old_metrics_data[index]?.label &&
!!new_column_config[old_metrics_data[index]?.label]
) {
(superset) 04/07: fix: In chart gallery thumbnail is rendered in case of no example in #16707 (#26415)
Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 333b18db5a9b3ae436d2acb46f489c0a3dc12fb6
Author: Siva Sathyaseelan <95...@users.noreply.github.com>
AuthorDate: Tue Jan 9 00:14:58 2024 +0530
fix: In chart gallery thumbnail is rendered in case of no example in #16707 (#26415)
(cherry picked from commit 6d585667799d9fdc511c45fe062f54bf0ce9022f)
---
.../components/controls/VizTypeControl/VizTypeGallery.tsx | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
index 90b7856b05..5ae545e2ae 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
@@ -848,10 +848,18 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
grid-area: examples-header;
`}
>
- {!!selectedVizMetadata?.exampleGallery?.length && t('Examples')}
+ {t('Examples')}
</SectionTitle>
<Examples>
- {(selectedVizMetadata?.exampleGallery || []).map(example => (
+ {(selectedVizMetadata?.exampleGallery?.length
+ ? selectedVizMetadata.exampleGallery
+ : [
+ {
+ url: selectedVizMetadata?.thumbnail,
+ caption: selectedVizMetadata?.name,
+ },
+ ]
+ ).map(example => (
<img
src={example.url}
alt={example.caption}