From c201e86b97c25108804a8ca95d20005acb31d63e Mon Sep 17 00:00:00 2001 From: bymyself Date: Sun, 8 Sep 2024 17:48:29 -0700 Subject: [PATCH] Improve sidebar accessibility (#759) * Add ARIA label to sidebar buttons * Add component test * Add generalized component tests --- src/components/sidebar/SidebarIcon.vue | 13 ++-- .../sidebar/__tests__/SidebarIcon.spec.ts | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/components/sidebar/__tests__/SidebarIcon.spec.ts diff --git a/src/components/sidebar/SidebarIcon.vue b/src/components/sidebar/SidebarIcon.vue index 4afa3e5ed..76316a8a1 100644 --- a/src/components/sidebar/SidebarIcon.vue +++ b/src/components/sidebar/SidebarIcon.vue @@ -3,11 +3,14 @@ :class="props.class" text :pt="{ - root: `side-bar-button ${ - props.selected - ? 'p-button-primary side-bar-button-selected' - : 'p-button-secondary' - }` + root: { + class: `side-bar-button ${ + props.selected + ? 'p-button-primary side-bar-button-selected' + : 'p-button-secondary' + }`, + 'aria-label': props.tooltip + } }" @click="emit('click', $event)" v-tooltip="{ value: props.tooltip, showDelay: 300, hideDelay: 300 }" diff --git a/src/components/sidebar/__tests__/SidebarIcon.spec.ts b/src/components/sidebar/__tests__/SidebarIcon.spec.ts new file mode 100644 index 000000000..97edbaa72 --- /dev/null +++ b/src/components/sidebar/__tests__/SidebarIcon.spec.ts @@ -0,0 +1,75 @@ +import { mount } from '@vue/test-utils' +import { expect, describe, it } from 'vitest' +import SidebarIcon from '../SidebarIcon.vue' +import OverlayBadge from 'primevue/overlaybadge' +import Button from 'primevue/button' +import Tooltip from 'primevue/tooltip' +import PrimeVue from 'primevue/config' + +type SidebarIconProps = { + icon: string + selected: boolean + tooltip?: string + class?: string + iconBadge?: string | (() => string | null) +} + +describe('SidebarIcon', () => { + const exampleProps: SidebarIconProps = { + icon: 'pi pi-cog', + selected: false + } + + const mountSidebarIcon = (props: Partial, options = {}) => { + return mount(SidebarIcon, { + global: { + plugins: [PrimeVue], + directives: { tooltip: Tooltip }, + components: { OverlayBadge, Button } + }, + props: { ...exampleProps, ...props }, + ...options + }) + } + + it('renders label', () => { + const wrapper = mountSidebarIcon({}) + expect(wrapper.find('.p-button.p-component').exists()).toBe(true) + expect(wrapper.find('.p-button-label').exists()).toBe(true) + }) + + it('renders icon', () => { + const wrapper = mountSidebarIcon({}) + expect(wrapper.find('.p-button-icon-only').exists()).toBe(true) + }) + + it('creates badge when iconBadge prop is set', () => { + const badge = '2' + const wrapper = mountSidebarIcon({ iconBadge: badge }) + const badgeEl = wrapper.findComponent(OverlayBadge) + expect(badgeEl.exists()).toBe(true) + expect(badgeEl.find('.p-badge').text()).toEqual(badge) + }) + + it('shows tooltip on hover', async () => { + const tooltipShowDelay = 300 + const tooltipText = 'Settings' + const wrapper = mountSidebarIcon({ tooltip: tooltipText }) + + const tooltipElBeforeHover = document.querySelector('[role="tooltip"]') + expect(tooltipElBeforeHover).toBeNull() + + // Hover over the icon + await wrapper.trigger('mouseenter') + await new Promise((resolve) => setTimeout(resolve, tooltipShowDelay + 16)) + + const tooltipElAfterHover = document.querySelector('[role="tooltip"]') + expect(tooltipElAfterHover).not.toBeNull() + }) + + it('sets aria-label attribute when tooltip is provided', () => { + const tooltipText = 'Settings' + const wrapper = mountSidebarIcon({ tooltip: tooltipText }) + expect(wrapper.attributes('aria-label')).toEqual(tooltipText) + }) +})