diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx index a42f0344acef..6757219e4553 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx @@ -16,18 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; +import React, { useEffect, useState, ReactNode } from 'react'; import { styled } from '@superset-ui/core'; import { Tooltip } from './Tooltip'; import { ColumnTypeLabel } from './ColumnTypeLabel'; import InfoTooltipWithTrigger from './InfoTooltipWithTrigger'; import CertifiedIconWithTooltip from './CertifiedIconWithTooltip'; import { ColumnMeta } from '../types'; +import { getColumnLabelText, getColumnTooltipNode } from './labelUtils'; export type ColumnOptionProps = { column: ColumnMeta; showType?: boolean; - showTooltip?: boolean; labelRef?: React.RefObject; }; @@ -41,11 +41,15 @@ export function ColumnOption({ column, labelRef, showType = false, - showTooltip = true, }: ColumnOptionProps) { const { expression, column_name, type_generic } = column; const hasExpression = expression && expression !== column_name; const type = hasExpression ? 'expression' : type_generic; + const [tooltipText, setTooltipText] = useState(column.column_name); + + useEffect(() => { + setTooltipText(getColumnTooltipNode(column, labelRef)); + }, [labelRef, column]); return ( @@ -57,25 +61,17 @@ export function ColumnOption({ details={column.certification_details} /> )} - {showTooltip ? ( - - - {column.verbose_name || column.column_name} - - - ) : ( + - {column.verbose_name || column.column_name} + {getColumnLabelText(column)} - )} + + {column.description && ( ; } @@ -48,7 +48,6 @@ export function MetricOption({ openInNewWindow = false, showFormula = true, showType = false, - showTooltip = true, url = '', }: MetricOptionProps) { const verbose = metric.verbose_name || metric.metric_name || metric.label; @@ -62,6 +61,12 @@ export function MetricOption({ const warningMarkdown = metric.warning_markdown || metric.warning_text; + const [tooltipText, setTooltipText] = useState(metric.metric_name); + + useEffect(() => { + setTooltipText(getMetricTooltipNode(metric, labelRef)); + }, [labelRef, metric]); + return ( {showType && } @@ -72,22 +77,16 @@ export function MetricOption({ details={metric.certification_details} /> )} - {showTooltip ? ( - - - {link} - - - ) : ( + {link} - )} + {metric.description && ( ): boolean => + !!( + labelRef && + labelRef.current && + labelRef.current.scrollWidth > labelRef.current.clientWidth + ); + +export const getColumnLabelText = (column: ColumnMeta): string => + column.verbose_name || column.column_name; + +export const getColumnTooltipNode = ( + column: ColumnMeta, + labelRef?: React.RefObject, +): ReactNode => { + // don't show tooltip if it hasn't verbose_name and hasn't truncated + if (!column.verbose_name && !isLabelTruncated(labelRef)) { + return null; + } + + if (column.verbose_name) { + return ( + <> +
{t('column name: %s', column.column_name)}
+
{t('verbose name: %s', column.verbose_name)}
+ + ); + } + + // show column name in tooltip when column truncated + return t('column name: %s', column.column_name); +}; + +type MetricType = Omit & { label?: string }; + +export const getMetricTooltipNode = ( + metric: MetricType, + labelRef?: React.RefObject, +): ReactNode => { + // don't show tooltip if it hasn't verbose_name, label and hasn't truncated + if (!metric.verbose_name && !metric.label && !isLabelTruncated(labelRef)) { + return null; + } + + if (metric.verbose_name) { + return ( + <> +
{t('metric name: %s', metric.metric_name)}
+
{t('verbose name: %s', metric.verbose_name)}
+ + ); + } + + if (isLabelTruncated(labelRef) && metric.label) { + return t('label name: %s', metric.label); + } + + return t('metric name: %s', metric.metric_name); +}; diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/components/labelUtils.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/components/labelUtils.test.tsx new file mode 100644 index 000000000000..b5e5cd6adfa2 --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/test/components/labelUtils.test.tsx @@ -0,0 +1,185 @@ +/** + * 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 { + getColumnLabelText, + getColumnTooltipNode, + getMetricTooltipNode, +} from '../../src/components/labelUtils'; + +test("should get column name when column doesn't have verbose_name", () => { + expect( + getColumnLabelText({ + id: 123, + column_name: 'column name', + verbose_name: '', + }), + ).toBe('column name'); +}); + +test('should get verbose name when column have verbose_name', () => { + expect( + getColumnLabelText({ + id: 123, + column_name: 'column name', + verbose_name: 'verbose name', + }), + ).toBe('verbose name'); +}); + +test('should get null as tooltip', () => { + const ref = { current: { scrollWidth: 100, clientWidth: 100 } }; + expect( + getColumnTooltipNode( + { + id: 123, + column_name: 'column name', + verbose_name: '', + }, + ref, + ), + ).toBe(null); +}); + +test('should get column name and verbose name when it has a verbose name', () => { + const rvNode = ( + <> +
column name: column name
+
verbose name: verbose name
+ + ); + + const ref = { current: { scrollWidth: 100, clientWidth: 100 } }; + expect( + getColumnTooltipNode( + { + id: 123, + column_name: 'column name', + verbose_name: 'verbose name', + }, + ref, + ), + ).toStrictEqual(rvNode); +}); + +test('should get column name as tooltip if it overflowed', () => { + const ref = { current: { scrollWidth: 200, clientWidth: 100 } }; + expect( + getColumnTooltipNode( + { + id: 123, + column_name: 'long long long long column name', + verbose_name: '', + }, + ref, + ), + ).toBe('column name: long long long long column name'); +}); + +test('should get column name and verbose name as tooltip if it overflowed', () => { + const rvNode = ( + <> +
column name: long long long long column name
+
verbose name: long long long long verbose name
+ + ); + + const ref = { current: { scrollWidth: 200, clientWidth: 100 } }; + expect( + getColumnTooltipNode( + { + id: 123, + column_name: 'long long long long column name', + verbose_name: 'long long long long verbose name', + }, + ref, + ), + ).toStrictEqual(rvNode); +}); + +test('should get null as tooltip in metric', () => { + const ref = { current: { scrollWidth: 100, clientWidth: 100 } }; + expect( + getMetricTooltipNode( + { + metric_name: 'count', + label: '', + verbose_name: '', + }, + ref, + ), + ).toBe(null); +}); + +test('should get metric name and verbose name as tooltip in metric', () => { + const rvNode = ( + <> +
metric name: count
+
verbose name: count(*)
+ + ); + + const ref = { current: { scrollWidth: 100, clientWidth: 100 } }; + expect( + getMetricTooltipNode( + { + metric_name: 'count', + label: 'count(*)', + verbose_name: 'count(*)', + }, + ref, + ), + ).toStrictEqual(rvNode); +}); + +test('should get metric name and verbose name in tooltip if it overflowed', () => { + const rvNode = ( + <> +
metric name: count
+
verbose name: longlonglonglonglong verbose metric
+ + ); + + const ref = { current: { scrollWidth: 200, clientWidth: 100 } }; + expect( + getMetricTooltipNode( + { + metric_name: 'count', + label: '', + verbose_name: 'longlonglonglonglong verbose metric', + }, + ref, + ), + ).toStrictEqual(rvNode); +}); + +test('should get label name as tooltip in metric if it overflowed', () => { + const ref = { current: { scrollWidth: 200, clientWidth: 100 } }; + expect( + getMetricTooltipNode( + { + metric_name: 'count', + label: 'longlonglonglonglong metric label', + verbose_name: '', + }, + ref, + ), + ).toBe('label name: longlonglonglonglong metric label'); +}); diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/shared-controls/emitFilterControl.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/shared-controls/emitFilterControl.test.tsx index 019263ff8e57..6070ccccfdee 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/shared-controls/emitFilterControl.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/shared-controls/emitFilterControl.test.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { emitFilterControl } from '../../src/shared-controls/emitFilterControl'; +import { emitFilterControl } from '@superset-ui/chart-controls'; describe('isFeatureFlagEnabled', () => { it('returns empty array for unset feature flag', () => { diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 0a3cf7b76674..caf8c1ff59c6 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -45,6 +45,8 @@ export interface Props { datasource: DatasourceControl; }; actions: Partial & Pick; + // we use this props control force update when this panel resize + shouldForceUpdate?: number; } const Button = styled.button` @@ -123,32 +125,11 @@ const LabelContainer = (props: { className: string; }) => { const labelRef = useRef(null); - const [showTooltip, setShowTooltip] = useState(true); - const isLabelTruncated = () => - !!( - labelRef && - labelRef.current && - labelRef.current.scrollWidth > labelRef.current.clientWidth - ); - const handleShowTooltip = () => { - const shouldShowTooltip = isLabelTruncated(); - if (shouldShowTooltip !== showTooltip) { - setShowTooltip(shouldShowTooltip); - } - }; - const handleResetTooltip = () => { - setShowTooltip(true); - }; const extendedProps = { labelRef, - showTooltip, }; return ( - + {React.cloneElement(props.children, extendedProps)} ); @@ -162,6 +143,7 @@ export default function DataSourcePanel({ datasource, controls: { datasource: datasourceControl }, actions, + shouldForceUpdate, }: Props) { const { columns: _columns, metrics } = datasource; @@ -309,7 +291,10 @@ export default function DataSourcePanel({ )} {metricSlice.map(m => ( - + {enableExploreDnd ? ( {columnSlice.map(col => ( - + {enableExploreDnd ? ( )} - setSidebarWidths(LocalStorageKeys.datasource_width, d) - } + onResizeStop={(evt, direction, ref, d) => { + setShouldForceUpdate(d?.width); + setSidebarWidths(LocalStorageKeys.datasource_width, d); + }} defaultSize={{ width: getSidebarWidths(LocalStorageKeys.datasource_width), height: '100%', @@ -559,6 +561,7 @@ function ExploreViewContainer(props) { datasource={props.datasource} controls={props.controls} actions={props.actions} + shouldForceUpdate={shouldForceUpdate} /> {isCollapsed ? ( diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx index 450a858b312d..18cd57645eca 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx @@ -144,7 +144,6 @@ export default function OptionWrapper( ); diff --git a/superset-frontend/src/explore/components/controls/OptionControls/index.tsx b/superset-frontend/src/explore/components/controls/OptionControls/index.tsx index 716e10a908b7..0e68b5d4d045 100644 --- a/superset-frontend/src/explore/components/controls/OptionControls/index.tsx +++ b/superset-frontend/src/explore/components/controls/OptionControls/index.tsx @@ -280,13 +280,7 @@ export const OptionControlLabel = ({ labelRef.current.scrollWidth > labelRef.current.clientWidth); if (savedMetric && hasMetricName) { - return ( - - ); + return ; } if (!shouldShowTooltip) { return {label};