diff --git a/packages/grafana-data/src/types/legacyEvents.ts b/packages/grafana-data/src/types/legacyEvents.ts index 9a2d97f35ca..a0040e67d57 100644 --- a/packages/grafana-data/src/types/legacyEvents.ts +++ b/packages/grafana-data/src/types/legacyEvents.ts @@ -22,7 +22,6 @@ export const PanelEvents = { dataSnapshotLoad: eventFactory('data-snapshot-load'), editModeInitialized: eventFactory('init-edit-mode'), initPanelActions: eventFactory('init-panel-actions'), - panelInitialized: eventFactory('panel-initialized'), panelSizeChanged: eventFactory('panel-size-changed'), panelTeardown: eventFactory('panel-teardown'), render: eventFactory('render'), diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx index 9e39cb1f558..49d2b97d270 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import AutoSizer from 'react-virtualized-auto-sizer'; import { css, cx } from 'emotion'; -import { Unsubscribable } from 'rxjs'; +import { Subscription } from 'rxjs'; import { FieldConfigSource, GrafanaTheme, PanelPlugin } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -36,6 +36,7 @@ import { CoreEvents, LocationState, StoreState } from 'app/types'; import { DisplayMode, displayModes, PanelEditorTab } from './types'; import { VariableModel } from 'app/features/variables/types'; import { DashboardModel, PanelModel } from '../../state'; +import { PanelOptionsChangedEvent } from 'app/types/events'; interface OwnProps { dashboard: DashboardModel; @@ -64,15 +65,30 @@ interface DispatchProps { type Props = OwnProps & ConnectedProps & DispatchProps; export class PanelEditorUnconnected extends PureComponent { - querySubscription: Unsubscribable; + private eventSubs?: Subscription; componentDidMount() { this.props.initPanelEditor(this.props.sourcePanel, this.props.dashboard); } + + componentDidUpdate() { + const { panel, initDone } = this.props; + + if (initDone && !this.eventSubs) { + this.eventSubs = new Subscription(); + this.eventSubs.add(panel.events.subscribe(PanelOptionsChangedEvent, this.triggerForceUpdate)); + } + } + componentWillUnmount() { this.props.panelEditorCleanUp(); + this.eventSubs?.unsubscribe(); } + triggerForceUpdate = () => { + this.forceUpdate(); + }; + onPanelExit = () => { this.props.updateLocation({ query: { editPanel: null, tab: null }, @@ -113,8 +129,9 @@ export class PanelEditorUnconnected extends PureComponent { }; onPanelOptionsChanged = (options: any) => { + // we do not need to trigger force update here as the function call below + // fires PanelOptionsChangedEvent which we subscribe to above this.props.panel.updateOptions(options); - this.forceUpdate(); }; onPanelConfigChanged = (configKey: string, value: any) => { diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx index bdf6c3dfafb..646d5bf3fc2 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx @@ -6,8 +6,9 @@ import { QueriesTab } from '../../panel_editor/QueriesTab'; import { AlertTab } from 'app/features/alerting/AlertTab'; import { TransformationsEditor } from '../TransformationsEditor/TransformationsEditor'; import { DashboardModel, PanelModel } from '../../state'; -import { CoreEvents } from 'app/types'; import { PanelEditorTab, PanelEditorTabId } from './types'; +import { Subscription } from 'rxjs'; +import { PanelQueriesChangedEvent, PanelTransformationsChangedEvent } from 'app/types/events'; interface PanelEditorTabsProps { panel: PanelModel; @@ -17,16 +18,16 @@ interface PanelEditorTabsProps { } export class PanelEditorTabs extends PureComponent { + private eventSubs = new Subscription(); + componentDidMount() { - const { panel } = this.props; - panel.on(CoreEvents.queryChanged, this.triggerForceUpdate); - panel.on(CoreEvents.transformationChanged, this.triggerForceUpdate); + const { events } = this.props.panel; + this.eventSubs.add(events.subscribe(PanelQueriesChangedEvent, this.triggerForceUpdate)); + this.eventSubs.add(events.subscribe(PanelTransformationsChangedEvent, this.triggerForceUpdate)); } componentWillUnmount() { - const { panel } = this.props; - panel.off(CoreEvents.queryChanged, this.triggerForceUpdate); - panel.off(CoreEvents.transformationChanged, this.triggerForceUpdate); + this.eventSubs.unsubscribe(); } triggerForceUpdate = () => { diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index c1738ec5088..70454f784cb 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -14,8 +14,8 @@ import { DashboardRow } from '../components/DashboardRow'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; import { DashboardPanel } from './DashboardPanel'; import { DashboardModel, PanelModel } from '../state'; -import { CoreEvents } from 'app/types'; -import { panelAdded, panelRemoved } from '../state/PanelModel'; +import { Subscription } from 'rxjs'; +import { DashboardPanelsChangedEvent } from 'app/types/events'; let lastGridWidth = 1200; let ignoreNextWidthChange = false; @@ -102,26 +102,17 @@ export interface Props { } export class DashboardGrid extends PureComponent { - panelMap: { [id: string]: PanelModel }; - panelRef: { [id: string]: HTMLElement } = {}; + private panelMap: { [id: string]: PanelModel }; + private panelRef: { [id: string]: HTMLElement } = {}; + private eventSubs = new Subscription(); componentDidMount() { const { dashboard } = this.props; - - dashboard.on(panelAdded, this.triggerForceUpdate); - dashboard.on(panelRemoved, this.triggerForceUpdate); - dashboard.on(CoreEvents.repeatsProcessed, this.triggerForceUpdate); - dashboard.on(CoreEvents.rowCollapsed, this.triggerForceUpdate); - dashboard.on(CoreEvents.rowExpanded, this.triggerForceUpdate); + this.eventSubs.add(dashboard.events.subscribe(DashboardPanelsChangedEvent, this.triggerForceUpdate)); } componentWillUnmount() { - const { dashboard } = this.props; - dashboard.off(panelAdded, this.triggerForceUpdate); - dashboard.off(panelRemoved, this.triggerForceUpdate); - dashboard.off(CoreEvents.repeatsProcessed, this.triggerForceUpdate); - dashboard.off(CoreEvents.rowCollapsed, this.triggerForceUpdate); - dashboard.off(CoreEvents.rowExpanded, this.triggerForceUpdate); + this.eventSubs.unsubscribe(); } buildLayout() { diff --git a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap index 0cc9e30177f..88313ad2513 100644 --- a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap +++ b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap @@ -71,33 +71,13 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` "events": EventBusSrv { "emitter": EventEmitter { "_events": Object { - "panel-added": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "panel-removed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "repeats-processed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-collapsed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-expanded": EE { + "dashboard-panels-changed": EE { "context": [Circular], "fn": [Function], "once": false, }, }, - "_eventsCount": 5, + "_eventsCount": 1, }, }, "getVariables": [Function], @@ -313,33 +293,13 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` "events": EventBusSrv { "emitter": EventEmitter { "_events": Object { - "panel-added": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "panel-removed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "repeats-processed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-collapsed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-expanded": EE { + "dashboard-panels-changed": EE { "context": [Circular], "fn": [Function], "once": false, }, }, - "_eventsCount": 5, + "_eventsCount": 1, }, }, "getVariables": [Function], @@ -555,33 +515,13 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` "events": EventBusSrv { "emitter": EventEmitter { "_events": Object { - "panel-added": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "panel-removed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "repeats-processed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-collapsed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-expanded": EE { + "dashboard-panels-changed": EE { "context": [Circular], "fn": [Function], "once": false, }, }, - "_eventsCount": 5, + "_eventsCount": 1, }, }, "getVariables": [Function], @@ -797,33 +737,13 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` "events": EventBusSrv { "emitter": EventEmitter { "_events": Object { - "panel-added": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "panel-removed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "repeats-processed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-collapsed": EE { - "context": [Circular], - "fn": [Function], - "once": false, - }, - "row-expanded": EE { + "dashboard-panels-changed": EE { "context": [Circular], "fn": [Function], "once": false, }, }, - "_eventsCount": 5, + "_eventsCount": 1, }, }, "getVariables": [Function], diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 070c162ea91..429c4a5ea8b 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -7,7 +7,7 @@ import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, REPEAT_DIR_VERT import { contextSrv } from 'app/core/services/context_srv'; import sortByKeys from 'app/core/utils/sort_by_keys'; // Types -import { GridPos, panelAdded, PanelModel, panelRemoved } from './PanelModel'; +import { GridPos, PanelModel } from './PanelModel'; import { DashboardMigrator } from './DashboardMigrator'; import { AppEvent, @@ -27,6 +27,7 @@ import { variableAdapters } from 'app/features/variables/adapters'; import { onTimeRangeUpdated } from 'app/features/variables/state/actions'; import { dispatch } from '../../../store/store'; import { isAllVariable } from '../../variables/utils'; +import { DashboardPanelsChangedEvent } from 'app/types/events'; export interface CloneOptions { saveVariables?: boolean; @@ -285,8 +286,6 @@ export class DashboardModel { } panelInitialized(panel: PanelModel) { - panel.initialized(); - const lastResult = panel.getQueryRunner().getLastResult(); if (!this.otherPanelInFullscreen(panel) && !lastResult) { @@ -379,13 +378,11 @@ export class DashboardModel { addPanel(panelData: any) { panelData.id = this.getNextPanelId(); - const panel = new PanelModel(panelData); - - this.panels.unshift(panel); + this.panels.unshift(new PanelModel(panelData)); this.sortPanelsByGridPos(); - this.events.emit(panelAdded, panel); + this.events.publish(new DashboardPanelsChangedEvent()); } sortPanelsByGridPos() { @@ -422,7 +419,7 @@ export class DashboardModel { _.pull(this.panels, ...panelsToRemove); panelsToRemove.map(p => p.destroy()); this.sortPanelsByGridPos(); - this.events.emit(CoreEvents.repeatsProcessed); + this.events.publish(new DashboardPanelsChangedEvent()); } processRepeats() { @@ -442,7 +439,7 @@ export class DashboardModel { } this.sortPanelsByGridPos(); - this.events.emit(CoreEvents.repeatsProcessed); + this.events.publish(new DashboardPanelsChangedEvent()); } cleanUpRowRepeats(rowPanels: PanelModel[]) { @@ -681,9 +678,8 @@ export class DashboardModel { } removePanel(panel: PanelModel) { - const index = _.indexOf(this.panels, panel); - this.panels.splice(index, 1); - this.events.emit(panelRemoved, panel); + this.panels = this.panels.filter(item => item !== panel); + this.events.publish(new DashboardPanelsChangedEvent()); } removeRow(row: PanelModel, removePanels: boolean) { @@ -848,7 +844,7 @@ export class DashboardModel { this.sortPanelsByGridPos(); // emit change event - this.events.emit(CoreEvents.rowExpanded); + this.events.publish(new DashboardPanelsChangedEvent()); return; } @@ -861,7 +857,7 @@ export class DashboardModel { row.collapsed = true; // emit change event - this.events.emit(CoreEvents.rowCollapsed); + this.events.publish(new DashboardPanelsChangedEvent()); } /** diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index 7a96b5d69dc..074fdfaac03 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -5,12 +5,10 @@ import { getTemplateSrv } from '@grafana/runtime'; import { getNextRefIdChar } from 'app/core/utils/query'; // Types import { - AppEvent, DataConfigSource, DataLink, DataQuery, DataTransformerConfig, - eventFactory, FieldColorConfigSettings, FieldColorModeId, fieldColorModeRegistry, @@ -29,10 +27,7 @@ import { EDIT_PANEL_ID } from 'app/core/constants'; import config from 'app/core/config'; import { PanelQueryRunner } from './PanelQueryRunner'; import { getDatasourceSrv } from '../../plugins/datasource_srv'; -import { CoreEvents } from '../../../types'; - -export const panelAdded = eventFactory('panel-added'); -export const panelRemoved = eventFactory('panel-removed'); +import { PanelOptionsChangedEvent, PanelQueriesChangedEvent, PanelTransformationsChangedEvent } from 'app/types/events'; export interface GridPos { x: number; @@ -219,6 +214,7 @@ export class PanelModel implements DataConfigSource { updateOptions(options: object) { this.options = options; + this.events.publish(new PanelOptionsChangedEvent()); this.render(); } @@ -291,10 +287,6 @@ export class PanelModel implements DataConfigSource { } } - initialized() { - this.events.emit(PanelEvents.panelInitialized); - } - private getOptionsToRemember() { return Object.keys(this).reduce((acc, property) => { if (notPersistedProperties[property] || mustKeepProps[property]) { @@ -418,7 +410,7 @@ export class PanelModel implements DataConfigSource { } updateQueries(queries: DataQuery[]) { - this.events.emit(CoreEvents.queryChanged); + this.events.publish(new PanelQueriesChangedEvent()); this.targets = queries; } @@ -501,9 +493,9 @@ export class PanelModel implements DataConfigSource { } setTransformations(transformations: DataTransformerConfig[]) { - this.events.emit(CoreEvents.transformationChanged); this.transformations = transformations; this.resendLastResult(); + this.events.publish(new PanelTransformationsChangedEvent()); } replaceVariables(value: string, extraVars?: ScopedVars, format?: string) { @@ -529,14 +521,6 @@ export class PanelModel implements DataConfigSource { getSavedId(): number { return this.editSourceId ?? this.id; } - - on(event: AppEvent, callback: (payload?: T) => void) { - this.events.on(event, callback); - } - - off(event: AppEvent, callback: (payload?: T) => void) { - this.events.off(event, callback); - } } function applyFieldConfigDefaults(fieldConfig: FieldConfigSource, defaults: FieldConfigSource): FieldConfigSource { diff --git a/public/app/types/events.ts b/public/app/types/events.ts index 230c9ac202f..92f5bbe4ea4 100644 --- a/public/app/types/events.ts +++ b/public/app/types/events.ts @@ -1,4 +1,4 @@ -import { eventFactory, TimeRange } from '@grafana/data'; +import { BusEventBase, eventFactory, TimeRange } from '@grafana/data'; import { DashboardModel } from 'app/features/dashboard/state'; /** @@ -106,10 +106,6 @@ export const toggleKioskMode = eventFactory('toggle-kios export const toggleViewMode = eventFactory('toggle-view-mode'); export const timeRangeUpdated = eventFactory('time-range-updated'); - -export const repeatsProcessed = eventFactory('repeats-processed'); -export const rowExpanded = eventFactory('row-expanded'); -export const rowCollapsed = eventFactory('row-collapsed'); export const templateVariableValueUpdated = eventFactory('template-variable-value-updated'); export const submenuVisibilityChanged = eventFactory('submenu-visibility-changed'); @@ -125,5 +121,32 @@ export const elasticQueryUpdated = eventFactory('elastic-query-updated'); export const routeUpdated = eventFactory('$routeUpdate'); -export const queryChanged = eventFactory('queryChanged'); -export const transformationChanged = eventFactory('transformationChanged'); +/** + * Used for syncing queries badge count in panel edit queries tab + * Think we can get rid of this soon + */ +export class PanelQueriesChangedEvent extends BusEventBase { + static type = 'panel-queries-changed'; +} + +/** + * Used for syncing transformations badge count in panel edit transform tab + * Think we can get rid of this soon + */ +export class PanelTransformationsChangedEvent extends BusEventBase { + static type = 'panel-transformations-changed'; +} + +/** + * Used by panel editor to know when panel plugin it'self trigger option updates + */ +export class PanelOptionsChangedEvent extends BusEventBase { + static type = 'panels-options-changed'; +} + +/** + * Used internally by DashboardModel to commmunicate with DashboardGrid that it needs to re-render + */ +export class DashboardPanelsChangedEvent extends BusEventBase { + static type = 'dashboard-panels-changed'; +}