@eeacms/volto-cca-policy 1.0.18 → 1.0.19
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 +15 -0
- package/package.json +1 -1
- package/src/components/Search/NavigatorCatalogue/utils.js +0 -12
- package/src/components/Search/NavigatorCatalogue/utils.test.js +0 -4
- package/src/components/theme/CompareTools/CompareToolsView.jsx +150 -81
- package/src/components/theme/CompareTools/CompareToolsView.test.jsx +72 -9
- package/src/components/theme/CompareTools/utils.js +33 -1
- package/src/components/theme/CompareTools/utils.test.jsx +128 -17
- package/theme/globals/navigator.less +42 -7
- package/theme/globals/views.less +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ 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.19](https://github.com/eea/volto-cca-policy/compare/1.0.18...1.0.19) - 10 September 2026
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: enhance comparison table with new output type and adaptation support cycle step features [kreafox - [`40e56ad`](https://github.com/eea/volto-cca-policy/commit/40e56adb144e283ce18bf36e87db5c36f286ab9c)]
|
|
12
|
+
- feat: enhance CompareToolsView with new comparison options and styling [kreafox - [`a912b50`](https://github.com/eea/volto-cca-policy/commit/a912b5006af55aefbd024b494cd9c810bfb22684)]
|
|
13
|
+
|
|
14
|
+
#### :nail_care: Enhancements
|
|
15
|
+
|
|
16
|
+
- change: comparison table export by removing unused fields and improve export functionality [kreafox - [`f9ee0f7`](https://github.com/eea/volto-cca-policy/commit/f9ee0f74d02153b9a233ef13264c026503534038)]
|
|
17
|
+
- change: update comparison options handling and enhance tests for adaptation support cycle steps [kreafox - [`fc2736b`](https://github.com/eea/volto-cca-policy/commit/fc2736b293e938b3e13367d96d3c8158c5435f49)]
|
|
18
|
+
|
|
19
|
+
#### :hammer_and_wrench: Others
|
|
20
|
+
|
|
21
|
+
- test: improve test readability and structure in Compare Tools utilities [kreafox - [`b6fc550`](https://github.com/eea/volto-cca-policy/commit/b6fc550db8f3ddbadf093c56596968caf94900ad)]
|
|
7
22
|
### [1.0.18](https://github.com/eea/volto-cca-policy/compare/1.0.17...1.0.18) - 10 September 2026
|
|
8
23
|
|
|
9
24
|
#### :rocket: New Features
|
package/package.json
CHANGED
|
@@ -124,12 +124,6 @@ export const exportComparisonTable = (tools, getToolField) => {
|
|
|
124
124
|
formatFunctionalityScore(getToolField(tool, 'functionality')),
|
|
125
125
|
),
|
|
126
126
|
],
|
|
127
|
-
[
|
|
128
|
-
'Spatial scale',
|
|
129
|
-
...tools.map((tool) =>
|
|
130
|
-
arrayFieldToString(getToolField(tool, 'cca_geographical_scale')),
|
|
131
|
-
),
|
|
132
|
-
],
|
|
133
127
|
[
|
|
134
128
|
'Output type',
|
|
135
129
|
...tools.map((tool) =>
|
|
@@ -144,12 +138,6 @@ export const exportComparisonTable = (tools, getToolField) => {
|
|
|
144
138
|
),
|
|
145
139
|
),
|
|
146
140
|
],
|
|
147
|
-
[
|
|
148
|
-
'Sector',
|
|
149
|
-
...tools.map((tool) =>
|
|
150
|
-
arrayFieldToString(getToolField(tool, 'cca_adaptation_sectors')),
|
|
151
|
-
),
|
|
152
|
-
],
|
|
153
141
|
];
|
|
154
142
|
|
|
155
143
|
downloadCsv('navigator-catalogue-comparison.csv', rows);
|
|
@@ -104,10 +104,8 @@ describe('Navigator Catalogue utilities', () => {
|
|
|
104
104
|
values: {
|
|
105
105
|
accessibility_and_usability: 'High',
|
|
106
106
|
functionality: 5,
|
|
107
|
-
cca_geographical_scale: ['Local'],
|
|
108
107
|
cca_type_of_outputs: ['Maps'],
|
|
109
108
|
cca_adaptation_support_cycle_step: ['Step 1'],
|
|
110
|
-
cca_adaptation_sectors: ['Water'],
|
|
111
109
|
},
|
|
112
110
|
},
|
|
113
111
|
];
|
|
@@ -118,9 +116,7 @@ describe('Navigator Catalogue utilities', () => {
|
|
|
118
116
|
expect(csv).toContain('"Criteria","Tool A"');
|
|
119
117
|
expect(csv).toContain('"Usability","High"');
|
|
120
118
|
expect(csv).toContain('"Functionality","5/6"');
|
|
121
|
-
expect(csv).toContain('"Spatial scale","Local"');
|
|
122
119
|
expect(csv).toContain('"Output type","Maps"');
|
|
123
120
|
expect(csv).toContain('"Adaptation support cycle step","Step 1"');
|
|
124
|
-
expect(csv).toContain('"Sector","Water"');
|
|
125
121
|
});
|
|
126
122
|
});
|
|
@@ -92,6 +92,14 @@ const messages = defineMessages({
|
|
|
92
92
|
id: 'Spatial scale',
|
|
93
93
|
defaultMessage: 'Spatial scale',
|
|
94
94
|
},
|
|
95
|
+
available: {
|
|
96
|
+
id: 'Available',
|
|
97
|
+
defaultMessage: 'Available',
|
|
98
|
+
},
|
|
99
|
+
unavailable: {
|
|
100
|
+
id: 'Not available',
|
|
101
|
+
defaultMessage: 'Not available',
|
|
102
|
+
},
|
|
95
103
|
outputType: {
|
|
96
104
|
id: 'Output type',
|
|
97
105
|
defaultMessage: 'Output type',
|
|
@@ -108,36 +116,99 @@ const getToolField = (tool, field) =>
|
|
|
108
116
|
const getToolFieldDisplay = (tool, field) =>
|
|
109
117
|
asArray(getToolField(tool, field)).join(', ') || '—';
|
|
110
118
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</span>
|
|
123
|
-
))}
|
|
124
|
-
</div>
|
|
125
|
-
) : (
|
|
126
|
-
'—'
|
|
119
|
+
const getMetadataItem = (item) => item?.title || item?.token || item;
|
|
120
|
+
|
|
121
|
+
const getComparisonOptions = (tools, field) => {
|
|
122
|
+
const fetchedOptions = tools[0]?.comparisonOptions?.[field];
|
|
123
|
+
const values = fetchedOptions?.length
|
|
124
|
+
? fetchedOptions
|
|
125
|
+
: tools.flatMap((tool) => asArray(getToolField(tool, field)));
|
|
126
|
+
|
|
127
|
+
return [...new Set(values.map(getMetadataItem).filter(Boolean))].sort(
|
|
128
|
+
(first, second) =>
|
|
129
|
+
first.localeCompare(second, undefined, { numeric: true }),
|
|
127
130
|
);
|
|
128
131
|
};
|
|
129
132
|
|
|
130
|
-
const
|
|
131
|
-
|
|
133
|
+
const getApplicableComparisonOptions = (tools, field) =>
|
|
134
|
+
[
|
|
135
|
+
...new Set(
|
|
136
|
+
tools
|
|
137
|
+
.flatMap((tool) => asArray(getToolField(tool, field)))
|
|
138
|
+
.map(getMetadataItem)
|
|
139
|
+
.filter(Boolean),
|
|
140
|
+
),
|
|
141
|
+
].sort((first, second) =>
|
|
142
|
+
first.localeCompare(second, undefined, { numeric: true }),
|
|
143
|
+
);
|
|
132
144
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
const getExportTools = (tools) => {
|
|
146
|
+
const comparisonOptions = {
|
|
147
|
+
...(tools[0]?.comparisonOptions || {}),
|
|
148
|
+
cca_type_of_outputs: getApplicableComparisonOptions(
|
|
149
|
+
tools,
|
|
150
|
+
'cca_type_of_outputs',
|
|
151
|
+
),
|
|
152
|
+
cca_adaptation_support_cycle_step: getApplicableComparisonOptions(
|
|
153
|
+
tools,
|
|
154
|
+
'cca_adaptation_support_cycle_step',
|
|
155
|
+
),
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return tools.map((tool) => ({
|
|
159
|
+
...tool,
|
|
160
|
+
comparisonOptions,
|
|
161
|
+
}));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const ComparisonRows = ({ tools, field, label, options }) => {
|
|
165
|
+
const intl = useIntl();
|
|
166
|
+
const selectedItems = tools.map(
|
|
167
|
+
(tool) => new Set(asArray(getToolField(tool, field)).map(getMetadataItem)),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<Table.Body>
|
|
172
|
+
{(options.length ? options : [null]).map((item, index) => (
|
|
173
|
+
<Table.Row key={item || field}>
|
|
174
|
+
{index === 0 && (
|
|
175
|
+
<Table.Cell as="th" scope="rowgroup" rowSpan={options.length || 1}>
|
|
176
|
+
<div className="compare-criteria-title">{label}</div>
|
|
177
|
+
</Table.Cell>
|
|
178
|
+
)}
|
|
179
|
+
{tools.map((tool, toolIndex) =>
|
|
180
|
+
item ? (
|
|
181
|
+
<React.Fragment key={tool.id}>
|
|
182
|
+
<Table.Cell
|
|
183
|
+
className={`compare-option-label${
|
|
184
|
+
selectedItems[toolIndex].has(item) ? ' available' : ''
|
|
185
|
+
}`}
|
|
186
|
+
>
|
|
187
|
+
{intl.formatMessage({ id: item, defaultMessage: item })}
|
|
188
|
+
</Table.Cell>
|
|
189
|
+
<Table.Cell className="compare-option-status">
|
|
190
|
+
{selectedItems[toolIndex].has(item) ? (
|
|
191
|
+
<Icon
|
|
192
|
+
className="ri-check-line"
|
|
193
|
+
aria-label={intl.formatMessage(messages.available)}
|
|
194
|
+
/>
|
|
195
|
+
) : (
|
|
196
|
+
<Icon
|
|
197
|
+
className="ri-subtract-line"
|
|
198
|
+
aria-label={intl.formatMessage(messages.unavailable)}
|
|
199
|
+
/>
|
|
200
|
+
)}
|
|
201
|
+
</Table.Cell>
|
|
202
|
+
</React.Fragment>
|
|
203
|
+
) : (
|
|
204
|
+
<Table.Cell key={tool.id} colSpan={2}>
|
|
205
|
+
—
|
|
206
|
+
</Table.Cell>
|
|
207
|
+
),
|
|
208
|
+
)}
|
|
209
|
+
</Table.Row>
|
|
137
210
|
))}
|
|
138
|
-
</
|
|
139
|
-
) : (
|
|
140
|
-
'—'
|
|
211
|
+
</Table.Body>
|
|
141
212
|
);
|
|
142
213
|
};
|
|
143
214
|
|
|
@@ -185,6 +256,7 @@ const getToolHref = (result) => result?.href || result?._result?.id?.raw || '';
|
|
|
185
256
|
const getTools = async (uids, registry) => {
|
|
186
257
|
try {
|
|
187
258
|
const results = await fetchResultsByUid(uids, registry);
|
|
259
|
+
const comparisonOptions = results.comparisonOptions || {};
|
|
188
260
|
const resultsByUid = new Map(
|
|
189
261
|
results.map((result) => [getCompareToolUid(result), result]),
|
|
190
262
|
);
|
|
@@ -198,6 +270,7 @@ const getTools = async (uids, registry) => {
|
|
|
198
270
|
href: getToolHref(result),
|
|
199
271
|
result,
|
|
200
272
|
error: !result,
|
|
273
|
+
comparisonOptions,
|
|
201
274
|
};
|
|
202
275
|
});
|
|
203
276
|
} catch {
|
|
@@ -220,6 +293,7 @@ const CompareToolsView = () => {
|
|
|
220
293
|
() => getCompareUids(location.search),
|
|
221
294
|
[location.search],
|
|
222
295
|
);
|
|
296
|
+
|
|
223
297
|
const registry = config.settings.searchlib;
|
|
224
298
|
const landingPageURL = getNavigatorCataloguePageURL(currentLang);
|
|
225
299
|
const compareToolsTitle = intl.formatMessage(messages.compareTools);
|
|
@@ -291,6 +365,15 @@ const CompareToolsView = () => {
|
|
|
291
365
|
|
|
292
366
|
const failedTools = tools.filter((tool) => tool.error);
|
|
293
367
|
const visibleTools = tools.filter((tool) => !tool.error);
|
|
368
|
+
const outputOptions = getComparisonOptions(
|
|
369
|
+
visibleTools,
|
|
370
|
+
'cca_type_of_outputs',
|
|
371
|
+
);
|
|
372
|
+
const cycleOptions = getComparisonOptions(
|
|
373
|
+
visibleTools,
|
|
374
|
+
'cca_adaptation_support_cycle_step',
|
|
375
|
+
);
|
|
376
|
+
|
|
294
377
|
const visibleToolsCount = visibleTools.length;
|
|
295
378
|
const hasLoadedRequestedTools = tools.length === ids.length;
|
|
296
379
|
const hasEnoughTools = visibleToolsCount >= 2;
|
|
@@ -345,10 +428,16 @@ const CompareToolsView = () => {
|
|
|
345
428
|
<Icon className="ri-arrow-left-line" />
|
|
346
429
|
{intl.formatMessage(messages.backToResults)}
|
|
347
430
|
</Button>
|
|
431
|
+
|
|
348
432
|
<Button
|
|
349
433
|
className="primary inverted"
|
|
350
434
|
disabled={!hasEnoughTools}
|
|
351
|
-
onClick={() =>
|
|
435
|
+
onClick={() =>
|
|
436
|
+
exportComparisonTable(
|
|
437
|
+
getExportTools(visibleTools),
|
|
438
|
+
getToolField,
|
|
439
|
+
)
|
|
440
|
+
}
|
|
352
441
|
>
|
|
353
442
|
<Icon className="ri-download-2-line" />
|
|
354
443
|
{intl.formatMessage(messages.exportTable)}
|
|
@@ -372,6 +461,16 @@ const CompareToolsView = () => {
|
|
|
372
461
|
|
|
373
462
|
{!isLoading && hasEnoughTools && (
|
|
374
463
|
<Table celled aria-label={compareToolsTitle} unstackable>
|
|
464
|
+
<colgroup>
|
|
465
|
+
<col className="compare-criteria-column" />
|
|
466
|
+
{visibleTools.map((tool) => (
|
|
467
|
+
<React.Fragment key={tool.id}>
|
|
468
|
+
<col />
|
|
469
|
+
<col className="compare-status-column" />
|
|
470
|
+
</React.Fragment>
|
|
471
|
+
))}
|
|
472
|
+
</colgroup>
|
|
473
|
+
|
|
375
474
|
<Table.Header>
|
|
376
475
|
<Table.Row>
|
|
377
476
|
<Table.HeaderCell>
|
|
@@ -379,15 +478,18 @@ const CompareToolsView = () => {
|
|
|
379
478
|
{intl.formatMessage(messages.criteria)}
|
|
380
479
|
</span>
|
|
381
480
|
</Table.HeaderCell>
|
|
481
|
+
|
|
382
482
|
{visibleTools.map((tool) => (
|
|
383
|
-
<Table.HeaderCell key={tool.id}>
|
|
483
|
+
<Table.HeaderCell key={tool.id} colSpan={2}>
|
|
384
484
|
<div className="compare-tool-header">
|
|
385
485
|
<div className="compare-tool-title-row">
|
|
386
486
|
<ToolThumbnail result={tool.result} />
|
|
487
|
+
|
|
387
488
|
<div className="compare-tool-title" title={tool.title}>
|
|
388
489
|
{tool.title}
|
|
389
490
|
</div>
|
|
390
491
|
</div>
|
|
492
|
+
|
|
391
493
|
<div className="compare-tool-actions">
|
|
392
494
|
{tool.href && (
|
|
393
495
|
<UniversalLink
|
|
@@ -398,6 +500,7 @@ const CompareToolsView = () => {
|
|
|
398
500
|
<Icon className="ri-external-link-line" />
|
|
399
501
|
</UniversalLink>
|
|
400
502
|
)}
|
|
503
|
+
|
|
401
504
|
<Button
|
|
402
505
|
className="icon compare-tool-clear"
|
|
403
506
|
aria-label={intl.formatMessage(messages.removeTool, {
|
|
@@ -413,6 +516,7 @@ const CompareToolsView = () => {
|
|
|
413
516
|
))}
|
|
414
517
|
</Table.Row>
|
|
415
518
|
</Table.Header>
|
|
519
|
+
|
|
416
520
|
<Table.Body>
|
|
417
521
|
<Table.Row>
|
|
418
522
|
<Table.Cell as="th" scope="row">
|
|
@@ -422,14 +526,16 @@ const CompareToolsView = () => {
|
|
|
422
526
|
</div>
|
|
423
527
|
</div>
|
|
424
528
|
</Table.Cell>
|
|
529
|
+
|
|
425
530
|
{visibleTools.map((tool) => (
|
|
426
|
-
<Table.Cell key={`usability-${tool.id}`}>
|
|
531
|
+
<Table.Cell key={`usability-${tool.id}`} colSpan={2}>
|
|
427
532
|
<div className="usability-value">
|
|
428
533
|
{getToolFieldDisplay(tool, 'accessibility_and_usability')}
|
|
429
534
|
</div>
|
|
430
535
|
</Table.Cell>
|
|
431
536
|
))}
|
|
432
537
|
</Table.Row>
|
|
538
|
+
|
|
433
539
|
<Table.Row>
|
|
434
540
|
<Table.Cell as="th" scope="row">
|
|
435
541
|
<div className="compare-criteria">
|
|
@@ -438,8 +544,9 @@ const CompareToolsView = () => {
|
|
|
438
544
|
</div>
|
|
439
545
|
</div>
|
|
440
546
|
</Table.Cell>
|
|
547
|
+
|
|
441
548
|
{visibleTools.map((tool) => (
|
|
442
|
-
<Table.Cell key={`functionality-${tool.id}`}>
|
|
549
|
+
<Table.Cell key={`functionality-${tool.id}`} colSpan={2}>
|
|
443
550
|
<div className="functionality-value">
|
|
444
551
|
<FunctionalityScore
|
|
445
552
|
value={getToolField(tool, 'functionality')}
|
|
@@ -448,59 +555,21 @@ const CompareToolsView = () => {
|
|
|
448
555
|
</Table.Cell>
|
|
449
556
|
))}
|
|
450
557
|
</Table.Row>
|
|
451
|
-
{/* <Table.Row>
|
|
452
|
-
<Table.Cell as="th" scope="row">
|
|
453
|
-
<div className="compare-criteria">
|
|
454
|
-
<div className="compare-criteria-title">
|
|
455
|
-
{intl.formatMessage(messages.spatialScale)}
|
|
456
|
-
</div>
|
|
457
|
-
</div>
|
|
458
|
-
</Table.Cell>
|
|
459
|
-
{visibleTools.map((tool) => (
|
|
460
|
-
<Table.Cell key={`spatial-scale-${tool.id}`}>
|
|
461
|
-
{getToolFieldDisplay(tool, 'cca_geographical_scale')}
|
|
462
|
-
</Table.Cell>
|
|
463
|
-
))}
|
|
464
|
-
</Table.Row> */}
|
|
465
|
-
<Table.Row>
|
|
466
|
-
<Table.Cell as="th" scope="row">
|
|
467
|
-
<div className="compare-criteria">
|
|
468
|
-
<div className="compare-criteria-title">
|
|
469
|
-
{intl.formatMessage(messages.outputType)}
|
|
470
|
-
</div>
|
|
471
|
-
</div>
|
|
472
|
-
</Table.Cell>
|
|
473
|
-
{visibleTools.map((tool) => (
|
|
474
|
-
<Table.Cell key={`output-type-${tool.id}`}>
|
|
475
|
-
<MetadataTags
|
|
476
|
-
value={getToolField(tool, 'cca_type_of_outputs')}
|
|
477
|
-
/>
|
|
478
|
-
</Table.Cell>
|
|
479
|
-
))}
|
|
480
|
-
</Table.Row>
|
|
481
|
-
<Table.Row>
|
|
482
|
-
<Table.Cell as="th" scope="row">
|
|
483
|
-
<div className="compare-criteria">
|
|
484
|
-
<div className="compare-criteria-title">
|
|
485
|
-
{intl.formatMessage(messages.adaptationSupportCycleStep)}
|
|
486
|
-
</div>
|
|
487
|
-
</div>
|
|
488
|
-
</Table.Cell>
|
|
489
|
-
{visibleTools.map((tool) => (
|
|
490
|
-
<Table.Cell key={`adaptation-support-cycle-step-${tool.id}`}>
|
|
491
|
-
<FieldValueList
|
|
492
|
-
label={intl.formatMessage(
|
|
493
|
-
messages.adaptationSupportCycleStep,
|
|
494
|
-
)}
|
|
495
|
-
value={getToolField(
|
|
496
|
-
tool,
|
|
497
|
-
'cca_adaptation_support_cycle_step',
|
|
498
|
-
)}
|
|
499
|
-
/>
|
|
500
|
-
</Table.Cell>
|
|
501
|
-
))}
|
|
502
|
-
</Table.Row>
|
|
503
558
|
</Table.Body>
|
|
559
|
+
|
|
560
|
+
<ComparisonRows
|
|
561
|
+
tools={visibleTools}
|
|
562
|
+
field="cca_type_of_outputs"
|
|
563
|
+
label={intl.formatMessage(messages.outputType)}
|
|
564
|
+
options={outputOptions}
|
|
565
|
+
/>
|
|
566
|
+
|
|
567
|
+
<ComparisonRows
|
|
568
|
+
tools={visibleTools}
|
|
569
|
+
field="cca_adaptation_support_cycle_step"
|
|
570
|
+
label={intl.formatMessage(messages.adaptationSupportCycleStep)}
|
|
571
|
+
options={cycleOptions}
|
|
572
|
+
/>
|
|
504
573
|
</Table>
|
|
505
574
|
)}
|
|
506
575
|
</Container>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import '@testing-library/jest-dom';
|
|
3
|
-
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import { fireEvent, render, screen, within } from '@testing-library/react';
|
|
4
4
|
import { IntlProvider } from 'react-intl';
|
|
5
5
|
import { useAtom } from 'jotai';
|
|
6
6
|
import { useDispatch, useSelector } from 'react-redux';
|
|
@@ -69,7 +69,7 @@ describe('CompareToolsView', () => {
|
|
|
69
69
|
hash: '',
|
|
70
70
|
state: {},
|
|
71
71
|
});
|
|
72
|
-
|
|
72
|
+
const results = [
|
|
73
73
|
{
|
|
74
74
|
cca_uid: { raw: 'one' },
|
|
75
75
|
title: 'Tool one',
|
|
@@ -78,8 +78,14 @@ describe('CompareToolsView', () => {
|
|
|
78
78
|
scales: { thumb: { download: '/uploaded-compare-thumb.jpg' } },
|
|
79
79
|
},
|
|
80
80
|
functionality: { raw: 4 },
|
|
81
|
+
cca_type_of_outputs: { raw: ['Maps and graphs'] },
|
|
81
82
|
cca_adaptation_support_cycle_step: {
|
|
82
|
-
raw: [
|
|
83
|
+
raw: [
|
|
84
|
+
{
|
|
85
|
+
title:
|
|
86
|
+
'Step 2: Assessing Climate Change Risks and Vulnerabilities',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
83
89
|
},
|
|
84
90
|
},
|
|
85
91
|
{
|
|
@@ -88,7 +94,16 @@ describe('CompareToolsView', () => {
|
|
|
88
94
|
href: '/tool-two',
|
|
89
95
|
image: null,
|
|
90
96
|
},
|
|
91
|
-
]
|
|
97
|
+
];
|
|
98
|
+
results.comparisonOptions = {
|
|
99
|
+
cca_type_of_outputs: ['Reports and decision support', 'Maps and graphs'],
|
|
100
|
+
cca_adaptation_support_cycle_step: [
|
|
101
|
+
'Step 10: Additional step from the catalogue',
|
|
102
|
+
'Step 2: Assessing Climate Change Risks and Vulnerabilities',
|
|
103
|
+
'Step 1: Preparing the Ground for Adaptation',
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
fetchResultsByUid.mockResolvedValue(results);
|
|
92
107
|
});
|
|
93
108
|
|
|
94
109
|
it('labels the table, row headers, and remove actions', async () => {
|
|
@@ -117,13 +132,61 @@ describe('CompareToolsView', () => {
|
|
|
117
132
|
expect(
|
|
118
133
|
functionalityScore.querySelectorAll('.functionality-dot.filled'),
|
|
119
134
|
).toHaveLength(4);
|
|
120
|
-
const
|
|
135
|
+
const cycleHeader = screen.getByRole('rowheader', {
|
|
121
136
|
name: 'Adaptation support cycle step',
|
|
122
137
|
});
|
|
123
|
-
expect(
|
|
124
|
-
expect(
|
|
125
|
-
|
|
126
|
-
expect(
|
|
138
|
+
expect(cycleHeader).toHaveAttribute('scope', 'rowgroup');
|
|
139
|
+
expect(cycleHeader).toHaveAttribute('rowspan', '3');
|
|
140
|
+
const cycleRows = within(cycleHeader.closest('tbody')).getAllByRole('row');
|
|
141
|
+
expect(cycleRows).toHaveLength(3);
|
|
142
|
+
expect(
|
|
143
|
+
within(cycleRows[2]).getAllByText(
|
|
144
|
+
'Step 10: Additional step from the catalogue',
|
|
145
|
+
),
|
|
146
|
+
).toHaveLength(2);
|
|
147
|
+
expect(
|
|
148
|
+
within(cycleRows[1]).getAllByText(
|
|
149
|
+
'Step 2: Assessing Climate Change Risks and Vulnerabilities',
|
|
150
|
+
),
|
|
151
|
+
).toHaveLength(2);
|
|
152
|
+
expect(within(cycleRows[1]).getAllByLabelText('Available')).toHaveLength(1);
|
|
153
|
+
expect(
|
|
154
|
+
within(cycleRows[1]).getAllByLabelText('Not available'),
|
|
155
|
+
).toHaveLength(1);
|
|
156
|
+
expect(
|
|
157
|
+
within(cycleRows[0]).getAllByLabelText('Not available'),
|
|
158
|
+
).toHaveLength(2);
|
|
159
|
+
const outputRow = screen
|
|
160
|
+
.getByRole('rowheader', { name: 'Output type' })
|
|
161
|
+
.closest('tr');
|
|
162
|
+
expect(within(outputRow).getAllByText('Maps and graphs')).toHaveLength(2);
|
|
163
|
+
expect(within(outputRow).getAllByLabelText('Available')).toHaveLength(1);
|
|
164
|
+
expect(within(outputRow).getAllByLabelText('Not available')).toHaveLength(
|
|
165
|
+
1,
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('falls back to selected tool values when aggregations are empty', async () => {
|
|
170
|
+
const results = await fetchResultsByUid();
|
|
171
|
+
results.comparisonOptions = {};
|
|
172
|
+
render(
|
|
173
|
+
<IntlProvider locale="en">
|
|
174
|
+
<CompareToolsView />
|
|
175
|
+
</IntlProvider>,
|
|
176
|
+
);
|
|
177
|
+
await screen.findByRole('table', { name: 'Compare tools' });
|
|
178
|
+
const cycleHeader = screen.getByRole('rowheader', {
|
|
179
|
+
name: 'Adaptation support cycle step',
|
|
180
|
+
});
|
|
181
|
+
expect(cycleHeader).toHaveAttribute('rowspan', '1');
|
|
182
|
+
expect(
|
|
183
|
+
within(cycleHeader.closest('tbody')).getAllByText(
|
|
184
|
+
'Step 2: Assessing Climate Change Risks and Vulnerabilities',
|
|
185
|
+
),
|
|
186
|
+
).toHaveLength(2);
|
|
187
|
+
expect(
|
|
188
|
+
screen.queryByText('Step 1: Preparing the Ground for Adaptation'),
|
|
189
|
+
).not.toBeInTheDocument();
|
|
127
190
|
});
|
|
128
191
|
|
|
129
192
|
it('shows the uploaded tool result image and keeps the file icon for a missing image', async () => {
|
|
@@ -111,11 +111,43 @@ export const fetchResultsByUid = async (uids, registry) => {
|
|
|
111
111
|
],
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
|
+
aggs: {
|
|
115
|
+
allOutputTypes: {
|
|
116
|
+
global: {},
|
|
117
|
+
aggs: {
|
|
118
|
+
values: {
|
|
119
|
+
terms: { field: 'cca_type_of_outputs.keyword', size: 10000 },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
allCycleSteps: {
|
|
124
|
+
global: {},
|
|
125
|
+
aggs: {
|
|
126
|
+
values: {
|
|
127
|
+
terms: {
|
|
128
|
+
field: 'cca_adaptation_support_cycle_step.keyword',
|
|
129
|
+
size: 10000,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
114
135
|
},
|
|
115
136
|
appConfig,
|
|
116
137
|
);
|
|
117
138
|
const hits = response.body?.hits?.hits || [];
|
|
118
139
|
const Model = registry.resolve[appConfig.resultItemModel.factory];
|
|
140
|
+
const results = hits.map((hit) => new Model(hit, appConfig));
|
|
141
|
+
results.comparisonOptions = {
|
|
142
|
+
cca_type_of_outputs:
|
|
143
|
+
response.body?.aggregations?.allOutputTypes?.values?.buckets?.map(
|
|
144
|
+
(bucket) => bucket.key,
|
|
145
|
+
) || [],
|
|
146
|
+
cca_adaptation_support_cycle_step:
|
|
147
|
+
response.body?.aggregations?.allCycleSteps?.values?.buckets?.map(
|
|
148
|
+
(bucket) => bucket.key,
|
|
149
|
+
) || [],
|
|
150
|
+
};
|
|
119
151
|
|
|
120
|
-
return
|
|
152
|
+
return results;
|
|
121
153
|
};
|
|
@@ -70,9 +70,11 @@ describe('Compare Tools utilities', () => {
|
|
|
70
70
|
it('resolves comparison identifiers and titles from supported shapes', () => {
|
|
71
71
|
expect(getCompareToolUid({ cca_uid: { raw: 'cca-id' } })).toBe('cca-id');
|
|
72
72
|
expect(getCompareToolUid({ UID: 'plone-id' })).toBe('plone-id');
|
|
73
|
-
expect(
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
expect(
|
|
74
|
+
getCompareToolUid({
|
|
75
|
+
_result: { cca_uid: { raw: 'raw-id' } },
|
|
76
|
+
}),
|
|
77
|
+
).toBe('raw-id');
|
|
76
78
|
expect(getCompareToolUid({})).toBe('');
|
|
77
79
|
expect(getCompareToolTitle({ title: 'Tool title' })).toBe('Tool title');
|
|
78
80
|
expect(getCompareToolTitle({})).toBe('');
|
|
@@ -101,9 +103,13 @@ describe('Compare Tools utilities', () => {
|
|
|
101
103
|
).toEqual({
|
|
102
104
|
pathname: '/ro/navigator/compare',
|
|
103
105
|
search: '?uid=one&uid=two&uid=three',
|
|
104
|
-
state: {
|
|
106
|
+
state: {
|
|
107
|
+
returnURL: '/en/navigator/tool-catalogue?q=test',
|
|
108
|
+
},
|
|
105
109
|
});
|
|
110
|
+
|
|
106
111
|
expect(MAX_COMPARE_TOOLS).toBe(4);
|
|
112
|
+
|
|
107
113
|
expect(getCompareLocation([])).toEqual({
|
|
108
114
|
pathname: '/en/navigator/compare',
|
|
109
115
|
search: '',
|
|
@@ -118,22 +124,46 @@ describe('Compare Tools utilities', () => {
|
|
|
118
124
|
this.config = config;
|
|
119
125
|
}
|
|
120
126
|
}
|
|
127
|
+
|
|
121
128
|
const appConfig = {
|
|
122
129
|
index_name: 'data_searchui',
|
|
123
130
|
resultItemModel: { factory: 'resultModel' },
|
|
124
131
|
};
|
|
132
|
+
|
|
125
133
|
const registry = {
|
|
126
|
-
searchui: {
|
|
127
|
-
|
|
134
|
+
searchui: {
|
|
135
|
+
navigatorCatalogueSearch: appConfig,
|
|
136
|
+
},
|
|
137
|
+
resolve: {
|
|
138
|
+
resultModel: ResultModel,
|
|
139
|
+
},
|
|
128
140
|
};
|
|
141
|
+
|
|
129
142
|
runRequest.mockResolvedValue({
|
|
130
|
-
body: {
|
|
143
|
+
body: {
|
|
144
|
+
hits: {
|
|
145
|
+
hits: [{ _id: 'one' }, { _id: 'two' }],
|
|
146
|
+
},
|
|
147
|
+
aggregations: {
|
|
148
|
+
allOutputTypes: {
|
|
149
|
+
values: {
|
|
150
|
+
buckets: [{ key: 'Map' }, { key: 'Chart' }],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
allCycleSteps: {
|
|
154
|
+
values: {
|
|
155
|
+
buckets: [{ key: 'Assess' }, { key: 'Plan' }],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
131
160
|
});
|
|
132
161
|
|
|
133
162
|
const results = await fetchResultsByUid(['one', 'two'], registry);
|
|
134
163
|
|
|
135
164
|
expect(rebind).toHaveBeenCalledWith(appConfig);
|
|
136
165
|
expect(applyConfigurationSchema).toHaveBeenCalledWith(appConfig);
|
|
166
|
+
|
|
137
167
|
expect(runRequest).toHaveBeenCalledWith(
|
|
138
168
|
{
|
|
139
169
|
index: 'data_searchui',
|
|
@@ -142,59 +172,127 @@ describe('Compare Tools utilities', () => {
|
|
|
142
172
|
bool: {
|
|
143
173
|
minimum_should_match: 1,
|
|
144
174
|
should: [
|
|
145
|
-
{
|
|
146
|
-
|
|
175
|
+
{
|
|
176
|
+
terms: {
|
|
177
|
+
'cca_uid.keyword': ['one', 'two'],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
terms: {
|
|
182
|
+
cca_uid: ['one', 'two'],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
147
185
|
],
|
|
148
186
|
},
|
|
149
187
|
},
|
|
188
|
+
aggs: {
|
|
189
|
+
allOutputTypes: {
|
|
190
|
+
global: {},
|
|
191
|
+
aggs: {
|
|
192
|
+
values: {
|
|
193
|
+
terms: {
|
|
194
|
+
field: 'cca_type_of_outputs.keyword',
|
|
195
|
+
size: 10000,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
allCycleSteps: {
|
|
201
|
+
global: {},
|
|
202
|
+
aggs: {
|
|
203
|
+
values: {
|
|
204
|
+
terms: {
|
|
205
|
+
field: 'cca_adaptation_support_cycle_step.keyword',
|
|
206
|
+
size: 10000,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
150
212
|
},
|
|
151
213
|
appConfig,
|
|
152
214
|
);
|
|
215
|
+
|
|
153
216
|
expect(results).toHaveLength(2);
|
|
154
217
|
expect(results[0]).toBeInstanceOf(ResultModel);
|
|
218
|
+
expect(results[1]).toBeInstanceOf(ResultModel);
|
|
219
|
+
|
|
220
|
+
expect(results.comparisonOptions).toEqual({
|
|
221
|
+
cca_type_of_outputs: ['Map', 'Chart'],
|
|
222
|
+
cca_adaptation_support_cycle_step: ['Assess', 'Plan'],
|
|
223
|
+
});
|
|
155
224
|
});
|
|
156
225
|
|
|
157
226
|
it('handles missing hits and configurations without an index', async () => {
|
|
158
227
|
const registry = {
|
|
159
228
|
searchui: {
|
|
160
229
|
navigatorCatalogueSearch: {
|
|
161
|
-
resultItemModel: {
|
|
230
|
+
resultItemModel: {
|
|
231
|
+
factory: 'resultModel',
|
|
232
|
+
},
|
|
162
233
|
},
|
|
163
234
|
},
|
|
164
|
-
resolve: {
|
|
235
|
+
resolve: {
|
|
236
|
+
resultModel: class ResultModel {},
|
|
237
|
+
},
|
|
165
238
|
};
|
|
239
|
+
|
|
166
240
|
runRequest.mockResolvedValue({});
|
|
167
241
|
|
|
168
|
-
await
|
|
242
|
+
const results = await fetchResultsByUid(['one'], registry);
|
|
243
|
+
|
|
244
|
+
expect(results).toHaveLength(0);
|
|
245
|
+
|
|
246
|
+
expect(results.comparisonOptions).toEqual({
|
|
247
|
+
cca_type_of_outputs: [],
|
|
248
|
+
cca_adaptation_support_cycle_step: [],
|
|
249
|
+
});
|
|
250
|
+
|
|
169
251
|
expect(runRequest.mock.calls[0][0]).not.toHaveProperty('index');
|
|
170
252
|
});
|
|
171
253
|
|
|
172
254
|
it('adds, toggles, and removes a comparison tool', () => {
|
|
173
|
-
renderCompareHarness({
|
|
255
|
+
renderCompareHarness({
|
|
256
|
+
uid: 'one',
|
|
257
|
+
title: 'Tool one',
|
|
258
|
+
});
|
|
174
259
|
|
|
175
260
|
expect(screen.getByTestId('selected')).toHaveTextContent('false');
|
|
176
261
|
expect(screen.getByTestId('limit')).toHaveTextContent('false');
|
|
177
262
|
|
|
178
263
|
fireEvent.click(screen.getByText('Toggle'));
|
|
264
|
+
|
|
179
265
|
expect(screen.getByTestId('selected')).toHaveTextContent('true');
|
|
266
|
+
|
|
180
267
|
expect(
|
|
181
268
|
JSON.parse(window.localStorage.getItem('cca-compare-tools')),
|
|
182
|
-
).toEqual([
|
|
269
|
+
).toEqual([
|
|
270
|
+
{
|
|
271
|
+
uid: 'one',
|
|
272
|
+
title: 'Tool one',
|
|
273
|
+
},
|
|
274
|
+
]);
|
|
183
275
|
|
|
184
276
|
fireEvent.click(screen.getByText('Select'));
|
|
277
|
+
|
|
185
278
|
expect(screen.getByTestId('selected')).toHaveTextContent('true');
|
|
186
279
|
|
|
187
280
|
fireEvent.click(screen.getByText('Remove'));
|
|
281
|
+
|
|
188
282
|
expect(screen.getByTestId('selected')).toHaveTextContent('false');
|
|
283
|
+
|
|
189
284
|
expect(
|
|
190
285
|
JSON.parse(window.localStorage.getItem('cca-compare-tools')),
|
|
191
286
|
).toEqual([]);
|
|
192
287
|
});
|
|
193
288
|
|
|
194
289
|
it('does not add tools without an identifier', () => {
|
|
195
|
-
renderCompareHarness({
|
|
290
|
+
renderCompareHarness({
|
|
291
|
+
title: 'Missing UID',
|
|
292
|
+
});
|
|
196
293
|
|
|
197
294
|
fireEvent.click(screen.getByText('Select'));
|
|
295
|
+
|
|
198
296
|
expect(screen.getByTestId('selected')).toHaveTextContent('false');
|
|
199
297
|
});
|
|
200
298
|
|
|
@@ -205,15 +303,26 @@ describe('Compare Tools utilities', () => {
|
|
|
205
303
|
uid: `tool-${index}`,
|
|
206
304
|
}),
|
|
207
305
|
);
|
|
208
|
-
|
|
306
|
+
|
|
307
|
+
renderCompareHarness(
|
|
308
|
+
{
|
|
309
|
+
uid: 'extra',
|
|
310
|
+
},
|
|
311
|
+
initialTools,
|
|
312
|
+
);
|
|
209
313
|
|
|
210
314
|
expect(screen.getByTestId('limit')).toHaveTextContent('true');
|
|
315
|
+
|
|
211
316
|
fireEvent.click(screen.getByText('Select'));
|
|
317
|
+
|
|
212
318
|
expect(screen.getByTestId('selected')).toHaveTextContent('false');
|
|
213
319
|
});
|
|
214
320
|
|
|
215
321
|
it('exposes hasMounted state to safely coordinate client hydration', () => {
|
|
216
|
-
renderCompareHarness({
|
|
322
|
+
renderCompareHarness({
|
|
323
|
+
uid: 'one',
|
|
324
|
+
title: 'Tool one',
|
|
325
|
+
});
|
|
217
326
|
|
|
218
327
|
expect(screen.getByTestId('mounted')).toHaveTextContent('true');
|
|
219
328
|
});
|
|
@@ -221,10 +330,12 @@ describe('Compare Tools utilities', () => {
|
|
|
221
330
|
it('tracks mounting status with useHasMounted', () => {
|
|
222
331
|
const MountedComponent = () => {
|
|
223
332
|
const mounted = useHasMounted();
|
|
333
|
+
|
|
224
334
|
return <div data-testid="mounted-flag">{String(mounted)}</div>;
|
|
225
335
|
};
|
|
226
336
|
|
|
227
337
|
render(<MountedComponent />);
|
|
338
|
+
|
|
228
339
|
expect(screen.getByTestId('mounted-flag')).toHaveTextContent('true');
|
|
229
340
|
});
|
|
230
341
|
});
|
|
@@ -359,13 +359,12 @@
|
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
-
.
|
|
363
|
-
.ui.celled.table td:first-child {
|
|
362
|
+
.compare-criteria-column {
|
|
364
363
|
width: 180px;
|
|
365
364
|
}
|
|
366
365
|
|
|
367
366
|
.ui.celled.table th:not(:first-child),
|
|
368
|
-
.ui.celled.table td
|
|
367
|
+
.ui.celled.table td {
|
|
369
368
|
overflow-wrap: anywhere;
|
|
370
369
|
}
|
|
371
370
|
|
|
@@ -398,10 +397,46 @@
|
|
|
398
397
|
}
|
|
399
398
|
}
|
|
400
399
|
|
|
401
|
-
.compare-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
400
|
+
.compare-status-column {
|
|
401
|
+
width: 64px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.ui.celled.table {
|
|
405
|
+
td.compare-option-label,
|
|
406
|
+
td.compare-option-status {
|
|
407
|
+
padding: 0.4rem 1.1rem;
|
|
408
|
+
vertical-align: middle;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
td.compare-option-label {
|
|
412
|
+
border-left: 1px solid @navigatorBorderColor;
|
|
413
|
+
|
|
414
|
+
&.available {
|
|
415
|
+
color: @secondaryColor;
|
|
416
|
+
font-weight: 700;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
td.compare-option-status {
|
|
421
|
+
padding-right: 0.5rem;
|
|
422
|
+
padding-left: 0.5rem;
|
|
423
|
+
text-align: center;
|
|
424
|
+
|
|
425
|
+
i.icon.ri-check-line {
|
|
426
|
+
color: @secondaryColor;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
i.icon {
|
|
430
|
+
margin: 0;
|
|
431
|
+
font-size: 24px;
|
|
432
|
+
line-height: 1;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
tbody + tbody tr:first-child > th,
|
|
437
|
+
tbody + tbody tr:first-child > td {
|
|
438
|
+
border-top: 1px solid @navigatorBorderColor;
|
|
439
|
+
}
|
|
405
440
|
}
|
|
406
441
|
|
|
407
442
|
.compare-criteria-label {
|
package/theme/globals/views.less
CHANGED
|
@@ -666,6 +666,18 @@ hr {
|
|
|
666
666
|
font-size: 14px;
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
+
.navigator-catalogue-compare-view {
|
|
670
|
+
.metadata-tag {
|
|
671
|
+
font-weight: 400;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.metadata-tag.selected {
|
|
675
|
+
border-color: @secondaryColor;
|
|
676
|
+
color: @secondaryColor;
|
|
677
|
+
font-weight: 600;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
669
681
|
.metadata-tag-more {
|
|
670
682
|
background: transparent;
|
|
671
683
|
cursor: pointer;
|