Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* 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 { fireEvent, render } from 'spec/helpers/testing-library';
import { OptionControlLabel } from 'src/explore/components/controls/OptionControls';

import ExploreContainer, { DraggingContext } from '.';
import OptionWrapper from '../controls/DndColumnSelectControl/OptionWrapper';

const MockChildren = () => {
const dragging = React.useContext(DraggingContext);
return (
<div data-test="mock-children" className={dragging ? 'dragging' : ''}>
{dragging ? 'dragging' : 'not dragging'}
</div>
);
};

test('should render children', () => {
const { getByTestId, getByText } = render(
<ExploreContainer>
<MockChildren />
</ExploreContainer>,
{ useRedux: true, useDnd: true },
);
expect(getByTestId('mock-children')).toBeInTheDocument();
expect(getByText('not dragging')).toBeInTheDocument();
});

test('should update the style on dragging state', () => {
const defaultProps = {
label: <span>Test label</span>,
tooltipTitle: 'This is a tooltip title',
onRemove: jest.fn(),
onMoveLabel: jest.fn(),
onDropLabel: jest.fn(),
type: 'test',
index: 0,
};
const { container, getByText } = render(
<ExploreContainer>
<OptionControlLabel
{...defaultProps}
index={1}
label={<span>Label 1</span>}
/>
<OptionWrapper
{...defaultProps}
index={2}
label="Label 2"
clickClose={() => {}}
onShiftOptions={() => {}}
/>
<MockChildren />
</ExploreContainer>,
{
useRedux: true,
useDnd: true,
},
);
expect(container.getElementsByClassName('dragging')).toHaveLength(0);
fireEvent.dragStart(getByText('Label 1'));
expect(container.getElementsByClassName('dragging')).toHaveLength(1);
fireEvent.dragEnd(getByText('Label 1'));
expect(container.getElementsByClassName('dragging')).toHaveLength(0);
// don't show dragging state for the sorting item
fireEvent.dragStart(getByText('Label 2'));
expect(container.getElementsByClassName('dragging')).toHaveLength(0);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* 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, { useEffect } from 'react';
import { styled } from '@superset-ui/core';
import { useDragDropManager } from 'react-dnd';

export const DraggingContext = React.createContext(false);
const StyledDiv = styled.div`
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
`;
const ExploreContainer: React.FC<{}> = ({ children }) => {
const dragDropManager = useDragDropManager();
const [dragging, setDragging] = React.useState(
dragDropManager.getMonitor().isDragging(),
);

useEffect(() => {
const monitor = dragDropManager.getMonitor();
const unsub = monitor.subscribeToStateChange(() => {
const item = monitor.getItem() || {};
// don't show dragging state for the sorting item
if ('dragIndex' in item) {
return;
}
const isDragging = monitor.isDragging();
setDragging(isDragging);
});

return () => {
unsub();
};
}, [dragDropManager]);

return (
<DraggingContext.Provider value={dragging}>
<StyledDiv>{children}</StyledDiv>
</DraggingContext.Provider>
);
};

export default ExploreContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import ConnectedControlPanelsContainer from '../ControlPanelsContainer';
import SaveModal from '../SaveModal';
import DataSourcePanel from '../DatasourcePanel';
import ConnectedExploreChartHeader from '../ExploreChartHeader';
import ExploreContainer from '../ExploreContainer';

const propTypes = {
...ExploreChartPanel.propTypes,
Expand All @@ -90,13 +91,6 @@ const propTypes = {
isSaveModalVisible: PropTypes.bool,
};

const ExploreContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
`;

const ExplorePanelContainer = styled.div`
${({ theme }) => css`
background: ${theme.colors.grayscale.light5};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { ReactNode, useMemo } from 'react';
import React, { ReactNode, useContext, useMemo } from 'react';
import { useDrop } from 'react-dnd';
import { t, useTheme } from '@superset-ui/core';
import ControlHeader from 'src/explore/components/ControlHeader';
Expand All @@ -31,6 +31,7 @@ import {
} from 'src/explore/components/DatasourcePanel/types';
import Icons from 'src/components/Icons';
import { DndItemType } from '../../DndItemType';
import { DraggingContext } from '../../ExploreContainer';

export type DndSelectLabelProps = {
name: string;
Expand All @@ -43,18 +44,20 @@ export type DndSelectLabelProps = {
valuesRenderer: () => ReactNode;
displayGhostButton?: boolean;
onClickGhostButton: () => void;
isLoading?: boolean;
};

export default function DndSelectLabel({
displayGhostButton = true,
accept,
valuesRenderer,
isLoading,
...props
}: DndSelectLabelProps) {
const theme = useTheme();

const [{ isOver, canDrop }, datasourcePanelDrop] = useDrop({
accept,
accept: isLoading ? [] : accept,

drop: (item: DatasourcePanelDndItem) => {
props.onDrop(item);
Expand All @@ -70,6 +73,7 @@ export default function DndSelectLabel({
type: monitor.getItemType(),
}),
});
const isDragging = useContext(DraggingContext);

const values = useMemo(() => valuesRenderer(), [valuesRenderer]);

Expand All @@ -94,6 +98,8 @@ export default function DndSelectLabel({
data-test="dnd-labels-container"
canDrop={canDrop}
isOver={isOver}
isDragging={isDragging}
isLoading={isLoading}
>
{values}
{displayGhostButton && renderGhostButton()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
import React, { useRef } from 'react';
import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd';
import { styled, t, useTheme } from '@superset-ui/core';
import { styled, t, useTheme, keyframes, css } from '@superset-ui/core';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import { Tooltip } from 'src/components/Tooltip';
import Icons from 'src/components/Icons';
Expand Down Expand Up @@ -103,21 +103,89 @@ export const LabelsContainer = styled.div`
border-radius: ${({ theme }) => theme.gridUnit}px;
`;

const borderPulse = keyframes`
0% {
right: 100%;
}
50% {
left: 4px;
}
90% {
right: 4px;
}
100% {
left: 100%;
}
`;

export const DndLabelsContainer = styled.div<{
canDrop?: boolean;
isOver?: boolean;
isDragging?: boolean;
isLoading?: boolean;
}>`
padding: ${({ theme }) => theme.gridUnit}px;
border: ${({ canDrop, isOver, theme }) => {
if (canDrop) {
return `dashed 1px ${theme.colors.info.dark1}`;
}
if (isOver && !canDrop) {
return `dashed 1px ${theme.colors.error.dark1}`;
}
return `solid 1px ${theme.colors.grayscale.light2}`;
}};
border-radius: ${({ theme }) => theme.gridUnit}px;
${({ theme, isLoading, canDrop, isDragging, isOver }) => `
position: relative;
padding: ${theme.gridUnit}px;
border: ${
!isLoading && isDragging
? `dashed 1px ${
canDrop ? theme.colors.info.dark1 : theme.colors.error.dark1
}`
: `solid 1px ${
isLoading && isDragging
? theme.colors.warning.light1
: theme.colors.grayscale.light2
}`
};
border-radius: ${theme.gridUnit}px;
&:before,
&:after {
content: ' ';
position: absolute;
border-radius: ${theme.gridUnit}px;
}
&:before {
display: ${isDragging || isLoading ? 'block' : 'none'};
background-color: ${
canDrop ? theme.colors.primary.base : theme.colors.error.light1
};
z-index: ${theme.zIndex.aboveDashboardCharts};
opacity: ${theme.opacity.light};
top: 1px;
right: 1px;
bottom: 1px;
left: 1px;
}
&:after {
display: ${isLoading || (canDrop && isOver) ? 'block' : 'none'};
background-color: ${
isLoading ? theme.colors.grayscale.light3 : theme.colors.primary.base
};
z-index: ${theme.zIndex.dropdown};
opacity: ${theme.opacity.mediumLight};
top: ${-theme.gridUnit}px;
right: ${-theme.gridUnit}px;
bottom: ${-theme.gridUnit}px;
left: ${-theme.gridUnit}px;
cursor: ${isLoading ? 'wait' : 'auto'};
}
`}

&:before {
${({ theme, isLoading }) =>
isLoading &&
css`
animation: ${borderPulse} 2s ease-in infinite;
background: linear-gradient(currentColor 0 0) 0 100%/0% 3px no-repeat;
background-size: 100% ${theme.gridUnit / 2}px;
top: auto;
right: ${theme.gridUnit}px;
left: ${theme.gridUnit}px;
bottom: -${theme.gridUnit / 2}px;
height: ${theme.gridUnit / 2}px;
`};
}
`;

export const AddControlLabel = styled.div<{
Expand Down
3 changes: 3 additions & 0 deletions superset-frontend/src/pages/Chart/Chart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe('ChartPage', () => {
const { getByTestId } = render(<ChartPage />, {
useRouter: true,
useRedux: true,
useDnd: true,
});
await waitFor(() =>
expect(fetchMock.calls(exploreApiRoute).length).toBe(1),
Expand Down Expand Up @@ -110,6 +111,7 @@ describe('ChartPage', () => {
const { getByTestId } = render(<ChartPage />, {
useRouter: true,
useRedux: true,
useDnd: true,
});
await waitFor(() =>
expect(fetchMock.calls(exploreApiRoute).length).toBe(1),
Expand Down Expand Up @@ -156,6 +158,7 @@ describe('ChartPage', () => {
{
useRouter: true,
useRedux: true,
useDnd: true,
},
);
await waitFor(() =>
Expand Down