@atlaskit/eslint-plugin-design-system 8.20.0 → 8.22.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 +12 -0
- package/README.md +1 -0
- package/constellation/index/usage.mdx +114 -5
- package/dist/cjs/presets/all.codegen.js +2 -1
- package/dist/cjs/presets/recommended.codegen.js +2 -1
- package/dist/cjs/rules/consistent-css-prop-usage/index.js +98 -9
- package/dist/cjs/rules/index.codegen.js +3 -1
- package/dist/cjs/rules/use-button-group-label/index.js +83 -0
- package/dist/es2019/presets/all.codegen.js +2 -1
- package/dist/es2019/presets/recommended.codegen.js +2 -1
- package/dist/es2019/rules/consistent-css-prop-usage/index.js +95 -8
- package/dist/es2019/rules/index.codegen.js +3 -1
- package/dist/es2019/rules/use-button-group-label/index.js +75 -0
- package/dist/esm/presets/all.codegen.js +2 -1
- package/dist/esm/presets/recommended.codegen.js +2 -1
- package/dist/esm/rules/consistent-css-prop-usage/index.js +98 -9
- package/dist/esm/rules/index.codegen.js +3 -1
- package/dist/esm/rules/use-button-group-label/index.js +77 -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-button-group-label/index.d.ts +3 -0
- 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-button-group-label/index.d.ts +3 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 8.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#63589](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/63589) [`f59d997d1913`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/f59d997d1913) - Implemented new fixers for cssOnTopOfModule and cssAtBottomOfModule violation cases
|
|
8
|
+
|
|
9
|
+
## 8.21.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#66250](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/66250) [`6ff74a16aee7`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/6ff74a16aee7) - Introducing new rule to encourage adding/referencing accessible name to a ButtonGroup component.
|
|
14
|
+
|
|
3
15
|
## 8.20.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ module.exports = {
|
|
|
65
65
|
| <a href="./src/rules/no-unsafe-style-overrides/README.md">no-unsafe-style-overrides</a> | Discourage usage of unsafe style overrides used against the Atlassian Design System. | Yes | | |
|
|
66
66
|
| <a href="./src/rules/no-unsupported-drag-and-drop-libraries/README.md">no-unsupported-drag-and-drop-libraries</a> | Disallow importing unsupported drag and drop modules. | Yes | | |
|
|
67
67
|
| <a href="./src/rules/prefer-primitives/README.md">prefer-primitives</a> | Increase awareness of primitive components via code hints. Strictly used for education purposes and discoverability. To enforce usage please refer to the `use-primitives` rule. | | | |
|
|
68
|
+
| <a href="./src/rules/use-button-group-label/README.md">use-button-group-label</a> | Ensures button groups are described to assistive technology by a direct label or by another element. | Yes | | Yes |
|
|
68
69
|
| <a href="./src/rules/use-drawer-label/README.md">use-drawer-label</a> | Encourages to provide accessible name for Atlassian Design System Drawer component. | Yes | | Yes |
|
|
69
70
|
| <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 | |
|
|
70
71
|
| <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 |
|
|
@@ -29,6 +29,7 @@ This plugin contains rules that should be used when working with the [Atlassian
|
|
|
29
29
|
| <a href="#no-unsafe-style-overrides">no-unsafe-style-overrides</a> | Discourage usage of unsafe style overrides used against the Atlassian Design System. | Yes | | |
|
|
30
30
|
| <a href="#no-unsupported-drag-and-drop-libraries">no-unsupported-drag-and-drop-libraries</a> | Disallow importing unsupported drag and drop modules. | Yes | | |
|
|
31
31
|
| <a href="#prefer-primitives">prefer-primitives</a> | Increase awareness of primitive components via code hints. Strictly used for education purposes and discoverability. To enforce usage please refer to the `use-primitives` rule. | | | |
|
|
32
|
+
| <a href="#use-button-group-label">use-button-group-label</a> | Ensures button groups are described to assistive technology by a direct label or by another element. | Yes | | Yes |
|
|
32
33
|
| <a href="#use-drawer-label">use-drawer-label</a> | Encourages to provide accessible name for Atlassian Design System Drawer component. | Yes | | Yes |
|
|
33
34
|
| <a href="#use-heading-level-in-spotlight-card">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 | |
|
|
34
35
|
| <a href="#use-href-in-link-item">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 |
|
|
@@ -65,6 +66,8 @@ This rule has options - see below.
|
|
|
65
66
|
|
|
66
67
|
👎 Example of **incorrect** code for this rule:
|
|
67
68
|
|
|
69
|
+
**Calling a css/xcss function or direct objects inside the JSX attribute.**
|
|
70
|
+
|
|
68
71
|
```js
|
|
69
72
|
function Button({ children }) {
|
|
70
73
|
return <div css={css({...})}>{children}</div>;
|
|
@@ -72,6 +75,8 @@ function Button({ children }) {
|
|
|
72
75
|
}
|
|
73
76
|
```
|
|
74
77
|
|
|
78
|
+
**Inserting a non css-function based object identifier into a css JSX attribute.**
|
|
79
|
+
|
|
75
80
|
```js
|
|
76
81
|
const container = {
|
|
77
82
|
^^^^^^^^^ should be a css function call
|
|
@@ -83,6 +88,8 @@ function Button({ children }) {
|
|
|
83
88
|
}
|
|
84
89
|
```
|
|
85
90
|
|
|
91
|
+
**Importing styles from another file.**
|
|
92
|
+
|
|
86
93
|
```js
|
|
87
94
|
import { container } from './styles';
|
|
88
95
|
^^^^^^^^^ styles should be local, not shared
|
|
@@ -92,6 +99,8 @@ function Button({ children }) {
|
|
|
92
99
|
}
|
|
93
100
|
```
|
|
94
101
|
|
|
102
|
+
**Nesting styles with objects instead of arrays.**
|
|
103
|
+
|
|
95
104
|
```js
|
|
96
105
|
const baseContainerStyles = css({
|
|
97
106
|
zIndex: 5,
|
|
@@ -110,6 +119,13 @@ function Button({ children }) {
|
|
|
110
119
|
|
|
111
120
|
👍 Example of **correct** code for this rule:
|
|
112
121
|
|
|
122
|
+
**Using the css() function to create a style object that follows the naming convention (ends in Styles) and passing it as a variable into the css={...} JSX attribute.**
|
|
123
|
+
|
|
124
|
+
With the following options turned on:
|
|
125
|
+
|
|
126
|
+
- cssFunctions = ['css']
|
|
127
|
+
- stylesPlacement = 'top'
|
|
128
|
+
|
|
113
129
|
```js
|
|
114
130
|
const containerStyles = css({
|
|
115
131
|
zIndex: 1,
|
|
@@ -120,7 +136,38 @@ function Button({ children }) {
|
|
|
120
136
|
}
|
|
121
137
|
```
|
|
122
138
|
|
|
139
|
+
**Technically correct usage of the cssMap function.**
|
|
140
|
+
|
|
141
|
+
With the following options turned on:
|
|
142
|
+
|
|
143
|
+
- cssFunctions = ['css']
|
|
144
|
+
- stylesPlacement = 'top'
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
const borderStyles = cssMap({
|
|
148
|
+
'solid': '1px solid';
|
|
149
|
+
'none': '0px';
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
function Button({ children }) {
|
|
153
|
+
return <button css={borderStyles[solid]}>{children}</button>;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Create composite styles with arrays, not objects.**
|
|
158
|
+
|
|
159
|
+
With the following options turned on:
|
|
160
|
+
|
|
161
|
+
- cssFunctions = ['css']
|
|
162
|
+
- stylesPlacement = 'bottom'
|
|
163
|
+
|
|
123
164
|
```js
|
|
165
|
+
function Button({ children }) {
|
|
166
|
+
return (
|
|
167
|
+
<button css={[baseContainerStyles, containerStyles]}>{children}</button>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
124
171
|
const baseContainerStyles = css({
|
|
125
172
|
zIndex: 5,
|
|
126
173
|
});
|
|
@@ -128,12 +175,15 @@ const baseContainerStyles = css({
|
|
|
128
175
|
const containerStyles = css({
|
|
129
176
|
zIndex: 7,
|
|
130
177
|
});
|
|
178
|
+
```
|
|
131
179
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
180
|
+
**Ternaries can be used inline**
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
const baseStyles = css({ color: token('color.text.primary') });
|
|
184
|
+
const disabledStyles = css({ color: token('color.text.disabled') });
|
|
185
|
+
|
|
186
|
+
<div css={props.disabled ? disabledStyles : baseStyles}></div>;
|
|
137
187
|
```
|
|
138
188
|
|
|
139
189
|
<h3>Options</h3>
|
|
@@ -767,6 +817,65 @@ This rule marks code as violations when it may be able to be replaced with a pri
|
|
|
767
817
|
</Component>
|
|
768
818
|
```
|
|
769
819
|
|
|
820
|
+
## use-button-group-label
|
|
821
|
+
|
|
822
|
+
ButtonGroup should have an accessible name or a reference to it, so that upon opening, users of assistive technologies could have contextual information of interaction with current element.
|
|
823
|
+
|
|
824
|
+
<h3>Examples</h3>
|
|
825
|
+
|
|
826
|
+
This rule will indicate user with warning to strongly recommend usage of either `label` or `titleId` prop.
|
|
827
|
+
|
|
828
|
+
#### Incorrect
|
|
829
|
+
|
|
830
|
+
```tsx
|
|
831
|
+
<ButtonGroup>
|
|
832
|
+
^^^^^^^^^^^ Missing either `label` or `titleId` prop.
|
|
833
|
+
<Button>Save</Button>
|
|
834
|
+
<Button>Edit</Button>
|
|
835
|
+
<Button>Delete</Button>
|
|
836
|
+
</ButtonGroup>
|
|
837
|
+
|
|
838
|
+
<ButtonGroup label="">
|
|
839
|
+
^^^^^ `label` prop is missing accessible name value.
|
|
840
|
+
<Button>Save</Button>
|
|
841
|
+
<Button>Edit</Button>
|
|
842
|
+
<Button>Delete</Button>
|
|
843
|
+
</ButtonGroup>
|
|
844
|
+
|
|
845
|
+
<h2 id="button-group-title">ButtonGroup content title</hi>
|
|
846
|
+
<ButtonGroup titleId="">
|
|
847
|
+
^^^^^^^ `titleId` prop is missing reference value.
|
|
848
|
+
<Button>Save</Button>
|
|
849
|
+
<Button>Edit</Button>
|
|
850
|
+
<Button>Delete</Button>
|
|
851
|
+
</ButtonGroup>
|
|
852
|
+
|
|
853
|
+
<h2 id="button-group-title">ButtonGroup content title</h2>
|
|
854
|
+
<ButtonGroup titleId="button-group-title" label="">
|
|
855
|
+
^^^^^^^ ^^^^^ 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.
|
|
856
|
+
<Button>Save</Button>
|
|
857
|
+
<Button>Edit</Button>
|
|
858
|
+
<Button>Delete</Button>
|
|
859
|
+
</ButtonGroup>
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
#### Correct
|
|
863
|
+
|
|
864
|
+
```tsx
|
|
865
|
+
<ButtonGroup label="ButtonGroup content title">
|
|
866
|
+
<Button>Save</Button>
|
|
867
|
+
<Button>Edit</Button>
|
|
868
|
+
<Button>Delete</Button>
|
|
869
|
+
</ButtonGroup>
|
|
870
|
+
|
|
871
|
+
<h2 id="button-group-title">ButtonGroup content title</h2>
|
|
872
|
+
<ButtonGroup titleId="button-group-title">
|
|
873
|
+
<Button>Save</Button>
|
|
874
|
+
<Button>Edit</Button>
|
|
875
|
+
<Button>Delete</Button>
|
|
876
|
+
</ButtonGroup>
|
|
877
|
+
```
|
|
878
|
+
|
|
770
879
|
## use-drawer-label
|
|
771
880
|
|
|
772
881
|
Drawer should have an accessible name or a reference to it, so that upon opening, users of assistive technologies could have contextual information of interaction with current element.
|
|
@@ -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::d1a459e1ea71650f65b2890dc86cc398>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = exports.default = {
|
|
@@ -28,6 +28,7 @@ var _default = exports.default = {
|
|
|
28
28
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
29
29
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
30
30
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
31
|
+
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
31
32
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
32
33
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
33
34
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
@@ -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::3b93cfbbe0ea14514b9600509632394b>>
|
|
10
10
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
11
11
|
*/
|
|
12
12
|
var _default = exports.default = {
|
|
@@ -23,6 +23,7 @@ var _default = exports.default = {
|
|
|
23
23
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
24
24
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
25
25
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
26
|
+
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
26
27
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
27
28
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
28
29
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
@@ -6,15 +6,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
11
11
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
12
12
|
var _assign = _interopRequireDefault(require("lodash/assign"));
|
|
13
13
|
var _createRule = require("../utils/create-rule");
|
|
14
14
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
15
15
|
|
|
16
|
-
var declarationSuffix = 'Styles';
|
|
17
16
|
function isCssCallExpression(node, cssFunctions) {
|
|
17
|
+
cssFunctions = [].concat((0, _toConsumableArray2.default)(cssFunctions), ['cssMap']);
|
|
18
18
|
return !!((0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && cssFunctions.includes(node.callee.name) && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
|
|
19
19
|
}
|
|
20
20
|
function findSpreadProperties(node) {
|
|
@@ -25,6 +25,44 @@ function findSpreadProperties(node) {
|
|
|
25
25
|
property.type === 'ExperimentalSpreadProperty';
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
+
var getTopLevelNode = function getTopLevelNode(expression) {
|
|
29
|
+
while (expression.parent.type !== 'Program') {
|
|
30
|
+
expression = expression.parent;
|
|
31
|
+
}
|
|
32
|
+
return expression;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// TODO: This can be optimised by implementing a fixer at the very end (Program:exit) and handling all validations at once
|
|
36
|
+
/**
|
|
37
|
+
* Generates the declarator string when fixing the cssOnTopOfModule/cssAtBottomOfModule cases.
|
|
38
|
+
* When `styles` already exists, `styles_1, styles_2, ..., styles_X` are incrementally created for each unhoisted style.
|
|
39
|
+
* The generated `styles` varibale declaration names must be manually modified to be more informative at the discretion of owning teams.
|
|
40
|
+
*/
|
|
41
|
+
var getDeclaratorString = function getDeclaratorString(context) {
|
|
42
|
+
var scope = context.getScope();
|
|
43
|
+
|
|
44
|
+
// Get to ModuleScope
|
|
45
|
+
while (scope && scope.upper && scope.upper.type !== 'global') {
|
|
46
|
+
var _scope;
|
|
47
|
+
scope = (_scope = scope) === null || _scope === void 0 ? void 0 : _scope.upper;
|
|
48
|
+
}
|
|
49
|
+
var variables = scope.variables.map(function (variable) {
|
|
50
|
+
return variable.name;
|
|
51
|
+
});
|
|
52
|
+
var count = 2;
|
|
53
|
+
var declaratorName = 'styles';
|
|
54
|
+
|
|
55
|
+
// Base case
|
|
56
|
+
if (!variables.includes(declaratorName)) {
|
|
57
|
+
return declaratorName;
|
|
58
|
+
} else {
|
|
59
|
+
// If styles already exists, increment the number
|
|
60
|
+
while (variables.includes("".concat(declaratorName, "_").concat(count))) {
|
|
61
|
+
count++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return "".concat(declaratorName, "_").concat(count);
|
|
65
|
+
};
|
|
28
66
|
function analyzeIdentifier(context, sourceIdentifier, configuration) {
|
|
29
67
|
var _getIdentifierInParen, _getIdentifierInParen2;
|
|
30
68
|
var scope = context.getScope();
|
|
@@ -47,11 +85,14 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
|
|
|
47
85
|
// When variable is declared inside the component
|
|
48
86
|
context.report({
|
|
49
87
|
node: sourceIdentifier,
|
|
50
|
-
messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
|
|
88
|
+
messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule',
|
|
89
|
+
fix: function fix(fixer) {
|
|
90
|
+
return fixCssNotInModuleScope(fixer, context, configuration, identifier);
|
|
91
|
+
}
|
|
51
92
|
});
|
|
52
93
|
return;
|
|
53
94
|
}
|
|
54
|
-
if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init,
|
|
95
|
+
if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
|
|
55
96
|
// When variable value is not of type css({})
|
|
56
97
|
context.report({
|
|
57
98
|
node: identifier,
|
|
@@ -71,13 +112,50 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
|
|
|
71
112
|
}
|
|
72
113
|
}
|
|
73
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Fixer for the cssOnTopOfModule/cssAtBottomOfModule violation cases.
|
|
117
|
+
* This deals with Identifiers and Expressions passed from the traverseExpressionWithConfig() function.
|
|
118
|
+
* @param fixer The ESLint RuleFixer object
|
|
119
|
+
* @param context The context of the node
|
|
120
|
+
* @param configuration The configuration of the rule, determining whether the fix is implmeneted at the top or bottom of the module
|
|
121
|
+
* @param node Either an IdentifierWithParent node. Expression, or SpreadElement that we handle
|
|
122
|
+
* @param cssAttributeName An optional parameter only added when we fix an ObjectExpression
|
|
123
|
+
*/
|
|
124
|
+
var fixCssNotInModuleScope = function fixCssNotInModuleScope(fixer, context, configuration, node, cssAttributeName) {
|
|
125
|
+
var sourceCode = context.getSourceCode();
|
|
126
|
+
var topLevelNode = getTopLevelNode(node);
|
|
127
|
+
var moduleString;
|
|
128
|
+
var implementFixer = [];
|
|
129
|
+
if (node.type === 'Identifier') {
|
|
130
|
+
var identifier = node;
|
|
131
|
+
var declarator = identifier.parent.parent;
|
|
132
|
+
moduleString = sourceCode.getText(declarator);
|
|
133
|
+
implementFixer.push(fixer.remove(declarator));
|
|
134
|
+
} else {
|
|
135
|
+
var _declarator = getDeclaratorString(context);
|
|
136
|
+
var text = sourceCode.getText(node);
|
|
137
|
+
|
|
138
|
+
// If this has been passed, then we know it's an ObjectExpression
|
|
139
|
+
if (cssAttributeName) {
|
|
140
|
+
moduleString = "const ".concat(_declarator, " = ").concat(cssAttributeName, "(").concat(text, ");");
|
|
141
|
+
} else {
|
|
142
|
+
moduleString = moduleString = "const ".concat(_declarator, " = ").concat(text, ";");
|
|
143
|
+
}
|
|
144
|
+
implementFixer.push(fixer.replaceText(node, _declarator));
|
|
145
|
+
}
|
|
146
|
+
return [].concat(implementFixer, [
|
|
147
|
+
// Insert the node either before or after
|
|
148
|
+
configuration.stylesPlacement === 'bottom' ? fixer.insertTextAfter(topLevelNode, '\n' + moduleString) : fixer.insertTextBefore(topLevelNode, moduleString + '\n')]);
|
|
149
|
+
};
|
|
150
|
+
|
|
74
151
|
/**
|
|
75
152
|
* Handle different cases based on what's been passed in the css-related JSXAttribute
|
|
76
153
|
* @param context the context of the node
|
|
77
154
|
* @param expression the expression of the JSXAttribute value
|
|
78
155
|
* @param configuration what css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions
|
|
156
|
+
* @param cssAttributeName used to encapsulate ObjectExpressions when cssOnTopOfModule/cssAtBottomOfModule violations are triggered
|
|
79
157
|
*/
|
|
80
|
-
var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration) {
|
|
158
|
+
var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration, cssAttributeName) {
|
|
81
159
|
function traverseExpression(expression) {
|
|
82
160
|
switch (expression.type) {
|
|
83
161
|
case 'Identifier':
|
|
@@ -105,14 +183,24 @@ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context
|
|
|
105
183
|
traverseExpression(expression.consequent);
|
|
106
184
|
traverseExpression(expression.alternate);
|
|
107
185
|
break;
|
|
108
|
-
case 'CallExpression':
|
|
109
186
|
case 'ObjectExpression':
|
|
187
|
+
case 'CallExpression':
|
|
110
188
|
case 'TaggedTemplateExpression':
|
|
111
189
|
case 'TemplateLiteral':
|
|
112
190
|
// We've found elements that shouldn't be here! Report an error.
|
|
113
191
|
context.report({
|
|
114
192
|
node: expression,
|
|
115
|
-
messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
|
|
193
|
+
messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule',
|
|
194
|
+
fix: function fix(fixer) {
|
|
195
|
+
// Don't fix CallExpressions unless they're from cssFunctions or cssMap
|
|
196
|
+
if (expression.type === 'CallExpression' && !isCssCallExpression(expression, configuration.cssFunctions)) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
if (expression.type === 'ObjectExpression') {
|
|
200
|
+
return fixCssNotInModuleScope(fixer, context, configuration, expression, cssAttributeName);
|
|
201
|
+
}
|
|
202
|
+
return fixCssNotInModuleScope(fixer, context, configuration, expression);
|
|
203
|
+
}
|
|
116
204
|
});
|
|
117
205
|
break;
|
|
118
206
|
default:
|
|
@@ -148,6 +236,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
148
236
|
create: function create(context) {
|
|
149
237
|
var _ref3;
|
|
150
238
|
var mergedConfig = (0, _assign.default)({}, defaultConfig, context.options[0]);
|
|
239
|
+
var declarationSuffix = 'Styles';
|
|
151
240
|
var callSelectorFunctions = [].concat((0, _toConsumableArray2.default)(mergedConfig.cssFunctions), ['cssMap']);
|
|
152
241
|
var callSelector = callSelectorFunctions.map(function (fn) {
|
|
153
242
|
return "CallExpression[callee.name=".concat(fn, "]");
|
|
@@ -158,7 +247,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
158
247
|
return;
|
|
159
248
|
}
|
|
160
249
|
var identifier = node.parent.id;
|
|
161
|
-
if (identifier.type === 'Identifier' && identifier.name.endsWith(declarationSuffix)) {
|
|
250
|
+
if (identifier.type === 'Identifier' && (identifier.name.endsWith(declarationSuffix) || identifier.name.startsWith(declarationSuffix.toLowerCase() + '_') || identifier.name === declarationSuffix.toLowerCase())) {
|
|
162
251
|
// Already prefixed! Nothing to do.
|
|
163
252
|
return;
|
|
164
253
|
}
|
|
@@ -196,7 +285,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
196
285
|
});
|
|
197
286
|
return;
|
|
198
287
|
}
|
|
199
|
-
traverseExpressionWithConfig(context, value.expression, mergedConfig);
|
|
288
|
+
traverseExpressionWithConfig(context, value.expression, mergedConfig, name.name);
|
|
200
289
|
}
|
|
201
290
|
}), _ref3;
|
|
202
291
|
}
|
|
@@ -21,6 +21,7 @@ var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./no-unsafe-desi
|
|
|
21
21
|
var _noUnsafeStyleOverrides = _interopRequireDefault(require("./no-unsafe-style-overrides"));
|
|
22
22
|
var _noUnsupportedDragAndDropLibraries = _interopRequireDefault(require("./no-unsupported-drag-and-drop-libraries"));
|
|
23
23
|
var _preferPrimitives = _interopRequireDefault(require("./prefer-primitives"));
|
|
24
|
+
var _useButtonGroupLabel = _interopRequireDefault(require("./use-button-group-label"));
|
|
24
25
|
var _useDrawerLabel = _interopRequireDefault(require("./use-drawer-label"));
|
|
25
26
|
var _useHeadingLevelInSpotlightCard = _interopRequireDefault(require("./use-heading-level-in-spotlight-card"));
|
|
26
27
|
var _useHrefInLinkItem = _interopRequireDefault(require("./use-href-in-link-item"));
|
|
@@ -28,7 +29,7 @@ var _usePrimitives = _interopRequireDefault(require("./use-primitives"));
|
|
|
28
29
|
var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
|
|
29
30
|
/**
|
|
30
31
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
31
|
-
* @codegen <<SignedSource::
|
|
32
|
+
* @codegen <<SignedSource::14cdfdcbd8b999ee097a1a5b245d7117>>
|
|
32
33
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
33
34
|
*/
|
|
34
35
|
var _default = exports.default = {
|
|
@@ -48,6 +49,7 @@ var _default = exports.default = {
|
|
|
48
49
|
'no-unsafe-style-overrides': _noUnsafeStyleOverrides.default,
|
|
49
50
|
'no-unsupported-drag-and-drop-libraries': _noUnsupportedDragAndDropLibraries.default,
|
|
50
51
|
'prefer-primitives': _preferPrimitives.default,
|
|
52
|
+
'use-button-group-label': _useButtonGroupLabel.default,
|
|
51
53
|
'use-drawer-label': _useDrawerLabel.default,
|
|
52
54
|
'use-heading-level-in-spotlight-card': _useHeadingLevelInSpotlightCard.default,
|
|
53
55
|
'use-href-in-link-item': _useHrefInLinkItem.default,
|
|
@@ -0,0 +1,83 @@
|
|
|
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-button-group-label',
|
|
15
|
+
type: 'suggestion',
|
|
16
|
+
docs: {
|
|
17
|
+
description: 'Ensures button groups are described to assistive technology by a direct label or by another element.',
|
|
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/button') {
|
|
34
|
+
if (node.specifiers.length) {
|
|
35
|
+
var defaultImport = node.specifiers.filter(function (spec) {
|
|
36
|
+
return spec.type === 'ImportSpecifier';
|
|
37
|
+
});
|
|
38
|
+
if (defaultImport && 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 componentLabelProps = node.openingElement.attributes.filter(function (attr) {
|
|
55
|
+
return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && (0, _eslintCodemodUtils.isNodeOfType)(attr.name, 'JSXIdentifier') && elementsAccessibleNameProps.includes(attr.name.name);
|
|
56
|
+
});
|
|
57
|
+
if (componentLabelProps.length === 1) {
|
|
58
|
+
var prop = componentLabelProps[0];
|
|
59
|
+
if ('value' in prop && prop.value) {
|
|
60
|
+
if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal') && !prop.value.value || (0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'JSXExpressionContainer') && !prop.value.expression) {
|
|
61
|
+
context.report({
|
|
62
|
+
node: prop,
|
|
63
|
+
messageId: prop.name.name === 'label' ? 'labelPropShouldHaveContents' : 'titleIdShouldHaveValue'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (componentLabelProps.length > 1) {
|
|
68
|
+
context.report({
|
|
69
|
+
node: node.openingElement,
|
|
70
|
+
messageId: 'noBothPropsUsage'
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
context.report({
|
|
74
|
+
node: node.openingElement,
|
|
75
|
+
messageId: 'missingLabelProp'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
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::d1a459e1ea71650f65b2890dc86cc398>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -22,6 +22,7 @@ export default {
|
|
|
22
22
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
23
23
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
24
24
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
25
|
+
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
25
26
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
26
27
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
27
28
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
@@ -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::3b93cfbbe0ea14514b9600509632394b>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -17,6 +17,7 @@ export default {
|
|
|
17
17
|
'@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
|
|
18
18
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
19
19
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
20
|
+
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
20
21
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
21
22
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
22
23
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|