Skip to content

Commit

Permalink
Implement Voicing for ComboBox, see #726 and phetsims/friction#268
Browse files Browse the repository at this point in the history
  • Loading branch information
jessegreenberg committed Oct 29, 2021
1 parent 6b6f489 commit 377f2f8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 5 deletions.
9 changes: 8 additions & 1 deletion js/ComboBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions js/ComboBoxButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class ComboBoxButton extends RectangularPushButton {

// pdom
this.innerContent = item.a11yLabel;
this.voicingNameResponse = item.a11yLabel;
};
property.link( propertyObserver );

Expand Down
6 changes: 3 additions & 3 deletions js/ComboBoxListBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 ] ) ) {

Expand Down
15 changes: 14 additions & 1 deletion js/ComboBoxListItemNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -24,6 +25,8 @@ class ComboBoxListItemNode extends Node {
* @param {number} highlightWidth
* @param {number} highlightHeight
* @param {Object} [options]
*
* @mixes {Voicing}
*/
constructor( item, highlightWidth, highlightHeight, options ) {

Expand Down Expand Up @@ -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, {
Expand Down Expand Up @@ -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 );

Expand All @@ -109,5 +120,7 @@ class ComboBoxListItemNode extends Node {
}
}

Voicing.compose( ComboBoxListItemNode );

sun.register( 'ComboBoxListItemNode', ComboBoxListItemNode );
export default ComboBoxListItemNode;

0 comments on commit 377f2f8

Please sign in to comment.