@eeacms/volto-arcgis-block 0.1.475 → 0.1.477

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/.env CHANGED
@@ -1 +1 @@
1
- JEST_USE_SETUP=OFF # Jest configuration variables: ON, OFF
1
+ JEST_USE_SETUP=OFF # Jest configuration variables: ON, OFF
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ 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.477](https://github.com/eea/volto-arcgis-block/compare/0.1.476...0.1.477) - 2 September 2026
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - (lint): lint and prettified [Unai Bolivar - [`e340270`](https://github.com/eea/volto-arcgis-block/commit/e340270b53e3591428cc13acb69041a6f90a448e)]
12
+ - (chore): update hotspots widget for hsm II data [Unai Bolivar - [`9c9d672`](https://github.com/eea/volto-arcgis-block/commit/9c9d67254f5f69faf23d6aa6cb721b7cdafe9b16)]
13
+ - (task): refactor hotspot widget to comply with updated JSON for geo klc hot spot data. [Unai Bolivar - [`39e8be7`](https://github.com/eea/volto-arcgis-block/commit/39e8be7e0fd0e1bfbf3dfe42e43ec7e3eabaafca)]
14
+ ### [0.1.476](https://github.com/eea/volto-arcgis-block/compare/0.1.475...0.1.476) - 31 August 2026
15
+
7
16
  ### [0.1.475](https://github.com/eea/volto-arcgis-block/compare/0.1.474...0.1.475) - 19 August 2026
8
17
 
9
18
  #### :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.475",
3
+ "version": "0.1.477",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -629,7 +629,13 @@ class HotspotWidget extends React.Component {
629
629
  data-target-property="layout"
630
630
  onBlur={() => {}}
631
631
  onChange={(e) => {
632
- this.disableButton();
632
+ this.setState(
633
+ { lcYear: e.target.value, lccYear: null },
634
+ () => {
635
+ this.updateLccOptionsForSelectedLc();
636
+ this.disableButton();
637
+ },
638
+ );
633
639
  }}
634
640
  ></select>
635
641
  </label>
@@ -647,9 +653,14 @@ class HotspotWidget extends React.Component {
647
653
  let dichotomousKLCAreas = [];
648
654
  let activeLayers = [];
649
655
 
650
- if (this.props.hotspotData['activeLayers'] === undefined) return;
651
-
652
- activeLayers = Object.keys(this.props.hotspotData['activeLayers']);
656
+ if (
657
+ this.props.hotspotData &&
658
+ this.props.hotspotData['activeLayers'] !== undefined
659
+ ) {
660
+ activeLayers = Object.keys(this.props.hotspotData['activeLayers']);
661
+ } else {
662
+ activeLayers = [];
663
+ }
653
664
 
654
665
  if (selectedOption === undefined) return;
655
666
 
@@ -657,19 +668,22 @@ class HotspotWidget extends React.Component {
657
668
  selectBoxLccTime = document.getElementById('select-klc-lccTime');
658
669
  selectBoxLcTime = document.getElementById('select-klc-lcTime');
659
670
 
660
- if (selectedOption !== this.state.selectedArea) {
661
- this.setState({
662
- lcYear: null,
663
- lccYear: null,
664
- });
665
- }
666
-
667
671
  //this.selectedArea = selectedOption;
668
672
 
669
673
  for (let i = 0; i < data.length; i++) {
670
674
  var option = data[i].node.klc_name;
671
675
 
672
- let keyMapInfoObj = JSON.parse(data[i].node.keymap_info) || {};
676
+ let rawKeymapInfo = data[i].node.keymap_info;
677
+ let keyMapInfoObj = {};
678
+ if (typeof rawKeymapInfo === 'string') {
679
+ try {
680
+ keyMapInfoObj = JSON.parse(rawKeymapInfo) || {};
681
+ } catch (e) {
682
+ keyMapInfoObj = {};
683
+ }
684
+ } else if (rawKeymapInfo && typeof rawKeymapInfo === 'object') {
685
+ keyMapInfoObj = rawKeymapInfo;
686
+ }
673
687
 
674
688
  if (keyMapInfoObj.b_classes === true) {
675
689
  modularKLCAreas.push(option);
@@ -695,36 +709,107 @@ class HotspotWidget extends React.Component {
695
709
  selectBoxLccTime.options[0].disabled = true;
696
710
  }
697
711
 
698
- if (
699
- data[i].node.keymap_info
700
- .toLowerCase()
701
- .includes('multiple_dates":true')
702
- ) {
703
- //new select year options values
704
- var optionLcTime = data[i].node.present_lc_year;
705
- var indexStart = data[i].node.keymap_info
706
- .toLowerCase()
707
- .indexOf('"dates":[ {');
708
- var indexEnd = data[i].node.keymap_info.toLowerCase().indexOf(' }],');
709
- var strOut = data[i].node.keymap_info.substring(indexStart, indexEnd);
710
- var numbers = strOut.match(/\d+/g).map(Number);
711
- numbers.forEach((element) => {
712
- selectBoxLccTime.options.add(new Option(element, element, element));
712
+ let lccDateList = [];
713
+ let lcDateList = [];
714
+ let lccDatesByLcYear = {};
715
+
716
+ if (Array.isArray(keyMapInfoObj.dates)) {
717
+ keyMapInfoObj.dates.forEach((entry) => {
718
+ const lccDate = Number(
719
+ entry && typeof entry === 'object' ? entry?.date : entry,
720
+ );
721
+ const lcDate = Number(
722
+ entry && typeof entry === 'object' ? entry?.lc_date : undefined,
723
+ );
724
+ if (Number.isFinite(lccDate)) {
725
+ lccDateList.push(lccDate);
726
+ }
727
+ if (Number.isFinite(lcDate)) {
728
+ lcDateList.push(lcDate);
729
+ if (!lccDatesByLcYear[lcDate]) {
730
+ lccDatesByLcYear[lcDate] = [];
731
+ }
732
+ if (Number.isFinite(lccDate)) {
733
+ lccDatesByLcYear[lcDate].push(lccDate);
734
+ }
735
+ }
713
736
  });
714
- selectBoxLcTime.options.add(
715
- new Option(optionLcTime, optionLcTime, optionLcTime),
716
- );
717
- } else {
718
- //new select year options values
719
- var optionLccTime = data[i].node.lcc_year;
720
- selectBoxLccTime.options.add(
721
- new Option(optionLccTime, optionLccTime, optionLccTime),
737
+ }
738
+
739
+ if (Array.isArray(keyMapInfoObj.multiple_lc_dates)) {
740
+ lcDateList = lcDateList.concat(
741
+ keyMapInfoObj.multiple_lc_dates
742
+ .map((yearEntry) =>
743
+ Number(
744
+ yearEntry && typeof yearEntry === 'object'
745
+ ? yearEntry?.date
746
+ : yearEntry,
747
+ ),
748
+ )
749
+ .filter((year) => Number.isFinite(year)),
722
750
  );
723
- optionLcTime = data[i].node.present_lc_year;
724
- selectBoxLcTime.options.add(
725
- new Option(optionLcTime, optionLcTime, optionLcTime),
751
+ }
752
+
753
+ const dynamicYearValues = Object.keys(keyMapInfoObj)
754
+ .filter((key) => /^year\d+$/.test(key))
755
+ .map((key) => Number(keyMapInfoObj[key]))
756
+ .filter((year) => Number.isFinite(year));
757
+ if (dynamicYearValues.length) {
758
+ lccDateList = lccDateList.concat(dynamicYearValues);
759
+ }
760
+
761
+ const presentLcYearFromKeymap = Number(keyMapInfoObj.year_present_lc);
762
+ if (Number.isFinite(presentLcYearFromKeymap)) {
763
+ lcDateList.push(presentLcYearFromKeymap);
764
+ }
765
+
766
+ if (
767
+ !lccDateList.length &&
768
+ Number.isFinite(Number(data[i].node.lcc_year))
769
+ ) {
770
+ lccDateList = [Number(data[i].node.lcc_year)];
771
+ }
772
+ if (
773
+ !lcDateList.length &&
774
+ Number.isFinite(Number(data[i].node.present_lc_year))
775
+ ) {
776
+ lcDateList = [Number(data[i].node.present_lc_year)];
777
+ }
778
+
779
+ lccDateList = Array.from(new Set(lccDateList)).sort((a, b) => a - b);
780
+ lcDateList = Array.from(new Set(lcDateList)).sort((a, b) => a - b);
781
+
782
+ Object.keys(lccDatesByLcYear).forEach((lcDateKey) => {
783
+ lccDatesByLcYear[lcDateKey] = Array.from(
784
+ new Set(lccDatesByLcYear[lcDateKey]),
785
+ ).sort((a, b) => a - b);
786
+ });
787
+
788
+ lcDateList.forEach((element) => {
789
+ selectBoxLcTime.options.add(new Option(element, element, element));
790
+ });
791
+
792
+ if (this.state.lcYear !== null) {
793
+ const hasLcYearOption = Array.from(selectBoxLcTime.options).some(
794
+ (option) => option.value === this.state.lcYear,
726
795
  );
796
+ selectBoxLcTime.value = hasLcYearOption
797
+ ? this.state.lcYear
798
+ : 'default';
727
799
  }
800
+
801
+ const selectedLcYear = Number(this.state.lcYear);
802
+ const mappedLccDateList = Number.isFinite(selectedLcYear)
803
+ ? lccDatesByLcYear[selectedLcYear]
804
+ : null;
805
+ const lccOptionsToUse =
806
+ mappedLccDateList && mappedLccDateList.length
807
+ ? mappedLccDateList
808
+ : lccDateList;
809
+
810
+ lccOptionsToUse.forEach((element) => {
811
+ selectBoxLccTime.options.add(new Option(element, element, element));
812
+ });
728
813
  }
729
814
  }
730
815
  if (selectBox) {
@@ -811,6 +896,22 @@ class HotspotWidget extends React.Component {
811
896
  break;
812
897
  }
813
898
  }
899
+ } else if (selectBox) {
900
+ const allKLCAreas = Array.from(
901
+ new Set(modularKLCAreas.concat(dichotomousKLCAreas)),
902
+ ).sort((a, b) => a.localeCompare(b));
903
+ for (let i = 0; i < allKLCAreas.length; i++) {
904
+ let option = allKLCAreas[i];
905
+ selectBox.options.add(new Option(option, option, option));
906
+ }
907
+ if (this.state.selectedArea !== null) {
908
+ const hasSelectedOption = Array.from(selectBox.options).some(
909
+ (option) => option.value === this.state.selectedArea,
910
+ );
911
+ selectBox.value = hasSelectedOption
912
+ ? this.state.selectedArea
913
+ : 'default';
914
+ }
814
915
  }
815
916
  if (selectBox.value === 'default') {
816
917
  if (selectBoxLcTime) {
@@ -840,6 +941,99 @@ class HotspotWidget extends React.Component {
840
941
  }
841
942
  }
842
943
 
944
+ updateLccOptionsForSelectedLc() {
945
+ const selectBoxLccTime = document.getElementById('select-klc-lccTime');
946
+ if (
947
+ !selectBoxLccTime ||
948
+ !this.state.selectedArea ||
949
+ !Array.isArray(this.dataJSONNames)
950
+ ) {
951
+ return;
952
+ }
953
+
954
+ const selectedNode = this.dataJSONNames.find(
955
+ (entry) => entry?.node?.klc_name === this.state.selectedArea,
956
+ )?.node;
957
+
958
+ if (!selectedNode) {
959
+ return;
960
+ }
961
+
962
+ let rawKeymapInfo = selectedNode.keymap_info;
963
+ let keyMapInfoObj = {};
964
+ if (typeof rawKeymapInfo === 'string') {
965
+ try {
966
+ keyMapInfoObj = JSON.parse(rawKeymapInfo) || {};
967
+ } catch (e) {
968
+ keyMapInfoObj = {};
969
+ }
970
+ } else if (rawKeymapInfo && typeof rawKeymapInfo === 'object') {
971
+ keyMapInfoObj = rawKeymapInfo;
972
+ }
973
+
974
+ let lccDateList = [];
975
+ let lccDatesByLcYear = {};
976
+
977
+ if (Array.isArray(keyMapInfoObj.dates)) {
978
+ keyMapInfoObj.dates.forEach((entry) => {
979
+ const lccDate = Number(
980
+ entry && typeof entry === 'object' ? entry?.date : entry,
981
+ );
982
+ const lcDate = Number(
983
+ entry && typeof entry === 'object' ? entry?.lc_date : undefined,
984
+ );
985
+ if (Number.isFinite(lccDate)) {
986
+ lccDateList.push(lccDate);
987
+ }
988
+ if (Number.isFinite(lcDate)) {
989
+ if (!lccDatesByLcYear[lcDate]) {
990
+ lccDatesByLcYear[lcDate] = [];
991
+ }
992
+ if (Number.isFinite(lccDate)) {
993
+ lccDatesByLcYear[lcDate].push(lccDate);
994
+ }
995
+ }
996
+ });
997
+ }
998
+
999
+ const dynamicYearValues = Object.keys(keyMapInfoObj)
1000
+ .filter((key) => /^year\d+$/.test(key))
1001
+ .map((key) => Number(keyMapInfoObj[key]))
1002
+ .filter((year) => Number.isFinite(year));
1003
+ if (dynamicYearValues.length) {
1004
+ lccDateList = lccDateList.concat(dynamicYearValues);
1005
+ }
1006
+
1007
+ if (!lccDateList.length && Number.isFinite(Number(selectedNode.lcc_year))) {
1008
+ lccDateList = [Number(selectedNode.lcc_year)];
1009
+ }
1010
+
1011
+ lccDateList = Array.from(new Set(lccDateList)).sort((a, b) => a - b);
1012
+ Object.keys(lccDatesByLcYear).forEach((lcDateKey) => {
1013
+ lccDatesByLcYear[lcDateKey] = Array.from(
1014
+ new Set(lccDatesByLcYear[lcDateKey]),
1015
+ ).sort((a, b) => a - b);
1016
+ });
1017
+
1018
+ const selectedLcYear = Number(this.state.lcYear);
1019
+ const mappedLccDateList = Number.isFinite(selectedLcYear)
1020
+ ? lccDatesByLcYear[selectedLcYear]
1021
+ : null;
1022
+ const lccOptionsToUse =
1023
+ mappedLccDateList && mappedLccDateList.length
1024
+ ? mappedLccDateList
1025
+ : lccDateList;
1026
+
1027
+ this.removeOptions(selectBoxLccTime);
1028
+ selectBoxLccTime.options.add(
1029
+ new Option('Select a year', 'default', true, true),
1030
+ );
1031
+ selectBoxLccTime.options[0].disabled = true;
1032
+ lccOptionsToUse.forEach((element) => {
1033
+ selectBoxLccTime.options.add(new Option(element, element, element));
1034
+ });
1035
+ }
1036
+
843
1037
  renderLandCoverChange() {
844
1038
  return (
845
1039
  <div className="measurement-dropdown" id="LandCoverChangeDropdown">
@@ -955,7 +1149,11 @@ class HotspotWidget extends React.Component {
955
1149
  <select
956
1150
  onBlur={() => {}}
957
1151
  onChange={(e) => {
958
- this.setState({ selectedArea: e.target.value });
1152
+ this.setState({
1153
+ selectedArea: e.target.value,
1154
+ lcYear: null,
1155
+ lccYear: null,
1156
+ });
959
1157
  }}
960
1158
  id="select-klc-area"
961
1159
  className="esri-select"
@@ -999,6 +1197,8 @@ class HotspotWidget extends React.Component {
999
1197
  });
1000
1198
  this.layerModelInit();
1001
1199
  this.getBBoxData();
1200
+ this.getKLCNames(this.dataJSONNames, this.state.selectedArea);
1201
+ this.disableButton();
1002
1202
  this.arcgisEventHandles = [];
1003
1203
  this.props.view.when(() => {
1004
1204
  const handle = this.props.view.map.layers.on('change', () => {
@@ -1070,11 +1270,54 @@ class HotspotWidget extends React.Component {
1070
1270
  }
1071
1271
  }
1072
1272
 
1073
- componentDidUpdate(prevState, prevProps) {
1074
- if (prevProps.hotspotData !== this.props.hotspotData) {
1273
+ getHotspotLayerSignature(hotspotData) {
1274
+ if (!hotspotData || typeof hotspotData !== 'object') {
1275
+ return '';
1276
+ }
1277
+ const activeLayers = hotspotData.activeLayers;
1278
+ if (!activeLayers || typeof activeLayers !== 'object') {
1279
+ return '';
1280
+ }
1281
+ return Object.keys(activeLayers)
1282
+ .filter(
1283
+ (key) => key.includes('all_present_lc_') || key.includes('all_lcc_'),
1284
+ )
1285
+ .sort()
1286
+ .join('|');
1287
+ }
1288
+
1289
+ componentDidUpdate(prevProps, prevState) {
1290
+ const prevHotspotLayerSignature = this.getHotspotLayerSignature(
1291
+ prevProps.hotspotData,
1292
+ );
1293
+ const nextHotspotLayerSignature = this.getHotspotLayerSignature(
1294
+ this.props.hotspotData,
1295
+ );
1296
+
1297
+ if (
1298
+ prevHotspotLayerSignature !== nextHotspotLayerSignature &&
1299
+ (this.state.selectedArea !== null ||
1300
+ this.state.lcYear !== null ||
1301
+ this.state.lccYear !== null)
1302
+ ) {
1303
+ this.setState({
1304
+ selectedArea: null,
1305
+ lcYear: null,
1306
+ lccYear: null,
1307
+ });
1308
+ return;
1309
+ }
1310
+
1311
+ if (
1312
+ prevProps.hotspotData !== this.props.hotspotData ||
1313
+ prevState.selectedArea !== this.state.selectedArea
1314
+ ) {
1075
1315
  this.getKLCNames(this.dataJSONNames, this.state.selectedArea);
1076
1316
  this.disableButton();
1077
1317
  }
1318
+ if (prevState.lcYear !== this.state.lcYear) {
1319
+ this.disableButton();
1320
+ }
1078
1321
  }
1079
1322
  }
1080
1323
  export default HotspotWidget;
@@ -1512,7 +1512,7 @@ class MapViewer extends React.Component {
1512
1512
  let toc_panel_scrolls = sessionStorage.getItem('toc_panel_scrolls');
1513
1513
  let checkedLayers = sessionStorage.getItem('checkedLayers');
1514
1514
  if (!sessionStorage.getItem('TMSLayerObj')) {
1515
- sessionStorage.clear();
1515
+ //sessionStorage.clear();
1516
1516
  }
1517
1517
  sessionStorage.setItem('toc_panel_scrolls', toc_panel_scrolls);
1518
1518
  sessionStorage.setItem('checkedLayers', checkedLayers);
@@ -5738,18 +5738,25 @@ class MenuWidget extends React.Component {
5738
5738
  'expandedDropdowns',
5739
5739
  JSON.stringify(expandedDropdowns),
5740
5740
  );
5741
- const userKey = this.userID ? 'user_' + this.userID : 'user_anonymous';
5742
- const existing = localStorage.getItem(userKey);
5743
- let storeObj = {};
5744
- if (existing) {
5745
- try {
5746
- storeObj = JSON.parse(existing) || {};
5747
- } catch (e) {
5748
- storeObj = {};
5741
+ if (
5742
+ !this.location.search.includes('product=') &&
5743
+ !this.location.search.includes('dataset=')
5744
+ ) {
5745
+ const userKey = this.userID
5746
+ ? 'user_' + this.userID
5747
+ : 'user_anonymous';
5748
+ const existing = localStorage.getItem(userKey);
5749
+ let storeObj = {};
5750
+ if (existing) {
5751
+ try {
5752
+ storeObj = JSON.parse(existing) || {};
5753
+ } catch (e) {
5754
+ storeObj = {};
5755
+ }
5749
5756
  }
5757
+ storeObj.expandedDropdowns = expandedDropdowns;
5758
+ localStorage.setItem(userKey, JSON.stringify(storeObj));
5750
5759
  }
5751
- storeObj.expandedDropdowns = expandedDropdowns;
5752
- localStorage.setItem(userKey, JSON.stringify(storeObj));
5753
5760
  }
5754
5761
  } catch (e) {}
5755
5762
  }
@@ -7000,14 +7007,25 @@ class MenuWidget extends React.Component {
7000
7007
  layersReorder() {
7001
7008
  let activeLayers = document.querySelectorAll('.active-layer');
7002
7009
  let counter = activeLayers.length - 1;
7003
- let filterLayer;
7010
+ const hotspotData = this.props.hotspotData || {};
7011
+ const hotspotFilteredLayers =
7012
+ hotspotData.filteredLayers &&
7013
+ typeof hotspotData.filteredLayers === 'object'
7014
+ ? hotspotData.filteredLayers
7015
+ : null;
7016
+ const hotspotActiveLayers =
7017
+ hotspotData.activeLayers && typeof hotspotData.activeLayers === 'object'
7018
+ ? hotspotData.activeLayers
7019
+ : null;
7020
+
7004
7021
  activeLayers.forEach((item, index) => {
7022
+ let filterLayer;
7005
7023
  if (
7006
- this.props.hotspotData &&
7007
- this.props.hotspotData.filteredLayers &&
7008
- Object.keys(this.props.hotspotData.filteredLayers).length > 0
7024
+ hotspotFilteredLayers &&
7025
+ Object.keys(hotspotFilteredLayers).length > 0 &&
7026
+ hotspotActiveLayers
7009
7027
  ) {
7010
- Object.keys(this.props.hotspotData.activeLayers).forEach((key) => {
7028
+ Object.keys(hotspotActiveLayers).forEach((key) => {
7011
7029
  if (item.id.includes(key)) {
7012
7030
  if (key.includes('all_lcc')) {
7013
7031
  filterLayer = this.layers['lcc_filter'];
@@ -7015,12 +7033,11 @@ class MenuWidget extends React.Component {
7015
7033
  filterLayer = this.layers['lc_filter'];
7016
7034
  }
7017
7035
  }
7018
- this.map.reorder(filterLayer, index);
7019
7036
  });
7020
7037
  }
7021
7038
  let order = counter - index;
7022
7039
  item.setAttribute('layer-order', order);
7023
- if (filterLayer === undefined) {
7040
+ if (filterLayer === undefined || filterLayer === null) {
7024
7041
  this.layerReorder(this.layers[item.getAttribute('layer-id')], order);
7025
7042
  } else {
7026
7043
  this.layerReorder(filterLayer, order);
@@ -7049,6 +7066,7 @@ class MenuWidget extends React.Component {
7049
7066
  * @param {*} index
7050
7067
  */
7051
7068
  layerReorder(layer, index) {
7069
+ if (!layer || !this.map) return;
7052
7070
  this.map.reorder(layer, index);
7053
7071
  }
7054
7072