From b41864bf5f75bcccb7007322af70d3ed9e84a601 Mon Sep 17 00:00:00 2001 From: Andrew Su Date: Thu, 29 Dec 2022 22:57:55 -0800 Subject: [PATCH] feat(cwn-dashboard): added a preview of the customizable navbar --- .../admin/global/navbarPreview.cy.js | 69 +++++++++ src/components/Navbar.js | 21 ++- src/components/dashboard/CustomizeNavbar.js | 12 +- src/hooks/globalHooks/useLocalStorage.js | 3 +- src/pages/admin/global.js | 140 ++++++++++-------- 5 files changed, 176 insertions(+), 69 deletions(-) create mode 100644 cypress/tests/integration/admin/global/navbarPreview.cy.js diff --git a/cypress/tests/integration/admin/global/navbarPreview.cy.js b/cypress/tests/integration/admin/global/navbarPreview.cy.js new file mode 100644 index 000000000..b1b005abe --- /dev/null +++ b/cypress/tests/integration/admin/global/navbarPreview.cy.js @@ -0,0 +1,69 @@ +describe('navbarPreview', { defaultCommandTimeout: 5000 }, () => { + beforeEach(() => { + cy.viewport(1440, 1080); + }); + it('should not exist yet', () => { + cy.visit('/admin/global'); + cy.get('My Test Link').should('not.exist'); + }); + it('should add a new tab', () => { + // Add first tab + cy.get('[data-cy="addNavButton"]').click(); + cy.get('[data-cy="customizeNavName"]').contains('New Url').click(); + cy.get('input[name="title"][value="New Url"]').clear().type('My Test Link'); + cy.get('input[name="url"][value="/new-url"]').clear().type('/My-Test-Link'); + cy.contains('Update').click(); + + // Add second tab + cy.get('[data-cy="addNavButton"]').click(); + cy.get('[data-cy="customizeNavName"]').contains('New Url').click(); + cy.get('input[name="title"][value="New Url"]') + .clear() + .type('My Test Link 2'); + cy.get('input[name="url"][value="/new-url"]') + .clear() + .type('/My-Test-Link-2'); + cy.get('[data-cy="customizeNavName"]') + .siblings() + .find('button') + .contains('Update') + .click(); + }); + + it('should remove a tab', () => { + cy.contains('My Test Link 2') + .siblings() + .find('[data-testid="DeleteIcon"]') + .click(); + cy.contains('My Test Link 2').should('not.exist'); + }); + + it('should update a tab', () => { + cy.get('input[name="title"][value="My Test Link"]') + .clear() + .type('UpdatedLink'); + cy.get('input[name="url"][value="/My-Test-Link"]') + .clear() + .type('/UpdatedLink'); + cy.get('[data-cy="customizeNavName"]') + .siblings() + .find('button') + .contains('Update') + .click(); + }); + + it('should navigate to new url tab', () => { + cy.get('[data-cy="addNavButton"]').click(); + cy.get('[data-cy="customizeNavName"]').contains('New Url').click(); + cy.get('input[name="title"][value="New Url"]').clear().type('Home'); + cy.get('input[name="url"][value="/new-url"]') + .clear() + .type('https://greenstand.org/home'); + cy.get('[data-cy="customizeNavName"]') + .siblings() + .find('button') + .contains('Update') + .click(); + cy.get('a[href="https://greenstand.org/home"]').click(); + }); +}); diff --git a/src/components/Navbar.js b/src/components/Navbar.js index b8418976c..41641e5a9 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -8,8 +8,9 @@ import { Typography, } from '@mui/material'; import Image from 'next/image'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { initialState } from 'context/configContext'; +import { getStorageValue } from 'hooks/globalHooks/useLocalStorage'; import MenuBar from 'images/MenuBar'; import { makeStyles } from 'models/makeStyles'; import ChangeThemeButton from './ChangeThemeButton'; @@ -60,11 +61,11 @@ const useStyles = makeStyles()((theme) => ({ }, })); -function Navbar() { +function Navbar({ preview }) { const [anchorEl, setAnchorEl] = useState(null); const isMobile = useMobile(); const [webMapConfig] = useLocalStorage('config', initialState); - const { items: navItems } = webMapConfig.navbar; + const [navItems, setNavItems] = useState(webMapConfig.navbar.items); const open = Boolean(anchorEl); const handleMenuClick = (event) => { @@ -74,10 +75,24 @@ function Navbar() { setAnchorEl(null); }; const { classes } = useStyles(); + + // Detect webMapConfig changes. Reactively update the nav-items; + useEffect(() => { + const updateFunction = () => { + const localWebMapConfig = getStorageValue('config', initialState); + setNavItems(localWebMapConfig.navbar.items); + }; + window.addEventListener('storage', updateFunction); + return () => { + window.removeEventListener('storage', updateFunction); + }; + }, []); + return ( diff --git a/src/components/dashboard/CustomizeNavbar.js b/src/components/dashboard/CustomizeNavbar.js index 3f5572110..125218a95 100644 --- a/src/components/dashboard/CustomizeNavbar.js +++ b/src/components/dashboard/CustomizeNavbar.js @@ -137,9 +137,13 @@ function CustomAccordion({ item: defaultItem, index }) {
- {item.title} + {item.title} - {hasItemChanged && } + {hasItemChanged && ( + + )} } onClick={handleDelete} @@ -197,7 +201,9 @@ function CustomizeNavbar() { )} - + ); } diff --git a/src/hooks/globalHooks/useLocalStorage.js b/src/hooks/globalHooks/useLocalStorage.js index e016179ed..7e89348c8 100644 --- a/src/hooks/globalHooks/useLocalStorage.js +++ b/src/hooks/globalHooks/useLocalStorage.js @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; const KEY_PREFIX = 'greenstand-web-map-client-'; // Get value from localStorage if possible, otherwise return provided default -function getStorageValue(key, defaultValue) { +export function getStorageValue(key, defaultValue) { if (typeof window !== 'undefined') { const value = localStorage.getItem(KEY_PREFIX + key); let saved; @@ -23,6 +23,7 @@ const useLocalStorage = (key, defaultValue) => { useEffect(() => { localStorage.setItem(KEY_PREFIX + key, JSON.stringify(value)); + window.dispatchEvent(new Event('storage')); }, [key, value]); return [value, setValue]; diff --git a/src/pages/admin/global.js b/src/pages/admin/global.js index d7b464bf5..d3a007561 100644 --- a/src/pages/admin/global.js +++ b/src/pages/admin/global.js @@ -2,10 +2,12 @@ import { Box, Typography, Divider, List } from '@mui/material'; import dynamic from 'next/dynamic'; import { useEffect, useState } from 'react'; import HeadTag from 'components/HeadTag'; +import Navbar from 'components/Navbar'; import ChangeLogoSection from 'components/dashboard/ChangeLogoSection'; import ChangeNavSection from 'components/dashboard/ChangeNavSection'; import { Tab, TabPanel } from 'components/dashboard/Tabs'; import { ConfigProvider, useConfigContext } from 'context/configContext'; +import { CustomThemeProvider } from 'context/themeContext'; import { getOrganizationById } from 'models/api'; import { updateLogoUrl } from 'models/config.reducer'; import { wrapper } from 'models/utils'; @@ -47,79 +49,93 @@ function Global({ organization }) { }, [mapContext, organization, state.map.initialLocation]); return ( - <> - - + + - - Dashboard - - - - - Navbar Settings - - - Theme Settings - - - Map Settings - - - - - - Navbar View - - - - - - - Theme View - - - Map View - - + Dashboard + + + + + Navbar Settings + + + Theme Settings + + + Map Settings + + + + + + Navbar Preview + + + + + + + + + + + Theme View + + + Map View + + + - - + ); }