mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 06:22:35 +08:00
Dashboard: Variable controls via simple react component (#103442)
* Dashboard: Variable controls refactor * Update tests * Fix name * fix lint
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { SceneDataLayerControls, SceneVariableSet, TextBoxVariable, VariableValueSelectors } from '@grafana/scenes';
|
||||
import { SceneVariableSet, TextBoxVariable } from '@grafana/scenes';
|
||||
|
||||
import { DashboardControls, DashboardControlsState } from './DashboardControls';
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
@ -10,7 +10,6 @@ describe('DashboardControls', () => {
|
||||
describe('Given a standard scene', () => {
|
||||
it('should initialize with default values', () => {
|
||||
const scene = buildTestScene();
|
||||
expect(scene.state.variableControls).toEqual([]);
|
||||
expect(scene.state.timePicker).toBeDefined();
|
||||
expect(scene.state.refreshPicker).toBeDefined();
|
||||
});
|
||||
@ -38,9 +37,7 @@ describe('DashboardControls', () => {
|
||||
});
|
||||
|
||||
it('should render visible controls', async () => {
|
||||
const scene = buildTestScene({
|
||||
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
|
||||
});
|
||||
const scene = buildTestScene({});
|
||||
const renderer = render(<scene.Component model={scene} />);
|
||||
|
||||
expect(await renderer.findByTestId(selectors.pages.Dashboard.Controls)).toBeInTheDocument();
|
||||
@ -55,7 +52,6 @@ describe('DashboardControls', () => {
|
||||
hideTimeControls: true,
|
||||
hideVariableControls: true,
|
||||
hideLinksControls: true,
|
||||
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
|
||||
});
|
||||
const renderer = render(<scene.Component model={scene} />);
|
||||
|
||||
|
@ -4,7 +4,6 @@ import { GrafanaTheme2, VariableHide } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import {
|
||||
SceneObjectState,
|
||||
SceneObject,
|
||||
SceneObjectBase,
|
||||
SceneComponentProps,
|
||||
SceneTimePicker,
|
||||
@ -22,9 +21,10 @@ import { PanelEditControls } from '../panel-edit/PanelEditControls';
|
||||
import { getDashboardSceneFor } from '../utils/utils';
|
||||
|
||||
import { DashboardLinksControls } from './DashboardLinksControls';
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { VariableControls } from './VariableControls';
|
||||
|
||||
export interface DashboardControlsState extends SceneObjectState {
|
||||
variableControls: SceneObject[];
|
||||
timePicker: SceneTimePicker;
|
||||
refreshPicker: SceneRefreshPicker;
|
||||
hideTimeControls?: boolean;
|
||||
@ -73,7 +73,6 @@ export class DashboardControls extends SceneObjectBase<DashboardControlsState> {
|
||||
|
||||
public constructor(state: Partial<DashboardControlsState>) {
|
||||
super({
|
||||
variableControls: [],
|
||||
timePicker: state.timePicker ?? new SceneTimePicker({}),
|
||||
refreshPicker: state.refreshPicker ?? new SceneRefreshPicker({}),
|
||||
...state,
|
||||
@ -119,8 +118,7 @@ export class DashboardControls extends SceneObjectBase<DashboardControlsState> {
|
||||
}
|
||||
|
||||
function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardControls>) {
|
||||
const { variableControls, refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } =
|
||||
model.useState();
|
||||
const { refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } = model.useState();
|
||||
const dashboard = getDashboardSceneFor(model);
|
||||
const { links, editPanel } = dashboard.useState();
|
||||
const styles = useStyles2(getStyles);
|
||||
@ -137,7 +135,12 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
||||
className={cx(styles.controls, editPanel && styles.controlsPanelEdit)}
|
||||
>
|
||||
<Stack grow={1} wrap={'wrap'}>
|
||||
{!hideVariableControls && variableControls.map((c) => <c.Component model={c} key={c.state.key} />)}
|
||||
{!hideVariableControls && (
|
||||
<>
|
||||
<VariableControls dashboard={dashboard} />
|
||||
<DataLayerControls dashboard={dashboard} />
|
||||
</>
|
||||
)}
|
||||
<Box grow={1} />
|
||||
{!hideLinksControls && !editPanel && <DashboardLinksControls links={links} dashboard={dashboard} />}
|
||||
{editPanel && <PanelEditControls panelEditor={editPanel} />}
|
||||
@ -153,6 +156,18 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
||||
);
|
||||
}
|
||||
|
||||
function DataLayerControls({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const layers = sceneGraph.getDataLayers(dashboard, true);
|
||||
|
||||
return (
|
||||
<>
|
||||
{layers.map((layer) => (
|
||||
<layer.Component model={layer} key={layer.state.key} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
controls: css({
|
||||
|
@ -0,0 +1,70 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { VariableHide } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { sceneGraph, useSceneObjectState, SceneVariable, SceneVariableState, ControlsLabel } from '@grafana/scenes';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
export function VariableControls({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const variables = sceneGraph.getVariables(dashboard)!.useState();
|
||||
|
||||
return (
|
||||
<>
|
||||
{variables.variables.map((variable) => (
|
||||
<VariableValueSelectWrapper key={variable.state.key} variable={variable} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface VariableSelectProps {
|
||||
variable: SceneVariable;
|
||||
}
|
||||
|
||||
export function VariableValueSelectWrapper({ variable }: VariableSelectProps) {
|
||||
const state = useSceneObjectState<SceneVariableState>(variable, { shouldActivateOrKeepAlive: true });
|
||||
|
||||
if (state.hide === VariableHide.hideVariable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={containerStyle} data-testid={selectors.pages.Dashboard.SubMenu.submenuItem}>
|
||||
<VariableLabel variable={variable} />
|
||||
<variable.Component model={variable} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function VariableLabel({ variable }: VariableSelectProps) {
|
||||
const { state } = variable;
|
||||
|
||||
if (variable.state.hide === VariableHide.hideLabel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const labelOrName = state.label || state.name;
|
||||
const elementId = `var-${state.key}`;
|
||||
|
||||
return (
|
||||
<ControlsLabel
|
||||
htmlFor={elementId}
|
||||
isLoading={state.loading}
|
||||
onCancel={() => variable.onCancel?.()}
|
||||
label={labelOrName}
|
||||
error={state.error}
|
||||
layout={'horizontal'}
|
||||
description={state.description ?? undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const containerStyle = css({
|
||||
display: 'flex',
|
||||
// No border for second element (inputs) as label and input border is shared
|
||||
'> :nth-child(2)': css({
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
}),
|
||||
});
|
@ -10,14 +10,12 @@ import {
|
||||
GroupByVariable,
|
||||
IntervalVariable,
|
||||
QueryVariable,
|
||||
SceneDataLayerControls,
|
||||
SceneRefreshPicker,
|
||||
SceneTimePicker,
|
||||
SceneTimeRange,
|
||||
SceneVariable,
|
||||
SceneVariableSet,
|
||||
TextBoxVariable,
|
||||
VariableValueSelectors,
|
||||
} from '@grafana/scenes';
|
||||
import {
|
||||
AdhocVariableKind,
|
||||
@ -191,7 +189,6 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<D
|
||||
annotationLayers,
|
||||
}),
|
||||
controls: new DashboardControls({
|
||||
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
|
||||
timePicker: new SceneTimePicker({
|
||||
quickRanges: dashboard.timeSettings.quickRanges,
|
||||
}),
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
AdHocFiltersVariable,
|
||||
behaviors,
|
||||
ConstantVariable,
|
||||
SceneDataLayerControls,
|
||||
SceneDataTransformer,
|
||||
SceneGridLayout,
|
||||
SceneGridRow,
|
||||
@ -836,7 +835,6 @@ describe('transformSaveModelToScene', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as DashboardDataDTO, meta: {} });
|
||||
|
||||
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
|
||||
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
|
||||
|
||||
const dataLayers = scene.state.$data as DashboardDataLayerSet;
|
||||
expect(dataLayers.state.annotationLayers).toHaveLength(4);
|
||||
@ -864,7 +862,6 @@ describe('transformSaveModelToScene', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as DashboardDataDTO, meta: {} });
|
||||
|
||||
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
|
||||
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
|
||||
|
||||
const dataLayers = scene.state.$data as DashboardDataLayerSet;
|
||||
expect(dataLayers.state.alertStatesLayer).toBeDefined();
|
||||
@ -877,7 +874,6 @@ describe('transformSaveModelToScene', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as DashboardDataDTO, meta: {} });
|
||||
|
||||
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
|
||||
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
|
||||
|
||||
const dataLayers = scene.state.$data as DashboardDataLayerSet;
|
||||
expect(dataLayers.state.alertStatesLayer).toBeDefined();
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
SceneGridRow,
|
||||
SceneTimeRange,
|
||||
SceneVariableSet,
|
||||
VariableValueSelectors,
|
||||
SceneRefreshPicker,
|
||||
SceneObject,
|
||||
VizPanelMenu,
|
||||
@ -17,7 +16,6 @@ import {
|
||||
VizPanelState,
|
||||
SceneGridItemLike,
|
||||
SceneDataLayerProvider,
|
||||
SceneDataLayerControls,
|
||||
UserActionEvent,
|
||||
SceneInteractionProfileEvent,
|
||||
SceneObjectState,
|
||||
@ -278,7 +276,6 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel,
|
||||
$behaviors: behaviorList,
|
||||
$data: new DashboardDataLayerSet({ annotationLayers, alertStatesLayer }),
|
||||
controls: new DashboardControls({
|
||||
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
|
||||
timePicker: new SceneTimePicker({
|
||||
quickRanges: oldModel.timepicker.quick_ranges,
|
||||
}),
|
||||
|
Reference in New Issue
Block a user