diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index e71e7c3c65..a015fd3fa3 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -83,7 +83,7 @@ jobs: needs: build steps: - name: "Download war" - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: path: artifacts/ - name: Display structure of downloaded files diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 574f98dea3..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,225 +0,0 @@ -# Contributing to MapStore - - 1. [Getting Involved](#getting-involved) - 2. [Reporting Bugs](#reporting-bugs) - 3. [Contributing Code](#contributing-code) - 4. [Improving Documentation](#improving-documentation) - -## Getting Involved - -There are several ways you can contribute to MapStore development. -If you are a developer, you can provide new features and bug fixes, by using pull requests. -But you can also help by: - -- discovering and [reporting bugs](#reporting-bugs); -- improving [documentation](#improving-documentation); -- helping others on the [MapStore users mailing list](https://groups.google.com/d/forum/mapstore-users) or [MapStore developers mailing list](https://groups.google.com/d/forum/mapstore-developers) -- helping others on the [GitHub issues](https://github.com/geosolutions-it/MapStore2/issues). - -## Reporting Bugs - -Before reporting a bug on the project's [issues page](https://github.com/geosolutions-it/MapStore2/issues), -first make sure that your issue is caused by MapStore, not your application code -(e.g. passing incorrect arguments to methods, etc.). -Second, search the already reported issues for similar cases, -and if it's already reported, just add any additional details in the comments. - -After you've made sure that you've found a new MapStore bug, -here are some tips for creating a helpful report that will make fixing it much easier and quicker: - -- Write a **descriptive, specific title**. Bad: *Map does not show*. Good: *Doing X in Chrome causes Z*. -- Include **browser, OS and MapStore version** info in the description. -- Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/) or [JS Bin](http://jsbin.com/)). -- Check whether the bug can be reproduced in **other browsers**. -- Check if the bug occurs in the stable version, master, or both. -- *Bonus tip:* if the bug only appears in the master version but the stable version is fine, - use `git bisect` to find the exact commit that introduced the bug. - -If you just want some help with your project, -try asking [on the MapStore users mailing list](https://groups.google.com/d/forum/mapstore-users) instead. - -## Contributing Code - -### Considerations for Accepting Patches - -While we happily accept patches, we're also committed to quality. -So bugfixes, performance optimizations and small improvements that don't add a lot of code -are much more likely to get accepted quickly. - -Before sending a pull request with a new feature, check if it's been discussed before already -(either on [GitHub issues](https://github.com/geosolutions-it/MapStore2/issues) -or [on the MapStore developers mailing list](https://groups.google.com/d/forum/mapstore-developers)), -and ask yourself two questions: - - 1. Are you sure that this new feature is important enough to justify its presence in the MapStore core? - Or will it look better as a plugin in a separate repository? - 2. Is it written in a simple, concise way that doesn't add bulk to the codebase? - -If your feature did get merged into master, -please consider submitting another pull request with the corresponding [documentation update](#improving-documentation). - -### Making Changes to MapStore Source - -If you're not yet familiar with the way GitHub works (forking, pull requests, etc.), -be sure to check out the awesome [article about forking](https://help.github.com/articles/fork-a-repo) -on the GitHub Help website — it will get you started quickly. - -You should always write each batch of changes (feature, bugfix, etc.) in **its own topic branch**. -Please do not commit to the `master` branch, or your unrelated changes will go into the same pull request. - -### Pull request guidelines - -Your pull request must: - -- Follow MapStore's coding style. - -- Pass the integration tests run automatically by the Github Action - Continuous - Integration system. - -- Address a single issue or add a single item of functionality. (Start the pull request title with the addressed issues if in case and #NUMBER_OF_ISSUE) - -- Contain a clean history of small, incremental, logically separate commits, - with no merge commits. - -- Use clear commit messages. - -- Be possible to merge automatically. - -### The `test` and `lint` build targets - -It is strongly recommended that you run - - npm test - npm run lint - -before every commit. This will catch many problems quickly, and it is much -faster than waiting for the CI integration tests to run. - -The `test` build target runs a number of quick tests on your code. - -The `lint` build target runs ESLint checks on your code. - -### File Naming Conventions - -The test files should in a folder named `__tests__` in the module folder. - -If you are testing a specific component follow the following convention: - -- Component: `MyComponent.jsx` -- Test File: `MyComponent-test.jsx` - -### Follow MapStore's coding style - -MapStore follows a strict coding style, enforced by [ESLint](http://eslint.org/) rules. - -The set of used rules can be found in the ESLint config package[eslint-config-mapstore](https://github.com/geosolutions-it/MapStore2/blob/master/utility/eslint/index.js) file, deployed on npm and configured in the `package.json`. - -Additionally if you use VScode these are the extensions to install that will help notify lint errors on the fly - -- [error lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens) -- [ESlint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - -We suggest also to add the following configuration that will fix lint errors while saving the file (`.vscode/settings.json`) - - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - -You can run the linter locally on your machine before committing using the `lint` -target: - - npm run lint - -In addition, take care of adding the standard file header in each javascript / css added file, and update copyright years in modified ones. - -This is the standard file header: - -```javascript -/** - * Copyright , GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -``` - -### Configure your editor - -If possible, configure your editor to follow the coding conventions of the -library. A `.editorconfig` file is included at the root of the repository that -can be used to configure whitespace and charset handling in your editor. See -that file for a description of the conventions. The [EditorConfig]( -http://editorconfig.org/#download) site links to plugins for various editors. - -### Pass the integration tests run automatically by the CI system - -The integration tests contain a number of automated checks to ensure that the -code follows the MapStore style and does not break tests or examples. You -can run the integration tests locally using the `test` target: - - npm test - -### Address a single issue or add a single item of functionality - -Please submit separate pull requests for separate issues. This allows each to -be reviewed on its own merits. - -### Contain a clean history of small, incremental, logically separate commits, with no merge commits - -The commit history explains to the reviewer the series of modifications to the -code that you have made and breaks the overall contribution into a series of -easily-understandable chunks. Any individual commit should not add more than -one new class or one new function. Do not submit commits that change thousands -of lines or that contain more than one distinct logical change. Trivial -commits, e.g. to fix lint errors, should be merged into the commit that -introduced the error. See the [Atomic Commit Convention on Wikipedia](http://en.wikipedia.org/wiki/Atomic_commit#Atomic_Commit_Convention) for more detail. - -`git apply --patch` and `git rebase` can help you create a clean commit -history. -[Reviewboard.org](http://www.reviewboard.org/docs/codebase/dev/git/clean-commits/) -and [Pro GIT](http://git-scm.com/book/en/Git-Tools-Rewriting-History) have -explain how to use them. - -### Use clear commit messages - -Commit messages should be short, begin with a verb in the imperative, and -contain no trailing punctuation. We follow - -for the formatting of commit messages. - -Git commit message should look like: - - Header line: explaining the commit in one line - - Body of commit message is a few lines of text, explaining things - in more detail, possibly giving some background about the issue - being fixed, etc etc. - - The body of the commit message can be several paragraphs, and - please do proper word-wrap and keep columns shorter than about - 74 characters or so. That way "git log" will show things - nicely even when it's indented. - - Further paragraphs come after blank lines. - -Please keep the header line short, no more than 50 characters. - -### Be possible to merge automatically - -Occasionally other changes to `master` might mean that your pull request cannot -be merged automatically. In this case you may need to merge your branch with -more recent `master`, resolve any conflicts, and `git push` to update -your branch so that it can be merged automatically. -Please try to **not** rewrite the history of a pull request after the first review or it will be very hard for the reviewer to see if the required changes has been applied, expecially for big pull requests. - -## Improving Documentation - -All MapStore objects (i.e. components, plugins, actions, reducers etc) should be properly documented in their JS documentation, please follow the required **JSDoc** syntax looking at the online documentation available in the [JSDoc web site](http://usejsdoc.org/index.html) for more details. - -Thank you for every contribution that helps to keep updated the MapStore's documentation. - -## Thank You - -At the end, however you decide to contribute to the project, your help is very -welcome and we would like to thank you for doing it. diff --git a/README.md b/README.md index 8cb29ca2ee..5a9e2be176 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,32 @@ [![Master Documentation Status](https://readthedocs.org/projects/mapstore/badge/?version=latest)](https://docs.mapstore.geosolutionsgroup.com/en/latest/?badge=master) [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40mapstore2)](https://twitter.com/mapstore2) -MapStore is a framework to build *web mapping* applications using standard mapping libraries, such as OpenLayers and Leaflet. For more information check the MapStore documentation! +MapStore is an open-source web mapping framework that enables users to create, share, and embed maps and dashboards with ease, drawing from a broad range of geospatial data sources. Designed for flexibility and scalability, MapStore integrates seamlessly with **OpenLayers**, **Leaflet**, and **Cesium** for both **2D** and **3D** visualization, allowing users to explore maps in a dynamic, real-time environment. + +With built-in support for **OGC** **standards** (such as **WMS**, **WMTS**, **WFS**, **3DTiles** and **CSW**), MapStore caters to the needs of professional GIS users while maintaining an intuitive interface for casual users. It supports rich feature configurations like layer styling, spatial analysis tools, and collaborative editing, making it a robust solution for diverse industries—from urban planning to environmental monitoring. + +MapStore's architecture is designed for modularity and extensibility, allowing developers to integrate custom plugins or adapt it for specific use cases. Whether you need to create interactive maps for publication or sophisticated geospatial applications, MapStore2 provides a solid foundation for building powerful web mapping solutions. + +For more information check the MapStore documentation! + +## Documentation + +You can find more documentation about how to build, install or develop with MapStore on the documentation site. + +## License + +MapStore is Free and Open Source software, it is based on OpenLayers, Cesium, Leaflet and ReactJS, and is licensed under the Simplified BSD License. + +## Demo Instances + +We have the following instances: + +1. a DEV instance, which can be accessed here, where all the changes are deployed once they are published on the Master branch of our repo +2. a QA instance, which can be accessed here, that becomes active 1 week before any release, during the hardening phase, and deploys the release branch whenever a fix is pushed onto it. +3. a STABLE instance, which can be accessed here, that gets deployed on demand after each release. + +As a user you need to be aware of STABLE and DEV, QA is used internally before a release; for 1 Week it will diverge from STABLE as it is actually anticipating the next stable. +So, if you want to test latest features use DEV, if you are not that brave use STABLE. You might forget that QA exists unless you are parte of the developers team. ## Download @@ -78,25 +103,6 @@ Then you can access MapStore using the following URL (assuming the web container Use the default credentials (admin / admin) to login and start creating your maps! -## Documentation - -You can find more documentation about how to build, install or develop with MapStore on the documentation site. - -## License - -MapStore is Free and Open Source software, it is based on OpenLayers, Leaflet and ReactJS, and is licensed under the Simplified BSD License. - -## Demo Instances - -We have the following instances: - -1. a DEV instance, which can be accessed here, where all the changes are deployed once they are published on the Master branch of our repo -2. a QA instance, which can be accessed here, that becomes active 1 week before any release, during the hardening phase, and deploys the release branch whenever a fix is pushed onto it. -3. a STABLE instance, which can be accessed here, that gets deployed on demand after each release. - -As a user you need to be aware of STABLE and DEV, QA is used internally before a release; for 1 Week it will diverge from STABLE as it is actually anticipating the next stable. -So, if you want to test latest features use DEV, if you are not that brave use STABLE. You might forget that QA exists unless you are parte of the developers team. - ## Start developing your custom app Clone the repository: @@ -150,7 +156,7 @@ We welcome contributions in any form: - pull requests for documentation - funding for any combination of the above -For more information check this page. +For more information check [this](https://github.com/geosolutions-it/MapStore2/wiki/Contributing-to-MapStore) page. ## Who uses MapStore diff --git a/binary/bin-war/pom.xml b/binary/bin-war/pom.xml index f20271cf9c..5ecaa12fc4 100644 --- a/binary/bin-war/pom.xml +++ b/binary/bin-war/pom.xml @@ -3,7 +3,7 @@ it.geosolutions.mapstore mapstore-bin-war war - 1.8-SNAPSHOT + 1.9-SNAPSHOT MapStore 2 Release Module WAR Creates the war for the binary package, adding customization (e.g. h2 database) http://www.geo-solutions.it diff --git a/binary/pom.xml b/binary/pom.xml index 3f5deea1cb..d4352ab278 100644 --- a/binary/pom.xml +++ b/binary/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-root - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore mapstore-binary @@ -116,13 +115,11 @@ package - - - - - + + + + + @@ -135,8 +132,7 @@ - + diff --git a/build/createExtensionWebpackConfig.js b/build/createExtensionWebpackConfig.js index c83af1fb23..a68082de0b 100644 --- a/build/createExtensionWebpackConfig.js +++ b/build/createExtensionWebpackConfig.js @@ -90,6 +90,7 @@ module.exports = ({ prod = true, name, exposes, sharedLibrariesEager = true, ali ] }, output: { + hashFunction: "xxhash64", // needed for newer version of node (> version 16) publicPath, chunkFilename: 'assets/js/[name].[chunkhash:8].js', path: destination, diff --git a/docs/developer-guide/integrations/geoserver.md b/docs/developer-guide/integrations/geoserver.md index 7275624d75..9ef1d1743f 100644 --- a/docs/developer-guide/integrations/geoserver.md +++ b/docs/developer-guide/integrations/geoserver.md @@ -213,3 +213,5 @@ The advanced Download, activated when GeoServer provides the WPS service above, - [DDS/BIL Plugin](https://docs.geoserver.org/stable/en/user/community/dds/index.html): this plugin add to geoserver the possibility to publish raster data in DDS/BIL format (World Wind). This particular plugin is useful if we want to use a raster data as elevation model for MapStore. This elevation model will be used in 3D mode or with the mouse coordinates plugin (displaying the elevation of a point on the map, together with the coordinates). - [WPS longitudinal profile process](https://docs.geoserver.org/stable/en/user/community/wps-longitudinal-profile/index.html): this plugin added to geoserver gives the possibility to calculate an altitude profile on a given path and it is required to use the [*Longitudinal Profile*](https://docs.mapstore.geosolutionsgroup.com/en/latest/user-guide/longitudinal-profile/) plugin of MapStore. + +- [WFS FreeMarker Extension](https://docs.geoserver.org/stable/en/user/community/wfs-freemarker/index.html): this plugin add to GeoServer the possibility to generate an HTML output for a WFS GetFeature response. As for the WMS Identify settings, it is applicable in MapStore by selecting the HTML format in the [*Feature Info Form*](https://docs.mapstore.geosolutionsgroup.com/en/latest/user-guide/layer-settings/#feature-info-form) for the *Identify Tool*. diff --git a/docs/developer-guide/integrations/users/openId.md b/docs/developer-guide/integrations/users/openId.md index 425a3d8928..fe7f0c08a9 100644 --- a/docs/developer-guide/integrations/users/openId.md +++ b/docs/developer-guide/integrations/users/openId.md @@ -32,7 +32,7 @@ Moreover the keycloak provider allows to configure advanced features like the ** !!! note For the moment the generic `oidc` provider does not support multiple instances. This means that you can configure only **one** generic `oidc` provider. You can configure the specific providers (e.g., `google`, `keycloak`) in addition to the generic `oidc` provider, but not multiple instances of the generic `oidc` provider. So for instance you can configure a generic `oidc` provider (e.g. connected to Github) plus a `google` and a `keycloak` provider, but not two generic `oidc` providers. -### OpenID connect (generic) +### OpenID connect (experimental) In order to configure the generic OpenID provider with a service of your choice you have to: diff --git a/docs/developer-guide/local-config.md b/docs/developer-guide/local-config.md index 918a90cca2..acef252e2a 100644 --- a/docs/developer-guide/local-config.md +++ b/docs/developer-guide/local-config.md @@ -73,7 +73,9 @@ This is the main structure: // Use POST requests for each WMS length URL highter than this value. "maxURLLength": 5000, // Custom path to home page - "homePath": '/home' + "homePath": '/home', + // If true it enables interactive legend for GeoServer WMS layers + "experimentalInteractiveLegend": true }, // optional state initializer (it will override the one defined in appConfig.js) "initialState": { diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md index 8cfcd3fd04..cbaf86c834 100644 --- a/docs/developer-guide/mapstore-migration-guide.md +++ b/docs/developer-guide/mapstore-migration-guide.md @@ -29,6 +29,51 @@ Please update your Node version accordingly on your develop machine or CI/CD. See the [requirements](./requirements.md#debug-build) section of the documentation for the details. +The same for projects of derived `MapStoreExtensions`. Make sure to: + +- Update `package.json` with the new versions of webpack and other dependencies. +- Remove and regenerate your `package-lock.json` with `npm install` after updating the Node version. + +Here the diff used for MapStore Extensions to update the `package.json` file: + +```diff +diff --git a/package.json b/package.json +index 62ddda0..62ce070 100644 +--- a/package.json ++++ b/package.json +@@ -44,6 +44,7 @@ + "@geosolutions/jsdoc": "3.4.4", + "@geosolutions/mocha": "6.2.1-3", + "@mapstore/eslint-config-mapstore": "1.0.5", ++ "@testing-library/react": "12.1.5", + "axios-mock-adapter": "1.16.0", + "babel-loader": "8.0.5", + "babel-plugin-add-module-exports": "0.1.4", +@@ -66,8 +67,8 @@ + "expect": "1.20.1", + "file-loader": "2.0.0", + "glob": "7.1.1", +- "html-loader": "0.5.1", +- "html-webpack-plugin": "4.5.0", ++ "html-loader": "2.0.0", ++ "html-webpack-plugin": "5.2.0", + "jsdoc-jsx": "0.1.0", + "karma": "6.4.0", + "karma-chrome-launcher": "3.1.1", +@@ -104,9 +105,10 @@ + "rimraf": "2.5.2", + "simple-git": "2.20.1", + "style-loader": "2.0.0", ++ "terser": "5.18.1", + "url-loader": "0.5.7", + "vusion-webfonts-generator": "0.4.1", +- "webpack": "5.9.0", ++ "webpack": "5.54.0", + "webpack-bundle-size-analyzer": "2.0.2", + "webpack-cli": "4.10.0", + "webpack-dev-server": "3.11.0", +``` + ### Java dependencies update Some libraries has been updated. if you have a MapStore project make sure to keep the versions aligned with the main product. diff --git a/docs/user-guide/catalog.md b/docs/user-guide/catalog.md index 4b206fc9d1..c4f9d65ce0 100644 --- a/docs/user-guide/catalog.md +++ b/docs/user-guide/catalog.md @@ -95,6 +95,8 @@ In **general settings of** CSW service the user can specify the title to assign !!! note If the **No Vendor** is set, then [MapStore](https://mapstore.geosolutionsgroup.com/mapstore/#/) will not use any vendor option supported only by GeoServer in the OGC requests where this source is involved. +* *Interactive legend*: if checked, allows to set by default the legend filter when a layer is added to the map from this source as described in [Layer Settings](layer-settings.md#display). + * *Format*: to assign the default *Tile* format for the layers added to the map (e.g. `png`, `png8`, `jpeg`, `vnd.jpeg-png`, `vnd.jpeg-png8` or `gif`) and to define the default *Information sheet* format for the layers added to the map (`text/plain`, `text/html`, `application/json` or `application/geo+json`). The list of available formats is automatically retrieved from the ones supported by the WMS server and can be also manually fetched through the **Fetch supported formats** button when necessary. !!! note @@ -233,6 +235,8 @@ Enabling that option, all layers added to the map from this catalog source will !!! note If the **No Vendor** is set, then [MapStore](https://mapstore.geosolutionsgroup.com/mapstore/#/) will not use any vendor option supported only by GeoServer in the OGC requests where this source is involved. +* *Interactive legend*: if checked, allows to set by default the legend filter when a layer is added to the map from this source as described in [Layer Settings](layer-settings.md#display). + * *Use remote custom tile grids*: if checked, allows to set by default the *custom tile grid caching strategy* when a layer is added to the map from this source as described in [Layer Settings](layer-settings.md#display). * *Format*: to define the default *Tile* format for the layers added to the map (`png`, `png8`, `jpeg`, `vnd.jpeg-png`, `vnd.jpeg-png8` or `gif`) and to define the default *Information sheet* format for the layers added to the map (`text/plain`, `text/html`, `application/json` or `application/geo+json`). The list of available formats is automatically retrieved from the ones supported by the WMS server and can be also manually fetched through the **Fetch supported formats** button when necessary. @@ -429,3 +433,27 @@ In **General Settings** of a IFC source type, it is possible to specify the serv * *Zoom to selected layer extent* : in order to zoom the map to the layer's extent * Access the [Layer Settings](layer-settings.md#layer-settings) to view/edit the [General Information](layer-settings.md#general-information) and the [Display](layer-settings.md#ifc-layer) options * *Remove* the layer + +### ArcGIS Catalog + +An [**ArcGIS Server Services Directory**](https://developers.arcgis.com/rest/services-reference/enterprise/get-started-with-the-services-directory/) is a RESTful representation of all the services running on an ArcGIS Server site. MapStore allows adding ArcGIS [Map Service](https://developers.arcgis.com/rest/services-reference/enterprise/map-service/) and [Image Service](https://developers.arcgis.com/rest/services-reference/enterprise/image-service/) types through its *Catalog* tool where a specific source type can be configured. + +In **General Settings** of a ArcGIS source type, it is possible to specify the service `Title` and its `URL`. + + + +!!! warning + The `URL` could have one of the following structures: + + * `https:///rest/services/` + + * `https:///rest/services//MapServer` + + * `https:///rest/services//ImageServer` + +!!! Note + The tool capabilities currently available for layers come from ArcGIS service are: + + * *Zoom to selected layer extent* : in order to zoom the map to the layer's extent + * Access the [Layer Settings](layer-settings.md#layer-settings) to view/edit the [General Information](layer-settings.md#general-information) and the [Display](layer-settings.md#ifc-layer) options + * *Remove* the layer diff --git a/docs/user-guide/img/catalog/ArcGIS_service.jpg b/docs/user-guide/img/catalog/ArcGIS_service.jpg new file mode 100644 index 0000000000..4f0c83c495 Binary files /dev/null and b/docs/user-guide/img/catalog/ArcGIS_service.jpg differ diff --git a/docs/user-guide/img/catalog/advanced_settings_csw.jpg b/docs/user-guide/img/catalog/advanced_settings_csw.jpg index 5ba7681bdb..fcafa87301 100644 Binary files a/docs/user-guide/img/catalog/advanced_settings_csw.jpg and b/docs/user-guide/img/catalog/advanced_settings_csw.jpg differ diff --git a/docs/user-guide/img/catalog/advanced_settings_wms.jpg b/docs/user-guide/img/catalog/advanced_settings_wms.jpg index 3aa7243cf1..4a700a2b13 100644 Binary files a/docs/user-guide/img/catalog/advanced_settings_wms.jpg and b/docs/user-guide/img/catalog/advanced_settings_wms.jpg differ diff --git a/docs/user-guide/img/layer-settings/interactive-legend.mp4 b/docs/user-guide/img/layer-settings/interactive-legend.mp4 new file mode 100644 index 0000000000..b1455f7d16 Binary files /dev/null and b/docs/user-guide/img/layer-settings/interactive-legend.mp4 differ diff --git a/docs/user-guide/img/longitudinal-profile/chart-tab.jpg b/docs/user-guide/img/longitudinal-profile/chart-tab.jpg index 0f990b3aff..b0ef0f7eb2 100644 Binary files a/docs/user-guide/img/longitudinal-profile/chart-tab.jpg and b/docs/user-guide/img/longitudinal-profile/chart-tab.jpg differ diff --git a/docs/user-guide/img/longitudinal-profile/export-profile.jpg b/docs/user-guide/img/longitudinal-profile/export-profile.jpg index a1b7d4f5ba..665cd89863 100644 Binary files a/docs/user-guide/img/longitudinal-profile/export-profile.jpg and b/docs/user-guide/img/longitudinal-profile/export-profile.jpg differ diff --git a/docs/user-guide/layer-settings.md b/docs/user-guide/layer-settings.md index bc3982d4f4..d352b4f2c0 100644 --- a/docs/user-guide/layer-settings.md +++ b/docs/user-guide/layer-settings.md @@ -104,6 +104,16 @@ When the *Use cache options* is enabled, more controls are enabled so that it is !!!warning The Gridset compatibility check made by MapStore whose result is shown by the Info tooltip, is usually quite reliable but should be considered anyway only to provide general matching indicators aimed at highlighting possible compatibility issues between the current layer/map settings and the remote Tile Grid. Due to the cache tolerance considered on the server side by GWC, it might even happen in some cases that the settings available on the client side don't HIT the tile cache even if all the checks listed are successful. At the same time, when the standard gridset is used, gridsets check may fail even if all WMS request are effectively HITTING the cache (e.g. because the WMTS reports a list of origins). +* Enable/disable the **Interactive legend**. If this option is enabled, legend entries with an associated valid filter are displayed as toggleable items inside the TOC, a user can click on these legend items to filter the content of the layer. An example can be the following one: + + + +!!! Note + Any type of [Filter](filtering-layers.md#filter-types) applied to the layer remains active when the legend filter is activated on the same layer. + +!!!Warning + The *Interactive legend* is an experimental option and it is disabled by default. The flag `miscSettings.experimentalInteractiveLegend` inside [localConfig](../developer-guide/local-config.md) must be set to true to enable this functionality. The *Interactive legend* is available only for GeoServer WMS layers that support the json format for GetLegendGraphic requests. + * Set the layer *Legend* with custom *Width* and *Height* options. Both of these field values if greater than the default legend's size of 12, then the custom values gets applied on the legend width and height display property * A preview of the legend is shown with the applied custom values from Legend fields above. @@ -527,6 +537,10 @@ In particular, the user can choose between: !!!note Without selecting any format here, the [Identify Tool](navigation-toolbar.md#identify-tool) will return the layers information with the format chosen in Map Settings ( in the [Side Toolbar](mapstore-toolbars.md#side-toolbar)). Once a user specifies the information format in layers settings, instead, that format will take precedence over the map settings only for that specific layer. +!!!note + From the Layer Settings panel, MapStore allows users to choose the *Information format* for `WMS` and `WFS` layer types. + The **Text** option is only available for `WMS` layers, while the **HTML** option for `WFS` layers is available only with GeoServer and if the [`wfs-freemarker`](https://docs.geoserver.org/main/en/user/community/wfs-freemarker/index.html) extension is installed on the GeoServer side. + ### Text An example of layer information in text format can be: diff --git a/docs/user-guide/longitudinal-profile.md b/docs/user-guide/longitudinal-profile.md index 1a7c28d15c..7b33daeaad 100644 --- a/docs/user-guide/longitudinal-profile.md +++ b/docs/user-guide/longitudinal-profile.md @@ -52,7 +52,7 @@ The **Chart toolbar**, displayed in the right corner of the chart, allows the us * **Reset axes** to return the chart to its initial state through the button. -[MapStore](https://mapstore.geosolutionsgroup.com/mapstore/#/) also allows to export the *Longitudinal Profile* as `CSV`, `PNG` or `PDF` file. +[MapStore](https://mapstore.geosolutionsgroup.com/mapstore/#/) also allows to **Export** the *Longitudinal Profile* as`DXT`,`CSV`,`GeoJSON`,`PNG`,`PDF` or `CSV` file. diff --git a/java/pom.xml b/java/pom.xml index 2d2ba983b2..0bf6d6b3c9 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-root - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore diff --git a/java/printing/pom.xml b/java/printing/pom.xml index 5a2f3de5df..b66c4bc385 100644 --- a/java/printing/pom.xml +++ b/java/printing/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-java - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore diff --git a/java/services/pom.xml b/java/services/pom.xml index 61520b82d5..8d51c92374 100644 --- a/java/services/pom.xml +++ b/java/services/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-java - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore diff --git a/java/web/pom.xml b/java/web/pom.xml index 92598d9c9a..7dbc7f943d 100644 --- a/java/web/pom.xml +++ b/java/web/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-java - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore diff --git a/package.json b/package.json index c54ce98386..5376dbd648 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapstore2", - "version": "0.9.0", + "version": "0.10.0", "description": "MapStore 2", "repository": "https://github.com/geosolutions-it/MapStore2", "main": "index.js", @@ -127,10 +127,9 @@ "@geosolutions/geostyler-sld-parser": "2.0.1-1", "@geosolutions/proj4": "2.4.9", "@geosolutions/react-joyride": "1.10.2", - "@geosolutions/wkt-parser": "1.2.2", "@googlemaps/js-api-loader": "1.12.9", - "@mapbox/geojsonhint": "2.0.1", - "@mapbox/togeojson": "0.16.0", + "@mapbox/geojsonhint": "3.3.0", + "@mapbox/togeojson": "0.16.2", "@mapstore/patcher": "https://github.com/geosolutions-it/Patcher/tarball/master", "@turf/along": "6.5.0", "@turf/area": "6.5.0", @@ -147,8 +146,6 @@ "@turf/difference": "6.5.0", "@turf/flatten": "6.5.0", "@turf/great-circle": "5.1.5", - "@turf/helpers": "6.5.0", - "@turf/inside": "4.1.0", "@turf/length": "6.5.0", "@turf/line-intersect": "4.1.0", "@turf/point-on-surface": "4.1.0", @@ -168,11 +165,10 @@ "cesium": "1.106.1", "chroma-js": "1.3.7", "classnames": "2.2.5", - "codemirror": "5.18.2", + "codemirror": "5.65.16", "colorbrewer": "1.0.0", "concurrently": "6.4.0", "connected-react-router": "6.3.2", - "create-react-class": "15.6.3", "d3-format": "3.1.0", "draft-js": "0.11.0", "draft-js-inline-toolbar-plugin": "3.0.1", @@ -200,16 +196,12 @@ "immutable": "4.0.0-rc.12", "intersection-observer": "0.7.0", "intl": "1.2.2", - "is-equal": "1.5.5", "ismobilejs": "0.5.0", "istanbul-instrumenter-loader": "3.0.1", - "jiff": "0.7.3", - "json-2-csv": "2.1.2", - "json-loader": "0.5.7", + "json-2-csv": "5.5.1", "jsonlint-mod": "1.7.5", - "jsonpath": "1.0.2", - "jszip": "3.1.5", - "keymirror": "0.1.1", + "jsonpath": "1.1.1", + "jszip": "3.10.1", "leaflet": "1.3.1", "leaflet-draw": "1.0.2", "leaflet-extra-markers": "1.0.6", @@ -230,19 +222,16 @@ "object-fit-images": "3.2.4", "ol": "7.4.0", "pdfmake": "0.2.7", - "pdfviewer": "0.3.2", "plotly.js-cartesian-dist": "2.5.1", "prop-types": "15.7.2", "qrcode.react": "0.9.3", "query-string": "6.9.0", - "react": "16.10.1", + "react": "16.14.0", "react-addons-css-transition-group": "15.6.2", - "react-addons-shallow-compare": "15.6.2", "react-bootstrap": "0.31.0", "react-checkbox-tree": "1.5.1", "react-codemirror2": "4.0.0", "react-color": "2.17.0", - "react-confirm-button": "0.0.2", "react-container-dimensions": "1.4.1", "react-contenteditable": "3.3.2", "react-copy-to-clipboard": "5.0.0", @@ -271,9 +260,7 @@ "react-quill": "1.1.0", "react-redux": "6.0.0", "react-resize-detector": "4.2.1", - "react-responsive": "1.3.0", "react-router": "4.1.1", - "react-router-dom": "4.2.2", "react-scroll-up": "1.3.7", "react-select": "1.3.0", "react-share": "1.15.1", @@ -298,7 +285,6 @@ "shpjs": "3.4.2", "simple-statistics": "7.8.3", "stickybits": "3.6.6", - "stream": "0.0.2", "tinycolor2": "1.4.1", "turf-bbox": "3.0.10", "turf-point": "2.0.1", @@ -311,7 +297,7 @@ "web-ifc": "0.0.50", "webfontloader": "1.6.28", "wellknown": "0.5.0", - "xml2js": "0.4.17", + "xml2js": "0.6.2", "xpath": "0.0.27" }, "scripts": { diff --git a/pom.xml b/pom.xml index c7aa88bee4..271f649b11 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-root pom - 1.8-SNAPSHOT + 1.9-SNAPSHOT MapStore Root diff --git a/product/pom.xml b/product/pom.xml index 07b397e52a..e4b44ba69e 100644 --- a/product/pom.xml +++ b/product/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 it.geosolutions.mapstore mapstore-root - 1.8-SNAPSHOT + 1.9-SNAPSHOT it.geosolutions.mapstore mapstore-product @@ -16,7 +15,7 @@ ${env.MAPSTORE_DATA_DIR} default - + diff --git a/project/standard/templates/build.sh b/project/standard/templates/build.sh index 7ba7bfacb2..9aabb82ba7 100755 --- a/project/standard/templates/build.sh +++ b/project/standard/templates/build.sh @@ -11,6 +11,9 @@ npm run lint if [ $# -eq 0 ] then mvn clean install -Dmapstore2.version=$VERSION - else - mvn clean install -Dmapstore2.version=$1 + elif [ $# -eq 1 ] + then + mvn clean install -Dmapstore2.version=$1 + else + mvn clean install -Dmapstore2.version=$1 -P$2 fi diff --git a/project/standard/templates/web/pom.xml b/project/standard/templates/web/pom.xml index 732bba00c5..9f4edf6d8f 100644 --- a/project/standard/templates/web/pom.xml +++ b/project/standard/templates/web/pom.xml @@ -15,7 +15,7 @@ UTF-8 9.0.89 8080 - 1.8-SNAPSHOT + 1.9-SNAPSHOT 2.1-SNAPSHOT 1.6-SNAPSHOT 2.3.1 diff --git a/web/client/actions/__tests__/catalog-test.js b/web/client/actions/__tests__/catalog-test.js index f93c6839fd..029e2aa4bc 100644 --- a/web/client/actions/__tests__/catalog-test.js +++ b/web/client/actions/__tests__/catalog-test.js @@ -153,7 +153,7 @@ describe('Test correctness of the catalog actions', () => { expect(retval.service).toBe(service); }); it('deleteService', () => { - var retval = deleteService(status); + var retval = deleteService(); expect(retval).toExist(); expect(retval.type).toBe(DELETE_SERVICE); diff --git a/web/client/actions/backgroundselector.js b/web/client/actions/backgroundselector.js index a08cdc8b57..ffa293353d 100644 --- a/web/client/actions/backgroundselector.js +++ b/web/client/actions/backgroundselector.js @@ -25,6 +25,7 @@ export const CLEAR_MODAL_PARAMETERS = 'BACKGROUND_SELECTOR:CLEAR_MODAL_PARAMETER export const CONFIRM_DELETE_BACKGROUND_MODAL = 'BACKGROUND_SELECTOR:CONFIRM_DELETE_BACKGROUND_MODAL'; export const ALLOW_BACKGROUNDS_DELETION = 'BACKGROUND_SELECTOR:ALLOW_BACKGROUNDS_DELETION'; export const SYNC_CURRENT_BACKGROUND_LAYER = 'BACKGROUND_SELECTOR:SYNC_CURRENT_BACKGROUND_LAYER'; +export const STASH_SELECTED_SERVICE = 'BACKGROUND_SELECTOR:STASH_SELECTED_SERVICE'; export function createBackgroundsList(backgrounds) { return { @@ -125,3 +126,20 @@ export function confirmDeleteBackgroundModal(show, layerTitle = null, layerId = layerId }; } + +/** + * Stashes the currently selected catalog service for later restoration. + * This is useful when closing the catalog and reopening it for background selection, + * allowing the user to easily return to their previous selection. + * + * @param {Object} service - The service object representing the currently selected catalog. + * @returns {Object} An action object with the type `STASH_SELECTED_SERVICE` + * and the selected service. + */ + +export function stashSelectedCatalogService(service) { + return { + type: STASH_SELECTED_SERVICE, + service + }; +} diff --git a/web/client/components/I18N/Localized.jsx b/web/client/components/I18N/Localized.jsx index 0f56f3b819..a810e72155 100644 --- a/web/client/components/I18N/Localized.jsx +++ b/web/client/components/I18N/Localized.jsx @@ -32,6 +32,16 @@ class Localized extends React.Component { }; } + componentDidMount() { + this.updateDocumentLangAttribute(); + } + + componentDidUpdate(prevProps) { + if (this.props.locale !== prevProps.locale) { + this.updateDocumentLangAttribute(); + } + } + render() { let { children } = this.props; @@ -63,6 +73,12 @@ class Localized extends React.Component { }; }, {}); }; + + updateDocumentLangAttribute() { + if (document?.documentElement) { + document.documentElement.setAttribute("lang", this.props.locale); + } + } } export default Localized; diff --git a/web/client/components/I18N/__tests__/Localized-test.jsx b/web/client/components/I18N/__tests__/Localized-test.jsx index 829b0a70eb..91ab7542d4 100644 --- a/web/client/components/I18N/__tests__/Localized-test.jsx +++ b/web/client/components/I18N/__tests__/Localized-test.jsx @@ -40,6 +40,21 @@ describe('Test the localization support HOC', () => { expect(dom.innerHTML).toBe("my message"); }); + it('correctly sets the document language', () => { + ReactDOM.render( + + {() => } + + , document.getElementById("container")); + expect(document.documentElement.lang).toBe("it-IT"); + ReactDOM.render( + + {() => } + + , document.getElementById("container")); + expect(document.documentElement.lang).toBe("de-DE"); + }); + it('localizes wrapped HTML component', () => { var localized = ReactDOM.render( diff --git a/web/client/components/TOC/fragments/settings/Display.jsx b/web/client/components/TOC/fragments/settings/Display.jsx index 3b00af2926..2f3ea6957d 100644 --- a/web/client/components/TOC/fragments/settings/Display.jsx +++ b/web/client/components/TOC/fragments/settings/Display.jsx @@ -25,6 +25,7 @@ import WMSCacheOptions from './WMSCacheOptions'; import ThreeDTilesSettings from './ThreeDTilesSettings'; import ModelTransformation from './ModelTransformation'; import StyleBasedWMSJsonLegend from '../../../../plugins/TOC/components/StyleBasedWMSJsonLegend'; +import { getMiscSetting } from '../../../../utils/ConfigUtils'; export default class extends React.Component { static propTypes = { opacityText: PropTypes.node, @@ -123,6 +124,8 @@ export default class extends React.Component { }; render() { const formatValue = this.props.element && this.props.element.format || "image/png"; + const experimentalInteractiveLegend = getMiscSetting('experimentalInteractiveLegend', false); + const enableInteractiveLegend = !!(experimentalInteractiveLegend && this.props.element?.enableInteractiveLegend); return ( - { this.props.element?.serverType !== ServerTypes.NO_VENDOR && !this.props?.hideInteractiveLegendOption && + { experimentalInteractiveLegend && this.props.element?.serverType !== ServerTypes.NO_VENDOR && !this.props?.hideInteractiveLegendOption && + checked={enableInteractiveLegend} >  } /> } - {!this.props.element?.enableInteractiveLegend && <> + {!enableInteractiveLegend && <>
- { this.props.element?.enableInteractiveLegend ? + { enableInteractiveLegend ? { beforeEach((done) => { document.body.innerHTML = '
'; mockAxios = new MockAdapter(axios); + setConfigProp('miscSettings', { experimentalInteractiveLegend: true }); setTimeout(done); }); @@ -26,6 +28,7 @@ describe('test Layer Properties Display module component', () => { ReactDOM.unmountComponentAtNode(document.getElementById("container")); document.body.innerHTML = ''; mockAxios.restore(); + setConfigProp('miscSettings', { }); setTimeout(done); }); diff --git a/web/client/components/app/__tests__/StandardAppComponent-test.jsx b/web/client/components/app/__tests__/StandardAppComponent-test.jsx index 50f72cd513..2a5cae426b 100644 --- a/web/client/components/app/__tests__/StandardAppComponent-test.jsx +++ b/web/client/components/app/__tests__/StandardAppComponent-test.jsx @@ -34,17 +34,21 @@ describe('StandardAppComponent', () => { setTimeout(done); }); - it('creates a default app', () => { + it('creates a default app', (done) => { const store = { dispatch: () => {}, subscribe: () => {}, getState: () => ({}) }; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + done(); + }); }); - it('creates a default app with plugins', () => { + it('creates a default app with plugins', (done) => { const plugins = { MyPlugin }; @@ -54,11 +58,12 @@ describe('StandardAppComponent', () => { subscribe: () => {}, getState: () => ({}) }; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('MyPlugin').length).toBe(1); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('MyPlugin').length).toBe(1); + done(); + }); }); }); diff --git a/web/client/components/app/__tests__/StandardContainer-test.jsx b/web/client/components/app/__tests__/StandardContainer-test.jsx index d28697a425..0609714724 100644 --- a/web/client/components/app/__tests__/StandardContainer-test.jsx +++ b/web/client/components/app/__tests__/StandardContainer-test.jsx @@ -49,7 +49,7 @@ describe('StandardContainer', () => { }); - it('creates a default app with component and plugins', () => { + it('creates a default app with component and plugins', (done) => { const plugins = { MyPlugin: {} }; @@ -63,11 +63,13 @@ describe('StandardContainer', () => { const componentConfig = { component: mycomponent }; - - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - const dom = ReactDOM.findDOMNode(app); - expect(dom.getElementsByClassName('mycomponent').length).toBe(1); - expect(dom.getElementsByClassName('MyPlugin').length).toBe(1); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('mycomponent').length).toBe(1); + expect(container.getElementsByClassName('MyPlugin').length).toBe(1); + done(); + }); }); }); diff --git a/web/client/components/app/__tests__/StandardRouter-test.jsx b/web/client/components/app/__tests__/StandardRouter-test.jsx index e067ee9a01..a7c08378e0 100644 --- a/web/client/components/app/__tests__/StandardRouter-test.jsx +++ b/web/client/components/app/__tests__/StandardRouter-test.jsx @@ -49,7 +49,7 @@ describe('StandardRouter', () => { setTimeout(done); }); - it('creates a default router app', () => { + it('creates a default router app', (done) => { const store = { dispatch: () => {}, subscribe: () => { @@ -58,11 +58,15 @@ describe('StandardRouter', () => { unsubscribe: () => {}, getState: () => ({}) }; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + done(); + }); }); - it('creates a default router app with pages', () => { + it('creates a default router app with pages', (done) => { const store = { dispatch: () => {}, subscribe: () => { @@ -75,14 +79,16 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('mycomponent').length).toBe(1); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('mycomponent').length).toBe(1); + done(); + }); }); - it('creates a default router app with pages and plugins', () => { + it('creates a default router app with pages and plugins', (done) => { const plugins = { MyPlugin: {} }; @@ -99,15 +105,16 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('MyPlugin').length).toBe(1); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('MyPlugin').length).toBe(1); + done(); + }); }); - it('if we dont wait for theme no spinner is shown', () => { + it('if we dont wait for theme no spinner is shown', (done) => { const plugins = { MyPlugin: {} }; @@ -124,14 +131,15 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('_ms2_init_spinner').length).toBe(0); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('_ms2_init_spinner').length).toBe(0); + done(); + }); }); - it('if we wait for theme no spinner is shown if the theme is already loaded', () => { + it('if we wait for theme no spinner is shown if the theme is already loaded', (done) => { const plugins = { MyPlugin: {} }; @@ -148,14 +156,15 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('_ms2_init_spinner').length).toBe(0); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('_ms2_init_spinner').length).toBe(0); + done(); + }); }); - it('if we wait for theme spinner is shown if the theme is not already loaded', () => { + it('if we wait for theme spinner is shown if the theme is not already loaded', (done) => { const plugins = { MyPlugin: {} }; @@ -172,12 +181,13 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); - - const dom = ReactDOM.findDOMNode(app); - - expect(dom.getElementsByClassName('_ms2_init_spinner').length).toBe(1); + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render(, container, () => { + expect(container.innerHTML).toExist(); + expect(container.getElementsByClassName('_ms2_init_spinner').length).toBe(1); + done(); + }); }); it('if we wait for theme onThemeLoaded is called when theme is loaded', (done) => { const plugins = { @@ -196,11 +206,10 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render(, document.getElementById("container")); - expect(app).toExist(); }); it('if we wait for theme onThemeLoaded is called when theme custom is loaded', (done) => { @@ -220,7 +229,7 @@ describe('StandardRouter', () => { path: '/', component: mycomponent }]; - const app = ReactDOM.render( + ReactDOM.render( { done(); }} /> , document.getElementById("container")); - expect(app).toExist(); }); }); diff --git a/web/client/components/buttons/__tests__/ZoomButton-test.jsx b/web/client/components/buttons/__tests__/ZoomButton-test.jsx index e9dd8c0b90..409e67fa13 100644 --- a/web/client/components/buttons/__tests__/ZoomButton-test.jsx +++ b/web/client/components/buttons/__tests__/ZoomButton-test.jsx @@ -43,57 +43,59 @@ describe('This test for ZoomButton', () => { }); // test DEFAULTS - it('test default properties', () => { - const zmeBtn = ReactDOM.render( + it('test default properties', (done) => { + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode.id).toBe("mapstore-zoom"); - - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode.className.indexOf('square-button') >= 0).toBe(true); - expect(zmeBtnNode.innerHTML).toExist(); + container, () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoom"); + expect(zmeBtnNode).toExist(); + expect(zmeBtnNode.className.indexOf('square-button') >= 0).toBe(true); + expect(zmeBtnNode.innerHTML).toExist(); + done(); + }); }); - it('test glyphicon property', () => { - const zmeBtn = ReactDOM.render( + it('test glyphicon property', (done) => { + const container = document.getElementById("container"); + expect(container.innerHTML).toNotExist(); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode).toExist(); - const icons = zmeBtnNode.getElementsByTagName('span'); - expect(icons.length).toBe(1); + container, () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoom"); + expect(zmeBtnNode).toExist(); + const icons = zmeBtnNode.getElementsByTagName('span'); + expect(icons.length).toBe(1); + done(); + }); }); - it('test glyphicon property with text', () => { - const zmeBtn = ReactDOM.render( + it('test glyphicon property with text', (done) => { + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode).toExist(); + document.getElementById("container"), + () => { + const zmeBtnNode = document.getElementById("mapstore-zoom"); + expect(zmeBtnNode).toExist(); - const btnItems = zmeBtnNode.getElementsByTagName('span'); - expect(btnItems.length).toBe(1); + const btnItems = zmeBtnNode.getElementsByTagName('span'); + expect(btnItems.length).toBe(1); - expect(zmeBtnNode.innerText.indexOf("button") !== -1).toBe(true); + expect(zmeBtnNode.innerText.indexOf("button") !== -1).toBe(true); + done(); + }); }); - it('test if click on button launches the proper action', () => { + it('test if click on button launches the proper action', (done) => { let genericTest = function() { let actions = { @@ -102,7 +104,7 @@ describe('This test for ZoomButton', () => { } }; let spy = expect.spyOn(actions, "onZoom"); - var cmp = ReactDOM.render( + ReactDOM.render( { } }} /> - , document.getElementById("container")); - expect(cmp).toExist(); + , document.getElementById("container") + , () => { + const cmpDom = document.getElementById("mapstore-zoom"); + expect(cmpDom).toExist(); - const cmpDom = document.getElementById("mapstore-zoom"); - expect(cmpDom).toExist(); - - cmpDom.click(); + cmpDom.click(); - expect(spy.calls.length).toBe(1); - expect(spy.calls[0].arguments.length).toBe(1); + expect(spy.calls.length).toBe(1); + expect(spy.calls[0].arguments.length).toBe(1); + done(); + }); }; genericTest(); }); - it('create glyphicon with custom css class', () => { - const zmeBtn = ReactDOM.render( + it('create glyphicon with custom css class', (done) => { + const container = document.getElementById("container"); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - - expect(zmeBtnNode.className.indexOf('custom') !== -1).toBe(true); + container, () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoom"); + expect(zmeBtnNode).toExist(); + + expect(zmeBtnNode.className.indexOf('custom') !== -1).toBe(true); + done(); + }); }); it('test zoom in', () => { diff --git a/web/client/components/buttons/__tests__/ZoomToMaxExtentButton-test.jsx b/web/client/components/buttons/__tests__/ZoomToMaxExtentButton-test.jsx index f5c448c09f..363c5896b6 100644 --- a/web/client/components/buttons/__tests__/ZoomToMaxExtentButton-test.jsx +++ b/web/client/components/buttons/__tests__/ZoomToMaxExtentButton-test.jsx @@ -43,54 +43,57 @@ describe('This test for ZoomToMaxExtentButton', () => { }); // test DEFAULTS - it('test default properties', () => { - const zmeBtn = ReactDOM.render( + it('test default properties', (done) => { + const container = document.getElementById("container"); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode.id).toBe("mapstore-zoomtomaxextent"); - - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode.className.indexOf('default') >= 0).toBe(true); - expect(zmeBtnNode.innerHTML).toExist(); + container, + () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoomtomaxextent"); + expect(zmeBtnNode).toExist(); + expect(zmeBtnNode.className.indexOf('default') >= 0).toBe(true); + expect(zmeBtnNode.innerHTML).toExist(); + done(); + }); }); - it('test glyphicon property', () => { - const zmeBtn = ReactDOM.render( + it('test glyphicon property', (done) => { + const container = document.getElementById("container"); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode).toExist(); - const icons = zmeBtnNode.getElementsByTagName('span'); - expect(icons.length).toBe(1); + container, + () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoomtomaxextent"); + expect(zmeBtnNode).toExist(); + const icons = zmeBtnNode.getElementsByTagName('span'); + expect(icons.length).toBe(1); + done(); + }); }); - it('test glyphicon property with text', () => { - const zmeBtn = ReactDOM.render( + it('test glyphicon property with text', (done) => { + const container = document.getElementById("container"); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - expect(zmeBtnNode).toExist(); - - const btnItems = zmeBtnNode.getElementsByTagName('span'); - expect(btnItems.length).toBe(1); - - expect(zmeBtnNode.innerText.indexOf("button") !== -1).toBe(true); + container, + () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoomtomaxextent"); + expect(zmeBtnNode).toExist(); + const btnItems = zmeBtnNode.getElementsByTagName('span'); + expect(btnItems.length).toBe(1); + + expect(zmeBtnNode.innerText.indexOf("button") !== -1).toBe(true); + done(); + }); }); it('test if click on button launches the proper action', () => { @@ -145,18 +148,20 @@ describe('This test for ZoomToMaxExtentButton', () => { genericTest("image"); }); - it('create glyphicon with custom css class', () => { - const zmeBtn = ReactDOM.render( + it('create glyphicon with custom css class', (done) => { + const container = document.getElementById("container"); + ReactDOM.render( , - document.getElementById("container")); - expect(zmeBtn).toExist(); - - const zmeBtnNode = ReactDOM.findDOMNode(zmeBtn); - expect(zmeBtnNode).toExist(); - - expect(zmeBtnNode.className.indexOf('custom') !== -1).toBe(true); + container, + () => { + expect(container.innerHTML).toExist(); + const zmeBtnNode = document.getElementById("mapstore-zoomtomaxextent"); + expect(zmeBtnNode).toExist(); + expect(zmeBtnNode.className.indexOf('custom') !== -1).toBe(true); + done(); + }); }); it('test zoom to initial extent', () => { diff --git a/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js b/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js index dc29cafd84..158f071dd5 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js +++ b/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js @@ -21,6 +21,7 @@ import WMSDomainAliases from "./WMSDomainAliases"; import tooltip from '../../../misc/enhancers/buttonTooltip'; import OverlayTrigger from '../../../misc/OverlayTrigger'; import FormControl from '../../../misc/DebouncedFormControl'; +import { getMiscSetting } from '../../../../utils/ConfigUtils'; const Button = tooltip(ButtonRB); const Select = localizedProps('noResultsText')(RS); @@ -82,8 +83,12 @@ export default ({ service.isNew && onChangeServiceProperty("autoSetVisibilityLimits", props.autoSetVisibilityLimits); }, [props.autoSetVisibilityLimits]); + const experimentalInteractiveLegend = getMiscSetting('experimentalInteractiveLegend', false); + const tileSelectOptions = getTileSizeSelectOptions(tileSizeOptions); const serverTypeOptions = getServerTypeOptions(); + // for CSW services with no vendor options, disable format and info format options + const canLoadInfo = !(['csw'].includes(service.type) && service.layerOptions?.serverType === ServerTypes.NO_VENDOR); return ( {(isLocalizedLayerStylesEnabled && !isNil(service.type) ? service.type === "wms" : false) && ( - {![ServerTypes.NO_VENDOR].includes(service.layerOptions?.serverType) && ['wms', 'csw'].includes(service.type) && + {experimentalInteractiveLegend && ![ServerTypes.NO_VENDOR].includes(service.layerOptions?.serverType) && ['wms', 'csw'].includes(service.type) && onChangeServiceProperty("layerOptions", { ...service.layerOptions, enableInteractiveLegend: e.target.checked})} checked={!isNil(service.layerOptions?.enableInteractiveLegend) ? service.layerOptions?.enableInteractiveLegend : false}> @@ -193,7 +198,8 @@ export default ({ title={} text={} /> : null}