@adyen/eslint-plugin-bento 2.14.0 → 2.16.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 +31 -0
- package/package.json +1 -1
- package/rules/deprecation-bento-timeline-item-variant-prop/README.md +13 -5
- package/rules/deprecation-bento-timeline-item-variant-prop/__tests__/deprecation-bento-timeline-item-variant-prop.vue +2 -2
- package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.integration.test.ts +6 -2
- package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.js +61 -12
- package/rules/deprecation-bento-timeline-item-variant-prop/deprecation-bento-timeline-item-variant-prop.test.ts +87 -12
- package/rules/deprecation-components-error-prop/README.md +143 -7
- package/rules/deprecation-components-error-prop/__tests__/eslint.config.js +1 -1
- package/rules/deprecation-components-error-prop/deprecation-components-error-prop.integration.test.ts +14 -10
- package/rules/deprecation-components-error-prop/deprecation-components-error-prop.js +20 -30
- package/rules/deprecation-components-error-prop/deprecation-components-error-prop.test.ts +69 -36
- package/rules/deprecation-components-has-error-prop/README.md +107 -6
- package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.integration.test.ts +10 -2
- package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.js +18 -24
- package/rules/deprecation-components-has-error-prop/deprecation-components-has-error-prop.test.ts +58 -69
- package/rules/deprecation-components-input-emit/README.md +170 -3
- package/rules/deprecation-components-input-emit/deprecation-components-input-emit.integration.test.ts +9 -1
- package/rules/deprecation-components-input-emit/deprecation-components-input-emit.js +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.16.0 (2026-06-09)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- added eslint rule readme to mcp `get-deprecations` tool ([44b9ce380](https://github.com/Adyen/bento/commit/44b9ce380))
|
|
8
|
+
|
|
9
|
+
### ❤️ Thank You
|
|
10
|
+
|
|
11
|
+
- erice
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 2.15.0 (2026-06-03)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
- fixed `deprecation-components-input-emit` error message wording ([5a22428fa](https://github.com/Adyen/bento/commit/5a22428fa))
|
|
19
|
+
- updated `deprecation-components-error-prop` rule from fix to suggest ([edcf472fd](https://github.com/Adyen/bento/commit/edcf472fd))
|
|
20
|
+
- updated `deprecation-components-has-error-prop` rule from fix to suggest ([f573b3643](https://github.com/Adyen/bento/commit/f573b3643))
|
|
21
|
+
- updated `deprecation-bento-timeline-item-variant-prop` to replace values correctly ([989c92838](https://github.com/Adyen/bento/commit/989c92838))
|
|
22
|
+
|
|
23
|
+
### Code Refactoring
|
|
24
|
+
|
|
25
|
+
- extended `deprecation-components-input-emit` error message with race conditions example ([4030de20f](https://github.com/Adyen/bento/commit/4030de20f))
|
|
26
|
+
|
|
27
|
+
### ❤️ Thank You
|
|
28
|
+
|
|
29
|
+
- daver
|
|
30
|
+
- erice
|
|
31
|
+
- gerald
|
|
32
|
+
|
|
33
|
+
|
|
3
34
|
## 2.14.0 (2026-05-27)
|
|
4
35
|
|
|
5
36
|
This was a version bump only for eslint to align it with other projects, there were no code changes.
|
package/package.json
CHANGED
|
@@ -2,20 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
|
-
The `variant` prop in
|
|
5
|
+
The `variant` prop in `bento-timeline-item` is deprecated. Use `status` instead.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The `variant` prop accepted `BentoTimelineItemVariant` enum values (icon names), while `status` accepts color strings.
|
|
8
|
+
The autofix remaps values automatically:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
| `variant` value | `status` value |
|
|
11
|
+
| ------------------------------- | -------------- |
|
|
12
|
+
| `SquareSmallIcon` / `DEFAULT` | `black` |
|
|
13
|
+
| `CheckedCircleIcon` / `SUCCESS` | `green` |
|
|
14
|
+
| `CrossCircleIcon` / `CRITICAL` | `red` |
|
|
15
|
+
|
|
16
|
+
> **Note:** If the variant value cannot be statically resolved (e.g. a variable reference), the autofix is skipped and
|
|
17
|
+
> only the deprecation warning is reported.
|
|
10
18
|
|
|
11
19
|
## Rule Details
|
|
12
20
|
|
|
13
|
-
This rule reports usages of the deprecated
|
|
21
|
+
This rule reports usages of the deprecated `variant` prop and autofixes it to `status` with the correct color value.
|
|
14
22
|
|
|
15
23
|
### ❌ Incorrect
|
|
16
24
|
|
|
17
25
|
```html
|
|
18
|
-
<bento-timeline-item variant="
|
|
26
|
+
<bento-timeline-item variant="CheckedCircleIcon" /> <bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" />
|
|
19
27
|
```
|
|
20
28
|
|
|
21
29
|
### ✅ Correct
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<bento-timeline-item variant="
|
|
3
|
+
<bento-timeline-item variant="CheckedCircleIcon" />
|
|
4
4
|
<bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" />
|
|
5
|
-
<bento-timeline-item :variant="BentoTimelineItemVariant.
|
|
5
|
+
<bento-timeline-item :variant="BentoTimelineItemVariant.DEFAULT" />
|
|
6
6
|
</div>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
@@ -8,8 +8,12 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
|
|
|
8
8
|
// Load the test file generated alongside this one
|
|
9
9
|
const vueFile = path.resolve(__dirname, '__tests__', 'deprecation-bento-timeline-item-variant-prop.vue');
|
|
10
10
|
|
|
11
|
+
// Strip any non-JSON preamble (e.g. pnpm warnings)
|
|
12
|
+
const rawOutput = execFileSyncEslintVueFiles(eslintrc, vueFile);
|
|
13
|
+
const jsonStart = rawOutput.indexOf('[');
|
|
14
|
+
|
|
11
15
|
// Execute the linter
|
|
12
|
-
const result = JSON.parse(
|
|
16
|
+
const result = JSON.parse(jsonStart >= 0 ? rawOutput.slice(jsonStart) : rawOutput);
|
|
13
17
|
|
|
14
18
|
// 3 examples: static, shorthand, longhand
|
|
15
19
|
expect(result[0].warningCount).toBe(3);
|
|
@@ -24,7 +28,7 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
|
|
|
24
28
|
message: `The "variant" prop is deprecated. Please use "status" instead.`,
|
|
25
29
|
|
|
26
30
|
fix: expect.objectContaining({
|
|
27
|
-
text: expect.stringContaining('status'),
|
|
31
|
+
text: expect.stringContaining('status='),
|
|
28
32
|
}),
|
|
29
33
|
});
|
|
30
34
|
});
|
|
@@ -7,6 +7,47 @@
|
|
|
7
7
|
|
|
8
8
|
const VueUtils = require('eslint-plugin-vue/lib/utils');
|
|
9
9
|
|
|
10
|
+
// Maps BentoTimelineItemVariant enum values (runtime strings) to status colors
|
|
11
|
+
const VARIANT_VALUE_TO_STATUS = {
|
|
12
|
+
SquareSmallIcon: 'black',
|
|
13
|
+
CheckedCircleIcon: 'green',
|
|
14
|
+
CrossCircleIcon: 'red',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Maps BentoTimelineItemVariant enum member names to status colors
|
|
18
|
+
const VARIANT_ENUM_KEY_TO_STATUS = {
|
|
19
|
+
DEFAULT: 'black',
|
|
20
|
+
SUCCESS: 'green',
|
|
21
|
+
CRITICAL: 'red',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolves a dynamic expression to a status color string.
|
|
26
|
+
* Returns null if the expression cannot be statically resolved.
|
|
27
|
+
*/
|
|
28
|
+
function resolveExpressionToStatus(expression) {
|
|
29
|
+
if (!expression) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// String literal: :variant="'CheckedCircleIcon'"
|
|
34
|
+
if (expression.type === 'Literal' && typeof expression.value === 'string') {
|
|
35
|
+
return VARIANT_VALUE_TO_STATUS[expression.value] ?? null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Enum member: :variant="BentoTimelineItemVariant.SUCCESS"
|
|
39
|
+
if (
|
|
40
|
+
expression.type === 'MemberExpression' &&
|
|
41
|
+
expression.object?.type === 'Identifier' &&
|
|
42
|
+
expression.object.name === 'BentoTimelineItemVariant' &&
|
|
43
|
+
expression.property?.type === 'Identifier'
|
|
44
|
+
) {
|
|
45
|
+
return VARIANT_ENUM_KEY_TO_STATUS[expression.property.name] ?? null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
10
51
|
/** @type {RuleModule} */
|
|
11
52
|
module.exports = {
|
|
12
53
|
meta: {
|
|
@@ -44,33 +85,41 @@ module.exports = {
|
|
|
44
85
|
/* PROP DEPRECATION */
|
|
45
86
|
/* ------------------------------------------------ */
|
|
46
87
|
return VueUtils.defineTemplateBodyVisitor(context, {
|
|
47
|
-
// 1. STATIC:
|
|
88
|
+
// 1. STATIC: variant="CheckedCircleIcon"
|
|
48
89
|
[`VElement:matches([name="bento-timeline-item"], [name="BentoTimelineItem"]) > VStartTag > VAttribute[directive=false][key.name="variant"]`](
|
|
49
90
|
node
|
|
50
91
|
) {
|
|
92
|
+
const staticValue = node.value?.value;
|
|
93
|
+
const mappedStatus = staticValue ? VARIANT_VALUE_TO_STATUS[staticValue] : null;
|
|
94
|
+
|
|
51
95
|
context.report({
|
|
52
96
|
node,
|
|
53
97
|
messageId: 'deprecation',
|
|
54
|
-
...(autofix &&
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
98
|
+
...(autofix &&
|
|
99
|
+
mappedStatus && {
|
|
100
|
+
fix(fixer) {
|
|
101
|
+
return fixer.replaceText(node, `status="${mappedStatus}"`);
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
59
104
|
});
|
|
60
105
|
},
|
|
61
106
|
|
|
62
|
-
// 2. DYNAMIC: :
|
|
107
|
+
// 2. DYNAMIC: :variant="BentoTimelineItemVariant.SUCCESS" or v-bind:variant="..."
|
|
63
108
|
[`VElement:matches([name="bento-timeline-item"], [name="BentoTimelineItem"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="variant"]`](
|
|
64
109
|
node
|
|
65
110
|
) {
|
|
111
|
+
const expression = node.value?.expression;
|
|
112
|
+
const mappedStatus = resolveExpressionToStatus(expression);
|
|
113
|
+
|
|
66
114
|
context.report({
|
|
67
115
|
node,
|
|
68
116
|
messageId: 'deprecation',
|
|
69
|
-
...(autofix &&
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
117
|
+
...(autofix &&
|
|
118
|
+
mappedStatus && {
|
|
119
|
+
fix(fixer) {
|
|
120
|
+
return fixer.replaceText(node, `status="${mappedStatus}"`);
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
74
123
|
});
|
|
75
124
|
},
|
|
76
125
|
});
|
|
@@ -18,20 +18,21 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
|
|
|
18
18
|
ruleTester.run('deprecation-bento-timeline-item-variant-prop', deprecationBentoTimelineItemVariantProp, {
|
|
19
19
|
valid: [
|
|
20
20
|
{
|
|
21
|
-
name: 'Valid usage
|
|
21
|
+
name: 'Valid usage with status prop',
|
|
22
22
|
filename: 'test.vue',
|
|
23
23
|
code: `
|
|
24
24
|
<template>
|
|
25
|
-
<bento-timeline-item status="
|
|
25
|
+
<bento-timeline-item status="green" />
|
|
26
26
|
</template>
|
|
27
27
|
`,
|
|
28
28
|
},
|
|
29
29
|
],
|
|
30
30
|
invalid: [
|
|
31
|
+
// No autofix when disabled
|
|
31
32
|
{
|
|
32
33
|
name: 'Invalid: autofix disabled (explicit false)',
|
|
33
34
|
filename: 'test.vue',
|
|
34
|
-
code: `<template><bento-timeline-item variant="
|
|
35
|
+
code: `<template><bento-timeline-item variant="CheckedCircleIcon" /></template>`,
|
|
35
36
|
output: null,
|
|
36
37
|
options: [{ autofix: false }],
|
|
37
38
|
errors: [{ messageId: 'deprecation' }],
|
|
@@ -39,25 +40,99 @@ describe('@adyen/bento/deprecation-bento-timeline-item-variant-prop', () => {
|
|
|
39
40
|
{
|
|
40
41
|
name: 'Invalid: autofix disabled (no options)',
|
|
41
42
|
filename: 'test.vue',
|
|
42
|
-
code: `<template><bento-timeline-item :variant="
|
|
43
|
+
code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.SUCCESS" /></template>`,
|
|
43
44
|
output: null,
|
|
44
45
|
errors: [{ messageId: 'deprecation' }],
|
|
45
46
|
},
|
|
46
|
-
|
|
47
|
+
|
|
48
|
+
// Static values — remaps enum value strings to colors
|
|
47
49
|
{
|
|
48
|
-
name: 'Invalid: Static
|
|
50
|
+
name: 'Invalid: Static variant="SquareSmallIcon" → status="black"',
|
|
49
51
|
filename: 'test.vue',
|
|
50
|
-
code: `<template><bento-timeline-item variant="
|
|
51
|
-
output: `<template><bento-timeline-item status="
|
|
52
|
+
code: `<template><bento-timeline-item variant="SquareSmallIcon" /></template>`,
|
|
53
|
+
output: `<template><bento-timeline-item status="black" /></template>`,
|
|
52
54
|
options: [{ autofix: true }],
|
|
53
55
|
errors: [{ messageId: 'deprecation' }],
|
|
54
56
|
},
|
|
55
|
-
// bento-timeline-item: Shorthand
|
|
56
57
|
{
|
|
57
|
-
name: 'Invalid:
|
|
58
|
+
name: 'Invalid: Static variant="CheckedCircleIcon" → status="green"',
|
|
58
59
|
filename: 'test.vue',
|
|
59
|
-
code: `<template><bento-timeline-item
|
|
60
|
-
output: `<template><bento-timeline-item
|
|
60
|
+
code: `<template><bento-timeline-item variant="CheckedCircleIcon" /></template>`,
|
|
61
|
+
output: `<template><bento-timeline-item status="green" /></template>`,
|
|
62
|
+
options: [{ autofix: true }],
|
|
63
|
+
errors: [{ messageId: 'deprecation' }],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Invalid: Static variant="CrossCircleIcon" → status="red"',
|
|
67
|
+
filename: 'test.vue',
|
|
68
|
+
code: `<template><bento-timeline-item variant="CrossCircleIcon" /></template>`,
|
|
69
|
+
output: `<template><bento-timeline-item status="red" /></template>`,
|
|
70
|
+
options: [{ autofix: true }],
|
|
71
|
+
errors: [{ messageId: 'deprecation' }],
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Static with unknown value — no fix applied
|
|
75
|
+
{
|
|
76
|
+
name: 'Invalid: Static variant with unknown value does not autofix',
|
|
77
|
+
filename: 'test.vue',
|
|
78
|
+
code: `<template><bento-timeline-item variant="unknownValue" /></template>`,
|
|
79
|
+
output: null,
|
|
80
|
+
options: [{ autofix: true }],
|
|
81
|
+
errors: [{ messageId: 'deprecation' }],
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Dynamic with enum member expression — remaps enum key to color
|
|
85
|
+
{
|
|
86
|
+
name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.DEFAULT" → status="black"',
|
|
87
|
+
filename: 'test.vue',
|
|
88
|
+
code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.DEFAULT" /></template>`,
|
|
89
|
+
output: `<template><bento-timeline-item status="black" /></template>`,
|
|
90
|
+
options: [{ autofix: true }],
|
|
91
|
+
errors: [{ messageId: 'deprecation' }],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.SUCCESS" → status="green"',
|
|
95
|
+
filename: 'test.vue',
|
|
96
|
+
code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.SUCCESS" /></template>`,
|
|
97
|
+
output: `<template><bento-timeline-item status="green" /></template>`,
|
|
98
|
+
options: [{ autofix: true }],
|
|
99
|
+
errors: [{ messageId: 'deprecation' }],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'Invalid: Dynamic :variant="BentoTimelineItemVariant.CRITICAL" → status="red"',
|
|
103
|
+
filename: 'test.vue',
|
|
104
|
+
code: `<template><bento-timeline-item :variant="BentoTimelineItemVariant.CRITICAL" /></template>`,
|
|
105
|
+
output: `<template><bento-timeline-item status="red" /></template>`,
|
|
106
|
+
options: [{ autofix: true }],
|
|
107
|
+
errors: [{ messageId: 'deprecation' }],
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Dynamic with string literal — remaps value to color
|
|
111
|
+
{
|
|
112
|
+
name: 'Invalid: Dynamic :variant="\'CheckedCircleIcon\'" → status="green"',
|
|
113
|
+
filename: 'test.vue',
|
|
114
|
+
code: `<template><bento-timeline-item :variant="'CheckedCircleIcon'" /></template>`,
|
|
115
|
+
output: `<template><bento-timeline-item status="green" /></template>`,
|
|
116
|
+
options: [{ autofix: true }],
|
|
117
|
+
errors: [{ messageId: 'deprecation' }],
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Dynamic with unknown expression — no fix applied
|
|
121
|
+
{
|
|
122
|
+
name: 'Invalid: Dynamic :variant with unknown expression does not autofix',
|
|
123
|
+
filename: 'test.vue',
|
|
124
|
+
code: `<template><bento-timeline-item :variant="computedVariant" /></template>`,
|
|
125
|
+
output: null,
|
|
126
|
+
options: [{ autofix: true }],
|
|
127
|
+
errors: [{ messageId: 'deprecation' }],
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Dynamic with non-BentoTimelineItemVariant enum — no fix applied
|
|
131
|
+
{
|
|
132
|
+
name: 'Invalid: Dynamic :variant with unrelated enum does not autofix',
|
|
133
|
+
filename: 'test.vue',
|
|
134
|
+
code: `<template><bento-timeline-item :variant="customStatus.SUCCESS" /></template>`,
|
|
135
|
+
output: null,
|
|
61
136
|
options: [{ autofix: true }],
|
|
62
137
|
errors: [{ messageId: 'deprecation' }],
|
|
63
138
|
},
|
|
@@ -10,34 +10,170 @@ The `error` prop in the following components is deprecated:
|
|
|
10
10
|
|
|
11
11
|
Please use `error-message` instead.
|
|
12
12
|
|
|
13
|
+
> **Note:** The `error` prop is a `boolean` that only toggles error styling, while `error-message` is a `string` that
|
|
14
|
+
> both displays a message and applies error styling. A direct rename is not type-safe — you must also update the bound
|
|
15
|
+
> value from a boolean to an error message string.
|
|
16
|
+
|
|
17
|
+
| Deprecated prop | Replacement prop | Type change |
|
|
18
|
+
| --------------- | ---------------- | -------------------- |
|
|
19
|
+
| `error` | `error-message` | `boolean` → `string` |
|
|
20
|
+
|
|
13
21
|
## Rule Details
|
|
14
22
|
|
|
15
|
-
This rule reports usages of the deprecated
|
|
23
|
+
This rule reports usages of the deprecated `error` prop and provides an IDE suggestion (not an autofix) to rename it.
|
|
16
24
|
|
|
17
25
|
### ❌ Incorrect
|
|
18
26
|
|
|
19
27
|
```html
|
|
20
|
-
|
|
28
|
+
<!-- Static boolean -->
|
|
29
|
+
<bento-dropdown :error="true" :items="items" label="Country" />
|
|
30
|
+
|
|
31
|
+
<!-- Dynamic binding -->
|
|
32
|
+
<bento-dropdown :error="hasError" :items="items" label="Country" />
|
|
21
33
|
```
|
|
22
34
|
|
|
23
35
|
```html
|
|
24
|
-
<bento-input-field error="
|
|
36
|
+
<bento-input-field :error="true" label="Email" />
|
|
37
|
+
|
|
38
|
+
<bento-input-field :error="hasError" label="Email" />
|
|
25
39
|
```
|
|
26
40
|
|
|
27
41
|
```html
|
|
28
|
-
<bento-input-field-phone-number error="
|
|
42
|
+
<bento-input-field-phone-number :error="true" label="Phone number" />
|
|
43
|
+
|
|
44
|
+
<bento-input-field-phone-number :error="hasError" label="Phone number" />
|
|
29
45
|
```
|
|
30
46
|
|
|
31
47
|
### ✅ Correct
|
|
32
48
|
|
|
33
49
|
```html
|
|
34
|
-
|
|
50
|
+
<!-- Static error message -->
|
|
51
|
+
<bento-dropdown error-message="Please select a country" :items="items" label="Country" />
|
|
52
|
+
|
|
53
|
+
<!-- Dynamic error message driven by validation -->
|
|
54
|
+
<bento-dropdown :error-message="errorMessage" :items="items" label="Country" />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<bento-input-field error-message="Enter a valid email address" label="Email" />
|
|
59
|
+
|
|
60
|
+
<bento-input-field :error-message="errorMessage" label="Email" />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<bento-input-field-phone-number error-message="Enter a valid phone number" label="Phone number" />
|
|
65
|
+
|
|
66
|
+
<bento-input-field-phone-number :error-message="errorMessage" label="Phone number" />
|
|
35
67
|
```
|
|
36
68
|
|
|
69
|
+
## Migration Examples
|
|
70
|
+
|
|
71
|
+
### Before — static boolean on `bento-dropdown`
|
|
72
|
+
|
|
37
73
|
```html
|
|
38
|
-
<
|
|
74
|
+
<template>
|
|
75
|
+
<bento-dropdown :error="true" :items="items" label="Country" />
|
|
76
|
+
</template>
|
|
39
77
|
```
|
|
40
78
|
|
|
79
|
+
### After — static error message on `bento-dropdown`
|
|
80
|
+
|
|
41
81
|
```html
|
|
42
|
-
<
|
|
82
|
+
<template>
|
|
83
|
+
<bento-dropdown error-message="Please select a country" :items="items" label="Country" />
|
|
84
|
+
</template>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Before — dynamic validation on `bento-input-field`
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<template>
|
|
91
|
+
<bento-input-field :error="hasError" v-model="email" label="Email" />
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
const email = ref('');
|
|
96
|
+
const hasError = ref(false);
|
|
97
|
+
|
|
98
|
+
const validate = () => {
|
|
99
|
+
hasError.value = !email.value.includes('@');
|
|
100
|
+
};
|
|
101
|
+
</script>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### After — dynamic validation with error message on `bento-input-field`
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<template>
|
|
108
|
+
<bento-input-field :error-message="errorMessage" v-model="email" label="Email" />
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
const email = ref('');
|
|
113
|
+
const errorMessage = ref<string | null>(null);
|
|
114
|
+
|
|
115
|
+
const validate = () => {
|
|
116
|
+
errorMessage.value = !email.value.includes('@') ? 'Enter a valid email address' : null;
|
|
117
|
+
};
|
|
118
|
+
</script>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Before — phone number validation
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<template>
|
|
125
|
+
<bento-input-field-phone-number
|
|
126
|
+
:error="hasError"
|
|
127
|
+
v-model="phone"
|
|
128
|
+
:selected-country="country"
|
|
129
|
+
label="Phone number"
|
|
130
|
+
@input:valid="onValid"
|
|
131
|
+
@blur="validate"
|
|
132
|
+
/>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<script setup lang="ts">
|
|
136
|
+
const phone = ref('');
|
|
137
|
+
const country = ref('US');
|
|
138
|
+
const hasError = ref(false);
|
|
139
|
+
const isValid = ref(false);
|
|
140
|
+
|
|
141
|
+
const onValid = (validity: boolean) => {
|
|
142
|
+
isValid.value = validity;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const validate = () => {
|
|
146
|
+
hasError.value = !isValid.value && !!phone.value;
|
|
147
|
+
};
|
|
148
|
+
</script>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### After — phone number validation with error message
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<template>
|
|
155
|
+
<bento-input-field-phone-number
|
|
156
|
+
:error-message="errorMessage"
|
|
157
|
+
v-model="phone"
|
|
158
|
+
:selected-country="country"
|
|
159
|
+
label="Phone number"
|
|
160
|
+
@input:valid="onValid"
|
|
161
|
+
@blur="validate"
|
|
162
|
+
/>
|
|
163
|
+
</template>
|
|
164
|
+
|
|
165
|
+
<script setup lang="ts">
|
|
166
|
+
const phone = ref('');
|
|
167
|
+
const country = ref('US');
|
|
168
|
+
const errorMessage = ref<string | null>(null);
|
|
169
|
+
const isValid = ref(false);
|
|
170
|
+
|
|
171
|
+
const onValid = (validity: boolean) => {
|
|
172
|
+
isValid.value = validity;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const validate = () => {
|
|
176
|
+
errorMessage.value = !isValid.value && !!phone.value ? 'Enter a valid phone number' : null;
|
|
177
|
+
};
|
|
178
|
+
</script>
|
|
43
179
|
```
|
|
@@ -19,7 +19,7 @@ module.exports = defineConfig([
|
|
|
19
19
|
},
|
|
20
20
|
rules: {
|
|
21
21
|
// Register the specific rule we are testing
|
|
22
|
-
'@adyen/bento/deprecation-components-error-prop':
|
|
22
|
+
'@adyen/bento/deprecation-components-error-prop': 'warn',
|
|
23
23
|
},
|
|
24
24
|
files: ['**/*.vue'],
|
|
25
25
|
},
|
|
@@ -8,8 +8,10 @@ describe('@adyen/bento/deprecation-components-error-prop', () => {
|
|
|
8
8
|
// Load the test file generated alongside this one
|
|
9
9
|
const vueFile = path.resolve(__dirname, '__tests__', 'deprecation-components-error-prop.vue');
|
|
10
10
|
|
|
11
|
-
// Execute the linter
|
|
12
|
-
const
|
|
11
|
+
// Execute the linter — strip any non-JSON preamble (e.g. pnpm warnings)
|
|
12
|
+
const rawOutput = execFileSyncEslintVueFiles(eslintrc, vueFile);
|
|
13
|
+
const jsonStart = rawOutput.indexOf('[{"');
|
|
14
|
+
const result = JSON.parse(jsonStart >= 0 ? rawOutput.slice(jsonStart) : rawOutput);
|
|
13
15
|
|
|
14
16
|
/* ------------------------------------------------ */
|
|
15
17
|
/* CASE 2: SINGLE REPLACEMENT */
|
|
@@ -20,18 +22,20 @@ describe('@adyen/bento/deprecation-components-error-prop', () => {
|
|
|
20
22
|
const numberOfComponents = 3;
|
|
21
23
|
expect(result[0].warningCount).toBe(numberOfComponents * 3);
|
|
22
24
|
|
|
23
|
-
// Verify the message structure on the first
|
|
24
|
-
// Since the rule is the same for all components, checking the first one confirms the message is correct.
|
|
25
|
+
// Verify the message structure on the first warning found.
|
|
25
26
|
expect(result[0].messages[0]).toMatchObject({
|
|
26
27
|
ruleId: '@adyen/bento/deprecation-components-error-prop',
|
|
27
28
|
severity: 1,
|
|
28
|
-
|
|
29
29
|
messageId: 'deprecation',
|
|
30
|
-
message: `The "error" prop is deprecated.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
message: `The "error" prop is deprecated. Use "error-message" instead. Note that "error" accepted a boolean, but "error-message" expects a string, so update the value to an error message.`,
|
|
31
|
+
suggestions: expect.arrayContaining([
|
|
32
|
+
expect.objectContaining({
|
|
33
|
+
messageId: 'suggestReplace',
|
|
34
|
+
fix: expect.objectContaining({
|
|
35
|
+
text: expect.stringContaining('error-message'),
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
]),
|
|
35
39
|
});
|
|
36
40
|
});
|
|
37
41
|
});
|
|
@@ -19,59 +19,49 @@ module.exports = {
|
|
|
19
19
|
recommended: true,
|
|
20
20
|
},
|
|
21
21
|
messages: {
|
|
22
|
-
deprecation: `The "error" prop is deprecated.
|
|
22
|
+
deprecation: `The "error" prop is deprecated. Use "error-message" instead. Note that "error" accepted a boolean, but "error-message" expects a string, so update the value to an error message.`,
|
|
23
|
+
suggestReplace: `Replace "error" with "error-message". Remember to update the value from a boolean to a string error message.`,
|
|
23
24
|
},
|
|
24
|
-
|
|
25
|
-
schema: [
|
|
26
|
-
{
|
|
27
|
-
type: 'object',
|
|
28
|
-
properties: {
|
|
29
|
-
autofix: {
|
|
30
|
-
type: 'boolean',
|
|
31
|
-
default: false,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
additionalProperties: false,
|
|
35
|
-
},
|
|
36
|
-
],
|
|
25
|
+
hasSuggestions: true,
|
|
26
|
+
schema: [],
|
|
37
27
|
},
|
|
38
28
|
|
|
39
29
|
/** @param {RuleContext} context */
|
|
40
30
|
create(context) {
|
|
41
|
-
const options = context.options[0] || {};
|
|
42
|
-
const autofix = options.autofix === true;
|
|
43
|
-
|
|
44
|
-
/* ------------------------------------------------ */
|
|
45
|
-
/* CASE 2: PROP DEPRECATION (Multiple Components) */
|
|
46
|
-
/* ------------------------------------------------ */
|
|
47
31
|
return VueUtils.defineTemplateBodyVisitor(context, {
|
|
48
|
-
// 1. STATIC:
|
|
32
|
+
// 1. STATIC: error="foo"
|
|
49
33
|
[`VElement:matches([name="bento-dropdown"], [name="BentoDropdown"], [name="bento-input-field"], [name="BentoInputField"], [name="bento-input-field-phone-number"], [name="BentoInputFieldPhoneNumber"]) > VStartTag > VAttribute[directive=false][key.name="error"]`](
|
|
50
34
|
node
|
|
51
35
|
) {
|
|
52
36
|
context.report({
|
|
53
37
|
node,
|
|
54
38
|
messageId: 'deprecation',
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
39
|
+
suggest: [
|
|
40
|
+
{
|
|
41
|
+
messageId: 'suggestReplace',
|
|
42
|
+
fix(fixer) {
|
|
43
|
+
return fixer.replaceText(node.key, 'error-message');
|
|
44
|
+
},
|
|
58
45
|
},
|
|
59
|
-
|
|
46
|
+
],
|
|
60
47
|
});
|
|
61
48
|
},
|
|
62
49
|
|
|
63
|
-
// 2. DYNAMIC: :
|
|
50
|
+
// 2. DYNAMIC: :error="foo" or v-bind:error="foo"
|
|
64
51
|
[`VElement:matches([name="bento-dropdown"], [name="BentoDropdown"], [name="bento-input-field"], [name="BentoInputField"], [name="bento-input-field-phone-number"], [name="BentoInputFieldPhoneNumber"]) > VStartTag > VAttribute[directive=true][key.name.name="bind"][key.argument.name="error"]`](
|
|
65
52
|
node
|
|
66
53
|
) {
|
|
67
54
|
context.report({
|
|
68
55
|
node,
|
|
69
56
|
messageId: 'deprecation',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
suggest: [
|
|
58
|
+
{
|
|
59
|
+
messageId: 'suggestReplace',
|
|
60
|
+
fix(fixer) {
|
|
61
|
+
return fixer.replaceText(node.key.argument, 'error-message');
|
|
62
|
+
},
|
|
73
63
|
},
|
|
74
|
-
|
|
64
|
+
],
|
|
75
65
|
});
|
|
76
66
|
},
|
|
77
67
|
});
|