Skip to content

fix(clerk-js): UserButton appearance #6427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const UserButtonTrigger = withAvatarShimmer(
boxElementDescriptor={descriptors.userButtonAvatarBox}
imageElementDescriptor={descriptors.userButtonAvatarImage}
{...user}
key={`user-avatar-${user?.imageUrl || 'no-image'}`}
size={theme => theme.sizes.$7}
/>
</Flex>
Expand Down
4 changes: 4 additions & 0 deletions packages/clerk-js/src/ui/elements/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export const Avatar = (props: AvatarProps) => {
} = props;
const [error, setError] = React.useState(false);

React.useEffect(() => {
setError(false);
}, [imageUrl]);

const ImgOrFallback =
initials && (!imageUrl || error) ? (
<InitialsAvatarFallback initials={initials} />
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/elements/UserPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export const UserPreview = (props: UserPreviewProps) => {
{...samlAccount}
name={name}
avatarUrl={imageUrl}
key={`user-preview-avatar-${imageUrl || 'no-image'}`}
size={getAvatarSizes}
sx={avatarSx}
rounded={rounded}
Expand Down
102 changes: 102 additions & 0 deletions packages/clerk-js/src/ui/elements/__tests__/Avatar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Avatar } from '../Avatar';
import { AppearanceProvider } from '../../customizables';

const mockTheme = {
colors: {
$colorForeground: '#000000',
$colorBackground: '#ffffff',
},
radii: {
$circle: '50%',
$avatar: '4px',
},
sizes: {
$4: '16px',
$6: '24px',
},
space: {
$2: '8px',
},
};

const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<AppearanceProvider
appearanceKey='userButton'
appearance={{
variables: mockTheme,
}}
>
{children}
</AppearanceProvider>
);

describe('Avatar', () => {
it('should reset error state when imageUrl changes', () => {
const { rerender } = render(
<TestWrapper>
<Avatar
imageUrl='https://example.com/image1.jpg'
title='Test User'
initials='TU'
/>
</TestWrapper>,
);

// Initially should show image
expect(screen.getByAltText("Test User's logo")).toBeInTheDocument();

// Simulate image load error
const img = screen.getByAltText("Test User's logo");
img.dispatchEvent(new Event('error'));

// Should show initials after error
expect(screen.getByText('TU')).toBeInTheDocument();

// Change imageUrl - should reset error state and show new image
rerender(
<TestWrapper>
<Avatar
imageUrl='https://example.com/image2.jpg'
title='Test User'
initials='TU'
/>
</TestWrapper>,
);

// Should show image again (error state reset)
expect(screen.getByAltText("Test User's logo")).toBeInTheDocument();
expect(screen.queryByText('TU')).not.toBeInTheDocument();
});

it('should handle null imageUrl properly', () => {
render(
<TestWrapper>
<Avatar
imageUrl={null}
title='Test User'
initials='TU'
/>
</TestWrapper>,
);

// Should show initials when no imageUrl
expect(screen.getByText('TU')).toBeInTheDocument();
});

it('should handle empty imageUrl properly', () => {
render(
<TestWrapper>
<Avatar
imageUrl=''
title='Test User'
initials='TU'
/>
</TestWrapper>,
);

// Should show initials when empty imageUrl
expect(screen.getByText('TU')).toBeInTheDocument();
});
});