From 7e351c18a371b59c1aa26c9a4c2f7bbf96a288ee Mon Sep 17 00:00:00 2001 From: Paul Sealock Date: Thu, 22 Oct 2020 16:01:25 +1300 Subject: [PATCH] Navigation: extend with WC Admin router links (https://github.com/woocommerce/woocommerce-admin/pull/5445) * add test case * save * pages * comments * use id * bail on no wcAdmin pages * prefix --- .../client/analytics/report/get-reports.js | 15 ++-- .../client/layout/controller.js | 5 ++ .../woocommerce-admin/client/layout/index.js | 1 + .../client/layout/navigation.js | 75 +++++++++++++++++++ .../navigation/components/Item/index.js | 16 ++-- .../client/navigation/index.js | 6 +- .../packages/navigation/src/index.js | 50 ++++++++++--- .../Features/Navigation/Navigation.php~HEAD | 72 ------------------ plugins/woocommerce-admin/src/Loader.php | 1 + 9 files changed, 144 insertions(+), 97 deletions(-) create mode 100644 plugins/woocommerce-admin/client/layout/navigation.js delete mode 100644 plugins/woocommerce-admin/src/Features/Navigation/Navigation.php~HEAD diff --git a/plugins/woocommerce-admin/client/analytics/report/get-reports.js b/plugins/woocommerce-admin/client/analytics/report/get-reports.js index ec3a66b0f39..5b130ae03b2 100644 --- a/plugins/woocommerce-admin/client/analytics/report/get-reports.js +++ b/plugins/woocommerce-admin/client/analytics/report/get-reports.js @@ -57,58 +57,63 @@ export default () => { report: 'revenue', title: __( 'Revenue', 'woocommerce-admin' ), component: RevenueReport, + id: 'woocommerce-analytics-revenue', }, { report: 'products', title: __( 'Products', 'woocommerce-admin' ), component: ProductsReport, + id: 'woocommerce-analytics-products', }, { report: 'variations', title: __( 'Variations', 'woocommerce-admin' ), component: VariationsReport, + id: 'woocommerce-analytics-variations', }, { report: 'orders', title: __( 'Orders', 'woocommerce-admin' ), component: OrdersReport, + id: 'woocommerce-analytics-orders', }, { report: 'categories', title: __( 'Categories', 'woocommerce-admin' ), component: CategoriesReport, + id: 'woocommerce-analytics-categories', }, { report: 'coupons', title: __( 'Coupons', 'woocommerce-admin' ), component: CouponsReport, + id: 'woocommerce-analytics-coupons', }, { report: 'taxes', title: __( 'Taxes', 'woocommerce-admin' ), component: TaxesReport, - }, - { - report: 'downloads', - title: __( 'Downloads', 'woocommerce-admin' ), - component: DownloadsReport, + id: 'woocommerce-analytics-taxes', }, manageStock === 'yes' ? { report: 'stock', title: __( 'Stock', 'woocommerce-admin' ), component: StockReport, + id: 'woocommerce-analytics-stock', } : null, { report: 'customers', title: __( 'Customers', 'woocommerce-admin' ), component: CustomersReport, + id: 'woocommerce-analytics-customers', }, { report: 'downloads', title: __( 'Downloads', 'woocommerce-admin' ), component: DownloadsReport, + id: 'woocommerce-analytics-downloads', }, ].filter( Boolean ); diff --git a/plugins/woocommerce-admin/client/layout/controller.js b/plugins/woocommerce-admin/client/layout/controller.js index 94eb7af3672..54f38352aa6 100644 --- a/plugins/woocommerce-admin/client/layout/controller.js +++ b/plugins/woocommerce-admin/client/layout/controller.js @@ -59,6 +59,7 @@ export const getPages = () => { __( 'Home', 'woocommerce-admin' ), ], wpOpenMenu: 'toplevel_page_woocommerce', + id: 'woocommerce-home', } ); if ( window.wcAdminFeatures.analytics ) { @@ -74,6 +75,7 @@ export const getPages = () => { __( 'Overview', 'woocommerce-admin' ), ], wpOpenMenu: 'toplevel_page_wc-admin-path--analytics-overview', + id: 'woocommerce-analytics-overview', } ); pages.push( { container: AnalyticsSettings, @@ -87,6 +89,7 @@ export const getPages = () => { __( 'Settings', 'woocommerce-admin' ), ], wpOpenMenu: 'toplevel_page_wc-admin-path--analytics-overview', + id: 'woocommerce-analytics-settings', } ); pages.push( { container: AnalyticsReport, @@ -96,6 +99,7 @@ export const getPages = () => { __( 'Customers', 'woocommerce-admin' ), ], wpOpenMenu: 'toplevel_page_woocommerce', + id: 'woocommerce-analytics-customers', } ); pages.push( { container: AnalyticsReport, @@ -130,6 +134,7 @@ export const getPages = () => { __( 'Overview', 'woocommerce-admin' ), ], wpOpenMenu: 'toplevel_page_woocommerce-marketing', + id: 'woocommerce-marketing', } ); } diff --git a/plugins/woocommerce-admin/client/layout/index.js b/plugins/woocommerce-admin/client/layout/index.js index fb7acdbb198..faaad61fdf4 100644 --- a/plugins/woocommerce-admin/client/layout/index.js +++ b/plugins/woocommerce-admin/client/layout/index.js @@ -26,6 +26,7 @@ import { Controller, getPages } from './controller'; import { Header } from '../header'; import Notices from './notices'; import TransientNotices from './transient-notices'; +import './navigation'; const StoreAlerts = lazy( () => import( /* webpackChunkName: "store-alerts" */ './store-alerts' ) diff --git a/plugins/woocommerce-admin/client/layout/navigation.js b/plugins/woocommerce-admin/client/layout/navigation.js new file mode 100644 index 00000000000..27f85ccd48f --- /dev/null +++ b/plugins/woocommerce-admin/client/layout/navigation.js @@ -0,0 +1,75 @@ +/** + * External dependencies + */ +import { registerPlugin } from '@wordpress/plugins'; +import { + WooNavigationItem, + getNewPath, + getPersistedQuery, +} from '@woocommerce/navigation'; +import { Link } from '@woocommerce/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import getReports from '../analytics/report/get-reports'; +import { getPages } from './controller'; +import { isWCAdmin } from '../dashboard/utils'; + +const NavigationPlugin = () => { + /** + * If the current page is embedded, stay with the default urls + * provided by Navigation because the router isn't present to + * respond to component's manipulation of the url. + */ + if ( ! isWCAdmin( window.location.href ) ) { + return null; + } + + const reports = getReports(); + const pages = getPages() + .filter( ( page ) => page.id ) + .map( ( page ) => { + if ( page.id === 'woocommerce-analytics-settings' ) { + return { + ...page, + breadcrumbs: [ __( 'Analytics', 'woocommerce-admin' ) ], + }; + } + return page; + } ); + const persistedQuery = getPersistedQuery( {} ); + return ( + <> + { pages.map( ( page ) => ( + + + { page.breadcrumbs[ page.breadcrumbs.length - 1 ] } + + + ) ) } + { reports.map( ( item ) => ( + + + { item.title } + + + ) ) } + + ); +}; + +registerPlugin( 'wc-admin-navigation', { render: NavigationPlugin } ); diff --git a/plugins/woocommerce-admin/client/navigation/components/Item/index.js b/plugins/woocommerce-admin/client/navigation/components/Item/index.js index 1b5ec27ea30..68dca0676e7 100644 --- a/plugins/woocommerce-admin/client/navigation/components/Item/index.js +++ b/plugins/woocommerce-admin/client/navigation/components/Item/index.js @@ -1,20 +1,22 @@ /** * External dependencies */ -import { - __experimentalNavigationItem as NavigationItem, - __experimentalUseSlot as useSlot, -} from '@wordpress/components'; -import { WooNavigationItem } from '@woocommerce/navigation'; +import { __experimentalNavigationItem as NavigationItem } from '@wordpress/components'; +import { WooNavigationItem, useNavSlot } from '@woocommerce/navigation'; const Item = ( { item } ) => { - const slot = useSlot( item.id ); + const slot = useNavSlot( 'woocommerce_navigation_' + item.id ); const hasFills = Boolean( slot.fills && slot.fills.length ); // Only render a slot if a coresponding Fill exists and the item is not a category if ( hasFills && ! item.isCategory ) { - return ; + return ( + + + + ); } + return ( ( - + - + ); const HydratedNavigation = withNavigationHydration( window.wcNavigation )( diff --git a/plugins/woocommerce-admin/packages/navigation/src/index.js b/plugins/woocommerce-admin/packages/navigation/src/index.js index 25c8c3c4526..46c0ab3f2f3 100644 --- a/plugins/woocommerce-admin/packages/navigation/src/index.js +++ b/plugins/woocommerce-admin/packages/navigation/src/index.js @@ -6,9 +6,10 @@ import { parse } from 'qs'; import { pick, uniq } from 'lodash'; import { applyFilters } from '@wordpress/hooks'; import { - __experimentalNavigationItem as NavigationItem, Slot, Fill, + SlotFillProvider, + __experimentalUseSlot as useSlot, } from '@wordpress/components'; /** @@ -170,13 +171,42 @@ export function updateQueryString( getHistory().push( newPath ); } -export const WooNavigationItem = ( { children, item, ...passProps } ) => { - return ( - - - { children } - - - ); +/** + * Create a Fill for extensions to add client facing custom Navigation Items. + * + * @param {Object} param0 + * @param {Array} param0.children - Node children. + * @param {string} param0.item - Navigation item slug. + */ +export const WooNavigationItem = ( { children, item } ) => { + return { children }; }; -WooNavigationItem.Slot = ( { name } ) => ; +WooNavigationItem.Slot = ( { name } ) => ( + +); + +/** + * Export @wordpress/components SlotFillProvider so that Slots, Fills, and useSlot + * have access to the same context. + * + * This is a workaround because components exported from this package do not have + * the same `context` as those created in the /client folder. This problem is due + * to WC Admin bundling @wordpress/components instead of enqueuing and using + * wp.components from the window. + * + * @param {Object} param0 + * @param {Array} param0.children - Node children. + */ +export const NavSlotFillProvider = ( { children } ) => ( + { children } +); + +/** + * Similar to NavSlotFillProvider above, this is a workaround because components + * exported from this package do not have the same `context` as those created + * in the /client folder. This problem is due to WC Admin bundling @wordpress/components + * instead of enqueuing and using wp.components from the window. + * + * @param {string} name - slot name. + */ +export const useNavSlot = ( name ) => useSlot( name ); diff --git a/plugins/woocommerce-admin/src/Features/Navigation/Navigation.php~HEAD b/plugins/woocommerce-admin/src/Features/Navigation/Navigation.php~HEAD deleted file mode 100644 index d4d944d0ce8..00000000000 --- a/plugins/woocommerce-admin/src/Features/Navigation/Navigation.php~HEAD +++ /dev/null @@ -1,72 +0,0 @@ -init(); - CoreMenu::instance()->init(); - Screen::instance()->init(); - } - } - - /** - * Overwrites the allowed features array using a local `feature-config.php` file. - * - * @param array $features Array of feature slugs. - */ - public function maybe_remove_nav_feature( $features ) { - if ( in_array( 'navigation', $features, true ) && 'yes' !== get_option( 'woocommerce_navigation_enabled', 'no' ) ) { - $features = array_diff( $features, array( 'navigation' ) ); - } - return $features; - } - - /** - * Preload options to prime state of the application. - * - * @param array $options Array of options to preload. - * @return array - */ - public function preload_options( $options ) { - $options[] = 'woocommerce_navigation_enabled'; - - return $options; - } - - /** - * Set up a div for the navigation. - * The initial contents here are meant as a place loader for when the PHP page initialy loads. - */ - public static function embed_navigation() { - if ( ! Screen::is_woocommerce_page() ) { - return; - } - - ?> -
-