Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a submenu #2018

Merged
merged 23 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b8a7a8e
Add submenu
eunjuhuss Feb 6, 2025
7b7da22
Add marign-left for submenu
eunjuhuss Feb 6, 2025
e5f1aae
add navlink
eunjuhuss Feb 6, 2025
1825101
Adjust background color for menu hover
eunjuhuss Feb 7, 2025
eef9ac2
add li last child margin bottom
eunjuhuss Feb 10, 2025
d36c96e
Remove submenu link
eunjuhuss Feb 10, 2025
92265fc
Adjust padding, margin in header
eunjuhuss Feb 12, 2025
fd90ab3
Set links for the submenus
eunjuhuss Feb 12, 2025
46cbbc2
Add an onclick function to close the nav menu when the user selects a…
eunjuhuss Feb 12, 2025
5bac6a9
Add an ID to articles to link it from the submenu click
eunjuhuss Feb 13, 2025
3e5f2fe
Add title for close button
eunjuhuss Feb 13, 2025
77f3694
extract messages
eunjuhuss Feb 13, 2025
658356c
add padding left for a larger click area
eunjuhuss Feb 13, 2025
7082952
Add a scrollbar to the nav header
eunjuhuss Feb 14, 2025
43ec3ca
Reuse formatted text that already exists
eunjuhuss Feb 14, 2025
03bf926
Remove commented out code
eunjuhuss Feb 14, 2025
62173c1
chvron up/down -> hamburger and close icon
eunjuhuss Feb 14, 2025
891dbce
Remove the list hover effect, add a background color for the active l…
eunjuhuss Feb 14, 2025
a300145
fix warning: React does not recognize the isActive prop on a DOM elem…
eunjuhuss Feb 17, 2025
e55e8e9
site variables, responsiveness, hover, list line-height
cartja Feb 17, 2025
7d6e7f1
space between open and active submenu
cartja Feb 17, 2025
cd72314
higher menu in case all open
cartja Feb 17, 2025
320ddc5
Npm audit fix
eunjuhuss Feb 17, 2025
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
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

228 changes: 180 additions & 48 deletions src/components/Common/HeaderNav.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { faArrowRightFromBracket, faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
import {
faArrowRightFromBracket,
faBars,
faChevronDown,
faChevronUp,
faXmark,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import EduIDButton from "components/Common/EduIDButton";
import { ACCOUNT_PATH, IDENTITY_PATH, SECURITY_PATH, START_PATH } from "components/IndexMain";
import { useAppSelector } from "eduid-hooks";
import React, { useEffect, useRef, useState } from "react";
import { FormattedMessage } from "react-intl";
import { NavLink } from "react-router-dom";
import { HashLink } from "react-router-hash-link";

// export for use in tests
export const activeClassName = "active";
type ButtonKey = "start" | "identity" | "security" | "account";

export interface HeaderNavProps {
handleLogout: () => void;
Expand Down Expand Up @@ -37,11 +45,7 @@ function RenderUserName(props: RenderUserNameProps): JSX.Element | null {
data-name={emails.filter((mail) => mail.primary)[0].email}
>
<span>{emails.filter((mail) => mail.primary)[0].email}</span>
{props.openMenu ? (
<FontAwesomeIcon icon={faChevronUp as IconProp} />
) : (
<FontAwesomeIcon icon={faChevronDown as IconProp} />
)}
{props.openMenu ? <FontAwesomeIcon icon={faXmark as IconProp} /> : <FontAwesomeIcon icon={faBars as IconProp} />}
</button>
);
}
Expand All @@ -67,58 +71,186 @@ function useCloseMenuClickOutside(ref: React.RefObject<HTMLElement>, handler: ()

export function HeaderNav(props: HeaderNavProps): JSX.Element {
const [openMenu, setOpenMenu] = useState<boolean>(false);
const [isOpen, setIsOpen] = useState<{ [key in ButtonKey]: boolean }>({
start: false,
identity: false,
security: false,
account: false,
});
const wrapperRef = useRef(null);

const toggleOpen = (button: ButtonKey) => {
setIsOpen((prevState) => ({
...prevState,
[button]: !prevState[button],
}));
};

useCloseMenuClickOutside(wrapperRef, () => setOpenMenu(false));
return (
<nav className="header-nav" ref={wrapperRef}>
<RenderUserName setOpenMenu={setOpenMenu} openMenu={openMenu} />
<div className={openMenu ? "nav-menu active" : "nav-menu"}>
<EduIDButton buttonstyle="close" size="sm" onClick={() => setOpenMenu(false)}></EduIDButton>
<ul>
<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={START_PATH}
end
>
<FormattedMessage defaultMessage="Start" description="Dashboard nav tab name" />
</NavLink>

<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={IDENTITY_PATH}
>
<FormattedMessage defaultMessage="Identity" description="Dashboard nav tab name" />
</NavLink>

<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={SECURITY_PATH}
>
<FormattedMessage defaultMessage="Security" description="Dashboard nav tab name" />
</NavLink>
<li className="flex-between">
<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={START_PATH}
end
>
<FormattedMessage defaultMessage="Start" description="Dashboard nav tab name" />
</NavLink>
<button onClick={() => toggleOpen("start")}>
{isOpen.start ? (
<FontAwesomeIcon icon={faChevronUp as IconProp} />
) : (
<FontAwesomeIcon icon={faChevronDown as IconProp} />
)}
</button>
</li>
<li className={isOpen.start ? "submenu-collapse" : "submenu-collapse submenu-close"}>
<ul>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${START_PATH}#status-overview`}>
<FormattedMessage defaultMessage="eduID status overview" description="status overview title" />
</HashLink>
</li>
</ul>
</li>

<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={ACCOUNT_PATH}
>
<FormattedMessage defaultMessage="Account" description="Dashboard nav tab name" />
</NavLink>
<li className="flex-between">
<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={IDENTITY_PATH}
>
<FormattedMessage defaultMessage="Identity" description="Dashboard nav tab name" />
</NavLink>
<button onClick={() => toggleOpen("identity")}>
{isOpen.identity ? (
<FontAwesomeIcon icon={faChevronUp as IconProp} />
) : (
<FontAwesomeIcon icon={faChevronDown as IconProp} />
)}
</button>
</li>
<li className={isOpen.identity ? "submenu-collapse" : "submenu-collapse submenu-close"}>
<ul>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${IDENTITY_PATH}#verify-identity`}>
<FormattedMessage defaultMessage="Verify Identity" description="Identity sub menu" />
</HashLink>
</li>
<li>
<HashLink
onClick={() => setOpenMenu(false)}
to={`${IDENTITY_PATH}#personal-data`}
aria-label="go to manage your security key section"
>
<FormattedMessage description="Names & Display Name" defaultMessage={`Names & Display Name`} />
</HashLink>
</li>
</ul>
</li>
<li className="flex-between">
<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={SECURITY_PATH}
>
<FormattedMessage defaultMessage="Security" description="security main title" />
</NavLink>

<EduIDButton
buttonstyle="link"
size="sm"
id="logout"
onClick={props.handleLogout}
disabled={!props.login_url}
>
<FontAwesomeIcon icon={faArrowRightFromBracket as IconProp} />
<FormattedMessage defaultMessage="Log out" description="Header logout" />
</EduIDButton>
<button onClick={() => toggleOpen("security")}>
{isOpen.security ? (
<FontAwesomeIcon icon={faChevronUp as IconProp} />
) : (
<FontAwesomeIcon icon={faChevronDown as IconProp} />
)}
</button>
</li>
<li className={isOpen.security ? "submenu-collapse" : "submenu-collapse submenu-close"}>
<ul>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${SECURITY_PATH}#add-two-factor`}>
<FormattedMessage defaultMessage="Two-factor Authentication (2FA)" description="security key title" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${SECURITY_PATH}#manage-security-keys`}>
<FormattedMessage defaultMessage="Manage your security keys" description="manage your tokens" />
</HashLink>
</li>
</ul>
</li>
<li className="flex-between">
<NavLink
onClick={() => setOpenMenu(false)}
className={({ isActive }) => (isActive ? `${activeClassName} menu` : `menu`)}
to={ACCOUNT_PATH}
>
<FormattedMessage defaultMessage="Account" description="Dashboard nav tab name" />
</NavLink>
<button onClick={() => toggleOpen("account")}>
{isOpen.account ? (
<FontAwesomeIcon icon={faChevronUp as IconProp} />
) : (
<FontAwesomeIcon icon={faChevronDown as IconProp} />
)}
</button>
</li>
<li className={isOpen.account ? "submenu-collapse" : "submenu-collapse submenu-close"}>
<ul>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#unique-id`}>
<FormattedMessage defaultMessage="Unique ID" description="Dashboard AccountId" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#add-email-addresses`}>
<FormattedMessage defaultMessage="Email addresses" description="Emails main title" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#language`}>
<FormattedMessage defaultMessage="Language" description="Language" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#change-password`}>
<FormattedMessage defaultMessage="Change password" description="Dashboard change password" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#orcid`}>
<FormattedMessage defaultMessage="ORCID account" description="Dashboard AccountLinking" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#ladok`}>
<FormattedMessage defaultMessage="ESI information" description="Ladok account linking" />
</HashLink>
</li>
<li>
<HashLink onClick={() => setOpenMenu(false)} to={`${ACCOUNT_PATH}#delete-account`}>
<FormattedMessage defaultMessage="Delete eduID" description="DeleteAccount" />
</HashLink>
</li>
</ul>
</li>
<li className="logout-button-wrapper">
<EduIDButton
buttonstyle="link"
size="sm"
id="logout"
onClick={props.handleLogout}
disabled={!props.login_url}
>
<FontAwesomeIcon icon={faArrowRightFromBracket as IconProp} />
<FormattedMessage defaultMessage="Log out" description="Header logout" />
</EduIDButton>
</li>
</ul>
</div>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/MultiFactorAuthentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export function MultiFactorAuthentication(): React.ReactElement | null {
if (!isPlatformAuthLoaded) return null;
return (
<>
<article>
<article id="add-two-factor">
<h2>
<FormattedMessage description="security key title" defaultMessage="Two-factor Authentication (2FA)" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/AccountId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function AccountId(): JSX.Element {

export function AccountIdDisplay(): JSX.Element {
return (
<article>
<article id="unique-id">
<h2>
<FormattedMessage defaultMessage="Unique ID" description="Dashboard AccountId" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/AccountLinking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";

export function AccountLinking() {
return (
<article>
<article id="orcid">
<h2>
<FormattedMessage defaultMessage="ORCID account" description="Dashboard AccountLinking" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/ChangePasswordDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function ChangePasswordDisplay() {
}
}
return (
<article>
<article id="change-password">
<h2>
<FormattedMessage defaultMessage="Change password" description="Dashboard change password" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/DeleteAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function DeleteAccount(): JSX.Element | null {
}

return (
<article>
<article id="delete-account">
<h2>
<FormattedMessage defaultMessage="Delete eduID" description="DeleteAccount" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/Emails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function Emails() {
}

return (
<article>
<article id="add-email-addresses">
<h2>
<FormattedMessage defaultMessage="Email addresses" description="Emails main title" />
</h2>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/Identity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function IdentityContent(): JSX.Element {
</div>
</section>

<article>
<article id="verify-identity">
{identities?.is_verified ? (
<React.Fragment>
<h2>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard/Ladok.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ const LadokContainer = (): JSX.Element => {
useEffect(() => setSwitchChecked(isLinked), [isLinked]);

return (
<article className="ladok">
<article className="ladok" id="ladok">
<h2>
<FormattedMessage defaultMessage="Ladok information" description="Ladok account linking" />
<FormattedMessage defaultMessage="ESI information" description="Ladok account linking" />
</h2>

<p>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard/Language.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function LanguagePreference() {
}

return (
<article>
<article id="language">
<h2>
<FormattedMessage description="pd main title" defaultMessage={`Language`} />
</h2>
Expand All @@ -58,7 +58,7 @@ export function LanguagePreference() {
<fieldset className="name-inputs">
<article>
<legend className="require">
<FormattedMessage defaultMessage="Language" description="Language radio group legend" />
<FormattedMessage defaultMessage="Language" description="Language" />
</legend>
<div className="radio-input-container">
{language_list.map((option: string[]) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard/PersonalDataParent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ function PersonalDataParent() {
};

return (
<article className="personal-data">
<article className="personal-data" id="personal-data">
<div className="heading">
<h2>
<FormattedMessage description="pd main title" defaultMessage={`Names & Display Name`} />
<FormattedMessage description="Names & Display Name" defaultMessage={`Names & Display Name`} />
</h2>
<RenderEditButton hasPersonalData={hasPersonalData} setEditMode={setEditMode} isEditMode={isEditMode} />
</div>
Expand Down
Loading
Loading