@fmidev/smartmet-alert-client 4.2.7 → 4.7.0-alpha.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.
Files changed (127) hide show
  1. package/.eslintignore +2 -14
  2. package/.github/workflows/test.yaml +26 -0
  3. package/.nvmrc +1 -0
  4. package/dist/index.dark.html +1 -1
  5. package/dist/index.en.html +1 -1
  6. package/dist/index.fi.html +1 -1
  7. package/dist/index.html +5 -0
  8. package/dist/index.js +105 -135
  9. package/dist/index.mjs +112 -135
  10. package/dist/index.sv.html +1 -1
  11. package/dist/locale-en-DCEKDw5G.js +8 -0
  12. package/dist/locale-fi-DPiOM1rB.js +8 -0
  13. package/dist/locale-sv-B0FlbgEF.js +8 -0
  14. package/dist/vendor-Cfkkvdz7.js +21 -0
  15. package/dist/vue/index.mjs +15245 -0
  16. package/dist/vue/style.css +1 -0
  17. package/dist/xml-parser-BiNO9kc-.js +13 -0
  18. package/package.json +61 -25
  19. package/public/index.dark.html +1 -1
  20. package/public/index.en.html +1 -1
  21. package/public/index.fi.html +1 -1
  22. package/public/index.sv.html +1 -1
  23. package/src/AlertClientVue.vue +170 -0
  24. package/src/App.vue +58 -176
  25. package/src/assets/img/ui/arrow-down.svg +4 -14
  26. package/src/assets/img/ui/arrow-up.svg +4 -14
  27. package/src/assets/img/ui/clear.svg +7 -21
  28. package/src/assets/img/ui/close.svg +4 -15
  29. package/src/assets/img/ui/toggle-selected.svg +12 -0
  30. package/src/assets/img/ui/toggle-unselected.svg +12 -0
  31. package/src/assets/img/warning/cold-weather.svg +3 -6
  32. package/src/assets/img/warning/flood-level-3.svg +4 -7
  33. package/src/assets/img/warning/forest-fire-weather.svg +2 -6
  34. package/src/assets/img/warning/grass-fire-weather.svg +2 -6
  35. package/src/assets/img/warning/hot-weather.svg +3 -6
  36. package/src/assets/img/warning/pedestrian-safety.svg +3 -7
  37. package/src/assets/img/warning/rain.svg +2 -7
  38. package/src/assets/img/warning/sea-icing.svg +2 -6
  39. package/src/assets/img/warning/sea-thunder-storm.svg +2 -5
  40. package/src/assets/img/warning/sea-water-height-high-water.svg +3 -8
  41. package/src/assets/img/warning/sea-water-height-shallow-water.svg +3 -7
  42. package/src/assets/img/warning/sea-wave-height.svg +4 -7
  43. package/src/assets/img/warning/sea-wind-legend.svg +2 -5
  44. package/src/assets/img/warning/sea-wind.svg +2 -5
  45. package/src/assets/img/warning/several.svg +2 -5
  46. package/src/assets/img/warning/thunder-storm.svg +2 -5
  47. package/src/assets/img/warning/traffic-weather.svg +2 -6
  48. package/src/assets/img/warning/uv-note.svg +2 -6
  49. package/src/assets/img/warning/wind.svg +2 -5
  50. package/src/components/AlertClient.vue +42 -20
  51. package/src/components/CollapsiblePanel.vue +284 -0
  52. package/src/components/DayLarge.vue +40 -32
  53. package/src/components/DaySmall.vue +17 -7
  54. package/src/components/Days.vue +77 -52
  55. package/src/components/DescriptionWarning.vue +26 -8
  56. package/src/components/GrayScaleToggle.vue +42 -35
  57. package/src/components/Legend.vue +36 -240
  58. package/src/components/MapLarge.vue +55 -50
  59. package/src/components/MapSmall.vue +48 -29
  60. package/src/components/PopupRow.vue +6 -3
  61. package/src/components/Region.vue +162 -53
  62. package/src/components/RegionWarning.vue +33 -11
  63. package/src/components/Regions.vue +59 -29
  64. package/src/components/Warning.vue +53 -49
  65. package/src/components/Warnings.vue +54 -16
  66. package/src/locales/en.json +21 -4
  67. package/src/locales/fi.json +23 -6
  68. package/src/locales/sv.json +20 -3
  69. package/src/main.js +1 -0
  70. package/src/mixins/alertClientCore.js +210 -0
  71. package/src/mixins/config.js +263 -257
  72. package/src/mixins/utils.js +46 -15
  73. package/src/plugins/index.js +1 -1
  74. package/src/scss/_utilities.scss +193 -0
  75. package/src/scss/backgrounds.scss +2 -0
  76. package/src/scss/colors.scss +5 -3
  77. package/src/scss/constants.scss +3 -2
  78. package/src/scss/themes/dark-gray.scss +3 -3
  79. package/src/scss/themes/dark.scss +1 -1
  80. package/src/scss/themes/light-gray.scss +5 -5
  81. package/src/scss/themes/light.scss +3 -3
  82. package/src/scss/warningImages.scss +9 -5
  83. package/src/vue.js +41 -0
  84. package/svgo.config.js +45 -0
  85. package/tests/README.md +430 -0
  86. package/tests/fixtures/mockWarningData.js +135 -0
  87. package/tests/integration/warning-flow.spec.js +452 -0
  88. package/tests/setup.js +41 -0
  89. package/tests/unit/components/AlertClient.spec.js +734 -0
  90. package/tests/unit/components/DayLarge.spec.js +281 -0
  91. package/tests/unit/components/DaySmall.spec.js +278 -0
  92. package/tests/unit/components/Days.spec.js +565 -0
  93. package/tests/unit/components/DescriptionWarning.spec.js +432 -0
  94. package/tests/unit/components/GrayScaleToggle.spec.js +311 -0
  95. package/tests/unit/components/Legend.spec.js +223 -0
  96. package/tests/unit/components/MapLarge.spec.js +276 -0
  97. package/tests/unit/components/MapSmall.spec.js +226 -0
  98. package/tests/unit/components/PopupRow.spec.js +261 -0
  99. package/tests/unit/components/Region.spec.js +430 -0
  100. package/tests/unit/components/RegionWarning.snapshot.spec.js +73 -0
  101. package/tests/unit/components/RegionWarning.spec.js +408 -0
  102. package/tests/unit/components/Regions.spec.js +335 -0
  103. package/tests/unit/components/Warning.snapshot.spec.js +107 -0
  104. package/tests/unit/components/Warning.spec.js +472 -0
  105. package/tests/unit/components/Warnings.spec.js +329 -0
  106. package/tests/unit/components/__snapshots__/RegionWarning.snapshot.spec.js.snap +21 -0
  107. package/tests/unit/components/__snapshots__/Warning.snapshot.spec.js.snap +199 -0
  108. package/tests/unit/mixins/config.spec.js +269 -0
  109. package/tests/unit/mixins/i18n.spec.js +115 -0
  110. package/tests/unit/mixins/keycodes.spec.js +37 -0
  111. package/tests/unit/mixins/utils.spec.js +624 -0
  112. package/vite.config.js +96 -26
  113. package/vitest.config.js +40 -0
  114. package/dist/index.mjs.map +0 -1
  115. package/dist/index.relative.html +0 -19
  116. package/dist/index.start.html +0 -20
  117. package/playwright.config.ts +0 -18
  118. package/public/index.relative.html +0 -19
  119. package/public/index.start.html +0 -20
  120. package/src/assets/img/ui/toggle-selected-blue.svg +0 -4
  121. package/src/assets/img/ui/toggle-selected-dark.svg +0 -4
  122. package/src/assets/img/ui/toggle-selected-light.svg +0 -4
  123. package/src/assets/img/ui/toggle-unselected-dark.svg +0 -4
  124. package/src/assets/img/ui/toggle-unselected-light.svg +0 -4
  125. package/src/mixins/panzoom.js +0 -900
  126. package/test/snapshot.test.ts +0 -126
  127. package/vitest.config.ts +0 -6
@@ -0,0 +1,472 @@
1
+ import { describe, it, expect, afterEach, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Warning from '@/components/Warning.vue'
4
+
5
+ const mockWarning = {
6
+ type: 'wind',
7
+ severity: 3,
8
+ visible: true,
9
+ }
10
+
11
+ describe('Warning.vue', () => {
12
+ let wrapper
13
+
14
+ afterEach(() => {
15
+ if (wrapper) {
16
+ wrapper.unmount()
17
+ }
18
+ })
19
+
20
+ describe('Component mounting', () => {
21
+ it('should mount with required props', () => {
22
+ wrapper = mount(Warning, {
23
+ props: {
24
+ input: mockWarning,
25
+ hideable: true,
26
+ language: 'fi',
27
+ theme: 'light-theme',
28
+ },
29
+ })
30
+
31
+ expect(wrapper.exists()).toBe(true)
32
+ })
33
+ })
34
+
35
+ describe('Computed properties', () => {
36
+ it('should compute id from warning type', () => {
37
+ wrapper = mount(Warning, {
38
+ props: {
39
+ input: mockWarning,
40
+ hideable: true,
41
+ language: 'fi',
42
+ theme: 'light-theme',
43
+ },
44
+ })
45
+
46
+ expect(wrapper.vm.id).toBe('fmi-warnings-flag-wind')
47
+ })
48
+
49
+ it('should compute title from translation', () => {
50
+ wrapper = mount(Warning, {
51
+ props: {
52
+ input: mockWarning,
53
+ hideable: true,
54
+ language: 'fi',
55
+ theme: 'light-theme',
56
+ },
57
+ })
58
+
59
+ expect(typeof wrapper.vm.title).toBe('string')
60
+ })
61
+
62
+ it('should compute warningLevelText', () => {
63
+ wrapper = mount(Warning, {
64
+ props: {
65
+ input: mockWarning,
66
+ hideable: true,
67
+ language: 'fi',
68
+ theme: 'light-theme',
69
+ },
70
+ })
71
+
72
+ expect(typeof wrapper.vm.warningLevelText).toBe('string')
73
+ })
74
+
75
+ it('should compute toggleText based on visibility', () => {
76
+ wrapper = mount(Warning, {
77
+ props: {
78
+ input: { ...mockWarning, visible: true },
79
+ hideable: true,
80
+ language: 'fi',
81
+ theme: 'light-theme',
82
+ },
83
+ })
84
+
85
+ const visibleText = wrapper.vm.toggleText
86
+
87
+ wrapper = mount(Warning, {
88
+ props: {
89
+ input: { ...mockWarning, visible: false },
90
+ hideable: true,
91
+ language: 'fi',
92
+ theme: 'light-theme',
93
+ },
94
+ })
95
+
96
+ const hiddenText = wrapper.vm.toggleText
97
+
98
+ expect(typeof visibleText).toBe('string')
99
+ expect(typeof hiddenText).toBe('string')
100
+ })
101
+ })
102
+
103
+ describe('Fields mixin computed properties', () => {
104
+ it('should compute typeClass from warning type', () => {
105
+ wrapper = mount(Warning, {
106
+ props: {
107
+ input: { type: 'thunderStorm', severity: 4, visible: true },
108
+ hideable: true,
109
+ language: 'fi',
110
+ theme: 'light-theme',
111
+ },
112
+ })
113
+
114
+ expect(wrapper.vm.typeClass).toBe('thunder-storm')
115
+ })
116
+
117
+ it('should compute typeClass for sea wind', () => {
118
+ wrapper = mount(Warning, {
119
+ props: {
120
+ input: { type: 'seaWind', severity: 3, visible: true },
121
+ hideable: true,
122
+ language: 'fi',
123
+ theme: 'light-theme',
124
+ },
125
+ })
126
+
127
+ expect(wrapper.vm.typeClass).toBe('sea-wind')
128
+ })
129
+
130
+ it('should compute rotation from direction', () => {
131
+ wrapper = mount(Warning, {
132
+ props: {
133
+ input: {
134
+ type: 'wind',
135
+ severity: 3,
136
+ visible: true,
137
+ direction: 90,
138
+ },
139
+ hideable: true,
140
+ language: 'fi',
141
+ theme: 'light-theme',
142
+ },
143
+ })
144
+
145
+ expect(wrapper.vm.rotation).toBe(90)
146
+ })
147
+
148
+ it('should round rotation to nearest 5 degrees', () => {
149
+ wrapper = mount(Warning, {
150
+ props: {
151
+ input: {
152
+ type: 'wind',
153
+ severity: 3,
154
+ visible: true,
155
+ direction: 93,
156
+ },
157
+ hideable: true,
158
+ language: 'fi',
159
+ theme: 'light-theme',
160
+ },
161
+ })
162
+
163
+ expect(wrapper.vm.rotation).toBe(95)
164
+ })
165
+
166
+ it('should handle negative direction', () => {
167
+ wrapper = mount(Warning, {
168
+ props: {
169
+ input: {
170
+ type: 'wind',
171
+ severity: 3,
172
+ visible: true,
173
+ direction: -45,
174
+ },
175
+ hideable: true,
176
+ language: 'fi',
177
+ theme: 'light-theme',
178
+ },
179
+ })
180
+
181
+ expect(wrapper.vm.rotation).toBeGreaterThanOrEqual(0)
182
+ expect(wrapper.vm.rotation).toBeLessThan(360)
183
+ })
184
+
185
+ it('should compute invertedRotation', () => {
186
+ wrapper = mount(Warning, {
187
+ props: {
188
+ input: {
189
+ type: 'wind',
190
+ severity: 3,
191
+ visible: true,
192
+ direction: 90,
193
+ },
194
+ hideable: true,
195
+ language: 'fi',
196
+ theme: 'light-theme',
197
+ },
198
+ })
199
+
200
+ expect(wrapper.vm.invertedRotation).toBe(270)
201
+ })
202
+
203
+ it('should compute severity from input', () => {
204
+ wrapper = mount(Warning, {
205
+ props: {
206
+ input: mockWarning,
207
+ hideable: true,
208
+ language: 'fi',
209
+ theme: 'light-theme',
210
+ },
211
+ })
212
+
213
+ expect(wrapper.vm.severity).toBe(3)
214
+ })
215
+
216
+ it('should return 0 for invalid severity levels', () => {
217
+ const invalidSeverities = [0, 1, 5, 6]
218
+
219
+ invalidSeverities.forEach((severity) => {
220
+ wrapper = mount(Warning, {
221
+ props: {
222
+ input: { ...mockWarning, severity },
223
+ hideable: true,
224
+ language: 'fi',
225
+ theme: 'light-theme',
226
+ },
227
+ })
228
+
229
+ expect(wrapper.vm.severity).toBe(0)
230
+ })
231
+ })
232
+
233
+ it('should accept valid severity levels 2-4', () => {
234
+ const validSeverities = [2, 3, 4]
235
+
236
+ validSeverities.forEach((severity) => {
237
+ wrapper = mount(Warning, {
238
+ props: {
239
+ input: { ...mockWarning, severity },
240
+ hideable: true,
241
+ language: 'fi',
242
+ theme: 'light-theme',
243
+ },
244
+ })
245
+
246
+ expect(wrapper.vm.severity).toBe(severity)
247
+ })
248
+ })
249
+ })
250
+
251
+ describe('Toggle functionality', () => {
252
+ it('should emit warningToggled when toggled on', () => {
253
+ wrapper = mount(Warning, {
254
+ props: {
255
+ input: { ...mockWarning, visible: false },
256
+ hideable: true,
257
+ language: 'fi',
258
+ theme: 'light-theme',
259
+ },
260
+ })
261
+
262
+ const event = { preventDefault: vi.fn() }
263
+ wrapper.vm.toggle(event)
264
+
265
+ expect(wrapper.emitted('warningToggled')).toBeTruthy()
266
+ expect(wrapper.emitted('warningToggled')[0][0]).toEqual({
267
+ warning: 'wind',
268
+ visible: true,
269
+ })
270
+ expect(event.preventDefault).toHaveBeenCalled()
271
+ })
272
+
273
+ it('should emit warningToggled when toggled off', () => {
274
+ wrapper = mount(Warning, {
275
+ props: {
276
+ input: { ...mockWarning, visible: true },
277
+ hideable: true,
278
+ language: 'fi',
279
+ theme: 'light-theme',
280
+ },
281
+ })
282
+
283
+ const event = { preventDefault: vi.fn() }
284
+ wrapper.vm.toggle(event)
285
+
286
+ expect(wrapper.emitted('warningToggled')[0][0]).toEqual({
287
+ warning: 'wind',
288
+ visible: false,
289
+ })
290
+ })
291
+
292
+ it('should call setWarningVisibility with correct value', () => {
293
+ wrapper = mount(Warning, {
294
+ props: {
295
+ input: mockWarning,
296
+ hideable: true,
297
+ language: 'fi',
298
+ theme: 'light-theme',
299
+ },
300
+ })
301
+
302
+ wrapper.vm.setWarningVisibility(false)
303
+
304
+ expect(wrapper.emitted('warningToggled')).toBeTruthy()
305
+ expect(wrapper.emitted('warningToggled')[0][0].visible).toBe(false)
306
+ })
307
+
308
+ it('should prevent default event', () => {
309
+ wrapper = mount(Warning, {
310
+ props: {
311
+ input: mockWarning,
312
+ hideable: true,
313
+ language: 'fi',
314
+ theme: 'light-theme',
315
+ },
316
+ })
317
+
318
+ const event = { preventDefault: vi.fn() }
319
+ wrapper.vm.preventEvents(event)
320
+
321
+ expect(event.preventDefault).toHaveBeenCalled()
322
+ })
323
+ })
324
+
325
+ describe('CSS classes', () => {
326
+ it('should apply severity level class', () => {
327
+ wrapper = mount(Warning, {
328
+ props: {
329
+ input: mockWarning,
330
+ hideable: true,
331
+ language: 'fi',
332
+ theme: 'light-theme',
333
+ },
334
+ })
335
+
336
+ const image = wrapper.find('.warning-image')
337
+ expect(image.classes()).toContain('level-3')
338
+ })
339
+
340
+ it('should apply type class', () => {
341
+ wrapper = mount(Warning, {
342
+ props: {
343
+ input: mockWarning,
344
+ hideable: true,
345
+ language: 'fi',
346
+ theme: 'light-theme',
347
+ },
348
+ })
349
+
350
+ const image = wrapper.find('.warning-image')
351
+ expect(image.classes()).toContain('wind')
352
+ })
353
+
354
+ it('should apply flag-selected class when visible', () => {
355
+ wrapper = mount(Warning, {
356
+ props: {
357
+ input: { ...mockWarning, visible: true },
358
+ hideable: true,
359
+ language: 'fi',
360
+ theme: 'light-theme',
361
+ },
362
+ })
363
+
364
+ const toggle = wrapper.find('.symbol-list-select')
365
+ expect(toggle.classes()).toContain('flag-selected')
366
+ })
367
+
368
+ it('should apply flag-unselected class when not visible', () => {
369
+ wrapper = mount(Warning, {
370
+ props: {
371
+ input: { ...mockWarning, visible: false },
372
+ hideable: true,
373
+ language: 'fi',
374
+ theme: 'light-theme',
375
+ },
376
+ })
377
+
378
+ const toggle = wrapper.find('.symbol-list-select')
379
+ expect(toggle.classes()).toContain('flag-unselected')
380
+ })
381
+
382
+ it('should apply theme class to container', () => {
383
+ wrapper = mount(Warning, {
384
+ props: {
385
+ input: mockWarning,
386
+ hideable: true,
387
+ language: 'fi',
388
+ theme: 'dark-theme',
389
+ },
390
+ })
391
+
392
+ expect(wrapper.find('.symbol-list-table').classes()).toContain(
393
+ 'dark-theme'
394
+ )
395
+ })
396
+ })
397
+
398
+ describe('Accessibility', () => {
399
+ it('should have correct ARIA attributes on toggle', () => {
400
+ wrapper = mount(Warning, {
401
+ props: {
402
+ input: { ...mockWarning, visible: true },
403
+ hideable: true,
404
+ language: 'fi',
405
+ theme: 'light-theme',
406
+ },
407
+ })
408
+
409
+ const toggle = wrapper.find('.symbol-list-select')
410
+ expect(toggle.attributes('role')).toBe('button')
411
+ expect(toggle.attributes('tabindex')).toBe('0')
412
+ expect(toggle.attributes('aria-pressed')).toBe('true')
413
+ })
414
+
415
+ it('should update aria-pressed when visibility changes', () => {
416
+ wrapper = mount(Warning, {
417
+ props: {
418
+ input: { ...mockWarning, visible: false },
419
+ hideable: true,
420
+ language: 'fi',
421
+ theme: 'light-theme',
422
+ },
423
+ })
424
+
425
+ const toggle = wrapper.find('.symbol-list-select')
426
+ expect(toggle.attributes('aria-pressed')).toBe('false')
427
+ })
428
+
429
+ it('should have aria-label on warning image', () => {
430
+ wrapper = mount(Warning, {
431
+ props: {
432
+ input: mockWarning,
433
+ hideable: true,
434
+ language: 'fi',
435
+ theme: 'light-theme',
436
+ },
437
+ })
438
+
439
+ const image = wrapper.find('.warning-image')
440
+ expect(image.attributes('aria-label')).toBeDefined()
441
+ })
442
+ })
443
+
444
+ describe('Hideable prop', () => {
445
+ it('should show toggle when hideable is true', () => {
446
+ wrapper = mount(Warning, {
447
+ props: {
448
+ input: mockWarning,
449
+ hideable: true,
450
+ language: 'fi',
451
+ theme: 'light-theme',
452
+ },
453
+ })
454
+
455
+ const toggle = wrapper.find('.symbol-list-select')
456
+ expect(toggle.classes()).toContain('d-md-block')
457
+ })
458
+
459
+ it('should respect hideable prop', () => {
460
+ wrapper = mount(Warning, {
461
+ props: {
462
+ input: mockWarning,
463
+ hideable: false,
464
+ language: 'fi',
465
+ theme: 'light-theme',
466
+ },
467
+ })
468
+
469
+ expect(wrapper.vm.hideable).toBe(false)
470
+ })
471
+ })
472
+ })