@zap-wunschlachen/wl-shared-components 1.0.76 → 1.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/playwright.yml +229 -229
- package/.github/workflows/static.yml +61 -61
- package/.github/workflows/update-snapshots.yml +37 -37
- package/.prettierrc.json +8 -8
- package/.storybook/main.ts +18 -18
- package/.storybook/preview.ts +37 -37
- package/.storybook/storyWrapper.vue +18 -18
- package/.storybook/withVuetifyTheme.decorator.ts +21 -21
- package/App.vue +139 -139
- package/README.md +56 -56
- package/docs/assets.md +62 -62
- package/heroicons.ts +75 -75
- package/index.html +19 -19
- package/package.json +71 -71
- package/playwright.config.ts +48 -48
- package/public/background.svg +60 -60
- package/public/style.css +187 -187
- package/public/technologies.svg +22 -22
- package/scripts/check-translations.ts +352 -352
- package/src/assets/css/base.css +242 -242
- package/src/assets/css/variables.css +176 -176
- package/src/components/Accordion/Accordion.css +65 -65
- package/src/components/Accordion/AccordionGroup.vue +88 -88
- package/src/components/Accordion/AccordionItem.vue +272 -272
- package/src/components/Accordion/presets/default.css +4 -4
- package/src/components/Accordion/presets/elevated.css +25 -25
- package/src/components/Accordion/presets/filled.css +26 -26
- package/src/components/Accordion/presets/index.css +5 -5
- package/src/components/Accordion/presets/plain.css +34 -34
- package/src/components/Appointment/Card/Actions.css +54 -54
- package/src/components/Appointment/Card/Actions.vue +99 -99
- package/src/components/Appointment/Card/AnamneseNotification.css +20 -20
- package/src/components/Appointment/Card/AnamneseNotification.vue +23 -23
- package/src/components/Appointment/Card/Card.css +99 -99
- package/src/components/Appointment/Card/Card.vue +97 -97
- package/src/components/Appointment/Card/Details.css +62 -62
- package/src/components/Appointment/Card/Details.vue +44 -44
- package/src/components/Audio/Audio.vue +187 -187
- package/src/components/Audio/Waveform.vue +118 -118
- package/src/components/Banner/Banner.css +29 -29
- package/src/components/Banner/Banner.vue +89 -89
- package/src/components/Button/Button.vue +257 -257
- package/src/components/CheckBox/CheckBox.css +234 -234
- package/src/components/CheckBox/Checkbox.vue +184 -184
- package/src/components/DateInput/DateInput.css +2 -2
- package/src/components/DateInput/DateInput.vue +376 -370
- package/src/components/Dialog/Dialog.css +6 -6
- package/src/components/Dialog/Dialog.vue +46 -46
- package/src/components/EditField/EditField.css +19 -19
- package/src/components/EditField/EditField.vue +211 -211
- package/src/components/ErrorPage/ErrorPage.css +172 -172
- package/src/components/IconBullet/IconBullet.vue +104 -104
- package/src/components/IconBullet/IconBulletList.vue +55 -55
- package/src/components/Icons/AdvanceAppointments.vue +161 -161
- package/src/components/Icons/Audio/CloudFailed.vue +27 -27
- package/src/components/Icons/Audio/CloudSaved.vue +28 -28
- package/src/components/Icons/Audio/Delete.vue +22 -22
- package/src/components/Icons/Audio/Pause.vue +25 -25
- package/src/components/Icons/Audio/Play.vue +22 -22
- package/src/components/Icons/Calendar.vue +28 -28
- package/src/components/Icons/CalendarNotification.vue +137 -137
- package/src/components/Icons/Chair.vue +43 -43
- package/src/components/Icons/ChairNotification.vue +46 -46
- package/src/components/Icons/Circle.vue +66 -66
- package/src/components/Icons/FavIcon.vue +69 -69
- package/src/components/Icons/FilledCircle.vue +11 -11
- package/src/components/Icons/Group3.vue +57 -57
- package/src/components/Icons/Play.vue +16 -16
- package/src/components/Icons/RingNotification.vue +65 -65
- package/src/components/Icons/SolidArrowRight.vue +14 -14
- package/src/components/Icons/checkbox.vue +19 -19
- package/src/components/Icons/outlineChecked.vue +38 -38
- package/src/components/Input/Input.css +234 -234
- package/src/components/Input/Input.vue +281 -281
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.css +7 -7
- package/src/components/Laboratory/AppointmentCard/AppointmentCard.vue +116 -116
- package/src/components/Laboratory/ChatBoxImage/ChatBoxImage.vue +81 -81
- package/src/components/Laboratory/ChatMessage/ChatMessage.vue +113 -113
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.css +4 -4
- package/src/components/Laboratory/ChatMessage/ChatMessageBadge.vue +99 -99
- package/src/components/Laboratory/ChatNotification/ChatNotification.vue +130 -130
- package/src/components/Laboratory/DocumentCard/DocumentCard.css +3 -3
- package/src/components/Laboratory/DocumentCard/DocumentCard.vue +50 -50
- package/src/components/Laboratory/DocumentCard/DocumentCardItem.vue +53 -53
- package/src/components/Laboratory/InfoCard/InfoCard.vue +162 -162
- package/src/components/Laboratory/MainColumnsBar/MainColumnsBar.vue +102 -102
- package/src/components/Laboratory/ProgressCircle/ProgressCircle.vue +152 -152
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.css +33 -33
- package/src/components/Laboratory/ProgressLinear/ProgressLinear.vue +75 -75
- package/src/components/Laboratory/SelectionColumnBar/SelectionColumnBar.vue +92 -92
- package/src/components/Laboratory/StatusNotification/StatusNotification.vue +49 -49
- package/src/components/Laboratory/TagLabel/TagLabel.vue +126 -126
- package/src/components/Laboratory/TagLabelGroup/TagLabelGroup.vue +97 -97
- package/src/components/Laboratory/TicketCard/TicketCard.css +3 -3
- package/src/components/Laboratory/TicketCard/TicketCard.vue +143 -143
- package/src/components/Laboratory/TimeLine/TimeLineEvent.css +18 -18
- package/src/components/Laboratory/TimeLine/TimeLineEvent.vue +119 -119
- package/src/components/Laboratory/TimeLine/Timeline.css +4 -4
- package/src/components/Laboratory/TimeLine/Timeline.vue +30 -30
- package/src/components/Loader/Loader.css +78 -78
- package/src/components/MaintenanceBanner/MaintenanceBanner.css +353 -353
- package/src/components/MaintenanceBanner/MaintenanceBanner.vue +140 -140
- package/src/components/MaintenanceBanner/MaintenanceIllustration.vue +54 -54
- package/src/components/Modal/Modal.css +5 -5
- package/src/components/Modal/Modal.vue +29 -29
- package/src/components/NotificationBubble/NotificationBubble.css +4 -4
- package/src/components/NotificationBubble/NotificationBubble.vue +90 -90
- package/src/components/OtpInput/OtpInput.css +43 -43
- package/src/components/OtpInput/OtpInput.vue +181 -181
- package/src/components/PhoneInput/PhoneInput.css +151 -126
- package/src/components/PhoneInput/PhoneInput.vue +230 -139
- package/src/components/RadioGroup/RadioGroup.css +65 -0
- package/src/components/RadioGroup/RadioGroup.vue +134 -0
- package/src/components/Select/Select.css +172 -172
- package/src/components/Select/Select.vue +377 -377
- package/src/components/SelectAutocomplete/SelectAutocomplete.css +172 -172
- package/src/components/SelectAutocomplete/SelectAutocomplete.vue +414 -414
- package/src/components/TextArea/TextArea.css +269 -269
- package/src/components/TextArea/TextArea.vue +207 -207
- package/src/components/TickBox/TickBox.css +116 -116
- package/src/components/TickBox/TickBox.vue +172 -172
- package/src/components/Tile/Tile.css +106 -106
- package/src/components/Tile/Tile.vue +173 -173
- package/src/components/accessibility.css +218 -218
- package/src/components/index.ts +110 -109
- package/src/constants/iconEnums.ts +3 -3
- package/src/i18n/i18n.ts +15 -15
- package/src/i18n/locales/de.json +30 -30
- package/src/i18n/locales/en.json +30 -30
- package/src/index.ts +43 -43
- package/src/main.ts +11 -11
- package/src/pages/AccordionGroupPage.vue +873 -873
- package/src/pages/AllPage.vue +2483 -2365
- package/src/pages/SelectPage.vue +1302 -1302
- package/src/pages/TilePage.vue +902 -902
- package/src/plugins/vuetify.ts +54 -54
- package/src/shims-vue.d.ts +30 -30
- package/src/utils/index.ts +733 -733
- package/src/vite-env.d.ts +1 -1
- package/tests/unit/accessibility/component-a11y.spec.ts +657 -657
- package/tests/unit/components/Accordion/AccordionGroup.spec.ts +228 -228
- package/tests/unit/components/Accordion/AccordionItem.spec.ts +257 -257
- package/tests/unit/components/Appointment/AnamneseNotification.spec.ts +176 -176
- package/tests/unit/components/Appointment/Card/Actions.spec.ts +436 -436
- package/tests/unit/components/Appointment/Card/Card.spec.ts +531 -531
- package/tests/unit/components/Appointment/Card/Details.spec.ts +395 -395
- package/tests/unit/components/Audio/Audio.spec.ts +403 -403
- package/tests/unit/components/Audio/Waveform.spec.ts +483 -483
- package/tests/unit/components/Background/Background.spec.ts +177 -177
- package/tests/unit/components/Core/AnamneseAnswerDialog.spec.ts +344 -0
- package/tests/unit/components/Core/Banner.spec.ts +187 -0
- package/tests/unit/components/Core/Button.spec.ts +346 -346
- package/tests/unit/components/Core/Checkbox.spec.ts +544 -544
- package/tests/unit/components/Core/DateInput.spec.ts +702 -702
- package/tests/unit/components/Core/Dialog.spec.ts +448 -448
- package/tests/unit/components/Core/EditField.spec.ts +541 -541
- package/tests/unit/components/Core/Input.spec.ts +512 -512
- package/tests/unit/components/Core/List.spec.ts +163 -0
- package/tests/unit/components/Core/ListItem.spec.ts +205 -0
- package/tests/unit/components/Core/Modal.spec.ts +518 -518
- package/tests/unit/components/Core/NotificationBubble.spec.ts +606 -606
- package/tests/unit/components/Core/OtpInput.spec.ts +708 -708
- package/tests/unit/components/Core/PhoneInput.spec.ts +757 -619
- package/tests/unit/components/Core/RadioGroup.spec.ts +318 -0
- package/tests/unit/components/Core/Select.spec.ts +712 -712
- package/tests/unit/components/Core/SelectAutocomplete.spec.ts +361 -0
- package/tests/unit/components/Core/TextArea.spec.ts +565 -565
- package/tests/unit/components/Core/TickBox.spec.ts +836 -836
- package/tests/unit/components/Core/Tile.spec.ts +286 -0
- package/tests/unit/components/DateInput/DateInput.spec.ts +128 -0
- package/tests/unit/components/ErrorPage/ErrorPage.spec.ts +313 -313
- package/tests/unit/components/ErrorPage/ErrorPageLogo.spec.ts +153 -153
- package/tests/unit/components/IconBullet/IconBullet.spec.ts +356 -356
- package/tests/unit/components/IconBullet/IconBulletList.spec.ts +371 -371
- package/tests/unit/components/Icons/AdvanceAppointments.spec.ts +186 -186
- package/tests/unit/components/Icons/Audio/CloudFailed.spec.ts +108 -108
- package/tests/unit/components/Icons/Audio/CloudSaved.spec.ts +149 -149
- package/tests/unit/components/Icons/Audio/Delete.spec.ts +158 -158
- package/tests/unit/components/Icons/Audio/Pause.spec.ts +208 -208
- package/tests/unit/components/Icons/Audio/Play.spec.ts +217 -217
- package/tests/unit/components/Icons/CalendarNotification.spec.ts +193 -193
- package/tests/unit/components/Icons/Chair.spec.ts +241 -241
- package/tests/unit/components/Icons/ChairNotification.spec.ts +318 -318
- package/tests/unit/components/Icons/Circle.spec.ts +255 -255
- package/tests/unit/components/Icons/FavIcon.spec.ts +259 -259
- package/tests/unit/components/Icons/FilledCircle.spec.ts +274 -274
- package/tests/unit/components/Icons/Group3.spec.ts +362 -362
- package/tests/unit/components/Icons/Logo.spec.ts +229 -229
- package/tests/unit/components/Icons/MiniLogo.spec.ts +38 -38
- package/tests/unit/components/Icons/RingNotification.spec.ts +400 -400
- package/tests/unit/components/Icons/SolidArrowRight.spec.ts +49 -49
- package/tests/unit/components/Icons/calendar.spec.ts +293 -293
- package/tests/unit/components/Icons/checkbox.spec.ts +315 -315
- package/tests/unit/components/Icons/outlineChecked.spec.ts +441 -441
- package/tests/unit/components/Icons/play.spec.ts +315 -315
- package/tests/unit/components/Laboratory/AppointmentCard.spec.ts +167 -167
- package/tests/unit/components/Laboratory/ChatBoxImage.spec.ts +179 -179
- package/tests/unit/components/Laboratory/ChatMessage.spec.ts +263 -263
- package/tests/unit/components/Laboratory/ChatMessageBadge.spec.ts +282 -282
- package/tests/unit/components/Laboratory/ChatNotification.spec.ts +256 -256
- package/tests/unit/components/Laboratory/DocumentCard.spec.ts +228 -228
- package/tests/unit/components/Laboratory/DocumentCardItem.spec.ts +236 -236
- package/tests/unit/components/Laboratory/InfoCard.spec.ts +308 -308
- package/tests/unit/components/Laboratory/MainColumnsBar.spec.ts +251 -251
- package/tests/unit/components/Laboratory/ProgressCircle.spec.ts +290 -290
- package/tests/unit/components/Laboratory/ProgressLinear.spec.ts +275 -275
- package/tests/unit/components/Laboratory/SelectionColumnBar.spec.ts +288 -288
- package/tests/unit/components/Laboratory/StatusNotification.spec.ts +296 -296
- package/tests/unit/components/Laboratory/TagLabel.spec.ts +353 -353
- package/tests/unit/components/Laboratory/TagLabelGroup.spec.ts +377 -377
- package/tests/unit/components/Laboratory/TicketCard.spec.ts +351 -351
- package/tests/unit/components/Laboratory/TimeLineEvent.spec.ts +381 -381
- package/tests/unit/components/Laboratory/Timeline.spec.ts +419 -419
- package/tests/unit/components/Loader/Loader.spec.ts +197 -197
- package/tests/unit/components/MaintenanceBanner/MaintenanceBanner.spec.ts +302 -302
- package/tests/unit/constants/iconEnums.spec.ts +39 -39
- package/tests/unit/i18n/i18n.spec.ts +88 -88
- package/tests/unit/plugins/vuetify.spec.ts +182 -182
- package/tests/unit/setup.ts +237 -237
- package/tests/unit/src/components/index.spec.ts.skip +192 -192
- package/tests/unit/src/index.spec.ts.skip +182 -182
- package/tests/unit/src/main.spec.ts +111 -111
- package/tests/unit/utils/accessibility.spec.ts +318 -318
- package/tests/unit/utils/anamnese.spec.ts +531 -0
- package/tsconfig.json +26 -26
- package/vite.config.ts +29 -29
- package/vitest.config.ts +91 -91
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import Tile from '@components/Tile/Tile.vue';
|
|
4
|
+
|
|
5
|
+
// Mock Banner component
|
|
6
|
+
vi.mock('@components/Banner/Banner.vue', () => ({
|
|
7
|
+
default: {
|
|
8
|
+
name: 'Banner',
|
|
9
|
+
template: '<div class="wl-banner" data-testid="banner">{{ text }}</div>',
|
|
10
|
+
props: ['text', 'color', 'bgColor', 'foldColor'],
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const mountTile = (props: Record<string, any> = {}, options: Record<string, any> = {}) => {
|
|
15
|
+
return mount(Tile, {
|
|
16
|
+
props,
|
|
17
|
+
...options,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
describe('Tile', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// ─── Default Rendering ────────────────────────────────────────────
|
|
27
|
+
describe('Default Rendering', () => {
|
|
28
|
+
it('renders with data-testid="root"', () => {
|
|
29
|
+
const wrapper = mountTile();
|
|
30
|
+
expect(wrapper.find('[data-testid="root"]').exists()).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('applies wl-tile CSS class', () => {
|
|
34
|
+
const wrapper = mountTile();
|
|
35
|
+
expect(wrapper.find('.wl-tile').exists()).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders the body container', () => {
|
|
39
|
+
const wrapper = mountTile();
|
|
40
|
+
expect(wrapper.find('.wl-tile__body').exists()).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('renders icon by default (showIcon=true)', () => {
|
|
44
|
+
const wrapper = mountTile();
|
|
45
|
+
expect(wrapper.find('.wl-tile__icon').exists()).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('is focusable by default (tabindex=0)', () => {
|
|
49
|
+
const wrapper = mountTile();
|
|
50
|
+
expect(wrapper.find('[data-testid="root"]').attributes('tabindex')).toBe('0');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── Title ────────────────────────────────────────────────────────
|
|
55
|
+
describe('Title', () => {
|
|
56
|
+
it('renders title when provided', () => {
|
|
57
|
+
const wrapper = mountTile({ title: 'My Tile' });
|
|
58
|
+
expect(wrapper.find('.wl-tile__title').text()).toBe('My Tile');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('does not render title element when title is undefined', () => {
|
|
62
|
+
const wrapper = mountTile({ showTitle: false });
|
|
63
|
+
expect(wrapper.find('.wl-tile__title').exists()).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('hides title when showTitle is false', () => {
|
|
67
|
+
const wrapper = mountTile({ title: 'Hidden', showTitle: false });
|
|
68
|
+
expect(wrapper.find('.wl-tile__title').exists()).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ─── Description ──────────────────────────────────────────────────
|
|
73
|
+
describe('Description', () => {
|
|
74
|
+
it('renders description when provided', () => {
|
|
75
|
+
const wrapper = mountTile({ description: 'Tile description' });
|
|
76
|
+
expect(wrapper.find('.wl-tile__description').text()).toBe('Tile description');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('does not render description element when not provided', () => {
|
|
80
|
+
const wrapper = mountTile();
|
|
81
|
+
expect(wrapper.find('.wl-tile__description').exists()).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ─── Label (default, non-banner) ─────────────────────────────────
|
|
86
|
+
describe('Label (default)', () => {
|
|
87
|
+
it('renders label text when provided', () => {
|
|
88
|
+
const wrapper = mountTile({ label: 'Status: OK' });
|
|
89
|
+
expect(wrapper.find('.wl-tile__label-text').text()).toBe('Status: OK');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not render label when not provided', () => {
|
|
93
|
+
const wrapper = mountTile();
|
|
94
|
+
expect(wrapper.find('.wl-tile__label').exists()).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('hides label when showLabel is false', () => {
|
|
98
|
+
const wrapper = mountTile({ label: 'Hidden', showLabel: false });
|
|
99
|
+
expect(wrapper.find('.wl-tile__label').exists()).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('does not show default label when useWLBanner is true', () => {
|
|
103
|
+
const wrapper = mountTile({ label: 'Banner', useWLBanner: true });
|
|
104
|
+
expect(wrapper.find('.wl-tile__label').exists()).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ─── Banner Mode ──────────────────────────────────────────────────
|
|
109
|
+
describe('Banner Mode (useWLBanner)', () => {
|
|
110
|
+
it('renders Banner component when useWLBanner and label are set', () => {
|
|
111
|
+
const wrapper = mountTile({ useWLBanner: true, label: 'Tag' });
|
|
112
|
+
expect(wrapper.find('[data-testid="banner"]').exists()).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('does not render Banner when label is missing', () => {
|
|
116
|
+
const wrapper = mountTile({ useWLBanner: true });
|
|
117
|
+
expect(wrapper.find('[data-testid="banner"]').exists()).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('passes bannerColor to Banner', () => {
|
|
121
|
+
const wrapper = mountTile({
|
|
122
|
+
useWLBanner: true,
|
|
123
|
+
label: 'Tag',
|
|
124
|
+
bannerColor: 'green',
|
|
125
|
+
});
|
|
126
|
+
const banner = wrapper.findComponent({ name: 'Banner' });
|
|
127
|
+
expect(banner.props('color')).toBe('green');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('passes custom bgColor/foldColor to Banner', () => {
|
|
131
|
+
const wrapper = mountTile({
|
|
132
|
+
useWLBanner: true,
|
|
133
|
+
label: 'Tag',
|
|
134
|
+
bannerBgColor: '#aaa',
|
|
135
|
+
bannerFoldColor: '#bbb',
|
|
136
|
+
});
|
|
137
|
+
const banner = wrapper.findComponent({ name: 'Banner' });
|
|
138
|
+
expect(banner.props('bgColor')).toBe('#aaa');
|
|
139
|
+
expect(banner.props('foldColor')).toBe('#bbb');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ─── Icon ─────────────────────────────────────────────────────────
|
|
144
|
+
describe('Icon', () => {
|
|
145
|
+
it('renders icon by default', () => {
|
|
146
|
+
const wrapper = mountTile();
|
|
147
|
+
expect(wrapper.find('.wl-tile__icon').exists()).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('hides icon when showIcon is false', () => {
|
|
151
|
+
const wrapper = mountTile({ showIcon: false });
|
|
152
|
+
expect(wrapper.find('.wl-tile__icon').exists()).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('renders default icon (mdi-check-circle)', () => {
|
|
156
|
+
const wrapper = mountTile();
|
|
157
|
+
const icon = wrapper.find('.v-icon');
|
|
158
|
+
expect(icon.exists()).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('uses custom icon when provided', () => {
|
|
162
|
+
const wrapper = mountTile({ icon: 'mdi-star' });
|
|
163
|
+
const icon = wrapper.find('.v-icon');
|
|
164
|
+
expect(icon.exists()).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// ─── Disabled State ───────────────────────────────────────────────
|
|
169
|
+
describe('Disabled State', () => {
|
|
170
|
+
it('adds wl-tile--disabled class', () => {
|
|
171
|
+
const wrapper = mountTile({ disabled: true });
|
|
172
|
+
expect(wrapper.find('.wl-tile--disabled').exists()).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('sets tabindex to -1 when disabled', () => {
|
|
176
|
+
const wrapper = mountTile({ disabled: true });
|
|
177
|
+
expect(wrapper.find('[data-testid="root"]').attributes('tabindex')).toBe('-1');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('does not add disabled class by default', () => {
|
|
181
|
+
const wrapper = mountTile();
|
|
182
|
+
expect(wrapper.find('.wl-tile--disabled').exists()).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ─── Slots ────────────────────────────────────────────────────────
|
|
187
|
+
describe('Slots', () => {
|
|
188
|
+
it('renders default slot content', () => {
|
|
189
|
+
const wrapper = mountTile({}, {
|
|
190
|
+
slots: { default: '<div class="custom">Custom body</div>' },
|
|
191
|
+
});
|
|
192
|
+
expect(wrapper.find('.custom').text()).toBe('Custom body');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('renders title slot', () => {
|
|
196
|
+
const wrapper = mountTile({}, {
|
|
197
|
+
slots: { title: '<span class="custom-title">Title slot</span>' },
|
|
198
|
+
});
|
|
199
|
+
expect(wrapper.find('.custom-title').text()).toBe('Title slot');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('renders description slot', () => {
|
|
203
|
+
const wrapper = mountTile({}, {
|
|
204
|
+
slots: { description: '<p class="custom-desc">Desc slot</p>' },
|
|
205
|
+
});
|
|
206
|
+
expect(wrapper.find('.custom-desc').text()).toBe('Desc slot');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('renders prepend slot', () => {
|
|
210
|
+
const wrapper = mountTile({}, {
|
|
211
|
+
slots: { prepend: '<div class="custom-prepend">Prepend</div>' },
|
|
212
|
+
});
|
|
213
|
+
expect(wrapper.find('.custom-prepend').text()).toBe('Prepend');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('renders label slot', () => {
|
|
217
|
+
const wrapper = mountTile({}, {
|
|
218
|
+
slots: { label: '<span class="custom-label">Label slot</span>' },
|
|
219
|
+
});
|
|
220
|
+
expect(wrapper.find('.custom-label').text()).toBe('Label slot');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// ─── WCAG Accessibility ───────────────────────────────────────────
|
|
225
|
+
describe('WCAG Accessibility', () => {
|
|
226
|
+
it('sets explicit aria-label when provided (WCAG 4.1.2)', () => {
|
|
227
|
+
const wrapper = mountTile({ ariaLabel: 'Tile for status' });
|
|
228
|
+
expect(wrapper.find('[data-testid="root"]').attributes('aria-label')).toBe('Tile for status');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('computes aria-label from title and label when ariaLabel not set', () => {
|
|
232
|
+
const wrapper = mountTile({ title: 'Check-up', label: 'Done' });
|
|
233
|
+
expect(wrapper.find('[data-testid="root"]').attributes('aria-label')).toBe('Check-up — Done');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('uses only title in aria-label when label is missing', () => {
|
|
237
|
+
const wrapper = mountTile({ title: 'Check-up' });
|
|
238
|
+
expect(wrapper.find('[data-testid="root"]').attributes('aria-label')).toBe('Check-up');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('uses only label in aria-label when title is missing', () => {
|
|
242
|
+
const wrapper = mountTile({ label: 'Done' });
|
|
243
|
+
expect(wrapper.find('[data-testid="root"]').attributes('aria-label')).toBe('Done');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('has no aria-label when neither title, label, nor ariaLabel is set', () => {
|
|
247
|
+
const wrapper = mountTile();
|
|
248
|
+
expect(wrapper.find('[data-testid="root"]').attributes('aria-label')).toBeUndefined();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('is focusable via keyboard (tabindex=0) (WCAG 2.1.1)', () => {
|
|
252
|
+
const wrapper = mountTile();
|
|
253
|
+
expect(wrapper.find('[data-testid="root"]').attributes('tabindex')).toBe('0');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('is not focusable when disabled (tabindex=-1)', () => {
|
|
257
|
+
const wrapper = mountTile({ disabled: true });
|
|
258
|
+
expect(wrapper.find('[data-testid="root"]').attributes('tabindex')).toBe('-1');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ─── Edge Cases ───────────────────────────────────────────────────
|
|
263
|
+
describe('Edge Cases', () => {
|
|
264
|
+
it('handles all props simultaneously', () => {
|
|
265
|
+
const wrapper = mountTile({
|
|
266
|
+
title: 'Title',
|
|
267
|
+
description: 'Desc',
|
|
268
|
+
label: 'Label',
|
|
269
|
+
icon: 'mdi-star',
|
|
270
|
+
disabled: false,
|
|
271
|
+
useWLBanner: false,
|
|
272
|
+
});
|
|
273
|
+
expect(wrapper.find('.wl-tile__title').text()).toBe('Title');
|
|
274
|
+
expect(wrapper.find('.wl-tile__description').text()).toBe('Desc');
|
|
275
|
+
expect(wrapper.find('.wl-tile__label-text').text()).toBe('Label');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('handles prop changes reactively', async () => {
|
|
279
|
+
const wrapper = mountTile({ title: 'Old' });
|
|
280
|
+
expect(wrapper.find('.wl-tile__title').text()).toBe('Old');
|
|
281
|
+
|
|
282
|
+
await wrapper.setProps({ title: 'New' });
|
|
283
|
+
expect(wrapper.find('.wl-tile__title').text()).toBe('New');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
});
|
|
@@ -770,4 +770,132 @@ describe('DateInput Edge Cases', () => {
|
|
|
770
770
|
|
|
771
771
|
expect(wrapper.vm.inputState).toBe('error');
|
|
772
772
|
});
|
|
773
|
+
|
|
774
|
+
// ─── External Error Prop ──────────────────────────────────────────
|
|
775
|
+
describe('External Error Prop', () => {
|
|
776
|
+
it('defaults error prop to false', () => {
|
|
777
|
+
wrapper = mountComponent();
|
|
778
|
+
expect(wrapper.vm.error).toBe(false);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('accepts error prop', () => {
|
|
782
|
+
wrapper = mountComponent({ error: true });
|
|
783
|
+
expect(wrapper.vm.error).toBe(true);
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('passes error state to Input component', () => {
|
|
787
|
+
wrapper = mountComponent({ error: true });
|
|
788
|
+
const root = wrapper.find('[data-testid="root"]');
|
|
789
|
+
// The :error binding uses `inputState === 'error' || props.error`
|
|
790
|
+
expect(root.classes()).toContain('is-error');
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it('external error does not override internal error', async () => {
|
|
794
|
+
wrapper = mountComponent({ error: true });
|
|
795
|
+
const input = wrapper.find('input');
|
|
796
|
+
|
|
797
|
+
// Type invalid date to trigger internal error
|
|
798
|
+
await input.setValue('99.99.9999');
|
|
799
|
+
await input.trigger('input');
|
|
800
|
+
|
|
801
|
+
// Internal error takes priority for the hint message
|
|
802
|
+
expect(wrapper.vm.inputState).toBe('error');
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('shows external error when no internal error', () => {
|
|
806
|
+
wrapper = mountComponent({ error: true });
|
|
807
|
+
// inputState is idle (no input), so error comes from external prop
|
|
808
|
+
expect(wrapper.vm.inputState).toBe('idle');
|
|
809
|
+
expect(wrapper.vm.error).toBe(true);
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
it('clears external error display when error prop becomes false', async () => {
|
|
813
|
+
wrapper = mountComponent({ error: true });
|
|
814
|
+
expect(wrapper.find('[data-testid="root"]').classes()).toContain('is-error');
|
|
815
|
+
|
|
816
|
+
await wrapper.setProps({ error: false });
|
|
817
|
+
// Without internal error, should not show error
|
|
818
|
+
expect(wrapper.find('[data-testid="root"]').classes()).not.toContain('is-error');
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// ─── External Error Message ───────────────────────────────────────
|
|
823
|
+
describe('External Error Message', () => {
|
|
824
|
+
it('defaults errorMessage to empty string', () => {
|
|
825
|
+
wrapper = mountComponent();
|
|
826
|
+
expect(wrapper.vm.errorMessage).toBe('');
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it('accepts errorMessage prop', () => {
|
|
830
|
+
wrapper = mountComponent({ errorMessage: 'Pflichtfeld' });
|
|
831
|
+
expect(wrapper.vm.errorMessage).toBe('Pflichtfeld');
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
it('shows errorMessage in hint when external error is active and no internal error', () => {
|
|
835
|
+
wrapper = mountComponent({ error: true, errorMessage: 'Pflichtfeld' });
|
|
836
|
+
// In default layout, hint = inputState === 'error' ? inputMessage : (props.error ? props.errorMessage : '')
|
|
837
|
+
// inputState is 'idle', so hint = props.errorMessage = 'Pflichtfeld'
|
|
838
|
+
const hint = wrapper.find('.hint-text');
|
|
839
|
+
if (hint.exists()) {
|
|
840
|
+
expect(hint.text()).toBe('Pflichtfeld');
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('internal error message takes priority over errorMessage', async () => {
|
|
845
|
+
wrapper = mountComponent({ error: true, errorMessage: 'Pflichtfeld' });
|
|
846
|
+
const input = wrapper.find('input');
|
|
847
|
+
|
|
848
|
+
// Trigger internal error
|
|
849
|
+
await input.setValue('99.99.9999');
|
|
850
|
+
await input.trigger('input');
|
|
851
|
+
|
|
852
|
+
// Internal error message should display, not errorMessage
|
|
853
|
+
expect(wrapper.vm.inputState).toBe('error');
|
|
854
|
+
expect(wrapper.vm.inputMessage).not.toBe('');
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
// ─── Validity Emission with External Error ────────────────────────
|
|
859
|
+
describe('Validity Emission with External Error', () => {
|
|
860
|
+
it('emits update:valid as false when external error is set even if date is valid', async () => {
|
|
861
|
+
wrapper = mountComponent({ error: true });
|
|
862
|
+
const input = wrapper.find('input');
|
|
863
|
+
|
|
864
|
+
// Enter a valid date
|
|
865
|
+
await input.setValue('12052024');
|
|
866
|
+
await input.trigger('input');
|
|
867
|
+
|
|
868
|
+
// inputState will be 'success', but external error means validity should be false
|
|
869
|
+
const validEvents = wrapper.emitted('update:valid') || [];
|
|
870
|
+
const lastEvent = validEvents[validEvents.length - 1];
|
|
871
|
+
expect(lastEvent?.[0]).toBe(false);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
it('emits update:valid as true when date is valid and no external error', async () => {
|
|
875
|
+
wrapper = mountComponent({ error: false });
|
|
876
|
+
const input = wrapper.find('input');
|
|
877
|
+
|
|
878
|
+
await input.setValue('12052024');
|
|
879
|
+
await input.trigger('input');
|
|
880
|
+
|
|
881
|
+
const validEvents = wrapper.emitted('update:valid') || [];
|
|
882
|
+
const lastEvent = validEvents[validEvents.length - 1];
|
|
883
|
+
expect(lastEvent?.[0]).toBe(true);
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
// ─── hideDetails with External Error ──────────────────────────────
|
|
888
|
+
describe('hideDetails with External Error', () => {
|
|
889
|
+
it('shows details when external error is active', () => {
|
|
890
|
+
wrapper = mountComponent({ error: true, errorMessage: 'Required' });
|
|
891
|
+
// hideDetails should be false when error is showing
|
|
892
|
+
// In default layout: !(inputState === 'error' || props.error)
|
|
893
|
+
// = !(false || true) = false → details shown
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('hides details when no error', () => {
|
|
897
|
+
wrapper = mountComponent({ error: false });
|
|
898
|
+
// !(false || false) = true → details hidden
|
|
899
|
+
});
|
|
900
|
+
});
|
|
773
901
|
});
|