diff --git a/.yarn/versions/19bdd400.yml b/.yarn/versions/19bdd400.yml
new file mode 100644
index 0000000000..50d060d514
--- /dev/null
+++ b/.yarn/versions/19bdd400.yml
@@ -0,0 +1,5 @@
+releases:
+ "@radix-ui/react-avatar": patch
+
+declined:
+ - primitives
diff --git a/packages/react/avatar/src/Avatar.test.tsx b/packages/react/avatar/src/Avatar.test.tsx
index e1705c6f36..4e71f84ed3 100644
--- a/packages/react/avatar/src/Avatar.test.tsx
+++ b/packages/react/avatar/src/Avatar.test.tsx
@@ -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';
@@ -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(
+
+ {FALLBACK_TEXT}
+
+
+ );
+ });
+
+ 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(
+
+ {FALLBACK_TEXT}
+
+
+ );
+ });
+
+ 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"');
+ }
+ });
+ });
+});
diff --git a/packages/react/avatar/src/Avatar.tsx b/packages/react/avatar/src/Avatar.tsx
index 3749fc218a..53841b7f0a 100644
--- a/packages/react/avatar/src/Avatar.tsx
+++ b/packages/react/avatar/src/Avatar.tsx
@@ -62,7 +62,7 @@ const AvatarImage = React.forwardRef(
(props: ScopedProps, 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);
@@ -116,7 +116,7 @@ AvatarFallback.displayName = FALLBACK_NAME;
/* -----------------------------------------------------------------------------------------------*/
-function useImageLoadingStatus(src?: string) {
+function useImageLoadingStatus(src?: string, referrerPolicy?: React.HTMLAttributeReferrerPolicy) {
const [loadingStatus, setLoadingStatus] = React.useState('idle');
useLayoutEffect(() => {
@@ -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;
}