diff --git a/js/ComboBox.js b/js/ComboBox.js index b13744f3..cfb48c11 100644 --- a/js/ComboBox.js +++ b/js/ComboBox.js @@ -168,7 +168,14 @@ class ComboBox extends Node { // @private the popup list box this.listBox = new ComboBoxListBox( property, items, this.hideListBox.bind( this ), // callback to hide the list box - this.button.focus.bind( this.button ), // callback to transfer focus to button + selectedItem => { + + // ComboBoxListBox will focus the button before setting the new Property value so we need to update + // the voicingNameResponse BEFORE moving focus to the button. This workaround makes sure that the correct + // name is ready before the button receives focus. See https://github.com/phetsims/friction/issues/268. + this.button.voicingNameResponse = selectedItem.a11yLabel; + this.button.focus(); + }, options.tandem.createTandem( 'listBox' ), { align: options.align, highlightFill: options.highlightFill, diff --git a/js/ComboBoxButton.js b/js/ComboBoxButton.js index 427b6941..6858f8b4 100644 --- a/js/ComboBoxButton.js +++ b/js/ComboBoxButton.js @@ -182,6 +182,7 @@ class ComboBoxButton extends RectangularPushButton { // pdom this.innerContent = item.a11yLabel; + this.voicingNameResponse = item.a11yLabel; }; property.link( propertyObserver ); diff --git a/js/ComboBoxListBox.js b/js/ComboBoxListBox.js index 7c649b7b..cb096c1f 100644 --- a/js/ComboBoxListBox.js +++ b/js/ComboBoxListBox.js @@ -26,7 +26,7 @@ class ComboBoxListBox extends Panel { * @param {Property} property * @param {ComboBoxItem[]} items * @param {function} hideListBoxCallback - called to hide the list box - * @param {function} focusButtonCallback - called to transfer focus to the combo box's button + * @param {function(ComboBoxItem):} focusButtonCallback - called to transfer focus to the combo box's button * @param {Tandem} tandem * @param {Object} [options] */ @@ -69,7 +69,7 @@ class ComboBoxListBox extends Panel { assert && assert( listItemNode instanceof ComboBoxListItemNode, 'expected a ComboBoxListItemNode' ); // So that something related to the ComboBox has focus before changing Property value. - focusButtonCallback(); + focusButtonCallback( listItemNode.item ); // set value based on which item was chosen in the list box property.value = listItemNode.item.value; @@ -201,7 +201,7 @@ class ComboBoxListBox extends Panel { // Escape and Tab hide the list box and return focus to the button hideListBoxCallback(); - focusButtonCallback(); + focusButtonCallback( this.getListItemNode( property.value ).item ); } else if ( KeyboardUtils.isAnyKeyEvent( event.domEvent, [ KeyboardUtils.KEY_DOWN_ARROW, KeyboardUtils.KEY_UP_ARROW ] ) ) { diff --git a/js/ComboBoxListItemNode.js b/js/ComboBoxListItemNode.js index 8402031d..b7e13231 100644 --- a/js/ComboBoxListItemNode.js +++ b/js/ComboBoxListItemNode.js @@ -10,6 +10,7 @@ import Shape from '../../kite/js/Shape.js'; import merge from '../../phet-core/js/merge.js'; +import Voicing from '../../scenery/js/accessibility/voicing/Voicing.js'; import IndexedNodeIO from '../../scenery/js/nodes/IndexedNodeIO.js'; import Node from '../../scenery/js/nodes/Node.js'; import Rectangle from '../../scenery/js/nodes/Rectangle.js'; @@ -24,6 +25,8 @@ class ComboBoxListItemNode extends Node { * @param {number} highlightWidth * @param {number} highlightHeight * @param {Object} [options] + * + * @mixes {Voicing} */ constructor( item, highlightWidth, highlightHeight, options ) { @@ -59,6 +62,7 @@ class ComboBoxListItemNode extends Node { // pdom: get innerContent from the item assert && assert( options.innerContent === undefined, 'ComboBoxListItemNode sets innerContent' ); options.innerContent = item.a11yLabel; + options.voicingNameResponse = item.a11yLabel; // Highlight that is shown when the pointer is over this item. This is not the a11y focus rectangle. const highlightRectangle = new Rectangle( 0, 0, highlightWidth, highlightHeight, { @@ -92,11 +96,18 @@ class ComboBoxListItemNode extends Node { assert && assert( !options.children, 'ComboBoxListItemNode sets children' ); options.children = [ highlightRectangle, itemNodeWrapper ]; - super( options ); + super(); + + // voicing - initialize the Voicing trait + this.initializeVoicing(); // @public (read-only) this.item = item; + // mutate after initializing the Voicing trait, but must come before setting the focusHighlight so that bounds + // are defined + this.mutate( options ); + // pdom focus highlight is fitted to this Node's bounds, so that it doesn't overlap other items in the list box this.focusHighlight = Shape.bounds( this.localBounds ); @@ -109,5 +120,7 @@ class ComboBoxListItemNode extends Node { } } +Voicing.compose( ComboBoxListItemNode ); + sun.register( 'ComboBoxListItemNode', ComboBoxListItemNode ); export default ComboBoxListItemNode; \ No newline at end of file