PublicDashboards: HoverWidget in panel fix (#66685)

Hover widget modified for public dashboards.
This commit is contained in:
Juan Cabanas 2023-04-26 09:06:38 -03:00 committed by GitHub
parent a62cb96089
commit 47aa8c7654
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 12 deletions

View File

@ -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: {

View File

@ -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<HTMLDivElement>(null);
@ -39,7 +42,7 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
<div
className={cx(styles.container, { 'show-on-hover': !menuOpen })}
style={{ top: `${offset}px` }}
data-testid="hover-header-container"
data-testid={selectors.container}
>
{dragClass && (
<div
@ -47,11 +50,12 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
onPointerDown={onPointerDown}
onPointerUp={onPointerUp}
ref={draggableRef}
data-testid={selectors.dragIcon}
>
<Icon name="expand-arrows" className={styles.draggableIcon} />
</div>
)}
{!title && <h6 className={cx(styles.untitled, styles.draggable, dragClass)}>Untitled</h6>}
{!title && <h6 className={cx(styles.untitled, { [styles.draggable]: !!dragClass }, dragClass)}>Untitled</h6>}
{children}
{menu && (
<PanelMenu

View File

@ -1,4 +1,5 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
@ -8,11 +9,12 @@ import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { locationService } from '@grafana/runtime';
import { Dashboard, DashboardCursorSync } from '@grafana/schema/src';
import { Dashboard, DashboardCursorSync, FieldConfigSource, ThresholdsMode, Panel } from '@grafana/schema/src';
import config from 'app/core/config';
import { GrafanaContext } from 'app/core/context/GrafanaContext';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { DashboardInitPhase, DashboardMeta, DashboardRoutes } from 'app/types';
import * as appTypes from 'app/types';
import { DashboardInitPhase, DashboardMeta, DashboardRoutes } from 'app/types';
import { SafeDynamicImport } from '../../../core/components/DynamicImports/SafeDynamicImport';
import { configureStore } from '../../../store/configureStore';
@ -125,6 +127,9 @@ const getTestDashboard = (overrides?: Partial<Dashboard>, 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', () => {

View File

@ -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',
}),
});

View File

@ -142,7 +142,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
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<Props, State> {
isViewing={panel.isViewing}
>
{(width: number, height: number) => {
return this.renderPanel(panel, width, height);
return this.renderPanel(panel, width, height, isDashboardDraggable);
}}
</GrafanaGridItem>
);
@ -180,7 +180,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
return panelElements;
}
renderPanel(panel: PanelModel, width: number, height: number) {
renderPanel(panel: PanelModel, width: number, height: number, isDraggable: boolean) {
if (panel.type === 'row') {
return <DashboardRow key={panel.key} panel={panel} dashboard={this.props.dashboard} />;
}
@ -202,6 +202,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
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<Props, State> {
onResizeStop={this.onResizeStop}
onLayoutChange={this.onLayoutChange}
>
{this.renderPanels(width)}
{this.renderPanels(width, draggable)}
</ReactGridLayout>
</div>
) : (
@ -290,7 +291,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
onResizeStop={this.onResizeStop}
onLayoutChange={this.onLayoutChange}
>
{this.renderPanels(width)}
{this.renderPanels(width, draggable)}
</ReactGridLayout>
</div>
);

View File

@ -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<Props> {
};
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<Props> {
isViewing={isViewing}
isEditing={isEditing}
isInView={isInView}
isDraggable={isDraggable}
width={width}
height={height}
/>
@ -101,6 +114,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
isViewing={isViewing}
isEditing={isEditing}
isInView={isInView}
isDraggable={isDraggable}
width={width}
height={height}
onInstanceStateChange={this.onInstanceStateChange}

View File

@ -28,6 +28,7 @@ interface OwnProps {
isViewing: boolean;
isEditing: boolean;
isInView: boolean;
isDraggable?: boolean;
width: number;
height: number;
hideMenu?: boolean;

View File

@ -65,6 +65,7 @@ export interface Props {
isViewing: boolean;
isEditing: boolean;
isInView: boolean;
isDraggable?: boolean;
width: number;
height: number;
onInstanceStateChange: (value: any) => void;

View File

@ -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();