Skip to content

Commit

Permalink
Case: One button in a title (private/public tag)
Browse files Browse the repository at this point in the history
Fixes #1582
  • Loading branch information
tom2drum committed Feb 9, 2024
1 parent 86a888e commit 7ca5354
Show file tree
Hide file tree
Showing 15 changed files with 307 additions and 69 deletions.
113 changes: 113 additions & 0 deletions ui/shared/AccountActionsMenu/AccountActionsMenu.pw.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';

import TestApp from 'playwright/TestApp';

import AccountActionsMenu from './AccountActionsMenu';

test.use({ viewport: { width: 200, height: 200 } });

test.describe('with multiple items', async() => {
const hooksConfig = {
router: {
query: { hash: '<hash>' },
pathname: '/token/[hash]',
isReady: true,
},
};

test('base view', async({ mount, page }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu/>
</TestApp>,
{ hooksConfig },
);

await component.getByRole('button').click();

await expect(page).toHaveScreenshot();
});

test('base view with styles', async({ mount, page }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);

await component.getByRole('button').click();

await expect(page).toHaveScreenshot();
});

test('loading', async({ mount }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu isLoading/>
</TestApp>,
{ hooksConfig },
);

await expect(component).toHaveScreenshot();
});

test('loading with styles', async({ mount }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu isLoading m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);

await expect(component).toHaveScreenshot();
});
});

test.describe('with one item', async() => {
const hooksConfig = {
router: {
query: { hash: '<hash>' },
pathname: '/tx/[hash]',
isReady: true,
},
};

test('base view', async({ mount, page }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu/>
</TestApp>,
{ hooksConfig },
);

await component.getByRole('button').hover();

await expect(page).toHaveScreenshot();
});

test('base view with styles', async({ mount, page }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);

await component.getByRole('button').hover();

await expect(page).toHaveScreenshot();
});

test('loading', async({ mount }) => {
const component = await mount(
<TestApp>
<AccountActionsMenu isLoading/>
</TestApp>,
{ hooksConfig },
);

await expect(component).toHaveScreenshot();
});
});
71 changes: 49 additions & 22 deletions ui/shared/AccountActionsMenu/AccountActionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { IconButton, Menu, MenuButton, MenuList, Skeleton, chakra } from '@chakra-ui/react';
import { Box, IconButton, Menu, MenuButton, MenuList, Skeleton, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';

import type { ItemProps } from './types';

import config from 'configs/app';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import * as mixpanel from 'lib/mixpanel/index';
Expand Down Expand Up @@ -33,30 +35,55 @@ const AccountActionsMenu = ({ isLoading, className }: Props) => {
return null;
}

const items = [
{
render: (props: ItemProps) => <TokenInfoMenuItem { ...props }/>,
enabled: isTokenPage && config.features.addressVerification.isEnabled,
},
{
render: (props: ItemProps) => <PrivateTagMenuItem { ...props } entityType={ isTxPage ? 'tx' : 'address' }/>,
enabled: true,
},
{
render: (props: ItemProps) => <PublicTagMenuItem { ...props }/>,
enabled: !isTxPage,
},
].filter(({ enabled }) => enabled);

if (items.length === 0) {
return null;
}

if (isLoading) {
return <Skeleton w="36px" h="32px" borderRadius="base" className={ className }/>;
}

if (items.length === 1) {
return (
<Box className={ className }>
{ items[0].render({ type: 'button', hash, onBeforeClick: isAccountActionAllowed }) }
</Box>
);
}

return (
<Menu>
<Skeleton isLoaded={ !isLoading } borderRadius="base" className={ className }>
<MenuButton
as={ IconButton }
size="sm"
variant="outline"
colorScheme="gray"
px="7px"
onClick={ handleButtonClick }
icon={ <IconSvg name="dots" boxSize="18px"/> }
/>
</Skeleton>
<MenuButton
as={ IconButton }
className={ className }
size="sm"
variant="outline"
colorScheme="gray"
px="7px"
onClick={ handleButtonClick }
icon={ <IconSvg name="dots" boxSize="18px"/> }
/>
<MenuList minWidth="180px" zIndex="popover">
{ isTokenPage && config.features.addressVerification.isEnabled &&
<TokenInfoMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> }
<PrivateTagMenuItem
py={ 2 }
px={ 4 }
hash={ hash }
onBeforeClick={ isAccountActionAllowed }
type={ isTxPage ? 'tx' : 'address' }
/>
{ !isTxPage && <PublicTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> }
{ items.map(({ render }, index) => (
<React.Fragment key={ index }>
{ render({ type: 'menu_item', hash, onBeforeClick: isAccountActionAllowed }) }
</React.Fragment>
)) }
</MenuList>
</Menu>
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 28 additions & 10 deletions ui/shared/AccountActionsMenu/items/PrivateTagMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { MenuItem, chakra, useDisclosure } from '@chakra-ui/react';
import { useDisclosure } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';

import type { ItemType } from '../types';
import type { Address } from 'types/api/address';
import type { Transaction } from 'types/api/transaction';

Expand All @@ -12,19 +13,23 @@ import AddressModal from 'ui/privateTags/AddressModal/AddressModal';
import TransactionModal from 'ui/privateTags/TransactionModal/TransactionModal';
import IconSvg from 'ui/shared/IconSvg';

import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';

interface Props {
className?: string;
hash: string;
onBeforeClick: () => boolean;
type?: 'address' | 'tx';
entityType?: 'address' | 'tx';
type: ItemType;
}

const PrivateTagMenuItem = ({ className, hash, onBeforeClick, type = 'address' }: Props) => {
const PrivateTagMenuItem = ({ className, hash, onBeforeClick, entityType = 'address', type }: Props) => {
const modal = useDisclosure();
const queryClient = useQueryClient();
const router = useRouter();

const queryKey = getResourceKey(type === 'tx' ? 'tx' : 'address', { pathParams: { hash } });
const queryKey = getResourceKey(entityType === 'tx' ? 'tx' : 'address', { pathParams: { hash } });
const queryData = queryClient.getQueryData<Address | Transaction>(queryKey);

const handleClick = React.useCallback(() => {
Expand Down Expand Up @@ -58,18 +63,31 @@ const PrivateTagMenuItem = ({ className, hash, onBeforeClick, type = 'address' }
pageType,
};

const element = (() => {
switch (type) {
case 'button': {
return <ButtonItem label="Add private tag" icon="privattags" onClick={ handleClick } className={ className }/>;
}
case 'menu_item': {
return (
<MenuItem className={ className } onClick={ handleClick }>
<IconSvg name="privattags" boxSize={ 6 } mr={ 2 }/>
<span>Add private tag</span>
</MenuItem>
);
}
}
})();

return (
<>
<MenuItem className={ className } onClick={ handleClick }>
<IconSvg name="privattags" boxSize={ 6 } mr={ 2 }/>
<span>Add private tag</span>
</MenuItem>
{ type === 'tx' ?
{ element }
{ entityType === 'tx' ?
<TransactionModal { ...modalProps } data={{ transaction_hash: hash }}/> :
<AddressModal { ...modalProps } data={{ address_hash: hash }}/>
}
</>
);
};

export default React.memo(chakra(PrivateTagMenuItem));
export default React.memo(PrivateTagMenuItem);
34 changes: 25 additions & 9 deletions ui/shared/AccountActionsMenu/items/PublicTagMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { MenuItem, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';

import type { ItemType } from '../types';

import IconSvg from 'ui/shared/IconSvg';

import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';

interface Props {
className?: string;
hash: string;
onBeforeClick: () => boolean;
type: ItemType;
}

const PublicTagMenuItem = ({ className, hash, onBeforeClick }: Props) => {
const PublicTagMenuItem = ({ className, hash, onBeforeClick, type }: Props) => {
const router = useRouter();

const handleClick = React.useCallback(() => {
Expand All @@ -21,12 +26,23 @@ const PublicTagMenuItem = ({ className, hash, onBeforeClick }: Props) => {
router.push({ pathname: '/account/public-tags-request', query: { address: hash } });
}, [ hash, onBeforeClick, router ]);

return (
<MenuItem className={ className }onClick={ handleClick }>
<IconSvg name="publictags" boxSize={ 6 } mr={ 2 }/>
<span>Add public tag</span>
</MenuItem>
);
const element = (() => {
switch (type) {
case 'button': {
return <ButtonItem label="Add public tag" icon="publictags" onClick={ handleClick } className={ className }/>;
}
case 'menu_item': {
return (
<MenuItem className={ className } onClick={ handleClick }>
<IconSvg name="publictags" boxSize={ 6 } mr={ 2 }/>
<span>Add private tag</span>
</MenuItem>
);
}
}
})();

return element;
};

export default React.memo(chakra(PublicTagMenuItem));
export default React.memo(PublicTagMenuItem);
Loading

0 comments on commit 7ca5354

Please sign in to comment.