@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,276 @@
1
+ import { describe, it, expect, afterEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MapLarge from '@/components/MapLarge.vue'
4
+
5
+ const mockRegions = {
6
+ land: [],
7
+ sea: [],
8
+ }
9
+
10
+ describe('MapLarge.vue', () => {
11
+ let wrapper
12
+
13
+ afterEach(() => {
14
+ if (wrapper) {
15
+ wrapper.unmount()
16
+ }
17
+ })
18
+
19
+ describe('Component mounting', () => {
20
+ it('should mount with required props', () => {
21
+ wrapper = mount(MapLarge, {
22
+ props: {
23
+ index: 0,
24
+ input: mockRegions,
25
+ visibleWarnings: [],
26
+ warnings: null,
27
+ geometryId: 2021,
28
+ loading: false,
29
+ theme: 'light-theme',
30
+ language: 'fi',
31
+ },
32
+ })
33
+
34
+ expect(wrapper.exists()).toBe(true)
35
+ })
36
+
37
+ it('should render SVG element', () => {
38
+ wrapper = mount(MapLarge, {
39
+ props: {
40
+ index: 0,
41
+ input: mockRegions,
42
+ visibleWarnings: [],
43
+ warnings: null,
44
+ geometryId: 2021,
45
+ loading: false,
46
+ theme: 'light-theme',
47
+ language: 'fi',
48
+ },
49
+ })
50
+
51
+ expect(wrapper.find('svg').exists()).toBe(true)
52
+ })
53
+
54
+ it('should have correct SVG viewBox', () => {
55
+ wrapper = mount(MapLarge, {
56
+ props: {
57
+ index: 0,
58
+ input: mockRegions,
59
+ visibleWarnings: [],
60
+ warnings: null,
61
+ geometryId: 2021,
62
+ loading: false,
63
+ theme: 'light-theme',
64
+ language: 'fi',
65
+ },
66
+ })
67
+
68
+ const svg = wrapper.find('svg')
69
+ expect(svg.attributes('viewBox')).toBe('0 0 440 550')
70
+ })
71
+ })
72
+
73
+ describe('Loading state', () => {
74
+ it('should show spinner when loading and spinnerEnabled', () => {
75
+ wrapper = mount(MapLarge, {
76
+ props: {
77
+ index: 0,
78
+ input: mockRegions,
79
+ visibleWarnings: [],
80
+ warnings: null,
81
+ geometryId: 2021,
82
+ loading: true,
83
+ spinnerEnabled: true,
84
+ theme: 'light-theme',
85
+ language: 'fi',
86
+ },
87
+ })
88
+
89
+ expect(wrapper.find('.spinner-container').exists()).toBe(true)
90
+ })
91
+
92
+ it('should not show spinner when not loading', () => {
93
+ wrapper = mount(MapLarge, {
94
+ props: {
95
+ index: 0,
96
+ input: mockRegions,
97
+ visibleWarnings: [],
98
+ warnings: null,
99
+ geometryId: 2021,
100
+ loading: false,
101
+ spinnerEnabled: true,
102
+ theme: 'light-theme',
103
+ language: 'fi',
104
+ },
105
+ })
106
+
107
+ expect(wrapper.find('.spinner-container').exists()).toBe(false)
108
+ })
109
+
110
+ it('should not show spinner when spinnerEnabled is false', () => {
111
+ wrapper = mount(MapLarge, {
112
+ props: {
113
+ index: 0,
114
+ input: mockRegions,
115
+ visibleWarnings: [],
116
+ warnings: null,
117
+ geometryId: 2021,
118
+ loading: true,
119
+ spinnerEnabled: false,
120
+ theme: 'light-theme',
121
+ language: 'fi',
122
+ },
123
+ })
124
+
125
+ expect(wrapper.find('.spinner-container').exists()).toBe(false)
126
+ })
127
+ })
128
+
129
+ describe('Theme support', () => {
130
+ it('should apply theme class', () => {
131
+ wrapper = mount(MapLarge, {
132
+ props: {
133
+ index: 0,
134
+ input: mockRegions,
135
+ visibleWarnings: [],
136
+ warnings: null,
137
+ geometryId: 2021,
138
+ loading: false,
139
+ theme: 'dark-theme',
140
+ language: 'fi',
141
+ },
142
+ })
143
+
144
+ expect(wrapper.find('.map-large').classes()).toContain('dark-theme')
145
+ })
146
+ })
147
+
148
+ describe('Computed properties', () => {
149
+ it('should compute mapText', () => {
150
+ wrapper = mount(MapLarge, {
151
+ props: {
152
+ index: 0,
153
+ input: mockRegions,
154
+ visibleWarnings: [],
155
+ warnings: null,
156
+ geometryId: 2021,
157
+ loading: false,
158
+ theme: 'light-theme',
159
+ language: 'fi',
160
+ },
161
+ })
162
+
163
+ expect(typeof wrapper.vm.mapText).toBe('string')
164
+ })
165
+
166
+ it('should have size Large', () => {
167
+ wrapper = mount(MapLarge, {
168
+ props: {
169
+ index: 0,
170
+ input: mockRegions,
171
+ visibleWarnings: [],
172
+ warnings: null,
173
+ geometryId: 2021,
174
+ loading: false,
175
+ theme: 'light-theme',
176
+ language: 'fi',
177
+ },
178
+ })
179
+
180
+ expect(wrapper.vm.size).toBe('Large')
181
+ })
182
+
183
+ it('should compute strokeWidth based on scale', () => {
184
+ wrapper = mount(MapLarge, {
185
+ props: {
186
+ index: 0,
187
+ input: mockRegions,
188
+ visibleWarnings: [],
189
+ warnings: null,
190
+ geometryId: 2021,
191
+ loading: false,
192
+ theme: 'light-theme',
193
+ language: 'fi',
194
+ },
195
+ })
196
+
197
+ // strokeWidth is computed: 1 - (scale - 1) / scale
198
+ // Default scale is 1, so: 1 - (1-1)/1 = 1
199
+ expect(wrapper.vm.strokeWidth).toBe('1')
200
+ })
201
+
202
+ it('should compute strokeOpacity based on scale', () => {
203
+ wrapper = mount(MapLarge, {
204
+ props: {
205
+ index: 0,
206
+ input: mockRegions,
207
+ visibleWarnings: [],
208
+ warnings: null,
209
+ geometryId: 2021,
210
+ loading: false,
211
+ theme: 'light-theme',
212
+ language: 'fi',
213
+ },
214
+ })
215
+
216
+ // strokeOpacity is also computed based on scale
217
+ expect(typeof wrapper.vm.strokeOpacity).toBe('string')
218
+ })
219
+ })
220
+
221
+ describe('Accessibility', () => {
222
+ it('should have aria-labelledby on SVG', () => {
223
+ wrapper = mount(MapLarge, {
224
+ props: {
225
+ index: 0,
226
+ input: mockRegions,
227
+ visibleWarnings: [],
228
+ warnings: null,
229
+ geometryId: 2021,
230
+ loading: false,
231
+ theme: 'light-theme',
232
+ language: 'fi',
233
+ },
234
+ })
235
+
236
+ const svg = wrapper.find('svg')
237
+ expect(svg.attributes('aria-labelledby')).toBe('finland-large-title')
238
+ })
239
+
240
+ it('should have role img on SVG', () => {
241
+ wrapper = mount(MapLarge, {
242
+ props: {
243
+ index: 0,
244
+ input: mockRegions,
245
+ visibleWarnings: [],
246
+ warnings: null,
247
+ geometryId: 2021,
248
+ loading: false,
249
+ theme: 'light-theme',
250
+ language: 'fi',
251
+ },
252
+ })
253
+
254
+ const svg = wrapper.find('svg')
255
+ expect(svg.attributes('role')).toBe('img')
256
+ })
257
+
258
+ it('should be focusable', () => {
259
+ wrapper = mount(MapLarge, {
260
+ props: {
261
+ index: 0,
262
+ input: mockRegions,
263
+ visibleWarnings: [],
264
+ warnings: null,
265
+ geometryId: 2021,
266
+ loading: false,
267
+ theme: 'light-theme',
268
+ language: 'fi',
269
+ },
270
+ })
271
+
272
+ const container = wrapper.find('.map-large')
273
+ expect(container.attributes('tabindex')).toBe('0')
274
+ })
275
+ })
276
+ })
@@ -0,0 +1,226 @@
1
+ import { describe, it, expect, afterEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import MapSmall from '@/components/MapSmall.vue'
4
+
5
+ const mockRegions = {
6
+ land: [],
7
+ sea: [],
8
+ }
9
+
10
+ describe('MapSmall.vue', () => {
11
+ let wrapper
12
+
13
+ afterEach(() => {
14
+ if (wrapper) {
15
+ wrapper.unmount()
16
+ }
17
+ })
18
+
19
+ describe('Component mounting', () => {
20
+ it('should mount with required props', () => {
21
+ wrapper = mount(MapSmall, {
22
+ props: {
23
+ index: 0,
24
+ input: mockRegions,
25
+ visibleWarnings: [],
26
+ warnings: null,
27
+ geometryId: 2021,
28
+ loading: false,
29
+ theme: 'light-theme',
30
+ },
31
+ })
32
+
33
+ expect(wrapper.exists()).toBe(true)
34
+ })
35
+
36
+ it('should render SVG element', () => {
37
+ wrapper = mount(MapSmall, {
38
+ props: {
39
+ index: 0,
40
+ input: mockRegions,
41
+ visibleWarnings: [],
42
+ warnings: null,
43
+ geometryId: 2021,
44
+ loading: false,
45
+ theme: 'light-theme',
46
+ },
47
+ })
48
+
49
+ expect(wrapper.find('svg').exists()).toBe(true)
50
+ })
51
+
52
+ it('should have correct SVG viewBox', () => {
53
+ wrapper = mount(MapSmall, {
54
+ props: {
55
+ index: 0,
56
+ input: mockRegions,
57
+ visibleWarnings: [],
58
+ warnings: null,
59
+ geometryId: 2021,
60
+ loading: false,
61
+ theme: 'light-theme',
62
+ },
63
+ })
64
+
65
+ const svg = wrapper.find('svg')
66
+ expect(svg.attributes('viewBox')).toBe('0 0 75 120')
67
+ })
68
+
69
+ it('should have correct width and height', () => {
70
+ wrapper = mount(MapSmall, {
71
+ props: {
72
+ index: 0,
73
+ input: mockRegions,
74
+ visibleWarnings: [],
75
+ warnings: null,
76
+ geometryId: 2021,
77
+ loading: false,
78
+ theme: 'light-theme',
79
+ },
80
+ })
81
+
82
+ const svg = wrapper.find('svg')
83
+ expect(svg.attributes('width')).toBe('75')
84
+ expect(svg.attributes('height')).toBe('120')
85
+ })
86
+ })
87
+
88
+ describe('Computed properties', () => {
89
+ it('should have size Small', () => {
90
+ wrapper = mount(MapSmall, {
91
+ props: {
92
+ index: 0,
93
+ input: mockRegions,
94
+ visibleWarnings: [],
95
+ warnings: null,
96
+ geometryId: 2021,
97
+ loading: false,
98
+ theme: 'light-theme',
99
+ },
100
+ })
101
+
102
+ expect(wrapper.vm.size).toBe('Small')
103
+ })
104
+
105
+ it('should use correct strokeWidth', () => {
106
+ wrapper = mount(MapSmall, {
107
+ props: {
108
+ index: 0,
109
+ input: mockRegions,
110
+ visibleWarnings: [],
111
+ warnings: null,
112
+ geometryId: 2021,
113
+ loading: false,
114
+ theme: 'light-theme',
115
+ },
116
+ })
117
+
118
+ expect(wrapper.vm.strokeWidth).toBe(0.6)
119
+ })
120
+
121
+ it('should initialize pathsNeeded as false', () => {
122
+ wrapper = mount(MapSmall, {
123
+ props: {
124
+ index: 0,
125
+ input: mockRegions,
126
+ visibleWarnings: [],
127
+ warnings: null,
128
+ geometryId: 2021,
129
+ loading: false,
130
+ theme: 'light-theme',
131
+ },
132
+ })
133
+
134
+ // pathsNeeded is set in mounted, so it depends on isFullMode()
135
+ expect(typeof wrapper.vm.pathsNeeded).toBe('boolean')
136
+ })
137
+
138
+ it('should have pathsNeeded property', () => {
139
+ wrapper = mount(MapSmall, {
140
+ props: {
141
+ index: 0,
142
+ input: mockRegions,
143
+ visibleWarnings: [],
144
+ warnings: null,
145
+ geometryId: 2021,
146
+ loading: true,
147
+ theme: 'light-theme',
148
+ },
149
+ })
150
+
151
+ expect(wrapper.vm).toHaveProperty('pathsNeeded')
152
+ })
153
+ })
154
+
155
+ describe('ID attribute', () => {
156
+ it('should have unique id based on index', () => {
157
+ wrapper = mount(MapSmall, {
158
+ props: {
159
+ index: 2,
160
+ input: mockRegions,
161
+ visibleWarnings: [],
162
+ warnings: null,
163
+ geometryId: 2021,
164
+ loading: false,
165
+ theme: 'light-theme',
166
+ },
167
+ })
168
+
169
+ expect(wrapper.attributes('id')).toBe('day-map-small-2')
170
+ })
171
+
172
+ it('should have unique SVG group id based on index', () => {
173
+ wrapper = mount(MapSmall, {
174
+ props: {
175
+ index: 3,
176
+ input: mockRegions,
177
+ visibleWarnings: [],
178
+ warnings: null,
179
+ geometryId: 2021,
180
+ loading: false,
181
+ theme: 'light-theme',
182
+ },
183
+ })
184
+
185
+ const group = wrapper.find('g')
186
+ if (group.exists()) {
187
+ expect(group.attributes('id')).toBe('finland-small-3')
188
+ }
189
+ })
190
+ })
191
+
192
+ describe('CSS classes', () => {
193
+ it('should have finland-small class on SVG', () => {
194
+ wrapper = mount(MapSmall, {
195
+ props: {
196
+ index: 0,
197
+ input: mockRegions,
198
+ visibleWarnings: [],
199
+ warnings: null,
200
+ geometryId: 2021,
201
+ loading: false,
202
+ theme: 'light-theme',
203
+ },
204
+ })
205
+
206
+ const svg = wrapper.find('svg')
207
+ expect(svg.classes()).toContain('finland-small')
208
+ })
209
+
210
+ it('should have map-small class on container', () => {
211
+ wrapper = mount(MapSmall, {
212
+ props: {
213
+ index: 0,
214
+ input: mockRegions,
215
+ visibleWarnings: [],
216
+ warnings: null,
217
+ geometryId: 2021,
218
+ loading: false,
219
+ theme: 'light-theme',
220
+ },
221
+ })
222
+
223
+ expect(wrapper.classes()).toContain('map-small')
224
+ })
225
+ })
226
+ })