From 6ed35292fe9107b7dae7e8bcb4ec09859a9df3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2022 08:02:24 +0100 Subject: [PATCH] Variables: SceneVariable update process (#57784) * First baby steps * First baby steps * No progress really * Updates * no progress * refactoring * Progress on sub menu and value selectors * Some more tweaks * Lots of progress * Progress * Updates * Progress * Tweaks * Updates * Updates to variable system * Cleaner tests * Update * Some cleanup * correct test name * Renames and moves * prop rename * Fixed scene template interpolator * More tests for SceneObjectBase and fixed issue in EventBus * Updates * More tweaks * More refinements * Fixed test * Added test to EventBus * Clone all scene object arrays * Simplify * tried to merge issue * Update * added more comments to interface * temp progress * Trying to simplify things, but struggling a bit * Updated * Tweaks * Progress on fixing the select componenet and typing, and sharing code in a base class * Updated * Multi select * Simpler loading state * Update * removed failOnConsole * Removed old funcs * Moved logic from update manage to MultiValueVariable * Added tests for MultiValueVariable logic * Made value a more abstract concept to support object values * renamed func to getValueText * Refactored and moved logic to VariableSet * Added test for deactivation and query cancelling * Tweaks * Fixed lint issues --- .../app/features/scenes/components/Scene.tsx | 12 +- .../scenes/components/SceneCanvasText.tsx | 5 +- .../scenes/components/SceneSubMenu.tsx | 22 +++ .../scenes/core/SceneObjectBase.test.ts | 2 +- .../features/scenes/core/SceneObjectBase.tsx | 4 +- public/app/features/scenes/core/types.ts | 1 + public/app/features/scenes/scenes/index.tsx | 3 +- .../features/scenes/scenes/variablesDemo.tsx | 63 +++++++ .../components/VariableValueSelect.tsx | 39 +++++ .../components/VariableValueSelectors.tsx | 85 ++++++++++ .../variables/getVariableDependencies.test.ts | 12 ++ .../variables/getVariableDependencies.ts | 20 +++ ...t.ts => sceneTemplateInterpolator.test.ts} | 18 +- ...bleSet.ts => sceneTemplateInterpolator.ts} | 24 ++- .../variables/sets/SceneVariableSet.test.ts | 95 +++++++++++ .../scenes/variables/sets/SceneVariableSet.ts | 157 ++++++++++++++++++ public/app/features/scenes/variables/types.ts | 48 +++++- .../variables/variants/ConstantVariable.ts | 15 ++ .../variants/MultiValueVariable.test.ts | 51 ++++++ .../variables/variants/MultiValueVariable.ts | 106 ++++++++++++ .../variables/variants/TestVariable.tsx | 79 +++++++++ .../variables/pickers/shared/VariableLink.tsx | 2 +- public/test/setupTests.ts | 1 + 23 files changed, 829 insertions(+), 35 deletions(-) create mode 100644 public/app/features/scenes/components/SceneSubMenu.tsx create mode 100644 public/app/features/scenes/scenes/variablesDemo.tsx create mode 100644 public/app/features/scenes/variables/components/VariableValueSelect.tsx create mode 100644 public/app/features/scenes/variables/components/VariableValueSelectors.tsx create mode 100644 public/app/features/scenes/variables/getVariableDependencies.test.ts create mode 100644 public/app/features/scenes/variables/getVariableDependencies.ts rename public/app/features/scenes/variables/{SceneVariableSet.test.ts => sceneTemplateInterpolator.test.ts} (68%) rename public/app/features/scenes/variables/{SceneVariableSet.ts => sceneTemplateInterpolator.ts} (59%) create mode 100644 public/app/features/scenes/variables/sets/SceneVariableSet.test.ts create mode 100644 public/app/features/scenes/variables/sets/SceneVariableSet.ts create mode 100644 public/app/features/scenes/variables/variants/ConstantVariable.ts create mode 100644 public/app/features/scenes/variables/variants/MultiValueVariable.test.ts create mode 100644 public/app/features/scenes/variables/variants/MultiValueVariable.ts create mode 100644 public/app/features/scenes/variables/variants/TestVariable.tsx diff --git a/public/app/features/scenes/components/Scene.tsx b/public/app/features/scenes/components/Scene.tsx index 2848d2d2a8e..fe4731c13da 100644 --- a/public/app/features/scenes/components/Scene.tsx +++ b/public/app/features/scenes/components/Scene.tsx @@ -14,6 +14,7 @@ interface SceneState extends SceneObjectStatePlain { title: string; layout: SceneObject; actions?: SceneObject[]; + subMenu?: SceneObject; isEditing?: boolean; } @@ -33,7 +34,7 @@ export class Scene extends SceneObjectBase { } function SceneRenderer({ model }: SceneComponentProps) { - const { title, layout, actions = [], isEditing, $editor } = model.useState(); + const { title, layout, actions = [], isEditing, $editor, subMenu } = model.useState(); const toolbarActions = (actions ?? []).map((action) => ); @@ -55,9 +56,12 @@ function SceneRenderer({ model }: SceneComponentProps) { return ( -
- - {$editor && <$editor.Component model={$editor} isEditing={isEditing} />} +
+ {subMenu && } +
+ + {$editor && <$editor.Component model={$editor} isEditing={isEditing} />} +
); diff --git a/public/app/features/scenes/components/SceneCanvasText.tsx b/public/app/features/scenes/components/SceneCanvasText.tsx index aee61d0dc85..93b5e9a74a8 100644 --- a/public/app/features/scenes/components/SceneCanvasText.tsx +++ b/public/app/features/scenes/components/SceneCanvasText.tsx @@ -4,6 +4,7 @@ import { Field, Input } from '@grafana/ui'; import { SceneObjectBase } from '../core/SceneObjectBase'; import { SceneComponentProps, SceneLayoutChildState } from '../core/types'; +import { sceneTemplateInterpolator } from '../variables/sceneTemplateInterpolator'; export interface SceneCanvasTextState extends SceneLayoutChildState { text: string; @@ -13,8 +14,10 @@ export interface SceneCanvasTextState extends SceneLayoutChildState { export class SceneCanvasText extends SceneObjectBase { public static Editor = Editor; + public static Component = ({ model }: SceneComponentProps) => { const { text, fontSize = 20, align = 'left' } = model.useState(); + const textInterpolated = sceneTemplateInterpolator(text, model); const style: CSSProperties = { fontSize: fontSize, @@ -25,7 +28,7 @@ export class SceneCanvasText extends SceneObjectBase { justifyContent: align, }; - return
{text}
; + return
{textInterpolated}
; }; } diff --git a/public/app/features/scenes/components/SceneSubMenu.tsx b/public/app/features/scenes/components/SceneSubMenu.tsx new file mode 100644 index 00000000000..2563c2f2a37 --- /dev/null +++ b/public/app/features/scenes/components/SceneSubMenu.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import { SceneObjectBase } from '../core/SceneObjectBase'; +import { SceneLayoutState, SceneComponentProps } from '../core/types'; + +interface SceneSubMenuState extends SceneLayoutState {} + +export class SceneSubMenu extends SceneObjectBase { + public static Component = SceneSubMenuRenderer; +} + +function SceneSubMenuRenderer({ model }: SceneComponentProps) { + const { children } = model.useState(); + + return ( +
+ {children.map((child) => ( + + ))} +
+ ); +} diff --git a/public/app/features/scenes/core/SceneObjectBase.test.ts b/public/app/features/scenes/core/SceneObjectBase.test.ts index 17c86014163..c8d3f138e76 100644 --- a/public/app/features/scenes/core/SceneObjectBase.test.ts +++ b/public/app/features/scenes/core/SceneObjectBase.test.ts @@ -1,4 +1,4 @@ -import { SceneVariableSet } from '../variables/SceneVariableSet'; +import { SceneVariableSet } from '../variables/sets/SceneVariableSet'; import { SceneDataNode } from './SceneDataNode'; import { SceneObjectBase } from './SceneObjectBase'; diff --git a/public/app/features/scenes/core/SceneObjectBase.tsx b/public/app/features/scenes/core/SceneObjectBase.tsx index d5871def18b..8c4e029e16d 100644 --- a/public/app/features/scenes/core/SceneObjectBase.tsx +++ b/public/app/features/scenes/core/SceneObjectBase.tsx @@ -9,7 +9,9 @@ import { SceneComponentWrapper } from './SceneComponentWrapper'; import { SceneObjectStateChangedEvent } from './events'; import { SceneDataState, SceneObject, SceneComponent, SceneEditor, SceneTimeRange, SceneObjectState } from './types'; -export abstract class SceneObjectBase implements SceneObject { +export abstract class SceneObjectBase + implements SceneObject +{ private _isActive = false; private _subject = new Subject(); private _state: TState; diff --git a/public/app/features/scenes/core/types.ts b/public/app/features/scenes/core/types.ts index 9791ba034e5..b531d6f95e6 100644 --- a/public/app/features/scenes/core/types.ts +++ b/public/app/features/scenes/core/types.ts @@ -114,6 +114,7 @@ export interface SceneEditor extends SceneObject { } export interface SceneTimeRangeState extends SceneObjectStatePlain, TimeRange {} + export interface SceneTimeRange extends SceneObject { onTimeRangeChange(timeRange: TimeRange): void; onIntervalChanged(interval: string): void; diff --git a/public/app/features/scenes/scenes/index.tsx b/public/app/features/scenes/scenes/index.tsx index c28184ab78d..0a1db6b2830 100644 --- a/public/app/features/scenes/scenes/index.tsx +++ b/public/app/features/scenes/scenes/index.tsx @@ -3,9 +3,10 @@ import { Scene } from '../components/Scene'; import { getFlexLayoutTest, getScenePanelRepeaterTest } from './demo'; import { getNestedScene } from './nested'; import { getSceneWithRows } from './sceneWithRows'; +import { getVariablesDemo } from './variablesDemo'; export function getScenes(): Scene[] { - return [getFlexLayoutTest(), getScenePanelRepeaterTest(), getNestedScene(), getSceneWithRows()]; + return [getFlexLayoutTest(), getScenePanelRepeaterTest(), getNestedScene(), getSceneWithRows(), getVariablesDemo()]; } const cache: Record = {}; diff --git a/public/app/features/scenes/scenes/variablesDemo.tsx b/public/app/features/scenes/scenes/variablesDemo.tsx new file mode 100644 index 00000000000..45ae1149d8c --- /dev/null +++ b/public/app/features/scenes/scenes/variablesDemo.tsx @@ -0,0 +1,63 @@ +import { getDefaultTimeRange } from '@grafana/data'; + +import { Scene } from '../components/Scene'; +import { SceneCanvasText } from '../components/SceneCanvasText'; +import { SceneFlexLayout } from '../components/SceneFlexLayout'; +import { SceneSubMenu } from '../components/SceneSubMenu'; +import { SceneTimePicker } from '../components/SceneTimePicker'; +import { SceneTimeRange } from '../core/SceneTimeRange'; +import { VariableValueSelectors } from '../variables/components/VariableValueSelectors'; +import { SceneVariableSet } from '../variables/sets/SceneVariableSet'; +import { TestVariable } from '../variables/variants/TestVariable'; + +export function getVariablesDemo(): Scene { + const scene = new Scene({ + title: 'Variables', + layout: new SceneFlexLayout({ + direction: 'row', + children: [ + new SceneCanvasText({ + text: 'Some text with a variable: ${server} - ${pod}', + fontSize: 40, + align: 'center', + }), + ], + }), + $variables: new SceneVariableSet({ + variables: [ + new TestVariable({ + name: 'server', + query: 'A.*', + value: 'server', + text: '', + delayMs: 1000, + options: [], + }), + new TestVariable({ + name: 'pod', + query: 'A.$server.*', + value: 'pod', + delayMs: 1000, + text: '', + options: [], + }), + new TestVariable({ + name: 'handler', + query: 'A.$server.$pod.*', + value: 'handler', + delayMs: 1000, + isMulti: true, + text: '', + options: [], + }), + ], + }), + $timeRange: new SceneTimeRange(getDefaultTimeRange()), + actions: [new SceneTimePicker({})], + subMenu: new SceneSubMenu({ + children: [new VariableValueSelectors({})], + }), + }); + + return scene; +} diff --git a/public/app/features/scenes/variables/components/VariableValueSelect.tsx b/public/app/features/scenes/variables/components/VariableValueSelect.tsx new file mode 100644 index 00000000000..b70dcc28920 --- /dev/null +++ b/public/app/features/scenes/variables/components/VariableValueSelect.tsx @@ -0,0 +1,39 @@ +import { isArray } from 'lodash'; +import React from 'react'; + +import { Select, MultiSelect } from '@grafana/ui'; + +import { SceneComponentProps } from '../../core/types'; +import { MultiValueVariable } from '../variants/MultiValueVariable'; + +export function VariableValueSelect({ model }: SceneComponentProps) { + const { value, key, loading, isMulti, options } = model.useState(); + + if (isMulti) { + return ( + + ); + } + + return ( +