Panel Header: Add an "untitled" placeholder, and change the dragging icon in the hover header (#64700)

Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
pull/64749/head^2
Alexa V 2 years ago committed by GitHub
parent a996344e14
commit bc5400d8f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/grafana-data/src/types/icon.ts
  2. 35
      packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx
  3. 23
      packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
  4. 50
      packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx

@ -29,6 +29,7 @@ export const availableIconsIndex = {
'arrow-up': true,
'arrows-h': true,
'arrows-v': true,
'expand-arrows': true,
at: true,
backward: true,
bars: true,

@ -1,5 +1,4 @@
import { css } from '@emotion/css';
import classnames from 'classnames';
import { css, cx } from '@emotion/css';
import React, { ReactElement, useCallback, useRef, useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
@ -38,18 +37,19 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
return (
<div
className={classnames(styles.container, { 'show-on-hover': !menuOpen })}
className={cx(styles.container, { 'show-on-hover': !menuOpen })}
style={{ top: `${offset}px` }}
data-testid="hover-header-container"
>
<div
className={classnames(styles.square, styles.draggable, dragClass)}
className={cx(styles.square, styles.draggable, dragClass)}
onPointerDown={onPointerDown}
onPointerUp={onPointerUp}
ref={draggableRef}
>
<Icon name="draggabledots" />
<Icon name="expand-arrows" className={styles.draggableIcon} />
</div>
{!title && <h6 className={cx(styles.untitled, styles.draggable, dragClass)}>Untitled</h6>}
{children}
<div className={styles.square}>
<PanelMenu
@ -77,7 +77,7 @@ function getStyles(theme: GrafanaTheme2) {
position: 'absolute',
zIndex: 1,
right: 0,
boxSizing: 'border-box',
boxSizing: 'content-box',
alignItems: 'center',
background: theme.colors.background.secondary,
color: theme.colors.text.primary,
@ -92,18 +92,37 @@ function getStyles(theme: GrafanaTheme2) {
alignItems: 'center',
width: theme.spacing(4),
height: '100%',
paddingRight: theme.spacing(0.5),
}),
draggable: css({
cursor: 'move',
// mobile do not support draggable panels
[theme.breakpoints.down('md')]: {
display: 'none',
},
}),
menuButton: css({
color: theme.colors.text.primary,
// Background and border are overriden when topnav toggle is disabled
background: 'inherit',
border: 'none',
'&:hover': {
background: 'inherit',
background: theme.colors.secondary.main,
},
}),
title: css({
padding: theme.spacing(0.75),
}),
untitled: css({
color: theme.colors.text.disabled,
fontStyle: 'italic',
marginBottom: 0,
}),
draggableIcon: css({
transform: 'rotate(45deg)',
color: theme.colors.text.secondary,
'&:hover': {
color: theme.colors.text.primary,
},
}),
};
}

@ -1,5 +1,6 @@
import { css, cx } from '@emotion/css';
import React, { CSSProperties, ReactElement, ReactNode } from 'react';
import { useMedia } from 'react-use';
import { GrafanaTheme2, LoadingState } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@ -84,7 +85,14 @@ export function PanelChrome({
}: PanelChromeProps) {
const theme = useTheme2();
const styles = useStyles2(getStyles);
const hasHeader = !hoverHeader;
const pointerQuery = '(pointer: coarse)';
// detect if we are on touch devices
const isTouchDevice = useMedia(pointerQuery);
const hasHeader = !hoverHeader || isTouchDevice;
// hover menu is only shown on hover when not on touch devices
const showOnHoverClass = !isTouchDevice ? 'show-on-hover' : '';
const headerHeight = getHeaderHeight(theme, hasHeader);
const { contentStyle, innerWidth, innerHeight } = getContentStyle(padding, theme, width, headerHeight, height);
@ -131,7 +139,7 @@ export function PanelChrome({
{loadingState === LoadingState.Loading ? <LoadingBar width={width} ariaLabel="Panel loading bar" /> : null}
</div>
{hoverHeader && (
{hoverHeader && !isTouchDevice && (
<>
{menu && (
<HoverWidget menu={menu} title={title} offset={hoverHeaderOffset} dragClass={dragClass}>
@ -162,7 +170,12 @@ export function PanelChrome({
menu={menu}
title={title}
placement="bottom-end"
menuButtonClass={cx(styles.menuItem, dragClassCancel, 'show-on-hover')}
menuButtonClass={cx(
{ [styles.hiddenMenu]: !isTouchDevice },
styles.menuItem,
dragClassCancel,
showOnHoverClass
)}
/>
)}
@ -296,9 +309,11 @@ const getStyles = (theme: GrafanaTheme2) => {
justifyContent: 'center',
alignItems: 'center',
}),
hiddenMenu: css({
visibility: 'hidden',
}),
menuItem: css({
label: 'panel-menu',
visibility: 'hidden',
border: 'none',
background: theme.colors.secondary.main,
'&:hover': {

@ -27,7 +27,7 @@ export const TitleItem = forwardRef<HTMLAnchorElement, TitleItemProps>(
onClick={onClick}
target={target}
title={title}
className={cx(styles.item, className)}
className={cx(styles.linkItem, className)}
{...rest}
>
{children}
@ -46,29 +46,33 @@ export const TitleItem = forwardRef<HTMLAnchorElement, TitleItemProps>(
TitleItem.displayName = 'TitleItem';
const getStyles = (theme: GrafanaTheme2) => {
return {
item: css({
color: `${theme.colors.text.secondary}`,
label: 'panel-header-item',
cursor: 'auto',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
padding: `${theme.spacing(0, 1)}`,
height: `${theme.spacing(theme.components.panel.headerHeight)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
const item = css({
color: `${theme.colors.text.secondary}`,
label: 'panel-header-item',
cursor: 'auto',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
padding: `${theme.spacing(0, 1)}`,
height: `${theme.spacing(theme.components.panel.headerHeight)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
background: `${theme.colors.background.secondary}`,
color: `${theme.colors.text.primary}`,
},
});
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
background: `${theme.colors.background.secondary}`,
},
}),
return {
item,
linkItem: cx(item, css({ cursor: 'pointer' })),
};
};

Loading…
Cancel
Save