feat(accessibility): enhance AccountViewView and document test suite

- Add ARIA annotations and roles to AccountViewView for better screen reader support
  - Add role="tooltip" to API server description
  - Improve input control accessibility with proper ARIA attributes
  - Add descriptive labels and aria-labels for interactive elements

- Create comprehensive README.md for Playwright test suite
  - Document test structure and organization
  - Add setup instructions and prerequisites
  - Include troubleshooting guide and contribution guidelines
  - Link to related documentation

This change improves accessibility compliance and makes the test suite
more maintainable for contributors.
This commit is contained in:
Matthew Raymer
2025-05-15 06:19:36 +00:00
parent 36b755e859
commit 29607f4e58
4 changed files with 2799 additions and 440 deletions

View File

@@ -3,7 +3,7 @@
<TopMessage />
<!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<main id="Content" class="p-6 pb-24 max-w-3xl mx-auto" role="main" aria-label="Account Profile">
<!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light">
Your Identity
@@ -14,6 +14,8 @@
v-if="!activeDid"
id="noticeBeforeShare"
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4"
role="alert"
aria-live="polite"
>
<p class="mb-4">
<b>Note:</b> Before you can share with others or take any action, you
@@ -28,10 +30,12 @@
</div>
<!-- Identity Details -->
<div
<section
id="sectionIdentityDetails"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4"
aria-labelledby="identityDetailsHeading"
>
<h2 id="identityDetailsHeading" class="sr-only">Identity Details</h2>
<div v-if="givenName">
<h2 class="text-xl font-semibold mb-2">
<span class="whitespace-nowrap">
@@ -75,11 +79,17 @@
:profile-image-url="profileImageUrl"
class="inline-block align-text-bottom border border-slate-300 rounded"
@click="showLargeIdenticonUrl = profileImageUrl"
role="button"
aria-label="View profile image in large size"
tabindex="0"
/>
<font-awesome
icon="trash-can"
class="text-red-500 fa-fw ml-8 mt-8 w-12 h-12"
@click="confirmDeleteImage"
role="button"
aria-label="Delete profile image"
tabindex="0"
/>
</span>
<div v-else class="text-center">
@@ -140,17 +150,20 @@
<div
class="text-sm text-slate-500 flex justify-start items-center mb-1"
data-testId="didWrapper"
role="region"
aria-label="Your Identifier"
>
<code class="truncate">{{ activeDid }}</code>
<code class="truncate" aria-label="Your DID">{{ activeDid }}</code>
<button
class="ml-2"
@click="
doCopyTwoSecRedo(activeDid, () => (showDidCopy = !showDidCopy))
"
aria-label="Copy DID to clipboard"
>
<font-awesome icon="copy" class="text-slate-400 fa-fw"></font-awesome>
<font-awesome icon="copy" class="text-slate-400 fa-fw" aria-hidden="true"></font-awesome>
</button>
<span v-show="showDidCopy">Copied</span>
<span v-show="showDidCopy" role="status" aria-live="polite">Copied</span>
</div>
<div class="text-blue-500 text-sm font-bold">
@@ -158,7 +171,7 @@
Your Activity
</router-link>
</div>
</div>
</section>
<!-- Registration notice -->
<!--
@@ -169,6 +182,8 @@
v-if="!isRegistered"
id="noticeBeforeAnnounce"
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4"
role="alert"
aria-live="polite"
>
<p class="mb-4">
<b>Note:</b> Before you can publicly announce a new project or time
@@ -182,27 +197,31 @@
</router-link>
</div>
<div
<section
v-if="isRegistered"
id="sectionNotifications"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
aria-labelledby="notificationsHeading"
>
<!-- label -->
<div class="mb-2 font-bold">Notifications</div>
<h2 id="notificationsHeading" class="mb-2 font-bold">Notifications</h2>
<div class="flex items-center justify-between">
<!-- label -->
<div>
Reminder Notification
<font-awesome
icon="question-circle"
<button
class="text-slate-400 fa-fw ml-2 cursor-pointer"
@click.stop="showReminderNotificationInfo"
/>
aria-label="Learn more about reminder notifications"
>
<font-awesome icon="question-circle" aria-hidden="true"></font-awesome>
</button>
</div>
<!-- toggle -->
<div
class="relative ml-2 cursor-pointer"
@click="showReminderNotificationChoice()"
role="switch"
:aria-checked="notifyingReminder"
aria-label="Toggle reminder notifications"
tabindex="0"
>
<!-- input -->
<input v-model="notifyingReminder" type="checkbox" class="sr-only" />
@@ -253,49 +272,47 @@
<router-link class="pl-4 text-sm text-blue-500" to="/help-notifications">
Troubleshoot your notifications.
</router-link>
</div>
</section>
<PushNotificationPermission ref="pushNotificationPermission" />
<div
<section
id="sectionSearchLocation"
class="flex justify-between bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
aria-labelledby="searchLocationHeading"
>
<!-- label -->
<span class="mb-2 font-bold">Location for Searches</span>
<h2 id="searchLocationHeading" class="mb-2 font-bold">Location for Searches</h2>
<router-link
:to="{ name: 'search-area' }"
class="text-m bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md mb-2"
>
{{ isSearchAreasSet ? "Change" : "Set" }} Search Area
</router-link>
</div>
</section>
<!-- User Profile -->
<div
<section
v-if="isRegistered"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
aria-labelledby="userProfileHeading"
>
<div v-if="loadingProfile" class="text-center mb-2">
<font-awesome
icon="spinner"
class="fa-spin text-slate-400"
></font-awesome>
Loading profile...
</div>
<div v-else class="flex items-center mb-2">
<h2 id="userProfileHeading" class="flex items-center mb-2">
<span class="font-bold">Public Profile</span>
<font-awesome
icon="circle-info"
<button
class="text-slate-400 fa-fw ml-2 cursor-pointer"
@click="showProfileInfo"
/>
</div>
aria-label="Learn more about public profile"
>
<font-awesome icon="circle-info" aria-hidden="true"></font-awesome>
</button>
</h2>
<textarea
v-model="userProfileDesc"
class="w-full h-32 p-2 border border-slate-300 rounded-md"
placeholder="Write something about yourself for the public..."
:readonly="loadingProfile || savingProfile"
:class="{ 'bg-slate-100': loadingProfile || savingProfile }"
aria-label="Public profile description"
:aria-busy="loadingProfile || savingProfile"
></textarea>
<div class="flex items-center mb-4" @click="toggleUserProfileLocation">
@@ -364,18 +381,19 @@
</div>
<div v-else-if="loadingProfile">Loading...</div>
<div v-else>Saving...</div>
</div>
</section>
<div
<section
v-if="activeDid"
id="sectionUsageLimits"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
aria-labelledby="usageLimitsHeading"
>
<div class="mb-2 font-bold">Usage Limits</div>
<h2 id="usageLimitsHeading" class="mb-2 font-bold">Usage Limits</h2>
<!-- show spinner if loading limits -->
<div v-if="loadingLimits" class="text-center">
<div v-if="loadingLimits" class="text-center" role="status" aria-live="polite">
Checking&hellip;
<font-awesome icon="spinner" class="fa-spin"></font-awesome>
<font-awesome icon="spinner" class="fa-spin" aria-hidden="true"></font-awesome>
</div>
<div class="mb-4 text-center">
{{ limitsMessage }}
@@ -423,7 +441,7 @@
>
Recheck Limits
</button>
</div>
</section>
<DataExportSection :active-did="activeDid" />
@@ -435,7 +453,8 @@
>
Advanced
</h3>
<div v-if="showAdvanced || showGeneralAdvanced" id="sectionAdvanced">
<section v-if="showAdvanced || showGeneralAdvanced" id="sectionAdvanced" aria-labelledby="advancedHeading">
<h2 id="advancedHeading" class="text-blue-500 text-sm font-semibold mb-3">Advanced</h2>
<p class="text-rose-600 mb-8">
Beware: the features here can be confusing and even change data in ways
you do not expect. But we support your freedom!
@@ -606,41 +625,60 @@
<div id="sectionClaimServer">
<h2 class="text-slate-500 text-sm font-bold mt-4">Claim Server</h2>
<div class="px-4 py-4">
<div class="px-4 py-4" role="group" aria-labelledby="claimServerHeading">
<h3 id="claimServerHeading" class="sr-only">Claim Server Configuration</h3>
<label for="apiServerInput" class="sr-only">API Server URL</label>
<input
id="apiServerInput"
v-model="apiServerInput"
type="text"
class="block w-full rounded border border-slate-400 px-4 py-2"
aria-describedby="apiServerDescription"
placeholder="Enter API server URL"
/>
<div
id="apiServerDescription"
class="sr-only"
role="tooltip"
>
Enter the URL for the claim server. You can use the buttons below to quickly set common server URLs.
</div>
<button
v-if="apiServerInput != apiServer"
class="w-full px-4 rounded bg-yellow-500 border border-slate-400"
@click="onClickSaveApiServer()"
aria-label="Save API server URL"
>
<font-awesome
icon="floppy-disk"
class="fa-fw"
color="white"
aria-hidden="true"
></font-awesome>
</button>
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
>
Use Prod
</button>
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
>
Use Test
</button>
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
>
Use Local
</button>
<div class="mt-2" role="group" aria-label="Quick server selection">
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.PROD_ENDORSER_API_SERVER"
aria-label="Use production server URL"
>
Use Prod
</button>
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.TEST_ENDORSER_API_SERVER"
aria-label="Use test server URL"
>
Use Test
</button>
<button
class="px-3 rounded bg-slate-200 border border-slate-400"
@click="apiServerInput = AppConstants.LOCAL_ENDORSER_API_SERVER"
aria-label="Use local server URL"
>
Use Local
</button>
</div>
</div>
<label
@@ -878,8 +916,8 @@
>
View Logs
</router-link>
</div>
</section>
</section>
</main>
</template>
<script lang="ts">