@fkui/vue 5.36.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/LICENSE.md +7 -0
- package/README.md +3 -0
- package/dist/cjs/index.cjs.js +896 -0
- package/dist/cjs/index.cjs.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pageobject.js +1692 -0
- package/dist/cjs/pageobject.js.map +7 -0
- package/dist/esm/index.esm.js +896 -0
- package/dist/esm/index.esm.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/types/index.d.ts +9441 -0
- package/dist/types/pageobject.d.ts +1028 -0
- package/dist/types/tsdoc-metadata.json +11 -0
- package/htmlvalidate/configs/index.js +5 -0
- package/htmlvalidate/configs/recommended.js +17 -0
- package/htmlvalidate/cypress.d.ts +2 -0
- package/htmlvalidate/cypress.js +18 -0
- package/htmlvalidate/elements/components.js +1476 -0
- package/htmlvalidate/elements/internal-components.js +240 -0
- package/htmlvalidate/elements/overrides.js +9 -0
- package/htmlvalidate/index.d.ts +4 -0
- package/htmlvalidate/index.js +10 -0
- package/htmlvalidate/rules/buttongroup.rule.js +42 -0
- package/htmlvalidate/rules/classdeprecated.rule.js +32 -0
- package/htmlvalidate/rules/common.js +15 -0
- package/htmlvalidate/rules/deprecated-validator.js +63 -0
- package/htmlvalidate/rules/ftextfieldFormatterValidation.rule.js +102 -0
- package/htmlvalidate/rules/index.js +15 -0
- package/htmlvalidate/rules/prefer-ficon.rule.js +36 -0
- package/htmlvalidate/rules/requiredmaxlength.rule.js +96 -0
- package/package.json +113 -0
- package/pageobject.d.ts +1 -0
- package/pageobject.js +1 -0
- package/tsconfig-consumer.json +12 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
const { defineMetadata } = require("html-validate");
|
|
2
|
+
|
|
3
|
+
module.exports = defineMetadata({
|
|
4
|
+
"i-flex": {
|
|
5
|
+
flow: true,
|
|
6
|
+
attributes: {
|
|
7
|
+
gap: ["1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x"],
|
|
8
|
+
collapse: {
|
|
9
|
+
boolean: true,
|
|
10
|
+
required: false,
|
|
11
|
+
},
|
|
12
|
+
wrap: {
|
|
13
|
+
boolean: true,
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
float: ["left", "center", "right"],
|
|
17
|
+
},
|
|
18
|
+
permittedContent: ["i-flex-item"],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"i-flex-item": {
|
|
22
|
+
flow: true,
|
|
23
|
+
attributes: {
|
|
24
|
+
align: ["top", "center", "bottom"],
|
|
25
|
+
grow: [],
|
|
26
|
+
shrink: [],
|
|
27
|
+
},
|
|
28
|
+
requiredAncestors: ["i-flex > i-flex-item"],
|
|
29
|
+
permittedContent: ["@flow"],
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
"i-calendar-day": {
|
|
33
|
+
flow: true,
|
|
34
|
+
phrasing: true,
|
|
35
|
+
permittedContent: ["@flow", "@phrasing"],
|
|
36
|
+
attributes: {
|
|
37
|
+
day: {
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
enabled: {
|
|
41
|
+
boolean: true,
|
|
42
|
+
},
|
|
43
|
+
highlight: {
|
|
44
|
+
boolean: true,
|
|
45
|
+
},
|
|
46
|
+
focused: {
|
|
47
|
+
boolean: true,
|
|
48
|
+
},
|
|
49
|
+
selected: {
|
|
50
|
+
boolean: true,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
"i-calendar-month": {
|
|
56
|
+
flow: true,
|
|
57
|
+
phrasing: true,
|
|
58
|
+
interactive: true,
|
|
59
|
+
permittedContent: ["template"],
|
|
60
|
+
attributes: {
|
|
61
|
+
"entry-date": {},
|
|
62
|
+
"min-date": {
|
|
63
|
+
required: true,
|
|
64
|
+
},
|
|
65
|
+
"max-date": {
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
slots: ["default"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
"i-calendar-month#default": {
|
|
73
|
+
permittedContent: ["f-calendar-day", "slot"],
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
"i-calendar-month-grid": {
|
|
77
|
+
flow: true,
|
|
78
|
+
phrasing: true,
|
|
79
|
+
permittedContent: ["template"],
|
|
80
|
+
attributes: {
|
|
81
|
+
"hide-week-numbers": {},
|
|
82
|
+
},
|
|
83
|
+
slots: ["default"],
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
"i-calendar-month-grid#default": {
|
|
87
|
+
permittedContent: ["button"],
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
"i-calendar-navbar": {
|
|
91
|
+
flow: true,
|
|
92
|
+
phrasing: true,
|
|
93
|
+
interactive: true,
|
|
94
|
+
permittedContent: [],
|
|
95
|
+
attributes: {
|
|
96
|
+
"min-date": {
|
|
97
|
+
required: true,
|
|
98
|
+
},
|
|
99
|
+
"max-date": {
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
"i-calendar-tab-main": {
|
|
106
|
+
flow: true,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
"i-animate-expand": {
|
|
110
|
+
flow: true,
|
|
111
|
+
attributes: {
|
|
112
|
+
expanded: ["/.*/"],
|
|
113
|
+
animate: {
|
|
114
|
+
boolean: true,
|
|
115
|
+
},
|
|
116
|
+
opacity: {
|
|
117
|
+
boolean: true,
|
|
118
|
+
},
|
|
119
|
+
"use-v-show": {
|
|
120
|
+
boolean: true,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
slots: ["default"],
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
"i-deprecated-calendar-year-month-item": {
|
|
127
|
+
flow: true,
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
"i-deprecated-calendar-month": {
|
|
131
|
+
flow: true,
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
"i-deprecated-calendar-year": {
|
|
135
|
+
flow: true,
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
"i-deprecated-calendar-month-day": {
|
|
139
|
+
flow: true,
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
"i-deprecated-calendar-month-week": {
|
|
143
|
+
flow: true,
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
"i-skip-link": {
|
|
147
|
+
inherit: "a",
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
"i-validation-form": {
|
|
151
|
+
deprecated: {
|
|
152
|
+
message:
|
|
153
|
+
"<i-validation-form> has been renamed to <f-validation-form>",
|
|
154
|
+
source: "fkui",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
"i-popup": {
|
|
159
|
+
flow: true,
|
|
160
|
+
interactive: true,
|
|
161
|
+
permittedContent: ["@flow"],
|
|
162
|
+
attributes: {
|
|
163
|
+
"is-open": {
|
|
164
|
+
boolean: true,
|
|
165
|
+
required: true,
|
|
166
|
+
},
|
|
167
|
+
anchor: {
|
|
168
|
+
required: true,
|
|
169
|
+
},
|
|
170
|
+
"always-inline": {
|
|
171
|
+
boolean: true,
|
|
172
|
+
required: false,
|
|
173
|
+
deprecated:
|
|
174
|
+
'`always-inline` is deprecated: use `inline="always"` instead.',
|
|
175
|
+
},
|
|
176
|
+
inline: {
|
|
177
|
+
required: false,
|
|
178
|
+
enum: ["always", "never", "auto"],
|
|
179
|
+
},
|
|
180
|
+
container: {},
|
|
181
|
+
viewport: {
|
|
182
|
+
required: false,
|
|
183
|
+
},
|
|
184
|
+
"focus-element": {
|
|
185
|
+
required: false,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
"i-popup-target": {
|
|
191
|
+
flow: true,
|
|
192
|
+
interactive: true,
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
"i-popup-error": {
|
|
196
|
+
flow: true,
|
|
197
|
+
interactive: true,
|
|
198
|
+
permittedContent: ["@flow"],
|
|
199
|
+
attributes: {
|
|
200
|
+
"is-open": {
|
|
201
|
+
boolean: true,
|
|
202
|
+
required: true,
|
|
203
|
+
},
|
|
204
|
+
"error-message": {
|
|
205
|
+
required: false,
|
|
206
|
+
},
|
|
207
|
+
anchor: {
|
|
208
|
+
required: true,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
"i-menu": {
|
|
213
|
+
flow: true,
|
|
214
|
+
interactive: true,
|
|
215
|
+
permittedContent: [],
|
|
216
|
+
textContent: "none",
|
|
217
|
+
attributes: {
|
|
218
|
+
items: {
|
|
219
|
+
required: true,
|
|
220
|
+
},
|
|
221
|
+
vertical: {
|
|
222
|
+
boolean: true,
|
|
223
|
+
required: false,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
"i-popup-menu": {
|
|
229
|
+
inherit: "i-menu",
|
|
230
|
+
attributes: {
|
|
231
|
+
"is-open": {
|
|
232
|
+
boolean: true,
|
|
233
|
+
required: true,
|
|
234
|
+
},
|
|
235
|
+
anchor: {
|
|
236
|
+
required: false,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { defineMetadata } = require("html-validate");
|
|
2
|
+
|
|
3
|
+
module.exports = defineMetadata({
|
|
4
|
+
/* override <title> element so it can be used under <f-icon> as well (and is
|
|
5
|
+
* inserted inside the <svg>) */
|
|
6
|
+
title: {
|
|
7
|
+
permittedParent: ["head", "f-icon"],
|
|
8
|
+
},
|
|
9
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { Rule } = require("html-validate");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
class ButtonGroupRule extends Rule {
|
|
5
|
+
documentation() {
|
|
6
|
+
return {
|
|
7
|
+
description:
|
|
8
|
+
"Buttons in a FKUI button-group must have the class button-group__item ",
|
|
9
|
+
url: getDocumentationUrl("/components/button.html#gruppering"),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setup() {
|
|
14
|
+
/* listen on dom ready event */
|
|
15
|
+
this.on("dom:ready", (event) => {
|
|
16
|
+
this.reportButtonGroupViolations(event);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reportButtonGroupViolations(event) {
|
|
21
|
+
const buttonGroups = event.document.querySelectorAll(".button-group");
|
|
22
|
+
|
|
23
|
+
for (const buttonGroup of buttonGroups) {
|
|
24
|
+
this.nestedButtonsHaveGroupItems(buttonGroup);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
nestedButtonsHaveGroupItems(buttonGroup) {
|
|
29
|
+
const buttons = buttonGroup.querySelectorAll("button");
|
|
30
|
+
|
|
31
|
+
for (const button of buttons) {
|
|
32
|
+
if (button.classList.contains("button-group__item") === false) {
|
|
33
|
+
this.report(
|
|
34
|
+
button,
|
|
35
|
+
"A button that is a direct child to a button group must have the following classes: ['button-group__item']",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = ButtonGroupRule;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { Rule } = require("html-validate");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
class ClassDeprecated extends Rule {
|
|
5
|
+
documentation() {
|
|
6
|
+
return {
|
|
7
|
+
description:
|
|
8
|
+
"The class button--text is deprecated and should be replaced with button--discrete",
|
|
9
|
+
url: getDocumentationUrl("/components/button.html"),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setup() {
|
|
14
|
+
/* listen on dom ready event */
|
|
15
|
+
this.on("dom:ready", (event) => {
|
|
16
|
+
this.reportDeprecatedClass(event);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reportDeprecatedClass(event) {
|
|
21
|
+
const buttons = event.document.querySelectorAll(".button--text");
|
|
22
|
+
|
|
23
|
+
for (const button of buttons) {
|
|
24
|
+
this.report(
|
|
25
|
+
button,
|
|
26
|
+
"['button--text'] is deprecated, please use ['button--discrete'] instead",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = ClassDeprecated;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { homepage } = require("../../package.json");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
* @param {string} path
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
function getDocumentationUrl(path) {
|
|
9
|
+
if (path.startsWith("/")) {
|
|
10
|
+
path = path.slice(1);
|
|
11
|
+
}
|
|
12
|
+
return `${homepage}${path}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { getDocumentationUrl };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { Rule, sliceLocation } = require("html-validate");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
const replacements = {
|
|
5
|
+
personnummer: ["personnummerLuhn", "personnummerFormat"],
|
|
6
|
+
};
|
|
7
|
+
const deprecated = Object.keys(replacements);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} text
|
|
11
|
+
* @returns {IterableIterator<{ value: string, begin: number, end: number }>}
|
|
12
|
+
*/
|
|
13
|
+
function* splitModifiers(text) {
|
|
14
|
+
const splitDots = /\.([^.]*)/g;
|
|
15
|
+
let match;
|
|
16
|
+
while ((match = splitDots.exec(text)) !== null) {
|
|
17
|
+
const value = match[1];
|
|
18
|
+
const end = splitDots.lastIndex;
|
|
19
|
+
const begin = end - value.length;
|
|
20
|
+
yield {
|
|
21
|
+
value,
|
|
22
|
+
begin,
|
|
23
|
+
end,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class DeprecatedValidator extends Rule {
|
|
29
|
+
documentation(context) {
|
|
30
|
+
return {
|
|
31
|
+
description: [
|
|
32
|
+
`The \`${context}\` validator is deprecated, replace with one or more of:`,
|
|
33
|
+
"",
|
|
34
|
+
...replacements[context].map((it) => ` - \`${it}\``),
|
|
35
|
+
].join("\n"),
|
|
36
|
+
url: getDocumentationUrl("/components/validering/validatorer.html"),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setup() {
|
|
41
|
+
this.on("attr", (event) => {
|
|
42
|
+
const { key, keyLocation } = event;
|
|
43
|
+
if (!key.startsWith("v-validation.")) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const modifier of splitModifiers(key)) {
|
|
47
|
+
const { value: validator, begin, end } = modifier;
|
|
48
|
+
if (!deprecated.includes(validator)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const location = sliceLocation(keyLocation, begin, end);
|
|
52
|
+
this.report({
|
|
53
|
+
node: event.target,
|
|
54
|
+
context: validator,
|
|
55
|
+
location,
|
|
56
|
+
message: `validator "${validator}" is deprecated`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = DeprecatedValidator;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const { Rule } = require("html-validate");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("html-validate").HtmlElement} HtmlElement
|
|
6
|
+
* @typedef {import("html-validate").Location} Location
|
|
7
|
+
*
|
|
8
|
+
* @typedef {object} ValidationDirective
|
|
9
|
+
* @property {string} directive Validation directive.
|
|
10
|
+
* @property {Set<string>} modifiers Validation modifiers.
|
|
11
|
+
* @property {Location} location Attribute location (key)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const matchVValidation = /^v-validation($|[.])/;
|
|
15
|
+
const validatorsWithFormatters = [
|
|
16
|
+
"bankAccountNumber",
|
|
17
|
+
"bankgiro",
|
|
18
|
+
"clearingnumber",
|
|
19
|
+
"date",
|
|
20
|
+
"number",
|
|
21
|
+
"personnummer",
|
|
22
|
+
"plusgiro",
|
|
23
|
+
"postalCode",
|
|
24
|
+
"organisationsnummer",
|
|
25
|
+
"percent",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function isRelevant(node) {
|
|
29
|
+
return node.target.tagName === "f-text-field";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class FtextFieldFormatterValidation extends Rule {
|
|
33
|
+
documentation() {
|
|
34
|
+
return {
|
|
35
|
+
description:
|
|
36
|
+
"FTextField is using a validator that previously provided formatting, \
|
|
37
|
+
now formatting must be provided separately using a formatter and/or parser. \
|
|
38
|
+
You can also consider switching to extended components.",
|
|
39
|
+
url: getDocumentationUrl(
|
|
40
|
+
"/components/inmatning/inmatningsfalt.html",
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setup() {
|
|
46
|
+
this.on("element:ready", isRelevant, (event) => {
|
|
47
|
+
const target = event.target;
|
|
48
|
+
|
|
49
|
+
const validationDirective = this.getValidationDirective(target);
|
|
50
|
+
if (!validationDirective) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const validator = this.getValidator(validationDirective);
|
|
55
|
+
if (!validator) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const formatter = target.getAttribute(":formatter");
|
|
60
|
+
const parser = target.getAttribute(":parser");
|
|
61
|
+
if (formatter || parser) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.report(
|
|
66
|
+
target,
|
|
67
|
+
`v-validation.${validator} must be used with :formatter and/or :parser`,
|
|
68
|
+
validationDirective.location,
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {HtmlElement} target
|
|
75
|
+
* @returns {ValidationDirective | null} Directive and modifiers
|
|
76
|
+
*/
|
|
77
|
+
getValidationDirective(target) {
|
|
78
|
+
const elementAttrs = target.attributes;
|
|
79
|
+
const attr = elementAttrs.find((it) => matchVValidation.test(it.key));
|
|
80
|
+
if (!attr) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const [directive, ...modifiers] = attr.key.split(".");
|
|
84
|
+
return {
|
|
85
|
+
directive,
|
|
86
|
+
modifiers: new Set(modifiers),
|
|
87
|
+
location: attr.keyLocation,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {ValidationDirective} validation
|
|
93
|
+
* @return {string}
|
|
94
|
+
*/
|
|
95
|
+
getValidator(validation) {
|
|
96
|
+
return validatorsWithFormatters.find((it) => {
|
|
97
|
+
return validation.modifiers.has(it);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = FtextFieldFormatterValidation;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const buttongroup = require("./buttongroup.rule");
|
|
2
|
+
const classdeprecated = require("./classdeprecated.rule");
|
|
3
|
+
const DeprecatedValidator = require("./deprecated-validator");
|
|
4
|
+
const PreferFIcon = require("./prefer-ficon.rule");
|
|
5
|
+
const requiredmaxlength = require("./requiredmaxlength.rule");
|
|
6
|
+
const ftextfieldFormatterValidation = require("./ftextfieldFormatterValidation.rule");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
"fkui/button-group": buttongroup,
|
|
10
|
+
"fkui/class-deprecated": classdeprecated,
|
|
11
|
+
"fkui/deprecated-validator": DeprecatedValidator,
|
|
12
|
+
"fkui/prefer-ficon": PreferFIcon,
|
|
13
|
+
"fkui/required-max-length": requiredmaxlength,
|
|
14
|
+
"fkui/ftextfield-formatter-validation": ftextfieldFormatterValidation,
|
|
15
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { Rule } = require("html-validate/node");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("html-validate/node").ElementReadyEvent} ElementReadyEvent
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {ElementReadyEvent} event
|
|
11
|
+
*/
|
|
12
|
+
function isRelevant(event) {
|
|
13
|
+
const { target } = event;
|
|
14
|
+
return target.is("svg") && target.classList.includes("icon");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class PreferFIcon extends Rule {
|
|
18
|
+
documentation() {
|
|
19
|
+
return {
|
|
20
|
+
description:
|
|
21
|
+
"Prefer using `<f-icon>` instead of directly using `<svg>`",
|
|
22
|
+
url: getDocumentationUrl("/components/ficon.html"),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setup() {
|
|
27
|
+
this.on("element:ready", isRelevant, () => {
|
|
28
|
+
this.report({
|
|
29
|
+
message:
|
|
30
|
+
"Prefer using <f-icon> instead of directly using <svg>",
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = PreferFIcon;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const { Rule } = require("html-validate");
|
|
2
|
+
const { getDocumentationUrl } = require("./common");
|
|
3
|
+
|
|
4
|
+
const formatters = [
|
|
5
|
+
"bankaccountnumber",
|
|
6
|
+
"bankgiro",
|
|
7
|
+
"clearingnumber",
|
|
8
|
+
"dateformat",
|
|
9
|
+
"date",
|
|
10
|
+
"email",
|
|
11
|
+
"maxlength",
|
|
12
|
+
"maxvalue",
|
|
13
|
+
"organisationsnummer",
|
|
14
|
+
"personnummer",
|
|
15
|
+
"phonenumber",
|
|
16
|
+
"plusgiro",
|
|
17
|
+
"postalCode",
|
|
18
|
+
];
|
|
19
|
+
const allowedTags = ["input", "f-text-field"];
|
|
20
|
+
|
|
21
|
+
class RequiredMaxLength extends Rule {
|
|
22
|
+
documentation() {
|
|
23
|
+
return {
|
|
24
|
+
description: "Element is missing a maxlength-validation",
|
|
25
|
+
url: getDocumentationUrl(
|
|
26
|
+
"/components/inmatning/inmatningsfalt.html",
|
|
27
|
+
),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setup() {
|
|
32
|
+
this.on("element:ready", (event) => {
|
|
33
|
+
const attributeKeys = [];
|
|
34
|
+
const target = event.target;
|
|
35
|
+
const elementAttrs = target.attributes;
|
|
36
|
+
const tagName = target.tagName;
|
|
37
|
+
const valueOfType = target.getAttributeValue("type");
|
|
38
|
+
|
|
39
|
+
if (!allowedTags.includes(tagName)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < elementAttrs.length; i++) {
|
|
44
|
+
attributeKeys.push(elementAttrs[i].key);
|
|
45
|
+
}
|
|
46
|
+
const validation = attributeKeys.find((attr) =>
|
|
47
|
+
attr.includes("v-validation"),
|
|
48
|
+
);
|
|
49
|
+
const maxlength =
|
|
50
|
+
attributeKeys.find((attr) => attr.includes("maxlength")) ===
|
|
51
|
+
"maxlength";
|
|
52
|
+
|
|
53
|
+
if (maxlength) {
|
|
54
|
+
return;
|
|
55
|
+
} else {
|
|
56
|
+
this.checkValidation(
|
|
57
|
+
formatters,
|
|
58
|
+
validation,
|
|
59
|
+
target,
|
|
60
|
+
valueOfType,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
checkValidation(formatters, validation, target, valueOfType) {
|
|
67
|
+
if ((!valueOfType || valueOfType === "text") && validation) {
|
|
68
|
+
this.checkError(formatters, validation, target);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!validation) {
|
|
72
|
+
this.checkInputType(valueOfType, target);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
checkError(formatters, validation, target) {
|
|
77
|
+
const validators = validation.split(".");
|
|
78
|
+
const lengthValidators = validators.filter((attrs) =>
|
|
79
|
+
formatters.includes(attrs),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (lengthValidators.length === 0) {
|
|
83
|
+
this.report(target, `v-validation must have a maxlength-validator`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
checkInputType(valueOfType, target) {
|
|
88
|
+
if (valueOfType && valueOfType !== "text") {
|
|
89
|
+
return;
|
|
90
|
+
} else {
|
|
91
|
+
this.report(target, "v-validation.maxLength must be used");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = RequiredMaxLength;
|