Skip to content

Commit

Permalink
feat(sdk/skyux-eslint): add no-radio-group-with-nested-list rule (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Blackbaud-SteveBrush authored Dec 18, 2024
1 parent 6a3a4c9 commit 7f34a3f
Show file tree
Hide file tree
Showing 14 changed files with 843 additions and 193 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# `skyux-eslint-template/no-radio-group-with-nested-list`

Prevents nesting of ordered and unordered lists within `sky-radio-group` components, for accessibility reasons.

- Type: problem

<br>

## Rule Options

The rule does not have any configuration options.

<br>

## Usage Examples

#### Default Config

```json
{
"rules": {
"skyux-eslint-template/no-radio-group-with-nested-list": ["error"]
}
}
```

#### ❌ Invalid Code

```html
<sky-radio-group>
<ul>
~~~~
<li>
~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
<li>
~~~~
<sky-radio>
~~~~~~~~~~~
<sky-radio-label>Foo</sky-radio-label>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</sky-radio>
~~~~~~~~~~~~
</li>
~~~~~
</ul>
~~~~~
</sky-radio-group>
```

```html
<sky-radio-group>
<ol>
~~~~
<li *ngFor="let item of items">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
</ol>
~~~~~
</sky-radio-group>
```
1 change: 1 addition & 0 deletions libs/sdk/skyux-eslint/src/configs/template-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default [
name: 'skyux-eslint-template-all',
rules: {
'skyux-eslint-template/no-deprecated-directives': 'error',
'skyux-eslint-template/no-radio-group-with-nested-list': 'error',
'skyux-eslint-template/no-unbound-id': 'error',
'skyux-eslint-template/prefer-label-text': 'error',
},
Expand Down
1 change: 1 addition & 0 deletions libs/sdk/skyux-eslint/src/configs/template-recommended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default [
name: 'skyux-eslint-template-all',
rules: {
'skyux-eslint-template/no-deprecated-directives': 'error',
'skyux-eslint-template/no-radio-group-with-nested-list': 'error',
'skyux-eslint-template/no-unbound-id': 'error',
'skyux-eslint-template/prefer-label-text': 'error',
},
Expand Down
5 changes: 5 additions & 0 deletions libs/sdk/skyux-eslint/src/plugins/template-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
rule as noDeprecatedDirectives,
RULE_NAME as noDeprecatedDirectivesRuleName,
} from '../rules/template/no-deprecated-directives';
import {
rule as noRadioGroupWithNestedList,
RULE_NAME as noRadioGroupWithNestedListRuleName,
} from '../rules/template/no-radio-group-with-nested-list';
import {
rule as noUnboundId,
RULE_NAME as noUnboundIdRuleName,
Expand All @@ -21,6 +25,7 @@ export default {
processors,
rules: {
[noDeprecatedDirectivesRuleName]: noDeprecatedDirectives,
[noRadioGroupWithNestedListRuleName]: noRadioGroupWithNestedList,
[noUnboundIdRuleName]: noUnboundId,
[preferLabelTextRuleName]: preferLabelText,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { convertAnnotatedSourceToFailureCase } from '@angular-eslint/test-utils';

import { createTemplateRuleTester } from '../testing/create-template-rule-tester';

import { RULE_NAME, messageId, rule } from './no-radio-group-with-nested-list';

const ruleTester = createTemplateRuleTester();

ruleTester.run(RULE_NAME, rule, {
valid: [
`<sky-radio-group></sky-radio-group>`,
`<sky-radio-group>
<sky-radio labelText="Foo" />
<sky-radio>
<sky-radio-label>Foo</sky-radio-label>
</sky-radio>
</sky-radio-group>`,
`<sky-radio-group>
@for (item of items; track item.name) {
<sky-radio labelText="Foo" />
}
</sky-radio-group>`,
`<sky-radio-group>
<sky-radio *ngFor="let item of items" labelText="Foo" />
</sky-radio-group>`,
],
invalid: [
convertAnnotatedSourceToFailureCase({
description: 'should fail if radio-group has nested list',
annotatedSource: `
<sky-radio-group>
<ul>
~~~~
<li>
~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
<li>
~~~~
<sky-radio><sky-radio-label>Foo</sky-radio-label></sky-radio>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
</ul>
~~~~~
</sky-radio-group>
`,
annotatedOutput: `
<sky-radio-group>
~
~
~
~
<sky-radio labelText="Foo" />
~
~
~
~
~
<sky-radio><sky-radio-label>Foo</sky-radio-label></sky-radio>
~
~
~
~
~
</sky-radio-group>
`,
messageId,
data: {},
}),
convertAnnotatedSourceToFailureCase({
description: 'should handle ngFor structural directives',
annotatedSource: `
<sky-radio-group>
<ul>
~~~~
<li *ngFor="let item of items">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
</ul>
~~~~~
</sky-radio-group>
`,
annotatedOutput: `
<sky-radio-group>
~
~
<ng-container *ngFor="let item of items">
~
<sky-radio labelText="Foo" />
~
</ng-container>
~
~
~
</sky-radio-group>
`,
messageId,
data: {},
}),
convertAnnotatedSourceToFailureCase({
description: 'should ignore other structural directives',
annotatedSource: `
<sky-radio-group>
<ul>
~~~~
<li *ngIf="foobar">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
</ul>
~~~~~
</sky-radio-group>
`,
annotatedOutput: `
<sky-radio-group>
~
~
~
~
<sky-radio labelText="Foo" />
~
~
~
~
~
</sky-radio-group>
`,
messageId,
data: {},
}),
convertAnnotatedSourceToFailureCase({
description: 'should handle built-in control flow',
annotatedSource: `
<sky-radio-group>
<ul>
~~~~
@for (item of items; track item.name) {
<li>
~~~~
<sky-radio labelText="Foo" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
~~~~~
}
</ul>
~~~~~
</sky-radio-group>
`,
annotatedOutput: `
<sky-radio-group>
~
~
@for (item of items; track item.name) {
~
~
<sky-radio labelText="Foo" />
~
~
~
}
~
~
</sky-radio-group>
`,
messageId,
data: {},
}),
],
});
Loading

0 comments on commit 7f34a3f

Please sign in to comment.