diff --git a/js/demo/testing/model/AmplitudeModulatorDemo.ts b/js/demo/testing/model/AmplitudeModulatorDemo.ts index 4af073b7..c217b655 100644 --- a/js/demo/testing/model/AmplitudeModulatorDemo.ts +++ b/js/demo/testing/model/AmplitudeModulatorDemo.ts @@ -1,5 +1,13 @@ // Copyright 2020-2022, University of Colorado Boulder +/** + * Demo and test harness for the AmplitudeModulator class. This creates several sound loops and routes them through + * an amplitude modulator instance. It also provides the properties that can be manipulated in the view to change the + * attributes of the modulation. + * + * @author John Blanco (PhET Interactive Simulations) + */ + import saturatedSineLoop220Hz_mp3 from '../../../../sounds/saturatedSineLoop220Hz_mp3.js'; import windsLoopC3Oscilloscope_mp3 from '../../../../sounds/demo-and-test/windsLoopC3Oscilloscope_mp3.js'; import windsLoopMiddleCOscilloscope_mp3 from '../../../../sounds/demo-and-test/windsLoopMiddleCOscilloscope_mp3.js'; @@ -9,18 +17,11 @@ import SoundGenerator, { SoundGeneratorOptions } from '../../../sound-generators import tambo from '../../../tambo.js'; import NumberProperty from '../../../../../axon/js/NumberProperty.js'; -/** - * Demo and test harness for the AmplitudeModulator class. This creates several sound loops and routes them through - * an amplitude modulator instance. It also provides the properties that can be manipulated in the view to change the - * attributes of the modulation. - */ - - class AmplitudeModulatorDemo extends SoundGenerator { public readonly amplitudeModulator: AmplitudeModulator; - constructor( sourceSoundIndexProperty: NumberProperty, options: SoundGeneratorOptions ) { + constructor( sourceSoundIndexProperty: NumberProperty, options?: SoundGeneratorOptions ) { super( options ); diff --git a/js/demo/testing/view/AmplitudeModulatorDemoNode.ts b/js/demo/testing/view/AmplitudeModulatorDemoNode.ts index d6b161aa..89a2dc99 100644 --- a/js/demo/testing/view/AmplitudeModulatorDemoNode.ts +++ b/js/demo/testing/view/AmplitudeModulatorDemoNode.ts @@ -1,14 +1,17 @@ // Copyright 2020-2021, University of Colorado Boulder -// @ts-nocheck +/** + * View portion of the demo and test harness for the AmplitudeModulator class. + * + * @author John Blanco (PhET Interactive Simulations) + */ +import BooleanProperty from '../../../../../axon/js/BooleanProperty.js'; import NumberProperty from '../../../../../axon/js/NumberProperty.js'; import Range from '../../../../../dot/js/Range.js'; -import merge from '../../../../../phet-core/js/merge.js'; +import optionize from '../../../../../phet-core/js/optionize.js'; import PhetFont from '../../../../../scenery-phet/js/PhetFont.js'; -import { HBox } from '../../../../../scenery/js/imports.js'; -import { Text } from '../../../../../scenery/js/imports.js'; -import { VBox } from '../../../../../scenery/js/imports.js'; +import { HBox, Text, VBox, VBoxOptions } from '../../../../../scenery/js/imports.js'; import AquaRadioButtonGroup from '../../../../../sun/js/AquaRadioButtonGroup.js'; import Checkbox from '../../../../../sun/js/Checkbox.js'; import HSlider from '../../../../../sun/js/HSlider.js'; @@ -16,15 +19,15 @@ import soundManager from '../../../soundManager.js'; import tambo from '../../../tambo.js'; import AmplitudeModulatorDemo from '../model/AmplitudeModulatorDemo.js'; +type SelfOptions = {}; +export type AmplitudeModulatorDemoNodeOptions = SelfOptions & VBoxOptions; + // constants const LABEL_FONT = new PhetFont( 16 ); -/** - * View portion of the demo and test harness for the AmplitudeModulator class. - */ class AmplitudeModulatorDemoNode extends VBox { - constructor( options ) { + constructor( providedOptions: AmplitudeModulatorDemoNodeOptions ) { const soundSourceRadioButtonItems = [ { @@ -62,7 +65,7 @@ class AmplitudeModulatorDemoNode extends VBox { // LFO enabled control const lfoEnabled = new Checkbox( new Text( 'LFO Enabled', { font: LABEL_FONT } ), - amplitudeModulatorDemo.amplitudeModulator.enabledProperty, + amplitudeModulatorDemo.amplitudeModulator.enabledProperty as BooleanProperty, { boxWidth: 16 } ); @@ -120,12 +123,11 @@ class AmplitudeModulatorDemoNode extends VBox { spacing: 5 } ); - - super( merge( { + super( optionize( { children: [ soundIndexSelectorVBox, lfoEnabled, frequencyControlHBox, depthControlHBox, waveformSelectorVBox ], spacing: 15, align: 'left' - }, options ) ); + }, providedOptions ) ); } } diff --git a/js/demo/testing/view/CompositeSoundClipTestNode.ts b/js/demo/testing/view/CompositeSoundClipTestNode.ts index 9394c1fc..75fc9e9a 100644 --- a/js/demo/testing/view/CompositeSoundClipTestNode.ts +++ b/js/demo/testing/view/CompositeSoundClipTestNode.ts @@ -1,7 +1,5 @@ // Copyright 2020-2022, University of Colorado Boulder -// @ts-nocheck - /** * Test and demo of the CompositeSoundClip. * @@ -10,7 +8,7 @@ import merge from '../../../../../phet-core/js/merge.js'; import PhetFont from '../../../../../scenery-phet/js/PhetFont.js'; -import { VBox } from '../../../../../scenery/js/imports.js'; +import { VBox, VBoxOptions } from '../../../../../scenery/js/imports.js'; import TextPushButton from '../../../../../sun/js/buttons/TextPushButton.js'; import brightMarimba_mp3 from '../../../../sounds/brightMarimba_mp3.js'; import loonCall_mp3 from '../../../../sounds/demo-and-test/loonCall_mp3.js'; @@ -21,10 +19,10 @@ import tambo from '../../../tambo.js'; class CompositeSoundClipTestNode extends VBox { - /** - * @param {Object} [options] - */ - constructor( options ) { + // dispose function + private readonly disposeCompositeSoundClipTestNode: () => void; + + constructor( options: VBoxOptions ) { // sound clips to be played const compositeSoundClip = new CompositeSoundClip( [ diff --git a/js/demo/testing/view/ContinuousPropertySoundGeneratorTestNode.ts b/js/demo/testing/view/ContinuousPropertySoundGeneratorTestNode.ts index 2b4d9e08..89f442a1 100644 --- a/js/demo/testing/view/ContinuousPropertySoundGeneratorTestNode.ts +++ b/js/demo/testing/view/ContinuousPropertySoundGeneratorTestNode.ts @@ -1,7 +1,5 @@ // Copyright 2020-2022, University of Colorado Boulder -// @ts-nocheck - /** * Test and demo of the ContinuousPropertySoundGenerator. * @@ -14,8 +12,7 @@ import NumberProperty from '../../../../../axon/js/NumberProperty.js'; import Range from '../../../../../dot/js/Range.js'; import merge from '../../../../../phet-core/js/merge.js'; import NumberControl from '../../../../../scenery-phet/js/NumberControl.js'; -import { Text } from '../../../../../scenery/js/imports.js'; -import { VBox } from '../../../../../scenery/js/imports.js'; +import { Text, VBox, VBoxOptions } from '../../../../../scenery/js/imports.js'; import Checkbox from '../../../../../sun/js/Checkbox.js'; import Panel from '../../../../../sun/js/Panel.js'; import saturatedSineLoop220Hz_mp3 from '../../../../sounds/saturatedSineLoop220Hz_mp3.js'; @@ -25,31 +22,36 @@ import windsLoopMiddleCOscilloscope_mp3 from '../../../../sounds/demo-and-test/w import ContinuousPropertySoundGenerator from '../../../sound-generators/ContinuousPropertySoundGenerator.js'; import soundManager from '../../../soundManager.js'; import tambo from '../../../tambo.js'; +import Emitter from '../../../../../axon/js/Emitter.js'; +import WrappedAudioBuffer from '../../../WrappedAudioBuffer.js'; + +type SelfOptions = {}; +export type ContinuousPropertySoundGeneratorTestNodeOptions = SelfOptions & VBoxOptions; class ContinuousPropertySoundGeneratorTestNode extends VBox { - /** - * @param {Emitter} stepEmitter - * @param {Object} [options] - */ - constructor( stepEmitter, options ) { + // dispose function + private readonly disposeContinuousPropertySoundGeneratorTestNode: () => void; + + constructor( stepEmitter: Emitter<[ number ]>, providedOptions: ContinuousPropertySoundGeneratorTestNodeOptions ) { // keep track of listeners added to the step emitter so that they can be disposed - const stepListeners = []; + const stepListeners: ( ( dt: number ) => void )[] = []; // creates a panel that demonstrates a ContinuousPropertySoundGenerator - const createTester = ( sound, max ) => { + const createTester = ( sound: WrappedAudioBuffer, max: number ) => { const numberProperty = new NumberProperty( 5 ); const range = new Range( 1, 10 ); const continuousPropertySoundGenerator = new ContinuousPropertySoundGenerator( numberProperty, sound, + // @ts-ignore range ); soundManager.addSoundGenerator( continuousPropertySoundGenerator ); const isOscillatingProperty = new BooleanProperty( false ); let phase = 0; - const stepListener = dt => { + const stepListener = ( dt: number ) => { if ( isOscillatingProperty.value ) { numberProperty.value = ( max * Math.sin( Date.now() / 1000 - phase ) + 1 ) * ( range.max - range.min ) / 2 + range.min; } @@ -81,9 +83,9 @@ class ContinuousPropertySoundGeneratorTestNode extends VBox { ], spacing: 15, align: 'left' - }, options ) ); + }, providedOptions ) ); - // @private - for memory cleanup + // define dispose function for memory cleanup this.disposeContinuousPropertySoundGeneratorTestNode = () => { stepListeners.forEach( listener => { stepEmitter.removeListener( listener ); diff --git a/js/demo/testing/view/RemoveAndDisposeSoundGeneratorsTestPanel.ts b/js/demo/testing/view/RemoveAndDisposeSoundGeneratorsTestPanel.ts index f0b8a2c9..c02af826 100644 --- a/js/demo/testing/view/RemoveAndDisposeSoundGeneratorsTestPanel.ts +++ b/js/demo/testing/view/RemoveAndDisposeSoundGeneratorsTestPanel.ts @@ -1,7 +1,5 @@ // Copyright 2018-2022, University of Colorado Boulder -// @ts-nocheck - /** * a panel that contains controls used to exercise the addition, removal, and disposal of sound generators * @@ -12,23 +10,28 @@ import createObservableArray from '../../../../../axon/js/createObservableArray. import Property from '../../../../../axon/js/Property.js'; import stepTimer from '../../../../../axon/js/stepTimer.js'; import dotRandom from '../../../../../dot/js/dotRandom.js'; -import merge from '../../../../../phet-core/js/merge.js'; +import optionize from '../../../../../phet-core/js/optionize.js'; import StringUtils from '../../../../../phetcommon/js/util/StringUtils.js'; import PhetFont from '../../../../../scenery-phet/js/PhetFont.js'; -import { HBox } from '../../../../../scenery/js/imports.js'; -import { Node } from '../../../../../scenery/js/imports.js'; -import { Text } from '../../../../../scenery/js/imports.js'; -import { VBox } from '../../../../../scenery/js/imports.js'; +import { HBox, Node, Text, VBox } from '../../../../../scenery/js/imports.js'; import TextPushButton from '../../../../../sun/js/buttons/TextPushButton.js'; import ComboBox from '../../../../../sun/js/ComboBox.js'; import ComboBoxItem from '../../../../../sun/js/ComboBoxItem.js'; -import Panel from '../../../../../sun/js/Panel.js'; +import Panel, { PanelOptions } from '../../../../../sun/js/Panel.js'; import birdCall_mp3 from '../../../../sounds/demo-and-test/birdCall_mp3.js'; import cricketsLoop_mp3 from '../../../../sounds/demo-and-test/cricketsLoop_mp3.js'; import PitchedPopGenerator from '../../../sound-generators/PitchedPopGenerator.js'; import SoundClip from '../../../sound-generators/SoundClip.js'; import soundManager from '../../../soundManager.js'; import tambo from '../../../tambo.js'; +import SoundGenerator from '../../../sound-generators/SoundGenerator.js'; + +type SelfOptions = {}; +export type RemoveAndDisposeSoundGeneratorsTestPanelOptions = SelfOptions & PanelOptions; +type SoundGeneratorComboBoxItemInfo = { + comboBoxItemName: string; + createSoundGenerator: () => SoundGenerator; +} // constants const BUTTON_FONT = new PhetFont( 18 ); @@ -37,37 +40,42 @@ const TOTAL_ADDED_TEMPLATE = 'Total Added: {{numSoundGenerators}}'; const ADD_BUTTON_COLOR = '#C0D890'; // info needed for selecting and using different sound generators from the combo box -const SOUND_GENERATOR_INFO = { - recordedOneShot: { - comboBoxName: 'Recorded one shot', - createSoundGenerator: () => new SoundClip( birdCall_mp3 ) - }, - recordedLoop: { - comboBoxName: 'Recorded loop', - createSoundGenerator: () => new SoundClip( cricketsLoop_mp3, { loop: true } ) - }, - synthesizedSound: { - comboBoxName: 'Synthesized sound', - createSoundGenerator: () => new PitchedPopGenerator( { numPopGenerators: 2 } ) - } -}; +const SOUND_GENERATOR_INFO = new Map( [ + [ + 'recordedOneShot', + { + comboBoxItemName: 'Recorded one shot', + createSoundGenerator: () => new SoundClip( birdCall_mp3 ) + } + ], + [ + 'recordedLoop', + { + comboBoxItemName: 'Recorded loop', + createSoundGenerator: () => new SoundClip( cricketsLoop_mp3, { loop: true } ) + } + ], + [ + 'synthesizedSound', + { + comboBoxItemName: 'Synthesized sound', + createSoundGenerator: () => new PitchedPopGenerator( { numPopGenerators: 2 } ) + } + ] +] ); class RemoveAndDisposeSoundGeneratorsTestPanel extends Panel { - /** - * @param {Object} [options] - * @constructor - */ - constructor( options ) { + constructor( providedOptions: RemoveAndDisposeSoundGeneratorsTestPanelOptions ) { - options = merge( { + const options = optionize( { fill: '#f5d3b3', xMargin: 14, yMargin: 14 - }, options ); + }, providedOptions ); // array of sound generators that have been added and not yet removed and disposed - const soundGenerators = createObservableArray(); + const soundGenerators = createObservableArray(); // node where the content goes, needed so that ComboBox will have a good place to put its list const panelContentNode = new Node(); @@ -77,11 +85,11 @@ class RemoveAndDisposeSoundGeneratorsTestPanel extends Panel { font: new PhetFont( { size: 18, weight: 'bold' } ) } ); - // create the combo box for selecting the type of sound generator to add - const comboBoxItems = []; - _.keys( SOUND_GENERATOR_INFO ).forEach( soundGeneratorKey => { + // Create the combo box for selecting the type of sound generator to add. + const comboBoxItems: ComboBoxItem[] = []; + SOUND_GENERATOR_INFO.forEach( ( soundGenerator, soundGeneratorKey ) => { comboBoxItems.push( new ComboBoxItem( - new Text( SOUND_GENERATOR_INFO[ soundGeneratorKey ].comboBoxName, { font: COMBO_BOX_FONT } ), + new Text( soundGenerator.comboBoxItemName, { font: COMBO_BOX_FONT } ), soundGeneratorKey ) ); } ); @@ -97,9 +105,9 @@ class RemoveAndDisposeSoundGeneratorsTestPanel extends Panel { spacing: 7 } ); - function addSoundGenerators( numToAdd ) { - _.times( numToAdd, () => { - const soundGenerator = SOUND_GENERATOR_INFO[ selectedSoundGeneratorTypeProperty.value ].createSoundGenerator(); + function addSoundGenerators( numberToAdd: number ) { + _.times( numberToAdd, () => { + const soundGenerator = SOUND_GENERATOR_INFO.get( selectedSoundGeneratorTypeProperty.value )!.createSoundGenerator(); soundManager.addSoundGenerator( soundGenerator ); soundGenerators.push( soundGenerator ); } ); diff --git a/js/demo/testing/view/SoundClipChordTestNode.ts b/js/demo/testing/view/SoundClipChordTestNode.ts index 5872ae9d..afa23ed1 100644 --- a/js/demo/testing/view/SoundClipChordTestNode.ts +++ b/js/demo/testing/view/SoundClipChordTestNode.ts @@ -1,28 +1,29 @@ // Copyright 2020-2022, University of Colorado Boulder -// @ts-nocheck +/** + * Test and demo of the SoundClipChord. + * + * @author Michael Kauzamann (PhET Interactive Simulations) + */ -import merge from '../../../../../phet-core/js/merge.js'; import PhetFont from '../../../../../scenery-phet/js/PhetFont.js'; -import { VBox } from '../../../../../scenery/js/imports.js'; +import { VBox, VBoxOptions } from '../../../../../scenery/js/imports.js'; import TextPushButton from '../../../../../sun/js/buttons/TextPushButton.js'; import brightMarimba_mp3 from '../../../../sounds/brightMarimba_mp3.js'; import SoundPlayer from '../../../SoundPlayer.js'; import SoundClipChord from '../../../sound-generators/SoundClipChord.js'; import soundManager from '../../../soundManager.js'; import tambo from '../../../tambo.js'; +import optionize from '../../../../../phet-core/js/optionize.js'; + +type SelfOptions = {}; +export type SoundClipChordTestNodeOptions = SelfOptions & VBoxOptions; -/** - * Test and demo of the SoundClipChord. - * - * @author Michael Kauzamann (PhET Interactive Simulations) - */ class SoundClipChordTestNode extends VBox { - /** - * @param {Object} [options] - */ - constructor( options ) { + private readonly disposeSoundClipChordTestNode: () => void; + + constructor( providedOptions: SoundClipChordTestNodeOptions ) { // sound clips to be played const chordSoundClipChord = new SoundClipChord( brightMarimba_mp3 ); @@ -46,10 +47,10 @@ class SoundClipChordTestNode extends VBox { listener: () => { arpeggioSoundClipChord.play(); } } ); - super( merge( { + super( optionize( { children: [ playBasicSoundButton, playEnhancedSoundButton ], spacing: 20 - }, options ) ); + }, providedOptions ) ); // @private - dispose function this.disposeSoundClipChordTestNode = () => { @@ -70,4 +71,5 @@ class SoundClipChordTestNode extends VBox { } tambo.register( 'SoundClipChordTestNode', SoundClipChordTestNode ); + export default SoundClipChordTestNode; \ No newline at end of file diff --git a/js/demo/testing/view/TestingScreenView.ts b/js/demo/testing/view/TestingScreenView.ts index 8895080a..62466d0e 100644 --- a/js/demo/testing/view/TestingScreenView.ts +++ b/js/demo/testing/view/TestingScreenView.ts @@ -1,7 +1,5 @@ // Copyright 2018-2022, University of Colorado Boulder -// @ts-nocheck - /** * view for a screen that allows testing and demonstration of various sound components and behaviors * @@ -15,10 +13,10 @@ import stepTimer from '../../../../../axon/js/stepTimer.js'; import merge from '../../../../../phet-core/js/merge.js'; import ResetAllButton from '../../../../../scenery-phet/js/buttons/ResetAllButton.js'; import PhetFont from '../../../../../scenery-phet/js/PhetFont.js'; -import { HBox, Image, Node, Text, VBox } from '../../../../../scenery/js/imports.js'; +import { HBox, Image, Node, NodeOptions, Text, VBox, VBoxOptions } from '../../../../../scenery/js/imports.js'; import TextPushButton from '../../../../../sun/js/buttons/TextPushButton.js'; import Checkbox from '../../../../../sun/js/Checkbox.js'; -import DemosScreenView from '../../../../../sun/js/demo/DemosScreenView.js'; +import DemosScreenView, { SunDemo } from '../../../../../sun/js/demo/DemosScreenView.js'; import Panel from '../../../../../sun/js/Panel.js'; import lightning_png from '../../../../images/lightning_png.js'; import checkboxChecked_mp3 from '../../../../sounds/checkboxChecked_mp3.js'; @@ -37,6 +35,8 @@ import CompositeSoundClipTestNode from './CompositeSoundClipTestNode.js'; import ContinuousPropertySoundGeneratorTestNode from './ContinuousPropertySoundGeneratorTestNode.js'; import RemoveAndDisposeSoundGeneratorsTestPanel from './RemoveAndDisposeSoundGeneratorsTestPanel.js'; import SoundClipChordTestNode from './SoundClipChordTestNode.js'; +import Bounds2 from '../../../../../dot/js/Bounds2.js'; +import { TimerListener } from '../../../../../axon/js/Timer.js'; // constants const CHECKBOX_SIZE = 16; @@ -45,6 +45,8 @@ const LIGHTNING_SHOWN_TIME = 0.750; // in seconds class TestingScreenView extends DemosScreenView { + private readonly stepEmitter: Emitter<[ number ]>; + /** * @constructor */ @@ -53,52 +55,52 @@ class TestingScreenView extends DemosScreenView { const resetInProgressProperty = new BooleanProperty( false ); // demo items, selected via the combo box in the parent class - const demos = [ + const demos: SunDemo[] = [ { label: 'AmplitudeModulatorTest', - createNode: layoutBounds => new AmplitudeModulatorDemoNode( { + createNode: ( layoutBounds: Bounds2 ) => new AmplitudeModulatorDemoNode( { center: layoutBounds.center } ) }, { label: 'AdditionalAudioNodesTestNode', - createNode: layoutBounds => new AdditionalAudioNodesTestNode( { + createNode: ( layoutBounds: Bounds2 ) => new AdditionalAudioNodesTestNode( { center: layoutBounds.center } ) }, { label: 'BasicAndEnhancedSounds', - createNode: layoutBounds => new BasicAndEnhancedSoundTestNode( { + createNode: ( layoutBounds: Bounds2 ) => new BasicAndEnhancedSoundTestNode( { center: layoutBounds.center } ) }, { label: 'ContinuousPropertySoundGeneratorTest', - createNode: layoutBounds => new ContinuousPropertySoundGeneratorTestNode( this.stepEmitter, { + createNode: ( layoutBounds: Bounds2 ) => new ContinuousPropertySoundGeneratorTestNode( this.stepEmitter, { center: layoutBounds.center } ) }, { label: 'CompositeSoundClipTestNode', - createNode: layoutBounds => new CompositeSoundClipTestNode( { + createNode: ( layoutBounds: Bounds2 ) => new CompositeSoundClipTestNode( { center: layoutBounds.center } ) }, { label: 'RemoveAndDisposeSoundGenerators', - createNode: layoutBounds => new RemoveAndDisposeSoundGeneratorsTestPanel( { + createNode: ( layoutBounds: Bounds2 ) => new RemoveAndDisposeSoundGeneratorsTestPanel( { center: layoutBounds.center } ) }, { label: 'LongSoundTest', - createNode: layoutBounds => new LongSoundTestPanel( resetInProgressProperty, { + createNode: ( layoutBounds: Bounds2 ) => new LongSoundTestPanel( resetInProgressProperty, { center: layoutBounds.center } ) }, { label: 'SoundClipChordTestNode', - createNode: layoutBounds => new SoundClipChordTestNode( { + createNode: ( layoutBounds: Bounds2 ) => new SoundClipChordTestNode( { center: layoutBounds.center } ) } @@ -123,12 +125,7 @@ class TestingScreenView extends DemosScreenView { this.addChild( resetAllButton ); } - /** - * @override - * @param {number} dt - * @public - */ - step( dt ) { + public step( dt: number ) { this.stepEmitter.emit( dt ); } } @@ -138,7 +135,9 @@ class TestingScreenView extends DemosScreenView { */ class BasicAndEnhancedSoundTestNode extends VBox { - constructor( options ) { + private readonly disposeBasicAndEnhancedSoundTestNode: () => void; + + constructor( options: VBoxOptions ) { // sound clips to be played const loonCallSoundClip = new SoundClip( loonCall_mp3 ); @@ -191,7 +190,9 @@ class BasicAndEnhancedSoundTestNode extends VBox { */ class AdditionalAudioNodesTestNode extends VBox { - constructor( options ) { + private readonly disposeBasicAndEnhancedSoundTestNode: () => void; + + constructor( options: VBoxOptions ) { // convolver node, which will be used to create the reverb effect const convolver = phetAudioContext.createConvolver(); @@ -265,11 +266,13 @@ class AdditionalAudioNodesTestNode extends VBox { */ class LongSoundTestPanel extends Node { - constructor( resetInProgressProperty, options ) { + private readonly disposeLongSoundTestPanel: () => void; + + constructor( resetInProgressProperty: BooleanProperty, options: NodeOptions ) { // internal state variables const lightningBoltVisibleProperty = new BooleanProperty( false ); - let lightningBoltVisibleTimeout = null; + let lightningBoltVisibleTimeout: null | TimerListener = null; // timeout function const timeoutFiredListener = () => { @@ -378,4 +381,5 @@ class LongSoundTestPanel extends Node { } tambo.register( 'TestingScreenView', TestingScreenView ); + export default TestingScreenView; \ No newline at end of file