Skip to content

Commit

Permalink
Implement user menu with Radix Navigation Menu
Browse files Browse the repository at this point in the history
This is now semantically correct and accessible user menu. We still need
to tweak the presentation of the menu content so it pop down below
trigger.
  • Loading branch information
jonathansick committed May 17, 2024
1 parent 5c28fa7 commit 327d438
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/squared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@lsst-sqre/global-css": "workspace:*",
"@lsst-sqre/rubin-style-dictionary": "workspace:*",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-navigation-menu": "^1.1.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-feather": "^2.0.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ export const Default: Story = {
render: (args) => (
<SWRConfig value={{ provider: () => new Map() }}>
<GafaelfawrUserMenu {...args}>
<p>Hello world</p>
<GafaelfawrUserMenu.Link href="#">
Account Settings
</GafaelfawrUserMenu.Link>
<GafaelfawrUserMenu.Link href="#">
Security tokens
</GafaelfawrUserMenu.Link>
</GafaelfawrUserMenu>
</SWRConfig>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react';

import styled from 'styled-components';

import { getLoginUrl, getLogoutUrl } from './authUrls';
import useGafaelfawrUser from '../../hooks/useGafaelfawrUser';
import { getLoginUrl, getLogoutUrl } from './authUrls';
import Menu, { MenuLink } from './Menu';

export interface GafaelfawrUserMenuProps {
children: React.ReactNode;
Expand All @@ -24,7 +25,11 @@ export const GafaelfawrUserMenu = ({
const logoutUrl = getLogoutUrl(currentUrl);
const loginUrl = getLoginUrl(currentUrl);
if (isLoggedIn && user) {
return <div>{children}</div>;
return (
<Menu logoutHref={logoutUrl} username={user.username}>
{children}
</Menu>
);
} else {
return <SiteNavLink href={loginUrl}>Log in / Sign up</SiteNavLink>;
}
Expand All @@ -38,4 +43,7 @@ const SiteNavLink = styled.a`
}
`;

// Associate child components with the parent for easier imports.
GafaelfawrUserMenu.Link = MenuLink;

export default GafaelfawrUserMenu;
124 changes: 124 additions & 0 deletions packages/squared/src/components/GafaelfawrUserMenu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React from 'react';
import styled from 'styled-components';
import { ChevronDown } from 'react-feather';

import * as RadixNavigationMenu from '@radix-ui/react-navigation-menu';

export interface MenuProps {
children: React.ReactNode;
/**
* The URL to use for the logout link. This is the Gafaelfawr logout endpoint.
*/
logoutHref: string;
/**
* The username to display in the menu trigger.
*/
username: string;
}

export const Menu = ({ children, logoutHref, username }: MenuProps) => {
return (
<MenuRoot>
<MenuList>
<RadixNavigationMenu.Item>
<MenuTrigger>
{username} <ChevronDown />
</MenuTrigger>
<MenuContent>
{children}
<MenuLink href={logoutHref}>Log out</MenuLink>
</MenuContent>
</RadixNavigationMenu.Item>
</MenuList>
</MenuRoot>
);
};

const MenuRoot = styled(RadixNavigationMenu.Root)``;

const MenuList = styled(RadixNavigationMenu.List)`
list-style: none;
`;

const MenuTrigger = styled(RadixNavigationMenu.Trigger)`
background-color: transparent;
color: var(--rsd-component-header-nav-text-color);
border: 1px solid transparent;
border-radius: 0.25rem;
&:focus {
outline: 1px solid var(--rsd-color-primary-500);
}
&:hover {
color: var(--rsd-component-header-nav-text-hover-color);
}
svg {
display: inline-block;
width: 1rem;
height: 1rem;
vertical-align: middle;
}
&[data-state='open'] {
svg {
transform: rotate(180deg);
}
}
`;

const MenuContent = styled(RadixNavigationMenu.Content)`
/* This unit for the padding is also the basis for the spacing and
* sizing of the menu items.
*/
--gafaelfawr-user-menu-padding: 0.5rem;
display: flex;
flex-direction: column;
gap: var(0.25rem);
font-size: 1rem;
background-color: var(--rsd-component-header-nav-menulist-background-color);
min-width: 12rem;
border-radius: 0.5rem;
padding: var(--gafaelfawr-user-menu-padding);
color: var(--rsd-component-header-nav-menulist-text-color);
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35),
0px 10px 20px -15px rgba(22, 23, 24, 0.2);
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
`;

export const MenuLink = styled(RadixNavigationMenu.Link)`
color: var(--rsd-component-header-nav-menulist-text-color);
border-radius: 0.5rem;
padding: calc(var(--gafaelfawr-user-menu-padding) / 2)
var(--gafaelfawr-user-menu-padding);
margin: calc(var(--gafaelfawr-user-menu-padding) / -2);
margin-bottom: calc(var(--gafaelfawr-user-menu-padding) / 2);
&:last-of-type {
margin-bottom: 0;
}
outline: 1px solid transparent;
&:focus {
outline: 1px solid
var(--rsd-component-header-nav-menulist-selected-background-color);
}
&:hover {
background-color: var(
--rsd-component-header-nav-menulist-selected-background-color
);
color: white;
a {
color: white;
}
}
`;

export default Menu;
64 changes: 62 additions & 2 deletions pnpm-lock.yaml

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

0 comments on commit 327d438

Please sign in to comment.