mirror of
https://github.com/apache/superset.git
synced 2026-04-07 10:31:50 +00:00
fix(Explore): Show the tooltip only when label does not fit the container in METRICS/FILTERS/GROUP BY/SORT BY of the DATA panel (#16060)
* Implement dynamic tooltip * Normalize and consolidate * Clean up * Refactor and clean up * Remove unnecessary var * Fix type import * Update superset-frontend/src/explore/components/controls/OptionControls/index.tsx Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com> * Remove unnecessary styled span * Show full tooltip title * Force show tooltip * Force show tooltip D&D off Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com> Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
This commit is contained in:
@@ -26,7 +26,6 @@ import OptionWrapper from 'src/explore/components/controls/DndColumnSelectContro
|
||||
import { OptionSelector } from 'src/explore/components/controls/DndColumnSelectControl/utils';
|
||||
import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/types';
|
||||
import { DndItemType } from 'src/explore/components/DndItemType';
|
||||
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
|
||||
import { useComponentDidUpdate } from 'src/common/hooks/useComponentDidUpdate';
|
||||
|
||||
export const DndColumnSelect = (props: LabelProps) => {
|
||||
@@ -121,9 +120,8 @@ export const DndColumnSelect = (props: LabelProps) => {
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={`${DndItemType.ColumnOption}_${name}_${label}`}
|
||||
canDelete={canDelete}
|
||||
>
|
||||
<StyledColumnOption column={column} showType />
|
||||
</OptionWrapper>
|
||||
column={column}
|
||||
/>
|
||||
)),
|
||||
[
|
||||
canDelete,
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import {
|
||||
OPERATOR_ENUM_TO_OPERATOR_TYPE,
|
||||
Operators,
|
||||
@@ -299,6 +298,7 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
||||
() =>
|
||||
values.map((adhocFilter: AdhocFilter, index: number) => {
|
||||
const label = adhocFilter.getDefaultLabel();
|
||||
const tooltipTitle = adhocFilter.getTooltipTitle();
|
||||
return (
|
||||
<AdhocFilterPopoverTrigger
|
||||
key={index}
|
||||
@@ -311,14 +311,14 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
||||
<OptionWrapper
|
||||
key={index}
|
||||
index={index}
|
||||
label={label}
|
||||
tooltipTitle={tooltipTitle}
|
||||
clickClose={onClickClose}
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={DndItemType.FilterOption}
|
||||
withCaret
|
||||
isExtra={adhocFilter.isExtra}
|
||||
>
|
||||
<Tooltip title={label}>{label}</Tooltip>
|
||||
</OptionWrapper>
|
||||
/>
|
||||
</AdhocFilterPopoverTrigger>
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -28,9 +28,8 @@ test('renders with default props', () => {
|
||||
clickClose={jest.fn()}
|
||||
type={'Column' as DndItemType}
|
||||
onShiftOptions={jest.fn()}
|
||||
>
|
||||
Option
|
||||
</OptionWrapper>,
|
||||
label="Option"
|
||||
/>,
|
||||
{ useDnd: true },
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
@@ -46,17 +45,15 @@ test('triggers onShiftOptions on drop', () => {
|
||||
clickClose={jest.fn()}
|
||||
type={'Column' as DndItemType}
|
||||
onShiftOptions={onShiftOptions}
|
||||
>
|
||||
Option 1
|
||||
</OptionWrapper>
|
||||
label="Option 1"
|
||||
/>
|
||||
<OptionWrapper
|
||||
index={2}
|
||||
clickClose={jest.fn()}
|
||||
type={'Column' as DndItemType}
|
||||
onShiftOptions={onShiftOptions}
|
||||
>
|
||||
Option 2
|
||||
</OptionWrapper>
|
||||
label="Option 2"
|
||||
/>
|
||||
</>,
|
||||
{ useDnd: true },
|
||||
);
|
||||
|
||||
@@ -28,8 +28,19 @@ import {
|
||||
OptionProps,
|
||||
OptionItemInterface,
|
||||
} from 'src/explore/components/controls/DndColumnSelectControl/types';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
|
||||
import { styled } from '@superset-ui/core';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import Option from './Option';
|
||||
|
||||
export const OptionLabel = styled.div`
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export default function OptionWrapper(
|
||||
props: OptionProps & {
|
||||
type: string;
|
||||
@@ -38,16 +49,19 @@ export default function OptionWrapper(
|
||||
) {
|
||||
const {
|
||||
index,
|
||||
label,
|
||||
tooltipTitle,
|
||||
column,
|
||||
type,
|
||||
onShiftOptions,
|
||||
clickClose,
|
||||
withCaret,
|
||||
isExtra,
|
||||
canDelete = true,
|
||||
children,
|
||||
...rest
|
||||
} = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const labelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const item: OptionItemInterface = useMemo(
|
||||
() => ({
|
||||
@@ -56,7 +70,7 @@ export default function OptionWrapper(
|
||||
}),
|
||||
[index, type],
|
||||
);
|
||||
const [, drag] = useDrag({
|
||||
const [{ isDragging }, drag] = useDrag({
|
||||
item,
|
||||
collect: (monitor: DragSourceMonitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
@@ -107,6 +121,51 @@ export default function OptionWrapper(
|
||||
},
|
||||
});
|
||||
|
||||
const shouldShowTooltip =
|
||||
(!isDragging && tooltipTitle && label && tooltipTitle !== label) ||
|
||||
(!isDragging &&
|
||||
labelRef &&
|
||||
labelRef.current &&
|
||||
labelRef.current.scrollWidth > labelRef.current.clientWidth);
|
||||
|
||||
const LabelContent = () => {
|
||||
if (!shouldShowTooltip) {
|
||||
return <span>{label}</span>;
|
||||
}
|
||||
return (
|
||||
<Tooltip title={tooltipTitle || label}>
|
||||
<span>{label}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const ColumnOption = () => (
|
||||
<StyledColumnOption
|
||||
column={column as ColumnMeta}
|
||||
labelRef={labelRef}
|
||||
showTooltip={!!shouldShowTooltip}
|
||||
showType
|
||||
/>
|
||||
);
|
||||
|
||||
const Label = () => {
|
||||
if (label) {
|
||||
return (
|
||||
<OptionLabel ref={labelRef}>
|
||||
<LabelContent />
|
||||
</OptionLabel>
|
||||
);
|
||||
}
|
||||
if (column) {
|
||||
return (
|
||||
<OptionLabel>
|
||||
<ColumnOption />
|
||||
</OptionLabel>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
drag(drop(ref));
|
||||
|
||||
return (
|
||||
@@ -118,7 +177,7 @@ export default function OptionWrapper(
|
||||
isExtra={isExtra}
|
||||
canDelete={canDelete}
|
||||
>
|
||||
{children}
|
||||
<Label />
|
||||
</Option>
|
||||
</DragContainer>
|
||||
);
|
||||
|
||||
@@ -26,8 +26,11 @@ import {
|
||||
import { DndItemType } from '../../DndItemType';
|
||||
|
||||
export interface OptionProps {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
index: number;
|
||||
label?: string;
|
||||
tooltipTitle?: string;
|
||||
column?: ColumnMeta;
|
||||
clickClose: (index: number) => void;
|
||||
withCaret?: boolean;
|
||||
isExtra?: boolean;
|
||||
|
||||
@@ -45,11 +45,10 @@ export const OptionControlContainer = styled.div<{
|
||||
border-radius: 3px;
|
||||
cursor: ${({ withCaret }) => (withCaret ? 'pointer' : 'default')};
|
||||
`;
|
||||
|
||||
export const Label = styled.div`
|
||||
${({ theme }) => `
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
align-items: center;
|
||||
@@ -71,6 +70,11 @@ export const Label = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
const LabelText = styled.span`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
export const CaretContainer = styled.div`
|
||||
height: 100%;
|
||||
border-left: solid 1px ${({ theme }) => theme.colors.grayscale.dark2}0C;
|
||||
@@ -197,6 +201,8 @@ export const OptionControlLabel = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const labelRef = useRef<HTMLDivElement>(null);
|
||||
const hasMetricName = savedMetric?.metric_name;
|
||||
const [, drop] = useDrop({
|
||||
accept: type,
|
||||
drop() {
|
||||
@@ -250,7 +256,7 @@ export const OptionControlLabel = ({
|
||||
item.index = hoverIndex;
|
||||
},
|
||||
});
|
||||
const [, drag] = useDrag({
|
||||
const [{ isDragging }, drag] = useDrag({
|
||||
item: {
|
||||
type,
|
||||
index,
|
||||
@@ -262,10 +268,34 @@ export const OptionControlLabel = ({
|
||||
});
|
||||
|
||||
const getLabelContent = () => {
|
||||
if (savedMetric?.metric_name) {
|
||||
return <StyledMetricOption metric={savedMetric} />;
|
||||
const shouldShowTooltip =
|
||||
(!isDragging &&
|
||||
typeof label === 'string' &&
|
||||
tooltipTitle &&
|
||||
label &&
|
||||
tooltipTitle !== label) ||
|
||||
(!isDragging &&
|
||||
labelRef &&
|
||||
labelRef.current &&
|
||||
labelRef.current.scrollWidth > labelRef.current.clientWidth);
|
||||
|
||||
if (savedMetric && hasMetricName) {
|
||||
return (
|
||||
<StyledMetricOption
|
||||
metric={savedMetric}
|
||||
labelRef={labelRef}
|
||||
showTooltip={!!shouldShowTooltip}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <Tooltip title={tooltipTitle}>{label}</Tooltip>;
|
||||
if (!shouldShowTooltip) {
|
||||
return <LabelText ref={labelRef}>{label}</LabelText>;
|
||||
}
|
||||
return (
|
||||
<Tooltip title={tooltipTitle || label}>
|
||||
<LabelText ref={labelRef}>{label}</LabelText>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const getOptionControlContent = () => (
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
const OptionContainer = styled.div`
|
||||
width: 100%;
|
||||
> span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user