Client - fix keyboard navigation when modal is displayed

This commit is contained in:
Sam 2023-08-16 19:04:32 +02:00
parent 651d2204b2
commit 94274a5ea2
7 changed files with 45 additions and 50 deletions

View File

@ -16,14 +16,15 @@
<div class="modal-buttons">
<button
class="confirm"
id="confirm-button"
v-if="!errorMessages"
@click="emit('confirmAction')"
>
{{ $t('buttons.YES') }}
</button>
<button
:tabindex="0"
:id="`${name}-cancel-button`"
tabindex="0"
id="cancel-button"
class="cancel"
@click="emit('cancelAction')"
>
@ -37,7 +38,14 @@
</template>
<script setup lang="ts">
import { ComputedRef, computed, toRefs, withDefaults, onUnmounted } from 'vue'
import {
ComputedRef,
computed,
onUnmounted,
onMounted,
toRefs,
withDefaults,
} from 'vue'
import { ROOT_STORE } from '@/store/constants'
import { useStore } from '@/use/useStore'
@ -46,11 +54,9 @@
title: string
message: string
strongMessage?: string | null
name?: string | null
}
const props = withDefaults(defineProps<Props>(), {
strongMessage: () => null,
name: 'modal',
})
const emit = defineEmits(['cancelAction', 'confirmAction'])
@ -61,7 +67,35 @@
const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
onUnmounted(() => store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES))
let confirmButton: HTMLElement | null = null
let cancelButton: HTMLElement | null = null
let previousFocusedElement: Element | null = null
function focusTrap(e: KeyboardEvent) {
if (e.key === 'Tab' || e.keyCode === 9) {
e.preventDefault()
if (document.activeElement?.id === 'cancel-button') {
confirmButton?.focus()
} else {
cancelButton?.focus()
}
}
}
onMounted(() => {
previousFocusedElement = document.activeElement
cancelButton = document.getElementById('cancel-button')
confirmButton = document.getElementById('confirm-button')
if (cancelButton) {
cancelButton.focus()
}
document.addEventListener('keydown', focusTrap)
})
onUnmounted(() => {
store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
document.removeEventListener('keydown', focusTrap)
previousFocusedElement?.focus()
})
</script>
<style lang="scss" scoped>

View File

@ -1,7 +1,7 @@
<template>
<header id="nav">
<Modal
v-show="displayModal"
v-if="displayModal"
:title="$t('common.CONFIRMATION')"
:message="$t('user.LOGOUT_CONFIRMATION')"
@confirmAction="logout"
@ -138,12 +138,6 @@
}
function updateDisplayModal(display: boolean) {
displayModal.value = display
if (display) {
const button = document.getElementById('modal-cancel-button')
if (button) {
button.focus()
}
}
}
</script>

View File

@ -2,7 +2,6 @@
<div id="user-infos" class="description-list">
<Modal
v-if="displayModal"
name="user"
:title="$t('common.CONFIRMATION')"
:message="
displayModal === 'delete'
@ -192,10 +191,6 @@
function updateDisplayModal(value: string) {
displayModal.value = value
if (value !== '') {
const button = document.getElementById('user-cancel-button')
if (button) {
button.focus()
}
store.commit(USERS_STORE.MUTATIONS.UPDATE_IS_SUCCESS, false)
}
}

View File

@ -2,7 +2,6 @@
<div id="user-infos-edition">
<Modal
v-if="displayModal"
name="account"
:title="$t('common.CONFIRMATION')"
:message="$t('user.CONFIRM_ACCOUNT_DELETION')"
@confirmAction="deleteAccount(user.username)"
@ -215,12 +214,6 @@
}
function updateDisplayModal(value: boolean) {
displayModal.value = value
if (displayModal.value) {
const button = document.getElementById('account-cancel-button')
if (button) {
button.focus()
}
}
}
function deleteAccount(username: string) {
store.dispatch(AUTH_USER_STORE.ACTIONS.DELETE_ACCOUNT, { username })

View File

@ -2,7 +2,6 @@
<div id="oauth2-app" class="description-list">
<Modal
v-if="displayModal"
name="app"
:title="$t('common.CONFIRMATION')"
:message="$t(messageToDisplay)"
@confirmAction="confirmAction(client.id)"
@ -180,11 +179,6 @@
displayModal.value = value
if (!value) {
messageToDisplay.value = null
} else {
const button = document.getElementById('app-cancel-button')
if (button) {
button.focus()
}
}
}
function confirmAction(clientId: number) {

View File

@ -45,13 +45,13 @@
<button
id="delete-workout-button"
class="transparent icon-button"
@click="displayDeleteModal"
@click.prevent="displayDeleteModal"
:aria-label="$t(`workouts.DELETE_WORKOUT`)"
>
<i class="fa fa-trash" aria-hidden="true" />
</button>
</div>
<div class="workout-title" v-else>
<div class="workout-title" v-else-if="workoutObject.segmentId">
{{ workoutObject.title }}
<span class="workout-segment">
@ -69,7 +69,7 @@
v-if="workoutObject.type === 'SEGMENT'"
:to="{
name: 'Workout',
params: { workoutId: workoutObject.workoutId }
params: { workoutId: workoutObject.workoutId },
}"
>
> {{ $t('workouts.BACK_TO_WORKOUT') }}
@ -129,8 +129,7 @@
gpxLink.click()
})
}
function displayDeleteModal(event: Event & { target: HTMLInputElement }) {
event.target.blur()
function displayDeleteModal() {
emit('displayModal', true)
}
</script>

View File

@ -2,7 +2,6 @@
<div class="workout-detail">
<Modal
v-if="displayModal"
name="workout"
:title="$t('common.CONFIRMATION')"
:message="$t('workouts.WORKOUT_DELETION_CONFIRMATION')"
@confirmAction="deleteWorkout(workoutObject.workoutId)"
@ -37,7 +36,6 @@
ComputedRef,
Ref,
computed,
nextTick,
ref,
toRefs,
watch,
@ -164,21 +162,9 @@
}
function updateDisplayModal(value: boolean) {
displayModal.value = value
if (displayModal.value) {
nextTick(() => {
const button = document.getElementById('workout-cancel-button')
if (button) {
button.focus()
}
})
}
}
function cancelDelete() {
updateDisplayModal(false)
const button = document.getElementById('delete-workout-button')
if (button) {
button.focus()
}
}
function deleteWorkout(workoutId: string) {
updateDisplayModal(false)