@eeacms/volto-marine-policy 2.0.3 → 2.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.
Files changed (25) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/jest-addon.config.js +4 -4
  3. package/package.json +6 -4
  4. package/src/components/Blocks/DemoSitesExplorer/DemoSitesExplorerEdit.js +5 -0
  5. package/src/components/Blocks/DemoSitesExplorer/DemoSitesExplorerView.js +115 -0
  6. package/src/components/Blocks/DemoSitesExplorer/DemoSitesFilters.jsx +406 -0
  7. package/src/components/Blocks/DemoSitesExplorer/DemoSitesFilters.test.jsxZ +91 -0
  8. package/src/components/Blocks/DemoSitesExplorer/DemoSitesListing.jsx +383 -0
  9. package/src/components/Blocks/DemoSitesExplorer/DemoSitesMap.jsx +230 -0
  10. package/src/components/Blocks/DemoSitesExplorer/FeatureDisplay.jsx +97 -0
  11. package/src/components/Blocks/DemoSitesExplorer/FeatureDisplay.test.jsxZ +48 -0
  12. package/src/components/Blocks/DemoSitesExplorer/FeatureInteraction.jsx +95 -0
  13. package/src/components/Blocks/DemoSitesExplorer/InfoOverlay.jsx +79 -0
  14. package/src/components/Blocks/DemoSitesExplorer/hooks.js +20 -0
  15. package/src/components/Blocks/DemoSitesExplorer/images/icon-depth.png +0 -0
  16. package/src/components/Blocks/DemoSitesExplorer/images/icon-light.png +0 -0
  17. package/src/components/Blocks/DemoSitesExplorer/images/search.svg +3 -0
  18. package/src/components/Blocks/DemoSitesExplorer/index.js +16 -0
  19. package/src/components/Blocks/DemoSitesExplorer/mockJsdom.js +8 -0
  20. package/src/components/Blocks/DemoSitesExplorer/styles.less +376 -0
  21. package/src/components/Blocks/DemoSitesExplorer/utils.js +193 -0
  22. package/src/components/Blocks/DemoSitesExplorer/utils.test.jsZ +63 -0
  23. package/src/components/index.js +1 -0
  24. package/src/express-middleware.js +37 -0
  25. package/src/index.js +11 -5
@@ -0,0 +1,376 @@
1
+ .section-nwrm-case-studies .listing .slot-top {
2
+ padding-top: 2em;
3
+ }
4
+
5
+ #csepopup > span {
6
+ display: 'block';
7
+ margin-bottom: '10px';
8
+ background-color: '#ddd';
9
+ }
10
+
11
+ .ol-map-wrapper,
12
+ .ol-map {
13
+ height: 600px;
14
+ min-height: 600px;
15
+ }
16
+
17
+ #csepopup {
18
+ // width: 300px;
19
+ width: 100%;
20
+ // height: 100%;
21
+ padding: 1em;
22
+ // border: 1px solid black;
23
+ // font-size: 14px;
24
+ // line-height: initial;
25
+ font-size: 1rem;
26
+
27
+ a:hover {
28
+ color: #0083e0;
29
+ }
30
+
31
+ h3 {
32
+ font-size: 1.4rem;
33
+ }
34
+
35
+ h4 {
36
+ margin: 0px;
37
+ }
38
+
39
+ div {
40
+ margin-bottom: 16px;
41
+ }
42
+
43
+ ul {
44
+ margin: 0px;
45
+
46
+ li {
47
+ line-height: 22px;
48
+ }
49
+ }
50
+
51
+ p {
52
+ font-size: 13px;
53
+
54
+ p {
55
+ margin-bottom: 10px;
56
+ }
57
+ }
58
+
59
+ span.blue {
60
+ color: #069;
61
+ }
62
+
63
+ span.popup-title {
64
+ font-weight: bold;
65
+ }
66
+
67
+ span.img {
68
+ display: block;
69
+ margin-bottom: 10px;
70
+ background-color: #ddd;
71
+
72
+ img {
73
+ max-height: 133px;
74
+ }
75
+ }
76
+ }
77
+
78
+ .ol-overlaycontainer {
79
+ // width: 370px;
80
+ // height: 100%;
81
+
82
+ .ol-overlay-container {
83
+ // position: relative !important;
84
+ position: absolute !important;
85
+ right: 30px;
86
+ bottom: 30px;
87
+ display: block !important;
88
+ // width: 100%;
89
+ // height: 100%;
90
+ background-color: #dae8f4;
91
+
92
+ #popup-overlay {
93
+ position: relative !important;
94
+ display: flex;
95
+ width: 100%;
96
+ height: 100%;
97
+ align-items: center;
98
+ }
99
+ }
100
+ }
101
+
102
+ #cse-filter {
103
+ display: flex;
104
+ flex-direction: row;
105
+ margin-top: 1em;
106
+ font-family: sans-serif;
107
+ // gap: 3em;
108
+
109
+ .filter-wrapper {
110
+ // flex-grow: 1;
111
+
112
+ &.active {
113
+ button span {
114
+ // box-shadow: 0 5px 0 #ffffff, 0 0 4px rgba(0, 0, 0, 0.25);
115
+ color: #0083e0;
116
+ }
117
+
118
+ button {
119
+ box-shadow:
120
+ 0 5px 0 #ffffff,
121
+ 0 0 4px rgba(0, 0, 0, 0.25) !important;
122
+ }
123
+
124
+ .filter-inputs-wrapper {
125
+ display: block;
126
+ }
127
+ }
128
+
129
+ .ri-arrow-down-s-line:before {
130
+ content: '\ea4e';
131
+ }
132
+
133
+ .ui.basic.button.facet-btn {
134
+ position: relative !important;
135
+ z-index: 99999;
136
+ display: inline-block;
137
+ overflow: hidden;
138
+ padding: 0.5em 0em;
139
+ margin: 0 0.25em 0.25em 0;
140
+ background-color: white !important;
141
+ color: #3d5265 !important;
142
+
143
+ &:active {
144
+ background: white !important;
145
+ }
146
+
147
+ // &:hover,
148
+ // &:focus {
149
+ // color: rgba(0, 0, 0, 0.6) !important;
150
+ // }
151
+ &:hover {
152
+ box-shadow: none;
153
+ }
154
+
155
+ span {
156
+ position: relative !important;
157
+ z-index: 99999;
158
+ display: inline-block;
159
+ overflow: hidden;
160
+ padding: 0.5em 1em;
161
+ background-color: white;
162
+ }
163
+ }
164
+
165
+ .filter-inputs-wrapper {
166
+ position: absolute;
167
+ z-index: 1;
168
+ display: none;
169
+ padding: 1em;
170
+ margin-top: -3px;
171
+ background-color: #fff;
172
+ box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
173
+ color: #4f4f4f;
174
+
175
+ .filterInputText {
176
+ padding: 6px;
177
+ border: 0.5px solid #c4c4c4;
178
+ border-radius: 0;
179
+ margin-bottom: 0.7em;
180
+ font-size: 14px;
181
+ }
182
+
183
+ .filter-inputs {
184
+ overflow: auto;
185
+ min-width: 200px;
186
+ max-width: 400px;
187
+ max-height: 200px;
188
+
189
+ &::-webkit-scrollbar {
190
+ width: 8px;
191
+ height: 0.3rem !important;
192
+ }
193
+
194
+ &::-webkit-scrollbar-track {
195
+ border-radius: 10px;
196
+ background-color: #d9d9d9;
197
+ }
198
+
199
+ &::-webkit-scrollbar-thumb {
200
+ border-radius: 10px;
201
+ background: #004b7f;
202
+ }
203
+
204
+ .filter-input {
205
+ display: flex;
206
+ align-items: center;
207
+ justify-content: flex-start;
208
+ cursor: pointer;
209
+ font-size: 14px;
210
+
211
+ span {
212
+ padding-right: 5px;
213
+ }
214
+
215
+ input {
216
+ margin-right: 8px;
217
+ cursor: pointer;
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ h4 {
225
+ position: relative;
226
+ padding: 10px;
227
+ border-bottom: 1px solid #d8d8d8;
228
+ margin-bottom: 0 !important;
229
+ background-color: #005c96;
230
+ color: #fff;
231
+ cursor: pointer;
232
+ font-size: 14px;
233
+ }
234
+ }
235
+
236
+ .active-filter-list {
237
+ border: 1px solid #e6e7e8;
238
+ background-color: #f9f9f9 !important;
239
+
240
+ .filter-list-header {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 1em;
244
+
245
+ h4 {
246
+ margin: 0;
247
+ color: #3d5265;
248
+ }
249
+ }
250
+
251
+ .filter-list-content {
252
+ padding: 1em 0 0;
253
+ background-color: transparent;
254
+
255
+ .filter {
256
+ display: flex;
257
+ flex-wrap: wrap;
258
+ font-size: 15px;
259
+
260
+ .filter-wrapper {
261
+ display: flex;
262
+ flex-direction: row;
263
+ flex-wrap: wrap;
264
+ align-items: baseline;
265
+ padding: 8px 15px;
266
+ margin-top: 10px;
267
+ margin-right: 11px;
268
+ background-color: #ebebeb;
269
+ gap: 1em;
270
+
271
+ .filter-label {
272
+ font-weight: 500;
273
+ }
274
+
275
+ .filter-value {
276
+ position: relative;
277
+ display: flex;
278
+ align-items: center;
279
+ padding: 0;
280
+ border: none;
281
+ margin: 0;
282
+ background-color: transparent;
283
+ font-size: 15px;
284
+
285
+ .close.icon {
286
+ position: relative;
287
+ width: 16px;
288
+ height: 16px;
289
+ padding: 3px;
290
+ border: 1px solid #004b7f !important;
291
+ border-radius: 2px;
292
+ color: #2e3e4c;
293
+ opacity: 1;
294
+
295
+ &:before,
296
+ &:after {
297
+ position: absolute;
298
+ top: 2px;
299
+ left: 7px;
300
+ width: 1px;
301
+ height: 9px;
302
+ background-color: #004b7f !important;
303
+ content: ' ';
304
+ }
305
+
306
+ &:before {
307
+ transform: rotate(45deg);
308
+ }
309
+
310
+ &:after {
311
+ transform: rotate(-45deg);
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ .sui-search-box .search-input .terms-box .terms-box-left .search-icon img {
321
+ fill: #004b7f !important;
322
+ }
323
+
324
+ .searchlib-block .ui.basic.button.clear-btn {
325
+ color: #0083e0 !important;
326
+ }
327
+
328
+ .searchlib-block .result-item .result-info {
329
+ align-items: center;
330
+
331
+ &.show-on-map {
332
+ color: #0083e0 !important;
333
+ cursor: pointer;
334
+
335
+ .result-info-title {
336
+ color: #0083e0 !important;
337
+ }
338
+ }
339
+ }
340
+
341
+ .u-item.listing-item .result-info a {
342
+ color: #0077bc;
343
+ }
344
+
345
+ .searchlib-block .search-body-footer {
346
+ margin-top: 2em;
347
+ }
348
+
349
+ .reset-map-button.ui.button.secondary {
350
+ position: absolute;
351
+ z-index: 2;
352
+ top: 1.3em;
353
+ right: 0.7em;
354
+ display: flex;
355
+ padding: 8px 6px 8px 12px;
356
+ border: 4px solid #efefef;
357
+ border-radius: 2px;
358
+ background-color: #0083e0;
359
+ gap: 0.5em;
360
+ opacity: 0.6;
361
+
362
+ &.active {
363
+ display: flex;
364
+ }
365
+
366
+ &.inactive {
367
+ display: none;
368
+ }
369
+ }
370
+
371
+ .ol-zoom.ol-control {
372
+ .ol-zoom-in,
373
+ .ol-zoom-out {
374
+ background-color: rgba(0, 131, 224, 0.5);
375
+ }
376
+ }
@@ -0,0 +1,193 @@
1
+ import { openlayers as ol } from '@eeacms/volto-openlayers-map';
2
+
3
+ export function isValidURL(string) {
4
+ try {
5
+ new URL(string);
6
+ return true;
7
+ } catch (e) {
8
+ return false;
9
+ }
10
+ }
11
+
12
+ export function centerAndResetMapZoom(map) {
13
+ map.getView().animate({
14
+ zoom: 2.5,
15
+ duration: 1000,
16
+ center: ol.proj.transform([10, 54], 'EPSG:4326', 'EPSG:3857'),
17
+ });
18
+ }
19
+
20
+ export function scrollToElement(elementId) {
21
+ const element = document.getElementById(elementId);
22
+ element.scrollIntoView({
23
+ behavior: 'smooth',
24
+ });
25
+ }
26
+
27
+ export function getExtentOfFeatures(features) {
28
+ const points = features.map((f) => f.getGeometry().flatCoordinates);
29
+ const point = new ol.geom.MultiPoint(points);
30
+ return point.getExtent();
31
+ }
32
+
33
+ export function zoomMapToFeatures(map, features, threshold = 500) {
34
+ const extent = getExtentOfFeatures(features);
35
+ let extentBuffer = (extent[3] - extent[1] + extent[2] - extent[0]) / 4;
36
+ extentBuffer = extentBuffer < threshold ? threshold : extentBuffer;
37
+ const paddedExtent = ol.extent.buffer(extent, extentBuffer);
38
+ map.getView().fit(paddedExtent, { ...map.getSize(), duration: 1000 });
39
+ }
40
+
41
+ export function getFeatures(cases) {
42
+ const Feature = ol.ol.Feature;
43
+
44
+ return cases.map((c, index) => {
45
+ const {
46
+ geometry: { coordinates },
47
+ } = c;
48
+ const point = new Feature(
49
+ new ol.geom.Point(ol.proj.fromLonLat(coordinates)),
50
+ );
51
+ point.setId(index);
52
+ point.setProperties(
53
+ {
54
+ title: c.properties.title,
55
+ image: c.properties.image,
56
+ project: c.properties.project,
57
+ project_link: c.properties.project_link,
58
+ country: c.properties.country,
59
+ type_is_region: c.properties.type_is_region,
60
+ type: c.properties.type,
61
+ indicators: c.properties.indicators,
62
+ info: c.properties.info,
63
+ website: c.properties.website,
64
+ objective: c.properties.objective,
65
+ description: c.properties.description,
66
+ index: index,
67
+ path: c.properties.path,
68
+ color: c.properties.nwrm_type === 'Light' ? '#50B0A4' : '#0083E0',
69
+ },
70
+ false,
71
+ );
72
+ return point;
73
+ });
74
+ }
75
+
76
+ export function filterCases(cases, activeFilters, demoSitesIds, searchInput) {
77
+ const data = cases.filter((_case) => {
78
+ let flag_searchInput = false;
79
+ let flag_objective = false;
80
+ // let flag_indicator = false;
81
+ let flag_project = false;
82
+ let flag_country = false;
83
+ let flag_case = demoSitesIds
84
+ ? demoSitesIds.includes(_case.properties.url.split('/').pop())
85
+ : true;
86
+
87
+ if (!searchInput) {
88
+ flag_searchInput = true;
89
+ } else {
90
+ if (_case.properties.title.toLowerCase().match(searchInput)) {
91
+ flag_searchInput = true;
92
+ // } else if (
93
+ // _case.properties.description.toLowerCase().match(searchInput)
94
+ // ) {
95
+ // flag_searchInput = true;
96
+ }
97
+ }
98
+
99
+ // debugger;
100
+ if (!activeFilters.objective_filter.length) {
101
+ flag_objective = true;
102
+ } else {
103
+ let objective = _case.properties.objective?.map((item) => {
104
+ return item['id'].toString();
105
+ });
106
+
107
+ activeFilters.objective_filter.forEach((filter) => {
108
+ if (objective?.includes(filter)) flag_objective = true;
109
+ });
110
+ }
111
+
112
+ // if (!activeFilters.sectors.length) {
113
+ // flag_sectors = true;
114
+ // } else {
115
+ // let sectors = _case.properties.sectors?.map((item) => {
116
+ // return item.toString();
117
+ // });
118
+
119
+ // activeFilters.sectors.forEach((filter) => {
120
+ // if (sectors?.includes(filter)) flag_sectors = true;
121
+ // });
122
+ // }
123
+
124
+ if (!activeFilters.project_filter.length) {
125
+ flag_project = true;
126
+ } else {
127
+ let project = _case.properties.project;
128
+
129
+ activeFilters.project_filter.forEach((filter) => {
130
+ if (project === filter) flag_project = true;
131
+ });
132
+ }
133
+
134
+ if (!activeFilters.country_filter.length) {
135
+ flag_country = true;
136
+ } else {
137
+ let countries = _case.properties.country?.map((item) => {
138
+ return item.toString();
139
+ });
140
+
141
+ activeFilters.country_filter.forEach((filter) => {
142
+ if (countries?.includes(filter)) flag_country = true;
143
+ });
144
+ }
145
+
146
+ return flag_case &&
147
+ flag_objective &&
148
+ flag_country &&
149
+ flag_project &&
150
+ flag_searchInput
151
+ ? _case
152
+ : false;
153
+ });
154
+
155
+ return data;
156
+ }
157
+
158
+ export function getFilters(cases) {
159
+ let _filters = {
160
+ objective_filter: {},
161
+ indicator_filter: {},
162
+ project_filter: {},
163
+ country_filter: {},
164
+ };
165
+
166
+ for (let key of Object.keys(cases)) {
167
+ const _case = cases[key];
168
+ // debugger;
169
+ // let nwrms_implemented = _case.properties.measures;
170
+ // nwrms_implemented.map((item) => {
171
+ // if (!_filters.nwrms_implemented.hasOwnProperty(item['id'])) {
172
+ // _filters.nwrms_implemented[item['id']] = item['title'];
173
+ // }
174
+ // return [];
175
+ // });
176
+
177
+ let project = _case.properties.project;
178
+
179
+ if (!_filters.project_filter.hasOwnProperty(project)) {
180
+ _filters.project_filter[project] = project;
181
+ }
182
+
183
+ let countries = _case.properties.country;
184
+ countries.map((item) => {
185
+ if (!_filters.country_filter.hasOwnProperty(item)) {
186
+ _filters.country_filter[item] = item;
187
+ }
188
+ return [];
189
+ });
190
+ }
191
+
192
+ return _filters;
193
+ }
@@ -0,0 +1,63 @@
1
+ import './mockJsdom';
2
+ import '@testing-library/jest-dom/extend-expect';
3
+ import { getFeatures, filterCases, getFilters } from './utils';
4
+
5
+ describe('utils.js', () => {
6
+ const mockCases = [
7
+ {
8
+ geometry: { coordinates: [0, 0] },
9
+ properties: {
10
+ title: 'test case study',
11
+ image: '',
12
+ nwrm_type: 'light',
13
+ measures: [{ id: 'test-measure1', title: 'test measure 1' }],
14
+ description: 'test',
15
+ sectors: ['testsector'],
16
+ path: '/test-case-study',
17
+ url: 'localhost.com/test-case-study',
18
+ },
19
+ },
20
+ {
21
+ geometry: { coordinates: [0, 0] },
22
+ properties: {
23
+ title: 'case study 2',
24
+ image: '',
25
+ nwrm_type: 'light',
26
+ measures: [{ id: 'test-measure1', title: 'test measure 1' }],
27
+ description: 'test',
28
+ sectors: ['testsector'],
29
+ path: '/test-case-study',
30
+ url: 'localhost.com/test-case-study',
31
+ },
32
+ },
33
+ ];
34
+
35
+ test('getFeatures', () => {
36
+ expect(() => {
37
+ getFeatures(mockCases);
38
+ }).not.toThrowError();
39
+ });
40
+
41
+ test('filterCases', () => {
42
+ const mockActiveFilters = {
43
+ nwrms_implemented: ['test measure 1'],
44
+ sectors: ['testsector'],
45
+ };
46
+ const mockDemoSitesIds = ['test-case-study'];
47
+ const mockCasesFiltered = filterCases(
48
+ mockCases,
49
+ mockActiveFilters,
50
+ mockDemoSitesIds,
51
+ 'test',
52
+ );
53
+ expect(mockCasesFiltered).toStrictEqual([]);
54
+ });
55
+
56
+ test('getFilters', () => {
57
+ const mockFilters = getFilters(mockCases);
58
+ expect(mockFilters).toStrictEqual({
59
+ nwrms_implemented: { 'test-measure1': 'test measure 1' },
60
+ sectors: { testsector: 'testsector' },
61
+ });
62
+ });
63
+ });
@@ -13,3 +13,4 @@ export TableauShare from './theme/Tableau/TableauShare';
13
13
  export TableauFullscreen from './theme/Tableau/TableauFullscreen';
14
14
  export ItemMetadataSnippet from './theme/ItemMetadata/ItemMetadataSnippet';
15
15
  export MapPreview from './theme/MetadataListingView/MapPreview';
16
+ export DemoSitesExplorer from './Blocks/DemoSitesExplorer/DemoSitesExplorerView';
@@ -0,0 +1,37 @@
1
+ import express from 'express';
2
+ import { getAPIResourceWithAuth } from '@plone/volto/helpers';
3
+
4
+ const HEADERS = [
5
+ 'Accept-Ranges',
6
+ 'Cache-Control',
7
+ 'Content-Disposition',
8
+ 'Content-Range',
9
+ 'Content-Type',
10
+ ];
11
+
12
+ function viewMiddleware(req, res, next) {
13
+ getAPIResourceWithAuth(req)
14
+ .then((resource) => {
15
+ // Just forward the headers that we need
16
+ HEADERS.forEach((header) => {
17
+ if (resource.get(header)) {
18
+ res.set(header, resource.get(header));
19
+ }
20
+ });
21
+ res.status(resource.statusCode);
22
+ res.send(resource.body);
23
+ })
24
+ .catch(next);
25
+ }
26
+
27
+ export default function middleware(config) {
28
+ const middleware = express.Router();
29
+
30
+ // TODO: do we want catch all?
31
+ // middleware.all(['**/@@*'], viewMiddleware);
32
+
33
+ middleware.all(['**/@@demo-sites-map.arcgis.json'], viewMiddleware);
34
+ middleware.id = 'viewsMiddleware';
35
+ config.settings.expressMiddleware.push(middleware);
36
+ return config;
37
+ }
package/src/index.js CHANGED
@@ -28,7 +28,7 @@ import { linkDeserializer } from '@plone/volto-slate/editor/plugins/AdvancedLink
28
28
  import LinkEditSchema from '@plone/volto-slate/editor/plugins/AdvancedLink/schema';
29
29
  import { getBlocks } from '@plone/volto/helpers';
30
30
  import { defineMessages } from 'react-intl'; // , defineMessages
31
-
31
+ import installDemoSitesExplorer from './components/Blocks/DemoSitesExplorer';
32
32
  import marineLogo from '@eeacms/volto-marine-policy/../theme/assets/images/Header/wise-marine-logo.svg';
33
33
  import marineLogoWhite from '@eeacms/volto-marine-policy/../theme/assets/images/Header/wise-marine-logo-white.svg';
34
34
  import eeaWhiteLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/eea-logo-white.svg';
@@ -109,6 +109,11 @@ const applyConfig = (config) => {
109
109
  localnavigation,
110
110
  };
111
111
 
112
+ if (__SERVER__) {
113
+ const installExpressMiddleware = require('./express-middleware').default;
114
+ config = installExpressMiddleware(config);
115
+ }
116
+
112
117
  config.widgets.widget.text_align = TextAlignWidget;
113
118
  // check if it breaks the 'theme' field in volto-tabs-block in the 'horizontal carousel' layout
114
119
  // We have a 'theme' field in the wise catalogue metadata (CatalogueMetadata)
@@ -512,10 +517,11 @@ const applyConfig = (config) => {
512
517
  const [installLinkEditor] = makeInlineElementPlugin(opts);
513
518
  config = installLinkEditor(config);
514
519
 
515
- const final = [installMsfdDataExplorerBlock, installSearchEngine].reduce(
516
- (acc, apply) => apply(acc),
517
- config,
518
- );
520
+ const final = [
521
+ installMsfdDataExplorerBlock,
522
+ installSearchEngine,
523
+ installDemoSitesExplorer,
524
+ ].reduce((acc, apply) => apply(acc), config);
519
525
 
520
526
  return final;
521
527
  };