-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Navigation Component: Composition Proposal #25057
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
53382ec
Add Navigation component [Feature branch] (#24107)
psealock c08901d
Make nav components more atomic (#24266)
joshuatf 63736b4
Use css-in-js for styling in navigation component (#24522)
joshuatf dbd96d6
Add nav badges (#24528)
joshuatf 81e8a5b
Add custom nav link props to Navigation component (#24570)
joshuatf 16141e0
Add animations to navigation component (#24771)
joshuatf e479ab9
Navigation Component: Update docs (#24880)
psealock c77b558
Fix prop error and missing classes in Navigation component (#24878)
joshuatf 80273e2
Add navigation component tests (#24860)
joshuatf 5897bfa
PR feedback
psealock e652c32
Navigation Component: Apply effect on activeItem change (#24958)
psealock 9cce14d
Navigation Component: Avoid animation on mounting (#24960)
psealock 5991899
Update nav styles to match core designs (#24987)
joshuatf 1365d0d
Navigation with component composition experiment
Copons d5031eb
Add a third nested menu
Copons b60bc5c
paul's tinkering
psealock 6a34450
Sort rebase inconsistency
Copons 016dd82
Separate the composition experiment from the original Nav proposal
Copons c1e351c
Simplify the exposed interface
Copons 21887bc
Add badge and href
Copons 1a88aea
Story parity with other approach
Copons e78beb4
Re-implement with Context API
Copons 1bc526b
Make the navigation controllable from outside
Copons 1b28149
Naming cleanup
Copons 3f5f943
Don't change the active item when navigating to nested level or click…
Copons 140893d
Add groups
Copons 3352d0c
Replace original approach
Copons 0818991
Delete leftover
Copons e2d4bca
Fix story's non-nav link positioning
Copons bf45b48
Rename utils to constants
Copons 4f48fb4
Add custom classNames support
Copons 49c9059
Update active colors to respect theme choice
Copons 66370a7
Simplify the readme
Copons ca124af
Improve namings
Copons f9c1c10
Simplify and improve the story
Copons 8906756
Rename level into menu
Copons 6e2aa46
Refactor style names
Copons 0985ad9
Rename parentMenuTitle as backButtonLabel
Copons File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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,193 @@ | ||
# Navigation | ||
|
||
Render a navigation list with optional groupings and hierarchy. | ||
|
||
## Usage | ||
|
||
```jsx | ||
import { | ||
__experimentalNavigation as Navigation, | ||
__experimentalNavigationGroup as NavigationGroup, | ||
__experimentalNavigationItem as NavigationItem, | ||
__experimentalNavigationMenu as NavigationMenu, | ||
} from '@wordpress/components'; | ||
|
||
const MyNavigation = () => ( | ||
<Navigation> | ||
<NavigationMenu title="Home"> | ||
<NavigationGroup title="Group 1"> | ||
<NavigationItem item="item-1" title="Item 1" /> | ||
<NavigationItem item="item-2" title="Item 2" /> | ||
</NavigationGroup> | ||
<NavigationGroup title="Group 2"> | ||
<NavigationItem | ||
item="item-3" | ||
navigateToMenu="category" | ||
title="Category" | ||
/> | ||
</NavigationGroup> | ||
</NavigationMenu> | ||
|
||
<NavigationMenu | ||
backButtonLabel="Home" | ||
menu="category" | ||
parentMenu="root" | ||
title="Category" | ||
> | ||
<ul> | ||
<NavigationItem | ||
badge="1" | ||
item="child-1" | ||
title="Child 1" | ||
/> | ||
<NavigationItem item="child-2" title="Child 2" /> | ||
</ul> | ||
</NavigationMenu> | ||
</Navigation> | ||
); | ||
``` | ||
|
||
## Navigation Props | ||
|
||
`Navigation` supports the following props. | ||
|
||
### `activeItem` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The active item slug. | ||
|
||
### `activeMenu` | ||
|
||
- Type: `string` | ||
- Required: No | ||
- Default: "root" | ||
|
||
The active menu slug. | ||
|
||
### className | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
Optional className for the `Navigation` component. | ||
|
||
### `onActivateItem` | ||
|
||
- Type: `function` | ||
- Required: No | ||
|
||
Sync the active item between the external state and the Navigation's internal state. | ||
|
||
### `onActivateMenu` | ||
|
||
- Type: `function` | ||
- Required: No | ||
|
||
Sync the active menu between the external state and the Navigation's internal state. | ||
|
||
## Navigation Menu Props | ||
|
||
`NavigationMenu` supports the following props. | ||
|
||
### `backButtonLabel` | ||
|
||
- Type: `string` | ||
- Required: No | ||
- Default: "Back" | ||
|
||
The back button label used in nested menus. | ||
|
||
### className | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
Optional className for the `NavigationMenu` component. | ||
|
||
### `menu` | ||
|
||
- Type: `string` | ||
- Required: No | ||
- Default: "root" | ||
|
||
The menu slug. | ||
|
||
### `parentMenu` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The parent menu slug; used by nested menus to indicate their parent menu. | ||
|
||
### `title` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The menu title. | ||
|
||
## Navigation Group Props | ||
|
||
`NavigationGroup` supports the following props. | ||
|
||
### className | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
Optional className for the `NavigationGroup` component. | ||
|
||
### `title` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The group title. | ||
|
||
## Navigation Item Props | ||
|
||
`NavigationItem` supports the following props. | ||
|
||
### `badge` | ||
|
||
- Type: `string|Number` | ||
- Required: No | ||
|
||
The item badge content. | ||
|
||
### className | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
Optional className for the `NavigationItem` component. | ||
|
||
### `href` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
If provided, renders `a` instead of `button`. | ||
|
||
### `navigateToMenu` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The child menu slug. If provided, clicking on the item will navigate to the target menu. | ||
|
||
### `onClick` | ||
|
||
- Type: `function` | ||
- Required: No | ||
|
||
A callback to handle clicking on a menu item. | ||
|
||
### `title` | ||
|
||
- Type: `string` | ||
- Required: No | ||
|
||
The item title. |
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 @@ | ||
export const ROOT_MENU = 'root'; |
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,22 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { noop } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { createContext, useContext } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { ROOT_MENU } from './constants'; | ||
|
||
export const NavigationContext = createContext( { | ||
activeItem: undefined, | ||
activeMenu: ROOT_MENU, | ||
setActiveItem: noop, | ||
setActiveMenu: noop, | ||
} ); | ||
export const useNavigationContext = () => useContext( NavigationContext ); |
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,28 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { GroupTitleUI } from './styles/navigation-styles'; | ||
|
||
export default function NavigationGroup( { children, className, title } ) { | ||
const classes = classnames( 'components-navigation__group', className ); | ||
|
||
return ( | ||
<div className={ classes }> | ||
{ title && ( | ||
<GroupTitleUI | ||
as="h3" | ||
className="components-navigation__group-title" | ||
variant="caption" | ||
> | ||
{ title } | ||
</GroupTitleUI> | ||
) } | ||
<ul>{ children }</ul> | ||
</div> | ||
); | ||
} |
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,91 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
import { noop } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useEffect, useRef, useState } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import Animate from '../animate'; | ||
import { ROOT_MENU } from './constants'; | ||
import { NavigationContext } from './context'; | ||
import { NavigationUI } from './styles/navigation-styles'; | ||
|
||
export default function Navigation( { | ||
activeItem, | ||
activeMenu = ROOT_MENU, | ||
children, | ||
className, | ||
onActivateItem = noop, | ||
onActivateMenu = noop, | ||
} ) { | ||
const [ item, setItem ] = useState( activeItem ); | ||
const [ menu, setMenu ] = useState( activeMenu ); | ||
const [ slideOrigin, setSlideOrigin ] = useState(); | ||
|
||
const setActiveItem = ( itemId ) => { | ||
setItem( itemId ); | ||
onActivateItem( itemId ); | ||
}; | ||
|
||
const setActiveMenu = ( menuId, slideInOrigin = 'left' ) => { | ||
setSlideOrigin( slideInOrigin ); | ||
setMenu( menuId ); | ||
onActivateMenu( menuId ); | ||
}; | ||
|
||
// Used to prevent the sliding animation on mount | ||
const isMounted = useRef( false ); | ||
Copons marked this conversation as resolved.
Show resolved
Hide resolved
|
||
useEffect( () => { | ||
if ( ! isMounted.current ) { | ||
isMounted.current = true; | ||
} | ||
}, [] ); | ||
|
||
useEffect( () => { | ||
if ( activeItem !== item ) { | ||
setActiveItem( activeItem ); | ||
} | ||
if ( activeMenu !== menu ) { | ||
setActiveMenu( activeMenu ); | ||
} | ||
}, [ activeItem, activeMenu ] ); | ||
|
||
const context = { | ||
activeItem: item, | ||
activeMenu: menu, | ||
setActiveItem, | ||
setActiveMenu, | ||
}; | ||
|
||
const classes = classnames( 'components-navigation', className ); | ||
|
||
return ( | ||
<NavigationUI className={ classes }> | ||
<Animate | ||
key={ menu } | ||
type="slide-in" | ||
options={ { origin: slideOrigin } } | ||
> | ||
{ ( { className: animateClassName } ) => ( | ||
<div | ||
className={ classnames( { | ||
[ animateClassName ]: | ||
isMounted.current && slideOrigin, | ||
} ) } | ||
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. A className would be helpful here. Otherwise, another |
||
> | ||
<NavigationContext.Provider value={ context }> | ||
{ children } | ||
</NavigationContext.Provider> | ||
</div> | ||
) } | ||
</Animate> | ||
</NavigationUI> | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Whoops, experimental features shouldn't appear in the CHANGELOG:
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.
I don't think this was correctly processed because it's under the
Unreleased
"version" but doesn't have a header like "Breaking Change" or "New Feature". This appears to have been released in10.2.0
but has been stuck here…cc: @gziolo
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.
Yes, I think there is a bug in the script that processes changelogs. It's primitive so I'm not surprised 😂
In this case you are correct that it should not be listed although as far as I remember React includes experimental APIs as well. It's all subjective.
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.
I opened #27436 that fixes the bug you discovered.
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.
Thanks for taking care of this, I'll be more careful next time!