Skip to content

Commit

Permalink
fix(eslint-plugin-template): accessibility-elements-content not allow…
Browse files Browse the repository at this point in the history
…ing some attributes/inputs
  • Loading branch information
rafaelss95 committed Apr 7, 2021
1 parent e36ed3e commit 469d415
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import {
createESLintRule,
getTemplateParserServices,
} from '../utils/create-eslint-rule';
import { isHiddenFromScreenReader } from '../utils/is-hidden-from-screen-reader';

type Options = [];
export type MessageIds = 'accessibilityElementsContent';
export const RULE_NAME = 'accessibility-elements-content';
const innerContentInputs: ReadonlySet<string> = new Set([
const safelistAttributes: ReadonlySet<string> = new Set([
'aria-label',
'innerHtml',
'innerHTML',
'innerText',
'outerHTML',
'title',
]);

export default createESLintRule<Options, MessageIds>({
Expand All @@ -19,30 +23,31 @@ export default createESLintRule<Options, MessageIds>({
type: 'suggestion',
docs: {
description:
'Ensures that the heading, anchor and button elements have content in it.',
'Ensures that the heading, anchor and button elements have content in it',
category: 'Best Practices',
recommended: false,
},
schema: [],
messages: {
accessibilityElementsContent: '<{{element}}/> should have content.',
accessibilityElementsContent: '<{{element}}> should have content',
},
},
defaultOptions: [],
create(context) {
const parserServices = getTemplateParserServices(context);

return {
'Element[name=/^(a|button|h1|h2|h3|h4|h5|h6)$/][children.length=0]'({
inputs,
name: element,
sourceSpan,
}: TmplAstElement) {
const hasInnerContent = inputs.some(({ name }) =>
innerContentInputs.has(name),
);
'Element[name=/^(a|button|h1|h2|h3|h4|h5|h6)$/][children.length=0]'(
node: TmplAstElement,
) {
if (isHiddenFromScreenReader(node)) return;

if (hasInnerContent) return;
const { attributes, inputs, name: element, sourceSpan } = node;
const hasAttributeSafelisted = [...attributes, ...inputs]
.map(({ name }) => name)
.some((inputName) => safelistAttributes.has(inputName));

if (hasAttributeSafelisted) return;

const loc = parserServices.convertNodeSourceSpanToLoc(sourceSpan);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,39 @@ ruleTester.run(RULE_NAME, rule, {
'<a><app-content></app-content></a>',
'<a [innerHTML]="dangerouslySetHTML"></a>',
'<a [innerText]="text"></a>',
'<a [outerHTML]="text"></a>',
'<a aria-hidden></a>',
'<button [attr.aria-hidden]="true"></button>',
'<h5 [attr.aria-label]="text"></h5>',
'<h6 title="text"></h6>',
],
invalid: [
convertAnnotatedSourceToFailureCase({
messageId,
description: 'should fail with no content in heading tag',
annotatedSource: `
<h1 class="size-1"></h1>
~~~~~~~~~~~~~~~~~~~~~~~~
`,
messageId,
data: { element: 'h1' }
}),
convertAnnotatedSourceToFailureCase({
messageId,
description: 'should fail with no content in anchor tag',
annotatedSource: `
<a href="#" [routerLink]="['route1']"></a>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`,
messageId,
data: { element: 'a' }
}),
convertAnnotatedSourceToFailureCase({
messageId,
description: 'should fail with no content in button tag',
annotatedSource: `
<button></button>
~~~~~~~~~~~~~~~~~
`,
messageId,
data: { element: 'button' }
}),
],
});

0 comments on commit 469d415

Please sign in to comment.