diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index b53074f8d0f..1e303539e32 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -77,6 +77,10 @@ export const Components = { containerByTitle: (title: string) => `${title} panel`, headerCornerInfo: (mode: string) => `Panel header ${mode}`, loadingBar: () => `Panel loading bar`, + HoverWidget: { + container: 'data-test-id hover-header-container', + dragIcon: 'data-testid drag-icon', + }, }, Visualization: { Graph: { diff --git a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx index 6a78d7d9389..3105d991c54 100644 --- a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx @@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css'; import React, { ReactElement, useCallback, useRef, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { useStyles2 } from '../../themes'; import { Icon } from '../Icon/Icon'; @@ -16,6 +17,8 @@ interface Props { dragClass?: string; } +const selectors = e2eSelectors.components.Panels.Panel.HoverWidget; + export function HoverWidget({ menu, title, dragClass, children, offset = -32 }: Props) { const styles = useStyles2(getStyles); const draggableRef = useRef(null); @@ -39,7 +42,7 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
{dragClass && (
)} - {!title &&
Untitled
} + {!title &&
Untitled
} {children} {menu && ( , metaOverrides?: Partia describe('PublicDashboardPage', () => { beforeEach(() => { + config.featureToggles.publicDashboards = true; + config.featureToggles.newPanelChromeUI = true; + jest.clearAllMocks(); }); @@ -175,6 +180,68 @@ describe('PublicDashboardPage', () => { expect(screen.queryByTestId(publicDashboardSelector.NotAvailable.container)).not.toBeInTheDocument(); }); }); + + it('Should render panel with hover widget but without drag icon when panel title is undefined', async () => { + const fieldConfig: FieldConfigSource = { + defaults: { + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [ + { + color: 'green', + value: 1, + }, + { + color: 'red', + value: 80, + }, + ], + }, + }, + overrides: [], + }; + const panels: Panel[] = [ + { + id: 1, + fieldConfig, + gridPos: { + h: 8, + w: 12, + x: 0, + y: 0, + }, + options: {}, + title: undefined, + type: 'timeseries', + repeatDirection: 'h', + transformations: [], + transparent: false, + }, + ]; + + const newState = { + dashboard: { + getModel: () => getTestDashboard({ panels }), + initError: null, + initPhase: DashboardInitPhase.Completed, + permissions: [], + }, + }; + setup(undefined, newState); + + await waitFor(() => { + expect(screen.getByTestId(selectors.Panels.Panel.HoverWidget.container)).toBeInTheDocument(); + }); + await userEvent.hover(screen.getByTestId(selectors.Panels.Panel.HoverWidget.container)); + expect(screen.queryByTestId(selectors.Panels.Panel.HoverWidget.dragIcon)).not.toBeInTheDocument(); + }); + + it('Should render panel without hover widget when panel title is not undefined', async () => { + setup(undefined, newState); + await waitFor(() => { + expect(screen.queryByTestId(selectors.Panels.Panel.HoverWidget.container)).not.toBeInTheDocument(); + }); + }); }); describe('Given a public dashboard with time range enabled', () => { diff --git a/public/app/features/dashboard/containers/PublicDashboardPage.tsx b/public/app/features/dashboard/containers/PublicDashboardPage.tsx index bf4ee765c2a..12e5fa6cda8 100644 --- a/public/app/features/dashboard/containers/PublicDashboardPage.tsx +++ b/public/app/features/dashboard/containers/PublicDashboardPage.tsx @@ -122,7 +122,7 @@ const PublicDashboardPage = (props: Props) => { const getStyles = (theme: GrafanaTheme2) => ({ gridContainer: css({ flex: 1, - padding: theme.spacing(0, 2, 2, 2), + padding: theme.spacing(2, 2, 2, 2), overflow: 'auto', }), }); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index efdfc481df9..532f1e3b824 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -142,7 +142,7 @@ export class DashboardGrid extends PureComponent { return { top, bottom: this.lastPanelBottom }; } - renderPanels(gridWidth: number) { + renderPanels(gridWidth: number, isDashboardDraggable: boolean) { const panelElements = []; // Reset last panel bottom @@ -171,7 +171,7 @@ export class DashboardGrid extends PureComponent { isViewing={panel.isViewing} > {(width: number, height: number) => { - return this.renderPanel(panel, width, height); + return this.renderPanel(panel, width, height, isDashboardDraggable); }} ); @@ -180,7 +180,7 @@ export class DashboardGrid extends PureComponent { return panelElements; } - renderPanel(panel: PanelModel, width: number, height: number) { + renderPanel(panel: PanelModel, width: number, height: number, isDraggable: boolean) { if (panel.type === 'row') { return ; } @@ -202,6 +202,7 @@ export class DashboardGrid extends PureComponent { dashboard={this.props.dashboard} isEditing={panel.isEditing} isViewing={panel.isViewing} + isDraggable={isDraggable} width={width} height={height} hideMenu={this.props.hidePanelMenus} @@ -258,7 +259,7 @@ export class DashboardGrid extends PureComponent { onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} > - {this.renderPanels(width)} + {this.renderPanels(width, draggable)}
) : ( @@ -290,7 +291,7 @@ export class DashboardGrid extends PureComponent { onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} > - {this.renderPanels(width)} + {this.renderPanels(width, draggable)} ); diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 3e6a31f5b5a..89da60bb032 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -17,6 +17,7 @@ export interface OwnProps { dashboard: DashboardModel; isEditing: boolean; isViewing: boolean; + isDraggable?: boolean; width: number; height: number; lazy?: boolean; @@ -72,7 +73,18 @@ export class DashboardPanelUnconnected extends PureComponent { }; renderPanel = ({ isInView }: { isInView: boolean }) => { - const { dashboard, panel, isViewing, isEditing, width, height, plugin, timezone, hideMenu } = this.props; + const { + dashboard, + panel, + isViewing, + isEditing, + width, + height, + plugin, + timezone, + hideMenu, + isDraggable = true, + } = this.props; if (!plugin) { return null; @@ -87,6 +99,7 @@ export class DashboardPanelUnconnected extends PureComponent { isViewing={isViewing} isEditing={isEditing} isInView={isInView} + isDraggable={isDraggable} width={width} height={height} /> @@ -101,6 +114,7 @@ export class DashboardPanelUnconnected extends PureComponent { isViewing={isViewing} isEditing={isEditing} isInView={isInView} + isDraggable={isDraggable} width={width} height={height} onInstanceStateChange={this.onInstanceStateChange} diff --git a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx index 47d9f581ef9..435341e227d 100644 --- a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx @@ -28,6 +28,7 @@ interface OwnProps { isViewing: boolean; isEditing: boolean; isInView: boolean; + isDraggable?: boolean; width: number; height: number; hideMenu?: boolean; diff --git a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx index a05518aacde..a593580c61e 100644 --- a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx +++ b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx @@ -65,6 +65,7 @@ export interface Props { isViewing: boolean; isEditing: boolean; isInView: boolean; + isDraggable?: boolean; width: number; height: number; onInstanceStateChange: (value: any) => void; diff --git a/public/app/features/dashboard/utils/getPanelChromeProps.tsx b/public/app/features/dashboard/utils/getPanelChromeProps.tsx index fb4a68d5c99..90cf4261878 100644 --- a/public/app/features/dashboard/utils/getPanelChromeProps.tsx +++ b/public/app/features/dashboard/utils/getPanelChromeProps.tsx @@ -17,6 +17,7 @@ interface CommonProps { isViewing: boolean; isEditing: boolean; isInView: boolean; + isDraggable?: boolean; width: number; height: number; hideMenu?: boolean; @@ -100,7 +101,8 @@ export function getPanelChromeProps(props: CommonProps) { const description = props.panel.description ? onShowPanelDescription() : undefined; - const dragClass = !(props.isViewing || props.isEditing) ? 'grid-drag-handle' : ''; + const dragClass = + !(props.isViewing || props.isEditing) && Boolean(props.isDraggable ?? true) ? 'grid-drag-handle' : ''; const title = props.panel.getDisplayTitle();