diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index eba1145b34..2f53353a3b 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -40,6 +40,30 @@ const ATTRIBUTE_TAGS_MAP = { property: ['meta'], viewBox: ['svg'], as: ['link'], + // Media events allowed only on audio and video tags, see https://github.com/facebook/react/blob/256aefbea1449869620fb26f6ec695536ab453f5/CHANGELOG.md#notable-enhancements + onAbort: ['audio', 'video'], + onCanPlay: ['audio', 'video'], + onCanPlayThrough: ['audio', 'video'], + onDurationChange: ['audio', 'video'], + onEmptied: ['audio', 'video'], + onEncrypted: ['audio', 'video'], + onEnded: ['audio', 'video'], + onError: ['audio', 'video'], + onLoadedData: ['audio', 'video'], + onLoadedMetadata: ['audio', 'video'], + onLoadStart: ['audio', 'video'], + onPause: ['audio', 'video'], + onPlay: ['audio', 'video'], + onPlaying: ['audio', 'video'], + onProgress: ['audio', 'video'], + onRateChange: ['audio', 'video'], + onSeeked: ['audio', 'video'], + onSeeking: ['audio', 'video'], + onStalled: ['audio', 'video'], + onSuspend: ['audio', 'video'], + onTimeUpdate: ['audio', 'video'], + onVolumeChange: ['audio', 'video'], + onWaiting: ['audio', 'video'], }; const SVGDOM_ATTRIBUTE_NAMES = { @@ -211,7 +235,11 @@ const DOM_PROPERTY_NAMES_TWO_WORDS = [ 'autoCorrect', // https://stackoverflow.com/questions/47985384/html-autocorrect-for-text-input-is-not-working 'autoSave', // https://stackoverflow.com/questions/25456396/what-is-autosave-attribute-supposed-to-do-how-do-i-use-it // React specific attributes https://reactjs.org/docs/dom-elements.html#differences-in-attributes - 'className', 'dangerouslySetInnerHTML', 'defaultValue', 'defaultChecked', 'htmlFor', 'onChange', 'suppressContentEditableWarning', 'suppressHydrationWarning', + 'className', 'dangerouslySetInnerHTML', 'defaultValue', 'defaultChecked', 'htmlFor', 'onChange', + 'onInvalid', 'onReset', 'onTouchCancel', 'onTouchEnd', 'onTouchMove', 'onTouchStart', 'suppressContentEditableWarning', 'suppressHydrationWarning', + 'onAbort', 'onCanPlay', 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', 'onEnded', + 'onLoadedData', 'onLoadedMetadata', 'onLoadStart', 'onPause', 'onPlay', 'onPlaying', 'onProgress', 'onRateChange', + 'onSeeked', 'onSeeking', 'onStalled', 'onSuspend', 'onTimeUpdate', 'onVolumeChange', 'onWaiting', ]; const DOM_PROPERTIES_IGNORE_CASE = ['charset']; diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js index c18e00b67c..677b473dd7 100644 --- a/tests/lib/rules/no-unknown-property.js +++ b/tests/lib/rules/no-unknown-property.js @@ -56,6 +56,7 @@ ruleTester.run('no-unknown-property', rule, { // React related attributes { code: '
' }, { code: '' }, + { code: '
' }, // Case ignored attributes, for `charset` discussion see https://github.com/jsx-eslint/eslint-plugin-react/pull/1863 { code: ';' }, { code: ';' }, @@ -88,6 +89,7 @@ ruleTester.run('no-unknown-property', rule, { { code: '
Some details
' }, { code: '' }, { code: 'Audio content' }, + { code: '' }, ]), invalid: parsers.all([ { @@ -317,5 +319,50 @@ ruleTester.run('no-unknown-property', rule, { }, ], }, + { + code: '
', + errors: [ + { + messageId: 'invalidPropOnTag', + data: { + name: 'onAbort', + tagName: 'div', + allowedTags: 'audio, video', + }, + }, + { + messageId: 'invalidPropOnTag', + data: { + name: 'onDurationChange', + tagName: 'div', + allowedTags: 'audio, video', + }, + }, + { + messageId: 'invalidPropOnTag', + data: { + name: 'onEmptied', + tagName: 'div', + allowedTags: 'audio, video', + }, + }, + { + messageId: 'invalidPropOnTag', + data: { + name: 'onEnded', + tagName: 'div', + allowedTags: 'audio, video', + }, + }, + { + messageId: 'invalidPropOnTag', + data: { + name: 'onError', + tagName: 'div', + allowedTags: 'audio, video', + }, + }, + ], + }, ]), });