@fiscozen/input 3.0.3 → 3.1.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.
@@ -1,1109 +1,1828 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import { FzInput } from '..'
1
+ import { describe, it, expect } from "vitest";
2
+ import { mount } from "@vue/test-utils";
3
+ import { FzInput } from "..";
4
4
 
5
- const NUMBER_OF_INPUTS = 1000
5
+ const NUMBER_OF_INPUTS = 1000;
6
6
 
7
- describe('FzInput', () => {
8
- describe('Rendering', () => {
9
- it('renders label', async () => {
7
+ describe("FzInput", () => {
8
+ describe("Rendering", () => {
9
+ it("renders label", async () => {
10
10
  const wrapper = mount(FzInput, {
11
11
  props: {
12
- label: 'Label',
12
+ label: "Label",
13
13
  },
14
14
  slots: {},
15
- })
15
+ });
16
16
 
17
- expect(wrapper.text()).toContain('Label')
18
- })
17
+ expect(wrapper.text()).toContain("Label");
18
+ });
19
19
 
20
- it('renders leftIcon', async () => {
20
+ it("renders leftIcon", async () => {
21
21
  const wrapper = mount(FzInput, {
22
22
  props: {
23
- label: 'Label',
24
- leftIcon: 'calendar-lines',
23
+ label: "Label",
24
+ leftIcon: "calendar-lines",
25
25
  },
26
26
  slots: {},
27
- })
27
+ });
28
28
 
29
- expect(wrapper.find('.fa-calendar-lines')).toBeTruthy()
30
- })
29
+ expect(wrapper.find(".fa-calendar-lines")).toBeTruthy();
30
+ });
31
31
 
32
- it('renders rightIcon', async () => {
32
+ it("renders rightIcon", async () => {
33
33
  const wrapper = mount(FzInput, {
34
34
  props: {
35
- label: 'Label',
36
- rightIcon: 'credit-card',
35
+ label: "Label",
36
+ rightIcon: "credit-card",
37
37
  },
38
38
  slots: {},
39
- })
39
+ });
40
40
 
41
- expect(wrapper.find('.fa-credit-card')).toBeTruthy()
42
- })
41
+ expect(wrapper.find(".fa-credit-card")).toBeTruthy();
42
+ });
43
43
 
44
- it('renders helpText', async () => {
44
+ it("renders helpText", async () => {
45
45
  const wrapper = mount(FzInput, {
46
46
  props: {
47
- label: 'Label',
47
+ label: "Label",
48
48
  },
49
49
  slots: {
50
- helpText: 'This is a helper text',
50
+ helpText: "This is a helper text",
51
51
  },
52
- })
52
+ });
53
53
 
54
- await wrapper.vm.$nextTick()
55
- expect(wrapper.text()).toContain('This is a helper text')
56
- })
54
+ await wrapper.vm.$nextTick();
55
+ expect(wrapper.text()).toContain("This is a helper text");
56
+ });
57
57
 
58
- it('renders errorMessage', async () => {
58
+ it("renders errorMessage", async () => {
59
59
  const wrapper = mount(FzInput, {
60
60
  props: {
61
- label: 'Label',
61
+ label: "Label",
62
62
  error: true,
63
63
  },
64
64
  slots: {
65
- errorMessage: 'This is an error message',
65
+ errorMessage: "This is an error message",
66
66
  },
67
- })
67
+ });
68
68
 
69
- await wrapper.vm.$nextTick()
70
- expect(wrapper.text()).toContain('This is an error message')
71
- })
72
- })
69
+ await wrapper.vm.$nextTick();
70
+ expect(wrapper.text()).toContain("This is an error message");
71
+ });
72
+ });
73
73
 
74
- describe('Input types', () => {
75
- it('renders email type', async () => {
74
+ describe("Input types", () => {
75
+ it("renders email type", async () => {
76
76
  const wrapper = mount(FzInput, {
77
77
  props: {
78
- label: 'Label',
79
- type: 'email',
78
+ label: "Label",
79
+ type: "email",
80
80
  },
81
81
  slots: {},
82
- })
82
+ });
83
83
 
84
- expect(wrapper.find('input').attributes('type')).toBe('email')
85
- })
84
+ expect(wrapper.find("input").attributes("type")).toBe("email");
85
+ });
86
86
 
87
- it('renders tel type', async () => {
87
+ it("renders tel type", async () => {
88
88
  const wrapper = mount(FzInput, {
89
89
  props: {
90
- label: 'Label',
91
- type: 'tel',
90
+ label: "Label",
91
+ type: "tel",
92
92
  },
93
93
  slots: {},
94
- })
94
+ });
95
95
 
96
- expect(wrapper.find('input').attributes('type')).toBe('tel')
97
- })
96
+ expect(wrapper.find("input").attributes("type")).toBe("tel");
97
+ });
98
98
 
99
- it('renders password type', async () => {
99
+ it("renders password type", async () => {
100
100
  const wrapper = mount(FzInput, {
101
101
  props: {
102
- label: 'Label',
103
- type: 'password',
102
+ label: "Label",
103
+ type: "password",
104
104
  },
105
105
  slots: {},
106
- })
106
+ });
107
107
 
108
- expect(wrapper.find('input').attributes('type')).toBe('password')
109
- })
110
- })
108
+ expect(wrapper.find("input").attributes("type")).toBe("password");
109
+ });
110
+ });
111
111
 
112
- describe('Input states', () => {
113
- it('renders disabled', async () => {
112
+ describe("Input states", () => {
113
+ it("renders disabled", async () => {
114
114
  const wrapper = mount(FzInput, {
115
115
  props: {
116
- label: 'Label',
116
+ label: "Label",
117
117
  disabled: true,
118
118
  },
119
119
  slots: {},
120
- })
120
+ });
121
121
 
122
- expect(wrapper.find('input').attributes('disabled')).toBe('')
123
- })
122
+ expect(wrapper.find("input").attributes("disabled")).toBe("");
123
+ });
124
124
 
125
- it('renders required', async () => {
125
+ it("renders required", async () => {
126
126
  const wrapper = mount(FzInput, {
127
127
  props: {
128
- label: 'Label',
128
+ label: "Label",
129
129
  required: true,
130
130
  },
131
131
  slots: {},
132
- })
132
+ });
133
133
 
134
- await wrapper.vm.$nextTick()
134
+ await wrapper.vm.$nextTick();
135
135
 
136
- expect(wrapper.find('input').attributes('required')).toBe('')
137
- expect(wrapper.text()).toContain('*')
138
- })
136
+ expect(wrapper.find("input").attributes("required")).toBe("");
137
+ expect(wrapper.text()).toContain("*");
138
+ });
139
139
 
140
140
  it('applies autocomplete="off" by default', async () => {
141
141
  const wrapper = mount(FzInput, {
142
142
  props: {
143
- label: 'Label',
143
+ label: "Label",
144
144
  },
145
145
  slots: {},
146
- })
146
+ });
147
147
 
148
- await wrapper.vm.$nextTick()
148
+ await wrapper.vm.$nextTick();
149
149
 
150
- expect(wrapper.find('input').attributes('autocomplete')).toBe('off')
151
- })
150
+ expect(wrapper.find("input").attributes("autocomplete")).toBe("off");
151
+ });
152
152
 
153
153
  it('applies autocomplete="off" when autocomplete is false', async () => {
154
154
  const wrapper = mount(FzInput, {
155
155
  props: {
156
- label: 'Label',
156
+ label: "Label",
157
157
  autocomplete: false,
158
158
  },
159
159
  slots: {},
160
- })
160
+ });
161
161
 
162
- await wrapper.vm.$nextTick()
162
+ await wrapper.vm.$nextTick();
163
163
 
164
- expect(wrapper.find('input').attributes('autocomplete')).toBe('off')
165
- })
164
+ expect(wrapper.find("input").attributes("autocomplete")).toBe("off");
165
+ });
166
166
 
167
167
  it('applies autocomplete="on" when autocomplete is true', async () => {
168
168
  const wrapper = mount(FzInput, {
169
169
  props: {
170
- label: 'Label',
170
+ label: "Label",
171
171
  autocomplete: true,
172
172
  },
173
173
  slots: {},
174
- })
174
+ });
175
175
 
176
- await wrapper.vm.$nextTick()
176
+ await wrapper.vm.$nextTick();
177
177
 
178
- expect(wrapper.find('input').attributes('autocomplete')).toBe('on')
179
- })
180
- })
178
+ expect(wrapper.find("input").attributes("autocomplete")).toBe("on");
179
+ });
180
+ });
181
181
 
182
- describe('Events', () => {
183
- it('emits fzinput:right-icon-click event', async () => {
182
+ describe("Events", () => {
183
+ it("emits fzinput:right-icon-click event", async () => {
184
184
  const wrapper = mount(FzInput, {
185
185
  props: {
186
- label: 'Label',
187
- rightIcon: 'eye',
186
+ label: "Label",
187
+ rightIcon: "eye",
188
188
  },
189
189
  slots: {},
190
- })
190
+ });
191
191
 
192
- await wrapper.find('.fa-eye').trigger('click')
192
+ await wrapper.find(".fa-eye").trigger("click");
193
193
 
194
- expect(wrapper.emitted('fzinput:right-icon-click')).toBeTruthy()
195
- })
194
+ expect(wrapper.emitted("fzinput:right-icon-click")).toBeTruthy();
195
+ });
196
196
 
197
- it('emits fzinput:left-icon-click event', async () => {
197
+ it("emits fzinput:left-icon-click event", async () => {
198
198
  const wrapper = mount(FzInput, {
199
199
  props: {
200
- label: 'Label',
201
- leftIcon: 'eye',
200
+ label: "Label",
201
+ leftIcon: "eye",
202
202
  },
203
203
  slots: {},
204
- })
204
+ });
205
205
 
206
- await wrapper.find('.fa-eye').trigger('click')
206
+ await wrapper.find(".fa-eye").trigger("click");
207
207
 
208
- expect(wrapper.emitted('fzinput:left-icon-click')).toBeTruthy()
209
- })
208
+ expect(wrapper.emitted("fzinput:left-icon-click")).toBeTruthy();
209
+ });
210
210
 
211
- it('does not emit fzinput:right-icon-click event when disabled', async () => {
211
+ it("does not emit fzinput:right-icon-click event when disabled", async () => {
212
212
  const wrapper = mount(FzInput, {
213
213
  props: {
214
- label: 'Label',
215
- rightIcon: 'eye',
214
+ label: "Label",
215
+ rightIcon: "eye",
216
216
  disabled: true,
217
217
  },
218
218
  slots: {},
219
- })
219
+ });
220
220
 
221
- await wrapper.find('.fa-eye').trigger('click')
221
+ await wrapper.find(".fa-eye").trigger("click");
222
222
 
223
- expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
224
- })
223
+ expect(wrapper.emitted("fzinput:right-icon-click")).toBeFalsy();
224
+ });
225
225
 
226
- it('does not emit fzinput:right-icon-click event when readonly', async () => {
226
+ it("does not emit fzinput:right-icon-click event when readonly", async () => {
227
227
  const wrapper = mount(FzInput, {
228
228
  props: {
229
- label: 'Label',
230
- rightIcon: 'eye',
229
+ label: "Label",
230
+ rightIcon: "eye",
231
231
  readonly: true,
232
232
  },
233
233
  slots: {},
234
- })
234
+ });
235
235
 
236
- await wrapper.find('.fa-eye').trigger('click')
236
+ await wrapper.find(".fa-eye").trigger("click");
237
237
 
238
- expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
239
- })
238
+ expect(wrapper.emitted("fzinput:right-icon-click")).toBeFalsy();
239
+ });
240
240
 
241
- it('does not emit fzinput:left-icon-click event when disabled', async () => {
241
+ it("does not emit fzinput:left-icon-click event when disabled", async () => {
242
242
  const wrapper = mount(FzInput, {
243
243
  props: {
244
- label: 'Label',
245
- leftIcon: 'eye',
244
+ label: "Label",
245
+ leftIcon: "eye",
246
246
  disabled: true,
247
247
  },
248
248
  slots: {},
249
- })
249
+ });
250
250
 
251
- await wrapper.find('.fa-eye').trigger('click')
251
+ await wrapper.find(".fa-eye").trigger("click");
252
252
 
253
- expect(wrapper.emitted('fzinput:left-icon-click')).toBeFalsy()
254
- })
253
+ expect(wrapper.emitted("fzinput:left-icon-click")).toBeFalsy();
254
+ });
255
255
 
256
- it('does not emit fzinput:left-icon-click event when readonly', async () => {
256
+ it("does not emit fzinput:left-icon-click event when readonly", async () => {
257
257
  const wrapper = mount(FzInput, {
258
258
  props: {
259
- label: 'Label',
260
- leftIcon: 'eye',
259
+ label: "Label",
260
+ leftIcon: "eye",
261
261
  readonly: true,
262
262
  },
263
263
  slots: {},
264
- })
264
+ });
265
265
 
266
- await wrapper.find('.fa-eye').trigger('click')
266
+ await wrapper.find(".fa-eye").trigger("click");
267
267
 
268
- expect(wrapper.emitted('fzinput:left-icon-click')).toBeFalsy()
269
- })
268
+ expect(wrapper.emitted("fzinput:left-icon-click")).toBeFalsy();
269
+ });
270
270
 
271
- it('does not emit fzinput:second-right-icon-click event when disabled', async () => {
271
+ it("does not emit fzinput:second-right-icon-click event when disabled", async () => {
272
272
  const wrapper = mount(FzInput, {
273
273
  props: {
274
- label: 'Label',
275
- secondRightIcon: 'eye',
274
+ label: "Label",
275
+ secondRightIcon: "eye",
276
276
  disabled: true,
277
277
  },
278
278
  slots: {},
279
- })
279
+ });
280
280
 
281
- await wrapper.find('.fa-eye').trigger('click')
281
+ await wrapper.find(".fa-eye").trigger("click");
282
282
 
283
- expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
284
- })
283
+ expect(wrapper.emitted("fzinput:second-right-icon-click")).toBeFalsy();
284
+ });
285
285
 
286
- it('does not emit fzinput:second-right-icon-click event when readonly', async () => {
286
+ it("does not emit fzinput:second-right-icon-click event when readonly", async () => {
287
287
  const wrapper = mount(FzInput, {
288
288
  props: {
289
- label: 'Label',
290
- secondRightIcon: 'eye',
289
+ label: "Label",
290
+ secondRightIcon: "eye",
291
291
  readonly: true,
292
292
  },
293
293
  slots: {},
294
- })
294
+ });
295
295
 
296
- await wrapper.find('.fa-eye').trigger('click')
296
+ await wrapper.find(".fa-eye").trigger("click");
297
297
 
298
- expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
299
- })
298
+ expect(wrapper.emitted("fzinput:second-right-icon-click")).toBeFalsy();
299
+ });
300
300
 
301
- it('does not emit fzinput:right-icon-click event when disabled and rightIconButton is true', async () => {
301
+ it("does not emit fzinput:right-icon-click event when disabled and rightIconButton is true", async () => {
302
302
  const wrapper = mount(FzInput, {
303
303
  props: {
304
- label: 'Label',
305
- rightIcon: 'eye',
304
+ label: "Label",
305
+ rightIcon: "eye",
306
306
  rightIconButton: true,
307
307
  disabled: true,
308
308
  },
309
309
  slots: {},
310
- })
310
+ });
311
311
 
312
- const button = wrapper.findComponent({ name: 'FzIconButton' })
313
- await button.trigger('click')
312
+ const button = wrapper.findComponent({ name: "FzIconButton" });
313
+ await button.trigger("click");
314
314
 
315
- expect(wrapper.emitted('fzinput:right-icon-click')).toBeFalsy()
316
- })
315
+ expect(wrapper.emitted("fzinput:right-icon-click")).toBeFalsy();
316
+ });
317
317
 
318
- it('does not emit fzinput:second-right-icon-click event when disabled and secondRightIconButton is true', async () => {
318
+ it("does not emit fzinput:second-right-icon-click event when disabled and secondRightIconButton is true", async () => {
319
319
  const wrapper = mount(FzInput, {
320
320
  props: {
321
- label: 'Label',
322
- secondRightIcon: 'eye',
321
+ label: "Label",
322
+ secondRightIcon: "eye",
323
323
  secondRightIconButton: true,
324
324
  disabled: true,
325
325
  },
326
326
  slots: {},
327
- })
327
+ });
328
328
 
329
- const buttons = wrapper.findAllComponents({ name: 'FzIconButton' })
330
- await buttons[0].trigger('click')
329
+ const buttons = wrapper.findAllComponents({ name: "FzIconButton" });
330
+ await buttons[0].trigger("click");
331
331
 
332
- expect(wrapper.emitted('fzinput:second-right-icon-click')).toBeFalsy()
333
- })
334
- })
332
+ expect(wrapper.emitted("fzinput:second-right-icon-click")).toBeFalsy();
333
+ });
334
+ });
335
335
 
336
- describe('Accessibility', () => {
337
- describe('Input ARIA attributes', () => {
338
- it('applies aria-required when required is true', async () => {
336
+ describe("Accessibility", () => {
337
+ describe("Input ARIA attributes", () => {
338
+ it("applies aria-required when required is true", async () => {
339
339
  const wrapper = mount(FzInput, {
340
340
  props: {
341
- label: 'Label',
341
+ label: "Label",
342
342
  required: true,
343
343
  },
344
344
  slots: {},
345
- })
345
+ });
346
346
 
347
- await wrapper.vm.$nextTick()
347
+ await wrapper.vm.$nextTick();
348
348
 
349
- const input = wrapper.find('input').element as HTMLInputElement
350
- expect(input.getAttribute('aria-required')).toBe('true')
351
- })
349
+ const input = wrapper.find("input").element as HTMLInputElement;
350
+ expect(input.getAttribute("aria-required")).toBe("true");
351
+ });
352
352
 
353
353
  it('applies aria-required="false" when required is false', async () => {
354
354
  const wrapper = mount(FzInput, {
355
355
  props: {
356
- label: 'Label',
356
+ label: "Label",
357
357
  required: false,
358
358
  },
359
359
  slots: {},
360
- })
360
+ });
361
361
 
362
- await wrapper.vm.$nextTick()
362
+ await wrapper.vm.$nextTick();
363
363
 
364
- const input = wrapper.find('input').element as HTMLInputElement
365
- expect(input.getAttribute('aria-required')).toBe('false')
366
- })
364
+ const input = wrapper.find("input").element as HTMLInputElement;
365
+ expect(input.getAttribute("aria-required")).toBe("false");
366
+ });
367
367
 
368
- it('applies aria-invalid when error is true', async () => {
368
+ it("applies aria-invalid when error is true", async () => {
369
369
  const wrapper = mount(FzInput, {
370
370
  props: {
371
- label: 'Label',
371
+ label: "Label",
372
372
  error: true,
373
373
  },
374
374
  slots: {
375
- errorMessage: 'Error message',
375
+ errorMessage: "Error message",
376
376
  },
377
- })
377
+ });
378
378
 
379
- await wrapper.vm.$nextTick()
379
+ await wrapper.vm.$nextTick();
380
380
 
381
- const input = wrapper.find('input').element as HTMLInputElement
382
- expect(input.getAttribute('aria-invalid')).toBe('true')
383
- })
381
+ const input = wrapper.find("input").element as HTMLInputElement;
382
+ expect(input.getAttribute("aria-invalid")).toBe("true");
383
+ });
384
384
 
385
385
  it('applies aria-invalid="false" when error is false', async () => {
386
386
  const wrapper = mount(FzInput, {
387
387
  props: {
388
- label: 'Label',
388
+ label: "Label",
389
389
  error: false,
390
390
  },
391
391
  slots: {},
392
- })
392
+ });
393
393
 
394
- await wrapper.vm.$nextTick()
394
+ await wrapper.vm.$nextTick();
395
395
 
396
- const input = wrapper.find('input').element as HTMLInputElement
397
- expect(input.getAttribute('aria-invalid')).toBe('false')
398
- })
396
+ const input = wrapper.find("input").element as HTMLInputElement;
397
+ expect(input.getAttribute("aria-invalid")).toBe("false");
398
+ });
399
399
 
400
- it('applies aria-disabled when disabled is true', async () => {
400
+ it("applies aria-disabled when disabled is true", async () => {
401
401
  const wrapper = mount(FzInput, {
402
402
  props: {
403
- label: 'Label',
403
+ label: "Label",
404
404
  disabled: true,
405
405
  },
406
406
  slots: {},
407
- })
407
+ });
408
408
 
409
- await wrapper.vm.$nextTick()
409
+ await wrapper.vm.$nextTick();
410
410
 
411
- const input = wrapper.find('input').element as HTMLInputElement
412
- expect(input.getAttribute('aria-disabled')).toBe('true')
413
- })
411
+ const input = wrapper.find("input").element as HTMLInputElement;
412
+ expect(input.getAttribute("aria-disabled")).toBe("true");
413
+ });
414
414
 
415
415
  it('applies aria-disabled="false" when disabled is false', async () => {
416
416
  const wrapper = mount(FzInput, {
417
417
  props: {
418
- label: 'Label',
418
+ label: "Label",
419
419
  disabled: false,
420
420
  },
421
421
  slots: {},
422
- })
422
+ });
423
423
 
424
- await wrapper.vm.$nextTick()
424
+ await wrapper.vm.$nextTick();
425
425
 
426
- const input = wrapper.find('input').element as HTMLInputElement
427
- expect(input.getAttribute('aria-disabled')).toBe('false')
428
- })
426
+ const input = wrapper.find("input").element as HTMLInputElement;
427
+ expect(input.getAttribute("aria-disabled")).toBe("false");
428
+ });
429
429
 
430
- it('applies aria-labelledby when label is provided', async () => {
430
+ it("applies aria-labelledby when label is provided", async () => {
431
431
  const wrapper = mount(FzInput, {
432
432
  props: {
433
- label: 'Test Label',
433
+ label: "Test Label",
434
434
  },
435
435
  slots: {},
436
- })
436
+ });
437
437
 
438
- await wrapper.vm.$nextTick()
438
+ await wrapper.vm.$nextTick();
439
+
440
+ const input = wrapper.find("input").element as HTMLInputElement;
441
+ const labelId = input.getAttribute("aria-labelledby");
442
+ expect(labelId).toBeTruthy();
439
443
 
440
- const input = wrapper.find('input').element as HTMLInputElement
441
- const labelId = input.getAttribute('aria-labelledby')
442
- expect(labelId).toBeTruthy()
443
-
444
444
  // Verify label element exists with matching id
445
- const label = wrapper.find('label').element as HTMLLabelElement
446
- expect(label.getAttribute('id')).toBe(labelId)
447
- })
445
+ const label = wrapper.find("label").element as HTMLLabelElement;
446
+ expect(label.getAttribute("id")).toBe(labelId);
447
+ });
448
448
 
449
- it('does not apply aria-labelledby when label is not provided', async () => {
449
+ it("does not apply aria-labelledby when label is not provided", async () => {
450
450
  const wrapper = mount(FzInput, {
451
451
  props: {},
452
452
  slots: {},
453
- })
453
+ });
454
454
 
455
- await wrapper.vm.$nextTick()
455
+ await wrapper.vm.$nextTick();
456
456
 
457
- const input = wrapper.find('input').element as HTMLInputElement
458
- expect(input.getAttribute('aria-labelledby')).toBeNull()
459
- })
457
+ const input = wrapper.find("input").element as HTMLInputElement;
458
+ expect(input.getAttribute("aria-labelledby")).toBeNull();
459
+ });
460
460
 
461
- it('does not apply aria-labelledby when custom label slot is provided', async () => {
461
+ it("does not apply aria-labelledby when custom label slot is provided", async () => {
462
462
  const wrapper = mount(FzInput, {
463
463
  props: {
464
- label: 'Test Label',
464
+ label: "Test Label",
465
465
  },
466
466
  slots: {
467
- label: () => 'Custom Label Slot',
467
+ label: () => "Custom Label Slot",
468
468
  },
469
- })
469
+ });
470
+
471
+ await wrapper.vm.$nextTick();
470
472
 
471
- await wrapper.vm.$nextTick()
473
+ const input = wrapper.find("input").element as HTMLInputElement;
474
+ const ariaLabelledBy = input.getAttribute("aria-labelledby");
472
475
 
473
- const input = wrapper.find('input').element as HTMLInputElement
474
- const ariaLabelledBy = input.getAttribute('aria-labelledby')
475
-
476
476
  // aria-labelledby should not be set because the default label element
477
477
  // with id="${uniqueId}-label" doesn't exist when custom slot is used
478
- expect(ariaLabelledBy).toBeNull()
479
-
478
+ expect(ariaLabelledBy).toBeNull();
479
+
480
480
  // Verify default label element is not rendered
481
- const defaultLabel = wrapper.find('label')
482
- expect(defaultLabel.exists()).toBe(false)
483
- })
481
+ const defaultLabel = wrapper.find("label");
482
+ expect(defaultLabel.exists()).toBe(false);
483
+ });
484
484
 
485
- it('applies aria-describedby when helpText slot is provided', async () => {
485
+ it("applies aria-describedby when helpText slot is provided", async () => {
486
486
  const wrapper = mount(FzInput, {
487
487
  props: {
488
- label: 'Label',
488
+ label: "Label",
489
489
  },
490
490
  slots: {
491
- helpText: 'Help text',
491
+ helpText: "Help text",
492
492
  },
493
- })
493
+ });
494
+
495
+ await wrapper.vm.$nextTick();
494
496
 
495
- await wrapper.vm.$nextTick()
497
+ const input = wrapper.find("input").element as HTMLInputElement;
498
+ const describedBy = input.getAttribute("aria-describedby");
499
+ expect(describedBy).toBeTruthy();
496
500
 
497
- const input = wrapper.find('input').element as HTMLInputElement
498
- const describedBy = input.getAttribute('aria-describedby')
499
- expect(describedBy).toBeTruthy()
500
-
501
501
  // Verify help text element exists with matching id
502
- const helpText = wrapper.find(`#${describedBy}`)
503
- expect(helpText.exists()).toBe(true)
504
- expect(helpText.text()).toContain('Help text')
505
- })
502
+ const helpText = wrapper.find(`#${describedBy}`);
503
+ expect(helpText.exists()).toBe(true);
504
+ expect(helpText.text()).toContain("Help text");
505
+ });
506
506
 
507
- it('applies aria-describedby when errorMessage slot is provided', async () => {
507
+ it("applies aria-describedby when errorMessage slot is provided", async () => {
508
508
  const wrapper = mount(FzInput, {
509
509
  props: {
510
- label: 'Label',
510
+ label: "Label",
511
511
  error: true,
512
512
  },
513
513
  slots: {
514
- errorMessage: 'Error message',
514
+ errorMessage: "Error message",
515
515
  },
516
- })
516
+ });
517
517
 
518
- await wrapper.vm.$nextTick()
518
+ await wrapper.vm.$nextTick();
519
+
520
+ const input = wrapper.find("input").element as HTMLInputElement;
521
+ const describedBy = input.getAttribute("aria-describedby");
522
+ expect(describedBy).toBeTruthy();
519
523
 
520
- const input = wrapper.find('input').element as HTMLInputElement
521
- const describedBy = input.getAttribute('aria-describedby')
522
- expect(describedBy).toBeTruthy()
523
-
524
524
  // Verify error message element exists with matching id
525
- const errorMessage = wrapper.find(`#${describedBy}`)
526
- expect(errorMessage.exists()).toBe(true)
527
- expect(errorMessage.text()).toContain('Error message')
528
- })
525
+ const errorMessage = wrapper.find(`#${describedBy}`);
526
+ expect(errorMessage.exists()).toBe(true);
527
+ expect(errorMessage.text()).toContain("Error message");
528
+ });
529
529
 
530
- it('does not apply aria-describedby when neither helpText nor errorMessage are provided', async () => {
530
+ it("does not apply aria-describedby when neither helpText nor errorMessage are provided", async () => {
531
531
  const wrapper = mount(FzInput, {
532
532
  props: {
533
- label: 'Label',
533
+ label: "Label",
534
534
  },
535
535
  slots: {},
536
- })
536
+ });
537
537
 
538
- await wrapper.vm.$nextTick()
538
+ await wrapper.vm.$nextTick();
539
539
 
540
- const input = wrapper.find('input').element as HTMLInputElement
541
- expect(input.getAttribute('aria-describedby')).toBeNull()
542
- })
543
- })
540
+ const input = wrapper.find("input").element as HTMLInputElement;
541
+ expect(input.getAttribute("aria-describedby")).toBeNull();
542
+ });
543
+ });
544
544
 
545
- describe('Error message accessibility', () => {
545
+ describe("Error message accessibility", () => {
546
546
  it('applies role="alert" to error message container', async () => {
547
547
  const wrapper = mount(FzInput, {
548
548
  props: {
549
- label: 'Label',
549
+ label: "Label",
550
550
  error: true,
551
551
  },
552
552
  slots: {
553
- errorMessage: 'Error message',
553
+ errorMessage: "Error message",
554
554
  },
555
- })
555
+ });
556
556
 
557
- await wrapper.vm.$nextTick()
557
+ await wrapper.vm.$nextTick();
558
558
 
559
- const errorContainer = wrapper.find('[role="alert"]')
560
- expect(errorContainer.exists()).toBe(true)
561
- expect(errorContainer.text()).toContain('Error message')
562
- })
559
+ const errorContainer = wrapper.find('[role="alert"]');
560
+ expect(errorContainer.exists()).toBe(true);
561
+ expect(errorContainer.text()).toContain("Error message");
562
+ });
563
563
 
564
- it('does not render error container when error is false', async () => {
564
+ it("does not render error container when error is false", async () => {
565
565
  const wrapper = mount(FzInput, {
566
566
  props: {
567
- label: 'Label',
567
+ label: "Label",
568
568
  error: false,
569
569
  },
570
570
  slots: {
571
- errorMessage: 'Error message',
571
+ errorMessage: "Error message",
572
572
  },
573
- })
573
+ });
574
574
 
575
- await wrapper.vm.$nextTick()
575
+ await wrapper.vm.$nextTick();
576
576
 
577
- const errorContainer = wrapper.find('[role="alert"]')
578
- expect(errorContainer.exists()).toBe(false)
579
- })
580
- })
577
+ const errorContainer = wrapper.find('[role="alert"]');
578
+ expect(errorContainer.exists()).toBe(false);
579
+ });
580
+ });
581
581
 
582
- describe('Decorative icons accessibility', () => {
582
+ describe("Decorative icons accessibility", () => {
583
583
  it('applies aria-hidden="true" to valid checkmark icon', async () => {
584
584
  const wrapper = mount(FzInput, {
585
585
  props: {
586
- label: 'Label',
586
+ label: "Label",
587
587
  valid: true,
588
588
  },
589
589
  slots: {},
590
- })
590
+ });
591
591
 
592
- await wrapper.vm.$nextTick()
592
+ await wrapper.vm.$nextTick();
593
593
 
594
594
  // Find the check icon (FzIcon with name="check")
595
- const checkIcons = wrapper.findAllComponents({ name: 'FzIcon' })
596
- const checkIcon = checkIcons.find((icon) => icon.props('name') === 'check')
597
-
598
- expect(checkIcon?.exists()).toBe(true)
599
- const rootElement = checkIcon?.element as HTMLElement
600
- expect(rootElement.getAttribute('aria-hidden')).toBe('true')
601
- })
595
+ const checkIcons = wrapper.findAllComponents({ name: "FzIcon" });
596
+ const checkIcon = checkIcons.find(
597
+ (icon) => icon.props("name") === "check",
598
+ );
599
+
600
+ expect(checkIcon?.exists()).toBe(true);
601
+ const rootElement = checkIcon?.element as HTMLElement;
602
+ expect(rootElement.getAttribute("aria-hidden")).toBe("true");
603
+ });
602
604
 
603
605
  it('applies aria-hidden="true" to error icon', async () => {
604
606
  const wrapper = mount(FzInput, {
605
607
  props: {
606
- label: 'Label',
608
+ label: "Label",
607
609
  error: true,
608
610
  },
609
611
  slots: {
610
- errorMessage: 'Error message',
612
+ errorMessage: "Error message",
611
613
  },
612
- })
614
+ });
613
615
 
614
- await wrapper.vm.$nextTick()
616
+ await wrapper.vm.$nextTick();
615
617
 
616
618
  // Find the error icon (FzIcon with name="circle-xmark")
617
- const errorIcons = wrapper.findAllComponents({ name: 'FzIcon' })
618
- const errorIcon = errorIcons.find((icon) => icon.props('name') === 'circle-xmark')
619
-
620
- expect(errorIcon?.exists()).toBe(true)
621
- const rootElement = errorIcon?.element as HTMLElement
622
- expect(rootElement.getAttribute('aria-hidden')).toBe('true')
623
- })
624
- })
625
-
626
- describe('Container accessibility', () => {
619
+ const errorIcons = wrapper.findAllComponents({ name: "FzIcon" });
620
+ const errorIcon = errorIcons.find(
621
+ (icon) => icon.props("name") === "circle-xmark",
622
+ );
623
+
624
+ expect(errorIcon?.exists()).toBe(true);
625
+ const rootElement = errorIcon?.element as HTMLElement;
626
+ expect(rootElement.getAttribute("aria-hidden")).toBe("true");
627
+ });
628
+ });
629
+
630
+ describe("Container accessibility", () => {
627
631
  it('applies tabindex="0" to container when not disabled', async () => {
628
632
  const wrapper = mount(FzInput, {
629
633
  props: {
630
- label: 'Label',
634
+ label: "Label",
631
635
  },
632
636
  slots: {},
633
- })
637
+ });
634
638
 
635
- await wrapper.vm.$nextTick()
639
+ await wrapper.vm.$nextTick();
636
640
 
637
- const container = wrapper.find('.fz-input > div').element as HTMLElement
638
- expect(container.getAttribute('tabindex')).toBe('0')
639
- })
641
+ const container = wrapper.find(".fz-input > div")
642
+ .element as HTMLElement;
643
+ expect(container.getAttribute("tabindex")).toBe("0");
644
+ });
640
645
 
641
- it('removes tabindex from container when disabled', async () => {
646
+ it("removes tabindex from container when disabled", async () => {
642
647
  const wrapper = mount(FzInput, {
643
648
  props: {
644
- label: 'Label',
649
+ label: "Label",
645
650
  disabled: true,
646
651
  },
647
652
  slots: {},
648
- })
653
+ });
649
654
 
650
- await wrapper.vm.$nextTick()
655
+ await wrapper.vm.$nextTick();
651
656
 
652
- const container = wrapper.find('.fz-input > div').element as HTMLElement
653
- expect(container.getAttribute('tabindex')).toBeNull()
654
- })
657
+ const container = wrapper.find(".fz-input > div")
658
+ .element as HTMLElement;
659
+ expect(container.getAttribute("tabindex")).toBeNull();
660
+ });
655
661
 
656
- it('removes tabindex from container when readonly', async () => {
662
+ it("removes tabindex from container when readonly", async () => {
657
663
  const wrapper = mount(FzInput, {
658
664
  props: {
659
- label: 'Label',
665
+ label: "Label",
660
666
  readonly: true,
661
667
  },
662
668
  slots: {},
663
- })
669
+ });
664
670
 
665
- await wrapper.vm.$nextTick()
671
+ await wrapper.vm.$nextTick();
666
672
 
667
- const container = wrapper.find('.fz-input > div').element as HTMLElement
668
- expect(container.getAttribute('tabindex')).toBeNull()
669
- })
670
- })
673
+ const container = wrapper.find(".fz-input > div")
674
+ .element as HTMLElement;
675
+ expect(container.getAttribute("tabindex")).toBeNull();
676
+ });
677
+ });
671
678
 
672
- describe('Left icon accessibility', () => {
673
- it('applies accessibility attributes when leftIconAriaLabel is provided', async () => {
679
+ describe("Left icon accessibility", () => {
680
+ it("applies accessibility attributes when leftIconAriaLabel is provided", async () => {
674
681
  const wrapper = mount(FzInput, {
675
682
  props: {
676
- label: 'Label',
677
- leftIcon: 'calendar-lines',
678
- leftIconAriaLabel: 'Open calendar',
683
+ label: "Label",
684
+ leftIcon: "calendar-lines",
685
+ leftIconAriaLabel: "Open calendar",
679
686
  },
680
687
  slots: {},
681
- })
688
+ });
682
689
 
683
- await wrapper.vm.$nextTick()
690
+ await wrapper.vm.$nextTick();
684
691
 
685
692
  // Find the FzIcon component wrapper div (root element)
686
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
687
- expect(iconComponent.exists()).toBe(true)
688
-
693
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
694
+ expect(iconComponent.exists()).toBe(true);
695
+
689
696
  // Get the root element (div wrapper)
690
- const rootElement = iconComponent.element as HTMLElement
691
-
697
+ const rootElement = iconComponent.element as HTMLElement;
698
+
692
699
  // Verify attributes are on the root element
693
- expect(rootElement.getAttribute('role')).toBe('button')
694
- expect(rootElement.getAttribute('aria-label')).toBe('Open calendar')
695
- expect(rootElement.getAttribute('tabindex')).toBe('0')
696
- })
700
+ expect(rootElement.getAttribute("role")).toBe("button");
701
+ expect(rootElement.getAttribute("aria-label")).toBe("Open calendar");
702
+ expect(rootElement.getAttribute("tabindex")).toBe("0");
703
+ });
697
704
 
698
- it('does not apply accessibility attributes when leftIconAriaLabel is not provided', async () => {
705
+ it("does not apply accessibility attributes when leftIconAriaLabel is not provided", async () => {
699
706
  const wrapper = mount(FzInput, {
700
707
  props: {
701
- label: 'Label',
702
- leftIcon: 'calendar-lines',
708
+ label: "Label",
709
+ leftIcon: "calendar-lines",
703
710
  },
704
711
  slots: {},
705
- })
712
+ });
706
713
 
707
- await wrapper.vm.$nextTick()
714
+ await wrapper.vm.$nextTick();
708
715
 
709
716
  // Find the FzIcon component wrapper
710
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
711
- expect(iconComponent.exists()).toBe(true)
712
-
717
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
718
+ expect(iconComponent.exists()).toBe(true);
719
+
713
720
  // Get the root element (div wrapper)
714
- const rootElement = iconComponent.element as HTMLElement
715
-
721
+ const rootElement = iconComponent.element as HTMLElement;
722
+
716
723
  // Verify attributes are not on the root element
717
- expect(rootElement.getAttribute('role')).toBeNull()
718
- expect(rootElement.getAttribute('aria-label')).toBeNull()
719
- expect(rootElement.getAttribute('tabindex')).toBeNull()
720
- })
724
+ expect(rootElement.getAttribute("role")).toBeNull();
725
+ expect(rootElement.getAttribute("aria-label")).toBeNull();
726
+ expect(rootElement.getAttribute("tabindex")).toBeNull();
727
+ });
721
728
 
722
- it('removes tabindex when disabled and leftIconAriaLabel is provided', async () => {
729
+ it("removes tabindex when disabled and leftIconAriaLabel is provided", async () => {
723
730
  const wrapper = mount(FzInput, {
724
731
  props: {
725
- label: 'Label',
726
- leftIcon: 'calendar-lines',
727
- leftIconAriaLabel: 'Open calendar',
732
+ label: "Label",
733
+ leftIcon: "calendar-lines",
734
+ leftIconAriaLabel: "Open calendar",
728
735
  disabled: true,
729
736
  },
730
737
  slots: {},
731
- })
738
+ });
732
739
 
733
- await wrapper.vm.$nextTick()
740
+ await wrapper.vm.$nextTick();
734
741
 
735
742
  // Find the FzIcon component wrapper
736
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
737
- expect(iconComponent.exists()).toBe(true)
738
-
743
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
744
+ expect(iconComponent.exists()).toBe(true);
745
+
739
746
  // Get the root element (div wrapper)
740
- const rootElement = iconComponent.element as HTMLElement
741
-
747
+ const rootElement = iconComponent.element as HTMLElement;
748
+
742
749
  // Verify attributes are on the root element
743
- expect(rootElement.getAttribute('role')).toBe('button')
744
- expect(rootElement.getAttribute('aria-label')).toBe('Open calendar')
745
- expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when disabled
746
- expect(rootElement.getAttribute('aria-disabled')).toBe('true')
747
- })
750
+ expect(rootElement.getAttribute("role")).toBe("button");
751
+ expect(rootElement.getAttribute("aria-label")).toBe("Open calendar");
752
+ expect(rootElement.getAttribute("tabindex")).toBeNull(); // Removed when disabled
753
+ expect(rootElement.getAttribute("aria-disabled")).toBe("true");
754
+ });
748
755
 
749
- it('has keyboard handler when leftIconAriaLabel is provided', async () => {
756
+ it("has keyboard handler when leftIconAriaLabel is provided", async () => {
750
757
  const wrapper = mount(FzInput, {
751
758
  props: {
752
- label: 'Label',
753
- leftIcon: 'calendar-lines',
754
- leftIconAriaLabel: 'Open calendar',
759
+ label: "Label",
760
+ leftIcon: "calendar-lines",
761
+ leftIconAriaLabel: "Open calendar",
755
762
  },
756
763
  slots: {},
757
- })
764
+ });
758
765
 
759
- await wrapper.vm.$nextTick()
766
+ await wrapper.vm.$nextTick();
760
767
 
761
768
  // Find the FzIcon component wrapper
762
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
763
- expect(iconComponent.exists()).toBe(true)
764
-
769
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
770
+ expect(iconComponent.exists()).toBe(true);
771
+
765
772
  // Get the root element (div wrapper)
766
- const rootElement = iconComponent.element as HTMLElement
767
-
773
+ const rootElement = iconComponent.element as HTMLElement;
774
+
768
775
  // Verify icon is keyboard accessible (has tabindex)
769
- expect(rootElement.getAttribute('tabindex')).toBe('0')
776
+ expect(rootElement.getAttribute("tabindex")).toBe("0");
770
777
  // Keyboard interaction is tested in Storybook play functions
771
- })
772
- })
778
+ });
779
+ });
773
780
 
774
- describe('Right icon accessibility', () => {
775
- it('applies accessibility attributes when rightIconAriaLabel is provided', async () => {
781
+ describe("Right icon accessibility", () => {
782
+ it("applies accessibility attributes when rightIconAriaLabel is provided", async () => {
776
783
  const wrapper = mount(FzInput, {
777
784
  props: {
778
- label: 'Label',
779
- rightIcon: 'eye',
780
- rightIconAriaLabel: 'Toggle visibility',
785
+ label: "Label",
786
+ rightIcon: "eye",
787
+ rightIconAriaLabel: "Toggle visibility",
781
788
  },
782
789
  slots: {},
783
- })
790
+ });
784
791
 
785
- await wrapper.vm.$nextTick()
792
+ await wrapper.vm.$nextTick();
786
793
 
787
794
  // Find the FzIcon component wrapper (not the inner img)
788
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
789
- expect(iconComponent.exists()).toBe(true)
790
-
795
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
796
+ expect(iconComponent.exists()).toBe(true);
797
+
791
798
  // Get the root element (div wrapper)
792
- const rootElement = iconComponent.element as HTMLElement
793
-
794
- // Verify attributes are on the root element
795
- expect(rootElement.getAttribute('role')).toBe('button')
796
- expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
797
- expect(rootElement.getAttribute('tabindex')).toBe('0')
798
- })
799
+ const rootElement = iconComponent.element as HTMLElement;
799
800
 
800
- it('does not apply accessibility attributes when rightIconAriaLabel is not provided', async () => {
801
+ // Verify attributes are on the root element
802
+ expect(rootElement.getAttribute("role")).toBe("button");
803
+ expect(rootElement.getAttribute("aria-label")).toBe(
804
+ "Toggle visibility",
805
+ );
806
+ expect(rootElement.getAttribute("tabindex")).toBe("0");
807
+ });
808
+
809
+ it("does not apply accessibility attributes when rightIconAriaLabel is not provided", async () => {
801
810
  const wrapper = mount(FzInput, {
802
811
  props: {
803
- label: 'Label',
804
- rightIcon: 'eye',
812
+ label: "Label",
813
+ rightIcon: "eye",
805
814
  },
806
815
  slots: {},
807
- })
816
+ });
808
817
 
809
- await wrapper.vm.$nextTick()
818
+ await wrapper.vm.$nextTick();
810
819
 
811
820
  // Find the FzIcon component wrapper
812
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
813
- expect(iconComponent.exists()).toBe(true)
814
-
821
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
822
+ expect(iconComponent.exists()).toBe(true);
823
+
815
824
  // Get the root element (div wrapper)
816
- const rootElement = iconComponent.element as HTMLElement
817
-
825
+ const rootElement = iconComponent.element as HTMLElement;
826
+
818
827
  // Verify attributes are not on the root element
819
- expect(rootElement.getAttribute('role')).toBeNull()
820
- expect(rootElement.getAttribute('aria-label')).toBeNull()
821
- expect(rootElement.getAttribute('tabindex')).toBeNull()
822
- })
828
+ expect(rootElement.getAttribute("role")).toBeNull();
829
+ expect(rootElement.getAttribute("aria-label")).toBeNull();
830
+ expect(rootElement.getAttribute("tabindex")).toBeNull();
831
+ });
823
832
 
824
- it('removes tabindex when disabled and rightIconAriaLabel is provided', async () => {
833
+ it("removes tabindex when disabled and rightIconAriaLabel is provided", async () => {
825
834
  const wrapper = mount(FzInput, {
826
835
  props: {
827
- label: 'Label',
828
- rightIcon: 'eye',
829
- rightIconAriaLabel: 'Toggle visibility',
836
+ label: "Label",
837
+ rightIcon: "eye",
838
+ rightIconAriaLabel: "Toggle visibility",
830
839
  disabled: true,
831
840
  },
832
841
  slots: {},
833
- })
842
+ });
834
843
 
835
- await wrapper.vm.$nextTick()
844
+ await wrapper.vm.$nextTick();
836
845
 
837
846
  // Find the FzIcon component wrapper
838
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
839
- expect(iconComponent.exists()).toBe(true)
840
-
847
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
848
+ expect(iconComponent.exists()).toBe(true);
849
+
841
850
  // Get the root element (div wrapper)
842
- const rootElement = iconComponent.element as HTMLElement
843
-
844
- // Verify attributes are on the root element
845
- expect(rootElement.getAttribute('role')).toBe('button')
846
- expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
847
- expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when disabled
848
- expect(rootElement.getAttribute('aria-disabled')).toBe('true')
849
- })
851
+ const rootElement = iconComponent.element as HTMLElement;
850
852
 
851
- it('removes tabindex when readonly and rightIconAriaLabel is provided', async () => {
853
+ // Verify attributes are on the root element
854
+ expect(rootElement.getAttribute("role")).toBe("button");
855
+ expect(rootElement.getAttribute("aria-label")).toBe(
856
+ "Toggle visibility",
857
+ );
858
+ expect(rootElement.getAttribute("tabindex")).toBeNull(); // Removed when disabled
859
+ expect(rootElement.getAttribute("aria-disabled")).toBe("true");
860
+ });
861
+
862
+ it("removes tabindex when readonly and rightIconAriaLabel is provided", async () => {
852
863
  const wrapper = mount(FzInput, {
853
864
  props: {
854
- label: 'Label',
855
- rightIcon: 'eye',
856
- rightIconAriaLabel: 'Toggle visibility',
865
+ label: "Label",
866
+ rightIcon: "eye",
867
+ rightIconAriaLabel: "Toggle visibility",
857
868
  readonly: true,
858
869
  },
859
870
  slots: {},
860
- })
871
+ });
861
872
 
862
- await wrapper.vm.$nextTick()
873
+ await wrapper.vm.$nextTick();
863
874
 
864
875
  // Find the FzIcon component wrapper
865
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
866
- expect(iconComponent.exists()).toBe(true)
867
-
876
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
877
+ expect(iconComponent.exists()).toBe(true);
878
+
868
879
  // Get the root element (div wrapper)
869
- const rootElement = iconComponent.element as HTMLElement
870
-
871
- // Verify attributes are on the root element
872
- expect(rootElement.getAttribute('role')).toBe('button')
873
- expect(rootElement.getAttribute('aria-label')).toBe('Toggle visibility')
874
- expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when readonly
875
- expect(rootElement.getAttribute('aria-disabled')).toBe('true')
876
- })
880
+ const rootElement = iconComponent.element as HTMLElement;
877
881
 
878
- it('does not apply accessibility attributes when rightIconButton is true', async () => {
882
+ // Verify attributes are on the root element
883
+ expect(rootElement.getAttribute("role")).toBe("button");
884
+ expect(rootElement.getAttribute("aria-label")).toBe(
885
+ "Toggle visibility",
886
+ );
887
+ expect(rootElement.getAttribute("tabindex")).toBeNull(); // Removed when readonly
888
+ expect(rootElement.getAttribute("aria-disabled")).toBe("true");
889
+ });
890
+
891
+ it("does not apply accessibility attributes when rightIconButton is true", async () => {
879
892
  const wrapper = mount(FzInput, {
880
893
  props: {
881
- label: 'Label',
882
- rightIcon: 'eye',
894
+ label: "Label",
895
+ rightIcon: "eye",
883
896
  rightIconButton: true,
884
- rightIconAriaLabel: 'Toggle visibility',
897
+ rightIconAriaLabel: "Toggle visibility",
885
898
  },
886
899
  slots: {},
887
- })
900
+ });
888
901
 
889
- await wrapper.vm.$nextTick()
902
+ await wrapper.vm.$nextTick();
890
903
 
891
904
  // When rightIconButton is true, FzIconButton is used instead of FzIcon
892
- const iconButton = wrapper.findComponent({ name: 'FzIconButton' })
893
- expect(iconButton.exists()).toBe(true)
894
- })
905
+ const iconButton = wrapper.findComponent({ name: "FzIconButton" });
906
+ expect(iconButton.exists()).toBe(true);
907
+ });
895
908
 
896
- it('has keyboard handler when rightIconAriaLabel is provided', async () => {
909
+ it("has keyboard handler when rightIconAriaLabel is provided", async () => {
897
910
  const wrapper = mount(FzInput, {
898
911
  props: {
899
- label: 'Label',
900
- rightIcon: 'eye',
901
- rightIconAriaLabel: 'Toggle visibility',
912
+ label: "Label",
913
+ rightIcon: "eye",
914
+ rightIconAriaLabel: "Toggle visibility",
902
915
  },
903
916
  slots: {},
904
- })
917
+ });
905
918
 
906
- await wrapper.vm.$nextTick()
919
+ await wrapper.vm.$nextTick();
907
920
 
908
921
  // Find the FzIcon component wrapper
909
- const iconComponent = wrapper.findComponent({ name: 'FzIcon' })
910
- expect(iconComponent.exists()).toBe(true)
911
-
922
+ const iconComponent = wrapper.findComponent({ name: "FzIcon" });
923
+ expect(iconComponent.exists()).toBe(true);
924
+
912
925
  // Get the root element (div wrapper)
913
- const rootElement = iconComponent.element as HTMLElement
914
-
926
+ const rootElement = iconComponent.element as HTMLElement;
927
+
915
928
  // Verify icon is keyboard accessible (has tabindex)
916
- expect(rootElement.getAttribute('tabindex')).toBe('0')
929
+ expect(rootElement.getAttribute("tabindex")).toBe("0");
917
930
  // Keyboard interaction is tested in Storybook play functions
918
- })
919
- })
931
+ });
932
+ });
920
933
 
921
- describe('Second right icon accessibility', () => {
922
- it('applies accessibility attributes when secondRightIconAriaLabel is provided', async () => {
934
+ describe("Second right icon accessibility", () => {
935
+ it("applies accessibility attributes when secondRightIconAriaLabel is provided", async () => {
923
936
  const wrapper = mount(FzInput, {
924
937
  props: {
925
- label: 'Label',
926
- secondRightIcon: 'info-circle',
927
- secondRightIconAriaLabel: 'Show information',
938
+ label: "Label",
939
+ secondRightIcon: "info-circle",
940
+ secondRightIconAriaLabel: "Show information",
928
941
  },
929
942
  slots: {},
930
- })
943
+ });
931
944
 
932
- await wrapper.vm.$nextTick()
945
+ await wrapper.vm.$nextTick();
933
946
 
934
947
  // Find all FzIcon components and get the one with secondRightIcon
935
- const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
936
- const secondIconComponent = iconComponents.find((icon) =>
937
- icon.props('name') === 'info-circle'
938
- )
939
-
940
- expect(secondIconComponent?.exists()).toBe(true)
941
-
948
+ const iconComponents = wrapper.findAllComponents({ name: "FzIcon" });
949
+ const secondIconComponent = iconComponents.find(
950
+ (icon) => icon.props("name") === "info-circle",
951
+ );
952
+
953
+ expect(secondIconComponent?.exists()).toBe(true);
954
+
942
955
  // Get the root element (div wrapper)
943
- const rootElement = secondIconComponent?.element as HTMLElement
944
-
956
+ const rootElement = secondIconComponent?.element as HTMLElement;
957
+
945
958
  // Verify attributes are on the root element
946
- expect(rootElement.getAttribute('role')).toBe('button')
947
- expect(rootElement.getAttribute('aria-label')).toBe('Show information')
948
- expect(rootElement.getAttribute('tabindex')).toBe('0')
949
- })
959
+ expect(rootElement.getAttribute("role")).toBe("button");
960
+ expect(rootElement.getAttribute("aria-label")).toBe("Show information");
961
+ expect(rootElement.getAttribute("tabindex")).toBe("0");
962
+ });
950
963
 
951
- it('removes tabindex when readonly and secondRightIconAriaLabel is provided', async () => {
964
+ it("removes tabindex when readonly and secondRightIconAriaLabel is provided", async () => {
952
965
  const wrapper = mount(FzInput, {
953
966
  props: {
954
- label: 'Label',
955
- secondRightIcon: 'info-circle',
956
- secondRightIconAriaLabel: 'Show information',
967
+ label: "Label",
968
+ secondRightIcon: "info-circle",
969
+ secondRightIconAriaLabel: "Show information",
957
970
  readonly: true,
958
971
  },
959
972
  slots: {},
960
- })
973
+ });
961
974
 
962
- await wrapper.vm.$nextTick()
975
+ await wrapper.vm.$nextTick();
963
976
 
964
977
  // Find all FzIcon components and get the one with secondRightIcon
965
- const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
966
- const secondIconComponent = iconComponents.find((icon) =>
967
- icon.props('name') === 'info-circle'
968
- )
969
-
970
- expect(secondIconComponent?.exists()).toBe(true)
971
-
978
+ const iconComponents = wrapper.findAllComponents({ name: "FzIcon" });
979
+ const secondIconComponent = iconComponents.find(
980
+ (icon) => icon.props("name") === "info-circle",
981
+ );
982
+
983
+ expect(secondIconComponent?.exists()).toBe(true);
984
+
972
985
  // Get the root element (div wrapper)
973
- const rootElement = secondIconComponent?.element as HTMLElement
974
-
975
- // Verify attributes are on the root element
976
- expect(rootElement.getAttribute('role')).toBe('button')
977
- expect(rootElement.getAttribute('aria-label')).toBe('Show information')
978
- expect(rootElement.getAttribute('tabindex')).toBeNull() // Removed when readonly
979
- expect(rootElement.getAttribute('aria-disabled')).toBe('true')
980
- })
981
- })
986
+ const rootElement = secondIconComponent?.element as HTMLElement;
982
987
 
983
- describe('Right icons order', () => {
984
- it('renders valid checkmark as last icon when all icons are present', async () => {
988
+ // Verify attributes are on the root element
989
+ expect(rootElement.getAttribute("role")).toBe("button");
990
+ expect(rootElement.getAttribute("aria-label")).toBe("Show information");
991
+ expect(rootElement.getAttribute("tabindex")).toBeNull(); // Removed when readonly
992
+ expect(rootElement.getAttribute("aria-disabled")).toBe("true");
993
+ });
994
+ });
995
+
996
+ describe("Right icons order", () => {
997
+ it("renders valid checkmark as last icon when all icons are present", async () => {
985
998
  const wrapper = mount(FzInput, {
986
999
  props: {
987
- label: 'Label',
1000
+ label: "Label",
988
1001
  valid: true,
989
- secondRightIcon: 'info-circle',
990
- rightIcon: 'envelope',
1002
+ secondRightIcon: "info-circle",
1003
+ rightIcon: "envelope",
991
1004
  },
992
1005
  slots: {},
993
- })
1006
+ });
994
1007
 
995
- await wrapper.vm.$nextTick()
1008
+ await wrapper.vm.$nextTick();
996
1009
 
997
1010
  // Find all FzIcon components in the right-icon slot
998
- const iconComponents = wrapper.findAllComponents({ name: 'FzIcon' })
999
- const validIcon = iconComponents.find((icon) => icon.props('name') === 'check')
1000
- const secondIcon = iconComponents.find((icon) => icon.props('name') === 'info-circle')
1001
- const rightIcon = iconComponents.find((icon) => icon.props('name') === 'envelope')
1002
-
1003
- expect(validIcon?.exists()).toBe(true)
1004
- expect(secondIcon?.exists()).toBe(true)
1005
- expect(rightIcon?.exists()).toBe(true)
1011
+ const iconComponents = wrapper.findAllComponents({ name: "FzIcon" });
1012
+ const validIcon = iconComponents.find(
1013
+ (icon) => icon.props("name") === "check",
1014
+ );
1015
+ const secondIcon = iconComponents.find(
1016
+ (icon) => icon.props("name") === "info-circle",
1017
+ );
1018
+ const rightIcon = iconComponents.find(
1019
+ (icon) => icon.props("name") === "envelope",
1020
+ );
1021
+
1022
+ expect(validIcon?.exists()).toBe(true);
1023
+ expect(secondIcon?.exists()).toBe(true);
1024
+ expect(rightIcon?.exists()).toBe(true);
1006
1025
 
1007
1026
  // Get the container div that wraps all right icons
1008
- const rightIconContainer = wrapper.find('.fz-input > div > div.flex.items-center.gap-4')
1009
- expect(rightIconContainer.exists()).toBe(true)
1027
+ const rightIconContainer = wrapper.find(
1028
+ ".fz-input > div > div.flex.items-center.gap-4",
1029
+ );
1030
+ expect(rightIconContainer.exists()).toBe(true);
1010
1031
 
1011
1032
  // Get all icon elements in order
1012
- const icons = rightIconContainer.findAllComponents({ name: 'FzIcon' })
1013
- expect(icons.length).toBeGreaterThanOrEqual(3)
1033
+ const icons = rightIconContainer.findAllComponents({ name: "FzIcon" });
1034
+ expect(icons.length).toBeGreaterThanOrEqual(3);
1014
1035
 
1015
1036
  // Verify order: secondRightIcon, rightIcon, valid (check)
1016
- const iconNames = icons.map((icon) => icon.props('name'))
1017
- const secondIndex = iconNames.indexOf('info-circle')
1018
- const rightIndex = iconNames.indexOf('envelope')
1019
- const validIndex = iconNames.indexOf('check')
1020
-
1021
- expect(secondIndex).toBeLessThan(rightIndex)
1022
- expect(rightIndex).toBeLessThan(validIndex)
1023
- })
1024
- })
1025
- })
1026
-
1027
- describe('Attribute forwarding (inheritAttrs: false)', () => {
1028
- it('applies consumer class to root wrapper div', async () => {
1037
+ const iconNames = icons.map((icon) => icon.props("name"));
1038
+ const secondIndex = iconNames.indexOf("info-circle");
1039
+ const rightIndex = iconNames.indexOf("envelope");
1040
+ const validIndex = iconNames.indexOf("check");
1041
+
1042
+ expect(secondIndex).toBeLessThan(rightIndex);
1043
+ expect(rightIndex).toBeLessThan(validIndex);
1044
+ });
1045
+ });
1046
+ });
1047
+
1048
+ describe("Attribute forwarding (inheritAttrs: false)", () => {
1049
+ it("applies consumer class to root wrapper div", async () => {
1029
1050
  const wrapper = mount(FzInput, {
1030
- props: { label: 'Label' },
1031
- attrs: { class: 'max-w-xs custom-class' },
1032
- })
1051
+ props: { label: "Label" },
1052
+ attrs: { class: "max-w-xs custom-class" },
1053
+ });
1033
1054
 
1034
- await wrapper.vm.$nextTick()
1055
+ await wrapper.vm.$nextTick();
1035
1056
 
1036
- const rootDiv = wrapper.element as HTMLElement
1037
- expect(rootDiv.classList.contains('max-w-xs')).toBe(true)
1038
- expect(rootDiv.classList.contains('custom-class')).toBe(true)
1039
- expect(rootDiv.classList.contains('fz-input')).toBe(true)
1040
- })
1057
+ const rootDiv = wrapper.element as HTMLElement;
1058
+ expect(rootDiv.classList.contains("max-w-xs")).toBe(true);
1059
+ expect(rootDiv.classList.contains("custom-class")).toBe(true);
1060
+ expect(rootDiv.classList.contains("fz-input")).toBe(true);
1061
+ });
1041
1062
 
1042
- it('does not apply consumer class to native input element', async () => {
1063
+ it("does not apply consumer class to native input element", async () => {
1043
1064
  const wrapper = mount(FzInput, {
1044
- props: { label: 'Label' },
1045
- attrs: { class: 'max-w-xs' },
1046
- })
1065
+ props: { label: "Label" },
1066
+ attrs: { class: "max-w-xs" },
1067
+ });
1047
1068
 
1048
- await wrapper.vm.$nextTick()
1069
+ await wrapper.vm.$nextTick();
1049
1070
 
1050
- const input = wrapper.find('input').element as HTMLInputElement
1051
- expect(input.classList.contains('max-w-xs')).toBe(false)
1052
- })
1071
+ const input = wrapper.find("input").element as HTMLInputElement;
1072
+ expect(input.classList.contains("max-w-xs")).toBe(false);
1073
+ });
1053
1074
 
1054
- it('forwards data-* attributes to native input element', async () => {
1075
+ it("forwards data-* attributes to native input element", async () => {
1055
1076
  const wrapper = mount(FzInput, {
1056
- props: { label: 'Label' },
1057
- attrs: { 'data-cy': 'my-input', 'data-testid': 'test-input' },
1058
- })
1077
+ props: { label: "Label" },
1078
+ attrs: { "data-cy": "my-input", "data-testid": "test-input" },
1079
+ });
1059
1080
 
1060
- await wrapper.vm.$nextTick()
1081
+ await wrapper.vm.$nextTick();
1061
1082
 
1062
- const input = wrapper.find('input').element as HTMLInputElement
1063
- expect(input.getAttribute('data-cy')).toBe('my-input')
1064
- expect(input.getAttribute('data-testid')).toBe('test-input')
1083
+ const input = wrapper.find("input").element as HTMLInputElement;
1084
+ expect(input.getAttribute("data-cy")).toBe("my-input");
1085
+ expect(input.getAttribute("data-testid")).toBe("test-input");
1065
1086
 
1066
- const rootDiv = wrapper.element as HTMLElement
1067
- expect(rootDiv.getAttribute('data-cy')).toBeNull()
1068
- })
1087
+ const rootDiv = wrapper.element as HTMLElement;
1088
+ expect(rootDiv.getAttribute("data-cy")).toBeNull();
1089
+ });
1069
1090
 
1070
- it('forwards consumer id to native input element (overriding internal id)', async () => {
1091
+ it("forwards consumer id to native input element (overriding internal id)", async () => {
1071
1092
  const wrapper = mount(FzInput, {
1072
- props: { label: 'Label' },
1073
- attrs: { id: 'custom-id' },
1074
- })
1093
+ props: { label: "Label" },
1094
+ attrs: { id: "custom-id" },
1095
+ });
1075
1096
 
1076
- await wrapper.vm.$nextTick()
1097
+ await wrapper.vm.$nextTick();
1077
1098
 
1078
- const input = wrapper.find('input').element as HTMLInputElement
1079
- expect(input.getAttribute('id')).toBe('custom-id')
1099
+ const input = wrapper.find("input").element as HTMLInputElement;
1100
+ expect(input.getAttribute("id")).toBe("custom-id");
1080
1101
 
1081
- const rootDiv = wrapper.element as HTMLElement
1082
- expect(rootDiv.getAttribute('id')).toBeNull()
1083
- })
1102
+ const rootDiv = wrapper.element as HTMLElement;
1103
+ expect(rootDiv.getAttribute("id")).toBeNull();
1104
+ });
1084
1105
 
1085
- it('forwards consumer aria-* attributes to native input element', async () => {
1106
+ it("forwards consumer aria-* attributes to native input element", async () => {
1086
1107
  const wrapper = mount(FzInput, {
1087
- props: { label: 'Label' },
1108
+ props: { label: "Label" },
1088
1109
  attrs: {
1089
- 'aria-expanded': 'true',
1090
- 'aria-haspopup': 'listbox',
1110
+ "aria-expanded": "true",
1111
+ "aria-haspopup": "listbox",
1091
1112
  },
1092
- })
1113
+ });
1114
+
1115
+ await wrapper.vm.$nextTick();
1116
+
1117
+ const input = wrapper.find("input").element as HTMLInputElement;
1118
+ expect(input.getAttribute("aria-expanded")).toBe("true");
1119
+ expect(input.getAttribute("aria-haspopup")).toBe("listbox");
1120
+
1121
+ const rootDiv = wrapper.element as HTMLElement;
1122
+ expect(rootDiv.getAttribute("aria-expanded")).toBeNull();
1123
+ expect(rootDiv.getAttribute("aria-haspopup")).toBeNull();
1124
+ });
1125
+ });
1126
+
1127
+ describe("Visual emphasis states", () => {
1128
+ describe("Highlighted state", () => {
1129
+ it("applies highlighted container classes when highlighted is true", async () => {
1130
+ const wrapper = mount(FzInput, {
1131
+ props: {
1132
+ label: "Label",
1133
+ highlighted: true,
1134
+ },
1135
+ slots: {},
1136
+ });
1137
+
1138
+ await wrapper.vm.$nextTick();
1139
+
1140
+ const container = wrapper.find(".fz-input > div")
1141
+ .element as HTMLElement;
1142
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1143
+ true,
1144
+ );
1145
+ expect(
1146
+ container.classList.contains("border-semantic-warning-200"),
1147
+ ).toBe(true);
1148
+ expect(container.classList.contains("ring-2")).toBe(true);
1149
+ expect(container.classList.contains("ring-semantic-warning-100")).toBe(
1150
+ true,
1151
+ );
1152
+ });
1153
+
1154
+ it("works with backoffice environment", async () => {
1155
+ const wrapper = mount(FzInput, {
1156
+ props: {
1157
+ label: "Label",
1158
+ highlighted: true,
1159
+ environment: "backoffice",
1160
+ },
1161
+ slots: {},
1162
+ });
1163
+
1164
+ await wrapper.vm.$nextTick();
1165
+
1166
+ const container = wrapper.find(".fz-input > div")
1167
+ .element as HTMLElement;
1168
+ expect(container.classList.contains("h-32")).toBe(true);
1169
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1170
+ true,
1171
+ );
1172
+ });
1173
+
1174
+ it("works with floating-label variant", async () => {
1175
+ const wrapper = mount(FzInput, {
1176
+ props: {
1177
+ label: "Label",
1178
+ highlighted: true,
1179
+ variant: "floating-label",
1180
+ placeholder: "Placeholder",
1181
+ },
1182
+ slots: {},
1183
+ });
1184
+
1185
+ await wrapper.vm.$nextTick();
1186
+
1187
+ const container = wrapper.find(".fz-input > div")
1188
+ .element as HTMLElement;
1189
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1190
+ true,
1191
+ );
1192
+ expect(container.classList.contains("h-44")).toBe(true);
1193
+ });
1194
+ });
1195
+
1196
+ describe("AIreasoning state", () => {
1197
+ it("applies aiReasoning container classes when aiReasoning is true", async () => {
1198
+ const wrapper = mount(FzInput, {
1199
+ props: {
1200
+ label: "Label",
1201
+ aiReasoning: true,
1202
+ },
1203
+ slots: {},
1204
+ });
1093
1205
 
1094
- await wrapper.vm.$nextTick()
1206
+ await wrapper.vm.$nextTick();
1095
1207
 
1096
- const input = wrapper.find('input').element as HTMLInputElement
1097
- expect(input.getAttribute('aria-expanded')).toBe('true')
1098
- expect(input.getAttribute('aria-haspopup')).toBe('listbox')
1208
+ const container = wrapper.find(".fz-input > div")
1209
+ .element as HTMLElement;
1210
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1211
+ expect(container.classList.contains("border-purple-600")).toBe(true);
1212
+ expect(container.classList.contains("ring-2")).toBe(true);
1213
+ expect(container.classList.contains("ring-purple-200")).toBe(true);
1214
+ });
1099
1215
 
1100
- const rootDiv = wrapper.element as HTMLElement
1101
- expect(rootDiv.getAttribute('aria-expanded')).toBeNull()
1102
- expect(rootDiv.getAttribute('aria-haspopup')).toBeNull()
1103
- })
1104
- })
1216
+ it("auto-renders sparkles icon when aiReasoning is true", async () => {
1217
+ const wrapper = mount(FzInput, {
1218
+ props: {
1219
+ label: "Label",
1220
+ aiReasoning: true,
1221
+ },
1222
+ slots: {},
1223
+ });
1224
+
1225
+ await wrapper.vm.$nextTick();
1226
+
1227
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1228
+ const sparklesIcon = icons.find(
1229
+ (icon: any) => icon.props("name") === "sparkles",
1230
+ );
1231
+ expect(sparklesIcon?.exists()).toBe(true);
1232
+ expect(sparklesIcon?.props("variant")).toBe("fas");
1233
+
1234
+ const rootElement = sparklesIcon?.element as HTMLElement;
1235
+ expect(rootElement.getAttribute("aria-hidden")).toBe("true");
1236
+ });
1237
+
1238
+ it("does not render sparkles when leftIcon is provided", async () => {
1239
+ const wrapper = mount(FzInput, {
1240
+ props: {
1241
+ label: "Label",
1242
+ aiReasoning: true,
1243
+ leftIcon: "search",
1244
+ },
1245
+ slots: {},
1246
+ });
1247
+
1248
+ await wrapper.vm.$nextTick();
1249
+
1250
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1251
+ const sparklesIcon = icons.find(
1252
+ (icon) => icon.props("name") === "sparkles",
1253
+ );
1254
+ expect(sparklesIcon).toBeUndefined();
1255
+
1256
+ const searchIcon = icons.find(
1257
+ (icon) => icon.props("name") === "search",
1258
+ );
1259
+ expect(searchIcon?.exists()).toBe(true);
1260
+ });
1261
+
1262
+ it("does not render sparkles when left-icon slot is provided", async () => {
1263
+ const wrapper = mount(FzInput, {
1264
+ props: {
1265
+ label: "Label",
1266
+ aiReasoning: true,
1267
+ },
1268
+ slots: {
1269
+ "left-icon": '<span data-testid="custom-icon">Custom</span>',
1270
+ },
1271
+ });
1272
+
1273
+ await wrapper.vm.$nextTick();
1274
+
1275
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1276
+ const sparklesIcon = icons.find(
1277
+ (icon) => icon.props("name") === "sparkles",
1278
+ );
1279
+ expect(sparklesIcon).toBeUndefined();
1280
+
1281
+ const customIcon = wrapper.find('[data-testid="custom-icon"]');
1282
+ expect(customIcon.exists()).toBe(true);
1283
+ });
1284
+
1285
+ it("sparkles icon has purple color by default", async () => {
1286
+ const wrapper = mount(FzInput, {
1287
+ props: {
1288
+ label: "Label",
1289
+ aiReasoning: true,
1290
+ },
1291
+ slots: {},
1292
+ });
1293
+
1294
+ await wrapper.vm.$nextTick();
1295
+
1296
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1297
+ const sparklesIcon = icons.find(
1298
+ (icon) => icon.props("name") === "sparkles",
1299
+ );
1300
+ const rootElement = sparklesIcon?.element as HTMLElement;
1301
+ expect(rootElement.classList.contains("text-purple-600")).toBe(true);
1302
+ });
1105
1303
 
1106
- describe('Edge cases', () => {
1304
+ it("sparkles icon is muted when error is true", async () => {
1305
+ const wrapper = mount(FzInput, {
1306
+ props: {
1307
+ label: "Label",
1308
+ aiReasoning: true,
1309
+ error: true,
1310
+ },
1311
+ slots: {
1312
+ errorMessage: "Error message",
1313
+ },
1314
+ });
1315
+
1316
+ await wrapper.vm.$nextTick();
1317
+
1318
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1319
+ const sparklesIcon = icons.find(
1320
+ (icon) => icon.props("name") === "sparkles",
1321
+ );
1322
+ const rootElement = sparklesIcon?.element as HTMLElement;
1323
+ expect(rootElement.classList.contains("text-grey-300")).toBe(true);
1324
+ });
1325
+
1326
+ it("sparkles icon is muted when disabled", async () => {
1327
+ const wrapper = mount(FzInput, {
1328
+ props: {
1329
+ label: "Label",
1330
+ aiReasoning: true,
1331
+ disabled: true,
1332
+ },
1333
+ slots: {},
1334
+ });
1335
+
1336
+ await wrapper.vm.$nextTick();
1337
+
1338
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1339
+ const sparklesIcon = icons.find(
1340
+ (icon) => icon.props("name") === "sparkles",
1341
+ );
1342
+ const rootElement = sparklesIcon?.element as HTMLElement;
1343
+ expect(rootElement.classList.contains("text-grey-300")).toBe(true);
1344
+ });
1345
+
1346
+ it("works with floating-label variant", async () => {
1347
+ const wrapper = mount(FzInput, {
1348
+ props: {
1349
+ label: "Label",
1350
+ aiReasoning: true,
1351
+ variant: "floating-label",
1352
+ placeholder: "Placeholder",
1353
+ },
1354
+ slots: {},
1355
+ });
1356
+
1357
+ await wrapper.vm.$nextTick();
1358
+
1359
+ const container = wrapper.find(".fz-input > div")
1360
+ .element as HTMLElement;
1361
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1362
+ expect(container.classList.contains("h-44")).toBe(true);
1363
+ });
1364
+ });
1365
+
1366
+ describe("State priority", () => {
1367
+ it("error overrides highlighted", async () => {
1368
+ const wrapper = mount(FzInput, {
1369
+ props: {
1370
+ label: "Label",
1371
+ highlighted: true,
1372
+ error: true,
1373
+ },
1374
+ slots: {
1375
+ errorMessage: "Error message",
1376
+ },
1377
+ });
1378
+
1379
+ await wrapper.vm.$nextTick();
1380
+
1381
+ const container = wrapper.find(".fz-input > div")
1382
+ .element as HTMLElement;
1383
+ expect(container.classList.contains("border-semantic-error-200")).toBe(
1384
+ true,
1385
+ );
1386
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1387
+ false,
1388
+ );
1389
+ expect(container.classList.contains("ring-2")).toBe(false);
1390
+ });
1391
+
1392
+ it("error overrides aiReasoning", async () => {
1393
+ const wrapper = mount(FzInput, {
1394
+ props: {
1395
+ label: "Label",
1396
+ aiReasoning: true,
1397
+ error: true,
1398
+ },
1399
+ slots: {
1400
+ errorMessage: "Error message",
1401
+ },
1402
+ });
1403
+
1404
+ await wrapper.vm.$nextTick();
1405
+
1406
+ const container = wrapper.find(".fz-input > div")
1407
+ .element as HTMLElement;
1408
+ expect(container.classList.contains("border-semantic-error-200")).toBe(
1409
+ true,
1410
+ );
1411
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1412
+ expect(container.classList.contains("ring-2")).toBe(false);
1413
+ });
1414
+
1415
+ it("disabled overrides highlighted", async () => {
1416
+ const wrapper = mount(FzInput, {
1417
+ props: {
1418
+ label: "Label",
1419
+ highlighted: true,
1420
+ disabled: true,
1421
+ },
1422
+ slots: {},
1423
+ });
1424
+
1425
+ await wrapper.vm.$nextTick();
1426
+
1427
+ const container = wrapper.find(".fz-input > div")
1428
+ .element as HTMLElement;
1429
+ expect(container.classList.contains("bg-grey-100")).toBe(true);
1430
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1431
+ false,
1432
+ );
1433
+ expect(container.classList.contains("ring-2")).toBe(false);
1434
+ });
1435
+
1436
+ it("disabled overrides aiReasoning", async () => {
1437
+ const wrapper = mount(FzInput, {
1438
+ props: {
1439
+ label: "Label",
1440
+ aiReasoning: true,
1441
+ disabled: true,
1442
+ },
1443
+ slots: {},
1444
+ });
1445
+
1446
+ await wrapper.vm.$nextTick();
1447
+
1448
+ const container = wrapper.find(".fz-input > div")
1449
+ .element as HTMLElement;
1450
+ expect(container.classList.contains("bg-grey-100")).toBe(true);
1451
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1452
+ expect(container.classList.contains("ring-2")).toBe(false);
1453
+ });
1454
+
1455
+ it("readonly overrides highlighted", async () => {
1456
+ const wrapper = mount(FzInput, {
1457
+ props: {
1458
+ label: "Label",
1459
+ highlighted: true,
1460
+ readonly: true,
1461
+ },
1462
+ slots: {},
1463
+ });
1464
+
1465
+ await wrapper.vm.$nextTick();
1466
+
1467
+ const container = wrapper.find(".fz-input > div")
1468
+ .element as HTMLElement;
1469
+ expect(container.classList.contains("bg-grey-100")).toBe(true);
1470
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1471
+ false,
1472
+ );
1473
+ });
1474
+
1475
+ it("highlighted takes priority over aiReasoning when both are set", async () => {
1476
+ const wrapper = mount(FzInput, {
1477
+ props: {
1478
+ label: "Label",
1479
+ highlighted: true,
1480
+ aiReasoning: true,
1481
+ },
1482
+ slots: {},
1483
+ });
1484
+
1485
+ await wrapper.vm.$nextTick();
1486
+
1487
+ const container = wrapper.find(".fz-input > div")
1488
+ .element as HTMLElement;
1489
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1490
+ true,
1491
+ );
1492
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1493
+ });
1494
+ });
1495
+ });
1496
+
1497
+ describe("User input resets emphasis", () => {
1498
+ const findSparkles = (wrapper: ReturnType<typeof mount>) => {
1499
+ const icons = wrapper.findAllComponents({ name: "FzIcon" });
1500
+ return icons.find((icon: any) => icon.props("name") === "sparkles");
1501
+ };
1502
+
1503
+ describe("programmatic modelValue change preserves emphasis", () => {
1504
+ it("highlighted styling persists after programmatic value change", async () => {
1505
+ const wrapper = mount(FzInput, {
1506
+ props: {
1507
+ label: "Label",
1508
+ highlighted: true,
1509
+ modelValue: "",
1510
+ },
1511
+ slots: {},
1512
+ });
1513
+
1514
+ await wrapper.vm.$nextTick();
1515
+ const container = wrapper.find(".fz-input > div")
1516
+ .element as HTMLElement;
1517
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1518
+ true,
1519
+ );
1520
+
1521
+ await wrapper.setProps({ modelValue: "programmatic value" });
1522
+
1523
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1524
+ true,
1525
+ );
1526
+ expect(container.classList.contains("ring-semantic-warning-100")).toBe(
1527
+ true,
1528
+ );
1529
+ expect(wrapper.emitted("update:highlighted")).toBeUndefined();
1530
+ });
1531
+
1532
+ it("aiReasoning styling and sparkles persist after programmatic value change", async () => {
1533
+ const wrapper = mount(FzInput, {
1534
+ props: {
1535
+ label: "Label",
1536
+ aiReasoning: true,
1537
+ modelValue: "",
1538
+ },
1539
+ slots: {},
1540
+ });
1541
+
1542
+ await wrapper.vm.$nextTick();
1543
+ const container = wrapper.find(".fz-input > div")
1544
+ .element as HTMLElement;
1545
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1546
+ expect(findSparkles(wrapper)?.exists()).toBe(true);
1547
+
1548
+ await wrapper.setProps({ modelValue: "programmatic value" });
1549
+
1550
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1551
+ expect(container.classList.contains("ring-purple-200")).toBe(true);
1552
+ expect(findSparkles(wrapper)?.exists()).toBe(true);
1553
+ expect(wrapper.emitted("update:aiReasoning")).toBeUndefined();
1554
+ });
1555
+
1556
+ it("emphasis persists through multiple programmatic value changes", async () => {
1557
+ const wrapper = mount(FzInput, {
1558
+ props: {
1559
+ label: "Label",
1560
+ highlighted: true,
1561
+ modelValue: "",
1562
+ },
1563
+ slots: {},
1564
+ });
1565
+
1566
+ await wrapper.vm.$nextTick();
1567
+ const container = wrapper.find(".fz-input > div")
1568
+ .element as HTMLElement;
1569
+
1570
+ await wrapper.setProps({ modelValue: "first" });
1571
+ await wrapper.setProps({ modelValue: "second" });
1572
+ await wrapper.setProps({ modelValue: "third" });
1573
+
1574
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1575
+ true,
1576
+ );
1577
+ expect(wrapper.emitted("update:highlighted")).toBeUndefined();
1578
+ });
1579
+
1580
+ it("emphasis persists when programmatic value is cleared to empty string", async () => {
1581
+ const wrapper = mount(FzInput, {
1582
+ props: {
1583
+ label: "Label",
1584
+ aiReasoning: true,
1585
+ modelValue: "initial",
1586
+ },
1587
+ slots: {},
1588
+ });
1589
+
1590
+ await wrapper.vm.$nextTick();
1591
+ const container = wrapper.find(".fz-input > div")
1592
+ .element as HTMLElement;
1593
+
1594
+ await wrapper.setProps({ modelValue: "" });
1595
+
1596
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1597
+ expect(findSparkles(wrapper)?.exists()).toBe(true);
1598
+ expect(wrapper.emitted("update:aiReasoning")).toBeUndefined();
1599
+ });
1600
+ });
1601
+
1602
+ describe("user input reverts to default state", () => {
1603
+ it("highlighted reverts to default styling on user input", async () => {
1604
+ const wrapper = mount(FzInput, {
1605
+ props: {
1606
+ label: "Label",
1607
+ highlighted: true,
1608
+ },
1609
+ slots: {},
1610
+ });
1611
+
1612
+ await wrapper.vm.$nextTick();
1613
+ const container = wrapper.find(".fz-input > div")
1614
+ .element as HTMLElement;
1615
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1616
+ true,
1617
+ );
1618
+
1619
+ await wrapper.find("input").setValue("user typed");
1620
+
1621
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1622
+ false,
1623
+ );
1624
+ expect(
1625
+ container.classList.contains("border-semantic-warning-200"),
1626
+ ).toBe(false);
1627
+ expect(container.classList.contains("ring-semantic-warning-100")).toBe(
1628
+ false,
1629
+ );
1630
+ expect(container.classList.contains("border-grey-300")).toBe(true);
1631
+ });
1632
+
1633
+ it("aiReasoning reverts to default styling and hides sparkles on user input", async () => {
1634
+ const wrapper = mount(FzInput, {
1635
+ props: {
1636
+ label: "Label",
1637
+ aiReasoning: true,
1638
+ },
1639
+ slots: {},
1640
+ });
1641
+
1642
+ await wrapper.vm.$nextTick();
1643
+ const container = wrapper.find(".fz-input > div")
1644
+ .element as HTMLElement;
1645
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1646
+ expect(findSparkles(wrapper)?.exists()).toBe(true);
1647
+
1648
+ await wrapper.find("input").setValue("user typed");
1649
+
1650
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1651
+ expect(container.classList.contains("border-purple-600")).toBe(false);
1652
+ expect(container.classList.contains("ring-purple-200")).toBe(false);
1653
+ expect(container.classList.contains("border-grey-300")).toBe(true);
1654
+ expect(findSparkles(wrapper)).toBeUndefined();
1655
+ });
1656
+
1657
+ it("both highlighted and aiReasoning revert on user input", async () => {
1658
+ const wrapper = mount(FzInput, {
1659
+ props: {
1660
+ label: "Label",
1661
+ highlighted: true,
1662
+ aiReasoning: true,
1663
+ },
1664
+ slots: {},
1665
+ });
1666
+
1667
+ await wrapper.vm.$nextTick();
1668
+ const container = wrapper.find(".fz-input > div")
1669
+ .element as HTMLElement;
1670
+ // highlighted takes priority
1671
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1672
+ true,
1673
+ );
1674
+
1675
+ await wrapper.find("input").setValue("user typed");
1676
+
1677
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1678
+ false,
1679
+ );
1680
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1681
+ expect(container.classList.contains("border-grey-300")).toBe(true);
1682
+ expect(findSparkles(wrapper)).toBeUndefined();
1683
+ });
1684
+
1685
+ it("emits update:highlighted false on user input", async () => {
1686
+ const wrapper = mount(FzInput, {
1687
+ props: { label: "Label", highlighted: true },
1688
+ slots: {},
1689
+ });
1690
+
1691
+ await wrapper.find("input").setValue("typed");
1692
+
1693
+ expect(wrapper.emitted("update:highlighted")).toEqual([[false]]);
1694
+ });
1695
+
1696
+ it("emits update:aiReasoning false on user input", async () => {
1697
+ const wrapper = mount(FzInput, {
1698
+ props: { label: "Label", aiReasoning: true },
1699
+ slots: {},
1700
+ });
1701
+
1702
+ await wrapper.find("input").setValue("typed");
1703
+
1704
+ expect(wrapper.emitted("update:aiReasoning")).toEqual([[false]]);
1705
+ });
1706
+
1707
+ it("emits both update events when both are active", async () => {
1708
+ const wrapper = mount(FzInput, {
1709
+ props: { label: "Label", highlighted: true, aiReasoning: true },
1710
+ slots: {},
1711
+ });
1712
+
1713
+ await wrapper.find("input").setValue("typed");
1714
+
1715
+ expect(wrapper.emitted("update:highlighted")).toEqual([[false]]);
1716
+ expect(wrapper.emitted("update:aiReasoning")).toEqual([[false]]);
1717
+ });
1718
+
1719
+ it("does not emit update events when no emphasis is active", async () => {
1720
+ const wrapper = mount(FzInput, {
1721
+ props: { label: "Label" },
1722
+ slots: {},
1723
+ });
1724
+
1725
+ await wrapper.find("input").setValue("typed");
1726
+
1727
+ expect(wrapper.emitted("update:highlighted")).toBeUndefined();
1728
+ expect(wrapper.emitted("update:aiReasoning")).toBeUndefined();
1729
+ });
1730
+
1731
+ it("only emits once even with multiple user keystrokes", async () => {
1732
+ const wrapper = mount(FzInput, {
1733
+ props: { label: "Label", highlighted: true },
1734
+ slots: {},
1735
+ });
1736
+
1737
+ const input = wrapper.find("input");
1738
+ await input.setValue("a");
1739
+ await input.setValue("ab");
1740
+ await input.setValue("abc");
1741
+
1742
+ // First keystroke resets emphasis; subsequent ones have nothing to reset
1743
+ expect(wrapper.emitted("update:highlighted")).toEqual([[false]]);
1744
+ });
1745
+ });
1746
+
1747
+ describe("re-enabling emphasis after user reset", () => {
1748
+ it("re-applies highlighted when prop cycles false → true after user reset", async () => {
1749
+ const wrapper = mount(FzInput, {
1750
+ props: { label: "Label", highlighted: true },
1751
+ slots: {},
1752
+ });
1753
+
1754
+ const container = wrapper.find(".fz-input > div")
1755
+ .element as HTMLElement;
1756
+ await wrapper.find("input").setValue("typed");
1757
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1758
+ false,
1759
+ );
1760
+
1761
+ // Parent syncs to false then re-enables
1762
+ await wrapper.setProps({ highlighted: false });
1763
+ await wrapper.setProps({ highlighted: true });
1764
+
1765
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1766
+ true,
1767
+ );
1768
+ });
1769
+
1770
+ it("re-applies aiReasoning and sparkles when prop cycles false → true after user reset", async () => {
1771
+ const wrapper = mount(FzInput, {
1772
+ props: { label: "Label", aiReasoning: true },
1773
+ slots: {},
1774
+ });
1775
+
1776
+ const container = wrapper.find(".fz-input > div")
1777
+ .element as HTMLElement;
1778
+ await wrapper.find("input").setValue("typed");
1779
+ expect(container.classList.contains("bg-purple-50")).toBe(false);
1780
+ expect(findSparkles(wrapper)).toBeUndefined();
1781
+
1782
+ await wrapper.setProps({ aiReasoning: false });
1783
+ await wrapper.setProps({ aiReasoning: true });
1784
+
1785
+ expect(container.classList.contains("bg-purple-50")).toBe(true);
1786
+ expect(findSparkles(wrapper)?.exists()).toBe(true);
1787
+ });
1788
+
1789
+ it("user can type again after re-enabled emphasis to reset it a second time", async () => {
1790
+ const wrapper = mount(FzInput, {
1791
+ props: { label: "Label", highlighted: true },
1792
+ slots: {},
1793
+ });
1794
+
1795
+ const container = wrapper.find(".fz-input > div")
1796
+ .element as HTMLElement;
1797
+ const input = wrapper.find("input");
1798
+
1799
+ // First cycle: user types → reset
1800
+ await input.setValue("first");
1801
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1802
+ false,
1803
+ );
1804
+
1805
+ // Parent re-enables
1806
+ await wrapper.setProps({ highlighted: false });
1807
+ await wrapper.setProps({ highlighted: true });
1808
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1809
+ true,
1810
+ );
1811
+
1812
+ // Second cycle: user types again → reset again
1813
+ await input.setValue("second");
1814
+ expect(container.classList.contains("bg-semantic-warning-50")).toBe(
1815
+ false,
1816
+ );
1817
+ expect(wrapper.emitted("update:highlighted")).toEqual([
1818
+ [false],
1819
+ [false],
1820
+ ]);
1821
+ });
1822
+ });
1823
+ });
1824
+
1825
+ describe("Edge cases", () => {
1107
1826
  it(`renders ${NUMBER_OF_INPUTS} input with different ids`, async () => {
1108
1827
  const wrapperList = Array.from({ length: NUMBER_OF_INPUTS }).map((_, i) =>
1109
1828
  mount(FzInput, {
@@ -1112,14 +1831,13 @@ describe('FzInput', () => {
1112
1831
  },
1113
1832
  slots: {},
1114
1833
  }),
1115
- )
1116
-
1117
- await Promise.all(wrapperList.map((w) => w.vm.$nextTick()))
1834
+ );
1118
1835
 
1119
- const ids = wrapperList.map((w) => w.find('input').attributes('id'))
1836
+ await Promise.all(wrapperList.map((w) => w.vm.$nextTick()));
1120
1837
 
1121
- expect(new Set(ids).size).toBe(NUMBER_OF_INPUTS)
1122
- })
1123
- })
1124
- })
1838
+ const ids = wrapperList.map((w) => w.find("input").attributes("id"));
1125
1839
 
1840
+ expect(new Set(ids).size).toBe(NUMBER_OF_INPUTS);
1841
+ });
1842
+ });
1843
+ });