diff --git a/TODO b/TODO index 79f3cdb..7afb942 100644 --- a/TODO +++ b/TODO @@ -1,38 +1,33 @@ -hamburger menu timeout / close when clicking elsewhere - -hamburger tests - does it appear on smaller screens, does the menu appear on click - -readme - +//TODO readme ---------------------------------------------------------------- -hover on table needs fixing - this only seemed to happen once I moved the logic into its own hook <- error is occurring on all hovers at the moment. Client/SSR issue? +//TODO hover on table needs fixing - this only seemed to happen once I moved the logic into its own hook <- error is occurring on all hovers at the moment. Client/SSR issue? -font size on mobile screen +//TODO font size on mobile screen -need a 'return to top' arrow button on smaller screens +//TODO need a 'return to top' arrow button on smaller screens -mobile map needs improving +//TODO mobile map needs improving -multi-language i18n support - https://nextjs.org/docs/app/building-your-application/routing/internationalization +//TODO multi-language i18n support - https://nextjs.org/docs/app/building-your-application/routing/internationalization -tests for tournois, testing the loading of map and table, then counting the markers +//TODO tests for tournois, testing the loading of map and table, then counting the markers -logo for navbar and favicon +//TODO logo for navbar and favicon -document my new hook <- consider removing as I believe this is the reason hover is broken +//TODO document my new hook <- consider removing as I believe this is the reason hover is broken -error handling +//TODO error handling -e2e tests for tournament page: +//TODO e2e tests for tournament page: - make sure number of markers = tournamentData.length -about page +//TODO about page -contact page needs working mailer +//TODO contact page needs working mailer -SEO +//TODO SEO -consider offering GraphQL support +//TODO consider offering GraphQL support -move smaller ui components into a new folder, and make them reusable - such as using generic prop names +//TODO move smaller ui components into a new folder, and make them reusable - such as using generic prop names diff --git a/components/Hamburger.tsx b/components/Hamburger.tsx index 7af1d84..abbace3 100644 --- a/components/Hamburger.tsx +++ b/components/Hamburger.tsx @@ -10,7 +10,7 @@ const Hamburger = () => { <>
setMenuVisible(!menuVisible)} >
>; hamburgerButtonRef: RefObject; -}; +} + +import Link from "next/link"; +import ThemeSwitcher from "@/components/ThemeSwitcher"; +import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react"; +import useHamburgerClose from "@/hooks/useHamburgerClose"; const HamburgerMenu = ({ menuVisible, setMenuVisible, hamburgerButtonRef, -}: hamburgerMenuState) => { +}: HamburgerMenuState) => { const [mouseOverMenu, setMouseOverMenu] = useState(false); const timeoutRef = useRef(); const menuRef = useRef(null); - useEffect(() => { - const handleMouseDownOutsideMenu = (event: MouseEvent) => { - if ( - menuVisible && - menuRef.current && - !menuRef.current.contains(event.target as Node) && - hamburgerButtonRef.current && - !hamburgerButtonRef.current.contains(event.target as Node) - ) { - setMenuVisible(false); - setMouseOverMenu(false); - } - }; - - document.addEventListener("mousedown", handleMouseDownOutsideMenu); - - return () => { - document.removeEventListener("mousedown", handleMouseDownOutsideMenu); - }; - }, [menuVisible]); - - useEffect(() => { - if (menuVisible && !mouseOverMenu) { - menuTimeout(); - } else { - clearTimeout(timeoutRef.current); - } - }, [mouseOverMenu, menuVisible]); + const menuTimeout = () => { + timeoutRef.current = setTimeout(() => { + setMenuVisible(false); + setMouseOverMenu(false); + }, 2000); + }; const handleMouseEnterMenu = () => { setMouseOverMenu(true); @@ -63,12 +35,16 @@ const HamburgerMenu = ({ clearTimeout(timeoutRef.current); }; - const menuTimeout = () => { - timeoutRef.current = setTimeout(() => { - setMenuVisible(false); - setMouseOverMenu(false); - }, 2000); - }; + useHamburgerClose({ + menuVisible, + setMenuVisible, + menuRef, + hamburgerButtonRef, + mouseOverMenu, + setMouseOverMenu, + timeoutRef, + menuTimeout, + }); return (
Echecs France
-
+
-
+
  • { const [mounted, setMounted] = useState(false); @@ -19,18 +20,12 @@ const ThemeSwitcher = () => { return (
    {colorTheme === "light" ? ( -
    setTheme("light")} - > - +
    setTheme("light")}> +
    ) : ( -
    setTheme("dark")} - > - +
    setTheme("dark")}> +
    )}
    diff --git a/css/theme-toggle.css b/css/theme-toggle.css new file mode 100644 index 0000000..44cf05c --- /dev/null +++ b/css/theme-toggle.css @@ -0,0 +1,30 @@ +.toggle { + height: 2em; + width: 3em; + border-radius: 50px; + margin: auto; + background-image: linear-gradient(aqua, skyblue); + position: relative; + cursor: pointer; +} +.toggle-dark { + height: 2em; + width: 3em; + border-radius: 50px; + margin: auto; + background-image: linear-gradient(midnightblue, rebeccapurple); + position: relative; + cursor: pointer; +} +.theme-icon{ + margin-left: 0.2em; + height: 2em; + width: 1.2em; + color: yellow; +} + +.theme-icon-dark{ + height: 2em; + width: 1.2em; + margin-left: auto; +} diff --git a/cypress/component/themeToggle.cy.tsx b/cypress/component/themeToggle.cy.tsx index c583362..410c86a 100644 --- a/cypress/component/themeToggle.cy.tsx +++ b/cypress/component/themeToggle.cy.tsx @@ -1,27 +1,30 @@ -import React from 'react'; +import React from "react"; import ThemeSwitcher from "../../components/ThemeSwitcher"; import Home from "@/app/page"; -import useDarkMode from '@/hooks/useDarkMode'; -import '../../app/globals.css'; +import "@/css/theme-toggle.css"; -describe('ThemeSwitcher component', () => { - it('should toggle between light and dark mode', () => { +describe("ThemeSwitcher component", () => { + it("should toggle between light and dark mode", () => { cy.mount(); cy.mount(); cy.wait(100); // checking that the toggle is there and light mode is active - cy.get('.toggle').should('exist'); - cy.get('.toggle').should(($toggle) => { - const backgroundImage = $toggle.css('background-image'); - expect(backgroundImage).to.include('linear-gradient(rgb(0, 255, 255), rgb(135, 206, 235)'); + cy.get(".toggle").should("exist"); + cy.get(".toggle").should(($toggle) => { + const backgroundImage = $toggle.css("background-image"); + expect(backgroundImage).to.include( + "linear-gradient(rgb(0, 255, 255), rgb(135, 206, 235)" + ); }); // checking that the toggle is clickable and dark mode is active - cy.get('.toggle').click(); - cy.get('.toggle-dark').should('exist'); - cy.get('.toggle-dark').should(($toggle) => { - const backgroundImage = $toggle.css('background-image'); - expect(backgroundImage).to.include('linear-gradient(rgb(25, 25, 112), rgb(102, 51, 153)'); + cy.get(".toggle").click(); + cy.get(".toggle-dark").should("exist"); + cy.get(".toggle-dark").should(($toggle) => { + const backgroundImage = $toggle.css("background-image"); + expect(backgroundImage).to.include( + "linear-gradient(rgb(25, 25, 112), rgb(102, 51, 153)" + ); }); }); -}); \ No newline at end of file +}); diff --git a/cypress/e2e/links.cy.ts b/cypress/e2e/links.cy.ts index 47383c6..8410ec1 100644 --- a/cypress/e2e/links.cy.ts +++ b/cypress/e2e/links.cy.ts @@ -6,8 +6,10 @@ describe("Test all links", () => { }; it("Check navbar links point to correct pathname as a slug", () => { + cy.viewport(600, 600); cy.visit("/"); pages.forEach((page) => { + cy.get(".hamburger-button").click(); cy.contains(page, { matchCase: false }).click(); cy.location("pathname").should("eq", `/${navLinkToSlug(page)}`); // url path matches link name, replacing whitespace with hyphens cy.go("back"); diff --git a/cypress/e2e/navbar.cy.tsx b/cypress/e2e/navbar.cy.tsx new file mode 100644 index 0000000..0335489 --- /dev/null +++ b/cypress/e2e/navbar.cy.tsx @@ -0,0 +1,31 @@ +describe("Mobile Navbar", () => { + beforeEach(() => { + cy.viewport(600, 600); + cy.visit("/"); + }); + + it("hamburger menu on mobile screens", () => { + cy.get(".mobile-menu").should("be.not.hidden"); + cy.get(".desktop-menu").should("be.hidden"); + }); + + it("hamburger button available", () => { + cy.get(".hamburger-button").should("be.visible"); + }); +}); + +describe("Desktop Navbar", () => { + beforeEach(() => { + cy.viewport("macbook-15"); + cy.visit("/"); + }); + + it("desktop menu on larger screens", () => { + cy.get(".mobile-menu").should("be.hidden"); + cy.get(".desktop-menu").should("be.not.hidden"); + }); + + it("hamburger button hidden", () => { + cy.get(".hamburger-button").should("be.not.visible"); + }); +}); diff --git a/cypress/videos/links.cy.ts.mp4 b/cypress/videos/links.cy.ts.mp4 index dfef946..65aab18 100644 Binary files a/cypress/videos/links.cy.ts.mp4 and b/cypress/videos/links.cy.ts.mp4 differ diff --git a/cypress/videos/navbar.cy.tsx.mp4 b/cypress/videos/navbar.cy.tsx.mp4 new file mode 100644 index 0000000..74304c9 Binary files /dev/null and b/cypress/videos/navbar.cy.tsx.mp4 differ diff --git a/hooks/useHamburgerClose.ts b/hooks/useHamburgerClose.ts new file mode 100644 index 0000000..caed63f --- /dev/null +++ b/hooks/useHamburgerClose.ts @@ -0,0 +1,55 @@ +interface HamburgerClose { + menuVisible: boolean; + setMenuVisible: Dispatch>; + menuRef: RefObject; + hamburgerButtonRef: RefObject; + mouseOverMenu: boolean; + setMouseOverMenu: Dispatch>; + timeoutRef: RefObject; + menuTimeout: () => void; +} + +import { Dispatch, RefObject, SetStateAction, useEffect } from "react"; + +const useHamburgerClose = ({ + menuVisible, + setMenuVisible, + menuRef, + hamburgerButtonRef, + mouseOverMenu, + setMouseOverMenu, + timeoutRef, + menuTimeout, +}: HamburgerClose) => { + useEffect(() => { + const handleMouseDownOutsideMenu = (event: MouseEvent) => { + if ( + menuVisible && + menuRef.current && + !menuRef.current.contains(event.target as Node) && + hamburgerButtonRef.current && + !hamburgerButtonRef.current.contains(event.target as Node) + ) { + setMenuVisible(false); + setMouseOverMenu(false); + } + }; + document.addEventListener("mousedown", handleMouseDownOutsideMenu); + + return () => { + document.removeEventListener("mousedown", handleMouseDownOutsideMenu); + }; + }, [menuVisible]); + + useEffect(() => { + if (menuVisible && !mouseOverMenu) { + menuTimeout(); + } else { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + } + }, [mouseOverMenu, menuVisible]); +}; + +export default useHamburgerClose;