@db-ux/core-eslint-plugin 0.0.0 → 4.4.2-eslint-plugin-28ea614
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 +1 -0
- package/README.md +754 -0
- package/build/index.d.ts +99 -0
- package/build/index.js +79 -0
- package/build/rules/accordion/no-nested-accordion.d.ts +5 -0
- package/build/rules/accordion/no-nested-accordion.js +37 -0
- package/build/rules/badge/badge-corner-placement-rules.d.ts +5 -0
- package/build/rules/badge/badge-corner-placement-rules.js +76 -0
- package/build/rules/badge/badge-no-inline-in-interactive.d.ts +5 -0
- package/build/rules/badge/badge-no-inline-in-interactive.js +67 -0
- package/build/rules/button/button-no-text-requires-tooltip.d.ts +5 -0
- package/build/rules/button/button-no-text-requires-tooltip.js +59 -0
- package/build/rules/button/button-single-icon-attribute.d.ts +5 -0
- package/build/rules/button/button-single-icon-attribute.js +35 -0
- package/build/rules/button/button-type-required.d.ts +5 -0
- package/build/rules/button/button-type-required.js +45 -0
- package/build/rules/close-button/close-button-text-required.d.ts +5 -0
- package/build/rules/close-button/close-button-text-required.js +40 -0
- package/build/rules/content/text-or-children-required.d.ts +5 -0
- package/build/rules/content/text-or-children-required.js +49 -0
- package/build/rules/form/form-label-required.d.ts +5 -0
- package/build/rules/form/form-label-required.js +47 -0
- package/build/rules/form/form-validation-message-required.d.ts +5 -0
- package/build/rules/form/form-validation-message-required.js +93 -0
- package/build/rules/header/header-burger-menu-label-required.d.ts +5 -0
- package/build/rules/header/header-burger-menu-label-required.js +32 -0
- package/build/rules/icon/prefer-icon-attribute.d.ts +5 -0
- package/build/rules/icon/prefer-icon-attribute.js +67 -0
- package/build/rules/input/input-file-type-validation.d.ts +5 -0
- package/build/rules/input/input-file-type-validation.js +52 -0
- package/build/rules/input/input-type-required.d.ts +5 -0
- package/build/rules/input/input-type-required.js +40 -0
- package/build/rules/link/link-external-security.d.ts +5 -0
- package/build/rules/link/link-external-security.js +50 -0
- package/build/rules/navigation/navigation-item-back-button-text-required.d.ts +5 -0
- package/build/rules/navigation/navigation-item-back-button-text-required.js +32 -0
- package/build/rules/select/custom-select-tags-remove-text-required.d.ts +5 -0
- package/build/rules/select/custom-select-tags-remove-text-required.js +35 -0
- package/build/rules/select/select-requires-options.d.ts +5 -0
- package/build/rules/select/select-requires-options.js +44 -0
- package/build/rules/tag/tag-removable-remove-button-required.d.ts +5 -0
- package/build/rules/tag/tag-removable-remove-button-required.js +35 -0
- package/build/rules/tooltip/no-interactive-tooltip-content.d.ts +5 -0
- package/build/rules/tooltip/no-interactive-tooltip-content.js +47 -0
- package/build/rules/tooltip/tooltip-requires-interactive-parent.d.ts +5 -0
- package/build/rules/tooltip/tooltip-requires-interactive-parent.js +47 -0
- package/build/shared/utils.d.ts +5 -0
- package/build/shared/utils.js +61 -0
- package/package.json +32 -1
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
declare const plugin: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
rules: {
|
|
7
|
+
'button-no-text-requires-tooltip': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingIcon" | "missingTooltip", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
'button-type-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingType", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
'button-single-icon-attribute': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"multipleIcons", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
'form-label-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
'form-validation-message-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingInvalidMessage", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
20
|
+
name: string;
|
|
21
|
+
};
|
|
22
|
+
'prefer-icon-attribute': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"preferAttribute", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
'text-or-children-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingContent", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
28
|
+
'no-interactive-tooltip-content': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noInteractive", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
'tooltip-requires-interactive-parent': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"requiresInteractive", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
'no-nested-accordion': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noNested", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
35
|
+
name: string;
|
|
36
|
+
};
|
|
37
|
+
'badge-corner-placement-rules': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"textTooLong" | "missingLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
'badge-no-inline-in-interactive': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"noInline", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
41
|
+
name: string;
|
|
42
|
+
};
|
|
43
|
+
'link-external-security': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingTargetBlank" | "missingReferrerPolicy" | "missingContentExternal", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
44
|
+
name: string;
|
|
45
|
+
};
|
|
46
|
+
'select-requires-options': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingOptions", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
47
|
+
name: string;
|
|
48
|
+
};
|
|
49
|
+
'custom-select-tags-remove-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingRemoveTagsTexts", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
50
|
+
name: string;
|
|
51
|
+
};
|
|
52
|
+
'close-button-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingCloseButtonText", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
53
|
+
name: string;
|
|
54
|
+
};
|
|
55
|
+
'header-burger-menu-label-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingBurgerMenuLabel", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
56
|
+
name: string;
|
|
57
|
+
};
|
|
58
|
+
'navigation-item-back-button-text-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingBackButtonText", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
59
|
+
name: string;
|
|
60
|
+
};
|
|
61
|
+
'tag-removable-remove-button-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingRemoveButton", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
62
|
+
name: string;
|
|
63
|
+
};
|
|
64
|
+
'input-type-required': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingType", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
65
|
+
name: string;
|
|
66
|
+
};
|
|
67
|
+
'input-file-type-validation': import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleModule<"missingAccept" | "invalidMultiple" | "invalidAccept", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint/Rule.js").RuleListener> & {
|
|
68
|
+
name: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
configs: {
|
|
72
|
+
recommended: {
|
|
73
|
+
rules: {
|
|
74
|
+
'db-ux/button-no-text-requires-tooltip': string;
|
|
75
|
+
'db-ux/button-type-required': string;
|
|
76
|
+
'db-ux/button-single-icon-attribute': string;
|
|
77
|
+
'db-ux/form-label-required': string;
|
|
78
|
+
'db-ux/form-validation-message-required': string;
|
|
79
|
+
'db-ux/prefer-icon-attribute': string;
|
|
80
|
+
'db-ux/text-or-children-required': string;
|
|
81
|
+
'db-ux/no-interactive-tooltip-content': string;
|
|
82
|
+
'db-ux/tooltip-requires-interactive-parent': string;
|
|
83
|
+
'db-ux/no-nested-accordion': string;
|
|
84
|
+
'db-ux/badge-corner-placement-rules': string;
|
|
85
|
+
'db-ux/badge-no-inline-in-interactive': string;
|
|
86
|
+
'db-ux/link-external-security': string;
|
|
87
|
+
'db-ux/select-requires-options': string;
|
|
88
|
+
'db-ux/custom-select-tags-remove-text-required': string;
|
|
89
|
+
'db-ux/close-button-text-required': string;
|
|
90
|
+
'db-ux/header-burger-menu-label-required': string;
|
|
91
|
+
'db-ux/navigation-item-back-button-text-required': string;
|
|
92
|
+
'db-ux/tag-removable-remove-button-required': string;
|
|
93
|
+
'db-ux/input-type-required': string;
|
|
94
|
+
'db-ux/input-file-type-validation': string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export default plugin;
|
package/build/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import noNestedAccordion from './rules/accordion/no-nested-accordion.js';
|
|
2
|
+
import badgeCornerPlacementRules from './rules/badge/badge-corner-placement-rules.js';
|
|
3
|
+
import badgeNoInlineInInteractive from './rules/badge/badge-no-inline-in-interactive.js';
|
|
4
|
+
import buttonNoTextRequiresTooltip from './rules/button/button-no-text-requires-tooltip.js';
|
|
5
|
+
import buttonSingleIconAttribute from './rules/button/button-single-icon-attribute.js';
|
|
6
|
+
import buttonTypeRequired from './rules/button/button-type-required.js';
|
|
7
|
+
import closeButtonTextRequired from './rules/close-button/close-button-text-required.js';
|
|
8
|
+
import textOrChildrenRequired from './rules/content/text-or-children-required.js';
|
|
9
|
+
import formLabelRequired from './rules/form/form-label-required.js';
|
|
10
|
+
import formValidationMessageRequired from './rules/form/form-validation-message-required.js';
|
|
11
|
+
import headerBurgerMenuLabelRequired from './rules/header/header-burger-menu-label-required.js';
|
|
12
|
+
import preferIconAttribute from './rules/icon/prefer-icon-attribute.js';
|
|
13
|
+
import inputFileTypeValidation from './rules/input/input-file-type-validation.js';
|
|
14
|
+
import inputTypeRequired from './rules/input/input-type-required.js';
|
|
15
|
+
import linkExternalSecurity from './rules/link/link-external-security.js';
|
|
16
|
+
import navigationItemBackButtonTextRequired from './rules/navigation/navigation-item-back-button-text-required.js';
|
|
17
|
+
import customSelectTagsRemoveTextRequired from './rules/select/custom-select-tags-remove-text-required.js';
|
|
18
|
+
import selectRequiresOptions from './rules/select/select-requires-options.js';
|
|
19
|
+
import tagRemovableRemoveButtonRequired from './rules/tag/tag-removable-remove-button-required.js';
|
|
20
|
+
import noInteractiveTooltipContent from './rules/tooltip/no-interactive-tooltip-content.js';
|
|
21
|
+
import tooltipRequiresInteractiveParent from './rules/tooltip/tooltip-requires-interactive-parent.js';
|
|
22
|
+
const recommended = {
|
|
23
|
+
rules: {
|
|
24
|
+
'db-ux/button-no-text-requires-tooltip': 'error',
|
|
25
|
+
'db-ux/button-type-required': 'error',
|
|
26
|
+
'db-ux/button-single-icon-attribute': 'error',
|
|
27
|
+
'db-ux/form-label-required': 'error',
|
|
28
|
+
'db-ux/form-validation-message-required': 'error',
|
|
29
|
+
'db-ux/prefer-icon-attribute': 'warn',
|
|
30
|
+
'db-ux/text-or-children-required': 'error',
|
|
31
|
+
'db-ux/no-interactive-tooltip-content': 'error',
|
|
32
|
+
'db-ux/tooltip-requires-interactive-parent': 'error',
|
|
33
|
+
'db-ux/no-nested-accordion': 'error',
|
|
34
|
+
'db-ux/badge-corner-placement-rules': 'error',
|
|
35
|
+
'db-ux/badge-no-inline-in-interactive': 'error',
|
|
36
|
+
'db-ux/link-external-security': 'warn',
|
|
37
|
+
'db-ux/select-requires-options': 'error',
|
|
38
|
+
'db-ux/custom-select-tags-remove-text-required': 'error',
|
|
39
|
+
'db-ux/close-button-text-required': 'error',
|
|
40
|
+
'db-ux/header-burger-menu-label-required': 'error',
|
|
41
|
+
'db-ux/navigation-item-back-button-text-required': 'error',
|
|
42
|
+
'db-ux/tag-removable-remove-button-required': 'error',
|
|
43
|
+
'db-ux/input-type-required': 'warn',
|
|
44
|
+
'db-ux/input-file-type-validation': 'error'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const plugin = {
|
|
48
|
+
meta: {
|
|
49
|
+
name: '@db-ux/eslint-plugin',
|
|
50
|
+
version: '0.0.1'
|
|
51
|
+
},
|
|
52
|
+
rules: {
|
|
53
|
+
'button-no-text-requires-tooltip': buttonNoTextRequiresTooltip,
|
|
54
|
+
'button-type-required': buttonTypeRequired,
|
|
55
|
+
'button-single-icon-attribute': buttonSingleIconAttribute,
|
|
56
|
+
'form-label-required': formLabelRequired,
|
|
57
|
+
'form-validation-message-required': formValidationMessageRequired,
|
|
58
|
+
'prefer-icon-attribute': preferIconAttribute,
|
|
59
|
+
'text-or-children-required': textOrChildrenRequired,
|
|
60
|
+
'no-interactive-tooltip-content': noInteractiveTooltipContent,
|
|
61
|
+
'tooltip-requires-interactive-parent': tooltipRequiresInteractiveParent,
|
|
62
|
+
'no-nested-accordion': noNestedAccordion,
|
|
63
|
+
'badge-corner-placement-rules': badgeCornerPlacementRules,
|
|
64
|
+
'badge-no-inline-in-interactive': badgeNoInlineInInteractive,
|
|
65
|
+
'link-external-security': linkExternalSecurity,
|
|
66
|
+
'select-requires-options': selectRequiresOptions,
|
|
67
|
+
'custom-select-tags-remove-text-required': customSelectTagsRemoveTextRequired,
|
|
68
|
+
'close-button-text-required': closeButtonTextRequired,
|
|
69
|
+
'header-burger-menu-label-required': headerBurgerMenuLabelRequired,
|
|
70
|
+
'navigation-item-back-button-text-required': navigationItemBackButtonTextRequired,
|
|
71
|
+
'tag-removable-remove-button-required': tagRemovableRemoveButtonRequired,
|
|
72
|
+
'input-type-required': inputTypeRequired,
|
|
73
|
+
'input-file-type-validation': inputFileTypeValidation
|
|
74
|
+
},
|
|
75
|
+
configs: {
|
|
76
|
+
recommended
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
export default plugin;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
export default createRule({
|
|
5
|
+
name: 'no-nested-accordion',
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Prevent nesting DBAccordion components'
|
|
10
|
+
},
|
|
11
|
+
messages: {
|
|
12
|
+
noNested: 'DBAccordion must not be nested inside another DBAccordion as it confuses users'
|
|
13
|
+
},
|
|
14
|
+
schema: []
|
|
15
|
+
},
|
|
16
|
+
defaultOptions: [],
|
|
17
|
+
create(context) {
|
|
18
|
+
return {
|
|
19
|
+
JSXElement(node) {
|
|
20
|
+
if (!isDBComponent(node.openingElement, 'DBAccordion'))
|
|
21
|
+
return;
|
|
22
|
+
let parent = node.parent;
|
|
23
|
+
while (parent) {
|
|
24
|
+
if (parent.type === 'JSXElement' &&
|
|
25
|
+
isDBComponent(parent.openingElement, 'DBAccordion')) {
|
|
26
|
+
context.report({
|
|
27
|
+
node: node.openingElement,
|
|
28
|
+
messageId: 'noNested'
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
parent = parent.parent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
function getTextContent(node) {
|
|
5
|
+
const textChild = node.children.find((child) => child.type === 'JSXText');
|
|
6
|
+
return textChild && textChild.type === 'JSXText'
|
|
7
|
+
? textChild.value.trim()
|
|
8
|
+
: null;
|
|
9
|
+
}
|
|
10
|
+
export default createRule({
|
|
11
|
+
name: 'badge-corner-placement-rules',
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Ensure DBBadge with corner placement has max 3 characters and label'
|
|
16
|
+
},
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
messages: {
|
|
19
|
+
textTooLong: 'DBBadge with corner placement must have max 3 characters in text/children',
|
|
20
|
+
missingLabel: 'DBBadge with corner placement must have a label attribute for accessibility'
|
|
21
|
+
},
|
|
22
|
+
schema: []
|
|
23
|
+
},
|
|
24
|
+
defaultOptions: [],
|
|
25
|
+
create(context) {
|
|
26
|
+
return {
|
|
27
|
+
JSXElement(node) {
|
|
28
|
+
if (!isDBComponent(node.openingElement, 'DBBadge'))
|
|
29
|
+
return;
|
|
30
|
+
const placement = getAttributeValue(node.openingElement, 'placement');
|
|
31
|
+
if (!placement || placement === 'inline')
|
|
32
|
+
return;
|
|
33
|
+
const text = getAttributeValue(node.openingElement, 'text');
|
|
34
|
+
const children = getTextContent(node);
|
|
35
|
+
const content = (typeof text === 'string' ? text : children) || '';
|
|
36
|
+
const label = getAttributeValue(node.openingElement, 'label');
|
|
37
|
+
if (content.length > 3) {
|
|
38
|
+
context.report({
|
|
39
|
+
node: node.openingElement,
|
|
40
|
+
messageId: 'textTooLong',
|
|
41
|
+
fix(fixer) {
|
|
42
|
+
const fixes = [];
|
|
43
|
+
const shortText = content.slice(0, 3);
|
|
44
|
+
if (text && typeof text === 'string') {
|
|
45
|
+
const textAttr = node.openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
|
|
46
|
+
a.name.name === 'text');
|
|
47
|
+
if (textAttr) {
|
|
48
|
+
fixes.push(fixer.replaceText(textAttr, `text="${shortText}" label="${content}"`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (children) {
|
|
52
|
+
const textChild = node.children.find((c) => c.type === 'JSXText');
|
|
53
|
+
if (textChild) {
|
|
54
|
+
fixes.push(fixer.replaceText(textChild, shortText));
|
|
55
|
+
const lastAttr = node.openingElement.attributes[node.openingElement.attributes
|
|
56
|
+
.length - 1];
|
|
57
|
+
const insertPos = lastAttr
|
|
58
|
+
? lastAttr.range[1]
|
|
59
|
+
: node.openingElement.name.range[1];
|
|
60
|
+
fixes.push(fixer.insertTextAfterRange([insertPos, insertPos], ` label="${content}"`));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return fixes;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (!label) {
|
|
68
|
+
context.report({
|
|
69
|
+
node: node.openingElement,
|
|
70
|
+
messageId: 'missingLabel'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
const INTERACTIVE_PARENTS = ['DBButton', 'DBLink', 'button', 'a'];
|
|
5
|
+
export default createRule({
|
|
6
|
+
name: 'badge-no-inline-in-interactive',
|
|
7
|
+
meta: {
|
|
8
|
+
type: 'problem',
|
|
9
|
+
docs: {
|
|
10
|
+
description: 'Prevent inline placement for DBBadge inside interactive elements'
|
|
11
|
+
},
|
|
12
|
+
fixable: 'code',
|
|
13
|
+
messages: {
|
|
14
|
+
noInline: 'DBBadge inside {{parent}} cannot have placement="inline". Use corner placement instead'
|
|
15
|
+
},
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
JSXElement(node) {
|
|
22
|
+
if (!isDBComponent(node.openingElement, 'DBBadge'))
|
|
23
|
+
return;
|
|
24
|
+
const placement = getAttributeValue(node.openingElement, 'placement');
|
|
25
|
+
if (placement && placement !== 'inline')
|
|
26
|
+
return;
|
|
27
|
+
let parent = node.parent;
|
|
28
|
+
while (parent) {
|
|
29
|
+
if (parent.type === 'JSXElement') {
|
|
30
|
+
const parentName = parent.openingElement.name;
|
|
31
|
+
if (parentName.type === 'JSXIdentifier') {
|
|
32
|
+
const name = parentName.name;
|
|
33
|
+
const matchedParent = INTERACTIVE_PARENTS.find((p) => name === p ||
|
|
34
|
+
name ===
|
|
35
|
+
p.toLowerCase().replace('db', 'db-'));
|
|
36
|
+
if (matchedParent) {
|
|
37
|
+
context.report({
|
|
38
|
+
node: node.openingElement,
|
|
39
|
+
messageId: 'noInline',
|
|
40
|
+
data: { parent: matchedParent },
|
|
41
|
+
fix(fixer) {
|
|
42
|
+
const placementAttr = node.openingElement.attributes.find((a) => a.type === 'JSXAttribute' &&
|
|
43
|
+
a.name.name === 'placement');
|
|
44
|
+
if (placementAttr) {
|
|
45
|
+
return fixer.replaceText(placementAttr, 'placement="corner-top-right"');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const lastAttr = node.openingElement.attributes[node.openingElement
|
|
49
|
+
.attributes.length - 1];
|
|
50
|
+
const insertPos = lastAttr
|
|
51
|
+
? lastAttr.range[1]
|
|
52
|
+
: node.openingElement.name
|
|
53
|
+
.range[1];
|
|
54
|
+
return fixer.insertTextAfterRange([insertPos, insertPos], ' placement="corner-top-right"');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
parent = parent.parent;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, hasChildOfType, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
export default createRule({
|
|
5
|
+
name: 'button-no-text-requires-tooltip',
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Ensure DBButton with noText has icon and DBTooltip child'
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
messages: {
|
|
13
|
+
missingIcon: 'DBButton with noText must have an icon prop',
|
|
14
|
+
missingTooltip: 'DBButton with noText must have a DBTooltip child for accessibility'
|
|
15
|
+
},
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
JSXElement(node) {
|
|
22
|
+
const openingElement = node.openingElement;
|
|
23
|
+
if (!isDBComponent(openingElement, 'DBButton'))
|
|
24
|
+
return;
|
|
25
|
+
const noText = getAttributeValue(openingElement, 'noText');
|
|
26
|
+
if (!noText)
|
|
27
|
+
return;
|
|
28
|
+
const icon = getAttributeValue(openingElement, 'icon') ||
|
|
29
|
+
getAttributeValue(openingElement, 'iconLeading') ||
|
|
30
|
+
getAttributeValue(openingElement, 'iconTrailing');
|
|
31
|
+
if (!icon) {
|
|
32
|
+
context.report({
|
|
33
|
+
node: openingElement,
|
|
34
|
+
messageId: 'missingIcon'
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const hasTooltip = hasChildOfType(node, 'DBTooltip');
|
|
38
|
+
if (!hasTooltip) {
|
|
39
|
+
context.report({
|
|
40
|
+
node: openingElement,
|
|
41
|
+
messageId: 'missingTooltip',
|
|
42
|
+
fix(fixer) {
|
|
43
|
+
const closingTag = node.closingElement;
|
|
44
|
+
if (!closingTag)
|
|
45
|
+
return null;
|
|
46
|
+
const componentName = openingElement.name.type === 'JSXIdentifier'
|
|
47
|
+
? openingElement.name.name
|
|
48
|
+
: 'DBButton';
|
|
49
|
+
const tooltipName = componentName.includes('-')
|
|
50
|
+
? 'db-tooltip'
|
|
51
|
+
: 'DBTooltip';
|
|
52
|
+
return fixer.insertTextBefore(closingTag, `\n <${tooltipName}>Describe action</${tooltipName}>`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
export default createRule({
|
|
5
|
+
name: 'button-single-icon-attribute',
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Ensure DBButton uses only one icon attribute'
|
|
10
|
+
},
|
|
11
|
+
messages: {
|
|
12
|
+
multipleIcons: 'DBButton can only use one of: icon, iconLeading, or iconTrailing'
|
|
13
|
+
},
|
|
14
|
+
schema: []
|
|
15
|
+
},
|
|
16
|
+
defaultOptions: [],
|
|
17
|
+
create(context) {
|
|
18
|
+
return {
|
|
19
|
+
JSXElement(node) {
|
|
20
|
+
if (!isDBComponent(node.openingElement, 'DBButton'))
|
|
21
|
+
return;
|
|
22
|
+
const icon = getAttributeValue(node.openingElement, 'icon');
|
|
23
|
+
const iconLeading = getAttributeValue(node.openingElement, 'iconLeading');
|
|
24
|
+
const iconTrailing = getAttributeValue(node.openingElement, 'iconTrailing');
|
|
25
|
+
const iconCount = [icon, iconLeading, iconTrailing].filter(Boolean).length;
|
|
26
|
+
if (iconCount > 1) {
|
|
27
|
+
context.report({
|
|
28
|
+
node: node.openingElement,
|
|
29
|
+
messageId: 'multipleIcons'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
export default createRule({
|
|
5
|
+
name: 'button-type-required',
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Ensure DBButton has explicit type attribute'
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
messages: {
|
|
13
|
+
missingType: 'DBButton must have an explicit type attribute (submit, button, or reset)'
|
|
14
|
+
},
|
|
15
|
+
schema: []
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: [],
|
|
18
|
+
create(context) {
|
|
19
|
+
return {
|
|
20
|
+
JSXElement(node) {
|
|
21
|
+
const openingElement = node.openingElement;
|
|
22
|
+
if (!isDBComponent(openingElement, 'DBButton'))
|
|
23
|
+
return;
|
|
24
|
+
const type = getAttributeValue(openingElement, 'type');
|
|
25
|
+
if (!type) {
|
|
26
|
+
const hasClickHandler = getAttributeValue(openingElement, 'onClick') ||
|
|
27
|
+
getAttributeValue(openingElement, '(click)') ||
|
|
28
|
+
getAttributeValue(openingElement, '@click');
|
|
29
|
+
const typeValue = hasClickHandler ? 'button' : 'submit';
|
|
30
|
+
context.report({
|
|
31
|
+
node: openingElement,
|
|
32
|
+
messageId: 'missingType',
|
|
33
|
+
fix(fixer) {
|
|
34
|
+
const lastAttr = openingElement.attributes[openingElement.attributes.length - 1];
|
|
35
|
+
const insertPos = lastAttr
|
|
36
|
+
? lastAttr.range[1]
|
|
37
|
+
: openingElement.name.range[1];
|
|
38
|
+
return fixer.insertTextAfterRange([insertPos, insertPos], ` type="${typeValue}"`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
const COMPONENTS_WITH_CLOSE_BUTTON = {
|
|
5
|
+
DBNotification: 'closeButtonText',
|
|
6
|
+
DBDrawer: 'closeButtonText',
|
|
7
|
+
DBCustomSelect: 'mobileCloseButtonText'
|
|
8
|
+
};
|
|
9
|
+
export default createRule({
|
|
10
|
+
name: 'close-button-text-required',
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'problem',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Ensure components have close button text for accessibility'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
missingCloseButtonText: '{{component}} must have {{attribute}} attribute for accessibility'
|
|
18
|
+
},
|
|
19
|
+
schema: []
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
return {
|
|
24
|
+
JSXElement(node) {
|
|
25
|
+
const component = Object.keys(COMPONENTS_WITH_CLOSE_BUTTON).find((comp) => isDBComponent(node.openingElement, comp));
|
|
26
|
+
if (!component)
|
|
27
|
+
return;
|
|
28
|
+
const attribute = COMPONENTS_WITH_CLOSE_BUTTON[component];
|
|
29
|
+
const value = getAttributeValue(node.openingElement, attribute);
|
|
30
|
+
if (!value) {
|
|
31
|
+
context.report({
|
|
32
|
+
node: node.openingElement,
|
|
33
|
+
messageId: 'missingCloseButtonText',
|
|
34
|
+
data: { component, attribute }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getAttributeValue, isDBComponent } from '../../shared/utils.js';
|
|
3
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/db-ux-design-system/core-web/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
4
|
+
const COMPONENTS_REQUIRING_CONTENT = [
|
|
5
|
+
'DBAccordionItem',
|
|
6
|
+
'DBBadge',
|
|
7
|
+
'DBButton',
|
|
8
|
+
'DBLink',
|
|
9
|
+
'DBIcon',
|
|
10
|
+
'DBInfotext',
|
|
11
|
+
'DBNavigationItem',
|
|
12
|
+
'DBNotification'
|
|
13
|
+
];
|
|
14
|
+
export default createRule({
|
|
15
|
+
name: 'text-or-children-required',
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'problem',
|
|
18
|
+
docs: {
|
|
19
|
+
description: 'Ensure components have text property or children content'
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
missingContent: '{{component}} must have either a text property or children content'
|
|
23
|
+
},
|
|
24
|
+
schema: []
|
|
25
|
+
},
|
|
26
|
+
defaultOptions: [],
|
|
27
|
+
create(context) {
|
|
28
|
+
return {
|
|
29
|
+
JSXElement(node) {
|
|
30
|
+
const openingElement = node.openingElement;
|
|
31
|
+
const component = COMPONENTS_REQUIRING_CONTENT.find((comp) => isDBComponent(openingElement, comp));
|
|
32
|
+
if (!component)
|
|
33
|
+
return;
|
|
34
|
+
const text = getAttributeValue(openingElement, 'text');
|
|
35
|
+
const hasChildren = node.children.some((child) => (child.type === 'JSXText' &&
|
|
36
|
+
child.value.trim() !== '') ||
|
|
37
|
+
child.type === 'JSXElement' ||
|
|
38
|
+
child.type === 'JSXExpressionContainer');
|
|
39
|
+
if (!text && !hasChildren) {
|
|
40
|
+
context.report({
|
|
41
|
+
node: openingElement,
|
|
42
|
+
messageId: 'missingContent',
|
|
43
|
+
data: { component }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|