Skip to content

Commit

Permalink
Add focus state styling for tabbable fields via Plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
dimak1 committed Jul 3, 2024
1 parent 53b62ca commit 81b2260
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 17 deletions.
20 changes: 20 additions & 0 deletions ppr-ui/src/assets/styles/accessibility.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.v-btn:after {
content: none;
}

a:focus-visible,
.v-radio.v-selection-control--focus-visible,
.v-btn:focus-visible,
.tab-focused .v-field {
outline: 2px solid $focus-outline;
outline-offset: 2px;
}

.v-checkbox-btn.v-selection-control--focus-visible .v-selection-control__wrapper {
outline: 2px solid $focus-outline;
border-radius: 50%;
}

.v-icon:focus-visible {
outline-color: $focus-outline;
}
1 change: 1 addition & 0 deletions ppr-ui/src/assets/styles/base.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import 'theme.scss';
@import 'accessibility.scss';

@font-face {
font-family: 'BCSans';
Expand Down
3 changes: 3 additions & 0 deletions ppr-ui/src/assets/styles/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ $app-orange: #f8661a; // same as the Vuetify theme caution
// Sizes for Fonts
$px-14: 0.8750rem;
$px-16: 1.0000rem;

// Accessibility
$focus-outline: #003366;
1 change: 1 addition & 0 deletions ppr-ui/src/components/common/AccountInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
class="mt-n1"
color="primary"
v-bind="props"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
1 change: 1 addition & 0 deletions ppr-ui/src/components/common/RegistrationsWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
color="primary"
v-bind="props"
class="mt-n1"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
30 changes: 15 additions & 15 deletions ppr-ui/src/components/dashboard/DashboardTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
<!-- Tabs -->
<v-tabs
v-model="tabNumber"
height="64"
hideSlider
alignTabs="center"
hide-slider
grow
class="ppr-mhr-tabs"
@update:model-value="onTabChange"
>
<v-tab
:value="0"
class="tab upper-border"
class="tab"
:ripple="false"
:class="{ 'mt-1': isMhrTab }"
tabindex="0"
>
<v-icon
class="mr-2"
Expand All @@ -27,9 +26,9 @@
</v-tab>
<v-tab
:value="1"
class="tab upper-border"
class="tab"
:ripple="false"
:class="{ 'mt-1': isPprTab }"
tabindex="0"
>
<v-icon
class="mr-2"
Expand All @@ -43,7 +42,7 @@
<!-- Window Items -->
<v-window
v-model="tabNumber"
class="rounded-bottom bg-white px-6"
class="rounded-bottom bg-white px-6 mx-1"
>
<v-window-item
:value="0"
Expand Down Expand Up @@ -150,6 +149,13 @@ export default defineComponent({

<style lang="scss" scoped>
@import '@/assets/styles/theme.scss';
.ppr-mhr-tabs {
height: 68px;
display: block;
overflow: visible;
}
.tab {
min-height: 64px !important;
background-color: $BCgovBlue5;
Expand All @@ -158,6 +164,7 @@ export default defineComponent({
letter-spacing: 0;
text-transform: none !important;
border-radius: 4px 4px 0 0!important;
margin: 5px 4px; // margin to make space for outline a11y
&:hover:not(.v-tab--selected) {
background-color: $primary-blue
}
Expand All @@ -167,13 +174,6 @@ export default defineComponent({
color: $gray9;
pointer-events: none;
}
.upper-border {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
min-height: 58px;
max-height: 58px;
margin: 0 2.5px;
}
.v-window {
min-height: 400px
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
color="primary"
v-bind="props"
class="mr-1"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
color="primary"
v-bind="props"
data-test-id="no-certification-tooltip"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@
>
<template #activator="{ props }">
<v-icon
class="circa-tooltip-icon pl-3 mt-4"
class="circa-tooltip-icon ml-1 mt-4"
color="primary"
v-bind="props"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
3 changes: 2 additions & 1 deletion ppr-ui/src/components/tables/SearchHistory.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,9 @@
v-else
color="primary"
size="20"
class="px-8"
class="ml-5"
v-bind="props"
tabindex="0"
>
mdi-information-outline
</v-icon>
Expand Down
2 changes: 2 additions & 0 deletions ppr-ui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getVueRouter } from '@/router'
import { getPiniaStore } from '@/store'
import * as Sentry from '@sentry/vue'
import vuetify from './plugins/vuetify'
import tabFocus from './plugins/tabFocus'
import { vMaska } from 'maska'

// Base App
Expand Down Expand Up @@ -69,6 +70,7 @@ async function start () {
app.use(router)
app.use(pinia)
app.use(vuetify)
app.use(tabFocus)
app.mount('#app')
}

Expand Down
64 changes: 64 additions & 0 deletions ppr-ui/src/plugins/tabFocus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Plugin to modify the VTextField component to add a 'tab-focused' css class when the input
* is focused via the Tab key. This allows to style the v-text-field's input differently
* when it's focused with the keyboard, compared to when it's clicked with the mouse.
*
* The event listeners are cleaned up when the component is unmounted.
*/
export default {
install(app) {
const TAB_KEY = 'Tab'

// Access the VTextField component if it's globally registered
const VTextField = app.component('VTextField')

if (!VTextField) return

// Store the original mount function
const originalMount = VTextField.mounted

// Modify the mounted lifecycle hook
VTextField.mounted = function () {
// Call any existing mounted functions
if (originalMount) originalMount.call(this)

const input = this.$el.querySelector('input')
if (!input) return

const handleKeyDown = event => {
if (event.key === TAB_KEY) {
input.lastKeyPressed = TAB_KEY
}
}

const handleFocus = () => {
if (input.lastKeyPressed === TAB_KEY) {
this.$el.classList.add('tab-focused')
}
input.lastKeyPressed = '' // Reset after check
}

const handleBlur = () => {
this.$el.classList.remove('tab-focused')
}

window.addEventListener('keydown', handleKeyDown)
input.addEventListener('focus', handleFocus)
input.addEventListener('blur', handleBlur)

input.cleanupEventListeners = () => {
window.removeEventListener('keydown', handleKeyDown)
input.removeEventListener('focus', handleFocus)
input.removeEventListener('blur', handleBlur)
}
}

// Modify the unmounted lifecycle hook to remove all event listeners
VTextField.unmounted = function () {
const input = this.$el.querySelector('input')
if (input && input.cleanupEventListeners) {
input.cleanupEventListeners()
}
}
}
}

0 comments on commit 81b2260

Please sign in to comment.