Skip to content

Commit 6d3e666

Browse files
committed
feat: Allow polymorphic linting to be restricted
This changes allows the consumer to restrict polymorphic linting to specified components. Linting components may raise false positives when a component handles behavior that the linter has no way to know. This means that linting components is preferred on very basic utility components.
1 parent 1be7b70 commit 6d3e666

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

__tests__/src/util/getElementType-test.js

+34
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,38 @@ describe('getElementType', () => {
7676
expect(elementType(JSXElementMock('CustomButton', [JSXAttributeMock('as', 'a')]).openingElement)).toBe('button');
7777
});
7878
});
79+
80+
describe('polymorphicPropName settings and explicitly defined polymorphicAllowList in context', () => {
81+
const elementType = getElementType({
82+
settings: {
83+
'jsx-a11y': {
84+
polymorphicPropName: 'asChild',
85+
polymorphicAllowList: [
86+
'Box',
87+
'Icon',
88+
],
89+
components: {
90+
Box: 'div',
91+
Icon: 'svg',
92+
},
93+
},
94+
},
95+
});
96+
97+
it('should not use the polymorphic prop if polymorphicAllowList is defined, but element is not part of polymorphicAllowList', () => {
98+
expect(elementType(JSXElementMock('Spinner', [JSXAttributeMock('asChild', 'img')]).openingElement)).toBe('Spinner');
99+
});
100+
101+
it('should use the polymorphic prop if it is in explicitly defined polymorphicAllowList', () => {
102+
expect(elementType(JSXElementMock('Icon', [JSXAttributeMock('asChild', 'img')]).openingElement)).toBe('img');
103+
});
104+
105+
it('should return the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag', () => {
106+
expect(elementType(JSXElementMock('Box', [JSXAttributeMock('asChild', 'span')]).openingElement)).toBe('span');
107+
});
108+
109+
it('should return the tag name provided by the component mapping if the polymorphic prop, "asChild", defined in the settings is not set', () => {
110+
expect(elementType(JSXElementMock('Box', [JSXAttributeMock('as', 'a')]).openingElement)).toBe('div');
111+
});
112+
});
79113
});

flow/eslint.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type ESLintSettings = {
1010
[string]: mixed,
1111
'jsx-a11y'?: {
1212
polymorphicPropName?: string,
13+
polymorphicAllowList?: Array<string>,
1314
components?: {[string]: string},
1415
},
1516
}

src/util/getElementType.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@ import type { ESLintContext } from '../../flow/eslint';
1111
const getElementType = (context: ESLintContext): ((node: JSXOpeningElement) => string) => {
1212
const { settings } = context;
1313
const polymorphicPropName = settings['jsx-a11y']?.polymorphicPropName;
14+
const polymorphicAllowList = settings['jsx-a11y']?.polymorphicAllowList;
15+
1416
const componentMap = settings['jsx-a11y']?.components;
1517

1618
return (node: JSXOpeningElement): string => {
1719
const polymorphicProp = polymorphicPropName ? getLiteralPropValue(getProp(node.attributes, polymorphicPropName)) : undefined;
18-
const rawType = polymorphicProp ?? elementType(node);
20+
21+
let rawType = elementType(node);
22+
if (polymorphicProp) {
23+
if (!polymorphicAllowList) {
24+
rawType = polymorphicProp;
25+
} else if (polymorphicAllowList.includes(rawType)) {
26+
rawType = polymorphicProp;
27+
}
28+
}
1929

2030
if (!componentMap) {
2131
return rawType;

0 commit comments

Comments
 (0)