Skip to content

Commit

Permalink
feat: adding avatar network to design system react
Browse files Browse the repository at this point in the history
  • Loading branch information
georgewrmarshall committed Feb 3, 2025
1 parent ac9d8c4 commit ce739f3
Show file tree
Hide file tree
Showing 6 changed files with 565 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';

import { FontWeight } 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',
name: 'Ethereum',
fallbackText: 'ETH',
},
};

export const Src: Story = {
render: () => (
<div className="flex gap-2">
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
src="https://cryptologos.cc/logos/ethereum-eth-logo.png"
/>
<AvatarNetwork
name="Avalanche"
fallbackText="AVA"
src="https://cryptologos.cc/logos/avalanche-avax-logo.png"
/>
<AvatarNetwork
name="Polygon"
fallbackText="POL"
src="https://cryptologos.cc/logos/polygon-matic-logo.png"
/>
</div>
),
};

export const Name: Story = {
render: () => (
<div className="flex gap-2">
<AvatarNetwork
name="Ethereum"
src="https://cryptologos.cc/logos/ethereum-eth-logo.png"
/>
<AvatarNetwork
name="Avalanche"
src="https://cryptologos.cc/logos/avalanche-avax-logo.png"
/>
<AvatarNetwork name="Polygon" />
</div>
),
};

export const FallbackText: Story = {
render: () => (
<div className="flex gap-2">
<AvatarNetwork name="Ethereum" fallbackText="ETH" />
<AvatarNetwork name="Avalanche" fallbackText="AVA" />
<AvatarNetwork name="Polygon" fallbackText="POL" />
</div>
),
};

export const Size: Story = {
render: () => (
<div className="flex gap-2 items-center">
<AvatarNetwork
name="Ethereum"
fallbackText="E"
size={AvatarNetworkSize.Xs}
/>
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
size={AvatarNetworkSize.Sm}
/>
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
size={AvatarNetworkSize.Md}
/>
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
size={AvatarNetworkSize.Lg}
/>
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
size={AvatarNetworkSize.Xl}
/>
</div>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
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" name="Ethereum" fallbackText="Eth" />,
);

const img = screen.getByRole('img');
expect(img).toBeInTheDocument();
expect(img).toHaveAttribute('src', 'test-image.jpg');
expect(img).toHaveAttribute('alt', 'Ethereum');
});

it('renders fallbackText when src is not provided', () => {
render(<AvatarNetwork name="Ethereum" fallbackText="Eth" />);
expect(screen.getByText('Eth')).toBeInTheDocument();
});

it('applies fallbackTextProps to Text component', () => {
render(
<AvatarNetwork
name="Ethereum"
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
name="Ethereum"
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"
name="Ethereum"
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
name="Ethereum"
fallbackText="Eth"
size={AvatarNetworkSize.Xs}
data-testid="avatar"
/>,
);

let avatar = screen.getByTestId('avatar');
expect(avatar).toHaveClass('h-4 w-4');

rerender(
<AvatarNetwork
name="Ethereum"
fallbackText="Eth"
size={AvatarNetworkSize.Sm}
data-testid="avatar"
/>,
);
avatar = screen.getByTestId('avatar');
expect(avatar).toHaveClass('h-6 w-6');

rerender(
<AvatarNetwork
name="Ethereum"
fallbackText="Eth"
size={AvatarNetworkSize.Md}
data-testid="avatar"
/>,
);
avatar = screen.getByTestId('avatar');
expect(avatar).toHaveClass('h-8 w-8');

rerender(
<AvatarNetwork
name="Ethereum"
fallbackText="Eth"
size={AvatarNetworkSize.Lg}
data-testid="avatar"
/>,
);
avatar = screen.getByTestId('avatar');
expect(avatar).toHaveClass('h-10 w-10');

rerender(
<AvatarNetwork
name="Ethereum"
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 name="Ethereum" data-testid="avatar" />);
const avatar = screen.getByTestId('avatar');
expect(avatar).toHaveClass('h-8 w-8');
});

it('uses name as alt text when fallbackText is not provided', () => {
render(<AvatarNetwork src="test-image.jpg" name="Ethereum" />);

const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'Ethereum');
});

it('uses first letter of name as fallback text when fallbackText is not provided', () => {
render(<AvatarNetwork name="Ethereum" />);
expect(screen.getByText('E')).toBeInTheDocument();
});

it('prioritizes fallbackText over name for both alt text and fallback display', () => {
const { rerender } = render(
<AvatarNetwork
src="test-image.jpg"
name="Ethereum"
fallbackText="ETH"
imageProps={{ alt: 'ETH' }}
/>,
);

let img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'ETH');

rerender(<AvatarNetwork name="Ethereum" fallbackText="ETH" />);

expect(screen.getByText('ETH')).toBeInTheDocument();
});
});

describe('text display and alt text logic', () => {
it('uses first letter of name when fallbackText is not provided', () => {
render(<AvatarNetwork name="Ethereum" />);
expect(screen.getByText('E')).toBeInTheDocument();
});

it('uses fallbackText for display when provided', () => {
render(<AvatarNetwork name="Ethereum" fallbackText="ETH" />);
expect(screen.getByText('ETH')).toBeInTheDocument();
});

it('uses name for alt text when src is provided', () => {
render(<AvatarNetwork name="Ethereum" src="test.jpg" />);
const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'Ethereum');
});

it('uses name for alt text even when fallbackText is provided', () => {
render(<AvatarNetwork name="Ethereum" fallbackText="ETH" src="test.jpg" />);
const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'Ethereum');
});

it('allows alt text override through imageProps', () => {
render(
<AvatarNetwork
name="Ethereum"
fallbackText="ETH"
src="test.jpg"
imageProps={{ alt: 'Custom Alt' }}
/>,
);
const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'Custom Alt');
});

it('uses empty string for display text when name is not provided', () => {
// @ts-expect-error testing invalid props
render(<AvatarNetwork data-testid="avatar" />);
const base = screen.getByTestId('avatar');
expect(base.querySelector('span')).toHaveTextContent('');
});

it('uses fallbackText for alt text when name is not provided', () => {
// @ts-expect-error testing invalid props
render(<AvatarNetwork fallbackText="ETH" src="test.jpg" />);
const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'ETH');
});

it('uses default "Network logo" for alt text when neither name nor fallbackText is provided', () => {
// @ts-expect-error testing invalid props
render(<AvatarNetwork src="test.jpg" />);
const img = screen.getByRole('img');
expect(img).toHaveAttribute('alt', 'Network logo');
});
});
Loading

0 comments on commit ce739f3

Please sign in to comment.