Skip to content

Commit

Permalink
feat: limit component (#7538)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew authored Jul 4, 2024
1 parent 30073d5 commit c93bfaf
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
67 changes: 67 additions & 0 deletions frontend/src/component/common/Limit/Limit.test.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 144 additions & 0 deletions frontend/src/component/common/Limit/Limit.tsx
Original file line number Diff line number Diff line change
@@ -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:[email protected]?subject=Increase limit'>
[email protected]
</a>
</Footer>
</StyledBox>
);
};

0 comments on commit c93bfaf

Please sign in to comment.