Create Pagination and ProductOrderSelect components (https://github.com/woocommerce/woocommerce-blocks/pull/926)
* Create ProductOrderSelect component * Create Pagination component * Add description to props * Use BEM class name * Use < > instead of ← → * Update product order select options to match Shop core page * Refactor pagination so it behaves like core pagination * Update snapshots
This commit is contained in:
parent
0b45200d36
commit
73f8f15bb3
|
@ -32,7 +32,7 @@ const Label = ( { label, screenReaderLabel, wrapperElement, wrapperProps } ) =>
|
|||
if ( label && screenReaderLabel && label !== screenReaderLabel ) {
|
||||
return (
|
||||
<Wrapper { ...wrapperProps }>
|
||||
<span aria-hidden>
|
||||
<span aria-hidden="true">
|
||||
{ label }
|
||||
</span>
|
||||
<span className="screen-reader-text">
|
||||
|
|
|
@ -6,7 +6,7 @@ exports[`Label with wrapperElement should render both label and screen reader la
|
|||
data-foo="bar"
|
||||
>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
aria-hidden="true"
|
||||
>
|
||||
Lorem
|
||||
</span>
|
||||
|
@ -39,7 +39,7 @@ exports[`Label with wrapperElement should render only the screen reader label 1`
|
|||
exports[`Label without wrapperElement should render both label and screen reader label 1`] = `
|
||||
Array [
|
||||
<span
|
||||
aria-hidden={true}
|
||||
aria-hidden="true"
|
||||
>
|
||||
Lorem
|
||||
</span>,
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Label from '../label';
|
||||
import { getIndexes } from './utils.js';
|
||||
import './style.scss';
|
||||
|
||||
const Pagination = ( { currentPage, displayFirstAndLastPages, displayNextAndPreviousArrows, pagesToDisplay, onPageChange, totalPages } ) => {
|
||||
const { minIndex, maxIndex } = getIndexes( pagesToDisplay, currentPage, totalPages );
|
||||
const pages = [];
|
||||
for ( let i = minIndex; i <= maxIndex; i++ ) {
|
||||
pages.push( i );
|
||||
}
|
||||
const showFirstPage = displayFirstAndLastPages && Boolean( minIndex !== 1 );
|
||||
const showLastPage = displayFirstAndLastPages && Boolean( maxIndex !== totalPages );
|
||||
const showFirstPageEllipsis = displayFirstAndLastPages && Boolean( minIndex > 2 );
|
||||
const showLastPageEllipsis = displayFirstAndLastPages && Boolean( maxIndex < totalPages - 1 );
|
||||
const showPreviousArrow = displayNextAndPreviousArrows && Boolean( currentPage !== 1 );
|
||||
const showNextArrow = displayNextAndPreviousArrows && Boolean( currentPage !== totalPages );
|
||||
|
||||
return (
|
||||
<div className="wc-block-pagination">
|
||||
<Label
|
||||
screenReaderLabel={ __( 'Navigate to another page', 'woo-gutenberg-products-block' ) }
|
||||
/>
|
||||
{ showPreviousArrow && (
|
||||
<button
|
||||
className="wc-block-pagination-page"
|
||||
onClick={ () => onPageChange( currentPage - 1 ) }
|
||||
title={ __( 'Previous page', 'woo-gutenberg-products-block' ) }
|
||||
>
|
||||
<Label
|
||||
label="<"
|
||||
screenReaderLabel={ __( 'Previous page', 'woo-gutenberg-products-block' ) }
|
||||
/>
|
||||
</button>
|
||||
) }
|
||||
{ showFirstPage && (
|
||||
<button
|
||||
className="wc-block-pagination-page"
|
||||
onClick={ () => onPageChange( 1 ) }
|
||||
>
|
||||
1
|
||||
</button>
|
||||
) }
|
||||
{ showFirstPageEllipsis && (
|
||||
<span className="wc-block-pagination-ellipsis" aria-hidden="true">
|
||||
{ __( '…', 'woo-gutenberg-products-block' ) }
|
||||
</span>
|
||||
) }
|
||||
{ pages.map( ( page ) => {
|
||||
return (
|
||||
<button
|
||||
key={ page }
|
||||
className={ classNames( 'wc-block-pagination-page', {
|
||||
'wc-block-pagination-page--active': currentPage === page,
|
||||
} ) }
|
||||
onClick={ currentPage === page ? null : () => onPageChange( page ) }
|
||||
>
|
||||
{ page }
|
||||
</button>
|
||||
);
|
||||
} ) }
|
||||
{ showLastPageEllipsis && (
|
||||
<span className="wc-block-pagination-ellipsis" aria-hidden="true">
|
||||
{ __( '…', 'woo-gutenberg-products-block' ) }
|
||||
</span>
|
||||
) }
|
||||
{ showLastPage && (
|
||||
<button
|
||||
className="wc-block-pagination-page"
|
||||
onClick={ () => onPageChange( totalPages ) }
|
||||
>
|
||||
{ totalPages }
|
||||
</button>
|
||||
) }
|
||||
{ showNextArrow && (
|
||||
<button
|
||||
className="wc-block-pagination-page"
|
||||
onClick={ () => onPageChange( currentPage + 1 ) }
|
||||
title={ __( 'Next page', 'woo-gutenberg-products-block' ) }
|
||||
>
|
||||
<Label
|
||||
label=">"
|
||||
screenReaderLabel={ __( 'Next page', 'woo-gutenberg-products-block' ) }
|
||||
/>
|
||||
</button>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Pagination.propTypes = {
|
||||
/**
|
||||
* Number of the page currently being displayed.
|
||||
*/
|
||||
currentPage: PropTypes.number.isRequired,
|
||||
/**
|
||||
* Total number of pages.
|
||||
*/
|
||||
totalPages: PropTypes.number.isRequired,
|
||||
/**
|
||||
* Displays first and last pages if they are not in the current range of pages displayed.
|
||||
*/
|
||||
displayFirstAndLastPages: PropTypes.bool,
|
||||
/**
|
||||
* Displays arrows to navigate to the previous and next pages.
|
||||
*/
|
||||
displayNextAndPreviousArrows: PropTypes.bool,
|
||||
/**
|
||||
* Callback function called when the user triggers a page change.
|
||||
*/
|
||||
onPageChange: PropTypes.func,
|
||||
/**
|
||||
* Number of pages to display at the same time, including the active page
|
||||
* and the pages displayed before and after it. It doesn't include the first
|
||||
* and last pages.
|
||||
*/
|
||||
pagesToDisplay: PropTypes.number,
|
||||
};
|
||||
|
||||
Pagination.defaultProps = {
|
||||
displayFirstAndLastPages: true,
|
||||
displayNextAndPreviousArrows: true,
|
||||
pagesToDisplay: 3,
|
||||
};
|
||||
|
||||
export default Pagination;
|
|
@ -0,0 +1,22 @@
|
|||
.wc-block-pagination-page,
|
||||
.wc-block-pagination-ellipsis {
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.wc-block-pagination-page {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
padding: 0.3em 0.6em;
|
||||
min-width: 2.2em;
|
||||
}
|
||||
|
||||
.wc-block-pagination-ellipsis {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
.wc-block-pagination-page--active {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getIndexes } from '../utils.js';
|
||||
|
||||
describe( 'getIndexes', () => {
|
||||
describe( 'when on the first page', () => {
|
||||
test( 'indexes include the first pages available', () => {
|
||||
expect( getIndexes( 5, 1, 100 ) ).toEqual( { minIndex: 1, maxIndex: 5 } );
|
||||
} );
|
||||
|
||||
test( 'indexes include the only available page if there is only one', () => {
|
||||
expect( getIndexes( 5, 1, 1 ) ).toEqual( { minIndex: 1, maxIndex: 1 } );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'when on a page in the middle', () => {
|
||||
test( 'indexes include pages before and after the current page', () => {
|
||||
expect( getIndexes( 5, 50, 100 ) ).toEqual( { minIndex: 48, maxIndex: 52 } );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'when on the last page', () => {
|
||||
test( 'indexes include the last pages available', () => {
|
||||
expect( getIndexes( 5, 100, 100 ) ).toEqual( { minIndex: 96, maxIndex: 100 } );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Given the number of pages to display, the current page and the total pages,
|
||||
* returns the min and max index of the pages to display in the pagination component.
|
||||
*
|
||||
* @param {integer} pagesToDisplay Maximum number of pages to display in the pagination component.
|
||||
* @param {integer} currentPage Page currently visible.
|
||||
* @param {integer} totalPages Total pages available.
|
||||
* @return {object} Object containing the min and max index to display in the pagination component.
|
||||
*/
|
||||
export const getIndexes = ( pagesToDisplay, currentPage, totalPages ) => {
|
||||
const extraPagesToDisplay = pagesToDisplay - 1;
|
||||
const tentativeMinIndex = Math.max( Math.floor( currentPage - ( extraPagesToDisplay / 2 ) ), 1 );
|
||||
const maxIndex = Math.min( Math.ceil( currentPage + ( extraPagesToDisplay - ( currentPage - tentativeMinIndex ) ) ), totalPages );
|
||||
const minIndex = Math.max( Math.floor( currentPage - ( extraPagesToDisplay - ( maxIndex - currentPage ) ) ), 1 );
|
||||
|
||||
return { minIndex, maxIndex };
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import OrderSelect from '../order-select';
|
||||
|
||||
const ProductOrderSelect = ( { defaultValue, onChange, readOnly, value } ) => {
|
||||
return (
|
||||
<OrderSelect
|
||||
className="wc-block-product-order-select"
|
||||
defaultValue={ defaultValue }
|
||||
name="orderby"
|
||||
onChange={ onChange }
|
||||
options={ [
|
||||
{ key: 'menu_order', label: __( 'Default sorting', 'woo-gutenberg-products-block' ) },
|
||||
{ key: 'popularity', label: __( 'Popularity', 'woo-gutenberg-products-block' ) },
|
||||
{ key: 'rating', label: __( 'Average rating', 'woo-gutenberg-products-block' ) },
|
||||
{ key: 'date', label: __( 'Latest', 'woo-gutenberg-products-block' ) },
|
||||
{ key: 'price', label: __( 'Price: low to high', 'woo-gutenberg-products-block' ) },
|
||||
{ key: 'price-desc', label: __( 'Price: high to low', 'woo-gutenberg-products-block' ) },
|
||||
] }
|
||||
readOnly={ readOnly }
|
||||
screenReaderLabel={ __( 'Order products by', 'woo-gutenberg-products-block' ) }
|
||||
value={ value }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ProductOrderSelect.propTypes = {
|
||||
defaultValue: PropTypes.oneOf( [ 'menu_order', 'popularity', 'rating', 'date', 'price', 'price-desc' ] ),
|
||||
onChange: PropTypes.func,
|
||||
readOnly: PropTypes.bool,
|
||||
value: PropTypes.oneOf( [ 'menu_order', 'popularity', 'rating', 'date', 'price', 'price-desc' ] ),
|
||||
};
|
||||
|
||||
export default ProductOrderSelect;
|
Loading…
Reference in New Issue