From dd290dd070fb7279808afeb9d35017fbb4bb9d19 Mon Sep 17 00:00:00 2001 From: zepumph Date: Tue, 11 Jan 2022 11:39:03 -0700 Subject: [PATCH] remove announcingEmitter, https://github.com/phetsims/utterance-queue/issues/41 --- js/Announcer.js | 1 - js/AriaLiveAnnouncer.js | 51 ++++++++++++++++------------------------- js/Utterance.js | 6 +++++ js/UtteranceTests.js | 9 +++++--- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/js/Announcer.js b/js/Announcer.js index 6e5afef..1cc93dc 100644 --- a/js/Announcer.js +++ b/js/Announcer.js @@ -29,7 +29,6 @@ class Announcer { // @public {Emitter} - Emits an event when this Announcer is finished with an Utterance. It is up // to the Announcer subclass to emit this because different speech technologies may have different APIs // to determine when speaking is finished. - // TODO: This should deprecate AriaLiveAnnouncer.announcingEmitter, see https://github.com/phetsims/utterance-queue/issues/41 this.announcementCompleteEmitter = new Emitter( { parameters: [ { valueType: Utterance } ] } ); diff --git a/js/AriaLiveAnnouncer.js b/js/AriaLiveAnnouncer.js index 16cefa5..5ebb061 100644 --- a/js/AriaLiveAnnouncer.js +++ b/js/AriaLiveAnnouncer.js @@ -26,14 +26,12 @@ * @author John Blanco */ -import Emitter from '../../axon/js/Emitter.js'; import stepTimer from '../../axon/js/stepTimer.js'; import EnumerationDeprecated from '../../phet-core/js/EnumerationDeprecated.js'; import merge from '../../phet-core/js/merge.js'; import platform from '../../phet-core/js/platform.js'; import { PDOMUtils } from '../../scenery/js/imports.js'; import Announcer from './Announcer.js'; -import Utterance from './Utterance.js'; import utteranceQueueNamespace from './utteranceQueueNamespace.js'; // constants @@ -79,11 +77,6 @@ class AriaLiveAnnouncer extends Announcer { this.politeElementIndex = 0; this.assertiveElementIndex = 0; - // @public {null|Emitter} - Emit whenever we announce. - this.announcingEmitter = new Emitter( { - parameters: [ { valueType: 'string' }, { valueType: AriaLiveAnnouncer.AriaLive }, { valueType: Utterance } ] - } ); - // @public (read-only) this.ariaLiveContainer = document.createElement( 'div' ); //container div this.ariaLiveContainer.setAttribute( 'id', `aria-live-elements-${ariaLiveAnnouncerIndex}` ); @@ -102,27 +95,6 @@ class AriaLiveAnnouncer extends Announcer { this.politeElements = Array.from( this.politeElements.children ); this.assertiveElements = Array.from( this.assertiveElements.children ); - // no need to be removed, exists for the lifetime of the simulation. - this.announcingEmitter.addListener( ( textContent, priority, utterance ) => { - - if ( priority === AriaLive.POLITE ) { - const element = this.politeElements[ this.politeElementIndex ]; - this.updateLiveElement( element, textContent, utterance ); - - // update index for next time - this.politeElementIndex = ( this.politeElementIndex + 1 ) % this.politeElements.length; - } - else if ( priority === AriaLive.ASSERTIVE ) { - const element = this.assertiveElements[ this.assertiveElementIndex ]; - this.updateLiveElement( element, textContent, utterance ); - // update index for next time - this.assertiveElementIndex = ( this.assertiveElementIndex + 1 ) % this.assertiveElements.length; - } - else { - assert && assert( false, 'unsupported aria live prioirity' ); - } - } ); - // increment index so the next AriaLiveAnnouncer instance has different ids for its elements. ariaLiveAnnouncerIndex++; } @@ -144,8 +116,25 @@ class AriaLiveAnnouncer extends Announcer { }, options ); // Note that getTextToAlert will have side effects on the Utterance as the Utterance - // may have have logic that changes its alert content each time it is used - this.announcingEmitter.emit( utterance.getTextToAlert( this.respectResponseCollectorProperties ), options.ariaLivePriority, utterance ); + // may have logic that changes its alert content each time it is used + const textContent = utterance.getTextToAlert( this.respectResponseCollectorProperties ); + + if ( options.ariaLivePriority === AriaLive.POLITE ) { + const element = this.politeElements[ this.politeElementIndex ]; + this.updateLiveElement( element, textContent, utterance ); + + // update index for next time + this.politeElementIndex = ( this.politeElementIndex + 1 ) % this.politeElements.length; + } + else if ( options.ariaLivePriority === AriaLive.ASSERTIVE ) { + const element = this.assertiveElements[ this.assertiveElementIndex ]; + this.updateLiveElement( element, textContent, utterance ); + // update index for next time + this.assertiveElementIndex = ( this.assertiveElementIndex + 1 ) % this.assertiveElements.length; + } + else { + assert && assert( false, 'unsupported aria live prioirity' ); + } // With aria-live we don't have information about when the screen reader is done speaking // the content, so we have to emit this right away @@ -199,7 +188,7 @@ class AriaLiveAnnouncer extends Announcer { } // @public - Possible values for the `aria-live` attribute (priority) that can be alerted (like "polite" and -// "assertive"), see AriaLiveAnnouncer.announcingEmitter for details. +// "assertive"), see AriaLiveAnnouncer.announce options for details. AriaLiveAnnouncer.AriaLive = AriaLive; utteranceQueueNamespace.register( 'AriaLiveAnnouncer', AriaLiveAnnouncer ); diff --git a/js/Utterance.js b/js/Utterance.js index 3536242..26a7f49 100644 --- a/js/Utterance.js +++ b/js/Utterance.js @@ -125,6 +125,10 @@ class Utterance { // @public - observable for the priority, can be set to change the priority of this Utterance // while it is still in the UtteranceQueue. See options documentation for behavior of priority. this.priorityProperty = new NumberProperty( options.priority ); + + // @public (read-only) {string|null}l - the previous value of "getAlertText", which formulates the alert into a + // string, depending on criteria. + this.previousAlertText = null; } /** @@ -171,6 +175,7 @@ class Utterance { alert = this.getAlertStringFromResponsePacket( alert, respectResponseCollectorProperties ); } + this.previousAlertText = alert; return alert; } @@ -237,6 +242,7 @@ class Utterance { */ reset() { this.numberOfTimesAlerted = 0; + this.previousAlertText = null; } } diff --git a/js/UtteranceTests.js b/js/UtteranceTests.js index 0d55931..8ec8fc7 100644 --- a/js/UtteranceTests.js +++ b/js/UtteranceTests.js @@ -38,8 +38,8 @@ QUnit.module( 'Utterance', { }, timerInterval * 1000 ); // whenever announcing, get a callback and populate the alerts array - ariaLiveAnnouncer.announcingEmitter.addListener( text => { - alerts.unshift( text ); + ariaLiveAnnouncer.announcementCompleteEmitter.addListener( utterance => { + alerts.unshift( utterance.previousAlertText ); } ); // slightly slower than the interval that the utteranceQueue will wait so we don't have a race condition @@ -75,6 +75,10 @@ QUnit.test( 'Basic Utterance testing', async assert => { utteranceQueue.addToBack( utterance ); await timeout( sleepTiming ); assert.ok( alerts[ 0 ] === otherAlert, 'second alert made it to ariaLiveAnnouncer' ); + + utterance.reset(); + assert.ok( utterance.numberOfTimesAlerted === 0, 'numberOfTimesAlerted reset' ); + assert.ok( utterance.previousAlertText === null, 'previousAlertText reset' ); } ); QUnit.test( 'Utterance options', async assert => { @@ -213,7 +217,6 @@ QUnit.test( 'alertMaximumDelay tests', async assert => { await timeout( 150 ); assert.ok( utteranceQueue.queue.length === 0, 'not stable, but past max' ); assert.ok( alerts[ 0 ] === rapidlyChanging, 'it was announced' ); - } ); QUnit.test( 'announceImmediately', async assert => {