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"> <div class="modal-buttons">
<button <button
class="confirm" class="confirm"
id="confirm-button"
v-if="!errorMessages" v-if="!errorMessages"
@click="emit('confirmAction')" @click="emit('confirmAction')"
> >
{{ $t('buttons.YES') }} {{ $t('buttons.YES') }}
</button> </button>
<button <button
:tabindex="0" tabindex="0"
:id="`${name}-cancel-button`" id="cancel-button"
class="cancel" class="cancel"
@click="emit('cancelAction')" @click="emit('cancelAction')"
> >
@ -37,7 +38,14 @@
</template> </template>
<script setup lang="ts"> <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 { ROOT_STORE } from '@/store/constants'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
@ -46,11 +54,9 @@
title: string title: string
message: string message: string
strongMessage?: string | null strongMessage?: string | null
name?: string | null
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
strongMessage: () => null, strongMessage: () => null,
name: 'modal',
}) })
const emit = defineEmits(['cancelAction', 'confirmAction']) const emit = defineEmits(['cancelAction', 'confirmAction'])
@ -61,7 +67,35 @@
const errorMessages: ComputedRef<string | string[] | null> = computed( const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES] () => 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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