Skip to content

Commit

Permalink
web/satellite/vuetify-poc: improve keyboard navigation for sidebars
Browse files Browse the repository at this point in the history
This change improves keyboard navigation for the Vuetify UI's
navigation sidebars. Navigation items can now be focused with the tab
key and selected with the enter or space key.

Resolves #6411

Change-Id: I6416efbee74209e089abbccd0e1a7f1c0f4b80cb
  • Loading branch information
jewharton authored and Storj Robot committed Oct 18, 2023
1 parent 6ae28e2 commit 4e0ffd1
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 286 deletions.
2 changes: 1 addition & 1 deletion web/satellite/.eslintrc.js
Expand Up @@ -45,7 +45,7 @@ module.exports = {
},
{
'group': 'internal',
'pattern': '@?(poc)/{components,views}/**',
'pattern': '@?(poc)/{components,views,layouts}/**',
'position': 'after',
},
{
Expand Down
1 change: 1 addition & 0 deletions web/satellite/vuetify-poc/src/App.vue
Expand Up @@ -10,6 +10,7 @@
import { onMounted } from 'vue';
import { useConfigStore } from '@/store/modules/configStore';
import Notifications from '@poc/layouts/default/Notifications.vue';
const configStore = useConfigStore();
Expand Down
48 changes: 9 additions & 39 deletions web/satellite/vuetify-poc/src/layouts/default/AccountNav.vue
Expand Up @@ -6,47 +6,35 @@
<v-sheet>
<v-list class="px-2" color="default" variant="flat">
<template v-if="pathBeforeAccountPage">
<v-list-item class="pa-4 rounded-lg" link router-link :to="pathBeforeAccountPage" @click="() => registerLinkClick(pathBeforeAccountPage)">
<navigation-item class="pa-4" title="Go back" :to="pathBeforeAccountPage">
<template #prepend>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 10C1 5.02944 5.02944 0.999999 10 0.999999C14.9706 0.999999 19 5.02944 19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10ZM1.99213 10C1.99213 14.4226 5.57737 18.0079 10 18.0079C14.4226 18.0079 18.0079 14.4226 18.0079 10C18.0079 5.57737 14.4226 1.99213 10 1.99213C5.57737 1.99213 1.99213 5.57737 1.99213 10ZM5.48501 9.73986L5.50374 9.7201L9.01144 6.2124C9.20516 6.01868 9.51925 6.01868 9.71297 6.2124C9.90024 6.39967 9.90648 6.69941 9.7317 6.89418L9.71297 6.91394L7.05211 9.5748L14.4646 9.5748C14.7385 9.5748 14.9606 9.7969 14.9606 10.0709C14.9606 10.3357 14.7531 10.5521 14.4918 10.5662L14.4646 10.5669L7.05211 10.5669L9.71297 13.2278C9.90024 13.4151 9.90648 13.7148 9.7317 13.9096L9.71297 13.9293C9.52571 14.1166 9.22597 14.1228 9.0312 13.9481L9.01144 13.9293L5.50374 10.4216C5.31647 10.2344 5.31023 9.93463 5.48501 9.73986Z" fill="currentColor" />
</svg>
</template>
<v-list-item-title link class="text-body-2 ml-3">
Go back
</v-list-item-title>
</v-list-item>
</navigation-item>

<v-divider class="my-2" />
</template>

<!-- All Projects -->
<v-list-item class="pa-4 rounded-lg" link router-link to="/projects" @click="() => registerLinkClick('/projects')">
<navigation-item title="All Projects" to="/projects">
<template #prepend>
<icon-all-projects />
</template>
<v-list-item-title link class="text-body-2 ml-3">
All Projects
</v-list-item-title>
</v-list-item>
</navigation-item>

<v-list-item link router-link to="settings" class="my-1 py-3" rounded="lg" @click="() => registerLinkClick('/settings')">
<navigation-item title="Account Settings" to="settings">
<template #prepend>
<icon-settings />
</template>
<v-list-item-title class="text-body-2 ml-3">
Account Settings
</v-list-item-title>
</v-list-item>
</navigation-item>

<v-list-item link router-link to="billing" class="my-1" rounded="lg" @click="() => registerLinkClick('/billing')">
<navigation-item title="Account Billing" to="billing">
<template #prepend>
<icon-card />
</template>
<v-list-item-title class="text-body-2 ml-3">
Account Billing
</v-list-item-title>
</v-list-item>
</navigation-item>

<v-divider class="my-2" />
</v-list>
Expand All @@ -60,8 +48,6 @@ import {
VNavigationDrawer,
VSheet,
VList,
VListItem,
VListItemTitle,
VDivider,
} from 'vuetify/components';
import { useDisplay } from 'vuetify';
Expand All @@ -72,6 +58,7 @@ import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import IconCard from '@poc/components/icons/IconCard.vue';
import IconSettings from '@poc/components/icons/IconSettings.vue';
import IconAllProjects from '@poc/components/icons/IconAllProjects.vue';
import NavigationItem from '@poc/layouts/default/NavigationItem.vue';
const analyticsStore = useAnalyticsStore();
const appStore = useAppStore();
Expand All @@ -92,23 +79,6 @@ const pathBeforeAccountPage = computed((): string | null => {
return path;
});
/**
* Conditionally closes the navigation drawer and tracks page visit.
*/
function registerLinkClick(page: string | null): void {
if (mdAndDown.value) {
model.value = false;
}
trackPageVisitEvent(page);
}
/**
* Sends "Page Visit" event to segment and opens link.
*/
function trackPageVisitEvent(page: string | null): void {
if (page) analyticsStore.pageVisit(page);
}
onBeforeMount(() => {
if (mdAndDown.value) {
model.value = false;
Expand Down
56 changes: 56 additions & 0 deletions web/satellite/vuetify-poc/src/layouts/default/NavigationItem.vue
@@ -0,0 +1,56 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.

<template>
<v-list-item link lines="one" :to="to" class="my-1" tabindex="0" @click="onClick" @keydown.space.prevent="onClick">
<template #prepend>
<slot name="prepend" />
</template>
<v-list-item-title class="ml-3">{{ title }}</v-list-item-title>
<v-list-item-subtitle v-if="subtitle" class="ml-3">{{ subtitle }}</v-list-item-subtitle>
<template #append>
<slot name="append" />
</template>
</v-list-item>
</template>

<script setup lang="ts">
import { VListItem, VListItemTitle, VListItemSubtitle } from 'vuetify/components';
import { useDisplay } from 'vuetify';
import { useRouter } from 'vue-router';
import { useAppStore } from '@poc/store/appStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useProjectsStore } from '@/store/modules/projectsStore';
const { mdAndDown } = useDisplay();
const router = useRouter();
const appStore = useAppStore();
const projectsStore = useProjectsStore();
const analyticsStore = useAnalyticsStore();
const props = defineProps<{
title: string;
subtitle?: string;
to?: string;
}>();
/**
* Conditionally closes the navigation drawer and tracks page visit.
*/
function onClick(e: MouseEvent | KeyboardEvent): void {
if (!props.to) return;
const next = router.resolve(props.to).path;
if (next === router.currentRoute.value.path) return;
if (mdAndDown.value) appStore.toggleNavigationDrawer(false);
analyticsStore.pageVisit(next);
// Vuetify handles navigation via click or pressing the Enter key.
// We must handle the space key ourselves.
if ('key' in e && e.key === ' ') router.push(props.to);
}
</script>

0 comments on commit 4e0ffd1

Please sign in to comment.