Commit 80ccea3d by Torkel Ödegaard Committed by GitHub

Merge pull request #15254 from grafana/update-add-panel-flow

Update add panel flow
parents a0bd0221 096751b6
import React from 'react';
import { shallow } from 'enzyme';
import { AddPanelWidget, Props } from './AddPanelWidget';
import { DashboardModel, PanelModel } from '../../state';
const setup = (propOverrides?: object) => {
const props: Props = {
dashboard: {} as DashboardModel,
panel: {} as PanelModel,
};
Object.assign(props, propOverrides);
return shallow(<AddPanelWidget {...props} />);
};
describe('Render', () => {
it('should render component', () => {
const wrapper = setup();
expect(wrapper).toMatchSnapshot();
});
});
// Libraries
import React from 'react';
import _ from 'lodash';
// Utils
import config from 'app/core/config';
import { PanelModel } from '../../state/PanelModel';
import { DashboardModel } from '../../state/DashboardModel';
import store from 'app/core/store';
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
import { updateLocation } from 'app/core/actions';
// Store
import { store as reduxStore } from 'app/store/store';
import { updateLocation } from 'app/core/actions';
// Types
import { PanelModel } from '../../state';
import { DashboardModel } from '../../state';
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
import { LocationUpdate } from 'app/types';
export interface Props {
panel: PanelModel;
......@@ -46,6 +54,7 @@ export class AddPanelWidget extends React.Component<Props, State> {
copiedPanels.push(pluginCopy);
}
}
return _.sortBy(copiedPanels, 'sort');
}
......@@ -54,28 +63,7 @@ export class AddPanelWidget extends React.Component<Props, State> {
this.props.dashboard.removePanel(this.props.dashboard.panels[0]);
}
copyButton(panel) {
return (
<button className="btn-inverse btn" onClick={() => this.onPasteCopiedPanel(panel)} title={panel.name}>
Paste copied Panel
</button>
);
}
moveToEdit(panel) {
reduxStore.dispatch(
updateLocation({
query: {
panelId: panel.id,
edit: true,
fullscreen: true,
},
partial: true,
})
);
}
onCreateNewPanel = () => {
onCreateNewPanel = (tab = 'queries') => {
const dashboard = this.props.dashboard;
const { gridPos } = this.props.panel;
......@@ -88,7 +76,21 @@ export class AddPanelWidget extends React.Component<Props, State> {
dashboard.addPanel(newPanel);
dashboard.removePanel(this.props.panel);
this.moveToEdit(newPanel);
const location: LocationUpdate = {
query: {
panelId: newPanel.id,
edit: true,
fullscreen: true,
},
partial: true,
};
if (tab === 'visualization') {
location.query.tab = 'visualization';
location.query.openVizPicker = true;
}
reduxStore.dispatch(updateLocation(location));
};
onPasteCopiedPanel = panelPluginInfo => {
......@@ -125,30 +127,50 @@ export class AddPanelWidget extends React.Component<Props, State> {
dashboard.removePanel(this.props.panel);
};
render() {
let addCopyButton;
renderOptionLink = (icon, text, onClick) => {
return (
<div>
<a href="#" onClick={onClick} className="add-panel-widget__link btn btn-inverse">
<div className="add-panel-widget__icon">
<i className={`gicon gicon-${icon}`} />
</div>
<span>{text}</span>
</a>
</div>
);
};
if (this.state.copiedPanelPlugins.length === 1) {
addCopyButton = this.copyButton(this.state.copiedPanelPlugins[0]);
}
render() {
const { copiedPanelPlugins } = this.state;
return (
<div className="panel-container add-panel-widget-container">
<div className="add-panel-widget">
<div className="add-panel-widget__header grid-drag-handle">
<i className="gicon gicon-add-panel" />
<span className="add-panel-widget__title">New Panel</span>
<button className="add-panel-widget__close" onClick={this.handleCloseAddPanel}>
<i className="fa fa-close" />
</button>
</div>
<div className="add-panel-widget__btn-container">
<button className="btn-success btn btn-large" onClick={this.onCreateNewPanel}>
Edit Panel
</button>
{addCopyButton}
<button className="btn-inverse btn" onClick={this.onCreateNewRow}>
Add Row
</button>
<div className="add-panel-widget__create">
{this.renderOptionLink('queries', 'Add Query', this.onCreateNewPanel)}
{this.renderOptionLink('visualization', 'Choose Visualization', () =>
this.onCreateNewPanel('visualization')
)}
</div>
<div className="add-panel-widget__actions">
<button className="btn btn-inverse add-panel-widget__action" onClick={this.onCreateNewRow}>Convert to row</button>
{copiedPanelPlugins.length === 1 && (
<button
className="btn btn-inverse add-panel-widget__action"
onClick={() => this.onPasteCopiedPanel(copiedPanelPlugins[0])}
>
Paste copied panel
</button>
)}
</div>
</div>
</div>
</div>
......
......@@ -14,6 +14,9 @@
align-items: center;
width: 100%;
cursor: move;
background: $page-header-bg;
box-shadow: $page-header-shadow;
border-bottom: 1px solid $page-header-border-color;
.gicon {
font-size: 30px;
......@@ -26,6 +29,29 @@
}
}
.add-panel-widget__title {
font-size: $font-size-md;
font-weight: $font-weight-semi-bold;
margin-right: $spacer*2;
}
.add-panel-widget__link {
margin: 0 8px;
width: 154px;
}
.add-panel-widget__icon {
margin-bottom: 8px;
.gicon {
color: white;
height: 44px;
width: 53px;
position: relative;
left: 5px;
}
}
.add-panel-widget__close {
margin-left: auto;
background-color: transparent;
......@@ -34,14 +60,25 @@
margin-right: -10px;
}
.add-panel-widget__create {
display: inherit;
margin-bottom: 24px;
// this is to have the big button appear centered
margin-top: 55px;
}
.add-panel-widget__actions {
display: inherit;
}
.add-panel-widget__action {
margin: 0 4px;
}
.add-panel-widget__btn-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
flex-direction: column;
.btn {
margin-bottom: 10px;
}
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<div
className="panel-container add-panel-widget-container"
>
<div
className="add-panel-widget"
>
<div
className="add-panel-widget__header grid-drag-handle"
>
<i
className="gicon gicon-add-panel"
/>
<span
className="add-panel-widget__title"
>
New Panel
</span>
<button
className="add-panel-widget__close"
onClick={[Function]}
>
<i
className="fa fa-close"
/>
</button>
</div>
<div
className="add-panel-widget__btn-container"
>
<div
className="add-panel-widget__create"
>
<div>
<a
className="add-panel-widget__link btn btn-inverse"
href="#"
onClick={[Function]}
>
<div
className="add-panel-widget__icon"
>
<i
className="gicon gicon-queries"
/>
</div>
<span>
Add Query
</span>
</a>
</div>
<div>
<a
className="add-panel-widget__link btn btn-inverse"
href="#"
onClick={[Function]}
>
<div
className="add-panel-widget__icon"
>
<i
className="gicon gicon-visualization"
/>
</div>
<span>
Choose Visualization
</span>
</a>
</div>
</div>
<div
className="add-panel-widget__actions"
>
<button
className="btn btn-inverse add-panel-widget__action"
onClick={[Function]}
>
Convert to row
</button>
</div>
</div>
</div>
</div>
`;
......@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import classNames from 'classnames';
import { QueriesTab } from './QueriesTab';
import { VisualizationTab } from './VisualizationTab';
import VisualizationTab from './VisualizationTab';
import { GeneralTab } from './GeneralTab';
import { AlertTab } from '../../alerting/AlertTab';
......@@ -38,7 +38,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
onChangeTab = (tab: PanelEditorTab) => {
store.dispatch(
updateLocation({
query: { tab: tab.id },
query: { tab: tab.id, openVizPicker: null },
partial: true,
})
);
......
......@@ -3,6 +3,9 @@ import React, { PureComponent } from 'react';
// Utils & Services
import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
import { connectWithStore } from 'app/core/utils/connectWithReduxStore';
import { StoreState } from 'app/types';
import { updateLocation } from 'app/core/actions';
// Components
import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
......@@ -21,6 +24,8 @@ interface Props {
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPlugin) => void;
updateLocation: typeof updateLocation;
urlOpenVizPicker: boolean;
}
interface State {
......@@ -38,7 +43,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
super(props);
this.state = {
isVizPickerOpen: false,
isVizPickerOpen: this.props.urlOpenVizPicker,
searchQuery: '',
scrollTop: 0,
};
......@@ -149,6 +154,10 @@ export class VisualizationTab extends PureComponent<Props, State> {
};
onCloseVizPicker = () => {
if (this.props.urlOpenVizPicker) {
this.props.updateLocation({ query: { openVizPicker: null }, partial: true });
}
this.setState({ isVizPickerOpen: false });
};
......@@ -236,3 +245,13 @@ export class VisualizationTab extends PureComponent<Props, State> {
);
}
}
const mapStateToProps = (state: StoreState) => ({
urlOpenVizPicker: !!state.location.query.openVizPicker
});
const mapDispatchToProps = {
updateLocation
};
export default connectWithStore(VisualizationTab, mapStateToProps, mapDispatchToProps);
......@@ -4,7 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0A0A0C;}
.st0{fill:#161719;}
.st1{fill:#E3E2E2;}
</style>
<g>
......
......@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#0A0A0C;}
.st1{fill:#161719;}
.st2{fill:url(#SVGID_2_);}
.st3{fill:url(#SVGID_3_);}
.st4{fill:url(#SVGID_4_);}
......
......@@ -4,7 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0A0A0C;}
.st0{fill:#161719;}
.st1{fill:#E3E2E2;}
</style>
<g>
......
......@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#0A0A0C;}
.st1{fill:#161719;}
.st2{fill:url(#SVGID_2_);}
.st3{fill:url(#SVGID_3_);}
</style>
......
......@@ -4,7 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0A0A0C;}
.st0{fill:#161719;}
.st1{fill:#E3E2E2;}
</style>
<g>
......
......@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#0A0A0C;}
.st1{fill:#161719;}
.st2{fill:url(#SVGID_2_);}
.st3{fill:url(#SVGID_3_);}
.st4{fill:url(#SVGID_4_);}
......
......@@ -4,7 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0A0A0C;}
.st0{fill:#161719;}
.st1{fill:#E3E2E2;}
</style>
<path class="st0" d="M94.3,50C94.3,25.6,74.4,5.7,50,5.7S5.7,25.6,5.7,50S25.6,94.3,50,94.3S94.3,74.4,94.3,50z"/>
......
......@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#0A0A0C;}
.st1{fill:#161719;}
.st2{fill:url(#SVGID_2_);}
.st3{fill:url(#SVGID_3_);}
</style>
......
......@@ -212,7 +212,7 @@
padding-right: 5px;
}
.panel-editor-tabs {
.panel-editor-tabs, .add-panel-widget__icon {
.gicon-advanced-active {
background-image: url('../img/icons_#{$theme-name}_theme/icon_advanced_active.svg');
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment