@atlaskit/eslint-plugin-design-system 9.5.2 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +991 -410
- package/README.md +1 -0
- package/constellation/index/usage.mdx +1 -0
- package/constellation/use-popup-label/usage.mdx +53 -0
- package/dist/cjs/presets/all.codegen.js +2 -1
- package/dist/cjs/presets/recommended.codegen.js +2 -1
- package/dist/cjs/rules/icon-label/index.js +16 -12
- package/dist/cjs/rules/index.codegen.js +3 -1
- package/dist/cjs/rules/use-popup-label/index.js +90 -0
- package/dist/es2019/presets/all.codegen.js +2 -1
- package/dist/es2019/presets/recommended.codegen.js +2 -1
- package/dist/es2019/rules/icon-label/index.js +12 -2
- package/dist/es2019/rules/index.codegen.js +3 -1
- package/dist/es2019/rules/use-popup-label/index.js +80 -0
- package/dist/esm/presets/all.codegen.js +2 -1
- package/dist/esm/presets/recommended.codegen.js +2 -1
- package/dist/esm/rules/icon-label/index.js +16 -12
- package/dist/esm/rules/index.codegen.js +3 -1
- package/dist/esm/rules/use-popup-label/index.js +84 -0
- package/dist/types/index.codegen.d.ts +2 -0
- package/dist/types/presets/all.codegen.d.ts +2 -1
- package/dist/types/presets/recommended.codegen.d.ts +2 -1
- package/dist/types/rules/index.codegen.d.ts +1 -0
- package/dist/types/rules/use-popup-label/index.d.ts +3 -0
- package/dist/types/rules/use-tokens-typography/utils.d.ts +0 -33
- package/dist/types-ts4.5/index.codegen.d.ts +2 -0
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
- package/dist/types-ts4.5/rules/use-popup-label/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/utils.d.ts +0 -33
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ module.exports = {
|
|
|
78
78
|
| <a href="./src/rules/use-heading/README.md">use-heading</a> | Encourage the usage of heading components. | | Yes | Yes |
|
|
79
79
|
| <a href="./src/rules/use-heading-level-in-spotlight-card/README.md">use-heading-level-in-spotlight-card</a> | Inform developers of eventual requirement of `headingLevel` prop in `SpotlightCard` component. The heading level should be the appropriate level according to the surrounding context. | Yes | Yes | |
|
|
80
80
|
| <a href="./src/rules/use-href-in-link-item/README.md">use-href-in-link-item</a> | Inform developers of eventual requirement of `href` prop in `LinkItem` component. Elements with a `link` role require an `href` attribute for users to properly navigate, particularly those using assistive technologies. If no valid `href` is required for your use case, consider using a `ButtonItem` instead. | Yes | Yes | Yes |
|
|
81
|
+
| <a href="./src/rules/use-popup-label/README.md">use-popup-label</a> | Encourages to provide accessible name for Atlassian Design System Popup component. | Yes | | Yes |
|
|
81
82
|
| <a href="./src/rules/use-primitives/README.md">use-primitives</a> | Encourage the usage of primitives components. | | Yes | Yes |
|
|
82
83
|
| <a href="./src/rules/use-primitives-text/README.md">use-primitives-text</a> | Encourage the usage of text components. | | Yes | Yes |
|
|
83
84
|
| <a href="./src/rules/use-tokens-space/README.md">use-tokens-space</a> | Enforces usage of space design tokens rather than hard-coded values. | | Yes | Yes |
|
|
@@ -40,6 +40,7 @@ This plugin contains rules that should be used when working with the [Atlassian
|
|
|
40
40
|
| <a href="use-heading/usage">use-heading</a> | Encourage the usage of heading components. | | Yes | Yes |
|
|
41
41
|
| <a href="use-heading-level-in-spotlight-card/usage">use-heading-level-in-spotlight-card</a> | Inform developers of eventual requirement of `headingLevel` prop in `SpotlightCard` component. The heading level should be the appropriate level according to the surrounding context. | Yes | Yes | |
|
|
42
42
|
| <a href="use-href-in-link-item/usage">use-href-in-link-item</a> | Inform developers of eventual requirement of `href` prop in `LinkItem` component. Elements with a `link` role require an `href` attribute for users to properly navigate, particularly those using assistive technologies. If no valid `href` is required for your use case, consider using a `ButtonItem` instead. | Yes | Yes | Yes |
|
|
43
|
+
| <a href="use-popup-label/usage">use-popup-label</a> | Encourages to provide accessible name for Atlassian Design System Popup component. | Yes | | Yes |
|
|
43
44
|
| <a href="use-primitives/usage">use-primitives</a> | Encourage the usage of primitives components. | | Yes | Yes |
|
|
44
45
|
| <a href="use-primitives-text/usage">use-primitives-text</a> | Encourage the usage of text components. | | Yes | Yes |
|
|
45
46
|
| <a href="use-tokens-space/usage">use-tokens-space</a> | Enforces usage of space design tokens rather than hard-coded values. | | Yes | Yes |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# use-popup-label
|
|
2
|
+
|
|
3
|
+
Popup should have an accessible name or a reference to it when `role="dialog"`, so that upon opening, users of assistive technologies could have contextual information of interaction with current element.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
This rule will indicate user with warning to strongly recommend usage of either `label` or `titleId` prop.
|
|
8
|
+
|
|
9
|
+
### Incorrect
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Popup role="dialog">
|
|
13
|
+
^^^^^^ Missing either `label` or `titleId` prop.
|
|
14
|
+
Popup content
|
|
15
|
+
</Popup>
|
|
16
|
+
|
|
17
|
+
<Popup role="dialog" label>
|
|
18
|
+
^^^^^ `label` prop is missing value.
|
|
19
|
+
Popup content
|
|
20
|
+
</Popup>
|
|
21
|
+
|
|
22
|
+
<Popup role="dialog" label="">
|
|
23
|
+
^^^^^ `label` prop is missing accessible name value.
|
|
24
|
+
Popup content
|
|
25
|
+
</Popup>
|
|
26
|
+
|
|
27
|
+
<Popup role="dialog" titleId>
|
|
28
|
+
^^^^^^^ `titleId` prop is missing reference value.
|
|
29
|
+
<h1 id="popup-title">Popup content title</hi>
|
|
30
|
+
</Popup>
|
|
31
|
+
|
|
32
|
+
<Popup role="dialog" titleId="">
|
|
33
|
+
^^^^^^^ `titleId` prop is missing reference value.
|
|
34
|
+
<h1 id="popup-title">Popup content title</hi>
|
|
35
|
+
</Popup>
|
|
36
|
+
|
|
37
|
+
<Popup role="dialog" titleId="popup-title" label="">
|
|
38
|
+
^^^^^^^ ^^^^^ Do not include both `titleId` and `label` properties. Use `titleId` if the label text is available in the DOM to reference it, otherwise use `label` to provide accessible name explicitly.
|
|
39
|
+
<h1 id="popup-title">Popup content title</hi>
|
|
40
|
+
</Popup>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Correct
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Popup role="dialog" label="Popup content title">
|
|
47
|
+
Popup content
|
|
48
|
+
</Popup>
|
|
49
|
+
|
|
50
|
+
<Popup role="dialog" titleId="popup-title">
|
|
51
|
+
<h1 id="popup-title">Popup content title</hi>
|
|
52
|
+
</Popup>
|
|
53
|
+
```
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
9
|
-
* @codegen <<SignedSource::
|
|
9
|
+
* @codegen <<SignedSource::5a5b0ed8cf86631274d7d30df4199e5d>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = exports.default = {
|
|
@@ -41,6 +41,7 @@ var _default = exports.default = {
|
|
|
41
41
|
'@atlaskit/design-system/use-heading': 'warn',
|
|
42
42
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
43
43
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
44
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
44
45
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
45
46
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
46
47
|
'@atlaskit/design-system/use-tokens-space': 'error',
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
9
|
-
* @codegen <<SignedSource::
|
|
9
|
+
* @codegen <<SignedSource::af55c21605e3b8ee7836cb8950a9f713>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = exports.default = {
|
|
@@ -32,6 +32,7 @@ var _default = exports.default = {
|
|
|
32
32
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
33
33
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
34
34
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
35
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
35
36
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
36
37
|
}
|
|
37
38
|
};
|
|
@@ -7,7 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
8
|
var _createRule = require("../utils/create-rule");
|
|
9
9
|
var _jsx = require("../utils/jsx");
|
|
10
|
-
var elements = ['AkButton', 'AKButton', 'Button', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
10
|
+
var elements = ['AkButton', 'AKButton', 'Button', 'IconButton', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
11
11
|
var elementsIconProps = ['iconBefore', 'iconAfter', 'icon'];
|
|
12
12
|
var rule = (0, _createRule.createLintRule)({
|
|
13
13
|
meta: {
|
|
@@ -46,15 +46,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
46
46
|
iconImports[defaultImportName] = true;
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
|
-
JSXElement: function (
|
|
50
|
-
function JSXElement(_x) {
|
|
51
|
-
return _JSXElement.apply(this, arguments);
|
|
52
|
-
}
|
|
53
|
-
JSXElement.toString = function () {
|
|
54
|
-
return _JSXElement.toString();
|
|
55
|
-
};
|
|
56
|
-
return JSXElement;
|
|
57
|
-
}(function (node) {
|
|
49
|
+
JSXElement: function JSXElement(node) {
|
|
58
50
|
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
|
|
59
51
|
return;
|
|
60
52
|
}
|
|
@@ -106,7 +98,19 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
106
98
|
if (iconImports[name]) {
|
|
107
99
|
// We've found an icon from @atlaskit - let's get to work.
|
|
108
100
|
var hasLabelProp = (0, _jsx.findProp)(node, 'label');
|
|
109
|
-
|
|
101
|
+
var hasSpreadPropApplied = false;
|
|
102
|
+
|
|
103
|
+
// Check to see if it's inside an arrow function and the props have been spread
|
|
104
|
+
// ✅ <IconButton icon={(iconProps) => <StarIcon {...iconProps} />} label="Add to favorites" />
|
|
105
|
+
// ❌ <IconButton icon={() => <StarIcon />} label="Add to favorites" />
|
|
106
|
+
if (node.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'ArrowFunctionExpression') && node.parent.params[0] && node.parent.params[0].type === 'Identifier') {
|
|
107
|
+
// We are using an arrow function, test if the params have been spread onto the icon component
|
|
108
|
+
var paramName = node.parent.params[0].name;
|
|
109
|
+
hasSpreadPropApplied = !!node.openingElement.attributes.find(function (attribute) {
|
|
110
|
+
return attribute.type === 'JSXSpreadAttribute' && attribute.argument.type === 'Identifier' && attribute.argument.name === paramName;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (!hasLabelProp && !hasSpreadPropApplied) {
|
|
110
114
|
context.report({
|
|
111
115
|
node: node.openingElement,
|
|
112
116
|
messageId: 'missingLabelProp'
|
|
@@ -114,7 +118,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
114
118
|
return;
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
|
-
}
|
|
121
|
+
}
|
|
118
122
|
};
|
|
119
123
|
}
|
|
120
124
|
});
|
|
@@ -34,6 +34,7 @@ var _useDrawerLabel = _interopRequireDefault(require("./use-drawer-label"));
|
|
|
34
34
|
var _useHeading = _interopRequireDefault(require("./use-heading"));
|
|
35
35
|
var _useHeadingLevelInSpotlightCard = _interopRequireDefault(require("./use-heading-level-in-spotlight-card"));
|
|
36
36
|
var _useHrefInLinkItem = _interopRequireDefault(require("./use-href-in-link-item"));
|
|
37
|
+
var _usePopupLabel = _interopRequireDefault(require("./use-popup-label"));
|
|
37
38
|
var _usePrimitives = _interopRequireDefault(require("./use-primitives"));
|
|
38
39
|
var _usePrimitivesText = _interopRequireDefault(require("./use-primitives-text"));
|
|
39
40
|
var _useTokensSpace = _interopRequireDefault(require("./use-tokens-space"));
|
|
@@ -41,7 +42,7 @@ var _useTokensTypography = _interopRequireDefault(require("./use-tokens-typograp
|
|
|
41
42
|
var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
|
|
42
43
|
/**
|
|
43
44
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
44
|
-
* @codegen <<SignedSource::
|
|
45
|
+
* @codegen <<SignedSource::5e5b4678175cdf8209b7e2443bc4abb4>>
|
|
45
46
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
46
47
|
*/
|
|
47
48
|
var _default = exports.default = {
|
|
@@ -74,6 +75,7 @@ var _default = exports.default = {
|
|
|
74
75
|
'use-heading': _useHeading.default,
|
|
75
76
|
'use-heading-level-in-spotlight-card': _useHeadingLevelInSpotlightCard.default,
|
|
76
77
|
'use-href-in-link-item': _useHrefInLinkItem.default,
|
|
78
|
+
'use-popup-label': _usePopupLabel.default,
|
|
77
79
|
'use-primitives': _usePrimitives.default,
|
|
78
80
|
'use-primitives-text': _usePrimitivesText.default,
|
|
79
81
|
'use-tokens-space': _useTokensSpace.default,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
|
+
var _createRule = require("../utils/create-rule");
|
|
9
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
10
|
+
|
|
11
|
+
var elementsAccessibleNameProps = ['label', 'titleId'];
|
|
12
|
+
var rule = (0, _createRule.createLintRule)({
|
|
13
|
+
meta: {
|
|
14
|
+
name: 'use-popup-label',
|
|
15
|
+
type: 'suggestion',
|
|
16
|
+
docs: {
|
|
17
|
+
description: 'Encourages to provide accessible name for Atlassian Design System Popup component.',
|
|
18
|
+
recommended: true,
|
|
19
|
+
severity: 'warn'
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
missingLabelProp: 'Missing accessible name. If there is no visible content to associate use `label` prop, otherwise pass id of element to `titleId` prop to be associated as label.',
|
|
23
|
+
labelPropShouldHaveContents: 'Define string that labels the interactive element.',
|
|
24
|
+
titleIdShouldHaveValue: '`titleId` should reference the id of element that define accessible name.',
|
|
25
|
+
noBothPropsUsage: 'Do not include both `titleId` and `label` properties. Use `titleId` if the label text is available in the DOM to reference it, otherwise use `label` to provide accessible name explicitly.'
|
|
26
|
+
},
|
|
27
|
+
hasSuggestions: true
|
|
28
|
+
},
|
|
29
|
+
create: function create(context) {
|
|
30
|
+
var contextLocalIdentifier = [];
|
|
31
|
+
return {
|
|
32
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
33
|
+
if (node.source.value === '@atlaskit/popup') {
|
|
34
|
+
if (node.specifiers.length) {
|
|
35
|
+
var defaultImport = node.specifiers.filter(function (spec) {
|
|
36
|
+
return spec.type === 'ImportDefaultSpecifier';
|
|
37
|
+
});
|
|
38
|
+
if (defaultImport.length) {
|
|
39
|
+
var local = defaultImport[0].local;
|
|
40
|
+
contextLocalIdentifier.push(local.name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
JSXElement: function JSXElement(node) {
|
|
46
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!(0, _eslintCodemodUtils.isNodeOfType)(node.openingElement.name, 'JSXIdentifier')) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
var name = node.openingElement.name.name;
|
|
53
|
+
if (contextLocalIdentifier.includes(name)) {
|
|
54
|
+
var componentRoleDialogProp = node.openingElement.attributes.find(function (attr) {
|
|
55
|
+
return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && (0, _eslintCodemodUtils.isNodeOfType)(attr.name, 'JSXIdentifier') && attr.value && (0, _eslintCodemodUtils.isNodeOfType)(attr.value, 'Literal') && attr.name.name === 'role' && attr.value.value === 'dialog';
|
|
56
|
+
});
|
|
57
|
+
var componentLabelProps = node.openingElement.attributes.filter(function (attr) {
|
|
58
|
+
return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && (0, _eslintCodemodUtils.isNodeOfType)(attr.name, 'JSXIdentifier') && elementsAccessibleNameProps.includes(attr.name.name);
|
|
59
|
+
});
|
|
60
|
+
if (componentLabelProps.length === 1) {
|
|
61
|
+
var prop = componentLabelProps[0];
|
|
62
|
+
if ('value' in prop && prop.value) {
|
|
63
|
+
if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal') && !prop.value.value || (0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'JSXExpressionContainer') && !prop.value.expression) {
|
|
64
|
+
context.report({
|
|
65
|
+
node: prop,
|
|
66
|
+
messageId: prop.name.name === 'label' ? 'labelPropShouldHaveContents' : 'titleIdShouldHaveValue'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else if (componentLabelProps.length > 1) {
|
|
71
|
+
context.report({
|
|
72
|
+
node: node.openingElement,
|
|
73
|
+
messageId: 'noBothPropsUsage'
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
if (componentRoleDialogProp) {
|
|
77
|
+
context.report({
|
|
78
|
+
node: node.openingElement,
|
|
79
|
+
messageId: 'missingLabelProp'
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
var _default = exports.default = rule;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5a5b0ed8cf86631274d7d30df4199e5d>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -35,6 +35,7 @@ export default {
|
|
|
35
35
|
'@atlaskit/design-system/use-heading': 'warn',
|
|
36
36
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
37
37
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
38
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
38
39
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
39
40
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
40
41
|
'@atlaskit/design-system/use-tokens-space': 'error',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::af55c21605e3b8ee7836cb8950a9f713>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
27
27
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
28
28
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
29
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
29
30
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
30
31
|
}
|
|
31
32
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
2
|
import { createLintRule } from '../utils/create-rule';
|
|
3
3
|
import { findProp } from '../utils/jsx';
|
|
4
|
-
const elements = ['AkButton', 'AKButton', 'Button', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
4
|
+
const elements = ['AkButton', 'AKButton', 'Button', 'IconButton', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
5
5
|
const elementsIconProps = ['iconBefore', 'iconAfter', 'icon'];
|
|
6
6
|
const rule = createLintRule({
|
|
7
7
|
meta: {
|
|
@@ -86,7 +86,17 @@ const rule = createLintRule({
|
|
|
86
86
|
if (iconImports[name]) {
|
|
87
87
|
// We've found an icon from @atlaskit - let's get to work.
|
|
88
88
|
const hasLabelProp = findProp(node, 'label');
|
|
89
|
-
|
|
89
|
+
let hasSpreadPropApplied = false;
|
|
90
|
+
|
|
91
|
+
// Check to see if it's inside an arrow function and the props have been spread
|
|
92
|
+
// ✅ <IconButton icon={(iconProps) => <StarIcon {...iconProps} />} label="Add to favorites" />
|
|
93
|
+
// ❌ <IconButton icon={() => <StarIcon />} label="Add to favorites" />
|
|
94
|
+
if (node.parent && isNodeOfType(node.parent, 'ArrowFunctionExpression') && node.parent.params[0] && node.parent.params[0].type === 'Identifier') {
|
|
95
|
+
// We are using an arrow function, test if the params have been spread onto the icon component
|
|
96
|
+
const paramName = node.parent.params[0].name;
|
|
97
|
+
hasSpreadPropApplied = !!node.openingElement.attributes.find(attribute => attribute.type === 'JSXSpreadAttribute' && attribute.argument.type === 'Identifier' && attribute.argument.name === paramName);
|
|
98
|
+
}
|
|
99
|
+
if (!hasLabelProp && !hasSpreadPropApplied) {
|
|
90
100
|
context.report({
|
|
91
101
|
node: node.openingElement,
|
|
92
102
|
messageId: 'missingLabelProp'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5e5b4678175cdf8209b7e2443bc4abb4>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
@@ -32,6 +32,7 @@ import useDrawerLabel from './use-drawer-label';
|
|
|
32
32
|
import useHeading from './use-heading';
|
|
33
33
|
import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
|
|
34
34
|
import useHrefInLinkItem from './use-href-in-link-item';
|
|
35
|
+
import usePopupLabel from './use-popup-label';
|
|
35
36
|
import usePrimitives from './use-primitives';
|
|
36
37
|
import usePrimitivesText from './use-primitives-text';
|
|
37
38
|
import useTokensSpace from './use-tokens-space';
|
|
@@ -67,6 +68,7 @@ export default {
|
|
|
67
68
|
'use-heading': useHeading,
|
|
68
69
|
'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
|
|
69
70
|
'use-href-in-link-item': useHrefInLinkItem,
|
|
71
|
+
'use-popup-label': usePopupLabel,
|
|
70
72
|
'use-primitives': usePrimitives,
|
|
71
73
|
'use-primitives-text': usePrimitivesText,
|
|
72
74
|
'use-tokens-space': useTokensSpace,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { createLintRule } from '../utils/create-rule';
|
|
5
|
+
const elementsAccessibleNameProps = ['label', 'titleId'];
|
|
6
|
+
const rule = createLintRule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'use-popup-label',
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Encourages to provide accessible name for Atlassian Design System Popup component.',
|
|
12
|
+
recommended: true,
|
|
13
|
+
severity: 'warn'
|
|
14
|
+
},
|
|
15
|
+
messages: {
|
|
16
|
+
missingLabelProp: 'Missing accessible name. If there is no visible content to associate use `label` prop, otherwise pass id of element to `titleId` prop to be associated as label.',
|
|
17
|
+
labelPropShouldHaveContents: 'Define string that labels the interactive element.',
|
|
18
|
+
titleIdShouldHaveValue: '`titleId` should reference the id of element that define accessible name.',
|
|
19
|
+
noBothPropsUsage: 'Do not include both `titleId` and `label` properties. Use `titleId` if the label text is available in the DOM to reference it, otherwise use `label` to provide accessible name explicitly.'
|
|
20
|
+
},
|
|
21
|
+
hasSuggestions: true
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
const contextLocalIdentifier = [];
|
|
25
|
+
return {
|
|
26
|
+
ImportDeclaration(node) {
|
|
27
|
+
if (node.source.value === '@atlaskit/popup') {
|
|
28
|
+
if (node.specifiers.length) {
|
|
29
|
+
const defaultImport = node.specifiers.filter(spec => spec.type === 'ImportDefaultSpecifier');
|
|
30
|
+
if (defaultImport.length) {
|
|
31
|
+
const {
|
|
32
|
+
local
|
|
33
|
+
} = defaultImport[0];
|
|
34
|
+
contextLocalIdentifier.push(local.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
JSXElement(node) {
|
|
40
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const name = node.openingElement.name.name;
|
|
47
|
+
if (contextLocalIdentifier.includes(name)) {
|
|
48
|
+
const componentRoleDialogProp = node.openingElement.attributes.find(attr => isNodeOfType(attr, 'JSXAttribute') && isNodeOfType(attr.name, 'JSXIdentifier') && attr.value && isNodeOfType(attr.value, 'Literal') && attr.name.name === 'role' && attr.value.value === 'dialog');
|
|
49
|
+
const componentLabelProps = node.openingElement.attributes.filter(attr => isNodeOfType(attr, 'JSXAttribute') && isNodeOfType(attr.name, 'JSXIdentifier') && elementsAccessibleNameProps.includes(attr.name.name));
|
|
50
|
+
if (componentLabelProps.length === 1) {
|
|
51
|
+
const prop = componentLabelProps[0];
|
|
52
|
+
if ('value' in prop && prop.value) {
|
|
53
|
+
if (isNodeOfType(prop.value, 'Literal') && !prop.value.value || isNodeOfType(prop.value, 'JSXExpressionContainer') && !prop.value.expression) {
|
|
54
|
+
context.report({
|
|
55
|
+
node: prop,
|
|
56
|
+
messageId: prop.name.name === 'label' ? 'labelPropShouldHaveContents' : 'titleIdShouldHaveValue'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else if (componentLabelProps.length > 1) {
|
|
61
|
+
context.report({
|
|
62
|
+
node: node.openingElement,
|
|
63
|
+
messageId: 'noBothPropsUsage'
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
if (componentRoleDialogProp) {
|
|
67
|
+
context.report({
|
|
68
|
+
node: node.openingElement,
|
|
69
|
+
messageId: 'missingLabelProp'
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
export default rule;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5a5b0ed8cf86631274d7d30df4199e5d>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -35,6 +35,7 @@ export default {
|
|
|
35
35
|
'@atlaskit/design-system/use-heading': 'warn',
|
|
36
36
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
37
37
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
38
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
38
39
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
39
40
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
40
41
|
'@atlaskit/design-system/use-tokens-space': 'error',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::af55c21605e3b8ee7836cb8950a9f713>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
27
27
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
28
28
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
29
|
+
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
29
30
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
30
31
|
}
|
|
31
32
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
2
|
import { createLintRule } from '../utils/create-rule';
|
|
3
3
|
import { findProp } from '../utils/jsx';
|
|
4
|
-
var elements = ['AkButton', 'AKButton', 'Button', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
4
|
+
var elements = ['AkButton', 'AKButton', 'Button', 'IconButton', 'MenuItem', 'ButtonItem', 'CustomItem', 'CustomThemeButton', 'LoadingButton', 'BreadcrumbsItem'];
|
|
5
5
|
var elementsIconProps = ['iconBefore', 'iconAfter', 'icon'];
|
|
6
6
|
var rule = createLintRule({
|
|
7
7
|
meta: {
|
|
@@ -40,15 +40,7 @@ var rule = createLintRule({
|
|
|
40
40
|
iconImports[defaultImportName] = true;
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
|
-
JSXElement: function (
|
|
44
|
-
function JSXElement(_x) {
|
|
45
|
-
return _JSXElement.apply(this, arguments);
|
|
46
|
-
}
|
|
47
|
-
JSXElement.toString = function () {
|
|
48
|
-
return _JSXElement.toString();
|
|
49
|
-
};
|
|
50
|
-
return JSXElement;
|
|
51
|
-
}(function (node) {
|
|
43
|
+
JSXElement: function JSXElement(node) {
|
|
52
44
|
if (!isNodeOfType(node, 'JSXElement')) {
|
|
53
45
|
return;
|
|
54
46
|
}
|
|
@@ -100,7 +92,19 @@ var rule = createLintRule({
|
|
|
100
92
|
if (iconImports[name]) {
|
|
101
93
|
// We've found an icon from @atlaskit - let's get to work.
|
|
102
94
|
var hasLabelProp = findProp(node, 'label');
|
|
103
|
-
|
|
95
|
+
var hasSpreadPropApplied = false;
|
|
96
|
+
|
|
97
|
+
// Check to see if it's inside an arrow function and the props have been spread
|
|
98
|
+
// ✅ <IconButton icon={(iconProps) => <StarIcon {...iconProps} />} label="Add to favorites" />
|
|
99
|
+
// ❌ <IconButton icon={() => <StarIcon />} label="Add to favorites" />
|
|
100
|
+
if (node.parent && isNodeOfType(node.parent, 'ArrowFunctionExpression') && node.parent.params[0] && node.parent.params[0].type === 'Identifier') {
|
|
101
|
+
// We are using an arrow function, test if the params have been spread onto the icon component
|
|
102
|
+
var paramName = node.parent.params[0].name;
|
|
103
|
+
hasSpreadPropApplied = !!node.openingElement.attributes.find(function (attribute) {
|
|
104
|
+
return attribute.type === 'JSXSpreadAttribute' && attribute.argument.type === 'Identifier' && attribute.argument.name === paramName;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (!hasLabelProp && !hasSpreadPropApplied) {
|
|
104
108
|
context.report({
|
|
105
109
|
node: node.openingElement,
|
|
106
110
|
messageId: 'missingLabelProp'
|
|
@@ -108,7 +112,7 @@ var rule = createLintRule({
|
|
|
108
112
|
return;
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
|
-
}
|
|
115
|
+
}
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5e5b4678175cdf8209b7e2443bc4abb4>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
@@ -32,6 +32,7 @@ import useDrawerLabel from './use-drawer-label';
|
|
|
32
32
|
import useHeading from './use-heading';
|
|
33
33
|
import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
|
|
34
34
|
import useHrefInLinkItem from './use-href-in-link-item';
|
|
35
|
+
import usePopupLabel from './use-popup-label';
|
|
35
36
|
import usePrimitives from './use-primitives';
|
|
36
37
|
import usePrimitivesText from './use-primitives-text';
|
|
37
38
|
import useTokensSpace from './use-tokens-space';
|
|
@@ -67,6 +68,7 @@ export default {
|
|
|
67
68
|
'use-heading': useHeading,
|
|
68
69
|
'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
|
|
69
70
|
'use-href-in-link-item': useHrefInLinkItem,
|
|
71
|
+
'use-popup-label': usePopupLabel,
|
|
70
72
|
'use-primitives': usePrimitives,
|
|
71
73
|
'use-primitives-text': usePrimitivesText,
|
|
72
74
|
'use-tokens-space': useTokensSpace,
|