From a15cc8c2dc1f122e6b8c79e65ec6b4081a055d1b Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 17 Jul 2024 15:33:39 +1200 Subject: [PATCH 01/12] disable data selection from results table --- superset-frontend/src/components/FilterableTable/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index f77f4aede0eb7..86197a550b5ce 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -50,6 +50,7 @@ const StyledFilterableTable = styled.div` min-width: 0px; align-self: center; font-size: ${theme.typography.sizes.s}px; + user-select: none; } .even-row { @@ -63,6 +64,7 @@ const StyledFilterableTable = styled.div` .cell-text-for-measuring { font-family: ${theme.typography.families.sansSerif}; font-size: ${theme.typography.sizes.s}px; + user-select: none; } `} `; From 2a58dd778e9f51184831f5b155bc6d7ecb83a867 Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 17 Jul 2024 16:00:29 +1200 Subject: [PATCH 02/12] disable selection from chart table --- superset-frontend/plugins/plugin-chart-table/src/Styles.tsx | 3 +++ .../plugins/plugin-chart-table/src/TableChart.tsx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/superset-frontend/plugins/plugin-chart-table/src/Styles.tsx b/superset-frontend/plugins/plugin-chart-table/src/Styles.tsx index e394d16446ee3..a75e6285afec4 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/Styles.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/Styles.tsx @@ -118,5 +118,8 @@ export default styled.div` table .right-border-only:last-child { border-right: none; } + .no-select { + user-select: none; + } `} `; diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx index a87371f440f04..e477cd71270e9 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx @@ -736,6 +736,8 @@ export default function TableChart( } } + className += ' no-select'; + return { id: String(i), // to allow duplicate column keys // must use custom accessor to allow `.` in column names From 62f5b00454777c66494dc2ec829052df8b56421b Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 17 Jul 2024 16:01:18 +1200 Subject: [PATCH 03/12] disable pivot chart select --- .../plugin-chart-pivot-table/src/react-pivottable/Styles.js | 1 + superset-frontend/src/components/FilterableTable/index.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js index 0854620f2b960..ba2f6bdf12bb2 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js @@ -30,6 +30,7 @@ export const Styles = styled.div` border-collapse: separate; font-family: ${theme.typography.families.sansSerif}; line-height: 1.4; + user-select: none; } table thead { diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index 86197a550b5ce..224d67e5f58f4 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -64,7 +64,6 @@ const StyledFilterableTable = styled.div` .cell-text-for-measuring { font-family: ${theme.typography.families.sansSerif}; font-size: ${theme.typography.sizes.s}px; - user-select: none; } `} `; From ca17002588d390d328bea50fb49b8777c77c424a Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Mon, 22 Jul 2024 17:52:16 +1200 Subject: [PATCH 04/12] add user select to permissions model --- .../src/components/FilterableTable/index.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index 224d67e5f58f4..0d691c6538d37 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -18,11 +18,14 @@ */ import _JSONbig from 'json-bigint'; import { useEffect, useRef, useState, useMemo } from 'react'; +import { useSelector } from 'react-redux'; import { getMultipleTextDimensions, styled } from '@superset-ui/core'; import { useDebounceValue } from 'src/hooks/useDebounceValue'; import { useCellContentParser } from './useCellContentParser'; import { renderResultCell } from './utils'; import { Table, TableSize } from '../Table'; +import { RootState } from 'src/dashboard/types'; +import { findPermission } from 'src/utils/findPermission'; const JSONbig = _JSONbig({ storeAsString: true, @@ -35,8 +38,12 @@ const SCROLL_BAR_HEIGHT = 15; // See https://stackoverflow.com/a/30987109 for more details const ONLY_NUMBER_REGEX = /^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$/; -const StyledFilterableTable = styled.div` - ${({ theme }) => ` +interface StyledFilterableTableProps { + canDownload?: boolean; +} + +const StyledFilterableTable = styled.div` + ${({ theme, canDownload }) => ` height: 100%; overflow: hidden; @@ -50,7 +57,7 @@ const StyledFilterableTable = styled.div` min-width: 0px; align-self: center; font-size: ${theme.typography.sizes.s}px; - user-select: none; + user-select: ${canDownload ? 'auto' : 'none'}; } .even-row { @@ -250,11 +257,16 @@ const FilterableTable = ({ }), })); + const canDownload = useSelector((state: RootState) => + findPermission('can_csv', 'Superset', state.user?.roles), + ); + return ( {fitted && ( Date: Mon, 22 Jul 2024 18:10:47 +1200 Subject: [PATCH 05/12] change perms to can_export_csv --- .../src/components/FilterableTable/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index 0d691c6538d37..fc9a593d00d2a 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -39,11 +39,11 @@ const SCROLL_BAR_HEIGHT = 15; const ONLY_NUMBER_REGEX = /^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$/; interface StyledFilterableTableProps { - canDownload?: boolean; + canExportData?: boolean; } const StyledFilterableTable = styled.div` - ${({ theme, canDownload }) => ` + ${({ theme, canExportData }) => ` height: 100%; overflow: hidden; @@ -57,7 +57,7 @@ const StyledFilterableTable = styled.div` min-width: 0px; align-self: center; font-size: ${theme.typography.sizes.s}px; - user-select: ${canDownload ? 'auto' : 'none'}; + user-select: ${canExportData ? 'auto' : 'none'}; } .even-row { @@ -257,8 +257,8 @@ const FilterableTable = ({ }), })); - const canDownload = useSelector((state: RootState) => - findPermission('can_csv', 'Superset', state.user?.roles), + const canExportData = useSelector((state: RootState) => + findPermission('can_export_csv', 'SQLLab', state.user?.roles), ); return ( @@ -266,7 +266,7 @@ const FilterableTable = ({ className="filterable-table-container" data-test="table-container" ref={container} - canDownload={canDownload} + canExportData={canExportData} > {fitted && (
Date: Mon, 29 Jul 2024 16:53:39 +1200 Subject: [PATCH 06/12] pass dataSelectionMode prop to plugin --- .../superset-ui-core/src/chart/components/SuperChart.tsx | 2 ++ .../superset-ui-core/src/chart/models/ChartProps.ts | 9 +++++++++ .../plugins/plugin-chart-table/src/TableChart.tsx | 5 ++++- .../plugins/plugin-chart-table/src/transformProps.ts | 2 ++ superset-frontend/src/components/Chart/ChartRenderer.jsx | 4 ++++ .../src/components/FilterableTable/index.tsx | 4 ++-- 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx index 473a3506a411e..e200f4126a986 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx @@ -97,6 +97,8 @@ export type Props = Omit & * Determines is the context menu related to the chart is open */ inContextMenu?: boolean; + /** Prop that controls user selection of chart data */ + dataSelectionMode?: string; }; type PropsWithDefault = Props & Readonly; diff --git a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts index 829440133a32c..8b01c7f27f8ec 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts +++ b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts @@ -105,6 +105,8 @@ export interface ChartPropsConfig { inputRef?: RefObject; /** Theme object */ theme: SupersetTheme; + /** Set chart data selectable by user **/ + dataSelectionMode?: string; } const DEFAULT_WIDTH = 800; @@ -155,6 +157,8 @@ export default class ChartProps { theme: SupersetTheme; + dataSelectionMode?: string; + constructor(config: ChartPropsConfig & { formData?: FormData } = {}) { const { annotationData = {}, @@ -176,6 +180,7 @@ export default class ChartProps { inContextMenu = false, emitCrossFilters = false, theme, + dataSelectionMode, } = config; this.width = width; this.height = height; @@ -198,6 +203,7 @@ export default class ChartProps { this.inContextMenu = inContextMenu; this.emitCrossFilters = emitCrossFilters; this.theme = theme; + this.dataSelectionMode = dataSelectionMode; } } @@ -223,6 +229,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { input => input.inContextMenu, input => input.emitCrossFilters, input => input.theme, + input => input.dataSelectionMode, ( annotationData, datasource, @@ -243,6 +250,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { inContextMenu, emitCrossFilters, theme, + dataSelectionMode, ) => new ChartProps({ annotationData, @@ -264,6 +272,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { inContextMenu, emitCrossFilters, theme, + dataSelectionMode, }), ); }; diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx index e477cd71270e9..7e481413ca008 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx @@ -264,6 +264,7 @@ export default function TableChart( isUsingTimeComparison, basicColorFormatters, basicColorColumnFormatters, + dataSelectionMode, } = props; const comparisonColumns = [ { key: 'all', label: t('Display all') }, @@ -736,7 +737,9 @@ export default function TableChart( } } - className += ' no-select'; + if (dataSelectionMode === 'None') { + className += ' no-select'; + } return { id: String(i), // to allow duplicate column keys diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts index 66bf6b68ed2a9..459717afab81f 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts @@ -384,6 +384,7 @@ const transformProps = ( onContextMenu, }, emitCrossFilters, + dataSelectionMode, } = chartProps; const { @@ -643,6 +644,7 @@ const transformProps = ( basicColorFormatters, startDateOffset, basicColorColumnFormatters, + dataSelectionMode, }; }; diff --git a/superset-frontend/src/components/Chart/ChartRenderer.jsx b/superset-frontend/src/components/Chart/ChartRenderer.jsx index b9cd6caf4fb94..3037774074cc1 100644 --- a/superset-frontend/src/components/Chart/ChartRenderer.jsx +++ b/superset-frontend/src/components/Chart/ChartRenderer.jsx @@ -48,6 +48,7 @@ const propTypes = { setControlValue: PropTypes.func, vizType: PropTypes.string.isRequired, triggerRender: PropTypes.bool, + // dataSelectionMode: PropTypes.string, // state chartAlert: PropTypes.string, chartStatus: PropTypes.string, @@ -315,6 +316,8 @@ class ChartRenderer extends Component { ); } + const dataSelectionMode = 'None'; + // Check for Behavior.DRILL_TO_DETAIL to tell if chart can receive Drill to // Detail props or if it'll cause side-effects (e.g. excessive re-renders). const drillToDetailProps = getChartMetadataRegistry() @@ -362,6 +365,7 @@ class ChartRenderer extends Component { postTransformProps={postTransformProps} emitCrossFilters={emitCrossFilters} legendState={this.state.legendState} + dataSelectionMode={dataSelectionMode} {...drillToDetailProps} /> diff --git a/superset-frontend/src/components/FilterableTable/index.tsx b/superset-frontend/src/components/FilterableTable/index.tsx index fc9a593d00d2a..a46b89d94796b 100644 --- a/superset-frontend/src/components/FilterableTable/index.tsx +++ b/superset-frontend/src/components/FilterableTable/index.tsx @@ -21,11 +21,11 @@ import { useEffect, useRef, useState, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { getMultipleTextDimensions, styled } from '@superset-ui/core'; import { useDebounceValue } from 'src/hooks/useDebounceValue'; +import { RootState } from 'src/dashboard/types'; +import { findPermission } from 'src/utils/findPermission'; import { useCellContentParser } from './useCellContentParser'; import { renderResultCell } from './utils'; import { Table, TableSize } from '../Table'; -import { RootState } from 'src/dashboard/types'; -import { findPermission } from 'src/utils/findPermission'; const JSONbig = _JSONbig({ storeAsString: true, From 703e7efcc0ba2a3c8963a5f5d6cf5e49ac46e6b6 Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Mon, 29 Jul 2024 17:27:46 +1200 Subject: [PATCH 07/12] remove unneeded prop --- .../superset-ui-core/src/chart/components/SuperChart.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx index e200f4126a986..473a3506a411e 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx @@ -97,8 +97,6 @@ export type Props = Omit & * Determines is the context menu related to the chart is open */ inContextMenu?: boolean; - /** Prop that controls user selection of chart data */ - dataSelectionMode?: string; }; type PropsWithDefault = Props & Readonly; From fafe95b29ba0e8a3226488ec1c60967293f0a1ab Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 31 Jul 2024 11:32:32 +1200 Subject: [PATCH 08/12] get permissions from role TableChart --- .../plugins/plugin-chart-table/src/TableChart.tsx | 2 +- superset-frontend/src/components/Chart/Chart.jsx | 8 ++++++++ superset-frontend/src/components/Chart/ChartContainer.jsx | 8 +++++++- superset-frontend/src/components/Chart/ChartRenderer.jsx | 6 ++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx index 7e481413ca008..b6132ed5a1830 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx @@ -737,7 +737,7 @@ export default function TableChart( } } - if (dataSelectionMode === 'None') { + if (dataSelectionMode === 'none') { className += ' no-select'; } diff --git a/superset-frontend/src/components/Chart/Chart.jsx b/superset-frontend/src/components/Chart/Chart.jsx index ea1a2d968c99f..794dfdae1c2c6 100644 --- a/superset-frontend/src/components/Chart/Chart.jsx +++ b/superset-frontend/src/components/Chart/Chart.jsx @@ -39,6 +39,7 @@ import { ResourceStatus } from 'src/hooks/apiResources/apiResources'; import ChartRenderer from './ChartRenderer'; import { ChartErrorMessage } from './ChartErrorMessage'; import { getChartRequiredFieldsMissingMessage } from '../../utils/getChartRequiredFieldsMissingMessage'; +import { findPermission } from 'src/utils/findPermission'; const propTypes = { annotationData: PropTypes.object, @@ -82,6 +83,7 @@ const propTypes = { datasetsStatus: PropTypes.oneOf(['loading', 'error', 'complete']), isInView: PropTypes.bool, emitCrossFilters: PropTypes.bool, + user: PropTypes.object, }; const BLANK = {}; @@ -156,6 +158,11 @@ class Chart extends PureComponent { super(props); this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind(this); + this.canExportData = findPermission( + 'can_export_csv', + 'SQLLab', + this.props.user?.roles, + ); } componentDidMount() { @@ -271,6 +278,7 @@ class Chart extends PureComponent { {...this.props} source={this.props.dashboardId ? 'dashboard' : 'explore'} data-test={this.props.vizType} + dataSelectionMode={this.canExportData ? 'auto' : 'none'} /> ) : ( diff --git a/superset-frontend/src/components/Chart/ChartContainer.jsx b/superset-frontend/src/components/Chart/ChartContainer.jsx index 50c036c28db3b..a358ba03f4359 100644 --- a/superset-frontend/src/components/Chart/ChartContainer.jsx +++ b/superset-frontend/src/components/Chart/ChartContainer.jsx @@ -24,6 +24,12 @@ import { logEvent } from '../../logger/actions'; import Chart from './Chart'; import { updateDataMask } from '../../dataMask/actions'; +function mapStateToProps(state) { + return { + user: state.user, + }; +} + function mapDispatchToProps(dispatch) { return { actions: bindActionCreators( @@ -37,4 +43,4 @@ function mapDispatchToProps(dispatch) { }; } -export default connect(null, mapDispatchToProps)(Chart); +export default connect(mapStateToProps, mapDispatchToProps)(Chart); diff --git a/superset-frontend/src/components/Chart/ChartRenderer.jsx b/superset-frontend/src/components/Chart/ChartRenderer.jsx index 3037774074cc1..48c1c5e44d310 100644 --- a/superset-frontend/src/components/Chart/ChartRenderer.jsx +++ b/superset-frontend/src/components/Chart/ChartRenderer.jsx @@ -48,8 +48,6 @@ const propTypes = { setControlValue: PropTypes.func, vizType: PropTypes.string.isRequired, triggerRender: PropTypes.bool, - // dataSelectionMode: PropTypes.string, - // state chartAlert: PropTypes.string, chartStatus: PropTypes.string, queriesResponse: PropTypes.arrayOf(PropTypes.object), @@ -64,6 +62,7 @@ const propTypes = { postTransformProps: PropTypes.func, source: PropTypes.oneOf([ChartSource.Dashboard, ChartSource.Explore]), emitCrossFilters: PropTypes.bool, + dataSelectionMode: PropTypes.string, }; const BLANK = {}; @@ -265,6 +264,7 @@ class ChartRenderer extends Component { formData, latestQueryFormData, postTransformProps, + dataSelectionMode, } = this.props; const currentFormData = @@ -316,8 +316,6 @@ class ChartRenderer extends Component { ); } - const dataSelectionMode = 'None'; - // Check for Behavior.DRILL_TO_DETAIL to tell if chart can receive Drill to // Detail props or if it'll cause side-effects (e.g. excessive re-renders). const drillToDetailProps = getChartMetadataRegistry() From 27d22d1262a56b6cd55f55b59e1ea5ac2f8f89b7 Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 31 Jul 2024 12:21:15 +1200 Subject: [PATCH 09/12] add user permissions to pivot table --- .../plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx | 2 ++ .../plugin-chart-pivot-table/src/plugin/transformProps.ts | 2 ++ .../plugin-chart-pivot-table/src/react-pivottable/Styles.js | 4 ++-- .../src/react-pivottable/TableRenderers.jsx | 2 +- .../plugins/plugin-chart-pivot-table/src/types.ts | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx index 0c83a152df95e..894084d1fe4a1 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx @@ -154,6 +154,7 @@ export default function PivotTableChart(props: PivotTableProps) { dateFormatters, onContextMenu, timeGrainSqla, + dataSelectionMode, } = props; const theme = useTheme(); @@ -555,6 +556,7 @@ export default function PivotTableChart(props: PivotTableProps) { subtotalOptions={subtotalOptions} namesMapping={verboseMap} onContextMenu={handleContextMenu} + dataSelectionMode={dataSelectionMode} /> diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts index d8fd463bc0253..767cdeff6cbc5 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/transformProps.ts @@ -81,6 +81,7 @@ export default function transformProps(chartProps: ChartProps) { filterState, datasource: { verboseMap = {}, columnFormats = {}, currencyFormats = {} }, emitCrossFilters, + dataSelectionMode, } = chartProps; const { data, colnames, coltypes } = queriesData[0]; const { @@ -174,5 +175,6 @@ export default function transformProps(chartProps: ChartProps) { dateFormatters, onContextMenu, timeGrainSqla, + dataSelectionMode, }; } diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js index ba2f6bdf12bb2..b9fdf42abb3d6 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/Styles.js @@ -20,7 +20,7 @@ import { css, styled } from '@superset-ui/core'; export const Styles = styled.div` - ${({ theme, isDashboardEditMode }) => css` + ${({ theme, isDashboardEditMode, dataSelectionMode }) => css` table.pvtTable { position: ${isDashboardEditMode ? 'inherit' : 'relative'}; width: calc(100% - ${theme.gridUnit}px); @@ -30,7 +30,7 @@ export const Styles = styled.div` border-collapse: separate; font-family: ${theme.typography.families.sansSerif}; line-height: 1.4; - user-select: none; + user-select: ${dataSelectionMode}; } table thead { diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx index 055c87b046344..bb2bebdbe542c 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx @@ -899,7 +899,7 @@ export class TableRenderer extends Component { }; return ( - +
{colAttrs.map((c, j) => diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts index 4e4dbbd058374..027790393827a 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/types.ts @@ -79,6 +79,7 @@ interface PivotTableCustomizeProps { dateFormatters: Record; legacy_order_by: QueryFormMetric[] | QueryFormMetric | null; order_desc: boolean; + dataSelectionMode: string; onContextMenu?: ( clientX: number, clientY: number, From 6b35723a7ccbfd34270504a0f031c9cde9f2f168 Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 31 Jul 2024 14:29:50 +1200 Subject: [PATCH 10/12] apply linter and fix compiler errors --- .../superset-ui-core/src/chart/models/ChartProps.ts | 4 ++-- .../src/react-pivottable/TableRenderers.jsx | 5 ++++- .../test/plugin/buildQuery.test.ts | 1 + .../plugins/plugin-chart-table/src/transformProps.ts | 2 +- .../plugins/plugin-chart-table/src/types.ts | 1 + superset-frontend/src/components/Chart/Chart.jsx | 12 ++++++------ 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts index 8b01c7f27f8ec..d5782177d3498 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts +++ b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts @@ -105,7 +105,7 @@ export interface ChartPropsConfig { inputRef?: RefObject; /** Theme object */ theme: SupersetTheme; - /** Set chart data selectable by user **/ + /** Set chart data selectable by user */ dataSelectionMode?: string; } @@ -272,7 +272,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { inContextMenu, emitCrossFilters, theme, - dataSelectionMode, + dataSelectionMode, }), ); }; diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx index bb2bebdbe542c..86beae1bba7c5 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx @@ -899,7 +899,10 @@ export class TableRenderer extends Component { }; return ( - +
{colAttrs.map((c, j) => diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts index 3bf887c0b60f3..5d7ac7a0a663b 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts +++ b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts @@ -54,6 +54,7 @@ const formData: PivotTableQueryFormData = { time_grain_sqla: TimeGranularity.MONTH, temporal_columns_lookup: { col1: true }, currencyFormat: { symbol: 'USD', symbolPosition: 'prefix' }, + dataSelectionMode: 'auto', }; test('should build groupby with series in form data', () => { diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts index 459717afab81f..1a3e646d026fc 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts @@ -644,7 +644,7 @@ const transformProps = ( basicColorFormatters, startDateOffset, basicColorColumnFormatters, - dataSelectionMode, + dataSelectionMode: dataSelectionMode ?? 'auto', }; }; diff --git a/superset-frontend/plugins/plugin-chart-table/src/types.ts b/superset-frontend/plugins/plugin-chart-table/src/types.ts index 1ec3cbe29d724..d9c28109c6c2a 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/types.ts +++ b/superset-frontend/plugins/plugin-chart-table/src/types.ts @@ -146,6 +146,7 @@ export interface TableChartTransformedProps { basicColorFormatters?: { [Key: string]: BasicColorFormatterType }[]; basicColorColumnFormatters?: { [Key: string]: BasicColorFormatterType }[]; startDateOffset?: string; + dataSelectionMode: string; } export enum ColorSchemeEnum { diff --git a/superset-frontend/src/components/Chart/Chart.jsx b/superset-frontend/src/components/Chart/Chart.jsx index 794dfdae1c2c6..32b5de44d2644 100644 --- a/superset-frontend/src/components/Chart/Chart.jsx +++ b/superset-frontend/src/components/Chart/Chart.jsx @@ -36,10 +36,10 @@ import { getUrlParam } from 'src/utils/urlUtils'; import { isCurrentUserBot } from 'src/utils/isBot'; import { ChartSource } from 'src/types/ChartSource'; import { ResourceStatus } from 'src/hooks/apiResources/apiResources'; +import { findPermission } from 'src/utils/findPermission'; import ChartRenderer from './ChartRenderer'; import { ChartErrorMessage } from './ChartErrorMessage'; import { getChartRequiredFieldsMissingMessage } from '../../utils/getChartRequiredFieldsMissingMessage'; -import { findPermission } from 'src/utils/findPermission'; const propTypes = { annotationData: PropTypes.object, @@ -158,11 +158,11 @@ class Chart extends PureComponent { super(props); this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind(this); - this.canExportData = findPermission( - 'can_export_csv', - 'SQLLab', - this.props.user?.roles, - ); + this.canExportData = findPermission( + 'can_export_csv', + 'SQLLab', + this.props.user?.roles, + ); } componentDidMount() { From 6156e5fa051febec0242c225f19164cfd8354ff2 Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Wed, 31 Jul 2024 15:35:51 +1200 Subject: [PATCH 11/12] update permissions to inline with previous features --- superset-frontend/src/components/Chart/Chart.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/components/Chart/Chart.jsx b/superset-frontend/src/components/Chart/Chart.jsx index 32b5de44d2644..6d28050d1b319 100644 --- a/superset-frontend/src/components/Chart/Chart.jsx +++ b/superset-frontend/src/components/Chart/Chart.jsx @@ -159,8 +159,8 @@ class Chart extends PureComponent { this.handleRenderContainerFailure = this.handleRenderContainerFailure.bind(this); this.canExportData = findPermission( - 'can_export_csv', - 'SQLLab', + 'can_csv', + 'Superset', this.props.user?.roles, ); } From 4b16a6816bc8dede425ae85f151da35fa907cf6d Mon Sep 17 00:00:00 2001 From: Mial Lewis Date: Fri, 2 Aug 2024 17:20:08 +1200 Subject: [PATCH 12/12] fix filterable table test --- .../FilterableTable/FilterableTable.test.tsx | 63 ++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/superset-frontend/src/components/FilterableTable/FilterableTable.test.tsx b/superset-frontend/src/components/FilterableTable/FilterableTable.test.tsx index 415d7f053dfce..de4c6f0eabe68 100644 --- a/superset-frontend/src/components/FilterableTable/FilterableTable.test.tsx +++ b/superset-frontend/src/components/FilterableTable/FilterableTable.test.tsx @@ -20,6 +20,8 @@ import { isValidElement } from 'react'; import FilterableTable from 'src/components/FilterableTable'; import { render, screen, within } from 'spec/helpers/testing-library'; import userEvent from '@testing-library/user-event'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; describe('FilterableTable', () => { const mockedProps = { @@ -31,12 +33,18 @@ describe('FilterableTable', () => { ], height: 500, }; + const initialState = { state: { user : {role : 'test'}} }; + const mockStore = configureStore(); + let store; it('is valid element', () => { expect(isValidElement()).toBe(true); }); it('renders a grid with 3 Table rows', () => { + store = mockStore(initialState); const { getByRole, getByText } = render( - , + + + , ); expect(getByRole('table')).toBeInTheDocument(); mockedProps.data.forEach(({ b: columnBContent }) => { @@ -48,7 +56,12 @@ describe('FilterableTable', () => { ...mockedProps, filterText: 'b1', }; - const { getByText, queryByText } = render(); + store = mockStore(initialState); + const { getByText, queryByText } = render( + + + , + ); expect(getByText(props.filterText)).toBeInTheDocument(); expect(queryByText('b2')).toBeFalsy(); expect(queryByText('b3')).toBeFalsy(); @@ -58,7 +71,12 @@ describe('FilterableTable', () => { ...mockedProps, filterText: '100', }; - const { getByText, queryByText } = render(); + store = mockStore(initialState); + const { getByText, queryByText } = render( + + + , + ); expect(getByText('b2')).toBeInTheDocument(); expect(queryByText('b1')).toBeFalsy(); expect(queryByText('b3')).toBeFalsy(); @@ -66,6 +84,9 @@ describe('FilterableTable', () => { }); describe('FilterableTable sorting - RTL', () => { + const initialState = { state: { user : {role : 'test'}} }; + const mockStore = configureStore(); + let store; it('sorts strings correctly', () => { const stringProps = { orderedColumnKeys: ['columnA'], @@ -76,7 +97,12 @@ describe('FilterableTable sorting - RTL', () => { ], height: 500, }; - render(); + store = mockStore(initialState); + render( + + + , + ); const stringColumn = within(screen.getByRole('table')) .getByText('columnA') @@ -122,7 +148,12 @@ describe('FilterableTable sorting - RTL', () => { data: [{ columnB: 21 }, { columnB: 0 }, { columnB: 623 }], height: 500, }; - render(); + store = mockStore(initialState); + render( + + + , + ); const integerColumn = within(screen.getByRole('table')) .getByText('columnB') @@ -157,7 +188,13 @@ describe('FilterableTable sorting - RTL', () => { data: [{ columnC: 45.67 }, { columnC: 1.23 }, { columnC: 89.0000001 }], height: 500, }; - render(); + mockStore(initialState); + store = mockStore(initialState); + render( + + + , + ); const floatColumn = within(screen.getByRole('table')) .getByText('columnC') @@ -212,7 +249,12 @@ describe('FilterableTable sorting - RTL', () => { ], height: 500, }; - render(); + store = mockStore(initialState); + render( + + + , + ); const mixedFloatColumn = within(screen.getByRole('table')) .getByText('columnD') @@ -310,7 +352,12 @@ describe('FilterableTable sorting - RTL', () => { ], height: 500, }; - render(); + store = mockStore(initialState); + render( + + + , + ); const dsColumn = within(screen.getByRole('table')) .getByText('columnDS')