Skip to content

Commit

Permalink
Feat: My Accounts page (#3233)
Browse files Browse the repository at this point in the history
  • Loading branch information
katspaugh authored Feb 13, 2024
1 parent 6cd73cb commit 06a35f5
Show file tree
Hide file tree
Showing 23 changed files with 387 additions and 88 deletions.
7 changes: 6 additions & 1 deletion cypress/e2e/pages/create_wallet.pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const ownerAddress = 'input[name^="owners"][name$="address"]'
const thresholdInput = 'input[name="threshold"]'
export const removeOwnerBtn = 'button[aria-label="Remove owner"]'
const connectingContainer = 'div[class*="connecting-container"]'
const createNewSafeBtn = 'span[data-track="create-safe: Continue to creation"]'
const createNewSafeBtn = '[data-testid="create-safe-btn"]'
const continueWithWalletBtn = 'span[data-track="create-safe: Continue to my accounts"]'
const connectWalletBtn = 'Connect wallet'
const googleConnectBtn = '[data-testid="google-connect-btn"]'
const googleSignedinBtn = '[data-testid="signed-in-account-btn"]'
Expand Down Expand Up @@ -110,6 +111,10 @@ export function clickOnCreateNewSafeBtn() {
cy.get(createNewSafeBtn).click().wait(1000)
}

export function clickOnContinueWithWalletBtn() {
cy.get(continueWithWalletBtn).click().wait(1000)
}

export function clickOnConnectWalletBtn() {
cy.get(welcomeLoginScreen).within(() => {
cy.get('button').contains(connectWalletBtn).should('be.visible').should('be.enabled').click().wait(1000)
Expand Down
9 changes: 9 additions & 0 deletions cypress/e2e/regression/create_safe_simple.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('Safe creation tests', () => {
it('Verify Next button is disabled until switching to network is done', () => {
owner.waitForConnectionStatus()
createwallet.selectNetwork(constants.networks.ethereum)
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.checkNetworkChangeWarningMsg()
createwallet.verifyNextBtnIsDisabled()
Expand All @@ -24,6 +25,7 @@ describe('Safe creation tests', () => {
// TODO: Check unit tests
it('Verify error message is displayed if wallet name input exceeds 50 characters', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.typeWalletName(main.generateRandomString(51))
owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars)
Expand All @@ -34,13 +36,15 @@ describe('Safe creation tests', () => {
// TODO: Check unit tests
it('Verify there is no error message is displayed if wallet name input contains less than 50 characters', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.typeWalletName(main.generateRandomString(50))
owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars)
})

it('Verify current connected account is shown as default owner', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
owner.verifyExistingOwnerAddress(0, constants.DEFAULT_OWNER_ADDRESS)
Expand All @@ -49,6 +53,7 @@ describe('Safe creation tests', () => {
// TODO: Check unit tests
it('Verify error message is displayed if owner name input exceeds 50 characters', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
owner.typeExistingOwnerName(main.generateRandomString(51))
owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars)
Expand All @@ -57,6 +62,7 @@ describe('Safe creation tests', () => {
// TODO: Check unit tests
it('Verify there is no error message is displayed if owner name input contains less than 50 characters', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
owner.typeExistingOwnerName(main.generateRandomString(50))
owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars)
Expand All @@ -65,6 +71,7 @@ describe('Safe creation tests', () => {
it('Verify data persistence', () => {
const ownerName = 'David'
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
createwallet.clickOnAddNewOwnerBtn()
Expand Down Expand Up @@ -97,6 +104,7 @@ describe('Safe creation tests', () => {

it('Verify tip is displayed on right side for threshold 1/1', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
createwallet.verifyPolicy1_1()
Expand All @@ -105,6 +113,7 @@ describe('Safe creation tests', () => {
// TODO: Check unit tests
it('Verify address input validation rules', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
createwallet.clickOnAddNewOwnerBtn()
Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/safe-apps/tx-builder.spec.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('Transaction Builder tests', { defaultCommandTimeout: 20000 }, () => {
})
})

it('Verify a batch cannot be created without asset amount', () => {
it.skip('Verify a batch cannot be created without asset amount', () => {
cy.enter(iframeSelector).then((getBody) => {
getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SEPOLIA_TEST_SAFE_10)
getBody().findByText(safeapps.addTransactionStr).click()
Expand Down Expand Up @@ -266,7 +266,7 @@ describe('Transaction Builder tests', { defaultCommandTimeout: 20000 }, () => {
})
})

it('Verify a valid batch as successful can be simulated', () => {
it.skip('Verify a valid batch as successful can be simulated', () => {
cy.enter(iframeSelector).then((getBody) => {
getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SEPOLIA_TEST_SAFE_10)
getBody().findByLabelText(safeapps.tokenAmount).type('0')
Expand All @@ -278,7 +278,7 @@ describe('Transaction Builder tests', { defaultCommandTimeout: 20000 }, () => {
})
})

it('Verify an invalid batch as failed can be simulated', () => {
it.skip('Verify an invalid batch as failed can be simulated', () => {
cy.enter(iframeSelector).then((getBody) => {
getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SEPOLIA_TEST_SAFE_10)
getBody().findByLabelText(safeapps.tokenAmount).type('100')
Expand Down
4 changes: 4 additions & 0 deletions cypress/e2e/smoke/create_safe_simple.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('[SMOKE] Safe creation tests', () => {
main.acceptCookies()
})
it('[SMOKE] Verify a Wallet can be connected', () => {
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
owner.clickOnWalletExpandMoreIcon()
owner.clickOnDisconnectBtn()
Expand All @@ -20,12 +21,14 @@ describe('[SMOKE] Safe creation tests', () => {

it('[SMOKE] Verify that a new Wallet has default name related to the selected network', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.verifyDefaultWalletName(createwallet.defaltSepoliaPlaceholder)
})

it('[SMOKE] Verify Add and Remove Owner Row works as expected', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
createwallet.clickOnAddNewOwnerBtn()
Expand All @@ -40,6 +43,7 @@ describe('[SMOKE] Safe creation tests', () => {

it('[SMOKE] Verify Threshold Setup', () => {
owner.waitForConnectionStatus()
createwallet.clickOnContinueWithWalletBtn()
createwallet.clickOnCreateNewSafeBtn()
createwallet.clickOnNextBtn()
createwallet.clickOnAddNewOwnerBtn()
Expand Down
11 changes: 9 additions & 2 deletions src/components/common/ChainIndicator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type ChainIndicatorProps = {
className?: string
showUnknown?: boolean
showLogo?: boolean
responsive?: boolean
}

const fallbackChainConfig = {
Expand All @@ -32,6 +33,7 @@ const ChainIndicator = ({
inline = false,
showUnknown = true,
showLogo = true,
responsive = false,
}: ChainIndicatorProps): ReactElement | null => {
const currentChainId = useChainId()
const id = chainId || currentChainId
Expand All @@ -56,7 +58,12 @@ const ChainIndicator = ({
<span
data-testid="chain-logo"
style={showLogo ? undefined : style}
className={classnames(inline ? css.inlineIndicator : css.indicator, showLogo ? css.withLogo : '', className)}
className={classnames(className || '', {
[css.inlineIndicator]: inline,
[css.indicator]: !inline,
[css.withLogo]: showLogo,
[css.responsive]: responsive,
})}
>
{showLogo && (
<img
Expand All @@ -68,7 +75,7 @@ const ChainIndicator = ({
/>
)}

{chainConfig.chainName}
<span className={css.name}>{chainConfig.chainName}</span>
</span>
) : null
}
Expand Down
13 changes: 12 additions & 1 deletion src/components/common/ChainIndicator/styles.module.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.indicator {
display: flex;
align-items: center;
justify-content: center;
min-width: 70px;
font-size: 12px;
justify-content: center;
}

.inlineIndicator {
Expand All @@ -21,5 +21,16 @@
align-items: center;
gap: var(--space-1);
padding: 0;
min-width: 115px;
font-size: 14px;
justify-content: start;
}

@media (max-width: 899.95px) {
.responsive .name {
display: none;
}
.indicator {
min-width: 35px;
}
}
2 changes: 1 addition & 1 deletion src/components/common/EthHashInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const EthHashInfo = ({
const currentChainId = useChainId()
const chain = useAppSelector((state) => selectChainById(state, props.chainId || currentChainId))
const addressBook = useAddressBook()
const link = chain ? getBlockExplorerLink(chain, props.address) : undefined
const link = chain && props.hasExplorer ? getBlockExplorerLink(chain, props.address) : undefined
const name = showName ? addressBook[props.address] || props.name : undefined

return (
Expand Down
6 changes: 2 additions & 4 deletions src/components/common/NetworkSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import useWallet from '@/hooks/wallets/useWallet'
import { isSocialWalletEnabled } from '@/hooks/wallets/wallets'
import { isSocialLoginWallet } from '@/services/mpc/SocialLoginModule'

const keepPathRoutes = [AppRoutes.welcome.index, AppRoutes.newSafe.create, AppRoutes.newSafe.load]

const MenuWithTooltip = forwardRef<HTMLUListElement>(function MenuWithTooltip(props: any, ref) {
return (
<Tooltip title="More network support coming soon" arrow placement="left">
Expand All @@ -43,10 +41,10 @@ const NetworkSelector = (props: { onChainSelect?: () => void }): ReactElement =>

const getNetworkLink = useCallback(
(shortName: string) => {
const shouldKeepPath = keepPathRoutes.includes(router.pathname)
const shouldKeepPath = !router.query.safe

const route = {
pathname: shouldKeepPath ? router.pathname : '/',
pathname: shouldKeepPath ? router.pathname : AppRoutes.index,
query: {
chain: shortName,
} as {
Expand Down
66 changes: 66 additions & 0 deletions src/components/welcome/MyAccounts/AccountItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useMemo } from 'react'
import { ListItemButton, Box, Typography } from '@mui/material'
import Link from 'next/link'
import SafeIcon from '@/components/common/SafeIcon'
import Track from '@/components/common/Track'
import { OPEN_SAFE_LABELS, OVERVIEW_EVENTS } from '@/services/analytics'
import { AppRoutes } from '@/config/routes'
import { useAppSelector } from '@/store'
import { selectChainById } from '@/store/chainsSlice'
import ChainIndicator from '@/components/common/ChainIndicator'
import css from './styles.module.css'
import { selectAllAddressBooks } from '@/store/addressBookSlice'
import { shortenAddress } from '@/utils/formatters'
import SafeListContextMenu from '@/components/sidebar/SafeListContextMenu'

type AccountItemProps = {
chainId: string
address: string
threshold?: number
owners?: number
}

const getSafeHref = (prefix: string, address: string) => ({
pathname: AppRoutes.home,
query: { safe: `${prefix}:${address}` },
})

const AccountItem = ({ chainId, address, ...rest }: AccountItemProps) => {
const chain = useAppSelector((state) => selectChainById(state, chainId))

const href = useMemo(() => {
return chain ? getSafeHref(chain.shortName, address) : ''
}, [chain, address])

const name = useAppSelector(selectAllAddressBooks)[chainId]?.[address]

return (
<ListItemButton className={css.listItem}>
<Track {...OVERVIEW_EVENTS.OPEN_SAFE} label={OPEN_SAFE_LABELS.login_page}>
<Link href={href} className={css.safeLink}>
<SafeIcon address={address} {...rest} />

<Typography variant="body2" component="div" className={css.safeAddress}>
{name && (
<Typography fontWeight="bold" fontSize="inherit">
{name}
</Typography>
)}
<b>{chain?.shortName}: </b>
<Typography color="text.secondary" fontSize="inherit" component="span">
{shortenAddress(address)}
</Typography>
</Typography>

<Box flex={1} />

<ChainIndicator chainId={chainId} responsive />
</Link>
</Track>

<SafeListContextMenu name={name} address={address} chainId={chainId} />
</ListItemButton>
)
}

export default AccountItem
28 changes: 28 additions & 0 deletions src/components/welcome/MyAccounts/CreateButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Button } from '@mui/material'
import Link from 'next/link'
import { AppRoutes } from '@/config/routes'
import { OVERVIEW_EVENTS, trackEvent } from '@/services/analytics'

const buttonSx = { width: ['100%', 'auto'] }

const onClick = () => {
trackEvent(OVERVIEW_EVENTS.CREATE_NEW_SAFE)
}

const CreateButton = () => (
<Link href={AppRoutes.newSafe.create} passHref legacyBehavior>
<Button
data-testid="create-safe-btn"
disableElevation
size="small"
variant="contained"
sx={buttonSx}
component="a"
onClick={onClick}
>
Create account
</Button>
</Link>
)

export default CreateButton
Loading

0 comments on commit 06a35f5

Please sign in to comment.