Просмотр исходного кода

Merge branch 'master' into grafana-ui/tooltip

Dominik Prokop 7 лет назад
Родитель
Сommit
1e7f3f2892

+ 6 - 0
docs/sources/installation/debian.md

@@ -47,6 +47,12 @@ Create a file `/etc/apt/sources.list.d/grafana.list` and add the following to it
 deb https://packages.grafana.com/oss/deb stable main
 deb https://packages.grafana.com/oss/deb stable main
 ```
 ```
 
 
+There is a separate repository if you want beta releases.
+
+```bash
+deb https://packages.grafana.com/oss/deb beta main
+```
+
 Use the above line even if you are on Ubuntu or another Debian version. Then add our gpg key. This allows you to install signed packages.
 Use the above line even if you are on Ubuntu or another Debian version. Then add our gpg key. This allows you to install signed packages.
 
 
 ```bash
 ```bash

+ 14 - 0
docs/sources/installation/rpm.md

@@ -76,6 +76,20 @@ sslverify=1
 sslcacert=/etc/pki/tls/certs/ca-bundle.crt
 sslcacert=/etc/pki/tls/certs/ca-bundle.crt
 ```
 ```
 
 
+There is a separate repository if you want beta releases.
+
+```bash
+[grafana]
+name=grafana
+baseurl=https://packages.grafana.com/oss/rpm-beta
+repo_gpgcheck=1
+enabled=1
+gpgcheck=1
+gpgkey=https://packages.grafana.com/gpg.key
+sslverify=1
+sslcacert=/etc/pki/tls/certs/ca-bundle.crt
+```
+
 Then install Grafana via the `yum` command.
 Then install Grafana via the `yum` command.
 
 
 ```bash
 ```bash

+ 10 - 2
packages/grafana-ui/src/components/Tooltip/Popper.tsx

@@ -4,6 +4,11 @@ import { Manager, Popper as ReactPopper } from 'react-popper';
 import Portal from 'app/core/components/Portal/Portal';
 import Portal from 'app/core/components/Portal/Portal';
 import Transition from 'react-transition-group/Transition';
 import Transition from 'react-transition-group/Transition';
 
 
+export enum Themes {
+  Default = 'popper__background--default',
+  Error = 'popper__background--error',
+}
+
 const defaultTransitionStyles = {
 const defaultTransitionStyles = {
   transition: 'opacity 200ms linear',
   transition: 'opacity 200ms linear',
   opacity: 0,
   opacity: 0,
@@ -23,13 +28,16 @@ interface Props extends React.DOMAttributes<HTMLDivElement> {
   content: string | ((props: any) => JSX.Element);
   content: string | ((props: any) => JSX.Element);
   refClassName?: string;
   refClassName?: string;
   referenceElement: PopperJS.ReferenceObject;
   referenceElement: PopperJS.ReferenceObject;
+  theme?: Themes;
 }
 }
 
 
 class Popper extends PureComponent<Props> {
 class Popper extends PureComponent<Props> {
   render() {
   render() {
-    const { renderContent, show, placement, onMouseEnter, onMouseLeave } = this.props;
+    const { renderContent, show, placement, onMouseEnter, onMouseLeave, theme } = this.props;
     const { content } = this.props;
     const { content } = this.props;
 
 
+    const popperBackgroundClassName = 'popper__background' + (theme ? ' ' + theme : '');
+
     return (
     return (
       <Manager>
       <Manager>
         <Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
         <Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
@@ -50,7 +58,7 @@ class Popper extends PureComponent<Props> {
                       data-placement={placement}
                       data-placement={placement}
                       className="popper"
                       className="popper"
                     >
                     >
-                      <div className="popper__background">
+                      <div className={popperBackgroundClassName}>
                         {renderContent(content)}
                         {renderContent(content)}
                         <div ref={arrowProps.ref} data-placement={placement} className="popper__arrow" />
                         <div ref={arrowProps.ref} data-placement={placement} className="popper__arrow" />
                       </div>
                       </div>

+ 6 - 1
packages/grafana-ui/src/components/Tooltip/PopperController.tsx

@@ -1,5 +1,6 @@
 import React from 'react';
 import React from 'react';
 import * as PopperJS from 'popper.js';
 import * as PopperJS from 'popper.js';
+import { Themes } from './Popper';
 
 
 type PopperContent = string | (() => JSX.Element);
 type PopperContent = string | (() => JSX.Element);
 
 
@@ -9,6 +10,7 @@ export interface UsingPopperProps {
   content: PopperContent;
   content: PopperContent;
   children: JSX.Element;
   children: JSX.Element;
   renderContent?: (content: PopperContent) => JSX.Element;
   renderContent?: (content: PopperContent) => JSX.Element;
+  theme?: Themes;
 }
 }
 
 
 type PopperControllerRenderProp = (
 type PopperControllerRenderProp = (
@@ -19,6 +21,7 @@ type PopperControllerRenderProp = (
     placement: PopperJS.Placement;
     placement: PopperJS.Placement;
     content: string | ((props: any) => JSX.Element);
     content: string | ((props: any) => JSX.Element);
     renderContent: (content: any) => any;
     renderContent: (content: any) => any;
+    theme?: Themes;
   }
   }
 ) => JSX.Element;
 ) => JSX.Element;
 
 
@@ -27,6 +30,7 @@ interface Props {
   content: PopperContent;
   content: PopperContent;
   className?: string;
   className?: string;
   children: PopperControllerRenderProp;
   children: PopperControllerRenderProp;
+  theme?: Themes;
 }
 }
 
 
 interface State {
 interface State {
@@ -79,7 +83,7 @@ class PopperController extends React.Component<Props, State> {
   }
   }
 
 
   render() {
   render() {
-    const { children, content } = this.props;
+    const { children, content, theme } = this.props;
     const { show, placement } = this.state;
     const { show, placement } = this.state;
 
 
     return children(this.showPopper, this.hidePopper, {
     return children(this.showPopper, this.hidePopper, {
@@ -87,6 +91,7 @@ class PopperController extends React.Component<Props, State> {
       placement,
       placement,
       content,
       content,
       renderContent: this.renderContent,
       renderContent: this.renderContent,
+      theme,
     });
     });
   }
   }
 }
 }

+ 17 - 9
packages/grafana-ui/src/components/Tooltip/_Tooltip.scss

@@ -8,7 +8,22 @@ $popper-margin-from-ref: 5px;
   text-align: center;
   text-align: center;
 }
 }
 
 
-.popper .popper__arrow {
+.popper__background {
+  background: $tooltipBackground;
+  border-radius: $border-radius;
+  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+  padding: 10px;
+
+  // Themes
+  &.popper__background--error {
+    background: $tooltipBackgroundError;
+    .popper__arrow {
+      border-color: $tooltipBackgroundError;
+    }
+  }
+}
+
+.popper__arrow {
   width: 0;
   width: 0;
   height: 0;
   height: 0;
   border-style: solid;
   border-style: solid;
@@ -16,17 +31,10 @@ $popper-margin-from-ref: 5px;
   margin: 0px;
   margin: 0px;
 }
 }
 
 
-.popper .popper__arrow {
+.popper__arrow {
   border-color: $tooltipBackground;
   border-color: $tooltipBackground;
 }
 }
 
 
-.popper__background {
-  background: $tooltipBackground;
-  border-radius: $border-radius;
-  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
-  padding: 10px;
-}
-
 // Top
 // Top
 .popper[data-placement^='top'] {
 .popper[data-placement^='top'] {
   padding-bottom: $popper-margin-from-ref;
   padding-bottom: $popper-margin-from-ref;

+ 1 - 0
packages/grafana-ui/src/visualizations/Graph/Graph.tsx

@@ -98,6 +98,7 @@ export class Graph extends PureComponent<GraphProps> {
       $.plot(this.element, timeSeries, flotOptions);
       $.plot(this.element, timeSeries, flotOptions);
     } catch (err) {
     } catch (err) {
       console.log('Graph rendering error', err, flotOptions, timeSeries);
       console.log('Graph rendering error', err, flotOptions, timeSeries);
+      throw new Error('Error rendering panel');
     }
     }
   }
   }
 
 

+ 44 - 0
public/app/core/components/ErrorBoundary/ErrorBoundary.tsx

@@ -0,0 +1,44 @@
+import { Component } from 'react';
+
+interface ErrorInfo {
+  componentStack: string;
+}
+
+interface RenderProps {
+  error: Error;
+  errorInfo: ErrorInfo;
+}
+
+interface Props {
+  children: (r: RenderProps) => JSX.Element;
+}
+
+interface State {
+  error: Error;
+  errorInfo: ErrorInfo;
+}
+
+class ErrorBoundary extends Component<Props, State> {
+  readonly state: State = {
+    error: null,
+    errorInfo: null,
+  };
+
+  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+    this.setState({
+      error: error,
+      errorInfo: errorInfo
+    });
+  }
+
+  render() {
+    const { children } = this.props;
+    const { error, errorInfo } = this.state;
+    return children({
+      error,
+      errorInfo,
+    });
+  }
+}
+
+export default ErrorBoundary;

+ 3 - 3
public/app/features/dashboard/dashgrid/AlertTab.tsx → public/app/features/alerting/AlertTab.tsx

@@ -6,14 +6,14 @@ import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoa
 import appEvents from 'app/core/app_events';
 import appEvents from 'app/core/app_events';
 
 
 // Components
 // Components
-import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
+import { EditorTabBody, EditorToolbarView } from '../dashboard/dashgrid/EditorTabBody';
 import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
 import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
 import StateHistory from './StateHistory';
 import StateHistory from './StateHistory';
 import 'app/features/alerting/AlertTabCtrl';
 import 'app/features/alerting/AlertTabCtrl';
 
 
 // Types
 // Types
-import { DashboardModel } from '../dashboard_model';
-import { PanelModel } from '../panel_model';
+import { DashboardModel } from '../dashboard/dashboard_model';
+import { PanelModel } from '../dashboard/panel_model';
 
 
 interface Props {
 interface Props {
   angularPanel?: AngularComponent;
   angularPanel?: AngularComponent;

+ 3 - 3
public/app/features/dashboard/dashgrid/StateHistory.tsx → public/app/features/alerting/StateHistory.tsx

@@ -1,8 +1,8 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
-import alertDef from '../../alerting/state/alertDef';
+import alertDef from './state/alertDef';
 import { getBackendSrv } from 'app/core/services/backend_srv';
 import { getBackendSrv } from 'app/core/services/backend_srv';
-import { DashboardModel } from '../dashboard_model';
-import appEvents from '../../../core/app_events';
+import { DashboardModel } from '../dashboard/dashboard_model';
+import appEvents from '../../core/app_events';
 
 
 interface Props {
 interface Props {
   dashboard: DashboardModel;
   dashboard: DashboardModel;

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

@@ -1,5 +1,7 @@
 // Library
 // Library
 import React, { Component } from 'react';
 import React, { Component } from 'react';
+import Tooltip from 'app/core/components/Tooltip/Tooltip';
+import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary';
 
 
 // Services
 // Services
 import { getDatasourceSrv, DatasourceSrv } from 'app/features/plugins/datasource_srv';
 import { getDatasourceSrv, DatasourceSrv } from 'app/features/plugins/datasource_srv';
@@ -10,6 +12,9 @@ import kbn from 'app/core/utils/kbn';
 // Types
 // Types
 import { DataQueryOptions, DataQueryResponse } from 'app/types';
 import { DataQueryOptions, DataQueryResponse } from 'app/types';
 import { TimeRange, TimeSeries, LoadingState } from '@grafana/ui';
 import { TimeRange, TimeSeries, LoadingState } from '@grafana/ui';
+import { Themes } from 'app/core/components/Tooltip/Popper';
+
+const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
 
 
 interface RenderProps {
 interface RenderProps {
   loading: LoadingState;
   loading: LoadingState;
@@ -33,6 +38,7 @@ export interface Props {
 export interface State {
 export interface State {
   isFirstLoad: boolean;
   isFirstLoad: boolean;
   loading: LoadingState;
   loading: LoadingState;
+  errorMessage: string;
   response: DataQueryResponse;
   response: DataQueryResponse;
 }
 }
 
 
@@ -51,6 +57,7 @@ export class DataPanel extends Component<Props, State> {
 
 
     this.state = {
     this.state = {
       loading: LoadingState.NotStarted,
       loading: LoadingState.NotStarted,
+      errorMessage: '',
       response: {
       response: {
         data: [],
         data: [],
       },
       },
@@ -90,7 +97,7 @@ export class DataPanel extends Component<Props, State> {
       return;
       return;
     }
     }
 
 
-    this.setState({ loading: LoadingState.Loading });
+    this.setState({ loading: LoadingState.Loading, errorMessage: '' });
 
 
     try {
     try {
       const ds = await this.dataSourceSrv.get(datasource);
       const ds = await this.dataSourceSrv.get(datasource);
@@ -128,10 +135,20 @@ export class DataPanel extends Component<Props, State> {
       });
       });
     } catch (err) {
     } catch (err) {
       console.log('Loading error', err);
       console.log('Loading error', err);
-      this.setState({ loading: LoadingState.Error, isFirstLoad: false });
+      this.onError('Request Error');
     }
     }
   };
   };
 
 
+  onError = (errorMessage: string) => {
+    if (this.state.loading !== LoadingState.Error || this.state.errorMessage !== errorMessage) {
+      this.setState({
+        loading: LoadingState.Error,
+        isFirstLoad: false,
+        errorMessage: errorMessage
+      });
+    }
+  }
+
   render() {
   render() {
     const { queries } = this.props;
     const { queries } = this.props;
     const { response, loading, isFirstLoad } = this.state;
     const { response, loading, isFirstLoad } = this.state;
@@ -139,7 +156,7 @@ export class DataPanel extends Component<Props, State> {
     const timeSeries = response.data;
     const timeSeries = response.data;
 
 
     if (isFirstLoad && loading === LoadingState.Loading) {
     if (isFirstLoad && loading === LoadingState.Loading) {
-      return this.renderLoadingSpinner();
+      return this.renderLoadingStates();
     }
     }
 
 
     if (!queries.length) {
     if (!queries.length) {
@@ -152,24 +169,48 @@ export class DataPanel extends Component<Props, State> {
 
 
     return (
     return (
       <>
       <>
-        {this.renderLoadingSpinner()}
-        {this.props.children({
-          timeSeries,
-          loading,
-        })}
+        {this.renderLoadingStates()}
+        <ErrorBoundary>
+          {({error, errorInfo}) => {
+            if (errorInfo) {
+              this.onError(error.message || DEFAULT_PLUGIN_ERROR);
+              return null;
+            }
+            return (
+              <>
+                {this.props.children({
+                  timeSeries,
+                  loading,
+                })}
+              </>
+            );
+          }}
+        </ErrorBoundary>
       </>
       </>
     );
     );
   }
   }
 
 
-  private renderLoadingSpinner(): JSX.Element {
-    const { loading } = this.state;
-
+  private renderLoadingStates(): JSX.Element {
+    const { loading, errorMessage } = this.state;
     if (loading === LoadingState.Loading) {
     if (loading === LoadingState.Loading) {
       return (
       return (
         <div className="panel-loading">
         <div className="panel-loading">
           <i className="fa fa-spinner fa-spin" />
           <i className="fa fa-spinner fa-spin" />
         </div>
         </div>
       );
       );
+    } else if (loading === LoadingState.Error) {
+      return (
+        <Tooltip
+          content={errorMessage}
+          className="popper__manager--block"
+          refClassName={`panel-info-corner panel-info-corner--error`}
+          placement="bottom-start"
+          theme={Themes.Error}
+        >
+          <i className="fa" />
+          <span className="panel-info-corner-inner" />
+        </Tooltip>
+      );
     }
     }
 
 
     return null;
     return null;

+ 0 - 1
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -87,7 +87,6 @@ export class PanelChrome extends PureComponent<Props, State> {
     const { datasource, targets, transparent } = panel;
     const { datasource, targets, transparent } = panel;
     const PanelComponent = plugin.exports.Panel;
     const PanelComponent = plugin.exports.Panel;
     const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`;
     const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`;
-
     return (
     return (
       <AutoSizer>
       <AutoSizer>
         {({ width, height }) => {
         {({ width, height }) => {

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

@@ -4,7 +4,7 @@ import classNames from 'classnames';
 import { QueriesTab } from './QueriesTab';
 import { QueriesTab } from './QueriesTab';
 import { VisualizationTab } from './VisualizationTab';
 import { VisualizationTab } from './VisualizationTab';
 import { GeneralTab } from './GeneralTab';
 import { GeneralTab } from './GeneralTab';
-import { AlertTab } from './AlertTab';
+import { AlertTab } from '../../alerting/AlertTab';
 
 
 import config from 'app/core/config';
 import config from 'app/core/config';
 import { store } from 'app/store/store';
 import { store } from 'app/store/store';

+ 6 - 2
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -50,17 +50,21 @@ export class QueriesTab extends PureComponent<Props, State> {
 
 
   constructor(props) {
   constructor(props) {
     super(props);
     super(props);
-    const { panel } = props;
 
 
     this.state = {
     this.state = {
-      currentDS: this.datasources.find(datasource => datasource.value === panel.datasource),
       isLoadingHelp: false,
       isLoadingHelp: false,
+      currentDS: this.findCurrentDataSource(),
       helpContent: null,
       helpContent: null,
       isPickerOpen: false,
       isPickerOpen: false,
       isAddingMixed: false,
       isAddingMixed: false,
     };
     };
   }
   }
 
 
+  findCurrentDataSource(): DataSourceSelectItem {
+    const { panel } = this.props;
+    return this.datasources.find(datasource => datasource.value === panel.datasource) || this.datasources[0];
+  }
+
   getAngularQueryComponentScope(): AngularQueryComponentScope {
   getAngularQueryComponentScope(): AngularQueryComponentScope {
     const { panel, dashboard } = this.props;
     const { panel, dashboard } = this.props;
 
 

+ 0 - 4
public/app/plugins/panel/graph2/GraphPanel.tsx

@@ -10,10 +10,6 @@ import { Options } from './types';
 interface Props extends PanelProps<Options> {}
 interface Props extends PanelProps<Options> {}
 
 
 export class GraphPanel extends PureComponent<Props> {
 export class GraphPanel extends PureComponent<Props> {
-  constructor(props) {
-    super(props);
-  }
-
   render() {
   render() {
     const { timeSeries, timeRange, width, height } = this.props;
     const { timeSeries, timeRange, width, height } = this.props;
     const { showLines, showBars, showPoints } = this.props.options;
     const { showLines, showBars, showPoints } = this.props.options;

+ 5 - 2
public/sass/_variables.dark.scss

@@ -103,6 +103,7 @@ $panel-bg: #212124;
 $panel-border-color: $dark-1;
 $panel-border-color: $dark-1;
 $panel-border: solid 1px $panel-border-color;
 $panel-border: solid 1px $panel-border-color;
 $panel-header-hover-bg: $dark-4;
 $panel-header-hover-bg: $dark-4;
+$panel-corner: $panel-bg;
 
 
 // page header
 // page header
 $page-header-bg: linear-gradient(90deg, #292a2d, black);
 $page-header-bg: linear-gradient(90deg, #292a2d, black);
@@ -302,12 +303,14 @@ $popover-error-bg: $btn-danger-bg;
 // Tooltips and popovers
 // Tooltips and popovers
 // -------------------------
 // -------------------------
 $tooltipColor: $popover-help-color;
 $tooltipColor: $popover-help-color;
-$tooltipBackground: $popover-help-bg;
 $tooltipArrowWidth: 5px;
 $tooltipArrowWidth: 5px;
-$tooltipArrowColor: $tooltipBackground;
 $tooltipLinkColor: $link-color;
 $tooltipLinkColor: $link-color;
 $graph-tooltip-bg: $dark-1;
 $graph-tooltip-bg: $dark-1;
 
 
+$tooltipBackground: $popover-help-bg;
+$tooltipArrowColor: $tooltipBackground;
+$tooltipBackgroundError: $brand-danger;
+
 // images
 // images
 $checkboxImageUrl: '../img/checkbox.png';
 $checkboxImageUrl: '../img/checkbox.png';
 
 

+ 5 - 2
public/sass/_variables.light.scss

@@ -102,6 +102,7 @@ $panel-bg: $white;
 $panel-border-color: $gray-5;
 $panel-border-color: $gray-5;
 $panel-border: solid 1px $panel-border-color;
 $panel-border: solid 1px $panel-border-color;
 $panel-header-hover-bg: $gray-6;
 $panel-header-hover-bg: $gray-6;
+$panel-corner: $gray-4;
 
 
 // Page header
 // Page header
 $page-header-bg: linear-gradient(90deg, $white, $gray-7);
 $page-header-bg: linear-gradient(90deg, $white, $gray-7);
@@ -307,12 +308,14 @@ $popover-error-bg: $btn-danger-bg;
 // Tooltips and popovers
 // Tooltips and popovers
 // -------------------------
 // -------------------------
 $tooltipColor: $popover-help-color;
 $tooltipColor: $popover-help-color;
-$tooltipBackground: $popover-help-bg;
 $tooltipArrowWidth: 5px;
 $tooltipArrowWidth: 5px;
-$tooltipArrowColor: $tooltipBackground;
 $tooltipLinkColor: lighten($popover-help-color, 5%);
 $tooltipLinkColor: lighten($popover-help-color, 5%);
 $graph-tooltip-bg: $gray-5;
 $graph-tooltip-bg: $gray-5;
 
 
+$tooltipBackground: $popover-help-bg;
+$tooltipArrowColor: $tooltipBackground; // Used by Angular tooltip
+$tooltipBackgroundError: $brand-danger;
+
 // images
 // images
 $checkboxImageUrl: '../img/checkbox_white.png';
 $checkboxImageUrl: '../img/checkbox_white.png';
 
 

+ 3 - 3
public/sass/pages/_dashboard.scss

@@ -214,7 +214,7 @@ div.flot-text {
 
 
   &--info {
   &--info {
     display: block;
     display: block;
-    @include panel-corner-color(lighten($panel-bg, 4%));
+    @include panel-corner-color(lighten($panel-corner, 4%));
     .fa:before {
     .fa:before {
       content: '\f129';
       content: '\f129';
     }
     }
@@ -222,7 +222,7 @@ div.flot-text {
 
 
   &--links {
   &--links {
     display: block;
     display: block;
-    @include panel-corner-color(lighten($panel-bg, 4%));
+    @include panel-corner-color(lighten($panel-corner, 4%));
     .fa {
     .fa {
       left: 4px;
       left: 4px;
     }
     }
@@ -233,7 +233,7 @@ div.flot-text {
 
 
   &--error {
   &--error {
     display: block;
     display: block;
-    color: $text-color;
+    color: $white;
     @include panel-corner-color($popover-error-bg);
     @include panel-corner-color($popover-error-bg);
     .fa:before {
     .fa:before {
       content: '\f12a';
       content: '\f12a';