feat(explore): make dnd controls clickable (#16119)
* Make ghost buttons clickable * Popover for column control * Make column dnd ghost button clickable * Prefill operator only if column is defined * Remove data-tests * lint fix * Hide new features behind a feature flag * Change ghost button texts * Remove caret for non clickable columns
This commit is contained in:
parent
5e75baf0cc
commit
203c311ca3
|
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* eslint-disable camelcase */
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import Tabs from 'src/components/Tabs';
|
||||
import Button from 'src/components/Button';
|
||||
import { NativeSelect as Select } from 'src/components/Select';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
|
||||
import { Form, FormItem } from 'src/components/Form';
|
||||
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
|
||||
const StyledSelect = styled(Select)`
|
||||
.metric-option {
|
||||
& > svg {
|
||||
min-width: ${({ theme }) => `${theme.gridUnit * 4}px`};
|
||||
}
|
||||
& > .option-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface ColumnSelectPopoverProps {
|
||||
columns: ColumnMeta[];
|
||||
editedColumn?: ColumnMeta;
|
||||
onChange: (column: ColumnMeta) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ColumnSelectPopover = ({
|
||||
columns,
|
||||
editedColumn,
|
||||
onChange,
|
||||
onClose,
|
||||
}: ColumnSelectPopoverProps) => {
|
||||
const [
|
||||
initialCalculatedColumn,
|
||||
initialSimpleColumn,
|
||||
] = editedColumn?.expression
|
||||
? [editedColumn, undefined]
|
||||
: [undefined, editedColumn];
|
||||
const [selectedCalculatedColumn, setSelectedCalculatedColumn] = useState(
|
||||
initialCalculatedColumn,
|
||||
);
|
||||
const [selectedSimpleColumn, setSelectedSimpleColumn] = useState(
|
||||
initialSimpleColumn,
|
||||
);
|
||||
|
||||
const [calculatedColumns, simpleColumns] = useMemo(
|
||||
() =>
|
||||
columns?.reduce(
|
||||
(acc: [ColumnMeta[], ColumnMeta[]], column: ColumnMeta) => {
|
||||
if (column.expression) {
|
||||
acc[0].push(column);
|
||||
} else {
|
||||
acc[1].push(column);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[[], []],
|
||||
),
|
||||
[columns],
|
||||
);
|
||||
|
||||
const onCalculatedColumnChange = useCallback(
|
||||
selectedColumnName => {
|
||||
const selectedColumn = calculatedColumns.find(
|
||||
col => col.column_name === selectedColumnName,
|
||||
);
|
||||
setSelectedCalculatedColumn(selectedColumn);
|
||||
setSelectedSimpleColumn(undefined);
|
||||
},
|
||||
[calculatedColumns],
|
||||
);
|
||||
|
||||
const onSimpleColumnChange = useCallback(
|
||||
selectedColumnName => {
|
||||
const selectedColumn = simpleColumns.find(
|
||||
col => col.column_name === selectedColumnName,
|
||||
);
|
||||
setSelectedCalculatedColumn(undefined);
|
||||
setSelectedSimpleColumn(selectedColumn);
|
||||
},
|
||||
[simpleColumns],
|
||||
);
|
||||
|
||||
const defaultActiveTabKey =
|
||||
initialSimpleColumn || calculatedColumns.length === 0 ? 'simple' : 'saved';
|
||||
|
||||
const onSave = useCallback(() => {
|
||||
const selectedColumn = selectedCalculatedColumn || selectedSimpleColumn;
|
||||
if (!selectedColumn) {
|
||||
return;
|
||||
}
|
||||
onChange(selectedColumn);
|
||||
onClose();
|
||||
}, [onChange, onClose, selectedCalculatedColumn, selectedSimpleColumn]);
|
||||
|
||||
const onResetStateAndClose = useCallback(() => {
|
||||
setSelectedCalculatedColumn(initialCalculatedColumn);
|
||||
setSelectedSimpleColumn(initialSimpleColumn);
|
||||
onClose();
|
||||
}, [initialCalculatedColumn, initialSimpleColumn, onClose]);
|
||||
|
||||
const stateIsValid = selectedCalculatedColumn || selectedSimpleColumn;
|
||||
const hasUnsavedChanges =
|
||||
selectedCalculatedColumn?.column_name !==
|
||||
initialCalculatedColumn?.column_name ||
|
||||
selectedSimpleColumn?.column_name !== initialSimpleColumn?.column_name;
|
||||
|
||||
const filterOption = useCallback(
|
||||
(input, option) =>
|
||||
option?.filterBy.toLowerCase().indexOf(input.toLowerCase()) >= 0,
|
||||
[],
|
||||
);
|
||||
|
||||
const getPopupContainer = useCallback(
|
||||
(triggerNode: any) => triggerNode.parentNode,
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form layout="vertical" id="metrics-edit-popover">
|
||||
<Tabs
|
||||
id="adhoc-metric-edit-tabs"
|
||||
defaultActiveKey={defaultActiveTabKey}
|
||||
className="adhoc-metric-edit-tabs"
|
||||
allowOverflow
|
||||
>
|
||||
<Tabs.TabPane key="saved" tab={t('Saved')}>
|
||||
<FormItem label={t('Saved expressions')}>
|
||||
<StyledSelect
|
||||
value={selectedCalculatedColumn?.column_name}
|
||||
getPopupContainer={getPopupContainer}
|
||||
onChange={onCalculatedColumnChange}
|
||||
allowClear
|
||||
showSearch
|
||||
autoFocus={!selectedCalculatedColumn}
|
||||
filterOption={filterOption}
|
||||
placeholder={t('%s column(s)', calculatedColumns.length)}
|
||||
>
|
||||
{calculatedColumns.map(calculatedColumn => (
|
||||
<Select.Option
|
||||
value={calculatedColumn.column_name}
|
||||
filterBy={
|
||||
calculatedColumn.verbose_name ||
|
||||
calculatedColumn.column_name
|
||||
}
|
||||
key={calculatedColumn.column_name}
|
||||
>
|
||||
<StyledColumnOption column={calculatedColumn} showType />
|
||||
</Select.Option>
|
||||
))}
|
||||
</StyledSelect>
|
||||
</FormItem>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="simple" tab={t('Simple')}>
|
||||
<FormItem label={t('Column')}>
|
||||
<Select
|
||||
value={selectedSimpleColumn?.column_name}
|
||||
getPopupContainer={getPopupContainer}
|
||||
onChange={onSimpleColumnChange}
|
||||
allowClear
|
||||
showSearch
|
||||
autoFocus={!selectedSimpleColumn}
|
||||
filterOption={filterOption}
|
||||
placeholder={t('%s column(s)', simpleColumns.length)}
|
||||
>
|
||||
{simpleColumns.map(simpleColumn => (
|
||||
<Select.Option
|
||||
value={simpleColumn.column_name}
|
||||
filterBy={
|
||||
simpleColumn.verbose_name || simpleColumn.column_name
|
||||
}
|
||||
key={simpleColumn.column_name}
|
||||
>
|
||||
<StyledColumnOption column={simpleColumn} showType />
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<div>
|
||||
<Button buttonSize="small" onClick={onResetStateAndClose} cta>
|
||||
{t('Close')}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!stateIsValid}
|
||||
buttonStyle={
|
||||
hasUnsavedChanges && stateIsValid ? 'primary' : 'default'
|
||||
}
|
||||
buttonSize="small"
|
||||
onClick={onSave}
|
||||
cta
|
||||
>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColumnSelectPopover;
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* 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, { useCallback, useMemo, useState } from 'react';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import Popover from 'src/components/Popover';
|
||||
import { ExplorePopoverContent } from 'src/explore/components/ExploreContentPopover';
|
||||
import ColumnSelectPopover from './ColumnSelectPopover';
|
||||
|
||||
interface ColumnSelectPopoverTriggerProps {
|
||||
columns: ColumnMeta[];
|
||||
editedColumn?: ColumnMeta;
|
||||
onColumnEdit: (editedColumn: ColumnMeta) => void;
|
||||
isControlledComponent?: boolean;
|
||||
visible?: boolean;
|
||||
togglePopover?: (visible: boolean) => void;
|
||||
closePopover?: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ColumnSelectPopoverTrigger = ({
|
||||
columns,
|
||||
editedColumn,
|
||||
onColumnEdit,
|
||||
isControlledComponent,
|
||||
children,
|
||||
...props
|
||||
}: ColumnSelectPopoverTriggerProps) => {
|
||||
const [popoverVisible, setPopoverVisible] = useState(false);
|
||||
|
||||
const togglePopover = useCallback((visible: boolean) => {
|
||||
setPopoverVisible(visible);
|
||||
}, []);
|
||||
|
||||
const closePopover = useCallback(() => {
|
||||
setPopoverVisible(false);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
visible,
|
||||
handleTogglePopover,
|
||||
handleClosePopover,
|
||||
} = isControlledComponent
|
||||
? {
|
||||
visible: props.visible,
|
||||
handleTogglePopover: props.togglePopover!,
|
||||
handleClosePopover: props.closePopover!,
|
||||
}
|
||||
: {
|
||||
visible: popoverVisible,
|
||||
handleTogglePopover: togglePopover,
|
||||
handleClosePopover: closePopover,
|
||||
};
|
||||
|
||||
const overlayContent = useMemo(
|
||||
() => (
|
||||
<ExplorePopoverContent>
|
||||
<ColumnSelectPopover
|
||||
editedColumn={editedColumn}
|
||||
columns={columns}
|
||||
onClose={handleClosePopover}
|
||||
onChange={onColumnEdit}
|
||||
/>
|
||||
</ExplorePopoverContent>
|
||||
),
|
||||
[columns, editedColumn, handleClosePopover, onColumnEdit],
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="right"
|
||||
trigger="click"
|
||||
content={overlayContent}
|
||||
defaultVisible={visible}
|
||||
visible={visible}
|
||||
onVisibleChange={handleTogglePopover}
|
||||
destroyTooltipOnHide
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColumnSelectPopoverTrigger;
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { tn } from '@superset-ui/core';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { FeatureFlag, isFeatureEnabled, tn } from '@superset-ui/core';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { LabelProps } from 'src/explore/components/controls/DndColumnSelectControl/types';
|
||||
|
|
@ -27,6 +27,7 @@ import { OptionSelector } from 'src/explore/components/controls/DndColumnSelectC
|
|||
import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/types';
|
||||
import { DndItemType } from 'src/explore/components/DndItemType';
|
||||
import { useComponentDidUpdate } from 'src/common/hooks/useComponentDidUpdate';
|
||||
import ColumnSelectPopoverTrigger from './ColumnSelectPopoverTrigger';
|
||||
|
||||
export const DndColumnSelect = (props: LabelProps) => {
|
||||
const {
|
||||
|
|
@ -39,6 +40,8 @@ export const DndColumnSelect = (props: LabelProps) => {
|
|||
name,
|
||||
label,
|
||||
} = props;
|
||||
const [newColumnPopoverVisible, setNewColumnPopoverVisible] = useState(false);
|
||||
|
||||
const optionSelector = useMemo(
|
||||
() => new OptionSelector(options, multi, value),
|
||||
[multi, options, value],
|
||||
|
|
@ -110,41 +113,120 @@ export const DndColumnSelect = (props: LabelProps) => {
|
|||
[onChange, optionSelector],
|
||||
);
|
||||
|
||||
const popoverOptions = useMemo(
|
||||
() =>
|
||||
Object.values(options).filter(
|
||||
col =>
|
||||
!optionSelector.values
|
||||
.map(val => val.column_name)
|
||||
.includes(col.column_name),
|
||||
),
|
||||
[optionSelector.values, options],
|
||||
);
|
||||
|
||||
const valuesRenderer = useCallback(
|
||||
() =>
|
||||
optionSelector.values.map((column, idx) => (
|
||||
<OptionWrapper
|
||||
key={idx}
|
||||
index={idx}
|
||||
clickClose={onClickClose}
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={`${DndItemType.ColumnOption}_${name}_${label}`}
|
||||
canDelete={canDelete}
|
||||
column={column}
|
||||
/>
|
||||
)),
|
||||
optionSelector.values.map((column, idx) =>
|
||||
isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX) ? (
|
||||
<ColumnSelectPopoverTrigger
|
||||
columns={popoverOptions}
|
||||
onColumnEdit={newColumn => {
|
||||
optionSelector.replace(idx, newColumn.column_name);
|
||||
onChange(optionSelector.getValues());
|
||||
}}
|
||||
editedColumn={column}
|
||||
>
|
||||
<OptionWrapper
|
||||
key={idx}
|
||||
index={idx}
|
||||
clickClose={onClickClose}
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={`${DndItemType.ColumnOption}_${name}_${label}`}
|
||||
canDelete={canDelete}
|
||||
column={column}
|
||||
withCaret
|
||||
/>
|
||||
</ColumnSelectPopoverTrigger>
|
||||
) : (
|
||||
<OptionWrapper
|
||||
key={idx}
|
||||
index={idx}
|
||||
clickClose={onClickClose}
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={`${DndItemType.ColumnOption}_${name}_${label}`}
|
||||
canDelete={canDelete}
|
||||
column={column}
|
||||
/>
|
||||
),
|
||||
),
|
||||
[
|
||||
canDelete,
|
||||
label,
|
||||
name,
|
||||
onChange,
|
||||
onClickClose,
|
||||
onShiftOptions,
|
||||
optionSelector.values,
|
||||
optionSelector,
|
||||
popoverOptions,
|
||||
],
|
||||
);
|
||||
|
||||
const addNewColumnWithPopover = useCallback(
|
||||
(newColumn: ColumnMeta) => {
|
||||
optionSelector.add(newColumn.column_name);
|
||||
onChange(optionSelector.getValues());
|
||||
},
|
||||
[onChange, optionSelector],
|
||||
);
|
||||
|
||||
const togglePopover = useCallback((visible: boolean) => {
|
||||
setNewColumnPopoverVisible(visible);
|
||||
}, []);
|
||||
|
||||
const closePopover = useCallback(() => {
|
||||
togglePopover(false);
|
||||
}, [togglePopover]);
|
||||
|
||||
const openPopover = useCallback(() => {
|
||||
togglePopover(true);
|
||||
}, [togglePopover]);
|
||||
|
||||
const defaultGhostButtonText = isFeatureEnabled(
|
||||
FeatureFlag.ENABLE_DND_WITH_CLICK_UX,
|
||||
)
|
||||
? tn(
|
||||
'Drop a column here or click',
|
||||
'Drop columns here or click',
|
||||
multi ? 2 : 1,
|
||||
)
|
||||
: tn('Drop column here', 'Drop columns here', multi ? 2 : 1);
|
||||
|
||||
return (
|
||||
<DndSelectLabel<string | string[], ColumnMeta[]>
|
||||
onDrop={onDrop}
|
||||
canDrop={canDrop}
|
||||
valuesRenderer={valuesRenderer}
|
||||
accept={DndItemType.Column}
|
||||
displayGhostButton={multi || optionSelector.values.length === 0}
|
||||
ghostButtonText={
|
||||
ghostButtonText ||
|
||||
tn('Drop column here', 'Drop columns here', multi ? 2 : 1)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
<div>
|
||||
<DndSelectLabel<string | string[], ColumnMeta[]>
|
||||
onDrop={onDrop}
|
||||
canDrop={canDrop}
|
||||
valuesRenderer={valuesRenderer}
|
||||
accept={DndItemType.Column}
|
||||
displayGhostButton={multi || optionSelector.values.length === 0}
|
||||
ghostButtonText={ghostButtonText || defaultGhostButtonText}
|
||||
onClickGhostButton={
|
||||
isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
|
||||
? openPopover
|
||||
: undefined
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
<ColumnSelectPopoverTrigger
|
||||
columns={popoverOptions}
|
||||
onColumnEdit={addNewColumnWithPopover}
|
||||
isControlledComponent
|
||||
togglePopover={togglePopover}
|
||||
closePopover={closePopover}
|
||||
visible={newColumnPopoverVisible}
|
||||
>
|
||||
<div />
|
||||
</ColumnSelectPopoverTrigger>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -333,6 +333,11 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||
],
|
||||
);
|
||||
|
||||
const handleClickGhostButton = useCallback(() => {
|
||||
setDroppedItem(null);
|
||||
togglePopover(true);
|
||||
}, [togglePopover]);
|
||||
|
||||
const adhocFilter = useMemo(() => {
|
||||
if (droppedItem?.metric_name) {
|
||||
return new AdhocFilter({
|
||||
|
|
@ -351,7 +356,7 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||
const config: Partial<AdhocFilter> = {
|
||||
subject: (droppedItem as ColumnMeta)?.column_name,
|
||||
};
|
||||
if (isFeatureEnabled(FeatureFlag.UX_BETA)) {
|
||||
if (config.subject && isFeatureEnabled(FeatureFlag.UX_BETA)) {
|
||||
config.operator = OPERATOR_ENUM_TO_OPERATOR_TYPE[Operators.IN].operation;
|
||||
config.operatorId = Operators.IN;
|
||||
}
|
||||
|
|
@ -367,6 +372,10 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||
[togglePopover],
|
||||
);
|
||||
|
||||
const ghostButtonText = isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
|
||||
? t('Drop columns/metrics here or click')
|
||||
: t('Drop columns or metrics here');
|
||||
|
||||
return (
|
||||
<>
|
||||
<DndSelectLabel<OptionValueType, OptionValueType[]>
|
||||
|
|
@ -374,7 +383,12 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||
canDrop={canDrop}
|
||||
valuesRenderer={valuesRenderer}
|
||||
accept={DND_ACCEPTED_TYPES}
|
||||
ghostButtonText={t('Drop columns or metrics here')}
|
||||
ghostButtonText={ghostButtonText}
|
||||
onClickGhostButton={
|
||||
isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
|
||||
? handleClickGhostButton
|
||||
: undefined
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
<AdhocFilterPopoverTrigger
|
||||
|
|
|
|||
|
|
@ -312,6 +312,11 @@ export const DndMetricSelect = (props: any) => {
|
|||
[onNewMetric, togglePopover],
|
||||
);
|
||||
|
||||
const handleClickGhostButton = useCallback(() => {
|
||||
setDroppedItem(null);
|
||||
togglePopover(true);
|
||||
}, [togglePopover]);
|
||||
|
||||
const adhocMetric = useMemo(() => {
|
||||
if (droppedItem?.type === DndItemType.Column) {
|
||||
const itemValue = droppedItem?.value as ColumnMeta;
|
||||
|
|
@ -334,6 +339,18 @@ export const DndMetricSelect = (props: any) => {
|
|||
return new AdhocMetric({ isNew: true });
|
||||
}, [droppedItem]);
|
||||
|
||||
const ghostButtonText = isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
|
||||
? tn(
|
||||
'Drop a column/metric here or click',
|
||||
'Drop columns/metrics here or click',
|
||||
multi ? 2 : 1,
|
||||
)
|
||||
: tn(
|
||||
'Drop column or metric here',
|
||||
'Drop columns or metrics here',
|
||||
multi ? 2 : 1,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="metrics-select">
|
||||
<DndSelectLabel<OptionValueType, OptionValueType[]>
|
||||
|
|
@ -341,12 +358,13 @@ export const DndMetricSelect = (props: any) => {
|
|||
canDrop={canDrop}
|
||||
valuesRenderer={valuesRenderer}
|
||||
accept={DND_ACCEPTED_TYPES}
|
||||
ghostButtonText={tn(
|
||||
'Drop column or metric here',
|
||||
'Drop columns or metrics here',
|
||||
multi ? 2 : 1,
|
||||
)}
|
||||
ghostButtonText={ghostButtonText}
|
||||
displayGhostButton={multi || value.length === 0}
|
||||
onClickGhostButton={
|
||||
isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
|
||||
? handleClickGhostButton
|
||||
: undefined
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
<AdhocMetricPopoverTrigger
|
||||
|
|
|
|||
|
|
@ -55,7 +55,10 @@ export default function DndSelectLabel<T, O>({
|
|||
|
||||
function renderGhostButton() {
|
||||
return (
|
||||
<AddControlLabel cancelHover>
|
||||
<AddControlLabel
|
||||
cancelHover={!props.onClickGhostButton}
|
||||
onClick={props.onClickGhostButton}
|
||||
>
|
||||
<Icons.PlusSmall iconColor={theme.colors.grayscale.light1} />
|
||||
{t(props.ghostButtonText || 'Drop columns here')}
|
||||
</AddControlLabel>
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export interface DndColumnSelectProps<
|
|||
accept: DndItemType | DndItemType[];
|
||||
ghostButtonText?: string;
|
||||
displayGhostButton?: boolean;
|
||||
onClickGhostButton?: () => void;
|
||||
}
|
||||
|
||||
export type OptionValueType = Record<string, any>;
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = {
|
|||
"OMNIBAR": False,
|
||||
"DASHBOARD_RBAC": False,
|
||||
"ENABLE_EXPLORE_DRAG_AND_DROP": False,
|
||||
"ENABLE_DND_WITH_CLICK_UX": False,
|
||||
# Enabling ALERTS_ATTACH_REPORTS, the system sends email and slack message
|
||||
# with screenshot and link
|
||||
# Disables ALERTS_ATTACH_REPORTS, the system DOES NOT generate screenshot
|
||||
|
|
|
|||
Loading…
Reference in New Issue