From b5dfc37022bd6431a9b0533d140c7587fb372d73 Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Sat, 11 Jul 2026 13:09:34 -0300 Subject: [PATCH] Show alt text inside the image lightbox (opt-in) --- .../js/components/ImagePreviewDialog.vue | 10 +++++++ .../posts/editor/PostEditorComposer.vue | 2 ++ tests/Browser/AltTextTest.php | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/resources/js/components/ImagePreviewDialog.vue b/resources/js/components/ImagePreviewDialog.vue index 4e57d7c3..a51e5b75 100644 --- a/resources/js/components/ImagePreviewDialog.vue +++ b/resources/js/components/ImagePreviewDialog.vue @@ -8,6 +8,7 @@ import { MediaType } from '@/lib/mediaType'; interface PreviewItem { url: string; type: MediaType; + altText?: string; } const items = ref([]); @@ -136,6 +137,15 @@ defineExpose({ open, openCollection, close }); +
+ {{ currentItem.altText }} +
+
{ media.value.map((m) => ({ url: m.url, type: classify(m) ?? MediaType.Image, + altText: m.meta?.alt_text, })), idx, ); @@ -244,6 +245,7 @@ const onAltTextSave = (value: string): void => { v-for="(item, index) in media" :key="item.id" :ref="(el) => { if (el) mediaThumbRefs[index] = el as HTMLElement; }" + data-testid="media-thumbnail" class="group relative aspect-square cursor-zoom-in overflow-hidden rounded-xl border-2 border-foreground bg-muted shadow-2xs transition-all focus:outline-none focus:ring-2 focus:ring-foreground focus:ring-offset-2" :class="[ dragMediaIndex === index ? 'opacity-40' : '', diff --git a/tests/Browser/AltTextTest.php b/tests/Browser/AltTextTest.php index f2ad3a30..c645070d 100644 --- a/tests/Browser/AltTextTest.php +++ b/tests/Browser/AltTextTest.php @@ -51,6 +51,29 @@ function waitForAutosave(mixed $page): void JS); } +/** + * Poll on the browser side until the lightbox alt-text caption has mounted and + * laid out. The lightbox is a Radix dialog with an open animation, so the + * element is not present the instant the thumbnail is clicked; the harness + * assertions do not auto-wait, so we wait here first. + */ +function waitForLightboxAltText(mixed $page): void +{ + $page->script(<<<'JS' + (async () => { + const visible = () => { + const el = document.querySelector('[data-testid="lightbox-alt-text"]'); + if (! el) return false; + const rect = el.getBoundingClientRect(); + return rect.width > 0 && rect.height > 0; + }; + for (let attempt = 0; attempt < 100 && ! visible(); attempt++) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + })(); + JS); +} + test('editing alt text on an attached image persists it to the post media', function () { $user = User::factory()->create(); $workspace = Workspace::factory()->create(['user_id' => $user->id]); @@ -84,4 +107,10 @@ function waitForAutosave(mixed $page): void expect($post->fresh()->media[0]['meta']['alt_text']) ->toBe('a golden retriever on a beach'); + + $page->click('@media-thumbnail'); + + waitForLightboxAltText($page); + + $page->assertSeeIn('@lightbox-alt-text', 'a golden retriever on a beach'); });