@eeacms/volto-cca-policy 1.0.37 → 1.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ 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
+ ### [1.0.38](https://github.com/eea/volto-cca-policy/compare/1.0.37...1.0.38) - 11 October 2026
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix(navigator): update map notice role and optimize geographic field checks [kreafox - [`f40bcdf`](https://github.com/eea/volto-cca-policy/commit/f40bcdfa6573143dc0ccff7017e8d4ec6feda055)]
12
+
13
+ #### :nail_care: Enhancements
14
+
15
+ - refactor(navigator): refactor map component, add tests [kreafox - [`91f5795`](https://github.com/eea/volto-cca-policy/commit/91f57957a00b9e8b014074edf58f5166ce1dc1f9)]
16
+
17
+ #### :hammer_and_wrench: Others
18
+
19
+ - test(navigator): add tests for tool removal and export functionality [kreafox - [`0eeda9d`](https://github.com/eea/volto-cca-policy/commit/0eeda9db7929e7665376813f664b54406162f3f2)]
7
20
  ### [1.0.37](https://github.com/eea/volto-cca-policy/compare/1.0.36...1.0.37) - 8 October 2026
8
21
 
9
22
  #### :hammer_and_wrench: Others
@@ -628,12 +641,15 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
628
641
 
629
642
  #### :house: Internal changes
630
643
 
631
- - chore: [JENKINSFILE] sanitize sonarqube command Refs #305084 [valentinab25 - [`8106a44`](https://github.com/eea/volto-cca-policy/commit/8106a44e8f40dd360a274cede4b3f9c215d026cf)]
632
644
 
633
645
  #### :hammer_and_wrench: Others
634
646
 
635
647
  - test: pin Chromium version 149 to work with Cypress [valentinab25 - [`659ec82`](https://github.com/eea/volto-cca-policy/commit/659ec823dd71fe592a3a7accda22effbe1fb07bc)]
636
- ## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.34...1.0.0) - 12 June 2026
648
+ ## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.36...1.0.0) - 12 June 2026
649
+
650
+ ### [1.0.0-alpha.36](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.35...1.0.0-alpha.36) - 9 October 2026
651
+
652
+ ### [1.0.0-alpha.35](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.34...1.0.0-alpha.35) - 9 October 2026
637
653
 
638
654
  ### [1.0.0-alpha.34](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.33...1.0.0-alpha.34) - 8 October 2026
639
655
 
@@ -1170,7 +1186,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1170
1186
 
1171
1187
  #### :house: Internal changes
1172
1188
 
1173
- - chore: [JENKINSFILE] sanitize sonarqube command Refs #305084 [valentinab25 - [`8106a44`](https://github.com/eea/volto-cca-policy/commit/8106a44e8f40dd360a274cede4b3f9c215d026cf)]
1174
1189
 
1175
1190
  #### :hammer_and_wrench: Others
1176
1191
 
@@ -1707,7 +1722,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1707
1722
 
1708
1723
  #### :house: Internal changes
1709
1724
 
1710
- - chore: [JENKINSFILE] add package version in sonarqube [valentinab25 - [`e901251`](https://github.com/eea/volto-cca-policy/commit/e901251592eb54609fbc7eb8fcf7bd452c2da9cd)]
1711
1725
 
1712
1726
  ### [0.3.102](https://github.com/eea/volto-cca-policy/compare/0.3.101...0.3.102) - 24 February 2026
1713
1727
 
@@ -1717,7 +1731,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1717
1731
 
1718
1732
  #### :house: Internal changes
1719
1733
 
1720
- - chore: [JENKINSFILE] use sonarqube branches [EEA Jenkins - [`8b3699a`](https://github.com/eea/volto-cca-policy/commit/8b3699a6cbaef662b5e80884ac2c1f7dc9c6591e)]
1721
1734
 
1722
1735
  ### [0.3.101](https://github.com/eea/volto-cca-policy/compare/0.3.100...0.3.101) - 28 January 2026
1723
1736
 
@@ -2005,7 +2018,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
2005
2018
  - style: fix item spacing [kreafox - [`7d4535d`](https://github.com/eea/volto-cca-policy/commit/7d4535dbc486e6cb80fc55d6ac8c7c01d9cf48af)]
2006
2019
  - chore: code cleanup [kreafox - [`842419b`](https://github.com/eea/volto-cca-policy/commit/842419b1bce3dc624755c81c9dea672198cab9e2)]
2007
2020
  - style: update teaser block styling [kreafox - [`1e0cad8`](https://github.com/eea/volto-cca-policy/commit/1e0cad8e1ed3ae827ee868dce8ad50b292a937ba)]
2008
- - chore: update Makefile, run yarn i18n [kreafox - [`61e6962`](https://github.com/eea/volto-cca-policy/commit/61e6962901068ae6512265417a730bd9befd65fe)]
2009
2021
  - chore: move styling to theme folder [kreafox - [`8cf6a1b`](https://github.com/eea/volto-cca-policy/commit/8cf6a1b8b14abf472a9c61b28db574f6d9e0bd3b)]
2010
2022
  - chore: remove ArrayWidget customization [kreafox - [`249bf93`](https://github.com/eea/volto-cca-policy/commit/249bf93054f7726c3d57a8b8ca01b75cd0d30522)]
2011
2023
  - chore: remove Footer customization [kreafox - [`7038af3`](https://github.com/eea/volto-cca-policy/commit/7038af37d48a328dfc31746f3fa84973e2f840ab)]
@@ -4768,13 +4780,10 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
4768
4780
  - Refs #260715 rast-block wip [Tripon Eugen - [`f19d54e`](https://github.com/eea/volto-cca-policy/commit/f19d54e0b9a6a86bf344eb85b6a1cda7f3de91bf)]
4769
4781
  - Refs #260715 rast-block wip [Tripon Eugen - [`2828537`](https://github.com/eea/volto-cca-policy/commit/2828537b6c084cd1a82162d552fb4ef025b71f9f)]
4770
4782
  - Refs #260715 rast-block updates [Tripon Eugen - [`1e803e5`](https://github.com/eea/volto-cca-policy/commit/1e803e5bd3d3fb7558f261c76c68866be7beb8b5)]
4771
- - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`0a15e1b`](https://github.com/eea/volto-cca-policy/commit/0a15e1b2ad081233685e80d5b3c60a8663f6b896)]
4772
- - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`9554e44`](https://github.com/eea/volto-cca-policy/commit/9554e44c92a621a52b2adb5a4830fb084ee5734b)]
4773
4783
  ### [0.1.49](https://github.com/eea/volto-cca-policy/compare/0.1.48...0.1.49) - 15 November 2023
4774
4784
 
4775
4785
  #### :house: Internal changes
4776
4786
 
4777
- - chore: [JENKINS] Refactor automated testing [valentinab25 - [`7b820a6`](https://github.com/eea/volto-cca-policy/commit/7b820a6369c2ddd5203b1a4abe352cb4bb43db7a)]
4778
4787
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`f0a8061`](https://github.com/eea/volto-cca-policy/commit/f0a8061c275c236deb00087c23fac9860a073106)]
4779
4788
 
4780
4789
  #### :hammer_and_wrench: Others
@@ -4791,9 +4800,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
4791
4800
  - Refs #259267 - jenkins test [Tripon Eugen - [`cacd31e`](https://github.com/eea/volto-cca-policy/commit/cacd31e7b1afe0983674ed5c7632d2e1d7fa752e)]
4792
4801
  - Refs #259267 - jenkins [Tripon Eugen - [`5b3affe`](https://github.com/eea/volto-cca-policy/commit/5b3affee8401239de10097884c1b7f2349d15ec0)]
4793
4802
  - Refs #259267 - add When, lead image and title to files [Tripon Eugen - [`2cedb23`](https://github.com/eea/volto-cca-policy/commit/2cedb237f898af9057e13fba94b615ef71077204)]
4794
- - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`4d607a5`](https://github.com/eea/volto-cca-policy/commit/4d607a576e9d0a5c34e48c41b409e7df616ee3d6)]
4795
- - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`b7f74d5`](https://github.com/eea/volto-cca-policy/commit/b7f74d53513a6edbfbca5cb6d19687929bb1e5db)]
4796
- - test: [JENKINS] Improve cypress time [valentinab25 - [`db65617`](https://github.com/eea/volto-cca-policy/commit/db656173391f65157098d95d388c25f6429753d8)]
4797
4803
  - Refs #259267 - cca event blocks attachments and check not mandatoty fields [Tripon Eugen - [`3138e5a`](https://github.com/eea/volto-cca-policy/commit/3138e5afb5bfbdbed14e27ed457b16867b7fa414)]
4798
4804
  - Refs #256681 - Fix error in CCA Event view menu. ([React Intl] An id must be provided to format a message.) [GhitaB - [`517eeb8`](https://github.com/eea/volto-cca-policy/commit/517eeb817264a47bbfd6b9b7d22aaf22d44ed224)]
4799
4805
  - Refs #161485 - Fix ECDE name conflict. [GhitaB - [`8bfd99f`](https://github.com/eea/volto-cca-policy/commit/8bfd99ff68bb82a04d1c0ed625fa514fcf46289e)]
@@ -5010,7 +5016,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
5010
5016
 
5011
5017
  #### :house: Internal changes
5012
5018
 
5013
- - chore: [JENKINS] Remove alpha testing version [valentinab25 - [`ad1ced0`](https://github.com/eea/volto-cca-policy/commit/ad1ced0971ba116c13a3b5fcc039172cc915c919)]
5014
5019
 
5015
5020
  #### :hammer_and_wrench: Others
5016
5021
 
@@ -5491,7 +5496,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
5491
5496
  #### :hammer_and_wrench: Others
5492
5497
 
5493
5498
  - Refs #158294 - Update supported languages list. [GhitaB - [`0a4f91f`](https://github.com/eea/volto-cca-policy/commit/0a4f91f39b7edc367bd4c127d6a8f273c7788361)]
5494
- - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`8f1f9ce`](https://github.com/eea/volto-cca-policy/commit/8f1f9ce6c22805670cc0800d3c779b6d619d0f31)]
5495
5499
  ### [0.1.1](https://github.com/eea/volto-cca-policy/compare/0.1.0...0.1.1) - 13 December 2022
5496
5500
 
5497
5501
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "@eeacms/volto-cca-policy: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { Button } from 'semantic-ui-react';
3
+ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
4
+
5
+ const messages = defineMessages({
6
+ countrySelectionRequired: {
7
+ id: 'Map view is only applicable when countries are selected in the <strong>Geographic coverage</strong>.',
8
+ defaultMessage:
9
+ 'Map view is only applicable when countries are selected in the <strong>Geographic coverage</strong>.',
10
+ },
11
+ clearGeographicCoverageFilters: {
12
+ id: 'Clear other geographic coverage filters to enable the map.',
13
+ defaultMessage:
14
+ 'Clear other geographic coverage filters to enable the map.',
15
+ },
16
+ clearGeographicCoverageFiltersButton: {
17
+ id: 'Clear filters',
18
+ defaultMessage: 'Clear filters',
19
+ },
20
+ selectCountryForMap: {
21
+ id: 'Select a country to explore the map',
22
+ defaultMessage: 'Select a country to explore the map',
23
+ },
24
+ });
25
+
26
+ const NavigatorCatalogueMapState = ({ children, isMapDisabled, onClear }) => {
27
+ const intl = useIntl();
28
+
29
+ return (
30
+ <div
31
+ className={`navigator-catalogue-map-state${
32
+ isMapDisabled ? ' is-disabled' : ''
33
+ }`}
34
+ aria-disabled={isMapDisabled || undefined}
35
+ >
36
+ <div
37
+ className="navigator-catalogue-map-content"
38
+ aria-hidden={isMapDisabled || undefined}
39
+ inert={isMapDisabled ? '' : undefined}
40
+ >
41
+ {children}
42
+ </div>
43
+
44
+ {isMapDisabled && (
45
+ <div className="navigator-catalogue-map-overlay">
46
+ <div className="navigator-catalogue-map-notice">
47
+ <h4>{intl.formatMessage(messages.selectCountryForMap)}</h4>
48
+ <p>
49
+ <FormattedMessage
50
+ {...messages.countrySelectionRequired}
51
+ values={{ strong: (text) => <strong>{text}</strong> }}
52
+ />
53
+ </p>
54
+ <p>
55
+ <FormattedMessage {...messages.clearGeographicCoverageFilters} />
56
+ </p>
57
+ <Button
58
+ className="primary map-notice-clear-button"
59
+ onClick={onClear}
60
+ >
61
+ {intl.formatMessage(
62
+ messages.clearGeographicCoverageFiltersButton,
63
+ )}
64
+ </Button>
65
+ </div>
66
+ </div>
67
+ )}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export default NavigatorCatalogueMapState;
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom';
3
+ import { fireEvent, render, screen } from '@testing-library/react';
4
+ import { IntlProvider } from 'react-intl';
5
+ import NavigatorCatalogueMapState from './NavigatorCatalogueMapState';
6
+
7
+ const renderMapState = (props) =>
8
+ render(
9
+ <IntlProvider locale="en">
10
+ <NavigatorCatalogueMapState {...props}>
11
+ <div data-testid="country-map">Map</div>
12
+ </NavigatorCatalogueMapState>
13
+ </IntlProvider>,
14
+ );
15
+
16
+ describe('NavigatorCatalogueMapState', () => {
17
+ it('leaves the map interactive when it is enabled', () => {
18
+ renderMapState({ isMapDisabled: false, onClear: jest.fn() });
19
+
20
+ expect(screen.getByTestId('country-map').parentElement).not.toHaveAttribute(
21
+ 'inert',
22
+ );
23
+ expect(
24
+ screen.queryByText('Select a country to explore the map'),
25
+ ).not.toBeInTheDocument();
26
+ });
27
+
28
+ it('disables the map and handles the clear action', () => {
29
+ const onClear = jest.fn();
30
+ renderMapState({ isMapDisabled: true, onClear });
31
+
32
+ expect(screen.getByTestId('country-map').parentElement).toHaveAttribute(
33
+ 'inert',
34
+ );
35
+ expect(
36
+ screen.getByText('Select a country to explore the map'),
37
+ ).toBeInTheDocument();
38
+
39
+ fireEvent.click(
40
+ screen.getByRole('button', {
41
+ name: 'Clear filters',
42
+ }),
43
+ );
44
+ expect(onClear).toHaveBeenCalledTimes(1);
45
+ });
46
+ });
@@ -22,10 +22,13 @@ import {
22
22
  getColorForCount,
23
23
  normalizeCountryName,
24
24
  buildCountryCounts,
25
+ clearMapIncompatibleGeographicFilters,
25
26
  filterCountryCounts,
27
+ hasMapIncompatibleGeographicSelection,
26
28
  mapLegendItems,
27
29
  setCountryFilter,
28
30
  } from './utils';
31
+ import NavigatorCatalogueMapState from './NavigatorCatalogueMapState';
29
32
 
30
33
  const messages = defineMessages({
31
34
  toolAvailability: {
@@ -140,7 +143,7 @@ const CountryClickInteractions = ({ ol, countryCounts, onExploreTools }) => {
140
143
  };
141
144
 
142
145
  /* istanbul ignore next */
143
- const NavigatorCatalogueMapViewInner = (props) => {
146
+ const NavigatorCatalogueCountryMap = (props) => {
144
147
  const { geofeatures, projection, ol } = props;
145
148
  const intl = useIntl();
146
149
  const searchContext = useSearchContext();
@@ -270,6 +273,22 @@ const NavigatorCatalogueMapViewInner = (props) => {
270
273
  );
271
274
  };
272
275
 
276
+ const NavigatorCatalogueMapViewInner = (props) => {
277
+ const searchContext = useSearchContext();
278
+ const isMapDisabled = hasMapIncompatibleGeographicSelection(
279
+ searchContext.filters,
280
+ );
281
+
282
+ return (
283
+ <NavigatorCatalogueMapState
284
+ isMapDisabled={isMapDisabled}
285
+ onClear={() => clearMapIncompatibleGeographicFilters(searchContext)}
286
+ >
287
+ <NavigatorCatalogueCountryMap {...props} />
288
+ </NavigatorCatalogueMapState>
289
+ );
290
+ };
291
+
273
292
  /* istanbul ignore next */
274
293
  export default compose(
275
294
  withClientOnly,
@@ -3,7 +3,9 @@ import {
3
3
  getColorForCount,
4
4
  normalizeCountryName,
5
5
  buildCountryCounts,
6
+ clearMapIncompatibleGeographicFilters,
6
7
  filterCountryCounts,
8
+ hasMapIncompatibleGeographicSelection,
7
9
  mapLegendItems,
8
10
  setCountryFilter,
9
11
  } from './utils';
@@ -185,6 +187,92 @@ describe('filterCountryCounts', () => {
185
187
  });
186
188
  });
187
189
 
190
+ describe('hasMapIncompatibleGeographicSelection', () => {
191
+ it('allows no geographic selection and country selections', () => {
192
+ expect(hasMapIncompatibleGeographicSelection()).toBe(false);
193
+ expect(hasMapIncompatibleGeographicSelection([])).toBe(false);
194
+ expect(
195
+ hasMapIncompatibleGeographicSelection([
196
+ {
197
+ field: 'cca_geographic_countries.keyword',
198
+ values: [],
199
+ type: 'any',
200
+ },
201
+ ]),
202
+ ).toBe(false);
203
+ expect(
204
+ hasMapIncompatibleGeographicSelection([
205
+ {
206
+ field: 'cca_geographic_countries.keyword',
207
+ values: ['Germany'],
208
+ type: 'any',
209
+ },
210
+ ]),
211
+ ).toBe(false);
212
+ });
213
+
214
+ it('rejects Global, Europe, and transnational selections', () => {
215
+ expect(
216
+ hasMapIncompatibleGeographicSelection([
217
+ {
218
+ field: 'cca_navigator_geographic_scope',
219
+ values: ['Global', 'Europe'],
220
+ type: 'any',
221
+ },
222
+ {
223
+ field: 'cca_geographic_transnational_region.keyword',
224
+ values: ['Alpine Space'],
225
+ type: 'any',
226
+ },
227
+ ]),
228
+ ).toBe(true);
229
+ });
230
+ });
231
+
232
+ describe('clearMapIncompatibleGeographicFilters', () => {
233
+ it('clears only map-incompatible geographic filters', () => {
234
+ const searchContext = {
235
+ filters: [
236
+ {
237
+ field: 'cca_navigator_geographic_scope',
238
+ values: ['Europe'],
239
+ type: 'any',
240
+ },
241
+ {
242
+ field: 'cca_geographic_transnational_region.keyword',
243
+ values: ['Alpine Space'],
244
+ type: 'any',
245
+ },
246
+ {
247
+ field: 'cca_geographic_countries.keyword',
248
+ values: ['Germany'],
249
+ type: 'any',
250
+ },
251
+ {
252
+ field: 'cca_climate_impacts.keyword',
253
+ values: ['Flooding'],
254
+ type: 'any',
255
+ },
256
+ ],
257
+ removeFilter: jest.fn(),
258
+ };
259
+
260
+ clearMapIncompatibleGeographicFilters(searchContext);
261
+
262
+ expect(searchContext.removeFilter).toHaveBeenCalledTimes(2);
263
+ expect(searchContext.removeFilter).toHaveBeenCalledWith(
264
+ 'cca_navigator_geographic_scope',
265
+ null,
266
+ 'any',
267
+ );
268
+ expect(searchContext.removeFilter).toHaveBeenCalledWith(
269
+ 'cca_geographic_transnational_region.keyword',
270
+ null,
271
+ 'any',
272
+ );
273
+ });
274
+ });
275
+
188
276
  describe('mapLegendItems', () => {
189
277
  it('has 5 legend items', () => {
190
278
  expect(mapLegendItems).toHaveLength(5);
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { Button, Card, Icon } from 'semantic-ui-react';
3
3
  import { defineMessages, useIntl } from 'react-intl';
4
4
  import { Term } from '@eeacms/search/components';
5
- import { useSearchContext } from '@eeacms/search/lib/hocs';
5
+ import { useSearchContext, useViews } from '@eeacms/search/lib/hocs';
6
6
  import { markSelectedFacetValuesFromFilters } from '@eeacms/search/lib/search/helpers';
7
7
  import {
8
8
  CHARACTERISATION_FIELD,
@@ -70,6 +70,7 @@ const FacetOption = ({ field, option, onChange }) => {
70
70
  };
71
71
 
72
72
  const ExpandableFacetGroup = ({
73
+ defaultOpen = false,
73
74
  field,
74
75
  id,
75
76
  label,
@@ -78,11 +79,11 @@ const ExpandableFacetGroup = ({
78
79
  onChange,
79
80
  }) => {
80
81
  const hasSelection = options.some(({ selected }) => selected);
81
- const [isOpen, setIsOpen] = React.useState(hasSelection);
82
+ const [isOpen, setIsOpen] = React.useState(hasSelection || defaultOpen);
82
83
 
83
84
  React.useEffect(() => {
84
- if (hasSelection) setIsOpen(true);
85
- }, [hasSelection]);
85
+ if (hasSelection || defaultOpen) setIsOpen(true);
86
+ }, [defaultOpen, hasSelection]);
86
87
 
87
88
  return (
88
89
  <div className="navigator-geographic-facet-group">
@@ -119,16 +120,10 @@ const ExpandableFacetGroup = ({
119
120
  );
120
121
  };
121
122
 
122
- const NavigatorActiveFilters = ({ filters, onRemove }) => {
123
+ const NavigatorActiveFilters = ({ fields, filters, onRemove }) => {
123
124
  const intl = useIntl();
124
- const geographicFields = [
125
- CHARACTERISATION_FIELD,
126
- TRANSNATIONAL_REGION_FIELD,
127
- COUNTRIES_FIELD,
128
- ];
129
125
  const activeFilters = filters.filter(
130
- ({ field, values }) =>
131
- geographicFields.includes(field) && values?.length > 0,
126
+ ({ field, values }) => fields.includes(field) && values?.length > 0,
132
127
  );
133
128
 
134
129
  if (!activeFilters.length) return null;
@@ -190,6 +185,8 @@ const NavigatorGeographicCoverageFacet = ({
190
185
  }) => {
191
186
  const intl = useIntl();
192
187
  const { addFilter, facets, filters = [], removeFilter } = useSearchContext();
188
+ const { activeViewId } = useViews();
189
+ const isMapView = activeViewId === 'map';
193
190
  const transnationalOptions = getFacetOptions(
194
191
  facets,
195
192
  filters,
@@ -219,6 +216,7 @@ const NavigatorGeographicCoverageFacet = ({
219
216
  }
220
217
 
221
218
  clearFilter(CHARACTERISATION_FIELD);
219
+ if (isMapView) clearFilter(TRANSNATIONAL_REGION_FIELD);
222
220
  addFilter(field, option.value, 'any');
223
221
  };
224
222
 
@@ -226,25 +224,28 @@ const NavigatorGeographicCoverageFacet = ({
226
224
  <fieldset
227
225
  className={`sui-facet searchlib-multiterm-facet navigator-geographic-facet ${className}`}
228
226
  >
229
- <div className="sui-multi-checkbox-facet navigator-geographic-facet-direct-options">
230
- {options.map((option) => {
231
- const directOption = {
232
- ...option,
233
- selected:
234
- option.selected || selectedDirectValues.includes(option.value),
235
- };
227
+ {!isMapView && (
228
+ <div className="sui-multi-checkbox-facet navigator-geographic-facet-direct-options">
229
+ {options.map((option) => {
230
+ const directOption = {
231
+ ...option,
232
+ selected:
233
+ option.selected || selectedDirectValues.includes(option.value),
234
+ };
236
235
 
237
- return (
238
- <FacetOption
239
- key={valueAsText(option.value)}
240
- field={CHARACTERISATION_FIELD}
241
- option={directOption}
242
- onChange={updateDirectFilter}
243
- />
244
- );
245
- })}
246
- </div>
236
+ return (
237
+ <FacetOption
238
+ key={valueAsText(option.value)}
239
+ field={CHARACTERISATION_FIELD}
240
+ option={directOption}
241
+ onChange={updateDirectFilter}
242
+ />
243
+ );
244
+ })}
245
+ </div>
246
+ )}
247
247
  <ExpandableFacetGroup
248
+ defaultOpen={isMapView}
248
249
  field={COUNTRIES_FIELD}
249
250
  id="navigator-geographic-countries"
250
251
  label={intl.formatMessage(countriesLabel)}
@@ -252,17 +253,24 @@ const NavigatorGeographicCoverageFacet = ({
252
253
  options={countryOptions}
253
254
  onChange={(option) => updateChildFilter(COUNTRIES_FIELD, option)}
254
255
  />
255
- <ExpandableFacetGroup
256
- field={TRANSNATIONAL_REGION_FIELD}
257
- id="navigator-geographic-transnational-regions"
258
- label={intl.formatMessage(transnationalRegionsLabel)}
259
- emptyMessage={intl.formatMessage(messages.noOptionsAvailable)}
260
- options={transnationalOptions}
261
- onChange={(option) =>
262
- updateChildFilter(TRANSNATIONAL_REGION_FIELD, option)
263
- }
264
- />
256
+ {!isMapView && (
257
+ <ExpandableFacetGroup
258
+ field={TRANSNATIONAL_REGION_FIELD}
259
+ id="navigator-geographic-transnational-regions"
260
+ label={intl.formatMessage(transnationalRegionsLabel)}
261
+ emptyMessage={intl.formatMessage(messages.noOptionsAvailable)}
262
+ options={transnationalOptions}
263
+ onChange={(option) =>
264
+ updateChildFilter(TRANSNATIONAL_REGION_FIELD, option)
265
+ }
266
+ />
267
+ )}
265
268
  <NavigatorActiveFilters
269
+ fields={[
270
+ CHARACTERISATION_FIELD,
271
+ TRANSNATIONAL_REGION_FIELD,
272
+ COUNTRIES_FIELD,
273
+ ]}
266
274
  filters={filters}
267
275
  onRemove={(field, value, type) => removeFilter(field, value, type)}
268
276
  />
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import '@testing-library/jest-dom';
3
3
  import { fireEvent, render, screen } from '@testing-library/react';
4
4
  import { IntlProvider } from 'react-intl';
5
- import { useSearchContext } from '@eeacms/search/lib/hocs';
5
+ import { useSearchContext, useViews } from '@eeacms/search/lib/hocs';
6
6
  import NavigatorGeographicCoverageFacet from './NavigatorGeographicCoverageFacet';
7
7
  import {
8
8
  CHARACTERISATION_FIELD,
@@ -15,9 +15,11 @@ jest.mock('@eeacms/search/components', () => ({
15
15
  }));
16
16
  jest.mock('@eeacms/search/lib/hocs', () => ({
17
17
  useSearchContext: jest.fn(),
18
+ useViews: jest.fn(),
18
19
  }));
19
20
 
20
21
  const renderFacet = ({
22
+ activeViewId = 'listing',
21
23
  detailedOptions = true,
22
24
  filters = [],
23
25
  onRemove = jest.fn(),
@@ -26,6 +28,8 @@ const renderFacet = ({
26
28
  const addFilter = jest.fn();
27
29
  const removeFilter = jest.fn();
28
30
 
31
+ useViews.mockReturnValue({ activeViewId });
32
+
29
33
  useSearchContext.mockReturnValue({
30
34
  addFilter,
31
35
  removeFilter,
@@ -190,4 +194,64 @@ describe('NavigatorGeographicCoverageFacet', () => {
190
194
  screen.getByRole('button', { name: 'Transnational regions' }),
191
195
  ).toBeEnabled();
192
196
  });
197
+
198
+ it('shows only countries in the map view', () => {
199
+ renderFacet({ activeViewId: 'map' });
200
+
201
+ expect(screen.queryByLabelText('Europe')).not.toBeInTheDocument();
202
+ expect(screen.queryByLabelText('Global')).not.toBeInTheDocument();
203
+ expect(screen.getByRole('button', { name: 'Countries' })).toHaveAttribute(
204
+ 'aria-expanded',
205
+ 'true',
206
+ );
207
+ expect(screen.getByLabelText('Germany')).toBeVisible();
208
+ expect(
209
+ screen.queryByRole('button', { name: 'Transnational regions' }),
210
+ ).not.toBeInTheDocument();
211
+ });
212
+
213
+ it('clears hidden geographic filters when selecting a country on the map', () => {
214
+ const { addFilter, removeFilter } = renderFacet({
215
+ activeViewId: 'map',
216
+ filters: [
217
+ {
218
+ field: TRANSNATIONAL_REGION_FIELD,
219
+ values: ['Alpine Space'],
220
+ type: 'any',
221
+ },
222
+ ],
223
+ });
224
+
225
+ fireEvent.click(screen.getByLabelText('Germany'));
226
+
227
+ expect(removeFilter).toHaveBeenCalledWith(
228
+ CHARACTERISATION_FIELD,
229
+ null,
230
+ 'any',
231
+ );
232
+ expect(removeFilter).toHaveBeenCalledWith(
233
+ TRANSNATIONAL_REGION_FIELD,
234
+ null,
235
+ 'any',
236
+ );
237
+ expect(addFilter).toHaveBeenCalledWith(COUNTRIES_FIELD, 'Germany', 'any');
238
+ });
239
+
240
+ it('shows hidden active geographic filters in the map view', () => {
241
+ renderFacet({
242
+ activeViewId: 'map',
243
+ detailedOptions: false,
244
+ filters: [
245
+ {
246
+ field: CHARACTERISATION_FIELD,
247
+ values: ['Europe'],
248
+ type: 'any',
249
+ },
250
+ ],
251
+ });
252
+
253
+ expect(screen.queryByLabelText('Europe')).not.toBeInTheDocument();
254
+ expect(screen.getByText('Active filters:')).toBeInTheDocument();
255
+ expect(screen.getByText('Europe')).toBeInTheDocument();
256
+ });
193
257
  });
@@ -61,6 +61,30 @@ export const filterCountryCounts = (countryCounts, filters) => {
61
61
  );
62
62
  };
63
63
 
64
+ const mapIncompatibleGeographicFields = new Set([
65
+ 'cca_navigator_geographic_scope',
66
+ 'cca_geographic_transnational_region.keyword',
67
+ ]);
68
+
69
+ export const hasMapIncompatibleGeographicSelection = (filters) =>
70
+ Boolean(
71
+ filters?.find(
72
+ ({ field, values }) =>
73
+ mapIncompatibleGeographicFields.has(field) && values?.length > 0,
74
+ ),
75
+ );
76
+
77
+ export const clearMapIncompatibleGeographicFilters = (searchContext) => {
78
+ searchContext?.filters
79
+ ?.filter(
80
+ ({ field, values }) =>
81
+ mapIncompatibleGeographicFields.has(field) && values?.length > 0,
82
+ )
83
+ .forEach(({ field, type = 'any' }) =>
84
+ searchContext.removeFilter(field, null, type),
85
+ );
86
+ };
87
+
64
88
  export const setCountryFilter = (searchContext, countryName) => {
65
89
  searchContext?.setFilter(
66
90
  'cca_geographic_countries.keyword',
@@ -268,3 +268,70 @@ describe('NavigatorGuideContentView climate hazards', () => {
268
268
  ).toHaveTextContent('Flooding');
269
269
  });
270
270
  });
271
+
272
+ describe('NavigatorGuideContentView actions', () => {
273
+ beforeEach(() => {
274
+ jest.clearAllMocks();
275
+ useSelector.mockReturnValue('en');
276
+ useSearchDriver.mockReturnValue(undefined);
277
+ useGuideFacetOptions.mockReturnValue({
278
+ 'cca_adaptation_sectors.keyword': ['Water'],
279
+ });
280
+ });
281
+
282
+ it('removes a selected option from the current step', () => {
283
+ const removeFilter = jest.fn();
284
+ useAtom.mockReturnValue([0, jest.fn()]);
285
+ useHistory.mockReturnValue({ push: jest.fn() });
286
+
287
+ renderGuide(
288
+ { title: 'Guide tool', href: '/tools/guide-tool' },
289
+ {
290
+ removeFilter,
291
+ facets: {
292
+ 'cca_adaptation_sectors.keyword': [
293
+ { data: [{ value: 'Water', count: 4 }] },
294
+ ],
295
+ },
296
+ },
297
+ );
298
+
299
+ fireEvent.click(screen.getByText('Water'));
300
+
301
+ expect(removeFilter).toHaveBeenCalledWith(
302
+ 'cca_adaptation_sectors.keyword',
303
+ 'Water',
304
+ 'all',
305
+ );
306
+ });
307
+
308
+ it('opens catalogue results with only supported Guide filters', () => {
309
+ const history = { push: jest.fn() };
310
+ useAtom.mockReturnValue([0, jest.fn()]);
311
+ useHistory.mockReturnValue(history);
312
+
313
+ renderGuide(
314
+ { title: 'Guide tool', href: '/tools/guide-tool' },
315
+ {
316
+ filters: [
317
+ {
318
+ field: 'cca_adaptation_sectors.keyword',
319
+ values: ['Water'],
320
+ type: 'all',
321
+ },
322
+ { field: 'unrelated.keyword', values: ['Ignored'], type: 'any' },
323
+ ],
324
+ },
325
+ );
326
+
327
+ fireEvent.click(screen.getByRole('button', { name: 'Skip to results' }));
328
+
329
+ expect(history.push).toHaveBeenCalledWith(
330
+ expect.objectContaining({
331
+ pathname: '/en/navigator/tool-catalogue',
332
+ search: expect.any(String),
333
+ }),
334
+ );
335
+ expect(history.push.mock.calls[0][0].search).not.toContain('unrelated');
336
+ });
337
+ });
@@ -12,6 +12,7 @@ import { useAtom } from 'jotai';
12
12
  import { useDispatch, useSelector } from 'react-redux';
13
13
  import { useHistory, useLocation } from 'react-router-dom';
14
14
  import CompareToolsView from './CompareToolsView';
15
+ import { exportComparisonTable } from '../../Search/NavigatorCatalogue/utils';
15
16
  import { fetchResultsByUid } from './utils';
16
17
 
17
18
  jest.mock('jotai', () => ({
@@ -62,6 +63,11 @@ jest.mock('./utils', () => ({
62
63
  getPathname: (url) => url?.split('?')[0] || '',
63
64
  }));
64
65
 
66
+ jest.mock('../../Search/NavigatorCatalogue/utils', () => ({
67
+ ...jest.requireActual('../../Search/NavigatorCatalogue/utils'),
68
+ exportComparisonTable: jest.fn(),
69
+ }));
70
+
65
71
  describe('CompareToolsView', () => {
66
72
  beforeEach(() => {
67
73
  jest.clearAllMocks();
@@ -246,4 +252,57 @@ describe('CompareToolsView', () => {
246
252
  expect(thumbnail.querySelector('img')).not.toBeInTheDocument();
247
253
  expect(thumbnail.querySelector('.ri-file-line')).toBeInTheDocument();
248
254
  });
255
+
256
+ it('removes a tool from the selection and URL', async () => {
257
+ const setSelectedTools = jest.fn();
258
+ const history = { push: jest.fn() };
259
+ useAtom.mockReturnValue([[], setSelectedTools]);
260
+ useHistory.mockReturnValue(history);
261
+
262
+ render(
263
+ <IntlProvider locale="en">
264
+ <CompareToolsView />
265
+ </IntlProvider>,
266
+ );
267
+ await screen.findByRole('table', { name: 'Compare tools' });
268
+
269
+ fireEvent.click(
270
+ screen.getByRole('button', {
271
+ name: 'Remove Tool one from comparison',
272
+ }),
273
+ );
274
+
275
+ const updateSelection = setSelectedTools.mock.calls[0][0];
276
+ expect(updateSelection([{ uid: 'one' }, { uid: 'two' }])).toEqual([
277
+ { uid: 'two' },
278
+ ]);
279
+ expect(history.push).toHaveBeenCalledWith({
280
+ pathname: '/en/navigator/compare',
281
+ search: '?uid=two',
282
+ hash: '',
283
+ state: {},
284
+ });
285
+ });
286
+
287
+ it('exports comparison data with only applicable table options', async () => {
288
+ render(
289
+ <IntlProvider locale="en">
290
+ <CompareToolsView />
291
+ </IntlProvider>,
292
+ );
293
+ await screen.findByRole('table', { name: 'Compare tools' });
294
+
295
+ fireEvent.click(screen.getByRole('button', { name: 'Export table' }));
296
+
297
+ expect(exportComparisonTable).toHaveBeenCalledTimes(1);
298
+ const [exportedTools] = exportComparisonTable.mock.calls[0];
299
+ expect(exportedTools[0].comparisonOptions).toEqual(
300
+ expect.objectContaining({
301
+ cca_type_of_outputs: ['Maps and graphs'],
302
+ cca_adaptation_support_cycle_step: [
303
+ 'Step 2: Assessing Climate Change Risks and Vulnerabilities',
304
+ ],
305
+ }),
306
+ );
307
+ });
249
308
  });
@@ -620,11 +620,89 @@
620
620
  }
621
621
 
622
622
  .navigator-catalogue-map-loading {
623
- text-align: center;
624
623
  display: flex;
625
624
  flex-direction: column;
626
625
  gap: 1em;
626
+ text-align: center;
627
+ }
628
+
629
+ .navigator-catalogue-map-state {
630
+ position: relative;
631
+ min-height: 600px;
632
+
633
+ .navigator-catalogue-map-content {
634
+ transition:
635
+ filter 0.2s ease,
636
+ opacity 0.2s ease;
637
+ }
638
+
639
+ &.is-disabled .navigator-catalogue-map-content {
640
+ filter: grayscale(35%);
641
+ opacity: 0.4;
642
+ }
643
+ }
644
+
645
+ .navigator-catalogue-map-overlay {
646
+ position: absolute;
647
+ z-index: 1001;
648
+ display: flex;
649
+ align-items: center;
650
+ justify-content: center;
651
+ padding: 2rem;
652
+ background: fade(#fff, 55%);
653
+ inset: 0;
654
+ }
655
+
656
+ .navigator-catalogue-map-notice {
657
+ display: flex;
658
+ width: 100%;
659
+ max-width: 700px;
660
+ box-sizing: border-box;
661
+ flex-direction: column;
662
+ align-items: center;
663
+ justify-content: center;
664
+ padding: 3rem 2rem;
665
+ border: 1px solid fade(#dce7ee, 80%);
666
+ background-color: #fff;
667
+ box-shadow: 0 10px 28px fade(#dce7ee, 18%);
668
+ cursor: default;
669
+ text-align: center;
670
+
671
+ // .map-notice-icon {
672
+ // display: inline-flex;
673
+ // width: 72px;
674
+ // height: 72px;
675
+ // align-items: center;
676
+ // justify-content: center;
677
+ // border-radius: 50%;
678
+ // background: @secondaryColor;
679
+ // color: #fff;
680
+
681
+ // i.icon {
682
+ // width: auto;
683
+ // height: auto;
684
+ // margin: 0 !important;
685
+ // font-size: 2rem;
686
+ // line-height: 1;
687
+ // }
688
+ // }
689
+
690
+ h4 {
691
+ margin: 0 0 0.75rem;
692
+ }
693
+
694
+ p {
695
+ max-width: 620px;
696
+ margin: 0;
697
+ font-size: 1rem;
698
+ line-height: 1.6;
699
+ }
700
+
701
+ .ui.button.map-notice-clear-button {
702
+ margin: 1.5rem 0 0;
703
+ }
627
704
  }
705
+
628
706
  .navigator-catalogue-map-tooltip {
629
707
  position: absolute;
630
708
  z-index: 1000;