diff --git a/src/renderers/native/__tests__/ReactNativeEvents-test.js b/src/renderers/native/__tests__/ReactNativeEvents-test.js index c6d0085a59..783f4bab16 100644 --- a/src/renderers/native/__tests__/ReactNativeEvents-test.js +++ b/src/renderers/native/__tests__/ReactNativeEvents-test.js @@ -87,6 +87,93 @@ it('handles events', () => { ]); }); +it('handles events on text nodes', () => { + expect(RCTEventEmitter.register.mock.calls.length).toBe(1); + var EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; + + var Text = createReactNativeComponentClass({ + validAttributes: {foo: true}, + uiViewClassName: 'Text', + }); + + class ContextHack extends React.Component { + static childContextTypes = {isInAParentText: React.PropTypes.bool}; + getChildContext() { + return {isInAParentText: true}; + } + render() { + return this.props.children; + } + } + + var log = []; + ReactNative.render( + + + log.push('string touchend')} + onTouchEndCapture={() => log.push('string touchend capture')} + onTouchStart={() => log.push('string touchstart')} + onTouchStartCapture={() => log.push('string touchstart capture')}> + Text Content + + log.push('number touchend')} + onTouchEndCapture={() => log.push('number touchend capture')} + onTouchStart={() => log.push('number touchstart')} + onTouchStartCapture={() => log.push('number touchstart capture')}> + {123} + + + , + 1, + ); + + expect(UIManager.createView.mock.calls.length).toBe(5); + + // Don't depend on the order of createView() calls. + // Stack creates views outside-in; fiber creates them inside-out. + var innerTagString = UIManager.createView.mock.calls.find( + args => args[3] && args[3].text === 'Text Content', + )[0]; + var innerTagNumber = UIManager.createView.mock.calls.find( + args => args[3] && args[3].text === '123', + )[0]; + + EventEmitter.receiveTouches( + 'topTouchStart', + [{target: innerTagString, identifier: 17}], + [0], + ); + EventEmitter.receiveTouches( + 'topTouchEnd', + [{target: innerTagString, identifier: 17}], + [0], + ); + + EventEmitter.receiveTouches( + 'topTouchStart', + [{target: innerTagNumber, identifier: 18}], + [0], + ); + EventEmitter.receiveTouches( + 'topTouchEnd', + [{target: innerTagNumber, identifier: 18}], + [0], + ); + + expect(log).toEqual([ + 'string touchstart capture', + 'string touchstart', + 'string touchend capture', + 'string touchend', + 'number touchstart capture', + 'number touchstart', + 'number touchend capture', + 'number touchend', + ]); +}); + it('handles when a responder is unmounted while a touch sequence is in progress', () => { var EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; var View = createReactNativeComponentClass({ diff --git a/src/renderers/shared/shared/event/EventPluginHub.js b/src/renderers/shared/shared/event/EventPluginHub.js index 7ed1950708..ac723ff890 100644 --- a/src/renderers/shared/shared/event/EventPluginHub.js +++ b/src/renderers/shared/shared/event/EventPluginHub.js @@ -138,7 +138,10 @@ var EventPluginHub = { return null; } } else { - if (typeof inst._currentElement === 'string') { + const currentElement = inst._currentElement; + if ( + typeof currentElement === 'string' || typeof currentElement === 'number' + ) { // Text node, let it bubble through. return null; } @@ -146,14 +149,10 @@ var EventPluginHub = { // If the instance is already unmounted, we have no listeners. return null; } - const props = inst._currentElement.props; + const props = currentElement.props; listener = props[registrationName]; if ( - shouldPreventMouseEvent( - registrationName, - inst._currentElement.type, - props, - ) + shouldPreventMouseEvent(registrationName, currentElement.type, props) ) { return null; }