ember-template-lint
Advanced tools
Comparing version 0.5.10 to 0.5.11
Changelog | ||
========= | ||
## v0.5.11 | ||
- Add internal helper for determining if a given element is an interactive element. | ||
- Update `nested-interactive` rule to use the new `isInteractiveElement` helper function. | ||
- Change `nested-interactive` configuration. Now uses an object (instead of an array). Example: | ||
```js | ||
rules: { | ||
'nested-interactive': { | ||
ignoredTags: ['a', 'button'], // list of tag names to ignore | ||
ignoreTabindex: true, // ignore the tabindex check | ||
ignoreUsemapAttribute: ['img', 'object'], // ignore `usemap` check for specific tag names | ||
additionalInteractiveTags: ['some-custom-tag'], // not sure this is needed, but it seams neat :P | ||
} | ||
} | ||
``` | ||
## v0.5.10 | ||
@@ -5,0 +22,0 @@ |
'use strict'; | ||
function AstNodeInfo() {} | ||
AstNodeInfo.isConfigurationHtmlComment = function(node) { | ||
function isConfigurationHtmlComment(node) { | ||
return node.type === 'CommentStatement' && node.value.trim().indexOf('template-lint ') === 0; | ||
}; | ||
} | ||
AstNodeInfo.isNonConfigurationHtmlComment = function(node) { | ||
function isNonConfigurationHtmlComment(node) { | ||
return node.type === 'CommentStatement' && node.value.trim().indexOf('template-lint ') !== 0; | ||
}; | ||
} | ||
AstNodeInfo.isTextNode = function(node) { | ||
function isTextNode(node) { | ||
return node.type === 'TextNode'; | ||
}; | ||
} | ||
AstNodeInfo.isElementNode = function(node) { | ||
return node.type === 'ElementNode'; | ||
}; | ||
function isElementNode(node) { | ||
return node && node.type === 'ElementNode'; | ||
} | ||
AstNodeInfo.isBlockStatement = function(node) { | ||
function isComponentNode(node) { | ||
return node && node.type === 'ComponentNode'; | ||
} | ||
function isBlockStatement(node) { | ||
return node.type === 'BlockStatement'; | ||
} | ||
function hasAttribute(node, attributeName) { | ||
var attribute = findAttribute(node, attributeName); | ||
return !!attribute; | ||
} | ||
function findAttribute(node, attributeName) { | ||
for (var i = 0; i < node.attributes.length; i++) { | ||
var attribute = node.attributes[i]; | ||
if (attribute.name === attributeName) { | ||
return attribute; | ||
} | ||
} | ||
} | ||
module.exports = { | ||
isConfigurationHtmlComment: isConfigurationHtmlComment, | ||
isNonConfigurationHtmlComment: isNonConfigurationHtmlComment, | ||
isTextNode: isTextNode, | ||
isElementNode: isElementNode, | ||
isComponentNode: isComponentNode, | ||
isBlockStatement: isBlockStatement, | ||
hasAttribute: hasAttribute, | ||
findAttribute: findAttribute | ||
}; | ||
module.exports = AstNodeInfo; |
@@ -30,6 +30,6 @@ 'use strict'; | ||
this._log = log; | ||
this.ruleName = ruleName; | ||
this.severity = defaultSeverity; | ||
this.config = this.parseConfig(config); | ||
this._log = log; | ||
} | ||
@@ -36,0 +36,0 @@ |
@@ -20,17 +20,72 @@ 'use strict'; | ||
var buildPlugin = require('./base'); | ||
var isInteractiveElement = require('../helpers/is-interactive-element'); | ||
var ARRAY_DEPRECATION_MESSAGE = 'Specifying an array as the configurate for the `nested-interactive` rule is deprecated and will be removed in future versions. Please update `.template-lintrc.js` to use the newer object format.'; | ||
function configValid(config) { | ||
for (var key in config) { | ||
var value = config[key]; | ||
switch (key) { | ||
case 'ignoredTags': | ||
case 'additionalInteractiveTags': | ||
if (!Array.isArray(value)) { | ||
return false; | ||
} | ||
break; | ||
case 'ignoreTabindex': | ||
case 'ignoreUsemapAttribute': | ||
if (typeof value !== 'boolean') { | ||
return false; | ||
} | ||
break; | ||
default: | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function convertConfigArrayToObject(config) { | ||
var base = { | ||
ignoredTags: [], | ||
ignoreTabindex: false, | ||
ignoreUsemapAttribute: false | ||
}; | ||
for (var i = 0; i < config.length; i++) { | ||
var value = config[i]; | ||
switch (value) { | ||
case 'tabindex': | ||
base.ignoreTabindex = true; | ||
break; | ||
case 'usemap': | ||
base.ignoreUsemapAttribute = true; | ||
break; | ||
default: | ||
base.ignoredTags.push(value); | ||
} | ||
} | ||
return base; | ||
} | ||
module.exports = function(addonContext) { | ||
var LogNestedInteractive = buildPlugin(addonContext, 'nested-interactive'); | ||
function isElementNode(node) { | ||
return node.type === 'ElementNode'; | ||
} | ||
LogNestedInteractive.prototype.parseConfig = function(config) { | ||
var configType = typeof config; | ||
var errorMessage = 'The nested-interactive rule accepts one of the following values.\n ' + | ||
' * boolean - `true` to enable / `false` to disable\n' + | ||
' * array -- an array of strings to whitelist\n' + | ||
'\nYou specified `' + JSON.stringify(config) + '`'; | ||
var errorMessage = [ | ||
'The nested-interactive rule accepts one of the following values.', | ||
' * boolean - `true` to enable / `false` to disable', | ||
' * object - Containing the following values:', | ||
' * `ignoredTags` - An array of element tag names that should be whitelisted. Default to `[]`.', | ||
' * `ignoreTabindex` - When `true` tabindex will be ignored. Defaults to `false`.', | ||
' * `ignoreUsemapAttribute` - When `true` ignores the `usemap` attribute on `img` and `object` elements. Defaults `false`.', | ||
' * `additionalInteractiveTags` - An array of element tag names that should also be considered as interactive. Defaults to `[]`.', | ||
'You specified `' + JSON.stringify(config) + '`' | ||
].join('\n'); | ||
@@ -41,4 +96,11 @@ switch (configType) { | ||
case 'object': | ||
if (Array.isArray(config)) { | ||
if (configValid(config)) { | ||
return config; | ||
} else if (Array.isArray(config)) { | ||
this.log({ | ||
message: ARRAY_DEPRECATION_MESSAGE, | ||
source: JSON.stringify(config), | ||
severity: 1 | ||
}); | ||
return convertConfigArrayToObject(config); | ||
} else { | ||
@@ -58,15 +120,3 @@ throw new Error(errorMessage); | ||
} else { | ||
return [ | ||
'a', | ||
'button', | ||
'details', | ||
'embed', | ||
'iframe', | ||
'img', | ||
'input', | ||
'object', | ||
'select', | ||
'tabindex', | ||
'textarea' | ||
]; | ||
return []; | ||
} | ||
@@ -76,189 +126,90 @@ }; | ||
LogNestedInteractive.prototype.visitors = function() { | ||
var pluginContext = this; | ||
pluginContext._parentInteractiveNode = null; | ||
this._parentInteractiveNode = null; | ||
return { | ||
CommentStatement: function(node) { | ||
if (node.value.indexOf('template-lint') > -1) { | ||
pluginContext._processConfigNode(node); | ||
var visitor = { | ||
enter: function(node) { | ||
var isInteractive = isInteractiveElement(node); | ||
if (this.isCustomInteractiveElement(node)) { | ||
isInteractive = true; | ||
} | ||
}, | ||
ElementNode: { | ||
enter: function(node) { | ||
if (pluginContext.isDisabled()) { return; } | ||
if (!isInteractive) { return; } | ||
if (this.isInteractiveExcluded(node)) { return; } | ||
var isInteractive = pluginContext.isInteractiveElement(node); | ||
if (this._parentInteractiveNode) { | ||
this.log({ | ||
message: this.getLogMessage(node, this._parentInteractiveNode), | ||
line: node.loc && node.loc.start.line, | ||
column: node.loc && node.loc.start.column, | ||
source: this.sourceForNode(node) | ||
}); | ||
} else { | ||
this._parentInteractiveNode = node; | ||
} | ||
}, | ||
if (!isInteractive) { return; } | ||
if (pluginContext._parentInteractiveNode) { | ||
pluginContext.log({ | ||
message: pluginContext.getLogMessage(node, pluginContext._parentInteractiveNode), | ||
line: node.loc && node.loc.start.line, | ||
column: node.loc && node.loc.start.column, | ||
source: pluginContext.sourceForNode(node) | ||
}); | ||
} else { | ||
pluginContext._parentInteractiveNode = node; | ||
} | ||
}, | ||
exit: function(node) { | ||
if (pluginContext._parentInteractiveNode === node) { | ||
pluginContext._parentInteractiveNode = null; | ||
} | ||
exit: function(node) { | ||
if (this._parentInteractiveNode === node) { | ||
this._parentInteractiveNode = null; | ||
} | ||
} | ||
}; | ||
}; | ||
LogNestedInteractive.prototype.hasNodeHaveAttribute = function(node, attributeName) { | ||
return node.attributes.some(function(attribute) { | ||
return attribute.name === attributeName; | ||
}); | ||
return { | ||
ElementNode: visitor, | ||
ComponentNode: visitor | ||
}; | ||
}; | ||
LogNestedInteractive.prototype.isNodeLink = function(node, whitelistTests) { | ||
return ( | ||
whitelistTests.indexOf('a') !== -1 && | ||
node.tag === 'a' && this.hasNodeHaveAttribute(node, 'href') | ||
); | ||
}; | ||
LogNestedInteractive.prototype.isCustomInteractiveElement = function(node) { | ||
LogNestedInteractive.prototype.isNodeNotHiddenInput = function(node, whitelistTests) { | ||
if (whitelistTests.indexOf('input') === -1) { | ||
var additionalInteractiveTags = this.config.additionalInteractiveTags || []; | ||
if (additionalInteractiveTags.indexOf(node.tag) > -1) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
if (node.tag === 'input') { | ||
var isInputTypeHidden = node.attributes.some(function(attribute) { | ||
return ( | ||
attribute.name === 'type' && | ||
attribute.value && | ||
attribute.value.chars === 'hidden' | ||
); | ||
}); | ||
// NOTE: `!` | ||
if (!isInputTypeHidden) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
LogNestedInteractive.prototype.hasNodeHaveTabIndex = function(node, whitelistTests) { | ||
return ( | ||
whitelistTests.indexOf('tabindex') !== -1 && | ||
this.hasNodeHaveAttribute(node, 'tabindex') | ||
); | ||
}; | ||
LogNestedInteractive.prototype.isInteractiveExcluded = function(node) { | ||
var reason = isInteractiveElement.reason(node); | ||
var ignoredTags = this.config.ignoredTags || []; | ||
var ignoreTabindex = this.config.ignoreTabindex; | ||
var ignoreUsemapAttribute = this.config.ignoreUsemapAttribute; | ||
LogNestedInteractive.prototype.isNodeUseMapInteractive = function(node, whitelistTests) { | ||
return ( | ||
whitelistTests.indexOf(node.tag) !== -1 && | ||
(node.tag === 'img' || node.tag === 'object') && | ||
this.hasNodeHaveAttribute(node, 'usemap') | ||
); | ||
}; | ||
LogNestedInteractive.prototype.isNodeInteractiveTag = function(node, whitelistTests) { | ||
var interactiveTags = [ | ||
'button', | ||
'details', | ||
'embed', | ||
'iframe', | ||
'select', | ||
'textarea' | ||
]; | ||
return interactiveTags.some(function(tagName) { | ||
return whitelistTests.indexOf(tagName) !== -1 && tagName === node.tag; | ||
}); | ||
}; | ||
/** | ||
* NOTE: `<label>` was omitted due to the ability nesting a label with an input tag. | ||
* NOTE: `<audio>` and `<video>` also omitted because use legacy browser support | ||
* there is a need to use it nested with `<object>` and `<a>` | ||
*/ | ||
LogNestedInteractive.prototype.isInteractiveElement = function(node) { | ||
var whitelistTests = this.getConfigWhiteList(); | ||
if (!node) { | ||
return false; | ||
} | ||
if (this.isNodeInteractiveTag(node, whitelistTests)) { | ||
if (ignoredTags.indexOf(node.tag) > -1) { | ||
return true; | ||
} | ||
if (this.isNodeLink(node, whitelistTests)) { | ||
if (ignoreTabindex && reason.indexOf('tabindex') > -1) { | ||
return true; | ||
} | ||
if (this.isNodeNotHiddenInput(node, whitelistTests)) { | ||
if (ignoreUsemapAttribute && reason.indexOf('usemap') > -1) { | ||
return true; | ||
} | ||
if (this.hasNodeHaveTabIndex(node, whitelistTests)) { | ||
return true; | ||
} | ||
if (this.isNodeUseMapInteractive(node, whitelistTests)) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
LogNestedInteractive.prototype.getLogMessage = function(node, parentNode) { | ||
var isParentHasTabIndexAttribute = this.hasNodeHaveAttribute(parentNode, 'tabindex'); | ||
var isParentHasUseMapAttribute = this.hasNodeHaveAttribute(parentNode, 'usemap'); | ||
var parentNodeError = '<' + parentNode.tag + '>'; | ||
var parentReason = isInteractiveElement.reason(parentNode); | ||
var childReason = isInteractiveElement.reason(node); | ||
if (isParentHasTabIndexAttribute) { | ||
parentNodeError = 'an element with attribute `tabindex`'; | ||
} else if (isParentHasUseMapAttribute) { | ||
parentNodeError = 'an element with attribute `usemap`'; | ||
// `reason` for `additionalInteractiveTags` would be `null` | ||
// so we need to handle that and update the reason correctly | ||
if (this.isCustomInteractiveElement(parentNode)) { | ||
parentReason = '<' + parentNode.tag + '>'; | ||
} | ||
var isChildHasTabIndexAttribute = this.hasNodeHaveAttribute(node, 'tabindex'); | ||
var isChildHasUseMapAttribute = this.hasNodeHaveAttribute(node, 'usemap'); | ||
var childNodeError = '<' + node.tag + '>'; | ||
if (isChildHasTabIndexAttribute) { | ||
childNodeError = 'an element with attribute `tabindex`'; | ||
} else if (isChildHasUseMapAttribute) { | ||
childNodeError = 'an element with attribute `usemap`'; | ||
if (this.isCustomInteractiveElement(node)) { | ||
childReason = '<' + node.tag + '>'; | ||
} | ||
return 'Do not use ' + childNodeError + ' inside ' + parentNodeError; | ||
return 'Do not use ' + childReason + ' inside ' + parentReason; | ||
}; | ||
LogNestedInteractive.prototype.findNestedInteractiveElements = function(node, parentInteractiveNode, whitelistTests) { | ||
if (this.isInteractiveElement(parentInteractiveNode, whitelistTests)) { | ||
if (this.isInteractiveElement(node, whitelistTests)) { | ||
this.log({ | ||
message: this.getLogMessage(node, parentInteractiveNode), | ||
line: node.loc && node.loc.start.line, | ||
column: node.loc && node.loc.start.column, | ||
source: this.sourceForNode(node) | ||
}); | ||
return; | ||
} | ||
} | ||
node.children | ||
.filter(isElementNode) | ||
.forEach(function(childNode) { | ||
this.findNestedInteractiveElements(childNode, node, whitelistTests); | ||
}, this); | ||
}; | ||
return LogNestedInteractive; | ||
}; | ||
module.exports.ARRAY_DEPRECATION_MESSAGE = ARRAY_DEPRECATION_MESSAGE; |
{ | ||
"name": "ember-template-lint", | ||
"version": "0.5.10", | ||
"version": "0.5.11", | ||
"description": "Lint your templates.", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -194,17 +194,8 @@ # ember-template-lint | ||
* boolean -- `true` indicates all whitelist test will run, `false` indicates that the rule is disabled. | ||
* array -- an array of whitelisted tests as the following | ||
* object - Containing the following values: | ||
* `ignoredTags` - An array of element tag names that should be whitelisted. Default to `[]`. | ||
* `ignoreTabindex` - When `true` tabindex will be ignored. Defaults to `false`. | ||
* `ignoreUsemapAttribute` - When `true` ignores the `usemap` attribute on `img` and `object` elements. Defaults `false`. | ||
* `additionalInteractiveTags` - An array of element tag names that should also be considered as interactive. Defaults to `[]`.' | ||
Whitelist of option in the configuration (are tags name or element attributes): | ||
* `button` | ||
* `details` | ||
* `embed` | ||
* `iframe` | ||
* `img` | ||
* `input` | ||
* `object` | ||
* `select` | ||
* `textarea` | ||
* `tabindex` | ||
### Deprecations | ||
@@ -211,0 +202,0 @@ |
'use strict'; | ||
var generateRuleTests = require('../../helpers/rule-test-harness'); | ||
var ARRAY_DEPRECATION_MESSAGE = require('../../../lib/rules/lint-nested-interactive').ARRAY_DEPRECATION_MESSAGE; | ||
@@ -8,2 +9,3 @@ generateRuleTests({ | ||
config: true, | ||
@@ -19,37 +21,20 @@ | ||
{ | ||
config: ['button', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<a href="/">button<a href="/">!</a></a>' | ||
}, { | ||
config: ['a', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<a href="/">button<button>!</button></a>' | ||
}, { | ||
config: ['a', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<button>button<button>!</button></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'img', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<button><input type="text"></button>' | ||
}, { | ||
config: ['a', 'button', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<button><details><summary>Some details</summary><p>!</p></details></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'iframe', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<button><embed type="video/quicktime" src="movie.mov" width="640" height="480"></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'img', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
template: '<button><iframe src="/frame.html" width="640" height="480"></iframe></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'tabindex', 'textarea'], | ||
template: '<button><select></select></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'tabindex'], | ||
template: '<button><textarea></textarea></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'img', 'input', 'object', 'select', 'textarea'], | ||
template: '<div tabindex="1"><button></button></div>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'input', 'object', 'select', 'tabindex', 'textarea'], | ||
config: { | ||
ignoredTags: ['button'] | ||
}, | ||
template: '<button><input></button>' | ||
}, | ||
{ | ||
config: { | ||
ignoreTabindex: true | ||
}, | ||
template: '<button><div tabindex=-1></div></button>' | ||
}, | ||
{ | ||
config: { | ||
ignoreUsemapAttribute: true | ||
}, | ||
template: '<button><img usemap=""></button>' | ||
}, { | ||
config: ['a', 'button', 'details', 'embed', 'iframe', 'img', 'input', 'select', 'tabindex', 'textarea'], | ||
template: '<object usemap=""><button></button></object>' | ||
} | ||
@@ -60,3 +45,2 @@ ], | ||
{ | ||
config: ['a'], | ||
template: '<a href="/">button<a href="/">!</a></a>', | ||
@@ -66,3 +50,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use <a> inside <a>', | ||
message: 'Do not use an <a> element with the `href` attribute inside an <a> element with the `href` attribute', | ||
moduleId: 'layout.hbs', | ||
@@ -75,3 +59,2 @@ source: '<a href=\"/\">!</a>', | ||
{ | ||
config: ['a', 'button'], | ||
template: '<a href="/">button<button>!</button></a>', | ||
@@ -81,3 +64,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use <button> inside <a>', | ||
message: 'Do not use <button> inside an <a> element with the `href` attribute', | ||
moduleId: 'layout.hbs', | ||
@@ -90,3 +73,2 @@ source: '<button>!</button>', | ||
{ | ||
config: ['a', 'button'], | ||
template: '<button>button<a href="/">!</a></button>', | ||
@@ -96,3 +78,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use <a> inside <button>', | ||
message: 'Do not use an <a> element with the `href` attribute inside <button>', | ||
moduleId: 'layout.hbs', | ||
@@ -105,3 +87,2 @@ source: '<a href=\"/\">!</a>', | ||
{ | ||
config: ['button'], | ||
template: '<button>button<button>!</button></button>', | ||
@@ -119,3 +100,2 @@ | ||
{ | ||
config: ['button', 'input'], | ||
template: '<button><input type="text"></button>', | ||
@@ -133,3 +113,2 @@ | ||
{ | ||
config: ['button', 'details'], | ||
template: '<button><details><summary>Some details</summary><p>!</p></details></button>', | ||
@@ -147,3 +126,2 @@ | ||
{ | ||
config: ['button', 'embed'], | ||
template: '<button><embed type="video/quicktime" src="movie.mov" width="640" height="480"></button>', | ||
@@ -161,3 +139,2 @@ | ||
{ | ||
config: ['button', 'iframe'], | ||
template: '<button><iframe src="/frame.html" width="640" height="480"></iframe></button>', | ||
@@ -175,3 +152,2 @@ | ||
{ | ||
config: ['button', 'select'], | ||
template: '<button><select></select></button>', | ||
@@ -189,3 +165,2 @@ | ||
{ | ||
config: ['button', 'textarea'], | ||
template: '<button><textarea></textarea></button>', | ||
@@ -203,3 +178,2 @@ | ||
{ | ||
config: ['button', 'tabindex'], | ||
template: '<div tabindex="1"><button></button></div>', | ||
@@ -209,3 +183,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use <button> inside an element with attribute `tabindex`', | ||
message: 'Do not use <button> inside an element with the `tabindex` attribute', | ||
moduleId: 'layout.hbs', | ||
@@ -218,3 +192,2 @@ source: '<button></button>', | ||
{ | ||
config: ['button', 'tabindex'], | ||
template: '<button><div tabindex="1"></div></button>', | ||
@@ -224,3 +197,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use an element with attribute `tabindex` inside <button>', | ||
message: 'Do not use an element with the `tabindex` attribute inside <button>', | ||
moduleId: 'layout.hbs', | ||
@@ -233,3 +206,2 @@ source: '<div tabindex=\"1\"></div>', | ||
{ | ||
config: ['button', 'img'], | ||
template: '<button><img usemap=""></button>', | ||
@@ -239,3 +211,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use an element with attribute `usemap` inside <button>', | ||
message: 'Do not use an <img> element with the `usemap` attribute inside <button>', | ||
moduleId: 'layout.hbs', | ||
@@ -248,3 +220,2 @@ source: '<img usemap=\"\">', | ||
{ | ||
config: ['button', 'object'], | ||
template: '<object usemap=""><button></button></object>', | ||
@@ -254,3 +225,3 @@ | ||
rule: 'nested-interactive', | ||
message: 'Do not use <button> inside an element with attribute `usemap`', | ||
message: 'Do not use <button> inside an <object> element with the `usemap` attribute', | ||
moduleId: 'layout.hbs', | ||
@@ -261,4 +232,55 @@ source: '<button></button>', | ||
} | ||
}, | ||
{ | ||
config: { | ||
additionalInteractiveTags: ['my-special-input'] | ||
}, | ||
template: '<button><my-special-input></my-special-input></button>', | ||
result: { | ||
rule: 'nested-interactive', | ||
message: 'Do not use <my-special-input> inside <button>', | ||
moduleId: 'layout.hbs', | ||
source: '<my-special-input></my-special-input>', | ||
line: 1, | ||
column: 8 | ||
} | ||
}, | ||
// deprecated | ||
{ | ||
config: ['button'], | ||
template: '<button><input></button>', | ||
result: { | ||
rule: 'nested-interactive', | ||
message: ARRAY_DEPRECATION_MESSAGE, | ||
source: '["button"]', | ||
severity: 1 | ||
} | ||
}, | ||
{ | ||
config: ['tabindex'], | ||
template: '<button><div tabindex=-1></div></button>', | ||
result: { | ||
rule: 'nested-interactive', | ||
message: ARRAY_DEPRECATION_MESSAGE, | ||
moduleId: 'layout.hbs', | ||
source: '["tabindex"]', | ||
severity: 1 | ||
} | ||
}, | ||
{ | ||
config: ['usemap'], | ||
template: '<button><img usemap=""></button>', | ||
result: { | ||
rule: 'nested-interactive', | ||
message: ARRAY_DEPRECATION_MESSAGE, | ||
source: '["usemap"]', | ||
severity: 1 | ||
} | ||
} | ||
] | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
107659
48
3075
268