mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat: limit component (#7538)
This commit is contained in:
parent
30073d527a
commit
c93bfafb7f
67
frontend/src/component/common/Limit/Limit.test.tsx
Normal file
67
frontend/src/component/common/Limit/Limit.test.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { render } from 'utils/testRenderer';
|
||||||
|
import { Limit } from './Limit';
|
||||||
|
|
||||||
|
test('Render approaching limit variant', () => {
|
||||||
|
render(
|
||||||
|
<Limit
|
||||||
|
name='strategies in this environment'
|
||||||
|
shortName='strategies'
|
||||||
|
limit={10}
|
||||||
|
currentValue={8}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText(
|
||||||
|
'You are nearing the limit for strategies in this environment',
|
||||||
|
);
|
||||||
|
screen.getByText('80%');
|
||||||
|
screen.getByText('Limit: 10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Render reached limit variant', () => {
|
||||||
|
render(
|
||||||
|
<Limit
|
||||||
|
name='strategies in this environment'
|
||||||
|
shortName='strategies'
|
||||||
|
limit={10}
|
||||||
|
currentValue={10}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText(
|
||||||
|
'You have reached the limit for strategies in this environment',
|
||||||
|
);
|
||||||
|
screen.getByText('100%');
|
||||||
|
screen.getByText('Limit: 10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Render exceeded limit variant', () => {
|
||||||
|
render(
|
||||||
|
<Limit
|
||||||
|
name='strategies in this environment'
|
||||||
|
shortName='strategies'
|
||||||
|
limit={10}
|
||||||
|
currentValue={20}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText(
|
||||||
|
'You have reached the limit for strategies in this environment',
|
||||||
|
);
|
||||||
|
screen.getByText('200%');
|
||||||
|
screen.getByText('Limit: 10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Do not render any limit below threshold', () => {
|
||||||
|
render(
|
||||||
|
<Limit
|
||||||
|
name='strategies in this environment'
|
||||||
|
shortName='strategies'
|
||||||
|
limit={10}
|
||||||
|
currentValue={7}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByText('Limit: 10')).not.toBeInTheDocument();
|
||||||
|
});
|
144
frontend/src/component/common/Limit/Limit.tsx
Normal file
144
frontend/src/component/common/Limit/Limit.tsx
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { Box, IconButton, styled, Tooltip, Typography } from '@mui/material';
|
||||||
|
import LinearProgress from '@mui/material/LinearProgress';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import WarningIcon from '@mui/icons-material/ErrorOutlined';
|
||||||
|
import ErrorIcon from '@mui/icons-material/Cancel';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
border: `2px solid ${theme.palette.background.application}`,
|
||||||
|
borderRadius: `${theme.shape.borderRadiusMedium}px`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
|
||||||
|
height: theme.spacing(1.5),
|
||||||
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledWarningIcon = styled(WarningIcon)(({ theme }) => ({
|
||||||
|
color: theme.palette.warning.border,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledErrorIcon = styled(ErrorIcon)(({ theme }) => ({
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Header = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
alignItems: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
borderBottom: `2px solid ${theme.palette.background.application}`,
|
||||||
|
padding: theme.spacing(3, 4),
|
||||||
|
fontSize: theme.typography.h2.fontSize,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Footer = styled(Box)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(3, 4),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Main = styled(Box)(({ theme }) => ({
|
||||||
|
borderBottom: `2px solid ${theme.palette.background.application}`,
|
||||||
|
padding: theme.spacing(3, 4),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LimitStats = styled(Box)(({ theme }) => ({
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LimitExplanation = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: theme.spacing(1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ExpandableBox = styled(Box)(({ theme }) => ({
|
||||||
|
flex: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const Limit: FC<{
|
||||||
|
name: string;
|
||||||
|
shortName?: string;
|
||||||
|
limit: number;
|
||||||
|
currentValue: number;
|
||||||
|
onClose?: () => void;
|
||||||
|
}> = ({ name, shortName, limit, currentValue, onClose }) => {
|
||||||
|
const percentageLimit = Math.round((currentValue / limit) * 100);
|
||||||
|
const belowLimit = currentValue < limit;
|
||||||
|
const threshold = 80;
|
||||||
|
|
||||||
|
if (percentageLimit < threshold) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledBox>
|
||||||
|
<Header>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={belowLimit}
|
||||||
|
show={<StyledWarningIcon fontSize='large' />}
|
||||||
|
elseShow={<StyledErrorIcon fontSize='large' />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={belowLimit}
|
||||||
|
show={
|
||||||
|
<ExpandableBox>
|
||||||
|
You are nearing the limit for {name}
|
||||||
|
</ExpandableBox>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<ExpandableBox>
|
||||||
|
You have reached the limit for {name}
|
||||||
|
</ExpandableBox>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={typeof onClose === 'function'}
|
||||||
|
show={
|
||||||
|
<Tooltip title='Close' arrow describeChild>
|
||||||
|
<IconButton onClick={onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Header>
|
||||||
|
<Main>
|
||||||
|
<LimitStats>
|
||||||
|
You have added {currentValue} {shortName ?? name}, which is
|
||||||
|
equivalent to{' '}
|
||||||
|
<Typography component='span' color='primary'>
|
||||||
|
{percentageLimit}%
|
||||||
|
</Typography>{' '}
|
||||||
|
of the limit.
|
||||||
|
</LimitStats>
|
||||||
|
<BorderLinearProgress
|
||||||
|
variant='determinate'
|
||||||
|
value={Math.min(100, percentageLimit)}
|
||||||
|
/>
|
||||||
|
<LimitExplanation>
|
||||||
|
<Link
|
||||||
|
target='_blank'
|
||||||
|
to={'https://docs.getunleash.io/reference/limits'}
|
||||||
|
>
|
||||||
|
Read more about limits
|
||||||
|
</Link>
|
||||||
|
<Typography fontWeight='bold'>Limit: {limit}</Typography>
|
||||||
|
</LimitExplanation>
|
||||||
|
</Main>
|
||||||
|
<Footer>
|
||||||
|
If you need more than <strong>{limit}</strong>{' '}
|
||||||
|
{shortName ?? name}, please reach out to us at{' '}
|
||||||
|
<a href='mailto:cs@getunleash.io?subject=Increase limit'>
|
||||||
|
cs@getunleash.io
|
||||||
|
</a>
|
||||||
|
</Footer>
|
||||||
|
</StyledBox>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user