Commit d611f2e2 authored by tom's avatar tom

ENS entity component

parent d58af66d
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.145 8.091c.198.423.689 1.258.689 1.258L9.508 0 3.97 3.854c-.33.22-.6.517-.788.865a3.989 3.989 0 0 0-.037 3.372ZM.897 11.17a6.277 6.277 0 0 0 2.478 4.573L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897c-.084.156-.247.475-.247.475A7.234 7.234 0 0 0 .877 8.84c-.064.776-.057 1.556.02 2.33ZM16.523 11.909c-.198-.423-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375ZM18.772 8.83a6.279 6.279 0 0 0-2.48-4.573L10.168 0s3.83 5.505 7.065 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475.328-.666.551-1.378.662-2.112.065-.776.059-1.555-.017-2.33Z" fill="currentColor"/>
<path d="M3.182 4.719c.188-.348.458-.645.788-.865L9.508 0 3.834 9.351s-.496-.835-.69-1.257a3.989 3.989 0 0 1 .038-3.375Zm-2.285 6.45a6.278 6.278 0 0 0 2.478 4.574L9.501 20S5.668 14.495 2.436 9.018a5.52 5.52 0 0 1-.65-1.868 2.98 2.98 0 0 1 0-.897 34.31 34.31 0 0 0-.247.475A7.234 7.234 0 0 0 .877 8.84c-.064.776-.057 1.555.02 2.33Zm15.616.742c-.198-.422-.689-1.258-.689-1.258L10.16 20l5.538-3.852c.33-.22.6-.516.788-.864a3.99 3.99 0 0 0 .037-3.375l-.01.002Zm2.249-3.078a6.277 6.277 0 0 0-2.48-4.574L10.169 0s3.83 5.505 7.064 10.982a5.52 5.52 0 0 1 .647 1.868c.045.297.045.6 0 .897.084-.156.248-.475.248-.475a7.233 7.233 0 0 0 .662-2.112c.064-.776.059-1.555-.018-2.33l-.01.003Z" fill="currentColor" style="mix-blend-mode:color"/>
</svg>
......@@ -70,6 +70,7 @@ export const token: Address = {
has_token_transfers: true,
has_tokens: true,
has_validated_blocks: false,
ens_domain_name: null,
};
export const contract: Address = {
......@@ -100,6 +101,7 @@ export const contract: Address = {
token: null,
watchlist_names: [ watchlistName ],
watchlist_address_id: 42,
ens_domain_name: null,
};
export const validator: Address = {
......@@ -130,4 +132,5 @@ export const validator: Address = {
token: null,
watchlist_names: [],
watchlist_address_id: null,
ens_domain_name: null,
};
......@@ -41,6 +41,7 @@ export const ADDRESS_INFO: Address = {
public_tags: [],
watchlist_names: [],
watchlist_address_id: null,
ens_domain_name: null,
};
export const ADDRESS_COUNTERS: AddressCounters = {
......
......@@ -12,6 +12,7 @@ export interface Address extends UserTags {
creator_address_hash: string | null;
creation_tx_hash: string | null;
exchange_rate: string | null;
ens_domain_name: string | null;
// TODO: if we are happy with tabs-counters method, should we delete has_something fields?
has_beacon_chain_withdrawals?: boolean;
has_custom_methods_read: boolean;
......
......@@ -29,6 +29,7 @@ import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'
import TextAd from 'ui/shared/ad/TextAd';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -172,14 +173,26 @@ const AddressPageContent = () => {
const titleSecondRow = (
<Flex alignItems="center" w="100%" columnGap={ 2 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
{ addressQuery.data?.ens_domain_name && (
<EnsEntity
name={ addressQuery.data?.ens_domain_name }
noLink
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
mr={ 1 }
maxW="300px"
/>
) }
<AddressEntity
address={{ ...addressQuery.data, hash, name: '' }}
address={{ ...addressQuery.data, hash, name: '', ens_domain_name: '' }}
isLoading={ isLoading }
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
noLink
isSafeAddress={ isSafeAddress }
mr={ 4 }
/>
{ !isLoading && addressQuery.data?.is_contract && addressQuery.data.token &&
<AddressAddToWallet token={ addressQuery.data.token } variant="button"/> }
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import EnsEntity from './EnsEntity';
const name = 'cat.eth';
const iconSizes = [ 'md', 'lg' ];
test.use({ viewport: { width: 180, height: 30 } });
test.describe('icon size', () => {
iconSizes.forEach((size) => {
test(size, async({ mount }) => {
const component = await mount(
<TestApp>
<EnsEntity
name={ name }
iconSize={ size }
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
});
});
test('loading', async({ mount }) => {
const component = await mount(
<TestApp>
<EnsEntity
name={ name }
isLoading
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with long name', async({ mount }) => {
const component = await mount(
<TestApp>
<EnsEntity
name="kitty.kitty.kitty.cat.eth"
/>
</TestApp>,
);
await component.getByText(name.slice(0, 4)).hover();
await expect(component).toHaveScreenshot();
});
test('customization', async({ mount }) => {
const component = await mount(
<TestApp>
<EnsEntity
name={ name }
p={ 3 }
borderWidth="1px"
borderColor="blue.700"
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import type { As } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import _omit from 'lodash/omit';
import React from 'react';
import { route } from 'nextjs-routes';
import ensIcon from 'icons/ENS.svg';
import * as EntityBase from 'ui/shared/entities/base/components';
import TruncatedValue from 'ui/shared/TruncatedValue';
type LinkProps = EntityBase.LinkBaseProps & Pick<EntityProps, 'name'>;
const Link = chakra((props: LinkProps) => {
// TODO @tom2drum change link href
const defaultHref = route({ pathname: '/tx/[hash]', query: { hash: props.name } });
return (
<EntityBase.Link
{ ...props }
href={ props.href ?? defaultHref }
>
{ props.children }
</EntityBase.Link>
);
});
type IconProps = Omit<EntityBase.IconBaseProps, 'asProp'> & {
asProp?: As;
};
const Icon = (props: IconProps) => {
return (
<EntityBase.Icon
{ ...props }
asProp={ props.asProp ?? ensIcon }
/>
);
};
type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<EntityProps, 'name'>;
const Content = chakra((props: ContentProps) => {
return (
<TruncatedValue
isLoading={ props.isLoading }
value={ props.name }
/>
);
});
type CopyProps = Omit<EntityBase.CopyBaseProps, 'text'> & Pick<EntityProps, 'name'>;
const Copy = (props: CopyProps) => {
return (
<EntityBase.Copy
{ ...props }
text={ props.name }
/>
);
};
const Container = EntityBase.Container;
export interface EntityProps extends EntityBase.EntityBaseProps {
name: string;
}
const EnsEntity = (props: EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
return (
<Container className={ props.className }>
<Icon { ...partsProps }/>
<Link { ...linkProps }>
<Content { ...partsProps }/>
</Link>
<Copy { ...partsProps }/>
</Container>
);
};
export default React.memo(chakra(EnsEntity));
export {
Container,
Link,
Icon,
Content,
Copy,
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment