Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix Link content type URL(external) in navigation being appended to the existing URL instead of replacing it. @iFlameing
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ vi.mock('@plone/volto/actions/navigation/navigation', () => ({
getNavigation: vi.fn(() => ({ type: 'GET_NAVIGATION' })),
}));

vi.mock('@plone/volto/helpers/Url/Url', () => ({
getBaseUrl: vi.fn((path: string) => path),
}));
vi.mock('@plone/volto/helpers/Url/Url', async (importOriginal) => {
const actual =
await importOriginal<typeof import('@plone/volto/helpers/Url/Url')>();
return {
...actual,
getBaseUrl: vi.fn((path: string) => path),
};
});

vi.mock('@plone/volto/helpers/Utils/Utils', () => ({
hasApiExpander: vi.fn(() => false),
Expand Down Expand Up @@ -165,6 +170,65 @@ describe('Navigation', () => {
expect(container.querySelector('.submenu.active')).toBeNull();
});

describe('isActive', () => {
it('marks subitem active on exact match', () => {
const { container } = renderNavigation({
hasFatMenuHeader: true,
items: [
{
title: 'About',
url: '/about',
items: [{ title: 'Team', url: '/about/team', items: [] }],
},
],
pathname: '/about/team',
});
expect(
container.querySelector('.subitem-wrapper a.active'),
).not.toBeNull();
});

it('does not mark /news subitem active when on /news-archive', () => {
const { container } = renderNavigation({
hasFatMenuHeader: true,
items: [
{
title: 'About',
url: '/about',
items: [{ title: 'News', url: '/news', items: [] }],
},
],
pathname: '/news-archive',
});
expect(container.querySelector('.subitem-wrapper a.active')).toBeNull();
});

it('marks parent item active when pathname is a child path', () => {
const { container } = renderNavigation({
hasFatMenuHeader: true,
items: [{ title: 'About', url: '/about', items: [] }],
pathname: '/about/team',
});
expect(container.querySelector('.submenu-header.active')).not.toBeNull();
});

it('marks only the matching top-level item active, not others', () => {
const { container } = renderNavigation({
hasFatMenuHeader: true,
items: [
{ title: 'About', url: '/about', items: [] },
{ title: 'News', url: '/news', items: [] },
],
pathname: '/news',
});
const activeHeaders = container.querySelectorAll(
'.submenu-header.active',
);
expect(activeHeaders).toHaveLength(1);
expect(activeHeaders[0]).toHaveTextContent('News');
});
});

it('fetches navigation data when no expander is present', () => {
mockedGetBaseUrl.mockReturnValue('/base');
const { store } = renderNavigation({ pathname: '/blog' });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import isEmpty from 'lodash/isEmpty';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { NavLink } from 'react-router-dom';
import doesNodeContainClick from '../../helpers/doesNodeContainClick';
import { useIntl, defineMessages } from 'react-intl';
import cx from 'classnames';
Expand All @@ -13,6 +12,7 @@ import { getNavigation } from '@plone/volto/actions/navigation/navigation';
import Icon from '@plone/volto/components/theme/Icon/Icon';
import clearSVG from '@plone/volto/icons/clear.svg';
import NavItem from '@plone/volto/components/theme/Navigation/NavItem';
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';

const messages = defineMessages({
closeMenu: {
Expand Down Expand Up @@ -129,10 +129,17 @@ const Navigation = ({ pathname }: NavigationProps) => {
}
}, [pathname, token, dispatch]);

const isActive = (url: string) => {
const isExact = (url: string) => {
return (url === '' && pathname === '/') || (url !== '' && pathname === url);
};

const isActive = (url: string) => {
return (
(url === '' && pathname === '/') ||
(url !== '' && (pathname === url || pathname.startsWith(url + '/')))
);
};

const openMenu = (index: number) => {
if (index === currentOpenIndex) {
setDesktopMenuOpen(null);
Expand Down Expand Up @@ -190,13 +197,15 @@ const Navigation = ({ pathname }: NavigationProps) => {
})}
>
<div className="submenu-inner">
<NavLink
to={item.url === '' ? '/' : item.url}
<UniversalLink
href={item.url === '' ? '/' : item.url}
onClick={() => closeMenu()}
className="submenu-header"
className={cx('submenu-header', {
active: isActive(item.url),
})}
>
<h2>{item.nav_title ?? item.title}</h2>
</NavLink>
</UniversalLink>
<button
className="close"
onClick={closeMenu}
Expand All @@ -209,18 +218,22 @@ const Navigation = ({ pathname }: NavigationProps) => {
item.items.length > 0 &&
item.items.map((subitem) => (
<li className="subitem-wrapper" key={subitem.url}>
<NavLink
to={subitem.url}
<UniversalLink
href={subitem.url}
onClick={() => closeMenu()}
className={cx({
current: isActive(subitem.url),
current: isExact(subitem.url),
active: isActive(subitem.url),
})}
{...(isExact(subitem.url)
? { 'aria-current': 'page' }
: {})}
>
<span className="left-arrow">&#8212;</span>
<span>
{subitem.nav_title || subitem.title}
</span>
</NavLink>
</UniversalLink>
<div className="sub-submenu">
<ul>
{subitem.items &&
Expand All @@ -230,12 +243,16 @@ const Navigation = ({ pathname }: NavigationProps) => {
className="subsubitem-wrapper"
key={subsubitem.url}
>
<NavLink
to={subsubitem.url}
<UniversalLink
href={subsubitem.url}
onClick={() => closeMenu()}
className={cx({
current: isActive(subsubitem.url),
current: isExact(subsubitem.url),
active: isActive(subsubitem.url),
})}
{...(isExact(subsubitem.url)
? { 'aria-current': 'page' }
: {})}
>
<span className="left-arrow">
&#8212;
Expand All @@ -244,7 +261,7 @@ const Navigation = ({ pathname }: NavigationProps) => {
{subsubitem.nav_title ||
subsubitem.title}
</span>
</NavLink>
</UniversalLink>
</li>
))}
</ul>
Expand Down
Loading