Fix product comparison + color scale on charts with lots of options (https://github.com/woocommerce/woocommerce-admin/pull/1481)

* Fix color scale on charts with lots of options

* Fix product comparison
This commit is contained in:
Justin Shreve 2019-02-06 10:46:37 -05:00 committed by GitHub
parent 43f3aba8dc
commit 81fcab5709
11 changed files with 49 additions and 31 deletions

View File

@ -31,7 +31,7 @@ class ProductsReport extends Component {
const isProductDetailsView = const isProductDetailsView =
'top_items' === query.filter || 'top_items' === query.filter ||
'top_sales' === query.filter || 'top_sales' === query.filter ||
'single_product' === query.filter; 'compare-products' === query.filter;
const mode = const mode =
isProductDetailsView || ( isSingleProductView && isSingleProductVariable ) isProductDetailsView || ( isSingleProductView && isSingleProductVariable )

View File

@ -2,6 +2,8 @@
- Improves display of charts where all values are 0. - Improves display of charts where all values are 0.
- Fix X-axis labels in hourly bar charts. - Fix X-axis labels in hourly bar charts.
- New `<Search>` prop named `showClearButton`, that will display a 'Clear' button when the search box contains one or more tags. - New `<Search>` prop named `showClearButton`, that will display a 'Clear' button when the search box contains one or more tags.
- Number of selectable chart elements is now limited to 5.
- Color scale logic for charts with lots of items has been fixed.
# 1.4.2 # 1.4.2
- Add emoji-flags dependency - Add emoji-flags dependency

View File

@ -0,0 +1,13 @@
/** @format */
// This is the max number of items that can be selected/shown on a chart at one time.
// If this number changes, the color scale also needs to be adjusted.
export const selectionLimit = 5;
export const colorScales = [
[],
[ 0.5 ],
[ 0.333, 0.667 ],
[ 0.25, 0.50, 0.75 ],
[ 0.20, 0.40, 0.60, 0.80 ],
[ 0.16, 0.32, 0.48, 0.64, 0.80 ],
];

View File

@ -116,6 +116,7 @@ class D3Chart extends Component {
const compact = this.shouldBeCompact(); const compact = this.shouldBeCompact();
const uniqueKeys = getUniqueKeys( data ); const uniqueKeys = getUniqueKeys( data );
const newOrderedKeys = orderedKeys || getOrderedKeys( data, uniqueKeys ); const newOrderedKeys = orderedKeys || getOrderedKeys( data, uniqueKeys );
const visibleKeys = newOrderedKeys.filter( key => key.visible );
const lineData = getLineData( data, newOrderedKeys ); const lineData = getLineData( data, newOrderedKeys );
const yMax = getYMax( lineData ); const yMax = getYMax( lineData );
const yScale = getYScale( adjHeight, yMax ); const yScale = getYScale( adjHeight, yMax );
@ -135,6 +136,7 @@ class D3Chart extends Component {
margin, margin,
mode, mode,
orderedKeys: newOrderedKeys, orderedKeys: newOrderedKeys,
visibleKeys,
parseDate, parseDate,
tooltipPosition, tooltipPosition,
tooltipLabelFormat: getFormatter( tooltipLabelFormat, d3TimeFormat ), tooltipLabelFormat: getFormatter( tooltipLabelFormat, d3TimeFormat ),

View File

@ -2,7 +2,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import classNames from 'classnames'; import classNames from 'classnames';
import { Component, createRef } from '@wordpress/element'; import { Component, createRef } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -12,6 +12,7 @@ import PropTypes from 'prop-types';
*/ */
import { getFormatter } from './utils/index'; import { getFormatter } from './utils/index';
import { getColor } from './utils/color'; import { getColor } from './utils/color';
import { selectionLimit } from '../constants';
/** /**
* A legend specifically designed for the WooCommerce admin charts. * A legend specifically designed for the WooCommerce admin charts.
@ -60,7 +61,9 @@ class D3Legend extends Component {
} = this.props; } = this.props;
const { isScrollable } = this.state; const { isScrollable } = this.state;
const numberOfRowsVisible = data.filter( row => row.visible ).length; const numberOfRowsVisible = data.filter( row => row.visible ).length;
const showTotalLabel = legendDirection === 'column' && data.length > 5 && totalLabel; const showTotalLabel = legendDirection === 'column' && data.length > numberOfRowsVisible && totalLabel;
const visibleKeys = data.filter( key => key.visible );
return ( return (
<div <div
@ -96,10 +99,13 @@ class D3Legend extends Component {
id={ row.key } id={ row.key }
disabled={ disabled={
( row.visible && numberOfRowsVisible <= 1 ) || ( row.visible && numberOfRowsVisible <= 1 ) ||
( ! row.visible && numberOfRowsVisible >= 5 ) || ( ! row.visible && numberOfRowsVisible >= selectionLimit ) ||
! interactive ! interactive
} }
title={ numberOfRowsVisible >= 5 ? __( 'You may select up to 5 items.', 'wc-admin' ) : '' } title={ numberOfRowsVisible >= selectionLimit
? sprintf( __( 'You may select up to %d items.', 'wc-admin' ), selectionLimit )
: ''
}
> >
<div className="woocommerce-legend__item-container" id={ row.key }> <div className="woocommerce-legend__item-container" id={ row.key }>
<span <span
@ -107,7 +113,7 @@ class D3Legend extends Component {
'woocommerce-legend__item-checkmark-checked': row.visible, 'woocommerce-legend__item-checkmark-checked': row.visible,
} ) } } ) }
id={ row.key } id={ row.key }
style={ { color: getColor( row.key, data, colorScheme ) } } style={ { color: getColor( row.key, visibleKeys, colorScheme ) } }
/> />
<span className="woocommerce-legend__item-title" id={ row.key }> <span className="woocommerce-legend__item-title" id={ row.key }>
{ row.key } { row.key }

View File

@ -76,7 +76,7 @@ export const drawBars = ( node, data, params ) => {
.attr( 'y', d => params.yScale( d.value ) ) .attr( 'y', d => params.yScale( d.value ) )
.attr( 'width', params.xGroupScale.bandwidth() ) .attr( 'width', params.xGroupScale.bandwidth() )
.attr( 'height', d => params.height - params.yScale( d.value ) ) .attr( 'height', d => params.height - params.yScale( d.value ) )
.attr( 'fill', d => getColor( d.key, params.orderedKeys, params.colorScheme ) ) .attr( 'fill', d => getColor( d.key, params.visibleKeys, params.colorScheme ) )
.attr( 'pointer-events', 'none' ) .attr( 'pointer-events', 'none' )
.attr( 'tabindex', '0' ) .attr( 'tabindex', '0' )
.attr( 'aria-label', d => { .attr( 'aria-label', d => {

View File

@ -1,25 +1,18 @@
/** @format */ /** @format */
/**
* Internal dependencies
*/
import { colorScales, selectionLimit } from '../../constants';
/** /**
* External dependencies * External dependencies
*/ */
import { findIndex } from 'lodash'; import { findIndex } from 'lodash';
export const getColor = ( key, orderedKeys, colorScheme ) => { export const getColor = ( key, orderedKeys, colorScheme ) => {
const smallColorScales = [ const len = orderedKeys.length > selectionLimit ? selectionLimit : orderedKeys.length;
[],
[ 0.5 ],
[ 0.333, 0.667 ],
[ 0.2, 0.5, 0.8 ],
[ 0.12, 0.375, 0.625, 0.88 ],
];
let keyValue = 0;
const len = orderedKeys.length;
const idx = findIndex( orderedKeys, d => d.key === key ); const idx = findIndex( orderedKeys, d => d.key === key );
if ( len < 5 ) { const keyValue = idx <= ( selectionLimit - 1 ) ? colorScales[ len ][ idx ] : 0;
keyValue = smallColorScales[ len ][ idx ];
} else {
keyValue = idx / ( orderedKeys.length - 1 );
}
return colorScheme( keyValue ); return colorScheme( keyValue );
}; };

View File

@ -43,7 +43,7 @@ export const drawLines = ( node, data, params, xOffset ) => {
.attr( 'stroke-width', lineStroke ) .attr( 'stroke-width', lineStroke )
.attr( 'stroke-linejoin', 'round' ) .attr( 'stroke-linejoin', 'round' )
.attr( 'stroke-linecap', 'round' ) .attr( 'stroke-linecap', 'round' )
.attr( 'stroke', d => getColor( d.key, params.orderedKeys, params.colorScheme ) ) .attr( 'stroke', d => getColor( d.key, params.visibleKeys, params.colorScheme ) )
.style( 'opacity', d => { .style( 'opacity', d => {
const opacity = d.focus ? 1 : 0.1; const opacity = d.focus ? 1 : 0.1;
return d.visible ? opacity : 0; return d.visible ? opacity : 0;
@ -59,7 +59,7 @@ export const drawLines = ( node, data, params, xOffset ) => {
.enter() .enter()
.append( 'circle' ) .append( 'circle' )
.attr( 'r', dotRadius ) .attr( 'r', dotRadius )
.attr( 'fill', d => getColor( d.key, params.orderedKeys, params.colorScheme ) ) .attr( 'fill', d => getColor( d.key, params.visibleKeys, params.colorScheme ) )
.attr( 'stroke', '#fff' ) .attr( 'stroke', '#fff' )
.attr( 'stroke-width', lineStroke + 1 ) .attr( 'stroke-width', lineStroke + 1 )
.style( 'opacity', d => { .style( 'opacity', d => {
@ -112,7 +112,7 @@ export const drawLines = ( node, data, params, xOffset ) => {
.enter() .enter()
.append( 'circle' ) .append( 'circle' )
.attr( 'r', dotRadius + 2 ) .attr( 'r', dotRadius + 2 )
.attr( 'fill', d => getColor( d.key, params.orderedKeys, params.colorScheme ) ) .attr( 'fill', d => getColor( d.key, params.visibleKeys, params.colorScheme ) )
.attr( 'stroke', '#fff' ) .attr( 'stroke', '#fff' )
.attr( 'stroke-width', lineStroke + 2 ) .attr( 'stroke-width', lineStroke + 2 )
.attr( 'cx', d => params.xLineScale( moment( d.date ).toDate() ) + xOffset ) .attr( 'cx', d => params.xLineScale( moment( d.date ).toDate() ) + xOffset )

View File

@ -115,13 +115,13 @@ const getTooltipRowLabel = ( d, row, params ) => {
}; };
export const showTooltip = ( params, d, position ) => { export const showTooltip = ( params, d, position ) => {
const keys = params.orderedKeys.filter( row => row.visible ).map( const keys = params.visibleKeys.map(
row => ` row => `
<li class="key-row"> <li class="key-row">
<div class="key-container"> <div class="key-container">
<span <span
class="key-color" class="key-color"
style="background-color:${ getColor( row.key, params.orderedKeys, params.colorScheme ) }"> style="background-color:${ getColor( row.key, params.visibleKeys, params.colorScheme ) }">
</span> </span>
<span class="key-key">${ getTooltipRowLabel( d, row, params ) }</span> <span class="key-key">${ getTooltipRowLabel( d, row, params ) }</span>
</div> </div>

View File

@ -24,6 +24,7 @@ import { updateQueryString } from '@woocommerce/navigation';
import ChartPlaceholder from './placeholder'; import ChartPlaceholder from './placeholder';
import { H, Section } from '../section'; import { H, Section } from '../section';
import { D3Chart, D3Legend } from './d3chart'; import { D3Chart, D3Legend } from './d3chart';
import { selectionLimit } from './constants';
function getD3CurrencyFormat( symbol, position ) { function getD3CurrencyFormat( symbol, position ) {
switch ( position ) { switch ( position ) {
@ -74,7 +75,7 @@ function getOrderedKeys( props, previousOrderedKeys = [] ) {
return updatedKeys.filter( key => key.total > 0 ).map( ( key, index ) => { return updatedKeys.filter( key => key.total > 0 ).map( ( key, index ) => {
return { return {
...key, ...key,
visible: index < 5 || key.visible, visible: index < selectionLimit || key.visible,
}; };
} ); } );
} }

View File

@ -142,10 +142,11 @@ class TableCard extends Component {
} }
onCompare() { onCompare() {
// Reset selected rows so the user can start a comparison again. const { compareBy, compareParam, onQueryChange } = this.props;
this.setState( { const { selectedRows } = this.state;
selectedRows: [], if ( compareBy ) {
} ); onQueryChange( 'compare' )( compareBy, compareParam, selectedRows.join( ',' ) );
}
} }
onSearch( values ) { onSearch( values ) {