@eeacms/volto-cca-policy 1.0.12 → 1.0.13

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,12 @@ 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.13](https://github.com/eea/volto-cca-policy/compare/1.0.12...1.0.13) - 31 August 2026
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Refs #307245 - Visualizations: fix order in view mode. [GhitaB - [`bfbe754`](https://github.com/eea/volto-cca-policy/commit/bfbe7549af5a4f767c3d023b894b55b5eb81918e)]
12
+ - Refs #307245 - Visualizations: order and descriptions. [GhitaB - [`5fff846`](https://github.com/eea/volto-cca-policy/commit/5fff846b623250e89cbf2a844828f545c0f2f9f9)]
7
13
  ### [1.0.12](https://github.com/eea/volto-cca-policy/compare/1.0.11...1.0.12) - 24 August 2026
8
14
 
9
15
  #### :bug: Bug Fixes
@@ -301,8 +307,24 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
301
307
  #### :hammer_and_wrench: Others
302
308
 
303
309
  - test: pin Chromium version 149 to work with Cypress [valentinab25 - [`659ec82`](https://github.com/eea/volto-cca-policy/commit/659ec823dd71fe592a3a7accda22effbe1fb07bc)]
304
- ## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.24...1.0.0) - 12 June 2026
310
+ ## [1.0.0](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.26...1.0.0) - 12 June 2026
311
+
312
+ ### [1.0.0-alpha.26](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.25...1.0.0-alpha.26) - 28 August 2026
313
+
314
+ ### [1.0.0-alpha.25](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.24...1.0.0-alpha.25) - 27 August 2026
315
+
316
+ #### :bug: Bug Fixes
317
+
318
+ - fix: disable unavailable navigator guide options [kreafox - [`4b114c2`](https://github.com/eea/volto-cca-policy/commit/4b114c2b0ea7a6a6528ae8ae378c2e671b640945)]
319
+
320
+ #### :nail_care: Enhancements
321
+
322
+ - change: add custom hook for guide facet options and add tests [kreafox - [`872eb9b`](https://github.com/eea/volto-cca-policy/commit/872eb9b35c41fc2094c605b113411448a8cef9a8)]
323
+
324
+ #### :hammer_and_wrench: Others
305
325
 
326
+ - Refs #307245 - Visualizations: order and descriptions. [GhitaB - [`5fff846`](https://github.com/eea/volto-cca-policy/commit/5fff846b623250e89cbf2a844828f545c0f2f9f9)]
327
+ - test: increase utility coverage [kreafox - [`d9c3db8`](https://github.com/eea/volto-cca-policy/commit/d9c3db8cc6fb91ead00f83b822f6fe48c26a70b1)]
306
328
  ### [1.0.0-alpha.24](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.23...1.0.0-alpha.24) - 24 August 2026
307
329
 
308
330
  ### [1.0.0-alpha.23](https://github.com/eea/volto-cca-policy/compare/1.0.0-alpha.22...1.0.0-alpha.23) - 19 August 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-cca-policy",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
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",
@@ -7,6 +7,7 @@ import './visualizations.less';
7
7
 
8
8
  const emptyVisualization = {
9
9
  title: '',
10
+ description: '',
10
11
  embed_code: '',
11
12
  height: '',
12
13
  full_width: false,
@@ -43,6 +44,19 @@ const VisualizationsWidget = (props) => {
43
44
  );
44
45
  };
45
46
 
47
+ const moveItem = (index, direction) => {
48
+ const nextIndex = index + direction;
49
+
50
+ if (nextIndex < 0 || nextIndex >= visualizations.length) return;
51
+
52
+ const nextVisualizations = [...visualizations];
53
+ [nextVisualizations[index], nextVisualizations[nextIndex]] = [
54
+ nextVisualizations[nextIndex],
55
+ nextVisualizations[index],
56
+ ];
57
+ updateVisualizations(nextVisualizations);
58
+ };
59
+
46
60
  return (
47
61
  <FormFieldWrapper {...props}>
48
62
  <div className="visualizations-widget">
@@ -53,16 +67,30 @@ const VisualizationsWidget = (props) => {
53
67
  >
54
68
  <div className="visualizations-widget-item-header">
55
69
  <h4>Visualization {index + 1}</h4>
56
- <Button
57
- type="button"
58
- basic
59
- compact
60
- negative
61
- disabled={isDisabled}
62
- onClick={() => deleteItem(index)}
63
- >
64
- Delete
65
- </Button>
70
+ <Button.Group basic compact>
71
+ <Button
72
+ type="button"
73
+ disabled={isDisabled || index === 0}
74
+ onClick={() => moveItem(index, -1)}
75
+ >
76
+ Up
77
+ </Button>
78
+ <Button
79
+ type="button"
80
+ disabled={isDisabled || index === visualizations.length - 1}
81
+ onClick={() => moveItem(index, 1)}
82
+ >
83
+ Down
84
+ </Button>
85
+ <Button
86
+ type="button"
87
+ negative
88
+ disabled={isDisabled}
89
+ onClick={() => deleteItem(index)}
90
+ >
91
+ Delete
92
+ </Button>
93
+ </Button.Group>
66
94
  </div>
67
95
 
68
96
  <Form.Field>
@@ -77,6 +105,19 @@ const VisualizationsWidget = (props) => {
77
105
  />
78
106
  </Form.Field>
79
107
 
108
+ <Form.Field>
109
+ <label htmlFor={`${id}-${index}-description`}>Description</label>
110
+ <TextArea
111
+ id={`${id}-${index}-description`}
112
+ value={item.description || ''}
113
+ disabled={isDisabled}
114
+ rows={3}
115
+ onChange={(event) =>
116
+ updateItem(index, 'description', event.target.value)
117
+ }
118
+ />
119
+ </Form.Field>
120
+
80
121
  <Form.Field>
81
122
  <label htmlFor={`${id}-${index}-embed-code`}>
82
123
  Embed code or URL
@@ -22,6 +22,10 @@
22
22
  color: #1f405c;
23
23
  font-size: 1rem;
24
24
  }
25
+
26
+ .ui.buttons {
27
+ flex-shrink: 0;
28
+ }
25
29
  }
26
30
 
27
31
  .field {
@@ -107,6 +107,9 @@ const MaybeIframeVisualization = ({ visualization }) => {
107
107
  const Visualization = ({ visualization }) => (
108
108
  <>
109
109
  {visualization?.title && <h2>{visualization.title}</h2>}
110
+ {visualization?.description && (
111
+ <p className="visualization-description">{visualization.description}</p>
112
+ )}
110
113
  <MaybeFlourishVisualization visualization={visualization} />
111
114
  <MaybeIframeVisualization visualization={visualization} />
112
115
  </>
@@ -129,6 +132,20 @@ const getVisualizations = (content) => {
129
132
  : [];
130
133
  };
131
134
 
135
+ const groupVisualizations = (visualizations) =>
136
+ visualizations.reduce((groups, visualization) => {
137
+ const fullWidth = !!visualization.full_width;
138
+ const lastGroup = groups[groups.length - 1];
139
+
140
+ if (lastGroup && lastGroup.fullWidth === fullWidth) {
141
+ lastGroup.items.push(visualization);
142
+ } else {
143
+ groups.push({ fullWidth, items: [visualization] });
144
+ }
145
+
146
+ return groups;
147
+ }, []);
148
+
132
149
  const BottomInfo = (props) => {
133
150
  const { content } = props;
134
151
  const { organisational_websites, organisational_contact_information } =
@@ -185,13 +202,11 @@ const DatabaseItemView = (props) => {
185
202
  const item_title = acronym ? title + ' (' + acronym + ')' : title;
186
203
  const subtitle = CONTENT_TYPE_LABELS[type] ?? '';
187
204
  const visualizations = getVisualizations(content);
188
- const columnVisualizations = visualizations.filter(
189
- (visualization) => !visualization.full_width,
205
+ const visualizationGroups = groupVisualizations(visualizations);
206
+ const firstVisualizationGroup = visualizationGroups[0];
207
+ const hasFullWidthVisualizations = visualizationGroups.some(
208
+ (group) => group.fullWidth,
190
209
  );
191
- const fullWidthVisualizations = visualizations.filter(
192
- (visualization) => visualization.full_width,
193
- );
194
- const hasFullWidthVisualizations = !!fullWidthVisualizations.length;
195
210
 
196
211
  const is_cmshare_video = SHARE_EEA.some((domain) =>
197
212
  content?.embed_url?.includes(domain),
@@ -284,9 +299,12 @@ const DatabaseItemView = (props) => {
284
299
  </>
285
300
  )}
286
301
 
287
- {!!columnVisualizations.length && (
288
- <Visualizations visualizations={columnVisualizations} />
289
- )}
302
+ {firstVisualizationGroup &&
303
+ !firstVisualizationGroup.fullWidth && (
304
+ <Visualizations
305
+ visualizations={firstVisualizationGroup.items}
306
+ />
307
+ )}
290
308
 
291
309
  {!hasFullWidthVisualizations && <BottomInfo {...props} />}
292
310
  </Grid.Column>
@@ -304,11 +322,22 @@ const DatabaseItemView = (props) => {
304
322
 
305
323
  {hasFullWidthVisualizations && (
306
324
  <>
307
- <Grid.Row>
308
- <Grid.Column mobile={12} tablet={12} computer={12}>
309
- <Visualizations visualizations={fullWidthVisualizations} />
310
- </Grid.Column>
311
- </Grid.Row>
325
+ {visualizationGroups.map((group, index) => {
326
+ if (index === 0 && !group.fullWidth) return null;
327
+
328
+ return (
329
+ <Grid.Row key={`visualization-group-${index}`}>
330
+ <Grid.Column
331
+ mobile={12}
332
+ tablet={12}
333
+ computer={group.fullWidth ? 12 : 8}
334
+ className={group.fullWidth ? undefined : 'col-left'}
335
+ >
336
+ <Visualizations visualizations={group.items} />
337
+ </Grid.Column>
338
+ </Grid.Row>
339
+ );
340
+ })}
312
341
  <Grid.Row>
313
342
  <Grid.Column
314
343
  mobile={12}
@@ -46,6 +46,7 @@ const visualizationEmbedCode =
46
46
 
47
47
  const getVisualization = (overrides = {}) => ({
48
48
  title: '',
49
+ description: '',
49
50
  embed_code: visualizationEmbedCode,
50
51
  height: '',
51
52
  full_width: false,
@@ -125,6 +126,39 @@ describe('DatabaseItemView', () => {
125
126
  expect(fullWidthColumn.findAllByType('iframe')).toHaveLength(1);
126
127
  });
127
128
 
129
+ it('renders visualization descriptions', () => {
130
+ const component = renderDatabaseItemView({
131
+ ...baseContent,
132
+ visualizations: [
133
+ getVisualization({ description: 'Visualization summary text.' }),
134
+ ],
135
+ });
136
+
137
+ expect(
138
+ component.root.findByProps({
139
+ className: 'visualization-description',
140
+ }).children,
141
+ ).toEqual(['Visualization summary text.']);
142
+ });
143
+
144
+ it('preserves visualization order when full-width and column items are mixed', () => {
145
+ const component = renderDatabaseItemView({
146
+ ...baseContent,
147
+ visualizations: [
148
+ getVisualization({ title: 'First', full_width: true }),
149
+ getVisualization({ title: 'Second' }),
150
+ getVisualization({ title: 'Third', full_width: true }),
151
+ ],
152
+ });
153
+
154
+ const headings = component.root
155
+ .findAllByType('h2')
156
+ .map((heading) => heading.children.join(''))
157
+ .filter((title) => ['First', 'Second', 'Third'].includes(title));
158
+
159
+ expect(headings).toEqual(['First', 'Second', 'Third']);
160
+ });
161
+
128
162
  it('does not render a visualization when the visualizations field is empty', () => {
129
163
  const component = renderDatabaseItemView({
130
164
  ...baseContent,