mirror of
https://github.com/apache/superset.git
synced 2024-09-06 22:07:34 -04:00
style: use tabs in dashboard edit pane (#10394)
* style: use tabs in dashboard edit pane * fix tests * more hackin' * getting ready to rip cell measurer * working * pogress * Fix cards * done * fix jest * fix cy
This commit is contained in:
parent
51a88cb19b
commit
ece91928a9
@ -23,7 +23,7 @@ describe('Dashboard edit mode', () => {
|
||||
cy.server();
|
||||
cy.login();
|
||||
cy.visit(WORLD_HEALTH_DASHBOARD);
|
||||
cy.get('.dashboard-header').contains('Edit dashboard').click();
|
||||
cy.get('.dashboard-header [data-test=pencil]').click();
|
||||
});
|
||||
|
||||
it('remove, and add chart flow', () => {
|
||||
@ -38,11 +38,12 @@ describe('Dashboard edit mode', () => {
|
||||
cy.get('.grid-container .box_plot').should('not.exist');
|
||||
});
|
||||
|
||||
// open charts list
|
||||
cy.get('.component-layer').contains('Your charts & filters').click();
|
||||
cy.get('.tabs-components .nav-tabs li a').contains('Charts').click();
|
||||
|
||||
// find box plot is available from list
|
||||
cy.get('.slices-layer').find('.chart-card-container').contains('Box plot');
|
||||
cy.get('.tabs-components')
|
||||
.find('.chart-card-container')
|
||||
.contains('Box plot');
|
||||
|
||||
// drag-n-drop
|
||||
const dataTransfer = { data: {} };
|
||||
@ -62,14 +63,14 @@ describe('Dashboard edit mode', () => {
|
||||
cy.get('.grid-container .box_plot').should('be.exist');
|
||||
|
||||
// should show Save changes button
|
||||
cy.get('.dashboard-header .button-container').contains('Save changes');
|
||||
cy.get('.dashboard-header .button-container').contains('Save');
|
||||
|
||||
// undo 2 steps
|
||||
cy.get('.dashboard-header .undo-action').click().click();
|
||||
|
||||
// no changes, can switch to view mode
|
||||
cy.get('.dashboard-header .button-container')
|
||||
.contains('Switch to view mode')
|
||||
.contains('Discard Changes')
|
||||
.click();
|
||||
});
|
||||
});
|
||||
|
@ -53,20 +53,18 @@ describe('Dashboard save action', () => {
|
||||
cy.get('.grid-container .box_plot', { timeout: 5000 }); // wait for 5 secs
|
||||
|
||||
// remove box_plot chart from dashboard
|
||||
cy.get('.dashboard-header')
|
||||
.contains('Edit dashboard')
|
||||
.trigger('click', { force: true });
|
||||
cy.get('.dashboard-header [data-test=pencil]').click();
|
||||
cy.get('.fa.fa-trash').last().trigger('click', { force: true });
|
||||
cy.get('.grid-container .box_plot').should('not.exist');
|
||||
|
||||
cy.route('POST', '/superset/save_dash/**/').as('saveRequest');
|
||||
cy.get('.dashboard-header')
|
||||
.contains('Save changes')
|
||||
.contains('Save')
|
||||
.trigger('click', { force: true });
|
||||
|
||||
// go back to view mode
|
||||
cy.wait('@saveRequest');
|
||||
cy.get('.dashboard-header').contains('Edit dashboard');
|
||||
cy.get('.dashboard-header [data-test=pencil]').click();
|
||||
cy.get('.grid-container .box_plot').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
21
superset-frontend/images/icons/more.svg
Normal file
21
superset-frontend/images/icons/more.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10ZM5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10ZM19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -32,7 +32,6 @@ import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DashboardHeader from 'src/dashboard/containers/DashboardHeader';
|
||||
import DashboardGrid from 'src/dashboard/containers/DashboardGrid';
|
||||
import * as dashboardStateActions from 'src/dashboard/actions/dashboardState';
|
||||
import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
|
||||
|
||||
import WithDragDropContext from '../helpers/WithDragDropContext';
|
||||
import {
|
||||
@ -64,7 +63,6 @@ describe('DashboardBuilder', () => {
|
||||
deleteTopLevelTabs() {},
|
||||
editMode: false,
|
||||
showBuilderPane() {},
|
||||
builderPaneType: BUILDER_PANE_TYPE.NONE,
|
||||
setColorSchemeAndUnsavedChanges() {},
|
||||
colorScheme: undefined,
|
||||
handleComponentDrop() {},
|
||||
@ -155,7 +153,6 @@ describe('DashboardBuilder', () => {
|
||||
wrapper.setProps({
|
||||
...props,
|
||||
editMode: true,
|
||||
builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS,
|
||||
});
|
||||
expect(wrapper.find(BuilderComponentPane)).toExist();
|
||||
});
|
||||
@ -167,7 +164,6 @@ describe('DashboardBuilder', () => {
|
||||
wrapper.setProps({
|
||||
...props,
|
||||
editMode: true,
|
||||
builderPaneType: BUILDER_PANE_TYPE.COLORS,
|
||||
});
|
||||
expect(wrapper.find(BuilderComponentPane)).toExist();
|
||||
});
|
||||
|
@ -68,7 +68,7 @@ describe('HeaderActionsDropdown', () => {
|
||||
|
||||
it('should render two MenuItems', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(MenuItem)).toHaveLength(2);
|
||||
expect(wrapper.find(MenuItem)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should render the RefreshIntervalModal', () => {
|
||||
|
@ -25,7 +25,6 @@ import PublishedStatus from 'src/dashboard/components/PublishedStatus';
|
||||
import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown';
|
||||
import Button from 'src/components/Button';
|
||||
import UndoRedoKeylisteners from 'src/dashboard/components/UndoRedoKeylisteners';
|
||||
import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
|
||||
|
||||
describe('Header', () => {
|
||||
const props = {
|
||||
@ -59,7 +58,6 @@ describe('Header', () => {
|
||||
editMode: false,
|
||||
setEditMode: () => {},
|
||||
showBuilderPane: () => {},
|
||||
builderPaneType: BUILDER_PANE_TYPE.NONE,
|
||||
updateCss: () => {},
|
||||
hasUnsavedChanges: false,
|
||||
maxUndoHistoryExceeded: false,
|
||||
@ -111,11 +109,6 @@ describe('Header', () => {
|
||||
expect(wrapper.find(HeaderActionsDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should render one Button', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(Button)).toExist();
|
||||
});
|
||||
|
||||
it('should not set up undo/redo', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
|
||||
@ -154,11 +147,6 @@ describe('Header', () => {
|
||||
expect(wrapper.find(HeaderActionsDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should render one Button', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(Button)).toExist();
|
||||
});
|
||||
|
||||
it('should not set up undo/redo', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
|
||||
@ -199,7 +187,7 @@ describe('Header', () => {
|
||||
|
||||
it('should render five Buttons', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(Button)).toHaveLength(5);
|
||||
expect(wrapper.find(Button)).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('should set up undo/redo', () => {
|
||||
@ -239,11 +227,6 @@ describe('Header', () => {
|
||||
expect(wrapper.find(HeaderActionsDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should render one Button', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(Button)).toExist();
|
||||
});
|
||||
|
||||
it('should not set up undo/redo', () => {
|
||||
const wrapper = setup(overrideProps);
|
||||
expect(wrapper.find(UndoRedoKeylisteners)).not.toExist();
|
||||
|
@ -16,14 +16,12 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
|
||||
import { sliceId } from './mockChartQueries';
|
||||
|
||||
export default {
|
||||
sliceIds: [sliceId],
|
||||
expandedSlices: {},
|
||||
editMode: false,
|
||||
builderPaneType: BUILDER_PANE_TYPE.NONE,
|
||||
hasUnsavedChanges: false,
|
||||
maxUndoHistoryExceeded: false,
|
||||
isStarred: true,
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
} from 'src/dashboard/actions/dashboardState';
|
||||
|
||||
import dashboardStateReducer from 'src/dashboard/reducers/dashboardState';
|
||||
import { BUILDER_PANE_TYPE } from 'src/dashboard/util/constants';
|
||||
|
||||
describe('dashboardState reducer', () => {
|
||||
it('should return initial state', () => {
|
||||
@ -72,7 +71,6 @@ describe('dashboardState reducer', () => {
|
||||
),
|
||||
).toEqual({
|
||||
editMode: true,
|
||||
builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS,
|
||||
});
|
||||
});
|
||||
|
||||
@ -128,7 +126,6 @@ describe('dashboardState reducer', () => {
|
||||
hasUnsavedChanges: false,
|
||||
maxUndoHistoryExceeded: false,
|
||||
editMode: false,
|
||||
builderPaneType: BUILDER_PANE_TYPE.NONE,
|
||||
updatedColorScheme: false,
|
||||
});
|
||||
});
|
||||
|
@ -32,6 +32,7 @@ import { ReactComponent as ErrorIcon } from 'images/icons/error.svg';
|
||||
import { ReactComponent as FavoriteSelectedIcon } from 'images/icons/favorite-selected.svg';
|
||||
import { ReactComponent as FavoriteUnselectedIcon } from 'images/icons/favorite-unselected.svg';
|
||||
import { ReactComponent as PencilIcon } from 'images/icons/pencil.svg';
|
||||
import { ReactComponent as MoreIcon } from 'images/icons/more.svg';
|
||||
import { ReactComponent as SearchIcon } from 'images/icons/search.svg';
|
||||
import { ReactComponent as SortAscIcon } from 'images/icons/sort-asc.svg';
|
||||
import { ReactComponent as SortDescIcon } from 'images/icons/sort-desc.svg';
|
||||
@ -55,6 +56,7 @@ type IconName =
|
||||
| 'error'
|
||||
| 'favorite-selected'
|
||||
| 'favorite-unselected'
|
||||
| 'more'
|
||||
| 'pencil'
|
||||
| 'search'
|
||||
| 'sort'
|
||||
@ -84,6 +86,7 @@ export const iconsRegistry: Record<
|
||||
close: CloseIcon,
|
||||
compass: CompassIcon,
|
||||
error: ErrorIcon,
|
||||
more: MoreIcon,
|
||||
pencil: PencilIcon,
|
||||
search: SearchIcon,
|
||||
sort: SortIcon,
|
||||
|
@ -253,8 +253,8 @@ export function fetchCharts(
|
||||
}
|
||||
|
||||
export const SHOW_BUILDER_PANE = 'SHOW_BUILDER_PANE';
|
||||
export function showBuilderPane(builderPaneType) {
|
||||
return { type: SHOW_BUILDER_PANE, builderPaneType };
|
||||
export function showBuilderPane() {
|
||||
return { type: SHOW_BUILDER_PANE };
|
||||
}
|
||||
|
||||
export function addSliceToDashboard(id, component) {
|
||||
|
@ -22,24 +22,28 @@ import PropTypes from 'prop-types';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
const propTypes = {
|
||||
datasourceLink: PropTypes.string,
|
||||
datasourceUrl: PropTypes.string,
|
||||
datasourceName: PropTypes.string,
|
||||
innerRef: PropTypes.func,
|
||||
isSelected: PropTypes.bool,
|
||||
lastModified: PropTypes.string.isRequired,
|
||||
lastModified: PropTypes.string,
|
||||
sliceName: PropTypes.string.isRequired,
|
||||
style: PropTypes.object,
|
||||
visType: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
datasourceLink: '—',
|
||||
datasourceUrl: null,
|
||||
datasourceName: '-',
|
||||
innerRef: null,
|
||||
isSelected: false,
|
||||
style: null,
|
||||
lastModified: null,
|
||||
};
|
||||
|
||||
function AddSliceCard({
|
||||
datasourceLink,
|
||||
datasourceUrl,
|
||||
datasourceName,
|
||||
innerRef,
|
||||
isSelected,
|
||||
lastModified,
|
||||
@ -62,9 +66,7 @@ function AddSliceCard({
|
||||
</div>
|
||||
<div className="item">
|
||||
<span>{t('Data source')} </span>
|
||||
<span // eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{ __html: datasourceLink }}
|
||||
/>
|
||||
<a href={datasourceUrl}>{datasourceName}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,37 +19,53 @@
|
||||
/* eslint-env browser */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Tabs, Tab } from 'react-bootstrap';
|
||||
import { StickyContainer, Sticky } from 'react-sticky';
|
||||
import { ParentSize } from '@vx/responsive';
|
||||
|
||||
import InsertComponentPane, {
|
||||
SUPERSET_HEADER_HEIGHT,
|
||||
} from './InsertComponentPane';
|
||||
import ColorComponentPane from './ColorComponentPane';
|
||||
import { BUILDER_PANE_TYPE } from '../util/constants';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import NewColumn from './gridComponents/new/NewColumn';
|
||||
import NewDivider from './gridComponents/new/NewDivider';
|
||||
import NewHeader from './gridComponents/new/NewHeader';
|
||||
import NewRow from './gridComponents/new/NewRow';
|
||||
import NewTabs from './gridComponents/new/NewTabs';
|
||||
import NewMarkdown from './gridComponents/new/NewMarkdown';
|
||||
import SliceAdder from '../containers/SliceAdder';
|
||||
|
||||
const propTypes = {
|
||||
topOffset: PropTypes.number,
|
||||
showBuilderPane: PropTypes.func.isRequired,
|
||||
builderPaneType: PropTypes.string.isRequired,
|
||||
setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
|
||||
colorScheme: PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
topOffset: 0,
|
||||
colorScheme: undefined,
|
||||
};
|
||||
|
||||
const SUPERSET_HEADER_HEIGHT = 59;
|
||||
|
||||
class BuilderComponentPane extends React.PureComponent {
|
||||
renderTabs(height) {
|
||||
const { isSticky } = this.props;
|
||||
return (
|
||||
<Tabs className="m-t-10 tabs-components">
|
||||
<Tab eventKey={1} title={t('Components')}>
|
||||
<NewTabs />
|
||||
<NewRow />
|
||||
<NewColumn />
|
||||
<NewHeader />
|
||||
<NewMarkdown />
|
||||
<NewDivider />
|
||||
</Tab>
|
||||
<Tab eventKey={2} title={t('Charts')} className="tab-charts">
|
||||
<SliceAdder
|
||||
height={height + (isSticky ? SUPERSET_HEADER_HEIGHT : 0)}
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
topOffset,
|
||||
builderPaneType,
|
||||
showBuilderPane,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
colorScheme,
|
||||
} = this.props;
|
||||
const { topOffset } = this.props;
|
||||
return (
|
||||
<div
|
||||
className="dashboard-builder-sidepane"
|
||||
@ -66,22 +82,7 @@ class BuilderComponentPane extends React.PureComponent {
|
||||
className="viewport"
|
||||
style={isSticky ? { ...style, top: topOffset } : null}
|
||||
>
|
||||
{builderPaneType === BUILDER_PANE_TYPE.ADD_COMPONENTS && (
|
||||
<InsertComponentPane
|
||||
height={height}
|
||||
isSticky={isSticky}
|
||||
showBuilderPane={showBuilderPane}
|
||||
/>
|
||||
)}
|
||||
{builderPaneType === BUILDER_PANE_TYPE.COLORS && (
|
||||
<ColorComponentPane
|
||||
showBuilderPane={showBuilderPane}
|
||||
setColorSchemeAndUnsavedChanges={
|
||||
setColorSchemeAndUnsavedChanges
|
||||
}
|
||||
colorScheme={colorScheme}
|
||||
/>
|
||||
)}
|
||||
{this.renderTabs(height)}
|
||||
</div>
|
||||
)}
|
||||
</Sticky>
|
||||
|
@ -1,107 +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.
|
||||
*/
|
||||
/* eslint-env browser */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { getCategoricalSchemeRegistry } from '@superset-ui/color';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import ColorSchemeControl from '../../explore/components/controls/ColorSchemeControl';
|
||||
import { BUILDER_PANE_TYPE } from '../util/constants';
|
||||
|
||||
const propTypes = {
|
||||
showBuilderPane: PropTypes.func.isRequired,
|
||||
setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
|
||||
colorScheme: PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
colorScheme: undefined,
|
||||
};
|
||||
|
||||
class ColorComponentPane extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hovered: false };
|
||||
this.categoricalSchemeRegistry = getCategoricalSchemeRegistry();
|
||||
this.getChoices = this.getChoices.bind(this);
|
||||
this.getSchemes = this.getSchemes.bind(this);
|
||||
this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
|
||||
this.onMouseEnter = this.setHover.bind(this, true);
|
||||
this.onMouseLeave = this.setHover.bind(this, false);
|
||||
}
|
||||
|
||||
onCloseButtonClick() {
|
||||
this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE);
|
||||
}
|
||||
|
||||
getChoices() {
|
||||
return this.categoricalSchemeRegistry.keys().map(s => [s, s]);
|
||||
}
|
||||
|
||||
getSchemes() {
|
||||
return this.categoricalSchemeRegistry.getMap();
|
||||
}
|
||||
|
||||
setHover(hovered) {
|
||||
this.setState({ hovered });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { setColorSchemeAndUnsavedChanges, colorScheme } = this.props;
|
||||
|
||||
return (
|
||||
<div className="slider-container">
|
||||
<div className="component-layer slide-content">
|
||||
<div className="dashboard-builder-sidepane-header">
|
||||
<span>{'Color Settings'}</span>
|
||||
<i
|
||||
className="fa fa-times trigger"
|
||||
onClick={this.onCloseButtonClick}
|
||||
role="none"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="panel-body"
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
<ColorSchemeControl
|
||||
description={t(
|
||||
"Any color palette selected here will override the colors applied to this dashboard's individual charts",
|
||||
)}
|
||||
label={t('Color Scheme')}
|
||||
name="color_scheme"
|
||||
onChange={setColorSchemeAndUnsavedChanges}
|
||||
value={colorScheme}
|
||||
choices={this.getChoices}
|
||||
schemes={this.getSchemes}
|
||||
hovered={this.state.hovered}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ColorComponentPane.propTypes = propTypes;
|
||||
ColorComponentPane.defaultProps = defaultProps;
|
||||
|
||||
export default ColorComponentPane;
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* eslint-env browser */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { getCategoricalSchemeRegistry } from '@superset-ui/color';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import ColorSchemeControl from 'src/explore/components/controls/ColorSchemeControl';
|
||||
|
||||
const propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
colorScheme: PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
colorScheme: undefined,
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
class ColorSchemeControlWrapper extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hovered: false };
|
||||
this.categoricalSchemeRegistry = getCategoricalSchemeRegistry();
|
||||
this.choices = this.categoricalSchemeRegistry.keys().map(s => [s, s]);
|
||||
this.schemes = this.categoricalSchemeRegistry.getMap();
|
||||
}
|
||||
setHover(hovered) {
|
||||
this.setState({ hovered });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { colorScheme } = this.props;
|
||||
return (
|
||||
<ColorSchemeControl
|
||||
description={t(
|
||||
"Any color palette selected here will override the colors applied to this dashboard's individual charts",
|
||||
)}
|
||||
label={t('Color Scheme')}
|
||||
name="color_scheme"
|
||||
onChange={this.props.onChange}
|
||||
value={colorScheme}
|
||||
choices={this.choices}
|
||||
clearable
|
||||
schemes={this.schemes}
|
||||
hovered={this.state.hovered}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ColorSchemeControlWrapper.propTypes = propTypes;
|
||||
ColorSchemeControlWrapper.defaultProps = defaultProps;
|
||||
|
||||
export default ColorSchemeControlWrapper;
|
@ -39,7 +39,6 @@ import getDragDropManager from '../util/getDragDropManager';
|
||||
import findTabIndexByComponentId from '../util/findTabIndexByComponentId';
|
||||
|
||||
import {
|
||||
BUILDER_PANE_TYPE,
|
||||
DASHBOARD_GRID_ID,
|
||||
DASHBOARD_ROOT_ID,
|
||||
DASHBOARD_ROOT_DEPTH,
|
||||
@ -56,7 +55,6 @@ const propTypes = {
|
||||
deleteTopLevelTabs: PropTypes.func.isRequired,
|
||||
editMode: PropTypes.bool.isRequired,
|
||||
showBuilderPane: PropTypes.func.isRequired,
|
||||
builderPaneType: PropTypes.string.isRequired,
|
||||
colorScheme: PropTypes.string,
|
||||
setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
|
||||
handleComponentDrop: PropTypes.func.isRequired,
|
||||
@ -161,7 +159,6 @@ class DashboardBuilder extends React.Component {
|
||||
dashboardLayout,
|
||||
editMode,
|
||||
showBuilderPane,
|
||||
builderPaneType,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
colorScheme,
|
||||
} = this.props;
|
||||
@ -265,11 +262,10 @@ class DashboardBuilder extends React.Component {
|
||||
)}
|
||||
</ParentSize>
|
||||
</div>
|
||||
{editMode && builderPaneType !== BUILDER_PANE_TYPE.NONE && (
|
||||
{editMode && (
|
||||
<BuilderComponentPane
|
||||
topOffset={HEADER_HEIGHT + (topLevelTabs ? TABS_HEIGHT : 0)}
|
||||
showBuilderPane={showBuilderPane}
|
||||
builderPaneType={builderPaneType}
|
||||
setColorSchemeAndUnsavedChanges={setColorSchemeAndUnsavedChanges}
|
||||
colorScheme={colorScheme}
|
||||
/>
|
||||
|
@ -21,20 +21,21 @@ import moment from 'moment';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from '@superset-ui/style';
|
||||
import { ButtonGroup } from 'react-bootstrap';
|
||||
import { CategoricalColorNamespace } from '@superset-ui/color';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import Icon from 'src/components/Icon';
|
||||
|
||||
import HeaderActionsDropdown from './HeaderActionsDropdown';
|
||||
import EditableTitle from '../../components/EditableTitle';
|
||||
import Button from '../../components/Button';
|
||||
import FaveStar from '../../components/FaveStar';
|
||||
import FilterScopeModal from './filterscope/FilterScopeModal';
|
||||
import PublishedStatus from './PublishedStatus';
|
||||
import UndoRedoKeylisteners from './UndoRedoKeylisteners';
|
||||
|
||||
import { chartPropShape } from '../util/propShapes';
|
||||
import {
|
||||
BUILDER_PANE_TYPE,
|
||||
UNDO_LIMIT,
|
||||
SAVE_TYPE_OVERWRITE,
|
||||
DASHBOARD_POSITION_DATA_LIMIT,
|
||||
@ -62,6 +63,7 @@ const propTypes = {
|
||||
customCss: PropTypes.string.isRequired,
|
||||
colorNamespace: PropTypes.string,
|
||||
colorScheme: PropTypes.string,
|
||||
setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
|
||||
isStarred: PropTypes.bool.isRequired,
|
||||
isPublished: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
@ -75,7 +77,6 @@ const propTypes = {
|
||||
editMode: PropTypes.bool.isRequired,
|
||||
setEditMode: PropTypes.func.isRequired,
|
||||
showBuilderPane: PropTypes.func.isRequired,
|
||||
builderPaneType: PropTypes.string.isRequired,
|
||||
updateCss: PropTypes.func.isRequired,
|
||||
logEvent: PropTypes.func.isRequired,
|
||||
hasUnsavedChanges: PropTypes.bool.isRequired,
|
||||
@ -124,10 +125,6 @@ class Header extends React.PureComponent {
|
||||
this.handleChangeText = this.handleChangeText.bind(this);
|
||||
this.handleCtrlZ = this.handleCtrlZ.bind(this);
|
||||
this.handleCtrlY = this.handleCtrlY.bind(this);
|
||||
this.onInsertComponentsButtonClick = this.onInsertComponentsButtonClick.bind(
|
||||
this,
|
||||
);
|
||||
this.onColorsButtonClick = this.onColorsButtonClick.bind(this);
|
||||
this.toggleEditMode = this.toggleEditMode.bind(this);
|
||||
this.forceRefresh = this.forceRefresh.bind(this);
|
||||
this.startPeriodicRender = this.startPeriodicRender.bind(this);
|
||||
@ -162,14 +159,6 @@ class Header extends React.PureComponent {
|
||||
clearTimeout(this.ctrlZTimeout);
|
||||
}
|
||||
|
||||
onInsertComponentsButtonClick() {
|
||||
this.props.showBuilderPane(BUILDER_PANE_TYPE.ADD_COMPONENTS);
|
||||
}
|
||||
|
||||
onColorsButtonClick() {
|
||||
this.props.showBuilderPane(BUILDER_PANE_TYPE.COLORS);
|
||||
}
|
||||
|
||||
handleChangeText(nextText) {
|
||||
const { updateDashboardTitle, onChange } = this.props;
|
||||
if (nextText && this.props.dashboardTitle !== nextText) {
|
||||
@ -340,6 +329,7 @@ class Header extends React.PureComponent {
|
||||
expandedSlices,
|
||||
customCss,
|
||||
colorNamespace,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
colorScheme,
|
||||
onUndo,
|
||||
onRedo,
|
||||
@ -350,7 +340,6 @@ class Header extends React.PureComponent {
|
||||
updateCss,
|
||||
editMode,
|
||||
isPublished,
|
||||
builderPaneType,
|
||||
dashboardInfo,
|
||||
hasUnsavedChanges,
|
||||
isLoading,
|
||||
@ -366,7 +355,6 @@ class Header extends React.PureComponent {
|
||||
const refreshWarning =
|
||||
dashboardInfo.common.conf
|
||||
.SUPERSET_DASHBOARD_PERIODICAL_REFRESH_WARNING_MESSAGE;
|
||||
const popButton = hasUnsavedChanges;
|
||||
|
||||
return (
|
||||
<StyledDashboardHeader className="dashboard-header">
|
||||
@ -399,92 +387,63 @@ class Header extends React.PureComponent {
|
||||
{userCanSaveAs && (
|
||||
<div className="button-container">
|
||||
{editMode && (
|
||||
<>
|
||||
<ButtonGroup className="m-r-5">
|
||||
<Button
|
||||
bsSize="small"
|
||||
onClick={onUndo}
|
||||
disabled={undoLength < 1}
|
||||
bsStyle={this.state.emphasizeUndo ? 'primary' : undefined}
|
||||
>
|
||||
<div title="Undo" className="undo-action fa fa-reply" />
|
||||
<i title="Undo" className="undo-action fa fa-reply" />
|
||||
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
<Button
|
||||
bsSize="small"
|
||||
onClick={onRedo}
|
||||
disabled={redoLength < 1}
|
||||
bsStyle={this.state.emphasizeRedo ? 'primary' : undefined}
|
||||
>
|
||||
<div title="Redo" className="redo-action fa fa-share" />
|
||||
|
||||
<i title="Redo" className="redo-action fa fa-share" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
</ButtonGroup>
|
||||
<Button
|
||||
active={builderPaneType === BUILDER_PANE_TYPE.ADD_COMPONENTS}
|
||||
bsSize="small"
|
||||
onClick={this.onInsertComponentsButtonClick}
|
||||
className="m-r-5"
|
||||
onClick={this.constructor.discardChanges}
|
||||
bsStyle="default"
|
||||
>
|
||||
{t('Components')}
|
||||
{t('Discard Changes')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
<Button
|
||||
active={builderPaneType === BUILDER_PANE_TYPE.COLORS}
|
||||
bsSize="small"
|
||||
onClick={this.onColorsButtonClick}
|
||||
>
|
||||
{t('Colors')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
<FilterScopeModal
|
||||
triggerNode={<Button bsSize="small">{t('Filters')}</Button>}
|
||||
/>
|
||||
)}
|
||||
|
||||
{editMode && hasUnsavedChanges && (
|
||||
<Button
|
||||
bsSize="small"
|
||||
bsStyle={popButton ? 'primary' : undefined}
|
||||
disabled={!hasUnsavedChanges}
|
||||
bsStyle="primary"
|
||||
onClick={this.overwriteDashboard}
|
||||
>
|
||||
{t('Save changes')}
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{editMode && !hasUnsavedChanges && (
|
||||
<Button
|
||||
bsSize="small"
|
||||
onClick={this.toggleEditMode}
|
||||
bsStyle={undefined}
|
||||
disabled={!userCanEdit}
|
||||
>
|
||||
{t('Switch to view mode')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{editMode && (
|
||||
<UndoRedoKeylisteners
|
||||
onUndo={this.handleCtrlZ}
|
||||
onRedo={this.handleCtrlY}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!editMode && !hasUnsavedChanges && (
|
||||
<Button
|
||||
bsSize="small"
|
||||
{!editMode && (
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="action-button"
|
||||
onClick={this.toggleEditMode}
|
||||
bsStyle={popButton ? 'primary' : undefined}
|
||||
disabled={!userCanEdit}
|
||||
>
|
||||
{t('Edit dashboard')}
|
||||
</Button>
|
||||
<Icon name="pencil" />
|
||||
</span>
|
||||
)}
|
||||
|
||||
{this.state.showingPropertiesModal && (
|
||||
@ -492,12 +451,18 @@ class Header extends React.PureComponent {
|
||||
dashboardId={dashboardInfo.id}
|
||||
show={this.state.showingPropertiesModal}
|
||||
onHide={this.hidePropertiesModal}
|
||||
onDashboardSave={updates => {
|
||||
this.props.dashboardInfoChanged({
|
||||
colorScheme={this.props.colorScheme}
|
||||
onSubmit={updates => {
|
||||
const {
|
||||
dashboardInfoChanged,
|
||||
dashboardTitleChanged,
|
||||
} = this.props;
|
||||
dashboardInfoChanged({
|
||||
slug: updates.slug,
|
||||
metadata: JSON.parse(updates.jsonMetadata),
|
||||
});
|
||||
this.props.dashboardTitleChanged(updates.title);
|
||||
setColorSchemeAndUnsavedChanges(updates.colorScheme);
|
||||
dashboardTitleChanged(updates.title);
|
||||
if (updates.slug) {
|
||||
history.pushState(
|
||||
{ event: 'dashboard_properties_changed' },
|
||||
|
@ -18,16 +18,20 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { SupersetClient } from '@superset-ui/connection';
|
||||
import { DropdownButton, MenuItem } from 'react-bootstrap';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import Icon from 'src/components/Icon';
|
||||
|
||||
import CssEditor from './CssEditor';
|
||||
import RefreshIntervalModal from './RefreshIntervalModal';
|
||||
import SaveModal from './SaveModal';
|
||||
import injectCustomCss from '../util/injectCustomCss';
|
||||
import { SAVE_TYPE_NEWDASHBOARD } from '../util/constants';
|
||||
import URLShortLinkModal from '../../components/URLShortLinkModal';
|
||||
import FilterScopeModal from './filterscope/FilterScopeModal';
|
||||
import downloadAsImage from '../../utils/downloadAsImage';
|
||||
import getDashboardUrl from '../util/getDashboardUrl';
|
||||
import { getActiveFilters } from '../util/activeDashboardFilters';
|
||||
@ -38,7 +42,6 @@ const propTypes = {
|
||||
dashboardInfo: PropTypes.object.isRequired,
|
||||
dashboardId: PropTypes.number.isRequired,
|
||||
dashboardTitle: PropTypes.string.isRequired,
|
||||
hasUnsavedChanges: PropTypes.bool.isRequired,
|
||||
customCss: PropTypes.string.isRequired,
|
||||
colorNamespace: PropTypes.string,
|
||||
colorScheme: PropTypes.string,
|
||||
@ -128,7 +131,6 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
customCss,
|
||||
colorNamespace,
|
||||
colorScheme,
|
||||
hasUnsavedChanges,
|
||||
layout,
|
||||
expandedSlices,
|
||||
onSave,
|
||||
@ -145,13 +147,15 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<DropdownButton
|
||||
title=""
|
||||
title={<Icon name="more" />}
|
||||
noCaret
|
||||
id="save-dash-split-button"
|
||||
bsStyle={hasUnsavedChanges ? 'primary' : undefined}
|
||||
bsSize="small"
|
||||
style={{ border: 'none', padding: 0, marginLeft: '4px' }}
|
||||
pullRight
|
||||
>
|
||||
{userCanSave && (
|
||||
<>
|
||||
<SaveModal
|
||||
addSuccessToast={this.props.addSuccessToast}
|
||||
addDangerToast={this.props.addDangerToast}
|
||||
@ -171,46 +175,8 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
triggerNode={<span>{t('Save as')}</span>}
|
||||
canOverwrite={userCanEdit}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasUnsavedChanges && userCanSave && (
|
||||
<div>
|
||||
<MenuItem
|
||||
eventKey="discard"
|
||||
onSelect={HeaderActionsDropdown.discardChanges}
|
||||
>
|
||||
{t('Discard changes')}
|
||||
</MenuItem>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{userCanSave && <MenuItem divider />}
|
||||
|
||||
<MenuItem onClick={forceRefreshAllCharts} disabled={isLoading}>
|
||||
{t('Force refresh dashboard')}
|
||||
</MenuItem>
|
||||
|
||||
<RefreshIntervalModal
|
||||
refreshFrequency={refreshFrequency}
|
||||
refreshLimit={refreshLimit}
|
||||
refreshWarning={refreshWarning}
|
||||
onChange={this.changeRefreshInterval}
|
||||
editMode={editMode}
|
||||
triggerNode={
|
||||
<span>
|
||||
{editMode
|
||||
? t('Set auto-refresh interval')
|
||||
: t('Auto-refresh dashboard')}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
|
||||
{editMode && (
|
||||
<MenuItem onClick={this.props.showPropertiesModal}>
|
||||
{t('Edit dashboard properties')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<URLShortLinkModal
|
||||
url={getDashboardUrl(
|
||||
window.location.pathname,
|
||||
@ -223,14 +189,37 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
isMenuItem
|
||||
triggerNode={<span>{t('Share dashboard')}</span>}
|
||||
/>
|
||||
<MenuItem onClick={forceRefreshAllCharts} disabled={isLoading}>
|
||||
{t('Refresh dashboard')}
|
||||
</MenuItem>
|
||||
<MenuItem divider />
|
||||
<RefreshIntervalModal
|
||||
refreshFrequency={refreshFrequency}
|
||||
refreshLimit={refreshLimit}
|
||||
refreshWarning={refreshWarning}
|
||||
onChange={this.changeRefreshInterval}
|
||||
editMode={editMode}
|
||||
triggerNode={<span>{t('Set auto-refresh interval')}</span>}
|
||||
/>
|
||||
|
||||
{editMode && (
|
||||
<>
|
||||
<FilterScopeModal
|
||||
className="m-r-5"
|
||||
triggerNode={
|
||||
<MenuItem bsSize="small">{t('Set filter mapping')}</MenuItem>
|
||||
}
|
||||
/>
|
||||
<MenuItem onClick={this.props.showPropertiesModal}>
|
||||
{t('Edit dashboard properties')}
|
||||
</MenuItem>
|
||||
<CssEditor
|
||||
triggerNode={<span>{t('Edit CSS')}</span>}
|
||||
initialCss={this.state.css}
|
||||
templates={this.state.cssTemplates}
|
||||
onChange={this.changeCss}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!editMode && (
|
||||
|
@ -1,118 +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.
|
||||
*/
|
||||
/* eslint-env browser */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import NewColumn from './gridComponents/new/NewColumn';
|
||||
import NewDivider from './gridComponents/new/NewDivider';
|
||||
import NewHeader from './gridComponents/new/NewHeader';
|
||||
import NewRow from './gridComponents/new/NewRow';
|
||||
import NewTabs from './gridComponents/new/NewTabs';
|
||||
import NewMarkdown from './gridComponents/new/NewMarkdown';
|
||||
import SliceAdder from '../containers/SliceAdder';
|
||||
import { BUILDER_PANE_TYPE } from '../util/constants';
|
||||
|
||||
export const SUPERSET_HEADER_HEIGHT = 59;
|
||||
|
||||
const propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
isSticky: PropTypes.bool.isRequired,
|
||||
showBuilderPane: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class InsertComponentPane extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
slideDirection: 'slide-out',
|
||||
};
|
||||
|
||||
this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
|
||||
this.openSlicesPane = this.slide.bind(this, 'slide-in');
|
||||
this.closeSlicesPane = this.slide.bind(this, 'slide-out');
|
||||
}
|
||||
|
||||
onCloseButtonClick() {
|
||||
this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE);
|
||||
}
|
||||
|
||||
slide(direction) {
|
||||
this.setState({
|
||||
slideDirection: direction,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={cx('slider-container', this.state.slideDirection)}>
|
||||
<div className="component-layer slide-content">
|
||||
<div className="dashboard-builder-sidepane-header">
|
||||
<span>{t('Insert components')}</span>
|
||||
<i
|
||||
className="fa fa-times trigger"
|
||||
onClick={this.onCloseButtonClick}
|
||||
role="none"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="new-component static"
|
||||
role="none"
|
||||
onClick={this.openSlicesPane}
|
||||
>
|
||||
<div className="new-component-placeholder fa fa-area-chart" />
|
||||
<div className="new-component-label">
|
||||
{t('Your charts & filters')}
|
||||
</div>
|
||||
|
||||
<i className="fa fa-arrow-right trigger" />
|
||||
</div>
|
||||
<NewTabs />
|
||||
<NewRow />
|
||||
<NewColumn />
|
||||
<NewHeader />
|
||||
<NewMarkdown />
|
||||
<NewDivider />
|
||||
</div>
|
||||
<div className="slices-layer slide-content">
|
||||
<div
|
||||
className="dashboard-builder-sidepane-header"
|
||||
onClick={this.closeSlicesPane}
|
||||
role="none"
|
||||
>
|
||||
<i className="fa fa-arrow-left trigger" />
|
||||
<span>{t('Your charts and filters')}</span>
|
||||
</div>
|
||||
<SliceAdder
|
||||
height={
|
||||
this.props.height +
|
||||
(this.props.isSticky ? SUPERSET_HEADER_HEIGHT : 0)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InsertComponentPane.propTypes = propTypes;
|
||||
|
||||
export default InsertComponentPane;
|
@ -27,6 +27,7 @@ import { t } from '@superset-ui/translation';
|
||||
import { SupersetClient } from '@superset-ui/connection';
|
||||
|
||||
import FormLabel from 'src/components/FormLabel';
|
||||
import ColorSchemeControlWrapper from 'src/dashboard/components/ColorSchemeControlWrapper';
|
||||
import getClientErrorObject from '../../utils/getClientErrorObject';
|
||||
import withToasts from '../../messageToasts/enhancers/withToasts';
|
||||
import '../stylesheets/buttons.less';
|
||||
@ -35,14 +36,20 @@ const propTypes = {
|
||||
dashboardId: PropTypes.number.isRequired,
|
||||
show: PropTypes.bool.isRequired,
|
||||
onHide: PropTypes.func,
|
||||
onDashboardSave: PropTypes.func,
|
||||
colorScheme: PropTypes.object,
|
||||
setColorSchemeAndUnsavedChanges: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
addSuccessToast: PropTypes.func.isRequired,
|
||||
onlyApply: PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
onHide: () => {},
|
||||
onDashboardSave: () => {},
|
||||
setColorSchemeAndUnsavedChanges: () => {},
|
||||
onSubmit: () => {},
|
||||
show: false,
|
||||
colorScheme: undefined,
|
||||
onlyApply: false,
|
||||
};
|
||||
|
||||
class PropertiesModal extends React.PureComponent {
|
||||
@ -55,6 +62,7 @@ class PropertiesModal extends React.PureComponent {
|
||||
slug: '',
|
||||
owners: [],
|
||||
json_metadata: '',
|
||||
colorScheme: props.colorScheme,
|
||||
},
|
||||
isDashboardLoaded: false,
|
||||
isAdvancedOpen: false,
|
||||
@ -62,15 +70,19 @@ class PropertiesModal extends React.PureComponent {
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onMetadataChange = this.onMetadataChange.bind(this);
|
||||
this.onOwnersChange = this.onOwnersChange.bind(this);
|
||||
this.save = this.save.bind(this);
|
||||
this.submit = this.submit.bind(this);
|
||||
this.toggleAdvanced = this.toggleAdvanced.bind(this);
|
||||
this.loadOwnerOptions = this.loadOwnerOptions.bind(this);
|
||||
this.handleErrorResponse = this.handleErrorResponse.bind(this);
|
||||
this.onColorSchemeChange = this.onColorSchemeChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchDashboardDetails();
|
||||
}
|
||||
onColorSchemeChange(value) {
|
||||
this.updateFormState('colorScheme', value);
|
||||
}
|
||||
|
||||
onOwnersChange(value) {
|
||||
this.updateFormState('owners', value);
|
||||
@ -155,12 +167,23 @@ class PropertiesModal extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
save(e) {
|
||||
submit(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const { values } = this.state;
|
||||
const { onlyApply } = this.props;
|
||||
const owners = values.owners.map(o => o.value);
|
||||
|
||||
if (onlyApply) {
|
||||
this.props.onSubmit({
|
||||
id: this.props.dashboardId,
|
||||
title: values.dashboard_title,
|
||||
slug: values.slug,
|
||||
jsonMetadata: values.json_metadata,
|
||||
ownerIds: owners,
|
||||
colorScheme: values.colorScheme,
|
||||
});
|
||||
this.props.onHide();
|
||||
} else {
|
||||
SupersetClient.put({
|
||||
endpoint: `/api/v1/dashboard/${this.props.dashboardId}`,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@ -172,22 +195,27 @@ class PropertiesModal extends React.PureComponent {
|
||||
}),
|
||||
}).then(({ json }) => {
|
||||
this.props.addSuccessToast(t('The dashboard has been saved'));
|
||||
this.props.onDashboardSave({
|
||||
this.props.onSubmit({
|
||||
id: this.props.dashboardId,
|
||||
title: json.result.dashboard_title,
|
||||
slug: json.result.slug,
|
||||
jsonMetadata: json.result.json_metadata,
|
||||
ownerIds: json.result.owners,
|
||||
colorScheme: values.colorScheme,
|
||||
});
|
||||
this.props.onHide();
|
||||
}, this.handleErrorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { values, isDashboardLoaded, isAdvancedOpen } = this.state;
|
||||
const { values, isDashboardLoaded, isAdvancedOpen, errors } = this.state;
|
||||
const { onHide, onlyApply } = this.props;
|
||||
|
||||
const saveLabel = onlyApply ? t('Apply') : t('Save');
|
||||
return (
|
||||
<Modal show={this.props.show} onHide={this.props.onHide} bsSize="lg">
|
||||
<form onSubmit={this.save}>
|
||||
<form onSubmit={this.submit}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
<div>
|
||||
@ -249,6 +277,13 @@ class PropertiesModal extends React.PureComponent {
|
||||
)}
|
||||
</p>
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<h3 style={{ marginTop: '1em' }}>{t('Colors')}</h3>
|
||||
<ColorSchemeControlWrapper
|
||||
onChange={this.onColorSchemeChange}
|
||||
colorScheme={values.colorScheme}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
@ -300,11 +335,11 @@ class PropertiesModal extends React.PureComponent {
|
||||
bsSize="sm"
|
||||
bsStyle="primary"
|
||||
className="m-r-5"
|
||||
disabled={this.state.errors.length > 0}
|
||||
disabled={errors.length > 0}
|
||||
>
|
||||
{t('Save')}
|
||||
{saveLabel}
|
||||
</Button>
|
||||
<Button type="button" bsSize="sm" onClick={this.props.onHide}>
|
||||
<Button type="button" bsSize="sm" onClick={onHide}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Dialog
|
||||
|
@ -22,7 +22,8 @@ import Select from 'src/components/Select';
|
||||
import { t } from '@superset-ui/translation';
|
||||
import { Alert, Button } from 'react-bootstrap';
|
||||
|
||||
import ModalTrigger from '../../components/ModalTrigger';
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import FormLabel from 'src/components/FormLabel';
|
||||
|
||||
const propTypes = {
|
||||
triggerNode: PropTypes.node.isRequired,
|
||||
@ -96,7 +97,7 @@ class RefreshIntervalModal extends React.PureComponent {
|
||||
modalTitle={t('Refresh Interval')}
|
||||
modalBody={
|
||||
<div>
|
||||
{t('Choose the refresh frequency for this dashboard')}
|
||||
<FormLabel>{t('Refresh frequency')}</FormLabel>
|
||||
<Select
|
||||
options={options}
|
||||
value={this.state.refreshFrequency}
|
||||
@ -115,10 +116,12 @@ class RefreshIntervalModal extends React.PureComponent {
|
||||
}
|
||||
modalFooter={
|
||||
<>
|
||||
<Button bsStyle="primary" onClick={this.onSave}>
|
||||
<Button bsStyle="primary" bsSize="sm" onClick={this.onSave}>
|
||||
{editMode ? t('Save') : t('Save for this session')}
|
||||
</Button>
|
||||
<Button onClick={this.onCancel}>{t('Cancel')}</Button>
|
||||
<Button onClick={this.onCancel} bsSize="sm">
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
@ -20,7 +20,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DropdownButton, MenuItem } from 'react-bootstrap';
|
||||
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||
import { List } from 'react-virtualized';
|
||||
import SearchInput, { createFilter } from 'react-search-input';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
@ -60,14 +60,9 @@ const KEYS_TO_SORT = [
|
||||
];
|
||||
|
||||
const MARGIN_BOTTOM = 16;
|
||||
const SIDEPANE_HEADER_HEIGHT = 55;
|
||||
const SIDEPANE_HEADER_HEIGHT = 30;
|
||||
const SLICE_ADDER_CONTROL_HEIGHT = 64;
|
||||
const DEFAULT_CELL_HEIGHT = 136;
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
defaultHeight: DEFAULT_CELL_HEIGHT,
|
||||
fixedWidth: true,
|
||||
});
|
||||
const DEFAULT_CELL_HEIGHT = 112;
|
||||
|
||||
class SliceAdder extends React.Component {
|
||||
static sortByComparator(attr) {
|
||||
@ -91,7 +86,6 @@ class SliceAdder extends React.Component {
|
||||
sortBy: KEYS_TO_SORT.findIndex(item => item.key === 'changed_on'),
|
||||
selectedSliceIdsSet: new Set(props.selectedSliceIds),
|
||||
};
|
||||
|
||||
this.rowRenderer = this.rowRenderer.bind(this);
|
||||
this.searchUpdated = this.searchUpdated.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
@ -159,7 +153,7 @@ class SliceAdder extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
rowRenderer({ key, index, style, parent }) {
|
||||
rowRenderer({ key, index, style }) {
|
||||
const { filteredSlices, selectedSliceIdsSet } = this.state;
|
||||
const cellData = filteredSlices[index];
|
||||
const isSelected = selectedSliceIdsSet.has(cellData.slice_id);
|
||||
@ -190,23 +184,16 @@ class SliceAdder extends React.Component {
|
||||
style={{}}
|
||||
>
|
||||
{({ dragSourceRef }) => (
|
||||
<CellMeasurer
|
||||
cache={cache}
|
||||
columnIndex={0}
|
||||
key={key}
|
||||
parent={parent}
|
||||
rowIndex={index}
|
||||
>
|
||||
<AddSliceCard
|
||||
innerRef={dragSourceRef}
|
||||
style={style}
|
||||
sliceName={cellData.slice_name}
|
||||
lastModified={cellData.changed_on_humanized}
|
||||
visType={cellData.viz_type}
|
||||
datasourceLink={cellData.datasource_link}
|
||||
datasourceUrl={cellData.datasource_url}
|
||||
datasourceName={cellData.datasource_name}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
</CellMeasurer>
|
||||
)}
|
||||
</DragDroppable>
|
||||
);
|
||||
@ -227,7 +214,6 @@ class SliceAdder extends React.Component {
|
||||
onChange={this.searchUpdated}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
/>
|
||||
|
||||
<DropdownButton
|
||||
title={`Sort by ${KEYS_TO_SORT[this.state.sortBy].label}`}
|
||||
onSelect={this.handleSelect}
|
||||
@ -246,8 +232,7 @@ class SliceAdder extends React.Component {
|
||||
width={376}
|
||||
height={slicesListHeight}
|
||||
rowCount={this.state.filteredSlices.length}
|
||||
deferredMeasurementCache={cache}
|
||||
rowHeight={cache.rowHeight}
|
||||
rowHeight={DEFAULT_CELL_HEIGHT}
|
||||
rowRenderer={this.rowRenderer}
|
||||
searchTerm={this.state.searchTerm}
|
||||
sortBy={this.state.sortBy}
|
||||
|
@ -72,9 +72,10 @@ function AddSliceDragPreview({ dragItem, slices, isDragging, currentOffset }) {
|
||||
transform: `translate(${currentOffset.x}px, ${currentOffset.y}px)`,
|
||||
}}
|
||||
sliceName={slice.slice_name}
|
||||
lastModified={slice.modified}
|
||||
lastModified={slice.changed_on_humanized}
|
||||
visType={slice.viz_type}
|
||||
datasourceLink={slice.datasource_link}
|
||||
datasourceUrl={slice.datasource_url}
|
||||
datasourceName={slice.datasource_name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -513,9 +513,11 @@ export default class FilterScopeSelector extends React.PureComponent {
|
||||
</div>
|
||||
|
||||
<div className="dashboard-modal-actions-container">
|
||||
<Button onClick={this.onClose}>{t('Close')}</Button>
|
||||
<Button bsSize="sm" onClick={this.onClose}>
|
||||
{t('Close')}
|
||||
</Button>
|
||||
{showSelector && (
|
||||
<Button bsStyle="primary" onClick={this.onSave}>
|
||||
<Button bsSize="sm" bsStyle="primary" onClick={this.onSave}>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
)}
|
||||
|
@ -36,7 +36,6 @@ function mapStateToProps({ dashboardLayout: undoableLayout, dashboardState }) {
|
||||
editMode: dashboardState.editMode,
|
||||
showBuilderPane: dashboardState.showBuilderPane,
|
||||
directPathToChild: dashboardState.directPathToChild,
|
||||
builderPaneType: dashboardState.builderPaneType,
|
||||
colorScheme: dashboardState.colorScheme,
|
||||
};
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
fetchFaveStar,
|
||||
saveFaveStar,
|
||||
savePublished,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
fetchCharts,
|
||||
updateCss,
|
||||
onChange,
|
||||
@ -83,7 +84,8 @@ function mapStateToProps({
|
||||
hasUnsavedChanges: !!dashboardState.hasUnsavedChanges,
|
||||
maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded,
|
||||
editMode: !!dashboardState.editMode,
|
||||
builderPaneType: dashboardState.builderPaneType,
|
||||
slug: dashboardInfo.slug,
|
||||
metadata: dashboardInfo.metadata,
|
||||
};
|
||||
}
|
||||
|
||||
@ -97,6 +99,7 @@ function mapDispatchToProps(dispatch) {
|
||||
onRedo: redoLayoutAction,
|
||||
setEditMode,
|
||||
showBuilderPane,
|
||||
setColorSchemeAndUnsavedChanges,
|
||||
fetchFaveStar,
|
||||
saveFaveStar,
|
||||
savePublished,
|
||||
|
@ -35,7 +35,6 @@ import {
|
||||
SET_DIRECT_PATH,
|
||||
SET_FOCUSED_FILTER_FIELD,
|
||||
} from '../actions/dashboardState';
|
||||
import { BUILDER_PANE_TYPE } from '../util/constants';
|
||||
|
||||
export default function dashboardStateReducer(state = {}, action) {
|
||||
const actionHandlers = {
|
||||
@ -70,9 +69,6 @@ export default function dashboardStateReducer(state = {}, action) {
|
||||
return {
|
||||
...state,
|
||||
editMode: action.editMode,
|
||||
builderPaneType: action.editMode
|
||||
? BUILDER_PANE_TYPE.ADD_COMPONENTS
|
||||
: BUILDER_PANE_TYPE.NONE,
|
||||
};
|
||||
},
|
||||
[SET_MAX_UNDO_HISTORY_EXCEEDED]() {
|
||||
@ -80,7 +76,7 @@ export default function dashboardStateReducer(state = {}, action) {
|
||||
return { ...state, maxUndoHistoryExceeded };
|
||||
},
|
||||
[SHOW_BUILDER_PANE]() {
|
||||
return { ...state, builderPaneType: action.builderPaneType };
|
||||
return { ...state };
|
||||
},
|
||||
[SET_COLOR_SCHEME]() {
|
||||
return {
|
||||
@ -108,7 +104,6 @@ export default function dashboardStateReducer(state = {}, action) {
|
||||
hasUnsavedChanges: false,
|
||||
maxUndoHistoryExceeded: false,
|
||||
editMode: false,
|
||||
builderPaneType: BUILDER_PANE_TYPE.NONE,
|
||||
updatedColorScheme: false,
|
||||
};
|
||||
},
|
||||
|
@ -31,7 +31,6 @@ import { getParam } from '../../modules/utils';
|
||||
import { applyDefaultFormData } from '../../explore/store';
|
||||
import { buildActiveFilters } from '../util/activeDashboardFilters';
|
||||
import {
|
||||
BUILDER_PANE_TYPE,
|
||||
DASHBOARD_HEADER_ID,
|
||||
GRID_DEFAULT_CHART_WIDTH,
|
||||
GRID_COLUMN_COUNT,
|
||||
@ -301,10 +300,6 @@ export default function (bootstrapData) {
|
||||
colorScheme: dashboard.metadata.color_scheme,
|
||||
editMode: dashboard.dash_edit_perm && editMode,
|
||||
isPublished: dashboard.published,
|
||||
builderPaneType:
|
||||
dashboard.dash_edit_perm && editMode
|
||||
? BUILDER_PANE_TYPE.ADD_COMPONENTS
|
||||
: BUILDER_PANE_TYPE.NONE,
|
||||
hasUnsavedChanges: false,
|
||||
maxUndoHistoryExceeded: false,
|
||||
},
|
||||
|
@ -94,11 +94,13 @@
|
||||
.chart-card {
|
||||
border: 1px solid @gray-light;
|
||||
font-weight: @font-weight-light;
|
||||
padding: 16px;
|
||||
margin: 0 16px 16px 16px;
|
||||
padding: 8px;
|
||||
margin: 0 8px 8px 8px;
|
||||
position: relative;
|
||||
cursor: move;
|
||||
background: fade(@lightest, @opacity-medium-light);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background: @gray-bg;
|
||||
@ -147,6 +149,7 @@
|
||||
|
||||
.slice-adder-container {
|
||||
position: relative;
|
||||
background-color: white;
|
||||
min-height: 200px; /* for loader positioning */
|
||||
|
||||
.error-message {
|
||||
@ -185,18 +188,4 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.color-scheme-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.color-scheme-container li {
|
||||
flex-basis: 9px;
|
||||
height: 10px;
|
||||
margin: 9px 1px;
|
||||
}
|
||||
}
|
||||
|
@ -115,19 +115,6 @@ body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
& > :nth-child(3) {
|
||||
border-radius: @border-radius-normal 0px 0px @border-radius-normal;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
& > :nth-child(4) {
|
||||
border-radius: 0px @border-radius-normal @border-radius-normal 0px;
|
||||
}
|
||||
|
||||
& > :not(:nth-child(3)):not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,6 @@ export const SAVE_TYPE_NEWDASHBOARD = 'newDashboard';
|
||||
// could be overwritten by server-side config
|
||||
export const DASHBOARD_POSITION_DATA_LIMIT = 65535;
|
||||
|
||||
// Dashboard pane types
|
||||
export const BUILDER_PANE_TYPE = {
|
||||
NONE: 'NONE',
|
||||
ADD_COMPONENTS: 'ADD_COMPONENTS',
|
||||
COLORS: 'COLORS',
|
||||
};
|
||||
|
||||
// filter indicators display length
|
||||
export const FILTER_INDICATORS_DISPLAY_LENGTH = 3;
|
||||
|
||||
|
@ -102,7 +102,6 @@ export const dashboardStatePropShape = PropTypes.shape({
|
||||
expandedSlices: PropTypes.object,
|
||||
editMode: PropTypes.bool,
|
||||
isPublished: PropTypes.bool.isRequired,
|
||||
builderPaneType: PropTypes.string.isRequired,
|
||||
colorNamespace: PropTypes.string,
|
||||
colorScheme: PropTypes.string,
|
||||
updatedColorScheme: PropTypes.bool,
|
||||
|
@ -22,6 +22,7 @@ import { isFunction } from 'lodash';
|
||||
import { CreatableSelect } from 'src/components/Select';
|
||||
import ControlHeader from '../ControlHeader';
|
||||
import TooltipWrapper from '../../../components/TooltipWrapper';
|
||||
import './ColorSchemeControl.less';
|
||||
|
||||
const propTypes = {
|
||||
description: PropTypes.string,
|
||||
@ -29,6 +30,7 @@ const propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
clearable: PropTypes.bool,
|
||||
default: PropTypes.string,
|
||||
choices: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.array),
|
||||
@ -41,6 +43,7 @@ const propTypes = {
|
||||
const defaultProps = {
|
||||
choices: [],
|
||||
schemes: {},
|
||||
clearable: false,
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
@ -111,7 +114,7 @@ export default class ColorSchemeControl extends React.PureComponent {
|
||||
options,
|
||||
value: this.props.value,
|
||||
autosize: false,
|
||||
clearable: false,
|
||||
clearable: this.props.clearable,
|
||||
onChange: this.onChange,
|
||||
optionRenderer: this.renderOption,
|
||||
valueRenderer: this.renderOption,
|
||||
|
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
.color-scheme-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
li {
|
||||
flex-basis: 9px;
|
||||
height: 10px;
|
||||
margin: 9px 1px;
|
||||
}
|
||||
}
|
@ -61,20 +61,6 @@
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.color-scheme-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
li {
|
||||
flex-basis: 9px;
|
||||
height: 10px;
|
||||
margin: 9px 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.control-panel-section {
|
||||
.panel-body {
|
||||
margin-left: 15px;
|
||||
|
@ -495,9 +495,8 @@ class DashboardList extends React.PureComponent<Props, State> {
|
||||
{dashboardToEdit && (
|
||||
<PropertiesModal
|
||||
dashboardId={dashboardToEdit.id}
|
||||
onDashboardSave={this.handleDashboardEdit}
|
||||
onHide={() => this.setState({ dashboardToEdit: null })}
|
||||
show
|
||||
onSubmit={this.handleDashboardEdit}
|
||||
/>
|
||||
)}
|
||||
<ListView
|
||||
|
@ -234,6 +234,7 @@ table,
|
||||
& > li > a:hover,
|
||||
& > li > a:focus {
|
||||
background-image: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,10 +152,9 @@ class DashboardDAO(BaseDAO):
|
||||
key: v for key, v in default_filters_data.items() if int(key) in slice_ids
|
||||
}
|
||||
md["default_filters"] = json.dumps(applicable_filters)
|
||||
md["color_scheme"] = data.get("color_scheme")
|
||||
if data.get("color_namespace"):
|
||||
md["color_namespace"] = data.get("color_namespace")
|
||||
if data.get("color_scheme"):
|
||||
md["color_scheme"] = data.get("color_scheme")
|
||||
if data.get("label_colors"):
|
||||
md["label_colors"] = data.get("label_colors")
|
||||
dashboard.json_metadata = json.dumps(md)
|
||||
|
@ -104,7 +104,7 @@ class DashboardJSONMetadataSchema(Schema):
|
||||
default_filters = fields.Str()
|
||||
stagger_refresh = fields.Boolean()
|
||||
stagger_time = fields.Integer()
|
||||
color_scheme = fields.Str()
|
||||
color_scheme = fields.Str(allow_none=True)
|
||||
label_colors = fields.Dict()
|
||||
|
||||
|
||||
|
@ -84,6 +84,7 @@ class SliceAsync(SliceModelView): # pylint: disable=too-many-ancestors
|
||||
"creator",
|
||||
"datasource_id",
|
||||
"datasource_link",
|
||||
"datasource_url",
|
||||
"datasource_name_text",
|
||||
"datasource_type",
|
||||
"description",
|
||||
|
Loading…
Reference in New Issue
Block a user