@eeacms/volto-eea-website-theme 3.2.0 → 3.3.0

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,7 +4,20 @@ 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
- ### [3.2.0](https://github.com/eea/volto-eea-website-theme/compare/3.1.0...3.2.0) - 11 November 2024
7
+ ### [3.3.0](https://github.com/eea/volto-eea-website-theme/compare/3.2.0...3.3.0) - 28 November 2024
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix(tests): add unit tests for ReportNavigation [ana-oprea - [`55ac4c2`](https://github.com/eea/volto-eea-website-theme/commit/55ac4c2a1edf0c8abdb83a2c7e3c7d578464708a)]
12
+
13
+ #### :house: Internal changes
14
+
15
+ - chore: package.json [alin - [`4a8a4cb`](https://github.com/eea/volto-eea-website-theme/commit/4a8a4cb014db839b90eceed935c97b85785ddf71)]
16
+
17
+ #### :hammer_and_wrench: Others
18
+
19
+ - Update package.json [Ichim David - [`53be025`](https://github.com/eea/volto-eea-website-theme/commit/53be025c116dfc71a2de708075e4e77262eeecf8)]
20
+ ### [3.2.0](https://github.com/eea/volto-eea-website-theme/compare/3.1.0...3.2.0) - 14 November 2024
8
21
 
9
22
  #### :hammer_and_wrench: Others
10
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
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",
@@ -24,8 +24,8 @@
24
24
  "url": "git@github.com:eea/volto-eea-website-theme.git"
25
25
  },
26
26
  "dependencies": {
27
- "@eeacms/volto-block-toc": "*",
28
27
  "@eeacms/volto-block-style": "*",
28
+ "@eeacms/volto-block-toc": "*",
29
29
  "@eeacms/volto-eea-design-system": "*",
30
30
  "@eeacms/volto-group-block": "*",
31
31
  "volto-subsites": "*"
@@ -0,0 +1,144 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { Link as RouterLink } from 'react-router-dom';
4
+ import cx from 'classnames';
5
+ import { compose } from 'redux';
6
+ import { withRouter } from 'react-router';
7
+ import { defineMessages, useIntl } from 'react-intl';
8
+
9
+ import { flattenToAppURL } from '@plone/volto/helpers';
10
+ import { UniversalLink, MaybeWrap } from '@plone/volto/components';
11
+ import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation';
12
+
13
+ const messages = defineMessages({
14
+ navigation: {
15
+ id: 'Navigation',
16
+ defaultMessage: 'Navigation',
17
+ },
18
+ });
19
+
20
+ /**
21
+ * Handles click on summary links and closes parent details elements
22
+ * @param {Event} e - Click event
23
+ * @param {boolean} wrapWithDetails - Whether the element is wrapped in details
24
+ */
25
+ function handleSummaryClick(e, wrapWithDetails) {
26
+ if (wrapWithDetails) {
27
+ e.preventDefault();
28
+
29
+ const currentDetails = e.target.closest('details');
30
+ // toggle the current details
31
+ if (currentDetails) {
32
+ currentDetails.open = !currentDetails.open;
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Renders a navigation node as a list item with proper styling and links
39
+ * @param {Object} node - Navigation node object containing title, href, type etc
40
+ * @param {number} parentLevel - Parent level in navigation hierarchy
41
+ * @returns {React.Component} UL component with navigation node content
42
+ */
43
+ function renderNode(node, parentLevel) {
44
+ const level = parentLevel + 1;
45
+ const hasChildItems = node.items?.length;
46
+ const nodeType = node.type;
47
+ const isDocument = nodeType === 'document';
48
+ let wrapWithDetails = isDocument && level > 2;
49
+ return (
50
+ <li
51
+ key={node['@id']}
52
+ active={node.is_current}
53
+ className={`list-item level-${level}`}
54
+ >
55
+ <MaybeWrap
56
+ condition={wrapWithDetails}
57
+ as="details"
58
+ className="context-navigation-detail"
59
+ >
60
+ {nodeType !== 'link' ? (
61
+ <MaybeWrap
62
+ condition={wrapWithDetails}
63
+ as="summary"
64
+ className="context-navigation-summary"
65
+ >
66
+ <RouterLink
67
+ to={flattenToAppURL(node.href)}
68
+ tabIndex={wrapWithDetails ? '-1' : 0}
69
+ title={node.description}
70
+ className={cx(`list-link contenttype-${nodeType}`, {
71
+ in_path: node.is_in_path,
72
+ })}
73
+ onClick={(e) =>
74
+ wrapWithDetails && handleSummaryClick(e, wrapWithDetails)
75
+ }
76
+ >
77
+ {node.title}
78
+ {nodeType === 'file' && node.getObjSize
79
+ ? ' [' + node.getObjSize + ']'
80
+ : ''}
81
+ </RouterLink>
82
+ </MaybeWrap>
83
+ ) : (
84
+ <UniversalLink href={flattenToAppURL(node.href)}>
85
+ {node.title}
86
+ </UniversalLink>
87
+ )}
88
+ {(hasChildItems && (
89
+ <ul className="list">
90
+ {node.items.map((node) => renderNode(node, level))}
91
+ </ul>
92
+ )) ||
93
+ ''}
94
+ </MaybeWrap>
95
+ </li>
96
+ );
97
+ }
98
+ /**
99
+ * A navigation slot implementation, similar to the classic Plone navigation
100
+ * portlet. It uses the same API, so the options are similar to
101
+ * INavigationPortlet
102
+ */
103
+ export function ReportNavigation(props) {
104
+ const { navigation = {} } = props;
105
+ const { items = [] } = navigation;
106
+ const intl = useIntl();
107
+
108
+ return items.length ? (
109
+ <nav className="context-navigation smart-toc">
110
+ {navigation.has_custom_name ? (
111
+ <div className="context-navigation-header">
112
+ <RouterLink to={flattenToAppURL(navigation.url || '')}>
113
+ {navigation.title}
114
+ </RouterLink>
115
+ </div>
116
+ ) : (
117
+ <div className="context-navigation-header">
118
+ {intl.formatMessage(messages.navigation)}
119
+ </div>
120
+ )}
121
+ <ul className="list">{items.map((node) => renderNode(node, 0))}</ul>
122
+ </nav>
123
+ ) : (
124
+ ''
125
+ );
126
+ }
127
+
128
+ ReportNavigation.propTypes = {
129
+ /**
130
+ * Navigation tree returned from @contextnavigation restapi endpoint
131
+ */
132
+ navigation: PropTypes.shape({
133
+ items: PropTypes.arrayOf(
134
+ PropTypes.shape({
135
+ title: PropTypes.string,
136
+ url: PropTypes.string,
137
+ }),
138
+ ),
139
+ has_custom_name: PropTypes.bool,
140
+ title: PropTypes.string,
141
+ }),
142
+ };
143
+
144
+ export default compose(withRouter, withContentNavigation)(ReportNavigation);
@@ -0,0 +1,131 @@
1
+ import { render, fireEvent } from '@testing-library/react';
2
+ import { Provider } from 'react-intl-redux';
3
+ import { Router } from 'react-router-dom';
4
+ import { createMemoryHistory } from 'history';
5
+ import ReportNavigation from './ReportNavigation';
6
+ import configureStore from 'redux-mock-store';
7
+ import '@testing-library/jest-dom/extend-expect';
8
+
9
+ const mockStore = configureStore();
10
+ const store = mockStore({
11
+ intl: {
12
+ locale: 'en',
13
+ messages: {},
14
+ },
15
+ });
16
+
17
+ jest.mock(
18
+ '@plone/volto/components/theme/Navigation/withContentNavigation',
19
+ () => ({
20
+ withContentNavigation: (Component) => (props) => (
21
+ <Component {...props} navigation={mockNavigation} />
22
+ ),
23
+ }),
24
+ );
25
+
26
+ // Mock navigation data
27
+ const mockNavigation = {
28
+ items: [
29
+ {
30
+ '@id': '/item1',
31
+ title: 'Item 1',
32
+ href: '/item1',
33
+ type: 'document',
34
+ description: 'Item 1 description',
35
+ is_current: false,
36
+ is_in_path: false,
37
+ items: [
38
+ {
39
+ '@id': '/item1/subitem1',
40
+ title: 'Subitem 1',
41
+ href: '/item1/subitem1',
42
+ type: 'document',
43
+ is_current: false,
44
+ is_in_path: false,
45
+ items: [],
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ '@id': '/item2',
51
+ title: 'Item 2',
52
+ href: '/item2',
53
+ type: 'document',
54
+ description: 'Item 2 description',
55
+ is_current: true,
56
+ is_in_path: true,
57
+ items: [],
58
+ },
59
+ ],
60
+ has_custom_name: true,
61
+ title: 'Custom Navigation',
62
+ url: '/custom-navigation',
63
+ };
64
+
65
+ describe('ReportNavigation', () => {
66
+ it('renders navigation items correctly', () => {
67
+ const history = createMemoryHistory();
68
+ const { getByText } = render(
69
+ <Provider store={store}>
70
+ <Router history={history}>
71
+ <ReportNavigation />
72
+ </Router>
73
+ </Provider>,
74
+ );
75
+
76
+ // Check if the navigation header is rendered
77
+ expect(getByText('Custom Navigation')).toBeInTheDocument();
78
+
79
+ // Check if the navigation items are rendered
80
+ expect(getByText('Item 1')).toBeInTheDocument();
81
+ expect(getByText('Item 2')).toBeInTheDocument();
82
+ expect(getByText('Subitem 1')).toBeInTheDocument();
83
+ });
84
+
85
+ it('toggles details on summary click', () => {
86
+ const history = createMemoryHistory();
87
+ const { container } = render(
88
+ <Provider store={store}>
89
+ <Router history={history}>
90
+ <ReportNavigation />
91
+ </Router>
92
+ </Provider>,
93
+ );
94
+
95
+ const detailsElement = container.querySelector('a[href="/item1"]');
96
+
97
+ // Simulate click on summary
98
+ fireEvent.click(detailsElement);
99
+ });
100
+
101
+ it('renders links with correct href attributes', () => {
102
+ const history = createMemoryHistory();
103
+ const { getByText } = render(
104
+ <Provider store={store}>
105
+ <Router history={history}>
106
+ <ReportNavigation />
107
+ </Router>
108
+ </Provider>,
109
+ );
110
+
111
+ expect(getByText('Item 1').closest('a')).toHaveAttribute('href', '/item1');
112
+ expect(getByText('Subitem 1').closest('a')).toHaveAttribute(
113
+ 'href',
114
+ '/item1/subitem1',
115
+ );
116
+ });
117
+
118
+ it('applies active class to the current navigation item', () => {
119
+ const history = createMemoryHistory();
120
+ const { getByText } = render(
121
+ <Provider store={store}>
122
+ <Router history={history}>
123
+ <ReportNavigation />
124
+ </Router>
125
+ </Provider>,
126
+ );
127
+
128
+ const activeItem = getByText('Item 2');
129
+ expect(activeItem).toHaveClass('in_path');
130
+ });
131
+ });
@@ -1,5 +1,6 @@
1
1
  import Accordion from './Accordion';
2
2
  import Default from './Default';
3
+ import ReportNavigation from './ReportNavigation';
3
4
 
4
5
  const contextBlockVariations = [
5
6
  {
@@ -13,6 +14,11 @@ const contextBlockVariations = [
13
14
  title: 'Accordion',
14
15
  view: Accordion,
15
16
  },
17
+ {
18
+ id: 'report_navigation',
19
+ title: 'Additional files',
20
+ view: ReportNavigation,
21
+ },
16
22
  ];
17
23
 
18
24
  export default contextBlockVariations;
package/src/index.js CHANGED
@@ -4,6 +4,8 @@ import { Icon } from '@plone/volto/components';
4
4
  import { default as TokenWidgetEdit } from '@plone/volto/components/manage/Widgets/TokenWidget';
5
5
  import SelectAutoCompleteWidget from '@plone/volto/components/manage/Widgets/SelectAutoComplete';
6
6
  import { serializeNodesToText } from '@plone/volto-slate/editor/render';
7
+ import TableBlockEdit from '@plone/volto-slate/blocks/Table/TableBlockEdit';
8
+ import TableBlockView from '@plone/volto-slate/blocks/Table/TableBlockView';
7
9
  import { nanoid } from '@plone/volto-slate/utils';
8
10
 
9
11
  import InpageNavigation from '@eeacms/volto-eea-design-system/ui/InpageNavigation/InpageNavigation';
@@ -230,6 +232,15 @@ const applyConfig = (config) => {
230
232
  config.blocks.blocksConfig.description.restricted = false;
231
233
  config.blocks.requiredBlocks = [];
232
234
 
235
+ // 281166 fix paste of tables in edit mode where paste action deemed the type
236
+ // of slate type to be table which in Volto 17 is mapped to the Table block which is draftjs based
237
+ // with this fix we load the edit and view of the slateTable avoiding any draftjs loading and error
238
+ config.blocks.blocksConfig.table = {
239
+ ...config.blocks.blocksConfig.table,
240
+ view: TableBlockView,
241
+ edit: TableBlockEdit,
242
+ };
243
+
233
244
  // Date format for EU
234
245
  config.settings.dateLocale = 'en-gb';
235
246
 
@@ -566,7 +577,7 @@ const applyConfig = (config) => {
566
577
  GET_CONTENT: ['breadcrumbs'], // 'navigation', 'actions', 'types'],
567
578
  });
568
579
 
569
- // Custom blocks: Title,Layout settings, Context navigation
580
+ // Custom blocks: Title, Layout settings, Context navigation
570
581
  return [
571
582
  installCustomTitle,
572
583
  installLayoutSettingsBlock,