Merge pull request #24880 from debsmita1/status-icons

adds icons to status component
This commit is contained in:
Fredrik Adelöw
2024-06-11 08:51:39 +02:00
committed by GitHub
6 changed files with 253 additions and 56 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Adds icons to status component
@@ -28,42 +28,58 @@ import {
describe('<StatusOK />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusOK />);
const { getByLabelText, getByTestId } = await renderInTestApp(<StatusOK />);
expect(getByLabelText('Status ok')).toBeInTheDocument();
expect(getByTestId('status-ok')).toBeInTheDocument();
});
});
describe('<StatusWarning />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusWarning />);
const { getByLabelText, getByTestId } = await renderInTestApp(
<StatusWarning />,
);
expect(getByLabelText('Status warning')).toBeInTheDocument();
expect(getByTestId('status-warning')).toBeInTheDocument();
});
});
describe('<StatusError />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusError />);
const { getByLabelText, getByTestId } = await renderInTestApp(
<StatusError />,
);
expect(getByLabelText('Status error')).toBeInTheDocument();
expect(getByTestId('status-error')).toBeInTheDocument();
});
});
describe('<StatusPending />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusPending />);
const { getByLabelText, getByTestId } = await renderInTestApp(
<StatusPending />,
);
expect(getByLabelText('Status pending')).toBeInTheDocument();
expect(getByTestId('status-pending')).toBeInTheDocument();
});
});
describe('<StatusRunning />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusRunning />);
const { getByLabelText, getByTestId } = await renderInTestApp(
<StatusRunning />,
);
expect(getByLabelText('Status running')).toBeInTheDocument();
expect(getByTestId('status-running')).toBeInTheDocument();
});
});
describe('<StatusAborted />', () => {
it('renders without exploding', async () => {
const { getByLabelText } = await renderInTestApp(<StatusAborted />);
const { getByLabelText, getByTestId } = await renderInTestApp(
<StatusAborted />,
);
expect(getByLabelText('Status aborted')).toBeInTheDocument();
expect(getByTestId('status-aborted')).toBeInTheDocument();
});
});
@@ -16,8 +16,14 @@
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
import WarningOutline from '@material-ui/icons/ReportProblemOutlined';
import ErrorOutline from '@material-ui/icons/ErrorOutline';
import classNames from 'classnames';
import React, { PropsWithChildren } from 'react';
import { PendingIcon } from './icons/PendingIcon';
import { RunningIcon } from './icons/RunningIcon';
import { AbortedIcon } from './icons/AbortedIcon';
export type StatusClassKey =
| 'status'
@@ -32,123 +38,185 @@ const useStyles = makeStyles(
theme => ({
status: {
fontWeight: theme.typography.fontWeightMedium,
'&::before': {
width: '0.7em',
height: '0.7em',
display: 'inline-block',
marginRight: theme.spacing(1),
borderRadius: '50%',
content: '""',
},
alignItems: 'baseline',
display: 'flex',
},
statusIcon: {
flexShrink: 0,
position: 'relative',
top: '0.125em',
marginRight: theme.spacing(1),
},
statusIconSize: {
width: '0.8em',
height: '0.8em',
},
statusIconSizeForImg: {
width: '1.2em',
height: '1.2em',
},
ok: {
'&::before': {
backgroundColor: theme.palette.status.ok,
},
fill: theme.palette.status.ok || '#3E8635',
},
warning: {
'&::before': {
backgroundColor: theme.palette.status.warning,
},
fill: theme.palette.status.warning || '#F0AB00',
},
error: {
'&::before': {
backgroundColor: theme.palette.status.error,
},
fill: theme.palette.status.error || '#C9190B',
},
pending: {
'&::before': {
backgroundColor: theme.palette.status.pending,
},
fill: theme.palette.status.aborted || '#6A6E73',
},
running: {
'&::before': {
backgroundColor: theme.palette.status.running,
},
fill: theme.palette.status.aborted || '#6A6E73',
},
aborted: {
'&::before': {
backgroundColor: theme.palette.status.aborted,
},
fill: theme.palette.status.aborted || '#6A6E73',
},
}),
{ name: 'BackstageStatus' },
);
export function StatusOK(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.ok)}
className={classNames(classes.status)}
aria-label="Status ok"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<CheckCircleOutline
data-testid="status-ok"
className={classNames(
classes.ok,
classes.statusIconSize,
classes.statusIcon,
)}
/>
{children}
</Typography>
);
}
export function StatusWarning(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.warning)}
className={classNames(classes.status)}
aria-label="Status warning"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<WarningOutline
data-testid="status-warning"
className={classNames(
classes.warning,
classes.statusIconSize,
classes.statusIcon,
)}
/>
{children}
</Typography>
);
}
export function StatusError(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.error)}
className={classNames(classes.status)}
aria-label="Status error"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<ErrorOutline
data-testid="status-error"
className={classNames(
classes.error,
classes.statusIconSize,
classes.statusIcon,
)}
/>
{children}
</Typography>
);
}
export function StatusPending(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.pending)}
className={classNames(classes.status)}
aria-label="Status pending"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<PendingIcon
dataTestId="status-pending"
className={classNames(
classes.pending,
classes.statusIconSizeForImg,
classes.statusIcon,
)}
/>
{children}
</Typography>
);
}
export function StatusRunning(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.running)}
className={classNames(classes.status)}
aria-label="Status running"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<RunningIcon
dataTestId="status-running"
className={classNames(
classes.running,
classes.statusIcon,
classes.statusIconSizeForImg,
)}
/>
{children}
</Typography>
);
}
export function StatusAborted(props: PropsWithChildren<{}>) {
const classes = useStyles(props);
const { children, ...otherProps } = props;
const classes = useStyles(otherProps);
return (
<Typography
component="span"
className={classNames(classes.status, classes.aborted)}
className={classNames(classes.status)}
aria-label="Status aborted"
aria-hidden="true"
{...props}
/>
{...otherProps}
>
<AbortedIcon
dataTestId="status-aborted"
className={classNames(
classes.aborted,
classes.statusIcon,
classes.statusIconSizeForImg,
)}
/>
{children}
</Typography>
);
}
@@ -0,0 +1,36 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
export const AbortedIcon = ({
className,
dataTestId,
}: {
className: string;
dataTestId: string;
}): React.ReactElement => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
className={className}
data-testid={dataTestId}
>
<path d="M140-440q-25 0-42.5-17.5T80-500q0-25 17.5-42.5T140-560h240q25 0 42.5 17.5T440-500q0 25-17.5 42.5T380-440H140Zm440 0q-25 0-42.5-17.5T520-500q0-25 17.5-42.5T580-560h240q25 0 42.5 17.5T880-500q0 25-17.5 42.5T820-440H580Z" />
</svg>
);
};
@@ -0,0 +1,36 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
export const PendingIcon = ({
className,
dataTestId,
}: {
className: string;
dataTestId: string;
}): React.ReactElement => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
className={className}
data-testid={dataTestId}
>
<path d="M280-420q25 0 42.5-17.5T340-480q0-25-17.5-42.5T280-540q-25 0-42.5 17.5T220-480q0 25 17.5 42.5T280-420Zm200 0q25 0 42.5-17.5T540-480q0-25-17.5-42.5T480-540q-25 0-42.5 17.5T420-480q0 25 17.5 42.5T480-420Zm200 0q25 0 42.5-17.5T740-480q0-25-17.5-42.5T680-540q-25 0-42.5 17.5T620-480q0 25 17.5 42.5T680-420ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" />
</svg>
);
};
@@ -0,0 +1,36 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
export const RunningIcon = ({
className,
dataTestId,
}: {
className: string;
dataTestId: string;
}): React.ReactElement => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
className={className}
data-testid={dataTestId}
>
<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q17 0 28.5 11.5T520-840q0 17-11.5 28.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160q133 0 226.5-93.5T800-480q0-17 11.5-28.5T840-520q17 0 28.5 11.5T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Z" />
</svg>
);
};