diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.test.js
index a842264be2..a4c63505ab 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.test.js
@@ -97,15 +97,24 @@ describe('Dashboard tabs', () => {
cy.get('[data-test="dashboard-component-tabs"]')
.first()
- .find('[data-test="nav-list"]')
- .children()
+ .find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('top-level-tabs');
- cy.get('@top-level-tabs').first().click().should('have.class', 'active');
- cy.get('@top-level-tabs').last().should('not.have.class', 'active');
+ cy.get('@top-level-tabs')
+ .first()
+ .click()
+ .should('have.class', 'ant-tabs-tab-active');
+ cy.get('@top-level-tabs')
+ .last()
+ .should('not.have.class', 'ant-tabs-tab-active');
- cy.get('@top-level-tabs').last().click().should('have.class', 'active');
- cy.get('@top-level-tabs').first().should('not.have.class', 'active');
+ cy.get('@top-level-tabs')
+ .last()
+ .click()
+ .should('have.class', 'ant-tabs-tab-active');
+ cy.get('@top-level-tabs')
+ .first()
+ .should('not.have.class', 'ant-tabs-tab-active');
});
it('should load charts when tab is visible', () => {
@@ -128,8 +137,7 @@ describe('Dashboard tabs', () => {
// click row level tab, see 1 more chart
cy.get('[data-test="dashboard-component-tabs"]')
.last()
- .find('[data-test="nav-list"]')
- .children()
+ .find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('row-level-tabs');
cy.get('@row-level-tabs').last().click();
@@ -141,8 +149,7 @@ describe('Dashboard tabs', () => {
handleException();
cy.get('[data-test="dashboard-component-tabs"]')
.first()
- .find('[data-test="nav-list"]')
- .children()
+ .find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.as('top-level-tabs');
cy.get('@top-level-tabs').last().click();
diff --git a/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
index 98ad189007..eb90589644 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx
@@ -179,7 +179,7 @@ describe('DashboardBuilder', () => {
expect(wrapper.find(TabContainer).prop('activeKey')).toBe(0);
wrapper
- .find('.dashboard-component-tabs .nav-tabs a')
+ .find('.dashboard-component-tabs .ant-tabs .ant-tabs-tab')
.at(1)
.simulate('click');
diff --git a/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tab_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tab_spec.jsx
index c4d6c30403..689de44661 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tab_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tab_spec.jsx
@@ -22,10 +22,8 @@ import { styledMount as mount } from 'spec/helpers/theming';
import sinon from 'sinon';
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
-import DeleteComponentModal from 'src/dashboard/components/DeleteComponentModal';
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
import EditableTitle from 'src/components/EditableTitle';
-import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
import Tab, {
RENDER_TAB,
RENDER_TAB_CONTENT,
@@ -96,42 +94,6 @@ describe('Tabs', () => {
'New title',
);
});
-
- it('should render a WithPopoverMenu', () => {
- const wrapper = setup();
- expect(wrapper.find(WithPopoverMenu)).toExist();
- });
-
- it('should render a DeleteComponentModal when focused if its not the only tab', () => {
- let wrapper = setup();
- wrapper.find(WithPopoverMenu).simulate('click'); // focus
- expect(wrapper.find(DeleteComponentModal)).not.toExist();
-
- wrapper = setup({ editMode: true });
- wrapper.find(WithPopoverMenu).simulate('click');
- expect(wrapper.find(DeleteComponentModal)).toExist();
-
- wrapper = setup({
- editMode: true,
- parentComponent: {
- ...props.parentComponent,
- children: props.parentComponent.children.slice(0, 1),
- },
- });
- wrapper.find(WithPopoverMenu).simulate('click');
- expect(wrapper.find(DeleteComponentModal)).not.toExist();
- });
-
- it('should show modal when clicked delete icon', () => {
- const deleteComponent = sinon.spy();
- const wrapper = setup({ editMode: true, deleteComponent });
- wrapper.find(WithPopoverMenu).simulate('click'); // focus
- wrapper.find('.icon-button').simulate('click');
-
- const modal = document.getElementsByClassName('ant-modal');
- expect(modal).toHaveLength(1);
- expect(deleteComponent.callCount).toBe(0);
- });
});
describe('renderType=RENDER_TAB_CONTENT', () => {
diff --git a/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tabs_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tabs_spec.jsx
index 28f4b4d3bc..63dc8000c4 100644
--- a/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tabs_spec.jsx
+++ b/superset-frontend/spec/javascripts/dashboard/components/gridComponents/Tabs_spec.jsx
@@ -18,11 +18,12 @@
*/
import { Provider } from 'react-redux';
import React from 'react';
-import { mount, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
import sinon from 'sinon';
-import { Tabs as BootstrapTabs, Tab as BootstrapTab } from 'react-bootstrap';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { LineEditableTabs } from 'src/common/components/Tabs';
+import { Modal } from 'src/common/components';
+import { styledMount as mount } from 'spec/helpers/theming';
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
@@ -54,6 +55,7 @@ describe('Tabs', () => {
deleteComponent() {},
updateComponents() {},
logEvent() {},
+ setMountedTab() {},
};
function setup(overrideProps) {
@@ -65,10 +67,6 @@ describe('Tabs', () => {
,
- {
- wrappingComponent: ThemeProvider,
- wrappingComponentProps: { theme: supersetTheme },
- },
);
return wrapper;
}
@@ -79,31 +77,23 @@ describe('Tabs', () => {
expect(wrapper.find(DragDroppable)).toExist();
});
- it('should render BootstrapTabs', () => {
+ it('should render non-editable tabs', () => {
const wrapper = setup();
- expect(wrapper.find(BootstrapTabs)).toExist();
+ expect(wrapper.find(LineEditableTabs)).toExist();
+ expect(wrapper.find('.ant-tabs-nav-add').exists()).toBeFalsy();
});
- it('should set animation=true, mountOnEnter=true, and unmounOnExit=false on BootstrapTabs for perf', () => {
+ it('should render a tab pane for each child', () => {
const wrapper = setup();
- const tabProps = wrapper.find(BootstrapTabs).props();
- expect(tabProps.animation).toBe(true);
- expect(tabProps.mountOnEnter).toBe(true);
- expect(tabProps.unmountOnExit).toBe(false);
- });
-
- it('should render a BootstrapTab for each child', () => {
- const wrapper = setup();
- expect(wrapper.find(BootstrapTab)).toHaveLength(
+ expect(wrapper.find(LineEditableTabs.TabPane)).toHaveLength(
props.component.children.length,
);
});
- it('should render an extra (+) BootstrapTab in editMode', () => {
+ it('should render editable tabs in editMode', () => {
const wrapper = setup({ editMode: true });
- expect(wrapper.find(BootstrapTab)).toHaveLength(
- props.component.children.length + 1,
- );
+ expect(wrapper.find(LineEditableTabs)).toExist();
+ expect(wrapper.find('.ant-tabs-nav-add')).toExist();
});
it('should render a DashboardComponent for each child', () => {
@@ -118,7 +108,7 @@ describe('Tabs', () => {
const createComponent = sinon.spy();
const wrapper = setup({ editMode: true, createComponent });
wrapper
- .find('.dashboard-component-tabs .nav-tabs a')
+ .find('[data-test="dashboard-component-tabs"] .ant-tabs-nav-add')
.last()
.simulate('click');
@@ -129,7 +119,7 @@ describe('Tabs', () => {
const onChangeTab = sinon.spy();
const wrapper = setup({ editMode: true, onChangeTab });
wrapper
- .find('.dashboard-component-tabs .nav-tabs a')
+ .find('[data-test="dashboard-component-tabs"] .ant-tabs-tab')
.at(1) // will not call if it is already selected
.simulate('click');
@@ -140,7 +130,9 @@ describe('Tabs', () => {
const onChangeTab = sinon.spy();
const wrapper = setup({ editMode: true, onChangeTab });
wrapper
- .find('.dashboard-component-tabs .nav-tabs a .short-link-trigger')
+ .find(
+ '[data-test="dashboard-component-tabs"] .ant-tabs-tab [data-test="short-link-button"]',
+ )
.at(1) // will not call if it is already selected
.simulate('click');
@@ -186,4 +178,13 @@ describe('Tabs', () => {
wrapper = shallow();
expect(wrapper.state('tabIndex')).toBe(1);
});
+
+ it('should render Modal when clicked remove tab button', () => {
+ const deleteComponent = sinon.spy();
+ const modalMock = jest.spyOn(Modal, 'confirm');
+ const wrapper = setup({ editMode: true, deleteComponent });
+ wrapper.find('.ant-tabs-tab-remove').at(0).simulate('click');
+ expect(modalMock.mock.calls).toHaveLength(1);
+ expect(deleteComponent.callCount).toBe(0);
+ });
});
diff --git a/superset-frontend/src/common/components/Tabs/Tabs.tsx b/superset-frontend/src/common/components/Tabs/Tabs.tsx
index 027f286665..de2fabe376 100644
--- a/superset-frontend/src/common/components/Tabs/Tabs.tsx
+++ b/superset-frontend/src/common/components/Tabs/Tabs.tsx
@@ -40,6 +40,24 @@ const StyledTabs = styled(AntdTabs, {
&.ant-tabs-tab-active .ant-tabs-tab-btn {
color: inherit;
}
+
+ &:hover {
+ .anchor-link-container {
+ cursor: pointer;
+
+ .fa.fa-link {
+ visibility: visible;
+ }
+ }
+ }
+
+ .short-link-trigger.btn {
+ padding: 0 ${({ theme }) => theme.gridUnit}px;
+
+ & > .fa.fa-link {
+ top: 0;
+ }
+ }
}
${({ fullWidth }) =>
@@ -124,15 +142,37 @@ EditableTabs.TabPane.defaultProps = {
),
};
-const StyledCardTabs = styled(EditableTabs)``;
+const StyledLineEditableTabs = styled(EditableTabs)`
+ &.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab {
+ margin: 0 ${({ theme }) => theme.gridUnit * 4}px;
+ padding: ${({ theme }) => `${theme.gridUnit * 3}px ${theme.gridUnit}px`};
+ background: transparent;
+ border: none;
+ }
-const CardTabs = Object.assign(StyledCardTabs, {
+ &.ant-tabs-card > .ant-tabs-nav .ant-tabs-ink-bar {
+ visibility: visible;
+ }
+
+ .ant-tabs-tab-btn {
+ font-size: ${({ theme }) => theme.typography.sizes.m}px;
+ }
+
+ .ant-tabs-tab-remove {
+ margin-left: 0;
+ padding-right: 0;
+ }
+
+ .ant-tabs-nav-add {
+ min-width: unset !important;
+ background: transparent !important;
+ border: none !important;
+ }
+`;
+
+const LineEditableTabs = Object.assign(StyledLineEditableTabs, {
TabPane: StyledTabPane,
});
-CardTabs.defaultProps = {
- type: 'card',
-};
-
export default Tabs;
-export { CardTabs, EditableTabs };
+export { EditableTabs, LineEditableTabs };
diff --git a/superset-frontend/src/components/URLShortLinkButton.jsx b/superset-frontend/src/components/URLShortLinkButton.jsx
index f35525aa1e..625d2df4c7 100644
--- a/superset-frontend/src/components/URLShortLinkButton.jsx
+++ b/superset-frontend/src/components/URLShortLinkButton.jsx
@@ -48,7 +48,8 @@ class URLShortLinkButton extends React.Component {
}));
}
- getCopyUrl() {
+ getCopyUrl(e) {
+ e.stopPropagation();
getShortUrl(this.props.url)
.then(this.onShortUrlSuccess)
.catch(this.props.addDangerToast);
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
index 54a9eb17ff..3772893bf9 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
@@ -101,7 +101,7 @@ class DashboardBuilder extends React.Component {
static shouldFocusTabs(event, container) {
// don't focus the tabs when we click on a tab
return (
- event.target.tagName === 'UL' ||
+ event.target.className === 'ant-tabs-nav-wrap' ||
(/icon-button/.test(event.target.className) &&
container.contains(event.target))
);
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index c159d17ae2..97b128c97c 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -23,8 +23,6 @@ import DashboardComponent from '../../containers/DashboardComponent';
import DragDroppable from '../dnd/DragDroppable';
import EditableTitle from '../../../components/EditableTitle';
import AnchorLink from '../../../components/AnchorLink';
-import DeleteComponentModal from '../DeleteComponentModal';
-import WithPopoverMenu from '../menu/WithPopoverMenu';
import { componentShape } from '../../util/propShapes';
export const RENDER_TAB = 'RENDER_TAB';
@@ -39,7 +37,6 @@ const propTypes = {
depth: PropTypes.number.isRequired,
renderType: PropTypes.oneOf([RENDER_TAB, RENDER_TAB_CONTENT]).isRequired,
onDropOnTab: PropTypes.func,
- onDeleteTab: PropTypes.func,
editMode: PropTypes.bool.isRequired,
filters: PropTypes.object.isRequired,
@@ -52,7 +49,6 @@ const propTypes = {
// redux
handleComponentDrop: PropTypes.func.isRequired,
- deleteComponent: PropTypes.func.isRequired,
updateComponents: PropTypes.func.isRequired,
setDirectPathToChild: PropTypes.func.isRequired,
};
@@ -61,7 +57,6 @@ const defaultProps = {
availableColumnCount: 0,
columnWidth: 0,
onDropOnTab() {},
- onDeleteTab() {},
onResizeStart() {},
onResize() {},
onResizeStop() {},
@@ -70,21 +65,12 @@ const defaultProps = {
export default class Tab extends React.PureComponent {
constructor(props) {
super(props);
- this.state = {
- isFocused: false,
- };
- this.handleChangeFocus = this.handleChangeFocus.bind(this);
this.handleChangeText = this.handleChangeText.bind(this);
- this.handleDeleteComponent = this.handleDeleteComponent.bind(this);
this.handleDrop = this.handleDrop.bind(this);
this.handleTopDropTargetDrop = this.handleTopDropTargetDrop.bind(this);
this.handleChangeTab = this.handleChangeTab.bind(this);
}
- handleChangeFocus(nextFocus) {
- this.setState(() => ({ isFocused: nextFocus }));
- }
-
handleChangeTab({ pathToTabIndex }) {
this.props.setDirectPathToChild(pathToTabIndex);
}
@@ -104,12 +90,6 @@ export default class Tab extends React.PureComponent {
}
}
- handleDeleteComponent() {
- const { index, id, parentId } = this.props;
- this.props.deleteComponent(id, parentId);
- this.props.onDeleteTab(index);
- }
-
handleDrop(dropResult) {
this.props.handleComponentDrop(dropResult);
this.props.onDropOnTab(dropResult);
@@ -204,7 +184,6 @@ export default class Tab extends React.PureComponent {
}
renderTab() {
- const { isFocused } = this.state;
const {
component,
parentComponent,
@@ -212,12 +191,8 @@ export default class Tab extends React.PureComponent {
depth,
editMode,
filters,
+ isFocused,
} = this.props;
- const deleteTabIcon = (
-
-
-
- );
return (
- {({ dropIndicatorProps, dragSourceRef }) => (
-
-
,
- ]
- }
- editMode={editMode}
- >
+ {({ dropIndicatorProps, dragSourceRef }) => {
+ return (
+
= 5 ? 'left' : 'right'}
/>
)}
-
- {dropIndicatorProps && }
-
- )}
+ {dropIndicatorProps &&
}
+
+ );
+ }}
);
}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
index e2fb8c6a80..bd185e39ef 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
@@ -18,8 +18,10 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
-import { Tabs as BootstrapTabs, Tab as BootstrapTab } from 'react-bootstrap';
-
+import { LineEditableTabs } from 'src/common/components/Tabs';
+import { LOG_ACTIONS_SELECT_DASHBOARD_TAB } from 'src/logger/LogUtils';
+import { Modal } from 'src/common/components';
+import { styled, t } from '@superset-ui/core';
import DragDroppable from '../dnd/DragDroppable';
import DragHandle from '../dnd/DragHandle';
import DashboardComponent from '../../containers/DashboardComponent';
@@ -32,9 +34,7 @@ import { componentShape } from '../../util/propShapes';
import { NEW_TAB_ID, DASHBOARD_ROOT_ID } from '../../util/constants';
import { RENDER_TAB, RENDER_TAB_CONTENT } from './Tab';
import { TAB_TYPE } from '../../util/componentTypes';
-import { LOG_ACTIONS_SELECT_DASHBOARD_TAB } from '../../../logger/LogUtils';
-const NEW_TAB_INDEX = -1;
const MAX_TAB_COUNT = 10;
const propTypes = {
@@ -79,6 +79,38 @@ const defaultProps = {
onResizeStop() {},
};
+const StyledTabsContainer = styled.div`
+ width: 100%;
+ background-color: ${({ theme }) => theme.colors.grayscale.light5};
+
+ .dashboard-component-tabs-content {
+ min-height: ${({ theme }) => theme.gridUnit * 12}px;
+ margin-top: ${({ theme }) => theme.gridUnit / 4}px;
+ position: relative;
+ }
+
+ .drop-indicator--left {
+ left: ${({ theme }) => -theme.gridUnit * 3}px !important;
+ }
+ .drop-indicator--right {
+ left: ${({ theme }) => `calc(100% + ${theme.gridUnit * 6}px)`} !important;
+ }
+
+ .drop-indicator--bottom,
+ .drop-indicator--top {
+ width: ${({ theme }) => `calc(100% + ${theme.gridUnit * 6}px)`} !important;
+ }
+
+ .drop-indicator--top {
+ top: ${({ theme }) => theme.gridUnit * 2}px;
+ }
+
+ .editable-title input {
+ cursor: pointer;
+ text-transform: uppercase;
+ }
+`;
+
class Tabs extends React.PureComponent {
constructor(props) {
super(props);
@@ -127,19 +159,32 @@ class Tabs extends React.PureComponent {
}
}
- handleClickTab(tabIndex, ev) {
- if (ev) {
- const { target } = ev;
- // special handler for clicking on anchor link icon (or whitespace nearby):
- // will show short link popover but do not change tab
- if (target && target.classList.contains('short-link-trigger')) {
- return;
- }
- }
+ showDeleteConfirmModal = key => {
+ const { component, deleteComponent } = this.props;
+ Modal.confirm({
+ title: t('Delete dashboard tab?'),
+ content: (
+
+ Deleting a tab will remove all content within it. You may still
+ reverse this action with the undo button (cmd + z) until you
+ save your changes.
+
+ ),
+ onOk: () => {
+ deleteComponent(key, component.id);
+ const tabIndex = component.children.indexOf(key);
+ this.handleClickTab(Math.max(0, tabIndex - 1));
+ },
+ okType: 'danger',
+ okText: 'DELETE',
+ cancelText: 'CANCEL',
+ icon: null,
+ });
+ };
+ handleEdit = (key, action) => {
const { component, createComponent } = this.props;
-
- if (tabIndex === NEW_TAB_INDEX) {
+ if (action === 'add') {
createComponent({
destination: {
id: component.id,
@@ -151,7 +196,15 @@ class Tabs extends React.PureComponent {
type: TAB_TYPE,
},
});
- } else if (tabIndex !== this.state.tabIndex) {
+ } else if (action === 'remove') {
+ this.showDeleteConfirmModal(key);
+ }
+ };
+
+ handleClickTab(tabIndex) {
+ const { component, renderTabContent } = this.props;
+
+ if (tabIndex !== this.state.tabIndex) {
const pathToTabIndex = getDirectPathToTabIndex(component, tabIndex);
const targetTabId = pathToTabIndex[pathToTabIndex.length - 1];
this.props.logEvent(LOG_ACTIONS_SELECT_DASHBOARD_TAB, {
@@ -161,6 +214,11 @@ class Tabs extends React.PureComponent {
this.props.onChangeTab({ pathToTabIndex });
}
+ if (renderTabContent) {
+ const tabIds = component.children;
+ const activeKey = tabIds[this.state.tabIndex];
+ this.props.setMountedTab(activeKey);
+ }
}
handleDeleteComponent() {
@@ -212,6 +270,8 @@ class Tabs extends React.PureComponent {
const { tabIndex: selectedTabIndex } = this.state;
const { children: tabIds } = tabsComponent;
+ const activeKey = tabIds[selectedTabIndex];
+
return (
(
-
@@ -237,23 +297,21 @@ class Tabs extends React.PureComponent {
)}
-
{
+ this.handleClickTab(tabIds.indexOf(key));
+ }}
+ onEdit={this.handleEdit}
+ hideAdd={tabIds.length >= MAX_TAB_COUNT}
data-test="nav-list"
+ type={editMode ? 'editable-card' : 'card'}
>
{tabIds.map((tabId, tabIndex) => (
- // react-bootstrap doesn't render a Tab if we move this to its own Tab.jsx so we
- // use `renderType` to indicate what the DashboardComponent should render. This
- // prevents us from passing the entire dashboard component lookup to render Tabs.jsx
-
}
- onEntering={() => {
- // Entering current tab, DOM is visible and has dimension
- if (renderTabContent) {
- this.props.setMountedTab(tabId);
- }
- }}
>
{renderTabContent && (
)}
-
+
))}
-
- {editMode && tabIds.length < MAX_TAB_COUNT && (
- }
- />
- )}
-
+
{/* don't indicate that a drop on root is allowed when tabs already exist */}
{tabsDropIndicatorProps &&
parentComponent.id !== DASHBOARD_ROOT_ID && (
)}
-
+
)}
);
diff --git a/superset-frontend/src/dashboard/stylesheets/components/index.less b/superset-frontend/src/dashboard/stylesheets/components/index.less
index 4669bd830b..d99e11df2a 100644
--- a/superset-frontend/src/dashboard/stylesheets/components/index.less
+++ b/superset-frontend/src/dashboard/stylesheets/components/index.less
@@ -22,5 +22,4 @@
@import './header.less';
@import './new-component.less';
@import './row.less';
-@import './tabs.less';
@import './markdown.less';
diff --git a/superset-frontend/src/dashboard/stylesheets/components/tabs.less b/superset-frontend/src/dashboard/stylesheets/components/tabs.less
deleted file mode 100644
index 39987b0727..0000000000
--- a/superset-frontend/src/dashboard/stylesheets/components/tabs.less
+++ /dev/null
@@ -1,106 +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-tabs {
- width: 100%;
- background-color: @lightest;
-
- & .nav-tabs {
- border-bottom: none;
-
- /* by moving padding from to we can restrict the selected tab indicator to text width */
- & > li {
- margin: 0 16px;
-
- & > a {
- color: @almost-black;
- border: none;
- padding: 12px 0 14px 0;
- font-size: @font-size-m;
- margin-right: 0;
-
- &:hover {
- border: none;
- background: inherit;
- color: @almost-black;
- }
-
- &:focus {
- outline: none;
- background: @lightest;
- }
- }
-
- & .dragdroppable-tab[draggable='true'] {
- cursor: move;
- }
-
- & .drop-indicator {
- top: -12px !important;
- height: ~'calc(100% + 24px)' !important;
- }
-
- & .drop-indicator--left {
- left: -12px !important;
- }
- & .drop-indicator--right {
- right: -12px !important;
- }
-
- & .drop-indicator--bottom,
- & .drop-indicator--top {
- left: -12px !important;
- width: ~'calc(100% + 24px)' !important; /* escape for .less */
- opacity: 0.4;
- }
-
- & .fa-plus {
- color: @gray-dark;
- font-size: @font-size-m;
- margin-top: 3px;
- }
-
- & .editable-title input[type='button'] {
- cursor: pointer;
- }
- }
-
- & li.active > a {
- border: none;
-
- &:after {
- content: '';
- position: absolute;
- height: 3px;
- width: 100%;
- bottom: 0;
- background: linear-gradient(
- to right,
- @indicator-color,
- shade(@indicator-color, @colorstop-two)
- );
- }
- }
- }
-
- & .dashboard-component-tabs-content {
- min-height: 48px;
- margin-top: 1px;
- position: relative;
- }
-}
diff --git a/superset-frontend/stylesheets/superset.less b/superset-frontend/stylesheets/superset.less
index 4cd60805a9..cdd259b6d0 100644
--- a/superset-frontend/stylesheets/superset.less
+++ b/superset-frontend/stylesheets/superset.less
@@ -283,18 +283,12 @@ table.table-no-hover tr:hover {
}
}
-.nav.nav-tabs li .anchor-link-container {
- top: 0;
- right: -32px;
-}
-
.dashboard-component.dashboard-component-header .anchor-link-container {
.fa.fa-link {
font-size: @font-size-l;
}
}
-.nav.nav-tabs li:hover,
.dashboard-component.dashboard-component-header:hover {
.anchor-link-container {
cursor: pointer;