feat: Allow GaugeCard to handle multi-line titles

By adding a fullHeightFixedContent variant.
Also, add support for a small version.

Signed-off-by: Gustaf Räntilä <g.rantila@gmail.com>
This commit is contained in:
Gustaf Räntilä
2024-03-22 08:46:49 +01:00
parent 9dbf9f6cf4
commit a2ee4df20a
6 changed files with 137 additions and 7 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Add a fullHeightFixedContent variant of the GaugeCard, and a small size version. Fixed content will vertically align the gauge in the cards, even when the card titles span across multiple lines.
+6 -1
View File
@@ -448,6 +448,7 @@ export type GaugeProps = {
inverse?: boolean;
unit?: string;
max?: number;
size?: 'normal' | 'small';
description?: ReactNode;
getColor?: GaugePropsGetColor;
};
@@ -609,7 +610,11 @@ export type InfoCardClassKey =
| 'headerContent';
// @public (undocumented)
export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem';
export type InfoCardVariants =
| 'flex'
| 'fullHeight'
| 'fullHeightFixedContent'
| 'gridItem';
// Warning: (ae-forgotten-export) The symbol "ItemCardProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ItemCard" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -19,6 +19,7 @@ import { makeStyles, useTheme } from '@material-ui/core/styles';
import { Circle } from 'rc-progress';
import React, { ReactNode, useEffect, useState } from 'react';
import Box from '@material-ui/core/Box';
import classNames from 'classnames';
/** @public */
export type GaugeClassKey =
@@ -43,6 +44,9 @@ const useStyles = makeStyles(
fontWeight: theme.typography.fontWeightBold,
color: theme.palette.textContrast,
},
overlaySmall: {
fontSize: theme.typography.pxToRem(25),
},
description: {
fontSize: '100%',
top: '50%',
@@ -68,6 +72,7 @@ export type GaugeProps = {
inverse?: boolean;
unit?: string;
max?: number;
size?: 'normal' | 'small';
description?: ReactNode;
getColor?: GaugePropsGetColor;
};
@@ -121,7 +126,7 @@ export const getProgressColor: GaugePropsGetColor = ({
export function Gauge(props: GaugeProps) {
const [hoverRef, setHoverRef] = useState<HTMLDivElement | null>(null);
const { getColor = getProgressColor } = props;
const { getColor = getProgressColor, size = 'normal' } = props;
const classes = useStyles(props);
const { palette } = useTheme();
const { value, fractional, inverse, unit, max, description } = {
@@ -165,7 +170,12 @@ export function Gauge(props: GaugeProps) {
{description && isHovering ? (
<Box className={classes.description}>{description}</Box>
) : (
<Box className={classes.overlay}>
<Box
className={classNames(
classes.overlay,
size === 'small' ? classes.overlaySmall : undefined,
)}
>
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
</Box>
)}
@@ -175,6 +175,82 @@ export const InfoMessage = () => (
</Wrapper>
);
export const AlignedBottom = () => (
<Wrapper>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
title="Progress"
subheader="With a subheader"
progress={0.3}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
title="Progress"
subheader="With a subheader"
progress={0.57}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
title="Progress with longer title"
subheader="With a subheader"
progress={0.89}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
title="Progress"
subheader="With a subheader"
inverse
progress={0.2}
/>
</Grid>
</Wrapper>
);
export const Small = () => (
<Wrapper>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
size="small"
title="Progress"
progress={0.3}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
size="small"
title="Progress"
progress={0.57}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
size="small"
title="Progress, longer title"
progress={0.89}
/>
</Grid>
<Grid item>
<GaugeCard
variant="fullHeightFixedContent"
size="small"
title="Progress"
inverse
progress={0.2}
/>
</Grid>
</Wrapper>
);
export const HoverMessage = () => (
<Wrapper>
<Grid item>
@@ -27,6 +27,7 @@ type Props = {
variant?: InfoCardVariants;
/** Progress in % specified as decimal, e.g. "0.23" */
progress: number;
size?: 'normal' | 'small';
description?: ReactNode;
icon?: ReactNode;
inverse?: boolean;
@@ -43,6 +44,10 @@ const useStyles = makeStyles(
height: '100%',
width: 250,
},
rootSmall: {
height: '100%',
width: 160,
},
},
{ name: 'BackstageGaugeCard' },
);
@@ -64,6 +69,7 @@ export function GaugeCard(props: Props) {
description,
icon,
variant,
size = 'normal',
getColor,
} = props;
@@ -75,15 +81,22 @@ export function GaugeCard(props: Props) {
};
return (
<Box className={classes.root}>
<Box className={size === 'small' ? classes.rootSmall : classes.root}>
<InfoCard
title={title}
subheader={subheader}
deepLink={deepLink}
variant={variant}
icon={icon}
titleTypographyProps={
size === 'small'
? {
variant: 'h6',
}
: undefined
}
>
<Gauge {...gaugeProps} />
<Gauge {...gaugeProps} size={size} />
</InfoCard>
</Box>
);
@@ -46,6 +46,10 @@ const useStyles = makeStyles(
header: {
padding: theme.spacing(2, 2, 2, 2.5),
},
headerFixedContent: {
flexGrow: 1,
alignItems: 'flex-start',
},
headerTitle: {
fontWeight: theme.typography.fontWeightBold,
},
@@ -87,6 +91,11 @@ const VARIANT_STYLES = {
flexDirection: 'column',
height: '100%',
},
fullHeightFixedContent: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
gridItem: {
display: 'flex',
flexDirection: 'column',
@@ -102,6 +111,9 @@ const VARIANT_STYLES = {
fullHeight: {
flex: 1,
},
fullHeightFixedContent: {
flex: '0 1 0%',
},
gridItem: {
flex: 1,
},
@@ -109,7 +121,11 @@ const VARIANT_STYLES = {
};
/** @public */
export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem';
export type InfoCardVariants =
| 'flex'
| 'fullHeight'
| 'fullHeightFixedContent'
| 'gridItem';
/**
* InfoCard is used to display a paper-styled block on the screen, similar to a panel.
@@ -228,7 +244,12 @@ export function InfoCard(props: Props): JSX.Element {
{title && (
<CardHeader
classes={{
root: classes.header,
root: classNames(
classes.header,
variant === 'fullHeightFixedContent'
? classes.headerFixedContent
: undefined,
),
title: classes.headerTitle,
subheader: classes.headerSubheader,
avatar: classes.headerAvatar,