Merge pull request woocommerce/woocommerce-admin#1332 from woocommerce/fix/summary-number-close-on-mobile

SummaryNumbers: close on click for mobile
This commit is contained in:
Paul Sealock 2019-01-22 12:09:07 +13:00 committed by GitHub
commit a52fab85de
7 changed files with 182 additions and 97 deletions

View File

@ -42,33 +42,35 @@ export class ReportSummary extends Component {
const secondaryTotals = totals.secondary || {};
const { compare } = getDateParamsFromQuery( query );
const summaryNumbers = charts.map( chart => {
const { key, label, type } = chart;
const delta = calculateDelta( primaryTotals[ key ], secondaryTotals[ key ] );
const href = getNewPath( { chart: key } );
const prevValue = formatValue( type, secondaryTotals[ key ] );
const isSelected = selectedChart.key === key;
const value = formatValue( type, primaryTotals[ key ] );
const renderSummaryNumbers = ( { onToggle } ) =>
charts.map( chart => {
const { key, label, type } = chart;
const delta = calculateDelta( primaryTotals[ key ], secondaryTotals[ key ] );
const href = getNewPath( { chart: key } );
const prevValue = formatValue( type, secondaryTotals[ key ] );
const isSelected = selectedChart.key === key;
const value = formatValue( type, primaryTotals[ key ] );
return (
<SummaryNumber
key={ key }
delta={ delta }
href={ href }
label={ label }
prevLabel={
'previous_period' === compare
? __( 'Previous Period:', 'wc-admin' )
: __( 'Previous Year:', 'wc-admin' )
}
prevValue={ prevValue }
selected={ isSelected }
value={ value }
/>
);
} );
return (
<SummaryNumber
key={ key }
delta={ delta }
href={ href }
label={ label }
prevLabel={
'previous_period' === compare
? __( 'Previous Period:', 'wc-admin' )
: __( 'Previous Year:', 'wc-admin' )
}
prevValue={ prevValue }
selected={ isSelected }
value={ value }
onLinkClickCallback={ onToggle }
/>
);
} );
return <SummaryList>{ summaryNumbers }</SummaryList>;
return <SummaryList>{ renderSummaryNumbers }</SummaryList>;
}
}

View File

@ -112,37 +112,39 @@ class StorePerformance extends Component {
: __( 'Previous Year:', 'wc-admin' );
return (
<SummaryList>
{ userIndicators.map( ( indicator, i ) => {
const primaryItem = find( primaryData.data, data => data.stat === indicator.stat );
const secondaryItem = find( secondaryData.data, data => data.stat === indicator.stat );
{ () =>
userIndicators.map( ( indicator, i ) => {
const primaryItem = find( primaryData.data, data => data.stat === indicator.stat );
const secondaryItem = find( secondaryData.data, data => data.stat === indicator.stat );
if ( ! primaryItem || ! secondaryItem ) {
return null;
}
if ( ! primaryItem || ! secondaryItem ) {
return null;
}
const href =
( primaryItem._links &&
primaryItem._links.report[ 0 ] &&
primaryItem._links.report[ 0 ].href ) ||
'';
const reportUrl =
( href && getNewPath( persistedQuery, href, { chart: primaryItem.chart } ) ) || '';
const delta = calculateDelta( primaryItem.value, secondaryItem.value );
const primaryValue = formatValue( primaryItem.format, primaryItem.value );
const secondaryValue = formatValue( secondaryItem.format, secondaryItem.value );
const href =
( primaryItem._links &&
primaryItem._links.report[ 0 ] &&
primaryItem._links.report[ 0 ].href ) ||
'';
const reportUrl =
( href && getNewPath( persistedQuery, href, { chart: primaryItem.chart } ) ) || '';
const delta = calculateDelta( primaryItem.value, secondaryItem.value );
const primaryValue = formatValue( primaryItem.format, primaryItem.value );
const secondaryValue = formatValue( secondaryItem.format, secondaryItem.value );
return (
<SummaryNumber
key={ i }
href={ reportUrl }
label={ indicator.label }
value={ primaryValue }
prevLabel={ prevLabel }
prevValue={ secondaryValue }
delta={ delta }
/>
);
} ) }
return (
<SummaryNumber
key={ i }
href={ reportUrl }
label={ indicator.label }
value={ primaryValue }
prevLabel={ prevLabel }
prevValue={ secondaryValue }
delta={ delta }
/>
);
} )
}
</SummaryList>
);
}

View File

@ -3,13 +3,16 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { Children, cloneElement } from '@wordpress/element';
import { Dropdown, NavigableMenu } from '@wordpress/components';
import { Dropdown } from '@wordpress/components';
import PropTypes from 'prop-types';
import { uniqueId } from 'lodash';
import { withViewportMatch } from '@wordpress/viewport';
/**
* Internal dependencies
*/
import Menu from './menu';
/**
* A container element for a list of SummaryNumbers. This component handles detecting & switching to
* the mobile format on smaller screens.
@ -17,45 +20,27 @@ import { withViewportMatch } from '@wordpress/viewport';
* @return { object } -
*/
const SummaryList = ( { children, isDropdownBreakpoint, label } ) => {
if ( ! label ) {
label = __( 'Performance Indicators', 'wc-admin' );
}
const items = children( {} );
// We default to "one" because we can't have empty children.
const itemCount = Children.count( children ) || 1;
const hasItemsClass = itemCount < 10 ? `has-${ itemCount }-items` : 'has-10-items';
const classes = classnames( 'woocommerce-summary', {
[ hasItemsClass ]: ! isDropdownBreakpoint,
} );
const instanceId = uniqueId( 'woocommerce-summary-helptext-' );
const menu = (
<NavigableMenu
aria-label={ label }
aria-describedby={ instanceId }
orientation={ isDropdownBreakpoint ? 'vertical' : 'horizontal' }
stopNavigationEvents
>
<p id={ instanceId } className="screen-reader-text">
{ __(
'List of data points available for filtering. Use arrow keys to cycle through ' +
'the list. Click a data point for a detailed report.',
'wc-admin'
) }
</p>
<ul className={ classes }>{ children }</ul>
</NavigableMenu>
const itemCount = Children.count( items ) || 1;
const orientation = isDropdownBreakpoint ? 'vertical' : 'horizontal';
const summaryMenu = (
<Menu
label={ label }
orientation={ orientation }
itemCount={ itemCount }
items={ items }
/>
);
// On large screens, or if there are not multiple SummaryNumbers, we'll display the plain list.
if ( ! isDropdownBreakpoint || itemCount < 2 ) {
return menu;
return summaryMenu;
}
const items = Children.toArray( children );
const selected = items.find( item => !! item.props.selected );
if ( ! selected ) {
return menu;
return summaryMenu;
}
return (
@ -64,22 +49,33 @@ const SummaryList = ( { children, isDropdownBreakpoint, label } ) => {
position="bottom"
headerTitle={ label }
renderToggle={ ( { isOpen, onToggle } ) => cloneElement( selected, { onToggle, isOpen } ) }
renderContent={ () => menu }
renderContent={ renderContentArgs => (
<Menu
label={ label }
orientation={ orientation }
itemCount={ itemCount }
items={ children( renderContentArgs ) }
/>
) }
/>
);
};
SummaryList.propTypes = {
/**
* A list of `<SummaryNumber />`s
* A function returning a list of `<SummaryNumber />`s
*/
children: PropTypes.node.isRequired,
children: PropTypes.func.isRequired,
/**
* An optional label of this group, read to screen reader users. Defaults to "Performance Indicators".
* An optional label of this group, read to screen reader users.
*/
label: PropTypes.string,
};
SummaryList.defaultProps = {
label: __( 'Performance Indicators', 'wc-admin' ),
};
export default withViewportMatch( {
isDropdownBreakpoint: '< large',
} )( SummaryList );

View File

@ -0,0 +1,63 @@
/** @format */
/**
* External dependencies
*/
import { NavigableMenu } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { uniqueId } from 'lodash';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import { getHasItemsClass } from './utils';
const Menu = ( { label, orientation, itemCount, items } ) => {
const instanceId = uniqueId( 'woocommerce-summary-helptext-' );
const hasItemsClass = getHasItemsClass( itemCount );
const classes = classnames( 'woocommerce-summary', {
[ hasItemsClass ]: orientation === 'horizontal',
} );
return (
<NavigableMenu
aria-label={ label }
aria-describedby={ instanceId }
orientation={ orientation }
stopNavigationEvents
>
<p id={ instanceId } className="screen-reader-text">
{ __(
'List of data points available for filtering. Use arrow keys to cycle through ' +
'the list. Click a data point for a detailed report.',
'wc-admin'
) }
</p>
<ul className={ classes }>
{ items }
</ul>
</NavigableMenu>
);
};
Menu.propTypes = {
/**
* An optional label of this group, read to screen reader users.
*/
label: PropTypes.string,
/**
* Item layout orientation.
*/
orientation: PropTypes.oneOf( [ 'vertical', 'horizontal' ] ).isRequired,
/**
* A list of `<SummaryNumber />`s.
*/
items: PropTypes.node.isRequired,
/**
* Number of items.
*/
itemCount: PropTypes.number.isRequired,
};
export default Menu;

View File

@ -6,7 +6,7 @@ import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import classnames from 'classnames';
import Gridicon from 'gridicons';
import { isNil } from 'lodash';
import { isNil, noop } from 'lodash';
import PropTypes from 'prop-types';
/**
@ -30,6 +30,7 @@ const SummaryNumber = ( {
reverseTrend,
selected,
value,
onLinkClickCallback,
} ) => {
const liClasses = classnames( 'woocommerce-summary__item-container', {
'is-dropdown-button': onToggle,
@ -59,13 +60,15 @@ const SummaryNumber = ( {
};
if ( onToggle || href ) {
Container = onToggle ? Button : Link;
if ( ! onToggle ) {
containerProps.href = href;
containerProps.role = 'menuitem';
} else {
const isButton = !! onToggle;
Container = isButton ? Button : Link;
if ( isButton ) {
containerProps.onClick = onToggle;
containerProps[ 'aria-expanded' ] = isOpen;
} else {
containerProps.href = href;
containerProps.role = 'menuitem';
containerProps.onClick = onLinkClickCallback;
}
} else {
Container = 'div';
@ -150,6 +153,10 @@ SummaryNumber.propTypes = {
* A string or number value to display - a string is allowed so we can accept currency formatting.
*/
value: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
/**
* A function to be called after a SummaryNumber, rendered as a link, is clicked.
*/
onLinkClickCallback: PropTypes.func,
};
SummaryNumber.defaultProps = {
@ -158,6 +165,7 @@ SummaryNumber.defaultProps = {
prevLabel: __( 'Previous Period:', 'wc-admin' ),
reverseTrend: false,
selected: false,
onLinkClickCallback: noop,
};
export default SummaryNumber;

View File

@ -8,6 +8,11 @@ import { range } from 'lodash';
import PropTypes from 'prop-types';
import { withViewportMatch } from '@wordpress/viewport';
/**
* Internal dependencies
*/
import { getHasItemsClass } from './utils';
/**
* `SummaryListPlaceholder` behaves like `SummaryList` but displays placeholder summary items instead of data.
* This can be used while loading data.
@ -17,7 +22,7 @@ class SummaryListPlaceholder extends Component {
const { isDropdownBreakpoint } = this.props;
const numberOfItems = isDropdownBreakpoint ? 1 : this.props.numberOfItems;
const hasItemsClass = numberOfItems < 10 ? `has-${ numberOfItems }-items` : 'has-10-items';
const hasItemsClass = getHasItemsClass( numberOfItems );
const classes = classnames( 'woocommerce-summary', {
[ hasItemsClass ]: ! isDropdownBreakpoint,
'is-placeholder': true,

View File

@ -0,0 +1,9 @@
/**
* Get a class name depending on item count.
*
* @param {number} count - Item count.
* @returns {string} - class name.
*/
export function getHasItemsClass( count ) {
return count < 10 ? `has-${ count }-items` : 'has-10-items';
}