From feafce8b33dde2d2d27483002725e9fa851537f5 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Thu, 15 Dec 2022 16:49:56 +0100 Subject: [PATCH 01/22] Move row.less, new-component.less and divider.less to Emotion --- .../DashboardBuilder.test.tsx | 4 +- .../DashboardBuilder/DashboardBuilder.tsx | 52 ++++++++++- .../components/gridComponents/Divider.jsx | 21 ++++- .../components/gridComponents/Row.jsx | 39 +++++++- .../new/DraggableNewComponent.jsx | 58 ++++++++++-- .../stylesheets/components/divider.less | 42 --------- .../stylesheets/components/index.less | 3 - .../stylesheets/components/new-component.less | 55 ----------- .../dashboard/stylesheets/components/row.less | 92 ------------------- 9 files changed, 159 insertions(+), 207 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/divider.less delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/new-component.less delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/row.less diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx index 65969e2ca6d7f..1edd09ceeda11 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx @@ -125,7 +125,7 @@ describe('DashboardBuilder', () => { it('should render a StickyContainer with class "dashboard"', () => { const { getByTestId } = setup(); - const stickyContainer = getByTestId('dashboard-content'); + const stickyContainer = getByTestId('dashboard-content-wrapper'); expect(stickyContainer).toHaveClass('dashboard'); }); @@ -133,7 +133,7 @@ describe('DashboardBuilder', () => { const { getByTestId } = setup({ dashboardState: { ...mockState.dashboardState, editMode: true }, }); - const stickyContainer = getByTestId('dashboard-content'); + const stickyContainer = getByTestId('dashboard-content-wrapper'); expect(stickyContainer).toHaveClass('dashboard dashboard--editing'); }); diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index d2ce6a2f1a3ed..733e9258422e7 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -164,6 +164,52 @@ const StyledContent = styled.div<{ ${({ fullSizeChartId }) => fullSizeChartId && `z-index: 101;`} `; +const DashboardContentWrapper = styled.div` + &.dashboard--editing { + .grid-row:after, + .dashboard-component-tabs > .hover-menu:hover + div:after { + border: 1px dashed transparent; + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + } + + .resizable-container.resizable-container--resizing:hover > .grid-row:after, + .hover-menu:hover + .grid-row:after, + .dashboard-component-tabs > .hover-menu:hover + div:after { + border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + z-index: 2; + } + + .grid-row:after, + .dashboard-component-tabs > .hover-menu + div:after { + border: 1px dashed ${({ theme }) => theme.colors.grayscale.light4}; + } + + /* provide hit area in case row contents is edge to edge */ + .dashboard-component-tabs-content { + .dragdroppable-row { + padding-top: ${({ theme }) => theme.gridUnit * 4}px; + } + + & > div:not(:last-child):not(.empty-droptarget) { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } + } + } + + & .dashboard-component-tabs-content { + & > div:not(:last-child):not(.empty-droptarget) { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } + } +`; + const StyledDashboardContent = styled.div<{ dashboardFiltersOpen: boolean; editMode: boolean; @@ -513,8 +559,8 @@ const DashboardBuilder: FC = () => { image="dashboard.svg" /> )} -
= () => { )} {editMode && } -
+ ); diff --git a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx index 8be4fe8924851..0a1c101107fad 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx @@ -18,6 +18,7 @@ */ import React from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@superset-ui/core'; import DragDroppable from '../dnd/DragDroppable'; import HoverMenu from '../menu/HoverMenu'; @@ -36,6 +37,24 @@ const propTypes = { deleteComponent: PropTypes.func.isRequired, }; +const DividerLine = styled.div` + width: 100%; + padding: ${({ theme }) => theme.gridUnit * 2}px 0; /* this is padding not margin to enable a larger mouse target */ + background-color: transparent; + + &:after { + content: ''; + height: 1px; + width: 100%; + background-color: ${({ theme }) => theme.colors.grayscale.light2}; + display: block; + } + + div[draggable='true'] & { + cursor: move; + } +`; + class Divider extends React.PureComponent { constructor(props) { super(props); @@ -75,7 +94,7 @@ class Divider extends React.PureComponent { )} -
+ {dropIndicatorProps &&
}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx index 26980701c7a4f..ccfdd4b9cc9b0 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core'; +import { FeatureFlag, isFeatureEnabled, styled } from '@superset-ui/core'; import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; import DragHandle from 'src/dashboard/components/dnd/DragHandle'; @@ -58,6 +58,39 @@ const propTypes = { updateComponents: PropTypes.func.isRequired, }; +const GridRow = styled.div` + position: relative; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: flex-start; + width: 100%; + height: fit-content; + + & > :not(:last-child):not(.hover-menu) { + margin-right: ${({ theme }) => theme.gridUnit * 4}px; + } + + &.grid-row--empty { + /* this centers the empty note content */ + align-items: center; + height: ${({ theme }) => theme.gridUnit * 25}px; + + &:before { + position: absolute; + top: 0; + left: 0; + content: 'Empty row'; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + color: ${({ theme }) => theme.colors.text.label}; + } + } +`; + class Row extends React.PureComponent { constructor(props) { super(props); @@ -201,7 +234,7 @@ class Row extends React.PureComponent { /> )} -
} -
+ )} diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx index f4f33c9333dfb..cbfe1b4d68f8c 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx @@ -19,10 +19,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; +import { styled } from '@superset-ui/core'; -import DragDroppable from '../../dnd/DragDroppable'; -import { NEW_COMPONENTS_SOURCE_ID } from '../../../util/constants'; -import { NEW_COMPONENT_SOURCE_TYPE } from '../../../util/componentTypes'; +import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; +import { NEW_COMPONENTS_SOURCE_ID } from 'src/dashboard/util/constants'; +import { NEW_COMPONENT_SOURCE_TYPE } from 'src/dashboard/util/componentTypes'; const propTypes = { id: PropTypes.string.isRequired, @@ -35,6 +36,49 @@ const defaultProps = { className: null, }; +const NewComponent = styled.div` + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + padding: ${({ theme }) => theme.gridUnit * 4}px; + background: ${({ theme }) => theme.colors.grayscale.light5}; + cursor: move; + + &:not(.static):hover { + background: ${({ theme }) => theme.colors.grayscale.light4}; + } +`; + +const NewComponentPlaceholder = styled.div` + position: relative; + background: ${({ theme }) => theme.colors.grayscale.light4}; + width: ${({ theme }) => theme.gridUnit * 10}px; + height: ${({ theme }) => theme.gridUnit * 10}px; + margin-right: ${({ theme }) => theme.gridUnit * 4}px; + border: 1px solid ${({ theme }) => theme.colors.grayscale.light5}; + display: flex; + align-items: center; + justify-content: center; + color: ${({ theme }) => theme.colors.text.label}; + font-size: ${({ theme }) => theme.typography.sizes.xxl}px; + + &.fa-window-restore { + font-size: ${({ theme }) => theme.typography.sizes.l}px; + } + + &.fa-area-chart { + font-size: ${({ theme }) => theme.typography.sizes.xl}px; + } + + &.divider-placeholder:after { + content: ''; + height: 2px; + width: 100%; + background-color: ${({ theme }) => theme.colors.grayscale.light2}; + } +`; + export default class DraggableNewComponent extends React.PureComponent { render() { const { label, id, type, className, meta } = this.props; @@ -50,14 +94,16 @@ export default class DraggableNewComponent extends React.PureComponent { editMode > {({ dragSourceRef }) => ( -
-
+ {label} -
+ )} ); diff --git a/superset-frontend/src/dashboard/stylesheets/components/divider.less b/superset-frontend/src/dashboard/stylesheets/components/divider.less deleted file mode 100644 index 7ee4956ddfc09..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/divider.less +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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. - */ -.dashboard-component-divider { - width: 100%; - padding: 8px 0; /* this is padding not margin to enable a larger mouse target */ - background-color: transparent; -} - -.dashboard-component-divider:after { - content: ''; - height: 1px; - width: 100%; - background-color: @gray-light; - display: block; -} - -.new-component-placeholder.divider-placeholder:after { - content: ''; - height: 2px; - width: 100%; - background-color: @gray-light; -} - -.dragdroppable .dashboard-component-divider { - cursor: move; -} diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less index d99e11df2a45a..cb012b14a266e 100644 --- a/superset-frontend/src/dashboard/stylesheets/components/index.less +++ b/superset-frontend/src/dashboard/stylesheets/components/index.less @@ -18,8 +18,5 @@ */ @import './chart.less'; @import './column.less'; -@import './divider.less'; @import './header.less'; -@import './new-component.less'; -@import './row.less'; @import './markdown.less'; diff --git a/superset-frontend/src/dashboard/stylesheets/components/new-component.less b/superset-frontend/src/dashboard/stylesheets/components/new-component.less deleted file mode 100644 index 2202a59ff70b6..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/new-component.less +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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 '../../../assets/stylesheets/less/variables.less'; - -.new-component { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - padding: 16px; - background: @lightest; - cursor: move; - - &:not(.static):hover { - background: @gray-bg; - } -} - -.new-component-placeholder { - position: relative; - background: @gray-bg; - width: 40px; - height: 40px; - margin-right: 16px; - border: 1px solid @lightest; - display: flex; - align-items: center; - justify-content: center; - color: @gray; - font-size: @font-size-xxl; - - &.fa-window-restore { - font-size: @font-size-l; - } - - &.fa-area-chart { - font-size: @font-size-xl; - } -} diff --git a/superset-frontend/src/dashboard/stylesheets/components/row.less b/superset-frontend/src/dashboard/stylesheets/components/row.less deleted file mode 100644 index ec096d0bfa949..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/row.less +++ /dev/null @@ -1,92 +0,0 @@ -/** - * 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. - */ -.grid-row { - position: relative; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: flex-start; - width: 100%; - height: fit-content; -} - -/* gutters between elements in a row */ -.grid-row > :not(:only-child):not(:last-child):not(.hover-menu) { - margin-right: 16px; -} - -/* hover indicator */ -.dashboard--editing { - .grid-row:after, - .dashboard-component-tabs > .hover-menu:hover + div:after { - border: 1px dashed transparent; - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: @z-index-chart; - pointer-events: none; - } - - .resizable-container.resizable-container--resizing:hover > .grid-row:after, - .hover-menu:hover + .grid-row:after, - .dashboard-component-tabs > .hover-menu:hover + div:after { - border: 1px dashed @indicator-color; - z-index: @z-index-chart--dragging; - } - - .grid-row:after, - .dashboard-component-tabs > .hover-menu + div:after { - border: 1px dashed @gray-light; - } - - /* provide hit area in case row contents is edge to edge */ - .dashboard-component-tabs-content { - .dragdroppable-row { - padding-top: 16px; - } - } -} - -/* gutters between rows within tab */ -.dashboard-component-tabs-content - > div:not(:only-child):not(:last-child):not(.empty-droptarget) { - margin-bottom: 16px; -} - -.grid-row.grid-row--empty { - /* this centers the empty note content */ - align-items: center; - height: 100px; - - &:before { - position: absolute; - top: 0; - left: 0; - content: 'Empty row'; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - color: @gray; - } -} From 7cad50cab17b70f062375a545ecd5c7f26dafd8d Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Mon, 19 Dec 2022 15:34:14 +0100 Subject: [PATCH 02/22] Move chart.less to Emotion --- .../DashboardBuilder/DashboardBuilder.tsx | 87 +++++++++- .../components/SliceHeaderControls/index.tsx | 8 + .../components/gridComponents/Chart.jsx | 13 +- .../components/gridComponents/ChartHolder.tsx | 19 ++- .../stylesheets/components/chart.less | 150 ------------------ .../stylesheets/components/index.less | 1 - superset-frontend/src/explore/main.less | 8 - 7 files changed, 116 insertions(+), 170 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/chart.less diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 733e9258422e7..4ffd3ea106511 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -179,6 +179,33 @@ const DashboardContentWrapper = styled.div` pointer-events: none; } + .resizable-container { + & .dashboard-component-chart-holder { + .dashboard-chart { + .chart-container { + cursor: move; + opacity: 0.2; + } + + .slice_container { + /* disable chart interactions in edit mode */ + pointer-events: none; + } + } + + &:hover .dashboard-chart .chart-container { + opacity: 0.7; + } + } + + &:hover, + &.resizable-container--resizing:hover { + & > .dashboard-component-chart-holder:after { + border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + } + } + } + .resizable-container.resizable-container--resizing:hover > .grid-row:after, .hover-menu:hover + .grid-row:after, .dashboard-component-tabs > .hover-menu:hover + div:after { @@ -188,7 +215,7 @@ const DashboardContentWrapper = styled.div` .grid-row:after, .dashboard-component-tabs > .hover-menu + div:after { - border: 1px dashed ${({ theme }) => theme.colors.grayscale.light4}; + border: 1px dashed ${({ theme }) => theme.colors.grayscale.light2}; } /* provide hit area in case row contents is edge to edge */ @@ -201,6 +228,25 @@ const DashboardContentWrapper = styled.div` margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; } } + + .dashboard-component-chart-holder { + &:after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + border: 1px solid transparent; + } + + &:hover:after { + border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + z-index: 2; + } + } } & .dashboard-component-tabs-content { @@ -268,11 +314,42 @@ const StyledDashboardContent = styled.div<{ } .dashboard-component-chart-holder { + width: 100%; + height: 100%; + background-color: ${({ theme }) => theme.colors.grayscale.light5}; + position: relative; + padding: ${({ theme }) => theme.gridUnit * 4}px; + overflow-y: visible; + // transitionable traits to show filter relevance - transition: opacity ${({ theme }) => theme.transitionTiming}s, - border-color ${({ theme }) => theme.transitionTiming}s, - box-shadow ${({ theme }) => theme.transitionTiming}s; - border: 0 solid transparent; + transition: opacity ${({ theme }) => theme.transitionTiming}s ease-in-out, + border-color ${({ theme }) => theme.transitionTiming}s ease-in-out, + box-shadow ${({ theme }) => theme.transitionTiming}s ease-in-out; + + &.fade-in { + border-radius: 4px; + box-shadow: inset 0 0 0 2px ${({ theme }) => theme.colors.primary.base}, + 0 0 0 3px fade(${({ theme }) => theme.colors.primary.base}, 10%); + } + + &.fade-out { + border-radius: 4px; + box-shadow: none; + } + + & .missing-chart-container { + display: flex; + flex-direction: column; + align-items: center; + overflow-y: auto; + justify-content: center; + + .missing-chart-body { + font-size: ${({ theme }) => theme.typography.sizes.s}px; + position: relative; + display: flex; + } + } } `; diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx index 5bdc442ba19a2..00e83799c76ef 100644 --- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx +++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx @@ -69,12 +69,20 @@ const MENU_KEYS = { DRILL_TO_DETAIL: 'drill_to_detail', }; +// TODO: replace 3 dots with an icon const VerticalDotsContainer = styled.div` padding: ${({ theme }) => theme.gridUnit / 4}px ${({ theme }) => theme.gridUnit * 1.5}px; .dot { display: block; + + height: ${({ theme }) => theme.gridUnit}px; + width: ${({ theme }) => theme.gridUnit}px; + border-radius: 50%; + margin: ${({ theme }) => theme.gridUnit / 2}px 0; + + background-color: ${({ theme }) => theme.colors.text.label}; } &:hover { diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index f5fa7fa4b34e3..f16805fba7cab 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -114,6 +114,15 @@ const SHOULD_UPDATE_ON_PROP_CHANGES = Object.keys(propTypes).filter( const OVERFLOWABLE_VIZ_TYPES = new Set(['filter_box']); const DEFAULT_HEADER_HEIGHT = 22; +const ChartWrapper = styled.div` + overflow: hidden; + position: relative; + + &.dashboard-chart--overflowable { + overflow: visible; + } +`; + const ChartOverlay = styled.div` position: absolute; top: 0; @@ -486,7 +495,7 @@ class Chart extends React.Component { /> )} -
-
+ ); } diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx index 30626a6ba5803..c6019e217b004 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx @@ -20,7 +20,7 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { ResizeCallback, ResizeStartCallback } from 're-resizable'; import cx from 'classnames'; import { useSelector } from 'react-redux'; - +import { css, styled } from '@superset-ui/core'; import { LayoutItem, RootState } from 'src/dashboard/types'; import AnchorLink from 'src/dashboard/components/AnchorLink'; import Chart from 'src/dashboard/containers/Chart'; @@ -69,6 +69,17 @@ interface ChartHolderProps { isInView: boolean; } +const FullSizeWrapper = styled.div<{ isFullSize: boolean }>` + ${({ isFullSize }) => + isFullSize && + css` + position: fixed; + z-index: 3000; + left: 0; + top: 0; + `}; +`; + const ChartHolder: React.FC = ({ id, parentId, @@ -261,17 +272,17 @@ const ChartHolder: React.FC = ({ onResizeStop={onResizeStop} editMode={editMode} > -
{!editMode && ( @@ -314,7 +325,7 @@ const ChartHolder: React.FC = ({
)} -
+ {dropIndicatorProps &&
} )} diff --git a/superset-frontend/src/dashboard/stylesheets/components/chart.less b/superset-frontend/src/dashboard/stylesheets/components/chart.less deleted file mode 100644 index 9f557ff95c6f7..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/chart.less +++ /dev/null @@ -1,150 +0,0 @@ -/** - * 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. - */ -.dashboard-component-chart-holder { - width: 100%; - height: 100%; - color: @gray-dark; - background-color: @lightest; - position: relative; - padding: 16px; - overflow-y: visible; - - // transitionable traits for when a filter is being actively focused - transition: opacity 0.2s, border-color 0.2s, box-shadow 0.2s; - border: 2px solid transparent; - - .missing-chart-container { - display: flex; - flex-direction: column; - align-items: center; - overflow-y: auto; - justify-content: center; - - .missing-chart-body { - font-size: @font-size-s; - position: relative; - display: flex; - } - } - - &.fade-in { - border-radius: @border-radius-large; - box-shadow: inset 0 0 0 2px @shadow-highlight, - 0 0 0 3px fade(@shadow-highlight, @opacity-light); - transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out, - border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; - } - - &.fade-out { - border-radius: @border-radius-large; - box-shadow: none; - transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out, - border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; - } -} - -.dashboard-chart { - overflow: hidden; - position: relative; -} - -.dashboard-chart.dashboard-chart--overflowable { - overflow: visible; -} - -.dashboard--editing { - .dashboard-component-chart-holder { - &:after { - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0px; - left: 0px; - z-index: @z-index-chart; - pointer-events: none; - border: 1px solid transparent; - } - - &:hover:after { - border: 1px dashed @indicator-color; - z-index: @z-index-chart--dragging; - } - } - - .resizable-container { - &:hover, - &.resizable-container--resizing:hover { - & > .dashboard-component-chart-holder:after { - border: 1px dashed @indicator-color; - } - } - } - - .resizable-container .dashboard-component-chart-holder { - .dashboard-chart { - .chart-container { - cursor: move; - opacity: 0.2; - } - - .slice_container { - /* disable chart interactions in edit mode */ - pointer-events: none; - } - } - - &:hover .dashboard-chart .chart-container { - opacity: 0.7; - } - } -} - -.dot { - @dot-diameter: 4px; - - height: @dot-diameter; - width: @dot-diameter; - border-radius: @dot-diameter / 2; - margin: @dot-diameter / 2 0; - - background-color: @gray; - display: inline-block; - - a[role='menuitem'] & { - width: 8px; - height: 8px; - margin-right: 8px; - } -} - -.time-filter-tabs > .nav-tabs { - margin-bottom: 8px; -} - -.time-filter-tabs > .nav-tabs > li > a { - padding: 4px; -} - -.full-size { - position: fixed; - z-index: @z-index-max; - left: 0; - top: 0; -} diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less index cb012b14a266e..7ead5e4ba324d 100644 --- a/superset-frontend/src/dashboard/stylesheets/components/index.less +++ b/superset-frontend/src/dashboard/stylesheets/components/index.less @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -@import './chart.less'; @import './column.less'; @import './header.less'; @import './markdown.less'; diff --git a/superset-frontend/src/explore/main.less b/superset-frontend/src/explore/main.less index 015a8a1a3bed3..df248f461216a 100644 --- a/superset-frontend/src/explore/main.less +++ b/superset-frontend/src/explore/main.less @@ -119,14 +119,6 @@ position: static; } -.time-filter-tabs > .nav-tabs { - margin-bottom: 8px; - - & > li > a { - padding: 4px; - } -} - div.section-header { font-size: @font-size-s; font-weight: @font-weight-bold; From c127539ff7105600c75c16d0f3218a9f838c82ab Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Mon, 19 Dec 2022 16:03:06 +0100 Subject: [PATCH 03/22] Move header.less to Emotion --- .../components/gridComponents/Divider.jsx | 5 ++ .../components/gridComponents/Header.jsx | 61 ++++++++++++- .../stylesheets/components/header.less | 87 ------------------- .../stylesheets/components/index.less | 1 - 4 files changed, 64 insertions(+), 90 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/header.less diff --git a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx index 0a1c101107fad..6c0313162ad0b 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx @@ -53,6 +53,11 @@ const DividerLine = styled.div` div[draggable='true'] & { cursor: move; } + + .dashboard-component-tabs & { + padding-left: ${({ theme }) => theme.gridUnit * 4}px; + padding-right: ${({ theme }) => theme.gridUnit * 4}px; + } `; class Divider extends React.PureComponent { diff --git a/superset-frontend/src/dashboard/components/gridComponents/Header.jsx b/superset-frontend/src/dashboard/components/gridComponents/Header.jsx index 938a48bf099d9..74827d1609991 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Header.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Header.jsx @@ -19,6 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; +import { styled } from '@superset-ui/core'; import PopoverDropdown from 'src/components/PopoverDropdown'; import EditableTitle from 'src/components/EditableTitle'; @@ -55,6 +56,62 @@ const propTypes = { const defaultProps = {}; +const HeaderStyles = styled.div` + font-weight: ${({ theme }) => theme.typography.weights.bold}; + width: 100%; + padding: ${({ theme }) => theme.gridUnit * 4}px 0; + + &.header-small { + font-size: ${({ theme }) => theme.typography.sizes.l}px; + } + + &.header-medium { + font-size: ${({ theme }) => theme.typography.sizes.xl}px; + } + + &.header-large { + font-size: ${({ theme }) => theme.typography.sizes.xxl}px; + } + + .dashboard--editing .dashboard-grid & { + &:after { + border: 1px dashed transparent; + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + } + + &:hover:after { + border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + z-index: 2; + } + } + + .dashboard--editing .dragdroppable-row & { + cursor: move; + } + + /** + * grids add margin between items, so don't double pad within columns + * we'll not worry about double padding on top as it can serve as a visual separator + */ + .grid-column > :not(:last-child) & { + margin-bottom: ${({ theme }) => theme.gridUnit * -4}px; + } + + .background--white &, + &.background--white, + .dashboard-component-tabs & { + padding-left: ${({ theme }) => theme.gridUnit * 4}px; + padding-right: ${({ theme }) => theme.gridUnit * 4}px; + } +`; + class Header extends React.PureComponent { constructor(props) { super(props); @@ -154,7 +211,7 @@ class Header extends React.PureComponent { ]} editMode={editMode} > -
)} -
+ {dropIndicatorProps &&
} diff --git a/superset-frontend/src/dashboard/stylesheets/components/header.less b/superset-frontend/src/dashboard/stylesheets/components/header.less deleted file mode 100644 index 355385d373fd6..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/header.less +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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. - */ -.dashboard-component-header { - width: 100%; - font-weight: @font-weight-bold; - padding: 16px 0; - color: @almost-black; -} - -.dashboard--editing { - .dashboard-grid { - .dashboard-component-header { - &:after { - border: 1px dashed transparent; - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: @z-index-chart; - pointer-events: none; - } - - &:hover:after { - border: 1px dashed @indicator-color; - z-index: @z-index-chart--dragging; - } - } - } - - .dragdroppable-row .dashboard-component-header { - cursor: move; - } -} - -.header-style-option { - font-weight: @font-weight-bold; - color: @almost-black; -} - -.dashboard--editing - -/* note: sizes should be a multiple of the 8px grid unit so that rows in the grid align */ -.header-small { - font-size: @font-size-l; -} - -.header-medium { - font-size: @font-size-xl; -} - -.header-large { - font-size: @font-size-xxl; -} - -.background--white .dashboard-component-header, -.dashboard-component-header.background--white, -.dashboard-component-tabs .dashboard-component-header, -.dashboard-component-tabs .dashboard-component-divider { - padding-left: 16px; - padding-right: 16px; -} - -/* - * grids add margin between items, so don't double pad within columns - * we'll not worry about double padding on top as it can serve as a visual separator - */ -.grid-column > :not(:only-child):not(:last-child) .dashboard-component-header { - margin-bottom: -16px; -} diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less index 7ead5e4ba324d..5b1992e952728 100644 --- a/superset-frontend/src/dashboard/stylesheets/components/index.less +++ b/superset-frontend/src/dashboard/stylesheets/components/index.less @@ -17,5 +17,4 @@ * under the License. */ @import './column.less'; -@import './header.less'; @import './markdown.less'; From 342962825274208ae0989b48618a7259d5f24cd2 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 20 Dec 2022 11:04:24 +0100 Subject: [PATCH 04/22] Move column.less to Emotion --- .../components/gridComponents/Column.jsx | 51 ++++++++++++++- .../stylesheets/components/column.less | 64 ------------------- .../stylesheets/components/index.less | 1 - 3 files changed, 49 insertions(+), 67 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/column.less diff --git a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx index 90666d6d98836..20d2ef3cd946a 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx @@ -19,6 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; +import { styled } from '@superset-ui/core'; import Icons from 'src/components/Icons'; import DashboardComponent from 'src/dashboard/containers/DashboardComponent'; import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton'; @@ -58,6 +59,52 @@ const propTypes = { const defaultProps = {}; +const ColumnStyles = styled.div` + &.grid-column { + width: 100%; + position: relative; + + & > :not(.hover-menu):not(:last-child) { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } + } + + &.grid-column--empty { + min-height: ${({ theme }) => theme.gridUnit * 25}px; + + &:before { + content: 'Empty column'; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: ${({ theme }) => theme.colors.grayscale.light2}; + } + } + + .dashboard--editing &:after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + border: 1px dashed ${({ theme }) => theme.colors.grayscale.light2}; + } + .dashboard--editing + .resizable-container.resizable-container--resizing:hover + > &:after, + .dashboard--editing .hover-menu:hover + &:after { + border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + z-index: 2; + } +`; class Column extends React.PureComponent { constructor(props) { super(props); @@ -172,7 +219,7 @@ class Column extends React.PureComponent { /> )} -
} -
+ )} diff --git a/superset-frontend/src/dashboard/stylesheets/components/column.less b/superset-frontend/src/dashboard/stylesheets/components/column.less deleted file mode 100644 index 7d7b7baba3646..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/column.less +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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. - */ -.grid-column { - width: 100%; - position: relative; -} - -/* gutters between elements in a column */ -.grid-column > :not(:only-child):not(.hover-menu):not(:last-child) { - margin-bottom: 16px; -} - -.dashboard--editing { - .grid-column:after { - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: @z-index-chart; - pointer-events: none; - border: 1px dashed @gray-light; - } - - .resizable-container.resizable-container--resizing:hover > .grid-column:after, - .hover-menu:hover + .grid-column:after { - border: 1px dashed @indicator-color; - z-index: @z-index-chart--dragging; - } -} - -.grid-column--empty { - min-height: 100px; - - &:before { - content: 'Empty column'; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - color: @gray-light; - } -} diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less index 5b1992e952728..ad552ee0565f0 100644 --- a/superset-frontend/src/dashboard/stylesheets/components/index.less +++ b/superset-frontend/src/dashboard/stylesheets/components/index.less @@ -16,5 +16,4 @@ * specific language governing permissions and limitations * under the License. */ -@import './column.less'; @import './markdown.less'; From ed14d2e5527da545dc02f3ca40c8151fe13a7e08 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 20 Dec 2022 11:21:19 +0100 Subject: [PATCH 05/22] Move markdown.less to Emotion --- .../components/gridComponents/Markdown.jsx | 35 +++++++++++- .../stylesheets/components/index.less | 19 ------- .../stylesheets/components/markdown.less | 57 ------------------- .../src/dashboard/stylesheets/index.less | 1 - 4 files changed, 32 insertions(+), 80 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/index.less delete mode 100644 superset-frontend/src/dashboard/stylesheets/components/markdown.less diff --git a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx index d4a4f7790b096..18be0dc46b960 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import cx from 'classnames'; -import { t, SafeMarkdown } from '@superset-ui/core'; +import { styled, t, SafeMarkdown } from '@superset-ui/core'; import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils'; import { MarkdownEditor } from 'src/components/AsyncAceEditor'; @@ -83,6 +83,35 @@ Click here to learn more about [markdown formatting](https://bit.ly/1dQOfRK)`; const MARKDOWN_ERROR_MESSAGE = t('This markdown component has an error.'); +const MarkdownStyles = styled.div` + &.dashboard-markdown { + overflow: hidden; + + h4, + h5, + h6 { + font-weight: ${({ theme }) => theme.typography.weights.normal}; + } + + h5 { + color: ${({ theme }) => theme.colors.grayscale.base}; + } + + h6 { + font-size: ${({ theme }) => theme.typography.sizes.s}px; + } + + .dashboard-component-chart-holder { + overflow-y: auto; + overflow-x: hidden; + } + + .dashboard--editing & { + cursor: move; + } + } +`; + class Markdown extends React.PureComponent { constructor(props) { super(props); @@ -322,7 +351,7 @@ class Markdown extends React.PureComponent { ]} editMode={editMode} > -
-
+ {dropIndicatorProps &&
} )} diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less deleted file mode 100644 index ad552ee0565f0..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/index.less +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 './markdown.less'; diff --git a/superset-frontend/src/dashboard/stylesheets/components/markdown.less b/superset-frontend/src/dashboard/stylesheets/components/markdown.less deleted file mode 100644 index d8b3676eb8e96..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/components/markdown.less +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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. - */ -.dashboard-markdown { - overflow: hidden; - - h4, - h5, - h6 { - font-weight: @font-weight-normal; - } - - h5 { - color: @gray-heading; - } - - h6 { - font-size: @font-size-s; - } - - .dashboard-component-chart-holder { - overflow-y: auto; - overflow-x: hidden; - } - - .dashboard--editing & { - cursor: move; - } - - #ace-editor { - border: none; - } -} - -/* maximize editing space */ -.dashboard-markdown--editing { - .dashboard-component-chart-holder { - .with-popover-menu--focused & { - padding: 1px; - } - } -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index 0b11c2bec8061..ff0fb7d7c2545 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -25,4 +25,3 @@ @import './grid.less'; @import './popover-menu.less'; @import './resizable.less'; -@import './components/index.less'; From 9e548c2e33ddbe64be2b13cec2426863ed0a261b Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 20 Dec 2022 14:04:07 +0100 Subject: [PATCH 06/22] Move builder.less to Emotion --- .../DashboardBuilder/DashboardBuilder.tsx | 37 ++++++++++++++ .../src/dashboard/stylesheets/builder.less | 49 ------------------- .../src/dashboard/stylesheets/index.less | 1 - 3 files changed, 37 insertions(+), 50 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/builder.less diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 4ffd3ea106511..150aec9863ffb 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -165,6 +165,43 @@ const StyledContent = styled.div<{ `; const DashboardContentWrapper = styled.div` + &.dashboard { + position: relative; + flex-grow: 1; + display: flex; + flex-direction: column; + height: 100%; + + /* only top-level tabs have popover, give it more padding to match header + tabs */ + & > .with-popover-menu > .popover-menu { + left: ${({ theme }) => theme.gridUnit * 6}px; + } + + /* drop shadow for top-level tabs only */ + & .dashboard-component-tabs { + box-shadow: 0 ${({ theme }) => theme.gridUnit}px + ${({ theme }) => theme.gridUnit}px 0 + fade( + ${({ theme }) => theme.colors.grayscale.dark2}, + ${({ theme }) => theme.opacity.light} + ); + padding-left: ${({ theme }) => + theme.gridUnit * + 2}px; /* note this is added to tab-level padding, to match header */ + } + + .dropdown-toggle.btn.btn-primary .caret { + color: ${({ theme }) => theme.colors.grayscale.light5}; + } + + .background--transparent { + background-color: transparent; + } + + .background--white { + background-color: ${({ theme }) => theme.colors.grayscale.light5}; + } + } &.dashboard--editing { .grid-row:after, .dashboard-component-tabs > .hover-menu:hover + div:after { diff --git a/superset-frontend/src/dashboard/stylesheets/builder.less b/superset-frontend/src/dashboard/stylesheets/builder.less deleted file mode 100644 index 422d455622a40..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/builder.less +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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. - */ -.dashboard { - position: relative; - color: @almost-black; - flex-grow: 1; - display: flex; - flex-direction: column; - height: 100%; -} - -/* only top-level tabs have popover, give it more padding to match header + tabs */ -.dashboard > .with-popover-menu > .popover-menu { - left: 24px; -} - -/* drop shadow for top-level tabs only */ -.dashboard .dashboard-component-tabs { - box-shadow: 0 4px 4px 0 fade(@darkest, @opacity-light); - padding-left: 8px; /* note this is added to tab-level padding, to match header */ -} - -.dropdown-toggle.btn.btn-primary .caret { - color: @lightest; -} - -.background--transparent { - background-color: transparent; -} - -.background--white { - background-color: @lightest; -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index ff0fb7d7c2545..455e7d101c065 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -18,7 +18,6 @@ */ @import '../../assets/stylesheets/less/variables.less'; -@import './builder.less'; @import './dashboard.less'; @import './dnd.less'; @import './filter-scope-selector.less'; From 65e93f73ba0cbd7eb81cb9ba05b9452e6f5f1bbc Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 20 Dec 2022 15:25:49 +0100 Subject: [PATCH 07/22] Move grid.less to Emotion --- .../DashboardBuilder/DashboardBuilder.tsx | 5 ++ .../dashboard/components/DashboardGrid.jsx | 67 ++++++++++--------- .../components/DashboardGrid.test.jsx | 7 -- .../src/dashboard/stylesheets/grid.less | 53 --------------- .../src/dashboard/stylesheets/index.less | 1 - 5 files changed, 42 insertions(+), 91 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/grid.less diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 150aec9863ffb..0ede9a2ab2d93 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -343,6 +343,11 @@ const StyledDashboardContent = styled.div<{ BUILDER_SIDEPANEL_WIDTH + theme.gridUnit * 16 }px); `} + + /* this is the ParentSize wrapper */ + & > div:first-child { + height: inherit !important; + } } .dashboard-builder-sidepane { diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx index fa45486b20907..2acf80cdc1acf 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx @@ -18,7 +18,7 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import { styled, t } from '@superset-ui/core'; +import { addAlpha, css, styled, t } from '@superset-ui/core'; import { EmptyStateBig } from 'src/components/EmptyState'; import { componentShape } from '../util/propShapes'; import DashboardComponent from '../containers/DashboardComponent'; @@ -58,16 +58,45 @@ const DashboardEmptyStateContainer = styled.div` right: 0; `; +const GridContent = styled.div` + display: flex; + flex-direction: column; + + /* gutters between rows */ + & > div:not(:last-child):not(.empty-droptarget) { + margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + } +`; + +const GridColumnGuide = styled.div` + ${({ theme }) => css` + // /* Editing guides */ + &.grid-column-guide { + position: absolute; + top: 0; + min-height: 100%; + background-color: ${addAlpha( + theme.colors.primary.base, + parseFloat(theme.opacity.light) / 100, + )}; + pointer-events: none; + box-shadow: inset 0 0 0 1px + ${addAlpha( + theme.colors.primary.base, + parseFloat(theme.opacity.mediumHeavy) / 100, + )}; + } + `}; +`; + class DashboardGrid extends React.PureComponent { constructor(props) { super(props); this.state = { isResizing: false, - rowGuideTop: null, }; this.handleResizeStart = this.handleResizeStart.bind(this); - this.handleResize = this.handleResize.bind(this); this.handleResizeStop = this.handleResizeStop.bind(this); this.handleTopDropTargetDrop = this.handleTopDropTargetDrop.bind(this); this.getRowGuidePosition = this.getRowGuidePosition.bind(this); @@ -90,30 +119,17 @@ class DashboardGrid extends React.PureComponent { this.grid = ref; } - handleResizeStart({ ref, direction }) { - let rowGuideTop = null; - if (direction === 'bottom' || direction === 'bottomRight') { - rowGuideTop = this.getRowGuidePosition(ref); - } - + handleResizeStart() { this.setState(() => ({ isResizing: true, - rowGuideTop, })); } - handleResize({ ref, direction }) { - if (direction === 'bottom' || direction === 'bottomRight') { - this.setState(() => ({ rowGuideTop: this.getRowGuidePosition(ref) })); - } - } - handleResizeStop({ id, widthMultiple: width, heightMultiple: height }) { this.props.resizeComponent({ id, width, height }); this.setState(() => ({ isResizing: false, - rowGuideTop: null, })); } @@ -150,7 +166,7 @@ class DashboardGrid extends React.PureComponent { (width + GRID_GUTTER_SIZE) / GRID_COLUMN_COUNT; const columnWidth = columnPlusGutterWidth - GRID_GUTTER_SIZE; - const { isResizing, rowGuideTop } = this.state; + const { isResizing } = this.state; const shouldDisplayEmptyState = gridComponent?.children?.length === 0; const shouldDisplayTopLevelTabEmptyState = @@ -227,7 +243,7 @@ class DashboardGrid extends React.PureComponent { )}
-
+ {/* make the area above components droppable */} {editMode && ( ( -
))} - {isResizing && rowGuideTop && ( -
- )} -
+
); diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx index 4fd1704b230ec..c954ecff7be6e 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.test.jsx @@ -76,13 +76,6 @@ describe('DashboardGrid', () => { expect(wrapper.find('.grid-column-guide')).toHaveLength(GRID_COLUMN_COUNT); }); - it('should render a grid row guide when resizing', () => { - const wrapper = setup(); - expect(wrapper.find('.grid-row-guide')).not.toExist(); - wrapper.setState({ isResizing: true, rowGuideTop: 10 }); - expect(wrapper.find('.grid-row-guide')).toExist(); - }); - it('should call resizeComponent when a child DashboardComponent calls resizeStop', () => { const resizeComponent = sinon.spy(); const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 }; diff --git a/superset-frontend/src/dashboard/stylesheets/grid.less b/superset-frontend/src/dashboard/stylesheets/grid.less deleted file mode 100644 index 5b793b96bdb6a..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/grid.less +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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. - */ - -/* this is the ParentSize wrapper */ -.grid-container > div:first-child { - height: inherit !important; -} - -.grid-content { - display: flex; - flex-direction: column; -} - -/* gutters between rows */ -.grid-content > div:not(:only-child):not(:last-child):not(.empty-droptarget) { - margin-bottom: 16px; -} - -/* Editing guides */ -.grid-column-guide { - position: absolute; - top: 0; - min-height: 100%; - background-color: fade(@indicator-color, @opacity-light); - pointer-events: none; - box-shadow: inset 0 0 0 1px fade(@indicator-color, @opacity-medium-heavy); -} - -.grid-row-guide { - position: absolute; - left: 0; - bottom: 2; - height: 2; - background-color: @indicator-color; - pointer-events: none; - z-index: @z-index-above-dashboard-charts; -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index 455e7d101c065..384171147d3b8 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -21,6 +21,5 @@ @import './dashboard.less'; @import './dnd.less'; @import './filter-scope-selector.less'; -@import './grid.less'; @import './popover-menu.less'; @import './resizable.less'; From 6bed30c60390f8fc833df01b1b1e97afc5c73461 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 15:29:29 +0100 Subject: [PATCH 08/22] Move dnd.less to Emotion --- .../DashboardBuilder/DashboardBuilder.tsx | 24 ++++ .../dashboard/components/DashboardGrid.jsx | 29 +++- .../components/dnd/DragDroppable.jsx | 63 ++++++++- .../components/gridComponents/Column.jsx | 4 +- .../src/dashboard/stylesheets/dnd.less | 130 ------------------ .../src/dashboard/stylesheets/index.less | 1 - 6 files changed, 109 insertions(+), 142 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/dnd.less diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 0ede9a2ab2d93..cd663bd5f5c86 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -284,12 +284,36 @@ const DashboardContentWrapper = styled.div` z-index: 2; } } + + .contract-trigger:before { + display: none; + } } & .dashboard-component-tabs-content { & > div:not(:last-child):not(.empty-droptarget) { margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; } + + & > .empty-droptarget { + position: absolute; + width: 100%; + } + + & > .empty-droptarget:first-child { + height: ${({ theme }) => theme.gridUnit * 4}px; + top: -2px; + z-index: 10; + } + + & > .empty-droptarget:last-child { + height: ${({ theme }) => theme.gridUnit * 3}px; + bottom: 0; + } + } + + .empty-droptarget:first-child .drop-indicator--bottom { + top: ${({ theme }) => theme.gridUnit * 6}px; } `; diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx index 2acf80cdc1acf..601dbac4a4cdc 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx @@ -59,13 +59,30 @@ const DashboardEmptyStateContainer = styled.div` `; const GridContent = styled.div` - display: flex; - flex-direction: column; + ${({ theme }) => css` + display: flex; + flex-direction: column; - /* gutters between rows */ - & > div:not(:last-child):not(.empty-droptarget) { - margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; - } + /* gutters between rows */ + & > div:not(:last-child):not(.empty-droptarget) { + margin-bottom: ${theme.gridUnit * 4}px; + } + + & > .empty-droptarget { + width: 100%; + height: 100%; + } + + & > .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 { + height: 80vh; + } + `} `; const GridColumnGuide = styled.div` diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx index 8675c0bc63a37..3bc9f4d299a00 100644 --- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx +++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx @@ -21,6 +21,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { DragSource, DropTarget } from 'react-dnd'; import cx from 'classnames'; +import { css, styled } from '@superset-ui/core'; import { componentShape } from '../../util/propShapes'; import { dragConfig, dropConfig } from './dragDroppableConfig'; @@ -73,6 +74,64 @@ const defaultProps = { dragPreviewRef() {}, }; +const DragDroppableStyles = styled.div` + ${({ theme }) => css` + position: relative; + + &.dragdroppable--dragging { + opacity: 0.2; + } + + &.dragdroppable-row { + width: 100%; + } + + &.dragdroppable-column .resizable-container span div { + z-index: 10; + } + + & { + .drop-indicator { + display: block; + background-color: ${theme.colors.primary.base}; + position: absolute; + z-index: 10; + } + + .drop-indicator--top { + top: 0; + left: 0; + height: ${theme.gridUnit}px; + width: 100%; + min-width: ${theme.gridUnit * 4}px; + } + + .drop-indicator--bottom { + top: 100%; + left: 0; + height: ${theme.gridUnit}px; + width: 100%; + min-width: ${theme.gridUnit * 4}px; + } + + .drop-indicator--right { + top: 0; + left: 100%; + height: 100%; + width: ${theme.gridUnit}px; + min-height: ${theme.gridUnit * 4}px; + } + + .drop-indicator--left { + top: 0; + left: 0; + height: 100%; + width: ${theme.gridUnit}px; + min-height: ${theme.gridUnit * 4}px; + } + } + `}; +`; // export unwrapped component for testing export class UnwrappedDragDroppable extends React.PureComponent { constructor(props) { @@ -141,7 +200,7 @@ export class UnwrappedDragDroppable extends React.PureComponent { : {}; return ( -
{children(childProps)} -
+ ); } } diff --git a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx index 20d2ef3cd946a..63c2d7c89167f 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx @@ -97,9 +97,7 @@ const ColumnStyles = styled.div` pointer-events: none; border: 1px dashed ${({ theme }) => theme.colors.grayscale.light2}; } - .dashboard--editing - .resizable-container.resizable-container--resizing:hover - > &:after, + .dashboard--editing .resizable-container--resizing:hover > &:after, .dashboard--editing .hover-menu:hover + &:after { border: 1px dashed ${({ theme }) => theme.colors.primary.base}; z-index: 2; diff --git a/superset-frontend/src/dashboard/stylesheets/dnd.less b/superset-frontend/src/dashboard/stylesheets/dnd.less deleted file mode 100644 index 2465c6fac0661..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/dnd.less +++ /dev/null @@ -1,130 +0,0 @@ -/** - * 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. - */ -.dragdroppable { - position: relative; -} - -// Fixes ISSUE-12181 - before in chart's contract-trigger breaks drag and drop mode -.dashboard--editing { - .contract-trigger:before { - display: none; - } -} - -.dragdroppable--dragging { - opacity: 0.2; -} - -.dragdroppable-row { - width: 100%; -} - -.dragdroppable-column { - .resizable-container { - span { - div { - z-index: @z-index-above-dashboard-charts; - } - } - } -} - -/* drop indicators */ -.drop-indicator { - display: block; - background-color: @indicator-color; - position: absolute; - z-index: @z-index-above-dashboard-charts; -} - -.drop-indicator--top { - top: 0; - left: 0; - height: 4px; - width: 100%; - min-width: 16px; -} - -.drop-indicator--bottom { - top: 100%; - left: 0; - height: 4px; - width: 100%; - min-width: 16px; -} - -.empty-droptarget:first-child { - .drop-indicator--bottom { - top: 24px; - } -} - -.drop-indicator--right { - top: 0; - left: 100%; - height: 100%; - width: 4px; - min-height: 16px; -} - -.drop-indicator--left { - top: 0; - left: 0; - height: 100%; - width: 4px; - min-height: 16px; -} - -/* empty drop targets */ -.dashboard-component-tabs-content { - & > .empty-droptarget { - position: absolute; - width: 100%; - } - - & > .empty-droptarget:first-child { - height: 14px; - top: -2px; - z-index: @z-index-above-dashboard-charts; - } - - & > .empty-droptarget:last-child { - height: 12px; - bottom: 0px; - } -} - -.grid-content { - /* note we don't do a :last-child selection because - assuming bottom empty-droptarget is last child is fragile */ - & > .empty-droptarget { - width: 100%; - height: 100%; - } - - & > .empty-droptarget:first-child { - height: 48px; - margin-top: -24px; - margin-bottom: -24px; - } - - & > .empty-droptarget:only-child { - height: 80vh; - } -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index 384171147d3b8..497ac40b4acbb 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -19,7 +19,6 @@ @import '../../assets/stylesheets/less/variables.less'; @import './dashboard.less'; -@import './dnd.less'; @import './filter-scope-selector.less'; @import './popover-menu.less'; @import './resizable.less'; From 5fa06e47f5805aa08f79aaa37eadecafd81ccfdb Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 15:56:19 +0100 Subject: [PATCH 09/22] Move resizable.less to Emotion --- .../resizable/ResizableContainer.jsx | 93 +++++++++++++++- .../src/dashboard/stylesheets/index.less | 1 - .../src/dashboard/stylesheets/resizable.less | 105 ------------------ 3 files changed, 91 insertions(+), 108 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/resizable.less diff --git a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx index ff576101f4916..44b94f9681a4d 100644 --- a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx +++ b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx @@ -20,6 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Resizable } from 're-resizable'; import cx from 'classnames'; +import { css, styled } from '@superset-ui/core'; import ResizableHandle from './ResizableHandle'; import resizableConfig from '../../util/resizableConfig'; @@ -80,6 +81,94 @@ const HANDLE_CLASSES = { right: 'resizable-container-handle--right', bottom: 'resizable-container-handle--bottom', }; + +const StyledResizable = styled(Resizable)` + ${({ theme }) => css` + &.resizable-container { + background-color: transparent; + position: relative; + + /* re-resizable sets an empty div to 100% width and height, which doesn't + play well with many 100% height containers we need + */ + + & ~ div { + width: auto !important; + height: auto !important; + } + } + + &.resizable-container--resizing { + /* after ensures border visibility on top of any children */ + + &:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: inset 0 0 0 2px ${theme.colors.primary.base}; + } + + & > span .resize-handle { + border-color: ${theme.colors.primary.base}; + } + } + + .resize-handle { + opacity: 0; + z-index: 10; + + &--bottom-right { + position: absolute; + border-right: 1px solid ${theme.colors.text.label}; + border-bottom: 1px solid ${theme.colors.text.label}; + right: ${theme.gridUnit * 4}px; + bottom: ${theme.gridUnit * 4}px; + width: ${theme.gridUnit * 2}px; + height: ${theme.gridUnit * 2}px; + } + + &--right { + width: ${theme.gridUnit / 2}px; + height: ${theme.gridUnit * 5}px; + right: ${theme.gridUnit}px; + top: 50%; + transform: translate(0, -50%); + position: absolute; + border-left: 1px solid ${theme.colors.text.label}; + border-right: 1px solid ${theme.colors.text.label}; + } + + &--bottom { + height: ${theme.gridUnit / 2}px; + width: ${theme.gridUnit * 5}px; + bottom: ${theme.gridUnit}px; + left: 50%; + transform: translate(-50%); + position: absolute; + border-top: 1px solid ${theme.colors.text.label}; + border-bottom: 1px solid ${theme.colors.text.label}; + } + } + `} + + &.resizable-container:hover .resize-handle, + &.resizable-container--resizing .resize-handle { + opacity: 1; + } + + .dragdroppable-column & .resizable-container-handle--right { + /* override the default because the inner column's handle's mouse target is very small */ + right: 0 !important; + } + + & .resizable-container-handle--bottom { + bottom: 0 !important; + } +`; + class ResizableContainer extends React.PureComponent { constructor(props) { super(props); @@ -186,7 +275,7 @@ class ResizableContainer extends React.PureComponent { const { isResizing } = this.state; return ( - {children} - + ); } } diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index 497ac40b4acbb..8d19a1ca5f46f 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -21,4 +21,3 @@ @import './dashboard.less'; @import './filter-scope-selector.less'; @import './popover-menu.less'; -@import './resizable.less'; diff --git a/superset-frontend/src/dashboard/stylesheets/resizable.less b/superset-frontend/src/dashboard/stylesheets/resizable.less deleted file mode 100644 index be7182716d044..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/resizable.less +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 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. - */ -.resizable-container { - background-color: transparent; - position: relative; - - /* re-resizable sets an empty div to 100% width and height, which doesn't - play well with many 100% height containers we need - */ - & ~ div { - width: auto !important; - height: auto !important; - } -} - -.resizable-container--resizing { - /* after ensures border visibility on top of any children */ - &:after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - box-shadow: inset 0 0 0 2px @indicator-color; - } - - & > span .resize-handle { - border-color: @indicator-color; - } -} - -.resize-handle { - opacity: 0; - z-index: @z-index-above-dashboard-charts; - - &--bottom-right { - position: absolute; - border: solid; - border-width: 0 1.5px 1.5px 0; - border-right-color: @gray; - border-bottom-color: @gray; - right: 16px; - bottom: 16px; - width: 8px; - height: 8px; - } - - &--right { - width: 2px; - height: 20px; - right: 4px; - top: 50%; - transform: translate(0, -50%); - position: absolute; - border-left: 1px solid @gray; - border-right: 1px solid @gray; - } - - &--bottom { - height: 2px; - width: 20px; - bottom: 4px; - left: 50%; - transform: translate(-50%); - position: absolute; - border-top: 1px solid @gray; - border-bottom: 1px solid @gray; - } -} - -.resizable-container:hover .resize-handle, -.resizable-container--resizing .resize-handle { - opacity: 1; -} - -.dragdroppable-column .resizable-container-handle--right { - /* override the default because the inner column's handle's mouse target is very small */ - right: 0 !important; -} - -.dragdroppable-column .dragdroppable-column .resizable-container-handle--right { - /* override the default because the inner column's handle's mouse target is very small */ - right: 0 !important; -} - -.resizable-container-handle--bottom { - bottom: 0 !important; -} From da91c1073917a31493309def4615e987be984381 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 16:40:41 +0100 Subject: [PATCH 10/22] Move filter-scope-selector.less to Emotion --- .../filterscope/FilterScopeSelector.jsx | 282 +++++++++++++++++- .../stylesheets/filter-scope-selector.less | 259 ---------------- .../src/dashboard/stylesheets/index.less | 1 - 3 files changed, 273 insertions(+), 269 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/filter-scope-selector.less diff --git a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx index 995cac01ca1f5..81635e79eef2c 100644 --- a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx +++ b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.jsx @@ -20,7 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; import Button from 'src/components/Button'; -import { t, styled } from '@superset-ui/core'; +import { css, t, styled } from '@superset-ui/core'; import buildFilterScopeTreeEntry from 'src/dashboard/util/buildFilterScopeTreeEntry'; import getFilterScopeNodesTree from 'src/dashboard/util/getFilterScopeNodesTree'; @@ -49,6 +49,270 @@ const propTypes = { onCloseModal: PropTypes.func.isRequired, }; +const ScopeContainer = styled.div` + ${({ theme }) => css` + display: flex; + flex-direction: column; + height: 80%; + margin-right: ${theme.gridUnit * -6}px; + font-size: ${theme.typography.sizes.m}px; + + & .nav.nav-tabs { + border: none; + } + + & .filter-scope-body { + flex: 1; + max-height: calc(100% - ${theme.gridUnit * 32}px); + + .filter-field-pane, + .filter-scope-pane { + overflow-y: scroll; + } + } + + & .warning-message { + padding: ${theme.gridUnit * 6}px; + } + `} +`; + +const ScopeBody = styled.div` + ${({ theme }) => css` + &.filter-scope-body { + flex: 1; + max-height: calc(100% - ${theme.gridUnit * 32}px); + + .filter-field-pane, + .filter-scope-pane { + overflow-y: scroll; + } + } + `} +`; + +const ScopeHeader = styled.div` + ${({ theme }) => css` + height: ${theme.gridUnit * 16}px; + border-bottom: 1px solid ${theme.colors.grayscale.light2}; + padding-left: ${theme.gridUnit * 6}px; + margin-left: ${theme.gridUnit * -6}px; + + h4 { + margin-top: 0; + } + + .selected-fields { + margin: ${theme.gridUnit * 3}px 0 ${theme.gridUnit * 4}px; + visibility: hidden; + + &.multi-edit-mode { + visibility: visible; + } + + .selected-scopes { + padding-left: ${theme.gridUnit}px; + } + } + `} +`; + +const ScopeSelector = styled.div` + ${({ theme }) => css` + &.filters-scope-selector { + display: flex; + flex-direction: row; + position: relative; + height: 100%; + + a, + a:active, + a:hover { + color: inherit; + text-decoration: none; + } + + .react-checkbox-tree .rct-icon.rct-icon-expand-all, + .react-checkbox-tree .rct-icon.rct-icon-collapse-all { + font-family: ${theme.typography.families.sansSerif}; + font-size: ${theme.typography.sizes.m}px; + color: ${theme.colors.primary.base}; + + &::before { + content: ''; + } + + &:hover { + text-decoration: underline; + } + + &:focus { + outline: none; + } + } + + .filter-field-pane { + position: relative; + width: 40%; + padding: ${theme.gridUnit * 4}px; + padding-left: 0; + border-right: 1px solid ${theme.colors.grayscale.light2}; + + .filter-container { + label { + font-weight: ${theme.typography.weights.normal}; + margin: 0 0 0 ${theme.gridUnit * 4}px; + word-break: break-all; + } + } + + .filter-field-item { + height: ${theme.gridUnit * 9}px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 ${theme.gridUnit * 6}px; + margin-left: ${theme.gridUnit * -6}px; + + &.is-selected { + border: 1px solid ${theme.colors.text.label}; + border-radius: ${theme.borderRadius}px; + background-color: ${theme.colors.grayscale.light4}; + margin-left: ${theme.gridUnit * -6}px; + } + } + + .react-checkbox-tree { + .rct-title .root { + font-weight: ${theme.typography.weights.bold}; + } + + .rct-text { + height: ${theme.gridUnit * 10}px; + } + } + } + + .filter-scope-pane { + position: relative; + flex: 1; + padding: ${theme.gridUnit * 4}px; + padding-right: ${theme.gridUnit * 6}px; + } + + .react-checkbox-tree { + flex-direction: column; + color: ${theme.colors.grayscale.dark1}; + font-size: ${theme.typography.sizes.m}px; + + .filter-scope-type { + padding: ${theme.gridUnit * 2}px 0; + display: flex; + align-items: center; + + &.chart { + font-weight: ${theme.typography.weights.normal}; + } + + &.selected-filter { + padding-left: ${theme.gridUnit * 7}px; + position: relative; + color: ${theme.colors.text.label}; + + &::before { + content: ' '; + position: absolute; + left: 0; + top: 50%; + width: ${theme.gridUnit * 4}px; + height: ${theme.gridUnit * 4}px; + border-radius: ${theme.borderRadius}px; + margin-top: ${theme.gridUnit * -2}px; + box-shadow: inset 0 0 0 2px ${theme.colors.grayscale.light2}; + background: ${theme.colors.grayscale.light3}; + } + } + + &.root { + font-weight: ${theme.typography.weights.bold}; + } + } + + .rct-checkbox { + svg { + position: relative; + top: 3px; + width: ${theme.gridUnit * 4.5}px; + } + } + + .rct-node-leaf { + .rct-bare-label { + &::before { + padding-left: ${theme.gridUnit}px; + } + } + } + + .rct-options { + text-align: left; + margin-left: 0; + margin-bottom: ${theme.gridUnit * 2}px; + } + + .rct-text { + margin: 0; + display: flex; + } + + .rct-title { + display: block; + } + + // disable style from react-checkbox-trees.css + .rct-node-clickable:hover, + .rct-node-clickable:focus, + label:hover, + label:active { + background: none !important; + } + } + + .multi-edit-mode { + &.filter-scope-pane { + .rct-node.rct-node-leaf .filter-scope-type.filter_box { + display: none; + } + } + + .filter-field-item { + padding: 0 ${theme.gridUnit * 4}px 0 ${theme.gridUnit * 12}px; + margin-left: ${theme.gridUnit * -12}px; + + &.is-selected { + margin-left: ${theme.gridUnit * -13}px; + } + } + } + + .scope-search { + position: absolute; + right: ${theme.gridUnit * 4}px; + top: ${theme.gridUnit * 4}px; + border-radius: ${theme.borderRadius}px; + border: 1px solid ${theme.colors.grayscale.light2}; + padding: ${theme.gridUnit}px ${theme.gridUnit * 2}px; + font-size: ${theme.typography.sizes.m}px; + outline: none; + + &:focus { + border: 1px solid ${theme.colors.primary.base}; + } + } + } + `} +`; + const ActionsContainer = styled.div` ${({ theme }) => ` height: ${theme.gridUnit * 16}px; @@ -496,28 +760,28 @@ export default class FilterScopeSelector extends React.PureComponent { const { showSelector } = this.state; return ( -
-
+ +

{t('Configure filter scopes')}

{showSelector && this.renderEditingFiltersName()} -
+ -
+ {!showSelector ? (
{t('There are no filters in this dashboard.')}
) : ( -
+
{this.renderFilterFieldList()}
{this.renderFilterScopeTree()}
-
+ )} -
+ )} -
+ ); } } diff --git a/superset-frontend/src/dashboard/stylesheets/filter-scope-selector.less b/superset-frontend/src/dashboard/stylesheets/filter-scope-selector.less deleted file mode 100644 index e12054e09da7c..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/filter-scope-selector.less +++ /dev/null @@ -1,259 +0,0 @@ -/** - * 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 '../../assets/stylesheets/less/variables.less'; - -.filter-scope-container { - display: flex; - flex-direction: column; - height: 80%; - margin-right: -24px; - font-size: @font-size-m; - - .nav.nav-tabs { - border: none; - } - - .filter-scope-body { - flex: 1; - max-height: calc(100% - 128px); - - .filter-field-pane, - .filter-scope-pane { - overflow-y: scroll; - } - } - - .warning-message { - padding: 24px; - } -} - -.filter-scope-header { - height: 64px; - border-bottom: 1px solid @gray-light; - padding-left: 24px; - margin-left: -24px; - - h4 { - margin-top: 0; - } - - .selected-fields { - margin: 12px 0 16px; - visibility: hidden; - - &.multi-edit-mode { - visibility: visible; - } - - .selected-scopes { - padding-left: 5px; - } - } -} - -.filters-scope-selector { - display: flex; - flex-direction: row; - position: relative; - height: 100%; - - a, - a:active, - a:hover { - color: @almost-black; - text-decoration: none; - } - - .react-checkbox-tree .rct-icon.rct-icon-expand-all, - .react-checkbox-tree .rct-icon.rct-icon-collapse-all { - font-size: @font-size-m; - font-family: @font-family-sans-serif; - color: @brand-primary; - - &::before { - content: ''; - } - - &:hover { - text-decoration: underline; - } - - &:focus { - outline: none; - } - } - - .filter-field-pane { - position: relative; - width: 40%; - padding: 16px 16px 16px 0; - border-right: 1px solid @gray-light; - - .filter-container { - label { - font-weight: @font-weight-normal; - margin: 0 0 0 16px; - word-break: break-all; - } - } - - .filter-field-item { - height: 35px; - display: flex; - align-items: center; - justify-content: center; - padding: 0 24px; - margin-left: -24px; - - &.is-selected { - border: 1px solid @gray-heading; - border-radius: @border-radius-large; - background-color: @gray-bg; - margin-left: -25px; - } - } - - .react-checkbox-tree { - .rct-title .root { - font-weight: @font-weight-bold; - } - - .rct-text { - height: 40px; - } - } - } - - .filter-scope-pane { - position: relative; - flex: 1; - padding: 16px 24px 16px 16px; - } - - .react-checkbox-tree { - flex-direction: column; - color: @almost-black; - font-size: @font-size-m; - - .filter-scope-type { - padding: 8px 0; - display: flex; - align-items: center; - - &.chart { - font-weight: @font-weight-normal; - } - - &.selected-filter { - padding-left: 28px; - position: relative; - color: @gray-heading; - - &::before { - content: ' '; - position: absolute; - left: 0; - top: 50%; - width: 18px; - height: 18px; - border-radius: @border-radius-normal; - margin-top: -9px; - box-shadow: inset 0 0 0 2px @gray-light; - background: #f2f2f2; - } - } - - &.root { - font-weight: @font-weight-bold; - } - } - - .rct-checkbox { - svg { - position: relative; - top: 3px; - width: 18px; - } - } - - .rct-node-leaf { - .rct-bare-label { - &::before { - padding-left: 5px; - } - } - } - - .rct-options { - text-align: left; - margin-left: 0; - margin-bottom: 8px; - } - - .rct-text { - margin: 0; - display: flex; - } - - .rct-title { - display: block; - } - - // disable style from react-checkbox-trees.css - .rct-node-clickable:hover, - .rct-node-clickable:focus, - label:hover, - label:active { - background: none !important; - } - } - - .multi-edit-mode { - &.filter-scope-pane { - .rct-node.rct-node-leaf .filter-scope-type.filter_box { - display: none; - } - } - - .filter-field-item { - padding: 0 16px 0 50px; - margin-left: -50px; - - &.is-selected { - margin-left: -51px; - } - } - } - - .scope-search { - position: absolute; - right: 16px; - top: 16px; - border-radius: @border-radius-large; - border: 1px solid @gray-light; - padding: 4px 8px 4px 8px; - font-size: @font-size-m; - outline: none; - - &:focus { - border: 1px solid @brand-primary; - } - } -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index 8d19a1ca5f46f..eb94c07a182b4 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -19,5 +19,4 @@ @import '../../assets/stylesheets/less/variables.less'; @import './dashboard.less'; -@import './filter-scope-selector.less'; @import './popover-menu.less'; From 1b648f1ab6bf6cfb0d6ea64a3ed64ba1591da8be Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 17:24:07 +0100 Subject: [PATCH 11/22] Fix bug in full screen chart in dashboard --- .../components/gridComponents/ChartHolder.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx index c6019e217b004..fa2350f4ae20f 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx @@ -73,10 +73,12 @@ const FullSizeWrapper = styled.div<{ isFullSize: boolean }>` ${({ isFullSize }) => isFullSize && css` - position: fixed; - z-index: 3000; - left: 0; - top: 0; + && { + position: fixed; + z-index: 3000; + left: 0; + top: 0; + } `}; `; From 5d78e61a478658660c8d1ed3665cb9d808a9e7b8 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 18:24:01 +0100 Subject: [PATCH 12/22] Move dashboard.less to Emotion --- .../src/components/Chart/Chart.jsx | 4 + .../DashboardBuilder/DashboardBuilder.tsx | 125 ++++++++----- .../components/SliceHeader/index.tsx | 73 +++++++- .../src/dashboard/stylesheets/dashboard.less | 164 ------------------ .../src/dashboard/stylesheets/index.less | 1 - .../src/views/components/Menu.tsx | 2 + 6 files changed, 160 insertions(+), 209 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/dashboard.less diff --git a/superset-frontend/src/components/Chart/Chart.jsx b/superset-frontend/src/components/Chart/Chart.jsx index 8be36994505a7..aac44a5186c57 100644 --- a/superset-frontend/src/components/Chart/Chart.jsx +++ b/superset-frontend/src/components/Chart/Chart.jsx @@ -113,6 +113,10 @@ const Styles = styled.div` .pivot_table tbody tr { font-feature-settings: 'tnum' 1; } + + .alert { + margin: ${({ theme }) => theme.gridUnit * 2}px; + } } `; diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index cd663bd5f5c86..0d4b8c367390e 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -82,52 +82,95 @@ import { useNativeFilters } from './state'; type DashboardBuilderProps = {}; const StyledDiv = styled.div` - display: grid; - grid-template-columns: auto 1fr; - grid-template-rows: auto 1fr; - flex: 1; - /* Special cases */ + ${({ 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: -12px; - background: ${({ theme }) => theme.colors.grayscale.light5}; - border: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - } + /* 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; - } + .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: -12px; - background: ${({ theme }) => theme.colors.grayscale.light5}; - border: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - } + /* 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; - } + /* 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: 8px; - top: 8px; - background: transparent; - border: none; - transform: unset; - left: unset; - } - div:hover > .hover-menu-container .hover-menu, - .hover-menu-container .hover-menu:hover { - opacity: 1; - } + /* 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; + } + + h1 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.xxl}px; + letter-spacing: -0.2px; + margin-top: ${theme.gridUnit * 3}px; + margin-bottom: ${theme.gridUnit * 3}px; + } + + h2 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.xl}px; + margin-top: ${theme.gridUnit * 3}px; + margin-bottom: ${theme.gridUnit * 2}px; + } + + h3, + h4, + h5, + h6 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.l}px; + letter-spacing: 0.2px; + margin-top: ${theme.gridUnit * 2}px; + margin-bottom: ${theme.gridUnit}px; + } + + 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 diff --git a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx index eaf045c114ec9..c647075b893cd 100644 --- a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx +++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx @@ -25,7 +25,7 @@ import React, { useRef, useState, } from 'react'; -import { styled, t } from '@superset-ui/core'; +import { css, styled, t } from '@superset-ui/core'; import { useUiConfig } from 'src/components/UiConfigContext'; import { Tooltip } from 'src/components/Tooltip'; import { useDispatch, useSelector } from 'react-redux'; @@ -64,6 +64,69 @@ const CrossFilterIcon = styled(Icons.CursorTarget)` width: 22px; `; +const ChartHeaderStyles = styled.div` + ${({ theme }) => css` + font-size: ${theme.typography.sizes.l}px; + font-weight: ${theme.typography.weights.bold}; + margin-bottom: ${theme.gridUnit}px; + display: flex; + max-width: 100%; + align-items: flex-start; + min-height: 0; + + & > .header-title { + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + flex-grow: 1; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + + & > span.ant-tooltip-open { + display: inline; + } + } + + & > .header-controls { + display: flex; + + & > * { + margin-left: ${theme.gridUnit * 2}px; + } + } + + .dropdown.btn-group { + pointer-events: none; + vertical-align: top; + & > * { + pointer-events: auto; + } + } + + .dropdown-toggle.btn.btn-default { + background: none; + border: none; + box-shadow: none; + } + + .dropdown-menu.dropdown-menu-right { + top: ${theme.gridUnit * 5}px; + } + + .divider { + margin: ${theme.gridUnit}px 0; + } + + .refresh-tooltip { + display: block; + height: ${theme.gridUnit * 4}px; + margin: ${theme.gridUnit}px 0; + color: ${theme.colors.text.label}; + } + `} +`; + const SliceHeader: FC = ({ innerRef = null, forceRefresh = () => ({}), @@ -134,7 +197,11 @@ const SliceHeader: FC = ({ const exploreUrl = `/explore/?dashboard_page_id=${dashboardPageId}&slice_id=${slice.slice_id}`; return ( -
+
= ({ )}
-
+ ); }; diff --git a/superset-frontend/src/dashboard/stylesheets/dashboard.less b/superset-frontend/src/dashboard/stylesheets/dashboard.less deleted file mode 100644 index cdbdeb6481579..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/dashboard.less +++ /dev/null @@ -1,164 +0,0 @@ -/** - * 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. - */ -/* header has mysterious extra margin */ -header.top { - margin-bottom: 2px; - z-index: 10; -} - -body { - h1 { - font-weight: @font-weight-bold; - line-height: @line-height-base; - font-size: @font-size-xxl; - letter-spacing: -0.2px; - margin-top: 12px; - margin-bottom: 12px; - } - - h2 { - font-weight: @font-weight-bold; - line-height: @line-height-base; - font-size: @font-size-xl; - margin-top: 12px; - margin-bottom: 8px; - } - - h3, - h4, - h5, - h6 { - font-weight: @font-weight-bold; - line-height: @line-height-base; - font-size: @font-size-l; - letter-spacing: 0.2px; - margin-top: 8px; - margin-bottom: 4px; - } - - p { - margin: 0 0 8px 0; - } -} - -.dashboard .chart-header { - font-size: @font-size-l; - font-weight: @font-weight-bold; - margin-bottom: 4px; - display: flex; - max-width: 100%; - align-items: flex-start; - min-height: 0; - - & > .header-title { - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - flex-grow: 1; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - - & > span.ant-tooltip-open { - display: inline; - } - } - - & > .header-controls { - display: flex; - - & > * { - margin-left: 8px; - } - } - - .dropdown.btn-group { - pointer-events: none; - vertical-align: top; - & > * { - pointer-events: auto; - } - } - - .dropdown-toggle.btn.btn-default { - background: none; - border: none; - box-shadow: none; - } - - .dropdown-menu.dropdown-menu-right { - top: 20px; - } - - .divider { - margin: 5px 0; - } - - .refresh-tooltip { - display: block; - height: 16px; - margin: 3px 0; - color: @gray; - } -} - -.dashboard .chart-header, -.dashboard .dashboard-header { - .dropdown-menu { - padding: 9px 0; - } - - .dropdown-menu li { - font-weight: @font-weight-normal; - } -} - -.react-bs-container-body { - max-height: 400px; - overflow-y: auto; -} - -.hidden, -#pageDropDown { - display: none; -} - -.separator .chart-container { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} - -.dashboard .title { - margin: 0 20px; -} - -.slice_container .alert { - margin: 10px; -} - -i.danger { - color: @danger; -} - -i.warning { - color: @warning; -} diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less index eb94c07a182b4..a921e44d5d6e0 100644 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ b/superset-frontend/src/dashboard/stylesheets/index.less @@ -18,5 +18,4 @@ */ @import '../../assets/stylesheets/less/variables.less'; -@import './dashboard.less'; @import './popover-menu.less'; diff --git a/superset-frontend/src/views/components/Menu.tsx b/superset-frontend/src/views/components/Menu.tsx index 31d8af656dfa9..9b50710999916 100644 --- a/superset-frontend/src/views/components/Menu.tsx +++ b/superset-frontend/src/views/components/Menu.tsx @@ -45,6 +45,8 @@ const StyledHeader = styled.header` ${({ theme }) => ` background-color: ${theme.colors.grayscale.light5}; margin-bottom: 2px; + z-index: 10; + &:nth-last-of-type(2) nav { margin-bottom: 2px; } From 453980cee27504157a2242041793f75e5077abe3 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 18:40:01 +0100 Subject: [PATCH 13/22] Fix `fade` not working in Emotion --- .../DashboardBuilder/DashboardBuilder.tsx | 386 +++++++++--------- 1 file changed, 193 insertions(+), 193 deletions(-) diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 0d4b8c367390e..442a42047d3dc 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -26,7 +26,7 @@ import React, { useRef, useState, } from 'react'; -import { css, JsonObject, styled, t } from '@superset-ui/core'; +import { addAlpha, css, JsonObject, styled, t } from '@superset-ui/core'; import { Global } from '@emotion/react'; import { useDispatch, useSelector } from 'react-redux'; import ErrorBoundary from 'src/components/ErrorBoundary'; @@ -208,156 +208,156 @@ const StyledContent = styled.div<{ `; const DashboardContentWrapper = styled.div` - &.dashboard { - position: relative; - flex-grow: 1; - display: flex; - flex-direction: column; - height: 100%; + ${({ theme }) => css` + &.dashboard { + position: relative; + flex-grow: 1; + display: flex; + flex-direction: column; + height: 100%; - /* only top-level tabs have popover, give it more padding to match header + tabs */ - & > .with-popover-menu > .popover-menu { - left: ${({ theme }) => theme.gridUnit * 6}px; - } + /* only top-level tabs have popover, give it more padding to match header + tabs */ + & > .with-popover-menu > .popover-menu { + left: ${theme.gridUnit * 6}px; + } - /* drop shadow for top-level tabs only */ - & .dashboard-component-tabs { - box-shadow: 0 ${({ theme }) => theme.gridUnit}px - ${({ theme }) => theme.gridUnit}px 0 - fade( - ${({ theme }) => theme.colors.grayscale.dark2}, - ${({ theme }) => theme.opacity.light} - ); - padding-left: ${({ theme }) => - theme.gridUnit * + /* drop shadow for top-level tabs only */ + & .dashboard-component-tabs { + box-shadow: 0 ${theme.gridUnit}px ${theme.gridUnit}px 0 + ${addAlpha( + theme.colors.grayscale.dark2, + parseFloat(theme.opacity.light) / 100, + )}; + padding-left: ${theme.gridUnit * 2}px; /* note this is added to tab-level padding, to match header */ - } + } - .dropdown-toggle.btn.btn-primary .caret { - color: ${({ theme }) => theme.colors.grayscale.light5}; - } + .dropdown-toggle.btn.btn-primary .caret { + color: ${theme.colors.grayscale.light5}; + } - .background--transparent { - background-color: transparent; - } + .background--transparent { + background-color: transparent; + } - .background--white { - background-color: ${({ theme }) => theme.colors.grayscale.light5}; - } - } - &.dashboard--editing { - .grid-row:after, - .dashboard-component-tabs > .hover-menu:hover + div:after { - border: 1px dashed transparent; - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 1; - pointer-events: none; + .background--white { + background-color: ${theme.colors.grayscale.light5}; + } } + &.dashboard--editing { + .grid-row:after, + .dashboard-component-tabs > .hover-menu:hover + div:after { + border: 1px dashed transparent; + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + } - .resizable-container { - & .dashboard-component-chart-holder { - .dashboard-chart { - .chart-container { - cursor: move; - opacity: 0.2; + .resizable-container { + & .dashboard-component-chart-holder { + .dashboard-chart { + .chart-container { + cursor: move; + opacity: 0.2; + } + + .slice_container { + /* disable chart interactions in edit mode */ + pointer-events: none; + } } - .slice_container { - /* disable chart interactions in edit mode */ - pointer-events: none; + &:hover .dashboard-chart .chart-container { + opacity: 0.7; } } - &:hover .dashboard-chart .chart-container { - opacity: 0.7; + &:hover, + &.resizable-container--resizing:hover { + & > .dashboard-component-chart-holder:after { + border: 1px dashed ${theme.colors.primary.base}; + } } } - &:hover, - &.resizable-container--resizing:hover { - & > .dashboard-component-chart-holder:after { - border: 1px dashed ${({ theme }) => theme.colors.primary.base}; + .resizable-container--resizing:hover > .grid-row:after, + .hover-menu:hover + .grid-row:after, + .dashboard-component-tabs > .hover-menu:hover + div:after { + border: 1px dashed ${theme.colors.primary.base}; + z-index: 2; + } + + .grid-row:after, + .dashboard-component-tabs > .hover-menu + div:after { + border: 1px dashed ${theme.colors.grayscale.light2}; + } + + /* provide hit area in case row contents is edge to edge */ + .dashboard-component-tabs-content { + .dragdroppable-row { + padding-top: ${theme.gridUnit * 4}px; + } + + & > div:not(:last-child):not(.empty-droptarget) { + margin-bottom: ${theme.gridUnit * 4}px; } } - } - .resizable-container.resizable-container--resizing:hover > .grid-row:after, - .hover-menu:hover + .grid-row:after, - .dashboard-component-tabs > .hover-menu:hover + div:after { - border: 1px dashed ${({ theme }) => theme.colors.primary.base}; - z-index: 2; - } + .dashboard-component-chart-holder { + &:after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + border: 1px solid transparent; + } - .grid-row:after, - .dashboard-component-tabs > .hover-menu + div:after { - border: 1px dashed ${({ theme }) => theme.colors.grayscale.light2}; - } + &:hover:after { + border: 1px dashed ${theme.colors.primary.base}; + z-index: 2; + } + } - /* provide hit area in case row contents is edge to edge */ - .dashboard-component-tabs-content { - .dragdroppable-row { - padding-top: ${({ theme }) => theme.gridUnit * 4}px; + .contract-trigger:before { + display: none; } + } + & .dashboard-component-tabs-content { & > div:not(:last-child):not(.empty-droptarget) { - margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + margin-bottom: ${theme.gridUnit * 4}px; } - } - .dashboard-component-chart-holder { - &:after { - content: ''; + & > .empty-droptarget { position: absolute; width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 1; - pointer-events: none; - border: 1px solid transparent; } - &:hover:after { - border: 1px dashed ${({ theme }) => theme.colors.primary.base}; - z-index: 2; + & > .empty-droptarget:first-child { + height: ${theme.gridUnit * 4}px; + top: -2px; + z-index: 10; } - } - .contract-trigger:before { - display: none; - } - } - - & .dashboard-component-tabs-content { - & > div:not(:last-child):not(.empty-droptarget) { - margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; - } - - & > .empty-droptarget { - position: absolute; - width: 100%; - } - - & > .empty-droptarget:first-child { - height: ${({ theme }) => theme.gridUnit * 4}px; - top: -2px; - z-index: 10; + & > .empty-droptarget:last-child { + height: ${theme.gridUnit * 3}px; + bottom: 0; + } } - & > .empty-droptarget:last-child { - height: ${({ theme }) => theme.gridUnit * 3}px; - bottom: 0; + .empty-droptarget:first-child .drop-indicator--bottom { + top: ${theme.gridUnit * 6}px; } - } - - .empty-droptarget:first-child .drop-indicator--bottom { - top: ${({ theme }) => theme.gridUnit * 6}px; - } + `} `; const StyledDashboardContent = styled.div<{ @@ -366,100 +366,100 @@ const StyledDashboardContent = styled.div<{ nativeFiltersEnabled: boolean; filterBarOrientation: FilterBarOrientation; }>` - display: flex; - flex-direction: row; - flex-wrap: nowrap; - height: auto; - flex: 1; - - .grid-container .dashboard-component-tabs { - box-shadow: none; - padding-left: 0; - } - - .grid-container { - /* without this, the grid will not get smaller upon toggling the builder panel on */ - width: 0; + ${({ + theme, + dashboardFiltersOpen, + editMode, + nativeFiltersEnabled, + filterBarOrientation, + }) => css` + display: flex; + flex-direction: row; + flex-wrap: nowrap; + height: auto; flex: 1; - position: relative; - margin-top: ${({ theme }) => theme.gridUnit * 6}px; - margin-right: ${({ theme }) => theme.gridUnit * 8}px; - margin-bottom: ${({ theme }) => theme.gridUnit * 6}px; - margin-left: ${({ - theme, - dashboardFiltersOpen, - editMode, - nativeFiltersEnabled, - filterBarOrientation, - }) => { - if ( - !dashboardFiltersOpen && - !editMode && - nativeFiltersEnabled && - filterBarOrientation !== FilterBarOrientation.HORIZONTAL - ) { - return 0; - } - return theme.gridUnit * 8; - }}px; - ${({ editMode, theme }) => - editMode && + .grid-container .dashboard-component-tabs { + box-shadow: none; + padding-left: 0; + } + + .grid-container { + /* without this, the grid will not get smaller upon toggling the builder panel on */ + width: 0; + flex: 1; + position: relative; + margin-top: ${theme.gridUnit * 6}px; + margin-right: ${theme.gridUnit * 8}px; + margin-bottom: ${theme.gridUnit * 6}px; + margin-left: ${!dashboardFiltersOpen && + !editMode && + nativeFiltersEnabled && + filterBarOrientation !== FilterBarOrientation.HORIZONTAL + ? 0 + : theme.gridUnit * 8}px; + + ${editMode && ` max-width: calc(100% - ${ BUILDER_SIDEPANEL_WIDTH + theme.gridUnit * 16 }px); `} - /* this is the ParentSize wrapper */ + /* this is the ParentSize wrapper */ & > div:first-child { - height: inherit !important; - } - } - - .dashboard-builder-sidepane { - width: ${BUILDER_SIDEPANEL_WIDTH}px; - z-index: 1; - } - - .dashboard-component-chart-holder { - width: 100%; - height: 100%; - background-color: ${({ theme }) => theme.colors.grayscale.light5}; - position: relative; - padding: ${({ theme }) => theme.gridUnit * 4}px; - overflow-y: visible; - - // transitionable traits to show filter relevance - transition: opacity ${({ theme }) => theme.transitionTiming}s ease-in-out, - border-color ${({ theme }) => theme.transitionTiming}s ease-in-out, - box-shadow ${({ theme }) => theme.transitionTiming}s ease-in-out; - - &.fade-in { - border-radius: 4px; - box-shadow: inset 0 0 0 2px ${({ theme }) => theme.colors.primary.base}, - 0 0 0 3px fade(${({ theme }) => theme.colors.primary.base}, 10%); + height: inherit !important; + } } - &.fade-out { - border-radius: 4px; - box-shadow: none; + .dashboard-builder-sidepane { + width: ${BUILDER_SIDEPANEL_WIDTH}px; + z-index: 1; } - & .missing-chart-container { - display: flex; - flex-direction: column; - align-items: center; - overflow-y: auto; - justify-content: center; + .dashboard-component-chart-holder { + width: 100%; + height: 100%; + background-color: ${theme.colors.grayscale.light5}; + position: relative; + padding: ${theme.gridUnit * 4}px; + overflow-y: visible; + + // transitionable traits to show filter relevance + transition: opacity ${theme.transitionTiming}s ease-in-out, + border-color ${theme.transitionTiming}s ease-in-out, + box-shadow ${theme.transitionTiming}s ease-in-out; + + &.fade-in { + border-radius: ${theme.borderRadius}px; + box-shadow: inset 0 0 0 2px ${theme.colors.primary.base}, + 0 0 0 3px + ${addAlpha( + theme.colors.primary.base, + parseFloat(theme.opacity.light) / 100, + )}; + } + + &.fade-out { + border-radius: ${theme.borderRadius}px; + box-shadow: none; + } - .missing-chart-body { - font-size: ${({ theme }) => theme.typography.sizes.s}px; - position: relative; + & .missing-chart-container { display: flex; + flex-direction: column; + align-items: center; + overflow-y: auto; + justify-content: center; + + .missing-chart-body { + font-size: ${theme.typography.sizes.s}px; + position: relative; + display: flex; + } } } - } + `} `; const DashboardBuilder: FC = () => { From 775e563750c65fea44e36df270aad116ee79f70e Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 19:47:41 +0100 Subject: [PATCH 14/22] Move popover-menu.less to Emotion --- .../src/dashboard/components/Dashboard.jsx | 1 - .../menu/BackgroundStyleDropdown.tsx | 53 ++++++- .../components/menu/WithPopoverMenu.tsx | 76 +++++++++- .../FilterControls/FilterDivider.stories.tsx | 1 - .../src/dashboard/stylesheets/index.less | 21 --- .../dashboard/stylesheets/popover-menu.less | 140 ------------------ 6 files changed, 121 insertions(+), 171 deletions(-) delete mode 100644 superset-frontend/src/dashboard/stylesheets/index.less delete mode 100644 superset-frontend/src/dashboard/stylesheets/popover-menu.less diff --git a/superset-frontend/src/dashboard/components/Dashboard.jsx b/superset-frontend/src/dashboard/components/Dashboard.jsx index 5da8f501f8eb5..be70fa5579745 100644 --- a/superset-frontend/src/dashboard/components/Dashboard.jsx +++ b/superset-frontend/src/dashboard/components/Dashboard.jsx @@ -38,7 +38,6 @@ import { } from '../../logger/LogUtils'; import { areObjectsEqual } from '../../reduxUtils'; -import '../stylesheets/index.less'; import getLocationHash from '../util/getLocationHash'; import isDashboardEmpty from '../util/isDashboardEmpty'; import { getAffectedOwnDataCharts } from '../util/charts/getOwnDataCharts'; diff --git a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx index 82c8994102533..cf9bdc1a58ab2 100644 --- a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx +++ b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx @@ -18,6 +18,7 @@ */ import React from 'react'; import cx from 'classnames'; +import { css, styled } from '@superset-ui/core'; import backgroundStyleOptions from 'src/dashboard/util/backgroundStyleOptions'; import PopoverDropdown, { @@ -31,19 +32,63 @@ interface BackgroundStyleDropdownProps { onChange: OnChangeHandler; } +const BackgroundStyleOption = styled.div` + ${({ theme }) => css` + display: inline-block; + + &:before { + content: ''; + width: 1em; + height: 1em; + margin-right: ${theme.gridUnit * 2}px; + display: inline-block; + vertical-align: middle; + } + + &.background--white { + padding-left: 0; + background: transparent; + + &:before { + background: ${theme.colors.grayscale.light5}; + border: 1px solid ${theme.colors.grayscale.light2}; + } + } + + /* Create the transparent rect icon */ + &.background--transparent:before { + background-image: linear-gradient( + 45deg, + ${theme.colors.text.label} 25%, + transparent 25% + ), + linear-gradient(-45deg, ${theme.colors.text.label} 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, ${theme.colors.text.label} 75%), + linear-gradient(-45deg, transparent 75%, ${theme.colors.text.label} 75%); + background-size: ${theme.gridUnit * 2}px ${theme.gridUnit * 2}px; + background-position: 0 0, 0 ${theme.gridUnit}px, + ${theme.gridUnit}px ${-theme.gridUnit}px, ${-theme.gridUnit}px 0px; + } + `} +`; + function renderButton(option: OptionProps) { return ( -
+ {`${option.label} background`} -
+ ); } function renderOption(option: OptionProps) { return ( -
+ {option.label} -
+ ); } diff --git a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx index df44b4e1c1f24..ef075af908372 100644 --- a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx +++ b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx @@ -18,6 +18,7 @@ */ import React from 'react'; import cx from 'classnames'; +import { addAlpha, css, styled } from '@superset-ui/core'; type ShouldFocusContainer = HTMLDivElement & { contains: (event_target: EventTarget & HTMLElement) => Boolean; @@ -41,6 +42,73 @@ interface WithPopoverMenuState { isFocused: Boolean; } +const WithPopoverMenuStyles = styled.div` + ${({ theme }) => css` + position: relative; + outline: none; + + .grid-row.grid-row--empty & { + /* drop indicator doesn't show up without this */ + width: 100%; + height: 100%; + } + + &.with-popover-menu--focused:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 2px solid ${theme.colors.primary.base}; + pointer-events: none; + } + + .dashboard-component-tabs li &.with-popover-menu--focused:after { + top: ${theme.gridUnit * -3}px; + left: ${theme.gridUnit * -2}px; + width: calc(100% + ${theme.gridUnit * 4}px); + height: calc(100% + ${theme.gridUnit * 7}px); + } + `} +`; + +const PopoverMenuStyles = styled.div` + ${({ theme }) => css` + position: absolute; + flex-wrap: nowrap; + left: 1px; + top: -42px; + height: ${theme.gridUnit * 10}px; + padding: 0 ${theme.gridUnit * 4}px; + background: ${theme.colors.grayscale.light5}; + box-shadow: 0 1px 2px 1px + ${addAlpha( + theme.colors.grayscale.dark2, + parseFloat(theme.opacity.mediumLight) / 100, + )}; + font-size: ${theme.typography.sizes.m}px; + cursor: default; + z-index: 3000; + + &, + .menu-item { + display: flex; + flex-direction: row; + align-items: center; + } + + /* vertical spacer after each menu item */ + .menu-item:not(:last-child):after { + content: ''; + width: 1px; + height: 100%; + background: ${theme.colors.grayscale.light2}; + margin: 0 ${theme.gridUnit * 4}px; + } + `} +`; + export default class WithPopoverMenu extends React.PureComponent< WithPopoverMenuProps, WithPopoverMenuState @@ -126,7 +194,7 @@ export default class WithPopoverMenu extends React.PureComponent< const { isFocused } = this.state; return ( -
{children} {editMode && isFocused && (menuItems?.length ?? 0) > 0 && ( -
+ {menuItems.map((node: React.ReactNode, i: Number) => (
{node}
))} -
+ )} -
+ ); } } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterDivider.stories.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterDivider.stories.tsx index 212e9033588f3..46e7c03fadb5d 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterDivider.stories.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterDivider.stories.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { css } from '@emotion/react'; import { FilterBarOrientation } from 'src/dashboard/types'; import FilterDivider from './FilterDivider'; -import 'src/dashboard/stylesheets/index.less'; import { FilterDividerProps } from './types'; export default { diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/dashboard/stylesheets/index.less deleted file mode 100644 index a921e44d5d6e0..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/index.less +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 '../../assets/stylesheets/less/variables.less'; - -@import './popover-menu.less'; diff --git a/superset-frontend/src/dashboard/stylesheets/popover-menu.less b/superset-frontend/src/dashboard/stylesheets/popover-menu.less deleted file mode 100644 index 4abc494993609..0000000000000 --- a/superset-frontend/src/dashboard/stylesheets/popover-menu.less +++ /dev/null @@ -1,140 +0,0 @@ -/** - * 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. - */ -.with-popover-menu { - position: relative; - outline: none; -} - -.grid-row.grid-row--empty .with-popover-menu { - /* drop indicator doesn't show up without this */ - width: 100%; - height: 100%; -} - -.with-popover-menu--focused:after { - content: ''; - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 100%; - border: 2px solid @indicator-color; - pointer-events: none; -} - -.popover-menu { - position: absolute; - flex-wrap: nowrap; - left: 1px; - top: -42px; - height: 40px; - padding: 0 16px; - background: @lightest; - box-shadow: 0 1px 2px 1px fade(@darkest, @opacity-medium-light); - font-size: @font-size-m; - cursor: default; - z-index: @z-index-max; - - &, - .menu-item { - display: flex; - flex-direction: row; - align-items: center; - } - - /* vertical spacer after each menu item */ - .menu-item:not(:only-child):not(:last-child):after { - content: ''; - width: 1; - height: 100%; - background: @gray-light; - margin: 0 16px; - } -} - -/* the focus menu doesn't account for parent padding */ -.dashboard-component-tabs li .with-popover-menu--focused:after { - top: -12px; - left: -8px; - width: ~'calc(100% + 16px)'; /* escape for .less */ - height: ~'calc(100% + 28px)'; -} - -.dashboard-component-tabs li .popover-menu { - top: -56px; - left: -7px; -} - -.hover-dropdown .btn { - &:hover, - &:active, - &:focus { - background: initial; - box-shadow: none; - } -} - -.hover-dropdown, -.popover-menu { - li.dropdown-item { - &:hover a { - background: @menu-hover; - } - - &.active a { - background: @gray-light; - font-weight: @font-weight-bold; - color: @almost-black; - } - } -} - -/* background style menu */ -.background-style-option { - display: inline-block; - - &:before { - content: ''; - width: 1em; - height: 1em; - margin-right: 8px; - display: inline-block; - vertical-align: middle; - } - - &.background--white { - padding-left: 0; - background: transparent; - - &:before { - background: @lightest; - border: 1px solid @gray-light; - } - } - - /* Create the transparent rect icon */ - &.background--transparent:before { - background-image: linear-gradient(45deg, @gray 25%, transparent 25%), - linear-gradient(-45deg, @gray 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, @gray 75%), - linear-gradient(-45deg, transparent 75%, @gray 75%); - background-size: 8px 8px; - background-position: 0 0, 0 4px, 4px -4px, -4px 0px; - } -} From 71ba94f5783277bb18ec165202ab625dec14b9c4 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 21 Dec 2022 20:12:02 +0100 Subject: [PATCH 15/22] Remove explore/main.less file --- superset-frontend/src/explore/main.less | 129 ------------------------ 1 file changed, 129 deletions(-) delete mode 100644 superset-frontend/src/explore/main.less diff --git a/superset-frontend/src/explore/main.less b/superset-frontend/src/explore/main.less deleted file mode 100644 index df248f461216a..0000000000000 --- a/superset-frontend/src/explore/main.less +++ /dev/null @@ -1,129 +0,0 @@ -/** - * 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 '../assets/stylesheets/less/variables.less'; - -.scrollbar-container { - position: relative; - overflow: hidden; - width: 100%; - height: 100%; -} - -.scrollbar-content { - position: absolute; - top: 0px; - left: 0px; - right: 0px; - bottom: 0px; - overflow-y: auto; - margin-right: 0px; - margin-bottom: 40px; -} - -.edit-desc-icon { - padding: 0 0 0 0.5em; - font-size: @font-size-m; -} - -.checkbox { - float: left; - margin-top: 0px; - margin-right: 3px; -} - -.background-transparent { - background-color: transparent !important; -} - -.fa.expander { - width: 15px; -} - -.list-group { - margin-bottom: 10px; -} - -.color-popover.popover { - border: none; - background-color: transparent; -} - -.color-popover .popover-content { - padding: 0; - background-color: transparent; -} - -.column-option { - margin-left: 3px; -} - -.datasource-container { - overflow: auto; -} - -.adhoc-metric-edit-tabs > .nav-tabs { - margin-bottom: 6px; - - & > li > a { - padding: 4px 4px 4px 4px; - } -} - -#metrics-edit-popover { - max-width: none; - - .inline-editable { - line-height: 30px; // hand-tweaked to match the height of the input - } -} - -.adhoc-option { - cursor: pointer; -} - -.label-dropdown ul.dropdown-menu { - position: fixed; - top: auto; - left: auto; - margin: 20px 0 0; -} - -.label-btn:hover, -.label-btn-label:hover { - background-color: @gray-dark; -} - -.label-btn-label { - cursor: pointer; -} - -.adhoc-label-arrow { - font-size: @font-size-s; - margin-left: 3px; - position: static; -} - -div.section-header { - font-size: @font-size-s; - font-weight: @font-weight-bold; - color: @gray-light5; - margin-bottom: 0; - margin-top: 0; - padding-bottom: 16px; -} From 0fba068380eda1de51c269baa1446c4c42a3e0c3 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Thu, 22 Dec 2022 16:54:55 +0100 Subject: [PATCH 16/22] Move SqlLab/main.less to Emotion --- superset-frontend/src/GlobalStyles.tsx | 12 + superset-frontend/src/SqlLab/App.jsx | 1 - .../components/AceEditorWrapper/index.tsx | 26 +- .../src/SqlLab/components/App/index.jsx | 69 ++- .../QuerySearch/QuerySearch.test.jsx | 139 ----- .../SqlLab/components/QuerySearch/index.tsx | 289 ----------- .../components/QueryStateLabel/index.tsx | 10 +- .../src/SqlLab/components/ResultSet/index.tsx | 5 +- .../src/SqlLab/components/SqlEditor/index.jsx | 62 ++- .../components/SqlEditorLeftBar/index.tsx | 19 +- .../components/SqlEditorTabHeader/index.tsx | 26 +- .../SqlLab/components/TabStatusIcon/index.tsx | 39 +- .../components/TabbedSqlEditors/index.jsx | 10 +- .../SqlLab/components/TableElement/index.tsx | 41 +- superset-frontend/src/SqlLab/main.less | 491 ------------------ .../src/assets/stylesheets/superset.less | 4 + 16 files changed, 286 insertions(+), 957 deletions(-) delete mode 100644 superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx delete mode 100644 superset-frontend/src/SqlLab/components/QuerySearch/index.tsx delete mode 100644 superset-frontend/src/SqlLab/main.less diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx index 1e2cf449ff637..cf64648396859 100644 --- a/superset-frontend/src/GlobalStyles.tsx +++ b/superset-frontend/src/GlobalStyles.tsx @@ -23,6 +23,14 @@ import { Global } from '@emotion/react'; export const GlobalStyles = () => ( css` + body { + min-height: max( + 100vh, + ${theme.gridUnit * 125}px + ); // Set a min height so the gutter is always visible when resizing + overflow: hidden; + } + h1, h2, h3, @@ -33,6 +41,10 @@ export const GlobalStyles = () => ( th { font-weight: ${theme.typography.weights.bold}; } + + .cost-estimate { + font-size: ${theme.typography.sizes.s}; + } `} /> ); diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx index 812202eec20f8..918e6c1d38c84 100644 --- a/superset-frontend/src/SqlLab/App.jsx +++ b/superset-frontend/src/SqlLab/App.jsx @@ -41,7 +41,6 @@ import { import { BYTES_PER_CHAR, KB_STORAGE } from './constants'; import setupApp from '../setup/setupApp'; -import './main.less'; import '../assets/stylesheets/reactable-pagination.less'; import { theme } from '../preamble'; diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx index e241a121dbee8..faf4a16ddabf5 100644 --- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx +++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx @@ -18,6 +18,8 @@ */ import React, { useState, useEffect, useRef } from 'react'; import { useDispatch } from 'react-redux'; +import { css, styled } from '@superset-ui/core'; + import { usePrevious } from 'src/hooks/usePrevious'; import { areArraysShallowEqual } from 'src/reduxUtils'; import sqlKeywords from 'src/SqlLab/utils/sqlKeywords'; @@ -57,6 +59,28 @@ type AceEditorWrapperProps = { hotkeys: HotKey[]; }; +const StyledAceEditor = styled(AceEditor)` + ${({ theme }) => css` + && { + //double class is better than !important + border: 1px solid ${theme.colors.grayscale.light2}; + font-feature-settings: 'liga' off, 'calt' off; + // Fira Code causes problem with Ace under Firefox + font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono', + 'source-code-pro', 'Lucida Console', monospace; + + &.ace_autocomplete { + // Use !important because Ace Editor applies extra CSS at the last second + // when opening the autocomplete. + width: ${theme.gridUnit * 130}px !important; + } + + .ace_scroller { + background-color: ${theme.colors.grayscale.light4}; + } + } + `} +`; const AceEditorWrapper = ({ autocomplete, onBlur = () => {}, @@ -258,7 +282,7 @@ const AceEditorWrapper = ({ }; return ( - css` + &.SqlLab { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 0 ${theme.gridUnit * 2}px; + + pre { + padding: 0 !important; + margin: 0; + border: none; + font-size: ${theme.typography.sizes.s}px; + background: transparent !important; + } + + .north-pane { + display: flex; + flex-direction: column; + } + + .ace_editor { + flex-grow: 1; + } + + .ace_content { + height: 100%; + } + + .ant-tabs-content-holder { + /* This is needed for Safari */ + height: 100%; + } + + .ant-tabs-content { + height: 100%; + position: relative; + background-color: ${theme.colors.grayscale.light5}; + overflow-x: auto; + overflow-y: auto; + + > .ant-tabs-tabpane { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + } + + .ResultsModal .ant-modal-body { + min-height: ${theme.gridUnit * 140}px; + } + + .ant-modal-body { + overflow: auto; + } + } + `}; +`; + class App extends React.PureComponent { constructor(props) { super(props); @@ -99,7 +162,7 @@ class App extends React.PureComponent { return window.location.replace('/superset/sqllab/history/'); } return ( -
+ -
+ ); } } diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx b/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx deleted file mode 100644 index 2a891d34af84c..0000000000000 --- a/superset-frontend/src/SqlLab/components/QuerySearch/QuerySearch.test.jsx +++ /dev/null @@ -1,139 +0,0 @@ -/** - * 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 thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import QuerySearch from 'src/SqlLab/components/QuerySearch'; -import { Provider } from 'react-redux'; -import { supersetTheme, ThemeProvider } from '@superset-ui/core'; -import { fireEvent, render, screen, act } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import userEvent from '@testing-library/user-event'; -import { user } from 'src/SqlLab/fixtures'; - -const mockStore = configureStore([thunk]); -const store = mockStore({ - sqlLab: user, -}); - -const SEARCH_ENDPOINT = 'glob:*/superset/search_queries?*'; -const USER_ENDPOINT = 'glob:*/api/v1/query/related/user'; -const DATABASE_ENDPOINT = 'glob:*/api/v1/database/?*'; - -fetchMock.get(SEARCH_ENDPOINT, []); -fetchMock.get(USER_ENDPOINT, []); -fetchMock.get(DATABASE_ENDPOINT, []); - -describe('QuerySearch', () => { - const mockedProps = { - displayLimit: 50, - }; - - it('is valid', () => { - expect( - React.isValidElement( - - - - - , - ), - ).toBe(true); - }); - - beforeEach(async () => { - // You need this await function in order to change state in the app. In fact you need it everytime you re-render. - await act(async () => { - render( - - - - - , - ); - }); - }); - - it('should have three Selects', () => { - expect(screen.getByText(/28 days ago/i)).toBeInTheDocument(); - expect(screen.getByText(/now/i)).toBeInTheDocument(); - expect(screen.getByText(/success/i)).toBeInTheDocument(); - }); - - it('updates fromTime on user selects from time', () => { - const role = screen.getByText(/28 days ago/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/1 hour ago/i)); - expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument(); - }); - - it('updates toTime on user selects on time', () => { - const role = screen.getByText(/now/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/1 hour ago/i)); - expect(screen.getByText(/1 hour ago/i)).toBeInTheDocument(); - }); - - it('updates status on user selects status', () => { - const role = screen.getByText(/success/i); - fireEvent.keyDown(role, { key: 'ArrowDown', keyCode: 40 }); - userEvent.click(screen.getByText(/failed/i)); - expect(screen.getByText(/failed/i)).toBeInTheDocument(); - }); - - it('should have one input for searchText', () => { - expect( - screen.getByPlaceholderText(/Query search string/i), - ).toBeInTheDocument(); - }); - - it('updates search text on user inputs search text', () => { - const search = screen.getByPlaceholderText(/Query search string/i); - userEvent.type(search, 'text'); - expect(search.value).toBe('text'); - }); - - it('should have one Button', () => { - const button = screen.getAllByRole('button'); - expect(button.length).toEqual(1); - }); - - it('should call API when search button is pressed', async () => { - fetchMock.resetHistory(); - const button = screen.getByRole('button'); - await act(async () => { - userEvent.click(button); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1); - }); - - it('should call API when (only)enter key is pressed', async () => { - fetchMock.resetHistory(); - const search = screen.getByPlaceholderText(/Query search string/i); - await act(async () => { - userEvent.type(search, 'a'); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(0); - await act(async () => { - userEvent.type(search, '{enter}'); - }); - expect(fetchMock.calls(SEARCH_ENDPOINT)).toHaveLength(1); - }); -}); diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx deleted file mode 100644 index 3018ff1924586..0000000000000 --- a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx +++ /dev/null @@ -1,289 +0,0 @@ -/** - * 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, { useState, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; - -import { setDatabases, addDangerToast } from 'src/SqlLab/actions/sqlLab'; -import Button from 'src/components/Button'; -import Select from 'src/components/DeprecatedSelect'; -import { styled, t, SupersetClient, QueryResponse } from '@superset-ui/core'; -import { debounce } from 'lodash'; -import Loading from 'src/components/Loading'; -import { - now, - epochTimeXHoursAgo, - epochTimeXDaysAgo, - epochTimeXYearsAgo, -} from 'src/utils/dates'; -import AsyncSelect from 'src/components/AsyncSelect'; -import { STATUS_OPTIONS, TIME_OPTIONS } from 'src/SqlLab/constants'; -import QueryTable from '../QueryTable'; - -interface QuerySearchProps { - displayLimit: number; -} - -interface UserMutatorProps { - value: number; - text: string; -} - -interface DbMutatorProps { - id: number; - database_name: string; -} - -const TableWrapper = styled.div` - display: flex; - flex-direction: column; - flex: 1; - height: 100%; -`; - -const TableStyles = styled.div` - table { - background-color: ${({ theme }) => theme.colors.grayscale.light4}; - } - - .table > thead > tr > th { - border-bottom: ${({ theme }) => theme.gridUnit / 2}px solid - ${({ theme }) => theme.colors.grayscale.light2}; - background: ${({ theme }) => theme.colors.grayscale.light4}; - } -`; - -const StyledTableStylesContainer = styled.div` - overflow: auto; -`; - -const QuerySearch = ({ displayLimit }: QuerySearchProps) => { - const dispatch = useDispatch(); - - const [databaseId, setDatabaseId] = useState(''); - const [userId, setUserId] = useState(''); - const [searchText, setSearchText] = useState(''); - const [from, setFrom] = useState('28 days ago'); - const [to, setTo] = useState('now'); - const [status, setStatus] = useState('success'); - const [queriesArray, setQueriesArray] = useState([]); - const [queriesLoading, setQueriesLoading] = useState(true); - - const getTimeFromSelection = (selection: string) => { - switch (selection) { - case 'now': - return now(); - case '1 hour ago': - return epochTimeXHoursAgo(1); - case '1 day ago': - return epochTimeXDaysAgo(1); - case '7 days ago': - return epochTimeXDaysAgo(7); - case '28 days ago': - return epochTimeXDaysAgo(28); - case '90 days ago': - return epochTimeXDaysAgo(90); - case '1 year ago': - return epochTimeXYearsAgo(1); - default: - return null; - } - }; - - const insertParams = (baseUrl: string, params: string[]) => { - const validParams = params.filter(function (p) { - return p !== ''; - }); - return `${baseUrl}?${validParams.join('&')}`; - }; - - const refreshQueries = async () => { - setQueriesLoading(true); - const params = [ - userId && `user_id=${userId}`, - databaseId && `database_id=${databaseId}`, - searchText && `search_text=${searchText}`, - status && `status=${status}`, - from && `from=${getTimeFromSelection(from)}`, - to && `to=${getTimeFromSelection(to)}`, - ]; - - try { - const response = await SupersetClient.get({ - endpoint: insertParams('/superset/search_queries', params), - }); - const queries = Object.values(response.json); - setQueriesArray(queries); - } catch (err) { - dispatch(addDangerToast(t('An error occurred when refreshing queries'))); - } finally { - setQueriesLoading(false); - } - }; - useEffect(() => { - refreshQueries(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onUserClicked = (userId: string) => { - setUserId(userId); - refreshQueries(); - }; - - const onDbClicked = (dbId: string) => { - setDatabaseId(dbId); - refreshQueries(); - }; - - const onKeyDown = (event: React.KeyboardEvent) => { - if (event.keyCode === 13) { - refreshQueries(); - } - }; - - const onChange = (e: React.ChangeEvent) => { - e.persist(); - const handleChange = debounce(e => { - setSearchText(e.target.value); - }, 200); - handleChange(e); - }; - - const userMutator = ({ result }: { result: UserMutatorProps[] }) => - result.map(({ value, text }: UserMutatorProps) => ({ - label: text, - value, - })); - - const dbMutator = ({ result }: { result: DbMutatorProps[] }) => { - const options = result.map(({ id, database_name }: DbMutatorProps) => ({ - value: id, - label: database_name, - })); - dispatch(setDatabases(result)); - if (result.length === 0) { - dispatch( - addDangerToast(t("It seems you don't have access to any database")), - ); - } - return options; - }; - - return ( - -
-
- setUserId(selected?.value)} - placeholder={t('Filter by user')} - /> -
-
- setDatabaseId(db?.value)} - dataEndpoint="/api/v1/database/?q=(filters:!((col:expose_in_sqllab,opr:eq,value:!t)))" - value={databaseId} - mutator={dbMutator} - placeholder={t('Filter by database')} - /> -
-
- -
-
- ({ value: xt, label: xt }))} - value={{ value: to, label: to }} - autosize={false} - onChange={(selected: any) => setTo(selected?.value)} - /> - - -
+ ); }; diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index cc62022a123d4..9db7ecc84d3f3 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -89,12 +89,25 @@ const collapseStyles = (theme: SupersetTheme) => css` .ant-collapse-arrow { top: ${theme.gridUnit * 2}px !important; color: ${theme.colors.primary.dark1} !important; - &: hover { + &:hover { color: ${theme.colors.primary.dark2} !important; } } `; +const LeftBarStyles = styled.div` + ${({ theme }) => css` + height: 100%; + display: flex; + flex-direction: column; + + .divider { + border-bottom: 1px solid ${theme.colors.grayscale.light4}; + margin: ${theme.gridUnit * 4}px 0; + } + `} +`; + const SqlEditorLeftBar = ({ database, queryEditorId, @@ -228,7 +241,7 @@ const SqlEditorLeftBar = ({ }, []); return ( -
+ {t('Reset state')} )} -
+ ); }; diff --git a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx index 8e4372d1091be..1e1b22a81d245 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx @@ -41,6 +41,12 @@ const TabTitle = styled.span` text-transform: none; `; +const IconContainer = styled.div` + display: inline-block; + width: ${({ theme }) => theme.gridUnit * 8}px; + text-align: center; +`; + interface Props { queryEditor: QueryEditor; } @@ -91,9 +97,9 @@ const SqlEditorTabHeader: React.FC = ({ queryEditor }) => { onClick={() => actions.removeQueryEditor(qe)} data-test="close-tab-menu-option" > -
+ -
+ {t('Close tab')} = ({ queryEditor }) => { onClick={renameTab} data-test="rename-tab-menu-option" > -
+ -
+ {t('Rename tab')}
= ({ queryEditor }) => { onClick={() => actions.toggleLeftBar(qe)} data-test="toggle-menu-option" > -
+ -
+ {qe.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
= ({ queryEditor }) => { onClick={() => actions.removeAllOtherQueryEditors(qe)} data-test="close-all-other-menu-option" > -
+ -
+ {t('Close all other tabs')}
= ({ queryEditor }) => { onClick={() => actions.cloneQueryToNewTab(qe, false)} data-test="clone-tab-menu-option" > -
+ -
+ {t('Duplicate tab')}
diff --git a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx index ab6348e835026..f40e946866586 100644 --- a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx +++ b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx @@ -17,13 +17,42 @@ * under the License. */ import React from 'react'; -import { QueryState, styled } from '@superset-ui/core'; +import { css, QueryState, styled } from '@superset-ui/core'; import Icons, { IconType } from 'src/components/Icons'; const IconContainer = styled.span` position: absolute; - top: -7px; - left: 0px; + top: -6px; + left: 1px; +`; + +const Circle = styled.div` + ${({ theme }) => css` + border-radius: 50%; + width: ${theme.gridUnit * 3}px; + height: ${theme.gridUnit * 3}px; + + display: inline-block; + background-color: ${theme.colors.grayscale.light2}; + text-align: center; + vertical-align: middle; + font-size: ${theme.typography.sizes.m}px; + font-weight: ${theme.typography.weights.bold}; + color: ${theme.colors.grayscale.light5}; + position: relative; + + &.running { + background-color: ${theme.colors.info.base}; + } + + &.success { + background-color: ${theme.colors.success.base}; + } + + &.failed { + background-color: ${theme.colors.error.base}; + } + `} `; interface TabStatusIconProps { @@ -38,12 +67,12 @@ const STATE_ICONS: Record> = { export default function TabStatusIcon({ tabState }: TabStatusIconProps) { const StatusIcon = STATE_ICONS[tabState]; return ( -
+ {StatusIcon && ( )} -
+ ); } diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index 63c7cc862caff..a06f3b7b60195 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -53,6 +53,12 @@ const defaultProps = { scheduleQueryWarning: null, }; +const StyledEditableTabs = styled(EditableTabs)` + height: 100%; + display: flex; + flex-direction: column; +`; + const StyledTab = styled.span` line-height: 24px; `; @@ -305,7 +311,7 @@ class TabbedSqlEditors extends React.PureComponent { ); return ( - {editors} {noQueryEditors && emptyTabState} - + ); } } diff --git a/superset-frontend/src/SqlLab/components/TableElement/index.tsx b/superset-frontend/src/SqlLab/components/TableElement/index.tsx index 44fbe6e1cc0cd..7d6e1ec6cd28c 100644 --- a/superset-frontend/src/SqlLab/components/TableElement/index.tsx +++ b/superset-frontend/src/SqlLab/components/TableElement/index.tsx @@ -21,7 +21,7 @@ import { useDispatch } from 'react-redux'; import Collapse from 'src/components/Collapse'; import Card from 'src/components/Card'; import ButtonGroup from 'src/components/ButtonGroup'; -import { t, styled } from '@superset-ui/core'; +import { css, t, styled } from '@superset-ui/core'; import { debounce } from 'lodash'; import { removeDataPreview, removeTables } from 'src/SqlLab/actions/sqlLab'; @@ -61,7 +61,7 @@ export interface TableElementProps { const StyledSpan = styled.span` color: ${({ theme }) => theme.colors.primary.dark1}; - &: hover { + &:hover { color: ${({ theme }) => theme.colors.primary.dark2}; } cursor: pointer; @@ -72,6 +72,39 @@ const Fade = styled.div` opacity: ${(props: { hovered: boolean }) => (props.hovered ? 1 : 0)}; `; +const StyledCollapsePanel = styled(Collapse.Panel)` + ${({ theme }) => css` + & { + .ws-el-controls { + margin-right: ${-theme.gridUnit}px; + display: flex; + } + + .header-container { + display: flex; + flex: 1; + align-items: center; + width: 100%; + + .table-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: ${theme.typography.sizes.l}px; + flex: 1; + } + + .header-right-side { + margin-left: auto; + display: flex; + align-items: center; + margin-right: ${theme.gridUnit * 8}px; + } + } + } + `} +`; + const TableElement = ({ table, ...props }: TableElementProps) => { const dispatch = useDispatch(); @@ -287,7 +320,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => { }; return ( - { forceRender > {renderBody()} - + ); }; diff --git a/superset-frontend/src/SqlLab/main.less b/superset-frontend/src/SqlLab/main.less deleted file mode 100644 index aa75c0ec00b63..0000000000000 --- a/superset-frontend/src/SqlLab/main.less +++ /dev/null @@ -1,491 +0,0 @@ -/** - * 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 '../assets/stylesheets/less/variables.less'; - -body { - min-height: ~'max(100vh, 500px)'; // Set a min height so the gutter is always visible when resizing - overflow: hidden; -} - -.inlineBlock { - display: inline-block; -} - -.valignTop { - vertical-align: top; -} - -.inline { - display: inline; -} - -.nopadding { - padding: 0px; -} - -.pane-cell { - padding: 10px; - overflow: auto; - width: 100%; - height: 100%; -} - -.ant-tabs-content-holder { - /* This is needed for Safari */ - height: 100%; -} - -.ant-tabs-content { - height: 100%; - position: relative; - background-color: @lightest; - overflow-x: auto; - overflow-y: auto; - - > .ant-tabs-tabpane { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} - -.Workspace .btn-sm { - box-shadow: 1px 1px 2px fade(@darkest, @opacity-light); - margin-top: 2px; - padding: 4px; -} - -.Workspace hr { - margin-top: 10px; - margin-bottom: 10px; -} - -div.Workspace { - height: 100%; - margin: 0px; -} - -.padded { - padding: 10px; -} - -.p-t-10 { - padding-top: 10px; -} - -.p-t-5 { - padding-top: 5px; -} - -.m-r-5 { - margin-right: 5px; -} - -.m-r-3 { - margin-right: 3px; -} - -.m-l-1 { - margin-left: 1px; -} - -.m-l-2 { - margin-left: 2px; -} - -.m-r-10 { - margin-right: 10px; -} - -.m-l-10 { - margin-left: 10px; -} - -.m-l-5 { - margin-left: 5px; -} - -.m-b-10 { - margin-bottom: 10px; -} - -.m-t-5 { - margin-top: 5px; -} - -.m-t-10 { - margin-top: 10px; -} - -.p-t-10 { - padding-top: 10px; -} - -.no-shadow { - box-shadow: none; - background-color: transparent; -} - -.pane-west { - height: 100%; - overflow: auto; -} - -.circle { - @circle-diameter: 10px; - border-radius: (@circle-diameter / 2); - width: @circle-diameter; - height: @circle-diameter; - - display: inline-block; - background-color: @gray-light; - text-align: center; - vertical-align: middle; - font-size: @font-size-m; - font-weight: @font-weight-bold; - color: @lightest; - position: relative; -} - -.running { - background-color: @info; -} - -.success { - background-color: @success; -} - -.failed { - background-color: @danger; -} - -.handle { - cursor: move; -} - -#a11y-query-editor-tabs { - height: 100%; - display: flex; - flex-direction: column; -} - -.SqlLab { - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - padding: 0 10px; - - pre { - padding: 0px !important; - margin: 0px; - border: none; - font-size: @font-size-s; - background-color: transparent !important; - } - - .north-pane { - display: flex; - flex-direction: column; - } - - #ace-editor { - height: calc(100% - 51px); - flex-grow: 1; - } - - .ace_content { - height: 100%; - } -} - -.SqlEditorTabs li { - a:focus { - outline: 0; - } - - .ddbtn-tab { - font-size: inherit; - color: black; - - &:active { - background: none; - } - - svg { - vertical-align: middle; - } - } - - .dropdown.btn-group.btn-group-sm { - width: 3px; - height: 3px; - border-radius: 1.5px; - background: #bababa; - margin-right: 8px; - font-weight: @font-weight-normal; - display: inline-flex; - - &:hover { - background-color: @primary-color; - - &:before, - &:after { - background-color: @primary-color; - } - } - - &:before, - &:after { - position: absolute; - content: ' '; - width: 3px; - height: 3px; - border-radius: 1.5px; - background-color: #bababa; - } - &:before { - transform: translateY(-5px); - } - &:after { - transform: translateY(5px); - } - } - - ul.dropdown-menu { - margin-top: 10px; - } - - .dropdown-toggle { - padding-top: 2px; - } -} - -.SqlEditor { - display: flex; - flex-direction: row; - height: 100%; - - .schemaPane { - transition: transform @timing-normal ease-in-out; - } - - .queryPane { - flex: 1 1 auto; - padding: 10px; - overflow-y: none; - overflow-x: scroll; - } - - .schemaPane-enter-done, - .schemaPane-exit { - transform: translateX(0); - z-index: 7; - } - - .schemaPane-exit-active { - transform: translateX(-120%); - } - - .schemaPane-enter-active { - transform: translateX(0); - max-width: 300px; - } - - .schemaPane-enter, - .schemaPane-exit-done { - max-width: 0; - transform: translateX(-120%); - overflow: hidden; - } - - .schemaPane-exit-done + .queryPane { - margin-left: 0; - } - - .gutter { - border-top: 1px solid @gray-light; - border-bottom: 1px solid @gray-light; - width: 3%; - margin: 3px 47%; - } - - .gutter.gutter-vertical { - cursor: row-resize; - } -} - -.SqlEditorLeftBar { - height: 100%; - display: flex; - flex-direction: column; - - .divider { - border-bottom: 1px solid @gray-bg; - margin: 15px 0; - } -} - -.popover { - max-width: 400px; -} - -.table-label { - margin-top: 5px; - margin-right: 10px; - float: left; -} - -div.tablePopover { - opacity: 0.7 !important; - - &:hover { - opacity: 1 !important; - } -} - -.ace_editor.ace_editor { - //double class is better than !important - border: 1px solid @gray-light; - font-feature-settings: @font-feature-settings; - // Fira Code causes problem with Ace under Firefox - font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono', - 'source-code-pro', 'Lucida Console', monospace; - - &.ace_autocomplete { - // Use !important because Ace Editor applies extra CSS at the last second - // when opening the autocomplete. - width: 520px !important; - } -} - -.Select__menu-outer { - min-width: 100%; - width: inherit; - z-index: @z-index-dropdown; -} - -.Select__clear-indicator { - margin-top: -2px; -} - -.Select__arrow { - margin-top: 5px; -} - -.ace_scroller { - background-color: @gray-bg; -} - -.TableElement { - .well { - margin-top: 5px; - margin-bottom: 5px; - padding: 5px 10px; - } - - .ws-el-controls { - margin-right: -0.3em; - display: flex; - } - - .header-container { - display: flex; - flex: 1; - align-items: center; - width: 100%; - - .table-name { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-size: 16px; - flex: 1; - } - - .header-right-side { - margin-left: auto; - display: flex; - align-items: center; - margin-right: 33px; - } - } -} - -.QueryTable .label { - display: inline-block; -} - -.QueryTable .ant-btn { - position: static; -} - -.ResultsModal .ant-modal-body { - min-height: 560px; -} - -.ant-modal-body { - overflow: auto; -} - -a.Link { - cursor: pointer; -} - -.QueryTable .well { - padding: 3px 5px; - margin: 3px 5px; -} - -.nav-tabs .ddbtn-tab { - padding: 0; - border: none; - background: none; - position: relative; - top: 2px; - - &:focus { - outline: 0; - } - - &:active { - box-shadow: none; - } -} - -.icon-container { - display: inline-block; - width: 30px; - text-align: center; -} - -.search-date-filter-container { - display: flex; - - .Select { - margin-right: 3px; - } -} - -.cost-estimate { - font-size: @font-size-s; -} diff --git a/superset-frontend/src/assets/stylesheets/superset.less b/superset-frontend/src/assets/stylesheets/superset.less index 5808d0144bc73..f54a6d498537a 100644 --- a/superset-frontend/src/assets/stylesheets/superset.less +++ b/superset-frontend/src/assets/stylesheets/superset.less @@ -327,6 +327,10 @@ table.table-no-hover tr:hover { margin-bottom: 10px; } +.m-l-2 { + margin-left: 2px; +} + .m-l-4 { margin-left: 4px; } From 4373b26c2b2427374ba0b19d0e3af966e32983c6 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 4 Jan 2023 13:24:45 +0100 Subject: [PATCH 17/22] Fix unit tests --- .../components/QueryStateLabel/QueryStateLabel.test.jsx | 2 +- .../src/SqlLab/components/QueryStateLabel/index.tsx | 2 ++ .../components/TabbedSqlEditors/TabbedSqlEditors.test.jsx | 4 ++-- .../src/SqlLab/components/TabbedSqlEditors/index.jsx | 2 ++ .../src/dashboard/components/dnd/DragDroppable.test.jsx | 5 ++++- .../gridComponents/new/DraggableNewComponent.jsx | 8 +++----- .../gridComponents/new/DraggableNewComponent.test.jsx | 4 ++-- 7 files changed, 16 insertions(+), 11 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx index a53225d96abcd..464c5755dc59c 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx @@ -35,6 +35,6 @@ describe('SavedQuery', () => { }); it('has an Overlay and a Popover', () => { const wrapper = shallow(); - expect(wrapper.find(Label)).toExist(); + expect(wrapper.find('Label')).toExist(); }); }); diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx index 4a8c581a82b1a..df10978f91410 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx @@ -28,6 +28,8 @@ interface QueryStateLabelProps { const StyledLabel = styled(Label)` margin-right: ${({ theme }) => theme.gridUnit}px; `; +// Setting a displayName makes it easier to test with enzyme +StyledLabel.displayName = 'Label'; export default function QueryStateLabel({ query }: QueryStateLabelProps) { return ( diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx index 73ff450b3827c..a354dcd1629f2 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx @@ -212,9 +212,9 @@ describe('TabbedSqlEditors', () => { }); it('should disable new tab when offline', () => { wrapper = getWrapper(); - expect(wrapper.find(EditableTabs).props().hideAdd).toBe(false); + expect(wrapper.find('EditableTabs').props().hideAdd).toBe(false); wrapper.setProps({ offline: true }); - expect(wrapper.find(EditableTabs).props().hideAdd).toBe(true); + expect(wrapper.find('EditableTabs').props().hideAdd).toBe(true); }); it('should have an empty state when query editors is empty', () => { wrapper = getWrapper(); diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index a06f3b7b60195..c09e8c56bae4e 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -58,6 +58,8 @@ const StyledEditableTabs = styled(EditableTabs)` display: flex; flex-direction: column; `; +// Setting a displayName makes it easier to test with enzyme +StyledEditableTabs.displayName = 'EditableTabs'; const StyledTab = styled.span` line-height: 24px; diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.test.jsx b/superset-frontend/src/dashboard/components/dnd/DragDroppable.test.jsx index 65d77e773c566..e8366815a6dd3 100644 --- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.test.jsx +++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.test.jsx @@ -17,7 +17,10 @@ * under the License. */ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { + styledMount as mount, + styledShallow as shallow, +} from 'spec/helpers/theming'; import sinon from 'sinon'; import newComponentFactory from 'src/dashboard/util/newComponentFactory'; diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx index cbfe1b4d68f8c..769a23940f19a 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx @@ -49,6 +49,8 @@ const NewComponent = styled.div` background: ${({ theme }) => theme.colors.grayscale.light4}; } `; +// Setting a displayName makes it easier to test with enzyme +NewComponent.displayName = 'NewComponent'; const NewComponentPlaceholder = styled.div` position: relative; @@ -94,11 +96,7 @@ export default class DraggableNewComponent extends React.PureComponent { editMode > {({ dragSourceRef }) => ( - + diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx index 2b56f02f5d91b..e912d1530dc96 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { mount } from 'enzyme'; +import { styledMount as mount } from 'spec/helpers/theming'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; @@ -73,7 +73,7 @@ describe('DraggableNewComponent', () => { it('should render the passed label', () => { const wrapper = setup(); - expect(wrapper.find('.new-component').text()).toBe(props.label); + expect(wrapper.find('NewComponent').text()).toBe(props.label); }); it('should add the passed className', () => { From ec41ed24208c0dc0646929ed2dba246c58b98a2b Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 4 Jan 2023 13:47:17 +0100 Subject: [PATCH 18/22] Fix native filters bar headers --- .../DashboardBuilder/DashboardBuilder.tsx | 29 ---------------- .../dashboard/containers/DashboardPage.tsx | 4 +-- superset-frontend/src/dashboard/styles.ts | 33 +++++++++++++++++++ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 442a42047d3dc..f39cda20a07a7 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -130,35 +130,6 @@ const StyledDiv = styled.div` opacity: 1; } - h1 { - font-weight: ${theme.typography.weights.bold}; - line-height: 1.4; - font-size: ${theme.typography.sizes.xxl}px; - letter-spacing: -0.2px; - margin-top: ${theme.gridUnit * 3}px; - margin-bottom: ${theme.gridUnit * 3}px; - } - - h2 { - font-weight: ${theme.typography.weights.bold}; - line-height: 1.4; - font-size: ${theme.typography.sizes.xl}px; - margin-top: ${theme.gridUnit * 3}px; - margin-bottom: ${theme.gridUnit * 2}px; - } - - h3, - h4, - h5, - h6 { - font-weight: ${theme.typography.weights.bold}; - line-height: 1.4; - font-size: ${theme.typography.sizes.l}px; - letter-spacing: 0.2px; - margin-top: ${theme.gridUnit * 2}px; - margin-bottom: ${theme.gridUnit}px; - } - p { margin: 0 0 ${theme.gridUnit * 2}px 0; } diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx b/superset-frontend/src/dashboard/containers/DashboardPage.tsx index a835523ae7684..0066354f589b5 100644 --- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx +++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx @@ -63,7 +63,7 @@ import { getFilterValue, getPermalinkValue, } from 'src/dashboard/components/nativeFilters/FilterBar/keyValue'; -import { filterCardPopoverStyle } from 'src/dashboard/styles'; +import { filterCardPopoverStyle, headerStyles } from 'src/dashboard/styles'; import { DashboardContextForExplore } from 'src/types/DashboardContextForExplore'; import shortid from 'shortid'; import { RootState } from '../types'; @@ -365,7 +365,7 @@ export const DashboardPage: FC = ({ idOrSlug }: PageProps) => { return ( <> - + css` + body { + h1 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.xxl}px; + letter-spacing: -0.2px; + margin-top: ${theme.gridUnit * 3}px; + margin-bottom: ${theme.gridUnit * 3}px; + } + + h2 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.xl}px; + margin-top: ${theme.gridUnit * 3}px; + margin-bottom: ${theme.gridUnit * 2}px; + } + + h3, + h4, + h5, + h6 { + font-weight: ${theme.typography.weights.bold}; + line-height: 1.4; + font-size: ${theme.typography.sizes.l}px; + letter-spacing: 0.2px; + margin-top: ${theme.gridUnit * 2}px; + margin-bottom: ${theme.gridUnit}px; + } + } +`; + export const filterCardPopoverStyle = (theme: SupersetTheme) => css` .filter-card-popover { width: 240px; From 0cee5800a988254962b47190dca71b9a8ad91075 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 4 Jan 2023 14:27:37 +0100 Subject: [PATCH 19/22] Fix lint --- .../components/QueryStateLabel/QueryStateLabel.test.jsx | 2 -- .../src/SqlLab/components/ResultSet/index.tsx | 8 +++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx index 464c5755dc59c..34c1847e9d78d 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx @@ -18,8 +18,6 @@ */ import React from 'react'; import { shallow } from 'enzyme'; - -import Label from 'src/components/Label'; import QueryStateLabel from 'src/SqlLab/components/QueryStateLabel'; describe('SavedQuery', () => { diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index c3573b7dbe362..81a4e47a11368 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -22,7 +22,13 @@ import ButtonGroup from 'src/components/ButtonGroup'; import Alert from 'src/components/Alert'; import Button from 'src/components/Button'; import shortid from 'shortid'; -import { QueryResponse, QueryState, styled, t, useTheme } from '@superset-ui/core'; +import { + QueryResponse, + QueryState, + styled, + t, + useTheme, +} from '@superset-ui/core'; import { usePrevious } from 'src/hooks/usePrevious'; import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace'; import { From 74259cf89afa3cba8c4a8cee91b9ab98089275e7 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Wed, 4 Jan 2023 16:55:38 +0100 Subject: [PATCH 20/22] Fix scrolling issue --- superset-frontend/src/GlobalStyles.tsx | 12 ------ superset-frontend/src/SqlLab/App.jsx | 2 + .../src/SqlLab/SqlLabGlobalStyles.tsx | 40 +++++++++++++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx index cf64648396859..1e2cf449ff637 100644 --- a/superset-frontend/src/GlobalStyles.tsx +++ b/superset-frontend/src/GlobalStyles.tsx @@ -23,14 +23,6 @@ import { Global } from '@emotion/react'; export const GlobalStyles = () => ( css` - body { - min-height: max( - 100vh, - ${theme.gridUnit * 125}px - ); // Set a min height so the gutter is always visible when resizing - overflow: hidden; - } - h1, h2, h3, @@ -41,10 +33,6 @@ export const GlobalStyles = () => ( th { font-weight: ${theme.typography.weights.bold}; } - - .cost-estimate { - font-size: ${theme.typography.sizes.s}; - } `} /> ); diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx index 918e6c1d38c84..0bb08cade039b 100644 --- a/superset-frontend/src/SqlLab/App.jsx +++ b/superset-frontend/src/SqlLab/App.jsx @@ -43,6 +43,7 @@ import setupApp from '../setup/setupApp'; import '../assets/stylesheets/reactable-pagination.less'; import { theme } from '../preamble'; +import { SqlLabGlobalStyles } from './SqlLabGlobalStyles'; setupApp(); setupExtensions(); @@ -140,6 +141,7 @@ const Application = () => ( + diff --git a/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx new file mode 100644 index 0000000000000..a65cd92762776 --- /dev/null +++ b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx @@ -0,0 +1,40 @@ +/** + * 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 { Global } from '@emotion/react'; +import { css } from '@superset-ui/core'; + +export const SqlLabGlobalStyles = () => ( + css` + body { + min-height: max( + 100vh, + ${theme.gridUnit * 125}px + ); // Set a min height so the gutter is always visible when resizing + overflow: hidden; + } + + .cost-estimate { + font-size: ${theme.typography.sizes.s}; + } + `} + /> +); From 6517947bbf12aa4cd66627bf811fe388c06fa071 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 10 Jan 2023 16:13:53 +0100 Subject: [PATCH 21/22] Address code review comments --- .../cypress/support/directories.ts | 7 +- .../src/SqlLab/SqlLabGlobalStyles.tsx | 4 - .../components/AceEditorWrapper/index.tsx | 2 +- .../EstimateQueryCostButton/index.tsx | 23 ++-- .../QueryStateLabel/QueryStateLabel.test.jsx | 7 +- .../components/QueryStateLabel/index.tsx | 2 - .../components/SqlEditorLeftBar/index.tsx | 2 +- .../TabbedSqlEditors.test.jsx | 4 +- .../components/TabbedSqlEditors/index.jsx | 2 - .../src/assets/stylesheets/superset.less | 14 --- .../DashboardBuilder/DashboardBuilder.tsx | 46 ++++---- .../components/SliceHeader/index.tsx | 6 +- .../filterscope/FilterScopeSelector.jsx | 18 ++- .../components/gridComponents/ChartHolder.tsx | 26 ++--- .../components/gridComponents/Column.jsx | 108 +++++++++--------- .../components/gridComponents/Divider.jsx | 38 +++--- .../components/gridComponents/Header.jsx | 86 +++++++------- .../components/gridComponents/Markdown.jsx | 54 ++++----- .../components/gridComponents/Row.jsx | 102 +++++++++-------- .../new/DraggableNewComponent.jsx | 74 ++++++------ .../new/DraggableNewComponent.test.jsx | 4 +- .../components/menu/WithPopoverMenu.tsx | 8 +- .../resizable/ResizableContainer.jsx | 3 +- 23 files changed, 310 insertions(+), 330 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/support/directories.ts b/superset-frontend/cypress-base/cypress/support/directories.ts index fde9ee0cdeacf..d74aef607d76a 100644 --- a/superset-frontend/cypress-base/cypress/support/directories.ts +++ b/superset-frontend/cypress-base/cypress/support/directories.ts @@ -127,10 +127,11 @@ export const databasesPage = { export const sqlLabView = { sqlEditorLeftBar: { - sqlEditorLeftBar: '[class="SqlEditorLeftBar"]', - databaseSchemaTableSection: '[class="SqlEditorLeftBar"] > :nth-child(1)', + sqlEditorLeftBar: '[data-test="sql-editor-left-bar"]', + databaseSchemaTableSection: + '[data-test="sql-editor-left-bar"] > :nth-child(1)', tableSchemaSection: - '[class="SqlEditorLeftBar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)', + '[data-test="sql-editor-left-bar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)', tableSchemaInputEmpty: '[aria-label="Select table or type table name"]', }, databaseInput: '[data-test=DatabaseSelector] > :nth-child(1)', diff --git a/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx index a65cd92762776..f1398e3be9ac4 100644 --- a/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx +++ b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx @@ -31,10 +31,6 @@ export const SqlLabGlobalStyles = () => ( ); // Set a min height so the gutter is always visible when resizing overflow: hidden; } - - .cost-estimate { - font-size: ${theme.typography.sizes.s}; - } `} /> ); diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx index faf4a16ddabf5..0dd3385ea57e6 100644 --- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx +++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx @@ -62,7 +62,7 @@ type AceEditorWrapperProps = { const StyledAceEditor = styled(AceEditor)` ${({ theme }) => css` && { - //double class is better than !important + // double class is better than !important border: 1px solid ${theme.colors.grayscale.light2}; font-feature-settings: 'liga' off, 'calt' off; // Fira Code causes problem with Ace under Firefox diff --git a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx index c0c92e3a5549a..4dd5c489582a6 100644 --- a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx @@ -18,7 +18,7 @@ */ import React, { useMemo } from 'react'; import { useSelector } from 'react-redux'; -import { t } from '@superset-ui/core'; +import { css, styled, t } from '@superset-ui/core'; import Alert from 'src/components/Alert'; import TableView from 'src/components/TableView'; @@ -36,6 +36,12 @@ export interface EstimateQueryCostButtonProps { disabled?: boolean; } +const CostEstimateModalStyles = styled.div` + ${({ theme }) => css` + font-size: ${theme.typography.sizes.s}; + `} +`; + const EstimateQueryCostButton = ({ getEstimate, queryEditorId, @@ -76,13 +82,14 @@ const EstimateQueryCostButton = ({ } if (queryCostEstimate?.completed) { return ( - + + + ); } return ; diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx index 34c1847e9d78d..a14e08a9fe722 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx @@ -17,7 +17,8 @@ * under the License. */ import React from 'react'; -import { shallow } from 'enzyme'; +import { styledMount as mount } from 'spec/helpers/theming'; +import Label from 'src/components/Label'; import QueryStateLabel from 'src/SqlLab/components/QueryStateLabel'; describe('SavedQuery', () => { @@ -32,7 +33,7 @@ describe('SavedQuery', () => { ); }); it('has an Overlay and a Popover', () => { - const wrapper = shallow(); - expect(wrapper.find('Label')).toExist(); + const wrapper = mount(); + expect(wrapper.find(Label)).toExist(); }); }); diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx index df10978f91410..4a8c581a82b1a 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx @@ -28,8 +28,6 @@ interface QueryStateLabelProps { const StyledLabel = styled(Label)` margin-right: ${({ theme }) => theme.gridUnit}px; `; -// Setting a displayName makes it easier to test with enzyme -StyledLabel.displayName = 'Label'; export default function QueryStateLabel({ query }: QueryStateLabelProps) { return ( diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index 9db7ecc84d3f3..a96cb841167ad 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -241,7 +241,7 @@ const SqlEditorLeftBar = ({ }, []); return ( - + { }); it('should disable new tab when offline', () => { wrapper = getWrapper(); - expect(wrapper.find('EditableTabs').props().hideAdd).toBe(false); + expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(false); wrapper.setProps({ offline: true }); - expect(wrapper.find('EditableTabs').props().hideAdd).toBe(true); + expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(true); }); it('should have an empty state when query editors is empty', () => { wrapper = getWrapper(); diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index c09e8c56bae4e..a06f3b7b60195 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -58,8 +58,6 @@ const StyledEditableTabs = styled(EditableTabs)` display: flex; flex-direction: column; `; -// Setting a displayName makes it easier to test with enzyme -StyledEditableTabs.displayName = 'EditableTabs'; const StyledTab = styled.span` line-height: 24px; diff --git a/superset-frontend/src/assets/stylesheets/superset.less b/superset-frontend/src/assets/stylesheets/superset.less index f54a6d498537a..39bd719d272d3 100644 --- a/superset-frontend/src/assets/stylesheets/superset.less +++ b/superset-frontend/src/assets/stylesheets/superset.less @@ -42,10 +42,6 @@ input.form-control { background-color: @lightest; } -.chart-header a.danger { - color: @danger; -} - .disabledButton { pointer-events: none; } @@ -165,16 +161,6 @@ img.viz-thumb-option { max-height: 700px; } -.chart-header .header-text { - font-size: @font-size-xl; - line-height: 22px; - padding-bottom: 8px; - border-bottom: 1px solid @gray; - margin-top: 10px; - margin-left: 10px; - margin-right: 10px; -} - #is_cached { display: none; } diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index f39cda20a07a7..31187740744c6 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -26,7 +26,14 @@ import React, { useRef, useState, } from 'react'; -import { addAlpha, css, JsonObject, styled, t } from '@superset-ui/core'; +import { + addAlpha, + css, + JsonObject, + styled, + t, + useTheme, +} from '@superset-ui/core'; import { Global } from '@emotion/react'; import { useDispatch, useSelector } from 'react-redux'; import ErrorBoundary from 'src/components/ErrorBoundary'; @@ -187,11 +194,6 @@ const DashboardContentWrapper = styled.div` flex-direction: column; height: 100%; - /* only top-level tabs have popover, give it more padding to match header + tabs */ - & > .with-popover-menu > .popover-menu { - left: ${theme.gridUnit * 6}px; - } - /* drop shadow for top-level tabs only */ & .dashboard-component-tabs { box-shadow: 0 ${theme.gridUnit}px ${theme.gridUnit}px 0 @@ -332,18 +334,10 @@ const DashboardContentWrapper = styled.div` `; const StyledDashboardContent = styled.div<{ - dashboardFiltersOpen: boolean; editMode: boolean; - nativeFiltersEnabled: boolean; - filterBarOrientation: FilterBarOrientation; + marginLeft: number; }>` - ${({ - theme, - dashboardFiltersOpen, - editMode, - nativeFiltersEnabled, - filterBarOrientation, - }) => css` + ${({ theme, editMode, marginLeft }) => css` display: flex; flex-direction: row; flex-wrap: nowrap; @@ -363,12 +357,7 @@ const StyledDashboardContent = styled.div<{ margin-top: ${theme.gridUnit * 6}px; margin-right: ${theme.gridUnit * 8}px; margin-bottom: ${theme.gridUnit * 6}px; - margin-left: ${!dashboardFiltersOpen && - !editMode && - nativeFiltersEnabled && - filterBarOrientation !== FilterBarOrientation.HORIZONTAL - ? 0 - : theme.gridUnit * 8}px; + margin-left: ${marginLeft}px; ${editMode && ` @@ -436,6 +425,7 @@ const StyledDashboardContent = styled.div<{ const DashboardBuilder: FC = () => { const dispatch = useDispatch(); const uiConfig = useUiConfig(); + const theme = useTheme(); const dashboardId = useSelector( ({ dashboardInfo }) => `${dashboardInfo.id}`, @@ -633,6 +623,14 @@ const DashboardBuilder: FC = () => { ], ); + const dashboardContentMarginLeft = + !dashboardFiltersOpen && + !editMode && + nativeFiltersEnabled && + filterBarOrientation !== FilterBarOrientation.HORIZONTAL + ? 0 + : theme.gridUnit * 8; + return ( {showFilterBar && filterBarOrientation === FilterBarOrientation.VERTICAL && ( @@ -722,10 +720,8 @@ const DashboardBuilder: FC = () => { > {showDashboard ? ( diff --git a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx index c647075b893cd..8c4bef5c29e19 100644 --- a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx +++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx @@ -197,11 +197,7 @@ const SliceHeader: FC = ({ const exploreUrl = `/explore/?dashboard_page_id=${dashboardPageId}&slice_id=${slice.slice_id}`; return ( - +
- + +

{t('Configure filter scopes')}

{showSelector && this.renderEditingFiltersName()}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx index fa2350f4ae20f..a93f3b0b8db63 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx @@ -20,7 +20,7 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { ResizeCallback, ResizeStartCallback } from 're-resizable'; import cx from 'classnames'; import { useSelector } from 'react-redux'; -import { css, styled } from '@superset-ui/core'; +import { css } from '@superset-ui/core'; import { LayoutItem, RootState } from 'src/dashboard/types'; import AnchorLink from 'src/dashboard/components/AnchorLink'; import Chart from 'src/dashboard/containers/Chart'; @@ -69,17 +69,13 @@ interface ChartHolderProps { isInView: boolean; } -const FullSizeWrapper = styled.div<{ isFullSize: boolean }>` - ${({ isFullSize }) => - isFullSize && - css` - && { - position: fixed; - z-index: 3000; - left: 0; - top: 0; - } - `}; +const fullSizeStyle = css` + && { + position: fixed; + z-index: 3000; + left: 0; + top: 0; + } `; const ChartHolder: React.FC = ({ @@ -274,11 +270,11 @@ const ChartHolder: React.FC = ({ onResizeStop={onResizeStop} editMode={editMode} > - = ({
)} - +
{dropIndicatorProps &&
} )} diff --git a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx index 63c2d7c89167f..1883531404f77 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { styled } from '@superset-ui/core'; +import { css, styled, t } from '@superset-ui/core'; import Icons from 'src/components/Icons'; import DashboardComponent from 'src/dashboard/containers/DashboardComponent'; import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton'; @@ -60,49 +60,45 @@ const propTypes = { const defaultProps = {}; const ColumnStyles = styled.div` - &.grid-column { - width: 100%; - position: relative; + ${({ theme }) => css` + &.grid-column { + width: 100%; + position: relative; - & > :not(.hover-menu):not(:last-child) { - margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; + & > :not(.hover-menu):not(:last-child) { + margin-bottom: ${theme.gridUnit * 4}px; + } } - } - &.grid-column--empty { - min-height: ${({ theme }) => theme.gridUnit * 25}px; - - &:before { - content: 'Empty column'; + .dashboard--editing &:after { + content: ''; position: absolute; - top: 0; - left: 0; width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; - color: ${({ theme }) => theme.colors.grayscale.light2}; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + border: 1px dashed ${theme.colors.grayscale.light2}; } - } + .dashboard--editing .resizable-container--resizing:hover > &:after, + .dashboard--editing .hover-menu:hover + &:after { + border: 1px dashed ${theme.colors.primary.base}; + z-index: 2; + } + `} +`; - .dashboard--editing &:after { - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 1; - pointer-events: none; - border: 1px dashed ${({ theme }) => theme.colors.grayscale.light2}; - } - .dashboard--editing .resizable-container--resizing:hover > &:after, - .dashboard--editing .hover-menu:hover + &:after { - border: 1px dashed ${({ theme }) => theme.colors.primary.base}; - z-index: 2; - } +const emptyColumnContentStyles = theme => css` + min-height: ${theme.gridUnit * 25}px; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: ${theme.colors.text.label}; `; + class Column extends React.PureComponent { constructor(props) { super(props); @@ -218,28 +214,28 @@ class Column extends React.PureComponent { )} - {columnItems.map((componentId, itemIndex) => ( - - ))} + {columnItems.length === 0 ? ( +
{t('Empty column')}
+ ) : ( + columnItems.map((componentId, itemIndex) => ( + + )) + )} {dropIndicatorProps &&
} diff --git a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx index 6c0313162ad0b..078405be3e4a3 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx @@ -18,7 +18,7 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import { styled } from '@superset-ui/core'; +import { css, styled } from '@superset-ui/core'; import DragDroppable from '../dnd/DragDroppable'; import HoverMenu from '../menu/HoverMenu'; @@ -38,26 +38,28 @@ const propTypes = { }; const DividerLine = styled.div` - width: 100%; - padding: ${({ theme }) => theme.gridUnit * 2}px 0; /* this is padding not margin to enable a larger mouse target */ - background-color: transparent; - - &:after { - content: ''; - height: 1px; + ${({ theme }) => css` width: 100%; - background-color: ${({ theme }) => theme.colors.grayscale.light2}; - display: block; - } + padding: ${theme.gridUnit * 2}px 0; /* this is padding not margin to enable a larger mouse target */ + background-color: transparent; - div[draggable='true'] & { - cursor: move; - } + &:after { + content: ''; + height: 1px; + width: 100%; + background-color: ${theme.colors.grayscale.light2}; + display: block; + } - .dashboard-component-tabs & { - padding-left: ${({ theme }) => theme.gridUnit * 4}px; - padding-right: ${({ theme }) => theme.gridUnit * 4}px; - } + div[draggable='true'] & { + cursor: move; + } + + .dashboard-component-tabs & { + padding-left: ${theme.gridUnit * 4}px; + padding-right: ${theme.gridUnit * 4}px; + } + `} `; class Divider extends React.PureComponent { diff --git a/superset-frontend/src/dashboard/components/gridComponents/Header.jsx b/superset-frontend/src/dashboard/components/gridComponents/Header.jsx index 74827d1609991..253f377fc2f7d 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Header.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Header.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { styled } from '@superset-ui/core'; +import { css, styled } from '@superset-ui/core'; import PopoverDropdown from 'src/components/PopoverDropdown'; import EditableTitle from 'src/components/EditableTitle'; @@ -57,59 +57,61 @@ const propTypes = { const defaultProps = {}; const HeaderStyles = styled.div` - font-weight: ${({ theme }) => theme.typography.weights.bold}; - width: 100%; - padding: ${({ theme }) => theme.gridUnit * 4}px 0; + ${({ theme }) => css` + font-weight: ${theme.typography.weights.bold}; + width: 100%; + padding: ${theme.gridUnit * 4}px 0; - &.header-small { - font-size: ${({ theme }) => theme.typography.sizes.l}px; - } - - &.header-medium { - font-size: ${({ theme }) => theme.typography.sizes.xl}px; - } + &.header-small { + font-size: ${theme.typography.sizes.l}px; + } - &.header-large { - font-size: ${({ theme }) => theme.typography.sizes.xxl}px; - } + &.header-medium { + font-size: ${theme.typography.sizes.xl}px; + } - .dashboard--editing .dashboard-grid & { - &:after { - border: 1px dashed transparent; - content: ''; - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 1; - pointer-events: none; + &.header-large { + font-size: ${theme.typography.sizes.xxl}px; } - &:hover:after { - border: 1px dashed ${({ theme }) => theme.colors.primary.base}; - z-index: 2; + .dashboard--editing .dashboard-grid & { + &:after { + border: 1px dashed transparent; + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; + } + + &:hover:after { + border: 1px dashed ${theme.colors.primary.base}; + z-index: 2; + } } - } - .dashboard--editing .dragdroppable-row & { - cursor: move; - } + .dashboard--editing .dragdroppable-row & { + cursor: move; + } - /** + /** * grids add margin between items, so don't double pad within columns * we'll not worry about double padding on top as it can serve as a visual separator */ - .grid-column > :not(:last-child) & { - margin-bottom: ${({ theme }) => theme.gridUnit * -4}px; - } + .grid-column > :not(:last-child) & { + margin-bottom: ${theme.gridUnit * -4}px; + } - .background--white &, - &.background--white, - .dashboard-component-tabs & { - padding-left: ${({ theme }) => theme.gridUnit * 4}px; - padding-right: ${({ theme }) => theme.gridUnit * 4}px; - } + .background--white &, + &.background--white, + .dashboard-component-tabs & { + padding-left: ${theme.gridUnit * 4}px; + padding-right: ${theme.gridUnit * 4}px; + } + `} `; class Header extends React.PureComponent { diff --git a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx index 18be0dc46b960..1d358529b5777 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import cx from 'classnames'; -import { styled, t, SafeMarkdown } from '@superset-ui/core'; +import { css, styled, t, SafeMarkdown } from '@superset-ui/core'; import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils'; import { MarkdownEditor } from 'src/components/AsyncAceEditor'; @@ -84,32 +84,34 @@ Click here to learn more about [markdown formatting](https://bit.ly/1dQOfRK)`; const MARKDOWN_ERROR_MESSAGE = t('This markdown component has an error.'); const MarkdownStyles = styled.div` - &.dashboard-markdown { - overflow: hidden; - - h4, - h5, - h6 { - font-weight: ${({ theme }) => theme.typography.weights.normal}; - } - - h5 { - color: ${({ theme }) => theme.colors.grayscale.base}; - } - - h6 { - font-size: ${({ theme }) => theme.typography.sizes.s}px; + ${({ theme }) => css` + &.dashboard-markdown { + overflow: hidden; + + h4, + h5, + h6 { + font-weight: ${theme.typography.weights.normal}; + } + + h5 { + color: ${theme.colors.grayscale.base}; + } + + h6 { + font-size: ${theme.typography.sizes.s}px; + } + + .dashboard-component-chart-holder { + overflow-y: auto; + overflow-x: hidden; + } + + .dashboard--editing & { + cursor: move; + } } - - .dashboard-component-chart-holder { - overflow-y: auto; - overflow-x: hidden; - } - - .dashboard--editing & { - cursor: move; - } - } + `} `; class Markdown extends React.PureComponent { diff --git a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx index ccfdd4b9cc9b0..c46abe71c25df 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx @@ -19,7 +19,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { FeatureFlag, isFeatureEnabled, styled } from '@superset-ui/core'; +import { + css, + FeatureFlag, + isFeatureEnabled, + styled, + t, + useTheme, +} from '@superset-ui/core'; import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; import DragHandle from 'src/dashboard/components/dnd/DragHandle'; @@ -59,36 +66,33 @@ const propTypes = { }; const GridRow = styled.div` - position: relative; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: flex-start; - width: 100%; - height: fit-content; - - & > :not(:last-child):not(.hover-menu) { - margin-right: ${({ theme }) => theme.gridUnit * 4}px; - } + ${({ theme }) => css` + position: relative; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: flex-start; + width: 100%; + height: fit-content; - &.grid-row--empty { - /* this centers the empty note content */ - align-items: center; - height: ${({ theme }) => theme.gridUnit * 25}px; + & > :not(:last-child):not(.hover-menu) { + margin-right: ${theme.gridUnit * 4}px; + } - &:before { - position: absolute; - top: 0; - left: 0; - content: 'Empty row'; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - color: ${({ theme }) => theme.colors.text.label}; + &.grid-row--empty { + min-height: ${theme.gridUnit * 25}px; } - } + `} +`; + +const emptyRowContentStyles = theme => css` + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: ${theme.colors.text.label}; `; class Row extends React.PureComponent { @@ -243,25 +247,29 @@ class Row extends React.PureComponent { data-test={`grid-row-${backgroundStyle.className}`} ref={this.containerRef} > - {rowItems.map((componentId, itemIndex) => ( - - ))} + {rowItems.length === 0 ? ( +
{t('Empty row')}
+ ) : ( + rowItems.map((componentId, itemIndex) => ( + + )) + )} {dropIndicatorProps &&
} diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx index 769a23940f19a..c261fd2da25e1 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.jsx @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; -import { styled } from '@superset-ui/core'; +import { css, styled } from '@superset-ui/core'; import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; import { NEW_COMPONENTS_SOURCE_ID } from 'src/dashboard/util/constants'; @@ -37,48 +37,50 @@ const defaultProps = { }; const NewComponent = styled.div` - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - padding: ${({ theme }) => theme.gridUnit * 4}px; - background: ${({ theme }) => theme.colors.grayscale.light5}; - cursor: move; + ${({ theme }) => css` + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + padding: ${theme.gridUnit * 4}px; + background: ${theme.colors.grayscale.light5}; + cursor: move; - &:not(.static):hover { - background: ${({ theme }) => theme.colors.grayscale.light4}; - } + &:not(.static):hover { + background: ${theme.colors.grayscale.light4}; + } + `} `; -// Setting a displayName makes it easier to test with enzyme -NewComponent.displayName = 'NewComponent'; const NewComponentPlaceholder = styled.div` - position: relative; - background: ${({ theme }) => theme.colors.grayscale.light4}; - width: ${({ theme }) => theme.gridUnit * 10}px; - height: ${({ theme }) => theme.gridUnit * 10}px; - margin-right: ${({ theme }) => theme.gridUnit * 4}px; - border: 1px solid ${({ theme }) => theme.colors.grayscale.light5}; - display: flex; - align-items: center; - justify-content: center; - color: ${({ theme }) => theme.colors.text.label}; - font-size: ${({ theme }) => theme.typography.sizes.xxl}px; + ${({ theme }) => css` + position: relative; + background: ${theme.colors.grayscale.light4}; + width: ${theme.gridUnit * 10}px; + height: ${theme.gridUnit * 10}px; + margin-right: ${theme.gridUnit * 4}px; + border: 1px solid ${theme.colors.grayscale.light5}; + display: flex; + align-items: center; + justify-content: center; + color: ${theme.colors.text.label}; + font-size: ${theme.typography.sizes.xxl}px; - &.fa-window-restore { - font-size: ${({ theme }) => theme.typography.sizes.l}px; - } + &.fa-window-restore { + font-size: ${theme.typography.sizes.l}px; + } - &.fa-area-chart { - font-size: ${({ theme }) => theme.typography.sizes.xl}px; - } + &.fa-area-chart { + font-size: ${theme.typography.sizes.xl}px; + } - &.divider-placeholder:after { - content: ''; - height: 2px; - width: 100%; - background-color: ${({ theme }) => theme.colors.grayscale.light2}; - } + &.divider-placeholder:after { + content: ''; + height: 2px; + width: 100%; + background-color: ${theme.colors.grayscale.light2}; + } + `} `; export default class DraggableNewComponent extends React.PureComponent { diff --git a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx index e912d1530dc96..a105fa43964f3 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/new/DraggableNewComponent.test.jsx @@ -73,7 +73,9 @@ describe('DraggableNewComponent', () => { it('should render the passed label', () => { const wrapper = setup(); - expect(wrapper.find('NewComponent').text()).toBe(props.label); + expect( + wrapper.find('[data-test="new-component"]').at(0).childAt(0).text(), + ).toBe(props.label); }); it('should add the passed className', () => { diff --git a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx index ef075af908372..3aa0aa81d3d2f 100644 --- a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx +++ b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx @@ -47,12 +47,6 @@ const WithPopoverMenuStyles = styled.div` position: relative; outline: none; - .grid-row.grid-row--empty & { - /* drop indicator doesn't show up without this */ - width: 100%; - height: 100%; - } - &.with-popover-menu--focused:after { content: ''; position: absolute; @@ -206,7 +200,7 @@ export default class WithPopoverMenu extends React.PureComponent< > {children} {editMode && isFocused && (menuItems?.length ?? 0) > 0 && ( - + {menuItems.map((node: React.ReactNode, i: Number) => (
{node} diff --git a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx index 44b94f9681a4d..a14731574e582 100644 --- a/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx +++ b/superset-frontend/src/dashboard/components/resizable/ResizableContainer.jsx @@ -89,8 +89,7 @@ const StyledResizable = styled(Resizable)` position: relative; /* re-resizable sets an empty div to 100% width and height, which doesn't - play well with many 100% height containers we need - */ + play well with many 100% height containers we need */ & ~ div { width: auto !important; From a3965562f3f2e5b8f224af720d4fa2172355016d Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 10 Jan 2023 16:49:39 +0100 Subject: [PATCH 22/22] Lint fix --- .../src/dashboard/components/gridComponents/Row.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx index c46abe71c25df..ae3db36d1dbf5 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Row.jsx @@ -25,7 +25,6 @@ import { isFeatureEnabled, styled, t, - useTheme, } from '@superset-ui/core'; import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';