@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.
Files changed (29) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -0
  3. package/constellation/index/usage.mdx +114 -5
  4. package/dist/cjs/presets/all.codegen.js +2 -1
  5. package/dist/cjs/presets/recommended.codegen.js +2 -1
  6. package/dist/cjs/rules/consistent-css-prop-usage/index.js +98 -9
  7. package/dist/cjs/rules/index.codegen.js +3 -1
  8. package/dist/cjs/rules/use-button-group-label/index.js +83 -0
  9. package/dist/es2019/presets/all.codegen.js +2 -1
  10. package/dist/es2019/presets/recommended.codegen.js +2 -1
  11. package/dist/es2019/rules/consistent-css-prop-usage/index.js +95 -8
  12. package/dist/es2019/rules/index.codegen.js +3 -1
  13. package/dist/es2019/rules/use-button-group-label/index.js +75 -0
  14. package/dist/esm/presets/all.codegen.js +2 -1
  15. package/dist/esm/presets/recommended.codegen.js +2 -1
  16. package/dist/esm/rules/consistent-css-prop-usage/index.js +98 -9
  17. package/dist/esm/rules/index.codegen.js +3 -1
  18. package/dist/esm/rules/use-button-group-label/index.js +77 -0
  19. package/dist/types/index.codegen.d.ts +2 -0
  20. package/dist/types/presets/all.codegen.d.ts +2 -1
  21. package/dist/types/presets/recommended.codegen.d.ts +2 -1
  22. package/dist/types/rules/index.codegen.d.ts +1 -0
  23. package/dist/types/rules/use-button-group-label/index.d.ts +3 -0
  24. package/dist/types-ts4.5/index.codegen.d.ts +2 -0
  25. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  26. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
  27. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  28. package/dist/types-ts4.5/rules/use-button-group-label/index.d.ts +3 -0
  29. 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
- function Button({ children }) {
133
- return (
134
- <button css={[baseContainerStyles, containerStyles]}>{children}</button>
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::6efa1e48692b3e287d6dfcd500a5f0ab>>
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::be810d87ec2d253e3b053dc06ff1b99a>>
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, [].concat((0, _toConsumableArray2.default)(configuration.cssFunctions), ['cssMap']))) {
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::ab1f5b129d07027c228dbd79da5f3572>>
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::6efa1e48692b3e287d6dfcd500a5f0ab>>
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::be810d87ec2d253e3b053dc06ff1b99a>>
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',