@eeacms/volto-eea-website-theme 4.0.2 → 4.0.4

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,14 @@ 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
+ ### [4.0.4](https://github.com/eea/volto-eea-website-theme/compare/4.0.3...4.0.4) - 10 April 2026
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: EEA Design System dependency version [Alin Voinea - [`2e7be46`](https://github.com/eea/volto-eea-website-theme/commit/2e7be464a1ccec1a86f51aa011639fd9b1111438)]
12
+
13
+ ### [4.0.3](https://github.com/eea/volto-eea-website-theme/compare/4.0.2...4.0.3) - 9 April 2026
14
+
7
15
  ### [4.0.2](https://github.com/eea/volto-eea-website-theme/compare/4.0.1...4.0.2) - 8 April 2026
8
16
 
9
17
  ### [4.0.1](https://github.com/eea/volto-eea-website-theme/compare/4.0.0...4.0.1) - 8 April 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "@eeacms/volto-eea-website-theme: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -28,7 +28,7 @@
28
28
  "@eeacms/volto-anchors": "*",
29
29
  "@eeacms/volto-block-style": "*",
30
30
  "@eeacms/volto-block-toc": "*",
31
- "@eeacms/volto-eea-design-system": "eea/volto-eea-design-system#develop",
31
+ "@eeacms/volto-eea-design-system": "*",
32
32
  "@eeacms/volto-group-block": "*",
33
33
  "volto-subsites": "*"
34
34
  },
@@ -0,0 +1,36 @@
1
+ /**
2
+ * PATCH from Volto 18.33.0
3
+ * To be removed when issue is fixed and released:
4
+ * https://github.com/plone/volto/issues/7309
5
+ * https://github.com/plone/volto/pull/8093
6
+ * https://github.com/plone/volto/pull/8094
7
+ * */
8
+ import config from '@plone/volto/registry';
9
+ import Helmet from '@plone/volto/helpers/Helmet/Helmet';
10
+ import { flattenToAppURL, toPublicURL } from '@plone/volto/helpers/Url/Url';
11
+
12
+ const AlternateHrefLangs = (props) => {
13
+ const { content } = props;
14
+ return (
15
+ <Helmet>
16
+ {config.settings.isMultilingual &&
17
+ content['@components']?.translations?.items &&
18
+ content.language?.token &&
19
+ [
20
+ ...content['@components']?.translations?.items,
21
+ { '@id': content['@id'], language: content.language.token },
22
+ ].map((item, key) => {
23
+ return (
24
+ <link
25
+ key={key}
26
+ rel="alternate"
27
+ hrefLang={item.language}
28
+ href={toPublicURL(flattenToAppURL(item['@id']))}
29
+ />
30
+ );
31
+ })}
32
+ </Helmet>
33
+ );
34
+ };
35
+
36
+ export { AlternateHrefLangs };
@@ -0,0 +1,223 @@
1
+ import React from 'react';
2
+ import Helmet from '@plone/volto/helpers/Helmet/Helmet';
3
+
4
+ import renderer from 'react-test-renderer';
5
+ import configureStore from 'redux-mock-store';
6
+ import { Provider } from 'react-intl-redux';
7
+ import config from '@plone/volto/registry';
8
+
9
+ import { AlternateHrefLangs } from './AlternateHrefLangs';
10
+
11
+ const mockStore = configureStore();
12
+
13
+ describe('AlternateHrefLangs', () => {
14
+ beforeEach(() => {});
15
+ it('non multilingual site, renders nothing', () => {
16
+ config.settings.isMultilingual = false;
17
+ const content = {
18
+ '@id': '/',
19
+ '@components': {},
20
+ };
21
+ const store = mockStore({
22
+ intl: {
23
+ locale: 'en',
24
+ messages: {},
25
+ },
26
+ });
27
+ // We need to force the component rendering
28
+ // to fill the Helmet
29
+ renderer.create(
30
+ <Provider store={store}>
31
+ <AlternateHrefLangs content={content} />
32
+ </Provider>,
33
+ );
34
+
35
+ const helmetLinks = Helmet.peek().linkTags;
36
+ expect(helmetLinks.length).toBe(0);
37
+ });
38
+
39
+ it('multilingual site, content without language field, renders nothing', () => {
40
+ config.settings.publicURL = 'https://plone.org';
41
+ config.settings.supportedLanguages = ['en', 'es'];
42
+
43
+ const content = {
44
+ '@id': 'http://localhost:8080/Plone/en/newsroom/news',
45
+ '@components': {
46
+ translations: {
47
+ items: [{ '@id': 'http://localhost:8080/Plone/es', language: 'es' }],
48
+ },
49
+ },
50
+ };
51
+
52
+ const store = mockStore({
53
+ intl: {
54
+ locale: 'en',
55
+ messages: {},
56
+ },
57
+ });
58
+
59
+ renderer.create(
60
+ <Provider store={store}>
61
+ <AlternateHrefLangs content={content} />
62
+ </Provider>,
63
+ );
64
+
65
+ const helmetLinks = Helmet.peek().linkTags;
66
+ expect(helmetLinks.length).toBe(0);
67
+ });
68
+
69
+ it('multilingual site, with some translations', () => {
70
+ config.settings.publicURL = 'https://plone.org';
71
+ config.settings.isMultilingual = true;
72
+ config.settings.supportedLanguages = ['en', 'es', 'eu'];
73
+
74
+ const content = {
75
+ '@id': 'http://localhost:8080/Plone/en',
76
+ language: { token: 'en', title: 'English' },
77
+ '@components': {
78
+ translations: {
79
+ items: [{ '@id': 'http://localhost:8080/Plone/es', language: 'es' }],
80
+ },
81
+ },
82
+ };
83
+
84
+ const store = mockStore({
85
+ intl: {
86
+ locale: 'en',
87
+ messages: {},
88
+ },
89
+ });
90
+
91
+ // We need to force the component rendering
92
+ // to fill the Helmet
93
+ renderer.create(
94
+ <Provider store={store}>
95
+ <>
96
+ <AlternateHrefLangs content={content} />
97
+ </>
98
+ </Provider>,
99
+ );
100
+ const helmetLinks = Helmet.peek().linkTags;
101
+
102
+ expect(helmetLinks.length).toBe(2);
103
+
104
+ expect(helmetLinks).toContainEqual({
105
+ rel: 'alternate',
106
+ href: 'https://plone.org/es',
107
+ hrefLang: 'es',
108
+ });
109
+ expect(helmetLinks).toContainEqual({
110
+ rel: 'alternate',
111
+ href: 'https://plone.org/en',
112
+ hrefLang: 'en',
113
+ });
114
+ });
115
+
116
+ it('multilingual site, with all available translations', () => {
117
+ config.settings.publicURL = 'https://plone.org';
118
+ config.settings.isMultilingual = true;
119
+ config.settings.supportedLanguages = ['en', 'es', 'eu'];
120
+ const store = mockStore({
121
+ intl: {
122
+ locale: 'en',
123
+ messages: {},
124
+ },
125
+ });
126
+
127
+ const content = {
128
+ '@id': 'http://localhost:8080/Plone/en',
129
+ language: { token: 'en', title: 'English' },
130
+ '@components': {
131
+ translations: {
132
+ items: [
133
+ { '@id': 'http://localhost:8080/Plone/eu', language: 'eu' },
134
+ { '@id': 'http://localhost:8080/Plone/es', language: 'es' },
135
+ ],
136
+ },
137
+ },
138
+ };
139
+
140
+ // We need to force the component rendering
141
+ // to fill the Helmet
142
+ renderer.create(
143
+ <Provider store={store}>
144
+ <AlternateHrefLangs content={content} />
145
+ </Provider>,
146
+ );
147
+
148
+ const helmetLinks = Helmet.peek().linkTags;
149
+
150
+ // We expect having 3 links
151
+ expect(helmetLinks.length).toBe(3);
152
+
153
+ expect(helmetLinks).toContainEqual({
154
+ rel: 'alternate',
155
+ href: 'https://plone.org/eu',
156
+ hrefLang: 'eu',
157
+ });
158
+ expect(helmetLinks).toContainEqual({
159
+ rel: 'alternate',
160
+ href: 'https://plone.org/es',
161
+ hrefLang: 'es',
162
+ });
163
+ expect(helmetLinks).toContainEqual({
164
+ rel: 'alternate',
165
+ href: 'https://plone.org/en',
166
+ hrefLang: 'en',
167
+ });
168
+ });
169
+
170
+ it('multilingual site, with all available translations - with server URL', () => {
171
+ config.settings.publicURL = 'https://plone.org';
172
+ config.settings.isMultilingual = true;
173
+ config.settings.supportedLanguages = ['en', 'es', 'eu'];
174
+ const store = mockStore({
175
+ intl: {
176
+ locale: 'en',
177
+ messages: {},
178
+ },
179
+ });
180
+
181
+ const content = {
182
+ '@id': 'http://localhost:8080/Plone/en',
183
+ language: { token: 'en', title: 'English' },
184
+ '@components': {
185
+ translations: {
186
+ items: [
187
+ { '@id': 'http://localhost:8080/Plone/eu', language: 'eu' },
188
+ { '@id': 'http://localhost:8080/Plone/es', language: 'es' },
189
+ ],
190
+ },
191
+ },
192
+ };
193
+
194
+ // We need to force the component rendering
195
+ // to fill the Helmet
196
+ renderer.create(
197
+ <Provider store={store}>
198
+ <AlternateHrefLangs content={content} />
199
+ </Provider>,
200
+ );
201
+
202
+ const helmetLinks = Helmet.peek().linkTags;
203
+
204
+ // We expect having 3 links
205
+ expect(helmetLinks.length).toBe(3);
206
+
207
+ expect(helmetLinks).toContainEqual({
208
+ rel: 'alternate',
209
+ href: 'https://plone.org/eu',
210
+ hrefLang: 'eu',
211
+ });
212
+ expect(helmetLinks).toContainEqual({
213
+ rel: 'alternate',
214
+ href: 'https://plone.org/es',
215
+ hrefLang: 'es',
216
+ });
217
+ expect(helmetLinks).toContainEqual({
218
+ rel: 'alternate',
219
+ href: 'https://plone.org/en',
220
+ hrefLang: 'en',
221
+ });
222
+ });
223
+ });