Skip to content

Commit cdfbea1

Browse files
committed
fix: more a11y fixes
1 parent 2b6990b commit cdfbea1

3 files changed

Lines changed: 117 additions & 20 deletions

File tree

app/components/ChartModal.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ dialog::backdrop {
6363
background: rgba(0, 0, 0, 0.6);
6464
}
6565
66-
dialog[open] {
67-
animation: fade-in 200ms ease-out;
66+
@media (prefers-reduced-motion: no-preference) {
67+
dialog[open] {
68+
animation: fade-in 200ms ease-out;
69+
}
6870
}
6971
7072
@keyframes fade-in {

app/components/PackageDownloadAnalytics.vue

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ const config = computed(() => ({
534534
<button
535535
v-if="showResetButton"
536536
type="button"
537-
title="Reset date range"
537+
aria-label="Reset date range"
538538
class="flex items-center justify-center px-2.5 py-1.75 border border-transparent rounded-md text-fg-subtle hover:text-fg transition-colors hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
539539
@click="
540540
() => {
@@ -546,7 +546,7 @@ const config = computed(() => ({
546546
}
547547
"
548548
>
549-
<span class="i-carbon-reset w-5 h-5 inline-block" />
549+
<span class="i-carbon-reset w-5 h-5 inline-block" aria-hidden="true" />
550550
</button>
551551
</div>
552552
</div>
@@ -556,41 +556,75 @@ const config = computed(() => ({
556556
<ClientOnly v-if="inModal">
557557
<VueUiXy :dataset="chartData.dataset" :config="config">
558558
<template #menuIcon="{ isOpen }">
559-
<span class="i-carbon-close w-6 h-6" v-if="isOpen" />
560-
<span class="i-carbon-overflow-menu-vertical w-6 h-6" v-else />
559+
<span v-if="isOpen" class="i-carbon-close w-6 h-6" aria-hidden="true" />
560+
<span v-else class="i-carbon-overflow-menu-vertical w-6 h-6" aria-hidden="true" />
561561
</template>
562562
<template #optionCsv>
563-
<span class="i-carbon-csv w-6 h-6 text-fg-subtle" style="pointer-events: none" />
563+
<span
564+
class="i-carbon-csv w-6 h-6 text-fg-subtle"
565+
style="pointer-events: none"
566+
aria-hidden="true"
567+
/>
564568
</template>
565569
<template #optionImg>
566-
<span class="i-carbon-png w-6 h-6 text-fg-subtle" style="pointer-events: none" />
570+
<span
571+
class="i-carbon-png w-6 h-6 text-fg-subtle"
572+
style="pointer-events: none"
573+
aria-hidden="true"
574+
/>
567575
</template>
568576
<template #optionSvg>
569-
<span class="i-carbon-svg w-6 h-6 text-fg-subtle" style="pointer-events: none" />
577+
<span
578+
class="i-carbon-svg w-6 h-6 text-fg-subtle"
579+
style="pointer-events: none"
580+
aria-hidden="true"
581+
/>
570582
</template>
571583

572584
<template #annotator-action-close>
573-
<span class="i-carbon-close w-6 h-6 text-fg-subtle" style="pointer-events: none" />
585+
<span
586+
class="i-carbon-close w-6 h-6 text-fg-subtle"
587+
style="pointer-events: none"
588+
aria-hidden="true"
589+
/>
574590
</template>
575591
<template #annotator-action-color="{ color }">
576-
<span class="i-carbon-color-palette w-6 h-6" :style="{ color }" />
592+
<span class="i-carbon-color-palette w-6 h-6" :style="{ color }" aria-hidden="true" />
577593
</template>
578-
<template #annotator-action-undo="{ disabled }">
579-
<span class="i-carbon-undo w-6 h-6 text-fg-subtle" style="pointer-events: none" />
594+
<template #annotator-action-undo>
595+
<span
596+
class="i-carbon-undo w-6 h-6 text-fg-subtle"
597+
style="pointer-events: none"
598+
aria-hidden="true"
599+
/>
580600
</template>
581-
<template #annotator-action-redo="{ disabled }">
582-
<span class="i-carbon-redo w-6 h-6 text-fg-subtle" style="pointer-events: none" />
601+
<template #annotator-action-redo>
602+
<span
603+
class="i-carbon-redo w-6 h-6 text-fg-subtle"
604+
style="pointer-events: none"
605+
aria-hidden="true"
606+
/>
583607
</template>
584-
<template #annotator-action-delete="{ disabled }">
585-
<span class="i-carbon-trash-can w-6 h-6 text-fg-subtle" style="pointer-events: none" />
608+
<template #annotator-action-delete>
609+
<span
610+
class="i-carbon-trash-can w-6 h-6 text-fg-subtle"
611+
style="pointer-events: none"
612+
aria-hidden="true"
613+
/>
586614
</template>
587615
<template #optionAnnotator="{ isAnnotator }">
588616
<span
617+
v-if="isAnnotator"
589618
class="i-carbon-edit-off w-6 h-6 text-fg-subtle"
590619
style="pointer-events: none"
591-
v-if="isAnnotator"
620+
aria-hidden="true"
621+
/>
622+
<span
623+
v-else
624+
class="i-carbon-edit w-6 h-6 text-fg-subtle"
625+
style="pointer-events: none"
626+
aria-hidden="true"
592627
/>
593-
<span class="i-carbon-edit w-6 h-6 text-fg-subtle" style="pointer-events: none" v-else />
594628
</template>
595629
</VueUiXy>
596630
<template #fallback>
@@ -600,6 +634,8 @@ const config = computed(() => ({
600634

601635
<div
602636
v-if="pending"
637+
role="status"
638+
aria-live="polite"
603639
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-xs text-fg-subtle font-mono bg-bg/70 backdrop-blur px-3 py-2 rounded-md border border-border"
604640
>
605641
Loading…

test/nuxt/components.spec.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ import ProvenanceBadge from '~/components/ProvenanceBadge.vue'
5959
import MarkdownText from '~/components/MarkdownText.vue'
6060
import PackageSkeleton from '~/components/PackageSkeleton.vue'
6161
import PackageCard from '~/components/PackageCard.vue'
62-
// import PackageDownloadAnalytics from '~/components/PackageDownloadAnalytics.vue'
62+
import ChartModal from '~/components/ChartModal.vue'
63+
import PackageDownloadAnalytics from '~/components/PackageDownloadAnalytics.vue'
6364
import PackagePlaygrounds from '~/components/PackagePlaygrounds.vue'
6465
import PackageDependencies from '~/components/PackageDependencies.vue'
6566
import PackageVersions from '~/components/PackageVersions.vue'
@@ -322,6 +323,64 @@ describe('component accessibility audits', () => {
322323
})
323324
})
324325

326+
// Note: PackageWeeklyDownloadStats tests are skipped because vue-data-ui VueUiSparkline
327+
// component has issues in the test environment (requires DOM measurements that aren't
328+
// available during SSR-like test mounting).
329+
330+
describe('ChartModal', () => {
331+
it('should have no accessibility violations when closed', async () => {
332+
const component = await mountSuspended(ChartModal, {
333+
props: { open: false },
334+
slots: { title: 'Downloads', default: '<div>Chart content</div>' },
335+
})
336+
const results = await runAxe(component)
337+
expect(results.violations).toEqual([])
338+
})
339+
340+
// Note: Testing the open state is challenging because native <dialog>.showModal()
341+
// requires the element to be in the DOM and connected, which doesn't work well
342+
// with the test environment's cloning approach. The dialog accessibility is
343+
// inherently provided by the native <dialog> element with aria-labelledby.
344+
})
345+
346+
describe('PackageDownloadAnalytics', () => {
347+
const mockWeeklyDownloads = [
348+
{ downloads: 1000, weekKey: '2024-W01', weekStart: '2024-01-01', weekEnd: '2024-01-07' },
349+
{ downloads: 1200, weekKey: '2024-W02', weekStart: '2024-01-08', weekEnd: '2024-01-14' },
350+
{ downloads: 1500, weekKey: '2024-W03', weekStart: '2024-01-15', weekEnd: '2024-01-21' },
351+
]
352+
353+
it('should have no accessibility violations (non-modal)', async () => {
354+
// Test only non-modal mode to avoid vue-data-ui chart rendering issues
355+
const component = await mountSuspended(PackageDownloadAnalytics, {
356+
props: {
357+
weeklyDownloads: mockWeeklyDownloads,
358+
packageName: 'vue',
359+
createdIso: '2020-01-01T00:00:00.000Z',
360+
inModal: false,
361+
},
362+
})
363+
const results = await runAxe(component)
364+
expect(results.violations).toEqual([])
365+
})
366+
367+
it('should have no accessibility violations with empty data', async () => {
368+
const component = await mountSuspended(PackageDownloadAnalytics, {
369+
props: {
370+
weeklyDownloads: [],
371+
packageName: 'vue',
372+
createdIso: null,
373+
inModal: false,
374+
},
375+
})
376+
const results = await runAxe(component)
377+
expect(results.violations).toEqual([])
378+
})
379+
380+
// Note: Modal mode tests with inModal: true are skipped because vue-data-ui VueUiXy
381+
// component has issues in the test environment (requires DOM measurements).
382+
})
383+
325384
describe('PackagePlaygrounds', () => {
326385
it('should have no accessibility violations with single link', async () => {
327386
const links = [

0 commit comments

Comments
 (0)