From ddac55be4f4eb71fdccb267ec4799962e6de895c Mon Sep 17 00:00:00 2001 From: Wojciech Grzebieniowski Date: Mon, 11 Mar 2024 19:21:08 +0100 Subject: [PATCH 1/2] Add support for referrerPolicy to AvatarImage --- packages/react/avatar/src/Avatar.test.tsx | 89 ++++++++++++++++++++++- packages/react/avatar/src/Avatar.tsx | 9 ++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/packages/react/avatar/src/Avatar.test.tsx b/packages/react/avatar/src/Avatar.test.tsx index abc3a0c873..45b40f316c 100644 --- a/packages/react/avatar/src/Avatar.test.tsx +++ b/packages/react/avatar/src/Avatar.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; 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'; @@ -101,3 +101,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 00899a5fba..e6a4b7ff34 100644 --- a/packages/react/avatar/src/Avatar.tsx +++ b/packages/react/avatar/src/Avatar.tsx @@ -63,7 +63,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); @@ -117,7 +117,7 @@ AvatarFallback.displayName = FALLBACK_NAME; /* -----------------------------------------------------------------------------------------------*/ -function useImageLoadingStatus(src?: string) { +function useImageLoadingStatus(src?: string, referrerPolicy?: React.HTMLAttributeReferrerPolicy) { const [loadingStatus, setLoadingStatus] = React.useState('idle'); useLayoutEffect(() => { @@ -138,11 +138,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; } From a479bc42d80c93893f9a3469126d1e77859942aa Mon Sep 17 00:00:00 2001 From: Wojciech Grzebieniowski Date: Mon, 11 Mar 2024 19:26:07 +0100 Subject: [PATCH 2/2] Bump version --- .yarn/versions/19bdd400.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .yarn/versions/19bdd400.yml 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