diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 9ce26d918e14..8e83bb090db1 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -16,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { styled, t } from '@superset-ui/core'; import Collapse from 'src/components/Collapse'; import { ControlConfig, DatasourceMeta } from '@superset-ui/chart-controls'; import { debounce } from 'lodash'; import { matchSorter, rankings } from 'match-sorter'; import { FAST_DEBOUNCE } from 'src/constants'; -import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; +import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; import DatasourcePanelDragWrapper from './DatasourcePanelDragWrapper'; @@ -120,7 +120,23 @@ export default function DataSourcePanel({ controls: { datasource: datasourceControl }, actions, }: Props) { - const { columns, metrics } = datasource; + const { columns: _columns, metrics } = datasource; + + // display temporal column first + const columns = useMemo( + () => + [..._columns].sort((col1, col2) => { + if (col1.is_dttm && !col2.is_dttm) { + return -1; + } + if (col2.is_dttm && !col1.is_dttm) { + return 1; + } + return 0; + }), + [_columns], + ); + const [inputValue, setInputValue] = useState(''); const [lists, setList] = useState({ columns, diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx index 29cd738d6d2f..d677aa2104d8 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx @@ -29,7 +29,14 @@ import { DndItemType } from 'src/explore/components/DndItemType'; import { StyledColumnOption } from 'src/explore/components/optionRenderers'; export const DndColumnSelect = (props: LabelProps) => { - const { value, options, multi = true, onChange } = props; + const { + value, + options, + multi = true, + onChange, + canDelete = true, + ghostButtonText, + } = props; const optionSelector = new OptionSelector(options, multi, value); // synchronize values in case of dataset changes @@ -66,9 +73,12 @@ export const DndColumnSelect = (props: LabelProps) => { onChange(optionSelector.getValues()); }; - const canDrop = (item: DatasourcePanelDndItem) => - (multi || optionSelector.values.length === 0) && - !optionSelector.has((item.value as ColumnMeta).column_name); + const canDrop = (item: DatasourcePanelDndItem) => { + const columnName = (item.value as ColumnMeta).column_name; + return ( + columnName in optionSelector.options && !optionSelector.has(columnName) + ); + }; const onClickClose = (index: number) => { optionSelector.del(index); @@ -88,6 +98,7 @@ export const DndColumnSelect = (props: LabelProps) => { clickClose={onClickClose} onShiftOptions={onShiftOptions} type={DndItemType.ColumnOption} + canDelete={canDelete} > @@ -100,7 +111,9 @@ export const DndColumnSelect = (props: LabelProps) => { valuesRenderer={valuesRenderer} accept={DndItemType.Column} displayGhostButton={multi || optionSelector.values.length === 0} - ghostButtonText={tn('Drop column', 'Drop columns', multi ? 2 : 1)} + ghostButtonText={ + ghostButtonText || tn('Drop column', 'Drop columns', multi ? 2 : 1) + } {...props} /> ); diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx index a3daeaf70211..9b38c159ee81 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx @@ -157,11 +157,11 @@ export const DndMetricSelect = (props: any) => { const canDrop = (item: DatasourcePanelDndItem) => { const isMetricAlreadyInValues = item.type === 'metric' ? value.includes(item.value.metric_name) : false; - return (props.multi || value.length === 0) && !isMetricAlreadyInValues; + return !isMetricAlreadyInValues; }; const onNewMetric = (newMetric: Metric) => { - const newValue = [...value, newMetric]; + const newValue = props.isMulti ? [...value, newMetric] : [newMetric]; setValue(newValue); handleChange(newValue); }; diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/Option.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/Option.tsx index 11c68a6373e4..50e1bfe0deec 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/Option.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/Option.tsx @@ -32,22 +32,28 @@ const StyledInfoTooltipWithTrigger = styled(InfoTooltipWithTrigger)` margin: 0 ${({ theme }) => theme.gridUnit}px; `; -export default function Option(props: OptionProps) { +export default function Option({ + children, + index, + clickClose, + withCaret, + isExtra, + canDelete = true, +}: OptionProps) { const theme = useTheme(); return ( - - props.clickClose(props.index)} - > - - - - {props.isExtra && ( + + {canDelete && ( + clickClose(index)} + > + + + )} + + {isExtra && ( )} - {props.withCaret && ( + {withCaret && ( diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx index 40da4c4ff57a..62230c56f87b 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/OptionWrapper.tsx @@ -44,6 +44,7 @@ export default function OptionWrapper( clickClose, withCaret, isExtra, + canDelete = true, children, ...rest } = props; @@ -113,6 +114,7 @@ export default function OptionWrapper( clickClose={clickClose} withCaret={withCaret} isExtra={isExtra} + canDelete={canDelete} > {children} diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts index da2c51a1d5a6..2d7142e2d735 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts @@ -28,6 +28,7 @@ export interface OptionProps { clickClose: (index: number) => void; withCaret?: boolean; isExtra?: boolean; + canDelete?: boolean; } export interface OptionItemInterface { @@ -41,6 +42,8 @@ export interface LabelProps { onChange: (value?: T) => void; options: { string: ColumnMeta }; multi?: boolean; + canDelete?: boolean; + ghostButtonText?: string; } export interface DndColumnSelectProps<