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

Navigation Component: Composition Proposal #25057

Merged
merged 38 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
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 Jul 29, 2020
c08901d
Make nav components more atomic (#24266)
joshuatf Aug 11, 2020
63736b4
Use css-in-js for styling in navigation component (#24522)
joshuatf Aug 18, 2020
dbd96d6
Add nav badges (#24528)
joshuatf Aug 18, 2020
81e8a5b
Add custom nav link props to Navigation component (#24570)
joshuatf Aug 20, 2020
16141e0
Add animations to navigation component (#24771)
joshuatf Aug 26, 2020
e479ab9
Navigation Component: Update docs (#24880)
psealock Aug 30, 2020
c77b558
Fix prop error and missing classes in Navigation component (#24878)
joshuatf Aug 30, 2020
80273e2
Add navigation component tests (#24860)
joshuatf Aug 31, 2020
5897bfa
PR feedback
psealock Aug 31, 2020
e652c32
Navigation Component: Apply effect on activeItem change (#24958)
psealock Sep 1, 2020
9cce14d
Navigation Component: Avoid animation on mounting (#24960)
psealock Sep 3, 2020
5991899
Update nav styles to match core designs (#24987)
joshuatf Sep 3, 2020
1365d0d
Navigation with component composition experiment
Copons Sep 3, 2020
d5031eb
Add a third nested menu
Copons Sep 3, 2020
b60bc5c
paul's tinkering
psealock Sep 4, 2020
6a34450
Sort rebase inconsistency
Copons Sep 4, 2020
016dd82
Separate the composition experiment from the original Nav proposal
Copons Sep 4, 2020
c1e351c
Simplify the exposed interface
Copons Sep 4, 2020
21887bc
Add badge and href
Copons Sep 4, 2020
1a88aea
Story parity with other approach
Copons Sep 4, 2020
e78beb4
Re-implement with Context API
Copons Sep 4, 2020
1bc526b
Make the navigation controllable from outside
Copons Sep 5, 2020
1b28149
Naming cleanup
Copons Sep 5, 2020
3f5f943
Don't change the active item when navigating to nested level or click…
Copons Sep 9, 2020
140893d
Add groups
Copons Sep 9, 2020
3352d0c
Replace original approach
Copons Sep 9, 2020
0818991
Delete leftover
Copons Sep 9, 2020
e2d4bca
Fix story's non-nav link positioning
Copons Sep 10, 2020
bf45b48
Rename utils to constants
Copons Sep 10, 2020
4f48fb4
Add custom classNames support
Copons Sep 10, 2020
49c9059
Update active colors to respect theme choice
Copons Sep 10, 2020
66370a7
Simplify the readme
Copons Sep 10, 2020
ca124af
Improve namings
Copons Sep 10, 2020
f9c1c10
Simplify and improve the story
Copons Sep 10, 2020
8906756
Rename level into menu
Copons Sep 10, 2020
6e2aa46
Refactor style names
Copons Sep 10, 2020
0985ad9
Rename parentMenuTitle as backButtonLabel
Copons Sep 10, 2020
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
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,12 @@
"markdown_source": "../packages/components/src/navigable-container/README.md",
"parent": "components"
},
{
"title": "Navigation",
"slug": "navigation",
"markdown_source": "../packages/components/src/navigation/README.md",
"parent": "components"
},
{
"title": "Notice",
"slug": "notice",
Expand Down
2 changes: 2 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Introduce `Navigation` component as `__experimentalNavigation` for displaying a hierarchy of items.
Copy link
Member

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:

To Project Contributors …
these APIs should neither be documented nor mentioned in any CHANGELOG.

Copy link
Member

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 in 10.2.0 but has been stuck here…

cc: @gziolo

Copy link
Member

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.

Copy link
Member

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.

Copy link
Contributor Author

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!


## 10.0.0 (2020-07-07)

### Breaking Change
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export { default as MenuItemsChoice } from './menu-items-choice';
export { default as Modal } from './modal';
export { default as ScrollLock } from './scroll-lock';
export { NavigableMenu, TabbableContainer } from './navigable-container';
export { default as __experimentalNavigation } from './navigation';
export { default as __experimentalNavigationGroup } from './navigation/group';
export { default as __experimentalNavigationItem } from './navigation/item';
export { default as __experimentalNavigationMenu } from './navigation/menu';
export { default as Notice } from './notice';
export { default as __experimentalNumberControl } from './number-control';
export { default as NoticeList } from './notice/list';
Expand Down
193 changes: 193 additions & 0 deletions packages/components/src/navigation/README.md
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.
1 change: 1 addition & 0 deletions packages/components/src/navigation/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ROOT_MENU = 'root';
22 changes: 22 additions & 0 deletions packages/components/src/navigation/context.js
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 );
28 changes: 28 additions & 0 deletions packages/components/src/navigation/group.js
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>
);
}
91 changes: 91 additions & 0 deletions packages/components/src/navigation/index.js
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,
} ) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A className would be helpful here. Otherwise, another height: 100% built in.

>
<NavigationContext.Provider value={ context }>
{ children }
</NavigationContext.Provider>
</div>
) }
</Animate>
</NavigationUI>
);
}
Loading