Skip to content

Commit

Permalink
feat(explore): UX improvements for drag'n'dropping time column (apach…
Browse files Browse the repository at this point in the history
  • Loading branch information
kgabryje authored and cccs-RyanS committed Dec 17, 2021
1 parent 4ae726f commit 7e7c89e
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 25 deletions.
22 changes: 19 additions & 3 deletions superset-frontend/src/explore/components/DatasourcePanel/index.tsx
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -88,6 +98,7 @@ export const DndColumnSelect = (props: LabelProps) => {
clickClose={onClickClose}
onShiftOptions={onShiftOptions}
type={DndItemType.ColumnOption}
canDelete={canDelete}
>
<StyledColumnOption column={column} showType />
</OptionWrapper>
Expand All @@ -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}
/>
);
Expand Down
Expand Up @@ -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);
};
Expand Down
Expand Up @@ -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 (
<OptionControlContainer
data-test="option-label"
withCaret={props.withCaret}
>
<CloseContainer
role="button"
data-test="remove-control-button"
onClick={() => props.clickClose(props.index)}
>
<Icons.XSmall iconColor={theme.colors.grayscale.light1} />
</CloseContainer>
<Label data-test="control-label">{props.children}</Label>
{props.isExtra && (
<OptionControlContainer data-test="option-label" withCaret={withCaret}>
{canDelete && (
<CloseContainer
role="button"
data-test="remove-control-button"
onClick={() => clickClose(index)}
>
<Icons.XSmall iconColor={theme.colors.grayscale.light1} />
</CloseContainer>
)}
<Label data-test="control-label">{children}</Label>
{isExtra && (
<StyledInfoTooltipWithTrigger
icon="exclamation-triangle"
placement="top"
Expand All @@ -58,7 +64,7 @@ export default function Option(props: OptionProps) {
`)}
/>
)}
{props.withCaret && (
{withCaret && (
<CaretContainer>
<Icons.CaretRight iconColor={theme.colors.grayscale.light1} />
</CaretContainer>
Expand Down
Expand Up @@ -44,6 +44,7 @@ export default function OptionWrapper(
clickClose,
withCaret,
isExtra,
canDelete = true,
children,
...rest
} = props;
Expand Down Expand Up @@ -113,6 +114,7 @@ export default function OptionWrapper(
clickClose={clickClose}
withCaret={withCaret}
isExtra={isExtra}
canDelete={canDelete}
>
{children}
</Option>
Expand Down
Expand Up @@ -28,6 +28,7 @@ export interface OptionProps {
clickClose: (index: number) => void;
withCaret?: boolean;
isExtra?: boolean;
canDelete?: boolean;
}

export interface OptionItemInterface {
Expand All @@ -41,6 +42,8 @@ export interface LabelProps<T = string[] | string> {
onChange: (value?: T) => void;
options: { string: ColumnMeta };
multi?: boolean;
canDelete?: boolean;
ghostButtonText?: string;
}

export interface DndColumnSelectProps<
Expand Down

0 comments on commit 7e7c89e

Please sign in to comment.