Merge pull request woocommerce/woocommerce-admin#2280 from woocommerce/add/dashboard-section-example
Dashboard Extentions: Add a section
This commit is contained in:
commit
1c750474a0
|
@ -8,7 +8,7 @@ import { Component, Fragment } from '@wordpress/element';
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
import Gridicon from 'gridicons';
|
import Gridicon from 'gridicons';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { IconButton, NavigableMenu, SelectControl, TextControl } from '@wordpress/components';
|
import { IconButton, NavigableMenu, SelectControl } from '@wordpress/components';
|
||||||
import { withDispatch } from '@wordpress/data';
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,24 +79,16 @@ class DashboardCharts extends Component {
|
||||||
);
|
);
|
||||||
} ) }
|
} ) }
|
||||||
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
||||||
<Fragment>
|
<Controls
|
||||||
<div className="woocommerce-ellipsis-menu__item">
|
onToggle={ onToggle }
|
||||||
<TextControl
|
onMove={ onMove }
|
||||||
label={ __( 'Section Title', 'woocommerce-admin' ) }
|
onRemove={ onRemove }
|
||||||
onBlur={ onTitleBlur }
|
isFirst={ isFirst }
|
||||||
onChange={ onTitleChange }
|
isLast={ isLast }
|
||||||
required
|
onTitleBlur={ onTitleBlur }
|
||||||
value={ titleInput }
|
onTitleChange={ onTitleChange }
|
||||||
/>
|
titleInput={ titleInput }
|
||||||
</div>
|
/>
|
||||||
<Controls
|
|
||||||
onToggle={ onToggle }
|
|
||||||
onMove={ onMove }
|
|
||||||
onRemove={ onRemove }
|
|
||||||
isFirst={ isFirst }
|
|
||||||
isLast={ isLast }
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
) }
|
) }
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) }
|
) }
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { __ } from '@wordpress/i18n';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { SelectControl, TextControl } from '@wordpress/components';
|
import { SelectControl } from '@wordpress/components';
|
||||||
import { withDispatch } from '@wordpress/data';
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,24 +87,16 @@ class Leaderboards extends Component {
|
||||||
onChange={ this.setRowsPerTable }
|
onChange={ this.setRowsPerTable }
|
||||||
/>
|
/>
|
||||||
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
||||||
<Fragment>
|
<Controls
|
||||||
<div className="woocommerce-ellipsis-menu__item">
|
onToggle={ onToggle }
|
||||||
<TextControl
|
onMove={ onMove }
|
||||||
label={ __( 'Section Title', 'woocommerce-admin' ) }
|
onRemove={ onRemove }
|
||||||
onBlur={ onTitleBlur }
|
isFirst={ isFirst }
|
||||||
onChange={ onTitleChange }
|
isLast={ isLast }
|
||||||
required
|
onTitleBlur={ onTitleBlur }
|
||||||
value={ titleInput }
|
onTitleChange={ onTitleChange }
|
||||||
/>
|
titleInput={ titleInput }
|
||||||
</div>
|
/>
|
||||||
<Controls
|
|
||||||
onToggle={ onToggle }
|
|
||||||
onMove={ onMove }
|
|
||||||
onRemove={ onRemove }
|
|
||||||
isFirst={ isFirst }
|
|
||||||
isLast={ isLast }
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
) }
|
) }
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) }
|
) }
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon } from '@wordpress/components';
|
import { Icon, TextControl } from '@wordpress/components';
|
||||||
import { Component } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -33,27 +33,38 @@ class SectionControls extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onRemove, isFirst, isLast } = this.props;
|
const { onRemove, isFirst, isLast, onTitleBlur, onTitleChange, titleInput } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woocommerce-dashboard-section-controls">
|
<Fragment>
|
||||||
{ ! isFirst && (
|
<div className="woocommerce-ellipsis-menu__item">
|
||||||
<MenuItem isClickable onInvoke={ this.onMoveUp }>
|
<TextControl
|
||||||
<Icon icon={ 'arrow-up-alt2' } label={ __( 'Move up' ) } />
|
label={ __( 'Section Title', 'woocommerce-admin' ) }
|
||||||
{ __( 'Move up', 'woocommerce-admin' ) }
|
onBlur={ onTitleBlur }
|
||||||
|
onChange={ onTitleChange }
|
||||||
|
required
|
||||||
|
value={ titleInput }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="woocommerce-dashboard-section-controls">
|
||||||
|
{ ! isFirst && (
|
||||||
|
<MenuItem isClickable onInvoke={ this.onMoveUp }>
|
||||||
|
<Icon icon={ 'arrow-up-alt2' } label={ __( 'Move up' ) } />
|
||||||
|
{ __( 'Move up', 'woocommerce-admin' ) }
|
||||||
|
</MenuItem>
|
||||||
|
) }
|
||||||
|
{ ! isLast && (
|
||||||
|
<MenuItem isClickable onInvoke={ this.onMoveDown }>
|
||||||
|
<Icon icon={ 'arrow-down-alt2' } label={ __( 'Move Down' ) } />
|
||||||
|
{ __( 'Move Down', 'woocommerce-admin' ) }
|
||||||
|
</MenuItem>
|
||||||
|
) }
|
||||||
|
<MenuItem isClickable onInvoke={ onRemove }>
|
||||||
|
<Icon icon={ 'trash' } label={ __( 'Remove block' ) } />
|
||||||
|
{ __( 'Remove section', 'woocommerce-admin' ) }
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) }
|
</div>
|
||||||
{ ! isLast && (
|
</Fragment>
|
||||||
<MenuItem isClickable onInvoke={ this.onMoveDown }>
|
|
||||||
<Icon icon={ 'arrow-down-alt2' } label={ __( 'Move Down' ) } />
|
|
||||||
{ __( 'Move Down', 'woocommerce-admin' ) }
|
|
||||||
</MenuItem>
|
|
||||||
) }
|
|
||||||
<MenuItem isClickable onInvoke={ onRemove }>
|
|
||||||
<Icon icon={ 'trash' } label={ __( 'Remove block' ) } />
|
|
||||||
{ __( 'Remove section', 'woocommerce-admin' ) }
|
|
||||||
</MenuItem>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { compose } from '@wordpress/compose';
|
||||||
import { withDispatch } from '@wordpress/data';
|
import { withDispatch } from '@wordpress/data';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { find } from 'lodash';
|
import { find } from 'lodash';
|
||||||
import { TextControl } from '@wordpress/components';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WooCommerce dependencies
|
* WooCommerce dependencies
|
||||||
|
@ -73,24 +72,16 @@ class StorePerformance extends Component {
|
||||||
);
|
);
|
||||||
} ) }
|
} ) }
|
||||||
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
{ window.wcAdminFeatures[ 'analytics-dashboard/customizable' ] && (
|
||||||
<Fragment>
|
<Controls
|
||||||
<div className="woocommerce-ellipsis-menu__item">
|
onToggle={ onToggle }
|
||||||
<TextControl
|
onMove={ onMove }
|
||||||
label={ __( 'Section Title', 'woocommerce-admin' ) }
|
onRemove={ onRemove }
|
||||||
onBlur={ onTitleBlur }
|
isFirst={ isFirst }
|
||||||
onChange={ onTitleChange }
|
isLast={ isLast }
|
||||||
required
|
onTitleBlur={ onTitleBlur }
|
||||||
value={ titleInput }
|
onTitleChange={ onTitleChange }
|
||||||
/>
|
titleInput={ titleInput }
|
||||||
</div>
|
/>
|
||||||
<Controls
|
|
||||||
onToggle={ onToggle }
|
|
||||||
onMove={ onMove }
|
|
||||||
onRemove={ onRemove }
|
|
||||||
isFirst={ isFirst }
|
|
||||||
isLast={ isLast }
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
) }
|
) }
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) }
|
) }
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { Card, Chart } from '@woocommerce/components';
|
||||||
|
import { formatCurrency } from '@woocommerce/currency';
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
|
||||||
|
for ( let i = 1; i <= 20; i++ ) {
|
||||||
|
const date = moment().subtract( i, 'days' );
|
||||||
|
data.push( {
|
||||||
|
date: date.format( 'YYYY-MM-DDT00:00:00' ),
|
||||||
|
primary: {
|
||||||
|
label: 'Global Apple Prices, last 20 days',
|
||||||
|
labelDate: date.format( 'YYYY-MM-DD 00:00:00' ),
|
||||||
|
value: Math.floor( Math.random() * 100 ),
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
const GlobalPrices = () => {
|
||||||
|
return (
|
||||||
|
<Card className="woocommerce-dashboard__chart-block woocommerce-analytics__card" title="Global Apple Prices">
|
||||||
|
<Chart
|
||||||
|
title="Global Apple Prices"
|
||||||
|
interval="day"
|
||||||
|
data={ data.reverse() }
|
||||||
|
dateParser="%Y-%m-%dT%H:%M:%S"
|
||||||
|
showHeaderControls={ false }
|
||||||
|
valueType={ 'currency' }
|
||||||
|
tooltipValueFormat={ formatCurrency }
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GlobalPrices;
|
|
@ -0,0 +1,126 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { addFilter } from '@wordpress/hooks';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { EllipsisMenu, MenuTitle, MenuItem, SectionHeader } from '@woocommerce/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import UpcomingEvents from './upcoming-events';
|
||||||
|
import GlobalPrices from './global-prices';
|
||||||
|
|
||||||
|
const config = [
|
||||||
|
{
|
||||||
|
title: 'Granny Smith',
|
||||||
|
events: [ { date: '2020-01-02', title: 'The Granny Apple Fair' } ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Golden Delicious',
|
||||||
|
events: [
|
||||||
|
{ date: '2020-04-15', title: 'Madrid Manzana Dorada' },
|
||||||
|
{ date: '2020-05-07', title: 'Golden CO Golden Delicious Day' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Gala',
|
||||||
|
events: [ { date: '2020-08-31', title: 'The Met Gala Pomme' } ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Braeburn',
|
||||||
|
events: [ { date: '2020-08-18', title: 'Mt. Aoraki Crisper' } ],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dashboardItems = [
|
||||||
|
{ title: 'Upcoming Events', component: UpcomingEvents, key: 'upcoming-events' },
|
||||||
|
{ title: 'Global Apple Prices', component: GlobalPrices, key: 'global-prices' },
|
||||||
|
];
|
||||||
|
|
||||||
|
class Section extends Component {
|
||||||
|
renderMenu() {
|
||||||
|
const {
|
||||||
|
hiddenBlocks,
|
||||||
|
onToggleHiddenBlock,
|
||||||
|
onTitleBlur,
|
||||||
|
onTitleChange,
|
||||||
|
titleInput,
|
||||||
|
onMove,
|
||||||
|
onRemove,
|
||||||
|
isFirst,
|
||||||
|
isLast,
|
||||||
|
controls: Controls,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EllipsisMenu
|
||||||
|
label={ __( 'Choose Apples', 'woocommerce-admin' ) }
|
||||||
|
renderContent={ ( { onToggle } ) => (
|
||||||
|
<Fragment>
|
||||||
|
<MenuTitle>{ __( 'My Apples', 'woocommerce-admin' ) }</MenuTitle>
|
||||||
|
{ dashboardItems.map( item => (
|
||||||
|
<MenuItem
|
||||||
|
checked={ ! hiddenBlocks.includes( item.key ) }
|
||||||
|
isCheckbox
|
||||||
|
isClickable
|
||||||
|
key={ item.key }
|
||||||
|
onInvoke={ () => onToggleHiddenBlock( item.key )() }
|
||||||
|
>
|
||||||
|
{ item.title }
|
||||||
|
</MenuItem>
|
||||||
|
) ) }
|
||||||
|
<Controls
|
||||||
|
onToggle={ onToggle }
|
||||||
|
onMove={ onMove }
|
||||||
|
onRemove={ onRemove }
|
||||||
|
isFirst={ isFirst }
|
||||||
|
isLast={ isLast }
|
||||||
|
onTitleBlur={ onTitleBlur }
|
||||||
|
onTitleChange={ onTitleChange }
|
||||||
|
titleInput={ titleInput }
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
) }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { title, hiddenBlocks } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<SectionHeader title={ title } menu={ this.renderMenu() } />
|
||||||
|
<div className="woocommerce-dashboard__columns">
|
||||||
|
{ dashboardItems.map( item => {
|
||||||
|
return hiddenBlocks.includes( item.key ) ? null : (
|
||||||
|
<item.component key={ item.key } config={ config } />
|
||||||
|
);
|
||||||
|
} ) }
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addFilter( 'woocommerce_dashboard_default_sections', 'plugin-domain', sections => {
|
||||||
|
return [
|
||||||
|
...sections,
|
||||||
|
{
|
||||||
|
key: 'dashboard-apples',
|
||||||
|
component: Section,
|
||||||
|
title: __( 'Apples', 'woocommerce-admin' ),
|
||||||
|
isVisible: true,
|
||||||
|
icon: 'carrot',
|
||||||
|
hiddenBlocks: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} );
|
|
@ -0,0 +1,39 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { flatten } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { TableCard } from '@woocommerce/components';
|
||||||
|
|
||||||
|
const UpcomingEvents = ( { config } ) => {
|
||||||
|
const rows = flatten(
|
||||||
|
config.map( apple => {
|
||||||
|
return apple.events.map( event => {
|
||||||
|
return [
|
||||||
|
{ display: apple.title, value: 'variety' },
|
||||||
|
{ display: event.title, value: 'event' },
|
||||||
|
{ display: event.date, value: 'date' },
|
||||||
|
];
|
||||||
|
} );
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<TableCard
|
||||||
|
title={ 'Upcoming Events' }
|
||||||
|
headers={ [
|
||||||
|
{ label: 'Variety', key: 'variety' },
|
||||||
|
{ label: 'Event', key: 'event' },
|
||||||
|
{ label: 'Date', key: 'date' },
|
||||||
|
] }
|
||||||
|
rows={ rows }
|
||||||
|
rowsPerPage={ 100 }
|
||||||
|
totalRows={ 1 }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpcomingEvents;
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: WooCommerce Admin Dashboard Section Example
|
||||||
|
*
|
||||||
|
* @package WC_Admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the JS.
|
||||||
|
*/
|
||||||
|
function dashboard_section_register_script() {
|
||||||
|
|
||||||
|
if ( ! class_exists( 'WC_Admin_Loader' ) || ! WC_Admin_Loader::is_admin_page() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_register_script(
|
||||||
|
'add-report',
|
||||||
|
plugins_url( '/dist/index.js', __FILE__ ),
|
||||||
|
array(
|
||||||
|
'wp-hooks',
|
||||||
|
'wp-element',
|
||||||
|
'wp-i18n',
|
||||||
|
'wc-components',
|
||||||
|
),
|
||||||
|
filemtime( dirname( __FILE__ ) . '/dist/index.js' ),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_enqueue_script( 'add-report' );
|
||||||
|
}
|
||||||
|
add_action( 'admin_enqueue_scripts', 'dashboard_section_register_script' );
|
|
@ -6,6 +6,7 @@ const path = require( 'path' );
|
||||||
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
|
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
|
||||||
const fs = require( 'fs' );
|
const fs = require( 'fs' );
|
||||||
const woocommerceAdminConfig = require( path.resolve( __dirname, '../../../webpack.config.js' ) );
|
const woocommerceAdminConfig = require( path.resolve( __dirname, '../../../webpack.config.js' ) );
|
||||||
|
const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );
|
||||||
|
|
||||||
const extArg = process.argv.find( arg => arg.startsWith( '--ext=' ) );
|
const extArg = process.argv.find( arg => arg.startsWith( '--ext=' ) );
|
||||||
|
|
||||||
|
@ -55,6 +56,13 @@ const webpackConfig = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.s?css$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
'css-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
@ -73,6 +81,9 @@ const webpackConfig = {
|
||||||
to: path.resolve( __dirname, `../../../../${ extension }/` ),
|
to: path.resolve( __dirname, `../../../../${ extension }/` ),
|
||||||
},
|
},
|
||||||
] ),
|
] ),
|
||||||
|
new MiniCssExtractPlugin( {
|
||||||
|
filename: '[name]/dist/style.css',
|
||||||
|
} ),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue