mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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