Skip to content

Commit

Permalink
[New] no-unknown-property: add requireDataLowercase option
Browse files Browse the repository at this point in the history
Fixes #3643
  • Loading branch information
HermanBilous authored and ljharb committed Oct 20, 2023
1 parent ca30f77 commit 70e6019
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Added
* [`sort-prop-types`]: give errors on TS types ([#3615][] @akulsr0)
* [`no-invalid-html-attribute`]: add support for `apple-touch-startup-image` `rel` attributes in `link` tags ([#3638][] @thomashockaday)
* [`no-unknown-property`]: add requireDataLowercase option ([#3645][] @HermanBilous)

### Fixed
* [`jsx-no-leaked-render`]: preserve RHS parens for multiline jsx elements while fixing ([#3623][] @akulsr0)
Expand All @@ -20,6 +21,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [Refactor] [`function-component-definition`]: exit early if no type params ([#3634][] @HenryBrown0)
* [Refactor] [`jsx-props-no-multi-spaces`]: extract type parameters to var ([#3634][] @HenryBrown0)

[#3645]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3645
[#3638]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3638
[#3634]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3634
[#3633]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3633
Expand Down
3 changes: 2 additions & 1 deletion docs/rules/no-unknown-property.md
Expand Up @@ -51,12 +51,13 @@ var AtomPanel = <atom-panel class="foo"></atom-panel>;

```js
...
"react/no-unknown-property": [<enabled>, { ignore: <ignore> }]
"react/no-unknown-property": [<enabled>, { ignore: <ignore>, requireDataLowercase: <requireDataLowercase> }]
...
```

- `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.
- `requireDataLowercase`: optional (default: `false`), require data-\* attributes to contain only lowercase characters. React will issue a warning when data-\* attributes contain uppercase characters. In order to catch such attributes, set the `requireDataLowercase` option to `true`.

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.

Expand Down
36 changes: 35 additions & 1 deletion lib/rules/no-unknown-property.js
Expand Up @@ -16,6 +16,7 @@ const report = require('../util/report');

const DEFAULTS = {
ignore: [],
requireDataLowercase: false,
};

const DOM_ATTRIBUTE_NAMES = {
Expand Down Expand Up @@ -429,6 +430,16 @@ function isValidDataAttribute(name) {
return /^data(-[^:]*)*$/.test(name) && !/^data-xml/i.test(name);
}

/**
* Checks if an attribute name has at least one uppercase characters
*
* @param {String} name
* @returns {boolean} Result
*/
function hasUpperCaseCharacter(name) {
return name.toLowerCase() !== name;
}

/**
* Checks if an attribute name is a standard aria attribute by compering it to a list
* of standard aria property names
Expand Down Expand Up @@ -493,6 +504,7 @@ const messages = {
invalidPropOnTag: 'Invalid property \'{{name}}\' found on tag \'{{tagName}}\', but it is only allowed on: {{allowedTags}}',
unknownPropWithStandardName: 'Unknown property \'{{name}}\' found, use \'{{standardName}}\' instead',
unknownProp: 'Unknown property \'{{name}}\' found',
dataLowercaseRequired: 'React does not recognize data-* props with uppercase characters on a DOM element. Found \'{{name}}\', use \'{{lowerCaseName}}\' instead',
};

module.exports = {
Expand All @@ -516,6 +528,10 @@ module.exports = {
type: 'string',
},
},
requireDataLowercase: {
type: 'boolean',
default: false,
},
},
additionalProperties: false,
}],
Expand All @@ -526,6 +542,12 @@ module.exports = {
return (context.options[0] && context.options[0].ignore) || DEFAULTS.ignore;
}

function getRequireDataLowercase() {
return (context.options[0] && typeof context.options[0].requireDataLowercase !== 'undefined')
? !!context.options[0].requireDataLowercase
: DEFAULTS.requireDataLowercase;
}

return {
JSXAttribute(node) {
const ignoreNames = getIgnoreConfig();
Expand All @@ -540,7 +562,19 @@ module.exports = {
return;
}

if (isValidDataAttribute(name)) { return; }
if (isValidDataAttribute(name)) {
if (getRequireDataLowercase() && hasUpperCaseCharacter(name)) {
report(context, messages.dataLowercaseRequired, 'dataLowercaseRequired', {
node,
data: {
name: actualName,
lowerCaseName: actualName.toLowerCase(),
},
});
}

return;
}

if (isValidAriaAttribute(name)) { return; }

Expand Down
24 changes: 24 additions & 0 deletions tests/lib/rules/no-unknown-property.js
Expand Up @@ -99,6 +99,10 @@ ruleTester.run('no-unknown-property', rule, {
{ code: '<div data-index-number="1234"></div>;' },
{ code: '<div data-e2e-id="5678"></div>;' },
{ code: '<div data-testID="bar" data-under_sCoRe="bar" />;' },
{
code: '<div data-testID="bar" data-under_sCoRe="bar" />;',
options: [{ requireDataLowercase: false }],
},
// Ignoring should work
{
code: '<div class="bar"></div>;',
Expand Down Expand Up @@ -573,6 +577,26 @@ ruleTester.run('no-unknown-property', rule, {
},
],
},
{
code: '<div data-testID="bar" data-under_sCoRe="bar" />;',
errors: [
{
messageId: 'dataLowercaseRequired',
data: {
name: 'data-testID',
lowerCaseName: 'data-testid',
},
},
{
messageId: 'dataLowercaseRequired',
data: {
name: 'data-under_sCoRe',
lowerCaseName: 'data-under_score',
},
},
],
options: [{ requireDataLowercase: true }],
},
{
code: '<div abbr="abbr" />',
errors: [
Expand Down

0 comments on commit 70e6019

Please sign in to comment.