Skip to content

Commit

Permalink
[Avatar] Support referrer policy in AvatarImage (#2772)
Browse files Browse the repository at this point in the history
Co-authored-by: Wojciech Grzebieniowski <[email protected]>
Co-authored-by: Chance Strickland <[email protected]>
  • Loading branch information
3 people authored Aug 16, 2024
1 parent 3ebe651 commit 91168c3
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .yarn/versions/19bdd400.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
releases:
"@radix-ui/react-avatar": patch

declined:
- primitives
89 changes: 88 additions & 1 deletion packages/react/avatar/src/Avatar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { axe } from 'jest-axe';
import type { RenderResult } from '@testing-library/react';
import { render } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import * as Avatar from '@radix-ui/react-avatar';

const ROOT_TEST_ID = 'avatar-root';
Expand Down Expand Up @@ -100,3 +100,90 @@ describe('given an Avatar with fallback and delayed render', () => {
expect(fallback).toBeInTheDocument();
});
});

describe('given an Avatar with an image that only works when referrerPolicy=no-referrer', () => {
let rendered: RenderResult;
const orignalGlobalImage = window.Image;

beforeAll(() => {
(window.Image as any) = class MockImage {
onload: () => void = () => {};
onerror: () => void = () => {};
src: string = '';
referrerPolicy: string | undefined;
constructor() {
setTimeout(() => {
if (this.referrerPolicy === 'no-referrer') {
this.onload();
} else {
this.onerror();
}
}, DELAY);
return this;
}
};
});

afterAll(() => {
window.Image = orignalGlobalImage;
});

describe('referrerPolicy=no-referrer', () => {
beforeEach(() => {
rendered = render(
<Avatar.Root data-testid={ROOT_TEST_ID}>
<Avatar.Fallback>{FALLBACK_TEXT}</Avatar.Fallback>
<Avatar.Image src="/test.jpg" alt={IMAGE_ALT_TEXT} referrerPolicy="no-referrer" />
</Avatar.Root>
);
});

it('should render the fallback initially', () => {
const fallback = rendered.queryByText(FALLBACK_TEXT);
expect(fallback).toBeInTheDocument();
});

it('should not render the image initially', () => {
const image = rendered.queryByRole('img');
expect(image).not.toBeInTheDocument();
});

it('should render the image after it has loaded', async () => {
const image = await rendered.findByRole('img');
expect(image).toBeInTheDocument();
});

it('should have alt text on the image', async () => {
const image = await rendered.findByAltText(IMAGE_ALT_TEXT);
expect(image).toBeInTheDocument();
});
});

describe('referrerPolicy=origin', () => {
beforeEach(() => {
rendered = render(
<Avatar.Root data-testid={ROOT_TEST_ID}>
<Avatar.Fallback>{FALLBACK_TEXT}</Avatar.Fallback>
<Avatar.Image src="/test.jpg" alt={IMAGE_ALT_TEXT} referrerPolicy="origin" />
</Avatar.Root>
);
});

it('should render the fallback initially', () => {
const fallback = rendered.queryByText(FALLBACK_TEXT);
expect(fallback).toBeInTheDocument();
});

it('should never render the image', async () => {
try {
await waitFor(() => rendered.getByRole('img'), {
timeout: DELAY + 100,
});
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect((error as Error).name).toBe('TestingLibraryElementError');
expect((error as Error).message).toContain('Unable to find role="img"');
}
});
});
});
9 changes: 6 additions & 3 deletions packages/react/avatar/src/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const AvatarImage = React.forwardRef<AvatarImageElement, AvatarImageProps>(
(props: ScopedProps<AvatarImageProps>, forwardedRef) => {
const { __scopeAvatar, src, onLoadingStatusChange = () => {}, ...imageProps } = props;
const context = useAvatarContext(IMAGE_NAME, __scopeAvatar);
const imageLoadingStatus = useImageLoadingStatus(src);
const imageLoadingStatus = useImageLoadingStatus(src, imageProps.referrerPolicy);
const handleLoadingStatusChange = useCallbackRef((status: ImageLoadingStatus) => {
onLoadingStatusChange(status);
context.onImageLoadingStatusChange(status);
Expand Down Expand Up @@ -116,7 +116,7 @@ AvatarFallback.displayName = FALLBACK_NAME;

/* -----------------------------------------------------------------------------------------------*/

function useImageLoadingStatus(src?: string) {
function useImageLoadingStatus(src?: string, referrerPolicy?: React.HTMLAttributeReferrerPolicy) {
const [loadingStatus, setLoadingStatus] = React.useState<ImageLoadingStatus>('idle');

useLayoutEffect(() => {
Expand All @@ -137,11 +137,14 @@ function useImageLoadingStatus(src?: string) {
image.onload = updateStatus('loaded');
image.onerror = updateStatus('error');
image.src = src;
if (referrerPolicy) {
image.referrerPolicy = referrerPolicy;
}

return () => {
isMounted = false;
};
}, [src]);
}, [src, referrerPolicy]);

return loadingStatus;
}
Expand Down

0 comments on commit 91168c3

Please sign in to comment.