diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx index 0aa35fca698..9b3ca4c3a81 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx @@ -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 ( diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx index be82080da43..33b7d876bfe 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelDescription.tsx @@ -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 !== '' ? ( - - - + + + ) : 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', diff --git a/packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx b/packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx new file mode 100644 index 00000000000..b6d1d4fd8cd --- /dev/null +++ b/packages/grafana-ui/src/components/PanelChrome/TitleItem.tsx @@ -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( + ({ className, children, href, onClick, target, title, ...rest }, ref) => { + const styles = useStyles2(getStyles); + + if (href) { + return ( + + {children} + + ); + } else { + return ( + + {children} + + ); + } + } +); + +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}`, + }, + }), + }; +}; diff --git a/packages/grafana-ui/src/components/PanelChrome/index.ts b/packages/grafana-ui/src/components/PanelChrome/index.ts index 22cf206bfbd..3f7e8845414 100644 --- a/packages/grafana-ui/src/components/PanelChrome/index.ts +++ b/packages/grafana-ui/src/components/PanelChrome/index.ts @@ -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 { LoadingIndicator: typeof LoadingIndicator; ErrorIndicator: typeof ErrorIndicator; + TitleItem: typeof TitleItem; } /** @@ -23,6 +25,7 @@ export interface PanelChromeType extends React.FC { 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 diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 6adfe0fd6c4..f5e93ebaaa8 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -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'; diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderTitleItems.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderTitleItems.tsx index 91967b2ab52..de802fc6fad 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderTitleItems.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderTitleItems.tsx @@ -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 = ( - - + ); const timeshift = ( <> - - - - {data.request?.timeInfo} - - + {data.request && data.request.timeInfo && ( + }> + + + {data.request?.timeInfo} + + + )} ); @@ -56,7 +55,7 @@ export function PanelHeaderTitleItems(props: Props) { )} {} - {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, }), diff --git a/public/app/features/dashboard/dashgrid/PanelLinks.tsx b/public/app/features/dashboard/dashgrid/PanelLinks.tsx index 706d29fa0ba..ae1e4444b5e 100644 --- a/public/app/features/dashboard/dashgrid/PanelLinks.tsx +++ b/public/app/features/dashboard/dashgrid/PanelLinks.tsx @@ -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 ( - - + ); } 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}`, - }, - }), }; };