From eaf977b38814be811c542e1bfe8d45cda7df5642 Mon Sep 17 00:00:00 2001 From: Herman Bilous Date: Fri, 20 Oct 2023 11:28:59 +0300 Subject: [PATCH] [New] add requireDataLowercase option to no-unknown-property rule Fixes #3643 --- docs/rules/no-unknown-property.md | 3 ++- lib/rules/no-unknown-property.js | 36 +++++++++++++++++++++++++- tests/lib/rules/no-unknown-property.js | 24 +++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/rules/no-unknown-property.md b/docs/rules/no-unknown-property.md index ac47ccac9d..0d6c9cdc06 100644 --- a/docs/rules/no-unknown-property.md +++ b/docs/rules/no-unknown-property.md @@ -51,12 +51,13 @@ var AtomPanel = ; ```js ... -"react/no-unknown-property": [, { ignore: }] +"react/no-unknown-property": [, { ignore: , 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. diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index fc57336695..069596d73c 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -16,6 +16,7 @@ const report = require('../util/report'); const DEFAULTS = { ignore: [], + requireDataLowercase: false, }; const DOM_ATTRIBUTE_NAMES = { @@ -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 @@ -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 = { @@ -516,6 +528,10 @@ module.exports = { type: 'string', }, }, + requireDataLowercase: { + type: 'boolean', + default: false, + }, }, additionalProperties: false, }], @@ -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(); @@ -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; } diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js index 0fc510e3b5..d6713286c1 100644 --- a/tests/lib/rules/no-unknown-property.js +++ b/tests/lib/rules/no-unknown-property.js @@ -99,6 +99,10 @@ ruleTester.run('no-unknown-property', rule, { { code: '
;' }, { code: '
;' }, { code: '
;' }, + { + code: '
;', + options: [{ requireDataLowercase: false }], + }, // Ignoring should work { code: '
;', @@ -573,6 +577,26 @@ ruleTester.run('no-unknown-property', rule, { }, ], }, + { + code: '
;', + 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: '
', errors: [