Skip to content

Commit

Permalink
Merge branch 'main' into kh-bump-aria-query
Browse files Browse the repository at this point in the history
  • Loading branch information
khiga8 authored Jul 12, 2023
2 parents 2fa5eca + 86d1fa5 commit 4d67b61
Show file tree
Hide file tree
Showing 14 changed files with 514 additions and 263 deletions.
57 changes: 29 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,16 @@ _Note: This is experimental and subject to change._

The `react` config includes rules which target specific HTML elements. You may provide a mapping of custom components to an HTML element in your `eslintrc` configuration to increase linter coverage.

For each component, you may specify a `default` and/or `props`. `default` may make sense if there's a 1:1 mapping between a component and an HTML element. However, if the HTML output of a component is dependent on a prop value, you can provide a mapping using the `props` key. To minimize conflicts and complexity, this currently only supports the mapping of a single prop type.
By default, these eslint rules will check the "as" prop for underlying element changes. If your repo uses a different prop name for polymorphic components provide the prop name in your `eslintrc` configuration under `polymorphicPropName`.

```json
{
"settings": {
"github": {
"polymorphicPropName": "asChild",
"components": {
"Box": {"default": "p"},
"Link": {"props": {"as": {"undefined": "a", "a": "a", "button": "button"}}}
"Box": "p",
"Link": "a"
}
}
}
Expand All @@ -66,9 +67,7 @@ This config will be interpreted in the following way:

- All `<Box>` elements will be treated as a `p` element type.
- `<Link>` without a defined `as` prop will be treated as a `a`.
- `<Link as='a'>` will treated as an `a` element type.
- `<Link as='button'>` will be treated as a `button` element type.
- `<Link as='summary'>` will be treated as the raw `Link` type because there is no configuration set for `as='summary'`.

### Rules

Expand All @@ -82,28 +81,30 @@ This config will be interpreted in the following way:
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
❌ Deprecated.

| Name                              | Description | 💼 | 🔧 ||
| :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |
| [a11y-aria-label-is-well-formatted](docs/rules/a11y-aria-label-is-well-formatted.md) | [aria-label] text should be formatted as you would visual text. | ⚛️ | | |
| [a11y-no-generic-link-text](docs/rules/a11y-no-generic-link-text.md) | disallow generic link text | | ||
| [array-foreach](docs/rules/array-foreach.md) | enforce `for..of` loops over `Array.forEach` || | |
| [async-currenttarget](docs/rules/async-currenttarget.md) | disallow `event.currentTarget` calls inside of async functions | 🔍 | | |
| [async-preventdefault](docs/rules/async-preventdefault.md) | disallow `event.preventDefault` calls inside of async functions | 🔍 | | |
| [authenticity-token](docs/rules/authenticity-token.md) | disallow usage of CSRF tokens in JavaScript | 🔐 | | |
| [get-attribute](docs/rules/get-attribute.md) | disallow wrong usage of attribute names | 🔍 | 🔧 | |
| [js-class-name](docs/rules/js-class-name.md) | enforce a naming convention for js- prefixed classes | 🔐 | | |
| [no-blur](docs/rules/no-blur.md) | disallow usage of `Element.prototype.blur()` | 🔍 | | |
| [no-d-none](docs/rules/no-d-none.md) | disallow usage the `d-none` CSS class | 🔐 | | |
| [no-dataset](docs/rules/no-dataset.md) | enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist` | 🔍 | | |
| [no-dynamic-script-tag](docs/rules/no-dynamic-script-tag.md) | disallow creating dynamic script tags || | |
| [no-implicit-buggy-globals](docs/rules/no-implicit-buggy-globals.md) | disallow implicit global variables || | |
| [no-inner-html](docs/rules/no-inner-html.md) | disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent` | 🔍 | | |
| [no-innerText](docs/rules/no-innerText.md) | disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent` | 🔍 | 🔧 | |
| [no-then](docs/rules/no-then.md) | enforce using `async/await` syntax over Promises || | |
| [no-useless-passive](docs/rules/no-useless-passive.md) | disallow marking a event handler as passive when it has no effect | 🔍 | 🔧 | |
| [prefer-observers](docs/rules/prefer-observers.md) | disallow poorly performing event listeners | 🔍 | | |
| [require-passive-events](docs/rules/require-passive-events.md) | enforce marking high frequency event handlers as passive | 🔍 | | |
| [role-supports-aria-props](docs/rules/role-supports-aria-props.md) | Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. | ⚛️ | | |
| [unescaped-html-literal](docs/rules/unescaped-html-literal.md) | disallow unescaped HTML literals | 🔍 | | |
| Name                                        | Description | 💼 | 🔧 ||
| :------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |
| [a11y-aria-label-is-well-formatted](docs/rules/a11y-aria-label-is-well-formatted.md) | [aria-label] text should be formatted as you would visual text. | ⚛️ | | |
| [a11y-no-generic-link-text](docs/rules/a11y-no-generic-link-text.md) | disallow generic link text | | ||
| [a11y-no-visually-hidden-interactive-element](docs/rules/a11y-no-visually-hidden-interactive-element.md) | Ensures that interactive elements are not visually hidden | ⚛️ | | |
| [a11y-svg-has-accessible-name](docs/rules/a11y-svg-has-accessible-name.md) | SVGs must have an accessible name | ⚛️ | | |
| [array-foreach](docs/rules/array-foreach.md) | enforce `for..of` loops over `Array.forEach` || | |
| [async-currenttarget](docs/rules/async-currenttarget.md) | disallow `event.currentTarget` calls inside of async functions | 🔍 | | |
| [async-preventdefault](docs/rules/async-preventdefault.md) | disallow `event.preventDefault` calls inside of async functions | 🔍 | | |
| [authenticity-token](docs/rules/authenticity-token.md) | disallow usage of CSRF tokens in JavaScript | 🔐 | | |
| [get-attribute](docs/rules/get-attribute.md) | disallow wrong usage of attribute names | 🔍 | 🔧 | |
| [js-class-name](docs/rules/js-class-name.md) | enforce a naming convention for js- prefixed classes | 🔐 | | |
| [no-blur](docs/rules/no-blur.md) | disallow usage of `Element.prototype.blur()` | 🔍 | | |
| [no-d-none](docs/rules/no-d-none.md) | disallow usage the `d-none` CSS class | 🔐 | | |
| [no-dataset](docs/rules/no-dataset.md) | enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist` | 🔍 | | |
| [no-dynamic-script-tag](docs/rules/no-dynamic-script-tag.md) | disallow creating dynamic script tags || | |
| [no-implicit-buggy-globals](docs/rules/no-implicit-buggy-globals.md) | disallow implicit global variables || | |
| [no-inner-html](docs/rules/no-inner-html.md) | disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent` | 🔍 | | |
| [no-innerText](docs/rules/no-innerText.md) | disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent` | 🔍 | 🔧 | |
| [no-then](docs/rules/no-then.md) | enforce using `async/await` syntax over Promises || | |
| [no-useless-passive](docs/rules/no-useless-passive.md) | disallow marking a event handler as passive when it has no effect | 🔍 | 🔧 | |
| [prefer-observers](docs/rules/prefer-observers.md) | disallow poorly performing event listeners | 🔍 | | |
| [require-passive-events](docs/rules/require-passive-events.md) | enforce marking high frequency event handlers as passive | 🔍 | | |
| [role-supports-aria-props](docs/rules/role-supports-aria-props.md) | Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. | ⚛️ | | |
| [unescaped-html-literal](docs/rules/unescaped-html-literal.md) | disallow unescaped HTML literals | 🔍 | | |

<!-- end auto-generated rules list -->
79 changes: 79 additions & 0 deletions docs/rules/a11y-no-visually-hidden-interactive-element.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Ensures that interactive elements are not visually hidden (`github/a11y-no-visually-hidden-interactive-element`)

💼 This rule is enabled in the ⚛️ `react` config.

<!-- end auto-generated rule header -->

## Rule Details

This rule guards against visually hiding interactive elements. If a sighted keyboard user navigates to an interactive element that is visually hidden they might become confused and assume that keyboard focus has been lost.

Note: we are not guarding against visually hidden `input` elements at this time. Some visually hidden inputs might cause a false positive (e.g. some file inputs).

### Why do we visually hide content?

Visually hiding content can be useful when you want to provide information specifically to screen reader users or other assitive technology users while keeping content hidden from sighted users.

Applying the following css will visually hide content while still making it accessible to screen reader users.

```css
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
```

👎 Examples of **incorrect** code for this rule:

```jsx
<button className="visually-hidden">Submit</button>
```

```jsx
<VisuallyHidden>
<button>Submit</button>
</VisuallyHidden>
```

```jsx
<VisuallyHidden as="button">Submit</VisuallyHidden>
```

👍 Examples of **correct** code for this rule:

```jsx
<h2 className="visually-hidden">Welcome to GitHub</h2>
```

```jsx
<VisuallyHidden>
<h2>Welcome to GitHub</h2>
</VisuallyHidden>
```

```jsx
<VisuallyHidden as="h2">Welcome to GitHub</VisuallyHidden>
```

## Options

- className - A css className that visually hides content. Defaults to `sr-only`.
- componentName - A react component name that visually hides content. Defaults to `VisuallyHidden`.
- htmlPropName - A prop name used to replace the semantic element that is rendered. Defaults to `as`.

```json
{
"a11y-no-visually-hidden-interactive-element": [
"error",
{
"className": "visually-hidden",
"componentName": "VisuallyHidden",
"htmlPropName": "as"
}
]
}
```

## Version
73 changes: 73 additions & 0 deletions docs/rules/a11y-svg-has-accessible-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SVGs must have an accessible name (`github/a11y-svg-has-accessible-name`)

💼 This rule is enabled in the ⚛️ `react` config.

<!-- end auto-generated rule header -->

## Rule Details

An `<svg>` must have an accessible name. Set `aria-label` or `aria-labelledby`, or nest a `<title>` element as the first child of the `<svg>` element.

However, if the `<svg>` is purely decorative, hide it with `aria-hidden="true"` or `role="presentation"`.

## Resources

- [Accessible SVGs](https://css-tricks.com/accessible-svgs/)

## Examples

### **Incorrect** code for this rule 👎

```html
<svg height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg height='100' width='100' title='Circle with a black outline and red fill'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
<title>Circle with a black outline and red fill</title>
</svg>
```

### **Correct** code for this rule 👍

```html
<svg height='100' width='100'>
<title>Circle with a black outline and red fill</title>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-label='Circle with a black outline and red fill' height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-labelledby='circle_text' height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-hidden='true' height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg role='presentation' height='100' width='100'>
<circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

## Version
2 changes: 2 additions & 0 deletions lib/configs/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module.exports = {
rules: {
'jsx-a11y/role-supports-aria-props': 'off', // Override with github/role-supports-aria-props until https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/910 is resolved
'github/a11y-aria-label-is-well-formatted': 'error',
'github/a11y-no-visually-hidden-interactive-element': 'error',
'github/a11y-svg-has-accessible-name': 'error',
'github/role-supports-aria-props': 'error',
'jsx-a11y/no-aria-hidden-on-focusable': 'error',
'jsx-a11y/no-autofocus': 'off',
Expand Down
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module.exports = {
rules: {
'a11y-no-visually-hidden-interactive-element': require('./rules/a11y-no-visually-hidden-interactive-element'),
'a11y-no-generic-link-text': require('./rules/a11y-no-generic-link-text'),
'a11y-aria-label-is-well-formatted': require('./rules/a11y-aria-label-is-well-formatted'),
'a11y-svg-has-accessible-name': require('./rules/a11y-svg-has-accessible-name'),
'array-foreach': require('./rules/array-foreach'),
'async-currenttarget': require('./rules/async-currenttarget'),
'async-preventdefault': require('./rules/async-preventdefault'),
Expand Down
Loading

0 comments on commit 4d67b61

Please sign in to comment.