@eeacms/volto-arcgis-block 0.1.429 → 0.1.431

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,10 +4,26 @@ 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.431](https://github.com/eea/volto-arcgis-block/compare/0.1.430...0.1.431) - 3 March 2026
8
+
9
+ #### :house: Internal changes
10
+
11
+ - chore: [JENKINSFILE] add package version in sonarqube [valentinab25 - [`78addd9`](https://github.com/eea/volto-arcgis-block/commit/78addd9908a5a16eb8b8c984b6d98fe992af4449)]
12
+ - chore: [JENKINSFILE] add package version in sonarqube [EEA Jenkins - [`3001a9d`](https://github.com/eea/volto-arcgis-block/commit/3001a9d84c1ceb2072d2c8fdf42b75b0fa2a36c7)]
13
+ - chore: [JENKINSFILE] add sonarqube branches Refs #293878 [EEA Jenkins - [`3430596`](https://github.com/eea/volto-arcgis-block/commit/34305966fc548b9213d2a914cdd61fdb4ef3a973)]
14
+
15
+ ### [0.1.430](https://github.com/eea/volto-arcgis-block/compare/0.1.429...0.1.430) - 19 February 2026
16
+
17
+ #### :hammer_and_wrench: Others
18
+
19
+ - Merge pull request #1106 from eea/develop [Unai Bolivar - [`d05af07`](https://github.com/eea/volto-arcgis-block/commit/d05af073c82e9137010ce0b2555a9405989e300f)]
20
+ - (bug): WFS fix [Unai Bolivar - [`dd08e60`](https://github.com/eea/volto-arcgis-block/commit/dd08e60240a885b9e611b6e096d413027a4d2b2c)]
21
+ - (bug): shapefileLimit error popup [Unai Bolivar - [`70d95a5`](https://github.com/eea/volto-arcgis-block/commit/70d95a5d58feed47bf027a0a213eb892a6d1a661)]
7
22
  ### [0.1.429](https://github.com/eea/volto-arcgis-block/compare/0.1.428...0.1.429) - 17 February 2026
8
23
 
9
24
  #### :hammer_and_wrench: Others
10
25
 
26
+ - Merge pull request #1104 from eea/develop [Unai Bolivar - [`b5afb9b`](https://github.com/eea/volto-arcgis-block/commit/b5afb9bb2ca6486c1d0b72c87caebf4acc630e07)]
11
27
  - (bug): styles for buttons in upload widget and error popup message for file size limit on hspae files [Unai Bolivar - [`d9ca59e`](https://github.com/eea/volto-arcgis-block/commit/d9ca59ec245fdc94a2d1bf678b6e3d610d5a792d)]
12
28
  - (bug): reset upload widget to allow users to start from zero without finishing a process first [Unai Bolivar - [`1539a3a`](https://github.com/eea/volto-arcgis-block/commit/1539a3ada8fef0fbda5203b61ec2aed60ab014b5)]
13
29
  - (bug): truncate title to 33 max char and add ellipsis for user created services and move trashcan icon to the right edge of the menu item div [Unai Bolivar - [`a31612d`](https://github.com/eea/volto-arcgis-block/commit/a31612df1ee629937e115b1fec1c50092b659626)]
package/Jenkinsfile CHANGED
@@ -1,7 +1,4 @@
1
1
  pipeline {
2
- tools {
3
- jdk 'Java17'
4
- }
5
2
  agent {
6
3
  node { label 'docker-host' }
7
4
  }
@@ -240,11 +237,17 @@ pipeline {
240
237
  // script {
241
238
  // def scannerHome = tool 'SonarQubeScanner'
242
239
  // def nodeJS = tool 'NodeJS'
240
+ // if (env.CHANGE_ID) {
241
+ // env.sonarParams = " -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.key=${env.CHANGE_ID} "
242
+ // }
243
+ // else {
244
+ // env.sonarParams = " -Dsonar.branch.name=${env.BRANCH_NAME}"
245
+ // }
243
246
  // withSonarQubeEnv('Sonarqube') {
244
247
  // sh '''sed -i "s#/app/src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
245
248
  // sh '''sed -i "s#src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
246
- // sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME-$BRANCH_NAME -Dsonar.projectVersion=$BRANCH_NAME-$BUILD_NUMBER"
247
- // sh '''try=5; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}-${BRANCH_NAME}&tags=${SONARQUBE_TAGS},${BRANCH_NAME}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 15; try=\$(( \$try - 1 )); fi; done'''
249
+ // sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME -Dsonar.projectName=$GIT_NAME -Dsonar.projectVersion=\$(jq -r '.version' package.json) ${env.sonarParams}"
250
+ // sh '''try=5; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}&tags=${SONARQUBE_TAGS}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 15; try=\$(( \$try - 1 )); fi; done'''
248
251
  // }
249
252
  // }
250
253
  // }
@@ -268,7 +271,7 @@ pipeline {
268
271
  // script {
269
272
  // sh '''echo "Error" > checkresult.txt'''
270
273
  // catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
271
- // sh '''set -o pipefail; docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemaster.sh | grep -v "Found script" | tee checkresult.txt'''
274
+ // sh '''set -o pipefail; docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemasterV2.sh | grep -v "Found script" | tee checkresult.txt'''
272
275
  // }
273
276
 
274
277
  // publishChecks name: 'SonarQube', title: 'Sonarqube Code Quality Check', summary: 'Quality check on the SonarQube metrics from branch develop, comparing it with the ones from master branch. No bugs are allowed',
package/README.md CHANGED
@@ -3,16 +3,16 @@
3
3
  [![Releases](https://img.shields.io/github/v/release/eea/volto-arcgis-block)](https://github.com/eea/volto-arcgis-block/releases)
4
4
 
5
5
  [![Pipeline](https://ci.eionet.europa.eu/buildStatus/icon?job=volto-addons%2Fvolto-arcgis-block%2Fmaster&subject=master)](https://ci.eionet.europa.eu/view/Github/job/volto-addons/job/volto-arcgis-block/job/master/display/redirect)
6
- [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-master&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-master)
7
- [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-master&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-master)
8
- [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-master&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-master)
9
- [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-master&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-master)
6
+ [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block)
7
+ [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block)
8
+ [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block)
9
+ [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block)
10
10
 
11
11
  [![Pipeline](https://ci.eionet.europa.eu/buildStatus/icon?job=volto-addons%2Fvolto-arcgis-block%2Fdevelop&subject=develop)](https://ci.eionet.europa.eu/view/Github/job/volto-addons/job/volto-arcgis-block/job/develop/display/redirect)
12
- [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-develop&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-develop)
13
- [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-develop&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-develop)
14
- [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-develop&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-develop)
15
- [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block-develop&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block-develop)
12
+ [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&branch=develop&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block&branch=develop)
13
+ [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&branch=develop&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block&branch=develop)
14
+ [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&branch=develop&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block&branch=develop)
15
+ [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-arcgis-block&branch=develop&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-arcgis-block&branch=develop)
16
16
 
17
17
  ## ArcGIS Map integration for Volto blocks
18
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.429",
3
+ "version": "0.1.431",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -15,7 +15,9 @@ var Graphic,
15
15
  SimpleLineSymbol,
16
16
  SimpleFillSymbol,
17
17
  SpatialReference,
18
- Polygon;
18
+ Polygon,
19
+ TextSymbol,
20
+ Point;
19
21
 
20
22
  class AreaWidget extends React.Component {
21
23
  /**
@@ -40,6 +42,12 @@ class AreaWidget extends React.Component {
40
42
  east: '',
41
43
  west: '',
42
44
  },
45
+ areaCoordinates: {
46
+ originLat: '',
47
+ originLon: '',
48
+ cursorLat: '',
49
+ cursorLon: '',
50
+ },
43
51
  };
44
52
  this.menuClass =
45
53
  'esri-icon-cursor-marquee esri-widget--button esri-widget esri-interactive';
@@ -74,6 +82,8 @@ class AreaWidget extends React.Component {
74
82
  'esri/symbols/SimpleFillSymbol',
75
83
  'esri/geometry/SpatialReference',
76
84
  'esri/geometry/Polygon',
85
+ 'esri/symbols/TextSymbol',
86
+ 'esri/geometry/Point',
77
87
  ]).then(
78
88
  ([
79
89
  _Graphic,
@@ -89,6 +99,8 @@ class AreaWidget extends React.Component {
89
99
  _SimpleFillSymbol,
90
100
  _SpatialReference,
91
101
  _Polygon,
102
+ _TextSymbol,
103
+ _Point,
92
104
  ]) => {
93
105
  [
94
106
  Graphic,
@@ -104,6 +116,8 @@ class AreaWidget extends React.Component {
104
116
  SimpleFillSymbol,
105
117
  SpatialReference,
106
118
  Polygon,
119
+ TextSymbol,
120
+ Point,
107
121
  ] = [
108
122
  _Graphic,
109
123
  _Extent,
@@ -118,6 +132,8 @@ class AreaWidget extends React.Component {
118
132
  _SimpleFillSymbol,
119
133
  _SpatialReference,
120
134
  _Polygon,
135
+ _TextSymbol,
136
+ _Point,
121
137
  ];
122
138
  },
123
139
  );
@@ -922,12 +938,35 @@ class AreaWidget extends React.Component {
922
938
 
923
939
  let extentGraphic = null;
924
940
  let origin = null;
941
+ let originGraphic = null;
942
+ let cursorGraphic = null;
925
943
  const drawGraphics = this.props.view.on('drag', (e) => {
926
944
  if (this.props.mapViewer.pan_enabled) return;
927
945
  e.stopPropagation();
928
946
  if (e.action === 'start') {
929
947
  if (extentGraphic) this.props.view.graphics.remove(extentGraphic);
948
+ if (originGraphic) this.props.view.graphics.remove(originGraphic);
930
949
  origin = this.props.view.toMap(e);
950
+ this.setState({
951
+ areaCoordinates: {
952
+ originLat: origin.latitude.toFixed(3),
953
+ originLon: origin.longitude.toFixed(3),
954
+ },
955
+ });
956
+ let originTextSymbol = new TextSymbol(
957
+ this.state.areaCoordinates.originLon +
958
+ ' , ' +
959
+ this.state.areaCoordinates.originLat,
960
+ );
961
+ originTextSymbol.horizontalAlignment = 'right';
962
+ originTextSymbol.verticalAlignment = 'bottom';
963
+ originTextSymbol.font.size = 8;
964
+ var point = new Point(
965
+ this.state.areaCoordinates.originLon,
966
+ this.state.areaCoordinates.originLat,
967
+ );
968
+ originGraphic = new Graphic(point, originTextSymbol);
969
+ this.props.view.graphics.add(originGraphic);
931
970
  if (extentGraphic && this.checkExtent(extentGraphic.geometry)) {
932
971
  this.setState({
933
972
  showInfoPopup: true,
@@ -963,7 +1002,16 @@ class AreaWidget extends React.Component {
963
1002
  }
964
1003
  } else if (e.action === 'update') {
965
1004
  if (extentGraphic) this.props.view.graphics.remove(extentGraphic);
1005
+ if (cursorGraphic) this.props.view.graphics.remove(cursorGraphic);
966
1006
  let p = this.props.view.toMap(e);
1007
+ this.setState({
1008
+ areaCoordinates: {
1009
+ originLat: this.state.areaCoordinates.originLat,
1010
+ originLon: this.state.areaCoordinates.originLon,
1011
+ cursorLat: p.latitude.toFixed(3),
1012
+ cursorLon: p.longitude.toFixed(3),
1013
+ },
1014
+ });
967
1015
  extentGraphic = new Graphic({
968
1016
  geometry: new Extent({
969
1017
  xmin: Math.min(p.x, origin.x),
@@ -1012,6 +1060,20 @@ class AreaWidget extends React.Component {
1012
1060
  end: { x: p.longitude, y: p.latitude },
1013
1061
  });
1014
1062
  this.props.view.graphics.add(extentGraphic);
1063
+ let cursorTextSymbol = new TextSymbol(
1064
+ this.state.areaCoordinates.cursorLon +
1065
+ ' , ' +
1066
+ this.state.areaCoordinates.cursorLat,
1067
+ );
1068
+ cursorTextSymbol.horizontalAlignment = 'left';
1069
+ cursorTextSymbol.verticalAlignment = 'top';
1070
+ cursorTextSymbol.font.size = 8;
1071
+ var point2 = new Point(
1072
+ this.state.areaCoordinates.cursorLon,
1073
+ this.state.areaCoordinates.cursorLat,
1074
+ );
1075
+ cursorGraphic = new Graphic(point2, cursorTextSymbol);
1076
+ this.props.view.graphics.add(cursorGraphic);
1015
1077
  }
1016
1078
  });
1017
1079
  this.setState({
@@ -2474,6 +2474,17 @@ class MenuWidget extends React.Component {
2474
2474
  const versionList = Array.from(versionSet);
2475
2475
  const formatList = Array.from(formatSet);
2476
2476
  const srsList = Array.from(srsSet);
2477
+ const normalizedSrsList = srsList
2478
+ .map((srsValue) => {
2479
+ const textValue = (srsValue || '').trim();
2480
+ const epsgMatch = textValue.match(/EPSG(?::|::|\/)(\d+)/i);
2481
+ if (epsgMatch && epsgMatch[1]) {
2482
+ return `EPSG:${epsgMatch[1]}`;
2483
+ }
2484
+ const normalizedSrsValue = textValue.toUpperCase();
2485
+ return normalizedSrsValue || null;
2486
+ })
2487
+ .filter(Boolean);
2477
2488
  const requestVersion = versionList.includes('2.0.0')
2478
2489
  ? '2.0.0'
2479
2490
  : versionList.includes('1.1.0')
@@ -2487,9 +2498,9 @@ class MenuWidget extends React.Component {
2487
2498
  const preferredXmlFormat = formatList.find((item) =>
2488
2499
  /text\/xml|xml|gml/i.test(item),
2489
2500
  );
2490
- const requestSrsName = srsList.includes('EPSG:4326')
2501
+ const requestSrsName = normalizedSrsList.includes('EPSG:4326')
2491
2502
  ? 'EPSG:4326'
2492
- : srsList[0] || 'EPSG:4326';
2503
+ : normalizedSrsList[0] || 'EPSG:4326';
2493
2504
  return {
2494
2505
  requestVersion,
2495
2506
  requestFormat: preferredJsonFormat || preferredXmlFormat || null,
@@ -2581,6 +2592,69 @@ class MenuWidget extends React.Component {
2581
2592
  );
2582
2593
  }
2583
2594
 
2595
+ resolveResponseData(responseData) {
2596
+ const resolveJsonData = (dataValue) => {
2597
+ if (!dataValue || typeof dataValue !== 'object') {
2598
+ return null;
2599
+ }
2600
+ if (
2601
+ dataValue.type === 'FeatureCollection' &&
2602
+ Array.isArray(dataValue.features)
2603
+ ) {
2604
+ return {
2605
+ ...dataValue,
2606
+ type: 'FeatureCollection',
2607
+ features: dataValue.features,
2608
+ };
2609
+ }
2610
+ if (Array.isArray(dataValue.features)) {
2611
+ return {
2612
+ ...dataValue,
2613
+ type: 'FeatureCollection',
2614
+ features: dataValue.features,
2615
+ };
2616
+ }
2617
+ if (dataValue.type === 'Feature' && dataValue.geometry) {
2618
+ return {
2619
+ type: 'FeatureCollection',
2620
+ features: [dataValue],
2621
+ };
2622
+ }
2623
+ return null;
2624
+ };
2625
+
2626
+ if (typeof responseData === 'string') {
2627
+ const textValue = responseData.trim();
2628
+ if (!textValue) {
2629
+ return null;
2630
+ }
2631
+ if (this.hasExceptionResponse(textValue)) {
2632
+ const exceptionMessage = this.resolveExceptionMessage(textValue);
2633
+ throw new Error(exceptionMessage || 'WFS request failed');
2634
+ }
2635
+ let parsedData = null;
2636
+ try {
2637
+ parsedData = JSON.parse(textValue);
2638
+ } catch (e) {}
2639
+ const jsonData = resolveJsonData(parsedData);
2640
+ if (jsonData) {
2641
+ return jsonData;
2642
+ }
2643
+ const xmlData = this.processXmlData(textValue);
2644
+ return this.resolveFeatureCollection(xmlData);
2645
+ }
2646
+
2647
+ return resolveJsonData(responseData);
2648
+ }
2649
+
2650
+ hasResultData(resultData) {
2651
+ return Boolean(
2652
+ resultData &&
2653
+ Array.isArray(resultData.features) &&
2654
+ resultData.features.length > 0,
2655
+ );
2656
+ }
2657
+
2584
2658
  processXmlData(xmlInput) {
2585
2659
  let doc = xmlInput;
2586
2660
  if (typeof xmlInput === 'string') {
@@ -2993,34 +3067,16 @@ class MenuWidget extends React.Component {
2993
3067
  const requestParams = this.buildRequestParams(name, requestConfig);
2994
3068
  if (requestConfig.hasJsonFormat && requestConfig.requestFormat) {
2995
3069
  requestParams.outputFormat = requestConfig.requestFormat;
3070
+ requestParams.outputformat = requestConfig.requestFormat;
2996
3071
  }
2997
3072
  const requestQuery = new URLSearchParams(requestParams).toString();
2998
3073
  const requestUrl = baseUrl + '?' + requestQuery;
2999
3074
 
3000
3075
  const id = (name || baseUrl).toUpperCase().replace(/[: ]/g, '_');
3001
- if (requestConfig.hasJsonFormat) {
3002
- const layer = new GeoJSONLayer({
3003
- url: requestUrl,
3004
- id: id,
3005
- title: title || name,
3006
- });
3007
- layer.LayerId = id;
3008
- layer.ViewService = baseUrl;
3009
- layer.name = name;
3010
- return layer;
3011
- }
3012
-
3013
- const xmlResponse = await esriRequest(requestUrl, {
3076
+ const response = await esriRequest(requestUrl, {
3014
3077
  responseType: 'text',
3015
3078
  });
3016
- if (this.hasExceptionResponse(xmlResponse.data)) {
3017
- const exceptionMessage = this.resolveExceptionMessage(
3018
- xmlResponse.data,
3019
- );
3020
- throw new Error(exceptionMessage || 'WFS request failed');
3021
- }
3022
- const xmlData = this.processXmlData(xmlResponse.data);
3023
- const featureCollection = this.resolveFeatureCollection(xmlData);
3079
+ const featureCollection = this.resolveResponseData(response.data);
3024
3080
  if (
3025
3081
  featureCollection &&
3026
3082
  featureCollection.memberCount > 0 &&
@@ -3031,7 +3087,7 @@ class MenuWidget extends React.Component {
3031
3087
  'Selected WFS feature type has no geometry and cannot be displayed on map',
3032
3088
  );
3033
3089
  }
3034
- if (!featureCollection || !featureCollection.features?.length) {
3090
+ if (!this.hasResultData(featureCollection)) {
3035
3091
  throw new Error('No WFS features were returned for this layer');
3036
3092
  }
3037
3093
  const blobData = new Blob([JSON.stringify(featureCollection)], {
@@ -4,6 +4,7 @@ import { loadModules } from 'esri-loader';
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
5
 
6
6
  var WMSLayer, WMTSLayer, WFSLayer, esriRequest;
7
+ const SHAPEFILE_MAX_SIZE_BYTES = 2097152;
7
8
 
8
9
  class UploadWidget extends React.Component {
9
10
  /**
@@ -193,18 +194,39 @@ class UploadWidget extends React.Component {
193
194
  if (!file) {
194
195
  return;
195
196
  }
196
- const name = (file.name || '').toLowerCase();
197
- const ext = name.split('.').pop();
198
- if (ext !== 'zip') {
199
- this.setState({ showInfoPopup: true, infoPopupType: 'fileUploadError' });
197
+ this.processInputPayload(file);
198
+ e.target.value = null;
199
+ }
200
+
201
+ resolveInputError(filePayload) {
202
+ const inputName = ((filePayload && filePayload.name) || '').toLowerCase();
203
+ const inputExtension = inputName.split('.').pop();
204
+ if (inputExtension !== 'zip') {
205
+ return 'fileUploadError';
206
+ }
207
+ const inputSize = (filePayload && filePayload.size) || 0;
208
+ if (inputSize > SHAPEFILE_MAX_SIZE_BYTES) {
209
+ return 'shapefileLimit';
210
+ }
211
+ return null;
212
+ }
213
+
214
+ processInputPayload(filePayload) {
215
+ const inputError = this.resolveInputError(filePayload);
216
+ if (inputError) {
217
+ this.setState({
218
+ showInfoPopup: true,
219
+ infoPopupType: inputError,
220
+ selectedFile: null,
221
+ globalDragActive: false,
222
+ });
200
223
  if (typeof this.uploadFileErrorHandler === 'function') {
201
- this.uploadFileErrorHandler();
224
+ this.uploadFileErrorHandler(inputError);
202
225
  }
203
- e.target.value = null;
204
- return;
226
+ return false;
205
227
  }
206
- this.setState({ selectedFile: file, globalDragActive: false });
207
- e.target.value = null;
228
+ this.setState({ selectedFile: filePayload, globalDragActive: false });
229
+ return true;
208
230
  }
209
231
 
210
232
  getNormalizedUrlForType = (serviceUrl, serviceType) => {
@@ -259,14 +281,13 @@ class UploadWidget extends React.Component {
259
281
  ) {
260
282
  return queryService;
261
283
  }
262
- const encodedUrl = (
263
- (parsedUrl.hostname || '') + (parsedUrl.pathname || '')
264
- ).toLowerCase();
265
- const serviceMatch = encodedUrl.match(
266
- /(^|[^a-z])(wmts|wms|wfs)([^a-z]|$)/i,
284
+ const pathName = (parsedUrl.pathname || '').toLowerCase();
285
+ const pathSegments = pathName.split('/').filter(Boolean);
286
+ const serviceMatch = pathSegments.find((segment) =>
287
+ /^(wmts|wms|wfs)$/i.test(segment),
267
288
  );
268
- if (serviceMatch && serviceMatch[2]) {
269
- return serviceMatch[2].toUpperCase();
289
+ if (serviceMatch) {
290
+ return serviceMatch.toUpperCase();
270
291
  }
271
292
  return null;
272
293
  } catch (e) {
@@ -1095,25 +1116,28 @@ class UploadWidget extends React.Component {
1095
1116
  const file =
1096
1117
  e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
1097
1118
  if (!file) return;
1098
- const name = (file.name || '').toLowerCase();
1099
- const ext = name.split('.').pop();
1100
- if (ext !== 'zip') {
1119
+ this.processInputPayload(file);
1120
+ }
1121
+
1122
+ handleAddClick() {
1123
+ const file = this.state.selectedFile;
1124
+ if (!file) return;
1125
+ const inputError = this.resolveInputError(file);
1126
+ if (inputError) {
1101
1127
  this.setState({
1102
1128
  showInfoPopup: true,
1103
- infoPopupType: 'fileUploadError',
1129
+ infoPopupType: inputError,
1104
1130
  selectedFile: null,
1131
+ globalDragActive: false,
1105
1132
  });
1106
1133
  if (typeof this.uploadFileErrorHandler === 'function') {
1107
- this.uploadFileErrorHandler();
1134
+ this.uploadFileErrorHandler(inputError);
1135
+ }
1136
+ if (this.fileInput && this.fileInput.current) {
1137
+ this.fileInput.current.value = null;
1108
1138
  }
1109
1139
  return;
1110
1140
  }
1111
- this.setState({ selectedFile: file, globalDragActive: false });
1112
- }
1113
-
1114
- handleAddClick() {
1115
- const file = this.state.selectedFile;
1116
- if (!file) return;
1117
1141
  try {
1118
1142
  this.uploadUrlServiceHandler(file, 'FILE');
1119
1143
  } catch (err) {