From 259422bcb8f80b712eb639402a14d0b32a55920e Mon Sep 17 00:00:00 2001 From: Okeke Chinedu Emmanuel Date: Thu, 28 May 2026 16:20:25 +0100 Subject: [PATCH] Add creator avatar lightbox escape handling --- .../common/CreatorProfileHeader.tsx | 81 ++++++++++++++++--- .../__tests__/CreatorInitialsAvatar.test.tsx | 5 +- .../__tests__/CreatorProfileHeader.test.tsx | 34 ++++++++ 3 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 src/components/common/__tests__/CreatorProfileHeader.test.tsx diff --git a/src/components/common/CreatorProfileHeader.tsx b/src/components/common/CreatorProfileHeader.tsx index 880de2e..103178b 100644 --- a/src/components/common/CreatorProfileHeader.tsx +++ b/src/components/common/CreatorProfileHeader.tsx @@ -1,7 +1,14 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { Copy, Check, Share2 } from 'lucide-react'; import showToast from '@/utils/toast.util'; import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; import { cn } from '@/lib/utils'; import VerifiedBadge from '@/components/common/VerifiedBadge'; import CreatorInitialsAvatar from '@/components/common/CreatorInitialsAvatar'; @@ -31,6 +38,8 @@ const CreatorProfileHeader: React.FC = ({ className, }) => { const [copied, setCopied] = useState(false); + const [avatarLightboxOpen, setAvatarLightboxOpen] = useState(false); + const avatarTriggerRef = useRef(null); // Display-normalised handle; raw `handle` is preserved for any equality / // URL construction the caller might do via the prop. @@ -67,6 +76,10 @@ const CreatorProfileHeader: React.FC = ({ const canNativeShare = typeof navigator !== 'undefined' && !!navigator.share; + const avatar = ( + + ); + return (
= ({ )} >
-
- -
+ {avatarUrl ? ( + + + + + { + event.preventDefault(); + avatarTriggerRef.current?.focus(); + }} + onEscapeKeyDown={() => { + setAvatarLightboxOpen(false); + }} + > + + {name} profile image + + + Expanded creator profile image. Press Escape or the close button to + dismiss it. + + {`${name} + + + ) : ( +
+ {avatar} +
+ )}

= ({ )} - {copied ? 'Copied!' : canNativeShare ? 'Share Profile' : 'Copy Profile Link'} + {copied + ? 'Copied!' + : canNativeShare + ? 'Share Profile' + : 'Copy Profile Link'} {copied ? 'Copied' : canNativeShare ? 'Share' : 'Copy'} diff --git a/src/components/common/__tests__/CreatorInitialsAvatar.test.tsx b/src/components/common/__tests__/CreatorInitialsAvatar.test.tsx index 5157bd8..98fedf7 100644 --- a/src/components/common/__tests__/CreatorInitialsAvatar.test.tsx +++ b/src/components/common/__tests__/CreatorInitialsAvatar.test.tsx @@ -17,11 +17,10 @@ describe('CreatorInitialsAvatar', () => { it('renders initials fallback with hashed background when image is missing', () => { render(); - const initials = screen.getByLabelText('Alex Rivers initials avatar'); - const avatar = initials.parentElement; + const avatar = screen.getByRole('img', { name: 'Alex Rivers avatar' }); const colors = getFallbackAvatarColors('creator-123'); - expect(initials).toHaveTextContent('AR'); + expect(avatar).toHaveTextContent('AR'); expect(avatar).toHaveStyle({ background: colors.background, color: colors.textColor, diff --git a/src/components/common/__tests__/CreatorProfileHeader.test.tsx b/src/components/common/__tests__/CreatorProfileHeader.test.tsx new file mode 100644 index 0000000..186cd75 --- /dev/null +++ b/src/components/common/__tests__/CreatorProfileHeader.test.tsx @@ -0,0 +1,34 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import CreatorProfileHeader from '@/components/common/CreatorProfileHeader'; + +describe('CreatorProfileHeader', () => { + it('closes the profile image lightbox with Escape and returns focus to the trigger', async () => { + render( + + ); + + const avatarTrigger = screen.getByRole('button', { + name: 'Open Alex Rivers profile image', + }); + + fireEvent.click(avatarTrigger); + + expect( + screen.getByRole('dialog', { name: 'Alex Rivers profile image' }) + ).toBeInTheDocument(); + + fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' }); + + await waitFor(() => { + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + expect(avatarTrigger).toHaveFocus(); + }); +});