-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automatically discover a11y text #1526
Comments
It seems like a small amount of effort could dramatically improve our screen reader coverage. But this issue seems like it is complex enough that it should be moved to an iteration + subteam. So I'll propose it on the upcoming priorities board, and unassign myself. |
This is a great idea @samreid and could lead to knowledge sharing around creating names for objects that work well in both the visual space and the described space. Totally, agree that default on-screen text is better than no text and a step in the right direction. |
@terracoda indicated difficulty in designing the keyboard traversal order and separating play area vs control area (see phetsims/projectile-data-lab#107) in Projectile Data Lab because the a11y text is all blank: Here is a patch that takes information from the PhET-iO IDs to populate the a11y view: Subject: [PATCH] Pass launchButtonEnabledProperty directly to the launchButton.enabledProperty, see https://github.com/phetsims/projectile-data-lab/issues/141
---
Index: js/accessibility/pdom/ParallelDOM.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/accessibility/pdom/ParallelDOM.ts b/js/accessibility/pdom/ParallelDOM.ts
--- a/js/accessibility/pdom/ParallelDOM.ts (revision 42fefc31fc2910edfd75a72c69e33e78b088c84a)
+++ b/js/accessibility/pdom/ParallelDOM.ts (date 1707927532191)
@@ -141,6 +141,7 @@
import TProperty from '../../../../axon/js/TProperty.js';
import isSettingPhetioStateProperty from '../../../../tandem/js/isSettingPhetioStateProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
+import { animationFrameTimer } from '../../../../axon/js/imports.js';
const INPUT_TAG = PDOMUtils.TAGS.INPUT;
const P_TAG = PDOMUtils.TAGS.P;
@@ -632,6 +633,16 @@
this.focusHighlightChangedEmitter = new TinyEmitter();
this.pdomDisplaysEmitter = new TinyEmitter();
this.pdomBoundInputEnabledListener = this.pdomInputEnabledListener.bind( this );
+
+ animationFrameTimer.setTimeout( () => {
+ if ( this.accessibleName === null && this.tandem && this.tandem.supplied ) {
+ let text = this.phetioID.substring( this.phetioID.lastIndexOf( '.' ) + 1 );
+ if ( text.endsWith( 'RadioButton' ) ) {
+ text = text.substring( 0, text.length - 'RadioButton'.length );
+ }
+ this.accessibleName = text;
+ }
+ }, 1000 );
}
/***********************************************************************************************************/
With this patch, the a11y view looks like this: Would be good to get thoughts from @terracoda about whether this is helpful enough or from @jessegreenberg about how we might bring an idea like this to production (even if only for team/development purposes). |
Thanks @samreid, I'll look again at the A11y View. |
The proposal above is not committed, it is a proposal to get the ball rolling toward a committable solution. Does it seem useful? |
I think it could be useful. I see some useful overlap with the description design tool. It pre-populates the accessible name with something unique and human readable. |
Upgrading to very useful. |
Yes, cool idea! This seems great for internal use, I could see adding a query parameter that does this. Thinking about the best place to put it, maybe somewhere in joist/SimDisplay? Like maybe we could override I will add this to the alt-input Monday board and there we can determine a priority for adding this (unless it gets added organically sooner though I don't think I will have time for a while). |
Adding to the Monday board and scheduling for a future subteam sounds good. Some questions for then:
|
The alt-input management team discussed this today and we would like to loop this into the greater description work: |
Meeting 3/11/24
|
@samreid @zepumph and I worked on this as part of phetsims/buoyancy#109 and they wrote a patch for doing this automatically in some sun components. Leaving it here in case it's useful for future work Subject: [PATCH] Pass launchButtonEnabledProperty directly to the launchButton.enabledProperty, see https://github.com/phetsims/projectile-data-lab/issues/141
---
Index: sun/js/Checkbox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/Checkbox.ts b/sun/js/Checkbox.ts
--- a/sun/js/Checkbox.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/Checkbox.ts (date 1715911723171)
@@ -121,6 +121,7 @@
tagName: 'input',
inputType: 'checkbox',
appendDescription: true,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'Checkbox' ),
// voicing
voicingCheckedObjectResponse: null,
Index: sun/js/ComboBoxListItemNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/ComboBoxListItemNode.ts b/sun/js/ComboBoxListItemNode.ts
--- a/sun/js/ComboBoxListItemNode.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/ComboBoxListItemNode.ts (date 1715912676366)
@@ -69,6 +69,7 @@
tagName: 'li',
focusable: true,
ariaRole: 'option',
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'Item' ),
// the `li` with ariaRole `option` does not get click events on iOS VoiceOver, so position
// elements so they receive pointer events
Index: sun/js/ComboBoxButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/ComboBoxButton.ts b/sun/js/ComboBoxButton.ts
--- a/sun/js/ComboBoxButton.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/ComboBoxButton.ts (date 1715912953495)
@@ -105,7 +105,8 @@
// pdom
containerTagName: 'div',
labelTagName: 'p', // NOTE: A `span` causes duplicate name-speaking with VO+safari in https://github.com/phetsims/ratio-and-proportion/issues/532
- accessibleNameBehavior: ACCESSIBLE_NAME_BEHAVIOR
+ accessibleNameBehavior: ACCESSIBLE_NAME_BEHAVIOR,
+ // accessibleName: Tandem.toAccessibleName( providedOptions, 'ComboBoxButton' )
}, providedOptions );
assert && assert( _.includes( ALIGN_VALUES, options.align ),
Index: sun/js/AccordionBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/AccordionBox.ts b/sun/js/AccordionBox.ts
--- a/sun/js/AccordionBox.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/AccordionBox.ts (date 1715912565653)
@@ -198,6 +198,7 @@
tagName: 'div',
headingTagName: 'h3', // specify the heading that this AccordionBox will be, TODO: use this.headingLevel when no longer experimental https://github.com/phetsims/scenery/issues/855
accessibleNameBehavior: AccordionBox.ACCORDION_BOX_ACCESSIBLE_NAME_BEHAVIOR,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'AccordionBox' ),
// voicing
voicingNameResponse: null,
Index: sun/js/ComboBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/ComboBox.ts b/sun/js/ComboBox.ts
--- a/sun/js/ComboBox.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/ComboBox.ts (date 1715913290569)
@@ -244,6 +244,7 @@
buttonLabelTagName: 'p',
accessibleNameBehavior: ACCESSIBLE_NAME_BEHAVIOR,
helpTextBehavior: HELP_TEXT_BEHAVIOR,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'ComboBox' ),
comboBoxVoicingNameResponsePattern: SunConstants.VALUE_NAMED_PLACEHOLDER,
comboBoxVoicingContextResponse: null,
@@ -565,6 +566,9 @@
else if ( typeof item.a11yName === 'string' ) {
property = new TinyProperty( item.a11yName );
}
+ else if ( item.tandemName ) {
+ property = new TinyProperty( Tandem.tandemNameToAccessibleName( item.tandemName, 'Item' ) );
+ }
else {
property = new TinyProperty( null );
}
Index: sun/js/AquaRadioButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/AquaRadioButton.ts b/sun/js/AquaRadioButton.ts
--- a/sun/js/AquaRadioButton.ts (revision 3cd5c702159e13688afd1b0b9da468f129ccee71)
+++ b/sun/js/AquaRadioButton.ts (date 1715912110736)
@@ -119,7 +119,8 @@
containerTagName: 'li',
labelTagName: 'label',
appendLabel: true,
- appendDescription: true
+ appendDescription: true,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'RadioButton' )
}, providedOptions );
Index: density-buoyancy-common/js/common/view/BuoyancyDisplayOptionsPanel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/density-buoyancy-common/js/common/view/BuoyancyDisplayOptionsPanel.ts b/density-buoyancy-common/js/common/view/BuoyancyDisplayOptionsPanel.ts
--- a/density-buoyancy-common/js/common/view/BuoyancyDisplayOptionsPanel.ts (revision f4549040101bae01a024fc86025259d7e038e20d)
+++ b/density-buoyancy-common/js/common/view/BuoyancyDisplayOptionsPanel.ts (date 1715912187354)
@@ -136,7 +136,7 @@
tandem: options.tandem.createTandem( 'massesCheckbox' )
}, checkboxOptions ) ),
new Checkbox( model.showForceValuesProperty, new Text( DensityBuoyancyCommonStrings.forceValuesStringProperty, labelOptions ), combineOptions<CheckboxOptions>( {
- tandem: options.tandem.createTandem( 'forcesCheckbox' )
+ tandem: options.tandem.createTandem( 'forceValuesCheckbox' )
}, checkboxOptions ) ),
...( model.supportsDepthLines ?
[ new Checkbox( model.showDepthLinesProperty, new Text( DensityBuoyancyCommonStrings.depthLinesStringProperty, labelOptions ), combineOptions<CheckboxOptions>( {
Index: tandem/js/Tandem.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tandem/js/Tandem.ts b/tandem/js/Tandem.ts
--- a/tandem/js/Tandem.ts (revision 27c3e2c9daa7165780d04465016c2e898a96c192)
+++ b/tandem/js/Tandem.ts (date 1715913246619)
@@ -11,9 +11,10 @@
import arrayRemove from '../../phet-core/js/arrayRemove.js';
import merge from '../../phet-core/js/merge.js';
import optionize from '../../phet-core/js/optionize.js';
-import PhetioObject from './PhetioObject.js';
+import PhetioObject, { PhetioObjectOptions } from './PhetioObject.js';
import TandemConstants, { PhetioID } from './TandemConstants.js';
import tandemNamespace from './tandemNamespace.js';
+import PickOptional from '../../phet-core/js/types/PickOptional.js';
// constants
// Tandem can't depend on joist, so cannot use packageJSON module
@@ -597,6 +598,29 @@
* Use this as the parent tandem for Properties that are related to sim-specific preferences.
*/
public static readonly PREFERENCES = Tandem.GLOBAL_MODEL.createTandem( 'preferences' );
+
+ /**
+ * Tandem names can be used to create accessible names for screen readers. This method will convert a tandem name to
+ * a human-readable name. For example, 'resetAllButton' would become 'Reset All'.
+ */
+ public static toAccessibleName( providedOptions: PickOptional<PhetioObjectOptions, 'tandem'> | undefined, suffix: string ): string | null {
+ if ( providedOptions && providedOptions.tandem ) {
+ return Tandem.tandemNameToAccessibleName( providedOptions.tandem.name, suffix );
+ }
+ return null;
+ }
+
+ public static tandemNameToAccessibleName( tandemName: string, suffix: string ): string | null {
+ assert && assert( tandemName.toLowerCase().endsWith( suffix.toLowerCase() ), `suffix should be at the end of the tandem name: ${tandemName}` );
+
+ // trim the suffix
+ const withoutSuffix = tandemName.slice( 0, -suffix.length );
+
+ const whitespaceName = withoutSuffix.replace( /([A-Z])/g, ' $1' ).trim();
+
+ // capitalize the first letter of each word, no matter how many words
+ return whitespaceName.replace( /\b\w/g, c => c.toUpperCase() );
+ }
}
Tandem.addLaunchListener( () => { |
In addition to the above, it does seem that
|
I really would like to apply some form of this patch, but there are too many cases where common code is using the older style of labelContent/innerContent for the accessible name. We can't just add accessibleName as the default, because it doesn't get overwritten in a standard way. My guess is that I could get to a commit point in 2 hours, but it would involve making pretty error prone changes to get more consistent to having common code set default accessible names. Subject: [PATCH] Pass launchButtonEnabledProperty directly to the launchButton.enabledProperty, see https://github.com/phetsims/projectile-data-lab/issues/141
---
Index: area-model-common/js/common/view/AreaScreenView.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/area-model-common/js/common/view/AreaScreenView.js b/area-model-common/js/common/view/AreaScreenView.js
--- a/area-model-common/js/common/view/AreaScreenView.js (revision 1241c8db48406403bb2f81ad204a2d47355911b1)
+++ b/area-model-common/js/common/view/AreaScreenView.js (date 1719857314680)
@@ -137,8 +137,7 @@
contentYSpacing: model.allowExponents ? 5 : 8,
// pdom
- labelTagName: 'h3',
- labelContent: AreaModelCommonStrings.a11y.factorsBoxStringProperty,
+ accessibleName: AreaModelCommonStrings.a11y.factorsBoxStringProperty,
titleBarOptions: {
descriptionContent: AreaModelCommonStrings.a11y.factorsBoxDescriptionStringProperty
}
Index: masses-and-springs/js/common/view/SpringScreenView.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/masses-and-springs/js/common/view/SpringScreenView.js b/masses-and-springs/js/common/view/SpringScreenView.js
--- a/masses-and-springs/js/common/view/SpringScreenView.js (revision 927626152eaf1c63178fe4006b8ecf636164adf5)
+++ b/masses-and-springs/js/common/view/SpringScreenView.js (date 1719855829019)
@@ -273,7 +273,7 @@
*/
createStopperButton( spring, tandem ) {
return new StopperButtonNode(
- tandem.createTandem( 'secondSpringStopperButtonNode' ), {
+ tandem.createTandem( 'secondSpringStopperButton' ), {
listener: () => {
spring.stopSpring();
},
Index: sun/js/AquaRadioButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/AquaRadioButton.ts b/sun/js/AquaRadioButton.ts
--- a/sun/js/AquaRadioButton.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/AquaRadioButton.ts (date 1719857020681)
@@ -119,10 +119,14 @@
containerTagName: 'li',
labelTagName: 'label',
appendLabel: true,
- appendDescription: true
+ appendDescription: true,
+ labelContent: Tandem.toAccessibleName( providedOptions, 'RadioButton' )
}, providedOptions );
+ if ( options.hasOwnProperty( 'accessibleName' ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for aquaRadioButton' );
+ }
super();
this.value = value;
Index: scenery-phet/js/buttons/ResetAllButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/scenery-phet/js/buttons/ResetAllButton.ts b/scenery-phet/js/buttons/ResetAllButton.ts
--- a/scenery-phet/js/buttons/ResetAllButton.ts (revision e1d063709def87011597a2edcaf12a6f465efc9c)
+++ b/scenery-phet/js/buttons/ResetAllButton.ts (date 1719856960491)
@@ -61,7 +61,7 @@
soundPlayer: resetAllSoundPlayer,
// pdom
- innerContent: SceneryPhetStrings.a11y.resetAll.labelStringProperty,
+ accessibleName: SceneryPhetStrings.a11y.resetAll.labelStringProperty,
// voicing
voicingNameResponse: SceneryPhetStrings.a11y.resetAll.labelStringProperty,
Index: tandem/js/Tandem.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tandem/js/Tandem.ts b/tandem/js/Tandem.ts
--- a/tandem/js/Tandem.ts (revision eb8d2f1587c2bb63481ca4cec19c6d27bfc1464b)
+++ b/tandem/js/Tandem.ts (date 1719856150660)
@@ -614,10 +614,12 @@
* a human-readable name. For example, 'resetAllButton' would become 'Reset All'.
*/
public static tandemNameToAccessibleName( tandemName: string, suffix: string ): string | null {
- assert && assert( tandemName.toLowerCase().endsWith( suffix.toLowerCase() ), `suffix should be at the end of the tandem name: ${tandemName}` );
// trim the suffix
- const withoutSuffix = tandemName.slice( 0, -suffix.length );
+ let withoutSuffix = tandemName;
+ if ( tandemName.toLowerCase().endsWith( suffix.toLowerCase() ) ) {
+ withoutSuffix = tandemName.slice( 0, -suffix.length );
+ }
const whitespaceName = withoutSuffix.replace( /([A-Z])/g, ' $1' ).trim();
Index: sun/js/buttons/RectangularRadioButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/buttons/RectangularRadioButton.ts b/sun/js/buttons/RectangularRadioButton.ts
--- a/sun/js/buttons/RectangularRadioButton.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/buttons/RectangularRadioButton.ts (date 1719857140461)
@@ -94,7 +94,7 @@
containerTagName: 'li',
appendDescription: true,
appendLabel: true,
- accessibleName: Tandem.toAccessibleName( providedOptions, 'RadioButton' ),
+ labelContent: Tandem.toAccessibleName( providedOptions, 'RadioButton' ),
// phet-io
tandem: Tandem.REQUIRED,
Index: sun/js/ComboBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/ComboBox.ts b/sun/js/ComboBox.ts
--- a/sun/js/ComboBox.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/ComboBox.ts (date 1719856936006)
@@ -244,6 +244,7 @@
buttonLabelTagName: 'p',
accessibleNameBehavior: ACCESSIBLE_NAME_BEHAVIOR,
helpTextBehavior: HELP_TEXT_BEHAVIOR,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'ComboBox' ),
comboBoxVoicingNameResponsePattern: SunConstants.VALUE_NAMED_PLACEHOLDER,
comboBoxVoicingContextResponse: null,
@@ -259,6 +260,9 @@
phetioEnabledPropertyInstrumented: true // opt into default PhET-iO instrumented enabledProperty
}, providedOptions );
+ if ( options.hasOwnProperty( 'labelContent' ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for combo' );
+ }
const nodes = getGroupItemNodes( items, options.tandem.createTandem( 'items' ) );
assert && nodes.forEach( node => {
@@ -561,6 +565,9 @@
else if ( typeof item.a11yName === 'string' ) {
property = new TinyProperty( item.a11yName );
}
+ else if ( item.tandemName ) {
+ property = new TinyProperty( Tandem.tandemNameToAccessibleName( item.tandemName, 'Item' ) );
+ }
else {
property = new TinyProperty( null );
}
Index: sun/js/buttons/TextPushButton.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/buttons/TextPushButton.ts b/sun/js/buttons/TextPushButton.ts
--- a/sun/js/buttons/TextPushButton.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/buttons/TextPushButton.ts (date 1719857070232)
@@ -38,7 +38,7 @@
// RectangularPushButtonOptions
tandem: Tandem.REQUIRED,
- innerContent: string
+ accessibleName: string
}, providedOptions );
const text = new Text( string, combineOptions<TextOptions>( {
Index: sun/js/buttons/ButtonNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/buttons/ButtonNode.ts b/sun/js/buttons/ButtonNode.ts
--- a/sun/js/buttons/ButtonNode.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/buttons/ButtonNode.ts (date 1719857268827)
@@ -23,6 +23,8 @@
import TButtonAppearanceStrategy, { TButtonAppearanceStrategyOptions } from './TButtonAppearanceStrategy.js';
import TContentAppearanceStrategy, { TContentAppearanceStrategyOptions } from './TContentAppearanceStrategy.js';
import TinyProperty from '../../../axon/js/TinyProperty.js';
+import Tandem from '../../../tandem/js/Tandem.js';
+import inheritance from '../../../phet-core/js/inheritance.js';
// constants
const CONTRAST_FILTER = new Contrast( 0.7 );
@@ -123,6 +125,8 @@
buttonAppearanceStrategyOptions: {},
contentAppearanceStrategy: null,
contentAppearanceStrategyOptions: {},
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'Button' ),
+
enabledAppearanceStrategy: ( enabled, button, background, content ) => {
background.filters = enabled ? [] : [ CONTRAST_FILTER, BRIGHTNESS_FILTER ];
@@ -151,6 +155,9 @@
options.enabledProperty = buttonModel.enabledProperty;
super();
+ if ( ( inheritance( this ).map( x => x.name ).includes( 'RectangularRadioButton' ) && options.hasOwnProperty( 'labelContent' ) ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for button' );
+ }
this.content = options.content;
this.buttonModel = buttonModel;
Index: sun/js/ComboBoxListItemNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/ComboBoxListItemNode.ts b/sun/js/ComboBoxListItemNode.ts
--- a/sun/js/ComboBoxListItemNode.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/ComboBoxListItemNode.ts (date 1719856936011)
@@ -69,6 +69,7 @@
tagName: 'li',
focusable: true,
ariaRole: 'option',
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'Item' ),
// the `li` with ariaRole `option` does not get click events on iOS VoiceOver, so position
// elements so they receive pointer events
@@ -89,6 +90,9 @@
visiblePropertyOptions: { phetioFeatured: true }
}, providedOptions );
+ if ( options.hasOwnProperty( 'labelContent' ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for combobox button' );
+ }
// @ts-expect-error convert Property into string
options.comboBoxVoicingNameResponsePattern = options.comboBoxVoicingNameResponsePattern.get ?
// @ts-expect-error convert Property into string
Index: sun/js/Checkbox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/Checkbox.ts b/sun/js/Checkbox.ts
--- a/sun/js/Checkbox.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/Checkbox.ts (date 1719856936021)
@@ -121,6 +121,7 @@
tagName: 'input',
inputType: 'checkbox',
appendDescription: true,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'Checkbox' ),
// voicing
voicingCheckedObjectResponse: null,
@@ -133,6 +134,9 @@
voiceNameResponseOnSelection: true
}, providedOptions );
+ if ( options.hasOwnProperty( 'labelContent' ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for checkbox' );
+ }
super();
// sends out notifications when the checkbox is toggled.
Index: sun/js/AccordionBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/sun/js/AccordionBox.ts b/sun/js/AccordionBox.ts
--- a/sun/js/AccordionBox.ts (revision eec0449cc1afd5b7885682250a167ae4d472e700)
+++ b/sun/js/AccordionBox.ts (date 1719856935998)
@@ -198,6 +198,7 @@
tagName: 'div',
headingTagName: 'h3', // specify the heading that this AccordionBox will be, TODO: use this.headingLevel when no longer experimental https://github.com/phetsims/scenery/issues/855
accessibleNameBehavior: AccordionBox.ACCORDION_BOX_ACCESSIBLE_NAME_BEHAVIOR,
+ accessibleName: Tandem.toAccessibleName( providedOptions, 'AccordionBox' ),
// voicing
voicingNameResponse: null,
@@ -218,6 +219,9 @@
}
}, providedOptions );
+ if ( options.hasOwnProperty( 'labelContent' ) || options.hasOwnProperty( 'innerContent' ) ) {
+ throw new Error( 'inner content for accordionbox' );
+ }
// expandCollapseButtonOptions defaults
options.expandCollapseButtonOptions = combineOptions<ExpandCollapseButtonOptions>( {
sideLength: 16, // button is a square, this is the length of one side |
Concerns about "discovering" accessible names from tandem names, and specific problems caused in RectangularRadioButtonGroup are noted in phetsims/sun#904. |
Here is an untested idea for discovering the translatable text for a checkbox: Subject: [PATCH] Pass repo to check, see https://github.com/phetsims/perennial/issues/364
---
Index: js/Checkbox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/Checkbox.ts b/js/Checkbox.ts
--- a/js/Checkbox.ts (revision 1e2dd5869880fd41672adeeba92aa300f40b410e)
+++ b/js/Checkbox.ts (date 1729696723066)
@@ -14,7 +14,7 @@
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import optionize from '../../phet-core/js/optionize.js';
import StrictOmit from '../../phet-core/js/types/StrictOmit.js';
-import { assertNoAdditionalChildren, FireListener, LayoutConstraint, Node, NodeOptions, Path, Rectangle, SceneryConstants, TPaint, Voicing, VoicingOptions, WidthSizable, WidthSizableOptions } from '../../scenery/js/imports.js';
+import { assertNoAdditionalChildren, FireListener, LayoutConstraint, Node, NodeOptions, Path, Rectangle, RichText, Text, SceneryConstants, TPaint, Voicing, VoicingOptions, WidthSizable, WidthSizableOptions } from '../../scenery/js/imports.js';
import checkEmptySolidShape from '../../sherpa/js/fontawesome-4/checkEmptySolidShape.js';
import checkSquareOSolidShape from '../../sherpa/js/fontawesome-4/checkSquareOSolidShape.js';
import sharedSoundPlayers from '../../tambo/js/sharedSoundPlayers.js';
@@ -25,6 +25,7 @@
import Tandem from '../../tandem/js/Tandem.js';
import Utterance, { TAlertable } from '../../utterance-queue/js/Utterance.js';
import sun from './sun.js';
+import TReadOnlyProperty from '../../axon/js/TReadOnlyProperty.js';
// constants
const BOOLEAN_VALIDATOR = { valueType: 'boolean' };
@@ -86,6 +87,26 @@
public constructor( property: PhetioProperty<boolean>, content: Node, providedOptions?: CheckboxOptions ) {
+ // recursive search over content.children to find any Text
+ // if found, set the accessibleName of the Checkbox to the text of the first Text found
+ const findText = ( node: Node ): string | null | TReadOnlyProperty<string> => {
+ if ( node instanceof Text ) {
+ return node.stringProperty;
+ }
+ else if ( node instanceof RichText ) {
+ return node.stringProperty;
+ }
+ else if ( node.children ) {
+ for ( const child of node.children ) {
+ const text = findText( child );
+ if ( text ) {
+ return text;
+ }
+ }
+ }
+ return null;
+ };
+
const options = optionize<CheckboxOptions, SelfOptions, ParentOptions>()( {
// CheckboxOptions
@@ -129,7 +150,9 @@
// the voicingContextResponse
checkedContextResponse: null,
uncheckedContextResponse: null,
- voiceNameResponseOnSelection: true
+ voiceNameResponseOnSelection: true,
+
+ accessibleName: findText( content )
}, providedOptions );
super(); |
In today's pose meeting, we discussed the importance of having |
This issue is kind of blocked with on #1526 (comment). But we like the idea to automatically discover the a11y text labels where possible, as long as it can be overridden. Therefore, we would like to remove the tandem support and proceed with automatically discovered text like #1526 (comment) when it is time. |
My input is that we should develop reusable strategies for discovering acessibleName, and use them in the appropriate UI components. For example, "look for string Property in content Node" seems like a strategy (and implementation) that would be identical for many UI components. |
Meeting 11/1/24
|
Thanks @samreid , #1526 (comment) is a good idea! I just committed an implementation in 8b34da4. @pixelzoom would you mind reviewing this? It is being used in Checkbox and Dialog now. |
8b34da4 looks good to me. The usage in Checkbox phetsims/sun@2cf8d3d also looks nice. Back to @jessegreenberg for next steps. |
Great, thank you for reviewing. I created phetsims/sun#911, where we will track rolling it out by using it in PhET's common code components. Closing this issue. |
In discussion with @jessegreenberg, we saw that visual textual information was not exposed to the a11y tree by default. We had an idea to pluck button/checkbox labels and move them to the PDOM automatically as a default.
The text was updated successfully, but these errors were encountered: