Skip to content

Commit

Permalink
[Fix] no-noninteractive-element-interactions: Ignore contenteditabl…
Browse files Browse the repository at this point in the history
…e elements in no-noninteractive-element-interactions
  • Loading branch information
JoshuaKGoldberg authored and ljharb committed Jan 9, 2023
1 parent 3d77c84 commit 9aa878b
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 0 deletions.
2 changes: 2 additions & 0 deletions __mocks__/JSXAttributeMock.js
Expand Up @@ -24,6 +24,8 @@ export default function JSXAttributeMock(prop: string, value: mixed, isExpressio
let attributeValue = astValue;
if (isExpressionContainer || astValue.type !== 'Literal') {
attributeValue = JSXExpressionContainerMock(astValue);
} else if (attributeValue.type === 'Literal' && !('raw' in (attributeValue: any))) {
(attributeValue: any).raw = JSON.stringify((attributeValue: any).value);
}

return {
Expand Down
Expand Up @@ -337,6 +337,9 @@ const neverValid = [
{ code: '<time onClick={() => {}} />;', errors: [expectedError] },
{ code: '<ol onClick={() => {}} />;', errors: [expectedError] },
{ code: '<ul onClick={() => {}} />;', errors: [expectedError] },
{ code: '<ul contentEditable="false" onClick={() => {}} />;', errors: [expectedError] },
{ code: '<article contentEditable onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div contentEditable role="article" onKeyDown={() => {}} />;', errors: [expectedError] },
/* HTML elements attributed with a non-interactive role */
{ code: '<div role="alert" onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div role="alertdialog" onClick={() => {}} />;', errors: [expectedError] },
Expand Down
51 changes: 51 additions & 0 deletions __tests__/src/util/isContentEditable-test.js
@@ -0,0 +1,51 @@
import expect from 'expect';
import isContentEditable from '../../../src/util/isContentEditable';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';

describe('isContentEditable', () => {
describe('HTML5', () => {
describe('content editable', () => {
it('should identify HTML5 contentEditable elements', () => {
const attributes = [
JSXAttributeMock('contentEditable', 'true'),
];
expect(isContentEditable('some tag', attributes))
.toBe(true);
});
});

describe('not content editable', () => {
it('should not identify HTML5 content editable elements with null as the value', () => {
const attributes = [
JSXAttributeMock('contentEditable', null),
];
expect(isContentEditable('some tag', attributes))
.toBe(false);
});

it('should not identify HTML5 content editable elements with undefined as the value', () => {
const attributes = [
JSXAttributeMock('contentEditable', undefined),
];
expect(isContentEditable('some tag', attributes))
.toBe(false);
});

it('should not identify HTML5 content editable elements with true as the value', () => {
const attributes = [
JSXAttributeMock('contentEditable', true),
];
expect(isContentEditable('some tag', attributes))
.toBe(false);
});

it('should not identify HTML5 content editable elements with "false" as the value', () => {
const attributes = [
JSXAttributeMock('contentEditable', 'false'),
];
expect(isContentEditable('some tag', attributes))
.toBe(false);
});
});
});
});
2 changes: 2 additions & 0 deletions src/rules/no-noninteractive-element-interactions.js
Expand Up @@ -22,6 +22,7 @@ import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '.
import { arraySchema, generateObjSchema } from '../util/schemas';
import getElementType from '../util/getElementType';
import isAbstractRole from '../util/isAbstractRole';
import isContentEditable from '../util/isContentEditable';
import isHiddenFromScreenReader from '../util/isHiddenFromScreenReader';
import isInteractiveElement from '../util/isInteractiveElement';
import isInteractiveRole from '../util/isInteractiveRole';
Expand Down Expand Up @@ -78,6 +79,7 @@ export default ({
}
if (
!hasInteractiveProps
|| isContentEditable(type, attributes)
|| isHiddenFromScreenReader(type, attributes)
|| isPresentationRole(type, attributes)
) {
Expand Down
7 changes: 7 additions & 0 deletions src/util/isContentEditable.js
@@ -0,0 +1,7 @@
import { getProp } from 'jsx-ast-utils';

export default function isContentEditable(tagName, attributes) {
const prop = getProp(attributes, 'contentEditable');

return prop?.value?.raw === '"true"';
}

0 comments on commit 9aa878b

Please sign in to comment.