mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
[Panel] Extract styling duplication in new TitleItem component (#61625)
This commit is contained in:
parent
f38e2856d8
commit
134ca6bb8f
@ -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 (
|
||||
|
@ -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',
|
||||
|
75
packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx
Normal file
75
packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx
Normal 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}`,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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';
|
||||
|
@ -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,
|
||||
}),
|
||||
|
@ -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}`,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user