diff --git a/docs/warnings/invalid-aria-prop.md b/docs/warnings/invalid-aria-prop.md new file mode 100644 index 000000000000..53ebdd9bc406 --- /dev/null +++ b/docs/warnings/invalid-aria-prop.md @@ -0,0 +1,11 @@ +--- +title: Invalid ARIA Prop Warning +layout: single +permalink: warnings/invalid-aria-prop.html +--- + +The invalid-aria-prop warning will fire if you attempt to render a DOM element with an aria-* prop that does not exist in the Web Accessibility Initiative (WAI) Accessible Rich Internet Application (ARIA) [specification](https://www.w3.org/TR/wai-aria-1.1/#states_and_properties). + +1. If you feel that you are using a valid prop, check the spelling carefully. `aria-labelledby` and `aria-activedescendant` are often misspelled. + +2. React does not yet recognize the attribute you specified. This will likely be fixed in a future version of React. However, React currently strips all unknown attributes, so specifying them in your React app will not cause them to be rendered \ No newline at end of file diff --git a/src/renderers/dom/ReactDOM.js b/src/renderers/dom/ReactDOM.js index c68819b43ea7..ff39dc5aa471 100644 --- a/src/renderers/dom/ReactDOM.js +++ b/src/renderers/dom/ReactDOM.js @@ -138,9 +138,11 @@ if (__DEV__) { var ReactInstrumentation = require('ReactInstrumentation'); var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook'); var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook'); + var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook'); ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook); ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook); + ReactInstrumentation.debugTool.addHook(ReactDOMInvalidARIAHook); } module.exports = ReactDOM; diff --git a/src/renderers/dom/shared/ARIADOMPropertyConfig.js b/src/renderers/dom/shared/ARIADOMPropertyConfig.js new file mode 100644 index 000000000000..108278940cd1 --- /dev/null +++ b/src/renderers/dom/shared/ARIADOMPropertyConfig.js @@ -0,0 +1,74 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ARIADOMPropertyConfig + */ + +'use strict'; + +var ARIADOMPropertyConfig = { + Properties: { + // Global States and Properties + 'aria-current': 0, // state + 'aria-details': 0, + 'aria-disabled': 0, // state + 'aria-hidden': 0, // state + 'aria-invalid': 0, // state + 'aria-keyshortcuts': 0, + 'aria-label': 0, + 'aria-roledescription': 0, + // Widget Attributes + 'aria-autocomplete': 0, + 'aria-checked': 0, + 'aria-expanded': 0, + 'aria-haspopup': 0, + 'aria-level': 0, + 'aria-modal': 0, + 'aria-multiline': 0, + 'aria-multiselectable': 0, + 'aria-orientation': 0, + 'aria-placeholder': 0, + 'aria-pressed': 0, + 'aria-readonly': 0, + 'aria-required': 0, + 'aria-selected': 0, + 'aria-sort': 0, + 'aria-valuemax': 0, + 'aria-valuemin': 0, + 'aria-valuenow': 0, + 'aria-valuetext': 0, + // Live Region Attributes + 'aria-atomic': 0, + 'aria-busy': 0, + 'aria-live': 0, + 'aria-relevant': 0, + // Drag-and-Drop Attributes + 'aria-dropeffect': 0, + 'aria-grabbed': 0, + // Relationship Attributes + 'aria-activedescendant': 0, + 'aria-colcount': 0, + 'aria-colindex': 0, + 'aria-colspan': 0, + 'aria-controls': 0, + 'aria-describedby': 0, + 'aria-errormessage': 0, + 'aria-flowto': 0, + 'aria-labelledby': 0, + 'aria-owns': 0, + 'aria-posinset': 0, + 'aria-rowcount': 0, + 'aria-rowindex': 0, + 'aria-rowspan': 0, + 'aria-setsize': 0, + }, + DOMAttributeNames: {}, + DOMPropertyNames: {}, +}; + +module.exports = ARIADOMPropertyConfig; diff --git a/src/renderers/dom/shared/ReactDefaultInjection.js b/src/renderers/dom/shared/ReactDefaultInjection.js index 6d42abd0ac02..20b89c8c9a20 100644 --- a/src/renderers/dom/shared/ReactDefaultInjection.js +++ b/src/renderers/dom/shared/ReactDefaultInjection.js @@ -11,6 +11,7 @@ 'use strict'; +var ARIADOMPropertyConfig = require('ARIADOMPropertyConfig'); var BeforeInputEventPlugin = require('BeforeInputEventPlugin'); var ChangeEventPlugin = require('ChangeEventPlugin'); var DefaultEventPluginOrder = require('DefaultEventPluginOrder'); @@ -73,6 +74,7 @@ function inject() { ReactDOMTextComponent ); + ReactInjection.DOMProperty.injectDOMPropertyConfig(ARIADOMPropertyConfig); ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig); ReactInjection.DOMProperty.injectDOMPropertyConfig(SVGDOMPropertyConfig); diff --git a/src/renderers/dom/shared/__tests__/ReactDOMInvalidARIAHook-test.js b/src/renderers/dom/shared/__tests__/ReactDOMInvalidARIAHook-test.js new file mode 100644 index 000000000000..3be7d6127dd6 --- /dev/null +++ b/src/renderers/dom/shared/__tests__/ReactDOMInvalidARIAHook-test.js @@ -0,0 +1,69 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +describe('ReactDOMInvalidARIAHook', () => { + var React; + var ReactTestUtils; + var mountComponent; + + beforeEach(() => { + jest.resetModuleRegistry(); + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + + mountComponent = function(props) { + ReactTestUtils.renderIntoDocument(
); + }; + }); + + describe('aria-* props', () => { + it('should allow valid aria-* props', () => { + spyOn(console, 'error'); + mountComponent({'aria-label': 'Bumble bees'}); + expect(console.error.calls.count()).toBe(0); + }); + it('should warn for one invalid aria-* prop', () => { + spyOn(console, 'error'); + mountComponent({'aria-badprop': 'maybe'}); + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toContain( + 'Warning: Invalid aria prop `aria-badprop` on