-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement some logic to ensure that aria-live messages, that are send in polite mode, do not interrupt the user, while typing some text into a text-element. The problem occurs because the CK5-Editor is not recognized as an input field by the browsers and by that the user interactions in it are not handled the same way as if he/she would type in a normal textfield. The solution that was implemented... - switches the notifier into a queueing mode that collects all messages that are generated - considers every key being pressed in the CK5-Editor as an interaction that should delay the notifications - switches back to write mode after 1500ms have passed without the user pressing any key in the CK5-Editor - writes all messages when being in write-mode --------- Co-authored-by: Murat Merdoglu <murat.merdoglu@dataport.de>
- Loading branch information
1 parent
9d6e81c
commit fecb37c
Showing
6 changed files
with
139 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,94 @@ | ||
import { computed, ref } from "vue"; | ||
import { useDebounceFn } from "@vueuse/core"; | ||
|
||
type Importance = "polite" | "assertive"; | ||
let mode: "write" | "queue" = "write"; | ||
const notifications = ref({ | ||
polite: <string[]>[], | ||
assertive: <string[]>[], | ||
}); | ||
let handle: NodeJS.Timeout | null = null; | ||
|
||
export const useAriaLiveNotifier = () => { | ||
const numberOfNotifications = computed( | ||
() => | ||
notifications.value["polite"].length + | ||
notifications.value["assertive"].length | ||
); | ||
|
||
const notifyOnScreenReader = ( | ||
message: string, | ||
importance: "off" | "polite" | "assertive" = "polite" | ||
importance: Importance = "polite" | ||
) => { | ||
// should be a div with aria-live="polite | assertive" attribute | ||
// and should be appended to the upper level of the DOM tree | ||
// the aria-live attribute should be set polite or assertive based on the importance of the message | ||
const element = document.getElementById( | ||
importance === "polite" | ||
? "notify-screen-reader-polite" | ||
: "notify-screen-reader-assertive" | ||
); | ||
notifications.value[importance].push(message); | ||
handleNotificationWriting(); | ||
}; | ||
|
||
if (!element) { | ||
console.error( | ||
`Element with id 'notify-screen-reader-${importance}' not found` | ||
); | ||
return; | ||
const ensurePoliteNotifications = () => { | ||
mode = "queue"; | ||
processNotificationsDebounced(); | ||
}; | ||
|
||
const processNotificationsDebounced = useDebounceFn( | ||
() => { | ||
mode = "write"; | ||
handleNotificationWriting(); // explicit call needed for test | ||
}, | ||
1500, | ||
{ maxWait: 30000 } | ||
); | ||
|
||
const handleNotificationWriting = () => { | ||
if (numberOfNotifications.value > 0) { | ||
if (mode === "write") { | ||
stopPeriodicRetry(); | ||
writeAllNotifications(); | ||
return; | ||
} else { | ||
startPeriodicRetry(); | ||
} | ||
} | ||
}; | ||
|
||
const startPeriodicRetry = () => { | ||
if (handle === null) { | ||
handle = setInterval(handleNotificationWriting, 1000); | ||
} | ||
}; | ||
|
||
const stopPeriodicRetry = () => { | ||
if (handle) { | ||
clearInterval(handle); | ||
handle = null; | ||
} | ||
}; | ||
|
||
const writeAllNotifications = () => { | ||
writeNotifications("polite"); | ||
writeNotifications("assertive"); | ||
}; | ||
|
||
const writeNotifications = (importance: Importance) => { | ||
const element = getElement(importance); | ||
|
||
if (element && notifications.value[importance].length > 0) { | ||
element.innerHTML = notifications.value[importance] | ||
.map((m) => `<span>${m}</span>`) | ||
.join(""); | ||
notifications.value[importance] = []; | ||
} | ||
}; | ||
|
||
const getElement = (importance: Importance): HTMLElement | null => { | ||
const element = document.getElementById( | ||
`notify-screen-reader-${importance}` | ||
); | ||
|
||
element.innerHTML += `<span>${message}</span>`; | ||
return element; | ||
}; | ||
|
||
return { | ||
notifyOnScreenReader, | ||
ensurePoliteNotifications, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters