Skip to content
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

PhET-iO instrumentation of Preferences and its dialog #744

Closed
zepumph opened this issue Sep 16, 2021 · 35 comments
Closed

PhET-iO instrumentation of Preferences and its dialog #744

zepumph opened this issue Sep 16, 2021 · 35 comments

Comments

@zepumph
Copy link
Member

zepumph commented Sep 16, 2021

Today during PhET-iO meeting we were discussing how to instrument these preferences.

We feel strongly about two things:

  1. We want a researcher to be able to know what preferences were selected during the sim run.
  2. We don't want preferences to be set onto another sim from setState.

For now we will achieve this by instrumenting preferences as phetioReadOnly: true, and phetioState:false. This way they show up in the data stream.

@zepumph
Copy link
Member Author

zepumph commented Sep 17, 2021

What about an instrumented Property that stored whether or not preferences should be stored? Then you can turn it on and off.

@zepumph
Copy link
Member Author

zepumph commented Sep 20, 2021

  • We should also instrument the Preferences button on the nav bar.

@arouinfar
Copy link

@zepumph based on our discussion in the last PhET-iO meeting, I think general.view.soundManager.enhancedSoundEnabledProperty may need to be deprecated. The "Enhanced Sound" option has been rebranded as "Extra Sounds" in the preferences dialog. You can see it in context in John Travoltage:

image

@zepumph
Copy link
Member Author

zepumph commented Oct 11, 2021

This blocks publication of Friction when published with Voicing (preferences) and also PhET-iO.

@zepumph
Copy link
Member Author

zepumph commented Jan 25, 2022

In #743 (comment) we decided that this issue is blocked by that one. Marking accordingly.

@pixelzoom pixelzoom changed the title iO instrumentation of Preferences and its dialog PhET-iO instrumentation of Preferences and its dialog Feb 16, 2022
@zepumph
Copy link
Member Author

zepumph commented Mar 10, 2022

During PhET-iO meeting today, we decided that preferences Properties would not be stateful. If in the future clients desire this feature, then we can discuss making it possible.

We also decided that it is acceptable and helpful to have data stream capabilities, so that a researcher could look at learners that use voicing or not (for example). This feels helpful to the goals of PhET-iO.

  • In general, we want to customize per feature, and not internal to a feature. So you can't hide a checkbox in the Voicing controls, but you can hide the whole voicing section.

  • Ability to hide tabs in the preferences, can we use the QP below, or do we need new Properties that are instrumented.
    supportsInteractiveHighlights=false
    supportsVoicing=false
    audio=disabled
    supportsSound=false

  • Ensure you can hide the preferences button

  • rename soundManager.enhancedSoundEnabledProperty to soundManager.extraSoundsEnabledProperty
    even though the query parameter is public, we can change it. In general, enhancedSound=>extraSounds (new issue)

  • Design meeting after initial implementation.

@zepumph
Copy link
Member Author

zepumph commented Mar 15, 2022

I got pretty far with initial instrumentation, but ran into a bug when trying to alert a description to aria live. I'll have to come back to this.

Index: phet-io-wrappers/input-record-and-playback/inputRecordAndPlayback.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/phet-io-wrappers/input-record-and-playback/inputRecordAndPlayback.js b/phet-io-wrappers/input-record-and-playback/inputRecordAndPlayback.js
--- a/phet-io-wrappers/input-record-and-playback/inputRecordAndPlayback.js	(revision 17e332da4b14b43f1a54f7f00e87ffa278e05ec1)
+++ b/phet-io-wrappers/input-record-and-playback/inputRecordAndPlayback.js	(date 1647380181461)
@@ -75,6 +75,14 @@
         } );
       } );
     }, 10 );
+
+
+    for ( let i = 0; i < sourceFrame.contentWindow.ALL_NODES.length; i++ ) {
+      if ( sourceFrame.contentWindow.ALL_NODES[ i ] !== destinationFrame.contentWindow.ALL_NODES[ i ] ) {
+        debugger;
+      }
+    }
+
   } );
 
   const setActivePropertyUpstreamSim = active => sourceClient.invoke( activePropertyPhetioID, 'setValue', [ active ] );
Index: joist/js/A11yButtonsHBox.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/A11yButtonsHBox.js b/joist/js/A11yButtonsHBox.js
--- a/joist/js/A11yButtonsHBox.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/A11yButtonsHBox.js	(date 1647380543570)
@@ -40,7 +40,7 @@
       const preferencesButton = new NavigationBarPreferencesButton(
         sim.preferencesManager,
         backgroundColorProperty, {
-          tandem: options.tandem.createTandem( 'navigationBarPreferencesButton' )
+          tandem: options.tandem.createTandem( 'preferencesButton' )
         } );
 
       a11yButtons.push( preferencesButton );
Index: joist/js/preferences/PreferencesToggleSwitch.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/PreferencesToggleSwitch.js b/joist/js/preferences/PreferencesToggleSwitch.js
--- a/joist/js/preferences/PreferencesToggleSwitch.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/PreferencesToggleSwitch.js	(date 1647381019873)
@@ -50,8 +50,8 @@
         trackFillRight: '#64bd5a'
       },
 
-      // phet-io - opting out of Tandems for now
-      tandem: Tandem.OPT_OUT
+      // phet-io
+      tandem: Tandem.REQUIRED
     }, options );
     assert && assert( options.labelNode === null || options.labelNode instanceof Node, 'labelNode is null or inserted as child' );
     assert && assert( options.descriptionNode === null || options.descriptionNode instanceof Node, 'labelNode is null or inserted as child' );
@@ -75,8 +75,8 @@
       voicingIgnoreVoicingManagerProperties: true,
       voicingNameResponse: options.a11yLabel,
 
-      // tandem - opting out of Tandems for now
-      tandem: Tandem.OPT_OUT
+      // tandem
+      tandem: options.tandem.createTandem( 'toggleSwitch' )
     } ) );
 
     this.addChild( toggleSwitch );
Index: joist/js/preferences/GeneralPreferencesPanel.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/GeneralPreferencesPanel.js b/joist/js/preferences/GeneralPreferencesPanel.js
--- a/joist/js/preferences/GeneralPreferencesPanel.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/GeneralPreferencesPanel.js	(date 1647382221566)
@@ -8,8 +8,8 @@
  */
 
 import merge from '../../../phet-core/js/merge.js';
-import { VoicingRichText } from '../../../scenery/js/imports.js';
-import { VBox } from '../../../scenery/js/imports.js';
+import { VBox, VoicingRichText } from '../../../scenery/js/imports.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
 import PreferencesDialog from './PreferencesDialog.js';
@@ -23,17 +23,24 @@
 
   /**
    * @param {Object} generalModel - configuration for the Tab, see PreferencesManager for entries
+   * @param {Object} [options]
    */
-  constructor( generalModel ) {
-    super( {
+  constructor( generalModel, options ) {
+
+    options = merge( {
       align: 'left',
       spacing: 40,
 
       // pdom
       tagName: 'section',
       labelTagName: 'h2',
-      labelContent: 'General'
-    } );
+      labelContent: 'General',
+
+      // phet-io
+      tandem: Tandem.REQUIRED
+    }, options );
+
+    super( options );
 
     const panelChildren = [];
     generalModel.simControls && panelChildren.push( new SimControlsPanelSection( generalModel.simControls ) );
Index: joist/js/toolbar/Toolbar.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/toolbar/Toolbar.js b/joist/js/toolbar/Toolbar.js
--- a/joist/js/toolbar/Toolbar.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/toolbar/Toolbar.js	(date 1647381873479)
@@ -18,11 +18,8 @@
 import stepTimer from '../../../axon/js/stepTimer.js';
 import Matrix3 from '../../../dot/js/Matrix3.js';
 import { Shape } from '../../../kite/js/imports.js';
-import { voicingManager } from '../../../scenery/js/imports.js';
-import { voicingUtteranceQueue } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Path } from '../../../scenery/js/imports.js';
-import { Rectangle } from '../../../scenery/js/imports.js';
+import merge from '../../../phet-core/js/merge.js';
+import { Node, Path, Rectangle, voicingManager, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
 import ButtonNode from '../../../sun/js/buttons/ButtonNode.js';
 import RoundPushButton from '../../../sun/js/buttons/RoundPushButton.js';
 import Tandem from '../../../tandem/js/Tandem.js';
@@ -48,14 +45,20 @@
 
   /**
    * @param {Sim} sim
+   * @param {Object} [options]
    */
-  constructor( sim ) {
+  constructor( sim, options ) {
 
-    super( {
+    options = merge( {
 
       // pdom
-      tagName: 'div'
-    } );
+      tagName: 'div',
+
+      // phet-io
+      tandem: Tandem.REQUIRED
+    }, options );
+
+    super( options );
 
     // @private {BooleanProperty} - Whether or not the Toolbar is enabled (visible to the user)
     this.isEnabledProperty = sim.preferencesManager.toolbarEnabledProperty;
@@ -89,7 +92,9 @@
     // @private {VoicingToolbarItem} - Contents for the Toolbar, currently only controls related to the voicing
     // feature.
     const voicingAlertManager = new VoicingToolbarAlertManager( sim.screenProperty );
-    this.menuContent = new VoicingToolbarItem( voicingAlertManager, sim.lookAndFeel );
+    this.menuContent = new VoicingToolbarItem( voicingAlertManager, sim.lookAndFeel, {
+      tandem: options.tandem.createTandem( 'menuContent' )
+    } );
 
     // icon for the openButton
     const chevronIcon = new DoubleChevron();
Index: joist/js/preferences/PreferencesTabs.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/PreferencesTabs.js b/joist/js/preferences/PreferencesTabs.js
--- a/joist/js/preferences/PreferencesTabs.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/PreferencesTabs.js	(date 1647382546235)
@@ -8,15 +8,9 @@
  */
 
 import Property from '../../../axon/js/Property.js';
+import merge from '../../../phet-core/js/merge.js';
 import StringUtils from '../../../phetcommon/js/util/StringUtils.js';
-import { FocusHighlightPath } from '../../../scenery/js/imports.js';
-import { KeyboardUtils } from '../../../scenery/js/imports.js';
-import { Voicing } from '../../../scenery/js/imports.js';
-import { PressListener } from '../../../scenery/js/imports.js';
-import { Line } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Rectangle } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
+import { FocusHighlightPath, KeyboardUtils, Line, Node, PressListener, Rectangle, Text, Voicing } from '../../../scenery/js/imports.js';
 import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
@@ -34,14 +28,21 @@
   /**
    * @param {PreferencesTab[]} supportedTabs - list of tabs the Dialog should include
    * @param {EnumerationDeprecatedProperty.<PreferencesDialog.PreferenceTab>} selectedPanelProperty
+   * @param {Object} [options]
    */
-  constructor( supportedTabs, selectedPanelProperty ) {
-    super( {
+  constructor( supportedTabs, selectedPanelProperty, options ) {
+    options = merge( {
 
+      // pdom
       tagName: 'ul',
       ariaRole: 'tablist',
-      groupFocusHighlight: true
-    } );
+      groupFocusHighlight: true,
+
+      // phet-io
+      tandem: Tandem.REQUIRED
+    }, options );
+
+    super( options );
 
     // @private {null|Node} - A reference to the selected and focusable tab content so that we can determine which
     // tab is next in order when cycling through with alternative input.
@@ -52,13 +53,14 @@
 
     // @private {Tab[]}
     this.content = [];
-    const addTabIfSupported = ( preferenceTab, titleString ) => {
-      _.includes( supportedTabs, preferenceTab ) && this.content.push( new Tab( titleString, selectedPanelProperty, preferenceTab ) );
+    const addTabIfSupported = ( preferenceTab, titleString, tandemName ) => {
+      const tandem = options.tandem.createTandem( tandemName );
+      _.includes( supportedTabs, preferenceTab ) && this.content.push( new Tab( titleString, selectedPanelProperty, preferenceTab, tandem ) );
     };
-    addTabIfSupported( PreferencesDialog.PreferencesTab.GENERAL, generalTitleString );
-    addTabIfSupported( PreferencesDialog.PreferencesTab.VISUAL, visualTitleString );
-    addTabIfSupported( PreferencesDialog.PreferencesTab.AUDIO, audioTitleString );
-    addTabIfSupported( PreferencesDialog.PreferencesTab.INPUT, inputTitleString );
+    addTabIfSupported( PreferencesDialog.PreferencesTab.GENERAL, generalTitleString, 'generalTab' );
+    addTabIfSupported( PreferencesDialog.PreferencesTab.VISUAL, visualTitleString, 'visualTab' );
+    addTabIfSupported( PreferencesDialog.PreferencesTab.AUDIO, audioTitleString, 'audioTab' );
+    addTabIfSupported( PreferencesDialog.PreferencesTab.INPUT, inputTitleString, 'audioTab' );
 
     for ( let i = 0; i < this.content.length; i++ ) {
       this.addChild( this.content[ i ] );
@@ -154,7 +156,7 @@
    * @param {EnumerationDeprecatedProperty.<PreferencesDialog.<PreferencesTab>} property
    * @param {PreferencesDialog.PreferencesTab} value - PreferencesTab shown when this tab is selected
    */
-  constructor( label, property, value ) {
+  constructor( label, property, value, tandem ) {
 
     const textNode = new Text( label, PreferencesDialog.TAB_OPTIONS );
 
@@ -178,7 +180,9 @@
       innerContent: label,
       ariaRole: 'tab',
       focusable: true,
-      containerTagName: 'li'
+      containerTagName: 'li',
+
+      tandem: tandem
     } );
 
     // @public {PreferenceTab}
@@ -188,7 +192,7 @@
       title: label
     } );
 
-    const buttonListener = new PressListener( {
+    const pressListener = new PressListener( {
       press: () => {
         property.set( value );
 
@@ -196,12 +200,12 @@
         this.voicingSpeakNameResponse();
       },
 
-      // phet-io - opting out for now to get CT working
-      tandem: Tandem.OPT_OUT
+      // phet-io
+      tandem: tandem.createTandem( 'pressListener' )
     } );
-    this.addInputListener( buttonListener );
+    this.addInputListener( pressListener );
 
-    Property.multilink( [ property, buttonListener.isOverProperty ], ( selectedTab, isOver ) => {
+    Property.multilink( [ property, pressListener.isOverProperty ], ( selectedTab, isOver ) => {
       textNode.opacity = selectedTab === value ? 1 :
                          isOver ? 0.8 :
                          0.6;
Index: joist/js/toolbar/VoicingToolbarItem.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/toolbar/VoicingToolbarItem.js b/joist/js/toolbar/VoicingToolbarItem.js
--- a/joist/js/toolbar/VoicingToolbarItem.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/toolbar/VoicingToolbarItem.js	(date 1647381946682)
@@ -8,15 +8,10 @@
  */
 
 import BooleanProperty from '../../../axon/js/BooleanProperty.js';
+import merge from '../../../phet-core/js/merge.js';
 import PlayStopButton from '../../../scenery-phet/js/buttons/PlayStopButton.js';
 import PhetFont from '../../../scenery-phet/js/PhetFont.js';
-import { VoicingText } from '../../../scenery/js/imports.js';
-import { ReadingBlockHighlight } from '../../../scenery/js/imports.js';
-import { voicingManager } from '../../../scenery/js/imports.js';
-import { AlignGroup } from '../../../scenery/js/imports.js';
-import { HBox } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
+import { AlignGroup, HBox, Node, ReadingBlockHighlight, Text, voicingManager, VoicingText } from '../../../scenery/js/imports.js';
 import Tandem from '../../../tandem/js/Tandem.js';
 import Utterance from '../../../utterance-queue/js/Utterance.js';
 import joist from '../joist.js';
@@ -46,16 +41,21 @@
   /**
    * @param {VoicingToolbarAlertManager} alertManager - generates the alert content when buttons are pressed
    * @param {LookAndFeel} lookAndFeel
+   * @param {Object} [options]
    */
-  constructor( alertManager, lookAndFeel ) {
-
-    super( {
+  constructor( alertManager, lookAndFeel, options ) {
+    options = merge( {
 
       // pdom
       tagName: 'section',
       labelTagName: 'h2',
-      labelContent: toolbarString
-    } );
+      labelContent: toolbarString,
+
+      // phet-io
+      tandem: Tandem.REQUIRED
+    }, options );
+
+    super( options );
 
     const titleTextOptions = {
       font: new PhetFont( 14 ),
@@ -76,7 +76,8 @@
       a11yLabel: titleString,
       toggleSwitchOptions: {
         voicingUtteranceQueue: joistVoicingUtteranceQueue
-      }
+      },
+      tandem: options.tandem.createTandem( 'muteSpeechSwitch' )
     } );
 
     // layout
Index: joist/js/preferences/SoundPanelSection.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/SoundPanelSection.js b/joist/js/preferences/SoundPanelSection.js
--- a/joist/js/preferences/SoundPanelSection.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/SoundPanelSection.js	(date 1647381462367)
@@ -8,11 +8,7 @@
 
 import merge from '../../../phet-core/js/merge.js';
 import StringUtils from '../../../phetcommon/js/util/StringUtils.js';
-import { VoicingRichText } from '../../../scenery/js/imports.js';
-import { VoicingText } from '../../../scenery/js/imports.js';
-import { voicingUtteranceQueue } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
-import { VBox } from '../../../scenery/js/imports.js';
+import { Text, VBox, VoicingRichText, VoicingText, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
 import Checkbox from '../../../sun/js/Checkbox.js';
 import soundManager from '../../../tambo/js/soundManager.js';
 import Tandem from '../../../tandem/js/Tandem.js';
@@ -47,12 +43,15 @@
       // PreferencesPanelSection. It is possible that the toggle for Sound can be redundant when Sound
       // is the only Audio feature supported. In that case, control of Sound should go through the
       // "All Audio" toggle.
-      includeTitleToggleSwitch: true
+      includeTitleToggleSwitch: true,
+
+      // phet-io
+      tandem: Tandem.REQUIRED
     }, options );
 
     const soundLabel = new Text( soundsLabelString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS );
 
-    const titleNode = new PreferencesToggleSwitch( soundManager.enabledProperty, false, true, {
+    const soundEnabledSwitch = new PreferencesToggleSwitch( soundManager.enabledProperty, false, true, {
       labelNode: soundLabel,
       descriptionNode: new VoicingText( soundDescriptionString, merge( {}, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS, {
         readingBlockContent: StringUtils.fillIn( labelledDescriptionPatternString, {
@@ -63,7 +62,8 @@
       toggleSwitchOptions: {
         visible: options.includeTitleToggleSwitch
       },
-      a11yLabel: soundsLabelString
+      a11yLabel: soundsLabelString,
+      tandem: options.tandem.createTandem( 'soundEnabledSwitch' )
     } );
 
     let enhancedSoundContent = null;
@@ -79,7 +79,7 @@
         voicingNameResponse: extraSoundsLabelString,
 
         // phet-io
-        tandem: Tandem.OPT_OUT
+        tandem: options.tandem.createTandem( 'enhancedSoundCheckbox' )
       } );
 
       const enhancedSoundDescription = new VoicingRichText( extraSoundsDescriptionString, merge( {}, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS, {
@@ -102,7 +102,7 @@
     }
 
     super( {
-      titleNode: titleNode,
+      titleNode: soundEnabledSwitch,
       contentNode: enhancedSoundContent
     } );
 
Index: joist/js/preferences/InputPreferencesPanel.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/InputPreferencesPanel.js b/joist/js/preferences/InputPreferencesPanel.js
--- a/joist/js/preferences/InputPreferencesPanel.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/InputPreferencesPanel.js	(date 1647382257387)
@@ -8,10 +8,8 @@
 
 import merge from '../../../phet-core/js/merge.js';
 import StringUtils from '../../../phetcommon/js/util/StringUtils.js';
-import { VoicingRichText } from '../../../scenery/js/imports.js';
-import { voicingUtteranceQueue } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
+import { Node, Text, VoicingRichText, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
 import PreferencesDialog from './PreferencesDialog.js';
@@ -32,17 +30,22 @@
 
   /**
    * @param {Object} inputModel - see PreferencesManager
+   * @param {Object} [options]
    */
-  constructor( inputModel ) {
-    super( {
+  constructor( inputModel, options ) {
+    options = merge( {
 
       // pdom
       tagName: 'div',
       labelTagName: 'h2',
-      labelContent: inputTitleString
-    } );
+      labelContent: inputTitleString,
+
+      tandem: Tandem.REQUIRED
+    }, options );
 
-    const toggleSwitch = new PreferencesToggleSwitch( inputModel.gestureControlsEnabledProperty, false, true, {
+    super( options );
+
+    const gestureControlsEnabledSwitch = new PreferencesToggleSwitch( inputModel.gestureControlsEnabledProperty, false, true, {
       labelNode: new Text( gestureControlsString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS ),
       descriptionNode: new VoicingRichText( gestureControlsDescriptionString, merge( {}, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS, {
         lineWrap: 350,
@@ -52,11 +55,12 @@
           description: gestureControlsDescriptionString
         } )
       } ) ),
-      a11yLabel: gestureControlsString
+      a11yLabel: gestureControlsString,
+      tandem: options.tandem.createTandem( 'gestureControlsEnabledSwitch' )
     } );
 
     const panelSection = new PreferencesPanelSection( {
-      titleNode: toggleSwitch
+      titleNode: gestureControlsEnabledSwitch
     } );
     this.addChild( panelSection );
 
Index: joist/js/preferences/AudioPreferencesPanel.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/AudioPreferencesPanel.js b/joist/js/preferences/AudioPreferencesPanel.js
--- a/joist/js/preferences/AudioPreferencesPanel.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/AudioPreferencesPanel.js	(date 1647381462364)
@@ -6,9 +6,9 @@
  * @author Jesse Greenberg (PhET Interactive Simulations)
  */
 
-import { HBox } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
-import { VBox } from '../../../scenery/js/imports.js';
+import merge from '../../../phet-core/js/merge.js';
+import { HBox, Text, VBox } from '../../../scenery/js/imports.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
 import PreferencesDialog from './PreferencesDialog.js';
@@ -24,13 +24,20 @@
   /**
    * @param {Object} audioModel - configuration for audio settings, see PreferencesManager
    * @param {BooleanProperty} enableToolbarProperty - whether the Toolbar is enabled
+   * @param {Object} [options]
    */
-  constructor( audioModel, enableToolbarProperty ) {
+  constructor( audioModel, enableToolbarProperty, options ) {
+
+    options = merge( {
+      tandem: Tandem.REQUIRED
+    }, options );
 
     const panelChildren = [];
 
     if ( audioModel.supportsVoicing ) {
-      panelChildren.push( new VoicingPanelSection( audioModel, enableToolbarProperty ) );
+      panelChildren.push( new VoicingPanelSection( audioModel, enableToolbarProperty, {
+        tandem: options.tandem.createTandem( 'voicingPanelSection' )
+      } ) );
     }
 
     if ( audioModel.supportsSound ) {
@@ -41,7 +48,8 @@
       const hideSoundToggle = audioModel.supportsVoicing !== audioModel.supportsSound;
 
       panelChildren.push( new SoundPanelSection( audioModel, {
-        includeTitleToggleSwitch: !hideSoundToggle
+        includeTitleToggleSwitch: !hideSoundToggle,
+        tandem: options.tandem.createTandem( 'soundPanelSection' )
       } ) );
     }
 
@@ -52,7 +60,8 @@
 
     const allAudioSwitch = new PreferencesToggleSwitch( audioModel.simSoundEnabledProperty, false, true, {
       labelNode: new Text( audioFeaturesString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS ),
-      a11yLabel: audioFeaturesString
+      a11yLabel: audioFeaturesString,
+      tandem: options.tandem.createTandem( 'allAudioSwitch' )
     } );
 
     const soundEnabledListener = enabled => {
Index: joist/js/preferences/VoicingPanelSection.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/VoicingPanelSection.js b/joist/js/preferences/VoicingPanelSection.js
--- a/joist/js/preferences/VoicingPanelSection.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/VoicingPanelSection.js	(date 1647383310313)
@@ -89,12 +89,17 @@
   /**
    * @param {Object} audioModel - configuration for audio settings, see PreferencesManager
    * @param {BooleanProperty} toolbarEnabledProperty - whether or not the Toolbar is enabled for use
+   * @param {Object} [options]
    */
-  constructor( audioModel, toolbarEnabledProperty ) {
+  constructor( audioModel, toolbarEnabledProperty, options ) {
+
+    options = merge( {
+      tandem: Tandem.REQUIRED
+    }, options );
 
     // the checkbox is the title for the section and totally enables/disables the feature
     const voicingLabel = new Text( voicingLabelString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS );
-    const voicingSwitch = new PreferencesToggleSwitch( audioModel.voicingEnabledProperty, false, true, {
+    const voicingEnabledSwitch = new PreferencesToggleSwitch( audioModel.voicingEnabledProperty, false, true, {
       labelNode: voicingLabel,
       descriptionNode: new VoicingText( voicingDescriptionString, merge( {}, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS, {
         readingBlockContent: StringUtils.fillIn( labelledDescriptionPatternString, {
@@ -102,14 +107,16 @@
           description: voicingDescriptionString
         } )
       } ) ),
-      a11yLabel: voicingLabelString
+      a11yLabel: voicingLabelString,
+      tandem: options.tandem.createTandem( 'voicingEnabledSwitch' )
     } );
 
     // checkbox for the toolbar
     const quickAccessLabel = new Text( toolbarLabelString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS );
-    const toolbarSwitch = new PreferencesToggleSwitch( toolbarEnabledProperty, false, true, {
+    const toolbarEnabledSwitch = new PreferencesToggleSwitch( toolbarEnabledProperty, false, true, {
       labelNode: quickAccessLabel,
-      a11yLabel: toolbarLabelString
+      a11yLabel: toolbarLabelString,
+      tandem: options.tandem.createTandem( 'toolbarEnabledSwitch' )
     } );
 
     // Speech output levels
@@ -129,9 +136,15 @@
       align: 'left',
       spacing: 5,
       children: [
-        createCheckbox( objectDetailsLabelString, audioModel.voicingObjectResponsesEnabledProperty ),
-        createCheckbox( contextChangesLabelString, audioModel.voicingContextResponsesEnabledProperty ),
-        createCheckbox( helpfulHintsLabelString, audioModel.voicingHintResponsesEnabledProperty )
+        createCheckbox( objectDetailsLabelString, audioModel.voicingObjectResponsesEnabledProperty,
+          options.tandem.createTandem( 'voicingObjectResponsesEnabledCheckbox' )
+        ),
+        createCheckbox( contextChangesLabelString, audioModel.voicingContextResponsesEnabledProperty,
+          options.tandem.createTandem( 'voicingContextResponsesEnabledCheckbox' )
+        ),
+        createCheckbox( helpfulHintsLabelString, audioModel.voicingHintResponsesEnabledProperty,
+          options.tandem.createTandem( 'voicingHintResponsesEnabledCheckbox' )
+        )
       ]
     } );
 
@@ -167,7 +180,7 @@
       voicingNameResponse: customizeVoiceString,
 
       // phet-io
-      tandem: Tandem.OPT_OUT
+      tandem: options.tandem.createTandem( 'expandCollapseButton' )
     } );
 
     const voiceOptionsContainer = new Node( {
@@ -186,13 +199,13 @@
     voiceOptionsLabel.addInputListener( voiceOptionsPressListener );
 
     const content = new Node( {
-      children: [ speechOutputContent, toolbarSwitch, voiceOptionsContainer, voiceOptionsContent ]
+      children: [ speechOutputContent, toolbarEnabledSwitch, voiceOptionsContainer, voiceOptionsContent ]
     } );
 
     // layout for section content, custom rather than using a LayoutBox because the voice options label needs
     // to be left aligned with other labels, while the ExpandCollapseButton extends to the left
-    toolbarSwitch.leftTop = speechOutputContent.leftBottom.plusXY( 0, 20 );
-    voiceOptionsLabel.leftTop = toolbarSwitch.leftBottom.plusXY( 0, 20 );
+    toolbarEnabledSwitch.leftTop = speechOutputContent.leftBottom.plusXY( 0, 20 );
+    voiceOptionsLabel.leftTop = toolbarEnabledSwitch.leftBottom.plusXY( 0, 20 );
     expandCollapseButton.leftCenter = voiceOptionsLabel.rightCenter.plusXY( 10, 0 );
     voiceOptionsContent.leftTop = voiceOptionsLabel.leftBottom.plusXY( 0, 10 );
     voiceOptionsOpenProperty.link( open => { voiceOptionsContent.visible = open; } );
@@ -201,7 +214,7 @@
     expandCollapseButton.focusHighlight = new FocusHighlightFromNode( voiceOptionsContainer );
 
     super( {
-      titleNode: voicingSwitch,
+      titleNode: voicingEnabledSwitch,
       contentNode: content
     } );
 
@@ -251,7 +264,10 @@
         voiceList = englishVoices.slice( 0, 12 );
       }
 
-      voiceComboBox = new VoiceComboBox( voiceList, audioModel.voiceProperty, phet.joist.sim.topLayer );
+      // phet-io - for when creating the Archetype for the Capsule housing the preferencesDialog, we don't have a sim global.
+      const parent = phet.joist.sim.topLayer || new Node();
+
+      voiceComboBox = new VoiceComboBox( voiceList, audioModel.voiceProperty, parent );
       voiceOptionsContent.addChild( voiceComboBox );
     };
     voicingManager.voicesChangedEmitter.addListener( voicesChangedListener );
@@ -297,7 +313,7 @@
  * @param {BooleanProperty} property
  * @returns {Checkbox}
  */
-const createCheckbox = ( labelString, property ) => {
+const createCheckbox = ( labelString, property, tandem ) => {
   const labelNode = new Text( labelString, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS );
   return new Checkbox( labelNode, property, {
 
@@ -309,7 +325,7 @@
     voicingNameResponse: labelString,
 
     // phet-io
-    tandem: Tandem.OPT_OUT
+    tandem: tandem
   } );
 };
 
@@ -399,8 +415,18 @@
    * @param {SpeechSynthesisVoice[]} voices - list of voices to include from the voicingManager
    * @param {Property.<SpeechSynthesisVoice|null>} voiceProperty
    * @param {Node} parentNode - node that acts as a parent for the ComboBox list
+   * @param {Object} [options]
    */
-  constructor( voices, voiceProperty, parentNode ) {
+  constructor( voices, voiceProperty, parentNode, options ) {
+
+    options = merge( {
+      listPosition: 'above',
+      accessibleName: voiceLabelString,
+
+      // phet-io, opt out because we would need to instrument voices, but those could change between runtimes.
+      tandem: Tandem.OPT_OUT
+    }, options );
+
     const items = [];
 
     if ( voices.length === 0 ) {
@@ -421,13 +447,7 @@
     // voices
     voiceProperty.set( items[ 0 ].value );
 
-    super( items, voiceProperty, parentNode, {
-      listPosition: 'above',
-      accessibleName: voiceLabelString,
-
-      // phet-io
-      tandem: Tandem.OPT_OUT
-    } );
+    super( items, voiceProperty, parentNode, options );
 
     // voicing -  responses for the button should always come through, regardless of user selection of
     // responses. As of 10/29/21, ComboBox will only read the name response (which are always read regardless)
Index: joist/js/preferences/PreferencesDialog.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/PreferencesDialog.js b/joist/js/preferences/PreferencesDialog.js
--- a/joist/js/preferences/PreferencesDialog.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/PreferencesDialog.js	(date 1647382028110)
@@ -10,15 +10,14 @@
  */
 
 import EnumerationProperty from '../../../axon/js/EnumerationProperty.js';
-import EnumerationValue from '../../../phet-core/js/EnumerationValue.js';
 import Enumeration from '../../../phet-core/js/Enumeration.js';
+import EnumerationValue from '../../../phet-core/js/EnumerationValue.js';
 import merge from '../../../phet-core/js/merge.js';
 import PhetFont from '../../../scenery-phet/js/PhetFont.js';
-import { KeyboardUtils } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
+import { KeyboardUtils, Node, Text } from '../../../scenery/js/imports.js';
 import Dialog from '../../../sun/js/Dialog.js';
 import HSeparator from '../../../sun/js/HSeparator.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import audioManager from '../audioManager.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
@@ -83,6 +82,7 @@
 
       // phet-io
       phetioDynamicElement: true,
+      tandem: Tandem.REQUIRED,
 
       // pdom
       positionInPDOM: true
@@ -100,10 +100,14 @@
     const selectedTabProperty = new EnumerationProperty( PreferencesTab.GENERAL );
 
     // the set of tabs you can can click to activate a tab panel
-    const preferencesTabs = new PreferencesTabs( supportedTabs, selectedTabProperty );
+    const preferencesTabs = new PreferencesTabs( supportedTabs, selectedTabProperty, {
+      tandem: options.tandem.createTandem( 'preferencesTabs' )
+    } );
 
     // the panels of content with UI components to select preferences, only one is displayed at a time
-    const preferencesPanels = new PreferencesPanels( preferencesModel, supportedTabs, selectedTabProperty );
+    const preferencesPanels = new PreferencesPanels( preferencesModel, supportedTabs, selectedTabProperty, {
+      tandem: options.tandem.createTandem( 'preferencesPanels' )
+    } );
 
     // visual separator between tabs and panels - as long as the widest separated content, which may change with i18n
     const tabPanelSeparator = new HSeparator( Math.max( preferencesPanels.width, preferencesTabs.width ), { lineWidth: 1 } );
Index: joist/js/preferences/VisualPreferencesPanel.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/VisualPreferencesPanel.js b/joist/js/preferences/VisualPreferencesPanel.js
--- a/joist/js/preferences/VisualPreferencesPanel.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/VisualPreferencesPanel.js	(date 1647381649236)
@@ -9,10 +9,8 @@
 
 import merge from '../../../phet-core/js/merge.js';
 import StringUtils from '../../../phetcommon/js/util/StringUtils.js';
-import { VoicingText } from '../../../scenery/js/imports.js';
-import { voicingUtteranceQueue } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
-import { Text } from '../../../scenery/js/imports.js';
+import { Node, Text, VoicingText, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import joistStrings from '../joistStrings.js';
 import PreferencesDialog from './PreferencesDialog.js';
@@ -30,18 +28,25 @@
 
   /**
    * @param {Object} visualModel - see PreferencesManager
+   * @param {Object} [options]
    */
-  constructor( visualModel ) {
-    super( {
+  constructor( visualModel, options ) {
+
+    options = merge( {
 
       // pdom
       tagName: 'div',
       labelTagName: 'h2',
-      labelContent: 'Visual'
-    } );
+      labelContent: 'Visual',
+
+      // phet-io
+      tandem: Tandem.REQUIRED
+    }, options );
+
+    super( options );
 
     const label = new Text( interactiveHighlightsString, PreferencesDialog.PANEL_SECTION_LABEL_OPTIONS );
-    const toggleSwitch = new PreferencesToggleSwitch( visualModel.interactiveHighlightsEnabledProperty, false, true, {
+    const interactiveHighlightsEnabledSwitch = new PreferencesToggleSwitch( visualModel.interactiveHighlightsEnabledProperty, false, true, {
       labelNode: label,
       descriptionNode: new VoicingText( interactiveHighlightsDescriptionString, merge( {}, PreferencesDialog.PANEL_SECTION_CONTENT_OPTIONS, {
         readingBlockContent: StringUtils.fillIn( labelledDescriptionPatternString, {
@@ -49,11 +54,12 @@
           description: interactiveHighlightsDescriptionString
         } )
       } ) ),
-      a11yLabel: interactiveHighlightsString
+      a11yLabel: interactiveHighlightsString,
+      tandem: options.tandem.createTandem( 'interactiveHighlightsEnabledSwitch' )
     } );
 
     const panelSection = new PreferencesPanelSection( {
-      titleNode: toggleSwitch
+      titleNode: interactiveHighlightsEnabledSwitch
     } );
     this.addChild( panelSection );
 
Index: joist/js/preferences/PreferencesPanels.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/PreferencesPanels.js b/joist/js/preferences/PreferencesPanels.js
--- a/joist/js/preferences/PreferencesPanels.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/PreferencesPanels.js	(date 1647382221563)
@@ -9,8 +9,9 @@
  * @author Jesse Greenberg (PhET Interactive Simulations)
  */
 
-import { AlignGroup } from '../../../scenery/js/imports.js';
-import { Node } from '../../../scenery/js/imports.js';
+import merge from '../../../phet-core/js/merge.js';
+import { AlignGroup, Node } from '../../../scenery/js/imports.js';
+import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import AudioPreferencesPanel from './AudioPreferencesPanel.js';
 import GeneralPreferencesPanel from './GeneralPreferencesPanel.js';
@@ -24,46 +25,59 @@
    * @param {PreferencesManager} preferencesModel
    * @param {PreferencesTab[]} supportedTabs - list of Tabs supported by this Dialog
    * @param {EnumerationDeprecatedProperty.<PreferencesTab>} selectedTabProperty
+   * @param {Object} [options]
    */
-  constructor( preferencesModel, supportedTabs, selectedTabProperty ) {
-    super();
+  constructor( preferencesModel, supportedTabs, selectedTabProperty, options ) {
+
+    options = merge( {
+      tandem: Tandem.REQUIRED
+    }, options );
+    super( options );
 
     const panelAlignGroup = new AlignGroup( {
       matchVertical: false
     } );
 
-    // @private {PreferencesPanel[]}
+    // @private {PreferencesPanelContainer[]}
     this.content = [];
 
     let generalPreferencesPanel = null;
     if ( supportedTabs.includes( PreferencesDialog.PreferencesTab.GENERAL ) ) {
-      generalPreferencesPanel = new GeneralPreferencesPanel( preferencesModel.generalModel );
+      generalPreferencesPanel = new GeneralPreferencesPanel( preferencesModel.generalModel, {
+        tandem: options.tandem.createTandem( 'generalPreferencesPanel' )
+      } );
       const generalBox = panelAlignGroup.createBox( generalPreferencesPanel );
       this.addChild( generalBox );
-      this.content.push( new PreferencesPanel( generalPreferencesPanel, PreferencesDialog.PreferencesTab.GENERAL ) );
+      this.content.push( new PreferencesPanelContainer( generalPreferencesPanel, PreferencesDialog.PreferencesTab.GENERAL ) );
     }
 
     let visualPreferencesPanel = null;
     if ( supportedTabs.includes( PreferencesDialog.PreferencesTab.VISUAL ) ) {
-      visualPreferencesPanel = new VisualPreferencesPanel( preferencesModel.visualModel );
+      visualPreferencesPanel = new VisualPreferencesPanel( preferencesModel.visualModel, {
+        tandem: options.tandem.createTandem( 'visualPreferencesPanel' )
+      } );
       const visualBox = panelAlignGroup.createBox( visualPreferencesPanel );
       this.addChild( visualBox );
-      this.content.push( new PreferencesPanel( visualPreferencesPanel, PreferencesDialog.PreferencesTab.VISUAL ) );
+      this.content.push( new PreferencesPanelContainer( visualPreferencesPanel, PreferencesDialog.PreferencesTab.VISUAL ) );
     }
 
     let audioPreferencesPanel = null;
     if ( supportedTabs.includes( PreferencesDialog.PreferencesTab.AUDIO ) ) {
-      audioPreferencesPanel = new AudioPreferencesPanel( preferencesModel.audioModel, preferencesModel.toolbarEnabledProperty );
+      audioPreferencesPanel = new AudioPreferencesPanel( preferencesModel.audioModel, preferencesModel.toolbarEnabledProperty, {
+        tandem: options.tandem.createTandem( 'audioPreferencesPanel' )
+      } );
       const audioBox = panelAlignGroup.createBox( audioPreferencesPanel );
       this.addChild( audioBox );
-      this.content.push( new PreferencesPanel( audioPreferencesPanel, PreferencesDialog.PreferencesTab.AUDIO ) );
+      this.content.push( new PreferencesPanelContainer( audioPreferencesPanel, PreferencesDialog.PreferencesTab.AUDIO ) );
     }
 
     let inputPreferencesPanel = null;
     if ( supportedTabs.includes( PreferencesDialog.PreferencesTab.INPUT ) ) {
-      inputPreferencesPanel = new InputPreferencesPanel( preferencesModel.inputModel );
+      inputPreferencesPanel = new InputPreferencesPanel( preferencesModel.inputModel, {
+        tandem: options.tandem.createTandem( 'inputPreferencesPanel' )
+      } );
       this.addChild( inputPreferencesPanel );
-      this.content.push( new PreferencesPanel( inputPreferencesPanel, PreferencesDialog.PreferencesTab.INPUT ) );
+      this.content.push( new PreferencesPanelContainer( inputPreferencesPanel, PreferencesDialog.PreferencesTab.INPUT ) );
     }
 
     this.selectedTabProperty = selectedTabProperty;
@@ -79,7 +93,7 @@
 
   /**
    * @private
-   * @returns {PreferencesPanel} - the currently selected preferences panel
+   * @returns {PreferencesPanelContainer} - the currently selected preferences panel
    */
   getSelectedContent() {
     for ( let i = 0; i < this.content.length; i++ ) {
@@ -95,7 +109,7 @@
   /**
    * Focus the selected panel. The panel should not be focusable until this is requested, so it is set to be
    * focusable before the focus() call. When focus is removed from the panel, it should become non-focusable
-   * again. That is handled in PreferencesPanel class.
+   * again. That is handled in PreferencesPanelContainer class.
    * @public
    */
   focusSelectedPanel() {
@@ -119,7 +133,7 @@
  * An inner class that manages the panelContent and its value. A listener as added to the panel so that
  * whenever focus is lost from the panel, it is removed from the traversal order.
  */
-class PreferencesPanel extends Node {
+class PreferencesPanelContainer extends Node {
 
   /**
    * @param {Node} panelContent
Index: joist/js/Sim.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/Sim.js b/joist/js/Sim.js
--- a/joist/js/Sim.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/Sim.js	(date 1647383669108)
@@ -529,7 +529,9 @@
       assert && assert( !options.simDisplayOptions.preferencesManager );
       options.simDisplayOptions.preferencesManager = this.preferencesManager;
 
-      this.toolbar = new Toolbar( this );
+      this.toolbar = new Toolbar( this, {
+        tandem: Tandem.GENERAL_VIEW.createTandem( 'toolbar' )
+      } );
 
       // when the Toolbar positions update, resize the sim to fit in the available space
       this.toolbar.rightPositionProperty.lazyLink( () => {
Index: joist/js/preferences/NavigationBarPreferencesButton.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/joist/js/preferences/NavigationBarPreferencesButton.js b/joist/js/preferences/NavigationBarPreferencesButton.js
--- a/joist/js/preferences/NavigationBarPreferencesButton.js	(revision 6f617114eef353e72bea7319355af3d753269558)
+++ b/joist/js/preferences/NavigationBarPreferencesButton.js	(date 1647383204788)
@@ -9,6 +9,8 @@
 import merge from '../../../phet-core/js/merge.js';
 import { Path } from '../../../scenery/js/imports.js';
 import userCogSolidShape from '../../../sherpa/js/fontawesome-5/userCogSolidShape.js';
+import Dialog from '../../../sun/js/Dialog.js';
+import PhetioCapsule from '../../../tandem/js/PhetioCapsule.js';
 import Tandem from '../../../tandem/js/Tandem.js';
 import joist from '../joist.js';
 import JoistButton from '../JoistButton.js';
@@ -32,15 +34,19 @@
       maxWidth: 25
     } );
 
-    let preferencesDialog = null;
-    super( icon, backgroundColorProperty, Tandem.OPT_OUT, {
+    assert && assert( !options.listener, 'PhetButton sets listener' );
+    const preferencesDialogCapsule = new PhetioCapsule( tandem => {
+      return new PreferencesDialog( preferencesModel, {
+        tandem: tandem
+      } );
+    }, [], {
+      tandem: options.tandem.createTandem( 'preferencesDialogCapsule' ),
+      phetioType: PhetioCapsule.PhetioCapsuleIO( Dialog.DialogIO )
+    } );
+
+    super( icon, backgroundColorProperty, options.tandem, {
       listener: () => {
-        if ( !preferencesDialog ) {
-          preferencesDialog = new PreferencesDialog( preferencesModel, {
-            tandem: Tandem.OPT_OUT
-          } );
-        }
-
+        const preferencesDialog = preferencesDialogCapsule.getElement();
         preferencesDialog.show();
         preferencesDialog.focusSelectedTab();
       },

zepumph added a commit to phetsims/ratio-and-proportion that referenced this issue Mar 29, 2022
zepumph added a commit that referenced this issue Mar 29, 2022
zepumph added a commit to phetsims/sun that referenced this issue Mar 29, 2022
zepumph added a commit to phetsims/sun that referenced this issue Mar 29, 2022
@zepumph
Copy link
Member Author

zepumph commented Mar 29, 2022

Alright. The view is committed above. It still isn't totally clear how we will have the model instrumented, since we largely don't want preferences to persist. Even so, this seems like a good commit point. I will work to see what more we would want to do with the model.

@samreid
Copy link
Member

samreid commented Aug 25, 2022

In today's meeting,

Regarding saving state with or without preferences

@JacquiHayes identified that privacy laws may forbid detecting what a11y preferences are selected without specialized permissions.
@kathy-phet points out that the PhET-iO clients may want to have this flexibility, like being able to save a state without these values, or another API call that also gives these values.
@zepumph asks if we could be liable by enabling this API? The team thinks the user of that API would be liable.
@JacquiHayes has not heard from clients about this.
@zepumph indicates that we can do this release without these features, but planning to add an option for this in the future. Adding "2 kinds of state" would lead to increased complexity in the future. Especially if we don't have a client that needs that at all.
@kathy-phet: But users would have to re-enable the preferences they want, but maybe that is OK. But what if a student is working on a sim in a wrapper. The wrapper saves progress. When the student comes back the next day, it would have forgotten their usability preferences.
@zepumph added a dev tool for localStorage to save the preferences. Would it work for your case?
@samreid: I don't think so
@zepumph also, we didn't want preferences to persist for cases like computer labs, etc.
@kathy-phet says maybe there should be a separate API call just to get the preferences.

So we can have getState vs getStateWithPreferences OR we can have getState + getPreferencesState. It is unclear which is preferable.

Regarding instrumenting the elements within the preferences dialog

@kathy-phet: We are only instrumenting the controls within the "Simulation" tab, right?
@zepumph: No
@samreid: Why wouldn't we allow PhET-iO Clients to make adjustments or changes in the Preferences Dialog? We trust the PhET-iO Clients to make good choices and want to give them the flexibility to do what they need in their product.
@JacquiHayes: We could leave it uninstrumented "for now" until a client requires?
@arouinfar: The whole preferences button can be hidden at the moment. It's a coarse-level granularity, but may be good enough for now?
@zepumph: It is difficult to change the list of the available tabs.
@kathy-phet: A Client may want to show a subset of the languages that can be selected from the localization tab.
@JacquiHayes: When I showed number play's in-sim translation feature, the multi-language feature was well-received. Being able to switch in other languages would be greatly appreciated as well.
@kathy-phet: So it is best to leave all the languages and not allow PhET-iO Clients to remove some of them?
@JacquiHayes: Yes, maybe that's best for now. But what about younger kids? Will they want to see all 88+ languages?
@kathy-phet: NumberPlay runs off of any 2 arbitrary languages (via a query parameter)

Conclusion: Leave it as it is (uninstrumented + mildly instrumented) for now, and wait for clients to ask for this feature before we start instrumenting these details.

@zepumph can you please split this up into side issues as appropriate?

@arouinfar will look into the tree as it is now and see if there are other changes to be made, after @zepumph gives the go-ahead.

@zepumph
Copy link
Member Author

zepumph commented Sep 8, 2022

Still on me, pinged during today's PhET-iO meeting.

@zepumph
Copy link
Member Author

zepumph commented Sep 14, 2022

Today during PhET-iO meeting. . . .

  • we want to be able to hide tabs from the preferences panels. This supports the ability to not have any sound in a sim. PhET-iO support for choosing to hide tabs on the preferences dialog #860
  • we want to continue to have all preferences phetioState:false, but we want to instead make them phetioReadOnly: false, if a client really wants to change one of these, it is ok. Furthermore, the client could implement their own preferences saving mechanism for homework etc if desired.
  • simSoundEnabledProperty is a weird name, and a weird linkedElement pointing back to the audioEnabledProperty, let's rename it throughout the preferences model to audioEnabledProperty.
  • Please review this issue michael.

@zepumph
Copy link
Member Author

zepumph commented Sep 16, 2022

I'd like to co-assign @samreid because my time pre-vacation is running short, and I don't want to be a bottle neck. I think that @samreid could definitely work on marking things as phetioReadOnly: true again, and likely also the simSoundEnabledProperty rename. I'm still trying to get to this though!

@samreid
Copy link
Member

samreid commented Sep 19, 2022

@marlitas and I will try a first draft of this today.

@samreid
Copy link
Member

samreid commented Sep 19, 2022

@marlitas and I implemented the first step by making things no longer read only. We have no confidence that we have covered model elements that do not appear in gravity and orbits, and we are considering adding a post start up a session check that all nested elements under the preferences model are marked as read only false. We think this would take 30 minutes or so but would protect occurrences outside gravity and orbits, and all future occurrences. But if one day, we decide to make even one preferences model property as phetioReadOnly: true then we would have to discard the assertion, so it may have limited use.

@samreid
Copy link
Member

samreid commented Sep 21, 2022

I completed the assertions that @marlitas and I started. I moved the remaining issue to #864. I think this issue can be closed. We may discover details during design review or QA testing, in that case, we can reopen or create side issues.

@zepumph
Copy link
Member Author

zepumph commented Nov 8, 2022

Looks like voiceVolumeProperty should not be instrumented.

@zepumph zepumph reopened this Nov 8, 2022
@zepumph zepumph assigned zepumph and unassigned samreid Nov 8, 2022
zepumph added a commit to phetsims/utterance-queue that referenced this issue Nov 8, 2022
zepumph added a commit to phetsims/scenery that referenced this issue Nov 8, 2022
@zepumph
Copy link
Member Author

zepumph commented Nov 8, 2022

Just a couple more tweaks in voicing implementation from a sloppy first pass (sorry!)

@zepumph zepumph closed this as completed Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants