@atlaskit/eslint-plugin-design-system 15.3.0 → 15.5.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,30 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 15.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`62b5aa6adc358`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/62b5aa6adc358) -
8
+ Completes deprecation guidance for the legacy WidthDetector default export.
9
+
10
+ Use `WidthObserver` from `@atlaskit/width-detector/width-observer` instead.
11
+
12
+ ## 15.4.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`0af52c9020b98`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0af52c9020b98) -
17
+ Add `expand-motion-shorthand` rule to `@atlaskit/eslint-plugin-design-system` so it is documented
18
+ on the design system website. Expands `transition` and `animation` CSS shorthand properties into
19
+ their individual sub-properties so values can be replaced with motion tokens.
20
+
21
+ Deprecate the `compiled/expand-motion-shorthand` rule in `@atlaskit/eslint-plugin-platform` in
22
+ favour of `@atlaskit/design-system/expand-motion-shorthand`.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies
27
+
3
28
  ## 15.3.0
4
29
 
5
30
  ### Minor Changes
package/README.md CHANGED
@@ -56,6 +56,7 @@ module.exports = {
56
56
  | <a href="./src/rules/ensure-design-token-usage-preview/README.md">ensure-design-token-usage/preview</a> | Enforces usage of pre-release design tokens rather than hard-coded values. | | Yes | Yes |
57
57
  | <a href="./src/rules/ensure-icon-color/README.md">ensure-icon-color</a> | Enforces that upcoming icon components have a color prop set, to enable a migration of the default value. | | | |
58
58
  | <a href="./src/rules/ensure-proper-xcss-usage/README.md">ensure-proper-xcss-usage</a> | Enforces proper xcss usage: migrate from xcss() to cssMap() and use cssMap objects with specific keys. | | | |
59
+ | <a href="./src/rules/expand-motion-shorthand/README.md">expand-motion-shorthand</a> | Expands `transition` and `animation` CSS shorthand properties into their individual sub-properties, so individual values can be replaced with motion tokens. | | Yes | |
59
60
  | <a href="./src/rules/icon-label/README.md">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
60
61
  | <a href="./src/rules/lozenge-badge-tag-labelling-system-migration/README.md">lozenge-badge-tag-labelling-system-migration</a> | Helps migrate Lozenge isBold prop, Badge appearance values, and SimpleTag/RemovableTag components as part of the Labelling System Phase 1 migration. | | Yes | |
61
62
  | <a href="./src/rules/no-banned-imports/README.md">no-banned-imports</a> | Disallow importing banned modules. | Yes | | |
package/compass.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  configVersion: 1
2
2
  id: ari:cloud:compass:a436116f-02ce-4520-8fbb-7301462a1674:component/c5751cc6-3513-4070-9deb-af31e86aed34/edf84219-0964-4453-82d0-ce6989586011
3
3
  name: '@atlaskit/eslint-plugin-design-system'
4
- ownerId: ari:cloud:identity::team/og-7ea5b0e8-d37c-4003-aae2-0bb5c4b00147 # Design System Team
4
+ ownerId: ari:cloud:identity::team/be19136e-6275-419f-be0c-fcee844b8684 # Design System Engineers
5
5
  labels:
6
6
  - platform-code
7
7
  - platform-afm
@@ -32,7 +32,7 @@ customFields:
32
32
  value: true
33
33
  - name: Reviewer Selection Mechanism
34
34
  type: text
35
- value: random(2)
35
+ value: random(3)
36
36
  - name: Required Reviewer Approvals
37
37
  type: number
38
38
  value: 1
@@ -100,6 +100,17 @@
100
100
  "@atlaskit/item": {
101
101
  "message": "item is deprecated. Please use '@atlaskit/menu' instead."
102
102
  },
103
+ "@atlaskit/width-detector": {
104
+ "importSpecifiers": [
105
+ {
106
+ "importName": "default",
107
+ "message": "WidthDetector is deprecated. Please use WidthObserver from '@atlaskit/width-detector/width-observer' instead."
108
+ }
109
+ ]
110
+ },
111
+ "@atlaskit/width-detector/width-detector": {
112
+ "message": "WidthDetector is deprecated. Please use WidthObserver from '@atlaskit/width-detector/width-observer' instead."
113
+ },
103
114
  "@atlaskit/theme/elevation": {
104
115
  "importSpecifiers": [
105
116
  {
@@ -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::5a52fa9f987063b4d0d9e825b02a6067>>
9
+ * @codegen <<SignedSource::22c8677c3fc921ba33acd505671124a2>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
 
@@ -21,6 +21,7 @@ var rules = {
21
21
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
22
22
  '@atlaskit/design-system/ensure-icon-color': 'error',
23
23
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
24
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
24
25
  '@atlaskit/design-system/icon-label': 'warn',
25
26
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
26
27
  '@atlaskit/design-system/no-banned-imports': '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::458994db7ee4b74c7fcbd2157dc4496e>>
9
+ * @codegen <<SignedSource::2e8702ed4d2cd2902fed7fc5a895ea6e>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
 
@@ -20,6 +20,7 @@ var rules = {
20
20
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
21
21
  '@atlaskit/design-system/ensure-icon-color': 'error',
22
22
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
23
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
23
24
  '@atlaskit/design-system/icon-label': 'warn',
24
25
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
25
26
  '@atlaskit/design-system/no-banned-imports': 'error',
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _createLintRule = require("../utils/create-lint-rule");
8
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
9
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
10
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
11
+ var EASING_KEYWORDS = ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear', 'step-start', 'step-end'];
12
+ var KEYWORD_VALUES = ['none', 'all', 'inherit', 'initial', 'unset'];
13
+ var isDuration = function isDuration(token) {
14
+ return /^(?:0|\d+(?:\.\d+)?m?s)$/.test(token);
15
+ };
16
+ var isEasing = function isEasing(token) {
17
+ return EASING_KEYWORDS.includes(token) || token.startsWith('cubic-bezier(') || token.startsWith('steps(');
18
+ };
19
+ /**
20
+ * Tokenizes a CSS shorthand value string, respecting function boundaries.
21
+ * e.g. 'opacity 200ms cubic-bezier(0.4, 0, 0, 1) 0ms' →
22
+ * ['opacity', '200ms', 'cubic-bezier(0.4, 0, 0, 1)', '0ms']
23
+ * Splits on whitespace only when not inside parentheses.
24
+ */
25
+ var tokenizeShorthand = function tokenizeShorthand(value) {
26
+ var tokens = [];
27
+ var depth = 0;
28
+ var current = '';
29
+ for (var i = 0; i < value.length; i++) {
30
+ var ch = value[i];
31
+ if (ch === '(') {
32
+ depth++;
33
+ current += ch;
34
+ } else if (ch === ')') {
35
+ depth--;
36
+ current += ch;
37
+ } else if (/\s/.test(ch) && depth === 0) {
38
+ if (current.length > 0) {
39
+ tokens.push(current);
40
+ current = '';
41
+ }
42
+ } else {
43
+ current += ch;
44
+ }
45
+ }
46
+ if (current.length > 0) {
47
+ tokens.push(current);
48
+ }
49
+ return tokens;
50
+ };
51
+
52
+ // Splits on top-level commas (outside function parens) — preserves cubic-bezier(...) commas.
53
+ var splitOnTopLevelCommas = function splitOnTopLevelCommas(value) {
54
+ var parts = [];
55
+ var depth = 0;
56
+ var current = '';
57
+ var _iterator = _createForOfIteratorHelper(value),
58
+ _step;
59
+ try {
60
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
61
+ var ch = _step.value;
62
+ if (ch === '(') {
63
+ depth++;
64
+ current += ch;
65
+ } else if (ch === ')') {
66
+ depth--;
67
+ current += ch;
68
+ } else if (ch === ',' && depth === 0) {
69
+ parts.push(current.trim());
70
+ current = '';
71
+ } else {
72
+ current += ch;
73
+ }
74
+ }
75
+ } catch (err) {
76
+ _iterator.e(err);
77
+ } finally {
78
+ _iterator.f();
79
+ }
80
+ if (current.trim().length > 0) {
81
+ parts.push(current.trim());
82
+ }
83
+ return parts;
84
+ };
85
+ var parseTransition = function parseTransition(value) {
86
+ var parts = tokenizeShorthand(value.trim());
87
+ var result = {};
88
+ var durationCount = 0;
89
+ var _iterator2 = _createForOfIteratorHelper(parts),
90
+ _step2;
91
+ try {
92
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
93
+ var part = _step2.value;
94
+ if (isDuration(part)) {
95
+ if (durationCount === 0) {
96
+ result.transitionDuration = part;
97
+ } else {
98
+ result.transitionDelay = part;
99
+ }
100
+ durationCount++;
101
+ } else if (isEasing(part)) {
102
+ result.transitionTimingFunction = part;
103
+ } else {
104
+ result.transitionProperty = part;
105
+ }
106
+ }
107
+ } catch (err) {
108
+ _iterator2.e(err);
109
+ } finally {
110
+ _iterator2.f();
111
+ }
112
+ return result;
113
+ };
114
+ var parseAnimation = function parseAnimation(value) {
115
+ var parts = tokenizeShorthand(value.trim());
116
+ var result = {};
117
+ var durationCount = 0;
118
+ var _iterator3 = _createForOfIteratorHelper(parts),
119
+ _step3;
120
+ try {
121
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
122
+ var part = _step3.value;
123
+ if (isDuration(part)) {
124
+ if (durationCount === 0) {
125
+ result.animationDuration = part;
126
+ } else {
127
+ result.animationDelay = part;
128
+ }
129
+ durationCount++;
130
+ } else if (isEasing(part)) {
131
+ result.animationTimingFunction = part;
132
+ } else if (part === 'infinite' || /^\d+(\.\d+)?$/.test(part)) {
133
+ result.animationIterationCount = part;
134
+ } else if (['normal', 'reverse', 'alternate', 'alternate-reverse'].includes(part)) {
135
+ result.animationDirection = part;
136
+ } else if (['none', 'forwards', 'backwards', 'both'].includes(part)) {
137
+ result.animationFillMode = part;
138
+ } else if (['running', 'paused'].includes(part)) {
139
+ result.animationPlayState = part;
140
+ } else {
141
+ result.animationName = part;
142
+ }
143
+ }
144
+ } catch (err) {
145
+ _iterator3.e(err);
146
+ } finally {
147
+ _iterator3.f();
148
+ }
149
+ return result;
150
+ };
151
+
152
+ // Combine sub-property values across comma-separated transitions/animations.
153
+ // If no segment explicitly set this sub-property, omit it entirely.
154
+ // Otherwise, fill missing slots with the CSS spec default to preserve positional alignment.
155
+ var combineSubPropertyValues = function combineSubPropertyValues(segments, subProperty, defaultValue) {
156
+ if (segments.every(function (s) {
157
+ return s[subProperty] === undefined;
158
+ })) {
159
+ return undefined;
160
+ }
161
+ return segments.map(function (s) {
162
+ var _s$subProperty;
163
+ return (_s$subProperty = s[subProperty]) !== null && _s$subProperty !== void 0 ? _s$subProperty : defaultValue;
164
+ }).join(', ');
165
+ };
166
+ var buildTransitionFix = function buildTransitionFix(segments, indent) {
167
+ var lines = [];
168
+ var property = combineSubPropertyValues(segments, 'transitionProperty', 'all');
169
+ var duration = combineSubPropertyValues(segments, 'transitionDuration', '0s');
170
+ var timing = combineSubPropertyValues(segments, 'transitionTimingFunction', 'ease');
171
+ var delay = combineSubPropertyValues(segments, 'transitionDelay', '0s');
172
+ if (property !== undefined) lines.push("transitionProperty: '".concat(property, "'"));
173
+ if (duration !== undefined) lines.push("transitionDuration: '".concat(duration, "'"));
174
+ if (timing !== undefined) lines.push("transitionTimingFunction: '".concat(timing, "'"));
175
+ if (delay !== undefined) lines.push("transitionDelay: '".concat(delay, "'"));
176
+ return lines.join(",\n".concat(indent));
177
+ };
178
+ var buildAnimationFix = function buildAnimationFix(segments, indent) {
179
+ var lines = [];
180
+ var name = combineSubPropertyValues(segments, 'animationName', 'none');
181
+ var duration = combineSubPropertyValues(segments, 'animationDuration', '0s');
182
+ var timing = combineSubPropertyValues(segments, 'animationTimingFunction', 'ease');
183
+ var delay = combineSubPropertyValues(segments, 'animationDelay', '0s');
184
+ var iter = combineSubPropertyValues(segments, 'animationIterationCount', '1');
185
+ var direction = combineSubPropertyValues(segments, 'animationDirection', 'normal');
186
+ var fill = combineSubPropertyValues(segments, 'animationFillMode', 'none');
187
+ var playState = combineSubPropertyValues(segments, 'animationPlayState', 'running');
188
+ if (name !== undefined) lines.push("animationName: '".concat(name, "'"));
189
+ if (duration !== undefined) lines.push("animationDuration: '".concat(duration, "'"));
190
+ if (timing !== undefined) lines.push("animationTimingFunction: '".concat(timing, "'"));
191
+ if (delay !== undefined) lines.push("animationDelay: '".concat(delay, "'"));
192
+ if (iter !== undefined) lines.push("animationIterationCount: '".concat(iter, "'"));
193
+ if (direction !== undefined) lines.push("animationDirection: '".concat(direction, "'"));
194
+ if (fill !== undefined) lines.push("animationFillMode: '".concat(fill, "'"));
195
+ if (playState !== undefined) lines.push("animationPlayState: '".concat(playState, "'"));
196
+ return lines.join(",\n".concat(indent));
197
+ };
198
+ var TRANSITION_SUB_PROPERTIES = ['transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay'];
199
+ var ANIMATION_SUB_PROPERTIES = ['animationName', 'animationDuration', 'animationTimingFunction', 'animationDelay', 'animationIterationCount', 'animationDirection', 'animationFillMode', 'animationPlayState'];
200
+ var executeExpandTransitionRule = function executeExpandTransitionRule(context, node, property) {
201
+ var _context$sourceCode, _node$loc;
202
+ if (node.value.type === 'CallExpression') {
203
+ return;
204
+ }
205
+ if (node.value.type === 'TemplateLiteral') {
206
+ return;
207
+ }
208
+ if (node.value.type !== 'Literal' || typeof node.value.value !== 'string') {
209
+ return;
210
+ }
211
+ var rawValue = node.value.value;
212
+ if (KEYWORD_VALUES.includes(rawValue)) {
213
+ return;
214
+ }
215
+ var subProperties = property === 'transition' ? TRANSITION_SUB_PROPERTIES : ANIMATION_SUB_PROPERTIES;
216
+
217
+ // Extract leading whitespace to preserve indentation style (tabs vs spaces)
218
+ var sourceCode = (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode();
219
+ var nodeStart = (_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start;
220
+ var indent = '\t';
221
+ if (nodeStart) {
222
+ var _sourceCode$lines;
223
+ var lineText = (_sourceCode$lines = sourceCode.lines[nodeStart.line - 1]) !== null && _sourceCode$lines !== void 0 ? _sourceCode$lines : '';
224
+ var leadingWhitespace = lineText.match(/^(\s*)/);
225
+ if (leadingWhitespace) {
226
+ indent = leadingWhitespace[1];
227
+ }
228
+ }
229
+ var segmentStrings = splitOnTopLevelCommas(rawValue);
230
+ if (property === 'transition') {
231
+ var segments = segmentStrings.map(parseTransition);
232
+ var fixText = buildTransitionFix(segments, indent);
233
+ context.report({
234
+ node: node,
235
+ messageId: 'expandTransitionShorthand',
236
+ data: {
237
+ property: property,
238
+ subProperties: subProperties.join(', ')
239
+ },
240
+ fix: function fix(fixer) {
241
+ return fixer.replaceText(node, fixText);
242
+ }
243
+ });
244
+ } else {
245
+ var _segments = segmentStrings.map(parseAnimation);
246
+ var _fixText = buildAnimationFix(_segments, indent);
247
+ context.report({
248
+ node: node,
249
+ messageId: 'expandTransitionShorthand',
250
+ data: {
251
+ property: property,
252
+ subProperties: subProperties.join(', ')
253
+ },
254
+ fix: function fix(fixer) {
255
+ return fixer.replaceText(node, _fixText);
256
+ }
257
+ });
258
+ }
259
+ };
260
+ var rule = (0, _createLintRule.createLintRule)({
261
+ meta: {
262
+ name: 'expand-motion-shorthand',
263
+ type: 'suggestion',
264
+ fixable: 'code',
265
+ docs: {
266
+ description: 'Expands `transition` and `animation` CSS shorthand properties into their individual sub-properties, so individual values can be replaced with motion tokens.',
267
+ recommended: false,
268
+ severity: 'warn'
269
+ },
270
+ messages: {
271
+ expandTransitionShorthand: "Use {{ subProperties }} instead of the '{{ property }}' shorthand so that individual values can be replaced with motion tokens."
272
+ }
273
+ },
274
+ create: function create(context) {
275
+ return {
276
+ 'Property[key.name="transition"]': function PropertyKeyNameTransition(node) {
277
+ executeExpandTransitionRule(context, node, 'transition');
278
+ },
279
+ 'Property[key.name="animation"]': function PropertyKeyNameAnimation(node) {
280
+ executeExpandTransitionRule(context, node, 'animation');
281
+ }
282
+ };
283
+ }
284
+ });
285
+ var _default = exports.default = rule;
@@ -12,6 +12,7 @@ var _ensureDesignTokenUsage = _interopRequireDefault(require("./ensure-design-to
12
12
  var _ensureDesignTokenUsagePreview = _interopRequireDefault(require("./ensure-design-token-usage-preview"));
13
13
  var _ensureIconColor = _interopRequireDefault(require("./ensure-icon-color"));
14
14
  var _ensureProperXcssUsage = _interopRequireDefault(require("./ensure-proper-xcss-usage"));
15
+ var _expandMotionShorthand = _interopRequireDefault(require("./expand-motion-shorthand"));
15
16
  var _iconLabel = _interopRequireDefault(require("./icon-label"));
16
17
  var _lozengeBadgeTagLabellingSystemMigration = _interopRequireDefault(require("./lozenge-badge-tag-labelling-system-migration"));
17
18
  var _noBannedImports = _interopRequireDefault(require("./no-banned-imports"));
@@ -85,7 +86,7 @@ var _useTokensTypography = _interopRequireDefault(require("./use-tokens-typograp
85
86
  var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
86
87
  /**
87
88
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
88
- * @codegen <<SignedSource::d527850500d4518cc654415fd5f4fbc0>>
89
+ * @codegen <<SignedSource::56038bd879a1d1c73de3addd961ade97>>
89
90
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
90
91
  */
91
92
 
@@ -97,6 +98,7 @@ var rules = exports.rules = {
97
98
  'ensure-design-token-usage/preview': _ensureDesignTokenUsagePreview.default,
98
99
  'ensure-icon-color': _ensureIconColor.default,
99
100
  'ensure-proper-xcss-usage': _ensureProperXcssUsage.default,
101
+ 'expand-motion-shorthand': _expandMotionShorthand.default,
100
102
  'icon-label': _iconLabel.default,
101
103
  'lozenge-badge-tag-labelling-system-migration': _lozengeBadgeTagLabellingSystemMigration.default,
102
104
  'no-banned-imports': _noBannedImports.default,
@@ -6,6 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.name = exports.default = void 0;
7
7
  var _eslintCodemodUtils = require("eslint-codemod-utils");
8
8
  var _createLintRule = require("../utils/create-lint-rule");
9
+ /* eslint-disable
10
+ @atlaskit/design-system/no-to-match-snapshot,
11
+ @atlaskit/design-system/no-unsafe-inline-snapshot
12
+ -- TODO(IND-4952): existing snapshot tests will be removed in a follow-up cleanup PR.
13
+ See https://hello.atlassian.net/wiki/spaces/afm/pages/7146174189/LDR+Unit+Tests+-+Ban+Snapshot+tests+in+Platform
14
+ and raise concerns in https://atlassian.enterprise.slack.com/archives/C0BD4K40BLH
15
+ */
16
+
9
17
  var name = exports.name = 'no-to-match-snapshot';
10
18
  var rule = (0, _createLintRule.createLintRule)({
11
19
  meta: {
@@ -100,6 +100,17 @@
100
100
  "@atlaskit/item": {
101
101
  "message": "item is deprecated. Please use '@atlaskit/menu' instead."
102
102
  },
103
+ "@atlaskit/width-detector": {
104
+ "importSpecifiers": [
105
+ {
106
+ "importName": "default",
107
+ "message": "WidthDetector is deprecated. Please use WidthObserver from '@atlaskit/width-detector/width-observer' instead."
108
+ }
109
+ ]
110
+ },
111
+ "@atlaskit/width-detector/width-detector": {
112
+ "message": "WidthDetector is deprecated. Please use WidthObserver from '@atlaskit/width-detector/width-observer' instead."
113
+ },
103
114
  "@atlaskit/theme/elevation": {
104
115
  "importSpecifiers": [
105
116
  {
@@ -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::5a52fa9f987063b4d0d9e825b02a6067>>
3
+ * @codegen <<SignedSource::22c8677c3fc921ba33acd505671124a2>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -15,6 +15,7 @@ const rules = {
15
15
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
16
16
  '@atlaskit/design-system/ensure-icon-color': 'error',
17
17
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
18
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
18
19
  '@atlaskit/design-system/icon-label': 'warn',
19
20
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
20
21
  '@atlaskit/design-system/no-banned-imports': '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::458994db7ee4b74c7fcbd2157dc4496e>>
3
+ * @codegen <<SignedSource::2e8702ed4d2cd2902fed7fc5a895ea6e>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -14,6 +14,7 @@ const rules = {
14
14
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
15
15
  '@atlaskit/design-system/ensure-icon-color': 'error',
16
16
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
17
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
17
18
  '@atlaskit/design-system/icon-label': 'warn',
18
19
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
19
20
  '@atlaskit/design-system/no-banned-imports': 'error',
@@ -0,0 +1,243 @@
1
+ import { createLintRule } from '../utils/create-lint-rule';
2
+ const EASING_KEYWORDS = ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear', 'step-start', 'step-end'];
3
+ const KEYWORD_VALUES = ['none', 'all', 'inherit', 'initial', 'unset'];
4
+ const isDuration = token => /^(?:0|\d+(?:\.\d+)?m?s)$/.test(token);
5
+ const isEasing = token => EASING_KEYWORDS.includes(token) || token.startsWith('cubic-bezier(') || token.startsWith('steps(');
6
+ /**
7
+ * Tokenizes a CSS shorthand value string, respecting function boundaries.
8
+ * e.g. 'opacity 200ms cubic-bezier(0.4, 0, 0, 1) 0ms' →
9
+ * ['opacity', '200ms', 'cubic-bezier(0.4, 0, 0, 1)', '0ms']
10
+ * Splits on whitespace only when not inside parentheses.
11
+ */
12
+ const tokenizeShorthand = value => {
13
+ const tokens = [];
14
+ let depth = 0;
15
+ let current = '';
16
+ for (let i = 0; i < value.length; i++) {
17
+ const ch = value[i];
18
+ if (ch === '(') {
19
+ depth++;
20
+ current += ch;
21
+ } else if (ch === ')') {
22
+ depth--;
23
+ current += ch;
24
+ } else if (/\s/.test(ch) && depth === 0) {
25
+ if (current.length > 0) {
26
+ tokens.push(current);
27
+ current = '';
28
+ }
29
+ } else {
30
+ current += ch;
31
+ }
32
+ }
33
+ if (current.length > 0) {
34
+ tokens.push(current);
35
+ }
36
+ return tokens;
37
+ };
38
+
39
+ // Splits on top-level commas (outside function parens) — preserves cubic-bezier(...) commas.
40
+ const splitOnTopLevelCommas = value => {
41
+ const parts = [];
42
+ let depth = 0;
43
+ let current = '';
44
+ for (const ch of value) {
45
+ if (ch === '(') {
46
+ depth++;
47
+ current += ch;
48
+ } else if (ch === ')') {
49
+ depth--;
50
+ current += ch;
51
+ } else if (ch === ',' && depth === 0) {
52
+ parts.push(current.trim());
53
+ current = '';
54
+ } else {
55
+ current += ch;
56
+ }
57
+ }
58
+ if (current.trim().length > 0) {
59
+ parts.push(current.trim());
60
+ }
61
+ return parts;
62
+ };
63
+ const parseTransition = value => {
64
+ const parts = tokenizeShorthand(value.trim());
65
+ const result = {};
66
+ let durationCount = 0;
67
+ for (const part of parts) {
68
+ if (isDuration(part)) {
69
+ if (durationCount === 0) {
70
+ result.transitionDuration = part;
71
+ } else {
72
+ result.transitionDelay = part;
73
+ }
74
+ durationCount++;
75
+ } else if (isEasing(part)) {
76
+ result.transitionTimingFunction = part;
77
+ } else {
78
+ result.transitionProperty = part;
79
+ }
80
+ }
81
+ return result;
82
+ };
83
+ const parseAnimation = value => {
84
+ const parts = tokenizeShorthand(value.trim());
85
+ const result = {};
86
+ let durationCount = 0;
87
+ for (const part of parts) {
88
+ if (isDuration(part)) {
89
+ if (durationCount === 0) {
90
+ result.animationDuration = part;
91
+ } else {
92
+ result.animationDelay = part;
93
+ }
94
+ durationCount++;
95
+ } else if (isEasing(part)) {
96
+ result.animationTimingFunction = part;
97
+ } else if (part === 'infinite' || /^\d+(\.\d+)?$/.test(part)) {
98
+ result.animationIterationCount = part;
99
+ } else if (['normal', 'reverse', 'alternate', 'alternate-reverse'].includes(part)) {
100
+ result.animationDirection = part;
101
+ } else if (['none', 'forwards', 'backwards', 'both'].includes(part)) {
102
+ result.animationFillMode = part;
103
+ } else if (['running', 'paused'].includes(part)) {
104
+ result.animationPlayState = part;
105
+ } else {
106
+ result.animationName = part;
107
+ }
108
+ }
109
+ return result;
110
+ };
111
+
112
+ // Combine sub-property values across comma-separated transitions/animations.
113
+ // If no segment explicitly set this sub-property, omit it entirely.
114
+ // Otherwise, fill missing slots with the CSS spec default to preserve positional alignment.
115
+ const combineSubPropertyValues = (segments, subProperty, defaultValue) => {
116
+ if (segments.every(s => s[subProperty] === undefined)) {
117
+ return undefined;
118
+ }
119
+ return segments.map(s => {
120
+ var _s$subProperty;
121
+ return (_s$subProperty = s[subProperty]) !== null && _s$subProperty !== void 0 ? _s$subProperty : defaultValue;
122
+ }).join(', ');
123
+ };
124
+ const buildTransitionFix = (segments, indent) => {
125
+ const lines = [];
126
+ const property = combineSubPropertyValues(segments, 'transitionProperty', 'all');
127
+ const duration = combineSubPropertyValues(segments, 'transitionDuration', '0s');
128
+ const timing = combineSubPropertyValues(segments, 'transitionTimingFunction', 'ease');
129
+ const delay = combineSubPropertyValues(segments, 'transitionDelay', '0s');
130
+ if (property !== undefined) lines.push(`transitionProperty: '${property}'`);
131
+ if (duration !== undefined) lines.push(`transitionDuration: '${duration}'`);
132
+ if (timing !== undefined) lines.push(`transitionTimingFunction: '${timing}'`);
133
+ if (delay !== undefined) lines.push(`transitionDelay: '${delay}'`);
134
+ return lines.join(`,\n${indent}`);
135
+ };
136
+ const buildAnimationFix = (segments, indent) => {
137
+ const lines = [];
138
+ const name = combineSubPropertyValues(segments, 'animationName', 'none');
139
+ const duration = combineSubPropertyValues(segments, 'animationDuration', '0s');
140
+ const timing = combineSubPropertyValues(segments, 'animationTimingFunction', 'ease');
141
+ const delay = combineSubPropertyValues(segments, 'animationDelay', '0s');
142
+ const iter = combineSubPropertyValues(segments, 'animationIterationCount', '1');
143
+ const direction = combineSubPropertyValues(segments, 'animationDirection', 'normal');
144
+ const fill = combineSubPropertyValues(segments, 'animationFillMode', 'none');
145
+ const playState = combineSubPropertyValues(segments, 'animationPlayState', 'running');
146
+ if (name !== undefined) lines.push(`animationName: '${name}'`);
147
+ if (duration !== undefined) lines.push(`animationDuration: '${duration}'`);
148
+ if (timing !== undefined) lines.push(`animationTimingFunction: '${timing}'`);
149
+ if (delay !== undefined) lines.push(`animationDelay: '${delay}'`);
150
+ if (iter !== undefined) lines.push(`animationIterationCount: '${iter}'`);
151
+ if (direction !== undefined) lines.push(`animationDirection: '${direction}'`);
152
+ if (fill !== undefined) lines.push(`animationFillMode: '${fill}'`);
153
+ if (playState !== undefined) lines.push(`animationPlayState: '${playState}'`);
154
+ return lines.join(`,\n${indent}`);
155
+ };
156
+ const TRANSITION_SUB_PROPERTIES = ['transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay'];
157
+ const ANIMATION_SUB_PROPERTIES = ['animationName', 'animationDuration', 'animationTimingFunction', 'animationDelay', 'animationIterationCount', 'animationDirection', 'animationFillMode', 'animationPlayState'];
158
+ const executeExpandTransitionRule = (context, node, property) => {
159
+ var _context$sourceCode, _node$loc;
160
+ if (node.value.type === 'CallExpression') {
161
+ return;
162
+ }
163
+ if (node.value.type === 'TemplateLiteral') {
164
+ return;
165
+ }
166
+ if (node.value.type !== 'Literal' || typeof node.value.value !== 'string') {
167
+ return;
168
+ }
169
+ const rawValue = node.value.value;
170
+ if (KEYWORD_VALUES.includes(rawValue)) {
171
+ return;
172
+ }
173
+ const subProperties = property === 'transition' ? TRANSITION_SUB_PROPERTIES : ANIMATION_SUB_PROPERTIES;
174
+
175
+ // Extract leading whitespace to preserve indentation style (tabs vs spaces)
176
+ const sourceCode = (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode();
177
+ const nodeStart = (_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start;
178
+ let indent = '\t';
179
+ if (nodeStart) {
180
+ var _sourceCode$lines;
181
+ const lineText = (_sourceCode$lines = sourceCode.lines[nodeStart.line - 1]) !== null && _sourceCode$lines !== void 0 ? _sourceCode$lines : '';
182
+ const leadingWhitespace = lineText.match(/^(\s*)/);
183
+ if (leadingWhitespace) {
184
+ indent = leadingWhitespace[1];
185
+ }
186
+ }
187
+ const segmentStrings = splitOnTopLevelCommas(rawValue);
188
+ if (property === 'transition') {
189
+ const segments = segmentStrings.map(parseTransition);
190
+ const fixText = buildTransitionFix(segments, indent);
191
+ context.report({
192
+ node,
193
+ messageId: 'expandTransitionShorthand',
194
+ data: {
195
+ property,
196
+ subProperties: subProperties.join(', ')
197
+ },
198
+ fix(fixer) {
199
+ return fixer.replaceText(node, fixText);
200
+ }
201
+ });
202
+ } else {
203
+ const segments = segmentStrings.map(parseAnimation);
204
+ const fixText = buildAnimationFix(segments, indent);
205
+ context.report({
206
+ node,
207
+ messageId: 'expandTransitionShorthand',
208
+ data: {
209
+ property,
210
+ subProperties: subProperties.join(', ')
211
+ },
212
+ fix(fixer) {
213
+ return fixer.replaceText(node, fixText);
214
+ }
215
+ });
216
+ }
217
+ };
218
+ const rule = createLintRule({
219
+ meta: {
220
+ name: 'expand-motion-shorthand',
221
+ type: 'suggestion',
222
+ fixable: 'code',
223
+ docs: {
224
+ description: 'Expands `transition` and `animation` CSS shorthand properties into their individual sub-properties, so individual values can be replaced with motion tokens.',
225
+ recommended: false,
226
+ severity: 'warn'
227
+ },
228
+ messages: {
229
+ expandTransitionShorthand: "Use {{ subProperties }} instead of the '{{ property }}' shorthand so that individual values can be replaced with motion tokens."
230
+ }
231
+ },
232
+ create(context) {
233
+ return {
234
+ 'Property[key.name="transition"]': function (node) {
235
+ executeExpandTransitionRule(context, node, 'transition');
236
+ },
237
+ 'Property[key.name="animation"]': function (node) {
238
+ executeExpandTransitionRule(context, node, 'animation');
239
+ }
240
+ };
241
+ }
242
+ });
243
+ 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::d527850500d4518cc654415fd5f4fbc0>>
3
+ * @codegen <<SignedSource::56038bd879a1d1c73de3addd961ade97>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -11,6 +11,7 @@ import ensureDesignTokenUsage from './ensure-design-token-usage';
11
11
  import ensureDesignTokenUsagePreview from './ensure-design-token-usage-preview';
12
12
  import ensureIconColor from './ensure-icon-color';
13
13
  import ensureProperXcssUsage from './ensure-proper-xcss-usage';
14
+ import expandMotionShorthand from './expand-motion-shorthand';
14
15
  import iconLabel from './icon-label';
15
16
  import lozengeBadgeTagLabellingSystemMigration from './lozenge-badge-tag-labelling-system-migration';
16
17
  import noBannedImports from './no-banned-imports';
@@ -90,6 +91,7 @@ export const rules = {
90
91
  'ensure-design-token-usage/preview': ensureDesignTokenUsagePreview,
91
92
  'ensure-icon-color': ensureIconColor,
92
93
  'ensure-proper-xcss-usage': ensureProperXcssUsage,
94
+ 'expand-motion-shorthand': expandMotionShorthand,
93
95
  'icon-label': iconLabel,
94
96
  'lozenge-badge-tag-labelling-system-migration': lozengeBadgeTagLabellingSystemMigration,
95
97
  'no-banned-imports': noBannedImports,
@@ -1,3 +1,11 @@
1
+ /* eslint-disable
2
+ @atlaskit/design-system/no-to-match-snapshot,
3
+ @atlaskit/design-system/no-unsafe-inline-snapshot
4
+ -- TODO(IND-4952): existing snapshot tests will be removed in a follow-up cleanup PR.
5
+ See https://hello.atlassian.net/wiki/spaces/afm/pages/7146174189/LDR+Unit+Tests+-+Ban+Snapshot+tests+in+Platform
6
+ and raise concerns in https://atlassian.enterprise.slack.com/archives/C0BD4K40BLH
7
+ */
8
+
1
9
  import { isNodeOfType } from 'eslint-codemod-utils';
2
10
  import { createLintRule } from '../utils/create-lint-rule';
3
11
  export const name = 'no-to-match-snapshot';
@@ -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::5a52fa9f987063b4d0d9e825b02a6067>>
3
+ * @codegen <<SignedSource::22c8677c3fc921ba33acd505671124a2>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -15,6 +15,7 @@ var rules = {
15
15
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
16
16
  '@atlaskit/design-system/ensure-icon-color': 'error',
17
17
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
18
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
18
19
  '@atlaskit/design-system/icon-label': 'warn',
19
20
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
20
21
  '@atlaskit/design-system/no-banned-imports': '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::458994db7ee4b74c7fcbd2157dc4496e>>
3
+ * @codegen <<SignedSource::2e8702ed4d2cd2902fed7fc5a895ea6e>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -14,6 +14,7 @@ var rules = {
14
14
  '@atlaskit/design-system/ensure-design-token-usage/preview': 'warn',
15
15
  '@atlaskit/design-system/ensure-icon-color': 'error',
16
16
  '@atlaskit/design-system/ensure-proper-xcss-usage': 'error',
17
+ '@atlaskit/design-system/expand-motion-shorthand': 'warn',
17
18
  '@atlaskit/design-system/icon-label': 'warn',
18
19
  '@atlaskit/design-system/lozenge-badge-tag-labelling-system-migration': 'warn',
19
20
  '@atlaskit/design-system/no-banned-imports': 'error',
@@ -0,0 +1,279 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
+ import { createLintRule } from '../utils/create-lint-rule';
5
+ var EASING_KEYWORDS = ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear', 'step-start', 'step-end'];
6
+ var KEYWORD_VALUES = ['none', 'all', 'inherit', 'initial', 'unset'];
7
+ var isDuration = function isDuration(token) {
8
+ return /^(?:0|\d+(?:\.\d+)?m?s)$/.test(token);
9
+ };
10
+ var isEasing = function isEasing(token) {
11
+ return EASING_KEYWORDS.includes(token) || token.startsWith('cubic-bezier(') || token.startsWith('steps(');
12
+ };
13
+ /**
14
+ * Tokenizes a CSS shorthand value string, respecting function boundaries.
15
+ * e.g. 'opacity 200ms cubic-bezier(0.4, 0, 0, 1) 0ms' →
16
+ * ['opacity', '200ms', 'cubic-bezier(0.4, 0, 0, 1)', '0ms']
17
+ * Splits on whitespace only when not inside parentheses.
18
+ */
19
+ var tokenizeShorthand = function tokenizeShorthand(value) {
20
+ var tokens = [];
21
+ var depth = 0;
22
+ var current = '';
23
+ for (var i = 0; i < value.length; i++) {
24
+ var ch = value[i];
25
+ if (ch === '(') {
26
+ depth++;
27
+ current += ch;
28
+ } else if (ch === ')') {
29
+ depth--;
30
+ current += ch;
31
+ } else if (/\s/.test(ch) && depth === 0) {
32
+ if (current.length > 0) {
33
+ tokens.push(current);
34
+ current = '';
35
+ }
36
+ } else {
37
+ current += ch;
38
+ }
39
+ }
40
+ if (current.length > 0) {
41
+ tokens.push(current);
42
+ }
43
+ return tokens;
44
+ };
45
+
46
+ // Splits on top-level commas (outside function parens) — preserves cubic-bezier(...) commas.
47
+ var splitOnTopLevelCommas = function splitOnTopLevelCommas(value) {
48
+ var parts = [];
49
+ var depth = 0;
50
+ var current = '';
51
+ var _iterator = _createForOfIteratorHelper(value),
52
+ _step;
53
+ try {
54
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
55
+ var ch = _step.value;
56
+ if (ch === '(') {
57
+ depth++;
58
+ current += ch;
59
+ } else if (ch === ')') {
60
+ depth--;
61
+ current += ch;
62
+ } else if (ch === ',' && depth === 0) {
63
+ parts.push(current.trim());
64
+ current = '';
65
+ } else {
66
+ current += ch;
67
+ }
68
+ }
69
+ } catch (err) {
70
+ _iterator.e(err);
71
+ } finally {
72
+ _iterator.f();
73
+ }
74
+ if (current.trim().length > 0) {
75
+ parts.push(current.trim());
76
+ }
77
+ return parts;
78
+ };
79
+ var parseTransition = function parseTransition(value) {
80
+ var parts = tokenizeShorthand(value.trim());
81
+ var result = {};
82
+ var durationCount = 0;
83
+ var _iterator2 = _createForOfIteratorHelper(parts),
84
+ _step2;
85
+ try {
86
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
87
+ var part = _step2.value;
88
+ if (isDuration(part)) {
89
+ if (durationCount === 0) {
90
+ result.transitionDuration = part;
91
+ } else {
92
+ result.transitionDelay = part;
93
+ }
94
+ durationCount++;
95
+ } else if (isEasing(part)) {
96
+ result.transitionTimingFunction = part;
97
+ } else {
98
+ result.transitionProperty = part;
99
+ }
100
+ }
101
+ } catch (err) {
102
+ _iterator2.e(err);
103
+ } finally {
104
+ _iterator2.f();
105
+ }
106
+ return result;
107
+ };
108
+ var parseAnimation = function parseAnimation(value) {
109
+ var parts = tokenizeShorthand(value.trim());
110
+ var result = {};
111
+ var durationCount = 0;
112
+ var _iterator3 = _createForOfIteratorHelper(parts),
113
+ _step3;
114
+ try {
115
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
116
+ var part = _step3.value;
117
+ if (isDuration(part)) {
118
+ if (durationCount === 0) {
119
+ result.animationDuration = part;
120
+ } else {
121
+ result.animationDelay = part;
122
+ }
123
+ durationCount++;
124
+ } else if (isEasing(part)) {
125
+ result.animationTimingFunction = part;
126
+ } else if (part === 'infinite' || /^\d+(\.\d+)?$/.test(part)) {
127
+ result.animationIterationCount = part;
128
+ } else if (['normal', 'reverse', 'alternate', 'alternate-reverse'].includes(part)) {
129
+ result.animationDirection = part;
130
+ } else if (['none', 'forwards', 'backwards', 'both'].includes(part)) {
131
+ result.animationFillMode = part;
132
+ } else if (['running', 'paused'].includes(part)) {
133
+ result.animationPlayState = part;
134
+ } else {
135
+ result.animationName = part;
136
+ }
137
+ }
138
+ } catch (err) {
139
+ _iterator3.e(err);
140
+ } finally {
141
+ _iterator3.f();
142
+ }
143
+ return result;
144
+ };
145
+
146
+ // Combine sub-property values across comma-separated transitions/animations.
147
+ // If no segment explicitly set this sub-property, omit it entirely.
148
+ // Otherwise, fill missing slots with the CSS spec default to preserve positional alignment.
149
+ var combineSubPropertyValues = function combineSubPropertyValues(segments, subProperty, defaultValue) {
150
+ if (segments.every(function (s) {
151
+ return s[subProperty] === undefined;
152
+ })) {
153
+ return undefined;
154
+ }
155
+ return segments.map(function (s) {
156
+ var _s$subProperty;
157
+ return (_s$subProperty = s[subProperty]) !== null && _s$subProperty !== void 0 ? _s$subProperty : defaultValue;
158
+ }).join(', ');
159
+ };
160
+ var buildTransitionFix = function buildTransitionFix(segments, indent) {
161
+ var lines = [];
162
+ var property = combineSubPropertyValues(segments, 'transitionProperty', 'all');
163
+ var duration = combineSubPropertyValues(segments, 'transitionDuration', '0s');
164
+ var timing = combineSubPropertyValues(segments, 'transitionTimingFunction', 'ease');
165
+ var delay = combineSubPropertyValues(segments, 'transitionDelay', '0s');
166
+ if (property !== undefined) lines.push("transitionProperty: '".concat(property, "'"));
167
+ if (duration !== undefined) lines.push("transitionDuration: '".concat(duration, "'"));
168
+ if (timing !== undefined) lines.push("transitionTimingFunction: '".concat(timing, "'"));
169
+ if (delay !== undefined) lines.push("transitionDelay: '".concat(delay, "'"));
170
+ return lines.join(",\n".concat(indent));
171
+ };
172
+ var buildAnimationFix = function buildAnimationFix(segments, indent) {
173
+ var lines = [];
174
+ var name = combineSubPropertyValues(segments, 'animationName', 'none');
175
+ var duration = combineSubPropertyValues(segments, 'animationDuration', '0s');
176
+ var timing = combineSubPropertyValues(segments, 'animationTimingFunction', 'ease');
177
+ var delay = combineSubPropertyValues(segments, 'animationDelay', '0s');
178
+ var iter = combineSubPropertyValues(segments, 'animationIterationCount', '1');
179
+ var direction = combineSubPropertyValues(segments, 'animationDirection', 'normal');
180
+ var fill = combineSubPropertyValues(segments, 'animationFillMode', 'none');
181
+ var playState = combineSubPropertyValues(segments, 'animationPlayState', 'running');
182
+ if (name !== undefined) lines.push("animationName: '".concat(name, "'"));
183
+ if (duration !== undefined) lines.push("animationDuration: '".concat(duration, "'"));
184
+ if (timing !== undefined) lines.push("animationTimingFunction: '".concat(timing, "'"));
185
+ if (delay !== undefined) lines.push("animationDelay: '".concat(delay, "'"));
186
+ if (iter !== undefined) lines.push("animationIterationCount: '".concat(iter, "'"));
187
+ if (direction !== undefined) lines.push("animationDirection: '".concat(direction, "'"));
188
+ if (fill !== undefined) lines.push("animationFillMode: '".concat(fill, "'"));
189
+ if (playState !== undefined) lines.push("animationPlayState: '".concat(playState, "'"));
190
+ return lines.join(",\n".concat(indent));
191
+ };
192
+ var TRANSITION_SUB_PROPERTIES = ['transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay'];
193
+ var ANIMATION_SUB_PROPERTIES = ['animationName', 'animationDuration', 'animationTimingFunction', 'animationDelay', 'animationIterationCount', 'animationDirection', 'animationFillMode', 'animationPlayState'];
194
+ var executeExpandTransitionRule = function executeExpandTransitionRule(context, node, property) {
195
+ var _context$sourceCode, _node$loc;
196
+ if (node.value.type === 'CallExpression') {
197
+ return;
198
+ }
199
+ if (node.value.type === 'TemplateLiteral') {
200
+ return;
201
+ }
202
+ if (node.value.type !== 'Literal' || typeof node.value.value !== 'string') {
203
+ return;
204
+ }
205
+ var rawValue = node.value.value;
206
+ if (KEYWORD_VALUES.includes(rawValue)) {
207
+ return;
208
+ }
209
+ var subProperties = property === 'transition' ? TRANSITION_SUB_PROPERTIES : ANIMATION_SUB_PROPERTIES;
210
+
211
+ // Extract leading whitespace to preserve indentation style (tabs vs spaces)
212
+ var sourceCode = (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode();
213
+ var nodeStart = (_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start;
214
+ var indent = '\t';
215
+ if (nodeStart) {
216
+ var _sourceCode$lines;
217
+ var lineText = (_sourceCode$lines = sourceCode.lines[nodeStart.line - 1]) !== null && _sourceCode$lines !== void 0 ? _sourceCode$lines : '';
218
+ var leadingWhitespace = lineText.match(/^(\s*)/);
219
+ if (leadingWhitespace) {
220
+ indent = leadingWhitespace[1];
221
+ }
222
+ }
223
+ var segmentStrings = splitOnTopLevelCommas(rawValue);
224
+ if (property === 'transition') {
225
+ var segments = segmentStrings.map(parseTransition);
226
+ var fixText = buildTransitionFix(segments, indent);
227
+ context.report({
228
+ node: node,
229
+ messageId: 'expandTransitionShorthand',
230
+ data: {
231
+ property: property,
232
+ subProperties: subProperties.join(', ')
233
+ },
234
+ fix: function fix(fixer) {
235
+ return fixer.replaceText(node, fixText);
236
+ }
237
+ });
238
+ } else {
239
+ var _segments = segmentStrings.map(parseAnimation);
240
+ var _fixText = buildAnimationFix(_segments, indent);
241
+ context.report({
242
+ node: node,
243
+ messageId: 'expandTransitionShorthand',
244
+ data: {
245
+ property: property,
246
+ subProperties: subProperties.join(', ')
247
+ },
248
+ fix: function fix(fixer) {
249
+ return fixer.replaceText(node, _fixText);
250
+ }
251
+ });
252
+ }
253
+ };
254
+ var rule = createLintRule({
255
+ meta: {
256
+ name: 'expand-motion-shorthand',
257
+ type: 'suggestion',
258
+ fixable: 'code',
259
+ docs: {
260
+ description: 'Expands `transition` and `animation` CSS shorthand properties into their individual sub-properties, so individual values can be replaced with motion tokens.',
261
+ recommended: false,
262
+ severity: 'warn'
263
+ },
264
+ messages: {
265
+ expandTransitionShorthand: "Use {{ subProperties }} instead of the '{{ property }}' shorthand so that individual values can be replaced with motion tokens."
266
+ }
267
+ },
268
+ create: function create(context) {
269
+ return {
270
+ 'Property[key.name="transition"]': function PropertyKeyNameTransition(node) {
271
+ executeExpandTransitionRule(context, node, 'transition');
272
+ },
273
+ 'Property[key.name="animation"]': function PropertyKeyNameAnimation(node) {
274
+ executeExpandTransitionRule(context, node, 'animation');
275
+ }
276
+ };
277
+ }
278
+ });
279
+ 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::d527850500d4518cc654415fd5f4fbc0>>
3
+ * @codegen <<SignedSource::56038bd879a1d1c73de3addd961ade97>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -11,6 +11,7 @@ import ensureDesignTokenUsage from './ensure-design-token-usage';
11
11
  import ensureDesignTokenUsagePreview from './ensure-design-token-usage-preview';
12
12
  import ensureIconColor from './ensure-icon-color';
13
13
  import ensureProperXcssUsage from './ensure-proper-xcss-usage';
14
+ import expandMotionShorthand from './expand-motion-shorthand';
14
15
  import iconLabel from './icon-label';
15
16
  import lozengeBadgeTagLabellingSystemMigration from './lozenge-badge-tag-labelling-system-migration';
16
17
  import noBannedImports from './no-banned-imports';
@@ -90,6 +91,7 @@ export var rules = {
90
91
  'ensure-design-token-usage/preview': ensureDesignTokenUsagePreview,
91
92
  'ensure-icon-color': ensureIconColor,
92
93
  'ensure-proper-xcss-usage': ensureProperXcssUsage,
94
+ 'expand-motion-shorthand': expandMotionShorthand,
93
95
  'icon-label': iconLabel,
94
96
  'lozenge-badge-tag-labelling-system-migration': lozengeBadgeTagLabellingSystemMigration,
95
97
  'no-banned-imports': noBannedImports,
@@ -1,3 +1,11 @@
1
+ /* eslint-disable
2
+ @atlaskit/design-system/no-to-match-snapshot,
3
+ @atlaskit/design-system/no-unsafe-inline-snapshot
4
+ -- TODO(IND-4952): existing snapshot tests will be removed in a follow-up cleanup PR.
5
+ See https://hello.atlassian.net/wiki/spaces/afm/pages/7146174189/LDR+Unit+Tests+-+Ban+Snapshot+tests+in+Platform
6
+ and raise concerns in https://atlassian.enterprise.slack.com/archives/C0BD4K40BLH
7
+ */
8
+
1
9
  import { isNodeOfType } from 'eslint-codemod-utils';
2
10
  import { createLintRule } from '../utils/create-lint-rule';
3
11
  export var name = 'no-to-match-snapshot';
@@ -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::5a52fa9f987063b4d0d9e825b02a6067>>
3
+ * @codegen <<SignedSource::22c8677c3fc921ba33acd505671124a2>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Linter } from 'eslint';
@@ -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::458994db7ee4b74c7fcbd2157dc4496e>>
3
+ * @codegen <<SignedSource::2e8702ed4d2cd2902fed7fc5a895ea6e>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { ESLint } from 'eslint';
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ 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::d527850500d4518cc654415fd5f4fbc0>>
3
+ * @codegen <<SignedSource::56038bd879a1d1c73de3addd961ade97>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import type { Rule } from 'eslint';
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": "15.3.0",
4
+ "version": "15.5.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@atlaskit/eslint-utils": "^3.0.0",
34
- "@atlaskit/icon": "^36.1.0",
35
- "@atlaskit/icon-lab": "^7.2.0",
36
- "@atlaskit/tokens": "^15.3.0",
34
+ "@atlaskit/icon": "^36.2.0",
35
+ "@atlaskit/icon-lab": "^7.3.0",
36
+ "@atlaskit/tokens": "^15.4.0",
37
37
  "@babel/runtime": "^7.0.0",
38
38
  "@typescript-eslint/utils": "^7.1.0",
39
39
  "ajv": "^6.12.6",