* Homwpage: add stats overview stat toggle

* toggle stats

* fix

* tests

* better hook name

* clean up

* add back in Fragment

* remove extra prop

* better test name
This commit is contained in:
Paul Sealock 2020-05-05 10:58:39 +12:00 committed by GitHub
parent 915ed69add
commit 3fc69b9739
9 changed files with 241 additions and 20 deletions

View File

@ -39,7 +39,7 @@ import { recordEvent } from 'lib/tracks';
import { CurrencyContext } from 'lib/currency-context';
const { performanceIndicators: indicators } = getSetting( 'dataEndpoints', {
performanceIndicators: '',
performanceIndicators: [],
} );
class StorePerformance extends Component {

View File

@ -14,6 +14,7 @@ import { Spinner } from '@woocommerce/components';
*/
import withSelect from 'wc-api/with-select';
import { isOnboardingEnabled } from 'dashboard/utils';
import StatsOverview from './stats-overview';
const ProfileWizard = lazy( () =>
import( /* webpackChunkName: "profile-wizard" */ '../profile-wizard' )
@ -28,7 +29,7 @@ const Homepage = ( { profileItems, query } ) => {
);
}
return <div>Hello World</div>;
return <StatsOverview />;
};
export default compose(

View File

@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { applyFilters } from '@wordpress/hooks';
export const DEFAULT_STATS = applyFilters(
'woocommerce_admin_homepage_default_stats',
[
'revenue/total_sales',
'revenue/net_revenue',
'orders/orders_count',
'products/items_sold',
]
);
export const DEFAULT_HIDDEN_STATS = [
'revenue/net_revenue',
'products/items_sold',
];

View File

@ -0,0 +1,123 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/compose';
import { Fragment } from '@wordpress/element';
import { xor } from 'lodash';
import { withDispatch } from '@wordpress/data';
import PropTypes from 'prop-types';
import { recordEvent } from 'lib/tracks';
/**
* WooCommerce dependencies
*/
import {
Card,
EllipsisMenu,
MenuItem,
MenuTitle,
} from '@woocommerce/components';
import { getSetting } from '@woocommerce/wc-admin-settings';
/**
* Internal dependencies
*/
import withSelect from 'wc-api/with-select';
import { DEFAULT_STATS, DEFAULT_HIDDEN_STATS } from './defaults';
const { performanceIndicators } = getSetting( 'dataEndpoints', {
performanceIndicators: [],
} );
const stats = performanceIndicators.filter( ( indicator ) => {
return DEFAULT_STATS.includes( indicator.stat );
} );
export const StatsOverview = ( { userPrefs, updateCurrentUserData } ) => {
const userHiddenStats = userPrefs.hiddenStats;
const hiddenStats = userHiddenStats
? userHiddenStats
: DEFAULT_HIDDEN_STATS;
const toggleStat = ( stat ) => {
const nextHiddenStats = xor( hiddenStats, [ stat ] );
updateCurrentUserData( {
homepage_stats: { hiddenStats: nextHiddenStats },
} );
recordEvent( 'statsoverview_indicators_toggle', {
indicator_name: stat,
status: nextHiddenStats.includes( stat ) ? 'off' : 'on',
} );
};
return (
<Card
className="woocommerce-analytics__card"
title={ __( 'Stats overview', 'woocommerce-admin' ) }
menu={
<EllipsisMenu
label={ __(
'Choose which values to display',
'woocommerce-admin'
) }
renderContent={ () => (
<Fragment>
<MenuTitle>
{ __( 'Display stats:', 'woocommerce-admin' ) }
</MenuTitle>
{ stats.map( ( item ) => {
const checked = ! hiddenStats.includes(
item.stat
);
return (
<MenuItem
checked={ checked }
isCheckbox
isClickable
key={ item.stat }
onInvoke={ () =>
toggleStat( item.stat )
}
>
{ item.label }
</MenuItem>
);
} ) }
</Fragment>
) }
/>
}
>
Content Here
</Card>
);
};
StatsOverview.propTypes = {
/**
* Homepage user preferences.
*/
userPrefs: PropTypes.object.isRequired,
/**
* A method to update user meta.
*/
updateCurrentUserData: PropTypes.func.isRequired,
};
export default compose(
withSelect( ( select ) => {
const { getCurrentUserData } = select( 'wc-api' );
return {
userPrefs: getCurrentUserData().homepage_stats || {},
};
} ),
withDispatch( ( dispatch ) => {
const { updateCurrentUserData } = dispatch( 'wc-api' );
return {
updateCurrentUserData,
};
} )
)( StatsOverview );

View File

@ -0,0 +1,68 @@
/**
* External dependencies
*/
import { render, fireEvent, screen } from '@testing-library/react';
import { StatsOverview } from '../index';
import { recordEvent } from 'lib/tracks';
jest.mock( 'lib/tracks' );
describe( 'StatsOverview tracking', () => {
it( 'should record an event when a stat is toggled', () => {
render(
<StatsOverview
userPrefs={ {
hiddenStats: null,
} }
updateCurrentUserData={ () => {} }
/>
);
const ellipsisBtn = screen.getByTitle(
'Choose which values to display'
);
fireEvent.click( ellipsisBtn );
const totalSalesBtn = screen.getByText( 'Total Sales' );
fireEvent.click( totalSalesBtn );
expect( recordEvent ).toHaveBeenCalledWith(
'statsoverview_indicators_toggle',
{
indicator_name: 'revenue/total_sales',
status: 'off',
}
);
} );
} );
describe( 'StatsOverview toggle and persist stat preference', () => {
it( 'should update preferences', () => {
const updateCurrentUserData = jest.fn();
render(
<StatsOverview
userPrefs={ {
hiddenStats: null,
} }
updateCurrentUserData={ updateCurrentUserData }
/>
);
const ellipsisBtn = screen.getByTitle(
'Choose which values to display'
);
fireEvent.click( ellipsisBtn );
const totalSalesBtn = screen.getByText( 'Total Sales' );
fireEvent.click( totalSalesBtn );
expect( updateCurrentUserData ).toHaveBeenCalledWith( {
homepage_stats: {
hiddenStats: [
'revenue/net_revenue',
'products/items_sold',
'revenue/total_sales',
],
},
} );
} );
} );

View File

@ -1,15 +0,0 @@
import { render } from '@testing-library/react';
import Homepage from '../index';
describe( 'homepage', () => {
it( 'should render', () => {
const { container } = render( <Homepage /> );
expect( container ).toMatchInlineSnapshot( `
<div>
<div>
Hello World
</div>
</div>
` );
} );
} );

View File

@ -47,6 +47,7 @@ function updateCurrentUserData( resourceNames, data, fetch ) {
'dashboard_chart_interval',
'dashboard_leaderboard_rows',
'activity_panel_inbox_last_read',
'homepage_stats',
];
if ( resourceNames.includes( resourceName ) ) {

View File

@ -71,6 +71,7 @@ class AnalyticsDashboard {
'dashboard_chart_type',
'dashboard_chart_interval',
'dashboard_leaderboard_rows',
'homepage_stats',
)
);
}
@ -80,8 +81,8 @@ class AnalyticsDashboard {
*/
public function register_page() {
$features = wc_admin_get_feature_config();
$id = $features['homepage'] ? 'woocommerce-home' : 'woocommerce-dashboard';
$title = $features['homepage'] ? __( 'Home', 'woocommerce-admin' ) : __( 'Dashboard', 'woocommerce-admin' );
$id = $features['homepage'] ? 'woocommerce-home' : 'woocommerce-dashboard';
$title = $features['homepage'] ? __( 'Home', 'woocommerce-admin' ) : __( 'Dashboard', 'woocommerce-admin' );
wc_admin_register_page(
array(

View File

@ -62,7 +62,30 @@ global.wcSettings = {
woocommerce_actionable_order_statuses: [],
woocommerce_excluded_report_order_statuses: [],
},
dataEndpoints: {},
dataEndpoints: {
performanceIndicators: [
{
chart: 'total_sales',
label: 'Total Sales',
stat: 'revenue/total_sales',
},
{
chart: 'net_revenue',
label: 'Net Sales',
stat: 'revenue/net_revenue',
},
{
chart: 'orders_count',
label: 'Orders',
stat: 'orders/orders_count',
},
{
chart: 'items_sold',
label: 'Items Sold',
stat: 'products/items_sold',
},
],
},
};
wordPressPackages.forEach( ( lib ) => {