diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f627147e4..bae48b418d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,18 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Fixed
* [`no-unknown-property`]: `onError` and `onLoad` both work on `img` and `script` ([#3388][] @ljharb)
+* [`no-unknown-property`]: data-* attributes can have numbers ([#3390][] @sjarva)
+* [`no-unknown-property`]: add more audio/video attributes ([#3390][] @sjarva)
+* [`no-unknown-property`]: move allowfullscreen to case ignored attributes ([#3390][] @sjarva)
+* [`no-unknown-property`]: fill works on line, mask, and use elements ([#3390][] @sjarva)
+* [`no-unknown-property`]: add onMouseMoveCapture as valid react-specific attribute ([#3390][] @sjarva)
+* [`no-unknown-property`]: make onLoad and onError be accepted on more elements ([#3390][] @sjarva)
+### Changed
+
+* [Docs] [`no-unknown-property`]: add a mention about using ignores properties with libraries that add props ([#3390][] @sjarva)
+
+[#3390]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3390
[#3388]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3388
## [7.31.5] - 2022.09.03
diff --git a/docs/rules/no-unknown-property.md b/docs/rules/no-unknown-property.md
index 108fa42a44..d142def850 100644
--- a/docs/rules/no-unknown-property.md
+++ b/docs/rules/no-unknown-property.md
@@ -43,7 +43,6 @@ var AnotherComponent = ;
// Custom web components are ignored
var MyElem =
;
var AtomPanel = ;
-
```
## Rule Options
@@ -57,6 +56,23 @@ var AtomPanel = ;
- `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
- `ignore`: optional array of property and attribute names to ignore during validation.
+If you are using a library that passes something as a prop to JSX elements, it is recommended to add those props to the ignored properties.
+
+For example, if you use [emotion](https://emotion.sh/docs/introduction) and its [`css` prop](https://emotion.sh/docs/css-prop)),
+add the following to your `.eslintrc` config file:
+
+```js
+...
+"react/no-unknown-property": ['error', { ignore: ['css'] }]
+...
+```
+
+Now, the following code passes:
+
+```jsx
+var StyledDiv = ;
+```
+
## When Not To Use It
If you are not using JSX you can disable this rule.
diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js
index 2867c5d5b1..25a8ece9c6 100644
--- a/lib/rules/no-unknown-property.js
+++ b/lib/rules/no-unknown-property.js
@@ -36,6 +36,8 @@ const ATTRIBUTE_TAGS_MAP = {
'circle',
'ellipse',
'g',
+ 'line',
+ 'mask',
'path',
'polygon',
'polyline',
@@ -45,6 +47,7 @@ const ATTRIBUTE_TAGS_MAP = {
'textPath',
'tref',
'tspan',
+ 'use',
// Animation final state
'animate',
'animateColor',
@@ -63,8 +66,8 @@ const ATTRIBUTE_TAGS_MAP = {
onEmptied: ['audio', 'video'],
onEncrypted: ['audio', 'video'],
onEnded: ['audio', 'video'],
- onError: ['audio', 'video', 'img', 'script'],
- onLoad: ['script', 'img'],
+ onError: ['audio', 'video', 'img', 'link', 'source', 'script'],
+ onLoad: ['script', 'img', 'link'],
onLoadedData: ['audio', 'video'],
onLoadedMetadata: ['audio', 'video'],
onLoadStart: ['audio', 'video'],
@@ -82,6 +85,16 @@ const ATTRIBUTE_TAGS_MAP = {
onWaiting: ['audio', 'video'],
scrolling: ['iframe'],
playsInline: ['video'],
+ // Video related attributes
+ autoPictureInPicture: ['video'],
+ controls: ['audio', 'video'],
+ controlList: ['video'],
+ disablePictureInPicture: ['video'],
+ disableRemotePlayback: ['audio', 'video'],
+ loop: ['audio', 'video'],
+ muted: ['audio', 'video'],
+ poster: ['video'],
+ preload: ['audio', 'video'],
};
const SVGDOM_ATTRIBUTE_NAMES = {
@@ -199,6 +212,8 @@ const DOM_PROPERTY_NAMES_ONE_WORD = [
'property',
// React specific attributes
'ref', 'key', 'children',
+ // Video specific
+ 'controls',
];
const DOM_PROPERTY_NAMES_TWO_WORDS = [
@@ -209,7 +224,7 @@ const DOM_PROPERTY_NAMES_TWO_WORDS = [
// Element specific attributes
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too)
// To be considered if these should be added also to ATTRIBUTE_TAGS_MAP
- 'acceptCharset', 'allowFullScreen', 'autoComplete', 'autoPlay', 'cellPadding', 'cellSpacing', 'classID', 'codeBase',
+ 'acceptCharset', 'autoComplete', 'autoPlay', 'cellPadding', 'cellSpacing', 'classID', 'codeBase',
'colSpan', 'contextMenu', 'dateTime', 'encType', 'formAction', 'formEncType', 'formMethod', 'formNoValidate', 'formTarget',
'frameBorder', 'hrefLang', 'httpEquiv', 'isMap', 'keyParams', 'keyType', 'marginHeight', 'marginWidth',
'maxLength', 'mediaGroup', 'minLength', 'noValidate', 'onAnimationEnd', 'onAnimationIteration', 'onAnimationStart',
@@ -258,9 +273,12 @@ const DOM_PROPERTY_NAMES_TWO_WORDS = [
'onAbort', 'onCanPlay', 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', 'onEnded',
'onLoadedData', 'onLoadedMetadata', 'onLoadStart', 'onPause', 'onPlay', 'onPlaying', 'onProgress', 'onRateChange',
'onSeeked', 'onSeeking', 'onStalled', 'onSuspend', 'onTimeUpdate', 'onVolumeChange', 'onWaiting',
+ 'onMouseMoveCapture',
+ // Video specific,
+ 'autoPictureInPicture', 'controlList', 'disablePictureInPicture', 'disableRemotePlayback',
];
-const DOM_PROPERTIES_IGNORE_CASE = ['charset'];
+const DOM_PROPERTIES_IGNORE_CASE = ['charset', 'allowfullscreen'];
const ARIA_PROPERTIES = [
// See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes
@@ -338,14 +356,14 @@ function isValidHTMLTagInJSX(childNode) {
/**
* Checks if an attribute name is a valid `data-*` attribute:
- * if the name starts with "data-" and has some lowcase (a to z) words, separated but hyphens (-)
+ * if the name starts with "data-" and has some lowcase (a to z) words that can contain numbers, separated but hyphens (-)
* (which is also called "kebab case" or "dash case"), then the attribute is valid data attribute.
*
* @param {String} name - Attribute name to be tested
* @returns {boolean} Result
*/
function isValidDataAttribute(name) {
- const dataAttrConvention = /^data(-[a-z]*)*$/;
+ const dataAttrConvention = /^data(-[a-z1-9]*)*$/;
return !!dataAttrConvention.test(name);
}
diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js
index d25c3d7cb4..5312003511 100644
--- a/tests/lib/rules/no-unknown-property.js
+++ b/tests/lib/rules/no-unknown-property.js
@@ -54,11 +54,14 @@ ruleTester.run('no-unknown-property', rule, {
{ code: ';' },
{ code: '' },
{ code: '' },
+ { code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
+ { code: '' },
+ { code: '' },
{
code: '',
settings: {
@@ -68,7 +71,7 @@ ruleTester.run('no-unknown-property', rule, {
// React related attributes
{ code: '' },
{ code: '' },
- { code: '' },
+ { code: '' },
// Case ignored attributes, for `charset` discussion see https://github.com/jsx-eslint/eslint-plugin-react/pull/1863
{ code: ';' },
{ code: ';' },
@@ -81,6 +84,7 @@ ruleTester.run('no-unknown-property', rule, {
{ code: ';' },
{ code: ';' },
{ code: ';' },
+ { code: ';' },
// Ignoring should work
{
code: ';',
@@ -90,6 +94,10 @@ ruleTester.run('no-unknown-property', rule, {
code: ';',
options: [{ ignore: ['someProp'] }],
},
+ {
+ code: ';',
+ options: [{ ignore: ['css'] }],
+ },
// aria-* attributes should work
{ code: ';' },
@@ -100,8 +108,10 @@ ruleTester.run('no-unknown-property', rule, {
{ code: '' },
{ code: 'Some details' },
{ code: '' },
+ { code: '' },
{ code: 'Audio content' },
- { code: '' },
+ { code: '' },
+ { code: '' },
]),
invalid: parsers.all([
{
@@ -385,7 +395,70 @@ ruleTester.run('no-unknown-property', rule, {
data: {
name: 'onError',
tagName: 'div',
- allowedTags: 'audio, video, img, script',
+ allowedTags: 'audio, video, img, link, source, script',
+ },
+ },
+ ],
+ },
+ {
+ code: '',
+ errors: [
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'onLoad',
+ tagName: 'div',
+ allowedTags: 'script, img, link',
+ },
+ },
+ ],
+ },
+ {
+ code: '',
+ errors: [
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'fill',
+ tagName: 'div',
+ allowedTags: 'altGlyph, circle, ellipse, g, line, mask, path, polygon, polyline, rect, svg, text, textPath, tref, tspan, use, animate, animateColor, animateMotion, animateTransform, set',
+ },
+ },
+ ],
+ },
+ {
+ code: '',
+ errors: [
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'controls',
+ tagName: 'div',
+ allowedTags: 'audio, video',
+ },
+ },
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'loop',
+ tagName: 'div',
+ allowedTags: 'audio, video',
+ },
+ },
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'muted',
+ tagName: 'div',
+ allowedTags: 'audio, video',
+ },
+ },
+ {
+ messageId: 'invalidPropOnTag',
+ data: {
+ name: 'playsInline',
+ tagName: 'div',
+ allowedTags: 'video',
},
},
],