diff --git a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx index 1400651dcc5..ea3e46ebc29 100644 --- a/packages/scratch-gui/src/lib/vm-listener-hoc.jsx +++ b/packages/scratch-gui/src/lib/vm-listener-hoc.jsx @@ -1,7 +1,7 @@ import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import React from 'react'; -import VM from '@scratch/scratch-vm'; +import VM from 'scratch-vm'; import {connect} from 'react-redux'; @@ -10,7 +10,7 @@ import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; import {setProjectChanged, setProjectUnchanged} from '../reducers/project-changed'; import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; -import {showExtensionAlert, showStandardAlert, closeAlertWithId} from '../reducers/alerts'; +import {showExtensionAlert} from '../reducers/alerts'; import {updateMicIndicator} from '../reducers/mic-indicator'; /* @@ -46,7 +46,6 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('PROJECT_START', this.props.onGreenFlag); this.props.vm.on('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); - this.props.vm.on('EXTENSION_DATA_LOADING', this.props.onExtensionDataLoading); } componentDidMount () { @@ -68,20 +67,7 @@ const vmListenerHOC = function (WrappedComponent) { } } componentWillUnmount () { - this.props.vm.removeListener('targetsUpdate', this.handleTargetsUpdate); - this.props.vm.removeListener('MONITORS_UPDATE', this.props.onMonitorsUpdate); - this.props.vm.removeListener('BLOCK_DRAG_UPDATE', this.props.onBlockDragUpdate); - this.props.vm.removeListener('TURBO_MODE_ON', this.props.onTurboModeOn); - this.props.vm.removeListener('TURBO_MODE_OFF', this.props.onTurboModeOff); - this.props.vm.removeListener('PROJECT_RUN_START', this.props.onProjectRunStart); - this.props.vm.removeListener('PROJECT_RUN_STOP', this.props.onProjectRunStop); - this.props.vm.removeListener('PROJECT_CHANGED', this.handleProjectChanged); - this.props.vm.removeListener('RUNTIME_STARTED', this.props.onRuntimeStarted); - this.props.vm.removeListener('PROJECT_START', this.props.onGreenFlag); this.props.vm.removeListener('PERIPHERAL_CONNECTION_LOST_ERROR', this.props.onShowExtensionAlert); - this.props.vm.removeListener('MIC_LISTENING', this.props.onMicListeningUpdate); - this.props.vm.removeListener('EXTENSION_DATA_LOADING', this.props.onExtensionDataLoading); - if (this.props.attachKeyboardEvents) { document.removeEventListener('keydown', this.handleKeyDown); document.removeEventListener('keyup', this.handleKeyUp); @@ -98,14 +84,9 @@ const vmListenerHOC = function (WrappedComponent) { } } handleKeyDown (e) { - // Don't capture keys intended for HTML inputs (e.g. project title). - // The Blockly workspace is rendered as SVG, so SVG-targeted events - // should always reach the VM for key-sensing — even when a block has - // Blockly focus — so that game controls are never silently dropped - // while the user is on the Code tab. - if (e.target !== document && e.target !== document.body) { - if (!(e.target instanceof SVGElement)) return; - } + // Don't capture keys intended for Blockly inputs. + if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || + e.target.isContentEditable) return; const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key; this.props.vm.postIOData('keyboard', { @@ -135,7 +116,7 @@ const vmListenerHOC = function (WrappedComponent) { } render () { const { - + /* eslint-disable no-unused-vars */ attachKeyboardEvents, projectChanged, shouldUpdateTargets, @@ -145,7 +126,6 @@ const vmListenerHOC = function (WrappedComponent) { onKeyDown, onKeyUp, onMicListeningUpdate, - onExtensionDataLoading, onMonitorsUpdate, onTargetsUpdate, onProjectChanged, @@ -156,7 +136,7 @@ const vmListenerHOC = function (WrappedComponent) { onTurboModeOff, onTurboModeOn, onShowExtensionAlert, - + /* eslint-enable no-unused-vars */ ...props } = this.props; return ; @@ -165,7 +145,6 @@ const vmListenerHOC = function (WrappedComponent) { VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, onBlockDragUpdate: PropTypes.func.isRequired, - onExtensionDataLoading: PropTypes.func.isRequired, onGreenFlag: PropTypes.func, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, @@ -190,7 +169,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: true, onGreenFlag: () => ({}) }; - const mapStateToProps = (state, ownProps) => ({ + const mapStateToProps = state => ({ projectChanged: state.scratchGui.projectChanged, // Do not emit target or project updates in fullscreen or player only mode // or when recording sounds (it leads to garbled recordings on low-power machines) @@ -199,11 +178,8 @@ const vmListenerHOC = function (WrappedComponent) { // Do not update the projectChanged state in fullscreen or player only mode shouldUpdateProjectChanged: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly, vm: state.scratchGui.vm, - username: ownProps.username ?? ( - state.session && state.session.session && state.session.session.user ? - state.session.session.user.username : - '' - ) + username: state.session && state.session.session && state.session.session.user ? + state.session.session.user.username : '' }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { @@ -227,13 +203,6 @@ const vmListenerHOC = function (WrappedComponent) { }, onMicListeningUpdate: listening => { dispatch(updateMicIndicator(listening)); - }, - onExtensionDataLoading: loading => { - if (loading) { - dispatch(showStandardAlert('loadingExtensionData')); - } else { - dispatch(closeAlertWithId('loadingExtensionData')); - } } }); return connect( diff --git a/packages/scratch-gui/test/unit/util/vm-listener-hoc.test.jsx b/packages/scratch-gui/test/unit/util/vm-listener-hoc.test.jsx index 4fd40669738..87c2785678b 100644 --- a/packages/scratch-gui/test/unit/util/vm-listener-hoc.test.jsx +++ b/packages/scratch-gui/test/unit/util/vm-listener-hoc.test.jsx @@ -1,10 +1,9 @@ import React from 'react'; import configureStore from 'redux-mock-store'; -import {render} from '@testing-library/react'; -import VM from '@scratch/scratch-vm'; +import {mount} from 'enzyme'; +import VM from 'scratch-vm'; import vmListenerHOC from '../../../src/lib/vm-listener-hoc.jsx'; -import '@testing-library/jest-dom'; describe('VMListenerHOC', () => { const mockStore = configureStore(); @@ -26,7 +25,7 @@ describe('VMListenerHOC', () => { const Component = () => (
); const WrappedComponent = vmListenerHOC(Component); const onGreenFlag = jest.fn(); - render( + mount( { }); test('onGreenFlag is not passed to the children', () => { - const Component = ({onGreenFlag}) => ( -
{`${onGreenFlag ? - onGreenFlag() : - onGreenFlag - }`}
- ); + const Component = () => (
); const WrappedComponent = vmListenerHOC(Component); - const {container} = render( + const wrapper = mount( ); - const element = container.querySelector('#onGreenFlag'); - expect(element).toHaveTextContent(/undefined/i); + const child = wrapper.find(Component); + expect(child.props().onGreenFlag).toBeUndefined(); }); test('targetsUpdate event from vm triggers targets update action', () => { const Component = () => (
); const WrappedComponent = vmListenerHOC(Component); - render( + mount( { vm: vm } }); - render( + mount( { vm: vm } }); - render( + mount( { vm: vm } }); - render( + mount( { vm: vm } }); - render( + mount( { /> ); - // keydown with an HTML target (e.g. project title input) should not be forwarded to VM - const inputEl = document.createElement('input'); - eventTriggers.keydown({key: 'A', target: inputEl}); + // keyboard events in text inputs are ignored + eventTriggers.keydown({key: 'A', target: document.createElement('input')}); expect(vm.postIOData).not.toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true}); - // keydown with an SVG target (Blockly workspace) should always be forwarded to VM - // even when a block has Blockly focus, so game controls work from the Code tab - const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - eventTriggers.keydown({key: 'A', target: svgEl}); + // keydown with other non-input targets are sent to the vm via postIOData + eventTriggers.keydown({key: 'A', target: document.createElement('div')}); expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true}); // keydown/up with target as the document are sent to the vm via postIOData