Skip to content

Commit

Permalink
VizPanel: Adds extendPanelContext so that consumers can control some …
Browse files Browse the repository at this point in the history
…of the PanelContext functions (#409)
  • Loading branch information
torkelo authored Oct 12, 2023
1 parent 5c76be3 commit e584b6b
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 37 deletions.
4 changes: 4 additions & 0 deletions docusaurus/docs/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ in a typical dashboard panel when you view the **JSON** tab in the panel inspect

`VizPanel` has a property named `headerActions` that can be either `React.ReactNode` or a custom `SceneObject`. This property is useful if you want to place links or buttons in the top right corner of the panel header. For example:

## Menu

The menu property of type VizPanelMenu is optionl, when set it defines a menu in the top right of the panel. The menu object is only activated when the dropdown menu itself is rendered. So the best way to add dynamic menu actions and links is by adding them in a [behavior](./advanced-behaviors.md) attached to the menu.

```ts
new VizPanel({
pluginId: 'timeseries',
Expand Down
4 changes: 2 additions & 2 deletions packages/scenes/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#### 🐛 Bug Fix

- Scenes: Interval Variable now considers $__auto_interval [#407](https://github.com/grafana/scenes/pull/407) ([@axelavargas](https://github.com/axelavargas))
- Scenes: Interval Variable now considers $\_\_auto_interval [#407](https://github.com/grafana/scenes/pull/407) ([@axelavargas](https://github.com/axelavargas))

#### Authors: 1

Expand All @@ -40,7 +40,7 @@

- Markup: element data keys changes [#403](https://github.com/grafana/scenes/pull/403) ([@torkelo](https://github.com/torkelo))
- QueryVariable: Fix sort default value [#398](https://github.com/grafana/scenes/pull/398) ([@torkelo](https://github.com/torkelo))
- QueryVariable: Support for queries that contain "$__searchFilter" [#395](https://github.com/grafana/scenes/pull/395) ([@torkelo](https://github.com/torkelo))
- QueryVariable: Support for queries that contain "$\_\_searchFilter" [#395](https://github.com/grafana/scenes/pull/395) ([@torkelo](https://github.com/torkelo))
- TextBoxVariable: Fixes and make it auto size [#394](https://github.com/grafana/scenes/pull/394) ([@torkelo](https://github.com/torkelo))

#### 🐛 Bug Fix
Expand Down
6 changes: 3 additions & 3 deletions packages/scenes/src/behaviors/CursorSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ export class CursorSync extends SceneObjectBase<CursorSyncState> {
return new PanelContextEventBus(this.parent, panel);
};

public getEventsScope = () => {
public getEventsScope() {
if (!this.parent) {
throw new Error('EnableCursorSync cannot be used as a standalone scene object');
}
// Since EnableCursorSync is a behavior, it is not a parent to any object in the scene graph.
// We need to get it's parent in order to provide correct EventBus context to the children.
return this.parent.state.key;
};
return this.parent.state.key!;
}
}

// This serves as a shared EventsBus that is shared by children or CursorSync behavior.
Expand Down
18 changes: 12 additions & 6 deletions packages/scenes/src/components/SceneTimePicker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { SceneTimeRange } from '../core/SceneTimeRange';
import { SceneTimeRangeState } from '../core/types';
import { EmbeddedScene } from './EmbeddedScene';
import { SceneFlexLayout } from './layout/SceneFlexLayout';
import { getShiftedTimeRange, getZoomedTimeRange, SceneTimePicker, SceneTimePickerState, TimeRangeDirection } from './SceneTimePicker';
import {
getShiftedTimeRange,
getZoomedTimeRange,
SceneTimePicker,
SceneTimePickerState,
TimeRangeDirection,
} from './SceneTimePicker';

function setupScene(
timeRangeProps: Partial<SceneTimeRangeState> = {},
Expand Down Expand Up @@ -114,11 +120,11 @@ it('calculates backward shift correctly', () => {
expect(shiftedRange).toEqual({
from: expectedFrom,
to: expectedTo,
raw: { from: expectedFrom, to: expectedTo }
raw: { from: expectedFrom, to: expectedTo },
});
});

it ('calculates forward shift correctly', () => {
it('calculates forward shift correctly', () => {
const from = dateTime('2023-12-17T10:00:00.433Z');
const to = dateTime('2023-12-17T12:00:00.433Z');
const upperLimit = dateTime('2023-12-17T15:48:27.433Z').valueOf();
Expand All @@ -134,11 +140,11 @@ it ('calculates forward shift correctly', () => {
expect(shiftedRange).toEqual({
from: expectedFrom,
to: expectedTo,
raw: { from: expectedFrom, to: expectedTo }
raw: { from: expectedFrom, to: expectedTo },
});
});

it ('calculates forward shift when moving past upper limit correctly', () => {
it('calculates forward shift when moving past upper limit correctly', () => {
const from = dateTime('2023-12-17T10:00:00.433Z');
const to = dateTime('2023-12-17T12:00:00.433Z');
const upperLimit = dateTime('2023-12-17T12:30:00.433Z');
Expand All @@ -154,6 +160,6 @@ it ('calculates forward shift when moving past upper limit correctly', () => {
expect(shiftedRange).toEqual({
from: expectedFrom,
to: expectedTo,
raw: { from: expectedFrom, to: expectedTo }
raw: { from: expectedFrom, to: expectedTo },
});
});
12 changes: 8 additions & 4 deletions packages/scenes/src/components/SceneTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ export class SceneTimePicker extends SceneObjectBase<SceneTimePickerState> {

public onMoveBackward = () => {
const timeRange = sceneGraph.getTimeRange(this);
const { state: { value: range } } = timeRange;
const {
state: { value: range },
} = timeRange;

timeRange.onTimeRangeChange(getShiftedTimeRange(TimeRangeDirection.Backward, range, Date.now()));
};

public onMoveForward = () => {
const timeRange = sceneGraph.getTimeRange(this);
const { state: { value: range } } = timeRange;
const {
state: { value: range },
} = timeRange;

timeRange.onTimeRangeChange(getShiftedTimeRange(TimeRangeDirection.Forward, range, Date.now()));
};
Expand Down Expand Up @@ -81,7 +85,7 @@ export function getZoomedTimeRange(timeRange: TimeRange, factor: number): TimeRa

export enum TimeRangeDirection {
Backward,
Forward
Forward,
}

export function getShiftedTimeRange(dir: TimeRangeDirection, timeRange: TimeRange, upperLimit: number): TimeRange {
Expand Down Expand Up @@ -109,6 +113,6 @@ export function getShiftedTimeRange(dir: TimeRangeDirection, timeRange: TimeRang
return {
from,
to,
raw: { from, to }
raw: { from, to },
};
}
52 changes: 32 additions & 20 deletions packages/scenes/src/components/VizPanel/VizPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,32 @@ export interface VizPanelState<TOptions = {}, TFieldConfig = {}> extends SceneOb
fieldConfig: FieldConfigSource<DeepPartial<TFieldConfig>>;
pluginVersion?: string;
displayMode?: 'default' | 'transparent';
/**
* Only shows header on hover, absolutly positioned above the panel.
*/
hoverHeader?: boolean;
/**
* Defines a menu in the top right of the panel. The menu object is only activated when the dropdown menu itself is shown.
* So the best way to add dynamic menu actions and links is by adding them in a behavior attached to the menu.
*/
menu?: VizPanelMenu;
/**
* Add action to the top right panel header
*/
headerActions?: React.ReactNode | SceneObject | SceneObject[];
// internal state
pluginLoadError?: string;
pluginInstanceState?: any;
/**
* Mainly for advanced use cases that need custom handling of PanelContext callbacks.
*/
extendPanelContext?: (vizPanel: VizPanel, context: PanelContext) => void;
/**
* @internal
* Only for use from core to handle migration from old angular panels
**/
_UNSAFE_customMigrationHandler?: (panel: PanelModel, plugin: PanelPlugin) => void;
/** Internal */
_pluginLoadError?: string;
/** Internal */
_pluginInstanceState?: any;
}

export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends SceneObjectBase<
Expand All @@ -67,8 +82,8 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene
});

// Not part of state as this is not serializable
protected _panelContext?: PanelContext;
private _plugin?: PanelPlugin;
private _panelContext?: PanelContext;
private _prevData?: PanelData;
private _dataWithFieldConfig?: PanelData;
private _structureRev: number = 0;
Expand All @@ -91,8 +106,6 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene
if (!this._plugin) {
this._loadPlugin(this.state.pluginId);
}

this.buildPanelContext();
}

private _loadPlugin(pluginId: string) {
Expand All @@ -109,7 +122,7 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene
});
} catch (err: unknown) {
this._pluginLoaded(getPanelPluginNotFound(pluginId));
this.setState({ pluginLoadError: (err as Error).message });
this.setState({ _pluginLoadError: (err as Error).message });
}
}
}
Expand Down Expand Up @@ -165,7 +178,7 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene

public getPanelContext(): PanelContext {
if (!this._panelContext) {
this.buildPanelContext();
this._panelContext = this.buildPanelContext();
}

return this._panelContext!;
Expand Down Expand Up @@ -298,7 +311,7 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene
};

private _onInstanceStateChange = (state: any) => {
this.setState({ pluginInstanceState: state });
this.setState({ _pluginInstanceState: state });
};

private _onToggleLegendSort = (sortKey: string) => {
Expand Down Expand Up @@ -330,31 +343,30 @@ export class VizPanel<TOptions = {}, TFieldConfig extends {} = {}> extends Scene
} as TOptions);
};

private buildPanelContext() {
private buildPanelContext(): PanelContext {
const sync = getCursorSyncScope(this);

this._panelContext = {
// @ts-ignore Waits for core release
const context = {
eventsScope: sync ? sync.getEventsScope() : '__global_',
eventBus: sync ? sync.getEventsBus(this) : getAppEvents(),
app: CoreApp.Unknown, // TODO,
app: CoreApp.Unknown,
sync: () => {
if (sync) {
return sync.state.sync;
}
return DashboardCursorSync.Off;
}, // TODO
},
onSeriesColorChange: this._onSeriesColorChange,
onToggleSeriesVisibility: this._onSeriesVisibilityChange,
onToggleLegendSort: this._onToggleLegendSort,
onInstanceStateChange: this._onInstanceStateChange,
onAnnotationCreate: () => {}, //this.onAnnotationCreate,
onAnnotationUpdate: () => {}, //this.onAnnotationUpdate,
onAnnotationDelete: () => {}, //this.onAnnotationDelete,
canAddAnnotations: () => false, //props.dashboard.canAddAnnotations.bind(props.dashboard),
canEditAnnotations: () => false, //props.dashboard.canEditAnnotations.bind(props.dashboard),
canDeleteAnnotations: () => false, // props.dashboard.canDeleteAnnotations.bind(props.dashboard),
};

if (this.state.extendPanelContext) {
this.state.extendPanelContext(this, context);
}

return context;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/scenes/src/components/VizPanel/VizPanelRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function VizPanelRenderer({ model }: SceneComponentProps<VizPanel>) {
description,
options,
fieldConfig,
pluginLoadError,
_pluginLoadError,
$data,
displayMode,
hoverHeader,
Expand Down Expand Up @@ -94,7 +94,7 @@ export function VizPanelRenderer({ model }: SceneComponentProps<VizPanel>) {
title={titleInterpolated}
description={description ? () => model.interpolate(description) : ''}
loadingState={data.state}
statusMessage={getChromeStatusMessage(data, pluginLoadError)}
statusMessage={getChromeStatusMessage(data, _pluginLoadError)}
width={width}
height={height}
displayMode={displayMode}
Expand Down

0 comments on commit e584b6b

Please sign in to comment.