diff --git a/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md b/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md index d1c596acb..c8b40585a 100644 --- a/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md +++ b/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md @@ -23,7 +23,17 @@ Ensures that the heading, anchor and button elements have content in it ## Rule Options -The rule does not have any configuration options. +The rule accepts an options object with the following properties: + +```ts +interface Options { + /** + * Default: `["aria-label","innerHtml","innerHTML","innerText","outerHTML","title"]` + */ + allowList?: string[]; +} + +```
@@ -113,26 +123,24 @@ The rule does not have any configuration options. ~~~~~~~~~~~~~~~~~ ``` - -
---
-
-✅ - Toggle examples of correct code for this rule - -
- -#### Default Config +#### Custom Config ```json { "rules": { "@angular-eslint/template/accessibility-elements-content": [ - "error" + "error", + { + "allowList": [ + "aria-labelledby" + ] + } ] } } @@ -140,37 +148,14 @@ The rule does not have any configuration options.
-#### ✅ Valid Code +#### ❌ Invalid Code ```html -

Heading Content!

-``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` -
- -#### ✅ Valid Code - -```html -

-``` +

@@ -178,29 +163,8 @@ The rule does not have any configuration options.
-#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -

-``` - -
- ---- +
+✅ - Toggle examples of correct code for this rule
@@ -221,215 +185,19 @@ The rule does not have any configuration options. #### ✅ Valid Code ```html +

Heading Content!

+

+

-``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html Anchor Content! -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html -``` - -
- ---- - -
- -#### Default Config - -```json -{ - "rules": { - "@angular-eslint/template/accessibility-elements-content": [ - "error" - ] - } -} -``` - -
- -#### ✅ Valid Code - -```html
+
```
@@ -438,13 +206,19 @@ The rule does not have any configuration options.
-#### Default Config +#### Custom Config ```json { "rules": { "@angular-eslint/template/accessibility-elements-content": [ - "error" + "error", + { + "allowList": [ + "appTooltipLabel", + "ariaLabel" + ] + } ] } } @@ -455,7 +229,10 @@ The rule does not have any configuration options. #### ✅ Valid Code ```html -
+ + +

+ ```
diff --git a/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts b/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts index 6c7985ab5..7812ffd99 100644 --- a/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts +++ b/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts @@ -5,17 +5,24 @@ import { } from '../utils/create-eslint-rule'; import { isHiddenFromScreenReader } from '../utils/is-hidden-from-screen-reader'; -type Options = []; +type Options = [ + { + readonly allowList?: readonly string[]; + }, +]; export type MessageIds = 'accessibilityElementsContent'; export const RULE_NAME = 'accessibility-elements-content'; -const safelistAttributes: ReadonlySet = new Set([ +const DEFAULT_SAFELIST_ATTRIBUTES: readonly string[] = [ 'aria-label', 'innerHtml', 'innerHTML', 'innerText', 'outerHTML', 'title', -]); +]; +const DEFAULT_OPTIONS: Options[0] = { + allowList: DEFAULT_SAFELIST_ATTRIBUTES, +}; export default createESLintRule({ name: RULE_NAME, @@ -26,13 +33,25 @@ export default createESLintRule({ 'Ensures that the heading, anchor and button elements have content in it', recommended: false, }, - schema: [], + schema: [ + { + additionalProperties: false, + properties: { + allowList: { + items: { type: 'string' }, + type: 'array', + uniqueItems: true, + }, + }, + type: 'object', + }, + ], messages: { accessibilityElementsContent: '<{{element}}> should have content', }, }, - defaultOptions: [], - create(context) { + defaultOptions: [DEFAULT_OPTIONS], + create(context, [{ allowList }]) { const parserServices = getTemplateParserServices(context); return { @@ -42,6 +61,10 @@ export default createESLintRule({ if (isHiddenFromScreenReader(node)) return; const { attributes, inputs, name: element, sourceSpan } = node; + const safelistAttributes: ReadonlySet = new Set([ + ...DEFAULT_SAFELIST_ATTRIBUTES, + ...(allowList ?? []), + ]); const hasAttributeSafelisted = [...attributes, ...inputs] .map(({ name }) => name) .some((inputName) => safelistAttributes.has(inputName)); diff --git a/packages/eslint-plugin-template/tests/rules/accessibility-elements-content/cases.ts b/packages/eslint-plugin-template/tests/rules/accessibility-elements-content/cases.ts index c4336e630..6c9fd49a8 100644 --- a/packages/eslint-plugin-template/tests/rules/accessibility-elements-content/cases.ts +++ b/packages/eslint-plugin-template/tests/rules/accessibility-elements-content/cases.ts @@ -4,19 +4,34 @@ import type { MessageIds } from '../../../src/rules/accessibility-elements-conte const messageId: MessageIds = 'accessibilityElementsContent'; export const valid = [ - '

Heading Content!

', - '

', - '

', - '

', - 'Anchor Content!', - '', - '', - '', - '', - '', - '', - '
', - '
', + ` +

Heading Content!

+

+

+

+ Anchor Content! + + + + + + +
+
+ `, + { + code: ` + + +

+ + `, + options: [ + { + allowList: ['appTooltipLabel', 'ariaLabel'], + }, + ], + }, ]; export const invalid = [ @@ -47,4 +62,15 @@ export const invalid = [ messageId, data: { element: 'button' }, }), + convertAnnotatedSourceToFailureCase({ + messageId, + description: + 'should fail if attribute/input/directive is not configured in allowList', + annotatedSource: ` + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + `, + options: [{ allowList: ['aria-labelledby'] }], + data: { element: 'button' }, + }), ];