Change:
Ref:
I'm taking a look at this as a first bug - from the docs and some testing, the event seems to be fired at the form element level rather than the form itself. That is to say: will not log on submit, but: will log on submit. Does this match the expected behaviour, and if so do we want the handler to be on the individual React.DOM form elements (i.e. so it is called when that element is invalid), or do we want it to bubble up to the parent (i.e. so it is called whenever at least one element in that form is invalid)?
We should match the DOM and fire it on the element (not the form). We already have support for these events which don't bubble. Most of our events are at the document level and then delegated but the ones which don't bubble need to be to specific elements on creation. Here's how we're doing it for forms and the submit & reset events: Thanks for taking this on! Feel free to get a PR up before it's completely ready if you have questions / need help.
Thanks Paul, good to know I was looking in the right place! Will update when I have had time to create a PR or if I need any help :) Cheers Tom
Hey, I've put together a PR at - it would be great to get your feedback on it. A couple of specific questions I had: should I handle automated testing of this? There's a lot of test code to look at and I'm unfamiliar with the React codebase, so I'm unsure if/how this should be tested. the splitting out of the logic make sense? Or do you think it would be better to have a separate statement for this bit, preceeding the other in , matching all the elements which need this setup, and then keep the other as is (minus the first ) Any feedback appreciated, this does seem to work but I'm totally new to the React code so not sure if the way I've gone about it is suitable or not! Cheers, Tom
Awesome!

Discussion:
captured: keyOf({onInputCapture: true}), }, }, <ins> invalid: { phasedRegistrationNames: { bubbled: keyOf({onInvalid: true}), captured: keyOf({onInvalidCapture: true}), }, }, </ins> keyDown: { phasedRegistrationNames: { bubbled: keyOf({onKeyDown: true}),

topError:           eventTypes.error, topFocus:           eventTypes.focus, topInput:           eventTypes.input, <ins> topInvalid:         eventTypes.invalid, </ins> topKeyDown:         eventTypes.keyDown, topKeyPress:        eventTypes.keyPress, topKeyUp:           eventTypes.keyUp,

case topLevelTypes.topEnded: case topLevelTypes.topError: case topLevelTypes.topInput: <ins> case topLevelTypes.topInvalid: </ins> case topLevelTypes.topLoad: case topLevelTypes.topLoadedData: case topLevelTypes.topLoadedMetadata:

), ]; break; <ins> case 'input': case 'select': case 'textarea': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( EventConstants.topLevelTypes.topInvalid, 'invalid', node ), ]; break; </ins> } }

case 'form': case 'video': case 'audio': <del> this._wrapperState = { listeners: null, }; transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); </del> <ins> this._setupTrapBubbledEventsLocal(transaction); </ins> break; case 'button': props = ReactDOMButton.getNativeProps(this, props, nativeParent); break; case 'input': <ins> this._setupTrapBubbledEventsLocal(transaction); </ins> ReactDOMInput.mountWrapper(this, props, nativeParent); props = ReactDOMInput.getNativeProps(this, props); break;

props = ReactDOMOption.getNativeProps(this, props); break; case 'select': <ins> this._setupTrapBubbledEventsLocal(transaction); </ins> ReactDOMSelect.mountWrapper(this, props, nativeParent); props = ReactDOMSelect.getNativeProps(this, props); break; case 'textarea': <ins> this._setupTrapBubbledEventsLocal(transaction); </ins> ReactDOMTextarea.mountWrapper(this, props, nativeParent); props = ReactDOMTextarea.getNativeProps(this, props); break;

}, /** <ins> * Setup this component to trap non-bubbling events locally * * @private * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction */ _setupTrapBubbledEventsLocal: function(transaction) { this._wrapperState = { listeners: null, }; transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); }, /** </ins> * Creates markup for the open tag and all attributes. * * This method has side effects because events get registered.

topError: null, topFocus: null, topInput: null, <ins> topInvalid: null, </ins> topKeyDown: null, topKeyPress: null, topKeyUp: null,