@eeacms/volto-cca-policy 0.3.44 → 0.3.46
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 +39 -0
- package/package.json +1 -1
- package/src/components/manage/Blocks/Listing/DropdownListingView.test.jsx +10 -27
- package/src/components/manage/Blocks/Listing/EventAccordionListingView.jsx +123 -0
- package/src/components/manage/Blocks/Listing/EventAccordionListingView.test.jsx +101 -0
- package/src/components/manage/Blocks/Listing/index.js +8 -0
- package/src/components/manage/Blocks/Listing/styles.less +19 -0
- package/src/constants.js +1 -1
- package/src/customizations/volto/components/theme/Footer/Footer.jsx +96 -0
- package/src/customizations/volto/helpers/Html/Html.jsx +219 -0
- package/src/customizations/volto/helpers/Html/README.md +3 -0
- package/src/customizations/volto/server.jsx +14 -0
- package/src/index.js +11 -0
- package/src/search/cca/config.js +11 -2
- package/src/search/common.js +1 -1
- package/theme/globals/mission.less +4 -2
- package/theme/globals/site.overrides +10 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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
|
+
### [0.3.46](https://github.com/eea/volto-cca-policy/compare/0.3.45...0.3.46) - 21 May 2025
|
|
8
|
+
|
|
9
|
+
#### :house: Internal changes
|
|
10
|
+
|
|
11
|
+
- style: Automated code fix [eea-jenkins - [`ea4b8be`](https://github.com/eea/volto-cca-policy/commit/ea4b8beed2cb7426c3125b6edb85d3b1fadf66a1)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
- Dont' need this now [Tiberiu Ichim - [`eff27fb`](https://github.com/eea/volto-cca-policy/commit/eff27fb4022b445c6c298a1c59e36ecab661e91e)]
|
|
16
|
+
- Add actions customization [Tiberiu Ichim - [`69f9b00`](https://github.com/eea/volto-cca-policy/commit/69f9b00cb45149e9b4d06085f4688800cb4d1d9f)]
|
|
17
|
+
- Remove etag from server [Tiberiu Ichim - [`7d94455`](https://github.com/eea/volto-cca-policy/commit/7d94455440b69ebd97c0ef5cc3dd839687b13e54)]
|
|
18
|
+
### [0.3.45](https://github.com/eea/volto-cca-policy/compare/0.3.44...0.3.45) - 21 May 2025
|
|
19
|
+
|
|
20
|
+
#### :rocket: New Features
|
|
21
|
+
|
|
22
|
+
- feat: add event accordion listing view - refs #286702 [kreafox - [`47b26e1`](https://github.com/eea/volto-cca-policy/commit/47b26e14d80e8a7bb0e067d9ffda87b7c028601a)]
|
|
23
|
+
|
|
24
|
+
#### :nail_care: Enhancements
|
|
25
|
+
|
|
26
|
+
- change: add Footer customization - refs #287013 [kreafox - [`875f5ab`](https://github.com/eea/volto-cca-policy/commit/875f5ab3bdf16fd2446757fd6949f9bf65eeaa96)]
|
|
27
|
+
|
|
28
|
+
#### :house: Internal changes
|
|
29
|
+
|
|
30
|
+
- style: Automated code fix [eea-jenkins - [`dc669ad`](https://github.com/eea/volto-cca-policy/commit/dc669ad88e0568b83821e99c3bc31066c3624261)]
|
|
31
|
+
- style: small css adjustments [kreafox - [`b93f7bb`](https://github.com/eea/volto-cca-policy/commit/b93f7bb521fbd2ecbb4245d5c07141ec8f4a74fd)]
|
|
32
|
+
|
|
33
|
+
#### :hammer_and_wrench: Others
|
|
34
|
+
|
|
35
|
+
- test: add tests to increase code coverage [kreafox - [`68fb5dc`](https://github.com/eea/volto-cca-policy/commit/68fb5dc83c58a77efa4f87cae4822758c93242e4)]
|
|
36
|
+
- Refs #284995 - add description [Tripon Eugen - [`9a2aa38`](https://github.com/eea/volto-cca-policy/commit/9a2aa38e4d0879acbcdedfdb95b08b1bf8ae6650)]
|
|
37
|
+
- Refs #284995 - add description [Tripon Eugen - [`2378952`](https://github.com/eea/volto-cca-policy/commit/2378952ca23bc4e4eae74445bec9a08869f6b8d7)]
|
|
7
38
|
### [0.3.44](https://github.com/eea/volto-cca-policy/compare/0.3.43...0.3.44) - 16 May 2025
|
|
8
39
|
|
|
9
40
|
#### :bug: Bug Fixes
|
|
@@ -2387,10 +2418,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
2387
2418
|
- Refs #260715 rast-block wip [Tripon Eugen - [`f19d54e`](https://github.com/eea/volto-cca-policy/commit/f19d54e0b9a6a86bf344eb85b6a1cda7f3de91bf)]
|
|
2388
2419
|
- Refs #260715 rast-block wip [Tripon Eugen - [`2828537`](https://github.com/eea/volto-cca-policy/commit/2828537b6c084cd1a82162d552fb4ef025b71f9f)]
|
|
2389
2420
|
- Refs #260715 rast-block updates [Tripon Eugen - [`1e803e5`](https://github.com/eea/volto-cca-policy/commit/1e803e5bd3d3fb7558f261c76c68866be7beb8b5)]
|
|
2421
|
+
- test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`0a15e1b`](https://github.com/eea/volto-cca-policy/commit/0a15e1b2ad081233685e80d5b3c60a8663f6b896)]
|
|
2422
|
+
- test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`9554e44`](https://github.com/eea/volto-cca-policy/commit/9554e44c92a621a52b2adb5a4830fb084ee5734b)]
|
|
2390
2423
|
### [0.1.49](https://github.com/eea/volto-cca-policy/compare/0.1.48...0.1.49) - 15 November 2023
|
|
2391
2424
|
|
|
2392
2425
|
#### :house: Internal changes
|
|
2393
2426
|
|
|
2427
|
+
- chore: [JENKINS] Refactor automated testing [valentinab25 - [`7b820a6`](https://github.com/eea/volto-cca-policy/commit/7b820a6369c2ddd5203b1a4abe352cb4bb43db7a)]
|
|
2394
2428
|
- chore: husky, lint-staged use fixed versions [valentinab25 - [`f0a8061`](https://github.com/eea/volto-cca-policy/commit/f0a8061c275c236deb00087c23fac9860a073106)]
|
|
2395
2429
|
|
|
2396
2430
|
#### :hammer_and_wrench: Others
|
|
@@ -2407,6 +2441,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
2407
2441
|
- Refs #259267 - jenkins test [Tripon Eugen - [`cacd31e`](https://github.com/eea/volto-cca-policy/commit/cacd31e7b1afe0983674ed5c7632d2e1d7fa752e)]
|
|
2408
2442
|
- Refs #259267 - jenkins [Tripon Eugen - [`5b3affe`](https://github.com/eea/volto-cca-policy/commit/5b3affee8401239de10097884c1b7f2349d15ec0)]
|
|
2409
2443
|
- Refs #259267 - add When, lead image and title to files [Tripon Eugen - [`2cedb23`](https://github.com/eea/volto-cca-policy/commit/2cedb237f898af9057e13fba94b615ef71077204)]
|
|
2444
|
+
- test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`4d607a5`](https://github.com/eea/volto-cca-policy/commit/4d607a576e9d0a5c34e48c41b409e7df616ee3d6)]
|
|
2445
|
+
- test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`b7f74d5`](https://github.com/eea/volto-cca-policy/commit/b7f74d53513a6edbfbca5cb6d19687929bb1e5db)]
|
|
2446
|
+
- test: [JENKINS] Improve cypress time [valentinab25 - [`db65617`](https://github.com/eea/volto-cca-policy/commit/db656173391f65157098d95d388c25f6429753d8)]
|
|
2410
2447
|
- Refs #259267 - cca event blocks attachments and check not mandatoty fields [Tripon Eugen - [`3138e5a`](https://github.com/eea/volto-cca-policy/commit/3138e5afb5bfbdbed14e27ed457b16867b7fa414)]
|
|
2411
2448
|
- Refs #256681 - Fix error in CCA Event view menu. ([React Intl] An id must be provided to format a message.) [GhitaB - [`517eeb8`](https://github.com/eea/volto-cca-policy/commit/517eeb817264a47bbfd6b9b7d22aaf22d44ed224)]
|
|
2412
2449
|
- Refs #161485 - Fix ECDE name conflict. [GhitaB - [`8bfd99f`](https://github.com/eea/volto-cca-policy/commit/8bfd99ff68bb82a04d1c0ed625fa514fcf46289e)]
|
|
@@ -2623,6 +2660,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
2623
2660
|
|
|
2624
2661
|
#### :house: Internal changes
|
|
2625
2662
|
|
|
2663
|
+
- chore: [JENKINS] Remove alpha testing version [valentinab25 - [`ad1ced0`](https://github.com/eea/volto-cca-policy/commit/ad1ced0971ba116c13a3b5fcc039172cc915c919)]
|
|
2626
2664
|
|
|
2627
2665
|
#### :hammer_and_wrench: Others
|
|
2628
2666
|
|
|
@@ -3103,6 +3141,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
3103
3141
|
#### :hammer_and_wrench: Others
|
|
3104
3142
|
|
|
3105
3143
|
- Refs #158294 - Update supported languages list. [GhitaB - [`0a4f91f`](https://github.com/eea/volto-cca-policy/commit/0a4f91f39b7edc367bd4c127d6a8f273c7788361)]
|
|
3144
|
+
- Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`8f1f9ce`](https://github.com/eea/volto-cca-policy/commit/8f1f9ce6c22805670cc0800d3c779b6d619d0f31)]
|
|
3106
3145
|
### [0.1.1](https://github.com/eea/volto-cca-policy/compare/0.1.0...0.1.1) - 13 December 2022
|
|
3107
3146
|
|
|
3108
3147
|
#### :hammer_and_wrench: Others
|
package/package.json
CHANGED
|
@@ -1,35 +1,14 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { MemoryRouter } from 'react-router-dom';
|
|
3
2
|
import configureStore from 'redux-mock-store';
|
|
4
|
-
import
|
|
5
|
-
import '@testing-library/jest-dom
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
6
5
|
import { Provider } from 'react-intl-redux';
|
|
7
6
|
import DropdownListingView from './DropdownListingView';
|
|
8
|
-
import config from '@plone/volto/registry';
|
|
9
|
-
|
|
10
|
-
config.blocks = {
|
|
11
|
-
blocksConfig: {
|
|
12
|
-
contentLinks: {
|
|
13
|
-
variations: [
|
|
14
|
-
{
|
|
15
|
-
id: 'default',
|
|
16
|
-
title: 'Simple list (default)',
|
|
17
|
-
isDefault: true,
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
id: 'navigationList',
|
|
21
|
-
title: 'Navigation list',
|
|
22
|
-
isDefault: false,
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
7
|
|
|
29
8
|
const mockStore = configureStore();
|
|
30
9
|
|
|
31
10
|
describe('DropdownListingView', () => {
|
|
32
|
-
it('
|
|
11
|
+
it('renders dropdown with items', () => {
|
|
33
12
|
const data = {
|
|
34
13
|
'@type': 'listing',
|
|
35
14
|
items: [
|
|
@@ -54,14 +33,18 @@ describe('DropdownListingView', () => {
|
|
|
54
33
|
},
|
|
55
34
|
});
|
|
56
35
|
|
|
57
|
-
|
|
36
|
+
render(
|
|
58
37
|
<Provider store={store}>
|
|
59
38
|
<MemoryRouter>
|
|
60
39
|
<DropdownListingView {...data} />
|
|
61
40
|
</MemoryRouter>
|
|
62
41
|
</Provider>,
|
|
63
42
|
);
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
|
|
44
|
+
// Check the placeholder text is present
|
|
45
|
+
expect(screen.getByText('Select')).toBeInTheDocument();
|
|
46
|
+
|
|
47
|
+
expect(screen.getAllByText('Item 1').length).toBeGreaterThan(0);
|
|
48
|
+
expect(screen.getAllByText('Item 2').length).toBeGreaterThan(0);
|
|
66
49
|
});
|
|
67
50
|
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
import { useSelector } from 'react-redux';
|
|
5
|
+
import { Icon, Accordion, Button } from 'semantic-ui-react';
|
|
6
|
+
import { ConditionalLink } from '@plone/volto/components';
|
|
7
|
+
import { When } from '@plone/volto/components/theme/View/EventDatesInfo';
|
|
8
|
+
|
|
9
|
+
import './styles.less';
|
|
10
|
+
|
|
11
|
+
function formatDateRange(startStr, endStr, locale = 'en') {
|
|
12
|
+
if (!startStr || !endStr) return null;
|
|
13
|
+
|
|
14
|
+
const startDate = new Date(startStr);
|
|
15
|
+
const endDate = new Date(endStr);
|
|
16
|
+
|
|
17
|
+
if (isNaN(startDate) || isNaN(endDate)) return null;
|
|
18
|
+
|
|
19
|
+
const sameDay = startDate.toDateString() === endDate.toDateString();
|
|
20
|
+
const sameMonth = startDate.getMonth() === endDate.getMonth();
|
|
21
|
+
const sameYear = startDate.getFullYear() === endDate.getFullYear();
|
|
22
|
+
|
|
23
|
+
const day = (date) => date.toLocaleDateString(locale, { day: 'numeric' });
|
|
24
|
+
|
|
25
|
+
const month = (date) => date.toLocaleDateString(locale, { month: 'long' });
|
|
26
|
+
|
|
27
|
+
const year = (date) => date.toLocaleDateString(locale, { year: 'numeric' });
|
|
28
|
+
|
|
29
|
+
if (sameDay) {
|
|
30
|
+
return `${day(startDate)} ${month(startDate)} ${year(startDate)}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (sameMonth && sameYear) {
|
|
34
|
+
return `${day(startDate)} - ${day(endDate)} ${month(endDate)} ${year(
|
|
35
|
+
endDate,
|
|
36
|
+
)}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (sameYear) {
|
|
40
|
+
return `${day(startDate)} ${month(startDate)} - ${day(endDate)} ${month(
|
|
41
|
+
endDate,
|
|
42
|
+
)} ${year(endDate)}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Different years
|
|
46
|
+
return `${day(startDate)} ${month(startDate)} ${year(startDate)} – ${day(
|
|
47
|
+
endDate,
|
|
48
|
+
)} ${month(endDate)} ${year(endDate)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const EventAccordionListingView = ({ items, isEditMode }) => {
|
|
52
|
+
const currentLang = useSelector((state) => state.intl.locale);
|
|
53
|
+
const [activeIndex, setActiveIndex] = React.useState(-1);
|
|
54
|
+
const handleAccordionClick = (index) => {
|
|
55
|
+
setActiveIndex(activeIndex === index ? -1 : index);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="ui fluid event-accordion-view">
|
|
60
|
+
<Accordion className="primary">
|
|
61
|
+
{items.map((item, index) => {
|
|
62
|
+
const formattedDate = formatDateRange(
|
|
63
|
+
item.start,
|
|
64
|
+
item.end,
|
|
65
|
+
currentLang,
|
|
66
|
+
);
|
|
67
|
+
return (
|
|
68
|
+
<React.Fragment key={item.UID}>
|
|
69
|
+
<Accordion.Title
|
|
70
|
+
active={activeIndex === index}
|
|
71
|
+
onClick={() => handleAccordionClick(index)}
|
|
72
|
+
>
|
|
73
|
+
<Icon
|
|
74
|
+
className={
|
|
75
|
+
activeIndex === index
|
|
76
|
+
? 'ri-arrow-up-s-line'
|
|
77
|
+
: 'ri-arrow-down-s-line'
|
|
78
|
+
}
|
|
79
|
+
/>
|
|
80
|
+
<div>
|
|
81
|
+
<div className="listing-header">
|
|
82
|
+
{item?.start && <span>{formattedDate}</span>}
|
|
83
|
+
{item.location && <> - {item.location}</>}
|
|
84
|
+
</div>
|
|
85
|
+
<div>{item.title}</div>
|
|
86
|
+
</div>
|
|
87
|
+
</Accordion.Title>
|
|
88
|
+
<Accordion.Content active={activeIndex === index}>
|
|
89
|
+
{item?.description && (
|
|
90
|
+
<p className="listing-description">{item.description}</p>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
<div className="bottom-info">
|
|
94
|
+
<ConditionalLink item={item} condition={!isEditMode}>
|
|
95
|
+
<Button inverted primary>
|
|
96
|
+
<FormattedMessage
|
|
97
|
+
id="Learn more"
|
|
98
|
+
defaultMessage="Learn more"
|
|
99
|
+
/>
|
|
100
|
+
</Button>
|
|
101
|
+
</ConditionalLink>
|
|
102
|
+
<When
|
|
103
|
+
start={item.start}
|
|
104
|
+
end={item.end}
|
|
105
|
+
whole_day={item.whole_day}
|
|
106
|
+
open_end={item.open_end}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</Accordion.Content>
|
|
110
|
+
</React.Fragment>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</Accordion>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
EventAccordionListingView.propTypes = {
|
|
119
|
+
items: PropTypes.arrayOf(PropTypes.any).isRequired,
|
|
120
|
+
isEditMode: PropTypes.bool,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export default EventAccordionListingView;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
4
|
+
import configureStore from 'redux-mock-store';
|
|
5
|
+
import { Provider } from 'react-intl-redux';
|
|
6
|
+
import EventAccordionListingView from './EventAccordionListingView';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore();
|
|
9
|
+
|
|
10
|
+
jest.mock('@plone/volto/components', () => ({
|
|
11
|
+
ConditionalLink: ({ children }) => <div>{children}</div>,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock('@plone/volto/components/theme/View/EventDatesInfo', () => ({
|
|
15
|
+
When: () => <div>Event Date Info</div>,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('EventAccordionListingView', () => {
|
|
19
|
+
let store;
|
|
20
|
+
const items = [
|
|
21
|
+
{
|
|
22
|
+
UID: 'event-123',
|
|
23
|
+
title: 'Test Event One',
|
|
24
|
+
description: 'Details about Event One',
|
|
25
|
+
start: '2025-05-20T10:00:00',
|
|
26
|
+
end: '2025-05-20T12:00:00',
|
|
27
|
+
location: 'Hall A',
|
|
28
|
+
whole_day: false,
|
|
29
|
+
open_end: false,
|
|
30
|
+
'@id': '/event-1',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
UID: 'event-456',
|
|
34
|
+
title: 'Test Event Two',
|
|
35
|
+
description: 'Details about Event Two',
|
|
36
|
+
start: '2025-06-10T09:00:00',
|
|
37
|
+
end: '2025-06-10T11:00:00',
|
|
38
|
+
location: 'Room B',
|
|
39
|
+
whole_day: false,
|
|
40
|
+
open_end: false,
|
|
41
|
+
'@id': '/event-2',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
store = mockStore({
|
|
47
|
+
intl: {
|
|
48
|
+
locale: 'en',
|
|
49
|
+
messages: {},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders all event titles', () => {
|
|
55
|
+
render(
|
|
56
|
+
<Provider store={store}>
|
|
57
|
+
<MemoryRouter>
|
|
58
|
+
<EventAccordionListingView items={items} isEditMode={false} />
|
|
59
|
+
</MemoryRouter>
|
|
60
|
+
</Provider>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(screen.getByText('Test Event One')).toBeInTheDocument();
|
|
64
|
+
expect(screen.getByText('Test Event Two')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('toggles accordion content on title click', () => {
|
|
68
|
+
render(
|
|
69
|
+
<Provider store={store}>
|
|
70
|
+
<MemoryRouter>
|
|
71
|
+
<EventAccordionListingView items={items} isEditMode={false} />
|
|
72
|
+
</MemoryRouter>
|
|
73
|
+
</Provider>,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const title = screen.getByText('Test Event One');
|
|
77
|
+
expect(title.closest('.title')).toHaveClass('title');
|
|
78
|
+
expect(title.closest('.title')).not.toHaveClass('active');
|
|
79
|
+
|
|
80
|
+
fireEvent.click(title);
|
|
81
|
+
|
|
82
|
+
expect(title.closest('.title')).toHaveClass('active');
|
|
83
|
+
|
|
84
|
+
expect(screen.getByText('Details about Event One')).toBeInTheDocument();
|
|
85
|
+
|
|
86
|
+
fireEvent.click(title);
|
|
87
|
+
expect(title.closest('.title')).not.toHaveClass('active');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('renders Learn more buttons', () => {
|
|
91
|
+
render(
|
|
92
|
+
<Provider store={store}>
|
|
93
|
+
<MemoryRouter>
|
|
94
|
+
<EventAccordionListingView items={items} isEditMode={false} />
|
|
95
|
+
</MemoryRouter>
|
|
96
|
+
</Provider>,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(screen.getAllByText('Learn more')).toHaveLength(2);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -2,6 +2,7 @@ import OrganisationCardsListingView from './OrganisationCardsListingView';
|
|
|
2
2
|
import IndicatorCardsListingView from './IndicatorCardsListingView';
|
|
3
3
|
import EventCardsListingView from './EventCardsListingView';
|
|
4
4
|
import DropdownListingView from './DropdownListingView';
|
|
5
|
+
import EventAccordionListingView from './EventAccordionListingView';
|
|
5
6
|
|
|
6
7
|
export default function installListing(config) {
|
|
7
8
|
config.blocks.blocksConfig.listing = {
|
|
@@ -43,6 +44,13 @@ export default function installListing(config) {
|
|
|
43
44
|
isDefault: false,
|
|
44
45
|
fullobjects: true,
|
|
45
46
|
},
|
|
47
|
+
{
|
|
48
|
+
id: 'eventAccordion',
|
|
49
|
+
title: 'Event Accordion',
|
|
50
|
+
template: EventAccordionListingView,
|
|
51
|
+
isDefault: false,
|
|
52
|
+
fullobjects: true,
|
|
53
|
+
},
|
|
46
54
|
],
|
|
47
55
|
};
|
|
48
56
|
|
|
@@ -142,3 +142,22 @@ div.indicatorCards {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
.event-accordion-view {
|
|
147
|
+
.listing-header {
|
|
148
|
+
font-weight: bold;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.bottom-info {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-wrap: wrap;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: flex-start;
|
|
156
|
+
margin-top: 2em;
|
|
157
|
+
gap: 1em;
|
|
158
|
+
|
|
159
|
+
p {
|
|
160
|
+
margin: 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -6,6 +6,7 @@ export const download_fields = [
|
|
|
6
6
|
{ field: 'issued', name: 'Publication Date' },
|
|
7
7
|
{ field: 'creators', name: 'Creator' },
|
|
8
8
|
{ field: 'objectProvides', name: 'Content type' },
|
|
9
|
+
{ field: 'fulltext', name: 'Description' },
|
|
9
10
|
{ field: 'cca_keywords', name: 'Keywords' },
|
|
10
11
|
{ field: 'cca_adaptation_sectors', name: 'Sectors' },
|
|
11
12
|
{ field: 'cca_climate_impacts', name: 'Climate impact' },
|
|
@@ -17,7 +18,6 @@ export const download_fields = [
|
|
|
17
18
|
{ field: 'cca_origin_websites', name: 'Origin website' },
|
|
18
19
|
{ field: 'cca_health_impacts', name: 'Health impacts' },
|
|
19
20
|
{ field: 'cca_partner_contributors', name: 'Observatory impacts' },
|
|
20
|
-
// { field: 'fulltext', name: 'Description' },
|
|
21
21
|
];
|
|
22
22
|
|
|
23
23
|
export const eea_languages = [
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Footer component.
|
|
3
|
+
* @module components/theme/Footer/Footer
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { useSelector, shallowEqual } from 'react-redux';
|
|
8
|
+
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
9
|
+
import EEAFooter from '@eeacms/volto-eea-design-system/ui/Footer/Footer';
|
|
10
|
+
import config from '@plone/volto/registry';
|
|
11
|
+
import isArray from 'lodash/isArray';
|
|
12
|
+
|
|
13
|
+
const Footer = () => {
|
|
14
|
+
const { eea } = config.settings;
|
|
15
|
+
const {
|
|
16
|
+
footerActions,
|
|
17
|
+
copyrightActions,
|
|
18
|
+
socialActions,
|
|
19
|
+
contactActions,
|
|
20
|
+
contactExtraActions,
|
|
21
|
+
} = useSelector(
|
|
22
|
+
(state) => ({
|
|
23
|
+
footerActions: state.actions?.actions?.footer_actions,
|
|
24
|
+
copyrightActions: state.actions?.actions?.copyright_actions,
|
|
25
|
+
socialActions: state.actions?.actions?.social_actions,
|
|
26
|
+
contactActions: state.actions?.actions?.contact_actions,
|
|
27
|
+
contactExtraActions: state.actions?.actions?.contact_extra_actions,
|
|
28
|
+
}),
|
|
29
|
+
shallowEqual,
|
|
30
|
+
);
|
|
31
|
+
// ZMI > portal_actions > footer_actions
|
|
32
|
+
const actions = isArray(footerActions)
|
|
33
|
+
? footerActions.map((action) => ({
|
|
34
|
+
title: action.title,
|
|
35
|
+
url: flattenToAppURL(action.url),
|
|
36
|
+
}))
|
|
37
|
+
: eea.footerOpts.actions;
|
|
38
|
+
|
|
39
|
+
// ZMI > portal_actions > copyright_actions
|
|
40
|
+
const copyright = isArray(copyrightActions)
|
|
41
|
+
? copyrightActions.map((action) => ({
|
|
42
|
+
title: action.title,
|
|
43
|
+
site: action.title,
|
|
44
|
+
url: flattenToAppURL(action.url),
|
|
45
|
+
}))
|
|
46
|
+
: eea.footerOpts.copyright;
|
|
47
|
+
|
|
48
|
+
// ZMI > portal_actions > social_actions
|
|
49
|
+
const social = isArray(socialActions)
|
|
50
|
+
? socialActions.map((action) => ({
|
|
51
|
+
name: action.id,
|
|
52
|
+
icon: action.icon,
|
|
53
|
+
url: action.url,
|
|
54
|
+
}))
|
|
55
|
+
: eea.footerOpts.social;
|
|
56
|
+
|
|
57
|
+
// ZMI > portal_actions > contact_actions
|
|
58
|
+
const contacts = isArray(contactActions)
|
|
59
|
+
? contactActions.map((action, idx) => ({
|
|
60
|
+
text: action.title,
|
|
61
|
+
icon: action.icon,
|
|
62
|
+
url: flattenToAppURL(action.url),
|
|
63
|
+
children:
|
|
64
|
+
idx === 0
|
|
65
|
+
? (contactExtraActions || []).map((child) => ({
|
|
66
|
+
text: child.title,
|
|
67
|
+
icon: child.icon,
|
|
68
|
+
url: flattenToAppURL(child.url),
|
|
69
|
+
}))
|
|
70
|
+
: [],
|
|
71
|
+
}))
|
|
72
|
+
: eea.footerOpts.contacts;
|
|
73
|
+
|
|
74
|
+
// Update options with actions from backend
|
|
75
|
+
const options = {
|
|
76
|
+
...eea.footerOpts,
|
|
77
|
+
social,
|
|
78
|
+
contacts,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<EEAFooter>
|
|
83
|
+
<EEAFooter.Header>{eea.footerOpts.logosHeader}</EEAFooter.Header>
|
|
84
|
+
<EEAFooter.SubFooter {...options} />
|
|
85
|
+
<EEAFooter.Header>{eea.footerOpts.header}</EEAFooter.Header>
|
|
86
|
+
<EEAFooter.SitesButton
|
|
87
|
+
buttonName={eea.footerOpts.buttonName}
|
|
88
|
+
hrefButton={eea.footerOpts.hrefButton}
|
|
89
|
+
/>
|
|
90
|
+
<EEAFooter.Social {...options} />
|
|
91
|
+
<EEAFooter.Actions actions={actions} copyright={copyright} />
|
|
92
|
+
</EEAFooter>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default Footer;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Html helper.
|
|
3
|
+
* @module helpers/Html
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { Component } from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
9
|
+
import serialize from 'serialize-javascript';
|
|
10
|
+
import { join } from 'lodash';
|
|
11
|
+
import BodyClass from '@plone/volto/helpers/BodyClass/BodyClass';
|
|
12
|
+
import { runtimeConfig } from '@plone/volto/runtime_config';
|
|
13
|
+
import config from '@plone/volto/registry';
|
|
14
|
+
|
|
15
|
+
const CRITICAL_CSS_TEMPLATE = `function alter() {
|
|
16
|
+
document.querySelectorAll("head link[rel='prefetch']").forEach(function(el) { el.rel = 'stylesheet'});
|
|
17
|
+
}
|
|
18
|
+
if (window.addEventListener) {
|
|
19
|
+
window.addEventListener('DOMContentLoaded', alter, false)
|
|
20
|
+
} else {
|
|
21
|
+
window.onload=alter
|
|
22
|
+
}`;
|
|
23
|
+
|
|
24
|
+
export const loadReducers = (state = {}) => {
|
|
25
|
+
const { settings } = config;
|
|
26
|
+
return Object.assign(
|
|
27
|
+
{},
|
|
28
|
+
...Object.keys(state).map((name) =>
|
|
29
|
+
settings.initialReducersBlacklist.includes(name)
|
|
30
|
+
? {}
|
|
31
|
+
: { [name]: state[name] },
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Html class.
|
|
38
|
+
* Wrapper component containing HTML metadata and boilerplate tags.
|
|
39
|
+
* Used in server-side code only to wrap the string output of the
|
|
40
|
+
* rendered route component.
|
|
41
|
+
*
|
|
42
|
+
* The only thing this component doesn't (and can't) include is the
|
|
43
|
+
* HTML doctype declaration, which is added to the rendered output
|
|
44
|
+
* by the server.js file.
|
|
45
|
+
*
|
|
46
|
+
* Critical.css behaviour: when a file `public/critical.css` is present, the
|
|
47
|
+
* loading of stylesheets is changed. The styles in critical.css are inlined in
|
|
48
|
+
* the generated HTML, and the whole story needs to change completely: instead
|
|
49
|
+
* of treating stylesheets as priority for rendering, we want to defer their
|
|
50
|
+
* loading as much as possible. So we change the stylesheets to be prefetched
|
|
51
|
+
* and we switch their rel back to stylesheets at document ready event.
|
|
52
|
+
*
|
|
53
|
+
* @function Html
|
|
54
|
+
* @param {Object} props Component properties.
|
|
55
|
+
* @param {Object} props.assets Assets to be rendered.
|
|
56
|
+
* @param {Object} props.component Content to be rendered as child node.
|
|
57
|
+
* @param {Object} props.store Store object.
|
|
58
|
+
* @returns {string} Markup of the not found page.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Html class.
|
|
63
|
+
* @class Html
|
|
64
|
+
* @extends Component
|
|
65
|
+
*/
|
|
66
|
+
class Html extends Component {
|
|
67
|
+
/**
|
|
68
|
+
* Property types.
|
|
69
|
+
* @property {Object} propTypes Property types.
|
|
70
|
+
* @static
|
|
71
|
+
*/
|
|
72
|
+
static propTypes = {
|
|
73
|
+
extractor: PropTypes.shape({
|
|
74
|
+
getLinkElements: PropTypes.func.isRequired,
|
|
75
|
+
getScriptElements: PropTypes.func.isRequired,
|
|
76
|
+
getStyleElements: PropTypes.func.isRequired,
|
|
77
|
+
}).isRequired,
|
|
78
|
+
markup: PropTypes.string.isRequired,
|
|
79
|
+
store: PropTypes.shape({
|
|
80
|
+
getState: PropTypes.func,
|
|
81
|
+
}).isRequired,
|
|
82
|
+
nonce: PropTypes.string,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Render method.
|
|
87
|
+
* @method render
|
|
88
|
+
* @returns {string} Markup for the component.
|
|
89
|
+
*/
|
|
90
|
+
render() {
|
|
91
|
+
const {
|
|
92
|
+
extractor,
|
|
93
|
+
markup,
|
|
94
|
+
store,
|
|
95
|
+
criticalCss,
|
|
96
|
+
apiPath,
|
|
97
|
+
publicURL,
|
|
98
|
+
nonce,
|
|
99
|
+
} = this.props;
|
|
100
|
+
const head = Helmet.rewind();
|
|
101
|
+
const bodyClass = join(BodyClass.rewind(), ' ');
|
|
102
|
+
const htmlAttributes = head.htmlAttributes.toComponent();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<html lang={htmlAttributes.lang}>
|
|
106
|
+
<head>
|
|
107
|
+
<meta charSet="utf-8" />
|
|
108
|
+
{head.base.toComponent()}
|
|
109
|
+
{head.title.toComponent()}
|
|
110
|
+
{head.meta.toComponent()}
|
|
111
|
+
{head.link.toComponent()}
|
|
112
|
+
{head.script.toComponent()}
|
|
113
|
+
|
|
114
|
+
{React.createElement('script', {
|
|
115
|
+
nonce: nonce,
|
|
116
|
+
dangerouslySetInnerHTML: {
|
|
117
|
+
__html: `window.env = ${serialize(
|
|
118
|
+
{
|
|
119
|
+
...runtimeConfig,
|
|
120
|
+
// Seamless mode requirement, the client need to know where the API is located
|
|
121
|
+
// if not set in the API_PATH
|
|
122
|
+
...(apiPath && {
|
|
123
|
+
apiPath,
|
|
124
|
+
}),
|
|
125
|
+
...(publicURL && {
|
|
126
|
+
publicURL,
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
{ space: 2 },
|
|
130
|
+
)};`,
|
|
131
|
+
},
|
|
132
|
+
})}
|
|
133
|
+
|
|
134
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
135
|
+
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
|
136
|
+
<link
|
|
137
|
+
rel="apple-touch-icon"
|
|
138
|
+
sizes="180x180"
|
|
139
|
+
href="/apple-touch-icon.png"
|
|
140
|
+
/>
|
|
141
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
142
|
+
<meta name="generator" content="Plone 6 - https://plone.org" />
|
|
143
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
144
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
145
|
+
{process.env.NODE_ENV === 'production' && criticalCss && (
|
|
146
|
+
<style
|
|
147
|
+
dangerouslySetInnerHTML={{ __html: this.props.criticalCss }}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
{/* Add the crossorigin while in development */}
|
|
151
|
+
{extractor.getLinkElements().map((elem) =>
|
|
152
|
+
React.cloneElement(elem, {
|
|
153
|
+
crossOrigin:
|
|
154
|
+
process.env.NODE_ENV === 'production' ? undefined : 'true',
|
|
155
|
+
rel: !criticalCss
|
|
156
|
+
? elem.props.rel
|
|
157
|
+
: elem.props.as === 'style'
|
|
158
|
+
? 'prefetch'
|
|
159
|
+
: elem.props.rel,
|
|
160
|
+
}),
|
|
161
|
+
)}
|
|
162
|
+
{/* Styles in development are loaded with Webpack's style-loader, in production,
|
|
163
|
+
they need to be static*/}
|
|
164
|
+
{process.env.NODE_ENV === 'production' ? (
|
|
165
|
+
criticalCss ? (
|
|
166
|
+
<>
|
|
167
|
+
<script
|
|
168
|
+
dangerouslySetInnerHTML={{
|
|
169
|
+
__html: CRITICAL_CSS_TEMPLATE,
|
|
170
|
+
}}
|
|
171
|
+
></script>
|
|
172
|
+
{extractor.getStyleElements().map((elem) => (
|
|
173
|
+
<noscript>
|
|
174
|
+
{React.cloneElement(elem, {
|
|
175
|
+
rel: 'stylesheet',
|
|
176
|
+
crossOrigin:
|
|
177
|
+
process.env.NODE_ENV === 'production'
|
|
178
|
+
? undefined
|
|
179
|
+
: 'true',
|
|
180
|
+
})}
|
|
181
|
+
</noscript>
|
|
182
|
+
))}
|
|
183
|
+
</>
|
|
184
|
+
) : (
|
|
185
|
+
extractor.getStyleElements()
|
|
186
|
+
)
|
|
187
|
+
) : undefined}
|
|
188
|
+
</head>
|
|
189
|
+
<body className={bodyClass}>
|
|
190
|
+
<div role="navigation" aria-label="Toolbar" id="toolbar" />
|
|
191
|
+
<div id="main" dangerouslySetInnerHTML={{ __html: markup }} />
|
|
192
|
+
<div role="complementary" aria-label="Sidebar" id="sidebar" />
|
|
193
|
+
{React.createElement('script', {
|
|
194
|
+
nonce: nonce,
|
|
195
|
+
dangerouslySetInnerHTML: {
|
|
196
|
+
__html: `window.__data=${serialize(
|
|
197
|
+
loadReducers(store.getState()),
|
|
198
|
+
{ space: 2 },
|
|
199
|
+
)};`,
|
|
200
|
+
},
|
|
201
|
+
charSet: 'UTF-8',
|
|
202
|
+
})}
|
|
203
|
+
{/* Add the crossorigin while in development */}
|
|
204
|
+
{this.props.extractScripts !== false
|
|
205
|
+
? extractor.getScriptElements().map((elem) =>
|
|
206
|
+
React.cloneElement(elem, {
|
|
207
|
+
nonce: nonce,
|
|
208
|
+
crossOrigin:
|
|
209
|
+
process.env.NODE_ENV === 'production' ? undefined : 'true',
|
|
210
|
+
}),
|
|
211
|
+
)
|
|
212
|
+
: ''}
|
|
213
|
+
</body>
|
|
214
|
+
</html>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export default Html;
|
|
@@ -20,6 +20,7 @@ import { resetServerContext } from 'react-beautiful-dnd';
|
|
|
20
20
|
import { CookiesProvider } from 'react-cookie';
|
|
21
21
|
import cookiesMiddleware from 'universal-cookie-express';
|
|
22
22
|
import debug from 'debug';
|
|
23
|
+
// import crypto from 'crypto';
|
|
23
24
|
|
|
24
25
|
import routes from '@root/routes';
|
|
25
26
|
import config from '@plone/volto/registry';
|
|
@@ -64,6 +65,18 @@ if (config.settings) {
|
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// function buildCSPHeader(opts, nonce) {
|
|
69
|
+
// return Object.keys(opts)
|
|
70
|
+
// .sort()
|
|
71
|
+
// .reduce((acc, key) => {
|
|
72
|
+
// return [
|
|
73
|
+
// ...acc,
|
|
74
|
+
// `${key} ${opts[key].replaceAll('{nonce}', `'nonce-${nonce}'`)}`,
|
|
75
|
+
// ];
|
|
76
|
+
// }, [])
|
|
77
|
+
// .join('; ');
|
|
78
|
+
// }
|
|
79
|
+
|
|
67
80
|
function reactIntlErrorHandler(error) {
|
|
68
81
|
debug('i18n')(error);
|
|
69
82
|
}
|
|
@@ -71,6 +84,7 @@ function reactIntlErrorHandler(error) {
|
|
|
71
84
|
const supported = new locale.Locales(keys(languages), 'en');
|
|
72
85
|
|
|
73
86
|
const server = express()
|
|
87
|
+
.set('etag', false)
|
|
74
88
|
.disable('x-powered-by')
|
|
75
89
|
.head('/*', function (req, res) {
|
|
76
90
|
// Support for HEAD requests. Required by start-test utility in CI.
|
package/src/index.js
CHANGED
|
@@ -158,6 +158,10 @@ const applyConfig = (config) => {
|
|
|
158
158
|
],
|
|
159
159
|
footerOpts: {
|
|
160
160
|
...(config.settings.eea?.footerOpts || {}),
|
|
161
|
+
buttonName: 'Explore our environmental information systems',
|
|
162
|
+
hrefButton: 'https://www.eea.europa.eu/en/information-systems#',
|
|
163
|
+
logosHeader: 'Managed by',
|
|
164
|
+
header: '',
|
|
161
165
|
description:
|
|
162
166
|
'The European Climate Adaptation Platform Climate-ADAPT is a partnership between the European Commission and the European Environment Agency.',
|
|
163
167
|
managedBy: [
|
|
@@ -551,6 +555,13 @@ const applyConfig = (config) => {
|
|
|
551
555
|
...config.settings.storeExtenders,
|
|
552
556
|
];
|
|
553
557
|
|
|
558
|
+
config.settings.initialReducersBlacklist = [
|
|
559
|
+
...config.settings.initialReducersBlacklist,
|
|
560
|
+
'intl',
|
|
561
|
+
// 'router',
|
|
562
|
+
// 'content',
|
|
563
|
+
];
|
|
564
|
+
|
|
554
565
|
config.widgets.vocabulary[
|
|
555
566
|
'plone.app.vocabularies.Users'
|
|
556
567
|
] = SelectAutoCompleteWidget;
|
package/src/search/cca/config.js
CHANGED
|
@@ -63,7 +63,6 @@ cca_build_runtime_mappings['op_cluster'] = {
|
|
|
63
63
|
|
|
64
64
|
export default function installMainSearch(config) {
|
|
65
65
|
const envConfig = ccaConfig;
|
|
66
|
-
|
|
67
66
|
const pjson = require('@eeacms/volto-cca-policy/../package.json');
|
|
68
67
|
|
|
69
68
|
envConfig.app_name = pjson.name;
|
|
@@ -221,7 +220,17 @@ export default function installMainSearch(config) {
|
|
|
221
220
|
config.searchui.ccaSearch.host =
|
|
222
221
|
process.env.RAZZLE_ES_PROXY_ADDR || getClientProxyAddress();
|
|
223
222
|
}
|
|
223
|
+
let fieldsMatchAll = [
|
|
224
|
+
'cca_climate_impacts.keyword',
|
|
225
|
+
'cca_adaptation_sectors.keyword',
|
|
226
|
+
'cca_adaptation_elements.keyword',
|
|
227
|
+
'cca_key_type_measure.keyword',
|
|
228
|
+
];
|
|
229
|
+
config.searchui.ccaSearch.facets.forEach((facet) => {
|
|
230
|
+
if (fieldsMatchAll.includes(facet.field)) {
|
|
231
|
+
facet.filterType = 'all';
|
|
232
|
+
}
|
|
233
|
+
});
|
|
224
234
|
|
|
225
|
-
// console.log(config);
|
|
226
235
|
return config;
|
|
227
236
|
}
|
package/src/search/common.js
CHANGED
|
@@ -251,6 +251,16 @@ iframe {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
#footer {
|
|
255
|
+
.theme-sites {
|
|
256
|
+
padding: 3rem 0 ;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.footer-header {
|
|
260
|
+
margin-bottom: 1.5em;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
254
264
|
#mega-menu {
|
|
255
265
|
.ui.list.has--4--columns {
|
|
256
266
|
a.item {
|
|
@@ -303,10 +313,6 @@ iframe {
|
|
|
303
313
|
padding-left: 0.8em;
|
|
304
314
|
min-width: 10em;
|
|
305
315
|
}
|
|
306
|
-
|
|
307
|
-
.noneu-langs-label {
|
|
308
|
-
// border-bottom: 1px solid black;
|
|
309
|
-
}
|
|
310
316
|
}
|
|
311
317
|
}
|
|
312
318
|
}
|