diff --git a/CHANGELOG.md b/CHANGELOG.md index 8679eb4c..b51f6613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,15 @@ 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.1.0](https://github.com/eea/volto-eea-website-theme/compare/3.0.0...3.1.0) - 5 November 2024 +### [3.2.0](https://github.com/eea/volto-eea-website-theme/compare/3.1.0...3.2.0) - 11 November 2024 + +#### :hammer_and_wrench: Others + +- Update package.json [Ichim David - [`4b5073b`](https://github.com/eea/volto-eea-website-theme/commit/4b5073b60a7d144597363f89d5cba6357baa9e19)] +### [3.1.0](https://github.com/eea/volto-eea-website-theme/compare/3.0.0...3.1.0) - 6 November 2024 #### :hammer_and_wrench: Others -- fix Grid conversion, ref #278618 [Miu Razvan - [`f80b786`](https://github.com/eea/volto-eea-website-theme/commit/f80b7869b4ac060bf35250cf5045ab930a0003de)] ## [3.0.0](https://github.com/eea/volto-eea-website-theme/compare/2.4.0...3.0.0) - 21 October 2024 #### :nail_care: Enhancements @@ -115,7 +119,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). #### :hammer_and_wrench: Others - modified param of dateIsInFuture to signal it's a string and not a date [David Ichim - [`e3243a0`](https://github.com/eea/volto-eea-website-theme/commit/e3243a075969a379462ba0b7889fe0d8b52af62a)] -## [2.0.0](https://github.com/eea/volto-eea-website-theme/compare/1.34.0...2.0.0) - 13 May 2024 +## [2.0.0](https://github.com/eea/volto-eea-website-theme/compare/1.35.0...2.0.0) - 13 May 2024 #### :rocket: New Features @@ -140,6 +144,18 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). #### :hammer_and_wrench: Others - Bump package version to 2.0.0 to signal major release due to Volto 17 jump [David Ichim - [`ffe3049`](https://github.com/eea/volto-eea-website-theme/commit/ffe3049b3b656093a44f05044dbe7cd63bac495f)] +### [1.35.0](https://github.com/eea/volto-eea-website-theme/compare/1.34.0...1.35.0) - 6 November 2024 + +#### :bug: Bug Fixes + +- fix(slate) - fix console error on list element - ref #278427 [kreafox - [`2ea7dd1`](https://github.com/eea/volto-eea-website-theme/commit/2ea7dd1488782db09ca3b8636a932930326f1175)] +- fix: cypress 13.1.0 [kreafox - [`0636604`](https://github.com/eea/volto-eea-website-theme/commit/0636604cf023730927c0006dcb146d5f9e086f1c)] + +#### :hammer_and_wrench: Others + +- Revert "Release 1.35.0" [alin - [`dcc318a`](https://github.com/eea/volto-eea-website-theme/commit/dcc318a5082e3c7fff44d00bfd9bb140c4efc21a)] +- Revert "1.35.1" [alin - [`6012bc6`](https://github.com/eea/volto-eea-website-theme/commit/6012bc68cf7a692fc964610a6071c82d4211fb2e)] +- Release 1.35.0 [kreafox - [`e21bee6`](https://github.com/eea/volto-eea-website-theme/commit/e21bee61e55e6c6b46d0119059f7a6063b0edad8)] ### [1.34.0](https://github.com/eea/volto-eea-website-theme/compare/1.33.2...1.34.0) - 9 May 2024 #### :bug: Bug Fixes diff --git a/cypress/e2e/01-block-basics.cy.js b/cypress/e2e/01-block-basics.cy.js index 4a9ce0a7..a7989ea9 100644 --- a/cypress/e2e/01-block-basics.cy.js +++ b/cypress/e2e/01-block-basics.cy.js @@ -105,6 +105,16 @@ describe('Blocks Tests', () => { // then the page view should contain our changes cy.get('.accordion-header').contains('Nav title'); + + // resize window to make tablet or mobile condition work + cy.viewport(375, 667); cy.get('.accordion-header').click(); + + // trigger outside click + cy.get('body').click(); + + // use enter to open accordion + cy.get('.accordion-header').trigger('keydown', { keyCode: 13, which: 13 }); + cy.get('.accordion-header').contains('Nav title'); }); }); diff --git a/package.json b/package.json index ab583afe..1d332f9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eeacms/volto-eea-website-theme", - "version": "3.1.0", + "version": "3.2.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-style": "*", "@eeacms/volto-block-toc": "*", + "@eeacms/volto-block-style": "*", "@eeacms/volto-eea-design-system": "*", "@eeacms/volto-group-block": "*", "volto-subsites": "*" diff --git a/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx b/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx index 82eaeac0..8496d8c1 100644 --- a/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +++ b/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx @@ -4,18 +4,15 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { withRouter } from 'react-router'; import { compose } from 'redux'; -import { Accordion } from 'semantic-ui-react'; +import { Accordion, Icon } from 'semantic-ui-react'; import Slugger from 'github-slugger'; -import { Icon, UniversalLink } from '@plone/volto/components'; +import { UniversalLink, MaybeWrap } from '@plone/volto/components'; import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation'; import withEEASideMenu from '@eeacms/volto-block-toc/hocs/withEEASideMenu'; import { flattenToAppURL } from '@plone/volto/helpers'; -import downIcon from '@plone/volto/icons/down-key.svg'; -import upIcon from '@plone/volto/icons/up-key.svg'; - const messages = defineMessages({ navigation: { id: 'Navigation', @@ -33,6 +30,8 @@ const AccordionNavigation = ({ const navOpen = ['mobile', 'tablet'].includes(device) ? false : true; const [isNavOpen, setIsNavOpen] = React.useState(navOpen); const [activeItems, setActiveItems] = React.useState({}); + const contextNavigationListRef = React.useRef(null); + const summaryRef = React.useRef(null); const onClickSummary = React.useCallback((e) => { e.preventDefault(); @@ -43,6 +42,26 @@ const AccordionNavigation = ({ if (isMenuOpenOnOutsideClick === false) setIsNavOpen(false); }, [isMenuOpenOnOutsideClick]); + React.useEffect(() => { + if (!navOpen) { + const handleOutsideClick = (event) => { + if ( + summaryRef.current && + contextNavigationListRef.current && + !summaryRef.current.contains(event.target) && + !contextNavigationListRef.current.contains(event.target) + ) { + setIsNavOpen(false); + } + }; + + document.addEventListener('click', handleOutsideClick); + return () => { + document.removeEventListener('click', handleOutsideClick); + }; + } + }, [summaryRef, navOpen]); + const onKeyDownSummary = React.useCallback( (e) => { if (e.keyCode === 13 || e.keyCode === 32) { @@ -95,7 +114,11 @@ const AccordionNavigation = ({ id={`accordion-title-${normalizedTitle}`} > {title} - + - + {/* eslint-disable-next-line */} - {has_custom_name ? title : intl.formatMessage(messages.navigation)} - + + {has_custom_name + ? title + : intl.formatMessage(messages.navigation)} + + - - {items.map((item) => renderItems({ item }))} - + + + {items.map((item) => renderItems({ item }))} + + > @@ -174,6 +217,9 @@ export default compose( (WrappedComponent) => (props) => withEEASideMenu(WrappedComponent)({ ...props, + targetParent: '.eea.header ', + fixedVisibilitySwitchTarget: '.main.bar', + insertBeforeOnMobile: '.banner', shouldRender: props.navigation?.items?.length > 0, }), )(AccordionNavigation); diff --git a/src/components/manage/Blocks/Title/index.js b/src/components/manage/Blocks/Title/index.js index 5af73d27..9a4aa506 100644 --- a/src/components/manage/Blocks/Title/index.js +++ b/src/components/manage/Blocks/Title/index.js @@ -3,7 +3,6 @@ import View from './View'; import DefaultTemplate from './variations/Default'; import WebReport from './variations/WebReport'; import WebReportPage from './variations/WebReportPage'; -import './variations/styles.less'; const applyConfig = (config) => { config.blocks.blocksConfig.title = { diff --git a/src/components/manage/Blocks/Title/schema.js b/src/components/manage/Blocks/Title/schema.js index ff1a9289..6e21503a 100644 --- a/src/components/manage/Blocks/Title/schema.js +++ b/src/components/manage/Blocks/Title/schema.js @@ -2,12 +2,20 @@ import alignTopSVG from '@plone/volto/icons/move-up.svg'; import alignCenterSVG from '@plone/volto/icons/row.svg'; import alignBottomSVG from '@plone/volto/icons/move-down.svg'; +import alignTextLeftSVG from '@plone/volto/icons/align-left.svg'; +import alignTextCenterSVG from '@plone/volto/icons/align-center.svg'; + const ALIGN_INFO_MAP_IMAGE_POSITION = { 'has--bg--top': [alignTopSVG, 'Top'], 'has--bg--center': [alignCenterSVG, 'Center'], 'has--bg--bottom': [alignBottomSVG, 'Bottom'], }; +const ALIGN_INFO_MAP_TEXT_ALIGN = { + 'has--text--left': [alignTextLeftSVG, 'Left'], + 'has--text--center': [alignTextCenterSVG, 'Center'], +}; + const infoSchema = { title: 'Info', fieldsets: [ @@ -145,8 +153,8 @@ const titleSchema = { copyrightPosition: { title: 'Align', widget: 'style_align', - actions: ['left', 'right'], - defaultValue: 'left', + actions: ['left', 'center', 'right'], + defaultValue: '', }, styles: { widget: 'object', @@ -156,7 +164,7 @@ const titleSchema = { { id: 'default', title: 'Default', - fields: ['bg'], + fields: ['bg', 'textAlign'], }, ], properties: { @@ -167,6 +175,13 @@ const titleSchema = { actionsInfoMap: ALIGN_INFO_MAP_IMAGE_POSITION, defaultValue: 'has--bg--center', }, + textAlign: { + title: 'Text align', + widget: 'style_align', + actions: Object.keys(ALIGN_INFO_MAP_TEXT_ALIGN), + actionsInfoMap: ALIGN_INFO_MAP_TEXT_ALIGN, + defaultValue: '', + }, }, required: [], }, diff --git a/src/components/manage/Blocks/Title/variations/WebReport.jsx b/src/components/manage/Blocks/Title/variations/WebReport.jsx index fc408f22..9e21973d 100644 --- a/src/components/manage/Blocks/Title/variations/WebReport.jsx +++ b/src/components/manage/Blocks/Title/variations/WebReport.jsx @@ -38,11 +38,12 @@ const WebReport = (props) => { {...props} data={{ ...props.data, - hideContentType: true, - aboveTitle: ( + aboveTitle: !props.data.hideContentType ? ( {props.data.content_type || props.properties.type_title} + ) : ( + ' ' ), belowTitle: ( <> diff --git a/src/components/manage/Blocks/Title/variations/WebReportPage.jsx b/src/components/manage/Blocks/Title/variations/WebReportPage.jsx index da539e78..5376360c 100644 --- a/src/components/manage/Blocks/Title/variations/WebReportPage.jsx +++ b/src/components/manage/Blocks/Title/variations/WebReportPage.jsx @@ -32,12 +32,13 @@ const WebReportPage = (props) => { {...props} data={{ ...props.data, - hideContentType: true, aboveTitle: ( <> - - {props.data.content_type || props.properties.type_title} - + {!props.data.hideContentType && ( + + {props.data.content_type || props.properties.type_title} + + )} {props.data.subtitle} > ), diff --git a/src/components/manage/Blocks/Title/variations/styles.less b/src/components/manage/Blocks/Title/variations/styles.less deleted file mode 100644 index 7fd9ad1f..00000000 --- a/src/components/manage/Blocks/Title/variations/styles.less +++ /dev/null @@ -1,28 +0,0 @@ -.view-viewview.light-header .main.bar { - position: relative; - z-index: 1; - width: 100%; - margin-bottom: -160px; -} -//Gradient styles for web report -.light-header .gradient { - background: linear-gradient( - 0deg, - #ffffff, - rgba(255, 255, 255, 0.9) 30%, - rgba(46, 82, 114, 0.7) 70%, - rgba(14, 21, 26, 0.8) 100% - ) !important; -} - -.ui.block.title .eea.banner .content { - padding-right: 1rem; - padding-left: 1rem; -} - -.share-popup { - .actions { - display: flex; - flex-flow: row; - } -} diff --git a/src/components/theme/Banner/View.jsx b/src/components/theme/Banner/View.jsx index 78423afe..82efd273 100644 --- a/src/components/theme/Banner/View.jsx +++ b/src/components/theme/Banner/View.jsx @@ -300,7 +300,9 @@ const View = (props) => { { const Breadcrumbs = (props) => { const dispatch = useDispatch(); const { items = [], root = '/' } = useSelector((state) => state?.breadcrumbs); - const content = useSelector((state) => state?.content?.data); + const token = useSelector((state) => state?.userSession?.token); // const pathname = useSelector((state) => state.location.pathname); const location = useLocation(); const { pathname } = location; - - const linkLevels = useMemo(() => { - if (content) { - const type = content['@type']; - const isContentTypesToAvoid = - config.settings.contentTypeToAvoidAsLinks || {}; - if (isContentTypesToAvoid[type]) { - return isContentTypesToAvoid[type]; - } - } - }, [content]); + const contentTypesAsBreadcrumbSection = !token + ? config.settings.contentTypesAsBreadcrumbSection + : []; const sections = items.map((item) => ({ - title: item.title, - href: item.url, + ...item, key: item.title, })); @@ -71,7 +62,7 @@ const Breadcrumbs = (props) => { pathname={pathname} sections={sections} root={root} - linkLevels={linkLevels} + contentTypesAsBreadcrumbSection={contentTypesAsBreadcrumbSection} /> ); diff --git a/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.test.jsx b/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.test.jsx new file mode 100644 index 00000000..1cb80ba5 --- /dev/null +++ b/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.test.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; +import { MemoryRouter } from 'react-router-dom'; +import config from '@plone/volto/registry'; + +import Breadcrumbs from './Breadcrumbs'; + +const mockStore = configureStore(); + +describe('Breadcrumbs', () => { + it('renders a breadcrumbs component', () => { + const store = mockStore({ + breadcrumbs: { + items: [ + { title: 'Blog', url: '/blog' }, + { title: 'My first blog', url: '/blog/my-first-blog' }, + ], + }, + intl: { + locale: 'en', + messages: {}, + }, + }); + const component = renderer.create( + + + + + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); + + it('renders breadcrumbs with contentTypesAsBreadcrumbSection', () => { + // Mock the config settings + config.settings = { + ...config.settings, + contentTypesAsBreadcrumbSection: ['Folder', 'News Item'], + }; + + const store = mockStore({ + breadcrumbs: { + items: [ + { title: 'Home', url: '/' }, + { title: 'News', url: '/news', portal_type: 'Folder' }, + { + title: 'Latest Update', + url: '/news/latest-update', + portal_type: 'News Item', + }, + ], + }, + intl: { + locale: 'en', + messages: {}, + }, + userSession: { + token: null, + }, + }); + + const component = renderer.create( + + + + + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); +}); 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 new file mode 100644 index 00000000..41558d08 --- /dev/null +++ b/src/customizations/volto/components/theme/Breadcrumbs/__snapshots__/Breadcrumbs.test.jsx.snap @@ -0,0 +1,167 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Breadcrumbs renders a breadcrumbs component 1`] = ` +Array [ + , + + + + + + + + + + + + + + + Blog + + + + + + + + My first blog + + + + + + , +] +`; + +exports[`Breadcrumbs renders breadcrumbs with contentTypesAsBreadcrumbSection 1`] = ` +Array [ + , + + + + + + + + + + + + + + + Home + + + + + + + + News + + + + + + + + Latest Update + + + + + + , +] +`; diff --git a/src/customizations/volto/components/theme/View/DefaultView.jsx b/src/customizations/volto/components/theme/View/DefaultView.jsx index be84e1b3..235e1888 100644 --- a/src/customizations/volto/components/theme/View/DefaultView.jsx +++ b/src/customizations/volto/components/theme/View/DefaultView.jsx @@ -94,8 +94,13 @@ const DefaultView = (props) => { const Container = config.getComponent({ name: 'Container' }).component || SemanticContainer; - const matchingNavigationPath = navigation_paths.find((navPath) => - path.includes(navPath.url), + + // choose last matching navigation path in case we get more specific paths + const matchingNavigationPath = navigation_paths.reduceRight( + (acc, navPath) => { + return acc || (path.includes(navPath.url) ? navPath : null); + }, + null, ); // If the content is not yet loaded, then do not show anything @@ -113,7 +118,7 @@ const DefaultView = (props) => { no_icons: matchingNavigationPath.no_icons || true, root_path: matchingNavigationPath.url, includeTop: matchingNavigationPath.includeTop || true, - bottomLevel: matchingNavigationPath.bottomLevel || 3, + bottomLevel: matchingNavigationPath.bottomLevel || 4, topLevel: matchingNavigationPath.topLevel || 1, currentFolderOnly: matchingNavigationPath.currentFolderOnly || false, diff --git a/src/customizations/volto/reducers/breadcrumbs/breadcrumbs.js b/src/customizations/volto/reducers/breadcrumbs/breadcrumbs.js new file mode 100644 index 00000000..02e0e6e0 --- /dev/null +++ b/src/customizations/volto/reducers/breadcrumbs/breadcrumbs.js @@ -0,0 +1,98 @@ +/** + * Breadcrumbs reducer. + * @module reducers/breadcrumbs/breadcrumbs + * Customized reducer as part of ticket 271001 in order to receive portal_type info + */ + +import { map } from 'lodash'; +import { + flattenToAppURL, + getBaseUrl, + hasApiExpander, +} from '@plone/volto/helpers'; + +import { + GET_BREADCRUMBS, + GET_CONTENT, +} from '@plone/volto/constants/ActionTypes'; + +const initialState = { + error: null, + items: [], + root: null, + loaded: false, + loading: false, +}; + +/** + * Breadcrumbs reducer. + * @function breadcrumbs + * @param {Object} state Current state. + * @param {Object} action Action to be handled. + * @returns {Object} New state. + */ +export default function breadcrumbs(state = initialState, action = {}) { + let hasExpander; + switch (action.type) { + case `${GET_BREADCRUMBS}_PENDING`: + return { + ...state, + error: null, + loaded: false, + loading: true, + }; + case `${GET_CONTENT}_SUCCESS`: + hasExpander = hasApiExpander( + 'breadcrumbs', + getBaseUrl(flattenToAppURL(action.result['@id'])), + ); + if (hasExpander && !action.subrequest) { + return { + ...state, + error: null, + items: map( + action.result['@components'].breadcrumbs.items, + (item) => ({ + ...item, + portal_type: item.portal_type, + url: flattenToAppURL(item['@id']), + }), + ), + root: flattenToAppURL(action.result['@components'].breadcrumbs.root), + loaded: true, + loading: false, + }; + } + return state; + case `${GET_BREADCRUMBS}_SUCCESS`: + hasExpander = hasApiExpander( + 'breadcrumbs', + getBaseUrl(flattenToAppURL(action.result['@id'])), + ); + if (!hasExpander) { + return { + ...state, + error: null, + items: map(action.result.items, (item) => ({ + ...item, + portal_type: item.portal_type, + url: flattenToAppURL(item['@id']), + })), + root: flattenToAppURL(action.result.root), + loaded: true, + loading: false, + }; + } + return state; + case `${GET_BREADCRUMBS}_FAIL`: + return { + ...state, + error: action.error, + items: [], + loaded: false, + loading: false, + }; + default: + return state; + } +} diff --git a/src/index.js b/src/index.js index 76de3028..7b94a7f7 100644 --- a/src/index.js +++ b/src/index.js @@ -495,11 +495,9 @@ const applyConfig = (config) => { // }, }; - //If you don't want to show the content type as a link in the breadcrumbs, you can set it to a number - // where 1 is the last item in the breadcrumbs, 2 is the second last, etc. - config.settings.contentTypeToAvoidAsLinks = { - web_report_section: 2, - }; + // If you don't want to show the content type as a link in the breadcrumbs, you can set it + // contentTypesAsBreadcrumbSection + config.settings.contentTypesAsBreadcrumbSection = ['web_report_section']; // Group if (config.blocks.blocksConfig.group) {