Skip to content

Commit

Permalink
Feature/loading design (#99)
Browse files Browse the repository at this point in the history
* wip loading on pools

* wip

* vault row loading

* lint

* move available collateral

* available collateral

* available collateral

* add test

* pool card test

* available collateral test

* move pool to snx/v3

* add exception test formatTimeToUnlock

* fix deps

* router-dom deps

* mismatched fix

* more deps

* update home

* jade feedback

* revert available row

---------

Co-authored-by: jmzwar <james@jmzwar.com>
  • Loading branch information
jmzwar and jmzwar authored Nov 14, 2023
1 parent 260b697 commit 4dae4f3
Show file tree
Hide file tree
Showing 26 changed files with 872 additions and 512 deletions.
2 changes: 1 addition & 1 deletion liquidity/components/BorderBox/BorderBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Flex, FlexProps } from '@chakra-ui/react';

export const BorderBox = (props: FlexProps) => (
<Flex bg="navy.900" borderWidth="1px" borderColor="gray.900" borderRadius="base" {...props} />
<Flex bg="navy.700" borderWidth="1px" borderColor="gray.900" borderRadius="base" {...props} />
);
63 changes: 63 additions & 0 deletions liquidity/components/Pools/AvailableCollateral.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { AvailableCollateralUi } from './AvailableCollateral';
import { wei } from '@synthetixio/wei';
import { AvailableCollateralRow } from './AvailableCollateralRow';

describe('AvailableCollateral Component', () => {
const accountCollaterals = [
{
tokenAddress: 'snxAddress',
symbol: 'SNX',
availableCollateral: wei(100),
totalAssigned: wei(100),
totalDeposited: wei(50),
totalLocked: wei(40),
},
];

it('renders loading state initially', () => {
cy.mount(
<AvailableCollateralUi
isLoading={true}
accountCollaterals={[]}
AvailableCollateralRow={AvailableCollateralRow}
/>
);
cy.get('h2').should('contain', 'Loading Collateral...');
});

it('renders available collateral when not in loading state', () => {
cy.mount(
<AvailableCollateralUi
isLoading={false}
accountCollaterals={accountCollaterals}
AvailableCollateralRow={AvailableCollateralRow}
/>
);

cy.get('h2').should('contain', 'Available Collateral');
cy.get('[data-testid="available-collateral"]').should('exist').should('contain', '100 SNX');
});

it('displays withdrawal information when not in loading state', () => {
const currentDate = new Date(); // Replace with a valid unlock date
const unlockDate = new Date(currentDate);
unlockDate.setDate(currentDate.getDate() + 3);

cy.mount(
<AvailableCollateralUi
isLoading={false}
accountCollaterals={accountCollaterals}
unlockDate={unlockDate}
unlockDateString="3 days"
timeToUnlock="in about 3 days"
AvailableCollateralRow={AvailableCollateralRow}
/>
);

// Check that the unlock date is displayed
cy.get('[data-testid="time-to-unlock"]').should('exist').should('contain', 'in about 3 days');
// Ensure button is disabled pre lock date
cy.get('[data-testid="withdraw-button"]').should('be.disabled');
});
});
150 changes: 150 additions & 0 deletions liquidity/components/Pools/AvailableCollateral.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { useEffect, useState } from 'react';
import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Box,
Button,
Fade,
Flex,
Heading,
Skeleton,
SkeletonCircle,
Table,
Tbody,
Td,
Text,
Thead,
Tr,
} from '@chakra-ui/react';
import { useParams } from '@snx-v3/useParams';
import { AccountCollateralWithSymbol, useAccountCollateral } from '@snx-v3/useAccountCollateral';
import { useAccountCollateralUnlockDate } from '@snx-v3/useAccountCollateralUnlockDate';
import { AvailableCollateralRow } from './';
import { BorderBox } from '@snx-v3/BorderBox';
import { CollateralIcon } from '@snx-v3/icons';
import { formatTimeToUnlock, unlockDateString } from '@snx-v3/formatters';

export function AvailableCollateralUi({
accountCollaterals,
timeToUnlock,
unlockDateString,
unlockDate,
isLoading,
}: {
accountCollaterals: AccountCollateralWithSymbol[];
timeToUnlock?: string;
unlockDateString?: string;
unlockDate?: Date;
isLoading: boolean;
AvailableCollateralRow: typeof AvailableCollateralRow;
}) {
return (
<BorderBox p={4} mt={8} flexDir="column">
<Heading fontSize="2xl" mb="2">
{isLoading ? 'Loading Collateral...' : 'Available Collateral'}
</Heading>
{!isLoading && (
<Fade in>
<Flex alignItems="center" mb="0">
<Text color="gray.500">
This collateral can be deposited to pools. As a security precaution, this collateral
cannot be withdrawn until at least 1 day has elapsed since previous account activity.
</Text>
<Alert
ml="auto"
status={timeToUnlock === '—' ? 'loading' : timeToUnlock ? 'error' : 'success'}
width="540px"
title={unlockDateString}
>
<AlertIcon />
<Box width="100%">
<AlertTitle>Withdrawals available</AlertTitle>
{timeToUnlock && (
<AlertDescription data-testid="time-to-unlock" display="block">
{timeToUnlock}
</AlertDescription>
)}
</Box>
</Alert>
</Flex>
</Fade>
)}
<Fade in>
<Box overflowX="auto">
<Table mt={8} size="sm" variant="unstyled" mb="9">
<Thead sx={{ tr: { borderBottomColor: 'gray.900', borderBottomWidth: '1px' } }}>
<Tr />
</Thead>
<Tbody sx={{ tr: { borderBottomColor: 'gray.900', borderBottomWidth: '1px' } }}>
{isLoading ? (
// Loading State
<Tr data-testid="available collateral row" alignItems="center">
<Td>
<Flex flexDir="row" py={4}>
<SkeletonCircle isLoaded={!isLoading} height="28px">
<CollateralIcon width="32px" height="32px" symbol="SNX" />
</SkeletonCircle>
</Flex>
</Td>
<Td textAlign="end">
<Flex height="100%" justifyContent="flex-end">
<Skeleton isLoaded={!isLoading} height="28px" width="200px" alignSelf="end">
<Button>Withdraw</Button>
</Skeleton>
</Flex>
</Td>
</Tr>
) : (
<>
{accountCollaterals?.map((accountCollateral) => (
<AvailableCollateralRow
key={accountCollateral.tokenAddress}
accountCollateralUnlockDate={unlockDate}
accountCollateral={accountCollateral}
/>
))}
</>
)}
</Tbody>
</Table>
</Box>
</Fade>
</BorderBox>
);
}

export function AvailableCollateral() {
const { accountId } = useParams();
const { data: accountCollateralsData, isLoading: isAccountCollateralsLoading } =
useAccountCollateral({ accountId, includeDelegationOff: true });

const { data: accountCollateralUnlockDate, isLoading: isAccountCollateralDateLoading } =
useAccountCollateralUnlockDate({
accountId,
});

const [timeToUnlock, setTimeToUnlock] = useState(formatTimeToUnlock(accountCollateralUnlockDate));

useEffect(() => {
const interval = setInterval(
() => setTimeToUnlock(formatTimeToUnlock(accountCollateralUnlockDate)),
1_000
);
return () => clearInterval(interval);
}, [accountCollateralUnlockDate]);

const isLoading = isAccountCollateralsLoading || isAccountCollateralDateLoading;

return (
<AvailableCollateralUi
accountCollaterals={accountCollateralsData || []}
timeToUnlock={timeToUnlock}
unlockDateString={unlockDateString?.toString() || ''}
unlockDate={accountCollateralUnlockDate}
isLoading={isLoading}
AvailableCollateralRow={AvailableCollateralRow}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useState, lazy, Suspense } from 'react';
import { Amount } from '@snx-v3/Amount';
import { Button, Flex, Td, Text, Tr } from '@chakra-ui/react';
import { Button, Fade, Flex, Td, Text, Tr } from '@chakra-ui/react';
import { CollateralIcon } from '@snx-v3/icons';
import { AccountCollateralWithSymbol } from '@snx-v3/useAccountCollateral';
import React from 'react';
import { safeImport } from '@synthetixio/safe-import';

const WithdrawModal = React.lazy(() => safeImport(() => import('@snx-v3/WithdrawModal')));
const WithdrawModal = lazy(() => safeImport(() => import('@snx-v3/WithdrawModal')));

function AvailableCollateralRowUi({
accountCollateral,
Expand All @@ -14,33 +14,43 @@ function AvailableCollateralRowUi({
accountCollateral: AccountCollateralWithSymbol;
isDisabled: boolean;
}) {
const [isOpen, setIsOpen] = React.useState(false);
const [isOpen, setIsOpen] = useState(false);

return (
<Tr data-testid="available collateral row">
<Td>
<Flex flexDir="row" py={4}>
<CollateralIcon width="32px" height="32px" symbol={accountCollateral.symbol} />
<Fade in>
<CollateralIcon width="32px" height="32px" symbol={accountCollateral.symbol} />
</Fade>
<Flex flexDirection="column" justifyContent="center" ml={2}>
<Text fontSize="lg" color="gray.500">
<Amount value={accountCollateral.availableCollateral} /> {accountCollateral.symbol}
</Text>
<Fade in>
<Text fontSize="lg" color="gray.500" data-testid="available-collateral">
<Amount value={accountCollateral.availableCollateral} /> {accountCollateral.symbol}
</Text>
</Fade>
</Flex>
</Flex>
</Td>
<Td textAlign="end">
<Button isDisabled={isDisabled} onClick={() => setIsOpen(true)}>
Withdraw
</Button>
<React.Suspense fallback={null}>
<Fade in>
<Button
data-testid="withdraw-button"
isDisabled={isDisabled}
onClick={() => setIsOpen(true)}
>
Withdraw
</Button>
</Fade>
<Suspense fallback={null}>
{isOpen ? (
<WithdrawModal
accountCollateral={accountCollateral}
onClose={() => setIsOpen(false)}
isOpen={isOpen}
/>
) : null}
</React.Suspense>
</Suspense>
</Td>
</Tr>
);
Expand All @@ -50,6 +60,7 @@ export type AvailableCollateralRowProps = {
accountCollateralUnlockDate?: Date;
accountCollateral: AccountCollateralWithSymbol;
};

export function AvailableCollateralRow({
accountCollateral,
accountCollateralUnlockDate,
Expand Down
74 changes: 74 additions & 0 deletions liquidity/components/Pools/PoolCard.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import { PoolCard } from './PoolCard'; // Import your PoolCard component
import { wei } from '@synthetixio/wei';

describe('PoolCard Component', () => {
const pool = {
name: 'Spartan Council Pool',
id: '1',
isPreferred: true,
};

const collateralTypes = [
{
tokenAddress: 'address1',
symbol: 'SNX',
depositingEnabled: true,
issuanceRatioD18: wei(400, 2, true),
liquidationRatioD18: wei(150, 2, true),
liquidationRewardD18: wei(5, 2, true),
oracleNodeId: '1',
minDelegationD18: wei(1, 2, true),
displaySymbol: 'SNX',
},
];

it('renders pool name and "Pool Info" button', () => {
cy.mount(<PoolCard pool={pool} />);

cy.get('h2').should('contain', 'Spartan Council Pool');
cy.get('a').should('contain', 'Pool Info');
});

it('renders a table with collateral information', () => {
cy.mount(<PoolCard pool={pool} collateralTypes={collateralTypes} />);

cy.get('table').should('exist');

cy.get('[data-testid="liquidation-ratio"]').should('exist').should('contain', '150%');
cy.get('[data-testid="issuance-ratio"]').should('exist').should('contain', '400%');
});

it('renders a table with collateral and liquidity information', () => {
const liquidityPositionsById = {
'1-SNX': {
id: `1-SNX` as `${string}-${string}`,
poolId: '1',
collateralType: collateralTypes[0],
accountId: '12344',
collateralAmount: wei(100),
collateralValue: wei(100),
debt: wei(100),
poolName: 'Spartan Council Pool',
cRatio: wei(300),
collateralPrice: wei(1),
},
};

cy.mount(
<PoolCard
pool={pool}
collateralTypes={collateralTypes}
liquidityPositionsById={liquidityPositionsById}
/>
);

cy.get('table').should('exist');
cy.get('[data-testid="liquidation-ratio"]').should('exist').should('contain', '150%');
cy.get('[data-testid="issuance-ratio"]').should('exist').should('contain', '400%');
cy.get('[data-testid="collateral-amount"]').should('exist').should('contain', '100 SNX');
cy.get('[data-testid="collateral-value"]').should('exist').should('contain', '$100');
cy.get('[data-testid="debt"]').should('exist').should('contain', '$100');
cy.get('[data-testid="c-ratio"]').should('exist').should('contain', '100%');
});
});
Loading

0 comments on commit 4dae4f3

Please sign in to comment.