From 08417da55312af1b73e043a09d85542ed66fc8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rey=20L=C3=B3pez?= Date: Fri, 5 Jul 2019 09:15:49 +0100 Subject: [PATCH] Allow each page to specify their breadcrumbs without having to render the Header component (https://github.com/woocommerce/woocommerce-admin/pull/2491) * Simplify Header rendering, remove the use of react-slot-fill * Remove the useless "/analytics" route * Move all the
renders to the new, declarative way of specifying breadcrumbs * Re-render the Layout when a report is added using the REPORTS_FILTER, since that affects the breadcrumbs output * Fix the base breadcrumb link and breadcrumbs on embedded pages * Expanded Layout.propTypes to specify the breadcrumbs' shape --- .../client/analytics/index.js | 33 ----------------- .../client/analytics/report/index.js | 19 ++-------- .../client/analytics/settings/index.js | 7 ---- .../client/dashboard/index.js | 13 +------ .../woocommerce-admin/client/devdocs/index.js | 4 -- plugins/woocommerce-admin/client/embedded.js | 8 +--- .../woocommerce-admin/client/header/README.md | 2 +- .../woocommerce-admin/client/header/index.js | 15 +++----- plugins/woocommerce-admin/client/index.js | 8 +--- .../client/layout/controller.js | 25 +++++++++---- .../woocommerce-admin/client/layout/index.js | 37 ++++++++++++++----- plugins/woocommerce-admin/package-lock.json | 5 --- plugins/woocommerce-admin/package.json | 1 - 13 files changed, 57 insertions(+), 120 deletions(-) delete mode 100644 plugins/woocommerce-admin/client/analytics/index.js diff --git a/plugins/woocommerce-admin/client/analytics/index.js b/plugins/woocommerce-admin/client/analytics/index.js deleted file mode 100644 index 5c4d9ef64b9..00000000000 --- a/plugins/woocommerce-admin/client/analytics/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** @format */ -/** - * External dependencies - */ -import { __ } from '@wordpress/i18n'; -import { Component, Fragment } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import Header from 'header'; - -export default class extends Component { - constructor( props ) { - super( props ); - this.state = { - selected: [], - }; - this.onChange = this.onChange.bind( this ); - } - - onChange( selected ) { - this.setState( { selected } ); - } - - render() { - return ( - -
- - ); - } -} diff --git a/plugins/woocommerce-admin/client/analytics/report/index.js b/plugins/woocommerce-admin/client/analytics/report/index.js index 0262f19c3b4..96ba6d3e18e 100644 --- a/plugins/woocommerce-admin/client/analytics/report/index.js +++ b/plugins/woocommerce-admin/client/analytics/report/index.js @@ -4,7 +4,7 @@ */ import { __ } from '@wordpress/i18n'; import { applyFilters } from '@wordpress/hooks'; -import { Component, Fragment } from '@wordpress/element'; +import { Component } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import PropTypes from 'prop-types'; import { find } from 'lodash'; @@ -19,7 +19,6 @@ import { getQuery, getSearchWords } from '@woocommerce/navigation'; * Internal dependencies */ import './style.scss'; -import Header from 'header'; import OrdersReport from './orders'; import ProductsReport from './products'; import RevenueReport from './revenue'; @@ -33,9 +32,9 @@ import ReportError from 'analytics/components/report-error'; import { searchItemsByString } from 'wc-api/items/utils'; import withSelect from 'wc-api/with-select'; -const REPORTS_FILTER = 'woocommerce_admin_reports_list'; +export const REPORTS_FILTER = 'woocommerce_admin_reports_list'; -const getReports = () => { +export const getReports = () => { const reports = [ { report: 'revenue', @@ -128,17 +127,7 @@ class Report extends Component { return null; } const Container = report.component; - return ( - -
- - - ); + return ; } } diff --git a/plugins/woocommerce-admin/client/analytics/settings/index.js b/plugins/woocommerce-admin/client/analytics/settings/index.js index 9e198d89c21..44fe86a56a8 100644 --- a/plugins/woocommerce-admin/client/analytics/settings/index.js +++ b/plugins/woocommerce-admin/client/analytics/settings/index.js @@ -19,7 +19,6 @@ import { SectionHeader, useFilters } from '@woocommerce/components'; */ import './index.scss'; import { analyticsSettings } from './config'; -import Header from 'header'; import Setting from './setting'; import HistoricalData from './historical-data'; import withSelect from 'wc-api/with-select'; @@ -160,12 +159,6 @@ class Settings extends Component { return ( -
{ analyticsSettings.map( setting => ( diff --git a/plugins/woocommerce-admin/client/dashboard/index.js b/plugins/woocommerce-admin/client/dashboard/index.js index eaa724ecc03..4cf422f646f 100644 --- a/plugins/woocommerce-admin/client/dashboard/index.js +++ b/plugins/woocommerce-admin/client/dashboard/index.js @@ -2,7 +2,6 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; import { Component, Fragment } from '@wordpress/element'; import { compose } from '@wordpress/compose'; @@ -12,7 +11,6 @@ import { compose } from '@wordpress/compose'; import './style.scss'; import CustomizableDashboard from './customizable'; import DashboardCharts from './dashboard-charts'; -import Header from 'header'; import Leaderboards from './leaderboards'; import { ReportFilters } from '@woocommerce/components'; import StorePerformance from './store-performance'; @@ -21,7 +19,7 @@ import ProfileWizard from './profile-wizard'; import withSelect from 'wc-api/with-select'; class Dashboard extends Component { - renderDashboardOutput() { + render() { const { path, profileItems, query } = this.props; if ( window.wcAdminFeatures.onboarding && ! profileItems.skipped && ! profileItems.completed ) { @@ -49,15 +47,6 @@ class Dashboard extends Component { ); } - - render() { - return ( - -
- { this.renderDashboardOutput() } - - ); - } } export default compose( diff --git a/plugins/woocommerce-admin/client/devdocs/index.js b/plugins/woocommerce-admin/client/devdocs/index.js index c421f208354..a6aadf883fc 100755 --- a/plugins/woocommerce-admin/client/devdocs/index.js +++ b/plugins/woocommerce-admin/client/devdocs/index.js @@ -13,7 +13,6 @@ import ComponentExample from './example'; import ComponentDocs from './docs'; import { Card, Link } from '@woocommerce/components'; import examples from './examples.json'; -import Header from 'header'; import './style.scss'; const camelCaseToSlug = name => { @@ -41,16 +40,13 @@ export default class extends Component { } ); let exampleList = examples; - let breadcrumbs = [ 'Documentation' ]; if ( component ) { const example = find( examples, ex => component === camelCaseToSlug( ex.component ) ); - breadcrumbs = [ [ '/devdocs', 'Documentation' ], example.component ]; exampleList = [ example ]; } return (
-
{ exampleList.map( example => { const { componentName, filePath, render, docPath } = getExampleData( example ); const cardClasses = classnames( diff --git a/plugins/woocommerce-admin/client/embedded.js b/plugins/woocommerce-admin/client/embedded.js index 00adc961403..a5a75525bf5 100644 --- a/plugins/woocommerce-admin/client/embedded.js +++ b/plugins/woocommerce-admin/client/embedded.js @@ -3,7 +3,6 @@ * External dependencies */ import { render } from '@wordpress/element'; -import { Provider as SlotFillProvider } from 'react-slot-fill'; /** * Internal dependencies @@ -16,12 +15,7 @@ import 'wc-api/wp-data-store'; const embeddedRoot = document.getElementById( 'woocommerce-embedded-root' ); // Render the header. -render( - - - , - embeddedRoot -); +render( , embeddedRoot ); embeddedRoot.classList.remove( 'is-embed-loading' ); diff --git a/plugins/woocommerce-admin/client/header/README.md b/plugins/woocommerce-admin/client/header/README.md index 2fa3e28ad61..4188356b480 100644 --- a/plugins/woocommerce-admin/client/header/README.md +++ b/plugins/woocommerce-admin/client/header/README.md @@ -1,7 +1,7 @@ Header ====== -A basic component for the app header. The header outputs breadcrumbs via the `sections` prop (required) and access to the activity panel. It also sets the document title. The Header component used in each section automatically fills into the "header" slot defined in ``. We're using [react-slot-fill](https://github.com/camwest/react-slot-fill) to avoid a duplicated `div` wrapper from Gutenberg's implementation. +A basic component for the app header. The header outputs breadcrumbs via the `sections` prop (required) and access to the activity panel. It also sets the document title. ## How to use: diff --git a/plugins/woocommerce-admin/client/header/index.js b/plugins/woocommerce-admin/client/header/index.js index d57c1781fc5..6e3762fb79f 100644 --- a/plugins/woocommerce-admin/client/header/index.js +++ b/plugins/woocommerce-admin/client/header/index.js @@ -6,7 +6,6 @@ import { __, sprintf } from '@wordpress/i18n'; import { Component, findDOMNode } from '@wordpress/element'; import classnames from 'classnames'; import { decodeEntities } from '@wordpress/html-entities'; -import { Fill } from 'react-slot-fill'; import PropTypes from 'prop-types'; /** @@ -84,12 +83,14 @@ class Header extends Component {

- WooCommerce + + WooCommerce + { _sections.map( ( section, i ) => { const sectionPiece = Array.isArray( section ) ? ( { section[ 1 ] } @@ -115,10 +116,4 @@ Header.defaultProps = { isEmbedded: false, }; -export default function( props ) { - return ( - -
- - ); -} +export default Header; diff --git a/plugins/woocommerce-admin/client/index.js b/plugins/woocommerce-admin/client/index.js index f57ca37af21..b5d548d7925 100644 --- a/plugins/woocommerce-admin/client/index.js +++ b/plugins/woocommerce-admin/client/index.js @@ -3,7 +3,6 @@ * External dependencies */ import { render } from '@wordpress/element'; -import { Provider as SlotFillProvider } from 'react-slot-fill'; /** * Internal dependencies @@ -13,9 +12,4 @@ import { PageLayout } from './layout'; import 'store'; import 'wc-api/wp-data-store'; -render( - - - , - document.getElementById( 'root' ) -); +render( , document.getElementById( 'root' ) ); diff --git a/plugins/woocommerce-admin/client/layout/controller.js b/plugins/woocommerce-admin/client/layout/controller.js index e42c266583b..f1ad44e3b7f 100644 --- a/plugins/woocommerce-admin/client/layout/controller.js +++ b/plugins/woocommerce-admin/client/layout/controller.js @@ -4,8 +4,9 @@ */ import { Component, createElement } from '@wordpress/element'; import { parse, stringify } from 'qs'; -import { isEqual, last } from 'lodash'; +import { find, isEqual, last } from 'lodash'; import { applyFilters } from '@wordpress/hooks'; +import { __ } from '@wordpress/i18n'; /** * WooCommerce dependencies @@ -15,8 +16,7 @@ import { getNewPath, getPersistedQuery, getHistory } from '@woocommerce/navigati /** * Internal dependencies */ -import Analytics from 'analytics'; -import AnalyticsReport from 'analytics/report'; +import AnalyticsReport, { getReports } from 'analytics/report'; import AnalyticsSettings from 'analytics/settings'; import Dashboard from 'dashboard'; import DevDocs from 'devdocs'; @@ -32,11 +32,13 @@ export const getPages = () => { pages.push( { container: DevDocs, path: '/devdocs', + breadcrumbs: [ 'Documentation' ], wpOpenMenu: 'toplevel_page_woocommerce', } ); pages.push( { container: DevDocs, path: '/devdocs/:component', + breadcrumbs: ( { match } ) => [ [ '/devdocs', 'Documentation' ], match.params.component ], wpOpenMenu: 'toplevel_page_woocommerce', } ); } @@ -45,24 +47,31 @@ export const getPages = () => { pages.push( { container: Dashboard, path: '/', + breadcrumbs: [ __( 'Dashboard', 'woocommerce-admin' ) ], wpOpenMenu: 'toplevel_page_woocommerce', } ); } if ( window.wcAdminFeatures.analytics ) { - pages.push( { - container: Analytics, - path: '/analytics', - wpOpenMenu: 'toplevel_page_wc-admin-path--analytics-revenue', - } ); pages.push( { container: AnalyticsSettings, path: '/analytics/settings', + breadcrumbs: [ + [ '/analytics/revenue', __( 'Analytics', 'woocommerce-admin' ) ], + __( 'Settings', 'woocommerce-admin' ), + ], wpOpenMenu: 'toplevel_page_wc-admin-path--analytics-revenue', } ); pages.push( { container: AnalyticsReport, path: '/analytics/:report', + breadcrumbs: ( { match } ) => { + const report = find( getReports(), { report: match.params.report } ); + if ( ! report ) { + return []; + } + return [ [ '/analytics/revenue', __( 'Analytics', 'woocommerce-admin' ) ], report.title ]; + }, wpOpenMenu: 'toplevel_page_wc-admin-path--analytics-revenue', } ); } diff --git a/plugins/woocommerce-admin/client/layout/index.js b/plugins/woocommerce-admin/client/layout/index.js index 7a2a0282fd6..277e160b3c3 100644 --- a/plugins/woocommerce-admin/client/layout/index.js +++ b/plugins/woocommerce-admin/client/layout/index.js @@ -2,12 +2,11 @@ /** * External dependencies */ -import { Component, Fragment } from '@wordpress/element'; +import { Component } from '@wordpress/element'; import { useFilters } from '@woocommerce/components'; import { Router, Route, Switch } from 'react-router-dom'; -import { Slot } from 'react-slot-fill'; import PropTypes from 'prop-types'; -import { get } from 'lodash'; +import { get, isFunction } from 'lodash'; /** * WooCommerce dependencies @@ -24,6 +23,7 @@ import Notices from './notices'; import { recordPageView } from 'lib/tracks'; import TransientNotices from './transient-notices'; import StoreAlerts from './store-alerts'; +import { REPORTS_FILTER } from 'analytics/report'; export class PrimaryLayout extends Component { render() { @@ -76,9 +76,13 @@ class Layout extends Component { render() { const { isEmbedded, ...restProps } = this.props; + const { breadcrumbs } = this.props.page; return (
- +
{ ! isEmbedded && ( @@ -94,6 +98,17 @@ class Layout extends Component { Layout.propTypes = { isEmbedded: PropTypes.bool, + page: PropTypes.shape( { + container: PropTypes.func.isRequired, + path: PropTypes.string.isRequired, + breadcrumbs: PropTypes.oneOfType( [ + PropTypes.func, + PropTypes.arrayOf( + PropTypes.oneOfType( [ PropTypes.arrayOf( PropTypes.string ), PropTypes.string ] ) + ), + ] ).isRequired, + wpOpenMenu: PropTypes.string.isRequired, + } ).isRequired, }; class _PageLayout extends Component { @@ -116,16 +131,18 @@ class _PageLayout extends Component { ); } } -// Use the useFilters HoC so PageLayout is re-rendered when the filter is used to add new pages -export const PageLayout = useFilters( PAGES_FILTER )( _PageLayout ); +// Use the useFilters HoC so PageLayout is re-rendered when filters are used to add new pages or reports +export const PageLayout = useFilters( [ PAGES_FILTER, REPORTS_FILTER ] )( _PageLayout ); export class EmbedLayout extends Component { render() { return ( - -
- - + ); } } diff --git a/plugins/woocommerce-admin/package-lock.json b/plugins/woocommerce-admin/package-lock.json index f3a75c67dd2..30b86b46ad0 100644 --- a/plugins/woocommerce-admin/package-lock.json +++ b/plugins/woocommerce-admin/package-lock.json @@ -18349,11 +18349,6 @@ "tiny-warning": "^1.0.0" } }, - "react-slot-fill": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-slot-fill/-/react-slot-fill-2.0.1.tgz", - "integrity": "sha512-jZzE+vbYAblPXSPFlir+aL5ljxwB0dJ62O2pR74OS/TUVDTW95msrdszJKLNp4lxzNcoHnCRIzLT6crBgTolGg==" - }, "react-test-renderer": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", diff --git a/plugins/woocommerce-admin/package.json b/plugins/woocommerce-admin/package.json index 61b1e139d43..eade92b3946 100644 --- a/plugins/woocommerce-admin/package.json +++ b/plugins/woocommerce-admin/package.json @@ -104,7 +104,6 @@ "react-dates": "17.2.0", "react-live": "1.12.0", "react-router-dom": "5.0.1", - "react-slot-fill": "2.0.1", "react-transition-group": "2.9.0", "redux": "4.0.1" },