From c93bfafb7f210adf249fc454f1da7da1558fb893 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 4 Jul 2024 09:49:31 +0200 Subject: [PATCH] feat: limit component (#7538) --- .../src/component/common/Limit/Limit.test.tsx | 67 ++++++++ frontend/src/component/common/Limit/Limit.tsx | 144 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 frontend/src/component/common/Limit/Limit.test.tsx create mode 100644 frontend/src/component/common/Limit/Limit.tsx diff --git a/frontend/src/component/common/Limit/Limit.test.tsx b/frontend/src/component/common/Limit/Limit.test.tsx new file mode 100644 index 000000000000..0c389606be33 --- /dev/null +++ b/frontend/src/component/common/Limit/Limit.test.tsx @@ -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( + , + ); + + 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( + , + ); + + 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( + , + ); + + 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( + , + ); + + expect(screen.queryByText('Limit: 10')).not.toBeInTheDocument(); +}); diff --git a/frontend/src/component/common/Limit/Limit.tsx b/frontend/src/component/common/Limit/Limit.tsx new file mode 100644 index 000000000000..7885089ad20e --- /dev/null +++ b/frontend/src/component/common/Limit/Limit.tsx @@ -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 ( + +
+ } + elseShow={} + /> + + + You are nearing the limit for {name} + + } + elseShow={ + + You have reached the limit for {name} + + } + /> + + + + + + + } + /> +
+
+ + You have added {currentValue} {shortName ?? name}, which is + equivalent to{' '} + + {percentageLimit}% + {' '} + of the limit. + + + + + Read more about limits + + Limit: {limit} + +
+ +
+ ); +};