PanelChrome: Implement hover header (#61774)

Closes #59078

Co-authored-by: polinaboneva <polina.boneva@grafana.com>
Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
Co-authored-by: Alexandra Vargas <alexa1866@gmail.com>
This commit is contained in:
kay delaney
2023-02-02 17:53:18 +00:00
committed by GitHub
parent ba731f7865
commit 40ec4ef5b8
11 changed files with 336 additions and 88 deletions

View File

@@ -1,3 +1,4 @@
import classnames from 'classnames';
import React, { PureComponent } from 'react';
import { PanelMenuItem } from '@grafana/data';
@@ -7,13 +8,15 @@ import { PanelHeaderMenuItem } from './PanelHeaderMenuItem';
export interface Props {
items: PanelMenuItem[];
style?: React.CSSProperties;
itemsClassName?: string;
className?: string;
}
export class PanelHeaderMenu extends PureComponent<Props> {
renderItems = (menu: PanelMenuItem[], isSubMenu = false) => {
return (
<ul
className="dropdown-menu dropdown-menu--menu panel-menu"
className={classnames('dropdown-menu', 'dropdown-menu--menu', 'panel-menu', this.props.itemsClassName)}
style={this.props.style}
role={isSubMenu ? '' : 'menu'}
>
@@ -36,6 +39,10 @@ export class PanelHeaderMenu extends PureComponent<Props> {
};
render() {
return <div className="panel-menu-container dropdown open">{this.renderItems(this.props.items)}</div>;
return (
<div className={classnames('panel-menu-container', 'dropdown', 'open', this.props.className)}>
{this.renderItems(this.props.items)}
</div>
);
}
}

View File

@@ -13,14 +13,28 @@ interface Props {
loadingState?: LoadingState;
onClose: () => void;
style?: React.CSSProperties;
menuItemsClassName?: string;
menuWrapperClassName?: string;
}
export function PanelHeaderMenuWrapper({ style, panel, dashboard, loadingState }: Props) {
export function PanelHeaderMenuWrapper({
style,
panel,
dashboard,
loadingState,
menuItemsClassName,
menuWrapperClassName,
}: Props) {
return (
<PanelHeaderMenuProvider panel={panel} dashboard={dashboard} loadingState={loadingState}>
{({ items }) => {
return <PanelHeaderMenu style={style} items={items} />;
}}
{({ items }) => (
<PanelHeaderMenu
className={menuWrapperClassName}
itemsClassName={menuItemsClassName}
style={style}
items={items}
/>
)}
</PanelHeaderMenuProvider>
);
}

View File

@@ -47,6 +47,8 @@ export function PanelLinks({ panelLinks, onShowPanelLinks }: Props) {
const getStyles = (theme: GrafanaTheme2) => {
return {
menuTrigger: css({
height: '100%',
background: 'inherit',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
cursor: 'context-menu',

View File

@@ -1,3 +1,4 @@
import { css } from '@emotion/css';
import classNames from 'classnames';
import React, { PureComponent } from 'react';
import { Subscription } from 'rxjs';
@@ -636,9 +637,12 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
const title = panel.getDisplayTitle();
const padding: PanelPadding = plugin.noPadding ? 'none' : 'md';
const dragClass = !(isViewing || isEditing) ? 'grid-drag-handle' : '';
const titleItems = [
const showTitleItems =
(panel.links && panel.links.length > 0 && this.onShowPanelLinks) ||
(data.series.length > 0 && data.series.some((v) => (v.meta?.notices?.length ?? 0) > 0)) ||
(data.request && data.request.timeInfo) ||
alertState;
const titleItems = showTitleItems && (
<PanelHeaderTitleItems
key="title-items"
alertState={alertState}
@@ -646,25 +650,60 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
panelId={panel.id}
panelLinks={panel.links}
onShowPanelLinks={this.onShowPanelLinks}
/>,
];
/>
);
const overrideStyles: { menuItemsClassName?: string; menuWrapperClassName?: string; pos?: React.CSSProperties } = {
menuItemsClassName: undefined,
menuWrapperClassName: undefined,
pos: { top: 0, left: '-156px' },
};
if (config.featureToggles.newPanelChromeUI) {
// set override styles
overrideStyles.menuItemsClassName = css`
width: inherit;
top: inherit;
left: inherit;
position: inherit;
float: inherit;
`;
overrideStyles.menuWrapperClassName = css`
position: inherit;
width: inherit;
top: inherit;
left: inherit;
float: inherit;
.dropdown-submenu > .dropdown-menu {
position: absolute;
}
`;
overrideStyles.pos = undefined;
}
// custom styles is neeeded to override legacy panel-menu styles and prevent menu from being cut off
let menu;
if (!dashboard.meta.publicDashboardAccessToken) {
menu = (
<div data-testid="panel-dropdown">
<PanelHeaderMenuWrapper
style={{ top: 0 }}
style={overrideStyles.pos}
panel={panel}
dashboard={dashboard}
loadingState={data.state}
onClose={() => {}}
menuItemsClassName={overrideStyles.menuItemsClassName}
menuWrapperClassName={overrideStyles.menuWrapperClassName}
/>
</div>
);
}
const dragClass = !(isViewing || isEditing) ? 'grid-drag-handle' : '';
if (config.featureToggles.newPanelChromeUI) {
// Shift the hover menu down if it's on the top row so it doesn't get clipped by topnav
const hoverHeaderOffset = (panel.gridPos?.y ?? 0) === 0 ? -16 : undefined;
return (
<PanelChrome
width={width}
@@ -679,6 +718,8 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
dragClass={dragClass}
dragClassCancel="grid-drag-cancel"
padding={padding}
hoverHeaderOffset={hoverHeaderOffset}
hoverHeader={title ? false : true}
displayMode={transparent ? 'transparent' : 'default'}
>
{(innerWidth, innerHeight) => (

View File

@@ -339,6 +339,9 @@ export class PanelModel implements DataConfigSource, IPanelModel {
if (manuallyUpdated) {
this.configRev++;
}
// Maybe a bit heavy. Could add a "GridPosChanged" event instead?
this.render();
}
runAllPanelQueries({