Skip to content

Commit c275519

Browse files
committed
feat: avatar with fallback icons component
Signed-off-by: Adam Setch <[email protected]>
1 parent c3a4b3c commit c275519

15 files changed

+949
-286
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { render, screen } from '@testing-library/react';
2+
3+
import { type Link, Size } from '../../types';
4+
import {
5+
AvatarWithFallback,
6+
type IAvatarWithFallback,
7+
} from './AvatarWithFallback';
8+
9+
describe('renderer/components/avatars/AvatarWithFallback.tsx', () => {
10+
const props: IAvatarWithFallback = {
11+
src: 'https://avatars.githubusercontent.com/u/133795385?s=200&v=4' as Link,
12+
alt: 'gitify-app',
13+
name: '@gitify-app',
14+
size: Size.MEDIUM,
15+
isNonHuman: false,
16+
};
17+
18+
it('should render avatar - human user', () => {
19+
const tree = render(<AvatarWithFallback {...props} />);
20+
expect(tree).toMatchSnapshot();
21+
});
22+
23+
it('should render avatar - non-human user', () => {
24+
const tree = render(<AvatarWithFallback {...props} isNonHuman />);
25+
expect(tree).toMatchSnapshot();
26+
});
27+
28+
it('renders the fallback icon when the image fails to load (isBroken = true) - human user', () => {
29+
render(<AvatarWithFallback {...props} />);
30+
31+
// Find the avatar element by its alt text
32+
const avatar = screen.getByAltText('gitify-app') as HTMLImageElement;
33+
34+
// Simulate an error event on the image element
35+
avatar.dispatchEvent(new Event('error'));
36+
37+
expect(screen.getByTestId('avatar')).toMatchSnapshot();
38+
});
39+
40+
it('renders the fallback icon when the image fails to load (isBroken = true) - non human user', () => {
41+
render(<AvatarWithFallback {...props} isNonHuman />);
42+
43+
// Find the avatar element by its alt text
44+
const avatar = screen.getByAltText('gitify-app') as HTMLImageElement;
45+
46+
// Simulate an error event on the image element
47+
avatar.dispatchEvent(new Event('error'));
48+
49+
expect(screen.getByTestId('avatar')).toMatchSnapshot();
50+
});
51+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type React from 'react';
2+
import { useState } from 'react';
3+
4+
import { FeedPersonIcon, MarkGithubIcon } from '@primer/octicons-react';
5+
import { Avatar, Stack, Text } from '@primer/react';
6+
7+
import { type Link, Size } from '../../types';
8+
9+
export interface IAvatarWithFallback {
10+
src: Link;
11+
alt: string;
12+
name?: string;
13+
size?: number;
14+
isNonHuman?: boolean;
15+
}
16+
17+
export const AvatarWithFallback: React.FC<IAvatarWithFallback> = ({
18+
src,
19+
alt,
20+
name,
21+
size = Size.MEDIUM,
22+
isNonHuman = false,
23+
}) => {
24+
const [isBroken, setIsBroken] = useState(false);
25+
26+
return (
27+
<Stack
28+
direction="horizontal"
29+
align="center"
30+
gap="condensed"
31+
data-testid="avatar"
32+
>
33+
{isBroken ? (
34+
isNonHuman ? (
35+
<MarkGithubIcon size={size} />
36+
) : (
37+
<FeedPersonIcon size={size} />
38+
)
39+
) : (
40+
<Avatar
41+
src={src}
42+
alt={alt}
43+
size={size}
44+
square={isNonHuman}
45+
onError={() => setIsBroken(true)}
46+
/>
47+
)}
48+
{name && <Text>{name}</Text>}
49+
</Stack>
50+
);
51+
};

src/renderer/components/avatars/__snapshots__/AvatarWithFallback.test.tsx.snap

Lines changed: 289 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)