@atlaskit/eslint-plugin-design-system 13.31.0 → 13.33.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 (46) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +76 -75
  3. package/dist/cjs/presets/all-flat.codegen.js +3 -2
  4. package/dist/cjs/presets/all.codegen.js +3 -2
  5. package/dist/cjs/presets/recommended-flat.codegen.js +3 -2
  6. package/dist/cjs/presets/recommended.codegen.js +3 -2
  7. package/dist/cjs/rules/index.codegen.js +5 -3
  8. package/dist/cjs/rules/no-placeholder/index.js +162 -0
  9. package/dist/cjs/rules/use-simple-field/index.js +138 -0
  10. package/dist/es2019/presets/all-flat.codegen.js +3 -2
  11. package/dist/es2019/presets/all.codegen.js +3 -2
  12. package/dist/es2019/presets/recommended-flat.codegen.js +3 -2
  13. package/dist/es2019/presets/recommended.codegen.js +3 -2
  14. package/dist/es2019/rules/index.codegen.js +5 -3
  15. package/dist/es2019/rules/no-placeholder/index.js +142 -0
  16. package/dist/es2019/rules/use-simple-field/index.js +124 -0
  17. package/dist/esm/presets/all-flat.codegen.js +3 -2
  18. package/dist/esm/presets/all.codegen.js +3 -2
  19. package/dist/esm/presets/recommended-flat.codegen.js +3 -2
  20. package/dist/esm/presets/recommended.codegen.js +3 -2
  21. package/dist/esm/rules/index.codegen.js +5 -3
  22. package/dist/esm/rules/no-placeholder/index.js +154 -0
  23. package/dist/esm/rules/use-simple-field/index.js +132 -0
  24. package/dist/types/presets/all-flat.codegen.d.ts +1 -1
  25. package/dist/types/presets/all.codegen.d.ts +1 -1
  26. package/dist/types/presets/recommended-flat.codegen.d.ts +1 -1
  27. package/dist/types/presets/recommended.codegen.d.ts +1 -1
  28. package/dist/types/rules/index.codegen.d.ts +1 -1
  29. package/dist/types/rules/no-placeholder/index.d.ts +6 -0
  30. package/dist/types/rules/{no-utility-icons → use-simple-field}/index.d.ts +1 -0
  31. package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -1
  32. package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -1
  33. package/dist/types-ts4.5/presets/recommended-flat.codegen.d.ts +1 -1
  34. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +1 -1
  35. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
  36. package/dist/types-ts4.5/rules/no-placeholder/index.d.ts +6 -0
  37. package/dist/types-ts4.5/rules/{no-utility-icons → use-simple-field}/index.d.ts +1 -0
  38. package/package.json +2 -2
  39. package/dist/cjs/rules/no-utility-icons/checks.js +0 -246
  40. package/dist/cjs/rules/no-utility-icons/index.js +0 -49
  41. package/dist/es2019/rules/no-utility-icons/checks.js +0 -177
  42. package/dist/es2019/rules/no-utility-icons/index.js +0 -44
  43. package/dist/esm/rules/no-utility-icons/checks.js +0 -239
  44. package/dist/esm/rules/no-utility-icons/index.js +0 -43
  45. package/dist/types/rules/no-utility-icons/checks.d.ts +0 -10
  46. package/dist/types-ts4.5/rules/no-utility-icons/checks.d.ts +0 -10
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = exports.ATLASKIT_FORM_PACKAGE = exports.ATLASKIT_FIELD_IMPORT = exports.AFFECTED_HTML_ELEMENTS = exports.AFFECTED_ATLASKIT_PACKAGES = void 0;
8
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
9
+ var ast = _interopRequireWildcard(require("../../ast-nodes"));
10
+ var _createRule = require("../utils/create-rule");
11
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
12
+ var AFFECTED_HTML_ELEMENTS = exports.AFFECTED_HTML_ELEMENTS = ['input', 'textarea'];
13
+ var AFFECTED_ATLASKIT_PACKAGES = exports.AFFECTED_ATLASKIT_PACKAGES = {
14
+ '@atlaskit/datetime-picker': ['DatePicker', 'TimePicker', 'DateTimePicker'],
15
+ '@atlaskit/select': ['default', 'AsyncCreatableSelect', 'CheckboxSelect', 'CountrySelect', 'CreatableSelect', 'PopupSelect', 'RadioSelect'],
16
+ '@atlaskit/textarea': ['default'],
17
+ '@atlaskit/textfield': ['default']
18
+ };
19
+ var ATLASKIT_FORM_PACKAGE = exports.ATLASKIT_FORM_PACKAGE = '@atlaskit/form';
20
+ var ATLASKIT_FIELD_IMPORT = exports.ATLASKIT_FIELD_IMPORT = 'Field';
21
+ var rule = (0, _createRule.createLintRule)({
22
+ meta: {
23
+ name: 'no-placeholder',
24
+ type: 'problem',
25
+ docs: {
26
+ description: 'Placeholders should not be used. If information should be given to the user about the proper type or formatting of a value, this should be included using a helper message that is associated to the input instead.',
27
+ recommended: true,
28
+ severity: 'warn'
29
+ },
30
+ messages: {
31
+ noPlaceholder: 'Placeholders should not be used. Use separate information to help users associated using `aria-describedby` instead.',
32
+ noPlaceholderOnSimpleField: 'Placeholders should not be used. Use the `helperMessage` prop instead.',
33
+ noPlaceholderOnComplexField: 'Placeholders should not be used. Use the `HelperMessage` component instead.'
34
+ }
35
+ },
36
+ create: function create(context) {
37
+ var hasFieldImport = false;
38
+ var fieldLocalName = null;
39
+ var localComponentNames = [];
40
+ return {
41
+ ImportDeclaration: function ImportDeclaration(node) {
42
+ var source = node.source.value;
43
+ if (typeof source !== 'string') {
44
+ return;
45
+ }
46
+
47
+ // Ignore non-atlaskit input/form packages
48
+ if (!Object.keys(AFFECTED_ATLASKIT_PACKAGES).includes(source) && ATLASKIT_FORM_PACKAGE !== source) {
49
+ return;
50
+ }
51
+ if (!node.specifiers.length) {
52
+ return;
53
+ }
54
+ var defaultImport = node.specifiers.filter(function (spec) {
55
+ return spec.type === 'ImportDefaultSpecifier';
56
+ });
57
+ var namedImport = node.specifiers.filter(function (spec) {
58
+ return spec.type === 'ImportSpecifier';
59
+ });
60
+ if (source === ATLASKIT_FORM_PACKAGE) {
61
+ if (namedImport.length && namedImport[0].type === 'ImportSpecifier' && 'name' in namedImport[0].imported && namedImport[0].imported.name === ATLASKIT_FIELD_IMPORT) {
62
+ hasFieldImport = true;
63
+ fieldLocalName = namedImport[0].local.name;
64
+ }
65
+ } else {
66
+ var importNames = AFFECTED_ATLASKIT_PACKAGES[source];
67
+ var usesDefaultImport = importNames.includes('default');
68
+ var possibleNamedImports = importNames.filter(function (importName) {
69
+ return importName !== 'default';
70
+ });
71
+ if (usesDefaultImport && defaultImport.length && defaultImport[0].local) {
72
+ localComponentNames.push(defaultImport[0].local.name);
73
+ // or if popup and using a named import
74
+ } else if (possibleNamedImports.length >= 1 && namedImport.length) {
75
+ namedImport.forEach(function (imp) {
76
+ if (imp.type === 'ImportSpecifier' && 'name' in imp.imported && possibleNamedImports.includes(imp.imported.name)) {
77
+ localComponentNames.push(imp.local.name);
78
+ }
79
+ });
80
+ }
81
+ }
82
+ },
83
+ JSXElement: function JSXElement(node) {
84
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
85
+ return false;
86
+ }
87
+ var elName = ast.JSXElement.getName(node);
88
+ if (!elName) {
89
+ return false;
90
+ }
91
+
92
+ // If it is one of the affected native HTML elements
93
+ if (AFFECTED_HTML_ELEMENTS.includes(elName)) {
94
+ // if has a placeholder attribute
95
+ var hasPlaceholderAttribute = node.openingElement.attributes.find(function (attr) {
96
+ return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && attr.name.name === 'placeholder';
97
+ });
98
+ if (hasPlaceholderAttribute) {
99
+ return context.report({
100
+ node: node.openingElement,
101
+ messageId: 'noPlaceholder'
102
+ });
103
+ }
104
+ // Else, it is a React component
105
+ } else {
106
+ // if none of the affected packages is imported, return
107
+ if (localComponentNames.length === 0) {
108
+ return;
109
+ }
110
+ // if component name is not in the list, exit
111
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.openingElement.name, 'JSXIdentifier') || !localComponentNames.includes(node.openingElement.name.name)) {
112
+ return;
113
+ }
114
+ if (hasFieldImport && fieldLocalName) {
115
+ var _node = node;
116
+ var hasParentField = false;
117
+ var fieldType = 'Complex';
118
+ // If node is a Field element or if
119
+ while ((0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXElement') && (0, _eslintCodemodUtils.isNodeOfType)(_node.openingElement.name, 'JSXIdentifier') && !hasParentField) {
120
+ var name = _node.openingElement.name.name;
121
+ hasParentField = hasParentField || name === fieldLocalName;
122
+ _node = _node.parent;
123
+ // Skip up until a JSXElement or JSXAttribute is reached
124
+ if ((0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXFragment') || (0, _eslintCodemodUtils.isNodeOfType)(_node, 'ArrowFunctionExpression') || (0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXExpressionContainer') || (0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXAttribute')) {
125
+ while (_node && !(0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXElement') && !(0, _eslintCodemodUtils.isNodeOfType)(_node, 'Program')) {
126
+ if ((0, _eslintCodemodUtils.isNodeOfType)(_node, 'JSXAttribute')) {
127
+ fieldType = 'Simple';
128
+ }
129
+ _node = _node.parent;
130
+ }
131
+ }
132
+ }
133
+ if (hasParentField) {
134
+ // if has a placeholder attribute
135
+ var _hasPlaceholderAttribute = node.openingElement.attributes.find(function (attr) {
136
+ return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && attr.name.name === 'placeholder';
137
+ });
138
+ if (_hasPlaceholderAttribute) {
139
+ return context.report({
140
+ node: node.openingElement,
141
+ messageId: "noPlaceholderOn".concat(fieldType, "Field")
142
+ });
143
+ }
144
+ }
145
+ } else {
146
+ // if has a placeholder attribute
147
+ var _hasPlaceholderAttribute2 = node.openingElement.attributes.find(function (attr) {
148
+ return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && attr.name.name === 'placeholder';
149
+ });
150
+ if (_hasPlaceholderAttribute2) {
151
+ return context.report({
152
+ node: node.openingElement,
153
+ messageId: 'noPlaceholder'
154
+ });
155
+ }
156
+ }
157
+ }
158
+ }
159
+ };
160
+ }
161
+ });
162
+ var _default = exports.default = rule;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.convertField = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ var _createRule = require("../utils/create-rule");
9
+ var FORM_PACKAGE = '@atlaskit/form';
10
+ var MESSAGE_COMPONENTS = ['ErrorMessage', 'HelperMessage', 'ValidMessage'];
11
+ var convertField = exports.convertField = 'Convert field to simple field';
12
+ var rule = (0, _createRule.createLintRule)({
13
+ meta: {
14
+ name: 'use-simple-field',
15
+ type: 'suggestion',
16
+ hasSuggestions: true,
17
+ docs: {
18
+ description: 'Encourage use of simple field for better developer experience and accessibility.',
19
+ recommended: true,
20
+ severity: 'warn'
21
+ },
22
+ messages: {
23
+ useSimpleField: 'The simplified field implementation can be used here.'
24
+ }
25
+ },
26
+ create: function create(context) {
27
+ var fieldImport;
28
+ var messageImports = [];
29
+ return {
30
+ ImportDeclaration: function ImportDeclaration(node) {
31
+ var source = node.source.value;
32
+
33
+ // Ignore anomalies
34
+ if (typeof source !== 'string') {
35
+ return;
36
+ }
37
+ if (!node.specifiers.length) {
38
+ return;
39
+ }
40
+
41
+ // If it's not from our package, ignore.
42
+ if (source !== FORM_PACKAGE) {
43
+ return;
44
+ }
45
+ var namedImportSpecifiers = node.specifiers.filter(function (spec) {
46
+ return (0, _eslintCodemodUtils.isNodeOfType)(spec, 'ImportSpecifier');
47
+ });
48
+ namedImportSpecifiers.forEach(function (spec) {
49
+ if (spec.type === 'ImportSpecifier' && 'name' in spec.imported) {
50
+ var name = spec.imported.name;
51
+ var local = spec.local;
52
+ if (MESSAGE_COMPONENTS.includes(name)) {
53
+ messageImports.push(local);
54
+ } else if (name === 'Field') {
55
+ fieldImport = local.name;
56
+ }
57
+ }
58
+ });
59
+ },
60
+ JSXElement: function JSXElement(node) {
61
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
62
+ return;
63
+ }
64
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.openingElement.name, 'JSXIdentifier')) {
65
+ return;
66
+ }
67
+
68
+ // if no field import exists, skip
69
+ if (!fieldImport) {
70
+ return;
71
+ }
72
+
73
+ // if component is not field, skip
74
+ if (node.openingElement.name.name !== fieldImport) {
75
+ return;
76
+ }
77
+
78
+ // if message imports exist, skip because we can't really dig around
79
+ // inside the field to see if they exist because of memory constraints
80
+ if (messageImports.length > 0) {
81
+ return;
82
+ }
83
+
84
+ // if component exists as a prop, skip because it's already simple
85
+ var attributes = node.openingElement.attributes;
86
+ var componentAttr = attributes.find(function (attr) {
87
+ return (0, _eslintCodemodUtils.isNodeOfType)(attr, 'JSXAttribute') && (0, _eslintCodemodUtils.isNodeOfType)(attr.name, 'JSXIdentifier') && attr.name.name === 'component';
88
+ });
89
+ if (componentAttr) {
90
+ return;
91
+ }
92
+
93
+ // if children is not a render prop, skip
94
+ var renderProps = node.children.find(function (child) {
95
+ return (0, _eslintCodemodUtils.isNodeOfType)(child, 'JSXExpressionContainer');
96
+ });
97
+ if (!renderProps) {
98
+ return;
99
+ }
100
+ var renderPropExpression = renderProps.expression;
101
+ // If not an arrow func, ignore
102
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(renderPropExpression, 'ArrowFunctionExpression')) {
103
+ return;
104
+ }
105
+ var renderPropParams = renderPropExpression.params;
106
+ // if it is not an object pattern, skip
107
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(renderPropParams[0], 'ObjectPattern')) {
108
+ return;
109
+ }
110
+ // if component uses more than just fieldProps in render props, skip
111
+ var renderPropArgProperties = renderPropParams[0].properties;
112
+ var fieldPropsProp = renderPropArgProperties.find(function (property) {
113
+ return (0, _eslintCodemodUtils.isNodeOfType)(property, 'Property') && (0, _eslintCodemodUtils.isNodeOfType)(property.key, 'Identifier') && property.key.name === 'fieldProps';
114
+ });
115
+ if (renderPropArgProperties.length > 1 || !fieldPropsProp) {
116
+ return;
117
+ }
118
+ var lastProp = attributes.slice(-1)[0] || node.openingElement.name;
119
+ var sourceCode = context.sourceCode;
120
+ var renderPropsText = sourceCode.getText(renderProps);
121
+ context.report({
122
+ node: node,
123
+ messageId: 'useSimpleField',
124
+ suggest: [{
125
+ desc: convertField,
126
+ fix: function fix(fixer) {
127
+ var fixes = [];
128
+ fixes.push(fixer.insertTextAfter(lastProp, " component=".concat(renderPropsText, " ")));
129
+ fixes.push(fixer.remove(renderProps));
130
+ return fixes;
131
+ }
132
+ }]
133
+ });
134
+ }
135
+ };
136
+ }
137
+ });
138
+ 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::23364c0ee38a3772c2a588841ed22c55>>
3
+ * @codegen <<SignedSource::05e8a8ff80cb4a88a89ccd31570686f6>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -45,6 +45,7 @@ const rules = {
45
45
  '@atlaskit/design-system/no-margin': 'warn',
46
46
  '@atlaskit/design-system/no-nested-styles': 'error',
47
47
  '@atlaskit/design-system/no-physical-properties': 'error',
48
+ '@atlaskit/design-system/no-placeholder': 'warn',
48
49
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
49
50
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
50
51
  '@atlaskit/design-system/no-to-match-snapshot': 'error',
@@ -53,7 +54,6 @@ const rules = {
53
54
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
54
55
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
55
56
  '@atlaskit/design-system/no-unused-css-map': 'warn',
56
- '@atlaskit/design-system/no-utility-icons': 'warn',
57
57
  '@atlaskit/design-system/prefer-primitives': 'warn',
58
58
  '@atlaskit/design-system/use-button-group-label': 'warn',
59
59
  '@atlaskit/design-system/use-character-counter-field': 'warn',
@@ -74,6 +74,7 @@ const rules = {
74
74
  '@atlaskit/design-system/use-primitives': 'warn',
75
75
  '@atlaskit/design-system/use-primitives-text': 'warn',
76
76
  '@atlaskit/design-system/use-should-render-to-parent': 'warn',
77
+ '@atlaskit/design-system/use-simple-field': 'warn',
77
78
  '@atlaskit/design-system/use-spotlight-package': 'warn',
78
79
  '@atlaskit/design-system/use-tag-group-label': 'warn',
79
80
  '@atlaskit/design-system/use-tokens-shape': '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::760141a986e4947e8a1ce74480931f43>>
3
+ * @codegen <<SignedSource::f2a8bbab8acdaf84ca6e295124378517>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -44,6 +44,7 @@ const rules = {
44
44
  '@atlaskit/design-system/no-margin': 'warn',
45
45
  '@atlaskit/design-system/no-nested-styles': 'error',
46
46
  '@atlaskit/design-system/no-physical-properties': 'error',
47
+ '@atlaskit/design-system/no-placeholder': 'warn',
47
48
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
48
49
  '@atlaskit/design-system/no-styled-tagged-template-expression': 'error',
49
50
  '@atlaskit/design-system/no-to-match-snapshot': 'error',
@@ -52,7 +53,6 @@ const rules = {
52
53
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
53
54
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
54
55
  '@atlaskit/design-system/no-unused-css-map': 'warn',
55
- '@atlaskit/design-system/no-utility-icons': 'warn',
56
56
  '@atlaskit/design-system/prefer-primitives': 'warn',
57
57
  '@atlaskit/design-system/use-button-group-label': 'warn',
58
58
  '@atlaskit/design-system/use-character-counter-field': 'warn',
@@ -73,6 +73,7 @@ const rules = {
73
73
  '@atlaskit/design-system/use-primitives': 'warn',
74
74
  '@atlaskit/design-system/use-primitives-text': 'warn',
75
75
  '@atlaskit/design-system/use-should-render-to-parent': 'warn',
76
+ '@atlaskit/design-system/use-simple-field': 'warn',
76
77
  '@atlaskit/design-system/use-spotlight-package': 'warn',
77
78
  '@atlaskit/design-system/use-tag-group-label': 'warn',
78
79
  '@atlaskit/design-system/use-tokens-shape': '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::ba71ef89188bdc20d0ec948cc00f6436>>
3
+ * @codegen <<SignedSource::2ca5e0a897270a5cfe3e234d7948ed0f>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -32,12 +32,12 @@ const rules = {
32
32
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
33
33
  }],
34
34
  '@atlaskit/design-system/no-nested-styles': 'error',
35
+ '@atlaskit/design-system/no-placeholder': 'warn',
35
36
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
36
37
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
37
38
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
38
39
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
39
40
  '@atlaskit/design-system/no-unused-css-map': 'warn',
40
- '@atlaskit/design-system/no-utility-icons': 'warn',
41
41
  '@atlaskit/design-system/use-button-group-label': 'warn',
42
42
  '@atlaskit/design-system/use-character-counter-field': 'warn',
43
43
  '@atlaskit/design-system/use-correct-field': 'warn',
@@ -56,6 +56,7 @@ const rules = {
56
56
  '@atlaskit/design-system/use-popup-label': 'warn',
57
57
  '@atlaskit/design-system/use-primitives-text': 'warn',
58
58
  '@atlaskit/design-system/use-should-render-to-parent': 'warn',
59
+ '@atlaskit/design-system/use-simple-field': 'warn',
59
60
  '@atlaskit/design-system/use-tag-group-label': 'warn',
60
61
  '@atlaskit/design-system/use-tokens-typography': 'warn',
61
62
  '@atlaskit/design-system/use-visually-hidden': '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::5c119e0ab08e1f39d471b2b827bbb4de>>
3
+ * @codegen <<SignedSource::aee2986e6e48e70acef7dfa4ba53747d>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -31,12 +31,12 @@ const rules = {
31
31
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
32
32
  }],
33
33
  '@atlaskit/design-system/no-nested-styles': 'error',
34
+ '@atlaskit/design-system/no-placeholder': 'warn',
34
35
  '@atlaskit/design-system/no-separator-with-list-elements': 'warn',
35
36
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
36
37
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
37
38
  '@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
38
39
  '@atlaskit/design-system/no-unused-css-map': 'warn',
39
- '@atlaskit/design-system/no-utility-icons': 'warn',
40
40
  '@atlaskit/design-system/use-button-group-label': 'warn',
41
41
  '@atlaskit/design-system/use-character-counter-field': 'warn',
42
42
  '@atlaskit/design-system/use-correct-field': 'warn',
@@ -55,6 +55,7 @@ const rules = {
55
55
  '@atlaskit/design-system/use-popup-label': 'warn',
56
56
  '@atlaskit/design-system/use-primitives-text': 'warn',
57
57
  '@atlaskit/design-system/use-should-render-to-parent': 'warn',
58
+ '@atlaskit/design-system/use-simple-field': 'warn',
58
59
  '@atlaskit/design-system/use-tag-group-label': 'warn',
59
60
  '@atlaskit/design-system/use-tokens-typography': 'warn',
60
61
  '@atlaskit/design-system/use-visually-hidden': '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::7b80dfb4568d4c5f7c84d7c382b81de9>>
3
+ * @codegen <<SignedSource::030718cab57dce4d3b39031485e53af4>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -42,6 +42,7 @@ import noLegacyIcons from './no-legacy-icons';
42
42
  import noMargin from './no-margin';
43
43
  import noNestedStyles from './no-nested-styles';
44
44
  import noPhysicalProperties from './no-physical-properties';
45
+ import noPlaceholder from './no-placeholder';
45
46
  import noSeparatorWithListElements from './no-separator-with-list-elements';
46
47
  import noStyledTaggedTemplateExpression from './no-styled-tagged-template-expression';
47
48
  import noToMatchSnapshot from './no-to-match-snapshot';
@@ -50,7 +51,6 @@ import noUnsafeInlineSnapshot from './no-unsafe-inline-snapshot';
50
51
  import noUnsafeStyleOverrides from './no-unsafe-style-overrides';
51
52
  import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-libraries';
52
53
  import noUnusedCssMap from './no-unused-css-map';
53
- import noUtilityIcons from './no-utility-icons';
54
54
  import preferPrimitives from './prefer-primitives';
55
55
  import useButtonGroupLabel from './use-button-group-label';
56
56
  import useCharacterCounterField from './use-character-counter-field';
@@ -71,6 +71,7 @@ import usePopupLabel from './use-popup-label';
71
71
  import usePrimitives from './use-primitives';
72
72
  import usePrimitivesText from './use-primitives-text';
73
73
  import useShouldRenderToParent from './use-should-render-to-parent';
74
+ import useSimpleField from './use-simple-field';
74
75
  import useSpotlightPackage from './use-spotlight-package';
75
76
  import useTagGroupLabel from './use-tag-group-label';
76
77
  import useTokensShape from './use-tokens-shape';
@@ -116,6 +117,7 @@ export const rules = {
116
117
  'no-margin': noMargin,
117
118
  'no-nested-styles': noNestedStyles,
118
119
  'no-physical-properties': noPhysicalProperties,
120
+ 'no-placeholder': noPlaceholder,
119
121
  'no-separator-with-list-elements': noSeparatorWithListElements,
120
122
  'no-styled-tagged-template-expression': noStyledTaggedTemplateExpression,
121
123
  'no-to-match-snapshot': noToMatchSnapshot,
@@ -124,7 +126,6 @@ export const rules = {
124
126
  'no-unsafe-style-overrides': noUnsafeStyleOverrides,
125
127
  'no-unsupported-drag-and-drop-libraries': noUnsupportedDragAndDropLibraries,
126
128
  'no-unused-css-map': noUnusedCssMap,
127
- 'no-utility-icons': noUtilityIcons,
128
129
  'prefer-primitives': preferPrimitives,
129
130
  'use-button-group-label': useButtonGroupLabel,
130
131
  'use-character-counter-field': useCharacterCounterField,
@@ -145,6 +146,7 @@ export const rules = {
145
146
  'use-primitives': usePrimitives,
146
147
  'use-primitives-text': usePrimitivesText,
147
148
  'use-should-render-to-parent': useShouldRenderToParent,
149
+ 'use-simple-field': useSimpleField,
148
150
  'use-spotlight-package': useSpotlightPackage,
149
151
  'use-tag-group-label': useTagGroupLabel,
150
152
  'use-tokens-shape': useTokensShape,
@@ -0,0 +1,142 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import * as ast from '../../ast-nodes';
3
+ import { createLintRule } from '../utils/create-rule';
4
+ export const AFFECTED_HTML_ELEMENTS = ['input', 'textarea'];
5
+ export const AFFECTED_ATLASKIT_PACKAGES = {
6
+ '@atlaskit/datetime-picker': ['DatePicker', 'TimePicker', 'DateTimePicker'],
7
+ '@atlaskit/select': ['default', 'AsyncCreatableSelect', 'CheckboxSelect', 'CountrySelect', 'CreatableSelect', 'PopupSelect', 'RadioSelect'],
8
+ '@atlaskit/textarea': ['default'],
9
+ '@atlaskit/textfield': ['default']
10
+ };
11
+ export const ATLASKIT_FORM_PACKAGE = '@atlaskit/form';
12
+ export const ATLASKIT_FIELD_IMPORT = 'Field';
13
+ const rule = createLintRule({
14
+ meta: {
15
+ name: 'no-placeholder',
16
+ type: 'problem',
17
+ docs: {
18
+ description: 'Placeholders should not be used. If information should be given to the user about the proper type or formatting of a value, this should be included using a helper message that is associated to the input instead.',
19
+ recommended: true,
20
+ severity: 'warn'
21
+ },
22
+ messages: {
23
+ noPlaceholder: 'Placeholders should not be used. Use separate information to help users associated using `aria-describedby` instead.',
24
+ noPlaceholderOnSimpleField: 'Placeholders should not be used. Use the `helperMessage` prop instead.',
25
+ noPlaceholderOnComplexField: 'Placeholders should not be used. Use the `HelperMessage` component instead.'
26
+ }
27
+ },
28
+ create(context) {
29
+ let hasFieldImport = false;
30
+ let fieldLocalName = null;
31
+ const localComponentNames = [];
32
+ return {
33
+ ImportDeclaration(node) {
34
+ const source = node.source.value;
35
+ if (typeof source !== 'string') {
36
+ return;
37
+ }
38
+
39
+ // Ignore non-atlaskit input/form packages
40
+ if (!Object.keys(AFFECTED_ATLASKIT_PACKAGES).includes(source) && ATLASKIT_FORM_PACKAGE !== source) {
41
+ return;
42
+ }
43
+ if (!node.specifiers.length) {
44
+ return;
45
+ }
46
+ const defaultImport = node.specifiers.filter(spec => spec.type === 'ImportDefaultSpecifier');
47
+ const namedImport = node.specifiers.filter(spec => spec.type === 'ImportSpecifier');
48
+ if (source === ATLASKIT_FORM_PACKAGE) {
49
+ if (namedImport.length && namedImport[0].type === 'ImportSpecifier' && 'name' in namedImport[0].imported && namedImport[0].imported.name === ATLASKIT_FIELD_IMPORT) {
50
+ hasFieldImport = true;
51
+ fieldLocalName = namedImport[0].local.name;
52
+ }
53
+ } else {
54
+ const importNames = AFFECTED_ATLASKIT_PACKAGES[source];
55
+ const usesDefaultImport = importNames.includes('default');
56
+ const possibleNamedImports = importNames.filter(importName => importName !== 'default');
57
+ if (usesDefaultImport && defaultImport.length && defaultImport[0].local) {
58
+ localComponentNames.push(defaultImport[0].local.name);
59
+ // or if popup and using a named import
60
+ } else if (possibleNamedImports.length >= 1 && namedImport.length) {
61
+ namedImport.forEach(imp => {
62
+ if (imp.type === 'ImportSpecifier' && 'name' in imp.imported && possibleNamedImports.includes(imp.imported.name)) {
63
+ localComponentNames.push(imp.local.name);
64
+ }
65
+ });
66
+ }
67
+ }
68
+ },
69
+ JSXElement(node) {
70
+ if (!isNodeOfType(node, 'JSXElement')) {
71
+ return false;
72
+ }
73
+ const elName = ast.JSXElement.getName(node);
74
+ if (!elName) {
75
+ return false;
76
+ }
77
+
78
+ // If it is one of the affected native HTML elements
79
+ if (AFFECTED_HTML_ELEMENTS.includes(elName)) {
80
+ // if has a placeholder attribute
81
+ const hasPlaceholderAttribute = node.openingElement.attributes.find(attr => isNodeOfType(attr, 'JSXAttribute') && attr.name.name === 'placeholder');
82
+ if (hasPlaceholderAttribute) {
83
+ return context.report({
84
+ node: node.openingElement,
85
+ messageId: 'noPlaceholder'
86
+ });
87
+ }
88
+ // Else, it is a React component
89
+ } else {
90
+ // if none of the affected packages is imported, return
91
+ if (localComponentNames.length === 0) {
92
+ return;
93
+ }
94
+ // if component name is not in the list, exit
95
+ if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier') || !localComponentNames.includes(node.openingElement.name.name)) {
96
+ return;
97
+ }
98
+ if (hasFieldImport && fieldLocalName) {
99
+ let _node = node;
100
+ let hasParentField = false;
101
+ let fieldType = 'Complex';
102
+ // If node is a Field element or if
103
+ while (isNodeOfType(_node, 'JSXElement') && isNodeOfType(_node.openingElement.name, 'JSXIdentifier') && !hasParentField) {
104
+ const name = _node.openingElement.name.name;
105
+ hasParentField = hasParentField || name === fieldLocalName;
106
+ _node = _node.parent;
107
+ // Skip up until a JSXElement or JSXAttribute is reached
108
+ if (isNodeOfType(_node, 'JSXFragment') || isNodeOfType(_node, 'ArrowFunctionExpression') || isNodeOfType(_node, 'JSXExpressionContainer') || isNodeOfType(_node, 'JSXAttribute')) {
109
+ while (_node && !isNodeOfType(_node, 'JSXElement') && !isNodeOfType(_node, 'Program')) {
110
+ if (isNodeOfType(_node, 'JSXAttribute')) {
111
+ fieldType = 'Simple';
112
+ }
113
+ _node = _node.parent;
114
+ }
115
+ }
116
+ }
117
+ if (hasParentField) {
118
+ // if has a placeholder attribute
119
+ const hasPlaceholderAttribute = node.openingElement.attributes.find(attr => isNodeOfType(attr, 'JSXAttribute') && attr.name.name === 'placeholder');
120
+ if (hasPlaceholderAttribute) {
121
+ return context.report({
122
+ node: node.openingElement,
123
+ messageId: `noPlaceholderOn${fieldType}Field`
124
+ });
125
+ }
126
+ }
127
+ } else {
128
+ // if has a placeholder attribute
129
+ const hasPlaceholderAttribute = node.openingElement.attributes.find(attr => isNodeOfType(attr, 'JSXAttribute') && attr.name.name === 'placeholder');
130
+ if (hasPlaceholderAttribute) {
131
+ return context.report({
132
+ node: node.openingElement,
133
+ messageId: 'noPlaceholder'
134
+ });
135
+ }
136
+ }
137
+ }
138
+ }
139
+ };
140
+ }
141
+ });
142
+ export default rule;