@eeacms/volto-arcgis-block 0.1.476 → 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,13 @@ 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)]
7
14
  ### [0.1.476](https://github.com/eea/volto-arcgis-block/compare/0.1.475...0.1.476) - 31 August 2026
8
15
 
9
16
  ### [0.1.475](https://github.com/eea/volto-arcgis-block/compare/0.1.474...0.1.475) - 19 August 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.476",
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;
@@ -7007,14 +7007,25 @@ class MenuWidget extends React.Component {
7007
7007
  layersReorder() {
7008
7008
  let activeLayers = document.querySelectorAll('.active-layer');
7009
7009
  let counter = activeLayers.length - 1;
7010
- 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
+
7011
7021
  activeLayers.forEach((item, index) => {
7022
+ let filterLayer;
7012
7023
  if (
7013
- this.props.hotspotData &&
7014
- this.props.hotspotData.filteredLayers &&
7015
- Object.keys(this.props.hotspotData.filteredLayers).length > 0
7024
+ hotspotFilteredLayers &&
7025
+ Object.keys(hotspotFilteredLayers).length > 0 &&
7026
+ hotspotActiveLayers
7016
7027
  ) {
7017
- Object.keys(this.props.hotspotData.activeLayers).forEach((key) => {
7028
+ Object.keys(hotspotActiveLayers).forEach((key) => {
7018
7029
  if (item.id.includes(key)) {
7019
7030
  if (key.includes('all_lcc')) {
7020
7031
  filterLayer = this.layers['lcc_filter'];
@@ -7022,12 +7033,11 @@ class MenuWidget extends React.Component {
7022
7033
  filterLayer = this.layers['lc_filter'];
7023
7034
  }
7024
7035
  }
7025
- this.map.reorder(filterLayer, index);
7026
7036
  });
7027
7037
  }
7028
7038
  let order = counter - index;
7029
7039
  item.setAttribute('layer-order', order);
7030
- if (filterLayer === undefined) {
7040
+ if (filterLayer === undefined || filterLayer === null) {
7031
7041
  this.layerReorder(this.layers[item.getAttribute('layer-id')], order);
7032
7042
  } else {
7033
7043
  this.layerReorder(filterLayer, order);
@@ -7056,6 +7066,7 @@ class MenuWidget extends React.Component {
7056
7066
  * @param {*} index
7057
7067
  */
7058
7068
  layerReorder(layer, index) {
7069
+ if (!layer || !this.map) return;
7059
7070
  this.map.reorder(layer, index);
7060
7071
  }
7061
7072