From de35b2935c800951e99a5e5ec71349cfa25d5cdd Mon Sep 17 00:00:00 2001 From: Sandi Barr Date: Wed, 16 Nov 2022 09:35:39 -0600 Subject: [PATCH 1/2] feat(eslint-plugin-template): [accessibility-elements-content] add allowlist option --- .../rules/accessibility-elements-content.md | 303 +++--------------- .../rules/accessibility-elements-content.ts | 35 +- .../accessibility-elements-content/cases.ts | 52 ++- 3 files changed, 108 insertions(+), 282 deletions(-) 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..00df825e0 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..3328b4bb3 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..19f225e79 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' }, + }), ]; From def4b217cf0f5c7a84a14e18621501838f986312 Mon Sep 17 00:00:00 2001 From: Sandi Barr Date: Wed, 16 Nov 2022 10:54:42 -0600 Subject: [PATCH 2/2] feat(eslint-plugin-template): [accessibility-elements-content] camelCase allowList option --- .../docs/rules/accessibility-elements-content.md | 6 +++--- .../src/rules/accessibility-elements-content.ts | 10 +++++----- .../rules/accessibility-elements-content/cases.ts | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) 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 00df825e0..c8b40585a 100644 --- a/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md +++ b/packages/eslint-plugin-template/docs/rules/accessibility-elements-content.md @@ -30,7 +30,7 @@ interface Options { /** * Default: `["aria-label","innerHtml","innerHTML","innerText","outerHTML","title"]` */ - allowlist?: string[]; + allowList?: string[]; } ``` @@ -137,7 +137,7 @@ interface Options { "@angular-eslint/template/accessibility-elements-content": [ "error", { - "allowlist": [ + "allowList": [ "aria-labelledby" ] } @@ -214,7 +214,7 @@ interface Options { "@angular-eslint/template/accessibility-elements-content": [ "error", { - "allowlist": [ + "allowList": [ "appTooltipLabel", "ariaLabel" ] 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 3328b4bb3..7812ffd99 100644 --- a/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts +++ b/packages/eslint-plugin-template/src/rules/accessibility-elements-content.ts @@ -7,7 +7,7 @@ import { isHiddenFromScreenReader } from '../utils/is-hidden-from-screen-reader' type Options = [ { - readonly allowlist?: readonly string[]; + readonly allowList?: readonly string[]; }, ]; export type MessageIds = 'accessibilityElementsContent'; @@ -21,7 +21,7 @@ const DEFAULT_SAFELIST_ATTRIBUTES: readonly string[] = [ 'title', ]; const DEFAULT_OPTIONS: Options[0] = { - allowlist: DEFAULT_SAFELIST_ATTRIBUTES, + allowList: DEFAULT_SAFELIST_ATTRIBUTES, }; export default createESLintRule({ @@ -37,7 +37,7 @@ export default createESLintRule({ { additionalProperties: false, properties: { - allowlist: { + allowList: { items: { type: 'string' }, type: 'array', uniqueItems: true, @@ -51,7 +51,7 @@ export default createESLintRule({ }, }, defaultOptions: [DEFAULT_OPTIONS], - create(context, [{ allowlist }]) { + create(context, [{ allowList }]) { const parserServices = getTemplateParserServices(context); return { @@ -63,7 +63,7 @@ export default createESLintRule({ const { attributes, inputs, name: element, sourceSpan } = node; const safelistAttributes: ReadonlySet = new Set([ ...DEFAULT_SAFELIST_ATTRIBUTES, - ...(allowlist ?? []), + ...(allowList ?? []), ]); const hasAttributeSafelisted = [...attributes, ...inputs] .map(({ name }) => name) 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 19f225e79..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 @@ -28,7 +28,7 @@ export const valid = [ `, options: [ { - allowlist: ['appTooltipLabel', 'ariaLabel'], + allowList: ['appTooltipLabel', 'ariaLabel'], }, ], }, @@ -65,12 +65,12 @@ export const invalid = [ convertAnnotatedSourceToFailureCase({ messageId, description: - 'should fail if attribute/input/directive is not configured in allowlist', + 'should fail if attribute/input/directive is not configured in allowList', annotatedSource: ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `, - options: [{ allowlist: ['aria-labelledby'] }], + options: [{ allowList: ['aria-labelledby'] }], data: { element: 'button' }, }), ];