Add support for "invalid" event within Form elements
Fixes #5152
Tom Duncalf committed
Oct 15, 2015 at 19:11 UTC
5ceb22953cc9d66f68fed44c26e93506d2f5e9eb
3 files changed
+37
-4
src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js
+8
@@ -177,6 +177,12 @@ var eventTypes = {
177
captured: keyOf({onInputCapture: true}),
178
},
179
},
180
+ invalid: {
181
+ phasedRegistrationNames: {
182
+ bubbled: keyOf({onInvalid: true}),
183
+ captured: keyOf({onInvalidCapture: true}),
184
+ },
185
+ },
186
keyDown: {
187
phasedRegistrationNames: {
188
bubbled: keyOf({onKeyDown: true}),
@@ -404,6 +410,7 @@ var topLevelEventsToDispatchConfig = {
410
topError: eventTypes.error,
411
topFocus: eventTypes.focus,
412
topInput: eventTypes.input,
413
+ topInvalid: eventTypes.invalid,
414
topKeyDown: eventTypes.keyDown,
415
topKeyPress: eventTypes.keyPress,
416
topKeyUp: eventTypes.keyUp,
@@ -480,6 +487,7 @@ var SimpleEventPlugin = {
487
case topLevelTypes.topEnded:
488
case topLevelTypes.topError:
489
case topLevelTypes.topInput:
490
+ case topLevelTypes.topInvalid:
491
case topLevelTypes.topLoad:
492
case topLevelTypes.topLoadedData:
493
case topLevelTypes.topLoadedMetadata:
src/renderers/dom/shared/ReactDOMComponent.js
+28
-4
@@ -414,6 +414,17 @@ function trapBubbledEventsLocal() {
414
),
415
];
416
break;
417
+ case 'input':
418
+ case 'select':
419
+ case 'textarea':
420
+ inst._wrapperState.listeners = [
421
+ ReactBrowserEventEmitter.trapBubbledEvent(
422
+ EventConstants.topLevelTypes.topInvalid,
423
+ 'invalid',
424
+ node
425
+ ),
426
+ ];
427
+ break;
428
}
429
}
430
@@ -557,15 +568,13 @@ ReactDOMComponent.Mixin = {
568
case 'form':
569
case 'video':
570
case 'audio':
560
- this._wrapperState = {
561
- listeners: null,
562
- };
563
- transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
571
+ this._setupTrapBubbledEventsLocal(transaction);
572
break;
573
case 'button':
574
props = ReactDOMButton.getNativeProps(this, props, nativeParent);
575
break;
576
case 'input':
577
+ this._setupTrapBubbledEventsLocal(transaction);
578
ReactDOMInput.mountWrapper(this, props, nativeParent);
579
props = ReactDOMInput.getNativeProps(this, props);
580
break;
@@ -574,10 +583,12 @@ ReactDOMComponent.Mixin = {
583
props = ReactDOMOption.getNativeProps(this, props);
584
break;
585
case 'select':
586
+ this._setupTrapBubbledEventsLocal(transaction);
587
ReactDOMSelect.mountWrapper(this, props, nativeParent);
588
props = ReactDOMSelect.getNativeProps(this, props);
589
break;
590
case 'textarea':
591
+ this._setupTrapBubbledEventsLocal(transaction);
592
ReactDOMTextarea.mountWrapper(this, props, nativeParent);
593
props = ReactDOMTextarea.getNativeProps(this, props);
594
break;
@@ -686,6 +697,19 @@ ReactDOMComponent.Mixin = {
697
return mountImage;
698
},
699
700
+ /**
701
+ * Setup this component to trap non-bubbling events locally
702
+ *
703
+ * @private
704
+ * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
705
+ */
706
+ _setupTrapBubbledEventsLocal: function(transaction) {
707
+ this._wrapperState = {
708
+ listeners: null,
709
+ };
710
+ transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
711
+ },
712
+
713
/**
714
* Creates markup for the open tag and all attributes.
715
*
src/renderers/shared/event/EventConstants.js
+1
@@ -47,6 +47,7 @@ var topLevelTypes = keyMirror({
47
topError: null,
48
topFocus: null,
49
topInput: null,
50
+ topInvalid: null,
51
topKeyDown: null,
52
topKeyPress: null,
53
topKeyUp: null,