generated from MetaMask/metamask-module-template
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: adding initial avatar network component
- Loading branch information
1 parent
fe89222
commit 58f1ffc
Showing
8 changed files
with
447 additions
and
0 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
packages/design-system-react/src/components/avatar-network/AvatarNetwork.constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Remove this file if it's not needed | ||
export const AVATARNETWORK_CLASSMAP = {}; | ||
|
||
import { AvatarBaseSize } from '../avatar-base'; | ||
import { TextVariant } from '../text'; | ||
|
||
export const AVATAR_NETWORK_SIZE_TO_TEXT_VARIANT_MAP: Record< | ||
AvatarBaseSize, | ||
TextVariant | ||
> = { | ||
[AvatarBaseSize.Xs]: TextVariant.BodyXs, | ||
[AvatarBaseSize.Sm]: TextVariant.BodyXs, | ||
[AvatarBaseSize.Md]: TextVariant.BodySm, | ||
[AvatarBaseSize.Lg]: TextVariant.BodyMd, | ||
[AvatarBaseSize.Xl]: TextVariant.BodyMd, | ||
}; |
107 changes: 107 additions & 0 deletions
107
packages/design-system-react/src/components/avatar-network/AvatarNetwork.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { TextColor } from '..'; | ||
import { AvatarNetwork } from './AvatarNetwork'; | ||
import { AvatarNetworkSize } from '.'; | ||
import README from './README.mdx'; | ||
|
||
const meta: Meta<typeof AvatarNetwork> = { | ||
title: 'React Components/AvatarNetwork', | ||
component: AvatarNetwork, | ||
parameters: { | ||
docs: { | ||
page: README, | ||
}, | ||
}, | ||
argTypes: { | ||
src: { | ||
control: 'text', | ||
description: | ||
'Optional URL for the network image. When provided, displays the image instead of fallback text', | ||
}, | ||
size: { | ||
control: 'select', | ||
options: Object.keys(AvatarNetworkSize), | ||
mapping: AvatarNetworkSize, | ||
description: | ||
'Optional prop to control the size of the avatar. Defaults to AvatarNetworkSize.Md', | ||
}, | ||
fallbackText: { | ||
control: 'text', | ||
description: | ||
'Required text to display when no image is provided. Also used as alt text for the image when src is provided', | ||
}, | ||
fallbackTextProps: { | ||
control: 'object', | ||
description: | ||
'Optional props to be passed to the Text component when rendering fallback text. Only used when src is not provided', | ||
}, | ||
className: { | ||
control: 'text', | ||
description: | ||
'Optional additional CSS classes to be applied to the component', | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof AvatarNetwork>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
src: 'https://cryptologos.cc/logos/ethereum-eth-logo.png', | ||
fallbackText: 'Eth', | ||
}, | ||
}; | ||
|
||
export const Src: Story = { | ||
render: () => ( | ||
<div className="flex gap-2"> | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
src="https://cryptologos.cc/logos/ethereum-eth-logo.png" | ||
/> | ||
<AvatarNetwork | ||
fallbackText="Ava" | ||
src="https://cryptologos.cc/logos/avalanche-avax-logo.png" | ||
/> | ||
<AvatarNetwork | ||
fallbackText="Pol" | ||
src="https://cryptologos.cc/logos/polygon-matic-logo.png" | ||
/> | ||
</div> | ||
), | ||
}; | ||
|
||
export const FallbackText: Story = { | ||
render: () => ( | ||
<div className="flex gap-2"> | ||
<AvatarNetwork fallbackText="Eth" /> | ||
<AvatarNetwork fallbackText="Ava" /> | ||
<AvatarNetwork fallbackText="Pol" /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const FallbackTextProps: Story = { | ||
args: { | ||
fallbackText: 'Eth', | ||
fallbackTextProps: { | ||
color: TextColor.ErrorDefault, | ||
'data-testid': 'fallback-text', | ||
}, | ||
}, | ||
}; | ||
|
||
export const Size: Story = { | ||
render: () => ( | ||
<div className="flex gap-2 items-center"> | ||
<AvatarNetwork fallbackText="E" size={AvatarNetworkSize.Xs} /> | ||
<AvatarNetwork fallbackText="Eth" size={AvatarNetworkSize.Sm} /> | ||
<AvatarNetwork fallbackText="Eth" size={AvatarNetworkSize.Md} /> | ||
<AvatarNetwork fallbackText="Eth" size={AvatarNetworkSize.Lg} /> | ||
<AvatarNetwork fallbackText="Eth" size={AvatarNetworkSize.Xl} /> | ||
</div> | ||
), | ||
}; |
127 changes: 127 additions & 0 deletions
127
packages/design-system-react/src/components/avatar-network/AvatarNetwork.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import React from 'react'; | ||
|
||
import { TextColor } from '..'; | ||
import { AvatarNetwork } from './AvatarNetwork'; | ||
import { AvatarNetworkSize } from '.'; | ||
|
||
describe('AvatarNetwork', () => { | ||
it('renders image when src is provided', () => { | ||
render(<AvatarNetwork src="test-image.jpg" fallbackText="Eth" />); | ||
|
||
const img = screen.getByRole('img'); | ||
expect(img).toBeInTheDocument(); | ||
expect(img).toHaveAttribute('src', 'test-image.jpg'); | ||
expect(img).toHaveAttribute('alt', 'Eth'); | ||
}); | ||
|
||
it('renders fallbackText when src is not provided', () => { | ||
render(<AvatarNetwork fallbackText="Eth" />); | ||
expect(screen.getByText('Eth')).toBeInTheDocument(); | ||
}); | ||
|
||
it('applies fallbackTextProps to Text component', () => { | ||
render( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
fallbackTextProps={{ | ||
color: TextColor.TextAlternative, | ||
className: 'test-class', | ||
'data-testid': 'fallback-text', | ||
}} | ||
/>, | ||
); | ||
|
||
const text = screen.getByTestId('fallback-text'); | ||
expect(text).toHaveClass('text-alternative', 'test-class'); | ||
}); | ||
|
||
it('applies custom className to root element', () => { | ||
render( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
className="custom-class" | ||
data-testid="avatar" | ||
/>, | ||
); | ||
|
||
const avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('custom-class'); | ||
}); | ||
|
||
it('passes through additional image props when src is provided', () => { | ||
render( | ||
<AvatarNetwork | ||
src="test-image.jpg" | ||
fallbackText="Eth" | ||
imageProps={{ | ||
loading: 'lazy', | ||
}} | ||
/>, | ||
); | ||
|
||
screen.debug(); | ||
|
||
const img = screen.getByRole('img'); | ||
expect(img).toHaveAttribute('loading', 'lazy'); | ||
}); | ||
|
||
it('applies size classes correctly', () => { | ||
const { rerender } = render( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
size={AvatarNetworkSize.Xs} | ||
data-testid="avatar" | ||
/>, | ||
); | ||
|
||
let avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-4 w-4'); | ||
|
||
rerender( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
size={AvatarNetworkSize.Sm} | ||
data-testid="avatar" | ||
/>, | ||
); | ||
avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-6 w-6'); | ||
|
||
rerender( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
size={AvatarNetworkSize.Md} | ||
data-testid="avatar" | ||
/>, | ||
); | ||
avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-8 w-8'); | ||
|
||
rerender( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
size={AvatarNetworkSize.Lg} | ||
data-testid="avatar" | ||
/>, | ||
); | ||
avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-10 w-10'); | ||
|
||
rerender( | ||
<AvatarNetwork | ||
fallbackText="Eth" | ||
size={AvatarNetworkSize.Xl} | ||
data-testid="avatar" | ||
/>, | ||
); | ||
avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-12 w-12'); | ||
}); | ||
|
||
it('uses medium size by default', () => { | ||
render(<AvatarNetwork fallbackText="Eth" data-testid="avatar" />); | ||
const avatar = screen.getByTestId('avatar'); | ||
expect(avatar).toHaveClass('h-8 w-8'); | ||
}); | ||
}); |
53 changes: 53 additions & 0 deletions
53
packages/design-system-react/src/components/avatar-network/AvatarNetwork.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React from 'react'; | ||
|
||
import { AvatarBase, AvatarBaseShape, AvatarBaseSize } from '../avatar-base'; | ||
import { Text } from '../text'; | ||
import { AVATAR_NETWORK_SIZE_TO_TEXT_VARIANT_MAP } from './AvatarNetwork.constants'; | ||
import type { AvatarNetworkProps } from './AvatarNetwork.types'; | ||
|
||
export const AvatarNetwork = React.forwardRef< | ||
HTMLDivElement, | ||
AvatarNetworkProps | ||
>( | ||
( | ||
{ | ||
src, | ||
fallbackText, | ||
fallbackTextProps, | ||
className, | ||
size = AvatarBaseSize.Md, | ||
'data-testid': dataTestId, | ||
imageProps, | ||
...props | ||
}, | ||
ref, | ||
) => ( | ||
<AvatarBase | ||
ref={ref} | ||
shape={AvatarBaseShape.Square} | ||
size={size} | ||
className={className} | ||
data-testid={dataTestId} | ||
{...props} | ||
> | ||
{src ? ( | ||
<img | ||
src={src} | ||
alt={fallbackText} | ||
className="w-full h-full object-cover" | ||
{...imageProps} | ||
/> | ||
) : ( | ||
<Text | ||
variant={AVATAR_NETWORK_SIZE_TO_TEXT_VARIANT_MAP[size]} | ||
asChild | ||
{...fallbackTextProps} | ||
> | ||
<span>{fallbackText}</span> | ||
</Text> | ||
)} | ||
</AvatarBase> | ||
), | ||
); | ||
|
||
AvatarNetwork.displayName = 'AvatarNetwork'; |
45 changes: 45 additions & 0 deletions
45
packages/design-system-react/src/components/avatar-network/AvatarNetwork.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import type { ComponentProps } from 'react'; | ||
|
||
import type { TextProps } from '../text'; | ||
import { AvatarNetworkSize } from '.'; | ||
|
||
export type AvatarNetworkProps = Omit< | ||
ComponentProps<'img'>, | ||
'children' | 'size' | ||
> & { | ||
/** | ||
* Optional URL for the network image | ||
* When provided, displays the image instead of fallback text | ||
*/ | ||
src?: string; | ||
/** | ||
* Optional prop to pass to the underlying img element | ||
*/ | ||
imageProps?: ComponentProps<'img'>; | ||
/** | ||
* Optional prop to control the size of the avatar | ||
* @default AvatarNetworkSize.Md | ||
*/ | ||
size?: AvatarNetworkSize; | ||
/** | ||
* Required text to display when no image is provided | ||
* Also used as alt text for the image when src is provided | ||
*/ | ||
fallbackText: string; | ||
/** | ||
* Optional props to be passed to the Text component when rendering fallback text | ||
* Only used when src is not provided | ||
*/ | ||
fallbackTextProps?: Partial< | ||
React.HTMLAttributes<HTMLSpanElement> & TextProps | ||
>; | ||
/** | ||
* Optional additional CSS classes to be applied to the component | ||
*/ | ||
className?: string; | ||
/** | ||
* Optional prop for testing purposes | ||
* Passed to the root element | ||
*/ | ||
'data-testid'?: string; | ||
}; |
Oops, something went wrong.