Show alt text inside the image lightbox (opt-in)

This commit is contained in:
Paulo Castellano 2026-07-11 13:09:34 -03:00
parent e61d12ea94
commit b5dfc37022
3 changed files with 41 additions and 0 deletions

View file

@ -8,6 +8,7 @@ import { MediaType } from '@/lib/mediaType';
interface PreviewItem {
url: string;
type: MediaType;
altText?: string;
}
const items = ref<PreviewItem[]>([]);
@ -136,6 +137,15 @@ defineExpose({ open, openCollection, close });
<IconChevronRight class="size-6" />
</button>
<div
v-if="currentItem?.altText"
data-testid="lightbox-alt-text"
class="pointer-events-none absolute left-1/2 max-w-2xl -translate-x-1/2 rounded-lg bg-black/70 px-4 py-2 text-center text-sm text-white backdrop-blur-sm"
:class="showNav ? 'bottom-14' : 'bottom-4'"
>
{{ currentItem.altText }}
</div>
<div
v-if="showNav"
class="absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full bg-black/60 px-3 py-1 text-xs text-white tabular-nums"

View file

@ -83,6 +83,7 @@ const openPreview = (item: MediaItem) => {
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' : '',

View file

@ -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');
});