diff --git a/CHANGELOG.md b/CHANGELOG.md
index b51f6613..b2dbcdd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,20 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
-### [3.2.0](https://github.com/eea/volto-eea-website-theme/compare/3.1.0...3.2.0) - 11 November 2024
+### [3.3.0](https://github.com/eea/volto-eea-website-theme/compare/3.2.0...3.3.0) - 28 November 2024
+
+#### :bug: Bug Fixes
+
+- fix(tests): add unit tests for ReportNavigation [ana-oprea - [`55ac4c2`](https://github.com/eea/volto-eea-website-theme/commit/55ac4c2a1edf0c8abdb83a2c7e3c7d578464708a)]
+
+#### :house: Internal changes
+
+- chore: package.json [alin - [`4a8a4cb`](https://github.com/eea/volto-eea-website-theme/commit/4a8a4cb014db839b90eceed935c97b85785ddf71)]
+
+#### :hammer_and_wrench: Others
+
+- Update package.json [Ichim David - [`53be025`](https://github.com/eea/volto-eea-website-theme/commit/53be025c116dfc71a2de708075e4e77262eeecf8)]
+### [3.2.0](https://github.com/eea/volto-eea-website-theme/compare/3.1.0...3.2.0) - 14 November 2024
#### :hammer_and_wrench: Others
diff --git a/package.json b/package.json
index 1d332f9e..feb59872 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@eeacms/volto-eea-website-theme",
- "version": "3.2.0",
+ "version": "3.3.0",
"description": "@eeacms/volto-eea-website-theme: Volto add-on",
"main": "src/index.js",
"author": "European Environment Agency: IDM2 A-Team",
@@ -24,8 +24,8 @@
"url": "git@github.com:eea/volto-eea-website-theme.git"
},
"dependencies": {
- "@eeacms/volto-block-toc": "*",
"@eeacms/volto-block-style": "*",
+ "@eeacms/volto-block-toc": "*",
"@eeacms/volto-eea-design-system": "*",
"@eeacms/volto-group-block": "*",
"volto-subsites": "*"
diff --git a/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.jsx b/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.jsx
new file mode 100644
index 00000000..bbb8bab1
--- /dev/null
+++ b/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.jsx
@@ -0,0 +1,144 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Link as RouterLink } from 'react-router-dom';
+import cx from 'classnames';
+import { compose } from 'redux';
+import { withRouter } from 'react-router';
+import { defineMessages, useIntl } from 'react-intl';
+
+import { flattenToAppURL } from '@plone/volto/helpers';
+import { UniversalLink, MaybeWrap } from '@plone/volto/components';
+import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation';
+
+const messages = defineMessages({
+ navigation: {
+ id: 'Navigation',
+ defaultMessage: 'Navigation',
+ },
+});
+
+/**
+ * Handles click on summary links and closes parent details elements
+ * @param {Event} e - Click event
+ * @param {boolean} wrapWithDetails - Whether the element is wrapped in details
+ */
+function handleSummaryClick(e, wrapWithDetails) {
+ if (wrapWithDetails) {
+ e.preventDefault();
+
+ const currentDetails = e.target.closest('details');
+ // toggle the current details
+ if (currentDetails) {
+ currentDetails.open = !currentDetails.open;
+ }
+ }
+}
+
+/**
+ * Renders a navigation node as a list item with proper styling and links
+ * @param {Object} node - Navigation node object containing title, href, type etc
+ * @param {number} parentLevel - Parent level in navigation hierarchy
+ * @returns {React.Component} UL component with navigation node content
+ */
+function renderNode(node, parentLevel) {
+ const level = parentLevel + 1;
+ const hasChildItems = node.items?.length;
+ const nodeType = node.type;
+ const isDocument = nodeType === 'document';
+ let wrapWithDetails = isDocument && level > 2;
+ return (
+
+
+ {nodeType !== 'link' ? (
+
+
+ wrapWithDetails && handleSummaryClick(e, wrapWithDetails)
+ }
+ >
+ {node.title}
+ {nodeType === 'file' && node.getObjSize
+ ? ' [' + node.getObjSize + ']'
+ : ''}
+
+
+ ) : (
+
+ {node.title}
+
+ )}
+ {(hasChildItems && (
+
+ {node.items.map((node) => renderNode(node, level))}
+
+ )) ||
+ ''}
+
+
+ );
+}
+/**
+ * A navigation slot implementation, similar to the classic Plone navigation
+ * portlet. It uses the same API, so the options are similar to
+ * INavigationPortlet
+ */
+export function ReportNavigation(props) {
+ const { navigation = {} } = props;
+ const { items = [] } = navigation;
+ const intl = useIntl();
+
+ return items.length ? (
+
+ {navigation.has_custom_name ? (
+
+
+ {navigation.title}
+
+
+ ) : (
+
+ {intl.formatMessage(messages.navigation)}
+
+ )}
+ {items.map((node) => renderNode(node, 0))}
+
+ ) : (
+ ''
+ );
+}
+
+ReportNavigation.propTypes = {
+ /**
+ * Navigation tree returned from @contextnavigation restapi endpoint
+ */
+ navigation: PropTypes.shape({
+ items: PropTypes.arrayOf(
+ PropTypes.shape({
+ title: PropTypes.string,
+ url: PropTypes.string,
+ }),
+ ),
+ has_custom_name: PropTypes.bool,
+ title: PropTypes.string,
+ }),
+};
+
+export default compose(withRouter, withContentNavigation)(ReportNavigation);
diff --git a/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.test.jsx b/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.test.jsx
new file mode 100644
index 00000000..e7279535
--- /dev/null
+++ b/src/components/manage/Blocks/ContextNavigation/variations/ReportNavigation.test.jsx
@@ -0,0 +1,131 @@
+import { render, fireEvent } from '@testing-library/react';
+import { Provider } from 'react-intl-redux';
+import { Router } from 'react-router-dom';
+import { createMemoryHistory } from 'history';
+import ReportNavigation from './ReportNavigation';
+import configureStore from 'redux-mock-store';
+import '@testing-library/jest-dom/extend-expect';
+
+const mockStore = configureStore();
+const store = mockStore({
+ intl: {
+ locale: 'en',
+ messages: {},
+ },
+});
+
+jest.mock(
+ '@plone/volto/components/theme/Navigation/withContentNavigation',
+ () => ({
+ withContentNavigation: (Component) => (props) => (
+
+ ),
+ }),
+);
+
+// Mock navigation data
+const mockNavigation = {
+ items: [
+ {
+ '@id': '/item1',
+ title: 'Item 1',
+ href: '/item1',
+ type: 'document',
+ description: 'Item 1 description',
+ is_current: false,
+ is_in_path: false,
+ items: [
+ {
+ '@id': '/item1/subitem1',
+ title: 'Subitem 1',
+ href: '/item1/subitem1',
+ type: 'document',
+ is_current: false,
+ is_in_path: false,
+ items: [],
+ },
+ ],
+ },
+ {
+ '@id': '/item2',
+ title: 'Item 2',
+ href: '/item2',
+ type: 'document',
+ description: 'Item 2 description',
+ is_current: true,
+ is_in_path: true,
+ items: [],
+ },
+ ],
+ has_custom_name: true,
+ title: 'Custom Navigation',
+ url: '/custom-navigation',
+};
+
+describe('ReportNavigation', () => {
+ it('renders navigation items correctly', () => {
+ const history = createMemoryHistory();
+ const { getByText } = render(
+
+
+
+
+ ,
+ );
+
+ // Check if the navigation header is rendered
+ expect(getByText('Custom Navigation')).toBeInTheDocument();
+
+ // Check if the navigation items are rendered
+ expect(getByText('Item 1')).toBeInTheDocument();
+ expect(getByText('Item 2')).toBeInTheDocument();
+ expect(getByText('Subitem 1')).toBeInTheDocument();
+ });
+
+ it('toggles details on summary click', () => {
+ const history = createMemoryHistory();
+ const { container } = render(
+
+
+
+
+ ,
+ );
+
+ const detailsElement = container.querySelector('a[href="/item1"]');
+
+ // Simulate click on summary
+ fireEvent.click(detailsElement);
+ });
+
+ it('renders links with correct href attributes', () => {
+ const history = createMemoryHistory();
+ const { getByText } = render(
+
+
+
+
+ ,
+ );
+
+ expect(getByText('Item 1').closest('a')).toHaveAttribute('href', '/item1');
+ expect(getByText('Subitem 1').closest('a')).toHaveAttribute(
+ 'href',
+ '/item1/subitem1',
+ );
+ });
+
+ it('applies active class to the current navigation item', () => {
+ const history = createMemoryHistory();
+ const { getByText } = render(
+
+
+
+
+ ,
+ );
+
+ const activeItem = getByText('Item 2');
+ expect(activeItem).toHaveClass('in_path');
+ });
+});
diff --git a/src/components/manage/Blocks/ContextNavigation/variations/index.js b/src/components/manage/Blocks/ContextNavigation/variations/index.js
index f85f69f6..54af63a6 100644
--- a/src/components/manage/Blocks/ContextNavigation/variations/index.js
+++ b/src/components/manage/Blocks/ContextNavigation/variations/index.js
@@ -1,5 +1,6 @@
import Accordion from './Accordion';
import Default from './Default';
+import ReportNavigation from './ReportNavigation';
const contextBlockVariations = [
{
@@ -13,6 +14,11 @@ const contextBlockVariations = [
title: 'Accordion',
view: Accordion,
},
+ {
+ id: 'report_navigation',
+ title: 'Additional files',
+ view: ReportNavigation,
+ },
];
export default contextBlockVariations;
diff --git a/src/customizations/volto/components/theme/Breadcrumbs/__snapshots__/Breadcrumbs.test.jsx.snap b/src/customizations/volto/components/theme/Breadcrumbs/__snapshots__/Breadcrumbs.test.jsx.snap
index 41558d08..a3db8e11 100644
--- a/src/customizations/volto/components/theme/Breadcrumbs/__snapshots__/Breadcrumbs.test.jsx.snap
+++ b/src/customizations/volto/components/theme/Breadcrumbs/__snapshots__/Breadcrumbs.test.jsx.snap
@@ -44,7 +44,7 @@ Array [
Blog
@@ -118,7 +118,7 @@ Array [
Home
@@ -134,13 +134,12 @@ Array [
onClick={[Function]}
/>
-
News
-
+
{
config.blocks.blocksConfig.description.restricted = false;
config.blocks.requiredBlocks = [];
+ // 281166 fix paste of tables in edit mode where paste action deemed the type
+ // of slate type to be table which in Volto 17 is mapped to the Table block which is draftjs based
+ // with this fix we load the edit and view of the slateTable avoiding any draftjs loading and error
+ config.blocks.blocksConfig.table = {
+ ...config.blocks.blocksConfig.table,
+ view: TableBlockView,
+ edit: TableBlockEdit,
+ };
+
// Date format for EU
config.settings.dateLocale = 'en-gb';
@@ -566,7 +577,7 @@ const applyConfig = (config) => {
GET_CONTENT: ['breadcrumbs'], // 'navigation', 'actions', 'types'],
});
- // Custom blocks: Title,Layout settings, Context navigation
+ // Custom blocks: Title, Layout settings, Context navigation
return [
installCustomTitle,
installLayoutSettingsBlock,