Use X-WP-Total header to set the total number of items in tables (https://github.com/woocommerce/woocommerce-admin/pull/952)

* Use X-WP-Total header to set the total number of items in tables

* Improve naming

* Improve tests

* Make getReportItems() return a 'data' property by default

* Make getSummary prop behavior more clear
This commit is contained in:
Albert Juhé Lluveras 2018-11-29 08:03:04 -06:00 committed by GitHub
parent b27aa9b0aa
commit 540a191b57
12 changed files with 82 additions and 46 deletions

View File

@ -29,7 +29,6 @@ class ReportTable extends Component {
itemIdField,
primaryData,
tableData,
totalsCountField,
// These two props are not used in the render function, but are destructured
// so they are not included in the `tableProps` variable.
endpoint,
@ -48,12 +47,11 @@ class ReportTable extends Component {
const isRequesting = tableData.isRequesting || primaryData.isRequesting;
const headers = getHeadersContent();
const orderedItems = orderBy( items, query.orderby, query.order );
const orderedItems = orderBy( items.data, query.orderby, query.order );
const ids = orderedItems.map( item => item[ itemIdField ] );
const rows = getRowsContent( orderedItems );
const totals = get( primaryData, [ 'data', 'totals' ], null );
const summary = getSummary( totals );
const totalRows = get( totals, [ totalsCountField ], items.length );
const summary = getSummary ? getSummary( totals ) : null;
return (
<TableCard
@ -65,7 +63,7 @@ class ReportTable extends Component {
rows={ rows }
rowsPerPage={ query.per_page }
summary={ summary }
totalRows={ totalRows }
totalRows={ items.totalCount || 0 }
{ ...tableProps }
/>
);
@ -109,25 +107,16 @@ ReportTable.propTypes = {
* String to display as the title of the table.
*/
title: PropTypes.string.isRequired,
/**
* Name of the property in the primary data totals object which contains the
* total number of items.
*/
totalsCountField: PropTypes.string,
};
ReportTable.defaultProps = {
getSummary: () => null,
tableQuery: {},
totalsCountField: '',
};
export default compose(
withSelect( ( select, props ) => {
const { endpoint, query, tableQuery } = props;
// @TODO allow loading the primary data for the variations table once #926 is fixed.
const primaryData =
endpoint === 'variations' ? {} : getReportChartData( endpoint, 'primary', query, select );
const { endpoint, getSummary, query, tableQuery } = props;
const primaryData = getSummary ? getReportChartData( endpoint, 'primary', query, select ) : {};
const tableData = getReportTableData( endpoint, query, select, tableQuery );
return {

View File

@ -133,7 +133,6 @@ export default class CategoriesReportTable extends Component {
getSummary={ this.getSummary }
itemIdField="category_id"
query={ query }
totalsCountField="categories_count"
title={ __( 'Categories', 'wc-admin' ) }
/>
);

View File

@ -160,7 +160,6 @@ export default class CouponsReportTable extends Component {
getSummary={ this.getSummary }
itemIdField="coupon_id"
query={ query }
totalsCountField="coupons_count"
title={ __( 'Coupons', 'wc-admin' ) }
/>
);

View File

@ -210,7 +210,6 @@ export default class ProductsReportTable extends Component {
order: query.order || 'desc',
extended_product_info: true,
} }
totalsCountField="products_count"
title={ __( 'Products', 'wc-admin' ) }
/>
);

View File

@ -153,7 +153,6 @@ export default class TaxesReportTable extends Component {
getSummary={ this.getSummary }
itemIdField="tax_rate_id"
query={ query }
totalsCountField="taxes_count"
title={ __( 'Taxes', 'wc-admin' ) }
/>
);

View File

@ -1,12 +1,13 @@
/** @format */
export default {
setReportItems( endpoint, items, query ) {
setReportItems( endpoint, query, data, totalCount ) {
return {
type: 'SET_REPORT_ITEMS',
endpoint,
items,
query: query || {},
data,
totalCount,
};
},
setReportItemsError( endpoint, query ) {

View File

@ -20,7 +20,10 @@ export default function reportItemsReducer( state = DEFAULT_STATE, action ) {
case 'SET_REPORT_ITEMS':
return merge( {}, state, {
[ action.endpoint ]: {
[ queryKey ]: action.items,
[ queryKey ]: {
data: action.data,
totalCount: action.totalCount,
},
},
} );

View File

@ -26,9 +26,9 @@ export default {
const response = await fetch(
SWAGGERNAMESPACE + 'reports/' + endpoint + stringifyQuery( query )
);
const items = await response.json();
const itemsData = await response.json();
dispatch( 'wc-admin' ).setReportItems( endpoint, items, query );
dispatch( 'wc-admin' ).setReportItems( endpoint, query, itemsData );
} catch ( error ) {
dispatch( 'wc-admin' ).setReportItemsError( endpoint, query );
}
@ -37,11 +37,14 @@ export default {
}
try {
const items = await apiFetch( {
const response = await apiFetch( {
parse: false,
path: NAMESPACE + 'reports/' + endpoint + stringifyQuery( query ),
} );
dispatch( 'wc-admin' ).setReportItems( endpoint, items, query );
const itemsData = await response.json();
const totalCount = parseInt( response.headers.get( 'x-wp-total' ) );
dispatch( 'wc-admin' ).setReportItems( endpoint, query, itemsData, totalCount );
} catch ( error ) {
dispatch( 'wc-admin' ).setReportItemsError( endpoint, query );
}

View File

@ -21,7 +21,7 @@ import { getJsonString } from 'store/utils';
* @return {Object} Report details
*/
function getReportItems( state, endpoint, query = {} ) {
return get( state, [ 'reports', 'items', endpoint, getJsonString( query ) ], [] );
return get( state, [ 'reports', 'items', endpoint, getJsonString( query ) ], { data: [] } );
}
export default {

View File

@ -27,17 +27,22 @@ describe( 'reportItemsReducer()', () => {
const query = {
orderby: 'orders_count',
};
const items = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsData = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsTotalCount = 50;
const state = reportItemsReducer( originalState, {
type: 'SET_REPORT_ITEMS',
endpoint,
query,
items,
data: itemsData,
totalCount: itemsTotalCount,
} );
const queryKey = getJsonString( query );
expect( state[ endpoint ][ queryKey ] ).toEqual( items );
expect( state[ endpoint ][ queryKey ] ).toEqual( {
data: itemsData,
totalCount: itemsTotalCount,
} );
} );
it( 'tracks multiple queries in items data', () => {
@ -45,28 +50,40 @@ describe( 'reportItemsReducer()', () => {
orderby: 'id',
};
const otherQueryKey = getJsonString( otherQuery );
const otherItems = [ { id: 1 }, { id: 2 }, { id: 3 } ];
const otherItemsData = [ { id: 1 }, { id: 2 }, { id: 3 } ];
const otherItemsTotalCount = 70;
const otherQueryState = {
[ endpoint ]: {
[ otherQueryKey ]: otherItems,
[ otherQueryKey ]: {
data: otherItemsData,
totalCount: otherItemsTotalCount,
},
},
};
const originalState = deepFreeze( otherQueryState );
const query = {
orderby: 'orders_count',
};
const items = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsData = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsTotalCount = 50;
const state = reportItemsReducer( originalState, {
type: 'SET_REPORT_ITEMS',
endpoint,
query,
items,
data: itemsData,
totalCount: itemsTotalCount,
} );
const queryKey = getJsonString( query );
expect( state[ endpoint ][ queryKey ] ).toEqual( items );
expect( state[ endpoint ][ otherQueryKey ] ).toEqual( otherItems );
expect( state[ endpoint ][ queryKey ] ).toEqual( {
data: itemsData,
totalCount: itemsTotalCount,
} );
expect( state[ endpoint ][ otherQueryKey ] ).toEqual( {
data: otherItemsData,
totalCount: otherItemsTotalCount,
} );
} );
it( 'returns with received error data', () => {

View File

@ -24,16 +24,28 @@ jest.mock( '@wordpress/api-fetch', () => jest.fn() );
describe( 'getReportItems', () => {
const ITEMS_1 = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const ITEMS_1_COUNT = 50;
const ITEMS_2 = [ { id: 1 }, { id: 2 }, { id: 3 } ];
const ITEMS_2_COUNT = 75;
const endpoint = 'products';
beforeAll( () => {
apiFetch.mockImplementation( options => {
if ( options.path === `/wc/v3/reports/${ endpoint }` ) {
return Promise.resolve( ITEMS_1 );
return Promise.resolve( {
headers: {
get: () => ITEMS_1_COUNT,
},
json: () => Promise.resolve( ITEMS_1 ),
} );
}
if ( options.path === `/wc/v3/reports/${ endpoint }?orderby=id` ) {
return Promise.resolve( ITEMS_2 );
return Promise.resolve( {
headers: {
get: () => ITEMS_2_COUNT,
},
json: () => Promise.resolve( ITEMS_2 ),
} );
}
} );
} );
@ -41,12 +53,22 @@ describe( 'getReportItems', () => {
it( 'returns requested report data', async () => {
expect.assertions( 1 );
await getReportItems( endpoint );
expect( dispatch().setReportItems ).toHaveBeenCalledWith( endpoint, ITEMS_1, undefined );
expect( dispatch().setReportItems ).toHaveBeenCalledWith(
endpoint,
undefined,
ITEMS_1,
ITEMS_1_COUNT
);
} );
it( 'returns requested report data for a specific query', async () => {
expect.assertions( 1 );
await getReportItems( endpoint, { orderby: 'id' } );
expect( dispatch().setReportItems ).toHaveBeenCalledWith( endpoint, ITEMS_2, { orderby: 'id' } );
expect( dispatch().setReportItems ).toHaveBeenCalledWith(
endpoint,
{ orderby: 'id' },
ITEMS_2,
ITEMS_2_COUNT
);
} );
} );

View File

@ -26,23 +26,28 @@ const queryKey = getJsonString( query );
const endpoint = 'coupons';
describe( 'getReportItems()', () => {
it( 'returns an empty array when no items are available', () => {
it( 'returns an empty object when no items are available', () => {
const state = deepFreeze( {} );
expect( getReportItems( state, endpoint, query ) ).toEqual( [] );
expect( getReportItems( state, endpoint, query ) ).toEqual( { data: [] } );
} );
it( 'returns stored items for current query', () => {
const items = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsData = [ { id: 1214 }, { id: 1215 }, { id: 1216 } ];
const itemsTotalCount = 50;
const queryState = {
data: itemsData,
totalCount: itemsTotalCount,
};
const state = deepFreeze( {
reports: {
items: {
[ endpoint ]: {
[ queryKey ]: items,
[ queryKey ]: queryState,
},
},
},
} );
expect( getReportItems( state, endpoint, query ) ).toEqual( items );
expect( getReportItems( state, endpoint, query ) ).toEqual( queryState );
} );
} );