From 9af3b34cfc925ccff67d8607c8c62fcc9eeaa583 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Wed, 13 Nov 2024 15:20:46 -0800 Subject: [PATCH 1/5] fix(SILVA-564): added breadcrumb and updated menu --- .../__test__/components/PageTitle.test.tsx | 84 +++++++++++++++++++ .../src/components/BCHeaderwSide/index.tsx | 2 + frontend/src/components/PageTitle/index.tsx | 59 +++++++++++-- 3 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 frontend/src/__test__/components/PageTitle.test.tsx diff --git a/frontend/src/__test__/components/PageTitle.test.tsx b/frontend/src/__test__/components/PageTitle.test.tsx new file mode 100644 index 00000000..00ca1f0e --- /dev/null +++ b/frontend/src/__test__/components/PageTitle.test.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import PageTitle from '../../components/PageTitle'; +import { describe, it, expect, vi } from 'vitest'; +import { MemoryRouter, useLocation } from 'react-router-dom'; + +vi.mock('react-router-dom', async () => { + const actual = await vi.importActual("react-router-dom"); + return { + ...actual, + useLocation: vi.fn(), + }; +}); + +vi.mock('../../components/BCHeaderwSide/constants', async () => { + const actual = await vi.importActual('../../components/BCHeaderwSide/constants'); + return { + ...actual, + leftMenu: [ + { + name: 'Main', + items: [ + { + name: 'Submain', + link: '/submain', + subItems: [ + { + name: 'Test Page', + link: '/test' + } + ] + } + ] + } + ] + }; +}); + +describe('PageTitle Component', () => { + + it('renders the title correctly', () => { + (useLocation as vi.Mock).mockReturnValue({ pathname: '/test' }); + render( + + + + ); + const titleElement = screen.getByText(/Test Title/i); + expect(titleElement).toBeInTheDocument(); + }); + + it('renders the breadcrumb correctly', () => { + (useLocation as vi.Mock).mockReturnValue({ pathname: '/test' }); + render( + + + + ); + + const titleElement = screen.getByText(/Submain/i); + expect(titleElement).toBeInTheDocument(); + }); + + it('renders empty breadcrumb if no path found on list', () => { + (useLocation as vi.Mock).mockReturnValue({ pathname: '/homer' }); + render( + + + + ); + + const olElement = screen.getByRole('list'); + const listItems = olElement.querySelectorAll('li'); + expect(olElement).toBeInTheDocument(); + expect(listItems.length).toBe(0); + }); + +}); \ No newline at end of file diff --git a/frontend/src/components/BCHeaderwSide/index.tsx b/frontend/src/components/BCHeaderwSide/index.tsx index 0295c581..f88efece 100644 --- a/frontend/src/components/BCHeaderwSide/index.tsx +++ b/frontend/src/components/BCHeaderwSide/index.tsx @@ -101,6 +101,8 @@ function BCHeaderwSide(): JSX.Element { title={subItem.name} renderIcon={IconComponent} isActive={isActive} + isSideNavExpanded={isActive} + defaultExpanded={isActive} > {subItem.subItems.map(subSubItem => ( = ({ title, subtitle }: PageTitleProps) => { - + + // This will return up to the second level, even if we use just the first one + const extractCurrentItems = (): LeftMenuItem[] => { + // Get the current location to use as lookup + const currentLocation = useLocation().pathname; + // Recursive function to find the full path to the current item, retaining full objects + for (const item of leftMenu) { + // Check for match at subItem level + if (item.items) { + for (const subItem of item.items) { + //Create as an array for the sake of the return type + const subPath = [subItem]; + + // Check for match at subItem level, ideally it should never match as this is a high level item + // but it can happen + if (subItem.link === currentLocation) { + return subPath; // Direct match, return the path + } + + // Check for match at subSubItem level, and this is usually the case for most of the + // items. This is the most common scenario, and for that, we will return both the item + // and the parent + if (subItem.subItems) { + for (const subSubItem of subItem.subItems) { + // Create an array, so we can generate the breadcrumb with the deeper levels + if (subSubItem.link === currentLocation) { + // We're returning the subPath here because we want to stop at the first level + // This is because so far we have just two levels, and we don't want to go deeper in the breadcrumb + // In case we wanted, we could just do this before this if and return the fullPath instead + //const fullPath = [...subPath, subSubItem]; + return subPath; + + } + } + } + } + } + } + return []; + } + return ( + + {extractCurrentItems().map(item => ( + {item.name} + ))} +

{title}

From fa6f008b6845027d50c262b1d3b7c50ed48f3f72 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Wed, 13 Nov 2024 15:43:20 -0800 Subject: [PATCH 2/5] chore: reducing complexity --- frontend/src/components/PageTitle/index.tsx | 26 ++++----------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/PageTitle/index.tsx b/frontend/src/components/PageTitle/index.tsx index 5580c458..e4439e21 100644 --- a/frontend/src/components/PageTitle/index.tsx +++ b/frontend/src/components/PageTitle/index.tsx @@ -20,35 +20,19 @@ const PageTitle: React.FC = ({ // This will return up to the second level, even if we use just the first one const extractCurrentItems = (): LeftMenuItem[] => { - // Get the current location to use as lookup const currentLocation = useLocation().pathname; - // Recursive function to find the full path to the current item, retaining full objects - for (const item of leftMenu) { - // Check for match at subItem level + + for (const item of leftMenu) { if (item.items) { for (const subItem of item.items) { - //Create as an array for the sake of the return type - const subPath = [subItem]; - - // Check for match at subItem level, ideally it should never match as this is a high level item - // but it can happen if (subItem.link === currentLocation) { - return subPath; // Direct match, return the path + return [subItem]; } - // Check for match at subSubItem level, and this is usually the case for most of the - // items. This is the most common scenario, and for that, we will return both the item - // and the parent if (subItem.subItems) { for (const subSubItem of subItem.subItems) { - // Create an array, so we can generate the breadcrumb with the deeper levels - if (subSubItem.link === currentLocation) { - // We're returning the subPath here because we want to stop at the first level - // This is because so far we have just two levels, and we don't want to go deeper in the breadcrumb - // In case we wanted, we could just do this before this if and return the fullPath instead - //const fullPath = [...subPath, subSubItem]; - return subPath; - + if (subSubItem.link === currentLocation) { + return [subItem]; } } } From c71f8311268faf8a55e1df19012a5ce4e3bec7b6 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Wed, 13 Nov 2024 15:43:55 -0800 Subject: [PATCH 3/5] chore: moved hook to outside of function --- frontend/src/components/PageTitle/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/PageTitle/index.tsx b/frontend/src/components/PageTitle/index.tsx index e4439e21..7c607801 100644 --- a/frontend/src/components/PageTitle/index.tsx +++ b/frontend/src/components/PageTitle/index.tsx @@ -18,10 +18,10 @@ const PageTitle: React.FC = ({ subtitle }: PageTitleProps) => { + const currentLocation = useLocation().pathname; + // This will return up to the second level, even if we use just the first one const extractCurrentItems = (): LeftMenuItem[] => { - const currentLocation = useLocation().pathname; - for (const item of leftMenu) { if (item.items) { for (const subItem of item.items) { From c7d9a66fd005be8d49a60fc80b50393e5206c0d1 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Thu, 14 Nov 2024 08:47:52 -0800 Subject: [PATCH 4/5] chore: removed breadcrumb from home page --- .../__test__/components/PageTitle.test.tsx | 32 ++++++++++++++++++- .../src/components/BCHeaderwSide/constants.ts | 14 +++++--- frontend/src/components/PageTitle/index.tsx | 6 ++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/src/__test__/components/PageTitle.test.tsx b/frontend/src/__test__/components/PageTitle.test.tsx index 00ca1f0e..4d1e5895 100644 --- a/frontend/src/__test__/components/PageTitle.test.tsx +++ b/frontend/src/__test__/components/PageTitle.test.tsx @@ -23,10 +23,24 @@ vi.mock('../../components/BCHeaderwSide/constants', async () => { { name: 'Submain', link: '/submain', + breadcrumb: true, subItems: [ { name: 'Test Page', - link: '/test' + link: '/test', + breadcrumb: true, + } + ] + }, + { + name: 'Intramain', + link: '/intramain', + breadcrumb: false, + subItems: [ + { + name: 'Internal Affairs', + link: '/intramain', + breadcrumb: false, } ] } @@ -81,4 +95,20 @@ describe('PageTitle Component', () => { expect(listItems.length).toBe(0); }); + it('should not render breadcrumb if breadcrumb is false', () => { + (useLocation as vi.Mock).mockReturnValue({ pathname: '/intramain' }); + render( + + + + ); + + const olElement = screen.getByRole('list'); + const listItems = olElement.querySelectorAll('li'); + expect(olElement).toBeInTheDocument(); + expect(listItems.length).toBe(0); + }); + }); \ No newline at end of file diff --git a/frontend/src/components/BCHeaderwSide/constants.ts b/frontend/src/components/BCHeaderwSide/constants.ts index 074342bc..c642efc6 100644 --- a/frontend/src/components/BCHeaderwSide/constants.ts +++ b/frontend/src/components/BCHeaderwSide/constants.ts @@ -5,6 +5,7 @@ export type LeftMenuItem = { icon?: keyof typeof Icons; link: string; disabled: boolean; + breadcrumb: boolean; subItems?: LeftMenuItem[]; } @@ -22,16 +23,19 @@ const mainActivitiesItems: LeftMenu[] = [ icon: 'MapBoundaryVegetation', link: '/opening', disabled: false, + breadcrumb: false, subItems: [ { name: 'Home page', link: '/opening', - disabled: false + disabled: false, + breadcrumb: false }, { name: 'Silviculture search', link: '/silviculture-search', - disabled: false + disabled: false, + breadcrumb: true } ] } @@ -47,13 +51,15 @@ const managementItems: LeftMenu[] = [ name: 'Settings', icon: 'Settings', link: '#', - disabled: true + disabled: true, + breadcrumb: false }, { name: 'Notifications', icon: 'Notification', link: '#', - disabled: true + disabled: true, + breadcrumb: false } ] } diff --git a/frontend/src/components/PageTitle/index.tsx b/frontend/src/components/PageTitle/index.tsx index 7c607801..118b3dfc 100644 --- a/frontend/src/components/PageTitle/index.tsx +++ b/frontend/src/components/PageTitle/index.tsx @@ -25,13 +25,15 @@ const PageTitle: React.FC = ({ for (const item of leftMenu) { if (item.items) { for (const subItem of item.items) { - if (subItem.link === currentLocation) { + if (subItem.link === currentLocation && subItem.breadcrumb) { + console.log(`${subItem.name} - ${subItem.link} - ${subItem.breadcrumb}`); return [subItem]; } if (subItem.subItems) { for (const subSubItem of subItem.subItems) { - if (subSubItem.link === currentLocation) { + console.log(` ${subSubItem.name} - ${subSubItem.link} - ${subSubItem.breadcrumb}`); + if (subSubItem.link === currentLocation && subSubItem.breadcrumb) { return [subItem]; } } From 25b6afabe73f8c3360173c7cc86575dc078a06d1 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Thu, 14 Nov 2024 08:49:11 -0800 Subject: [PATCH 5/5] chore: removing console log --- frontend/src/components/PageTitle/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/PageTitle/index.tsx b/frontend/src/components/PageTitle/index.tsx index 118b3dfc..fe2d7876 100644 --- a/frontend/src/components/PageTitle/index.tsx +++ b/frontend/src/components/PageTitle/index.tsx @@ -26,13 +26,11 @@ const PageTitle: React.FC = ({ if (item.items) { for (const subItem of item.items) { if (subItem.link === currentLocation && subItem.breadcrumb) { - console.log(`${subItem.name} - ${subItem.link} - ${subItem.breadcrumb}`); return [subItem]; } if (subItem.subItems) { for (const subSubItem of subItem.subItems) { - console.log(` ${subSubItem.name} - ${subSubItem.link} - ${subSubItem.breadcrumb}`); if (subSubItem.link === currentLocation && subSubItem.breadcrumb) { return [subItem]; }