@atlaskit/eslint-plugin-design-system 4.11.2 → 4.12.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 4.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`109c705cd9c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/109c705cd9c) - [ux] Adds a new case to the no-unsafe-design-token-usage rule to lint against uses of 'experimental' tokens and automatically replace them with their replacement (either a token or a fallback) via a fixer.
8
+
3
9
  ## 4.11.2
4
10
 
5
11
  ### Patch Changes
@@ -2,4 +2,312 @@
2
2
  order: 0
3
3
  ---
4
4
 
5
- examples
5
+ ## ensure-design-token-usage
6
+
7
+ Ensures that the codebase uses the global `token` function, rather than hard-coded values. This ruleset is great for codebases that are starting to adopt tokens and those that have already adopted tokens. This ruleset also prevents new contributors from accidentally adding hard-coded color values.
8
+
9
+ 👎 Example of **incorrect** code for this rule:
10
+
11
+ ```jsx
12
+ css({
13
+ color: 'red',
14
+ ^^^
15
+ });
16
+ ```
17
+
18
+ ```jsx
19
+ css({
20
+ boxShadow: '0px 1px 1px #161A1D32',
21
+ ^^^^^^^^^
22
+ })
23
+ ```
24
+
25
+ ```jsx
26
+ import { e100 } from '@atlaskit/theme/elevation';
27
+
28
+ css`
29
+ ${e100};
30
+ ^^^^
31
+ `;
32
+ ```
33
+
34
+ ```jsx
35
+ import { B100 } from '@atlaskit/theme/colors';
36
+
37
+ css({
38
+ color: B100,
39
+ ^^^^
40
+ });
41
+ ```
42
+
43
+ 👍 Example of **correct** code for this rule:
44
+
45
+ ```jsx
46
+ import { token } from '@atlaskit/tokens';
47
+
48
+ css({
49
+ boxShadow: token('elevation.shadow.card'),
50
+ });
51
+ ```
52
+
53
+ ```jsx
54
+ import { token } from '@atlaskit/tokens';
55
+
56
+ css`
57
+ color: ${token('color.text.highemphasis')};
58
+ `;
59
+ ```
60
+
61
+ ## no-deprecated-design-token-usage
62
+
63
+ Will catch deprecated token usage and autofix a replacement.
64
+
65
+ It's recommended to set this rule to "warn" on error to allow for new and old tokens to exist side-by-side for the duration of the deprecation period and avoid big-bang migrations.
66
+
67
+ Once the deprecation period is over for a token, it will be moved into `deleted` state, at which point the counterpart of this rule `eslint-plugin-design-system/no-unsafe-design-token-usage` will begin to throw errors.
68
+
69
+ Run `eslint --fix` will automatically apply replacement tokens.
70
+
71
+ 👎 Example of **incorrect** code for this rule:
72
+
73
+ ```jsx
74
+ import { token } from '@atlaskit/tokens';
75
+
76
+ css({
77
+ color: token('i.am.deprecated'), // 👎
78
+ });
79
+ ```
80
+
81
+ ```jsx
82
+ css({
83
+ color: token('i.am.a.token'), // 👍
84
+ });
85
+ ```
86
+
87
+ ## no-unsafe-design-token-usage
88
+
89
+ Ensures usages of the `token` function are done correctly, so that current token names are being used, and the resulting `var(--some-token)` statements aren't being used. This ruleset is great for codebases that have already adopted tokens.
90
+
91
+ 👎 Example of **incorrect** code for this rule:
92
+
93
+ ```jsx
94
+ const textColor = 'red';
95
+
96
+ css({
97
+ color: textColor,
98
+ ^^^^^^^^^
99
+ });
100
+ ```
101
+
102
+ ```jsx
103
+ css({
104
+ boxShadow: '0px 1px 1px var(--ds-accent-subtleBlue)',
105
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^
106
+ })
107
+ ```
108
+
109
+ 👍 Example of **correct** code for this rule:
110
+
111
+ ```jsx
112
+ import { token } from '@atlaskit/tokens';
113
+
114
+ css({
115
+ boxShadow: token('elevation.shadow.card'),
116
+ });
117
+ ```
118
+
119
+ ```jsx
120
+ import { token } from '@atlaskit/tokens';
121
+
122
+ css`
123
+ color: ${(token('color.text.highemphasis'), N20)};
124
+ `;
125
+ ```
126
+
127
+ ## icon-label
128
+
129
+ Enforces accessible usage of icon labels when composed with other Design System components.
130
+
131
+ 👎 Example of **incorrect** code for this rule:
132
+
133
+ ```jsx
134
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
135
+
136
+ <ActivityIcon>
137
+ ^^^^^^^^^^^^^^ missing `label` prop
138
+ ```
139
+
140
+ ```jsx
141
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
142
+
143
+ <Button iconLeft={<ActivityIcon label="">} />
144
+ ^^^^^ label should be defined
145
+ ```
146
+
147
+ ```jsx
148
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
149
+
150
+ <ButtonItem iconBefore={<ActivityIcon label="">}>
151
+ ^^^^^ label should not be defined
152
+ My activity
153
+ </ButtonItem>
154
+ ```
155
+
156
+ 👍 Example of **correct** code for this rule:
157
+
158
+ ```jsx
159
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
160
+
161
+ <ActivityIcon label="Activity">
162
+ ```
163
+
164
+ ```jsx
165
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
166
+
167
+ <Button iconLeft={<ActivityIcon label="Activity">} />
168
+ ```
169
+
170
+ ```jsx
171
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
172
+
173
+ <ButtonItem iconBefore={<ActivityIcon label="">}>
174
+ My activity
175
+ </ButtonItem>
176
+ ```
177
+
178
+ ## no-banned-imports
179
+
180
+ Prevents usage of private or experimental Atlassian Design System packages.
181
+
182
+ 👎 Example of **incorrect** code for this rule:
183
+
184
+ ```ts
185
+ import noop from '@atlaskit/ds-lib/noop';
186
+ ^^^^^^^^^^^^^^^^^^^^^
187
+ ```
188
+
189
+ ```ts
190
+ import { Text } from '@atlaskit/ds-explorations';
191
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
192
+ ```
193
+
194
+ ## no-deprecated-api-usage
195
+
196
+ Ensures usage of current Atlassian Design System API usage.
197
+
198
+ 👎 Example of **incorrect** code for this rule:
199
+
200
+ ```jsx
201
+ import { ButtonItem } from '@atlaskit/menu';
202
+
203
+ const Element = () => (
204
+ <ButtonItem cssFn={cssFn()} />
205
+ ^^^^
206
+ );
207
+ ```
208
+
209
+ ```jsx
210
+ import Drawer from '@atlaskit/drawer';
211
+
212
+ const Element = () => (
213
+ <Drawer overrides={overrides} />
214
+ ^^^^
215
+ );
216
+ ```
217
+
218
+ 👍 Example of **correct** code for this rule:
219
+
220
+ ```jsx
221
+ import { SomeElement } from 'some-other-library';
222
+
223
+ const Element = () => <SomeElement cssFn={cssFn()} />;
224
+ ```
225
+
226
+ ```jsx
227
+ import { ButtonItem } from '@atlaskit/menu';
228
+
229
+ const Element = () => <ButtonItem />;
230
+ ```
231
+
232
+ ```jsx
233
+ import Drawer from '@atlaskit/drawer';
234
+
235
+ const Element = () => <Drawer />;
236
+ ```
237
+
238
+ ## no-deprecated-imports
239
+
240
+ Ensures usage of current Atlassian Design System dependencies.
241
+
242
+ 👎 Example of **incorrect** code for this rule:
243
+
244
+ ```ts
245
+ import Item from '@atlaskit/item';
246
+ ^^^^^^^^^^^^^^
247
+ ```
248
+
249
+ ```ts
250
+ import GlobalNav from '@atlaskit/global-navigation';
251
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
252
+ ```
253
+
254
+ 👍 Example of **correct** code for this rule:
255
+
256
+ ```ts
257
+ import Modal from '@atlaskit/modal-dialog';
258
+ ```
259
+
260
+ ```ts
261
+ import { ButtonItem } from '@atlaskit/menu';
262
+ ```
263
+
264
+ ## use-visually-hidden
265
+
266
+ This rule makes Design System consumers aware of the existing solutions.
267
+
268
+ 👎 Example of **incorrect** code for this rule:
269
+
270
+ ```jsx
271
+ import { css } from '@emotion/core';
272
+
273
+ const visuallyHiddenStyles = css({
274
+ width: '1px',
275
+ height: '1px',
276
+ padding: '0',
277
+ position: 'absolute',
278
+ border: '0',
279
+ clip: 'rect(1px, 1px, 1px, 1px)',
280
+ overflow: 'hidden',
281
+ whiteSpace: 'nowrap',
282
+ });
283
+ ^^^^
284
+ ```
285
+
286
+ ```jsx
287
+ import styled from '@emotion/styled';
288
+
289
+ const VisuallyHidden = styled.span`
290
+ width: 1px;
291
+ height: 1px;
292
+ padding: 0;
293
+ position: absolute;
294
+ border: 0;
295
+ clip: rect(1px, 1px, 1px, 1px);
296
+ overflow: hidden;
297
+ whiteSpace: nowrap;
298
+ `;
299
+ ^^^^
300
+ ```
301
+
302
+ ```jsx
303
+ import { visuallyHidden } from '@atlaskit/theme/constants';
304
+
305
+ const VisuallyHidden = styled.span`${visuallyHidden()}`;
306
+ ^^^^^^^^^^^^^^
307
+ ```
308
+
309
+ 👍 Example of **correct** code for this rule:
310
+
311
+ ```jsx
312
+ import VisuallyHidden from '@atlaskit/visually-hidden';
313
+ ```
@@ -2,4 +2,44 @@
2
2
  order: 2
3
3
  ---
4
4
 
5
- usage
5
+ This plugin contains rules that should be used with the Atlassian Design System.
6
+ Where possible, these rules come with auto fixers.
7
+
8
+ You can read more about configuring eslint in their [documentation](https://eslint.org/docs/user-guide/configuring).
9
+
10
+ ## Configuration
11
+
12
+ Add the plugin to your `.eslintrc.js` file.
13
+
14
+ ```diff
15
+ module.exports = {
16
+ plugins: [
17
+ + '@atlaskit/design-system',
18
+ ],
19
+ };
20
+ ```
21
+
22
+ Extend the configuration file.
23
+
24
+ ```diff
25
+ module.exports = {
26
+ extends: [
27
+ + 'plugin:@atlaskit/design-system/recommended',
28
+ ],
29
+ };
30
+ ```
31
+
32
+ Enable any desired rules. The rules shown below are strongly recommended.
33
+
34
+ ```diff
35
+ module.exports = {
36
+ rules: {
37
+ + '@atlaskit/design-system/ensure-design-token-usage': ['error', { 'shouldEnsureFallbackUsage': true }],
38
+ + '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
39
+ + '@atlaskit/design-system/no-unsafe-design-token-usage': ['error', { 'shouldEnsureFallbackUsage': true }],
40
+ + '@atlaskit/design-system/use-visually-hidden': 'error',
41
+ + '@atlaskit/design-system/no-deprecated-imports': 'error',
42
+ + '@atlaskit/design-system/no-deprecated-api-usage': 'error'
43
+ }
44
+ };
45
+ ```
@@ -34,6 +34,7 @@ var rule = {
34
34
  staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
35
35
  invalidToken: 'The token "{{name}}" does not exist.',
36
36
  tokenRemoved: 'The token "{{name}}" is removed in favour of "{{replacement}}".',
37
+ tokenIsExperimental: 'The token "{{name}}" is experimental and should not be used directly at this time. It should be replaced by "{{replacement}}".',
37
38
  tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
38
39
  tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
39
40
  }
@@ -134,14 +135,14 @@ var rule = {
134
135
  return;
135
136
  }
136
137
 
137
- var migrationMeta = _renameMapping.default.filter(function (t) {
138
+ var deletedMigrationMeta = _renameMapping.default.filter(function (t) {
138
139
  return t.state === 'deleted';
139
140
  }).find(function (t) {
140
141
  return (0, _tokenIds.getTokenId)(t.path) === tokenKey;
141
142
  });
142
143
 
143
- if (typeof tokenKey === 'string' && migrationMeta) {
144
- var cleanTokenKey = (0, _tokenIds.getTokenId)(migrationMeta.replacement);
144
+ if (typeof tokenKey === 'string' && deletedMigrationMeta) {
145
+ var cleanTokenKey = (0, _tokenIds.getTokenId)(deletedMigrationMeta.replacement);
145
146
  context.report({
146
147
  messageId: 'tokenRemoved',
147
148
  node: node,
@@ -156,6 +157,31 @@ var rule = {
156
157
  return;
157
158
  }
158
159
 
160
+ var experimentalMigrationMeta = _renameMapping.default.filter(function (t) {
161
+ return t.state === 'experimental';
162
+ }).find(function (t) {
163
+ return (0, _tokenIds.getTokenId)(t.path) === tokenKey;
164
+ });
165
+
166
+ var tokenNames = Object.keys(_tokenNames.default);
167
+
168
+ if (typeof tokenKey === 'string' && experimentalMigrationMeta) {
169
+ var replacementValue = experimentalMigrationMeta.replacement;
170
+ var isReplacementAToken = tokenNames.includes(replacementValue);
171
+ context.report({
172
+ messageId: 'tokenIsExperimental',
173
+ node: node,
174
+ data: {
175
+ name: tokenKey,
176
+ replacement: replacementValue
177
+ },
178
+ fix: function fix(fixer) {
179
+ return isReplacementAToken ? fixer.replaceText(node.arguments[0], "'".concat(replacementValue, "'")) : fixer.replaceText(node, "'".concat(replacementValue, "'"));
180
+ }
181
+ });
182
+ return;
183
+ }
184
+
159
185
  if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !_tokenNames.default[tokenKey]) {
160
186
  context.report({
161
187
  messageId: 'invalidToken',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.11.2",
3
+ "version": "4.12.0",
4
4
  "sideEffects": false
5
5
  }
@@ -31,6 +31,7 @@ token('color.background.blanket');
31
31
  `,
32
32
  invalidToken: 'The token "{{name}}" does not exist.',
33
33
  tokenRemoved: 'The token "{{name}}" is removed in favour of "{{replacement}}".',
34
+ tokenIsExperimental: 'The token "{{name}}" is experimental and should not be used directly at this time. It should be replaced by "{{replacement}}".',
34
35
  tokenFallbackEnforced: `Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.
35
36
 
36
37
  \`\`\`
@@ -138,10 +139,10 @@ token('color.background.blanket');
138
139
  return;
139
140
  }
140
141
 
141
- const migrationMeta = renameMapping.filter(t => t.state === 'deleted').find(t => getTokenId(t.path) === tokenKey);
142
+ const deletedMigrationMeta = renameMapping.filter(t => t.state === 'deleted').find(t => getTokenId(t.path) === tokenKey);
142
143
 
143
- if (typeof tokenKey === 'string' && migrationMeta) {
144
- const cleanTokenKey = getTokenId(migrationMeta.replacement);
144
+ if (typeof tokenKey === 'string' && deletedMigrationMeta) {
145
+ const cleanTokenKey = getTokenId(deletedMigrationMeta.replacement);
145
146
  context.report({
146
147
  messageId: 'tokenRemoved',
147
148
  node,
@@ -154,6 +155,24 @@ token('color.background.blanket');
154
155
  return;
155
156
  }
156
157
 
158
+ const experimentalMigrationMeta = renameMapping.filter(t => t.state === 'experimental').find(t => getTokenId(t.path) === tokenKey);
159
+ const tokenNames = Object.keys(tokens);
160
+
161
+ if (typeof tokenKey === 'string' && experimentalMigrationMeta) {
162
+ const replacementValue = experimentalMigrationMeta.replacement;
163
+ const isReplacementAToken = tokenNames.includes(replacementValue);
164
+ context.report({
165
+ messageId: 'tokenIsExperimental',
166
+ node,
167
+ data: {
168
+ name: tokenKey,
169
+ replacement: replacementValue
170
+ },
171
+ fix: fixer => isReplacementAToken ? fixer.replaceText(node.arguments[0], `'${replacementValue}'`) : fixer.replaceText(node, `'${replacementValue}'`)
172
+ });
173
+ return;
174
+ }
175
+
157
176
  if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
158
177
  context.report({
159
178
  messageId: 'invalidToken',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.11.2",
3
+ "version": "4.12.0",
4
4
  "sideEffects": false
5
5
  }
@@ -19,6 +19,7 @@ var rule = {
19
19
  staticToken: "Token string should be inlined directly into the function call.\n\n```\ntoken('color.background.blanket');\n```\n",
20
20
  invalidToken: 'The token "{{name}}" does not exist.',
21
21
  tokenRemoved: 'The token "{{name}}" is removed in favour of "{{replacement}}".',
22
+ tokenIsExperimental: 'The token "{{name}}" is experimental and should not be used directly at this time. It should be replaced by "{{replacement}}".',
22
23
  tokenFallbackEnforced: "Token function requires a fallback, preferably something that best matches the light/default theme in case tokens aren't present.\n\n```\ntoken('color.background.blanket', N500A);\n```\n ",
23
24
  tokenFallbackRestricted: "Token function must not use a fallback.\n\n```\ntoken('color.background.blanket');\n```\n "
24
25
  }
@@ -119,14 +120,14 @@ var rule = {
119
120
  return;
120
121
  }
121
122
 
122
- var migrationMeta = renameMapping.filter(function (t) {
123
+ var deletedMigrationMeta = renameMapping.filter(function (t) {
123
124
  return t.state === 'deleted';
124
125
  }).find(function (t) {
125
126
  return getTokenId(t.path) === tokenKey;
126
127
  });
127
128
 
128
- if (typeof tokenKey === 'string' && migrationMeta) {
129
- var cleanTokenKey = getTokenId(migrationMeta.replacement);
129
+ if (typeof tokenKey === 'string' && deletedMigrationMeta) {
130
+ var cleanTokenKey = getTokenId(deletedMigrationMeta.replacement);
130
131
  context.report({
131
132
  messageId: 'tokenRemoved',
132
133
  node: node,
@@ -141,6 +142,30 @@ var rule = {
141
142
  return;
142
143
  }
143
144
 
145
+ var experimentalMigrationMeta = renameMapping.filter(function (t) {
146
+ return t.state === 'experimental';
147
+ }).find(function (t) {
148
+ return getTokenId(t.path) === tokenKey;
149
+ });
150
+ var tokenNames = Object.keys(tokens);
151
+
152
+ if (typeof tokenKey === 'string' && experimentalMigrationMeta) {
153
+ var replacementValue = experimentalMigrationMeta.replacement;
154
+ var isReplacementAToken = tokenNames.includes(replacementValue);
155
+ context.report({
156
+ messageId: 'tokenIsExperimental',
157
+ node: node,
158
+ data: {
159
+ name: tokenKey,
160
+ replacement: replacementValue
161
+ },
162
+ fix: function fix(fixer) {
163
+ return isReplacementAToken ? fixer.replaceText(node.arguments[0], "'".concat(replacementValue, "'")) : fixer.replaceText(node, "'".concat(replacementValue, "'"));
164
+ }
165
+ });
166
+ return;
167
+ }
168
+
144
169
  if (typeof tokenKey !== 'string' || typeof tokenKey === 'string' && !tokens[tokenKey]) {
145
170
  context.report({
146
171
  messageId: 'invalidToken',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.11.2",
3
+ "version": "4.12.0",
4
4
  "sideEffects": false
5
5
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "4.11.2",
4
+ "version": "4.12.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"
@@ -11,8 +11,7 @@
11
11
  "releaseModel": "scheduled",
12
12
  "website": {
13
13
  "name": "ESLint plugin",
14
- "category": "Tooling",
15
- "draft": true
14
+ "category": "Tooling"
16
15
  }
17
16
  },
18
17
  "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
File without changes