Merge pull request #8807 from Expelizabeth/Expelizabeth/gauge-updates

Adding hover message and an info icon.
This commit is contained in:
Johan Haals
2022-02-03 10:33:02 +01:00
committed by GitHub
7 changed files with 169 additions and 13 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Adding hover message to the Gauge and an info icon to the GaugeCard.
+7 -1
View File
@@ -384,7 +384,12 @@ export function GaugeCard(props: Props_10): JSX.Element;
export type GaugeCardClassKey = 'root';
// @public (undocumented)
export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown';
export type GaugeClassKey =
| 'root'
| 'overlay'
| 'description'
| 'circle'
| 'colorUnknown';
// @public (undocumented)
export type GaugeProps = {
@@ -393,6 +398,7 @@ export type GaugeProps = {
inverse?: boolean;
unit?: string;
max?: number;
description?: ReactNode;
getColor?: GaugePropsGetColor;
};
@@ -17,10 +17,15 @@
import { BackstagePalette, BackstageTheme } from '@backstage/theme';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import { Circle } from 'rc-progress';
import React from 'react';
import React, { ReactNode, useEffect, useState } from 'react';
/** @public */
export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown';
export type GaugeClassKey =
| 'root'
| 'overlay'
| 'description'
| 'circle'
| 'colorUnknown';
const useStyles = makeStyles<BackstageTheme>(
theme => ({
@@ -37,6 +42,15 @@ const useStyles = makeStyles<BackstageTheme>(
fontWeight: 'bold',
color: theme.palette.textContrast,
},
description: {
fontSize: '100%',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
position: 'absolute',
wordBreak: 'break-all',
display: 'inline-block',
},
circle: {
width: '80%',
transform: 'translate(10%, 0)',
@@ -53,6 +67,7 @@ export type GaugeProps = {
inverse?: boolean;
unit?: string;
max?: number;
description?: ReactNode;
getColor?: GaugePropsGetColor;
};
@@ -104,10 +119,11 @@ export const getProgressColor: GaugePropsGetColor = ({
*/
export function Gauge(props: GaugeProps) {
const [hoverRef, setHoverRef] = useState<HTMLDivElement | null>(null);
const { getColor = getProgressColor } = props;
const classes = useStyles(props);
const { palette } = useTheme<BackstageTheme>();
const { value, fractional, inverse, unit, max } = {
const { value, fractional, inverse, unit, max, description } = {
...defaultGaugeProps,
...props,
};
@@ -115,8 +131,28 @@ export function Gauge(props: GaugeProps) {
const asPercentage = fractional ? Math.round(value * max) : value;
const asActual = max !== 100 ? Math.round(value) : asPercentage;
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
const node = hoverRef;
const handleMouseOver = () => setIsHovering(true);
const handleMouseOut = () => setIsHovering(false);
if (node && description) {
node.addEventListener('mouseenter', handleMouseOver);
node.addEventListener('mouseleave', handleMouseOut);
return () => {
node.removeEventListener('mouseenter', handleMouseOver);
node.removeEventListener('mouseleave', handleMouseOut);
};
}
return () => {
setIsHovering(false);
};
}, [description, hoverRef]);
return (
<div className={classes.root}>
<div ref={setHoverRef} className={classes.root}>
<Circle
strokeLinecap="butt"
percent={asPercentage}
@@ -125,9 +161,13 @@ export function Gauge(props: GaugeProps) {
strokeColor={getColor({ palette, value: asActual, inverse, max })}
className={classes.circle}
/>
<div className={classes.overlay}>
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
</div>
{description && isHovering ? (
<div className={classes.description}>{description}</div>
) : (
<div className={classes.overlay}>
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
</div>
)}
</div>
);
}
@@ -18,6 +18,8 @@ import React, { PropsWithChildren } from 'react';
import { GaugeCard } from './GaugeCard';
import Grid from '@material-ui/core/Grid';
import { MemoryRouter } from 'react-router';
import Tooltip from '@material-ui/core/Tooltip';
import Info from '@material-ui/icons/Info';
const linkInfo = { title: 'Go to XYZ Location', link: '#' };
@@ -118,3 +120,79 @@ export const StaticColor = () => (
</Grid>
</Wrapper>
);
export const InfoMessage = () => (
<Wrapper>
<Grid item>
<GaugeCard
title="Progress"
subheader="With a subheader"
progress={0.3}
icon={
<Tooltip title="Info Message" arrow>
<Info style={{ float: 'right' }} />
</Tooltip>
}
/>
</Grid>
<Grid item>
<GaugeCard
title="Progress"
subheader="With a subheader"
progress={0.57}
icon={
<Tooltip title="Info Message" arrow>
<Info style={{ float: 'right' }} />
</Tooltip>
}
/>
</Grid>
<Grid item>
<GaugeCard
title="Progress"
subheader="With a subheader"
progress={0.89}
icon={
<Tooltip title="Info Message" arrow>
<Info style={{ float: 'right' }} />
</Tooltip>
}
/>
</Grid>
<Grid item>
<GaugeCard
title="Progress"
subheader="With a subheader"
inverse
progress={0.2}
icon={
<Tooltip title="Info Message" arrow>
<Info style={{ float: 'right' }} />
</Tooltip>
}
/>
</Grid>
</Wrapper>
);
export const HoverMessage = () => (
<Wrapper>
<Grid item>
<GaugeCard title="Progress" progress={0.3} description="Hover Message" />
</Grid>
<Grid item>
<GaugeCard title="Progress" progress={0.57} description="Hover Message" />
</Grid>
<Grid item>
<GaugeCard title="Progress" progress={0.89} description="Hover Message" />
</Grid>
<Grid item>
<GaugeCard
title="Progress"
inverse
progress={0.2}
description="Hover Message"
/>
</Grid>
</Wrapper>
);
@@ -15,7 +15,7 @@
*/
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
import React, { ReactNode } from 'react';
import { BottomLinkProps } from '../../layout/BottomLink';
import { InfoCard, InfoCardVariants } from '../../layout/InfoCard';
import { Gauge, GaugePropsGetColor } from './Gauge';
@@ -26,6 +26,8 @@ type Props = {
variant?: InfoCardVariants;
/** Progress in % specified as decimal, e.g. "0.23" */
progress: number;
description?: ReactNode;
icon?: ReactNode;
inverse?: boolean;
deepLink?: BottomLinkProps;
getColor?: GaugePropsGetColor;
@@ -52,11 +54,21 @@ const useStyles = makeStyles(
*/
export function GaugeCard(props: Props) {
const classes = useStyles(props);
const { title, subheader, progress, inverse, deepLink, variant, getColor } =
props;
const {
title,
subheader,
progress,
inverse,
deepLink,
description,
icon,
variant,
getColor,
} = props;
const gaugeProps = {
inverse,
description,
getColor,
value: progress,
};
@@ -68,6 +80,7 @@ export function GaugeCard(props: Props) {
subheader={subheader}
deepLink={deepLink}
variant={variant}
icon={icon}
>
<Gauge {...gaugeProps} />
</InfoCard>
@@ -55,6 +55,9 @@ const useStyles = makeStyles(
headerAvatar: {},
headerAction: {},
headerContent: {},
subheader: {
display: 'flex',
},
}),
{ name: 'BackstageInfoCard' },
);
@@ -134,6 +137,7 @@ type Props = {
children?: ReactNode;
headerStyle?: object;
headerProps?: CardHeaderProps;
icon?: ReactNode;
action?: ReactNode;
actionsClassName?: string;
actions?: ReactNode;
@@ -162,6 +166,7 @@ export function InfoCard(props: Props): JSX.Element {
children,
headerStyle,
headerProps,
icon,
action,
actionsClassName,
actions,
@@ -194,6 +199,15 @@ export function InfoCard(props: Props): JSX.Element {
});
}
const cardSubTitle = () => {
return (
<div className={classes.headerSubheader}>
{subheader && <div className={classes.subheader}>{subheader}</div>}
{icon}
</div>
);
};
const errProps: ErrorBoundaryProps =
errorBoundaryProps || (slackChannel ? { slackChannel } : {});
@@ -211,7 +225,7 @@ export function InfoCard(props: Props): JSX.Element {
content: classes.headerContent,
}}
title={title}
subheader={subheader}
subheader={cardSubTitle()}
action={action}
style={{ ...headerStyle }}
titleTypographyProps={titleTypographyProps}
@@ -55,7 +55,7 @@ describe('Features', () => {
expect(getByTestId(TEST_IDS.info.info)).toMatchInlineSnapshot(`
<div
class="MuiBox-root MuiBox-root-9"
class="MuiBox-root MuiBox-root-10"
data-testid="grm--info"
>
<h6