@eeacms/volto-cca-policy 1.0.34 → 1.0.36
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 +60 -2
- package/package.json +1 -1
- package/src/components/Search/NavigatorCatalogue/NavigatorGeographicCoverageFacet.jsx +45 -21
- package/src/components/Search/NavigatorCatalogue/NavigatorGeographicCoverageFacet.test.jsx +89 -23
- package/src/components/Search/NavigatorGuide/NavigatorGuideContentView.jsx +37 -16
- package/src/components/Search/NavigatorGuide/NavigatorGuideContentView.test.jsx +73 -3
- package/src/components/Search/NavigatorGuide/utils.js +7 -0
- package/src/components/Search/NavigatorGuide/utils.test.js +30 -1
- package/src/components/manage/Blocks/ChatbotCatalogue/CatalogueChatView.jsx +6 -14
- package/src/components/manage/Blocks/ChatbotCatalogue/CatalogueChatView.test.jsx +0 -9
- package/src/components/theme/CompareTools/CompareToolsView.jsx +82 -8
- package/src/components/theme/CompareTools/CompareToolsView.test.jsx +21 -3
- package/src/index.js +4 -0
- package/src/search/navigator_catalogue/config.js +5 -1
- package/src/search/navigator_catalogue/facets.js +2 -1
- package/src/search/navigator_catalogue/geographicCoverage.js +47 -0
- package/src/search/navigator_catalogue/geographicCoverage.test.js +65 -0
- package/src/search/navigator_guide/config.js +1 -0
- package/theme/globals/navigator.less +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,31 @@ 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.
|
|
7
|
+
### [1.0.36](https://github.com/eea/volto-cca-policy/compare/1.0.35...1.0.36) - 8 October 2026
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat(navigator): update geographic coverage functionality, add runtime mappings and facet value retrieval [kreafox - [`4787c3b`](https://github.com/eea/volto-cca-policy/commit/4787c3b890b69d46ee0806084983ac3c0f36fe43)]
|
|
12
|
+
- feat: add tooltips for usability and functionality criteria in CompareToolsView - refs #304612 [kreafox - [`d23175c`](https://github.com/eea/volto-cca-policy/commit/d23175c7e3e1df85e2c75deba6612be3dd94b88d)]
|
|
13
|
+
|
|
14
|
+
#### :nail_care: Enhancements
|
|
15
|
+
|
|
16
|
+
- refactor(navigator): clean up imports and test files [kreafox - [`4a79b41`](https://github.com/eea/volto-cca-policy/commit/4a79b414b713d387af0fcf7baefd699da747cdac)]
|
|
17
|
+
|
|
18
|
+
### [1.0.35](https://github.com/eea/volto-cca-policy/compare/1.0.34...1.0.35) - 6 October 2026
|
|
19
|
+
|
|
20
|
+
#### :bug: Bug Fixes
|
|
21
|
+
|
|
22
|
+
- fix(test): add waitFor to image load assertions [kreafox - [`84eed2e`](https://github.com/eea/volto-cca-policy/commit/84eed2e15a911d9a8a4a1d20e7d29b3de261bb7d)]
|
|
23
|
+
|
|
24
|
+
#### :nail_care: Enhancements
|
|
25
|
+
|
|
26
|
+
- change(navigator): conditionally render result count based on selections [kreafox - [`0ed5449`](https://github.com/eea/volto-cca-policy/commit/0ed54490c16c9a5b4652fd1de47adbad8d2c8256)]
|
|
27
|
+
- change(NavigatorGuide): implement sorting for guide options - refs #304612 [kreafox - [`4ffcbff`](https://github.com/eea/volto-cca-policy/commit/4ffcbff4f98a2c95b1c88dfc0f662c2906678c13)]
|
|
28
|
+
- change(navigator): implement default sorting in guide me tools listing - refs #304612 [kreafox - [`a9f7fb3`](https://github.com/eea/volto-cca-policy/commit/a9f7fb31b276ed2c28d0fa24b800bebfd0ff5425)]
|
|
29
|
+
- change(CatalogueChatView): remove CompareToolsPanel and update tests - refs #309337 [kreafox - [`a5bb420`](https://github.com/eea/volto-cca-policy/commit/a5bb420e8a3229b1a04fd38f36ee87266bb0e59d)]
|
|
30
|
+
|
|
31
|
+
### [1.0.34](https://github.com/eea/volto-cca-policy/compare/1.0.33...1.0.34) - 6 October 2026
|
|
8
32
|
|
|
9
33
|
#### :nail_care: Enhancements
|
|
10
34
|
|
|
@@ -602,8 +626,42 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
602
626
|
#### :hammer_and_wrench: Others
|
|
603
627
|
|
|
604
628
|
- test: pin Chromium version 149 to work with Cypress [valentinab25 - [`659ec82`](https://github.com/eea/volto-cca-policy/commit/659ec823dd71fe592a3a7accda22effbe1fb07bc)]
|
|
605
|
-
## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.
|
|
629
|
+
## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.33...1.0.0) - 12 June 2026
|
|
630
|
+
|
|
631
|
+
### [1.0.0-alpha.33](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.32...1.0.0-alpha.33) - 7 October 2026
|
|
632
|
+
|
|
633
|
+
### [1.0.0-alpha.32](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.31...1.0.0-alpha.32) - 7 October 2026
|
|
634
|
+
|
|
635
|
+
#### :bug: Bug Fixes
|
|
636
|
+
|
|
637
|
+
- fix(test): add waitFor to image load assertions [kreafox - [`84eed2e`](https://github.com/eea/volto-cca-policy/commit/84eed2e15a911d9a8a4a1d20e7d29b3de261bb7d)]
|
|
638
|
+
|
|
639
|
+
#### :nail_care: Enhancements
|
|
606
640
|
|
|
641
|
+
- change(navigator): conditionally render result count based on selections [kreafox - [`0ed5449`](https://github.com/eea/volto-cca-policy/commit/0ed54490c16c9a5b4652fd1de47adbad8d2c8256)]
|
|
642
|
+
- change(NavigatorGuide): implement sorting for guide options - refs #304612 [kreafox - [`4ffcbff`](https://github.com/eea/volto-cca-policy/commit/4ffcbff4f98a2c95b1c88dfc0f662c2906678c13)]
|
|
643
|
+
- change(navigator): implement default sorting in guide me tools listing - refs #304612 [kreafox - [`a9f7fb3`](https://github.com/eea/volto-cca-policy/commit/a9f7fb31b276ed2c28d0fa24b800bebfd0ff5425)]
|
|
644
|
+
- change(CatalogueChatView): remove CompareToolsPanel and update tests - refs #309337 [kreafox - [`a5bb420`](https://github.com/eea/volto-cca-policy/commit/a5bb420e8a3229b1a04fd38f36ee87266bb0e59d)]
|
|
645
|
+
- change(locales): update translations [kreafox - [`4541062`](https://github.com/eea/volto-cca-policy/commit/45410626484ff0cc536447ff75895b662e3e9554)]
|
|
646
|
+
- change(navigator): update adaptation stage questions [kreafox - [`a1e3705`](https://github.com/eea/volto-cca-policy/commit/a1e3705f1004777ce72a1395f89149c9d8f8e539)]
|
|
647
|
+
- change(locales): add "Governance level" translations [kreafox - [`23ae204`](https://github.com/eea/volto-cca-policy/commit/23ae2046557203a55b2dd40b761ed99e84b78d80)]
|
|
648
|
+
- change(navigator): update implementation level messages to governance level [kreafox - [`f28a93e`](https://github.com/eea/volto-cca-policy/commit/f28a93e408ba24a81ac27229947de1b959a5c42b)]
|
|
649
|
+
|
|
650
|
+
#### :house: Internal changes
|
|
651
|
+
|
|
652
|
+
- style: Automated code fix [eea-jenkins - [`3a0597d`](https://github.com/eea/volto-cca-policy/commit/3a0597dd9358ba2c7f365bef9d3e4ffae36ee68b)]
|
|
653
|
+
- style: Automated code fix [eea-jenkins - [`d91a798`](https://github.com/eea/volto-cca-policy/commit/d91a798f30b29e4bc058cc44ebcc02f07ac216ab)]
|
|
654
|
+
|
|
655
|
+
#### :hammer_and_wrench: Others
|
|
656
|
+
|
|
657
|
+
- update SonarQube messages [iugin - [`cf21375`](https://github.com/eea/volto-cca-policy/commit/cf21375202f946f7adb2959db45cb0e991bfae68)]
|
|
658
|
+
- add mapstyle test [iugin - [`e80b843`](https://github.com/eea/volto-cca-policy/commit/e80b8438e6ff47cb73e43cc8927511e7b8979a8f)]
|
|
659
|
+
- remove test [iugin - [`dcd7fb2`](https://github.com/eea/volto-cca-policy/commit/dcd7fb2873101fd7c0e6612e0eb11012b18412a3)]
|
|
660
|
+
- Refs #296263 - add more tests [iugin - [`898d8e7`](https://github.com/eea/volto-cca-policy/commit/898d8e7878e49a7e777c749e1f88dc6c94ce416b)]
|
|
661
|
+
- Refs #296263 - update tests img alt [iugin - [`1f720a8`](https://github.com/eea/volto-cca-policy/commit/1f720a83b389cb1cb5dbed33c96418fc8def73d9)]
|
|
662
|
+
- Refs #296263 - add tests [iugin - [`c9012e0`](https://github.com/eea/volto-cca-policy/commit/c9012e0e7ca850acda608459e9b78c70f37e7ad4)]
|
|
663
|
+
- Refs #296263 - duplication country profile mapstyle [iugin - [`a6771e9`](https://github.com/eea/volto-cca-policy/commit/a6771e921bcdc6b9ce2f0f9f40413a2fefe78e84)]
|
|
664
|
+
- Refs #296263 - duplicate content [iugin - [`400b661`](https://github.com/eea/volto-cca-policy/commit/400b6618cc3fb50df44f9007253aeaba5e5d36b0)]
|
|
607
665
|
### [1.0.0-alpha.31](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.30...1.0.0-alpha.31) - 2 October 2026
|
|
608
666
|
|
|
609
667
|
#### :rocket: New Features
|
package/package.json
CHANGED
|
@@ -4,11 +4,11 @@ import { defineMessages, useIntl } from 'react-intl';
|
|
|
4
4
|
import { Term } from '@eeacms/search/components';
|
|
5
5
|
import { useSearchContext } from '@eeacms/search/lib/hocs';
|
|
6
6
|
import { markSelectedFacetValuesFromFilters } from '@eeacms/search/lib/search/helpers';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import {
|
|
8
|
+
CHARACTERISATION_FIELD,
|
|
9
|
+
COUNTRIES_FIELD,
|
|
10
|
+
TRANSNATIONAL_REGION_FIELD,
|
|
11
|
+
} from '../../../search/navigator_catalogue/geographicCoverage';
|
|
12
12
|
|
|
13
13
|
const messages = defineMessages({
|
|
14
14
|
noOptionsAvailable: {
|
|
@@ -196,29 +196,53 @@ const NavigatorGeographicCoverageFacet = ({
|
|
|
196
196
|
TRANSNATIONAL_REGION_FIELD,
|
|
197
197
|
);
|
|
198
198
|
const countryOptions = getFacetOptions(facets, filters, COUNTRIES_FIELD);
|
|
199
|
+
const selectedDirectValues =
|
|
200
|
+
filters.find(({ field }) => field === CHARACTERISATION_FIELD)?.values || [];
|
|
201
|
+
|
|
202
|
+
const clearFilter = (field) => removeFilter(field, null, 'any');
|
|
203
|
+
|
|
204
|
+
const updateDirectFilter = (option) => {
|
|
205
|
+
if (option.selected) {
|
|
206
|
+
onRemove(option.value);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
clearFilter(COUNTRIES_FIELD);
|
|
211
|
+
clearFilter(TRANSNATIONAL_REGION_FIELD);
|
|
212
|
+
onSelect(option.value);
|
|
213
|
+
};
|
|
199
214
|
|
|
200
|
-
const updateChildFilter = (field, option) =>
|
|
201
|
-
option.selected
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
const updateChildFilter = (field, option) => {
|
|
216
|
+
if (option.selected) {
|
|
217
|
+
removeFilter(field, option.value, 'any');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
clearFilter(CHARACTERISATION_FIELD);
|
|
222
|
+
addFilter(field, option.value, 'any');
|
|
223
|
+
};
|
|
204
224
|
|
|
205
225
|
return (
|
|
206
226
|
<fieldset
|
|
207
227
|
className={`sui-facet searchlib-multiterm-facet navigator-geographic-facet ${className}`}
|
|
208
228
|
>
|
|
209
229
|
<div className="sui-multi-checkbox-facet navigator-geographic-facet-direct-options">
|
|
210
|
-
{options.map((option) =>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
{options.map((option) => {
|
|
231
|
+
const directOption = {
|
|
232
|
+
...option,
|
|
233
|
+
selected:
|
|
234
|
+
option.selected || selectedDirectValues.includes(option.value),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<FacetOption
|
|
239
|
+
key={valueAsText(option.value)}
|
|
240
|
+
field={CHARACTERISATION_FIELD}
|
|
241
|
+
option={directOption}
|
|
242
|
+
onChange={updateDirectFilter}
|
|
243
|
+
/>
|
|
244
|
+
);
|
|
245
|
+
})}
|
|
222
246
|
</div>
|
|
223
247
|
<ExpandableFacetGroup
|
|
224
248
|
field={COUNTRIES_FIELD}
|
|
@@ -3,10 +3,12 @@ import '@testing-library/jest-dom';
|
|
|
3
3
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
4
4
|
import { IntlProvider } from 'react-intl';
|
|
5
5
|
import { useSearchContext } from '@eeacms/search/lib/hocs';
|
|
6
|
-
import NavigatorGeographicCoverageFacet
|
|
6
|
+
import NavigatorGeographicCoverageFacet from './NavigatorGeographicCoverageFacet';
|
|
7
|
+
import {
|
|
8
|
+
CHARACTERISATION_FIELD,
|
|
7
9
|
COUNTRIES_FIELD,
|
|
8
10
|
TRANSNATIONAL_REGION_FIELD,
|
|
9
|
-
} from '
|
|
11
|
+
} from '../../../search/navigator_catalogue/geographicCoverage';
|
|
10
12
|
|
|
11
13
|
jest.mock('@eeacms/search/components', () => ({
|
|
12
14
|
Term: ({ term }) => <>{term}</>,
|
|
@@ -16,6 +18,7 @@ jest.mock('@eeacms/search/lib/hocs', () => ({
|
|
|
16
18
|
}));
|
|
17
19
|
|
|
18
20
|
const renderFacet = ({
|
|
21
|
+
detailedOptions = true,
|
|
19
22
|
filters = [],
|
|
20
23
|
onRemove = jest.fn(),
|
|
21
24
|
onSelect = jest.fn(),
|
|
@@ -30,18 +33,22 @@ const renderFacet = ({
|
|
|
30
33
|
facets: {
|
|
31
34
|
[TRANSNATIONAL_REGION_FIELD]: [
|
|
32
35
|
{
|
|
33
|
-
data:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
data: detailedOptions
|
|
37
|
+
? [
|
|
38
|
+
{ value: 'Alpine Space', count: 3 },
|
|
39
|
+
{ value: 'Outermost regions', count: 2 },
|
|
40
|
+
]
|
|
41
|
+
: [],
|
|
37
42
|
},
|
|
38
43
|
],
|
|
39
44
|
[COUNTRIES_FIELD]: [
|
|
40
45
|
{
|
|
41
|
-
data:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
data: detailedOptions
|
|
47
|
+
? [
|
|
48
|
+
{ value: 'Germany', count: 4 },
|
|
49
|
+
{ value: 'Spain', count: 2 },
|
|
50
|
+
]
|
|
51
|
+
: [],
|
|
45
52
|
},
|
|
46
53
|
],
|
|
47
54
|
},
|
|
@@ -58,14 +65,8 @@ const renderFacet = ({
|
|
|
58
65
|
defaultMessage: 'Transnational regions',
|
|
59
66
|
}}
|
|
60
67
|
options={[
|
|
61
|
-
{ value: 'Global', count: 5, selected: false },
|
|
62
68
|
{ value: 'Europe', count: 8, selected: false },
|
|
63
|
-
{
|
|
64
|
-
value: 'Macro-Transnational region',
|
|
65
|
-
count: 4,
|
|
66
|
-
selected: false,
|
|
67
|
-
},
|
|
68
|
-
{ value: 'Countries', count: 6, selected: false },
|
|
69
|
+
{ value: 'Global', count: 5, selected: false },
|
|
69
70
|
]}
|
|
70
71
|
/>
|
|
71
72
|
</IntlProvider>,
|
|
@@ -79,15 +80,37 @@ describe('NavigatorGeographicCoverageFacet', () => {
|
|
|
79
80
|
jest.clearAllMocks();
|
|
80
81
|
});
|
|
81
82
|
|
|
82
|
-
it('shows
|
|
83
|
-
|
|
83
|
+
it('shows Global and Europe as direct options', () => {
|
|
84
|
+
renderFacet();
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
screen
|
|
88
|
+
.getAllByRole('checkbox')
|
|
89
|
+
.slice(0, 2)
|
|
90
|
+
.map((option) => option.getAttribute('aria-label')),
|
|
91
|
+
).toEqual(['Europe', 'Global']);
|
|
92
|
+
});
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
it('clears detailed coverage before selecting a direct scope', () => {
|
|
95
|
+
const { onSelect, removeFilter } = renderFacet({
|
|
96
|
+
filters: [
|
|
97
|
+
{
|
|
98
|
+
field: COUNTRIES_FIELD,
|
|
99
|
+
values: ['Germany'],
|
|
100
|
+
type: 'any',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
});
|
|
87
104
|
|
|
88
|
-
fireEvent.click(screen.getByLabelText('
|
|
105
|
+
fireEvent.click(screen.getByLabelText('Europe'));
|
|
89
106
|
|
|
90
|
-
expect(
|
|
107
|
+
expect(removeFilter).toHaveBeenCalledWith(COUNTRIES_FIELD, null, 'any');
|
|
108
|
+
expect(removeFilter).toHaveBeenCalledWith(
|
|
109
|
+
TRANSNATIONAL_REGION_FIELD,
|
|
110
|
+
null,
|
|
111
|
+
'any',
|
|
112
|
+
);
|
|
113
|
+
expect(onSelect).toHaveBeenCalledWith('Europe');
|
|
91
114
|
});
|
|
92
115
|
|
|
93
116
|
it('expands and filters Macro-Transnational region values', () => {
|
|
@@ -124,4 +147,47 @@ describe('NavigatorGeographicCoverageFacet', () => {
|
|
|
124
147
|
'any',
|
|
125
148
|
);
|
|
126
149
|
});
|
|
150
|
+
|
|
151
|
+
it('shows empty detailed coverage groups while Europe is selected', () => {
|
|
152
|
+
const { onRemove } = renderFacet({
|
|
153
|
+
detailedOptions: false,
|
|
154
|
+
filters: [
|
|
155
|
+
{
|
|
156
|
+
field: CHARACTERISATION_FIELD,
|
|
157
|
+
values: ['Europe'],
|
|
158
|
+
type: 'any',
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(screen.getByLabelText('Europe')).toBeChecked();
|
|
164
|
+
fireEvent.click(screen.getByRole('button', { name: 'Countries' }));
|
|
165
|
+
fireEvent.click(
|
|
166
|
+
screen.getByRole('button', { name: 'Transnational regions' }),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
expect(screen.getAllByText('No options available.')).toHaveLength(2);
|
|
170
|
+
|
|
171
|
+
fireEvent.click(screen.getByLabelText('Europe'));
|
|
172
|
+
|
|
173
|
+
expect(onRemove).toHaveBeenCalledWith('Europe');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('keeps detailed coverage available when a country is selected', () => {
|
|
177
|
+
renderFacet({
|
|
178
|
+
filters: [
|
|
179
|
+
{
|
|
180
|
+
field: COUNTRIES_FIELD,
|
|
181
|
+
values: ['Germany'],
|
|
182
|
+
type: 'any',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(screen.getByRole('button', { name: 'Countries' })).toBeEnabled();
|
|
188
|
+
expect(screen.getByLabelText('Germany')).toBeEnabled();
|
|
189
|
+
expect(
|
|
190
|
+
screen.getByRole('button', { name: 'Transnational regions' }),
|
|
191
|
+
).toBeEnabled();
|
|
192
|
+
});
|
|
127
193
|
});
|
|
@@ -5,11 +5,11 @@ import { useHistory } from 'react-router-dom';
|
|
|
5
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
6
6
|
import { Button, Checkbox, Icon, Loader, Message } from 'semantic-ui-react';
|
|
7
7
|
import URLManager from '@elastic/search-ui/lib/cjs/URLManager';
|
|
8
|
-
import { useSearchContext } from '@eeacms/search/lib/hocs';
|
|
8
|
+
import { useSearchContext, useSearchDriver } from '@eeacms/search/lib/hocs';
|
|
9
9
|
import ToolThumbnail from '@eeacms/volto-cca-policy/components/theme/ToolThumbnail/ToolThumbnail';
|
|
10
10
|
import guideSteps from '../../../search/navigator_guide/guideSteps';
|
|
11
11
|
import { navigatorGuideStepAtom } from '../../../state';
|
|
12
|
-
import { mergeGuideOptions,
|
|
12
|
+
import { mergeGuideOptions, sortGuideOptions } from './utils';
|
|
13
13
|
import { rawValueAsArray } from '../NavigatorCatalogue/utils';
|
|
14
14
|
import useGuideFacetOptions from './useGuideFacetOptions';
|
|
15
15
|
import { getNavigatorCataloguePageURL } from '../../Search/NavigatorCatalogue/utils';
|
|
@@ -106,6 +106,7 @@ const NavigatorGuideContentView = ({ appConfig }) => {
|
|
|
106
106
|
const history = useHistory();
|
|
107
107
|
const currentLang = useSelector((state) => state.intl.locale || 'en');
|
|
108
108
|
const searchContext = useSearchContext();
|
|
109
|
+
const driver = useSearchDriver();
|
|
109
110
|
const {
|
|
110
111
|
addFilter,
|
|
111
112
|
facets,
|
|
@@ -113,8 +114,12 @@ const NavigatorGuideContentView = ({ appConfig }) => {
|
|
|
113
114
|
isLoading,
|
|
114
115
|
removeFilter,
|
|
115
116
|
results,
|
|
117
|
+
searchTerm,
|
|
118
|
+
sortDirection,
|
|
119
|
+
sortField,
|
|
116
120
|
totalResults,
|
|
117
121
|
} = searchContext;
|
|
122
|
+
const defaultSortApplied = React.useRef(false);
|
|
118
123
|
const steps = guideSteps;
|
|
119
124
|
const allFacetOptions = useGuideFacetOptions(appConfig, steps);
|
|
120
125
|
const [storedActiveStep, setActiveStep] = useAtom(navigatorGuideStepAtom);
|
|
@@ -137,10 +142,7 @@ const NavigatorGuideContentView = ({ appConfig }) => {
|
|
|
137
142
|
selectedValues,
|
|
138
143
|
hasSelections,
|
|
139
144
|
);
|
|
140
|
-
const options =
|
|
141
|
-
step?.id === 'adaptationStage'
|
|
142
|
-
? sortAdaptationSteps(mergedOptions)
|
|
143
|
-
: mergedOptions;
|
|
145
|
+
const options = sortGuideOptions(mergedOptions, step?.id, currentLang);
|
|
144
146
|
const isLastStep = activeStep === steps.length - 1;
|
|
145
147
|
const selectedStepLabels = steps
|
|
146
148
|
.filter(({ field }) => isStepSelected(filters, field))
|
|
@@ -171,6 +173,23 @@ const NavigatorGuideContentView = ({ appConfig }) => {
|
|
|
171
173
|
}
|
|
172
174
|
}, [activeStep, setActiveStep, storedActiveStep]);
|
|
173
175
|
|
|
176
|
+
React.useEffect(() => {
|
|
177
|
+
if (!driver || defaultSortApplied.current) return;
|
|
178
|
+
defaultSortApplied.current = true;
|
|
179
|
+
|
|
180
|
+
if (
|
|
181
|
+
searchTerm?.trim() ||
|
|
182
|
+
sortField ||
|
|
183
|
+
sortDirection ||
|
|
184
|
+
!appConfig.defaultSort
|
|
185
|
+
) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const [field, direction] = appConfig.defaultSort.split('|');
|
|
190
|
+
if (field && direction) driver.setSort(field, direction);
|
|
191
|
+
}, [appConfig.defaultSort, driver, searchTerm, sortDirection, sortField]);
|
|
192
|
+
|
|
174
193
|
const toggleValue = (value) => {
|
|
175
194
|
if (selectedValues.includes(value)) {
|
|
176
195
|
removeFilter(step.field, value, step.filterType);
|
|
@@ -339,16 +358,18 @@ const NavigatorGuideContentView = ({ appConfig }) => {
|
|
|
339
358
|
{intl.formatMessage(messages.livePreview)}
|
|
340
359
|
</div>
|
|
341
360
|
</div>
|
|
342
|
-
|
|
343
|
-
<
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
361
|
+
{hasSelections && (
|
|
362
|
+
<div className="navigator-guide-result-count">
|
|
363
|
+
<span className="navigator-guide-result-count-value">
|
|
364
|
+
{isLoading ? '…' : totalResults || 0}
|
|
365
|
+
</span>
|
|
366
|
+
<p>
|
|
367
|
+
{intl.formatMessage(messages.toolsMatch, {
|
|
368
|
+
count: totalResults || 0,
|
|
369
|
+
})}
|
|
370
|
+
</p>
|
|
371
|
+
</div>
|
|
372
|
+
)}
|
|
352
373
|
{selectedStepLabels.length > 0 && (
|
|
353
374
|
<p className="navigator-guide-preview-refinements">
|
|
354
375
|
{intl.formatMessage(messages.refinedBy, {
|
|
@@ -5,7 +5,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
5
5
|
import { useAtom } from 'jotai';
|
|
6
6
|
import { useSelector } from 'react-redux';
|
|
7
7
|
import { useHistory } from 'react-router-dom';
|
|
8
|
-
import { useSearchContext } from '@eeacms/search/lib/hocs';
|
|
8
|
+
import { useSearchContext, useSearchDriver } from '@eeacms/search/lib/hocs';
|
|
9
9
|
import NavigatorGuideContentView from './NavigatorGuideContentView';
|
|
10
10
|
import useGuideFacetOptions from './useGuideFacetOptions';
|
|
11
11
|
|
|
@@ -15,7 +15,10 @@ jest.mock('jotai', () => ({
|
|
|
15
15
|
}));
|
|
16
16
|
jest.mock('react-redux', () => ({ useSelector: jest.fn() }));
|
|
17
17
|
jest.mock('react-router-dom', () => ({ useHistory: jest.fn() }));
|
|
18
|
-
jest.mock('@eeacms/search/lib/hocs', () => ({
|
|
18
|
+
jest.mock('@eeacms/search/lib/hocs', () => ({
|
|
19
|
+
useSearchContext: jest.fn(),
|
|
20
|
+
useSearchDriver: jest.fn(),
|
|
21
|
+
}));
|
|
19
22
|
jest.mock('./useGuideFacetOptions', () => ({
|
|
20
23
|
__esModule: true,
|
|
21
24
|
default: jest.fn(),
|
|
@@ -32,11 +35,20 @@ const renderGuide = (result, searchContext = {}) => {
|
|
|
32
35
|
});
|
|
33
36
|
return render(
|
|
34
37
|
<IntlProvider locale="en">
|
|
35
|
-
<NavigatorGuideContentView
|
|
38
|
+
<NavigatorGuideContentView
|
|
39
|
+
appConfig={{
|
|
40
|
+
previewResultsLimit: 3,
|
|
41
|
+
defaultSort: 'title.index|asc',
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
36
44
|
</IntlProvider>,
|
|
37
45
|
);
|
|
38
46
|
};
|
|
39
47
|
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
useSearchDriver.mockReturnValue(undefined);
|
|
50
|
+
});
|
|
51
|
+
|
|
40
52
|
describe('NavigatorGuideContentView thumbnails', () => {
|
|
41
53
|
beforeEach(() => {
|
|
42
54
|
jest.clearAllMocks();
|
|
@@ -94,6 +106,64 @@ describe('NavigatorGuideContentView thumbnails', () => {
|
|
|
94
106
|
});
|
|
95
107
|
});
|
|
96
108
|
|
|
109
|
+
describe('NavigatorGuideContentView empty preview', () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
jest.clearAllMocks();
|
|
112
|
+
useAtom.mockReturnValue([0, jest.fn()]);
|
|
113
|
+
useSelector.mockReturnValue('en');
|
|
114
|
+
useHistory.mockReturnValue({ push: jest.fn() });
|
|
115
|
+
useGuideFacetOptions.mockReturnValue({});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('hides the result count while the preview empty state is visible', () => {
|
|
119
|
+
const { container } = renderGuide(
|
|
120
|
+
{ title: 'Guide tool', href: '/tools/guide-tool' },
|
|
121
|
+
{ filters: [] },
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(
|
|
125
|
+
container.querySelector('.navigator-guide-preview-empty'),
|
|
126
|
+
).toBeInTheDocument();
|
|
127
|
+
expect(
|
|
128
|
+
container.querySelector('.navigator-guide-result-count'),
|
|
129
|
+
).not.toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('NavigatorGuideContentView sorting', () => {
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
jest.clearAllMocks();
|
|
136
|
+
useAtom.mockReturnValue([0, jest.fn()]);
|
|
137
|
+
useSelector.mockReturnValue('en');
|
|
138
|
+
useHistory.mockReturnValue({ push: jest.fn() });
|
|
139
|
+
useGuideFacetOptions.mockReturnValue({});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('starts with title A-Z when URL filters leave the sort unset', () => {
|
|
143
|
+
const driver = { setSort: jest.fn() };
|
|
144
|
+
useSearchDriver.mockReturnValue(driver);
|
|
145
|
+
|
|
146
|
+
renderGuide(
|
|
147
|
+
{ title: 'Guide tool', href: '/tools/guide-tool' },
|
|
148
|
+
{ searchTerm: '', sortField: '', sortDirection: '' },
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(driver.setSort).toHaveBeenCalledWith('title.index', 'asc');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('preserves an active sort', () => {
|
|
155
|
+
const driver = { setSort: jest.fn() };
|
|
156
|
+
useSearchDriver.mockReturnValue(driver);
|
|
157
|
+
|
|
158
|
+
renderGuide(
|
|
159
|
+
{ title: 'Guide tool', href: '/tools/guide-tool' },
|
|
160
|
+
{ sortField: 'title.index', sortDirection: 'desc' },
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
expect(driver.setSort).not.toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
97
167
|
describe('NavigatorGuideContentView adaptation steps', () => {
|
|
98
168
|
beforeEach(() => {
|
|
99
169
|
jest.clearAllMocks();
|
|
@@ -43,3 +43,10 @@ export const sortAdaptationSteps = (options) =>
|
|
|
43
43
|
if (bStep) return 1;
|
|
44
44
|
return a.value.localeCompare(b.value);
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
export const sortGuideOptions = (options, stepId, locale = 'en') =>
|
|
48
|
+
stepId === 'adaptationStage'
|
|
49
|
+
? sortAdaptationSteps(options)
|
|
50
|
+
: [...options].sort((a, b) =>
|
|
51
|
+
a.value.localeCompare(b.value, locale, { sensitivity: 'base' }),
|
|
52
|
+
);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
mergeGuideOptions,
|
|
3
|
+
sortAdaptationSteps,
|
|
4
|
+
sortGuideOptions,
|
|
5
|
+
} from './utils';
|
|
2
6
|
|
|
3
7
|
describe('Navigator Guide utilities', () => {
|
|
4
8
|
it('keeps unavailable values and marks them as disabled', () => {
|
|
@@ -54,4 +58,29 @@ describe('Navigator Guide utilities', () => {
|
|
|
54
58
|
]);
|
|
55
59
|
expect(options[0].value).toBe('Step 10: Review');
|
|
56
60
|
});
|
|
61
|
+
|
|
62
|
+
it('sorts regular guide options alphabetically without mutating them', () => {
|
|
63
|
+
const options = [
|
|
64
|
+
{ value: 'Water', count: 3 },
|
|
65
|
+
{ value: 'Agriculture', count: 8 },
|
|
66
|
+
{ value: 'Biodiversity', count: 5 },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
expect(
|
|
70
|
+
sortGuideOptions(options, 'adaptationSectors').map(({ value }) => value),
|
|
71
|
+
).toEqual(['Agriculture', 'Biodiversity', 'Water']);
|
|
72
|
+
expect(options[0].value).toBe('Water');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('uses numeric cycle-step order for the adaptation stage', () => {
|
|
76
|
+
const options = [
|
|
77
|
+
{ value: 'Step 10: Review', count: 3 },
|
|
78
|
+
{ value: 'Step 2: Assess', count: 8 },
|
|
79
|
+
{ value: 'Step 1: Prepare', count: 5 },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
expect(
|
|
83
|
+
sortGuideOptions(options, 'adaptationStage').map(({ value }) => value),
|
|
84
|
+
).toEqual(['Step 1: Prepare', 'Step 2: Assess', 'Step 10: Review']);
|
|
85
|
+
});
|
|
57
86
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ChatWindow } from '@eeacms/volto-eea-chatbot/ChatBlock/chat';
|
|
3
|
-
import { CompareToolsPanel } from '../../../theme/CompareTools/CompareToolsPanel';
|
|
4
3
|
|
|
5
4
|
import { CcaDocCard } from './DocumentCard';
|
|
6
5
|
import { remarkCcaDocCards } from './docCards';
|
|
@@ -33,24 +32,17 @@ const extraRemarkPlugins = [remarkCcaDocCards, remarkCcaNextSteps];
|
|
|
33
32
|
* turned into inline Navigator-style catalogue cards (`CcaDocCard`),
|
|
34
33
|
* and "Suggested next steps" sections are turned into styled callout boxes.
|
|
35
34
|
*
|
|
36
|
-
* It also renders `<CompareToolsPanel />` so that when users activate the
|
|
37
|
-
* "Compare" checkbox on any document card, the floating comparison panel
|
|
38
|
-
* appears and allows navigating to the side-by-side comparison view.
|
|
39
|
-
*
|
|
40
35
|
* `ChatBlockView` renders this as `<Presentation persona {...blockData} />`,
|
|
41
36
|
* so all block fields arrive as top-level props and are forwarded unchanged.
|
|
42
37
|
*/
|
|
43
38
|
function CatalogueChatView(props) {
|
|
44
39
|
return (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/>
|
|
52
|
-
<CompareToolsPanel />
|
|
53
|
-
</>
|
|
40
|
+
<ChatWindow
|
|
41
|
+
{...props}
|
|
42
|
+
hideSourcesTab
|
|
43
|
+
extraRemarkPlugins={extraRemarkPlugins}
|
|
44
|
+
extraMarkdownComponents={extraMarkdownComponents}
|
|
45
|
+
/>
|
|
54
46
|
);
|
|
55
47
|
}
|
|
56
48
|
|
|
@@ -17,10 +17,6 @@ jest.mock('@eeacms/volto-eea-chatbot/ChatBlock/chat', () => ({
|
|
|
17
17
|
)),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
jest.mock('../../../theme/CompareTools/CompareToolsPanel', () => ({
|
|
21
|
-
CompareToolsPanel: jest.fn(() => <div data-testid="compare-tools-panel" />),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
20
|
describe('CatalogueChatView', () => {
|
|
25
21
|
beforeEach(() => {
|
|
26
22
|
ChatWindow.mockClear();
|
|
@@ -74,9 +70,4 @@ describe('CatalogueChatView', () => {
|
|
|
74
70
|
expect(secondMd).toBe(firstMd);
|
|
75
71
|
expect(secondPlugins).toBe(firstPlugins);
|
|
76
72
|
});
|
|
77
|
-
|
|
78
|
-
it('renders the CompareToolsPanel alongside the chat window', () => {
|
|
79
|
-
const { getByTestId } = render(<CatalogueChatView persona={{}} />);
|
|
80
|
-
expect(getByTestId('compare-tools-panel')).toBeInTheDocument();
|
|
81
|
-
});
|
|
82
73
|
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Icon,
|
|
8
8
|
Loader,
|
|
9
9
|
Message,
|
|
10
|
+
Popup,
|
|
10
11
|
Table,
|
|
11
12
|
Button,
|
|
12
13
|
} from 'semantic-ui-react';
|
|
@@ -84,10 +85,20 @@ const messages = defineMessages({
|
|
|
84
85
|
id: 'Usability',
|
|
85
86
|
defaultMessage: 'Usability',
|
|
86
87
|
},
|
|
88
|
+
usabilityTooltip: {
|
|
89
|
+
id: 'This indicates how easy the tool and its outputs are to understand and interpret, with low = high-level expertise needed, moderate = some prior technical/scientific knowledge needed, high = general user-friendly, minimal technical knowledge needed.',
|
|
90
|
+
defaultMessage:
|
|
91
|
+
'This indicates how easy the tool and its outputs are to understand and interpret, with low = high-level expertise needed, moderate = some prior technical/scientific knowledge needed, high = general user-friendly, minimal technical knowledge needed.',
|
|
92
|
+
},
|
|
87
93
|
functionality: {
|
|
88
94
|
id: 'Functionality',
|
|
89
95
|
defaultMessage: 'Functionality',
|
|
90
96
|
},
|
|
97
|
+
functionalityTooltip: {
|
|
98
|
+
id: 'Number of adaptation support cycle steps supported, from 1 to 6 steps.',
|
|
99
|
+
defaultMessage:
|
|
100
|
+
'Number of adaptation support cycle steps supported, from 1 to 6 steps.',
|
|
101
|
+
},
|
|
91
102
|
spatialScale: {
|
|
92
103
|
id: 'Spatial scale',
|
|
93
104
|
defaultMessage: 'Spatial scale',
|
|
@@ -231,6 +242,53 @@ const FunctionalityScore = ({ value }) => {
|
|
|
231
242
|
);
|
|
232
243
|
};
|
|
233
244
|
|
|
245
|
+
const UsabilityTooltipContent = ({ text }) => {
|
|
246
|
+
const parts = text.match(
|
|
247
|
+
/^(.* with) low = (.*), moderate = (.*), high = (.*)$/,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
if (!parts) return text;
|
|
251
|
+
|
|
252
|
+
const [, introduction, low, moderate, high] = parts;
|
|
253
|
+
const levels = [
|
|
254
|
+
['low', low],
|
|
255
|
+
['moderate', moderate],
|
|
256
|
+
['high', high],
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<div className="usability-tooltip-content">
|
|
261
|
+
<div className="usability-tooltip-introduction">{introduction}:</div>
|
|
262
|
+
{levels.map(([level, description]) => (
|
|
263
|
+
<div className="usability-tooltip-level" key={level}>
|
|
264
|
+
<strong>{level}</strong>
|
|
265
|
+
<span>{` = ${description}`}</span>
|
|
266
|
+
</div>
|
|
267
|
+
))}
|
|
268
|
+
</div>
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const CriterionLabel = ({ label, tooltip, tooltipContent }) => (
|
|
273
|
+
<div className="compare-criteria-title">
|
|
274
|
+
<span>{label}</span>
|
|
275
|
+
<Popup
|
|
276
|
+
className="compare-criteria-popup"
|
|
277
|
+
content={tooltipContent || tooltip}
|
|
278
|
+
position="top left"
|
|
279
|
+
trigger={
|
|
280
|
+
<button
|
|
281
|
+
type="button"
|
|
282
|
+
className="compare-criteria-tooltip"
|
|
283
|
+
aria-label={tooltip}
|
|
284
|
+
>
|
|
285
|
+
<Icon className="ri-information-line" aria-hidden="true" />
|
|
286
|
+
</button>
|
|
287
|
+
}
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
291
|
+
|
|
234
292
|
const getCompareUids = (search) => {
|
|
235
293
|
const params = new URLSearchParams(search);
|
|
236
294
|
|
|
@@ -297,6 +355,7 @@ const CompareToolsView = () => {
|
|
|
297
355
|
const registry = config.settings.searchlib;
|
|
298
356
|
const landingPageURL = getNavigatorCataloguePageURL(currentLang);
|
|
299
357
|
const compareToolsTitle = intl.formatMessage(messages.compareTools);
|
|
358
|
+
const usabilityTooltip = intl.formatMessage(messages.usabilityTooltip);
|
|
300
359
|
const returnURL =
|
|
301
360
|
location.state?.returnURL ||
|
|
302
361
|
getReturnURL(location.search) ||
|
|
@@ -519,11 +578,19 @@ const CompareToolsView = () => {
|
|
|
519
578
|
|
|
520
579
|
<Table.Body>
|
|
521
580
|
<Table.Row>
|
|
522
|
-
<Table.Cell
|
|
581
|
+
<Table.Cell
|
|
582
|
+
as="th"
|
|
583
|
+
scope="row"
|
|
584
|
+
aria-label={intl.formatMessage(messages.usability)}
|
|
585
|
+
>
|
|
523
586
|
<div className="compare-criteria">
|
|
524
|
-
<
|
|
525
|
-
{intl.formatMessage(messages.usability)}
|
|
526
|
-
|
|
587
|
+
<CriterionLabel
|
|
588
|
+
label={intl.formatMessage(messages.usability)}
|
|
589
|
+
tooltip={usabilityTooltip}
|
|
590
|
+
tooltipContent={
|
|
591
|
+
<UsabilityTooltipContent text={usabilityTooltip} />
|
|
592
|
+
}
|
|
593
|
+
/>
|
|
527
594
|
</div>
|
|
528
595
|
</Table.Cell>
|
|
529
596
|
|
|
@@ -537,11 +604,18 @@ const CompareToolsView = () => {
|
|
|
537
604
|
</Table.Row>
|
|
538
605
|
|
|
539
606
|
<Table.Row>
|
|
540
|
-
<Table.Cell
|
|
607
|
+
<Table.Cell
|
|
608
|
+
as="th"
|
|
609
|
+
scope="row"
|
|
610
|
+
aria-label={intl.formatMessage(messages.functionality)}
|
|
611
|
+
>
|
|
541
612
|
<div className="compare-criteria">
|
|
542
|
-
<
|
|
543
|
-
{intl.formatMessage(messages.functionality)}
|
|
544
|
-
|
|
613
|
+
<CriterionLabel
|
|
614
|
+
label={intl.formatMessage(messages.functionality)}
|
|
615
|
+
tooltip={intl.formatMessage(
|
|
616
|
+
messages.functionalityTooltip,
|
|
617
|
+
)}
|
|
618
|
+
/>
|
|
545
619
|
</div>
|
|
546
620
|
</Table.Cell>
|
|
547
621
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import '@testing-library/jest-dom';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
fireEvent,
|
|
5
|
+
render,
|
|
6
|
+
screen,
|
|
7
|
+
waitFor,
|
|
8
|
+
within,
|
|
9
|
+
} from '@testing-library/react';
|
|
4
10
|
import { IntlProvider } from 'react-intl';
|
|
5
11
|
import { useAtom } from 'jotai';
|
|
6
12
|
import { useDispatch, useSelector } from 'react-redux';
|
|
@@ -122,6 +128,16 @@ describe('CompareToolsView', () => {
|
|
|
122
128
|
expect(
|
|
123
129
|
screen.getByRole('rowheader', { name: 'Functionality' }),
|
|
124
130
|
).toHaveAttribute('scope', 'row');
|
|
131
|
+
expect(
|
|
132
|
+
screen.getByRole('button', {
|
|
133
|
+
name: 'This indicates how easy the tool and its outputs are to understand and interpret, with low = high-level expertise needed, moderate = some prior technical/scientific knowledge needed, high = general user-friendly, minimal technical knowledge needed.',
|
|
134
|
+
}),
|
|
135
|
+
).toBeInTheDocument();
|
|
136
|
+
expect(
|
|
137
|
+
screen.getByRole('button', {
|
|
138
|
+
name: 'Number of adaptation support cycle steps supported, from 1 to 6 steps.',
|
|
139
|
+
}),
|
|
140
|
+
).toBeInTheDocument();
|
|
125
141
|
expect(
|
|
126
142
|
screen.getByRole('button', {
|
|
127
143
|
name: 'Remove Tool one from comparison',
|
|
@@ -210,8 +226,10 @@ describe('CompareToolsView', () => {
|
|
|
210
226
|
|
|
211
227
|
fireEvent.load(img);
|
|
212
228
|
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
await waitFor(() => {
|
|
230
|
+
expect(img.style.display).toBe('');
|
|
231
|
+
expect(thumbnail.querySelector('.ri-file-line')).not.toBeInTheDocument();
|
|
232
|
+
});
|
|
215
233
|
});
|
|
216
234
|
|
|
217
235
|
it('falls back to the file icon when the image fails', async () => {
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
|
4
4
|
import loadable from '@loadable/component';
|
|
5
5
|
import { compose } from 'redux';
|
|
6
|
+
import { buildTermFacetAggregationRequest } from '@eeacms/search/lib/search';
|
|
6
7
|
import Sitemap from '@plone/volto/components/theme/Sitemap/Sitemap';
|
|
7
8
|
import DefaultView from '@plone/volto/components/theme/View/DefaultView';
|
|
8
9
|
import SelectAutoCompleteWidget from '@plone/volto/components/manage/Widgets/SelectAutoComplete';
|
|
@@ -57,6 +58,7 @@ import NavigatorCatalogueContentView from './components/Search/NavigatorCatalogu
|
|
|
57
58
|
import NavigatorGeographicCoverageFacet from './components/Search/NavigatorCatalogue/NavigatorGeographicCoverageFacet';
|
|
58
59
|
import NavigatorGuideLayout from './components/Search/NavigatorGuide/NavigatorGuideLayout';
|
|
59
60
|
import NavigatorGuideContentView from './components/Search/NavigatorGuide/NavigatorGuideContentView';
|
|
61
|
+
import { getGeographicCoverageFacetValue } from './search/navigator_catalogue/geographicCoverage';
|
|
60
62
|
|
|
61
63
|
import { langRedirection } from './store/middleware';
|
|
62
64
|
|
|
@@ -437,7 +439,9 @@ const applyConfig = (config) => {
|
|
|
437
439
|
};
|
|
438
440
|
config.settings.searchlib.resolve.NavigatorGeographicCoverageFacet = {
|
|
439
441
|
...config.settings.searchlib.resolve.MultiTermFacet,
|
|
442
|
+
buildRequest: buildTermFacetAggregationRequest,
|
|
440
443
|
component: NavigatorGeographicCoverageFacet,
|
|
444
|
+
getValue: getGeographicCoverageFacetValue,
|
|
441
445
|
};
|
|
442
446
|
config.settings.searchlib.resolve.NavigatorGuideLayout = {
|
|
443
447
|
component: NavigatorGuideLayout,
|
|
@@ -4,6 +4,7 @@ import { getClientProxyAddress } from './../utils';
|
|
|
4
4
|
import { vocab } from './../vocabulary';
|
|
5
5
|
|
|
6
6
|
import facets from './facets';
|
|
7
|
+
import { geographicCoverageRuntimeMappings } from './geographicCoverage';
|
|
7
8
|
import views from './views';
|
|
8
9
|
|
|
9
10
|
const navigatorCatalogueConfig = {
|
|
@@ -46,7 +47,10 @@ export default function installNavigatorCatalogueSearch(config) {
|
|
|
46
47
|
index_name: 'data_searchui',
|
|
47
48
|
host: process.env.RAZZLE_ES_PROXY_ADDR || 'http://localhost:3000',
|
|
48
49
|
vocab,
|
|
49
|
-
runtime_mappings:
|
|
50
|
+
runtime_mappings: {
|
|
51
|
+
...build_runtime_mappings(clusters),
|
|
52
|
+
...geographicCoverageRuntimeMappings,
|
|
53
|
+
},
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
const { navigatorCatalogueSearch } = config.searchui;
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
geographic_countries,
|
|
6
6
|
language,
|
|
7
7
|
} from './../common';
|
|
8
|
+
import { CHARACTERISATION_FIELD } from './geographicCoverage';
|
|
8
9
|
|
|
9
10
|
const messages = defineMessages({
|
|
10
11
|
geographicCoverage: {
|
|
@@ -68,7 +69,7 @@ const facets = [
|
|
|
68
69
|
alwaysVisible: false,
|
|
69
70
|
}),
|
|
70
71
|
multiTermFacet({
|
|
71
|
-
field:
|
|
72
|
+
field: CHARACTERISATION_FIELD,
|
|
72
73
|
factory: 'NavigatorGeographicCoverageFacet',
|
|
73
74
|
isFilterable: false,
|
|
74
75
|
isMulti: true,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const CHARACTERISATION_FIELD = 'cca_navigator_geographic_scope';
|
|
2
|
+
export const TRANSNATIONAL_REGION_FIELD =
|
|
3
|
+
'cca_geographic_transnational_region.keyword';
|
|
4
|
+
export const COUNTRIES_FIELD = 'cca_geographic_countries.keyword';
|
|
5
|
+
|
|
6
|
+
export const geographicCoverageRuntimeMappings = {
|
|
7
|
+
[CHARACTERISATION_FIELD]: {
|
|
8
|
+
type: 'keyword',
|
|
9
|
+
script: {
|
|
10
|
+
source: `
|
|
11
|
+
boolean hasCountries = doc.containsKey('cca_geographic_countries.keyword') && doc['cca_geographic_countries.keyword'].size() != 0;
|
|
12
|
+
boolean hasTransnational = doc.containsKey('cca_geographic_transnational_region.keyword') && doc['cca_geographic_transnational_region.keyword'].size() != 0;
|
|
13
|
+
boolean hasSubnational = doc.containsKey('cca_sub_nationals.keyword') && doc['cca_sub_nationals.keyword'].size() != 0;
|
|
14
|
+
boolean hasCity = doc.containsKey('cca_city.keyword') && doc['cca_city.keyword'].size() != 0;
|
|
15
|
+
|
|
16
|
+
if (!hasCountries && !hasTransnational && !hasSubnational && !hasCity) {
|
|
17
|
+
for (def value : doc['cca_geographic_characterisation.keyword']) {
|
|
18
|
+
emit(value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getGeographicCoverageFacetValue = ({
|
|
27
|
+
aggregations,
|
|
28
|
+
fieldName,
|
|
29
|
+
}) => {
|
|
30
|
+
const aggregation = aggregations?.[fieldName];
|
|
31
|
+
if (!aggregation) return undefined;
|
|
32
|
+
|
|
33
|
+
const counts = Object.fromEntries(
|
|
34
|
+
(aggregation.buckets || []).map(({ key, doc_count }) => [key, doc_count]),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
field: fieldName,
|
|
40
|
+
type: 'value',
|
|
41
|
+
data: ['Europe', 'Global'].map((value) => ({
|
|
42
|
+
value,
|
|
43
|
+
count: counts[value] || 0,
|
|
44
|
+
})),
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHARACTERISATION_FIELD,
|
|
3
|
+
geographicCoverageRuntimeMappings,
|
|
4
|
+
getGeographicCoverageFacetValue,
|
|
5
|
+
} from './geographicCoverage';
|
|
6
|
+
|
|
7
|
+
describe('geographic coverage runtime mapping', () => {
|
|
8
|
+
it('defines an exclusive keyword scope from all specific location fields', () => {
|
|
9
|
+
const mapping = geographicCoverageRuntimeMappings[CHARACTERISATION_FIELD];
|
|
10
|
+
|
|
11
|
+
expect(mapping.type).toBe('keyword');
|
|
12
|
+
expect(mapping.script.source).toContain(
|
|
13
|
+
"doc['cca_geographic_countries.keyword']",
|
|
14
|
+
);
|
|
15
|
+
expect(mapping.script.source).toContain(
|
|
16
|
+
"doc['cca_geographic_transnational_region.keyword']",
|
|
17
|
+
);
|
|
18
|
+
expect(mapping.script.source).toContain("doc['cca_sub_nationals.keyword']");
|
|
19
|
+
expect(mapping.script.source).toContain("doc['cca_city.keyword']");
|
|
20
|
+
expect(mapping.script.source).toContain(
|
|
21
|
+
"doc['cca_geographic_characterisation.keyword']",
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('geographic coverage facet value', () => {
|
|
27
|
+
it('keeps the facet available with zero counts when its buckets are empty', () => {
|
|
28
|
+
expect(
|
|
29
|
+
getGeographicCoverageFacetValue({
|
|
30
|
+
aggregations: {
|
|
31
|
+
[CHARACTERISATION_FIELD]: { buckets: [] },
|
|
32
|
+
},
|
|
33
|
+
fieldName: CHARACTERISATION_FIELD,
|
|
34
|
+
}),
|
|
35
|
+
).toEqual([
|
|
36
|
+
{
|
|
37
|
+
field: CHARACTERISATION_FIELD,
|
|
38
|
+
type: 'value',
|
|
39
|
+
data: [
|
|
40
|
+
{ value: 'Europe', count: 0 },
|
|
41
|
+
{ value: 'Global', count: 0 },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns Europe and Global alphabetically with their counts', () => {
|
|
48
|
+
expect(
|
|
49
|
+
getGeographicCoverageFacetValue({
|
|
50
|
+
aggregations: {
|
|
51
|
+
[CHARACTERISATION_FIELD]: {
|
|
52
|
+
buckets: [
|
|
53
|
+
{ key: 'Global', doc_count: 25 },
|
|
54
|
+
{ key: 'Europe', doc_count: 20 },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
fieldName: CHARACTERISATION_FIELD,
|
|
59
|
+
})[0].data,
|
|
60
|
+
).toEqual([
|
|
61
|
+
{ value: 'Europe', count: 20 },
|
|
62
|
+
{ value: 'Global', count: 25 },
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -354,6 +354,22 @@
|
|
|
354
354
|
margin-left: 0.5rem !important;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
.ui.popup.compare-criteria-popup {
|
|
358
|
+
max-width: min(450px, calc(100vw - 2rem));
|
|
359
|
+
|
|
360
|
+
.usability-tooltip-introduction {
|
|
361
|
+
margin-bottom: 0.65rem;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.usability-tooltip-level {
|
|
365
|
+
strong {
|
|
366
|
+
font-weight: 700;
|
|
367
|
+
text-transform: capitalize;
|
|
368
|
+
color: @secondaryColor;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
357
373
|
.navigator-catalogue-compare-view {
|
|
358
374
|
overflow-x: auto;
|
|
359
375
|
|
|
@@ -431,6 +447,20 @@
|
|
|
431
447
|
font-weight: 500;
|
|
432
448
|
}
|
|
433
449
|
|
|
450
|
+
.compare-criteria-tooltip {
|
|
451
|
+
padding: 0;
|
|
452
|
+
border: 0;
|
|
453
|
+
margin-left: 0.4rem;
|
|
454
|
+
background: transparent;
|
|
455
|
+
color: @primaryColor;
|
|
456
|
+
cursor: pointer;
|
|
457
|
+
line-height: 1;
|
|
458
|
+
|
|
459
|
+
i.icon {
|
|
460
|
+
margin: 0;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
434
464
|
.usability-value,
|
|
435
465
|
.functionality-value {
|
|
436
466
|
color: @secondaryColor;
|