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 <Header> 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
This commit is contained in:
parent
d640b15d09
commit
08417da553
|
@ -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 (
|
||||
<Fragment>
|
||||
<Header sections={ [ __( 'Analytics', 'woocommerce-admin' ) ] } />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 (
|
||||
<Fragment>
|
||||
<Header
|
||||
sections={ [
|
||||
[ '/analytics/revenue', __( 'Analytics', 'woocommerce-admin' ) ],
|
||||
report.title,
|
||||
] }
|
||||
/>
|
||||
<Container { ...this.props } />
|
||||
</Fragment>
|
||||
);
|
||||
return <Container { ...this.props } />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<Fragment>
|
||||
<Header
|
||||
sections={ [
|
||||
[ '/analytics/revenue', __( 'Analytics', 'woocommerce-admin' ) ],
|
||||
__( 'Settings', 'woocommerce-admin' ),
|
||||
] }
|
||||
/>
|
||||
<SectionHeader title={ __( 'Analytics Settings', 'woocommerce-admin' ) } />
|
||||
<div className="woocommerce-settings__wrapper">
|
||||
{ analyticsSettings.map( setting => (
|
||||
|
|
|
@ -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 {
|
|||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Header sections={ [ __( 'Dashboard', 'woocommerce-admin' ) ] } />
|
||||
{ this.renderDashboardOutput() }
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
|
|
|
@ -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 (
|
||||
<div className={ className }>
|
||||
<Header sections={ breadcrumbs } />
|
||||
{ exampleList.map( example => {
|
||||
const { componentName, filePath, render, docPath } = getExampleData( example );
|
||||
const cardClasses = classnames(
|
||||
|
|
|
@ -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(
|
||||
<SlotFillProvider>
|
||||
<EmbedLayout />
|
||||
</SlotFillProvider>,
|
||||
embeddedRoot
|
||||
);
|
||||
render( <EmbedLayout />, embeddedRoot );
|
||||
|
||||
embeddedRoot.classList.remove( 'is-embed-loading' );
|
||||
|
||||
|
|
|
@ -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 `<Layout />`. 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:
|
||||
|
||||
|
|
|
@ -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 {
|
|||
<div className={ className }>
|
||||
<h1 className="woocommerce-layout__header-breadcrumbs">
|
||||
<span>
|
||||
<Link href="/">WooCommerce</Link>
|
||||
<Link href={ 'admin.php?page=wc-admin' } type={ isEmbedded ? 'wp-admin' : 'wc-admin' }>
|
||||
WooCommerce
|
||||
</Link>
|
||||
</span>
|
||||
{ _sections.map( ( section, i ) => {
|
||||
const sectionPiece = Array.isArray( section ) ? (
|
||||
<Link
|
||||
href={ getNewPath( {}, section[ 0 ], {} ) }
|
||||
href={ isEmbedded ? section[ 0 ] : getNewPath( {}, section[ 0 ], {} ) }
|
||||
type={ isEmbedded ? 'wp-admin' : 'wc-admin' }
|
||||
>
|
||||
{ section[ 1 ] }
|
||||
|
@ -115,10 +116,4 @@ Header.defaultProps = {
|
|||
isEmbedded: false,
|
||||
};
|
||||
|
||||
export default function( props ) {
|
||||
return (
|
||||
<Fill name="header">
|
||||
<Header { ...props } />
|
||||
</Fill>
|
||||
);
|
||||
}
|
||||
export default Header;
|
||||
|
|
|
@ -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(
|
||||
<SlotFillProvider>
|
||||
<PageLayout />
|
||||
</SlotFillProvider>,
|
||||
document.getElementById( 'root' )
|
||||
);
|
||||
render( <PageLayout />, document.getElementById( 'root' ) );
|
||||
|
|
|
@ -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',
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<div className="woocommerce-layout">
|
||||
<Slot name="header" />
|
||||
<Header
|
||||
sections={ isFunction( breadcrumbs ) ? breadcrumbs( this.props ) : breadcrumbs }
|
||||
isEmbedded={ isEmbedded }
|
||||
/>
|
||||
<TransientNotices />
|
||||
{ ! isEmbedded && (
|
||||
<PrimaryLayout>
|
||||
|
@ -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 (
|
||||
<Fragment>
|
||||
<Header sections={ wcSettings.embedBreadcrumbs } isEmbedded />
|
||||
<Layout isEmbedded />
|
||||
</Fragment>
|
||||
<Layout
|
||||
page={ {
|
||||
breadcrumbs: wcSettings.embedBreadcrumbs,
|
||||
} }
|
||||
isEmbedded
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue