diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx
index dc4c492bf92..c01808f411e 100644
--- a/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx
+++ b/public/app/features/dashboard-scene/scene/DashboardControls.test.tsx
@@ -14,17 +14,56 @@ describe('DashboardControls', () => {
expect(scene.state.refreshPicker).toBeDefined();
});
- it('should return if time controls are hidden', () => {
- const scene = buildTestScene({
- hideTimeControls: false,
- hideVariableControls: false,
- hideLinksControls: false,
+ describe('.hasControls()', () => {
+ it('should return TRUE if any of the controls are available', () => {
+ const scene = buildTestScene({
+ hideTimeControls: false,
+ hideVariableControls: false,
+ hideLinksControls: false,
+ hideDashboardControls: false,
+ });
+
+ // All controls visible
+ expect(scene.hasControls()).toBeTruthy();
+
+ // Hiding time controls
+ scene.setState({
+ hideTimeControls: true,
+ hideVariableControls: false,
+ hideLinksControls: false,
+ hideDashboardControls: false,
+ });
+ expect(scene.hasControls()).toBeTruthy();
+
+ // Hide variable controls as well
+ scene.setState({
+ hideTimeControls: true,
+ hideVariableControls: true,
+ hideLinksControls: false,
+ hideDashboardControls: false,
+ });
+ expect(scene.hasControls()).toBeTruthy();
+
+ // Hide link controls as well
+ scene.setState({
+ hideTimeControls: true,
+ hideVariableControls: true,
+ hideLinksControls: true,
+ hideDashboardControls: false,
+ });
+ expect(scene.hasControls()).toBeTruthy();
+ });
+
+ it('should return FALSE if no controls are available', () => {
+ const scene = buildTestScene({
+ hideTimeControls: true,
+ hideVariableControls: true,
+ hideLinksControls: true,
+ hideDashboardControls: true,
+ });
+
+ expect(scene.hasControls()).toBeFalsy();
});
- expect(scene.hasControls()).toBeTruthy();
- scene.setState({ hideTimeControls: true });
- expect(scene.hasControls()).toBeTruthy();
- scene.setState({ hideVariableControls: true, hideLinksControls: true });
- expect(scene.hasControls()).toBeFalsy();
});
});
@@ -52,10 +91,11 @@ describe('DashboardControls', () => {
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
+ hideDashboardControls: true,
});
const renderer = render();
- expect(await renderer.queryByTestId(selectors.pages.Dashboard.Controls)).not.toBeInTheDocument();
+ expect(renderer.queryByTestId(selectors.pages.Dashboard.Controls)).not.toBeInTheDocument();
});
});
@@ -63,7 +103,12 @@ describe('DashboardControls', () => {
it('should return keys', () => {
const scene = buildTestScene();
// @ts-expect-error
- expect(scene._urlSync.getKeys()).toEqual(['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks']);
+ expect(scene._urlSync.getKeys()).toEqual([
+ '_dash.hideTimePicker',
+ '_dash.hideVariables',
+ '_dash.hideLinks',
+ '_dash.hideDashboardControls',
+ ]);
});
it('should not return url state for hide flags', () => {
@@ -73,6 +118,7 @@ describe('DashboardControls', () => {
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
+ hideDashboardControls: true,
});
expect(scene.getUrlState()).toEqual({});
});
@@ -83,18 +129,22 @@ describe('DashboardControls', () => {
'_dash.hideTimePicker': 'true',
'_dash.hideVariables': 'true',
'_dash.hideLinks': 'true',
+ '_dash.hideDashboardControls': 'true',
});
expect(scene.state.hideTimeControls).toBeTruthy();
expect(scene.state.hideVariableControls).toBeTruthy();
expect(scene.state.hideLinksControls).toBeTruthy();
+ expect(scene.state.hideDashboardControls).toBeTruthy();
scene.updateFromUrl({
'_dash.hideTimePicker': '',
'_dash.hideVariables': '',
'_dash.hideLinks': '',
+ '_dash.hideDashboardControls': '',
});
expect(scene.state.hideTimeControls).toBeTruthy();
expect(scene.state.hideVariableControls).toBeTruthy();
expect(scene.state.hideLinksControls).toBeTruthy();
+ expect(scene.state.hideDashboardControls).toBeTruthy();
});
it('should not override state if no new state comes from url', () => {
@@ -102,11 +152,13 @@ describe('DashboardControls', () => {
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
+ hideDashboardControls: true,
});
scene.updateFromUrl({});
expect(scene.state.hideTimeControls).toBeTruthy();
expect(scene.state.hideVariableControls).toBeTruthy();
expect(scene.state.hideLinksControls).toBeTruthy();
+ expect(scene.state.hideDashboardControls).toBeTruthy();
});
it('should not call setState if no changes', () => {
@@ -114,6 +166,7 @@ describe('DashboardControls', () => {
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
+ hideDashboardControls: true,
});
const setState = jest.spyOn(scene, 'setState');
@@ -121,6 +174,7 @@ describe('DashboardControls', () => {
'_dash.hideTimePicker': 'true',
'_dash.hideVariables': 'true',
'_dash.hideLinks': 'true',
+ '_dash.hideDashboardControls': 'true',
});
expect(setState).toHaveBeenCalledTimes(0);
@@ -151,6 +205,19 @@ function buildTestScene(state?: Partial): DashboardContr
targetBlank: false,
tooltip: 'Link',
},
+ {
+ title: 'Link (dashboard controls)',
+ url: 'http://localhost:3000/$A',
+ type: 'link',
+ asDropdown: false,
+ icon: '',
+ includeVars: true,
+ keepTime: true,
+ tags: [],
+ targetBlank: false,
+ tooltip: 'Link',
+ placement: 'inControlsMenu',
+ },
],
$variables: new SceneVariableSet({
variables: [variable],
diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.tsx
index f553cdb77b3..1a22dd8a2c0 100644
--- a/public/app/features/dashboard-scene/scene/DashboardControls.tsx
+++ b/public/app/features/dashboard-scene/scene/DashboardControls.tsx
@@ -31,6 +31,8 @@ export interface DashboardControlsState extends SceneObjectState {
hideTimeControls?: boolean;
hideVariableControls?: boolean;
hideLinksControls?: boolean;
+ // Hides the dashbaord-controls dropdown menu
+ hideDashboardControls?: boolean;
}
export class DashboardControls extends SceneObjectBase {
@@ -41,7 +43,7 @@ export class DashboardControls extends SceneObjectBase {
});
protected _urlSync = new SceneObjectUrlSyncConfig(this, {
- keys: ['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks'],
+ keys: ['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks', '_dash.hideDashboardControls'],
});
/**
@@ -53,7 +55,7 @@ export class DashboardControls extends SceneObjectBase {
}
updateFromUrl(values: SceneObjectUrlValues) {
- const { hideTimeControls, hideVariableControls, hideLinksControls } = this.state;
+ const { hideTimeControls, hideVariableControls, hideLinksControls, hideDashboardControls } = this.state;
const isEnabledViaUrl = (key: string) => values[key] === 'true' || values[key] === '';
// Only allow hiding, never "unhiding" from url
@@ -70,6 +72,10 @@ export class DashboardControls extends SceneObjectBase {
if (!hideLinksControls && isEnabledViaUrl('_dash.hideLinks')) {
this.setState({ hideLinksControls: true });
}
+
+ if (!hideDashboardControls && isEnabledViaUrl('_dash.hideDashboardControls')) {
+ this.setState({ hideDashboardControls: true });
+ }
}
public constructor(state: Partial) {
@@ -104,6 +110,18 @@ export class DashboardControls extends SceneObjectBase {
}
}
+ // Dashboard controls is a separate dropdown menu at the top-right of the controls
+ public hasDashboardControls(): boolean {
+ const dashboard = getDashboardSceneFor(this);
+ const { links } = dashboard.state;
+ const hasControlMenuVariables = sceneGraph
+ .getVariables(dashboard)
+ ?.state.variables.some((v) => v.state.showInControlsMenu === true);
+ const hasControlMenuLinks = links.some((link) => link.placement === 'inControlsMenu');
+
+ return hasControlMenuVariables || hasControlMenuLinks;
+ }
+
public hasControls(): boolean {
const hasVariables = sceneGraph
.getVariables(this)
@@ -113,22 +131,25 @@ export class DashboardControls extends SceneObjectBase {
const hideLinks = this.state.hideLinksControls || !hasLinks;
const hideVariables = this.state.hideVariableControls || (!hasAnnotations && !hasVariables);
const hideTimePicker = this.state.hideTimeControls;
+ const hideDashboardControls = this.state.hideDashboardControls || !this.hasDashboardControls();
- return !(hideVariables && hideLinks && hideTimePicker);
+ return !(hideVariables && hideLinks && hideTimePicker && hideDashboardControls);
}
}
function DashboardControlsRenderer({ model }: SceneComponentProps) {
- const { refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } = model.useState();
+ const {
+ refreshPicker,
+ timePicker,
+ hideTimeControls,
+ hideVariableControls,
+ hideLinksControls,
+ hideDashboardControls,
+ } = model.useState();
const dashboard = getDashboardSceneFor(model);
const { links, editPanel } = dashboard.useState();
const styles = useStyles2(getStyles);
const showDebugger = window.location.search.includes('scene-debugger');
- const hasControlMenuVariables = sceneGraph
- .getVariables(dashboard)
- .useState()
- .variables.some((v) => v.state.showInControlsMenu === true);
- const hasControlMenuLinks = links.some((link) => link.placement === 'inControlsMenu');
if (!model.hasControls()) {
// To still have spacing when no controls are rendered
@@ -157,7 +178,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps
)}
- {(hasControlMenuVariables || hasControlMenuLinks) && (
+ {!hideDashboardControls && model.hasDashboardControls() && (