PanelHeader: show streaming indicator (and allow unsubscribe) (#28682)

This commit is contained in:
Ryan McKinley
2020-11-05 08:03:34 -08:00
committed by GitHub
parent f39a8d630b
commit 7308028a90
4 changed files with 53 additions and 19 deletions

View File

@@ -13,7 +13,7 @@ export interface PopoverContentProps {
export type PopoverContent = string | React.ReactElement<any> | ((props: PopoverContentProps) => JSX.Element); export type PopoverContent = string | React.ReactElement<any> | ((props: PopoverContentProps) => JSX.Element);
export const Tooltip: FC<TooltipProps> = ({ children, theme, ...controllerProps }: TooltipProps) => { export const Tooltip: FC<TooltipProps> = React.memo(({ children, theme, ...controllerProps }: TooltipProps) => {
const tooltipTriggerRef = createRef<PopperJS.ReferenceObject>(); const tooltipTriggerRef = createRef<PopperJS.ReferenceObject>();
const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : ''); const popperBackgroundClassName = 'popper__background' + (theme ? ' popper__background--' + theme : '');
@@ -52,4 +52,4 @@ export const Tooltip: FC<TooltipProps> = ({ children, theme, ...controllerProps
}} }}
</PopoverController> </PopoverController>
); );
}; });

View File

@@ -1,9 +1,9 @@
import React, { Component } from 'react'; import React, { PureComponent } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { DataLink, LoadingState, PanelData, PanelMenuItem, QueryResultMetaNotice, ScopedVars } from '@grafana/data'; import { DataLink, LoadingState, PanelData, PanelMenuItem, QueryResultMetaNotice, ScopedVars } from '@grafana/data';
import { AngularComponent, getTemplateSrv } from '@grafana/runtime'; import { AngularComponent, config, getTemplateSrv } from '@grafana/runtime';
import { ClickOutsideWrapper, Icon, IconName, Tooltip } from '@grafana/ui'; import { ClickOutsideWrapper, Icon, IconName, Tooltip, stylesFactory } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import PanelHeaderCorner from './PanelHeaderCorner'; import PanelHeaderCorner from './PanelHeaderCorner';
@@ -14,6 +14,7 @@ import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
import { getPanelMenu } from 'app/features/dashboard/utils/getPanelMenu'; import { getPanelMenu } from 'app/features/dashboard/utils/getPanelMenu';
import { updateLocation } from 'app/core/actions'; import { updateLocation } from 'app/core/actions';
import { css } from 'emotion';
export interface Props { export interface Props {
panel: PanelModel; panel: PanelModel;
@@ -41,7 +42,7 @@ interface State {
menuItems: PanelMenuItem[]; menuItems: PanelMenuItem[];
} }
export class PanelHeader extends Component<Props, State> { export class PanelHeader extends PureComponent<Props, State> {
clickCoordinates: ClickCoordinates = { x: 0, y: 0 }; clickCoordinates: ClickCoordinates = { x: 0, y: 0 };
state: State = { state: State = {
@@ -90,14 +91,28 @@ export class PanelHeader extends Component<Props, State> {
this.props.panel.getQueryRunner().cancelQuery(); this.props.panel.getQueryRunner().cancelQuery();
}; };
private renderLoadingState(): JSX.Element { renderLoadingState(state: LoadingState): JSX.Element | null {
return ( if (state === LoadingState.Loading) {
<div className="panel-loading" onClick={this.onCancelQuery}> return (
<Tooltip content="Cancel query"> <div className="panel-loading" onClick={this.onCancelQuery}>
<Icon className="panel-loading__spinner spin-clockwise" name="sync" /> <Tooltip content="Cancel query">
</Tooltip> <Icon className="panel-loading__spinner spin-clockwise" name="sync" />
</div> </Tooltip>
); </div>
);
}
if (state === LoadingState.Streaming) {
const styles = getStyles();
return (
<div className="panel-loading" onClick={this.onCancelQuery}>
<div title="Streaming (click to stop)" className={styles.streamIndicator} />
</div>
);
}
return null;
} }
openInspect = (e: React.SyntheticEvent, tab: string) => { openInspect = (e: React.SyntheticEvent, tab: string) => {
@@ -156,7 +171,7 @@ export class PanelHeader extends Component<Props, State> {
return ( return (
<> <>
{data.state === LoadingState.Loading && this.renderLoadingState()} {this.renderLoadingState(data.state)}
<div className={panelHeaderClass}> <div className={panelHeaderClass}>
<PanelHeaderCorner <PanelHeaderCorner
panel={panel} panel={panel}
@@ -201,3 +216,21 @@ export class PanelHeader extends Component<Props, State> {
); );
} }
} }
/*
* Styles
*/
export const getStyles = stylesFactory(() => {
return {
streamIndicator: css`
width: 10px;
height: 10px;
background: ${config.theme.colors.textFaint};
box-shadow: 0 0 2px ${config.theme.colors.textFaint};
border-radius: 50%;
position: relative;
top: 6px;
right: 1px;
`,
};
});

View File

@@ -10,7 +10,7 @@ exports[`Render should render component 1`] = `
> >
External group sync External group sync
</h3> </h3>
<Component <Memo()
content="Sync LDAP or OAuth groups with your Grafana teams." content="Sync LDAP or OAuth groups with your Grafana teams."
placement="auto" placement="auto"
> >
@@ -18,7 +18,7 @@ exports[`Render should render component 1`] = `
className="icon--has-hover page-sub-heading-icon" className="icon--has-hover page-sub-heading-icon"
name="question-circle" name="question-circle"
/> />
</Component> </Memo()>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
/> />
@@ -92,7 +92,7 @@ exports[`Render should render groups table 1`] = `
> >
External group sync External group sync
</h3> </h3>
<Component <Memo()
content="Sync LDAP or OAuth groups with your Grafana teams." content="Sync LDAP or OAuth groups with your Grafana teams."
placement="auto" placement="auto"
> >
@@ -100,7 +100,7 @@ exports[`Render should render groups table 1`] = `
className="icon--has-hover page-sub-heading-icon" className="icon--has-hover page-sub-heading-icon"
name="question-circle" name="question-circle"
/> />
</Component> </Memo()>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
/> />

View File

@@ -98,6 +98,7 @@ export function runSignalStream(
subscriber.next({ subscriber.next({
data: [data], data: [data],
key: streamId, key: streamId,
state: LoadingState.Streaming,
}); });
timeoutId = setTimeout(pushNextEvent, speed); timeoutId = setTimeout(pushNextEvent, speed);