-
Notifications
You must be signed in to change notification settings - Fork 1
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
Feat: Introduce NavigationItem
subcomponent and showcase Header with Navigation and Dropdown
#1840
base: feature/unstyled-button
Are you sure you want to change the base?
Changes from all commits
6f8a344
e30fcfb
c38f5ee
d36c4c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import { useNavigationItemStyleProps } from '../useNavigationItemStyleProps'; | ||
|
||
describe('useNavigationItemStyleProps', () => { | ||
it('should return defaults', () => { | ||
const props = {}; | ||
const { result } = renderHook(() => useNavigationItemStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('NavigationItem'); | ||
}); | ||
|
||
it('should return stretchChildren class', () => { | ||
const props = { isStretchingChildren: true }; | ||
const { result } = renderHook(() => useNavigationItemStyleProps(props)); | ||
|
||
expect(result.current.classProps).toBe('NavigationItem NavigationItem--stretchChildren'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import classNames from 'classnames'; | ||
import { useClassNamePrefix } from '../../hooks'; | ||
import { SpiritNavigationItemProps } from '../../types'; | ||
|
||
export interface NavigationItemStyles { | ||
classProps: string; | ||
props: SpiritNavigationItemProps; | ||
} | ||
|
||
export const useNavigationItemStyleProps = (props: SpiritNavigationItemProps): NavigationItemStyles => { | ||
const { isStretchingChildren, ...restProps } = props; | ||
const itemClass = useClassNamePrefix('NavigationItem'); | ||
const itemIsStretchingChildrenClass = `${itemClass}--stretchChildren`; | ||
|
||
const className = classNames(itemClass, { | ||
[itemIsStretchingChildrenClass]: isStretchingChildren, | ||
}); | ||
|
||
return { | ||
classProps: className, | ||
props: restProps, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,35 @@ | ||
import React from 'react'; | ||
import { SpiritNavigationActionProps } from '../../../types'; | ||
import { Button } from '../../Button'; | ||
import { ButtonLink } from '../../ButtonLink'; | ||
import { Container } from '../../Container'; | ||
import { Dropdown, DropdownPopover, DropdownTrigger } from '../../Dropdown'; | ||
import { Flex } from '../../Flex'; | ||
import { Icon } from '../../Icon'; | ||
import { Item } from '../../Item'; | ||
import { Navigation, NavigationAction, NavigationItem } from '../../Navigation'; | ||
import { ProductLogo } from '../../ProductLogo'; | ||
import { defaultSvgLogo } from '../../ProductLogo/demo/ProductLogoDefault'; | ||
import { Text } from '../../Text'; | ||
import { UNSTABLE_Avatar } from '../../UNSTABLE_Avatar'; | ||
import UNSTABLE_Header from '../UNSTABLE_Header'; | ||
import UNSTABLE_HeaderLogo from '../UNSTABLE_HeaderLogo'; | ||
|
||
const NavigationActionAsDropdownTrigger = (props: SpiritNavigationActionProps) => ( | ||
<NavigationAction {...props} elementType="button" /> | ||
); | ||
const AvatarButtonAsDropdownTrigger = (props: SpiritNavigationActionProps) => ( | ||
<button {...props} className="button-unstyled" type="button" /> | ||
); | ||
|
||
const HeaderDefault = () => { | ||
const [isNavigationActionDropdownOpen, setIsNavigationActionDropdownOpen] = React.useState(false); | ||
const [isAvatarDropdownOpen, setIsAvatarDropdownOpen] = React.useState(false); | ||
|
||
return ( | ||
<UNSTABLE_Header> | ||
<Container> | ||
<Flex alignmentX="left" alignmentY="center" spacing="space-1000"> | ||
<Flex alignmentX="left" alignmentY="stretch" spacing="space-1000"> | ||
<UNSTABLE_HeaderLogo href="#"> | ||
<ProductLogo>{defaultSvgLogo}</ProductLogo> | ||
</UNSTABLE_HeaderLogo> | ||
|
@@ -27,6 +42,35 @@ const HeaderDefault = () => { | |
Selected | ||
</NavigationAction> | ||
</NavigationItem> | ||
<NavigationItem> | ||
<Dropdown | ||
alignmentX="stretch" | ||
alignmentY="stretch" | ||
id="navigation-action-dropdown" | ||
isOpen={isNavigationActionDropdownOpen} | ||
onToggle={() => setIsNavigationActionDropdownOpen(!isNavigationActionDropdownOpen)} | ||
placement="bottom-end" | ||
> | ||
<DropdownTrigger elementType={NavigationActionAsDropdownTrigger as unknown as HTMLButtonElement}> | ||
Dropdown | ||
<Icon name="chevron-up" boxSize={20} UNSAFE_className="accessibility-open" /> | ||
<Icon name="chevron-down" boxSize={20} UNSAFE_className="accessibility-closed" /> | ||
</DropdownTrigger> | ||
<DropdownPopover> | ||
<ul className="list-unstyled"> | ||
<li> | ||
<Item label="My Account" href="https://www.example.com" /> | ||
</li> | ||
<li> | ||
<Item label="Settings" href="https://www.example.com" /> | ||
</li> | ||
<li> | ||
<Item label="Log Out" href="https://www.example.com" /> | ||
</li> | ||
</ul> | ||
</DropdownPopover> | ||
</Dropdown> | ||
</NavigationItem> | ||
<NavigationItem> | ||
<NavigationAction href="#" isDisabled> | ||
Disabled | ||
|
@@ -39,9 +83,45 @@ const HeaderDefault = () => { | |
<Icon name="search" /> | ||
</Button> | ||
</NavigationItem> | ||
<NavigationItem isStretchingChildren> | ||
<Dropdown | ||
alignmentX="stretch" | ||
alignmentY="stretch" | ||
id="avatar-dropdown" | ||
isOpen={isAvatarDropdownOpen} | ||
onToggle={() => setIsAvatarDropdownOpen(!isAvatarDropdownOpen)} | ||
placement="bottom-end" | ||
> | ||
<DropdownTrigger elementType={AvatarButtonAsDropdownTrigger as unknown as HTMLButtonElement}> | ||
<Flex spacingX="space-500" alignmentX="stretch" alignmentY="center"> | ||
<UNSTABLE_Avatar isSquare aria-label="Profile of Jiří Bárta"> | ||
<Icon name="profile" boxSize={20} /> | ||
</UNSTABLE_Avatar> | ||
<Text elementType="span" size="small" emphasis="semibold" UNSAFE_className="text-primary"> | ||
My Account | ||
</Text> | ||
<Icon name="chevron-up" boxSize={20} UNSAFE_className="accessibility-open" /> | ||
<Icon name="chevron-down" boxSize={20} UNSAFE_className="accessibility-closed" /> | ||
</Flex> | ||
</DropdownTrigger> | ||
<DropdownPopover> | ||
<ul className="list-unstyled"> | ||
<li> | ||
<Item label="My Account" href="https://www.example.com" /> | ||
</li> | ||
<li> | ||
<Item label="Settings" href="https://www.example.com" /> | ||
</li> | ||
<li> | ||
<Item label="Log Out" href="https://www.example.com" /> | ||
</li> | ||
</ul> | ||
</DropdownPopover> | ||
</Dropdown> | ||
</NavigationItem> | ||
<NavigationItem> | ||
<ButtonLink href="#" color="secondary"> | ||
Sign up | ||
Log out | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
</ButtonLink> | ||
</NavigationItem> | ||
<NavigationItem> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,9 @@ export type NavigationActionProps<E extends ElementType> = { | |
elementType?: E; | ||
} & NavigationActionBaseProps; | ||
|
||
export interface SpiritNavigationItemProps extends ChildrenProps, StyleProps {} | ||
export interface SpiritNavigationItemProps extends ChildrenProps, StyleProps { | ||
isStretchingChildren?: boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🕺 🙈
|
||
} | ||
|
||
export type SpiritNavigationActionProps<E extends ElementType = 'a'> = NavigationActionProps<E> & | ||
SpiritPolymorphicElementPropsWithRef<E, NavigationActionProps<E>>; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ The `Navigation` component is a container for the navigation actions of the appl | |
It consists of these parts: | ||
|
||
- [Navigation](#navigation) | ||
- [NavigationItem](#navigation-item) | ||
- [NavigationAction](#navigation-action) | ||
|
||
## Navigation | ||
|
@@ -29,6 +30,31 @@ it will insert a gap between them. | |
|
||
ℹ️ Don't forget to add the `aria-label` attribute to the `Navigation` component for correct accessible title. | ||
|
||
## Navigation Item | ||
|
||
The `NavigationItem` is a container for a navigation action or any other action. | ||
|
||
```html | ||
<li class="NavigationItem"> | ||
<!-- action --> | ||
</li> | ||
``` | ||
|
||
It centers its children vertically if the children are not NavigationAction components or | ||
it does not have the `.NavigationItem--stretchChildren` class. | ||
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would turn this around so it's more comprehensible (hopefully): If there is a NavigationAction inside or the XY modifier is present, it stretches its content vertically. In all other cases, the content is vertically centered. |
||
|
||
```html | ||
<li class="NavigationItem"> | ||
<a href="#">Vertically centered link</a> | ||
</li> | ||
<li class="NavigationItem NavigationItem--stretchChildren"> | ||
<a href="#">Stretched link</a> | ||
</li> | ||
<li class="NavigationItem"> | ||
<a href="#" class="NavigationAction">Stretched NavigationAction</a> | ||
</li> | ||
``` | ||
|
||
## Navigation Action | ||
|
||
The `NavigationAction` is component that is styled to be used as a navigation action. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,9 @@ | |
@use 'theme'; | ||
|
||
.Navigation, | ||
.Navigation > ul, | ||
.Navigation > ul > li { | ||
.Navigation > ul { | ||
display: flex; | ||
align-items: stretch; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it needed? It works without it for me. |
||
} | ||
|
||
.Navigation:has(.NavigationAction) { | ||
|
@@ -15,7 +15,6 @@ | |
.Navigation:not(:has(.NavigationAction)), | ||
.Navigation:not(:has(.NavigationAction)) > ul { | ||
gap: theme.$spacing; | ||
align-items: center; | ||
} | ||
|
||
.Navigation > ul { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
@use '@tokens' as tokens; | ||
@use 'theme'; | ||
|
||
.NavigationItem { | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.NavigationItem--stretchChildren, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just the AlignmentYExtended dictionary alone, isn't it? 🙂 Now it looks a bit strange to me like this, but I think we will be able to get rid of this modifier once we abstract the alignment classes (and people will be able to use also other alignments here if needed). |
||
.NavigationItem:has(.NavigationAction) { | ||
align-items: stretch; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ $spacing: tokens.$space-700; | |
$height: 50px; | ||
|
||
$link-typography: tokens.$body-small-semibold; | ||
$link-gap: tokens.$space-600; | ||
$link-spacing: tokens.$space-600; | ||
$link-color-state-default: tokens.$component-header-item-state-default; | ||
$link-color-state-hover: tokens.$component-header-item-state-hover; | ||
Comment on lines
6
to
10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't these all start with |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise better than in
web
🙂.