From 43c18f211780ee8a4e7fa7e213c89f5e54238ee5 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 12:49:50 +0300 Subject: [PATCH 01/13] Break nav components into smaller pieces --- .../components/src/navigation/back-button.js | 24 +++++++ packages/components/src/navigation/index.js | 70 ++----------------- .../src/navigation/{item.js => menu-item.js} | 16 ++--- packages/components/src/navigation/menu.js | 7 ++ packages/components/src/navigation/title.js | 14 ++++ 5 files changed, 56 insertions(+), 75 deletions(-) create mode 100644 packages/components/src/navigation/back-button.js rename packages/components/src/navigation/{item.js => menu-item.js} (50%) create mode 100644 packages/components/src/navigation/menu.js create mode 100644 packages/components/src/navigation/title.js diff --git a/packages/components/src/navigation/back-button.js b/packages/components/src/navigation/back-button.js new file mode 100644 index 00000000000000..c67e33bd1669b2 --- /dev/null +++ b/packages/components/src/navigation/back-button.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { Icon, arrowLeft } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import Button from '../button'; + +const NavigationBackButton = ( { children, onClick } ) => { + return ( + + ); +}; + +export default NavigationBackButton; diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index bf5199edc2cb9e..235a4cfde49141 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -1,74 +1,14 @@ /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; -import { Icon, arrowLeft } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import Button from '../button'; -import Text from '../text'; -import Item from './item'; - -const Navigation = ( { data, initial } ) => { - const initialActive = data.find( ( item ) => item.slug === initial ); - const [ active, setActive ] = useState( initialActive ); - const parent = data.find( ( item ) => item.slug === active.parent ); - const items = data.filter( ( item ) => item.parent === active.parent ); - - const goBack = () => { - if ( ! parent.parent ) { - // We are at top level, will need to handle this case. - return; - } - const parentalSiblings = data.filter( - ( item ) => item.parent === parent.parent - ); - if ( parentalSiblings.length ) { - setActive( parentalSiblings[ 0 ] ); - } - }; +import { Children, cloneElement } from '@wordpress/element'; +const Navigation = ( { active, children } ) => { return (
- -
- { parent.title } -
-
- { items.map( ( item ) => - item.menu !== 'secondary' ? ( - - ) : null - ) } -
-
- { items.map( ( item ) => - item.menu === 'secondary' ? ( - - ) : null - ) } -
+ { Children.map( children, ( child ) => + child ? cloneElement( child, { active } ) : null + ) }
); }; diff --git a/packages/components/src/navigation/item.js b/packages/components/src/navigation/menu-item.js similarity index 50% rename from packages/components/src/navigation/item.js rename to packages/components/src/navigation/menu-item.js index b3453dbbf99793..0bdb5e657154c9 100644 --- a/packages/components/src/navigation/item.js +++ b/packages/components/src/navigation/menu-item.js @@ -14,23 +14,19 @@ import { Icon, chevronRight } from '@wordpress/icons'; import Button from '../button'; import Text from '../text'; -const Item = ( { data, item, setActive, isActive } ) => { - const children = data.filter( ( d ) => d.parent === item.slug ); - const onSelect = () => { - const next = children.length ? children[ 0 ] : item; - setActive( next ); - }; +const NavigationMenuItem = ( { children, hasChildren, isActive, onClick } ) => { const classes = classnames( 'components-navigation__menu-item', { 'is-active': isActive, } ); + return ( - ); }; -export default Item; +export default NavigationMenuItem; diff --git a/packages/components/src/navigation/menu.js b/packages/components/src/navigation/menu.js new file mode 100644 index 00000000000000..a63b96d90334e1 --- /dev/null +++ b/packages/components/src/navigation/menu.js @@ -0,0 +1,7 @@ +const NavigationMenu = ( { children } ) => { + return ( +
{ children }
+ ); +}; + +export default NavigationMenu; diff --git a/packages/components/src/navigation/title.js b/packages/components/src/navigation/title.js new file mode 100644 index 00000000000000..3825e923dfc52f --- /dev/null +++ b/packages/components/src/navigation/title.js @@ -0,0 +1,14 @@ +/** + * Internal dependencies + */ +import Text from '../text'; + +const NavigationTitle = ( { children } ) => { + return ( +
+ { children } +
+ ); +}; + +export default NavigationTitle; From a4bbf763287124ac13aadb282612c12dd6c16607 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 17:28:43 +0300 Subject: [PATCH 02/13] Get visible menu items based on menu id and active state --- packages/components/src/navigation/index.js | 9 ++- packages/components/src/navigation/menu.js | 48 ++++++++++++- .../src/navigation/stories/index.js | 72 ++++++++++++------- 3 files changed, 100 insertions(+), 29 deletions(-) diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index 235a4cfde49141..c05365f4f912df 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -3,11 +3,16 @@ */ import { Children, cloneElement } from '@wordpress/element'; -const Navigation = ( { active, children } ) => { +const Navigation = ( { active, children, items } ) => { + const activeItem = items.find( ( item ) => item.id === active ); + const parentItem = items.find( ( item ) => item.id === activeItem.parent ); + return (
{ Children.map( children, ( child ) => - child ? cloneElement( child, { active } ) : null + child + ? cloneElement( child, { active, items, parentItem } ) + : null ) }
); diff --git a/packages/components/src/navigation/menu.js b/packages/components/src/navigation/menu.js index a63b96d90334e1..6062479871aa15 100644 --- a/packages/components/src/navigation/menu.js +++ b/packages/components/src/navigation/menu.js @@ -1,6 +1,50 @@ -const NavigationMenu = ( { children } ) => { +/** + * Internal dependencies + */ +import NavigationMenuItem from './menu-item'; + +const NavigationMenu = ( { active, id, items, parentItem } ) => { + const menuItems = items.filter( ( item ) => { + if ( ! id ) { + return ! item.menu; + } + return item.menu === id; + } ); + const visibleItems = menuItems.filter( ( item ) => { + if ( ! parentItem ) { + return ! item.parent; + } + return item.parent === parentItem.id; + } ); + + const getChildren = ( item ) => { + return menuItems.filter( ( i ) => i.parent === item.id ); + }; + return ( -
{ children }
+
+ { visibleItems.map( ( item ) => { + const children = getChildren( item ); + return ( + { + if ( children.length ) { + children[ 0 ].onClick(); + return; + } + item.onClick(); + } } + parent={ item.parent } + > + { item.title } + + ); + } ) } +
); }; diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index ede16f3046136f..be18918d550485 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -1,7 +1,15 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + /** * Internal dependencies */ import Navigation from '../'; +import NavigationBackButton from '../back-button'; +import NavigationMenu from '../menu'; +import NavigationTitle from '../title'; export default { title: 'Components/Navigation', @@ -9,80 +17,94 @@ export default { }; const data = [ - { title: 'WooCommerce', slug: 'root', back: 'Dashboard' }, - { title: 'Home', slug: 'home', parent: 'root', menu: 'primary' }, + { title: 'Home', id: 'home' }, { title: 'Analytics', - slug: 'analytics', - parent: 'root', - back: 'WooCommerce Home', - menu: 'primary', + id: 'analytics', }, { title: 'Orders', - slug: 'orders', - parent: 'root', - back: 'WooCommerce Home', - menu: 'primary', + id: 'orders', }, { title: 'Overview', - slug: 'overview', + id: 'overview', parent: 'analytics', }, { title: 'Products report', - slug: 'products', + id: 'products', parent: 'analytics', }, { title: 'All orders', - slug: 'all_orders', + id: 'all_orders', parent: 'orders', }, { title: 'Payouts', - slug: 'payouts', + id: 'payouts', parent: 'orders', }, { title: 'Settings', - slug: 'settings', - parent: 'root', - back: 'WooCommerce Home', + id: 'settings', menu: 'secondary', }, { title: 'Extensions', - slug: 'extensions', - parent: 'root', - back: 'WooCommerce Home', + id: 'extensions', menu: 'secondary', }, { title: 'General', - slug: 'general', + id: 'general', parent: 'settings', }, { title: 'Tax', - slug: 'tax', + id: 'tax', parent: 'settings', }, { title: 'My extensions', - slug: 'my_extensions', + id: 'my_extensions', parent: 'extensions', }, { title: 'Marketplace', - slug: 'marketplace', + id: 'marketplace', parent: 'extensions', }, ]; function Example() { - return ; + const [ active, setActive ] = useState( 'home' ); + const activeItem = data.find( ( item ) => item.id === active ); + const parentItem = + activeItem && activeItem.parent + ? data.find( ( item ) => item.id === activeItem.parent ) + : null; + const title = parentItem ? parentItem.title : 'WooCommerce Home'; + const items = data.map( ( item ) => { + item.onClick = () => setActive( item.id ); + return item; + } ); + + return ( + + { activeItem && activeItem.parent && ( + setActive( activeItem.parent ) } + > + { title } + + ) } + { title } + + + + ); } export const _default = () => { From ee380c5b7e992b2b0516481afa8ada14aa26e622 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 18:03:46 +0300 Subject: [PATCH 03/13] Allow null and root text in back button --- .../components/src/navigation/back-button.js | 19 ++++++++++++++++--- packages/components/src/navigation/index.js | 19 ++++++++++++++++++- .../src/navigation/stories/index.js | 12 +++++------- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/components/src/navigation/back-button.js b/packages/components/src/navigation/back-button.js index c67e33bd1669b2..27c59b62600adc 100644 --- a/packages/components/src/navigation/back-button.js +++ b/packages/components/src/navigation/back-button.js @@ -8,15 +8,28 @@ import { Icon, arrowLeft } from '@wordpress/icons'; */ import Button from '../button'; -const NavigationBackButton = ( { children, onClick } ) => { +const NavigationBackButton = ( { backItem, nullText, onClick, rootText } ) => { + if ( ! backItem && ! rootText ) { + return null; + } + + const getText = () => { + if ( ! backItem ) { + return nullText; + } else if ( ! backItem.parent ) { + return rootText; + } + return backItem.title; + }; + return ( ); }; diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index c05365f4f912df..4d1e8aff1f72ca 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -6,12 +6,29 @@ import { Children, cloneElement } from '@wordpress/element'; const Navigation = ( { active, children, items } ) => { const activeItem = items.find( ( item ) => item.id === active ); const parentItem = items.find( ( item ) => item.id === activeItem.parent ); + const grandParentItem = + parentItem && parentItem.parent + ? items.find( ( item ) => item.id === parentItem.parent ) + : null; + const backItem = parentItem + ? items.find( ( item ) => { + if ( grandParentItem ) { + return item.parent === grandParentItem; + } + return ! item.parent; + } ) + : null; return (
{ Children.map( children, ( child ) => child - ? cloneElement( child, { active, items, parentItem } ) + ? cloneElement( child, { + active, + backItem, + items, + parentItem, + } ) : null ) }
diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index be18918d550485..0d1524320bfd00 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -93,13 +93,11 @@ function Example() { return ( - { activeItem && activeItem.parent && ( - setActive( activeItem.parent ) } - > - { title } - - ) } + setActive( item.id ) } + /> { title } From f93fcebe6239f98fb771280a7b1d2c0554c02a30 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 18:06:33 +0300 Subject: [PATCH 04/13] Allow root text for navigation title --- packages/components/src/navigation/stories/index.js | 8 +------- packages/components/src/navigation/title.js | 6 ++++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index 0d1524320bfd00..dbec443bd1e570 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -80,12 +80,6 @@ const data = [ function Example() { const [ active, setActive ] = useState( 'home' ); - const activeItem = data.find( ( item ) => item.id === active ); - const parentItem = - activeItem && activeItem.parent - ? data.find( ( item ) => item.id === activeItem.parent ) - : null; - const title = parentItem ? parentItem.title : 'WooCommerce Home'; const items = data.map( ( item ) => { item.onClick = () => setActive( item.id ); return item; @@ -98,7 +92,7 @@ function Example() { rootText="WooCommerce Home" onClick={ ( item ) => setActive( item.id ) } /> - { title } + diff --git a/packages/components/src/navigation/title.js b/packages/components/src/navigation/title.js index 3825e923dfc52f..fe6d5762d53ea1 100644 --- a/packages/components/src/navigation/title.js +++ b/packages/components/src/navigation/title.js @@ -3,10 +3,12 @@ */ import Text from '../text'; -const NavigationTitle = ( { children } ) => { +const NavigationTitle = ( { parentItem, rootText } ) => { return (
- { children } + + { parentItem ? parentItem.title : rootText } +
); }; From 9b7bc2f8ec2a0e9f0ce5ce1c683d2e4eee2a1f04 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 18:08:44 +0300 Subject: [PATCH 05/13] Add line break to make use of multiple menus more obvious --- packages/components/src/navigation/stories/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index dbec443bd1e570..ba1339d7232c4f 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -94,6 +94,7 @@ function Example() { /> +
); From 937c3c7feb1fc707f7e107f1a50f83267676ee9a Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 18:10:25 +0300 Subject: [PATCH 06/13] Fix back button case at root level --- packages/components/src/navigation/stories/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index ba1339d7232c4f..604dba2a4e5fcb 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -90,7 +90,7 @@ function Example() { setActive( item.id ) } + onClick={ ( item ) => ( item ? setActive( item.id ) : null ) } /> From 3444b29e19f9f23d6e1c84687deb76f4327b5e96 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 28 Jul 2020 18:11:04 +0300 Subject: [PATCH 07/13] Remove secondary nav styling --- packages/components/src/navigation/style.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/components/src/navigation/style.scss b/packages/components/src/navigation/style.scss index 1c90f0573f0439..ac22ed6f4beefa 100644 --- a/packages/components/src/navigation/style.scss +++ b/packages/components/src/navigation/style.scss @@ -14,10 +14,6 @@ .components-navigation__menu-items { display: flex; flex-direction: column; - - &.is-secondary { - margin-top: 24px; - } } .components-navigation__menu-item { From f93d0ad14692985f9fec06f292df6990be8606b5 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Thu, 30 Jul 2020 11:53:32 +0300 Subject: [PATCH 08/13] Simplify component props --- .../components/src/navigation/back-button.js | 19 ++----- .../components/src/navigation/menu-item.js | 14 +++--- packages/components/src/navigation/menu.js | 50 +------------------ packages/components/src/navigation/title.js | 6 +-- 4 files changed, 15 insertions(+), 74 deletions(-) diff --git a/packages/components/src/navigation/back-button.js b/packages/components/src/navigation/back-button.js index 27c59b62600adc..c67e33bd1669b2 100644 --- a/packages/components/src/navigation/back-button.js +++ b/packages/components/src/navigation/back-button.js @@ -8,28 +8,15 @@ import { Icon, arrowLeft } from '@wordpress/icons'; */ import Button from '../button'; -const NavigationBackButton = ( { backItem, nullText, onClick, rootText } ) => { - if ( ! backItem && ! rootText ) { - return null; - } - - const getText = () => { - if ( ! backItem ) { - return nullText; - } else if ( ! backItem.parent ) { - return rootText; - } - return backItem.title; - }; - +const NavigationBackButton = ( { children, onClick } ) => { return ( ); }; diff --git a/packages/components/src/navigation/menu-item.js b/packages/components/src/navigation/menu-item.js index 0bdb5e657154c9..956f947ebc6504 100644 --- a/packages/components/src/navigation/menu-item.js +++ b/packages/components/src/navigation/menu-item.js @@ -20,12 +20,14 @@ const NavigationMenuItem = ( { children, hasChildren, isActive, onClick } ) => { } ); return ( - +
  • + +
  • ); }; diff --git a/packages/components/src/navigation/menu.js b/packages/components/src/navigation/menu.js index 6062479871aa15..d5a62018f9a5f2 100644 --- a/packages/components/src/navigation/menu.js +++ b/packages/components/src/navigation/menu.js @@ -1,51 +1,5 @@ -/** - * Internal dependencies - */ -import NavigationMenuItem from './menu-item'; - -const NavigationMenu = ( { active, id, items, parentItem } ) => { - const menuItems = items.filter( ( item ) => { - if ( ! id ) { - return ! item.menu; - } - return item.menu === id; - } ); - const visibleItems = menuItems.filter( ( item ) => { - if ( ! parentItem ) { - return ! item.parent; - } - return item.parent === parentItem.id; - } ); - - const getChildren = ( item ) => { - return menuItems.filter( ( i ) => i.parent === item.id ); - }; - - return ( -
    - { visibleItems.map( ( item ) => { - const children = getChildren( item ); - return ( - { - if ( children.length ) { - children[ 0 ].onClick(); - return; - } - item.onClick(); - } } - parent={ item.parent } - > - { item.title } - - ); - } ) } -
    - ); +const NavigationMenu = ( { children } ) => { + return
      { children }
    ; }; export default NavigationMenu; diff --git a/packages/components/src/navigation/title.js b/packages/components/src/navigation/title.js index fe6d5762d53ea1..3825e923dfc52f 100644 --- a/packages/components/src/navigation/title.js +++ b/packages/components/src/navigation/title.js @@ -3,12 +3,10 @@ */ import Text from '../text'; -const NavigationTitle = ( { parentItem, rootText } ) => { +const NavigationTitle = ( { children } ) => { return (
    - - { parentItem ? parentItem.title : rootText } - + { children }
    ); }; From e7c0011f8e35860c03a6918d668ed44e7633dbc9 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Thu, 30 Jul 2020 14:08:45 +0300 Subject: [PATCH 09/13] Pass parsed data back to navigation children --- packages/components/src/navigation/index.js | 64 +++++++------- packages/components/src/navigation/pane.js | 5 ++ .../src/navigation/stories/index.js | 83 +++++++++++++++---- packages/components/src/navigation/style.scss | 2 +- 4 files changed, 111 insertions(+), 43 deletions(-) create mode 100644 packages/components/src/navigation/pane.js diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index 4d1e8aff1f72ca..e411bb12eb8fbf 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -1,36 +1,44 @@ -/** - * WordPress dependencies - */ -import { Children, cloneElement } from '@wordpress/element'; +const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { + const mapItemData = ( items ) => { + return items.map( ( item ) => { + const itemChildren = rawItems.filter( + ( i ) => i.parent === item.id + ); + return { + ...item, + children: itemChildren, + parent: item.parent || 'root', + }; + } ); + }; + const items = [ + { id: 'root', title: rootTitle }, + ...mapItemData( rawItems ), + ]; -const Navigation = ( { active, children, items } ) => { - const activeItem = items.find( ( item ) => item.id === active ); - const parentItem = items.find( ( item ) => item.id === activeItem.parent ); - const grandParentItem = - parentItem && parentItem.parent - ? items.find( ( item ) => item.id === parentItem.parent ) - : null; - const backItem = parentItem - ? items.find( ( item ) => { - if ( grandParentItem ) { - return item.parent === grandParentItem; - } - return ! item.parent; - } ) + const activeItem = items.find( ( item ) => item.id === activeId ); + const currentLevel = activeItem + ? items.find( ( item ) => item.id === activeItem.parent ) + : { id: 'root', title: rootTitle }; + const parentLevel = currentLevel + ? items.find( ( item ) => item.id === currentLevel.parent ) : null; + const currentItems = currentLevel + ? items.filter( ( item ) => item.parent === currentLevel.id ) + : items.filter( ( item ) => item.parent === 'root' ); + const parentItems = parentLevel + ? items.filter( ( item ) => item.parent === parentLevel.id ) + : items.filter( ( item ) => item.parent === 'root' ); return (
    - { Children.map( children, ( child ) => - child - ? cloneElement( child, { - active, - backItem, - items, - parentItem, - } ) - : null - ) } + { children( { + activeId, + currentItems, + currentLevel, + parentItems, + parentLevel, + } ) }
    ); }; diff --git a/packages/components/src/navigation/pane.js b/packages/components/src/navigation/pane.js new file mode 100644 index 00000000000000..70205338a1513a --- /dev/null +++ b/packages/components/src/navigation/pane.js @@ -0,0 +1,5 @@ +const NavigationPane = ( { children } ) => { + return
    { children }
    ; +}; + +export default NavigationPane; diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index 604dba2a4e5fcb..ed1658495f89d5 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -8,8 +8,10 @@ import { useState } from '@wordpress/element'; */ import Navigation from '../'; import NavigationBackButton from '../back-button'; +import NavigationPane from '../pane'; import NavigationMenu from '../menu'; import NavigationTitle from '../title'; +import NavigationMenuItem from '../menu-item'; export default { title: 'Components/Navigation', @@ -80,22 +82,75 @@ const data = [ function Example() { const [ active, setActive ] = useState( 'home' ); - const items = data.map( ( item ) => { - item.onClick = () => setActive( item.id ); - return item; - } ); return ( - - ( item ? setActive( item.id ) : null ) } - /> - - -
    - + + { ( { currentItems, currentLevel, parentItems, parentLevel } ) => { + return ( + + + parentLevel + ? setActive( parentItems[ 0 ].id ) + : ( window.location = + 'https://wordpress.com' ) + } + > + { ! parentLevel ? 'Dashboard' : parentLevel.title } + + + { currentLevel.id === 'root' + ? 'WooCommerce' + : currentLevel.title } + + + { currentItems + .filter( ( item ) => item.menu !== 'secondary' ) + .map( ( item ) => ( + 0 } + isActive={ item.id === active } + key={ item.id } + onClick={ () => + item.children.length + ? setActive( + item.children[ 0 ].id + ) + : setActive( item.id ) + } + > + { item.title } + + ) ) } + +
    + + { currentItems + .filter( ( item ) => item.menu === 'secondary' ) + .map( ( item ) => ( + 0 } + isActive={ item.id === active } + key={ item.id } + onClick={ () => + item.children.length + ? setActive( + item.children[ 0 ].id + ) + : setActive( item.id ) + } + > + { item.title } + + ) ) } + +
    + ); + } }
    ); } diff --git a/packages/components/src/navigation/style.scss b/packages/components/src/navigation/style.scss index ac22ed6f4beefa..df9ff20ed4f74e 100644 --- a/packages/components/src/navigation/style.scss +++ b/packages/components/src/navigation/style.scss @@ -11,7 +11,7 @@ margin-bottom: 40px; } -.components-navigation__menu-items { +.components-navigation__menu { display: flex; flex-direction: column; } From 53b85b26fbee93cbe8fa7922f00ba3c72e240695 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Thu, 30 Jul 2020 14:24:00 +0300 Subject: [PATCH 10/13] Move nav menu item logic into menu --- packages/components/src/navigation/menu.js | 27 ++++++++- .../src/navigation/stories/index.js | 55 +++++-------------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/packages/components/src/navigation/menu.js b/packages/components/src/navigation/menu.js index d5a62018f9a5f2..4bfd5b37628669 100644 --- a/packages/components/src/navigation/menu.js +++ b/packages/components/src/navigation/menu.js @@ -1,5 +1,28 @@ -const NavigationMenu = ( { children } ) => { - return
      { children }
    ; +/** + * Internal dependencies + */ +import NavigationMenuItem from './menu-item'; + +const NavigationMenu = ( { activeId, children, items, onSelect } ) => { + return ( +
      + { children } + { items.map( ( item ) => ( + 0 } + isActive={ item.id === activeId } + key={ item.id } + onClick={ () => + item.children.length + ? onSelect( item.children[ 0 ] ) + : onSelect( item ) + } + > + { item.title } + + ) ) } +
    + ); }; export default NavigationMenu; diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index ed1658495f89d5..99e8af71b1297c 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -11,7 +11,6 @@ import NavigationBackButton from '../back-button'; import NavigationPane from '../pane'; import NavigationMenu from '../menu'; import NavigationTitle from '../title'; -import NavigationMenuItem from '../menu-item'; export default { title: 'Components/Navigation', @@ -107,47 +106,21 @@ function Example() { ? 'WooCommerce' : currentLevel.title }
    - - { currentItems - .filter( ( item ) => item.menu !== 'secondary' ) - .map( ( item ) => ( - 0 } - isActive={ item.id === active } - key={ item.id } - onClick={ () => - item.children.length - ? setActive( - item.children[ 0 ].id - ) - : setActive( item.id ) - } - > - { item.title } - - ) ) } - + item.menu !== 'secondary' + ) } + onSelect={ ( item ) => setActive( item.id ) } + />
    - - { currentItems - .filter( ( item ) => item.menu === 'secondary' ) - .map( ( item ) => ( - 0 } - isActive={ item.id === active } - key={ item.id } - onClick={ () => - item.children.length - ? setActive( - item.children[ 0 ].id - ) - : setActive( item.id ) - } - > - { item.title } - - ) ) } - + item.menu === 'secondary' + ) } + onSelect={ ( item ) => setActive( item.id ) } + /> ); } } From 7b68ca65c62ba46806ff628da2163ad1f575005b Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Wed, 5 Aug 2020 13:28:41 +0300 Subject: [PATCH 11/13] Simplify nav components to bare essentials --- .../components/src/navigation/back-button.js | 24 ---- packages/components/src/navigation/index.js | 6 +- .../components/src/navigation/menu-item.js | 11 +- packages/components/src/navigation/menu.js | 27 +--- packages/components/src/navigation/pane.js | 5 - .../src/navigation/stories/index.js | 132 ++++++------------ packages/components/src/navigation/title.js | 14 -- 7 files changed, 56 insertions(+), 163 deletions(-) delete mode 100644 packages/components/src/navigation/back-button.js delete mode 100644 packages/components/src/navigation/pane.js delete mode 100644 packages/components/src/navigation/title.js diff --git a/packages/components/src/navigation/back-button.js b/packages/components/src/navigation/back-button.js deleted file mode 100644 index c67e33bd1669b2..00000000000000 --- a/packages/components/src/navigation/back-button.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * WordPress dependencies - */ -import { Icon, arrowLeft } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import Button from '../button'; - -const NavigationBackButton = ( { children, onClick } ) => { - return ( - - ); -}; - -export default NavigationBackButton; diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index e411bb12eb8fbf..013cb1002cf054 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -8,6 +8,8 @@ const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { ...item, children: itemChildren, parent: item.parent || 'root', + isActive: item.id === activeId, + hasChildren: itemChildren.length > 0, }; } ); }; @@ -28,12 +30,14 @@ const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { : items.filter( ( item ) => item.parent === 'root' ); const parentItems = parentLevel ? items.filter( ( item ) => item.parent === parentLevel.id ) - : items.filter( ( item ) => item.parent === 'root' ); + : []; + const backItem = parentItems.length ? parentItems[ 0 ] : null; return (
    { children( { activeId, + backItem, currentItems, currentLevel, parentItems, diff --git a/packages/components/src/navigation/menu-item.js b/packages/components/src/navigation/menu-item.js index 956f947ebc6504..6d43b161deffae 100644 --- a/packages/components/src/navigation/menu-item.js +++ b/packages/components/src/navigation/menu-item.js @@ -14,16 +14,21 @@ import { Icon, chevronRight } from '@wordpress/icons'; import Button from '../button'; import Text from '../text'; -const NavigationMenuItem = ( { children, hasChildren, isActive, onClick } ) => { +const NavigationMenuItem = ( props ) => { + const { children, hasChildren, isActive, onClick, title } = props; const classes = classnames( 'components-navigation__menu-item', { 'is-active': isActive, } ); + const handleClick = () => { + onClick( children.length ? children[ 0 ] : props ); + }; + return (
  • - diff --git a/packages/components/src/navigation/menu.js b/packages/components/src/navigation/menu.js index 4bfd5b37628669..d5a62018f9a5f2 100644 --- a/packages/components/src/navigation/menu.js +++ b/packages/components/src/navigation/menu.js @@ -1,28 +1,5 @@ -/** - * Internal dependencies - */ -import NavigationMenuItem from './menu-item'; - -const NavigationMenu = ( { activeId, children, items, onSelect } ) => { - return ( -
      - { children } - { items.map( ( item ) => ( - 0 } - isActive={ item.id === activeId } - key={ item.id } - onClick={ () => - item.children.length - ? onSelect( item.children[ 0 ] ) - : onSelect( item ) - } - > - { item.title } - - ) ) } -
    - ); +const NavigationMenu = ( { children } ) => { + return
      { children }
    ; }; export default NavigationMenu; diff --git a/packages/components/src/navigation/pane.js b/packages/components/src/navigation/pane.js deleted file mode 100644 index 70205338a1513a..00000000000000 --- a/packages/components/src/navigation/pane.js +++ /dev/null @@ -1,5 +0,0 @@ -const NavigationPane = ( { children } ) => { - return
    { children }
    ; -}; - -export default NavigationPane; diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index 99e8af71b1297c..b06a7ff4681f05 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -1,16 +1,15 @@ /** * WordPress dependencies */ +import { Button } from '@wordpress/components'; import { useState } from '@wordpress/element'; /** * Internal dependencies */ import Navigation from '../'; -import NavigationBackButton from '../back-button'; -import NavigationPane from '../pane'; import NavigationMenu from '../menu'; -import NavigationTitle from '../title'; +import NavigationMenuItem from '../menu-item'; export default { title: 'Components/Navigation', @@ -18,110 +17,61 @@ export default { }; const data = [ - { title: 'Home', id: 'home' }, { - title: 'Analytics', - id: 'analytics', + title: 'Item 1', + id: 'item-1', }, { - title: 'Orders', - id: 'orders', + title: 'Item 2', + id: 'item-2', }, { - title: 'Overview', - id: 'overview', - parent: 'analytics', + title: 'Category', + id: 'item-3', }, { - title: 'Products report', - id: 'products', - parent: 'analytics', + title: 'Child 1', + id: 'child-1', + parent: 'item-3', }, { - title: 'All orders', - id: 'all_orders', - parent: 'orders', - }, - { - title: 'Payouts', - id: 'payouts', - parent: 'orders', - }, - { - title: 'Settings', - id: 'settings', - menu: 'secondary', - }, - { - title: 'Extensions', - id: 'extensions', - menu: 'secondary', - }, - { - title: 'General', - id: 'general', - parent: 'settings', - }, - { - title: 'Tax', - id: 'tax', - parent: 'settings', - }, - { - title: 'My extensions', - id: 'my_extensions', - parent: 'extensions', - }, - { - title: 'Marketplace', - id: 'marketplace', - parent: 'extensions', + title: 'Child 2', + id: 'child-2', + parent: 'item-3', }, ]; function Example() { - const [ active, setActive ] = useState( 'home' ); + const [ active, setActive ] = useState( 'item-1' ); return ( - - { ( { currentItems, currentLevel, parentItems, parentLevel } ) => { + + { ( { currentItems, currentLevel, backItem } ) => { return ( - - - parentLevel - ? setActive( parentItems[ 0 ].id ) - : ( window.location = - 'https://wordpress.com' ) - } - > - { ! parentLevel ? 'Dashboard' : parentLevel.title } - - - { currentLevel.id === 'root' - ? 'WooCommerce' - : currentLevel.title } - - item.menu !== 'secondary' - ) } - onSelect={ ( item ) => setActive( item.id ) } - /> -
    - item.menu === 'secondary' - ) } - onSelect={ ( item ) => setActive( item.id ) } - /> -
    + <> + { backItem && ( + + ) } +

    { currentLevel.title }

    + + { currentItems.map( ( item ) => { + return ( + + setActive( selected.id ) + } + /> + ); + } ) } + + ); } }
    diff --git a/packages/components/src/navigation/title.js b/packages/components/src/navigation/title.js deleted file mode 100644 index 3825e923dfc52f..00000000000000 --- a/packages/components/src/navigation/title.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Internal dependencies - */ -import Text from '../text'; - -const NavigationTitle = ( { children } ) => { - return ( -
    - { children } -
    - ); -}; - -export default NavigationTitle; From aca45b564f47ff9c759df904c148baae68027fc3 Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Mon, 10 Aug 2020 12:30:50 -0400 Subject: [PATCH 12/13] Allow menu level navigation without changing active item --- packages/components/src/navigation/index.js | 39 +++++++++++-------- .../components/src/navigation/menu-item.js | 16 +++++++- .../src/navigation/stories/index.js | 13 ++++--- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index 013cb1002cf054..d46884d6fe7b28 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -1,4 +1,11 @@ +/** + * WordPress dependencies + */ +import { useEffect, useState } from '@wordpress/element'; + const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { + const [ activeLevel, setActiveLevel ] = useState( 'root' ); + const mapItemData = ( items ) => { return items.map( ( item ) => { const itemChildren = rawItems.filter( @@ -19,29 +26,27 @@ const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { ]; const activeItem = items.find( ( item ) => item.id === activeId ); - const currentLevel = activeItem - ? items.find( ( item ) => item.id === activeItem.parent ) - : { id: 'root', title: rootTitle }; - const parentLevel = currentLevel - ? items.find( ( item ) => item.id === currentLevel.parent ) - : null; - const currentItems = currentLevel - ? items.filter( ( item ) => item.parent === currentLevel.id ) - : items.filter( ( item ) => item.parent === 'root' ); - const parentItems = parentLevel - ? items.filter( ( item ) => item.parent === parentLevel.id ) - : []; - const backItem = parentItems.length ? parentItems[ 0 ] : null; + const level = items.find( ( item ) => item.id === activeLevel ); + const levelItems = items.filter( ( item ) => item.parent === level.id ); + const parentLevel = + level.id === 'root' + ? null + : items.find( ( item ) => item.id === level.parent ); + + useEffect( () => { + if ( activeItem ) { + setActiveLevel( activeItem.parent ); + } + }, [] ); return (
    { children( { activeId, - backItem, - currentItems, - currentLevel, - parentItems, + level, + levelItems, parentLevel, + setActiveLevel, } ) }
    ); diff --git a/packages/components/src/navigation/menu-item.js b/packages/components/src/navigation/menu-item.js index 6d43b161deffae..d665b778d52582 100644 --- a/packages/components/src/navigation/menu-item.js +++ b/packages/components/src/navigation/menu-item.js @@ -15,13 +15,25 @@ import Button from '../button'; import Text from '../text'; const NavigationMenuItem = ( props ) => { - const { children, hasChildren, isActive, onClick, title } = props; + const { + children, + hasChildren, + id, + isActive, + onClick, + setActiveLevel, + title, + } = props; const classes = classnames( 'components-navigation__menu-item', { 'is-active': isActive, } ); const handleClick = () => { - onClick( children.length ? children[ 0 ] : props ); + if ( children.length ) { + setActiveLevel( id ); + return; + } + onClick( props ); }; return ( diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index b06a7ff4681f05..062299452d48e9 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -46,20 +46,22 @@ function Example() { return ( - { ( { currentItems, currentLevel, backItem } ) => { + { ( { level, levelItems, parentLevel, setActiveLevel } ) => { return ( <> - { backItem && ( + { parentLevel && ( ) } -

    { currentLevel.title }

    +

    { level.title }

    - { currentItems.map( ( item ) => { + { levelItems.map( ( item ) => { return ( setActive( selected.id ) } + setActiveLevel={ setActiveLevel } /> ); } ) } From a5c44164d26d96a29b8954632e817186dc81641e Mon Sep 17 00:00:00 2001 From: Joshua Flowers Date: Tue, 11 Aug 2020 08:04:23 -0400 Subject: [PATCH 13/13] Handle PR feedback --- packages/components/src/navigation/index.js | 16 +++++----------- .../components/src/navigation/stories/index.js | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index d46884d6fe7b28..c33850d51f4518 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -3,29 +3,24 @@ */ import { useEffect, useState } from '@wordpress/element'; -const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { +const Navigation = ( { activeItemId, children, data, rootTitle } ) => { const [ activeLevel, setActiveLevel ] = useState( 'root' ); const mapItemData = ( items ) => { return items.map( ( item ) => { - const itemChildren = rawItems.filter( - ( i ) => i.parent === item.id - ); + const itemChildren = data.filter( ( i ) => i.parent === item.id ); return { ...item, children: itemChildren, parent: item.parent || 'root', - isActive: item.id === activeId, + isActive: item.id === activeItemId, hasChildren: itemChildren.length > 0, }; } ); }; - const items = [ - { id: 'root', title: rootTitle }, - ...mapItemData( rawItems ), - ]; + const items = [ { id: 'root', title: rootTitle }, ...mapItemData( data ) ]; - const activeItem = items.find( ( item ) => item.id === activeId ); + const activeItem = items.find( ( item ) => item.id === activeItemId ); const level = items.find( ( item ) => item.id === activeLevel ); const levelItems = items.filter( ( item ) => item.parent === level.id ); const parentLevel = @@ -42,7 +37,6 @@ const Navigation = ( { activeId, children, items: rawItems, rootTitle } ) => { return (
    { children( { - activeId, level, levelItems, parentLevel, diff --git a/packages/components/src/navigation/stories/index.js b/packages/components/src/navigation/stories/index.js index 062299452d48e9..c18d0a6e7e2e73 100644 --- a/packages/components/src/navigation/stories/index.js +++ b/packages/components/src/navigation/stories/index.js @@ -45,7 +45,7 @@ function Example() { const [ active, setActive ] = useState( 'item-1' ); return ( - + { ( { level, levelItems, parentLevel, setActiveLevel } ) => { return ( <>