@fmidev/smartmet-alert-client 4.4.19 → 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 (105) hide show
  1. package/.eslintignore +2 -14
  2. package/.github/workflows/test.yaml +26 -0
  3. package/.nvmrc +1 -0
  4. package/dist/index.html +5 -0
  5. package/dist/index.js +105 -135
  6. package/dist/index.mjs +112 -135
  7. package/dist/locale-en-DCEKDw5G.js +8 -0
  8. package/dist/locale-fi-DPiOM1rB.js +8 -0
  9. package/dist/locale-sv-B0FlbgEF.js +8 -0
  10. package/dist/vendor-Cfkkvdz7.js +21 -0
  11. package/dist/vue/index.mjs +15245 -0
  12. package/dist/vue/style.css +1 -0
  13. package/dist/xml-parser-BiNO9kc-.js +13 -0
  14. package/package.json +60 -24
  15. package/src/AlertClientVue.vue +170 -0
  16. package/src/App.vue +55 -205
  17. package/src/assets/img/ui/arrow-down.svg +4 -11
  18. package/src/assets/img/ui/arrow-up.svg +4 -11
  19. package/src/assets/img/ui/clear.svg +7 -21
  20. package/src/assets/img/ui/close.svg +4 -15
  21. package/src/assets/img/ui/toggle-selected.svg +5 -6
  22. package/src/assets/img/ui/toggle-unselected.svg +5 -6
  23. package/src/assets/img/warning/cold-weather.svg +3 -6
  24. package/src/assets/img/warning/flood-level-3.svg +4 -7
  25. package/src/assets/img/warning/forest-fire-weather.svg +2 -6
  26. package/src/assets/img/warning/grass-fire-weather.svg +2 -6
  27. package/src/assets/img/warning/hot-weather.svg +3 -6
  28. package/src/assets/img/warning/pedestrian-safety.svg +3 -7
  29. package/src/assets/img/warning/rain.svg +2 -7
  30. package/src/assets/img/warning/sea-icing.svg +2 -6
  31. package/src/assets/img/warning/sea-thunder-storm.svg +2 -5
  32. package/src/assets/img/warning/sea-water-height-high-water.svg +3 -8
  33. package/src/assets/img/warning/sea-water-height-shallow-water.svg +3 -7
  34. package/src/assets/img/warning/sea-wave-height.svg +4 -7
  35. package/src/assets/img/warning/sea-wind-legend.svg +2 -5
  36. package/src/assets/img/warning/sea-wind.svg +2 -5
  37. package/src/assets/img/warning/several.svg +2 -5
  38. package/src/assets/img/warning/thunder-storm.svg +2 -5
  39. package/src/assets/img/warning/traffic-weather.svg +2 -6
  40. package/src/assets/img/warning/uv-note.svg +2 -6
  41. package/src/assets/img/warning/wind.svg +2 -5
  42. package/src/components/AlertClient.vue +41 -19
  43. package/src/components/CollapsiblePanel.vue +284 -0
  44. package/src/components/DayLarge.vue +12 -7
  45. package/src/components/DaySmall.vue +16 -6
  46. package/src/components/Days.vue +76 -51
  47. package/src/components/DescriptionWarning.vue +15 -8
  48. package/src/components/GrayScaleToggle.vue +11 -6
  49. package/src/components/Legend.vue +36 -248
  50. package/src/components/MapLarge.vue +41 -42
  51. package/src/components/MapSmall.vue +44 -28
  52. package/src/components/PopupRow.vue +6 -3
  53. package/src/components/Region.vue +30 -15
  54. package/src/components/RegionWarning.vue +6 -5
  55. package/src/components/Regions.vue +50 -19
  56. package/src/components/Warning.vue +18 -10
  57. package/src/components/Warnings.vue +36 -21
  58. package/src/main.js +1 -0
  59. package/src/mixins/alertClientCore.js +210 -0
  60. package/src/mixins/config.js +262 -256
  61. package/src/mixins/utils.js +40 -26
  62. package/src/plugins/index.js +1 -1
  63. package/src/scss/_utilities.scss +193 -0
  64. package/src/scss/constants.scss +2 -1
  65. package/src/scss/warningImages.scss +8 -3
  66. package/src/vue.js +41 -0
  67. package/svgo.config.js +45 -0
  68. package/tests/README.md +430 -0
  69. package/tests/fixtures/mockWarningData.js +135 -0
  70. package/tests/integration/warning-flow.spec.js +452 -0
  71. package/tests/setup.js +41 -0
  72. package/tests/unit/components/AlertClient.spec.js +734 -0
  73. package/tests/unit/components/DayLarge.spec.js +281 -0
  74. package/tests/unit/components/DaySmall.spec.js +278 -0
  75. package/tests/unit/components/Days.spec.js +565 -0
  76. package/tests/unit/components/DescriptionWarning.spec.js +432 -0
  77. package/tests/unit/components/GrayScaleToggle.spec.js +311 -0
  78. package/tests/unit/components/Legend.spec.js +223 -0
  79. package/tests/unit/components/MapLarge.spec.js +276 -0
  80. package/tests/unit/components/MapSmall.spec.js +226 -0
  81. package/tests/unit/components/PopupRow.spec.js +261 -0
  82. package/tests/unit/components/Region.spec.js +430 -0
  83. package/tests/unit/components/RegionWarning.snapshot.spec.js +73 -0
  84. package/tests/unit/components/RegionWarning.spec.js +408 -0
  85. package/tests/unit/components/Regions.spec.js +335 -0
  86. package/tests/unit/components/Warning.snapshot.spec.js +107 -0
  87. package/tests/unit/components/Warning.spec.js +472 -0
  88. package/tests/unit/components/Warnings.spec.js +329 -0
  89. package/tests/unit/components/__snapshots__/RegionWarning.snapshot.spec.js.snap +21 -0
  90. package/tests/unit/components/__snapshots__/Warning.snapshot.spec.js.snap +199 -0
  91. package/tests/unit/mixins/config.spec.js +269 -0
  92. package/tests/unit/mixins/i18n.spec.js +115 -0
  93. package/tests/unit/mixins/keycodes.spec.js +37 -0
  94. package/tests/unit/mixins/utils.spec.js +624 -0
  95. package/vite.config.js +96 -26
  96. package/vitest.config.js +40 -0
  97. package/dist/index.mjs.map +0 -1
  98. package/dist/index.relative.html +0 -19
  99. package/dist/index.start.html +0 -20
  100. package/playwright.config.ts +0 -18
  101. package/public/index.relative.html +0 -19
  102. package/public/index.start.html +0 -20
  103. package/src/mixins/panzoom.js +0 -900
  104. package/test/snapshot.test.ts +0 -126
  105. package/vitest.config.ts +0 -6
@@ -0,0 +1,311 @@
1
+ import { describe, it, expect, afterEach, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import GrayScaleToggle from '@/components/GrayScaleToggle.vue'
4
+
5
+ describe('GrayScaleToggle.vue', () => {
6
+ let wrapper
7
+
8
+ afterEach(() => {
9
+ if (wrapper) {
10
+ wrapper.unmount()
11
+ }
12
+ })
13
+
14
+ describe('Component mounting', () => {
15
+ it('should mount with required props', () => {
16
+ wrapper = mount(GrayScaleToggle, {
17
+ props: {
18
+ language: 'fi',
19
+ grayScaleSelector: true,
20
+ },
21
+ })
22
+
23
+ expect(wrapper.exists()).toBe(true)
24
+ })
25
+
26
+ it('should not render when grayScaleSelector is false', () => {
27
+ wrapper = mount(GrayScaleToggle, {
28
+ props: {
29
+ language: 'fi',
30
+ grayScaleSelector: false,
31
+ },
32
+ })
33
+
34
+ expect(wrapper.find('#gray-scale-select-row').exists()).toBe(false)
35
+ })
36
+
37
+ it('should render when grayScaleSelector is true', () => {
38
+ wrapper = mount(GrayScaleToggle, {
39
+ props: {
40
+ language: 'fi',
41
+ grayScaleSelector: true,
42
+ },
43
+ })
44
+
45
+ expect(wrapper.find('#gray-scale-select-row').exists()).toBe(true)
46
+ })
47
+ })
48
+
49
+ describe('Computed properties', () => {
50
+ it('should detect gray scale from theme', () => {
51
+ wrapper = mount(GrayScaleToggle, {
52
+ props: {
53
+ language: 'fi',
54
+ grayScaleSelector: true,
55
+ theme: 'light-gray-theme',
56
+ },
57
+ })
58
+
59
+ expect(wrapper.vm.grayScale).toBe(true)
60
+ })
61
+
62
+ it('should detect non-gray theme', () => {
63
+ wrapper = mount(GrayScaleToggle, {
64
+ props: {
65
+ language: 'fi',
66
+ grayScaleSelector: true,
67
+ theme: 'light-theme',
68
+ },
69
+ })
70
+
71
+ expect(wrapper.vm.grayScale).toBe(false)
72
+ })
73
+
74
+ it('should handle dark-gray theme', () => {
75
+ wrapper = mount(GrayScaleToggle, {
76
+ props: {
77
+ language: 'fi',
78
+ grayScaleSelector: true,
79
+ theme: 'dark-gray-theme',
80
+ },
81
+ })
82
+
83
+ expect(wrapper.vm.grayScale).toBe(true)
84
+ })
85
+
86
+ it('should return false for empty theme', () => {
87
+ wrapper = mount(GrayScaleToggle, {
88
+ props: {
89
+ language: 'fi',
90
+ grayScaleSelector: true,
91
+ theme: '',
92
+ },
93
+ })
94
+
95
+ expect(wrapper.vm.grayScale).toBe(false)
96
+ })
97
+
98
+ it('should return false for null theme', () => {
99
+ wrapper = mount(GrayScaleToggle, {
100
+ props: {
101
+ language: 'fi',
102
+ grayScaleSelector: true,
103
+ theme: null,
104
+ },
105
+ })
106
+
107
+ expect(wrapper.vm.grayScale).toBe(false)
108
+ })
109
+
110
+ it('should compute grayScaleText', () => {
111
+ wrapper = mount(GrayScaleToggle, {
112
+ props: {
113
+ language: 'fi',
114
+ grayScaleSelector: true,
115
+ },
116
+ })
117
+
118
+ expect(typeof wrapper.vm.grayScaleText).toBe('string')
119
+ })
120
+
121
+ it('should show toggleOn text when gray scale is active', () => {
122
+ wrapper = mount(GrayScaleToggle, {
123
+ props: {
124
+ language: 'fi',
125
+ grayScaleSelector: true,
126
+ theme: 'light-gray-theme',
127
+ },
128
+ })
129
+
130
+ expect(typeof wrapper.vm.toggleText).toBe('string')
131
+ })
132
+
133
+ it('should show toggleOff text when gray scale is inactive', () => {
134
+ wrapper = mount(GrayScaleToggle, {
135
+ props: {
136
+ language: 'fi',
137
+ grayScaleSelector: true,
138
+ theme: 'light-theme',
139
+ },
140
+ })
141
+
142
+ expect(typeof wrapper.vm.toggleText).toBe('string')
143
+ })
144
+ })
145
+
146
+ describe('Toggle functionality', () => {
147
+ it('should emit themeChanged when toggling from normal to gray', () => {
148
+ wrapper = mount(GrayScaleToggle, {
149
+ props: {
150
+ language: 'fi',
151
+ grayScaleSelector: true,
152
+ theme: 'light-theme',
153
+ },
154
+ })
155
+
156
+ const event = { preventDefault: vi.fn() }
157
+ wrapper.vm.toggleGrayScale(event)
158
+
159
+ expect(wrapper.emitted('themeChanged')).toBeTruthy()
160
+ expect(wrapper.emitted('themeChanged')[0]).toEqual(['light-gray'])
161
+ expect(event.preventDefault).toHaveBeenCalled()
162
+ })
163
+
164
+ it('should emit themeChanged when toggling from gray to normal', () => {
165
+ wrapper = mount(GrayScaleToggle, {
166
+ props: {
167
+ language: 'fi',
168
+ grayScaleSelector: true,
169
+ theme: 'light-gray-theme',
170
+ },
171
+ })
172
+
173
+ const event = { preventDefault: vi.fn() }
174
+ wrapper.vm.toggleGrayScale(event)
175
+
176
+ expect(wrapper.emitted('themeChanged')).toBeTruthy()
177
+ expect(wrapper.emitted('themeChanged')[0]).toEqual(['light'])
178
+ })
179
+
180
+ it('should handle dark theme toggle', () => {
181
+ wrapper = mount(GrayScaleToggle, {
182
+ props: {
183
+ language: 'fi',
184
+ grayScaleSelector: true,
185
+ theme: 'dark-theme',
186
+ },
187
+ })
188
+
189
+ const event = { preventDefault: vi.fn() }
190
+ wrapper.vm.toggleGrayScale(event)
191
+
192
+ expect(wrapper.emitted('themeChanged')[0]).toEqual(['dark-gray'])
193
+ })
194
+
195
+ it('should not emit when theme is empty', () => {
196
+ wrapper = mount(GrayScaleToggle, {
197
+ props: {
198
+ language: 'fi',
199
+ grayScaleSelector: true,
200
+ theme: '',
201
+ },
202
+ })
203
+
204
+ const event = { preventDefault: vi.fn() }
205
+ wrapper.vm.toggleGrayScale(event)
206
+
207
+ expect(wrapper.emitted('themeChanged')).toBeFalsy()
208
+ })
209
+
210
+ it('should prevent default event', () => {
211
+ wrapper = mount(GrayScaleToggle, {
212
+ props: {
213
+ language: 'fi',
214
+ grayScaleSelector: true,
215
+ theme: 'light-theme',
216
+ },
217
+ })
218
+
219
+ const event = { preventDefault: vi.fn() }
220
+ wrapper.vm.preventEvents(event)
221
+
222
+ expect(event.preventDefault).toHaveBeenCalled()
223
+ })
224
+ })
225
+
226
+ describe('CSS classes', () => {
227
+ it('should apply gray-scale-selected class when active', () => {
228
+ wrapper = mount(GrayScaleToggle, {
229
+ props: {
230
+ language: 'fi',
231
+ grayScaleSelector: true,
232
+ theme: 'light-gray-theme',
233
+ },
234
+ })
235
+
236
+ expect(wrapper.find('#gray-scale-select').classes()).toContain(
237
+ 'gray-scale-selected'
238
+ )
239
+ })
240
+
241
+ it('should apply gray-scale-unselected class when inactive', () => {
242
+ wrapper = mount(GrayScaleToggle, {
243
+ props: {
244
+ language: 'fi',
245
+ grayScaleSelector: true,
246
+ theme: 'light-theme',
247
+ },
248
+ })
249
+
250
+ expect(wrapper.find('#gray-scale-select').classes()).toContain(
251
+ 'gray-scale-unselected'
252
+ )
253
+ })
254
+
255
+ it('should apply theme class to container', () => {
256
+ wrapper = mount(GrayScaleToggle, {
257
+ props: {
258
+ language: 'fi',
259
+ grayScaleSelector: true,
260
+ theme: 'dark-theme',
261
+ },
262
+ })
263
+
264
+ expect(wrapper.find('#gray-scale-select-row').classes()).toContain(
265
+ 'dark-theme'
266
+ )
267
+ })
268
+ })
269
+
270
+ describe('Accessibility', () => {
271
+ it('should have correct ARIA attributes when inactive', () => {
272
+ wrapper = mount(GrayScaleToggle, {
273
+ props: {
274
+ language: 'fi',
275
+ grayScaleSelector: true,
276
+ theme: 'light-theme',
277
+ },
278
+ })
279
+
280
+ const toggle = wrapper.find('#gray-scale-select')
281
+ expect(toggle.attributes('role')).toBe('button')
282
+ expect(toggle.attributes('tabindex')).toBe('0')
283
+ expect(toggle.attributes('aria-pressed')).toBe('false')
284
+ })
285
+
286
+ it('should have correct ARIA attributes when active', () => {
287
+ wrapper = mount(GrayScaleToggle, {
288
+ props: {
289
+ language: 'fi',
290
+ grayScaleSelector: true,
291
+ theme: 'light-gray-theme',
292
+ },
293
+ })
294
+
295
+ const toggle = wrapper.find('#gray-scale-select')
296
+ expect(toggle.attributes('aria-pressed')).toBe('true')
297
+ })
298
+
299
+ it('should have aria-label', () => {
300
+ wrapper = mount(GrayScaleToggle, {
301
+ props: {
302
+ language: 'fi',
303
+ grayScaleSelector: true,
304
+ },
305
+ })
306
+
307
+ const toggle = wrapper.find('#gray-scale-select')
308
+ expect(toggle.attributes('aria-label')).toBeDefined()
309
+ })
310
+ })
311
+ })
@@ -0,0 +1,223 @@
1
+ import { describe, it, expect, afterEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Legend from '@/components/Legend.vue'
4
+
5
+ const mockLegend = [
6
+ {
7
+ type: 'wind',
8
+ severity: 4,
9
+ visible: true,
10
+ },
11
+ {
12
+ type: 'thunderStorm',
13
+ severity: 3,
14
+ visible: true,
15
+ },
16
+ {
17
+ type: 'rain',
18
+ severity: 2,
19
+ visible: false,
20
+ },
21
+ ]
22
+
23
+ describe('Legend.vue', () => {
24
+ let wrapper
25
+
26
+ afterEach(() => {
27
+ if (wrapper) {
28
+ wrapper.unmount()
29
+ }
30
+ })
31
+
32
+ describe('Component mounting', () => {
33
+ it('should mount with required props', () => {
34
+ wrapper = mount(Legend, {
35
+ props: {
36
+ input: mockLegend,
37
+ language: 'fi',
38
+ },
39
+ })
40
+
41
+ expect(wrapper.exists()).toBe(true)
42
+ })
43
+
44
+ it('should initialize with visible false', () => {
45
+ wrapper = mount(Legend, {
46
+ props: {
47
+ input: mockLegend,
48
+ language: 'fi',
49
+ },
50
+ })
51
+
52
+ expect(wrapper.vm.visible).toBe(false)
53
+ })
54
+ })
55
+
56
+ describe('Computed properties', () => {
57
+ it('should compute warnings from input', () => {
58
+ wrapper = mount(Legend, {
59
+ props: {
60
+ input: mockLegend,
61
+ language: 'fi',
62
+ },
63
+ })
64
+
65
+ expect(wrapper.vm.warnings).toEqual(mockLegend)
66
+ })
67
+
68
+ it('should compute warningSymbolsText', () => {
69
+ wrapper = mount(Legend, {
70
+ props: {
71
+ input: mockLegend,
72
+ language: 'fi',
73
+ },
74
+ })
75
+
76
+ expect(typeof wrapper.vm.warningSymbolsText).toBe('string')
77
+ })
78
+
79
+ it('should compute toggleLegendsText based on visibility', () => {
80
+ wrapper = mount(Legend, {
81
+ props: {
82
+ input: mockLegend,
83
+ language: 'fi',
84
+ },
85
+ })
86
+
87
+ const closedText = wrapper.vm.toggleLegendsText
88
+ wrapper.vm.visible = true
89
+
90
+ const openText = wrapper.vm.toggleLegendsText
91
+
92
+ expect(closedText).not.toBe(openText)
93
+ })
94
+ })
95
+
96
+ describe('Event handling', () => {
97
+ it('should toggle visible state on legend toggle', () => {
98
+ wrapper = mount(Legend, {
99
+ props: {
100
+ input: mockLegend,
101
+ language: 'fi',
102
+ },
103
+ })
104
+
105
+ const initialVisible = wrapper.vm.visible
106
+ wrapper.vm.onLegendToggle()
107
+
108
+ expect(wrapper.vm.visible).toBe(!initialVisible)
109
+ })
110
+
111
+ it('should emit warningsToggled event', () => {
112
+ wrapper = mount(Legend, {
113
+ props: {
114
+ input: mockLegend,
115
+ language: 'fi',
116
+ },
117
+ })
118
+
119
+ const newVisibleWarnings = ['wind', 'rain']
120
+ wrapper.vm.onWarningsToggled(newVisibleWarnings)
121
+
122
+ expect(wrapper.emitted('warningsToggled')).toBeTruthy()
123
+ expect(wrapper.emitted('warningsToggled')[0]).toEqual([
124
+ newVisibleWarnings,
125
+ ])
126
+ })
127
+
128
+ it('should emit themeChanged event', () => {
129
+ wrapper = mount(Legend, {
130
+ props: {
131
+ input: mockLegend,
132
+ language: 'fi',
133
+ },
134
+ })
135
+
136
+ wrapper.vm.onThemeChanged('dark-theme')
137
+
138
+ expect(wrapper.emitted('themeChanged')).toBeTruthy()
139
+ expect(wrapper.emitted('themeChanged')[0]).toEqual(['dark-theme'])
140
+ })
141
+
142
+ it('should not emit themeChanged if theme is same', () => {
143
+ wrapper = mount(Legend, {
144
+ props: {
145
+ input: mockLegend,
146
+ theme: 'light-theme',
147
+ language: 'fi',
148
+ },
149
+ })
150
+
151
+ wrapper.vm.onThemeChanged('light-theme')
152
+
153
+ expect(wrapper.emitted('themeChanged')).toBeFalsy()
154
+ })
155
+
156
+ it('should emit warningsToggled with all warning types on showAll', () => {
157
+ wrapper = mount(Legend, {
158
+ props: {
159
+ input: mockLegend,
160
+ language: 'fi',
161
+ },
162
+ })
163
+
164
+ wrapper.vm.onShowAllWarnings()
165
+
166
+ expect(wrapper.emitted('warningsToggled')).toBeTruthy()
167
+ const emittedWarnings = wrapper.emitted('warningsToggled')[0][0]
168
+ expect(emittedWarnings).toContain('wind')
169
+ expect(emittedWarnings).toContain('thunderStorm')
170
+ expect(emittedWarnings).toContain('rain')
171
+ })
172
+ })
173
+
174
+ describe('Props validation', () => {
175
+ it('should accept grayScaleSelector prop', () => {
176
+ wrapper = mount(Legend, {
177
+ props: {
178
+ input: mockLegend,
179
+ grayScaleSelector: true,
180
+ language: 'fi',
181
+ },
182
+ })
183
+
184
+ expect(wrapper.vm.grayScaleSelector).toBe(true)
185
+ })
186
+
187
+ it('should use default grayScaleSelector false', () => {
188
+ wrapper = mount(Legend, {
189
+ props: {
190
+ input: mockLegend,
191
+ language: 'fi',
192
+ },
193
+ })
194
+
195
+ expect(wrapper.vm.grayScaleSelector).toBe(false)
196
+ })
197
+
198
+ it('should accept theme prop', () => {
199
+ wrapper = mount(Legend, {
200
+ props: {
201
+ input: mockLegend,
202
+ theme: 'dark-theme',
203
+ language: 'fi',
204
+ },
205
+ })
206
+
207
+ expect(wrapper.vm.theme).toBe('dark-theme')
208
+ })
209
+ })
210
+
211
+ describe('Window resize handling', () => {
212
+ it('should initialize windowWidth', () => {
213
+ wrapper = mount(Legend, {
214
+ props: {
215
+ input: mockLegend,
216
+ language: 'fi',
217
+ },
218
+ })
219
+
220
+ expect(typeof wrapper.vm.windowWidth).toBe('number')
221
+ })
222
+ })
223
+ })