@moni-labs/moni-ui 0.4.13 → 0.4.14
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/custom-elements.json +91 -81
- package/dist/browser/moni-ui.iife.min.js +1537 -1384
- package/dist/browser/moni-ui.iife.min.js.br +0 -0
- package/dist/browser/moni-ui.iife.min.js.gz +0 -0
- package/dist/browser/moni-ui.iife.min.js.map +1 -1
- package/dist/browser/moni-ui.min.js +2703 -2481
- package/dist/browser/moni-ui.min.js.br +0 -0
- package/dist/browser/moni-ui.min.js.gz +0 -0
- package/dist/browser/moni-ui.min.js.map +1 -1
- package/dist/cdn/chunks/{class-map-DsL1Jr2T.js → class-map-QyT9W7rZ.js} +1 -1
- package/dist/cdn/chunks/{decorate-DxJYS4gw.js → decorate-B7wZvwrn.js} +629 -543
- package/dist/cdn/chunks/if-defined-5hr_ccFw.js +1 -0
- package/dist/cdn/chunks/{live-D52JqeKf.js → live-Hm1Di_ND.js} +1 -1
- package/dist/cdn/components/moni-app-bar.js +1 -1
- package/dist/cdn/components/moni-badge.js +1 -1
- package/dist/cdn/components/moni-bottom-sheet.js +1 -1
- package/dist/cdn/components/moni-button-group.js +1 -1
- package/dist/cdn/components/moni-button-segment.js +1 -1
- package/dist/cdn/components/moni-button.js +1 -1
- package/dist/cdn/components/moni-card.js +1 -1
- package/dist/cdn/components/moni-carousel.js +1 -1
- package/dist/cdn/components/moni-checkbox.js +1 -1
- package/dist/cdn/components/moni-chip.js +1 -1
- package/dist/cdn/components/moni-color-field.js +1 -1
- package/dist/cdn/components/moni-context-menu.js +1 -1
- package/dist/cdn/components/moni-dialog.js +1 -1
- package/dist/cdn/components/moni-divider.js +1 -1
- package/dist/cdn/components/moni-expansion.js +1 -1
- package/dist/cdn/components/moni-fab-menu.js +1 -1
- package/dist/cdn/components/moni-fab.js +1 -1
- package/dist/cdn/components/moni-file-field.js +1 -1
- package/dist/cdn/components/moni-icon.js +1 -1
- package/dist/cdn/components/moni-list-item.js +1 -1
- package/dist/cdn/components/moni-list.js +1 -1
- package/dist/cdn/components/moni-loading-indicator.js +1 -1
- package/dist/cdn/components/moni-menu-item.js +1 -1
- package/dist/cdn/components/moni-menu.js +1 -1
- package/dist/cdn/components/moni-morph-modal.js +3 -2
- package/dist/cdn/components/moni-nav-item.js +1 -1
- package/dist/cdn/components/moni-nav.js +1 -1
- package/dist/cdn/components/moni-progress.js +1 -1
- package/dist/cdn/components/moni-radio.js +1 -1
- package/dist/cdn/components/moni-ripple.js +1 -1
- package/dist/cdn/components/moni-segmented-button.js +1 -1
- package/dist/cdn/components/moni-select-option.js +1 -1
- package/dist/cdn/components/moni-select.js +512 -473
- package/dist/cdn/components/moni-shape.js +1 -1
- package/dist/cdn/components/moni-side-sheet.js +1 -1
- package/dist/cdn/components/moni-slider.js +1 -1
- package/dist/cdn/components/moni-snackbar.js +1 -1
- package/dist/cdn/components/moni-split-button.js +1 -1
- package/dist/cdn/components/moni-step.js +1 -1
- package/dist/cdn/components/moni-stepper.js +1 -1
- package/dist/cdn/components/moni-switch.js +1 -1
- package/dist/cdn/components/moni-tab.js +1 -1
- package/dist/cdn/components/moni-tabs.js +1 -1
- package/dist/cdn/components/moni-text-field.js +122 -95
- package/dist/cdn/components/moni-textarea.js +52 -52
- package/dist/cdn/components/moni-time-picker.js +1 -1
- package/dist/cdn/components/moni-toolbar.js +1 -1
- package/dist/cdn/components/moni-tooltip.js +1 -1
- package/dist/cdn/components/moni-typography.js +1 -1
- package/dist/cdn/importmap.json +98 -98
- package/dist/components/_base/field-styles.d.ts.map +1 -1
- package/dist/components/_base/field-styles.js +662 -576
- package/dist/components/moni-morph-modal.d.ts +10 -0
- package/dist/components/moni-morph-modal.d.ts.map +1 -1
- package/dist/components/moni-morph-modal.js +323 -217
- package/dist/components/moni-select.d.ts +16 -16
- package/dist/components/moni-select.d.ts.map +1 -1
- package/dist/components/moni-select.js +797 -727
- package/dist/components/moni-text-field.d.ts +15 -15
- package/dist/components/moni-text-field.d.ts.map +1 -1
- package/dist/components/moni-text-field.js +224 -180
- package/dist/components/moni-textarea.d.ts +13 -13
- package/dist/components/moni-textarea.d.ts.map +1 -1
- package/dist/components/moni-textarea.js +127 -122
- package/docs/api.json +97 -88
- package/docs/assets/moni-morph-modal/autoHeight--false.png +0 -0
- package/docs/assets/moni-morph-modal/autoHeight--true.png +0 -0
- package/docs/components/morph-modal.md +16 -7
- package/docs/components/select.md +74 -74
- package/docs/components/text-field.md +75 -66
- package/docs/components/textarea.md +51 -51
- package/docs/llms.txt +3 -3
- package/package.json +1 -1
- package/src/components/_base/field-styles.ts +723 -637
- package/src/components/moni-morph-modal.test.ts +490 -413
- package/src/components/moni-morph-modal.ts +2274 -2162
- package/src/components/moni-select.test.ts +489 -466
- package/src/components/moni-select.ts +1963 -1815
- package/src/components/moni-text-field.test.ts +366 -342
- package/src/components/moni-text-field.ts +717 -645
- package/src/components/moni-textarea.test.ts +201 -197
- package/src/components/moni-textarea.ts +476 -460
- package/dist/cdn/chunks/if-defined-DdzvcoI7.js +0 -1
|
@@ -1,342 +1,366 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from
|
|
2
|
-
import
|
|
3
|
-
import { MoniTextField } from
|
|
4
|
-
|
|
5
|
-
describe(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import "./moni-text-field.js";
|
|
3
|
+
import { MoniTextField } from "./moni-text-field.js";
|
|
4
|
+
|
|
5
|
+
describe("moni-text-field", () => {
|
|
6
|
+
let el: MoniTextField;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
el = document.createElement("moni-text-field") as MoniTextField;
|
|
10
|
+
document.body.appendChild(el);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
el.remove();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("renderiza un contenedor de campo (field wrapper) y un input", async () => {
|
|
18
|
+
await el.updateComplete;
|
|
19
|
+
const field = el.shadowRoot?.querySelector(".field");
|
|
20
|
+
const input = el.shadowRoot?.querySelector("input");
|
|
21
|
+
expect(field).toBeTruthy();
|
|
22
|
+
expect(input).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("renderiza un icono inicial (leading) cuando se establece el atributo icon", async () => {
|
|
26
|
+
el.icon = "search";
|
|
27
|
+
await el.updateComplete;
|
|
28
|
+
const leading = el.shadowRoot?.querySelector(".leading-icon");
|
|
29
|
+
expect(leading).toBeTruthy();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renderiza un icono final (trailing) cuando se establece el atributo trailing-icon", async () => {
|
|
33
|
+
el.setAttribute("trailing-icon", "close");
|
|
34
|
+
await el.updateComplete;
|
|
35
|
+
const trailing = el.shadowRoot?.querySelector(".trailing-icon");
|
|
36
|
+
expect(trailing).toBeTruthy();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("renderiza un texto prefijo cuando se establece el atributo prefix", async () => {
|
|
40
|
+
el.prefix = "https://";
|
|
41
|
+
await el.updateComplete;
|
|
42
|
+
const leading = el.shadowRoot?.querySelector(".leading-icon");
|
|
43
|
+
expect(leading?.textContent?.trim()).toBe("https://");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("renderiza un texto sufijo cuando se establece el atributo suffix", async () => {
|
|
47
|
+
el.suffix = ".com";
|
|
48
|
+
await el.updateComplete;
|
|
49
|
+
const trailing = el.shadowRoot?.querySelector(".trailing-icon");
|
|
50
|
+
expect(trailing?.textContent?.trim()).toBe(".com");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("renderiza un botón suffix accesible y emite suffix-click", async () => {
|
|
54
|
+
el.suffixButtonIcon = "schedule";
|
|
55
|
+
el.suffixButtonLabel = "Elegir hora";
|
|
56
|
+
await el.updateComplete;
|
|
57
|
+
const button = el.shadowRoot?.querySelector(
|
|
58
|
+
".suffix-button",
|
|
59
|
+
) as HTMLButtonElement;
|
|
60
|
+
expect(button.getAttribute("aria-label")).toBe("Elegir hora");
|
|
61
|
+
let fired = false;
|
|
62
|
+
el.addEventListener("suffix-click", () => {
|
|
63
|
+
fired = true;
|
|
64
|
+
});
|
|
65
|
+
button.click();
|
|
66
|
+
expect(fired).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("acepta una acción personalizada mediante slot suffix", async () => {
|
|
70
|
+
const button = document.createElement("button");
|
|
71
|
+
button.slot = "suffix";
|
|
72
|
+
el.appendChild(button);
|
|
73
|
+
await el.updateComplete;
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
75
|
+
await el.updateComplete;
|
|
76
|
+
expect(
|
|
77
|
+
el.shadowRoot
|
|
78
|
+
?.querySelector(".field")
|
|
79
|
+
?.classList.contains("suffix-action-field"),
|
|
80
|
+
).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("aplica la clase square en el contenedor de campo para shape=no-round", async () => {
|
|
84
|
+
el.shape = "no-round";
|
|
85
|
+
await el.updateComplete;
|
|
86
|
+
const field = el.shadowRoot?.querySelector(".field");
|
|
87
|
+
expect(field?.classList.contains("square")).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("aplica la clase round en el contenedor de campo para shape=round", async () => {
|
|
91
|
+
el.shape = "round";
|
|
92
|
+
await el.updateComplete;
|
|
93
|
+
const field = el.shadowRoot?.querySelector(".field");
|
|
94
|
+
expect(field?.classList.contains("round")).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("aplica la clase small-round para un redondeado compacto", async () => {
|
|
98
|
+
el.shape = "small-round";
|
|
99
|
+
await el.updateComplete;
|
|
100
|
+
const field = el.shadowRoot?.querySelector(".field");
|
|
101
|
+
expect(field?.classList.contains("small-round")).toBe(true);
|
|
102
|
+
expect(field?.classList.contains("square")).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("renderiza underlined sin fondo, contorno completo ni forma redondeada", async () => {
|
|
106
|
+
el.variant = "underlined";
|
|
107
|
+
el.shape = "round";
|
|
108
|
+
await el.updateComplete;
|
|
109
|
+
const field = el.shadowRoot?.querySelector(".field");
|
|
110
|
+
expect(field?.classList.contains("fill")).toBe(false);
|
|
111
|
+
expect(field?.classList.contains("border")).toBe(false);
|
|
112
|
+
expect(field?.classList.contains("round")).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("muestra el indicador de carga cuando loading es true", async () => {
|
|
116
|
+
el.loading = true;
|
|
117
|
+
await el.updateComplete;
|
|
118
|
+
const trailing = el.shadowRoot?.querySelector(".trailing-icon");
|
|
119
|
+
const progress = trailing?.querySelector("moni-progress");
|
|
120
|
+
expect(progress).toBeTruthy();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("no renderiza un icono inicial por defecto", async () => {
|
|
124
|
+
await el.updateComplete;
|
|
125
|
+
const leading = el.shadowRoot?.querySelector(".leading-icon");
|
|
126
|
+
expect(leading).toBeFalsy();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("reenvía value al input", async () => {
|
|
130
|
+
el.value = "hello world";
|
|
131
|
+
await el.updateComplete;
|
|
132
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
133
|
+
expect(input.value).toBe("hello world");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("agrega la clase active al input cuando se establece el valor", async () => {
|
|
137
|
+
el.value = "something";
|
|
138
|
+
await el.updateComplete;
|
|
139
|
+
const input = el.shadowRoot?.querySelector("input");
|
|
140
|
+
expect(input?.classList.contains("active")).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("renderiza el label y alterna la clase active basándose en el valor", async () => {
|
|
144
|
+
el.label = "Email";
|
|
145
|
+
await el.updateComplete;
|
|
146
|
+
const label = el.shadowRoot?.querySelector("label");
|
|
147
|
+
expect(label?.textContent?.trim()).toBe("Email");
|
|
148
|
+
|
|
149
|
+
el.value = "a@b.com";
|
|
150
|
+
await el.updateComplete;
|
|
151
|
+
expect(label?.classList.contains("active")).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("formatea el valor usando una máscara y expone el valor sin formato", async () => {
|
|
155
|
+
el.mask = "99.999.999-9";
|
|
156
|
+
el.value = "123456789";
|
|
157
|
+
await el.updateComplete;
|
|
158
|
+
expect(el.value).toBe("12.345.678-9");
|
|
159
|
+
expect(el.unmaskedValue).toBe("123456789");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("descarta caracteres que no cumplen con las reglas de la máscara", async () => {
|
|
163
|
+
el.mask = "99-aa-**";
|
|
164
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
165
|
+
input.value = "1x2Aá7b";
|
|
166
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
167
|
+
await el.updateComplete;
|
|
168
|
+
expect(el.value).toBe("12-Aá-7b");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("permite borrar a través de los separadores de la máscara", async () => {
|
|
172
|
+
el.mask = "99.999-9";
|
|
173
|
+
el.value = "123456";
|
|
174
|
+
await el.updateComplete;
|
|
175
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
176
|
+
input.focus();
|
|
177
|
+
input.setSelectionRange(3, 3);
|
|
178
|
+
input.dispatchEvent(
|
|
179
|
+
new KeyboardEvent("keydown", {
|
|
180
|
+
key: "Backspace",
|
|
181
|
+
bubbles: true,
|
|
182
|
+
cancelable: true,
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
await el.updateComplete;
|
|
186
|
+
expect(el.value).not.toBe("12.345-6");
|
|
187
|
+
expect(el.unmaskedValue).toBe("13456");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("permite Delete delante de un separador de la máscara", async () => {
|
|
191
|
+
el.mask = "99-99";
|
|
192
|
+
el.value = "1234";
|
|
193
|
+
await el.updateComplete;
|
|
194
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
195
|
+
input.focus();
|
|
196
|
+
input.setSelectionRange(2, 2);
|
|
197
|
+
input.dispatchEvent(
|
|
198
|
+
new KeyboardEvent("keydown", {
|
|
199
|
+
key: "Delete",
|
|
200
|
+
bubbles: true,
|
|
201
|
+
cancelable: true,
|
|
202
|
+
}),
|
|
203
|
+
);
|
|
204
|
+
await el.updateComplete;
|
|
205
|
+
expect(el.value).toBe("12-4");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("acepta y normaliza K como dígito verificador de RUT", async () => {
|
|
209
|
+
el.mask = "99.999.999-K";
|
|
210
|
+
el.value = "12345678k";
|
|
211
|
+
await el.updateComplete;
|
|
212
|
+
expect(el.value).toBe("12.345.678-K");
|
|
213
|
+
expect(el.unmaskedValue).toBe("12345678K");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("acepta un dígito numérico en el token verificador K", async () => {
|
|
217
|
+
el.mask = "99.999.999-K";
|
|
218
|
+
el.value = "123456785";
|
|
219
|
+
await el.updateComplete;
|
|
220
|
+
expect(el.value).toBe("12.345.678-5");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("admite bloques opcionales y cuantificadores de Inputmask", async () => {
|
|
224
|
+
el.mask = "9{2,4}[-aa]";
|
|
225
|
+
el.value = "1234CL";
|
|
226
|
+
await el.updateComplete;
|
|
227
|
+
expect(el.value).toBe("1234-CL");
|
|
228
|
+
expect(el.unmaskedValue).toBe("1234CL");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("admite opciones avanzadas configuradas como objeto", async () => {
|
|
232
|
+
el.mask = "9999";
|
|
233
|
+
el.maskOptions = { placeholder: "·", showMaskOnHover: false };
|
|
234
|
+
el.value = "12";
|
|
235
|
+
await el.updateComplete;
|
|
236
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
237
|
+
input.focus();
|
|
238
|
+
expect(input.value).toContain("12");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("expone la instancia y los métodos completos de Inputmask", async () => {
|
|
242
|
+
el.mask = "999-999";
|
|
243
|
+
await el.updateComplete;
|
|
244
|
+
expect(el.inputmaskInstance).toBeTruthy();
|
|
245
|
+
expect(el.getEmptyMask()).toBe("-");
|
|
246
|
+
expect(el.isMaskValid("123-456")).toBe(true);
|
|
247
|
+
expect(el.formatWithMask("123456")).toBe("123-456");
|
|
248
|
+
el.setMaskedValue("987654");
|
|
249
|
+
expect(el.value).toBe("987-654");
|
|
250
|
+
expect(el.getMaskMetadata()).toBeTruthy();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("expone la API estática original de Inputmask", () => {
|
|
254
|
+
expect(MoniTextField.Inputmask.format("123456", { mask: "999-999" })).toBe(
|
|
255
|
+
"123-456",
|
|
256
|
+
);
|
|
257
|
+
expect(
|
|
258
|
+
MoniTextField.Inputmask.isValid("123-456", { mask: "999-999" }),
|
|
259
|
+
).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe("moni-text-field · nombre accesible", () => {
|
|
264
|
+
/*
|
|
265
|
+
* El <label> y el <input> son hermanos dentro del mismo shadow root: sin
|
|
266
|
+
* `for`/`id` no hay asociación y el lector de pantalla anuncia el campo sin
|
|
267
|
+
* nombre. La adyacencia no se puede cambiar porque `field-styles` la usa
|
|
268
|
+
* (`:focus + label`) para elevar la etiqueta, así que la unión va por id.
|
|
269
|
+
*/
|
|
270
|
+
it("asocia la etiqueta con el input mediante for/id", async () => {
|
|
271
|
+
const el = document.createElement("moni-text-field") as HTMLElement & {
|
|
272
|
+
label: string;
|
|
273
|
+
updateComplete: Promise<unknown>;
|
|
274
|
+
};
|
|
275
|
+
el.label = "Correo";
|
|
276
|
+
document.body.appendChild(el);
|
|
277
|
+
await el.updateComplete;
|
|
278
|
+
|
|
279
|
+
const input = el.shadowRoot?.querySelector("input");
|
|
280
|
+
const label = el.shadowRoot?.querySelector("label");
|
|
281
|
+
expect(input?.id).toBe("input");
|
|
282
|
+
expect(label?.getAttribute("for")).toBe("input");
|
|
283
|
+
expect(input?.nextElementSibling).toBe(label);
|
|
284
|
+
el.remove();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("moni-text-field · restricciones nativas", () => {
|
|
289
|
+
/*
|
|
290
|
+
* El componente sólo reenviaba type/placeholder/disabled/value/name, así que
|
|
291
|
+
* cualquier campo con `required` o `autocomplete` tenía que renunciar a
|
|
292
|
+
* moni-ui y usar un input nativo.
|
|
293
|
+
*/
|
|
294
|
+
it("reenvía las restricciones al input interno", async () => {
|
|
295
|
+
const el = document.createElement("moni-text-field") as HTMLElement & {
|
|
296
|
+
updateComplete: Promise<unknown>;
|
|
297
|
+
[key: string]: unknown;
|
|
298
|
+
};
|
|
299
|
+
Object.assign(el, {
|
|
300
|
+
label: "Correo",
|
|
301
|
+
type: "email",
|
|
302
|
+
required: true,
|
|
303
|
+
autocomplete: "email",
|
|
304
|
+
maxlength: 40,
|
|
305
|
+
minlength: 5,
|
|
306
|
+
pattern: ".+@.+",
|
|
307
|
+
inputmode: "email",
|
|
308
|
+
});
|
|
309
|
+
document.body.appendChild(el);
|
|
310
|
+
await el.updateComplete;
|
|
311
|
+
|
|
312
|
+
const input = el.shadowRoot?.querySelector("input") as HTMLInputElement;
|
|
313
|
+
expect(input.required).toBe(true);
|
|
314
|
+
expect(input.getAttribute("autocomplete")).toBe("email");
|
|
315
|
+
expect(input.maxLength).toBe(40);
|
|
316
|
+
expect(input.minLength).toBe(5);
|
|
317
|
+
expect(input.getAttribute("pattern")).toBe(".+@.+");
|
|
318
|
+
expect(input.getAttribute("inputmode")).toBe("email");
|
|
319
|
+
el.remove();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("acepta los tipos de fecha y hora", async () => {
|
|
323
|
+
const el = document.createElement("moni-text-field") as HTMLElement & {
|
|
324
|
+
type: string;
|
|
325
|
+
updateComplete: Promise<unknown>;
|
|
326
|
+
};
|
|
327
|
+
el.type = "date";
|
|
328
|
+
document.body.appendChild(el);
|
|
329
|
+
await el.updateComplete;
|
|
330
|
+
expect(el.shadowRoot?.querySelector("input")?.getAttribute("type")).toBe(
|
|
331
|
+
"date",
|
|
332
|
+
);
|
|
333
|
+
el.remove();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("publica la validez en el host para que el formulario la vea", async () => {
|
|
337
|
+
// jsdom no implementa checkValidity() en custom elements form-associated,
|
|
338
|
+
// así que se observa la llamada a setValidity. El bloqueo real del envío
|
|
339
|
+
// se verifica en navegador.
|
|
340
|
+
const calls: Array<Record<string, unknown>> = [];
|
|
341
|
+
const originalAttach = HTMLElement.prototype.attachInternals;
|
|
342
|
+
HTMLElement.prototype.attachInternals = function spy(this: HTMLElement) {
|
|
343
|
+
const internals = originalAttach.call(this);
|
|
344
|
+
const setValidity = internals.setValidity.bind(internals);
|
|
345
|
+
internals.setValidity = ((flags, message, anchor) => {
|
|
346
|
+
// ValidityState expone sus flags como getters del prototipo, así que un
|
|
347
|
+
// spread devolvería {}: hay que leerlas uña a uña.
|
|
348
|
+
calls.push({ valueMissing: flags?.valueMissing === true });
|
|
349
|
+
return setValidity(flags, message, anchor);
|
|
350
|
+
}) as typeof internals.setValidity;
|
|
351
|
+
return internals;
|
|
352
|
+
};
|
|
353
|
+
const el = document.createElement("moni-text-field") as HTMLElement & {
|
|
354
|
+
required: boolean;
|
|
355
|
+
updateComplete: Promise<unknown>;
|
|
356
|
+
};
|
|
357
|
+
HTMLElement.prototype.attachInternals = originalAttach;
|
|
358
|
+
|
|
359
|
+
el.required = true;
|
|
360
|
+
document.body.appendChild(el);
|
|
361
|
+
await el.updateComplete;
|
|
362
|
+
|
|
363
|
+
expect(calls.some((c) => c.valueMissing === true)).toBe(true);
|
|
364
|
+
el.remove();
|
|
365
|
+
});
|
|
366
|
+
});
|