@eeacms/volto-arcgis-block 0.1.406 → 0.1.408

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [0.1.408](https://github.com/eea/volto-arcgis-block/compare/0.1.407...0.1.408) - 19 November 2025
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - (bug): Fixed child node dissappearing errors that crash the data viewer [Unai Bolivar - [`d2d3503`](https://github.com/eea/volto-arcgis-block/commit/d2d3503371e90f50668964d0771be7de7ca3e447)]
12
+ - (feat): redid the titles produced for the error report [Unai Bolivar - [`08cd2bc`](https://github.com/eea/volto-arcgis-block/commit/08cd2bc75843b7313c54cffecd1b1c637ee2ab23)]
13
+ ### [0.1.407](https://github.com/eea/volto-arcgis-block/compare/0.1.406...0.1.407) - 19 November 2025
14
+
15
+ #### :hammer_and_wrench: Others
16
+
17
+ - (feat): Rebased develop with this branch [Unai Bolivar - [`dae3e96`](https://github.com/eea/volto-arcgis-block/commit/dae3e96d56bd21d54a348267289932d299ba67b2)]
18
+ - (feat): Linting and prettifying before push [Unai Bolivar - [`455aa13`](https://github.com/eea/volto-arcgis-block/commit/455aa133d4da07949f3751d099c3f9ccddd4d971)]
19
+ - (feat): Error Report widget added user instructions and style changes [Unai Bolivar - [`3e69a69`](https://github.com/eea/volto-arcgis-block/commit/3e69a690467e1a66552a0ff8579b30eaabb5b95e)]
20
+ - (feat): Error Report widget added functionality, fixed bugs in css styles. [Unai Bolivar - [`1c833b2`](https://github.com/eea/volto-arcgis-block/commit/1c833b2b62e279fee1adbac0233f8d1cc5f2f559)]
21
+ - (feat): Error Report widget creation [Unai Bolivar - [`47ad31e`](https://github.com/eea/volto-arcgis-block/commit/47ad31e724279be223f3bdd3d8d619db32c24293)]
7
22
  ### [0.1.406](https://github.com/eea/volto-arcgis-block/compare/0.1.405...0.1.406) - 19 November 2025
8
23
 
9
24
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.406",
3
+ "version": "0.1.408",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -0,0 +1,428 @@
1
+ import React, { createRef } from 'react';
2
+ import { loadModules } from 'esri-loader';
3
+
4
+ class ErrorReport extends React.Component {
5
+ constructor(props) {
6
+ super(props);
7
+ this.container = createRef();
8
+ this.originalParent = null;
9
+ this.state = {
10
+ showMapMenu: false,
11
+ latlong: null,
12
+ selecting: false,
13
+ datasets: [],
14
+ instructionsText: 'First select a pixel in the data viewer',
15
+ };
16
+ this.menuClass =
17
+ 'esri-icon-notice-round esri-widget--button esri-widget esri-interactive';
18
+ this.helpdeskUrl = 'https://land.copernicus.eu/en/contact-service-helpdesk';
19
+ }
20
+
21
+ loader() {
22
+ return loadModules([]).then(() => {});
23
+ }
24
+
25
+ openMenu() {
26
+ if (this.state.showMapMenu) {
27
+ this.props.mapViewer.setActiveWidget();
28
+ this.container.current.querySelector('.right-panel').style.display =
29
+ 'none';
30
+ this.container.current
31
+ .querySelector('.esri-widget--button')
32
+ .classList.remove('active-widget');
33
+ document
34
+ .querySelector('.esri-ui-top-right.esri-ui-corner')
35
+ .classList.remove('show-panel');
36
+ this.setState({ showMapMenu: false });
37
+ this.resetData();
38
+ } else {
39
+ this.props.mapViewer.setActiveWidget(this);
40
+ this.container.current.querySelector('.right-panel').style.display =
41
+ 'flex';
42
+ this.container.current
43
+ .querySelector('.esri-widget--button')
44
+ .classList.add('active-widget');
45
+ document
46
+ .querySelector('.esri-ui-top-right.esri-ui-corner')
47
+ .classList.add('show-panel');
48
+ this.setState({ showMapMenu: true });
49
+ this.startSelection();
50
+ }
51
+ }
52
+
53
+ startSelection() {
54
+ if (this.state.selecting) return;
55
+ var handler = this.props.view.on(
56
+ 'pointer-down',
57
+ function (evt) {
58
+ let pt = this.props.view.toMap({ x: evt.x, y: evt.y });
59
+ let ds = this.getCheckedDatasets();
60
+ let message =
61
+ 'Selected coordinate: Lat ' +
62
+ pt.latitude.toFixed(4) +
63
+ ' Lon ' +
64
+ pt.longitude.toFixed(4) +
65
+ '. Active datasets: ' +
66
+ ds.join(', ');
67
+ try {
68
+ if (
69
+ navigator &&
70
+ navigator.clipboard &&
71
+ navigator.clipboard.writeText
72
+ ) {
73
+ navigator.clipboard.writeText(message);
74
+ } else {
75
+ let ta = document.createElement('textarea');
76
+ ta.value = message;
77
+ ta.setAttribute('readonly', '');
78
+ ta.style.position = 'absolute';
79
+ ta.style.left = '-9999px';
80
+ document.body.appendChild(ta);
81
+ ta.select();
82
+ document.execCommand('copy');
83
+ document.body.removeChild(ta);
84
+ }
85
+ } catch {}
86
+ this.setState({
87
+ latlong: { x: pt.latitude.toFixed(4), y: pt.longitude.toFixed(4) },
88
+ selecting: false,
89
+ datasets: ds,
90
+ instructionsText:
91
+ "The error report data has been added to your clipboard. Click the 'Service Desk' button and paste the clipboard content inside the Helpdesk's message box",
92
+ });
93
+ handler.remove();
94
+ }.bind(this),
95
+ );
96
+ this.setState({ selecting: handler });
97
+ }
98
+
99
+ getCheckedDatasets() {
100
+ let checked = [];
101
+ try {
102
+ let uid = sessionStorage.getItem('mv_hydrated_for');
103
+ let key = uid ? 'user_' + uid : 'user_anonymous';
104
+ let raw = localStorage.getItem(key);
105
+ if (raw) {
106
+ let obj = JSON.parse(raw);
107
+ let cl = obj && obj.checkedLayers;
108
+ if (typeof cl === 'string') {
109
+ try {
110
+ cl = JSON.parse(cl);
111
+ } catch {}
112
+ }
113
+ if (Array.isArray(cl)) {
114
+ checked = [...new Set(cl)].filter((v) => v);
115
+ }
116
+ }
117
+ } catch {}
118
+ if (!checked.length) {
119
+ try {
120
+ let ss = sessionStorage.getItem('checkedLayers');
121
+ if (ss) {
122
+ let cl = ss;
123
+ if (typeof ss === 'string') {
124
+ try {
125
+ cl = JSON.parse(ss);
126
+ } catch {}
127
+ }
128
+ if (Array.isArray(cl)) {
129
+ checked = [...new Set(cl)].filter((v) => v);
130
+ }
131
+ }
132
+ } catch {}
133
+ }
134
+ if (checked.length) {
135
+ let titles = [];
136
+ try {
137
+ let esc = function (s) {
138
+ try {
139
+ return CSS && CSS.escape
140
+ ? CSS.escape(s)
141
+ : String(s).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
142
+ } catch {
143
+ return String(s).replace(/[^a-zA-Z0-9_-]/g, '\\$&');
144
+ }
145
+ };
146
+ let findTitleFor = function (val) {
147
+ let v = String(val);
148
+ let e = null;
149
+ try {
150
+ e = document.querySelector('[data-layer-id="' + esc(v) + '"]');
151
+ } catch {}
152
+ if (!e) {
153
+ try {
154
+ e = document.querySelector('[data-id="' + esc(v) + '"]');
155
+ } catch {}
156
+ }
157
+ if (!e) {
158
+ try {
159
+ let sel1 = 'input[type="checkbox"][value="' + esc(v) + '"]';
160
+ e = document.querySelector(sel1);
161
+ } catch {}
162
+ }
163
+ if (!e) {
164
+ try {
165
+ let sel2 = 'input[type="checkbox"][id*="' + esc(v) + '"]';
166
+ e = document.querySelector(sel2);
167
+ } catch {}
168
+ }
169
+ if (e && e.tagName && e.tagName.toLowerCase() === 'input') {
170
+ let id = e.id;
171
+ if (id) {
172
+ try {
173
+ let selLab = 'label[for="' + esc(id) + '"]';
174
+ let lab = document.querySelector(selLab);
175
+ if (lab && lab.textContent) return lab.textContent.trim();
176
+ } catch {}
177
+ }
178
+ try {
179
+ let cont = e.closest('.toc-item,.ccl-tree__item,.layer-item');
180
+ if (cont) {
181
+ let selT =
182
+ '.toc-item-title,.ccl-tree__label,.layer-title,.title';
183
+
184
+ let lab = cont.querySelector(selT);
185
+ if (lab && lab.textContent) return lab.textContent.trim();
186
+ }
187
+ } catch {}
188
+ }
189
+ if (e) {
190
+ try {
191
+ let sel3 = '.toc-item-title,.ccl-tree__label,.layer-title,.title';
192
+ let lab = e.querySelector(sel3);
193
+ if (lab && lab.textContent) return lab.textContent.trim();
194
+ } catch {}
195
+ try {
196
+ let t = e.getAttribute('data-title');
197
+ if (t) return t;
198
+ } catch {}
199
+ try {
200
+ if (e.textContent) return e.textContent.trim();
201
+ } catch {}
202
+ }
203
+ return null;
204
+ };
205
+ checked.forEach(function (v) {
206
+ let t = findTitleFor(v);
207
+ if (t) titles.push(t);
208
+ });
209
+ } catch {}
210
+ if (titles.length) return [...new Set(titles)];
211
+ let all = [];
212
+ try {
213
+ let coll =
214
+ this.props.view &&
215
+ this.props.view.map &&
216
+ this.props.view.map.allLayers;
217
+ if (coll && coll.items) {
218
+ all = coll.items;
219
+ } else if (coll && coll.toArray) {
220
+ all = coll.toArray();
221
+ }
222
+ } catch {}
223
+ let byId = {};
224
+ let byTitle = {};
225
+ try {
226
+ all.forEach(function (l) {
227
+ if (l && l.id) byId[l.id] = l.title || l.id;
228
+ if (l && l.title) byTitle[l.title] = l.title;
229
+ });
230
+ } catch {}
231
+ let titles2 = [];
232
+ checked.forEach(function (v) {
233
+ if (byTitle[v]) titles2.push(byTitle[v]);
234
+ else if (byId[v]) titles2.push(byId[v]);
235
+ });
236
+ if (titles2.length) return [...new Set(titles2)];
237
+ }
238
+ let layers = [];
239
+ this.props.view.map.layers.forEach(function (l) {
240
+ if (l.visible) {
241
+ layers.push(l.title || l.id || 'layer');
242
+ }
243
+ });
244
+ return layers;
245
+ }
246
+
247
+ serviceDeskRedirect() {
248
+ if (!this.state.latlong) return;
249
+ let datasets =
250
+ this.state.datasets && this.state.datasets.length > 0
251
+ ? this.state.datasets
252
+ : this.getCheckedDatasets();
253
+ let message =
254
+ 'Selected coordinate: Lat ' +
255
+ this.state.latlong.x +
256
+ ' Lon ' +
257
+ this.state.latlong.y +
258
+ '. Active datasets: ' +
259
+ datasets.join(', ');
260
+ try {
261
+ if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
262
+ navigator.clipboard.writeText(message);
263
+ } else {
264
+ let ta = document.createElement('textarea');
265
+ ta.value = message;
266
+ ta.setAttribute('readonly', '');
267
+ ta.style.position = 'absolute';
268
+ ta.style.left = '-9999px';
269
+ document.body.appendChild(ta);
270
+ ta.select();
271
+ document.execCommand('copy');
272
+ document.body.removeChild(ta);
273
+ }
274
+ } catch {}
275
+ window.open(this.helpdeskUrl, '_blank');
276
+ }
277
+
278
+ resetData() {
279
+ if (this.state.selecting) {
280
+ this.state.selecting.remove();
281
+ }
282
+ this.setState({
283
+ latlong: null,
284
+ selecting: false,
285
+ datasets: [],
286
+ instructionsText: 'First select a pixel in the data viewer',
287
+ });
288
+ }
289
+
290
+ async componentDidMount() {
291
+ await this.loader();
292
+ if (!this.container.current) return;
293
+ try {
294
+ this.originalParent = this.container.current.parentNode;
295
+ } catch {}
296
+ this.props.view.when(() => {
297
+ if (!this.container.current) return;
298
+ var group = document.querySelector('.esri-ui-top-right.esri-ui-corner');
299
+ var addSelf = () => {
300
+ this.props.view.ui.add(this.container.current, {
301
+ position: 'top-right',
302
+ // index: 9999,
303
+ });
304
+ };
305
+ if (group && group.children && group.children.length > 0) {
306
+ addSelf();
307
+ } else if (group) {
308
+ var observer = new MutationObserver((m) => {
309
+ if (group.children.length > 0) {
310
+ addSelf();
311
+ observer.disconnect();
312
+ }
313
+ });
314
+ observer.observe(group, { childList: true });
315
+ } else {
316
+ addSelf();
317
+ }
318
+ });
319
+ }
320
+
321
+ componentWillUnmount() {
322
+ try {
323
+ if (this.state && this.state.selecting) {
324
+ this.state.selecting.remove();
325
+ }
326
+ } catch {}
327
+ try {
328
+ if (
329
+ this.container &&
330
+ this.container.current &&
331
+ this.originalParent &&
332
+ this.container.current.parentNode !== this.originalParent
333
+ ) {
334
+ this.originalParent.appendChild(this.container.current);
335
+ }
336
+ } catch {}
337
+ }
338
+
339
+ render() {
340
+ return (
341
+ <>
342
+ <div ref={this.container} className="error-report-container">
343
+ <div tooltip="Report an issue" direction="left" type="widget">
344
+ <div
345
+ className={this.menuClass}
346
+ id="map_error_report_button"
347
+ aria-label="Report an issue"
348
+ onClick={this.openMenu.bind(this)}
349
+ onKeyDown={(e) => {
350
+ if (
351
+ !e.altKey &&
352
+ e.code !== 'Tab' &&
353
+ !e.ctrlKey &&
354
+ e.code !== 'Delete' &&
355
+ !e.shiftKey &&
356
+ !e.code.startsWith('F')
357
+ ) {
358
+ this.openMenu(this);
359
+ }
360
+ }}
361
+ tabIndex="0"
362
+ role="button"
363
+ ></div>
364
+ </div>
365
+ <div className="right-panel">
366
+ <div className="right-panel-header">
367
+ <span>Report an issue</span>
368
+ <span
369
+ className="map-menu-icon esri-icon-close"
370
+ onClick={this.openMenu.bind(this)}
371
+ onKeyDown={(e) => {
372
+ if (
373
+ !e.altKey &&
374
+ e.code !== 'Tab' &&
375
+ !e.ctrlKey &&
376
+ e.code !== 'Delete' &&
377
+ !e.shiftKey &&
378
+ !e.code.startsWith('F')
379
+ ) {
380
+ this.openMenu(this);
381
+ }
382
+ }}
383
+ tabIndex="0"
384
+ role="button"
385
+ ></span>
386
+ </div>
387
+ <div className="right-panel-content">
388
+ <div className="error-report-panel">
389
+ <div className="error-report-instructions">
390
+ {this.state.instructionsText}
391
+ </div>
392
+ <button
393
+ className="error-report-button"
394
+ disabled={!this.state.latlong}
395
+ onClick={this.serviceDeskRedirect.bind(this)}
396
+ >
397
+ Service desk
398
+ </button>
399
+ {this.state.latlong && (
400
+ <>
401
+ <h3 className="error-report-coords-heading">Coordinates</h3>
402
+ <div className="error-report-coords">
403
+ Lat {this.state.latlong.x} Lon {this.state.latlong.y}
404
+ </div>
405
+ </>
406
+ )}
407
+ {this.state.datasets && this.state.datasets.length > 0 && (
408
+ <>
409
+ <div className="error-report-datasets-title">
410
+ Active datasets
411
+ </div>
412
+ <ul className="error-report-datasets">
413
+ {this.state.datasets.map((d, i) => (
414
+ <li key={i}>{d}</li>
415
+ ))}
416
+ </ul>
417
+ </>
418
+ )}
419
+ </div>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ </>
424
+ );
425
+ }
426
+ }
427
+
428
+ export default ErrorReport;
@@ -30,6 +30,7 @@ import PanWidget from './PanWidget';
30
30
  import BookmarkWidget from './BookmarkWidget';
31
31
  import LoadingSpinner from './LoadingSpinner';
32
32
  import UploadWidget from './UploadWidget';
33
+ import ErrorReportWidget from './ErrorReportWidget';
33
34
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable';
34
35
  import { getTaxonomy } from '@eeacms/volto-taxonomy/actions';
35
36
  import { fetchCatalogApiDates } from '../../actions';
@@ -1016,6 +1017,10 @@ class MapViewer extends React.Component {
1016
1017
  />
1017
1018
  );
1018
1019
  }
1020
+ renderErrorReport() {
1021
+ if (this.view)
1022
+ return <ErrorReportWidget view={this.view} mapViewer={this} />;
1023
+ }
1019
1024
  /**
1020
1025
  * This method renders the map viewer, invoking if necessary the methods
1021
1026
  * to render the other widgets to display
@@ -1048,6 +1053,7 @@ class MapViewer extends React.Component {
1048
1053
  {this.renderLoadingSpinner()}
1049
1054
  <CheckUserID reference={this} />
1050
1055
  {this.renderUploadService()}
1056
+ {this.renderErrorReport()}
1051
1057
  </div>
1052
1058
  </div>
1053
1059
  );
@@ -313,6 +313,34 @@ button.upload-bookmark-button {
313
313
  font-size: 30px !important;
314
314
  }
315
315
 
316
+ /* Error report */
317
+ .error-report-button {
318
+ display: block;
319
+ width: 100%;
320
+ box-sizing: border-box;
321
+ padding: 0.75rem 1rem;
322
+ border: 1px solid #a0b128 !important;
323
+ margin: 1rem 0;
324
+ background-color: white !important;
325
+ color: #a0b128 !important;
326
+ cursor: pointer;
327
+ text-align: center;
328
+ transition: all 0.3s ease-out;
329
+ }
330
+
331
+ .error-report-button:hover {
332
+ border-color: #a0b128 !important;
333
+ background-color: #a0b128 !important;
334
+ color: white !important;
335
+ }
336
+
337
+ .error-report-datasets-title {
338
+ margin-top: 1em;
339
+ margin-bottom: 0.5em;
340
+ font-size: 1.17em;
341
+ font-weight: bold;
342
+ }
343
+
316
344
  body.section-map-viewer
317
345
  #page-document.ui.container
318
346
  .esri-widget__content--empty