Annotations: fire an event when changing annotations (#37175)

This commit is contained in:
Ryan McKinley 2021-07-26 11:57:52 -07:00 committed by GitHub
parent 3e35021f7e
commit ffa0ef9b3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 34 deletions

View File

@ -1,4 +1,4 @@
import { DataFrame } from '../types';
import { AnnotationEvent, DataFrame } from '../types';
import { BusEventWithPayload } from './types';
/**
@ -34,3 +34,8 @@ export class DataHoverClearEvent extends BusEventWithPayload<DataHoverPayload> {
export class DataSelectEvent extends BusEventWithPayload<DataHoverPayload> {
static type = 'data-select';
}
/** @alpha */
export class AnnotationChangeEvent extends BusEventWithPayload<Partial<AnnotationEvent>> {
static type = 'annotation-event';
}

View File

@ -15,6 +15,7 @@ import { DashboardModel, PanelModel } from '../state';
import { PANEL_BORDER } from 'app/core/constants';
import {
AbsoluteTimeRange,
AnnotationChangeEvent,
AnnotationEventUIModel,
DashboardCursorSync,
EventFilterOptions,
@ -278,7 +279,7 @@ export class PanelChrome extends Component<Props, State> {
onAnnotationCreate = async (event: AnnotationEventUIModel) => {
const isRegion = event.from !== event.to;
await saveAnnotation({
const anno = {
dashboardId: this.props.dashboard.id,
panelId: this.props.panel.id,
isRegion,
@ -286,18 +287,21 @@ export class PanelChrome extends Component<Props, State> {
timeEnd: isRegion ? event.to : 0,
tags: event.tags,
text: event.description,
});
};
await saveAnnotation(anno);
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));
};
onAnnotationDelete = async (id: string) => {
await deleteAnnotation({ id });
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent({ id }));
};
onAnnotationUpdate = async (event: AnnotationEventUIModel) => {
const isRegion = event.from !== event.to;
await updateAnnotation({
const anno = {
id: event.id,
dashboardId: this.props.dashboard.id,
panelId: this.props.panel.id,
@ -306,9 +310,11 @@ export class PanelChrome extends Component<Props, State> {
timeEnd: isRegion ? event.to : 0,
tags: event.tags,
text: event.description,
});
};
await updateAnnotation(anno);
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));
};
get hasPanelSnapshot() {

View File

@ -56,7 +56,10 @@ async function setupTestContext({
data: { state: LoadingState.Done, timeRange: getDefaultTimeRange(), series: [] },
eventBus: {
subscribe: jest.fn(),
getStream: jest.fn(),
getStream: () =>
({
subscribe: jest.fn(),
} as any),
publish: jest.fn(),
removeAllListeners: jest.fn(),
newScopedBus: jest.fn(),

View File

@ -2,14 +2,25 @@
import React, { PureComponent } from 'react';
// Types
import { AnnoOptions } from './types';
import { AnnotationEvent, AppEvents, dateTime, DurationUnit, locationUtil, PanelProps } from '@grafana/data';
import { getBackendSrv, locationService } from '@grafana/runtime';
import {
AnnotationChangeEvent,
AnnotationEvent,
AppEvents,
dateTime,
DurationUnit,
GrafanaTheme,
locationUtil,
PanelProps,
} from '@grafana/data';
import { config, getBackendSrv, locationService } from '@grafana/runtime';
import { AbstractList } from '@grafana/ui/src/components/List/AbstractList';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import appEvents from 'app/core/app_events';
import { AnnotationListItem } from './AnnotationListItem';
import { AnnotationListItemTags } from './AnnotationListItemTags';
import { CustomScrollbar } from '@grafana/ui';
import { CustomScrollbar, stylesFactory } from '@grafana/ui';
import { css } from '@emotion/css';
import { Subscription } from 'rxjs';
interface UserInfo {
id?: number;
@ -25,8 +36,10 @@ interface State {
queryUser?: UserInfo;
queryTags: string[];
}
export class AnnoListPanel extends PureComponent<Props, State> {
style = getStyles(config.theme);
subs = new Subscription();
constructor(props: Props) {
super(props);
@ -40,6 +53,19 @@ export class AnnoListPanel extends PureComponent<Props, State> {
componentDidMount() {
this.doSearch();
// When an annotation on this dashboard changes, re-run the query
this.subs.add(
this.props.eventBus.getStream(AnnotationChangeEvent).subscribe({
next: () => {
this.doSearch();
},
})
);
}
componentWillUnmount() {
this.subs.unsubscribe();
}
componentDidUpdate(prevProps: Props, prevState: State) {
@ -48,7 +74,7 @@ export class AnnoListPanel extends PureComponent<Props, State> {
options !== prevProps.options ||
this.state.queryTags !== prevState.queryTags ||
this.state.queryUser !== prevState.queryUser ||
timeRange !== prevProps.timeRange;
(options.onlyInTimeRange && timeRange !== prevProps.timeRange);
if (needsQuery) {
this.doSearch();
@ -228,10 +254,20 @@ export class AnnoListPanel extends PureComponent<Props, State> {
</div>
)}
{annotations.length < 1 && <div className="panel-alert-list__no-alerts">No Annotations Found</div>}
{annotations.length < 1 && <div className={this.style.noneFound}>No Annotations Found</div>}
<AbstractList items={annotations} renderItem={this.renderItem} getItemKey={(item) => `${item.id}`} />
</CustomScrollbar>
);
}
}
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
noneFound: css`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: calc(100% - 30px);
`,
}));

View File

@ -99,21 +99,17 @@ const TimeStamp: FC<TimeStampProps> = ({ time, formatDate }) => {
function getStyles(theme: GrafanaTheme) {
return {
pointer: css`
label: pointer;
cursor: pointer;
`,
item: css`
label: labelItem;
margin: ${theme.spacing.xs};
padding: ${theme.spacing.sm};
${styleMixins.listItem(theme)}// display: flex;
`,
title: css`
label: title;
flex-basis: 80%;
`,
link: css`
label: link;
display: flex;
.fa {
@ -125,7 +121,6 @@ function getStyles(theme: GrafanaTheme) {
}
`,
login: css`
label: login;
align-self: center;
flex: auto;
display: flex;
@ -133,13 +128,11 @@ function getStyles(theme: GrafanaTheme) {
font-size: ${theme.typography.size.sm};
`,
time: css`
label: time;
margin-left: ${theme.spacing.sm};
font-size: ${theme.typography.size.sm};
color: ${theme.colors.textWeak};
`,
avatar: css`
label: avatar;
padding: ${theme.spacing.xs};
img {
border-radius: 50%;

View File

@ -27,7 +27,7 @@ export const AnnotationListItemTags: FC<Props> = ({ tags, remove, onClick }) =>
}
return (
<div>
<>
{tags.map((tag) => {
return (
<span key={tag} onClick={(e) => onTagClicked(e, tag)} className={styles.pointer}>
@ -35,14 +35,13 @@ export const AnnotationListItemTags: FC<Props> = ({ tags, remove, onClick }) =>
</span>
);
})}
</div>
</>
);
};
function getStyles(theme: GrafanaTheme) {
return {
pointer: css`
label: pointer;
cursor: pointer;
padding: ${theme.spacing.xxs};
`,

View File

@ -42,7 +42,6 @@
@import 'components/tags';
@import 'components/panel_graph';
@import 'components/submenu';
@import 'components/panel_alertlist';
@import 'components/panel_dashlist';
@import 'components/panel_gettingstarted';
@import 'components/panel_piechart';

View File

@ -1,11 +0,0 @@
.panel-alert-list {
overflow-y: auto;
}
.panel-alert-list__no-alerts {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: calc(100% - 30px);
}