import React, { Component } from 'react'; import classNames from 'classnames'; import { isEqual } from 'lodash'; import { DataLink, ScopedVars, PanelMenuItem, PanelData, LoadingState, QueryResultMetaNotice } from '@grafana/data'; import { AngularComponent } from '@grafana/runtime'; import { ClickOutsideWrapper, Tooltip, Icon } from '@grafana/ui'; import { e2e } from '@grafana/e2e'; import PanelHeaderCorner from './PanelHeaderCorner'; import { PanelHeaderMenu } from './PanelHeaderMenu'; import templateSrv from 'app/features/templating/template_srv'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getPanelMenu } from 'app/features/dashboard/utils/getPanelMenu'; import { updateLocation } from 'app/core/actions'; export interface Props { panel: PanelModel; dashboard: DashboardModel; title?: string; description?: string; scopedVars?: ScopedVars; angularComponent?: AngularComponent | null; links?: DataLink[]; error?: string; isFullscreen: boolean; data: PanelData; updateLocation: typeof updateLocation; } interface ClickCoordinates { x: number; y: number; } interface State { panelMenuOpen: boolean; menuItems: PanelMenuItem[]; } export class PanelHeader extends Component { clickCoordinates: ClickCoordinates = { x: 0, y: 0 }; state: State = { panelMenuOpen: false, menuItems: [], }; eventToClickCoordinates = (event: React.MouseEvent) => { return { x: event.clientX, y: event.clientY, }; }; onMouseDown = (event: React.MouseEvent) => { this.clickCoordinates = this.eventToClickCoordinates(event); }; isClick = (clickCoordinates: ClickCoordinates) => { return isEqual(clickCoordinates, this.clickCoordinates); }; onMenuToggle = (event: React.MouseEvent) => { if (!this.isClick(this.eventToClickCoordinates(event))) { return; } event.stopPropagation(); const { dashboard, panel, angularComponent } = this.props; const menuItems = getPanelMenu(dashboard, panel, angularComponent); this.setState({ panelMenuOpen: !this.state.panelMenuOpen, menuItems, }); }; closeMenu = () => { this.setState({ panelMenuOpen: false, }); }; private renderLoadingState(): JSX.Element { return (
); } openInspect = (e: React.SyntheticEvent, tab: string) => { const { updateLocation, panel } = this.props; e.stopPropagation(); updateLocation({ query: { inspect: panel.id, tab }, partial: true, }); }; renderNotice = (notice: QueryResultMetaNotice) => { return ( {notice.inspect ? (
this.openInspect(e, notice.inspect!)}>
) : ( )}
); }; render() { const { panel, scopedVars, error, isFullscreen, data } = this.props; const { menuItems } = this.state; const title = templateSrv.replaceWithText(panel.title, scopedVars); const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen, }); // dedupe on severity const notices: Record = {}; for (const series of data.series) { if (series.meta && series.meta.notices) { for (const notice of series.meta.notices) { notices[notice.severity] = notice; } } } return ( <> {data.state === LoadingState.Loading && this.renderLoadingState()}
{Object.values(notices).map(this.renderNotice)} {title} {this.state.panelMenuOpen && ( )} {data.request && data.request.timeInfo && ( {data.request.timeInfo} )}
); } }