Some more TS weekend buffoonery
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withStyles } from '@material-ui/core';
|
||||
import { Circle } from 'rc-progress';
|
||||
import { COLORS, V1 } from '../theme/BackstageTheme';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
lineHeight: 0,
|
||||
},
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -60%)',
|
||||
fontSize: 45,
|
||||
fontWeight: 'bold',
|
||||
color: theme.palette.textSubtle,
|
||||
},
|
||||
circle: {
|
||||
width: '80%',
|
||||
transform: 'translate(10%, 0)',
|
||||
},
|
||||
});
|
||||
|
||||
class CircleProgress extends Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.any.isRequired,
|
||||
fractional: PropTypes.bool,
|
||||
classes: PropTypes.object.isRequired,
|
||||
inverse: PropTypes.bool,
|
||||
unit: PropTypes.string,
|
||||
max: PropTypes.number,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
fractional: true,
|
||||
inverse: false,
|
||||
unit: '%',
|
||||
max: 100,
|
||||
};
|
||||
|
||||
static getProgressColor(value, inverse, max /* , classes */) {
|
||||
if (isNaN(value)) {
|
||||
return V1.palette.textVerySubtle;
|
||||
}
|
||||
|
||||
const actualMax = max ? max : CircleProgress.defaultProps.max;
|
||||
const actualValue = inverse ? actualMax - value : value;
|
||||
|
||||
if (actualValue < actualMax / 3) {
|
||||
return COLORS.STATUS.ERROR;
|
||||
} else if (actualValue < actualMax * (2 / 3)) {
|
||||
return COLORS.STATUS.WARNING;
|
||||
}
|
||||
return COLORS.STATUS.OK;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value, fractional, classes, inverse, unit, max } = this.props;
|
||||
|
||||
const asPercentage = fractional ? Math.round(value * max) : value;
|
||||
const asActual = max !== 100 ? Math.round(value) : asPercentage;
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Circle
|
||||
strokeLinecap="butt"
|
||||
percent={asPercentage}
|
||||
strokeWidth="12"
|
||||
trailWidth="12"
|
||||
strokeColor={CircleProgress.getProgressColor(
|
||||
asActual,
|
||||
inverse,
|
||||
max,
|
||||
classes,
|
||||
)}
|
||||
className={classes.circle}
|
||||
/>
|
||||
<div className={classes.overlay}>
|
||||
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(CircleProgress);
|
||||
@@ -17,8 +17,8 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInThemedTestApp } from '../testUtils';
|
||||
import CircleProgress from './CircleProgress';
|
||||
import { COLORS, V1 } from '../theme/BackstageTheme';
|
||||
import CircleProgress, { getProgressColor } from './CircleProgress';
|
||||
import { COLORS } from '../theme/BackstageTheme';
|
||||
|
||||
describe('<CircleProgress />', () => {
|
||||
it('renders without exploding', () => {
|
||||
@@ -53,18 +53,16 @@ describe('<CircleProgress />', () => {
|
||||
});
|
||||
|
||||
it('colors the progress correctly', () => {
|
||||
expect(CircleProgress.getProgressColor()).toBe(V1.palette.textVerySubtle);
|
||||
expect(CircleProgress.getProgressColor(10)).toBe(COLORS.STATUS.ERROR);
|
||||
expect(CircleProgress.getProgressColor(50)).toBe(COLORS.STATUS.WARNING);
|
||||
expect(CircleProgress.getProgressColor(90)).toBe(COLORS.STATUS.OK);
|
||||
expect(getProgressColor()).toBe('#ddd');
|
||||
expect(getProgressColor(10)).toBe(COLORS.STATUS.ERROR);
|
||||
expect(getProgressColor(50)).toBe(COLORS.STATUS.WARNING);
|
||||
expect(getProgressColor(90)).toBe(COLORS.STATUS.OK);
|
||||
});
|
||||
|
||||
it('colors the inverse progress correctly', () => {
|
||||
expect(CircleProgress.getProgressColor()).toBe(V1.palette.textVerySubtle);
|
||||
expect(CircleProgress.getProgressColor(10, true)).toBe(COLORS.STATUS.OK);
|
||||
expect(CircleProgress.getProgressColor(50, true)).toBe(
|
||||
COLORS.STATUS.WARNING,
|
||||
);
|
||||
expect(CircleProgress.getProgressColor(90, true)).toBe(COLORS.STATUS.ERROR);
|
||||
expect(getProgressColor()).toBe('#ddd');
|
||||
expect(getProgressColor(10, true)).toBe(COLORS.STATUS.OK);
|
||||
expect(getProgressColor(50, true)).toBe(COLORS.STATUS.WARNING);
|
||||
expect(getProgressColor(90, true)).toBe(COLORS.STATUS.ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { makeStyles } from '@material-ui/core';
|
||||
import { Circle } from 'rc-progress';
|
||||
import React, { FC } from 'react';
|
||||
import { COLORS } from '../theme/BackstageTheme';
|
||||
import { BackstageTheme } from '../theme/theme';
|
||||
|
||||
const useStyles = makeStyles((theme: BackstageTheme) => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
lineHeight: 0,
|
||||
},
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -60%)',
|
||||
fontSize: 45,
|
||||
fontWeight: 'bold',
|
||||
color: theme.palette.textSubtle,
|
||||
},
|
||||
circle: {
|
||||
width: '80%',
|
||||
transform: 'translate(10%, 0)',
|
||||
},
|
||||
colorUnknown: {},
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
value: number;
|
||||
fractional?: boolean;
|
||||
inverse?: boolean;
|
||||
unit?: string;
|
||||
max?: number;
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
fractional: true,
|
||||
inverse: false,
|
||||
unit: '%',
|
||||
max: 100,
|
||||
};
|
||||
|
||||
export function getProgressColor(value, inverse, max) {
|
||||
if (isNaN(value)) {
|
||||
return '#ddd';
|
||||
}
|
||||
|
||||
const actualMax = max ? max : defaultProps.max;
|
||||
const actualValue = inverse ? actualMax - value : value;
|
||||
|
||||
if (actualValue < actualMax / 3) {
|
||||
return COLORS.STATUS.ERROR;
|
||||
} else if (actualValue < actualMax * (2 / 3)) {
|
||||
return COLORS.STATUS.WARNING;
|
||||
}
|
||||
|
||||
return COLORS.STATUS.OK;
|
||||
}
|
||||
|
||||
const CircleProgress: FC<Props> = props => {
|
||||
const classes = useStyles(props);
|
||||
const { value, fractional, inverse, unit, max } = {
|
||||
...defaultProps,
|
||||
...props,
|
||||
};
|
||||
|
||||
const asPercentage = fractional ? Math.round(value * max) : value;
|
||||
const asActual = max !== 100 ? Math.round(value) : asPercentage;
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Circle
|
||||
strokeLinecap="butt"
|
||||
percent={asPercentage}
|
||||
strokeWidth={12}
|
||||
trailWidth={12}
|
||||
strokeColor={getProgressColor(asActual, inverse, max)}
|
||||
className={classes.circle}
|
||||
/>
|
||||
<div className={classes.overlay}>
|
||||
{isNaN(value) ? 'N/A' : `${asActual}${unit}`}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CircleProgress;
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withStyles } from '@material-ui/core';
|
||||
|
||||
import InfoCard from '../layout/InfoCard';
|
||||
import CircleProgress from './CircleProgress';
|
||||
|
||||
const styles = {
|
||||
root: {
|
||||
height: '100%',
|
||||
width: 250,
|
||||
},
|
||||
};
|
||||
|
||||
class ProgressCard extends Component {
|
||||
static propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
subheader: PropTypes.string,
|
||||
progress: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||
.isRequired,
|
||||
deepLink: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.shape({
|
||||
title: PropTypes.string.isRequired,
|
||||
link: PropTypes.string.isRequired,
|
||||
}),
|
||||
]),
|
||||
gacontext: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
title,
|
||||
subheader,
|
||||
progress,
|
||||
deepLink,
|
||||
classes,
|
||||
variant,
|
||||
} = this.props;
|
||||
const link =
|
||||
deepLink &&
|
||||
(typeof deepLink === 'string'
|
||||
? { title: 'View more', link: deepLink }
|
||||
: deepLink);
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<InfoCard
|
||||
title={title}
|
||||
subheader={subheader}
|
||||
deepLink={link}
|
||||
variant={variant}
|
||||
>
|
||||
<CircleProgress value={progress} />
|
||||
</InfoCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ProgressCard);
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { FC } from 'react';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
import InfoCard from '../layout/InfoCard';
|
||||
import CircleProgress from './CircleProgress';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
subheader?: string;
|
||||
variant?: string;
|
||||
progress: number | string;
|
||||
deepLink?: string | { title: string; link: string };
|
||||
gacontext?: string;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
height: '100%',
|
||||
width: 250,
|
||||
},
|
||||
});
|
||||
|
||||
const ProgressCard: FC<Props> = props => {
|
||||
const classes = useStyles(props);
|
||||
const { title, subheader, progress, deepLink, variant } = props;
|
||||
|
||||
// TODO(freben): Make type more strict when InfoCard is written in TS
|
||||
let link: any = undefined;
|
||||
if (deepLink) {
|
||||
if (typeof deepLink === 'string') {
|
||||
link = { title: 'View more', link: deepLink };
|
||||
} else {
|
||||
link = deepLink;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<InfoCard
|
||||
title={title}
|
||||
subheader={subheader}
|
||||
deepLink={link}
|
||||
variant={variant}
|
||||
>
|
||||
<CircleProgress value={Number(progress)} />
|
||||
</InfoCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressCard;
|
||||
Vendored
+13
@@ -26,5 +26,18 @@ export type BackstageTheme = Theme & {
|
||||
running: string;
|
||||
background: string;
|
||||
};
|
||||
border: string;
|
||||
textVerySubtle: string;
|
||||
textSubtle: string;
|
||||
highlight: string;
|
||||
errorBackground: string;
|
||||
warningBackground: string;
|
||||
infoBackground: string;
|
||||
errorText: string;
|
||||
infoText: string;
|
||||
warningText: string;
|
||||
linkHover: string;
|
||||
link: string;
|
||||
gold: string;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user