navbar tests

This commit is contained in:
Owen Rees
2023-06-16 15:15:17 +02:00
parent 7450bc82e3
commit e2d99d8f5a
12 changed files with 184 additions and 97 deletions
+16 -21
View File
@@ -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
+1 -1
View File
@@ -10,7 +10,7 @@ const Hamburger = () => {
<>
<div
ref={hamburgerButtonRef}
className="space-y-2 relative z-[99999]"
className="hamburger-button space-y-2 relative z-[99999]"
onClick={() => setMenuVisible(!menuVisible)}
>
<div
+24 -48
View File
@@ -1,57 +1,29 @@
import Link from "next/link";
import ThemeSwitcher from "@/components/ThemeSwitcher";
import {
Dispatch,
RefObject,
SetStateAction,
useEffect,
useRef,
useState,
} from "react";
type hamburgerMenuState = {
interface HamburgerMenuState {
menuVisible: boolean;
setMenuVisible: Dispatch<SetStateAction<boolean>>;
hamburgerButtonRef: RefObject<HTMLDivElement>;
};
}
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<NodeJS.Timeout | undefined>();
const menuRef = useRef<HTMLDivElement>(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 (
<div
+2 -2
View File
@@ -14,11 +14,11 @@ export default function Navbar() {
<span className="text-2xl">Echecs France</span>
</Link>
</div>
<div className="pb-2 md:hidden">
<div className="mobile-menu pb-2 md:hidden">
<Hamburger />
</div>
<div className="hidden pt-2 justify-center md:flex md:w-1/2 md:justify-end">
<div className="desktop-menu hidden pt-2 justify-center md:flex md:w-1/2 md:justify-end">
<ul className="list-reset text-gray-900 dark:text-white no-underline flex flex-1 justify-around md:flex-none items-center">
<li className="mr-10">
<Link
+5 -10
View File
@@ -3,6 +3,7 @@
import { MdBrightness2, MdCircle } from "react-icons/md";
import useDarkMode from "@/hooks/useDarkMode";
import { useEffect, useState } from "react";
import "@/css/theme-toggle.css";
const ThemeSwitcher = () => {
const [mounted, setMounted] = useState(false);
@@ -19,18 +20,12 @@ const ThemeSwitcher = () => {
return (
<div>
{colorTheme === "light" ? (
<div
className="relative cursor-pointer h-8 w-12 rounded-[50px] margin-auto bg-gradient-to-r from-blue-900 to-purple-900"
onClick={() => setTheme("light")}
>
<MdBrightness2 className="ml-auto h-8 w-5" />
<div className="toggle-dark" onClick={() => setTheme("light")}>
<MdBrightness2 className="theme-icon-dark" />
</div>
) : (
<div
className="relative cursor-pointer h-8 w-12 rounded-[50px] margin-auto bg-gradient-to-r from-teal-300 to-sky-500"
onClick={() => setTheme("dark")}
>
<MdCircle className="ml-1 h-8 w-5 text-yellow-300" />
<div className="toggle" onClick={() => setTheme("dark")}>
<MdCircle className="theme-icon" />
</div>
)}
</div>
+30
View File
@@ -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;
}
+17 -14
View File
@@ -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(<Home />);
cy.mount(<ThemeSwitcher />);
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)"
);
});
});
});
+2
View File
@@ -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");
+31
View File
@@ -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");
});
});
Binary file not shown.
Binary file not shown.
+55
View File
@@ -0,0 +1,55 @@
interface HamburgerClose {
menuVisible: boolean;
setMenuVisible: Dispatch<SetStateAction<boolean>>;
menuRef: RefObject<HTMLDivElement>;
hamburgerButtonRef: RefObject<HTMLDivElement>;
mouseOverMenu: boolean;
setMouseOverMenu: Dispatch<SetStateAction<boolean>>;
timeoutRef: RefObject<NodeJS.Timeout | undefined>;
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;