@fiscozen/card 0.1.6 → 1.0.0-beta.1

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.
@@ -3,191 +3,574 @@ import { describe, it, expect, vi } from "vitest";
3
3
  import FzCard from "../FzCard.vue";
4
4
 
5
5
  describe("FzCard", () => {
6
- it("renders correctly", async () => {
7
- const wrapper = mount(FzCard, {
8
- props: {
9
- title: "Test Card",
10
- },
11
- });
12
- await wrapper.vm.$nextTick();
13
- expect(wrapper.html()).toContain("Test Card");
14
- expect(wrapper.html()).toMatchSnapshot();
15
- });
6
+ describe("Rendering", () => {
7
+ it("renders correctly and matches snapshot", async () => {
8
+ const wrapper = mount(FzCard, {
9
+ props: {
10
+ title: "Test Card",
11
+ },
12
+ });
13
+ await wrapper.vm.$nextTick();
14
+ expect(wrapper.html()).toContain("Test Card");
15
+ expect(wrapper.html()).toMatchSnapshot();
16
+ });
16
17
 
17
- it("computes correct background color", async () => {
18
- const wrapper = mount(FzCard, {
19
- props: {
20
- title: "Test Card",
21
- color: "orange",
22
- },
18
+ it("renders title when provided", async () => {
19
+ const wrapper = mount(FzCard, {
20
+ props: {
21
+ title: "Test Card Title",
22
+ },
23
+ });
24
+ await wrapper.vm.$nextTick();
25
+ expect(wrapper.find("h2").text()).toBe("Test Card Title");
23
26
  });
24
27
 
25
- await wrapper.vm.$nextTick();
26
- expect(wrapper.find("section").classes()).toContain(
27
- "bg-background-seashell",
28
- );
28
+ it("renders default slot content", async () => {
29
+ const wrapper = mount(FzCard, {
30
+ props: {
31
+ title: "Test Card",
32
+ },
33
+ slots: {
34
+ default: "<p>Test content</p>",
35
+ } as any,
36
+ });
37
+ await wrapper.vm.$nextTick();
38
+ expect(wrapper.html()).toContain("Test content");
39
+ });
29
40
  });
30
41
 
31
- it("computes correct border color", async () => {
32
- const wrapper = mount(FzCard, {
33
- props: {
34
- title: "Test Card",
35
- color: "blue",
36
- primaryAction: {
37
- label: "Action 1",
42
+ describe("Props: color", () => {
43
+ it("applies default background color when no color prop", async () => {
44
+ const wrapper = mount(FzCard, {
45
+ props: {
46
+ title: "Test Card",
38
47
  },
39
- },
48
+ });
49
+ await wrapper.vm.$nextTick();
50
+ expect(wrapper.find("section").classes()).toContain("bg-core-white");
40
51
  });
41
52
 
42
- await wrapper.vm.$nextTick();
43
- expect(wrapper.find("header").classes()).toContain("border-blue-200");
44
- expect(wrapper.find("section").classes()).toContain("border-grey-100");
45
- expect(wrapper.find("footer").classes()).toContain("border-blue-200");
46
- });
53
+ it("applies default background color when color='default'", async () => {
54
+ const wrapper = mount(FzCard, {
55
+ props: {
56
+ title: "Test Card",
57
+ color: "default",
58
+ },
59
+ });
60
+ await wrapper.vm.$nextTick();
61
+ expect(wrapper.find("section").classes()).toContain("bg-core-white");
62
+ });
47
63
 
48
- it("computes atLeastOneButton correctly", async () => {
49
- const wrapper = mount(FzCard, {
50
- props: {
51
- title: "Test Card",
52
- primaryAction: {
53
- label: "Action 1",
64
+ it("applies blue background color (alice-blue)", async () => {
65
+ const wrapper = mount(FzCard, {
66
+ props: {
67
+ title: "Test Card",
68
+ color: "blue",
54
69
  },
55
- },
70
+ });
71
+ await wrapper.vm.$nextTick();
72
+ expect(wrapper.find("section").classes()).toContain(
73
+ "bg-background-alice-blue",
74
+ );
56
75
  });
57
76
 
58
- await wrapper.vm.$nextTick();
59
- // @ts-ignore computed property are not typed
60
- expect(wrapper.vm.atLeastOneButton).toBe(true);
61
- });
77
+ it("applies orange background color (seashell)", async () => {
78
+ const wrapper = mount(FzCard, {
79
+ props: {
80
+ title: "Test Card",
81
+ color: "orange",
82
+ },
83
+ });
84
+ await wrapper.vm.$nextTick();
85
+ expect(wrapper.find("section").classes()).toContain(
86
+ "bg-background-seashell",
87
+ );
88
+ });
62
89
 
63
- it("warns if tertiaryAction is set without primaryAction and secondaryAction", () => {
64
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
65
- mount(FzCard, {
66
- props: {
67
- title: "Test Card",
68
- tertiaryAction: {
69
- icon: "bell",
70
- callback: () => {},
71
- },
72
- },
73
- });
74
- expect(warnSpy).toHaveBeenCalledWith(
75
- "[Fiscozen Design System]: You should set primaryAction and secondaryAction if you want to set tertiaryAction",
76
- );
77
- warnSpy.mockRestore();
78
- });
90
+ it("applies purple background color (pale-purple)", async () => {
91
+ const wrapper = mount(FzCard, {
92
+ props: {
93
+ title: "Test Card",
94
+ color: "purple",
95
+ },
96
+ });
97
+ await wrapper.vm.$nextTick();
98
+ expect(wrapper.find("section").classes()).toContain(
99
+ "bg-background-pale-purple",
100
+ );
101
+ });
102
+
103
+ it("applies grey background color (white-smoke)", async () => {
104
+ const wrapper = mount(FzCard, {
105
+ props: {
106
+ title: "Test Card",
107
+ color: "grey",
108
+ },
109
+ });
110
+ await wrapper.vm.$nextTick();
111
+ expect(wrapper.find("section").classes()).toContain(
112
+ "bg-background-white-smoke",
113
+ );
114
+ });
115
+
116
+ it("applies correct border color for blue", async () => {
117
+ const wrapper = mount(FzCard, {
118
+ props: {
119
+ title: "Test Card",
120
+ color: "blue",
121
+ primaryAction: {
122
+ label: "Action 1",
123
+ },
124
+ },
125
+ });
126
+ await wrapper.vm.$nextTick();
127
+ expect(wrapper.find("section").classes()).toContain(
128
+ "border-background-alice-blue",
129
+ );
130
+ });
131
+
132
+ it("applies correct border color for orange", async () => {
133
+ const wrapper = mount(FzCard, {
134
+ props: {
135
+ title: "Test Card",
136
+ color: "orange",
137
+ primaryAction: {
138
+ label: "Action 1",
139
+ },
140
+ },
141
+ });
142
+ await wrapper.vm.$nextTick();
143
+ expect(wrapper.find("section").classes()).toContain("border-orange-200");
144
+ });
79
145
 
80
- it("warns if secondaryAction is set without primaryAction", () => {
81
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
82
- mount(FzCard, {
83
- props: {
84
- title: "Test Card",
85
- secondaryAction: {
86
- label: "Action 2",
87
- callback: () => {},
146
+ it("applies correct border color for purple", async () => {
147
+ const wrapper = mount(FzCard, {
148
+ props: {
149
+ title: "Test Card",
150
+ color: "purple",
151
+ primaryAction: {
152
+ label: "Action 1",
153
+ },
88
154
  },
89
- },
155
+ });
156
+ await wrapper.vm.$nextTick();
157
+ expect(wrapper.find("section").classes()).toContain("border-purple-200");
90
158
  });
91
159
 
92
- expect(warnSpy).toHaveBeenCalledWith(
93
- "[Fiscozen Design System]: You should set primaryAction if you want to set secondaryAction",
94
- );
95
- warnSpy.mockRestore();
160
+ it("applies correct border color for grey", async () => {
161
+ const wrapper = mount(FzCard, {
162
+ props: {
163
+ title: "Test Card",
164
+ color: "grey",
165
+ primaryAction: {
166
+ label: "Action 1",
167
+ },
168
+ },
169
+ });
170
+ await wrapper.vm.$nextTick();
171
+ expect(wrapper.find("section").classes()).toContain("border-grey-200");
172
+ });
173
+
174
+ it("applies default border color when no color prop", async () => {
175
+ const wrapper = mount(FzCard, {
176
+ props: {
177
+ title: "Test Card",
178
+ primaryAction: {
179
+ label: "Action 1",
180
+ },
181
+ },
182
+ });
183
+ await wrapper.vm.$nextTick();
184
+ expect(wrapper.find("section").classes()).toContain("border-grey-100");
185
+ });
186
+
187
+ it("maps deprecated aliceblue to blue and shows warning", async () => {
188
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
189
+
190
+ const wrapper = mount(FzCard, {
191
+ props: {
192
+ title: "Test Card",
193
+ color: "aliceblue",
194
+ },
195
+ });
196
+
197
+ await wrapper.vm.$nextTick();
198
+
199
+ // Verify aliceblue is mapped to blue (alice-blue background)
200
+ expect(wrapper.find("section").classes()).toContain(
201
+ "bg-background-alice-blue",
202
+ );
203
+
204
+ // Verify warning was shown
205
+ expect(consoleSpy).toHaveBeenCalledTimes(1);
206
+ const warningMessage = consoleSpy.mock.calls[0][0] as string;
207
+ expect(warningMessage).toContain(
208
+ "[FzCard] The color prop value 'aliceblue' is deprecated",
209
+ );
210
+ expect(warningMessage).toContain("Please use 'blue' instead");
211
+
212
+ consoleSpy.mockRestore();
213
+ });
96
214
  });
97
215
 
98
- it("should emit click event when primaryAction is clicked", async () => {
99
- const wrapper = mount(FzCard, {
100
- props: {
101
- title: "Test Card",
102
- primaryAction: {
103
- label: "Action 1",
216
+ describe("Props: collapsible", () => {
217
+ it("should expand and collapse when collapsible is true", async () => {
218
+ const wrapper = mount(FzCard, {
219
+ props: {
220
+ title: "Test Card",
221
+ collapsible: true,
222
+ },
223
+ });
224
+ await wrapper.vm.$nextTick();
225
+
226
+ expect(wrapper.find("article").exists()).toBeFalsy();
227
+ await wrapper.find("header").find("button").trigger("click");
228
+ expect(wrapper.find("article").exists()).toBeTruthy();
229
+ });
230
+
231
+ it("should show content by default when not collapsible", async () => {
232
+ const wrapper = mount(FzCard, {
233
+ props: {
234
+ title: "Test Card",
235
+ },
236
+ });
237
+ await wrapper.vm.$nextTick();
238
+ expect(wrapper.find("article").exists()).toBeTruthy();
239
+ });
240
+
241
+ it("should respect defaultExpanded prop", async () => {
242
+ const wrapper = mount(FzCard, {
243
+ props: {
244
+ title: "Test Card",
245
+ collapsible: true,
246
+ defaultExpanded: true,
104
247
  },
105
- },
248
+ });
249
+ await wrapper.vm.$nextTick();
250
+ expect(wrapper.find("article").exists()).toBeTruthy();
106
251
  });
107
252
 
108
- await wrapper.find("button").trigger("click");
109
- expect(wrapper.emitted("fzprimary:click")).toBeTruthy();
253
+ it("should keep content alive when alwaysAlive is true", async () => {
254
+ const wrapper = mount(FzCard, {
255
+ props: {
256
+ title: "Test Card",
257
+ collapsible: true,
258
+ alwaysAlive: true,
259
+ },
260
+ });
261
+ await wrapper.vm.$nextTick();
262
+ // Content should exist even when collapsed
263
+ expect(wrapper.find("article").exists()).toBeTruthy();
264
+ });
110
265
  });
111
266
 
112
- it("should emit click event when secondaryAction is clicked", async () => {
113
- const wrapper = mount(FzCard, {
114
- props: {
115
- title: "Test Card",
116
- primaryAction: {
117
- label: "Action 1",
267
+ describe("Props: actions", () => {
268
+ it("renders footer with primaryAction", async () => {
269
+ const wrapper = mount(FzCard, {
270
+ props: {
271
+ title: "Test Card",
272
+ primaryAction: {
273
+ label: "Action 1",
274
+ },
118
275
  },
119
- secondaryAction: {
120
- label: "Action 2",
276
+ });
277
+
278
+ await wrapper.vm.$nextTick();
279
+ expect(wrapper.find("footer").exists()).toBeTruthy();
280
+ expect(wrapper.html()).toContain("Action 1");
281
+ });
282
+
283
+ it("renders footer with primaryAction and secondaryAction", async () => {
284
+ const wrapper = mount(FzCard, {
285
+ props: {
286
+ title: "Test Card",
287
+ primaryAction: {
288
+ label: "Primary",
289
+ },
290
+ secondaryAction: {
291
+ label: "Secondary",
292
+ },
293
+ },
294
+ });
295
+
296
+ await wrapper.vm.$nextTick();
297
+ expect(wrapper.find("footer").exists()).toBeTruthy();
298
+ expect(wrapper.html()).toContain("Primary");
299
+ expect(wrapper.html()).toContain("Secondary");
300
+ });
301
+
302
+ it("renders footer with all three actions", async () => {
303
+ const wrapper = mount(FzCard, {
304
+ props: {
305
+ title: "Test Card",
306
+ primaryAction: {
307
+ label: "Primary",
308
+ },
309
+ secondaryAction: {
310
+ label: "Secondary",
311
+ },
312
+ tertiaryAction: {
313
+ icon: "bell",
314
+ },
315
+ },
316
+ });
317
+
318
+ await wrapper.vm.$nextTick();
319
+ expect(wrapper.find("footer").exists()).toBeTruthy();
320
+ });
321
+
322
+ it("warns if tertiaryAction is set without primaryAction and secondaryAction", () => {
323
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
324
+ mount(FzCard, {
325
+ props: {
326
+ title: "Test Card",
327
+ tertiaryAction: {
328
+ icon: "bell",
329
+ },
121
330
  },
122
- },
331
+ });
332
+ expect(warnSpy).toHaveBeenCalledWith(
333
+ "[Fiscozen Design System]: You should set primaryAction and secondaryAction if you want to set tertiaryAction",
334
+ );
335
+ warnSpy.mockRestore();
123
336
  });
124
337
 
125
- await wrapper.find("button").trigger("click");
126
- expect(wrapper.emitted("fzsecondary:click")).toBeTruthy();
338
+ it("warns if secondaryAction is set without primaryAction", () => {
339
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
340
+ mount(FzCard, {
341
+ props: {
342
+ title: "Test Card",
343
+ secondaryAction: {
344
+ label: "Action 2",
345
+ },
346
+ },
347
+ });
348
+
349
+ expect(warnSpy).toHaveBeenCalledWith(
350
+ "[Fiscozen Design System]: You should set primaryAction if you want to set secondaryAction",
351
+ );
352
+ warnSpy.mockRestore();
353
+ });
127
354
  });
128
355
 
129
- it("should emit click event when tertiaryAction is clicked", async () => {
130
- const wrapper = mount(FzCard, {
131
- props: {
132
- title: "Test Card",
133
- primaryAction: {
134
- label: "Action 1",
356
+ describe("Events", () => {
357
+ it("should emit fzprimary:click when primaryAction is clicked", async () => {
358
+ const wrapper = mount(FzCard, {
359
+ props: {
360
+ title: "Test Card",
361
+ primaryAction: {
362
+ label: "Action 1",
363
+ },
135
364
  },
136
- secondaryAction: {
137
- label: "Action 2",
365
+ });
366
+
367
+ await wrapper.findAll("button")[0].trigger("click");
368
+ expect(wrapper.emitted("fzprimary:click")).toBeTruthy();
369
+ });
370
+
371
+ it("should emit fzsecondary:click when secondaryAction is clicked", async () => {
372
+ const wrapper = mount(FzCard, {
373
+ props: {
374
+ title: "Test Card",
375
+ primaryAction: {
376
+ label: "Action 1",
377
+ },
378
+ secondaryAction: {
379
+ label: "Action 2",
380
+ },
138
381
  },
139
- tertiaryAction: {
140
- icon: "bell",
382
+ });
383
+
384
+ await wrapper.findAll("button")[0].trigger("click");
385
+ expect(wrapper.emitted("fzsecondary:click")).toBeTruthy();
386
+ });
387
+
388
+ it("should emit fztertiary:click when tertiaryAction is clicked", async () => {
389
+ const wrapper = mount(FzCard, {
390
+ props: {
391
+ title: "Test Card",
392
+ primaryAction: {
393
+ label: "Action 1",
394
+ },
395
+ secondaryAction: {
396
+ label: "Action 2",
397
+ },
398
+ tertiaryAction: {
399
+ icon: "bell",
400
+ },
141
401
  },
142
- },
402
+ });
403
+
404
+ await wrapper.findAll("button")[0].trigger("click");
405
+ expect(wrapper.emitted("fztertiary:click")).toBeTruthy();
143
406
  });
144
407
 
145
- await wrapper.find("button").trigger("click");
146
- expect(wrapper.emitted("fztertiary:click")).toBeTruthy();
408
+ it("should emit fzcard:click-info when info icon is clicked", async () => {
409
+ const wrapper = mount(FzCard, {
410
+ props: {
411
+ title: "Test Card",
412
+ hasInfoIcon: true,
413
+ },
414
+ });
415
+
416
+ await wrapper.vm.$nextTick();
417
+ const buttons = wrapper.findAll("button");
418
+ // Find the info icon button (it should be the first one when only hasInfoIcon is true)
419
+ await buttons[0].trigger("click");
420
+ expect(wrapper.emitted("fzcard:click-info")).toBeTruthy();
421
+ });
147
422
  });
148
423
 
149
- it("should expand and collapse when collapsible is true", async () => {
150
- const wrapper = mount(FzCard, {
151
- props: {
152
- title: "Test Card",
153
- collapsible: true,
154
- },
424
+ describe("Slots", () => {
425
+ it("should hide the header when no title and no slots are defined", async () => {
426
+ const wrapper = mount(FzCard, {
427
+ props: {},
428
+ });
429
+ await wrapper.vm.$nextTick();
430
+
431
+ expect(wrapper.find("header").exists()).toBeFalsy();
155
432
  });
156
- await wrapper.vm.$nextTick();
157
433
 
158
- expect(wrapper.find("article").exists()).toBeFalsy();
159
- await wrapper.find("header").find("button").trigger("click");
160
- expect(wrapper.find("article").exists()).toBeTruthy();
434
+ it("should show the header when no title is defined but header slot is", async () => {
435
+ const wrapper = mount(FzCard, {
436
+ slots: {
437
+ header: "<div>Header</div>",
438
+ } as any,
439
+ });
440
+ await wrapper.vm.$nextTick();
441
+
442
+ expect(wrapper.find("header").exists()).toBeTruthy();
443
+ expect(wrapper.html()).toContain("Header");
444
+ });
445
+
446
+ it("should show the header when no title is defined but header-content slot is", async () => {
447
+ const wrapper = mount(FzCard, {
448
+ slots: {
449
+ "header-content": "<div>Header Content</div>",
450
+ } as any,
451
+ });
452
+ await wrapper.vm.$nextTick();
453
+
454
+ expect(wrapper.find("header").exists()).toBeTruthy();
455
+ expect(wrapper.html()).toContain("Header Content");
456
+ });
457
+
458
+ it("should render footer slot content when provided", async () => {
459
+ const wrapper = mount(FzCard, {
460
+ props: {
461
+ title: "Test Card",
462
+ },
463
+ slots: {
464
+ footer: "<div>Custom Footer</div>",
465
+ } as any,
466
+ });
467
+ await wrapper.vm.$nextTick();
468
+
469
+ expect(wrapper.find("footer").exists()).toBeTruthy();
470
+ expect(wrapper.html()).toContain("Custom Footer");
471
+ });
161
472
  });
162
473
 
163
- it("should hide the header when no title is defined", async () => {
164
- const wrapper = mount(FzCard, {
165
- props: {},
474
+ describe("Accessibility", () => {
475
+ it("should have proper title attribute on h2", async () => {
476
+ const wrapper = mount(FzCard, {
477
+ props: {
478
+ title: "Test Card Title",
479
+ },
480
+ });
481
+ await wrapper.vm.$nextTick();
482
+ expect(wrapper.find("h2").attributes("title")).toBe("Test Card Title");
166
483
  });
167
- await wrapper.vm.$nextTick();
168
484
 
169
- expect(wrapper.find("header").exists()).toBeFalsy();
485
+ it("should have proper role on collapsible header", async () => {
486
+ const wrapper = mount(FzCard, {
487
+ props: {
488
+ title: "Test Card",
489
+ collapsible: true,
490
+ },
491
+ });
492
+ await wrapper.vm.$nextTick();
493
+ expect(wrapper.find("header").classes()).toContain("cursor-pointer");
494
+ });
170
495
  });
171
496
 
172
- it("should show the header when no title is defined but header slot is", async () => {
173
- const wrapper = mount(FzCard, {
174
- slots: {
175
- header: "<div>Header</div>",
176
- },
497
+ describe("Props: hasInfoIcon", () => {
498
+ it("should not show info icon when hasInfoIcon is false", async () => {
499
+ const wrapper = mount(FzCard, {
500
+ props: {
501
+ title: "Test Card",
502
+ hasInfoIcon: false,
503
+ },
504
+ });
505
+ await wrapper.vm.$nextTick();
506
+ // No info icon button should be present
507
+ const buttons = wrapper.findAll("button");
508
+ expect(buttons.length).toBe(0);
177
509
  });
178
- await wrapper.vm.$nextTick();
179
510
 
180
- expect(wrapper.find("header").exists()).toBeTruthy();
511
+ it("should show info icon when hasInfoIcon is true", async () => {
512
+ const wrapper = mount(FzCard, {
513
+ props: {
514
+ title: "Test Card",
515
+ hasInfoIcon: true,
516
+ },
517
+ });
518
+ await wrapper.vm.$nextTick();
519
+ // Info icon button should be present
520
+ const buttons = wrapper.findAll("button");
521
+ expect(buttons.length).toBe(1);
522
+ });
523
+
524
+ it("should show info icon alongside collapse icon", async () => {
525
+ const wrapper = mount(FzCard, {
526
+ props: {
527
+ title: "Test Card",
528
+ hasInfoIcon: true,
529
+ collapsible: true,
530
+ },
531
+ });
532
+ await wrapper.vm.$nextTick();
533
+ // Both info and collapse icons should be present
534
+ const buttons = wrapper.findAll("button");
535
+ expect(buttons.length).toBe(2);
536
+ });
181
537
  });
182
538
 
183
- it("should show the header when no title is defined but header-content slot is", async () => {
184
- const wrapper = mount(FzCard, {
185
- slots: {
186
- "header-content": "<div>Header</div>",
187
- },
539
+ describe("Edge cases", () => {
540
+ it("should handle card without any props", async () => {
541
+ const wrapper = mount(FzCard);
542
+ await wrapper.vm.$nextTick();
543
+ expect(wrapper.find("section").exists()).toBeTruthy();
544
+ expect(wrapper.find("article").exists()).toBeTruthy();
188
545
  });
189
- await wrapper.vm.$nextTick();
190
546
 
191
- expect(wrapper.find("header").exists()).toBeTruthy();
547
+ it("should handle contentClass prop", async () => {
548
+ const wrapper = mount(FzCard, {
549
+ props: {
550
+ title: "Test Card",
551
+ contentClass: "custom-class",
552
+ },
553
+ });
554
+ await wrapper.vm.$nextTick();
555
+ expect(wrapper.find("article").classes()).toContain("custom-class");
556
+ });
557
+
558
+ it("exposes toggleOpen method", async () => {
559
+ const wrapper = mount(FzCard, {
560
+ props: {
561
+ title: "Test Card",
562
+ collapsible: true,
563
+ },
564
+ });
565
+ await wrapper.vm.$nextTick();
566
+
567
+ expect(wrapper.find("article").exists()).toBeFalsy();
568
+
569
+ // Call exposed method
570
+ (wrapper.vm as any).toggleOpen();
571
+ await wrapper.vm.$nextTick();
572
+
573
+ expect(wrapper.find("article").exists()).toBeTruthy();
574
+ });
192
575
  });
193
576
  });