diff --git a/src/renderers/dom/shared/DOMPropertyOperations.js b/src/renderers/dom/shared/DOMPropertyOperations.js index 7eb8240377615..00aa1a5e56bb2 100644 --- a/src/renderers/dom/shared/DOMPropertyOperations.js +++ b/src/renderers/dom/shared/DOMPropertyOperations.js @@ -13,6 +13,7 @@ 'use strict'; var DOMProperty = require('DOMProperty'); +var EventPluginRegistry = require('EventPluginRegistry'); var ReactPerf = require('ReactPerf'); var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser'); @@ -87,6 +88,20 @@ if (__DEV__) { standardName ); + var registrationName = ( + EventPluginRegistry.possibleRegistrationNames.hasOwnProperty( + lowerCasedName + ) ? + EventPluginRegistry.possibleRegistrationNames[lowerCasedName] : + null + ); + + warning( + registrationName == null, + 'Unknown event handler property %s. Did you mean `%s`?', + name, + registrationName + ); }; } diff --git a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js index 18e43f6577f70..099fd55da7996 100644 --- a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js +++ b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js @@ -50,7 +50,7 @@ describe('DOMPropertyOperations', function() { )).toBe('id="simple"'); }); - it('should warn about incorrect casing', function() { + it('should warn about incorrect casing on properties', function() { spyOn(console, 'error'); expect(DOMPropertyOperations.createMarkupForProperty( 'tabindex', @@ -60,6 +60,21 @@ describe('DOMPropertyOperations', function() { expect(console.error.argsForCall[0][0]).toContain('tabIndex'); }); + it('should warn about incorrect casing on event handlers', function() { + spyOn(console, 'error'); + expect(DOMPropertyOperations.createMarkupForProperty( + 'onclick', + '1' + )).toBe(null); + expect(DOMPropertyOperations.createMarkupForProperty( + 'onKeydown', + '1' + )).toBe(null); + expect(console.error.argsForCall.length).toBe(2); + expect(console.error.argsForCall[0][0]).toContain('onClick'); + expect(console.error.argsForCall[1][0]).toContain('onKeyDown'); + }); + it('should warn about class', function() { spyOn(console, 'error'); expect(DOMPropertyOperations.createMarkupForProperty( diff --git a/src/renderers/shared/event/EventPluginRegistry.js b/src/renderers/shared/event/EventPluginRegistry.js index df1ca5b4f98cd..756f3160dfb1a 100644 --- a/src/renderers/shared/event/EventPluginRegistry.js +++ b/src/renderers/shared/event/EventPluginRegistry.js @@ -128,6 +128,12 @@ function publishRegistrationName(registrationName, PluginModule, eventName) { EventPluginRegistry.registrationNameModules[registrationName] = PluginModule; EventPluginRegistry.registrationNameDependencies[registrationName] = PluginModule.eventTypes[eventName].dependencies; + + if (__DEV__) { + var lowerCasedName = registrationName.toLowerCase(); + EventPluginRegistry.possibleRegistrationNames[lowerCasedName] = + registrationName; + } } /** @@ -157,6 +163,14 @@ var EventPluginRegistry = { */ registrationNameDependencies: {}, + /** + * Mapping from lowercase registration names to the properly cased version, + * used to warn in the case of missing event handlers. Available + * only in __DEV__. + * @type {Object} + */ + possibleRegistrationNames: __DEV__ ? {} : null, + /** * Injects an ordering of plugins (by plugin name). This allows the ordering * to be decoupled from injection of the actual plugins so that ordering is @@ -265,6 +279,16 @@ var EventPluginRegistry = { delete registrationNameModules[registrationName]; } } + + if (__DEV__) { + var possibleRegistrationNames = + EventPluginRegistry.possibleRegistrationNames; + for (var lowerCasedName in possibleRegistrationNames) { + if (possibleRegistrationNames.hasOwnProperty(lowerCasedName)) { + delete possibleRegistrationNames[lowerCasedName]; + } + } + } }, };