Browse Source

wip: panel-header: More merge conflicts

Johannes Schill 7 years ago
parent
commit
6c0c1254fe

+ 1 - 1
public/app/features/dashboard/dashgrid/DashboardPanel.tsx

@@ -115,7 +115,6 @@ export class DashboardPanel extends PureComponent<Props, State> {
     const { pluginExports } = this.state;
     const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
     const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
-
     // this might look strange with these classes that change when edit, but
     // I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
     return (
@@ -126,6 +125,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
             withMenuOptions={pluginExports.withMenuOptions}
             panel={this.props.panel}
             dashboard={this.props.dashboard}
+            moduleMenu={pluginExports.moduleMenu}
           />
         </div>
         {this.props.panel.isEditing && (

+ 15 - 11
public/app/features/dashboard/dashgrid/DataPanel.tsx

@@ -1,11 +1,9 @@
 // Library
 import React, { Component } from 'react';
 
-// Services
-import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
-
 // Types
 import { TimeRange, LoadingState, DataQueryOptions, DataQueryResponse, TimeSeries } from 'app/types';
+import { DataSourceApi } from 'app/types/series';
 
 interface RenderProps {
   loading: LoadingState;
@@ -13,7 +11,7 @@ interface RenderProps {
 }
 
 export interface Props {
-  datasource: string | null;
+  dataSourceApi: DataSourceApi;
   queries: any[];
   panelId?: number;
   dashboardId?: number;
@@ -21,6 +19,7 @@ export interface Props {
   timeRange?: TimeRange;
   refreshCounter: number;
   children: (r: RenderProps) => JSX.Element;
+  onIssueQueryResponse: any;
 }
 
 export interface State {
@@ -60,13 +59,19 @@ export class DataPanel extends Component<Props, State> {
   }
 
   hasPropsChanged(prevProps: Props) {
-    return this.props.refreshCounter !== prevProps.refreshCounter || this.props.isVisible !== prevProps.isVisible;
+    const { refreshCounter, isVisible, dataSourceApi } = this.props;
+
+    return (
+      refreshCounter !== prevProps.refreshCounter ||
+      isVisible !== prevProps.isVisible ||
+      dataSourceApi !== prevProps.dataSourceApi
+    );
   }
 
   issueQueries = async () => {
-    const { isVisible, queries, datasource, panelId, dashboardId, timeRange } = this.props;
+    const { isVisible, queries, panelId, dashboardId, timeRange, dataSourceApi } = this.props;
 
-    if (!isVisible) {
+    if (!isVisible || !dataSourceApi) {
       return;
     }
 
@@ -78,9 +83,6 @@ export class DataPanel extends Component<Props, State> {
     this.setState({ loading: LoadingState.Loading });
 
     try {
-      const dataSourceSrv = getDatasourceSrv();
-      const ds = await dataSourceSrv.get(datasource);
-
       const queryOptions: DataQueryOptions = {
         timezone: 'browser',
         panelId: panelId,
@@ -96,7 +98,7 @@ export class DataPanel extends Component<Props, State> {
       };
 
       console.log('Issuing DataPanel query', queryOptions);
-      const resp = await ds.query(queryOptions);
+      const resp = await dataSourceApi.query(queryOptions);
       console.log('Issuing DataPanel query Resp', resp);
 
       this.setState({
@@ -104,6 +106,8 @@ export class DataPanel extends Component<Props, State> {
         response: resp,
         isFirstLoad: false,
       });
+
+      this.props.onIssueQueryResponse(resp.data);
     } catch (err) {
       console.log('Loading error', err);
       this.setState({ loading: LoadingState.Error, isFirstLoad: false });

+ 54 - 11
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -3,43 +3,62 @@ import React, { ComponentClass, PureComponent } from 'react';
 
 // Services
 import { getTimeSrv } from '../time_srv';
+import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
 
 // Components
 import { PanelHeader } from './PanelHeader/PanelHeader';
 import { DataPanel } from './DataPanel';
+import { PanelHeaderMenu } from './PanelHeader/PanelHeaderMenu';
 
 // Types
 import { PanelModel } from '../panel_model';
 import { DashboardModel } from '../dashboard_model';
-import { TimeRange, PanelProps } from 'app/types';
+import { TimeRange, PanelProps, TimeSeries } from 'app/types';
+import { DataSourceApi } from 'app/types/series';
 
 export interface PanelChromeProps {
   panel: PanelModel;
   dashboard: DashboardModel;
   component: ComponentClass<PanelProps>;
-  withMenuOptions: any;
+  withMenuOptions?: (c: typeof PanelHeaderMenu, p: PanelModel) => typeof PanelHeaderMenu;
+  moduleMenu?: any;
 }
 
 export interface PanelChromeState {
   refreshCounter: number;
   renderCounter: number;
   timeRange?: TimeRange;
+  timeSeries?: TimeSeries[];
+  dataSourceApi?: DataSourceApi;
 }
 
 export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeState> {
   constructor(props) {
     super(props);
-
     this.state = {
       refreshCounter: 0,
       renderCounter: 0,
     };
   }
 
-  componentDidMount() {
+  async componentDidMount() {
+    const { panel } = this.props;
+    const { datasource } = panel;
+
     this.props.panel.events.on('refresh', this.onRefresh);
     this.props.panel.events.on('render', this.onRender);
     this.props.dashboard.panelInitialized(this.props.panel);
+
+    try {
+      const dataSourceSrv = getDatasourceSrv();
+      const dataSourceApi = await dataSourceSrv.get(datasource);
+      this.setState(prevState => ({
+        ...prevState,
+        dataSourceApi,
+      }));
+    } catch (err) {
+      console.log('Datasource loading error', err);
+    }
   }
 
   componentWillUnmount() {
@@ -50,10 +69,11 @@ export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeStat
     const timeSrv = getTimeSrv();
     const timeRange = timeSrv.timeRange();
 
-    this.setState({
+    this.setState(prevState => ({
+      ...prevState,
       refreshCounter: this.state.refreshCounter + 1,
       timeRange: timeRange,
-    });
+    }));
   };
 
   onRender = () => {
@@ -63,27 +83,50 @@ export class PanelChrome extends PureComponent<PanelChromeProps, PanelChromeStat
     });
   };
 
+  onIssueQueryResponse = (timeSeries: any) => {
+    this.setState(prevState => ({
+      ...prevState,
+      timeSeries,
+    }));
+  };
+
   get isVisible() {
     return !this.props.dashboard.otherPanelInFullscreen(this.props.panel);
   }
 
   render() {
-    const { panel, dashboard, withMenuOptions } = this.props;
-    const { datasource, targets } = panel;
-    const { timeRange, renderCounter, refreshCounter } = this.state;
+    const { panel, dashboard, moduleMenu } = this.props;
+    const { refreshCounter, timeRange, dataSourceApi, timeSeries, renderCounter } = this.state;
+    const { targets } = panel;
     const PanelComponent = this.props.component;
     console.log('Panel chrome render');
+    // const PanelHeaderMenuComponent: typeof PanelHeaderMenu = withMenuOptions ? withMenuOptions(PanelHeaderMenu, panel) : PanelHeaderMenu;
+    const PanelHeaderMenuComponent = PanelHeaderMenu;
+    const mm = moduleMenu(panel, dataSourceApi, timeSeries);
+    const additionalMenuItems = mm.getAdditionalMenuItems || undefined;
+    const additionalSubMenuItems = mm.getAdditionalSubMenuItems || undefined;
 
+    console.log('panelChrome render');
     return (
       <div className="panel-container">
-        <PanelHeader panel={panel} dashboard={dashboard} withMenuOptions={withMenuOptions} />
+        <PanelHeader title={panel.title}>
+          <PanelHeaderMenuComponent
+            panel={panel}
+            dashboard={dashboard}
+            dataSourceApi={dataSourceApi}
+            additionalMenuItems={additionalMenuItems}
+            additionalSubMenuItems={additionalSubMenuItems}
+            timeSeries={timeSeries}
+          />
+        </PanelHeader>
         <div className="panel-content">
           <DataPanel
-            datasource={datasource}
+            dataSourceApi={dataSourceApi}
             queries={targets}
             timeRange={timeRange}
             isVisible={this.isVisible}
             refreshCounter={refreshCounter}
+            onIssueQueryResponse={this.onIssueQueryResponse}
           >
             {({ loading, timeSeries }) => {
               console.log('panelcrome inner render');

+ 5 - 10
public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx

@@ -1,21 +1,16 @@
 import React, { PureComponent } from 'react';
 import classNames from 'classnames';
-import { PanelModel } from 'app/features/dashboard/panel_model';
-import { DashboardModel } from 'app/features/dashboard/dashboard_model';
-import { PanelHeaderMenu } from './PanelHeaderMenu';
 
 interface PanelHeaderProps {
-  panel: PanelModel;
-  dashboard: DashboardModel;
-  withMenuOptions: any;
+  title: string;
 }
+
 export class PanelHeader extends PureComponent<PanelHeaderProps, any> {
   render() {
-    const { dashboard, withMenuOptions, panel } = this.props;
     const isFullscreen = false;
     const isLoading = false;
     const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen });
-    const PanelHeaderMenuComponent = withMenuOptions ? withMenuOptions(PanelHeaderMenu, panel) : PanelHeaderMenu;
+    const { title } = this.props;
 
     return (
       <div className={panelHeaderClass}>
@@ -34,10 +29,10 @@ export class PanelHeader extends PureComponent<PanelHeaderProps, any> {
           <div className="panel-title">
             <span className="icon-gf panel-alert-icon" />
             <span className="panel-title-text" data-toggle="dropdown">
-              {this.props.panel.title} <span className="fa fa-caret-down panel-menu-toggle" />
+              {title} <span className="fa fa-caret-down panel-menu-toggle" />
             </span>
 
-            <PanelHeaderMenuComponent panelId={panel.id} dashboard={dashboard} />
+            {this.props.children}
             <span className="panel-time-info">
               <i className="fa fa-clock-o" /> 4m
             </span>

+ 9 - 12
public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx

@@ -1,28 +1,25 @@
 import React, { PureComponent } from 'react';
 import { DashboardModel } from 'app/features/dashboard/dashboard_model';
+import { PanelModel } from 'app/features/dashboard/panel_model';
 import { PanelHeaderMenuItem, PanelHeaderMenuItemProps } from './PanelHeaderMenuItem';
 import { getPanelMenu } from 'app/features/dashboard/utils/panel_menu';
+import { DataSourceApi } from 'app/types/series';
+import { TimeSeries } from 'app/types';
 
 export interface PanelHeaderMenuProps {
-  panelId: number;
+  panel: PanelModel;
   dashboard: DashboardModel;
-  datasource: any;
+  dataSourceApi: DataSourceApi;
   additionalMenuItems?: PanelHeaderMenuItemProps[];
   additionalSubMenuItems?: PanelHeaderMenuItemProps[];
+  timeSeries?: TimeSeries[];
 }
 
 export class PanelHeaderMenu extends PureComponent<PanelHeaderMenuProps, any> {
-  getPanel = () => {
-    // Pass in panel as prop instead?
-    const { panelId, dashboard } = this.props;
-    const panelInfo = dashboard.getPanelInfoById(panelId);
-    return panelInfo.panel;
-  };
-
   renderItems = (menu: PanelHeaderMenuItemProps[], isSubMenu = false) => {
     return (
       <ul className="dropdown-menu dropdown-menu--menu panel-menu" role={isSubMenu ? '' : 'menu'}>
-        {menu.map((menuItem, idx) => {
+        {menu.map((menuItem, idx: number) => {
           return (
             <PanelHeaderMenuItem
               key={idx} // TODO: Fix proper key
@@ -42,8 +39,8 @@ export class PanelHeaderMenu extends PureComponent<PanelHeaderMenuProps, any> {
 
   render() {
     console.log('PanelHeaderMenu render');
-    const { dashboard, additionalMenuItems, additionalSubMenuItems } = this.props;
-    const menu = getPanelMenu(dashboard, this.getPanel(), additionalMenuItems, additionalSubMenuItems);
+    const { dashboard, additionalMenuItems, additionalSubMenuItems, panel } = this.props;
+    const menu = getPanelMenu(dashboard, panel, additionalMenuItems, additionalSubMenuItems);
     return <div className="panel-menu-container dropdown">{this.renderItems(menu)}</div>;
   }
 }

+ 10 - 6
public/app/features/dashboard/utils/panel_menu.ts

@@ -80,9 +80,11 @@ export const getPanelMenu = (
       handleClick: onEditPanelJson,
     });
 
-    additionalSubMenuItems.forEach(item => {
-      menu.push(item);
-    });
+    if (additionalSubMenuItems) {
+      additionalSubMenuItems.forEach(item => {
+        menu.push(item);
+      });
+    }
     return menu;
   };
 
@@ -115,9 +117,11 @@ export const getPanelMenu = (
     shortcut: 'p s',
   });
 
-  additionalMenuItems.forEach(item => {
-    menu.push(item);
-  });
+  if (additionalMenuItems) {
+    additionalMenuItems.forEach(item => {
+      menu.push(item);
+    });
+  }
 
   const subMenu: PanelHeaderMenuItemProps[] = getSubMenu();
 

+ 3 - 0
public/app/plugins/panel/graph2/module.tsx

@@ -7,6 +7,8 @@ import { Switch } from 'app/core/components/Switch/Switch';
 import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
 import { PanelProps, PanelOptionsProps, NullValueMode } from 'app/types';
 
+// import { moduleMenu } from './moduleMenu';
+
 interface Options {
   showBars: boolean;
   showLines: boolean;
@@ -74,3 +76,4 @@ export class GraphOptions extends PureComponent<PanelOptionsProps<Options>> {
 
 export { Graph2 as PanelComponent, GraphOptions as PanelOptionsComponent };
 export { withMenuOptions } from './withMenuOptions';
+export { moduleMenu } from './moduleMenu';

+ 76 - 0
public/app/plugins/panel/graph2/moduleMenu.tsx

@@ -0,0 +1,76 @@
+import config from 'app/core/config';
+import { contextSrv } from 'app/core/services/context_srv';
+import { getExploreUrl } from 'app/core/utils/explore';
+import { updateLocation } from 'app/core/actions';
+import { getTimeSrv } from 'app/features/dashboard/time_srv';
+import { store } from 'app/store/configureStore';
+import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
+import appEvents from 'app/core/app_events';
+
+import {
+  PanelHeaderMenuItemProps,
+  PanelHeaderMenuItemTypes,
+} from 'app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem';
+
+export const moduleMenu = (panel, dataSourceApi, timeSeries) => {
+  const onExploreClick = async () => {
+    const datasourceSrv = getDatasourceSrv();
+    const timeSrv = getTimeSrv();
+    const url = await getExploreUrl(panel, panel.targets, dataSourceApi, datasourceSrv, timeSrv);
+    if (url) {
+      store.dispatch(updateLocation({ path: url }));
+    }
+  };
+
+  const onExportCsv = () => {
+    const model = {} as { seriesList: string };
+    model.seriesList = timeSeries;
+    appEvents.emit('show-modal', {
+      templateHtml: '<export-data-modal data="model.seriesList"></export-data-modal>',
+      model,
+      modalClass: 'modal--narrow',
+    });
+  };
+
+  const getAdditionalMenuItems = () => {
+    const items = [];
+    if (
+      config.exploreEnabled &&
+      contextSrv.isEditor &&
+      dataSourceApi &&
+      (dataSourceApi.meta.explore || dataSourceApi.meta.id === 'mixed')
+    ) {
+      items.push({
+        type: PanelHeaderMenuItemTypes.Link,
+        text: 'Explore',
+        handleClick: onExploreClick,
+        iconClassName: 'fa fa-fw fa-rocket',
+        shortcut: 'x',
+      });
+    }
+    return items;
+  };
+
+  const getAdditionalSubMenuItems = () => {
+    return [
+      {
+        type: PanelHeaderMenuItemTypes.Link,
+        text: 'Hello Sub Menu',
+        handleClick: () => {
+          alert('Hello world from moduleMenu');
+        },
+        shortcut: 'hi',
+      },
+      {
+        type: PanelHeaderMenuItemTypes.Link,
+        text: 'Export CSV',
+        handleClick: onExportCsv,
+      },
+    ] as PanelHeaderMenuItemProps[];
+  };
+
+  return {
+    getAdditionalMenuItems: getAdditionalMenuItems(),
+    getAdditionalSubMenuItems: getAdditionalSubMenuItems(),
+  };
+};

+ 1 - 0
public/app/types/plugins.ts

@@ -14,6 +14,7 @@ export interface PluginExports {
   PanelComponent?: ComponentClass<PanelProps>;
   PanelOptionsComponent: ComponentClass<PanelOptionsProps>;
   withMenuOptions?: any;
+  moduleMenu?: any;
 }
 
 export interface PanelPlugin {