Merge pull request #14671 from luchillo17/feat/BCKSTG-73-core-components-theme-sync

Sync @backstage/core-components with Backstage's Component Design Guidelines
This commit is contained in:
Fredrik Adelöw
2022-12-01 15:41:56 +01:00
committed by GitHub
62 changed files with 491 additions and 358 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-components': patch
'@backstage/test-utils': patch
---
Sync components in @backstage/core-components with the Component Design Guidelines
+2 -2
View File
@@ -52,10 +52,10 @@ import { Theme } from '@material-ui/core/styles';
import { TooltipProps } from '@material-ui/core/Tooltip';
import { WithStyles } from '@material-ui/core/styles';
// @public (undocumented)
// @public
export function AlertDisplay(props: AlertDisplayProps): JSX.Element | null;
// @public
// @public (undocumented)
export type AlertDisplayProps = {
anchorOrigin?: {
vertical: 'top' | 'bottom';
@@ -13,14 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import { alertApiRef, AlertMessage, useApi } from '@backstage/core-plugin-api';
import IconButton from '@material-ui/core/IconButton';
import Snackbar from '@material-ui/core/Snackbar';
import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import { Alert } from '@material-ui/lab';
import { AlertMessage, useApi, alertApiRef } from '@backstage/core-plugin-api';
import pluralize from 'pluralize';
import React, { useEffect, useState } from 'react';
// TODO: improve on this and promote to a shared component for use by all apps.
/** @public */
export type AlertDisplayProps = {
anchorOrigin?: {
vertical: 'top' | 'bottom';
horizontal: 'left' | 'center' | 'right';
};
};
/**
* Displays alerts from {@link @backstage/core-plugin-api#AlertApi}
@@ -30,17 +40,6 @@ import pluralize from 'pluralize';
*
* Shown as SnackBar at the center top of the page by default. Configurable with props.
*/
// TODO: improve on this and promote to a shared component for use by all apps.
export type AlertDisplayProps = {
anchorOrigin?: {
vertical: 'top' | 'bottom';
horizontal: 'left' | 'center' | 'right';
};
};
/** @public */
export function AlertDisplay(props: AlertDisplayProps) {
const [messages, setMessages] = useState<Array<AlertMessage>>([]);
const alertApi = useApi(alertApiRef);
@@ -82,7 +81,7 @@ export function AlertDisplay(props: AlertDisplayProps) {
}
severity={firstMessage.severity}
>
<span>
<Typography component="span">
{String(firstMessage.message)}
{messages.length > 1 && (
<em>{` (${messages.length - 1} older ${pluralize(
@@ -90,7 +89,7 @@ export function AlertDisplay(props: AlertDisplayProps) {
messages.length - 1,
)})`}</em>
)}
</span>
</Typography>
</Alert>
</Snackbar>
);
@@ -29,7 +29,9 @@ describe('<Avatar />', () => {
it('generates a background color', async () => {
const bgcolor = stringToColor('John Doe');
const { getByText } = render(<Avatar displayName="John Doe" />);
expect(getByText('JD')).toHaveStyle(`background-color: ${bgcolor}`);
expect(getByText('JD').parentElement).toHaveStyle(
`background-color: ${bgcolor}`,
);
});
it('does not generate a background color when a picture is given', async () => {
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { CSSProperties } from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import MaterialAvatar from '@material-ui/core/Avatar';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React, { CSSProperties } from 'react';
import { extractInitials, stringToColor } from './utils';
/** @public */
@@ -28,6 +30,8 @@ const useStyles = makeStyles(
width: '4rem',
height: '4rem',
color: '#fff',
},
avatarText: {
fontWeight: theme.typography.fontWeightBold,
letterSpacing: '1px',
textTransform: 'uppercase',
@@ -68,6 +72,11 @@ export function Avatar(props: AvatarProps) {
const { displayName, picture, customStyles } = props;
const classes = useStyles();
let styles = { ...customStyles };
const fontStyles = {
fontFamily: styles.fontFamily,
fontSize: styles.fontSize,
fontWeight: styles.fontWeight,
};
// We only calculate the background color if there's not an avatar
// picture. If there is a picture, it might have a transparent
// background and we don't know whether the calculated background
@@ -85,7 +94,16 @@ export function Avatar(props: AvatarProps) {
className={classes.avatar}
style={styles}
>
{displayName && extractInitials(displayName)}
{displayName && (
<Typography
variant="h6"
component="span"
className={classes.avatarText}
style={fontStyles}
>
{extractInitials(displayName)}
</Typography>
)}
</MaterialAvatar>
);
}
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useTheme } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
import { CopyTextButton } from '../CopyTextButton';
import Box from '@material-ui/core/Box';
import { useTheme } from '@material-ui/core/styles';
import React from 'react';
import type {} from 'react-syntax-highlighter';
import { default as LightAsync } from 'react-syntax-highlighter/dist/esm/light-async';
import LightAsync from 'react-syntax-highlighter/dist/esm/light-async';
import dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';
import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
import { CopyTextButton } from '../CopyTextButton';
/**
* Properties for {@link CodeSnippet}
*
@@ -87,7 +88,7 @@ export function CodeSnippet(props: CodeSnippetProps) {
const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed';
return (
<div style={{ position: 'relative' }}>
<Box position="relative">
<LightAsync
customStyle={customStyle}
language={language}
@@ -108,10 +109,10 @@ export function CodeSnippet(props: CodeSnippetProps) {
{text}
</LightAsync>
{showCopyCodeButton && (
<div style={{ position: 'absolute', top: 0, right: 0 }}>
<Box position="absolute" top={0} right={0}>
<CopyTextButton text={text} />
</div>
</Box>
)}
</div>
</Box>
);
}
@@ -58,7 +58,7 @@ const useStyles = makeStyles(
zIndex: 'unset',
},
icon: {
fontSize: 20,
fontSize: theme.typography.h6.fontSize,
},
content: {
width: '100%',
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { CodeSnippet } from '../CodeSnippet';
import { Link } from '../Link';
import { EmptyState } from './EmptyState';
import { CodeSnippet } from '../CodeSnippet';
const COMPONENT_YAML_TEMPLATE = `apiVersion: backstage.io/v1alpha1
kind: Component
@@ -109,7 +110,7 @@ export function MissingAnnotationEmptyState(props: Props) {
Add the annotation to your component YAML as shown in the
highlighted example below:
</Typography>
<div className={classes.code}>
<Box className={classes.code}>
<CodeSnippet
text={generateComponentYaml(annotations)}
language="yaml"
@@ -117,7 +118,7 @@ export function MissingAnnotationEmptyState(props: Props) {
highlightedNumbers={generateLineNumbers(annotations.length)}
customStyle={{ background: 'inherit', fontSize: '115%' }}
/>
</div>
</Box>
<Button color="primary" component={Link} to={url}>
Read more
</Button>
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@material-ui/core/Box';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
@@ -26,6 +26,7 @@ import React, {
useState,
} from 'react';
import { createPortal } from 'react-dom';
import { usePortal } from './lib/usePortal';
import { useShowCallout } from './lib/useShowCallout';
@@ -168,14 +169,14 @@ export function FeatureCalloutCircular(props: PropsWithChildren<Props>) {
return (
<>
<div className={classes.featureWrapper} ref={wrapperRef}>
<Box className={classes.featureWrapper} {...{ ref: wrapperRef }}>
{children}
</div>
</Box>
{createPortal(
<div className={classes.backdrop}>
<Box className={classes.backdrop}>
<ClickAwayListener onClickAway={hide}>
<>
<div
<Box
className={classes.dot}
data-testid="dot"
style={{
@@ -190,9 +191,9 @@ export function FeatureCalloutCircular(props: PropsWithChildren<Props>) {
role="button"
tabIndex={0}
>
<div className={classes.pulseCircle} />
</div>
<div
<Box className={classes.pulseCircle} />
</Box>
<Box
className={classes.text}
data-testid="text"
style={{
@@ -205,10 +206,10 @@ export function FeatureCalloutCircular(props: PropsWithChildren<Props>) {
{title}
</Typography>
<Typography>{description}</Typography>
</div>
</Box>
</>
</ClickAwayListener>
</div>,
</Box>,
portalElement,
)}
</>
@@ -18,6 +18,8 @@ import classnames from 'classnames';
import { makeStyles } from '@material-ui/core/styles';
import LinkIcon from '@material-ui/icons/Link';
import { Link } from '../Link';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
export type IconLinkVerticalProps = {
color?: 'primary' | 'secondary';
@@ -56,9 +58,8 @@ const useIconStyles = makeStyles(
color: theme.palette.secondary.main,
},
label: {
fontSize: '0.7rem',
textTransform: 'uppercase',
fontWeight: 600,
fontWeight: theme.typography.fontWeightBold,
letterSpacing: 1.2,
},
}),
@@ -79,10 +80,16 @@ export function IconLinkVertical({
if (disabled) {
return (
<div title={title} className={classnames(classes.link, classes.disabled)}>
<Box title={title} className={classnames(classes.link, classes.disabled)}>
{icon}
<span className={classes.label}>{label}</span>
</div>
<Typography
variant="caption"
component="span"
className={classes.label}
>
{label}
</Typography>
</Box>
);
}
@@ -94,7 +101,9 @@ export function IconLinkVertical({
onClick={onClick}
>
{icon}
<span className={classes.label}>{label}</span>
<Typography variant="caption" component="span" className={classes.label}>
{label}
</Typography>
</Link>
);
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@material-ui/core/Box';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
import { makeStyles, Theme } from '@material-ui/core/styles';
@@ -227,7 +227,7 @@ export function HorizontalScrollGrid(props: PropsWithChildren<Props>) {
};
return (
<div {...otherProps} className={classes.root}>
<Box {...otherProps} className={classes.root}>
<Grid
container
direction="row"
@@ -237,12 +237,12 @@ export function HorizontalScrollGrid(props: PropsWithChildren<Props>) {
>
{children}
</Grid>
<div
<Box
className={classNames(classes.fade, classes.fadeLeft, {
[classes.fadeHidden]: scrollLeft === 0,
})}
/>
<div
<Box
className={classNames(classes.fade, classes.fadeRight, {
[classes.fadeHidden]: scrollRight === 0,
})}
@@ -265,6 +265,6 @@ export function HorizontalScrollGrid(props: PropsWithChildren<Props>) {
<ChevronRightIcon />
</IconButton>
)}
</div>
</Box>
);
}
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import CSS from 'csstype';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import CSS from 'csstype';
import React from 'react';
type Props = CSS.Properties & {
shorthand?: boolean;
@@ -47,15 +47,16 @@ export function Lifecycle(props: Props) {
const classes = useStyles(props);
const { shorthand, alpha } = props;
return shorthand ? (
<span
<Typography
component="span"
className={classes[alpha ? 'alpha' : 'beta']}
style={{ fontSize: '120%' }}
>
{alpha ? <>&alpha;</> : <>&beta;</>}
</span>
</Typography>
) : (
<span className={classes[alpha ? 'alpha' : 'beta']}>
<Typography component="span" className={classes[alpha ? 'alpha' : 'beta']}>
{alpha ? 'Alpha' : 'Beta'}
</span>
</Typography>
);
}
@@ -13,21 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { configApiRef, useAnalytics, useApi } from '@backstage/core-plugin-api';
import classnames from 'classnames';
// eslint-disable-next-line no-restricted-imports
import MaterialLink, {
LinkProps as MaterialLinkProps,
} from '@material-ui/core/Link';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import classnames from 'classnames';
import { trimEnd } from 'lodash';
import React, { ElementType } from 'react';
import {
createRoutesFromChildren,
Link as RouterLink,
LinkProps as RouterLinkProps,
Route,
} from 'react-router-dom';
import { trimEnd } from 'lodash';
import { createRoutesFromChildren, Route } from 'react-router-dom';
export function isReactRouterBeta(): boolean {
const [obj] = createRoutesFromChildren(<Route index element={<div />} />);
@@ -161,7 +162,9 @@ export const Link = React.forwardRef<any, LinkProps>(
className={classnames(classes.externalLink, props.className)}
>
{props.children}
<span className={classes.visuallyHidden}>, Opens in a new window</span>
<Typography component="span" className={classes.visuallyHidden}>
, Opens in a new window
</Typography>
</MaterialLink>
) : (
// Interact with React Router for internal links
@@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useMemo, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import Box from '@material-ui/core/Box';
import IconButton from '@material-ui/core/IconButton';
import CopyIcon from '@material-ui/icons/FileCopy';
import classnames from 'classnames';
import React, { useEffect, useMemo, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import AutoSizer from 'react-virtualized-auto-sizer';
import { FixedSizeList } from 'react-window';
import { AnsiProcessor } from './AnsiProcessor';
import { HEADER_SIZE, useStyles } from './styles';
import classnames from 'classnames';
import { LogLine } from './LogLine';
import { LogViewerControls } from './LogViewerControls';
import { HEADER_SIZE, useStyles } from './styles';
import { useLogViewerSearch } from './useLogViewerSearch';
import { useLogViewerSelection } from './useLogViewerSelection';
@@ -69,10 +70,10 @@ export function RealLogViewer(props: RealLogViewerProps) {
return (
<AutoSizer>
{({ height, width }) => (
<div style={{ width, height }} className={classes.root}>
<div className={classes.header}>
<Box style={{ width, height }} className={classes.root}>
<Box className={classes.header}>
<LogViewerControls {...search} />
</div>
</Box>
<FixedSizeList
ref={listRef}
className={classes.log}
@@ -86,7 +87,7 @@ export function RealLogViewer(props: RealLogViewerProps) {
const line = data[index];
const { lineNumber } = line;
return (
<div
<Box
style={{ ...style }}
className={classnames(classes.line, {
[classes.lineSelected]: selection.isSelected(lineNumber),
@@ -122,11 +123,11 @@ export function RealLogViewer(props: RealLogViewerProps) {
: undefined
}
/>
</div>
</Box>
);
}}
</FixedSizeList>
</div>
</Box>
)}
</AutoSizer>
);
@@ -13,23 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useState, useEffect, PropsWithChildren } from 'react';
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import LinearProgress, {
LinearProgressProps,
} from '@material-ui/core/LinearProgress';
import { useTheme } from '@material-ui/core/styles';
import React, { PropsWithChildren, useEffect, useState } from 'react';
export function Progress(props: PropsWithChildren<LinearProgressProps>) {
const theme = useTheme<BackstageTheme>();
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const handle = setTimeout(() => setIsVisible(true), 250);
const handle = setTimeout(
() => setIsVisible(true),
theme.transitions.duration.short,
);
return () => clearTimeout(handle);
}, []);
}, [theme.transitions.duration.short]);
return isVisible ? (
<LinearProgress {...props} data-testid="progress" />
) : (
<div style={{ display: 'none' }} />
<Box display="none" />
);
}
@@ -18,6 +18,7 @@ import { BackstagePalette, BackstageTheme } from '@backstage/theme';
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';
/** @public */
export type GaugeClassKey =
@@ -38,8 +39,8 @@ const useStyles = makeStyles<BackstageTheme>(
top: '50%',
left: '50%',
transform: 'translate(-50%, -60%)',
fontSize: 45,
fontWeight: 'bold',
fontSize: theme.typography.pxToRem(45),
fontWeight: theme.typography.fontWeightBold,
color: theme.palette.textContrast,
},
description: {
@@ -152,7 +153,7 @@ export function Gauge(props: GaugeProps) {
}, [description, hoverRef]);
return (
<div ref={setHoverRef} className={classes.root}>
<Box {...{ ref: setHoverRef }} className={classes.root}>
<Circle
strokeLinecap="butt"
percent={asPercentage}
@@ -162,12 +163,12 @@ export function Gauge(props: GaugeProps) {
className={classes.circle}
/>
{description && isHovering ? (
<div className={classes.description}>{description}</div>
<Box className={classes.description}>{description}</Box>
) : (
<div className={classes.overlay}>
<Box className={classes.overlay}>
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
</div>
</Box>
)}
</div>
</Box>
);
}
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import React, { ReactNode } from 'react';
import { BottomLinkProps } from '../../layout/BottomLink';
import { InfoCard, InfoCardVariants } from '../../layout/InfoCard';
import { Gauge, GaugePropsGetColor } from './Gauge';
@@ -74,7 +75,7 @@ export function GaugeCard(props: Props) {
};
return (
<div className={classes.root}>
<Box className={classes.root}>
<InfoCard
title={title}
subheader={subheader}
@@ -84,6 +85,6 @@ export function GaugeCard(props: Props) {
>
<Gauge {...gaugeProps} />
</InfoCard>
</div>
</Box>
);
}
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { BackstageTheme } from '@backstage/theme';
import { useTheme } from '@material-ui/core/styles';
import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import { Line } from 'rc-progress';
import { BackstageTheme } from '@backstage/theme';
import { getProgressColor, GaugePropsGetColor } from './Gauge';
import React from 'react';
import { GaugePropsGetColor, getProgressColor } from './Gauge';
type Props = {
/**
@@ -47,14 +48,14 @@ export function LinearGauge(props: Props) {
});
return (
<Tooltip title={`${percent}%`}>
<span>
<Typography component="span">
<Line
percent={percent}
strokeWidth={4}
trailWidth={4}
strokeColor={strokeColor}
/>
</span>
</Typography>
</Tooltip>
);
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@material-ui/core/Box';
import Checkbox from '@material-ui/core/Checkbox';
import Chip from '@material-ui/core/Chip';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
@@ -30,6 +30,7 @@ import {
} from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React, { useEffect, useState } from 'react';
import ClosedDropdown from './static/ClosedDropdown';
import OpenedDropdown from './static/OpenedDropdown';
@@ -45,17 +46,17 @@ const BootstrapInput = withStyles(
},
},
input: {
borderRadius: 4,
borderRadius: theme.shape.borderRadius,
position: 'relative',
backgroundColor: theme.palette.background.paper,
border: '1px solid #ced4da',
fontSize: 16,
padding: '10px 26px 10px 12px',
fontSize: theme.typography.body1.fontSize,
padding: theme.spacing(1.25, 3.25, 1.25, 1.5),
transition: theme.transitions.create(['border-color', 'box-shadow']),
fontFamily: 'Helvetica Neue',
'&:focus': {
background: theme.palette.background.paper,
borderRadius: 4,
borderRadius: theme.shape.borderRadius,
},
},
}),
@@ -81,7 +82,7 @@ const useStyles = makeStyles(
label: {
transform: 'initial',
fontWeight: 'bold',
fontSize: 14,
fontSize: theme.typography.body2.fontSize,
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
'&.Mui-focused': {
@@ -91,7 +92,7 @@ const useStyles = makeStyles(
formLabel: {
transform: 'initial',
fontWeight: 'bold',
fontSize: 14,
fontSize: theme.typography.body2.fontSize,
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
'&.Mui-focused': {
@@ -196,7 +197,7 @@ export function SelectComponent(props: SelectProps) {
};
return (
<div className={classes.root}>
<Box className={classes.root}>
<ClickAwayListener onClickAway={handleClickAway}>
<FormControl className={classes.formControl}>
<InputLabel className={classes.formLabel}>{label}</InputLabel>
@@ -217,7 +218,7 @@ export function SelectComponent(props: SelectProps) {
tabIndex={0}
renderValue={s =>
multiple && (value as any[]).length !== 0 ? (
<div className={classes.chips}>
<Box className={classes.chips}>
{(s as string[]).map(selectedValue => (
<Chip
key={items.find(el => el.value === selectedValue)?.value}
@@ -229,7 +230,7 @@ export function SelectComponent(props: SelectProps) {
className={classes.chip}
/>
))}
</div>
</Box>
) : (
<Typography>
{(value as any[]).length === 0
@@ -279,6 +280,6 @@ export function SelectComponent(props: SelectProps) {
</Select>
</FormControl>
</ClickAwayListener>
</div>
</Box>
);
}
@@ -21,11 +21,11 @@ import SvgIcon from '@material-ui/core/SvgIcon';
export type ClosedDropdownClassKey = 'icon';
const useStyles = makeStyles(
() =>
theme =>
createStyles({
icon: {
position: 'absolute',
right: '4px',
right: theme.spacing(0.5),
pointerEvents: 'none',
},
}),
@@ -20,11 +20,11 @@ import SvgIcon from '@material-ui/core/SvgIcon';
export type OpenedDropdownClassKey = 'icon';
const useStyles = makeStyles(
() =>
theme =>
createStyles({
icon: {
position: 'absolute',
right: '4px',
right: theme.spacing(0.5),
pointerEvents: 'none',
},
}),
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useContext, ReactNode, PropsWithChildren } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import { StepActions } from './types';
import { makeStyles } from '@material-ui/core/styles';
import React, { PropsWithChildren, ReactNode, useContext } from 'react';
import { VerticalStepperContext } from './SimpleStepper';
import { StepActions } from './types';
export type SimpleStepperFooterClassKey = 'root';
@@ -145,7 +147,7 @@ export const SimpleStepperFooter = ({
};
return (
<div className={classes.root}>
<Box className={classes.root}>
{[undefined, true].includes(actions.showBack) && stepIndex !== 0 && (
<BackBtn
text={actions.backText}
@@ -184,6 +186,6 @@ export const SimpleStepperFooter = ({
/>
)}
{children}
</div>
</Box>
);
};
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { PropsWithChildren } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
import MuiStep from '@material-ui/core/Step';
import StepContent from '@material-ui/core/StepContent';
import StepLabel from '@material-ui/core/StepLabel';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React, { PropsWithChildren } from 'react';
import { SimpleStepperFooter } from './SimpleStepperFooter';
import { StepProps } from './types';
@@ -40,11 +42,11 @@ export function SimpleStepperStep(props: PropsWithChildren<StepProps>) {
// The end step is not a part of the stepper
// It simply is the final screen with an option to have buttons such as reset or back
return end ? (
<div className={classes.end}>
<Box className={classes.end}>
<Typography variant="h6">{title}</Typography>
{children}
<SimpleStepperFooter actions={{ ...(actions || {}), showNext: false }} />
</div>
</Box>
) : (
<MuiStep {...muiProps}>
<StepLabel>
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { makeStyles } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import classNames from 'classnames';
import React, { PropsWithChildren } from 'react';
@@ -31,12 +31,12 @@ export type StatusClassKey =
const useStyles = makeStyles<BackstageTheme>(
theme => ({
status: {
fontWeight: 500,
fontWeight: theme.typography.fontWeightMedium,
'&::before': {
width: '0.7em',
height: '0.7em',
display: 'inline-block',
marginRight: 8,
marginRight: theme.spacing(1),
borderRadius: '50%',
content: '""',
},
@@ -78,7 +78,8 @@ const useStyles = makeStyles<BackstageTheme>(
export function StatusOK(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.ok)}
aria-label="Status ok"
aria-hidden="true"
@@ -90,7 +91,8 @@ export function StatusOK(props: PropsWithChildren<{}>) {
export function StatusWarning(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.warning)}
aria-label="Status warning"
aria-hidden="true"
@@ -102,7 +104,8 @@ export function StatusWarning(props: PropsWithChildren<{}>) {
export function StatusError(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.error)}
aria-label="Status error"
aria-hidden="true"
@@ -114,7 +117,8 @@ export function StatusError(props: PropsWithChildren<{}>) {
export function StatusPending(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.pending)}
aria-label="Status pending"
aria-hidden="true"
@@ -126,7 +130,8 @@ export function StatusPending(props: PropsWithChildren<{}>) {
export function StatusRunning(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.running)}
aria-label="Status running"
aria-hidden="true"
@@ -138,7 +143,8 @@ export function StatusRunning(props: PropsWithChildren<{}>) {
export function StatusAborted(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
return (
<span
<Typography
component="span"
className={classNames(classes.status, classes.aborted)}
aria-label="Status aborted"
aria-hidden="true"
@@ -13,25 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import {
withStyles,
createStyles,
WithStyles,
Theme,
WithStyles,
withStyles,
} from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableRow from '@material-ui/core/TableRow';
import React from 'react';
export type MetadataTableTitleCellClassKey = 'root';
const tableTitleCellStyles = (theme: Theme) =>
createStyles({
root: {
fontWeight: 'bolder',
fontWeight: theme.typography.fontWeightBold,
whiteSpace: 'nowrap',
paddingRight: theme.spacing(4),
border: '0',
@@ -53,9 +54,7 @@ export type MetadataTableListClassKey = 'root';
const listStyles = (theme: Theme) =>
createStyles({
root: {
listStyle: 'none',
margin: theme.spacing(0, 0, -1, 0),
padding: '0',
},
});
@@ -111,11 +110,13 @@ interface StyleProps extends WithStyles {
export const MetadataList = withStyles(listStyles, {
name: 'BackstageMetadataTableList',
})(({ classes, children }: StyleProps) => (
<ul className={classes.root}>{children}</ul>
<List disablePadding className={classes.root}>
{children}
</List>
));
export const MetadataListItem = withStyles(listItemStyles, {
name: 'BackstageMetadataTableListItem',
})(({ classes, children }: StyleProps) => (
<li className={classes.root}>{children}</li>
<ListItem className={classes.root}>{children}</ListItem>
));
@@ -22,6 +22,7 @@ import {
Theme,
} from '@material-ui/core/styles';
import startCase from 'lodash/startCase';
import Typography from '@material-ui/core/Typography';
import {
MetadataTable,
@@ -88,7 +89,9 @@ function renderMap(
: startCase(key);
return (
<MetadataListItem key={key}>
{`${fmtKey}: `}
<Typography variant="body2" component="span">
{`${fmtKey}: `}
</Typography>
{value}
</MetadataListItem>
);
@@ -122,7 +125,11 @@ function toValue(
return <Fragment>{value ? '✅' : '❌'}</Fragment>;
}
return <Fragment>{value}</Fragment>;
return (
<Typography variant="body2" component="span">
{value}
</Typography>
);
}
const ItemValue = ({ value, options }: { value: any; options: any }) => (
<Fragment>{toValue(value, options)}</Fragment>
@@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
import { BackstageTheme } from '@backstage/theme';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
import React, { useEffect, useState } from 'react';
import { Select } from '../Select';
import { SelectProps } from '../Select/Select';
@@ -39,7 +40,7 @@ const useFilterStyles = makeStyles<BackstageTheme>(
header: {
display: 'flex',
alignItems: 'center',
height: '60px',
height: theme.spacing(7.5),
justifyContent: 'space-between',
borderBottom: `1px solid ${theme.palette.grey[500]}`,
},
@@ -93,14 +94,14 @@ export const Filters = (props: Props) => {
// As material table doesn't provide a way to add a column filter tab we will make our own filter logic
return (
<div className={classes.root}>
<div className={classes.header}>
<div className={classes.value}>Filters</div>
<Box className={classes.root}>
<Box className={classes.header}>
<Box className={classes.value}>Filters</Box>
<Button color="primary" onClick={handleClick}>
Clear all
</Button>
</div>
<div className={classes.filters}>
</Box>
<Box className={classes.filters}>
{props.filters?.length &&
props.filters.map(filter => (
<Select
@@ -116,7 +117,7 @@ export const Filters = (props: Props) => {
}
/>
))}
</div>
</div>
</Box>
</Box>
);
};
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import React from 'react';
export type SubvalueCellClassKey = 'value' | 'subvalue';
const useSubvalueCellStyles = makeStyles<BackstageTheme>(
theme => ({
value: {
marginBottom: '6px',
marginBottom: theme.spacing(0.75),
},
subvalue: {
color: theme.palette.textSubtle,
@@ -44,8 +44,8 @@ export function SubvalueCell(props: SubvalueCellProps) {
return (
<>
<div className={classes.value}>{value}</div>
<div className={classes.subvalue}>{subvalue}</div>
<Box className={classes.value}>{value}</Box>
<Box className={classes.subvalue}>{subvalue}</Box>
</>
);
}
@@ -13,12 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import { makeStyles, useTheme, withStyles } from '@material-ui/core/styles';
import MTable, {
Column,
Icons,
MaterialTableProps,
MTableBody,
MTableHeader,
MTableToolbar,
Options,
} from '@material-table/core';
import Box from '@material-ui/core/Box';
import IconButton from '@material-ui/core/IconButton';
import { makeStyles, useTheme, withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
// Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51
import AddBox from '@material-ui/icons/AddBox';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import Check from '@material-ui/icons/Check';
@@ -34,15 +42,6 @@ import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import ViewColumn from '@material-ui/icons/ViewColumn';
import { isEqual, transform } from 'lodash';
import MTable, {
Column,
Icons,
MaterialTableProps,
MTableBody,
MTableHeader,
MTableToolbar,
Options,
} from '@material-table/core';
import React, {
forwardRef,
MutableRefObject,
@@ -51,9 +50,11 @@ import React, {
useEffect,
useState,
} from 'react';
import { SelectProps } from '../Select/Select';
import { Filter, Filters, SelectedFilters, Without } from './Filters';
// Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51
const tableIcons: Icons = {
Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
@@ -122,7 +123,7 @@ const StyledMTableToolbar = withStyles(
},
title: {
'& > h6': {
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
},
},
searchField: {
@@ -136,14 +137,14 @@ const StyledMTableToolbar = withStyles(
export type FiltersContainerClassKey = 'root' | 'title';
const useFilterStyles = makeStyles<BackstageTheme>(
() => ({
theme => ({
root: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
},
title: {
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
fontSize: 18,
whiteSpace: 'nowrap',
},
@@ -265,21 +266,21 @@ export function TableToolbar(toolbarProps: {
if (hasFilters) {
return (
<div className={filtersClasses.root}>
<div className={filtersClasses.root}>
<Box className={filtersClasses.root}>
<Box className={filtersClasses.root}>
<IconButton onClick={toggleFilters} aria-label="filter list">
<FilterList />
</IconButton>
<Typography className={filtersClasses.title}>
Filters ({selectedFiltersLength})
</Typography>
</div>
</Box>
<StyledMTableToolbar
{...toolbarProps}
ref={toolbarRef}
onSearchChanged={onSearchChanged}
/>
</div>
</Box>
);
}
@@ -484,7 +485,7 @@ export function Table<T extends object = {}>(props: TableProps<T>) {
);
return (
<div className={tableClasses.root}>
<Box className={tableClasses.root}>
{filtersOpen && data && typeof data !== 'function' && filters?.length && (
<Filters
filters={constructFilters(filters, data as any[])}
@@ -521,7 +522,7 @@ export function Table<T extends object = {}>(props: TableProps<T>) {
}}
{...restProps}
/>
</div>
</Box>
);
}
@@ -33,7 +33,7 @@ const useStyles = makeStyles<BackstageTheme>(
display: 'flex',
justifyContent: 'center',
backgroundColor: theme.palette.tabbar.indicator,
height: '4px',
height: theme.spacing(0.5),
},
flexContainer: {
alignItems: 'center',
@@ -29,16 +29,16 @@ interface StyledIconProps {
export type TabIconClassKey = 'root';
const useStyles = makeStyles<BackstageTheme, StyledIconProps>(
() => ({
theme => ({
root: {
color: '#6E6E6E',
overflow: 'visible',
fontSize: '1.5rem',
fontSize: theme.typography.h5.fontSize,
textAlign: 'center',
borderRadius: '50%',
backgroundColor: '#E6E6E6',
marginLeft: props => (props.isNext ? 'auto' : '0'),
marginRight: props => (props.isNext ? '0' : '10px'),
marginRight: props => (props.isNext ? '0' : theme.spacing(1.25)),
'&:hover': {
backgroundColor: '#E6E6E6',
opacity: '1',
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { PropsWithChildren } from 'react';
import Box from '@material-ui/core/Box';
import React, { PropsWithChildren } from 'react';
export interface TabPanelProps {
value?: any;
@@ -26,13 +25,13 @@ export const TabPanel = (props: PropsWithChildren<TabPanelProps>) => {
const { children, value, index, ...other } = props;
return (
<div
<Box
role="tabpanel"
hidden={value !== index}
aria-labelledby={`scrollable-auto-tab-${index}`}
{...other}
>
{value === index && <Box p={3}>{children}</Box>}
</div>
</Box>
);
};
@@ -29,6 +29,7 @@ import { TabPanel } from './TabPanel';
import { StyledIcon } from './TabIcon';
import { StyledTab } from './Tab';
import { StyledTabs } from './TabBar';
import Box from '@material-ui/core/Box';
/* Props Types */
@@ -56,8 +57,8 @@ const useStyles = makeStyles<BackstageTheme>(
appbar: {
boxShadow: 'none',
backgroundColor: theme.palette.background.paper,
paddingLeft: '10px',
paddingRight: '10px',
paddingLeft: theme.spacing(1.25),
paddingRight: theme.spacing(1.25),
},
}),
{ name: 'BackstageTabs' },
@@ -110,9 +111,9 @@ export function Tabs(props: TabsProps) {
const currentIndex = navIndex === value[0] ? value[1] : false;
return (
<div className={classes.root}>
<Box className={classes.root}>
<AppBar ref={wrapper} className={classes.appbar} position="static">
<div>
<Box>
<StyledTabs
value={currentIndex}
onChange={handleChange}
@@ -146,7 +147,7 @@ export function Tabs(props: TabsProps) {
</StyledIcon>
)}
</StyledTabs>
</div>
</Box>
</AppBar>
{currentIndex !== false ? (
chunkedTabs[navIndex].map((tab, index) => (
@@ -164,6 +165,6 @@ export function Tabs(props: TabsProps) {
{chunkedTabs[value[0]][value[1]].content}
</TabPanel>
)}
</div>
</Box>
);
}
@@ -92,7 +92,7 @@ const useStyles = makeStyles<BackstageTheme>(
severity as NonNullable<WarningProps['severity']>,
theme,
),
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
},
message: {
width: '100%',
@@ -61,7 +61,7 @@ export function BottomLink(props: BottomLinkProps) {
const classes = useStyles();
return (
<div>
<Box>
<Divider />
<Link to={link} onClick={onClick} underline="none">
<Box display="flex" alignItems="center" className={classes.root}>
@@ -73,6 +73,6 @@ export function BottomLink(props: BottomLinkProps) {
<ArrowIcon className={classes.arrow} />
</Box>
</Link>
</div>
</Box>
);
}
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* TODO favoriteable capability
*/
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React, { PropsWithChildren, ReactNode } from 'react';
import { Helmet } from 'react-helmet';
/**
* TODO: favoriteable capability
*/
/** @public */
export type ContentHeaderClassKey =
| 'container'
@@ -120,17 +120,17 @@ export function ContentHeader(props: PropsWithChildren<ContentHeaderProps>) {
return (
<>
<Helmet title={title} />
<div className={classes.container}>
<div className={classes.leftItemsBox}>
<Box className={classes.container}>
<Box className={classes.leftItemsBox}>
{renderedTitle}
{description && (
<Typography className={classes.description} variant="body2">
{description}
</Typography>
)}
</div>
<div className={classes.rightItemsBox}>{children}</div>
</div>
</Box>
<Box className={classes.rightItemsBox}>{children}</Box>
</Box>
</>
);
}
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import Typography from '@material-ui/core/Typography';
import React, { ComponentClass, Component, ErrorInfo } from 'react';
import { Button } from '../../components/Button';
import { ErrorPanel } from '../../components/ErrorPanel';
@@ -40,9 +41,11 @@ const SlackLink = (props: { slackChannel?: string | SlackChannel }) => {
if (!slackChannel) {
return null;
} else if (typeof slackChannel === 'string') {
return <>Please contact {slackChannel} for help.</>;
return <Typography>Please contact {slackChannel} for help.</Typography>;
} else if (!slackChannel.href) {
return <>Please contact {slackChannel.name} for help.</>;
return (
<Typography>Please contact {slackChannel.name} for help.</Typography>
);
}
return (
@@ -46,7 +46,7 @@ const useStyles = makeStyles<BackstageTheme>(
paddingBottom: theme.spacing(5),
[theme.breakpoints.down('xs')]: {
paddingBottom: theme.spacing(4),
fontSize: 32,
fontSize: theme.typography.h3.fontSize,
},
},
subtitle: {
@@ -44,7 +44,7 @@ const ActionItem = ({
onClick,
}: HeaderActionMenuItem) => {
return (
<React.Fragment>
<Fragment>
<ListItem
data-testid="header-action-item"
disabled={disabled}
@@ -58,7 +58,7 @@ const ActionItem = ({
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={label} secondary={secondaryLabel} />
</ListItem>
</React.Fragment>
</Fragment>
);
};
@@ -30,7 +30,7 @@ const useStyles = makeStyles(
},
label: {
color: theme.palette.common.white,
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
letterSpacing: 0,
fontSize: theme.typography.fontSize,
marginBottom: theme.spacing(1) / 2,
@@ -77,10 +77,10 @@ export function HeaderLabel(props: HeaderLabelProps) {
);
return (
<Grid item>
<span className={classes.root}>
<Typography component="span" className={classes.root}>
<Typography className={classes.label}>{label}</Typography>
{url ? <Link to={url}>{content}</Link> : content}
</span>
</Typography>
</Grid>
);
}
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// TODO(blam): Remove this implementation when the Tabs are ready
// This is just a temporary solution to implementing tabs for now
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import TabUI, { TabProps } from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import React, { useCallback, useEffect, useState } from 'react';
// TODO(blam): Remove this implementation when the Tabs are ready
// This is just a temporary solution to implementing tabs for now
/** @public */
export type HeaderTabsClassKey =
| 'tabsWrapper'
@@ -38,10 +38,10 @@ const useStyles = makeStyles(
minWidth: 0,
},
defaultTab: {
padding: theme.spacing(3, 3),
...theme.typography.caption,
padding: theme.spacing(3, 3),
textTransform: 'uppercase',
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
color: theme.palette.text.secondary,
},
selected: {
@@ -97,7 +97,7 @@ export function HeaderTabs(props: HeaderTabsProps) {
}, [selectedIndex]);
return (
<div className={styles.tabsWrapper}>
<Box className={styles.tabsWrapper}>
<Tabs
selectionFollowsFocus
indicatorColor="primary"
@@ -120,6 +120,6 @@ export function HeaderTabs(props: HeaderTabsProps) {
/>
))}
</Tabs>
</div>
</Box>
);
}
@@ -47,7 +47,7 @@ const useStyles = makeStyles(
padding: theme.spacing(2, 2, 2, 2.5),
},
headerTitle: {
fontWeight: 700,
fontWeight: theme.typography.fontWeightBold,
},
headerSubheader: {
paddingTop: theme.spacing(1),
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@material-ui/core/Box';
import {
createStyles,
makeStyles,
@@ -65,8 +65,8 @@ export function ItemCardGrid(props: ItemCardGridProps) {
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<div className={classes.root} {...otherProps}>
<Box className={classes.root} {...otherProps}>
{children}
</div>
</Box>
);
}
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import { createStyles, makeStyles, WithStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { BackstageTheme } from '@backstage/theme';
/** @public */
export type ItemCardHeaderClassKey = 'root';
@@ -77,7 +77,7 @@ export function ItemCardHeader(props: ItemCardHeaderProps) {
const { title, subtitle, children } = props;
const classes = useStyles(props);
return (
<div className={classes.root}>
<Box className={classes.root}>
{subtitle && (
<Typography variant="subtitle2" component="h3">
{subtitle}
@@ -89,6 +89,6 @@ export function ItemCardHeader(props: ItemCardHeaderProps) {
</Typography>
)}
{children}
</div>
</Box>
);
}
@@ -13,28 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import classnames from 'classnames';
import React, { useState, useContext, useRef } from 'react';
import Button from '@material-ui/core/Button';
import React, { useContext, useRef, useState } from 'react';
import {
makeSidebarConfig,
makeSidebarSubmenuConfig,
SidebarConfig,
SidebarConfigContext,
SubmenuConfig,
SidebarOptions,
SubmenuConfig,
SubmenuOptions,
} from './config';
import { BackstageTheme } from '@backstage/theme';
import { MobileSidebar } from './MobileSidebar';
import { useContent } from './Page';
import { SidebarOpenStateProvider } from './SidebarOpenStateContext';
import { useSidebarPinState } from './SidebarPinStateContext';
import { MobileSidebar } from './MobileSidebar';
/** @public */
export type SidebarClassKey = 'drawer' | 'drawerOpen';
@@ -191,7 +190,7 @@ const DesktopSidebar = (props: DesktopSidebarProps) => {
<nav style={{}} aria-label="sidebar nav">
<A11ySkipSidebar />
<SidebarOpenStateProvider value={{ isOpen, setOpen }}>
<div
<Box
className={classes.root}
data-testid="sidebar-root"
onMouseEnter={disableExpandOnHover ? () => {} : handleOpen}
@@ -199,14 +198,14 @@ const DesktopSidebar = (props: DesktopSidebarProps) => {
onMouseLeave={disableExpandOnHover ? () => {} : handleClose}
onBlur={disableExpandOnHover ? () => {} : handleClose}
>
<div
<Box
className={classnames(classes.drawer, {
[classes.drawerOpen]: isOpen,
})}
>
{children}
</div>
</div>
</Box>
</Box>
</SidebarOpenStateProvider>
</nav>
);
@@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import Collapse from '@material-ui/core/Collapse';
import { makeStyles } from '@material-ui/core/styles';
import IconButton from '@material-ui/core/IconButton';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import React, { useContext, useState } from 'react';
import { useLocalStorageValue } from '@react-hookz/web';
import React, { useContext, useState } from 'react';
import {
SidebarConfigContext,
SidebarConfig,
SIDEBAR_INTRO_LOCAL_STORAGE,
SidebarConfig,
SidebarConfigContext,
} from './config';
import { SidebarDivider } from './Items';
import { useSidebarOpenState } from './SidebarOpenStateContext';
@@ -45,8 +46,8 @@ const useStyles = makeStyles<BackstageTheme, { sidebarConfig: SidebarConfig }>(
// XXX (@koroeskohr): should I be using a Mui theme variable?
fontSize: 12,
width: props.sidebarConfig.drawerWidthOpen,
marginTop: 18,
marginBottom: 12,
marginTop: theme.spacing(2.25),
marginBottom: theme.spacing(1.5),
paddingLeft: props.sidebarConfig.iconPadding,
paddingRight: props.sidebarConfig.iconPadding,
}),
@@ -54,13 +55,13 @@ const useStyles = makeStyles<BackstageTheme, { sidebarConfig: SidebarConfig }>(
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
marginTop: 12,
marginTop: theme.spacing(1.5),
},
introDismissLink: {
color: '#dddddd',
display: 'flex',
alignItems: 'center',
marginBottom: 4,
marginBottom: theme.spacing(0.5),
'&:hover': {
color: theme.palette.linkHover,
transition: theme.transitions.create('color', {
@@ -78,7 +79,7 @@ const useStyles = makeStyles<BackstageTheme, { sidebarConfig: SidebarConfig }>(
introDismissIcon: {
width: 18,
height: 18,
marginRight: 12,
marginRight: theme.spacing(1.5),
},
}),
{ name: 'BackstageSidebarIntro' },
@@ -103,17 +104,17 @@ export function IntroCard(props: IntroCardProps) {
const handleClose = () => onClose();
return (
<div className={classes.introCard}>
<Box className={classes.introCard}>
<Typography variant="subtitle2">{text}</Typography>
<div className={classes.introDismiss}>
<Box className={classes.introDismiss}>
<IconButton onClick={handleClose} className={classes.introDismissLink}>
<CloseIcon className={classes.introDismissIcon} />
<Typography component="span" className={classes.introDismissText}>
Dismiss
</Typography>
</IconButton>
</div>
</div>
</Box>
</Box>
);
}
@@ -13,23 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { IconComponent, useElementFilter } from '@backstage/core-plugin-api';
import { BackstageTheme } from '@backstage/theme';
import { makeStyles, styled, Theme } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import Badge from '@material-ui/core/Badge';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import { makeStyles, styled, Theme } from '@material-ui/core/styles';
import {
CreateCSSProperties,
StyledComponentProps,
} from '@material-ui/core/styles/withStyles';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import ArrowDropUp from '@material-ui/icons/ArrowDropUp';
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
import SearchIcon from '@material-ui/icons/Search';
import ArrowDropUp from '@material-ui/icons/ArrowDropUp';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import classnames from 'classnames';
import { Location } from 'history';
import React, {
ComponentProps,
ComponentType,
@@ -48,18 +49,18 @@ import {
useLocation,
useResolvedPath,
} from 'react-router-dom';
import {
SidebarConfig,
SidebarConfigContext,
SidebarItemWithSubmenuContext,
SidebarConfig,
} from './config';
import { SidebarSubmenuProps, SidebarSubmenu } from './SidebarSubmenu';
import DoubleArrowLeft from './icons/DoubleArrowLeft';
import DoubleArrowRight from './icons/DoubleArrowRight';
import { isLocationMatch } from './utils';
import { Location } from 'history';
import { useSidebarOpenState } from './SidebarOpenStateContext';
import { SidebarSubmenu, SidebarSubmenuProps } from './SidebarSubmenu';
import { SidebarSubmenuItemProps } from './SidebarSubmenuItem';
import { isLocationMatch } from './utils';
/** @public */
export type SidebarItemClassKey =
@@ -145,7 +146,7 @@ const makeSidebarStyles = (sidebarConfig: SidebarConfig) =>
},
searchField: {
color: '#b5b5b5',
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
fontSize: theme.typography.fontSize,
},
searchFieldHTMLInput: {
@@ -373,10 +374,10 @@ const SidebarItemBase = forwardRef<any, SidebarItemProps>((props, ref) => {
!isOpen && hasSubmenu ? { display: 'flex', marginLeft: '24px' } : {};
const displayItemIcon = (
<div style={divStyle}>
<Box style={divStyle}>
<Icon fontSize="small" />
{!isOpen && hasSubmenu ? <ArrowRightIcon /> : <></>}
</div>
</Box>
);
const itemIcon = (
@@ -393,9 +394,9 @@ const SidebarItemBase = forwardRef<any, SidebarItemProps>((props, ref) => {
const openContent = (
<>
<div data-testid="login-button" className={classes.iconContainer}>
<Box data-testid="login-button" className={classes.iconContainer}>
{itemIcon}
</div>
</Box>
{text && (
<Typography variant="subtitle2" className={classes.label}>
{text}
@@ -572,7 +573,7 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) {
};
return (
<div className={classes.searchRoot}>
<Box className={classes.searchRoot}>
<SidebarItem
icon={Icon}
to={props.to}
@@ -595,7 +596,7 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) {
}}
/>
</SidebarItem>
</div>
</Box>
);
}
@@ -620,13 +621,13 @@ export const SidebarSpacer = styled('div')(
export type SidebarDividerClassKey = 'root';
export const SidebarDivider = styled('hr')(
{
({ theme }) => ({
height: 1,
width: '100%',
background: '#383838',
border: 'none',
margin: '12px 0px',
},
margin: theme.spacing(1.2, 0),
}),
{ name: 'BackstageSidebarDivider' },
) as ComponentType<ComponentProps<'hr'> & StyledComponentProps<'root'>>;
@@ -693,9 +694,9 @@ export const SidebarExpandButton = () => {
aria-label="Expand Sidebar"
data-testid="sidebar-expand-button"
>
<div className={classes.arrows}>
<Box className={classes.arrows}>
{isOpen ? <DoubleArrowLeft /> : <DoubleArrowRight />}
</div>
</Box>
</button>
);
};
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import React, {
createContext,
useCallback,
@@ -25,10 +26,9 @@ import React, {
useRef,
useState,
} from 'react';
import { SidebarConfigContext, SidebarConfig } from './config';
import { BackstageTheme } from '@backstage/theme';
import { SidebarConfig, SidebarConfigContext } from './config';
import { LocalStorage } from './localStorage';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import { SidebarPinStateProvider } from './SidebarPinStateContext';
export type SidebarPageClassKey = 'root';
@@ -122,7 +122,7 @@ export function SidebarPage(props: SidebarPageProps) {
}}
>
<PageContext.Provider value={pageContext}>
<div className={classes.root}>{props.children}</div>
<Box className={classes.root}>{props.children}</Box>
</PageContext.Provider>
</SidebarPinStateProvider>
);
@@ -13,17 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageTheme } from '@backstage/theme';
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import classnames from 'classnames';
import React, { ReactNode, useContext, useEffect, useState } from 'react';
import {
SidebarItemWithSubmenuContext,
SidebarConfigContext,
SidebarItemWithSubmenuContext,
SubmenuConfig,
} from './config';
import { useSidebarOpenState } from './SidebarOpenStateContext';
import { BackstageTheme } from '@backstage/theme';
const useStyles = makeStyles<
BackstageTheme,
@@ -76,10 +78,10 @@ const useStyles = makeStyles<
},
}),
title: {
fontSize: 24,
fontWeight: 500,
fontSize: theme.typography.h5.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: '#FFF',
padding: 20,
padding: theme.spacing(2.5),
[theme.breakpoints.down('xs')]: {
display: 'none',
},
@@ -120,7 +122,7 @@ export const SidebarSubmenu = (props: SidebarSubmenuProps) => {
}, [isHoveredOn]);
return (
<div
<Box
className={classnames(classes.drawer, {
[classes.drawerOpen]: isSubmenuOpen,
})}
@@ -129,6 +131,6 @@ export const SidebarSubmenu = (props: SidebarSubmenuProps) => {
{props.title}
</Typography>
{props.children}
</div>
</Box>
);
};
@@ -26,6 +26,7 @@ import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
import { SidebarItemWithSubmenuContext } from './config';
import { isLocationMatch } from './utils';
import Box from '@material-ui/core/Box';
const useStyles = makeStyles<BackstageTheme>(
theme => ({
@@ -39,7 +40,7 @@ const useStyles = makeStyles<BackstageTheme>(
display: 'flex',
alignItems: 'center',
color: theme.palette.navigation.color,
padding: 20,
padding: theme.spacing(2.5),
cursor: 'pointer',
position: 'relative',
background: 'none',
@@ -53,9 +54,9 @@ const useStyles = makeStyles<BackstageTheme>(
color: '#FFF',
},
label: {
margin: 14,
marginLeft: 7,
fontSize: 14,
margin: theme.spacing(1.75),
marginLeft: theme.spacing(1),
fontSize: theme.typography.body2.fontSize,
whiteSpace: 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis',
@@ -88,7 +89,7 @@ const useStyles = makeStyles<BackstageTheme>(
color: theme.palette.navigation.color,
paddingLeft: theme.spacing(4),
paddingRight: theme.spacing(1),
fontSize: '14px',
fontSize: theme.typography.body2.fontSize,
whiteSpace: 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis',
@@ -155,7 +156,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
return isActive;
});
return (
<div className={classes.itemContainer}>
<Box className={classes.itemContainer}>
<Tooltip title={title} enterDelay={500} enterNextDelay={500}>
<button
onClick={handleClickDropdown}
@@ -183,7 +184,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
</button>
</Tooltip>
{dropdownItems && showDropDown && (
<div className={classes.dropdown}>
<Box className={classes.dropdown}>
{dropdownItems.map((object, key) => (
<Tooltip
key={key}
@@ -204,14 +205,14 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
</Link>
</Tooltip>
))}
</div>
</Box>
)}
</div>
</Box>
);
}
return (
<div className={classes.itemContainer}>
<Box className={classes.itemContainer}>
<Tooltip title={title} enterDelay={500} enterNextDelay={500}>
<Link
to={to!}
@@ -235,6 +236,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
</Typography>
</Link>
</Tooltip>
</div>
</Box>
);
};
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
import React from 'react';
const useStyles = makeStyles({
iconContainer: {
@@ -34,14 +34,14 @@ const DoubleArrowLeft = () => {
const classes = useStyles();
return (
<div className={classes.iconContainer}>
<div className={classes.arrow1}>
<Box className={classes.iconContainer}>
<Box className={classes.arrow1}>
<ArrowBackIosIcon style={{ fontSize: '12px' }} />
</div>
<div>
</Box>
<Box>
<ArrowBackIosIcon style={{ fontSize: '12px' }} />
</div>
</div>
</Box>
</Box>
);
};
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import Box from '@material-ui/core/Box';
import { makeStyles } from '@material-ui/core/styles';
import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
import React from 'react';
const useStyles = makeStyles({
iconContainer: {
@@ -34,14 +34,14 @@ const DoubleArrowRight = () => {
const classes = useStyles();
return (
<div className={classes.iconContainer}>
<div className={classes.arrow1}>
<Box className={classes.iconContainer}>
<Box className={classes.arrow1}>
<ArrowForwardIosIcon style={{ fontSize: '12px' }} />
</div>
<div>
</Box>
<Box>
<ArrowForwardIosIcon style={{ fontSize: '12px' }} />
</div>
</div>
</Box>
</Box>
);
};
@@ -96,7 +96,7 @@ export function TabbedCard(props: PropsWithChildren<Props>) {
} else {
React.Children.map(children, child => {
if (React.isValidElement(child) && child?.props.value === value) {
selectedTabContent = child?.props.children;
selectedTabContent = child?.props?.children;
}
});
}
@@ -142,7 +142,7 @@ const useCardTabStyles = makeStyles(
},
},
selected: {
fontWeight: 'bold',
fontWeight: theme.typography.fontWeightBold,
},
}),
{ name: 'BackstageCardTab' },
+4
View File
@@ -23,6 +23,7 @@ import { FetchApi } from '@backstage/core-plugin-api';
import { IdentityApi } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
import { JsonValue } from '@backstage/types';
import { MatcherFunction } from '@testing-library/react';
import { Observable } from '@backstage/types';
import { PermissionApi } from '@backstage/plugin-permission-react';
import { PropsWithChildren } from 'react';
@@ -241,6 +242,9 @@ export type TestAppOptions = {
};
};
// @public
export const textContentMatcher: (text: string) => MatcherFunction;
// @public
export function withLogCollector(
callback: AsyncLogCollector,
@@ -29,7 +29,7 @@ import {
attachComponentData,
createRouteRef,
} from '@backstage/core-plugin-api';
import { RenderResult } from '@testing-library/react';
import { MatcherFunction, RenderResult } from '@testing-library/react';
import { renderWithEffects } from './testingLibrary';
import { defaultApis } from './defaultApis';
import { mockApis } from './mockApis';
@@ -242,3 +242,24 @@ export async function renderInTestApp(
wrapper: createTestAppWrapper(options),
});
}
/**
* Returns a `@testing-library/react` valid MatcherFunction for supplied text
*
* @param string - text Text to match by element's textContent
*
* @public
*/
export const textContentMatcher =
(text: string): MatcherFunction =>
(_, node) => {
if (!node) {
return false;
}
const hasText = (textNode: Element) => textNode?.textContent === text;
const childrenDontHaveText = (containerNode: Element) =>
Array.from(containerNode?.children).every(child => !hasText(child));
return hasText(node) && childrenDontHaveText(node);
};
@@ -19,6 +19,7 @@ export { default as mockBreakpoint } from './mockBreakpoint';
export {
wrapInTestApp,
renderInTestApp,
textContentMatcher,
createTestAppWrapper,
} from './appWrappers';
export type { TestAppOptions } from './appWrappers';
@@ -55,7 +55,7 @@ describe('Features', () => {
expect(getByTestId(TEST_IDS.info.info)).toMatchInlineSnapshot(`
<div
class="MuiBox-root MuiBox-root-10"
class="MuiBox-root MuiBox-root-11"
data-testid="grm--info"
>
<h6
@@ -72,7 +72,7 @@ describe('Features', () => {
: The source control system where releases reside in a practical sense. Read more about
<a
class="MuiTypography-root MuiLink-root MuiLink-underlineHover Link-externalLink-12 MuiTypography-colorPrimary"
class="MuiTypography-root MuiLink-root MuiLink-underlineHover Link-externalLink-13 MuiTypography-colorPrimary"
href="https://docs.github.com/en/github/administering-a-repository/managing-releases-in-a-repository"
rel="noopener"
target="_blank"
@@ -80,7 +80,7 @@ describe('Features', () => {
>
Git releases
<span
class="Link-visuallyHidden-11"
class="MuiTypography-root Link-visuallyHidden-12 MuiTypography-body1"
>
, Opens in a new window
</span>
@@ -16,7 +16,7 @@
import React from 'react';
import * as deployments from '../../__fixtures__/2-deployments.json';
import { renderInTestApp } from '@backstage/test-utils';
import { renderInTestApp, textContentMatcher } from '@backstage/test-utils';
import { DeploymentDrawer } from './DeploymentDrawer';
describe('DeploymentDrawer', () => {
@@ -33,9 +33,13 @@ describe('DeploymentDrawer', () => {
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Strategy')).toBeInTheDocument();
expect(getByText('Rolling Update:')).toBeInTheDocument();
expect(getByText('Max Surge: 25%')).toBeInTheDocument();
expect(getByText('Max Unavailable: 25%')).toBeInTheDocument();
expect(getByText('Type: RollingUpdate')).toBeInTheDocument();
expect(getByText(textContentMatcher('Max Surge: 25%'))).toBeInTheDocument();
expect(
getByText(textContentMatcher('Max Unavailable: 25%')),
).toBeInTheDocument();
expect(
getByText(textContentMatcher('Type: RollingUpdate')),
).toBeInTheDocument();
expect(getByText('Min Ready Seconds')).toBeInTheDocument();
expect(getByText('???')).toBeInTheDocument();
expect(getByText('Progress Deadline Seconds')).toBeInTheDocument();
@@ -17,7 +17,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import * as ingresses from './__fixtures__/2-ingresses.json';
import { wrapInTestApp } from '@backstage/test-utils';
import { textContentMatcher, wrapInTestApp } from '@backstage/test-utils';
import { IngressDrawer } from './IngressDrawer';
describe('IngressDrawer', () => {
@@ -28,11 +28,17 @@ describe('IngressDrawer', () => {
),
);
expect(getAllByText('awesome-service')).toHaveLength(2);
expect(getAllByText('awesome-service')).toHaveLength(4);
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Rules')).toBeInTheDocument();
expect(getByText('Host: api.awesome-host.io')).toBeInTheDocument();
expect(getAllByText('Service Port: 80')).toHaveLength(2);
expect(getAllByText('Service Name: awesome-service')).toHaveLength(2);
expect(
getByText(textContentMatcher('Host: api.awesome-host.io')),
).toBeInTheDocument();
expect(getAllByText(textContentMatcher('Service Port: 80'))).toHaveLength(
2,
);
expect(
getAllByText(textContentMatcher('Service Name: awesome-service')),
).toHaveLength(2);
});
});
+15 -7
View File
@@ -18,7 +18,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import * as pod from './__fixtures__/pod.json';
import * as crashingPod from './__fixtures__/crashing-pod.json';
import { wrapInTestApp } from '@backstage/test-utils';
import { textContentMatcher, wrapInTestApp } from '@backstage/test-utils';
import { PodDrawer } from './PodDrawer';
describe('PodDrawer', () => {
@@ -46,8 +46,10 @@ describe('PodDrawer', () => {
expect(getAllByText('True')).toHaveLength(4);
expect(getByText('Exposed Ports')).toBeInTheDocument();
expect(getByText('Nginx:')).toBeInTheDocument();
expect(getByText('Container Port: 80')).toBeInTheDocument();
expect(getByText('Protocol: TCP')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Container Port: 80')),
).toBeInTheDocument();
expect(getByText(textContentMatcher('Protocol: TCP'))).toBeInTheDocument();
});
it('should render crashing pod', async () => {
const { getByText, getAllByText } = render(
@@ -80,12 +82,18 @@ describe('PodDrawer', () => {
getAllByText('containers with unready status: [side-car other-side-car]'),
).toHaveLength(2);
expect(getByText('Exposed Ports')).toBeInTheDocument();
expect(getAllByText('Protocol: TCP')).toHaveLength(3);
expect(getAllByText(textContentMatcher('Protocol: TCP'))).toHaveLength(3);
expect(getByText('Nginx:')).toBeInTheDocument();
expect(getByText('Container Port: 80')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Container Port: 80')),
).toBeInTheDocument();
expect(getByText('Side Car:')).toBeInTheDocument();
expect(getByText('Container Port: 81')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Container Port: 81')),
).toBeInTheDocument();
expect(getByText('Other Side Car:')).toBeInTheDocument();
expect(getByText('Container Port: 82')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Container Port: 82')),
).toBeInTheDocument();
});
});
@@ -17,7 +17,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import * as services from './__fixtures__/2-services.json';
import { wrapInTestApp } from '@backstage/test-utils';
import { textContentMatcher, wrapInTestApp } from '@backstage/test-utils';
import { ServiceDrawer } from './ServiceDrawer';
describe('ServiceDrawer', () => {
@@ -33,7 +33,11 @@ describe('ServiceDrawer', () => {
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Cluster IP')).toBeInTheDocument();
expect(getByText('Ports')).toBeInTheDocument();
expect(getByText('Target Port: 1997')).toBeInTheDocument();
expect(getByText('App: awesome-service')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Target Port: 1997')),
).toBeInTheDocument();
expect(
getByText(textContentMatcher('App: awesome-service')),
).toBeInTheDocument();
});
});
@@ -16,7 +16,7 @@
import React from 'react';
import * as statefulsets from '../../__fixtures__/2-statefulsets.json';
import { renderInTestApp } from '@backstage/test-utils';
import { renderInTestApp, textContentMatcher } from '@backstage/test-utils';
import { StatefulSetDrawer } from './StatefulSetDrawer';
describe('StatefulSetDrawer', () => {
@@ -28,19 +28,25 @@ describe('StatefulSetDrawer', () => {
/>,
);
expect(getAllByText('dice-roller')).toHaveLength(3);
expect(getAllByText('dice-roller')).toHaveLength(4);
expect(getByText('StatefulSet')).toBeInTheDocument();
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Type: RollingUpdate')).toBeInTheDocument();
expect(
getByText(textContentMatcher('Type: RollingUpdate')),
).toBeInTheDocument();
expect(getByText('Rolling Update:')).toBeInTheDocument();
expect(getByText('Max Surge: 25%')).toBeInTheDocument();
expect(getByText('Max Unavailable: 25%')).toBeInTheDocument();
expect(getByText(textContentMatcher('Max Surge: 25%'))).toBeInTheDocument();
expect(
getByText(textContentMatcher('Max Unavailable: 25%')),
).toBeInTheDocument();
expect(getByText('Pod Management Policy')).toBeInTheDocument();
expect(getByText('Parallel')).toBeInTheDocument();
expect(getByText('Service Name')).toBeInTheDocument();
expect(getByText('Selector')).toBeInTheDocument();
expect(getByText('Match Labels:')).toBeInTheDocument();
expect(getByText('App: dice-roller')).toBeInTheDocument();
expect(
getByText(textContentMatcher('App: dice-roller')),
).toBeInTheDocument();
expect(getByText('Revision History Limit')).toBeInTheDocument();
expect(getByText('10')).toBeInTheDocument();
expect(getByText('namespace: default')).toBeInTheDocument();