From 2b27c815bc74689237495daa1dea4727031c6f18 Mon Sep 17 00:00:00 2001 From: Ben McGinnis Date: Mon, 23 Oct 2023 17:05:44 -0400 Subject: [PATCH] feat: iframe accessibility This change udpates the iframe-common script to set several important attributes for screen reader's to properly interpret the presence of the iframe. The attributes do the following things: * ariaBusy (aria-busy): notifies the screen reader that the iframe is loading * role=document: tells screen readers to treat the frame contents as a document * aria-live=assertive: tells screen readers to immediately read the frame content * tabindex=0: tabindex is required on document roles to ensure that it is treated as keyboard accessible J=TECHOPS-10193 TEST=manual Manually tested using both Mac VoiceOver and Windows Narrator and validated that the results were read by the screen reader. --- static/js/iframe-common.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/static/js/iframe-common.js b/static/js/iframe-common.js index 2919ab1d1..eb1535f4c 100644 --- a/static/js/iframe-common.js +++ b/static/js/iframe-common.js @@ -9,7 +9,7 @@ const iframeMessageQueue = []; /** * Puts an iframe on the page of an Answers experience and sets up resizing and cross-domain communication - * + * * @param {string} domain The location of the answers experience * @param {AnswersExperienceFrame} answersExperienceFrame */ @@ -65,7 +65,7 @@ export function generateIFrame(domain, answersExperienceFrame) { iframeUrl += '?' + new_params.join('&'); return iframeUrl; }; - + iframe.src = calcFrameSrc(); iframe.frameBorder = 0; @@ -74,8 +74,15 @@ export function generateIFrame(domain, answersExperienceFrame) { iframe.style.minWidth = '100%'; iframe.id = 'answers-frame'; + // for screen reader accessibility + iframe.ariaBusy = "true"; + iframe.setAttribute('role', 'document'); + iframe.setAttribute('aria-live', 'assertive'); + iframe.setAttribute('tabindex', "0"); + // Scroll to the top of the page when the iframe loads or a link is clicked. iframe.addEventListener('load', () => { + iframe.ariaBusy = "false"; document.documentElement.scrollTop = 0; // For Safari document.body.scrollTop = 0; @@ -136,7 +143,7 @@ function registerPopStateListener() { /** * Reloads the iframe with a recalculated src URL. - * Does not reload the iframe if the URL has only changed its hash param. + * Does not reload the iframe if the URL has only changed its hash param. */ function popStateListener() { /** @param {string} url */ @@ -154,7 +161,7 @@ function registerPopStateListener() { /** * Sends data to the answers iframe if possible. Otherwise the message is queued * so that it can be sent when the iframe initializes. - * @param {Object} obj + * @param {Object} obj */ export function sendToIframe (obj) { const iframe = document.querySelector('#answers-frame');