This is an automated email from the ASF dual-hosted git repository.
justinpark pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 300ddaedf9 fix(dashboard): narrow empty drop area (#26313)
300ddaedf9 is described below
commit 300ddaedf97f582a8d73bc78e02ff3c434a4934f
Author: JUST.in DO IT <[email protected]>
AuthorDate: Thu Jan 4 09:44:23 2024 -0800
fix(dashboard): narrow empty drop area (#26313)
---
.../DashboardBuilder/DashboardBuilder.tsx | 70 +----------
.../DashboardBuilder/DashboardWrapper.test.tsx | 75 ++++++++++++
.../DashboardBuilder/DashboardWrapper.tsx | 128 +++++++++++++++++++++
.../src/dashboard/components/DashboardGrid.jsx | 19 ++-
.../src/dashboard/components/dnd/DragDroppable.jsx | 11 +-
.../dashboard/components/gridComponents/Tab.jsx | 8 +-
6 files changed, 235 insertions(+), 76 deletions(-)
diff --git
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index a17b168374..51de15c7a1 100644
---
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -86,72 +86,10 @@ import {
import { getRootLevelTabsComponent, shouldFocusTabs } from './utils';
import DashboardContainer from './DashboardContainer';
import { useNativeFilters } from './state';
+import DashboardWrapper from './DashboardWrapper';
type DashboardBuilderProps = {};
-const StyledDiv = styled.div`
- ${({ theme }) => css`
- display: grid;
- grid-template-columns: auto 1fr;
- grid-template-rows: auto 1fr;
- flex: 1;
- /* Special cases */
-
- /* A row within a column has inset hover menu */
- .dragdroppable-column .dragdroppable-row .hover-menu--left {
- left: ${theme.gridUnit * -3}px;
- background: ${theme.colors.grayscale.light5};
- border: 1px solid ${theme.colors.grayscale.light2};
- }
-
- .dashboard-component-tabs {
- position: relative;
- }
-
- /* A column within a column or tabs has inset hover menu */
- .dragdroppable-column .dragdroppable-column .hover-menu--top,
- .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
- top: ${theme.gridUnit * -3}px;
- background: ${theme.colors.grayscale.light5};
- border: 1px solid ${theme.colors.grayscale.light2};
- }
-
- /* move Tabs hover menu to top near actual Tabs */
- .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
- top: 0;
- transform: unset;
- background: transparent;
- }
-
- /* push Chart actions to upper right */
- .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
- .dragdroppable .dashboard-component-header .hover-menu--top {
- right: ${theme.gridUnit * 2}px;
- top: ${theme.gridUnit * 2}px;
- background: transparent;
- border: none;
- transform: unset;
- left: unset;
- }
- div:hover > .hover-menu-container .hover-menu,
- .hover-menu-container .hover-menu:hover {
- opacity: 1;
- }
-
- p {
- margin: 0 0 ${theme.gridUnit * 2}px 0;
- }
-
- i.danger {
- color: ${theme.colors.error.base};
- }
-
- i.warning {
- color: ${theme.colors.alert.base};
- }
- `}
-`;
-
// @z-index-above-dashboard-charts + 1 = 11
const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
grid-column: 1;
@@ -317,7 +255,7 @@ const DashboardContentWrapper = styled.div`
width: 100%;
}
- & > .empty-droptarget:first-child {
+ & > .empty-droptarget:first-child:not(.empty-droptarget--full) {
height: ${theme.gridUnit * 4}px;
top: -2px;
z-index: 10;
@@ -640,7 +578,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
: theme.gridUnit * 8;
return (
- <StyledDiv>
+ <DashboardWrapper>
{showFilterBar && filterBarOrientation === FilterBarOrientation.VERTICAL
&& (
<>
<ResizableSidebar
@@ -749,7 +687,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
`}
/>
)}
- </StyledDiv>
+ </DashboardWrapper>
);
};
diff --git
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
new file mode 100644
index 0000000000..fb913b4627
--- /dev/null
+++
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
@@ -0,0 +1,75 @@
+/**
+ * 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 DashboardWrapper from './DashboardWrapper';
+
+test('should render children', () => {
+ const { getByTestId } = render(
+ <DashboardWrapper>
+ <div data-test="mock-children" />
+ </DashboardWrapper>,
+ { useRedux: true, useDnd: true },
+ );
+ expect(getByTestId('mock-children')).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(
+ <DashboardWrapper>
+ <OptionControlLabel
+ {...defaultProps}
+ index={1}
+ label={<span>Label 1</span>}
+ />
+ <OptionControlLabel
+ {...defaultProps}
+ index={2}
+ label={<span>Label 2</span>}
+ />
+ </DashboardWrapper>,
+ {
+ useRedux: true,
+ useDnd: true,
+ initialState: {
+ dashboardState: {
+ editMode: true,
+ },
+ },
+ },
+ );
+ expect(
+ container.getElementsByClassName('dragdroppable--dragging'),
+ ).toHaveLength(0);
+ fireEvent.dragStart(getByText('Label 1'));
+ expect(
+ container.getElementsByClassName('dragdroppable--dragging'),
+ ).toHaveLength(1);
+});
diff --git
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
new file mode 100644
index 0000000000..f39c7ed630
--- /dev/null
+++
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
@@ -0,0 +1,128 @@
+/**
+ * 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 { css, styled } from '@superset-ui/core';
+import { RootState } from 'src/dashboard/types';
+import { useSelector } from 'react-redux';
+import { useDragDropManager } from 'react-dnd';
+import classNames from 'classnames';
+
+const StyledDiv = styled.div`
+ ${({ theme }) => css`
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: auto 1fr;
+ flex: 1;
+ /* Special cases */
+
+ &.dragdroppable--dragging
+ .dashboard-component-tabs-content
+ > .empty-droptarget.empty-droptarget--full {
+ height: 100%;
+ }
+
+ /* A row within a column has inset hover menu */
+ .dragdroppable-column .dragdroppable-row .hover-menu--left {
+ left: ${theme.gridUnit * -3}px;
+ background: ${theme.colors.grayscale.light5};
+ border: 1px solid ${theme.colors.grayscale.light2};
+ }
+
+ .dashboard-component-tabs {
+ position: relative;
+ }
+
+ /* A column within a column or tabs has inset hover menu */
+ .dragdroppable-column .dragdroppable-column .hover-menu--top,
+ .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
+ top: ${theme.gridUnit * -3}px;
+ background: ${theme.colors.grayscale.light5};
+ border: 1px solid ${theme.colors.grayscale.light2};
+ }
+
+ /* move Tabs hover menu to top near actual Tabs */
+ .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
+ top: 0;
+ transform: unset;
+ background: transparent;
+ }
+
+ /* push Chart actions to upper right */
+ .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
+ .dragdroppable .dashboard-component-header .hover-menu--top {
+ right: ${theme.gridUnit * 2}px;
+ top: ${theme.gridUnit * 2}px;
+ background: transparent;
+ border: none;
+ transform: unset;
+ left: unset;
+ }
+ div:hover > .hover-menu-container .hover-menu,
+ .hover-menu-container .hover-menu:hover {
+ opacity: 1;
+ }
+
+ p {
+ margin: 0 0 ${theme.gridUnit * 2}px 0;
+ }
+
+ i.danger {
+ color: ${theme.colors.error.base};
+ }
+
+ i.warning {
+ color: ${theme.colors.alert.base};
+ }
+ `}
+`;
+
+type Props = {};
+
+const DashboardWrapper: React.FC<Props> = ({ children }) => {
+ const editMode = useSelector<RootState, boolean>(
+ state => state.dashboardState.editMode,
+ );
+ const dragDropManager = useDragDropManager();
+ const [isDragged, setIsDragged] = React.useState(
+ dragDropManager.getMonitor().isDragging(),
+ );
+
+ useEffect(() => {
+ const monitor = dragDropManager.getMonitor();
+ const unsub = monitor.subscribeToStateChange(() => {
+ setIsDragged(monitor.isDragging());
+ });
+
+ return () => {
+ unsub();
+ };
+ }, [dragDropManager]);
+
+ return (
+ <StyledDiv
+ className={classNames({
+ 'dragdroppable--dragging': editMode && isDragged,
+ })}
+ >
+ {children}
+ </StyledDiv>
+ );
+};
+
+export default DashboardWrapper;
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
index 601dbac4a4..70cf65218f 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
import { addAlpha, css, styled, t } from '@superset-ui/core';
import { EmptyStateBig } from 'src/components/EmptyState';
import { componentShape } from '../util/propShapes';
@@ -76,10 +77,14 @@ const GridContent = styled.div`
& > .empty-droptarget:first-child {
height: ${theme.gridUnit * 12}px;
margin-top: ${theme.gridUnit * -6}px;
- margin-bottom: ${theme.gridUnit * -6}px;
}
- & > .empty-droptarget:only-child {
+ & > .empty-droptarget:last-child {
+ height: ${theme.gridUnit * 12}px;
+ margin-top: ${theme.gridUnit * -6}px;
+ }
+
+ & > .empty-droptarget.empty-droptarget--full:only-child {
height: 80vh;
}
`}
@@ -270,10 +275,14 @@ class DashboardGrid extends React.PureComponent {
index={0}
orientation="column"
onDrop={this.handleTopDropTargetDrop}
- className="empty-droptarget"
+ className={classNames({
+ 'empty-droptarget': true,
+ 'empty-droptarget--full':
+ gridComponent?.children?.length === 0,
+ })}
editMode
>
- {renderDraggableContentBottom}
+ {renderDraggableContentTop}
</DragDroppable>
)}
{gridComponent?.children?.map((id, index) => (
@@ -304,7 +313,7 @@ class DashboardGrid extends React.PureComponent {
className="empty-droptarget"
editMode
>
- {renderDraggableContentTop}
+ {renderDraggableContentBottom}
</DragDroppable>
)}
{isResizing &&
diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
index 3bc9f4d299..6a49f98875 100644
--- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
@@ -90,6 +90,11 @@ const DragDroppableStyles = styled.div`
z-index: 10;
}
+ &.empty-droptarget--full > .drop-indicator--top {
+ height: 100%;
+ opacity: 0.3;
+ }
+
& {
.drop-indicator {
display: block;
@@ -99,7 +104,7 @@ const DragDroppableStyles = styled.div`
}
.drop-indicator--top {
- top: 0;
+ top: ${-theme.gridUnit - 2}px;
left: 0;
height: ${theme.gridUnit}px;
width: 100%;
@@ -107,7 +112,7 @@ const DragDroppableStyles = styled.div`
}
.drop-indicator--bottom {
- top: 100%;
+ bottom: ${-theme.gridUnit - 2}px;
left: 0;
height: ${theme.gridUnit}px;
width: 100%;
@@ -116,7 +121,7 @@ const DragDroppableStyles = styled.div`
.drop-indicator--right {
top: 0;
- left: 100%;
+ left: calc(100% - ${theme.gridUnit}px);
height: 100%;
width: ${theme.gridUnit}px;
min-height: ${theme.gridUnit * 4}px;
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index 32ac77936c..d1d08176ba 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { styled, t } from '@superset-ui/core';
@@ -173,7 +174,10 @@ class Tab extends React.PureComponent {
depth={depth}
onDrop={this.handleTopDropTargetDrop}
editMode
- className="empty-droptarget"
+ className={classNames({
+ 'empty-droptarget': true,
+ 'empty-droptarget--full': tabComponent.children.length === 0,
+ })}
>
{renderDraggableContentTop}
</DragDroppable>
@@ -234,7 +238,7 @@ class Tab extends React.PureComponent {
/>
))}
{/* Make bottom of tab droppable */}
- {editMode && (
+ {editMode && tabComponent.children.length > 0 && (
<DragDroppable
component={tabComponent}
parentComponent={tabParentComponent}