1
0
mirror of https://github.com/immich-app/immich.git synced 2025-01-13 15:35:15 +02:00

feat(web): announce notifications to screen readers (#12071)

This commit is contained in:
Ben 2024-08-27 18:13:17 -04:00 committed by GitHub
parent 98b3441cb1
commit 72ab664936
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 46 additions and 14 deletions

View File

@ -33,7 +33,7 @@ test.describe('Photo Viewer', () => {
await page.waitForLoadState('load');
// this is the spinner
await page.waitForSelector('svg[role=status]');
await expect(page.getByRole('status')).toBeVisible();
await expect(page.getByTestId('loading-spinner')).toBeVisible();
});
test('loads high resolution photo when zoomed', async ({ page }) => {

View File

@ -50,7 +50,7 @@
bind:this={menuElement}
class:max-h-[100vh]={isVisible}
class:max-h-0={!isVisible}
class="flex flex-col transition-all duration-[250ms] ease-in-out"
class="flex flex-col transition-all duration-[250ms] ease-in-out outline-none"
role="menu"
tabindex="-1"
>

View File

@ -11,6 +11,7 @@
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="loading-spinner"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"

View File

@ -39,6 +39,29 @@ describe('NotificationCard component', () => {
expect(sut.getByTestId('message')).toHaveTextContent('Notification message');
});
it('makes all buttons non-focusable and hidden from screen readers', () => {
sut = render(NotificationCard, {
notification: {
id: 1234,
message: 'Notification message',
timeout: 1000,
type: NotificationType.Info,
action: { type: 'discard' },
button: {
text: 'button',
onClick: vi.fn(),
},
},
});
const buttons = sut.container.querySelectorAll('button');
expect(buttons).toHaveLength(2);
for (const button of buttons) {
expect(button.getAttribute('tabindex')).toBe('-1');
expect(button.getAttribute('aria-hidden')).toBe('true');
}
});
it('shows title and renders component', () => {
sut = render(NotificationCard, {
notification: {

View File

@ -9,8 +9,6 @@ function _getNotificationListElement(sut: RenderResult<NotificationList>): HTMLA
}
describe('NotificationList component', () => {
const sut: RenderResult<NotificationList> = render(NotificationList);
beforeAll(() => {
// https://testing-library.com/docs/svelte-testing-library/faq#why-arent-transition-events-running
vi.stubGlobal('requestAnimationFrame', (fn: FrameRequestCallback) => {
@ -23,6 +21,10 @@ describe('NotificationList component', () => {
});
it('shows a notification when added and closes it automatically after the delay timeout', async () => {
const sut: RenderResult<NotificationList> = render(NotificationList);
const status = await sut.findAllByRole('status');
expect(status).toHaveLength(1);
expect(_getNotificationListElement(sut)).not.toBeInTheDocument();
notificationController.show({

View File

@ -91,6 +91,8 @@
size="20"
padding="2"
on:click={discard}
aria-hidden="true"
tabindex={-1}
/>
</div>
@ -108,6 +110,8 @@
type="button"
class="{buttonStyle[notification.type]} rounded px-3 pt-1.5 pb-1 transition-all duration-200"
on:click={handleButtonClick}
aria-hidden="true"
tabindex={-1}
>
{notification.button.text}
</button>

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { notificationController } from './notification';
import { fade } from 'svelte/transition';
import { t } from 'svelte-i18n';
import NotificationCard from './notification-card.svelte';
import { flip } from 'svelte/animate';
import { quintOut } from 'svelte/easing';
@ -9,12 +9,14 @@
const { notificationList } = notificationController;
</script>
{#if $notificationList.length > 0}
<section transition:fade={{ duration: 250 }} id="notification-list" class="fixed right-5 top-[80px] z-[99999999]">
{#each $notificationList as notification (notification.id)}
<div animate:flip={{ duration: 250, easing: quintOut }}>
<NotificationCard {notification} />
</div>
{/each}
</section>
{/if}
<div role="status" aria-relevant="additions" aria-label={$t('notifications')}>
{#if $notificationList.length > 0}
<section transition:fade={{ duration: 250 }} id="notification-list" class="fixed right-5 top-[80px] z-[99999999]">
{#each $notificationList as notification (notification.id)}
<div animate:flip={{ duration: 250, easing: quintOut }}>
<NotificationCard {notification} />
</div>
{/each}
</section>
{/if}
</div>