-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create a PrimaryNavigation component
This component wraps Radix UI's NavigationMenu and provide's a replacement for the current header navigation in Squareone. With NavigationMenu we'll be able to give individual menu items submenus, and not necessarily have to treat the user menu as a special case. This work styles the basic PrimaryNavigation component and demonstrates it with a storybook story. More work is need to make the submenu appear directly below its trigger.
- Loading branch information
1 parent
d025f83
commit dd3ce88
Showing
4 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
packages/squared/src/components/PrimaryNavigation/PrimaryNavigation.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { within, userEvent, screen } from '@storybook/testing-library'; | ||
import { expect } from '@storybook/jest'; | ||
|
||
import { ChevronDown } from 'react-feather'; | ||
|
||
import { PrimaryNavigation } from './PrimaryNavigation'; | ||
|
||
const meta: Meta<typeof PrimaryNavigation> = { | ||
title: 'Components/PrimaryNavigation', | ||
component: PrimaryNavigation, | ||
parameters: { | ||
layout: 'centered', | ||
backgrounds: { | ||
default: 'dark', | ||
values: [{ name: 'dark', value: '#1f2121' }], | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof PrimaryNavigation>; | ||
|
||
export const Default: Story = { | ||
args: {}, | ||
render: (args) => ( | ||
<PrimaryNavigation {...args}> | ||
<PrimaryNavigation.Item> | ||
<PrimaryNavigation.TriggerLink href="#"> | ||
Portal | ||
</PrimaryNavigation.TriggerLink> | ||
</PrimaryNavigation.Item> | ||
|
||
<PrimaryNavigation.Item> | ||
<PrimaryNavigation.TriggerLink href="/nb"> | ||
Notebooks | ||
</PrimaryNavigation.TriggerLink> | ||
</PrimaryNavigation.Item> | ||
|
||
<PrimaryNavigation.Item> | ||
<PrimaryNavigation.TriggerLink href="/docs"> | ||
Documentation | ||
</PrimaryNavigation.TriggerLink> | ||
</PrimaryNavigation.Item> | ||
|
||
<PrimaryNavigation.Item> | ||
<PrimaryNavigation.Trigger> | ||
Account <ChevronDown /> | ||
</PrimaryNavigation.Trigger> | ||
<PrimaryNavigation.SubMenuContent> | ||
<PrimaryNavigation.MenuItem> | ||
<PrimaryNavigation.Link href="#">Settings</PrimaryNavigation.Link> | ||
</PrimaryNavigation.MenuItem> | ||
<PrimaryNavigation.MenuItem> | ||
<PrimaryNavigation.Link href="#">Logout</PrimaryNavigation.Link> | ||
</PrimaryNavigation.MenuItem> | ||
</PrimaryNavigation.SubMenuContent> | ||
</PrimaryNavigation.Item> | ||
</PrimaryNavigation> | ||
), | ||
}; |
180 changes: 180 additions & 0 deletions
180
packages/squared/src/components/PrimaryNavigation/PrimaryNavigation.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
import React from 'react'; | ||
import styled from 'styled-components'; | ||
|
||
import * as RadixNavigationMenu from '@radix-ui/react-navigation-menu'; | ||
|
||
export interface PrimaryNavigationProps { | ||
children: React.ReactNode; | ||
} | ||
|
||
export const PrimaryNavigation = ({ children }: PrimaryNavigationProps) => { | ||
return ( | ||
<MenuRoot> | ||
<MenuList>{children}</MenuList> | ||
<ViewportContainer> | ||
<ContentViewport | ||
onPointerEnter={(event) => event.preventDefault()} | ||
onPointerLeave={(event) => event.preventDefault()} | ||
/> | ||
</ViewportContainer> | ||
</MenuRoot> | ||
); | ||
}; | ||
|
||
const MenuRoot = styled(RadixNavigationMenu.Root)` | ||
position: relative; | ||
`; | ||
|
||
const MenuList = styled(RadixNavigationMenu.List)` | ||
list-style: none; | ||
margin-bottom: 0; | ||
padding: 0; | ||
display: flex; | ||
justify-self: end; | ||
width: 100%; | ||
font-size: 1.2rem; | ||
`; | ||
|
||
const MenuItem = styled(RadixNavigationMenu.Item)` | ||
margin: 0 1em; | ||
`; | ||
|
||
const TriggerLink = styled(RadixNavigationMenu.Link)` | ||
color: var(--rsd-component-header-nav-text-color); | ||
border: none; | ||
border-radius: 0.5rem; | ||
padding: 2px 4px; | ||
display: inline-block; // For consistency with MenuTrigger button | ||
/* padding: 0; */ | ||
&:hover { | ||
color: var(--rsd-component-header-nav-text-hover-color); | ||
} | ||
&:focus { | ||
outline: 1px solid var(--rsd-color-primary-500); | ||
} | ||
`; | ||
|
||
const MenuTrigger = styled(RadixNavigationMenu.Trigger)` | ||
color: var(--rsd-component-header-nav-text-color); | ||
padding: 2px 4px; | ||
/* padding: 0; */ | ||
// Reset button styles | ||
background-color: transparent; | ||
border: none; | ||
border-radius: 0.5rem; | ||
&: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 SubMenuContent = 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: 0.25rem; | ||
list-style: none; | ||
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); | ||
padding-right: 0; // to avoid double padding on the right side with MenuLink | ||
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; | ||
`; | ||
|
||
const MenuContentItem = styled(RadixNavigationMenu.Item)` | ||
display: flex; | ||
`; | ||
|
||
export const MenuLink = styled(RadixNavigationMenu.Link)` | ||
/* The styling on the menu triggers is overriding this colour. Need to re-address. */ | ||
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); | ||
width: 100%; | ||
color: var(--rsd-component-header-nav-menulist-text-color); | ||
&: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 !important; | ||
} | ||
`; | ||
|
||
const ViewportContainer = styled.div` | ||
position: absolute; | ||
display: flex; | ||
justify-content: center; | ||
width: 100%; | ||
top: 100%; | ||
left: 0; | ||
perspective: 2000px; | ||
`; | ||
|
||
const ContentViewport = styled(RadixNavigationMenu.Viewport)` | ||
position: relative; | ||
transform-origin: top center; | ||
margin-top: 10px; | ||
width: 100%; | ||
background-color: white; | ||
border-radius: 6px; | ||
overflow: hidden; | ||
height: var(--radix-navigation-menu-viewport-height); | ||
width: var(--radix-navigation-menu-viewport-width); | ||
`; | ||
|
||
// Associate child components with the parent for easier imports. | ||
PrimaryNavigation.TriggerLink = TriggerLink; | ||
PrimaryNavigation.Link = MenuLink; | ||
PrimaryNavigation.Item = MenuItem; | ||
PrimaryNavigation.MenuItem = MenuContentItem; | ||
PrimaryNavigation.Trigger = MenuTrigger; | ||
PrimaryNavigation.SubMenuContent = SubMenuContent; | ||
|
||
export default PrimaryNavigation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './PrimaryNavigation'; | ||
export { default } from './PrimaryNavigation'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters