Skip to content

Commit

Permalink
Merge pull request #24880 from debsmita1/status-icons
Browse files Browse the repository at this point in the history
adds icons to status component
  • Loading branch information
freben committed Jun 11, 2024
2 parents 7bb2143 + 83c4251 commit 6839b05
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/spotty-ravens-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---

Adds icons to status component
28 changes: 22 additions & 6 deletions packages/core-components/src/components/Status/Status.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
168 changes: 118 additions & 50 deletions packages/core-components/src/components/Status/Status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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>
);
}
Original file line number Diff line number Diff line change
@@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -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>
);
};
Loading

0 comments on commit 6839b05

Please sign in to comment.