Browse Source

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

Update add panel flow
Torkel Ödegaard 6 years ago
parent
commit
80ccea3d85

+ 23 - 0
public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.test.tsx

@@ -0,0 +1,23 @@
+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();
+  });
+});

+ 61 - 39
public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx

@@ -1,12 +1,20 @@
+// 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>

+ 42 - 5
public/app/features/dashboard/components/AddPanelWidget/_AddPanelWidget.scss

@@ -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;
-  }
 }

+ 86 - 0
public/app/features/dashboard/components/AddPanelWidget/__snapshots__/AddPanelWidget.test.tsx.snap

@@ -0,0 +1,86 @@
+// 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 - 2
public/app/features/dashboard/panel_editor/PanelEditor.tsx

@@ -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,
       })
     );

+ 20 - 1
public/app/features/dashboard/panel_editor/VisualizationTab.tsx

@@ -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);

+ 1 - 1
public/img/icons_dark_theme/icon_advanced.svg

@@ -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>

+ 1 - 1
public/img/icons_dark_theme/icon_advanced_active.svg

@@ -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_);}

+ 1 - 1
public/img/icons_dark_theme/icon_alerting.svg

@@ -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>

+ 1 - 1
public/img/icons_dark_theme/icon_alerting_active.svg

@@ -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>

+ 1 - 1
public/img/icons_dark_theme/icon_query.svg

@@ -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>

+ 1 - 1
public/img/icons_dark_theme/icon_query_active.svg

@@ -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_);}

+ 1 - 1
public/img/icons_dark_theme/icon_visualize.svg

@@ -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"/>

+ 1 - 1
public/img/icons_dark_theme/icon_visualize_active.svg

@@ -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>

+ 1 - 1
public/sass/base/_icons.scss

@@ -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');
   }