[Panel] Extract styling duplication in new TitleItem component (#61625)

This commit is contained in:
Alexa V 2023-01-19 11:03:56 +00:00 committed by GitHub
parent f38e2856d8
commit 134ca6bb8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 96 deletions

View File

@ -181,7 +181,7 @@ const ZoomOutTooltip = () => (
</>
);
const TimePickerTooltip = ({ timeRange, timeZone }: { timeRange: TimeRange; timeZone?: TimeZone }) => {
export const TimePickerTooltip = ({ timeRange, timeZone }: { timeRange: TimeRange; timeZone?: TimeZone }) => {
const styles = useStyles2(getLabelStyles);
return (

View File

@ -1,20 +1,17 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
import { Icon } from '../Icon/Icon';
import { Tooltip } from '../Tooltip';
import { TitleItem } from './TitleItem';
interface Props {
description: string | (() => string);
}
export function PanelDescription({ description }: Props) {
const theme = useTheme2();
const styles = getStyles(theme);
const styles = getStyles();
const getDescriptionContent = (): JSX.Element => {
// description
@ -29,39 +26,16 @@ export function PanelDescription({ description }: Props) {
return description !== '' ? (
<Tooltip interactive content={getDescriptionContent}>
<span className={styles.description}>
<Icon name="info-circle" size="lg" aria-label="description" />
</span>
<TitleItem className={styles.description}>
<Icon name="info-circle" size="lg" title="description" />
</TitleItem>
</Tooltip>
) : null;
}
const getStyles = (theme: GrafanaTheme2) => {
const getStyles = () => {
return {
description: css({
color: `${theme.colors.text.secondary}`,
backgroundColor: `${theme.colors.background.primary}`,
cursor: 'auto',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
padding: `${theme.spacing(0, 1)}`,
height: ` ${theme.spacing(theme.components.height.md)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
color: `${theme.colors.text.primary}`,
background: `${theme.colors.background.secondary}`,
},
code: {
whiteSpace: 'normal',
wordWrap: 'break-word',

View File

@ -0,0 +1,75 @@
import { cx, css } from '@emotion/css';
import React, { forwardRef } from 'react';
import { GrafanaTheme2, LinkModel, LinkTarget } from '@grafana/data';
import { useStyles2 } from '../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
type TitleItemProps = {
className?: string;
children: React.ReactNode;
onClick?: LinkModel['onClick'];
href?: string;
target?: LinkTarget;
title?: string;
};
export const TitleItem = forwardRef<HTMLAnchorElement, TitleItemProps>(
({ className, children, href, onClick, target, title, ...rest }, ref) => {
const styles = useStyles2(getStyles);
if (href) {
return (
<a
ref={ref}
href={href}
onClick={onClick}
target={target}
title={title}
className={cx(styles.item, className)}
{...rest}
>
{children}
</a>
);
} else {
return (
<span ref={ref} className={cx(styles.item, className)} {...rest}>
{children}
</span>
);
}
}
);
TitleItem.displayName = 'TitleItem';
const getStyles = (theme: GrafanaTheme2) => {
return {
item: css({
color: `${theme.colors.text.secondary}`,
label: 'panel-header-item',
backgroundColor: `${theme.colors.background.primary}`,
cursor: 'auto',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
padding: `${theme.spacing(0, 1)}`,
height: `${theme.spacing(theme.components.height.md)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
background: `${theme.colors.background.secondary}`,
},
}),
};
};

View File

@ -3,6 +3,7 @@ import React from 'react';
import { ErrorIndicator } from './ErrorIndicator';
import { LoadingIndicator } from './LoadingIndicator';
import { PanelChrome as PanelChromeComponent, PanelChromeProps } from './PanelChrome';
import { TitleItem } from './TitleItem';
/**
* @internal
@ -15,6 +16,7 @@ export type { PanelChromeProps, PanelPadding } from './PanelChrome';
export interface PanelChromeType extends React.FC<PanelChromeProps> {
LoadingIndicator: typeof LoadingIndicator;
ErrorIndicator: typeof ErrorIndicator;
TitleItem: typeof TitleItem;
}
/**
@ -23,6 +25,7 @@ export interface PanelChromeType extends React.FC<PanelChromeProps> {
export const PanelChrome = PanelChromeComponent as PanelChromeType;
PanelChrome.LoadingIndicator = LoadingIndicator;
PanelChrome.ErrorIndicator = ErrorIndicator;
PanelChrome.TitleItem = TitleItem;
/**
* Exporting the components for extensibility and since it is a good practice

View File

@ -33,6 +33,7 @@ export { UnitPicker } from './UnitPicker/UnitPicker';
export { StatsPicker } from './StatsPicker/StatsPicker';
export { RefreshPicker, defaultIntervals } from './RefreshPicker/RefreshPicker';
export { TimeRangePicker, type TimeRangePickerProps } from './DateTimePickers/TimeRangePicker';
export { TimePickerTooltip } from './DateTimePickers/TimeRangePicker';
export { TimeOfDayPicker } from './DateTimePickers/TimeOfDayPicker';
export { TimeZonePicker } from './DateTimePickers/TimeZonePicker';
export { WeekStartPicker } from './DateTimePickers/WeekStartPicker';

View File

@ -2,8 +2,7 @@ import { css, cx } from '@emotion/css';
import React from 'react';
import { PanelData, GrafanaTheme2, PanelModel, LinkModel, AlertState, DataLink } from '@grafana/data';
import { Icon, Tooltip, useStyles2 } from '@grafana/ui';
import { getFocusStyles, getMouseFocusStyles } from '@grafana/ui/src/themes/mixins';
import { Icon, PanelChrome, Tooltip, useStyles2, TimePickerTooltip } from '@grafana/ui';
import { PanelLinks } from '../PanelLinks';
@ -24,28 +23,28 @@ export function PanelHeaderTitleItems(props: Props) {
// panel health
const alertStateItem = (
<Tooltip content={`alerting is ${alertState}`}>
<span
className={cx(styles.item, {
<PanelChrome.TitleItem
className={cx({
[styles.ok]: alertState === AlertState.OK,
[styles.pending]: alertState === AlertState.Pending,
[styles.alerting]: alertState === AlertState.Alerting,
})}
>
<Icon name={alertState === 'alerting' ? 'heart-break' : 'heart'} className="panel-alert-icon" />
</span>
</PanelChrome.TitleItem>
</Tooltip>
);
const timeshift = (
<>
<Tooltip
content={data.request?.range ? `Time Range: ${data.request.range.from} to ${data.request.range.to}` : ''}
>
<span className={cx(styles.item, styles.timeshift)}>
<Icon name="clock-nine" />
{data.request?.timeInfo}
</span>
</Tooltip>
{data.request && data.request.timeInfo && (
<Tooltip content={<TimePickerTooltip timeRange={data.request?.range} timeZone={data.request?.timezone} />}>
<PanelChrome.TitleItem className={styles.timeshift}>
<Icon name="clock-nine" />
{data.request?.timeInfo}
</PanelChrome.TitleItem>
</Tooltip>
)}
</>
);
@ -56,7 +55,7 @@ export function PanelHeaderTitleItems(props: Props) {
)}
{<PanelHeaderNotices panelId={panelId} frames={data.series} />}
{data.request && data.request.timeInfo && timeshift}
{timeshift}
{alertState && alertStateItem}
</>
);
@ -64,29 +63,6 @@ export function PanelHeaderTitleItems(props: Props) {
const getStyles = (theme: GrafanaTheme2) => {
return {
item: css({
label: 'panel-header-item',
backgroundColor: `${theme.colors.background.primary}`,
cursor: 'auto',
border: 'none',
borderRadius: `${theme.shape.borderRadius()}`,
padding: `${theme.spacing(0, 1)}`,
height: `${theme.spacing(theme.components.height.md)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
background: `${theme.colors.background.secondary}`,
},
}),
ok: css({
color: theme.colors.success.text,
}),

View File

@ -2,8 +2,7 @@ import { css } from '@emotion/css';
import React from 'react';
import { DataLink, GrafanaTheme2, LinkModel } from '@grafana/data';
import { Dropdown, Icon, Menu, ToolbarButton, useStyles2 } from '@grafana/ui';
import { getFocusStyles, getMouseFocusStyles } from '@grafana/ui/src/themes/mixins';
import { Dropdown, Icon, Menu, ToolbarButton, useStyles2, PanelChrome } from '@grafana/ui';
interface Props {
panelLinks: DataLink[];
@ -27,15 +26,14 @@ export function PanelLinks({ panelLinks, onShowPanelLinks }: Props) {
if (panelLinks.length === 1) {
const linkModel = onShowPanelLinks()[0];
return (
<a
<PanelChrome.TitleItem
href={linkModel.href}
onClick={linkModel.onClick}
target={linkModel.target}
title={linkModel.title}
className={styles.singleLink}
>
<Icon name="external-link-alt" size="lg" />
</a>
</PanelChrome.TitleItem>
);
} else {
return (
@ -53,25 +51,5 @@ const getStyles = (theme: GrafanaTheme2) => {
borderRadius: `${theme.shape.borderRadius()}`,
cursor: 'context-menu',
}),
singleLink: css({
color: theme.colors.text.secondary,
padding: `${theme.spacing(0, 1)}`,
height: ` ${theme.spacing(theme.components.height.md)}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'&:focus, &:focus-visible': {
...getFocusStyles(theme),
zIndex: 1,
},
'&: focus:not(:focus-visible)': getMouseFocusStyles(theme),
'&:hover ': {
boxShadow: `${theme.shadows.z1}`,
color: `${theme.colors.text.primary}`,
background: `${theme.colors.background.secondary}`,
},
}),
};
};