From c471c55f457e49a46ad39e75df0999c4fad990b6 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Sun, 3 Dec 2017 17:34:53 +0200 Subject: [PATCH] feat(Pagination): add component --- .../Types/PaginationExamplePagination.js | 2 +- .../Examples/addons/Pagination/Types/index.js | 1 - .../Usage/PaginationExampleControlled.js | 28 +++ .../Usage/PaginationExampleMenuProps.js | 14 ++ .../Usage/PaginationExampleOptions.js | 112 +++++++++ .../Usage/PaginationExampleShorthand.js | 17 ++ .../Examples/addons/Pagination/Usage/index.js | 31 +++ docs/app/Examples/addons/Pagination/index.js | 2 + .../Examples/collections/Menu/Types/index.js | 8 +- index.d.ts | 10 +- src/addons/Pagination/Pagination.d.ts | 58 ++++- src/addons/Pagination/Pagination.js | 221 +++++++++--------- src/addons/Pagination/PaginationItem.d.ts | 35 +++ src/addons/Pagination/PaginationItem.js | 91 +++++--- src/index.js | 3 +- .../createPaginationItems.js | 39 ++++ src/lib/createPaginationItems/index.js | 1 + .../createPaginationItems/itemFactories.js | 59 +++++ .../createPaginationItems/paginationUtils.js | 36 +++ .../rangeFactories.js | 16 +- .../suffixFactories.js | 7 +- src/lib/index.js | 2 +- src/lib/paginationFactory/index.js | 1 - src/lib/paginationFactory/itemFactories.js | 35 --- .../paginationFactory/paginationFactory.js | 46 ---- src/lib/paginationFactory/rangeUtils.js | 7 - .../addons/Pagination/Pagination-test.js | 87 +++---- .../addons/Pagination/PaginationItem-test.js | 81 +++++++ .../createPaginationItems-test.js | 36 +++ .../itemFactories-test.js | 103 ++++++++ .../paginationUtils-test.js | 33 +++ .../rangeFactories-test.js | 14 ++ .../suffixFactories-test.js | 33 +++ 33 files changed, 962 insertions(+), 307 deletions(-) create mode 100644 docs/app/Examples/addons/Pagination/Usage/PaginationExampleControlled.js create mode 100644 docs/app/Examples/addons/Pagination/Usage/PaginationExampleMenuProps.js create mode 100644 docs/app/Examples/addons/Pagination/Usage/PaginationExampleOptions.js create mode 100644 docs/app/Examples/addons/Pagination/Usage/PaginationExampleShorthand.js create mode 100644 docs/app/Examples/addons/Pagination/Usage/index.js create mode 100644 src/addons/Pagination/PaginationItem.d.ts create mode 100644 src/lib/createPaginationItems/createPaginationItems.js create mode 100644 src/lib/createPaginationItems/index.js create mode 100644 src/lib/createPaginationItems/itemFactories.js create mode 100644 src/lib/createPaginationItems/paginationUtils.js rename src/lib/{paginationFactory => createPaginationItems}/rangeFactories.js (55%) rename src/lib/{paginationFactory => createPaginationItems}/suffixFactories.js (69%) delete mode 100644 src/lib/paginationFactory/index.js delete mode 100644 src/lib/paginationFactory/itemFactories.js delete mode 100644 src/lib/paginationFactory/paginationFactory.js delete mode 100644 src/lib/paginationFactory/rangeUtils.js create mode 100644 test/specs/addons/Pagination/PaginationItem-test.js create mode 100644 test/specs/lib/createPaginationItems/createPaginationItems-test.js create mode 100644 test/specs/lib/createPaginationItems/itemFactories-test.js create mode 100644 test/specs/lib/createPaginationItems/paginationUtils-test.js create mode 100644 test/specs/lib/createPaginationItems/rangeFactories-test.js create mode 100644 test/specs/lib/createPaginationItems/suffixFactories-test.js diff --git a/docs/app/Examples/addons/Pagination/Types/PaginationExamplePagination.js b/docs/app/Examples/addons/Pagination/Types/PaginationExamplePagination.js index ce84d0f21b..17c1bbac10 100644 --- a/docs/app/Examples/addons/Pagination/Types/PaginationExamplePagination.js +++ b/docs/app/Examples/addons/Pagination/Types/PaginationExamplePagination.js @@ -2,7 +2,7 @@ import React from 'react' import { Pagination } from 'semantic-ui-react' const PaginationExamplePagination = () => ( - + ) export default PaginationExamplePagination diff --git a/docs/app/Examples/addons/Pagination/Types/index.js b/docs/app/Examples/addons/Pagination/Types/index.js index 3c430d37d1..9c50db0a55 100644 --- a/docs/app/Examples/addons/Pagination/Types/index.js +++ b/docs/app/Examples/addons/Pagination/Types/index.js @@ -1,5 +1,4 @@ import React from 'react' -import { Message } from 'semantic-ui-react' import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' diff --git a/docs/app/Examples/addons/Pagination/Usage/PaginationExampleControlled.js b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleControlled.js new file mode 100644 index 0000000000..cedb5f86aa --- /dev/null +++ b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleControlled.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react' +import { Grid, Input, Pagination, Segment } from 'semantic-ui-react' + +export default class PaginationExampleControlled extends Component { + state = { activePage: 1 } + + handleInputChange = (e, { value }) => this.setState({ activePage: value }) + + handlePaginationChange = (e, { activePage }) => this.setState({ activePage }) + + render() { + const { activePage } = this.state + + return ( + + + +
activePage: {activePage}
+ +
+
+ + + +
+ ) + } +} diff --git a/docs/app/Examples/addons/Pagination/Usage/PaginationExampleMenuProps.js b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleMenuProps.js new file mode 100644 index 0000000000..8371ee72e7 --- /dev/null +++ b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleMenuProps.js @@ -0,0 +1,14 @@ +import React from 'react' +import { Pagination } from 'semantic-ui-react' + +const PaginationExampleShorthand = () => ( + +) + +export default PaginationExampleShorthand diff --git a/docs/app/Examples/addons/Pagination/Usage/PaginationExampleOptions.js b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleOptions.js new file mode 100644 index 0000000000..30f6a2514e --- /dev/null +++ b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleOptions.js @@ -0,0 +1,112 @@ +import React, { Component } from 'react' +import { Grid, Form, Pagination, Segment } from 'semantic-ui-react' + +export default class PaginationExampleCustomization extends Component { + state = { + activePage: 5, + boundaryRange: 1, + siblingRange: 1, + showEllipsis: true, + showFirstAndLast: true, + showPreviousAndNext: true, + totalPages: 50, + } + + handleCheckboxChange = (e, { checked, name }) => this.setState({ [name]: checked }) + + handleInputChange = (e, { name, value }) => this.setState({ [name]: value }) + + handlePaginationChange = (e, { activePage }) => this.setState({ activePage }) + + render() { + const { + activePage, + boundaryRange, + siblingRange, + showEllipsis, + showFirstAndLast, + showPreviousAndNext, + totalPages, + } = this.state + + return ( + + + + + + +
+ + + + + + + + + + + + + +
+
+ +
+ ) + } +} diff --git a/docs/app/Examples/addons/Pagination/Usage/PaginationExampleShorthand.js b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleShorthand.js new file mode 100644 index 0000000000..a2ec52daa6 --- /dev/null +++ b/docs/app/Examples/addons/Pagination/Usage/PaginationExampleShorthand.js @@ -0,0 +1,17 @@ +import React from 'react' +import { Icon, Pagination } from 'semantic-ui-react' + +const PaginationExampleShorthand = () => ( + , icon: true }} + firstItem={{ content: , icon: true }} + lastItem={{ content: , icon: true }} + prevItem={{ content: , icon: true }} + nextItem={{ content: , icon: true }} + showFirstAndLast + totalPages={10} + /> +) + +export default PaginationExampleShorthand diff --git a/docs/app/Examples/addons/Pagination/Usage/index.js b/docs/app/Examples/addons/Pagination/Usage/index.js new file mode 100644 index 0000000000..9cd0cd9bd2 --- /dev/null +++ b/docs/app/Examples/addons/Pagination/Usage/index.js @@ -0,0 +1,31 @@ +import React from 'react' + +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const PaginationUsageExamples = () => ( + + + + + + +) + +export default PaginationUsageExamples diff --git a/docs/app/Examples/addons/Pagination/index.js b/docs/app/Examples/addons/Pagination/index.js index 7270e2ee38..5240229255 100644 --- a/docs/app/Examples/addons/Pagination/index.js +++ b/docs/app/Examples/addons/Pagination/index.js @@ -1,10 +1,12 @@ import React from 'react' import Types from './Types' +import Usage from './Usage' const PaginationExamples = () => (
+
) diff --git a/docs/app/Examples/collections/Menu/Types/index.js b/docs/app/Examples/collections/Menu/Types/index.js index ca12fe5cf6..12f9021529 100644 --- a/docs/app/Examples/collections/Menu/Types/index.js +++ b/docs/app/Examples/collections/Menu/Types/index.js @@ -1,4 +1,6 @@ import React from 'react' +import { Link } from 'react-router-dom' +import { Message } from 'semantic-ui-react' import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' @@ -67,7 +69,11 @@ const Types = () => ( title='Pagination' description='A pagination menu is specially formatted to present links to pages of content.' examplePath='collections/Menu/Types/MenuExamplePagination' - /> + > + + For fully featured pagination, see Pagination addon. + + ) diff --git a/index.d.ts b/index.d.ts index c17237711d..a983f152ab 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,13 +1,15 @@ // Addons +export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm'; +export { default as Pagination, PaginationProps } from './dist/commonjs/addons/Pagination'; +export { default as PaginationItem, PaginationItemProps } from './dist/commonjs/addons/Pagination/PaginationItem'; +export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal'; +export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio'; +export { default as Ref, RefProps } from './dist/commonjs/addons/Ref'; export { default as Responsive, ResponsiveProps, ResponsiveWidthShorthand } from './dist/commonjs/addons/Responsive'; -export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm'; -export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal'; -export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio'; -export { default as Ref, RefProps } from './dist/commonjs/addons/Ref'; export { default as Select, SelectProps } from './dist/commonjs/addons/Select'; export { default as TextArea, TextAreaProps } from './dist/commonjs/addons/TextArea'; export { diff --git a/src/addons/Pagination/Pagination.d.ts b/src/addons/Pagination/Pagination.d.ts index 4a2579748f..5a2ac87dc9 100644 --- a/src/addons/Pagination/Pagination.d.ts +++ b/src/addons/Pagination/Pagination.d.ts @@ -1,13 +1,67 @@ import * as React from 'react'; +import { SemanticShorthandItem } from '../..'; +import { default as PaginationItem, PaginationItemProps } from './PaginationItem'; + export interface PaginationProps { [key: string]: any; - /** An element type to render as (string or function). */ - as?: any; + /** A pagination item can have an aria label. */ + ariaLabel?: string; + + /** Initial activePage value. */ + defaultActivePage?: number | string; + + /** Index of the currently active page. */ + activePage?: number | string; + + /** Number of always visible pages at the beginning and end. */ + boundaryRange?: number | string; + + /** A shorthand for PaginationItem. */ + ellipsisItem?: SemanticShorthandItem; + + /** A shorthand for PaginationItem. */ + firstItem?: SemanticShorthandItem; + + /** A shorthand for PaginationItem. */ + lastItem?: SemanticShorthandItem; + + /** A shorthand for PaginationItem. */ + nextItem?: SemanticShorthandItem; + + /** A shorthand for PaginationItem. */ + pageItem?: SemanticShorthandItem; + + /** A shorthand for PaginationItem. */ + prevItem?: SemanticShorthandItem<>; + + /** + * Called on change of an active page. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onPageChange?: (event: React.MouseEvent, data: PaginationProps) => void; + + /** Boolean flag to show ellipsis. */ + showEllipsis?: boolean; + + /** Boolean flag to hide first and last page links. */ + showFirstAndLast?: boolean; + + /** Boolean flag to show previous and next page links. */ + showPreviousAndNext?: boolean; + + /** Number of always visible pages before and after the current one. */ + siblingRange?: number | string; + + /** Total number of pages. */ + totalPages: number | string; } declare class Pagination extends React.Component { + static Item: typeof PaginationItem; } export default Pagination; diff --git a/src/addons/Pagination/Pagination.js b/src/addons/Pagination/Pagination.js index 6b6a6aad21..d24169a555 100644 --- a/src/addons/Pagination/Pagination.js +++ b/src/addons/Pagination/Pagination.js @@ -4,9 +4,10 @@ import React from 'react' import { AutoControlledComponent as Component, + createPaginationItems, customPropTypes, + getUnhandledProps, META, - paginationFactory, } from '../../lib' import Menu from '../../collections/Menu' import PaginationItem from './PaginationItem' @@ -16,157 +17,159 @@ import PaginationItem from './PaginationItem' */ export default class Pagination extends Component { static propTypes = { - /** An element type to render as (string or function). */ - as: customPropTypes.as, + /** A pagination item can have an aria label. */ + ariaLabel: PropTypes.string, + /** Initial activePage value. */ defaultActivePage: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), + /** Index of the currently active page. */ activePage: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), - displayRange: PropTypes.oneOfType([ + /** Number of always visible pages at the beginning and end. */ + boundaryRange: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), - marginRange: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]), + /** A shorthand for PaginationItem. */ + ellipsisItem: customPropTypes.itemShorthand, - onChange: PropTypes.func, + /** A shorthand for PaginationItem. */ + firstItem: customPropTypes.itemShorthand, - totalPages: PropTypes.number.isRequired, - } + /** A shorthand for PaginationItem. */ + lastItem: customPropTypes.itemShorthand, - static autoControlledProps = [ - 'activePage', - ] + /** A shorthand for PaginationItem. */ + nextItem: customPropTypes.itemShorthand, - static defaultProps = { - displayRange: 2, - // firstItem: '«', - // lastItem: '»', - marginRange: 2, - // nextItem: '⟩', - // prevItem: '⟨', - } - - static _meta = { - name: 'Pagination', - type: META.TYPES.ADDON, - } - - // ---------------------------------------- - // Helpers - // ---------------------------------------- - - computeNextActivePage = (nextPage) => { - const { pageCount } = this.props - const { activePage } = this.state - - if (nextPage === 'first') return 1 - if (nextPage === 'last') return pageCount - - if (nextPage === 'prev') return activePage - 1 - if (nextPage === 'next') return activePage + 1 - - return nextPage - } + /** A shorthand for PaginationItem. */ + pageItem: customPropTypes.itemShorthand, - computeTabIndex = (pageName) => { - const { pageCount } = this.props + /** A shorthand for PaginationItem. */ + prevItem: customPropTypes.itemShorthand, - if (pageName === 'first') return 0 - if (pageName === 'prev') return 1 + /** + * Called on change of an active page. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onPageChange: PropTypes.func, - if (pageName === 'next') return pageCount + 1 - if (pageName === 'last') return pageCount + 2 + /** Boolean flag to show ellipsis. */ + showEllipsis: PropTypes.bool, - return pageName - } + /** Boolean flag to hide first and last page links. */ + showFirstAndLast: PropTypes.bool, - // ---------------------------------------- - // Event handlers - // ---------------------------------------- + /** Boolean flag to show previous and next page links. */ + showPreviousAndNext: PropTypes.bool, - handleItemClick = (e, { name }) => { - const nextActivePage = this.computeNextActivePage(name) + /** Number of always visible pages before and after the current one. */ + siblingRange: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), - this.trySetState({ activePage: nextActivePage }) - _.invoke(this.props, 'onChange', e, { ...this.props, activePage: nextActivePage }) + /** Total number of pages. */ + totalPages: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]).isRequired, } - handleItemLeft = (e) => { - const nextActivePage = this.computeNextActivePage('prev') + static autoControlledProps = [ + 'activePage', + ] - this.trySetState({ activePage: nextActivePage }) - _.invoke(this.props, 'onChange', e, { ...this.props, activePage: nextActivePage }) + static defaultProps = { + ariaLabel: 'Pagination Navigation', + boundaryRange: 1, + ellipsisItem: '...', + firstItem: { + ariaLabel: 'First item', + content: '«', + }, + lastItem: { + ariaLabel: 'Last item', + content: '»', + }, + nextItem: { + ariaLabel: 'Next item', + content: '⟩', + }, + pageItem: null, + prevItem: { + ariaLabel: 'Previous item', + content: '⟨', + }, + showEllipsis: true, + showFirstAndLast: false, + showPreviousAndNext: true, + siblingRange: 1, } - handleItemRight = (e) => { - const nextActivePage = this.computeNextActivePage('next') - - this.trySetState({ activePage: nextActivePage }) - _.invoke(this.props, 'onChange', e, { ...this.props, activePage: nextActivePage }) + static _meta = { + name: 'Pagination', + type: META.TYPES.ADDON, } - // ---------------------------------------- - // Overrides - // ---------------------------------------- - - handleItemOverrides = pageName => (predefinedProps) => { - const { activePage } = this.state + static Item = PaginationItem - return { - active: activePage === pageName, - key: pageName, - name: pageName, - onClick: (e, itemProps) => { - _.invoke(predefinedProps, 'onClick', e, itemProps) - this.handleItemClick(e, itemProps) - }, - onLeftKeyDown: this.handleItemLeft, - onRightKeyDown: this.handleItemRight, - tabIndex: this.computeTabIndex(pageName), - } + handleItemClick = (e, { value }) => { + this.trySetState({ activePage: value }) + _.invoke(this.props, 'onPageChange', e, { ...this.props, activePage: value }) } - // ---------------------------------------- - // Render - // ---------------------------------------- + handleItemOverrides = (active, type, value) => predefinedProps => ({ + active, + type, + key: `${type}-${value}`, + onClick: (e, itemProps) => { + _.invoke(predefinedProps, 'onClick', e, itemProps) + this.handleItemClick(e, itemProps) + }, + }) render() { - const { displayRange, firstItem, lastItem, navItem, nextItem, pageCount, prevItem, totalPages } = this.props - const items = paginationFactory({ - current: activePage, - total: totalPages, - }) - - const { marginRange } = this.props + const { + ariaLabel, + boundaryRange, + showEllipsis, + showFirstAndLast, + showPreviousAndNext, + siblingRange, + totalPages, + } = this.props const { activePage } = this.state - // console.log(computePrevRange(activePage, displayRange, marginRange)) + const items = createPaginationItems({ + activePage, + boundaryRange, + showEllipsis, + showFirstAndLast, + showPreviousAndNext, + siblingRange, + totalPages, + }) + const rest = getUnhandledProps(Pagination, this.props) return ( - - {PaginationItem.create(firstItem, { overrideProps: this.handleItemOverrides('first') })} - {PaginationItem.create(prevItem, { overrideProps: this.handleItemOverrides('prev') })} - - {_.map(items, index => PaginationItem.create(navItem || '', { - defaultProps: { - content: index, + + {_.map(items, ({ active, type, value }) => PaginationItem.create( + this.props[type] || value, { + defaultProps: { value }, + overrideProps: this.handleItemOverrides(active, type, value), }, - overrideProps: this.handleItemOverrides(index), - }))} - - {PaginationItem.create(nextItem, { overrideProps: this.handleItemOverrides('next') })} - {PaginationItem.create(lastItem, { overrideProps: this.handleItemOverrides('last') })} + ))} ) } diff --git a/src/addons/Pagination/PaginationItem.d.ts b/src/addons/Pagination/PaginationItem.d.ts new file mode 100644 index 0000000000..9777c65709 --- /dev/null +++ b/src/addons/Pagination/PaginationItem.d.ts @@ -0,0 +1,35 @@ +import * as React from 'react'; + +export interface PaginationItemProps { + [key: string]: any; + + /** A pagination item can be active. */ + active?: boolean; + + /** A pagination item can have an aria label. */ + ariaLabel?: string; + + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick?: (event: React.MouseEvent, data: PaginationItemProps) => void; + + /** + * Called on key down. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onKeyDown?: (event: React.MouseEvent, data: PaginationItemProps) => void; + + /** A pagination should have a type. */ + type?: 'ellipsisItem' | 'firstItem' | 'prevItem' | 'pageItem' | 'nextItem' | 'lastItem'; +} + +declare class PaginationItem extends React.Component { +} + +export default PaginationItem; diff --git a/src/addons/Pagination/PaginationItem.js b/src/addons/Pagination/PaginationItem.js index ae769dcb38..dd01571ec3 100644 --- a/src/addons/Pagination/PaginationItem.js +++ b/src/addons/Pagination/PaginationItem.js @@ -1,54 +1,83 @@ import _ from 'lodash' -import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Component } from 'react' import { createShorthandFactory, - customPropTypes, keyboardKey, META, } from '../../lib' -import Ref from '../Ref' -import Menu from '../../collections/Menu' - -const keyMapping = { - [keyboardKey.ArrowLeft]: 'onLeftKeyDown', - [keyboardKey.ArrowRight]: 'onRightKeyDown', -} +import MenuItem from '../../collections/Menu/MenuItem' /** * An item of a pagination. */ class PaginationItem extends Component { - componentDidUpdate({ active: prev }) { - const { active: current } = this.props + static propTypes = { + /** A pagination item can be active. */ + active: PropTypes.bool, + + /** A pagination item can have an aria label. */ + ariaLabel: PropTypes.string, - if (current && current !== prev) this.ref.focus() + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick: PropTypes.func, + + /** + * Called on key down. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onKeyDown: PropTypes.func, + + /** A pagination should have a type. */ + type: PropTypes.oneOf([ + 'ellipsisItem', + 'firstItem', + 'prevItem', + 'pageItem', + 'nextItem', + 'lastItem', + ]), } - handleKeyDown = (e) => { - const eventName = keyMapping[keyboardKey.getCode(e)] + static _meta = { + name: 'PaginationItem', + parent: 'Pagination', + type: META.TYPES.ADDON, + } + + handleClick = (e) => { + const { type } = this.props - if (eventName) _.invoke(this.props, eventName, e, this.props) + if (type !== 'ellipsisItem') _.invoke(this.props, 'onClick', e, this.props) } - handleRef = c => (this.ref = c) + handleKeyDown = (e) => { + _.invoke(this.props, 'onKeyDown', e, this.props) + if (keyboardKey.getCode(e) === keyboardKey.Enter) _.invoke(this.props, 'onClick', e, this.props) + } render() { - const { active, ...rest } = this.props - const ariaLabel = 'TODO' - - // TODO: Remove Ref https://github.com/Semantic-Org/Semantic-UI-React/pull/1879 will be merged - return ( - - {Menu.Item.create({ - ...rest, - active, - 'aria-current': active, - 'aria-label': ariaLabel, - onKeyDown: this.handleKeyDown, - })} - - ) + const { active, ariaLabel, type, ...rest } = this.props + const disabled = type === 'ellipsisItem' + + return MenuItem.create({ + ...rest, + active, + 'aria-current': active, + 'aria-label': ariaLabel, + disabled, + onClick: this.handleClick, + onKeyDown: this.handleKeyDown, + tabIndex: disabled ? -1 : 0, + }) } } diff --git a/src/index.js b/src/index.js index beefdaf3bf..4ce1bbd9ef 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,11 @@ // Addons -export { default as Responsive } from './addons/Responsive' export { default as Confirm } from './addons/Confirm' export { default as Pagination } from './addons/Pagination' +export { default as PaginationItem } from './addons/Pagination/PaginationItem' export { default as Portal } from './addons/Portal' export { default as Radio } from './addons/Radio' export { default as Ref } from './addons/Ref' +export { default as Responsive } from './addons/Responsive' export { default as Select } from './addons/Select' export { default as TextArea } from './addons/TextArea' export { default as TransitionablePortal } from './addons/TransitionablePortal' diff --git a/src/lib/createPaginationItems/createPaginationItems.js b/src/lib/createPaginationItems/createPaginationItems.js new file mode 100644 index 0000000000..08d4ab91b0 --- /dev/null +++ b/src/lib/createPaginationItems/createPaginationItems.js @@ -0,0 +1,39 @@ +import { + createFirstPage, + createLastItem, + createNextItem, + createPageFactory, + createPrevItem, +} from './itemFactories' +import { createComplexRange, createSimpleRange } from './rangeFactories' +import { isSimplePagination, typifyOptions } from './paginationUtils' + +/** + * @param {object} rawOptions + * @param {number} rawOptions.activePage + * @param {number} rawOptions.boundaryRange Number of always visible pages at the beginning and end. + * @param {bool} rawOptions.showEllipsis Boolean flag to show ellipsis. + * @param {bool} rawOptions.showFirstAndLast Boolean flag to hide first and last page links. + * @param {bool} rawOptions.showPreviousAndNext Boolean flag to show previous and next page links. + * @param {number} rawOptions.siblingRange Number of always visible pages before and after the current one. + * @param {number} rawOptions.totalPages Total number of pages. + */ +const createPaginationItems = (rawOptions) => { + const options = typifyOptions(rawOptions) + const { activePage, showFirstAndLast, showPreviousAndNext, totalPages } = options + + const pageFactory = createPageFactory(activePage) + const innerRange = isSimplePagination(options) + ? createSimpleRange(1, totalPages, pageFactory) + : createComplexRange(options, pageFactory) + + return [ + showFirstAndLast && createFirstPage(), + showPreviousAndNext && createPrevItem(activePage), + ...innerRange, + showPreviousAndNext && createNextItem(activePage, totalPages), + showFirstAndLast && createLastItem(totalPages), + ].filter(Boolean) +} + +export default createPaginationItems diff --git a/src/lib/createPaginationItems/index.js b/src/lib/createPaginationItems/index.js new file mode 100644 index 0000000000..01df3cfb93 --- /dev/null +++ b/src/lib/createPaginationItems/index.js @@ -0,0 +1 @@ +export default from './createPaginationItems' diff --git a/src/lib/createPaginationItems/itemFactories.js b/src/lib/createPaginationItems/itemFactories.js new file mode 100644 index 0000000000..b4a7ba6ebb --- /dev/null +++ b/src/lib/createPaginationItems/itemFactories.js @@ -0,0 +1,59 @@ +/** + * @param {number} pageNumber + * @return {Object} + */ +export const createEllipsisItem = pageNumber => ({ + active: false, + type: 'ellipsisItem', + value: pageNumber, +}) + +/** + * @return {Object} + */ +export const createFirstPage = () => ({ + active: false, + type: 'firstItem', + value: 1, +}) + +/** + * @param {number} activePage + * @return {Object} + */ +export const createPrevItem = activePage => ({ + active: false, + type: 'prevItem', + value: Math.max(1, activePage - 1), +}) + +/** + * @param {number} activePage + * @return {function} + */ +export const createPageFactory = activePage => pageNumber => ({ + active: activePage === pageNumber, + type: 'pageItem', + value: pageNumber, +}) + +/** + * @param {number} activePage + * @param {number} totalPages + * @return {Object} + */ +export const createNextItem = (activePage, totalPages) => ({ + active: false, + type: 'nextItem', + value: Math.min(activePage + 1, totalPages), +}) + +/** + * @param {number} totalPages + * @return {Object} + */ +export const createLastItem = totalPages => ({ + active: false, + type: 'lastItem', + value: totalPages, +}) diff --git a/src/lib/createPaginationItems/paginationUtils.js b/src/lib/createPaginationItems/paginationUtils.js new file mode 100644 index 0000000000..d237c1bd65 --- /dev/null +++ b/src/lib/createPaginationItems/paginationUtils.js @@ -0,0 +1,36 @@ +/** + * Checks the possibility of using simple range generation, if number of generated pages is equal + * or greater than total pages to show. + * + * @param {object} options + * @param {number} options.boundaryRange Number of always visible pages at the beginning and end. + * @param {number} options.siblingRange Number of always visible pages before and after the current one. + * @param {boolean} options.showEllipsis Boolean flag to show ellipsis. + * @param {number} options.totalPages Total number of pages. + * @return {boolean} + */ +export const isSimplePagination = ({ boundaryRange, showEllipsis, siblingRange, totalPages }) => { + const boundaryRangeSize = 2 * +boundaryRange + const ellipsisSize = 2 * +showEllipsis + const siblingRangeSize = 2 * +siblingRange + + return 1 + ellipsisSize + siblingRangeSize + boundaryRangeSize >= totalPages +} + +export const typifyOptions = ({ + activePage, + boundaryRange, + showEllipsis, + showFirstAndLast, + showPreviousAndNext, + siblingRange, + totalPages, +}) => ({ + activePage: +activePage, + boundaryRange: +boundaryRange, + showEllipsis: !!showEllipsis, + showFirstAndLast: !!showFirstAndLast, + showPreviousAndNext: !!showPreviousAndNext, + siblingRange: +siblingRange, + totalPages: +totalPages, +}) diff --git a/src/lib/paginationFactory/rangeFactories.js b/src/lib/createPaginationItems/rangeFactories.js similarity index 55% rename from src/lib/paginationFactory/rangeFactories.js rename to src/lib/createPaginationItems/rangeFactories.js index f4c7641dde..83b74b33b8 100644 --- a/src/lib/paginationFactory/rangeFactories.js +++ b/src/lib/createPaginationItems/rangeFactories.js @@ -1,22 +1,22 @@ import _ from 'lodash' import { createInnerPrefix, createInnerSuffix } from './suffixFactories' -export const createSimpleRange = (start, end, pageFactory) => _.map(_.range(start, end), pageFactory) +export const createSimpleRange = (start, end, pageFactory) => _.map(_.range(start, end + 1), pageFactory) -export const createRange = (options) => { - const { boundaryRange, current, pageFactory, showEllipsis, siblingRange, total } = options +export const createComplexRange = (options, pageFactory) => { + const { activePage, boundaryRange, showEllipsis, siblingRange, totalPages } = options const firstGroupEnd = boundaryRange const firstGroup = createSimpleRange(1, firstGroupEnd, pageFactory) - const lastGroupStart = total + 1 - boundaryRange - const lastGroup = createSimpleRange(lastGroupStart, total, pageFactory) + const lastGroupStart = (totalPages + 1) - boundaryRange + const lastGroup = createSimpleRange(lastGroupStart, totalPages, pageFactory) const innerGroupStart = Math.min( - Math.max(current - siblingRange, firstGroupEnd + Number(showEllipsis) + 1), - lastGroupStart - Number(showEllipsis) - 2 * siblingRange - 1, + Math.max(activePage - siblingRange, firstGroupEnd + (+showEllipsis + 1)), + lastGroupStart - +showEllipsis - (2 * siblingRange) - 1, ) - const innerGroupEnd = innerGroupStart + 2 * siblingRange + const innerGroupEnd = innerGroupStart + (2 * siblingRange) const innerGroup = createSimpleRange(innerGroupStart, innerGroupEnd, pageFactory) return [ diff --git a/src/lib/paginationFactory/suffixFactories.js b/src/lib/createPaginationItems/suffixFactories.js similarity index 69% rename from src/lib/paginationFactory/suffixFactories.js rename to src/lib/createPaginationItems/suffixFactories.js index 44188d4fab..b606ae0dbc 100644 --- a/src/lib/paginationFactory/suffixFactories.js +++ b/src/lib/createPaginationItems/suffixFactories.js @@ -1,9 +1,9 @@ -import { createEllipsis } from './itemFactories' +import { createEllipsisItem } from './itemFactories' export const createInnerPrefix = (firstGroupEnd, innerGroupStart, pageFactory) => { const prefixPage = innerGroupStart - 1 const showEllipsis = prefixPage !== (firstGroupEnd + 1) - const prefixFactory = showEllipsis ? createEllipsis : pageFactory + const prefixFactory = showEllipsis ? createEllipsisItem : pageFactory return prefixFactory(prefixPage) } @@ -11,7 +11,8 @@ export const createInnerPrefix = (firstGroupEnd, innerGroupStart, pageFactory) = export const createInnerSuffix = (innerGroupEnd, lastGroupStart, pageFactory) => { const suffixPage = innerGroupEnd + 1 const showEllipsis = suffixPage !== (lastGroupStart - 1) - const suffixFactory = showEllipsis ? createEllipsis : pageFactory + + const suffixFactory = showEllipsis ? createEllipsisItem : pageFactory return suffixFactory(suffixPage) } diff --git a/src/lib/index.js b/src/lib/index.js index b3fe4a4eff..ca583b905c 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -35,7 +35,7 @@ export { export { default as isBrowser } from './isBrowser' export { default as leven } from './leven' export * as META from './META' -export paginationFactory from './paginationFactory' +export createPaginationItems from './createPaginationItems' export * as SUI from './SUI' export { default as keyboardKey } from './keyboardKey' diff --git a/src/lib/paginationFactory/index.js b/src/lib/paginationFactory/index.js deleted file mode 100644 index 805e31bcf8..0000000000 --- a/src/lib/paginationFactory/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './paginationFactory' diff --git a/src/lib/paginationFactory/itemFactories.js b/src/lib/paginationFactory/itemFactories.js deleted file mode 100644 index 5e1ecd3bb3..0000000000 --- a/src/lib/paginationFactory/itemFactories.js +++ /dev/null @@ -1,35 +0,0 @@ -export const createEllipsis = page => ({ - active: false, - type: 'ELLIPSIS', - value: page, -}) - -export const createFirstPage = current => ({ - active: false, - type: 'FIRST_PAGE', - value: 1, -}) - -export const createPreviousPage = current => ({ - active: false, - type: 'PREVIOUS_PAGE', - value: Math.max(1, current - 1), -}) - -export const createPageFactory = current => page => ({ - active: current === page, - type: 'PAGE', - value: page, -}) - -export const createNextPage = (current, total) => ({ - active: false, - type: 'NEXT_PAGE', - value: Math.min(current + 1, total), -}) - -export const createLastPage = total => ({ - active: false, - type: 'LAST_PAGE_', - value: total, -}) diff --git a/src/lib/paginationFactory/paginationFactory.js b/src/lib/paginationFactory/paginationFactory.js deleted file mode 100644 index a4463da14e..0000000000 --- a/src/lib/paginationFactory/paginationFactory.js +++ /dev/null @@ -1,46 +0,0 @@ -import { - createFirstPage, - createLastPage, - createNextPage, - createPageFactory, - createPreviousPage, -} from './itemFactories' -import { createRange, createSimpleRange } from './rangeFactories' -import { isSimpleRange } from './rangeUtils' - -/** - * @param {object} options - * @param {number} options.current - * @param {number} options.total - * @param {number} [options.boundaryRange=1] - * @param {number} [options.siblingRange=1] - * @param {bool} [options.showEllipsis=true] - * @param {bool} [options.showPreviousAndNext=true] - * @param {bool} [options.showFirstAndLast=false] - */ -const paginationFactory = (options) => { - const { - current, - total, - boundaryRange = 1, - siblingRange = 1, - showEllipsis = true, - showPreviousAndNext = true, - showFirstAndLast = false, - } = options - - const pageFactory = createPageFactory(current) - const innerRange = isSimpleRange(boundaryRange, siblingRange, showEllipsis, total) - ? createSimpleRange(1, total, pageFactory) - : createRange({ boundaryRange, current, pageFactory, showEllipsis, siblingRange, total }) - - return [ - showFirstAndLast && createFirstPage(current), - showPreviousAndNext && createPreviousPage(current), - ...innerRange, - showPreviousAndNext && createNextPage(current, total), - showFirstAndLast && createLastPage(total), - ].filter(Boolean) -} - -export default paginationFactory diff --git a/src/lib/paginationFactory/rangeUtils.js b/src/lib/paginationFactory/rangeUtils.js deleted file mode 100644 index 685b506b3f..0000000000 --- a/src/lib/paginationFactory/rangeUtils.js +++ /dev/null @@ -1,7 +0,0 @@ -export const isSimpleRange = (boundaryRange, siblingRange, showEllipsis, total) => { - const boundaryRangeSize = 2 * boundaryRange - const ellipsisSize = (2 * Number(showEllipsis)) - const siblingRangeSize = 2 * siblingRange - - return 1 + ellipsisSize + siblingRangeSize + boundaryRangeSize >= total -} diff --git a/test/specs/addons/Pagination/Pagination-test.js b/test/specs/addons/Pagination/Pagination-test.js index 1074341665..38ecd274c4 100644 --- a/test/specs/addons/Pagination/Pagination-test.js +++ b/test/specs/addons/Pagination/Pagination-test.js @@ -1,63 +1,38 @@ import React from 'react' -import Pagination, { computeR } from 'src/addons/Pagination/Pagination' +import Pagination from 'src/addons/Pagination/Pagination' +import PaginationItem from 'src/addons/Pagination/PaginationItem' +import * as common from 'test/specs/commonTests' +import { sandbox } from 'test/utils' describe('Pagination', () => { - describe('computePrevRange', () => { - it('1', () => { - const range = computeR(10, 2, 2, 20) - console.log(range) - // 1 2 null 8 9 10 11 12 null - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, null, 8, 9, 10, 11, 12, null, 19, 20]) - }) - - it('2', () => { - const range = computeR(1, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, 3, 4, 5, 6, 7, null, 19, 20]) - }) - - it('3', () => { - const range = computeR(3, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, 3, 4, 5, 6, 7, null, 19, 20]) - }) - - it('4', () => { - const range = computeR(4, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, 3, 4, 5, 6, 7, null, 19, 20]) - }) - - it('5', () => { - const range = computeR(5, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, null, 3, 4, 5, 6, 7, null, 19, 20]) - }) - - it('6', () => { - const range = computeR(6, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, null, 4, 5, 6, 7, 8, null, 19, 20]) - }) - - it('20', () => { - const range = computeR(20, 2, 2, 20) - console.log(range) - - // range.should.have.lengthOf(2) - range.should.deep.equal([1, 2, null, 16, 17, 18, 19, 20]) + common.isConformant(Pagination, { requiredProps: { + totalPages: 0, + } }) + common.hasSubComponents(Pagination, [PaginationItem]) + + describe('onPageChange', () => { + it('is called with (e, data) when clicked on a pagination item', () => { + const event = { target: null } + const onPageChange = sandbox.spy() + const onPageItemClick = sandbox.spy() + + mount( + , + ) + .find('PaginationItem') + .at(3) + .simulate('click', event) + + onPageChange.should.have.been.calledOnce() + onPageChange.should.have.been.calledWithMatch(event, { activePage: 3 }) + onPageItemClick.should.have.been.calledOnce() + onPageItemClick.should.have.been.calledWithMatch(event, { value: 3 }) }) }) }) diff --git a/test/specs/addons/Pagination/PaginationItem-test.js b/test/specs/addons/Pagination/PaginationItem-test.js new file mode 100644 index 0000000000..1f8292a7fd --- /dev/null +++ b/test/specs/addons/Pagination/PaginationItem-test.js @@ -0,0 +1,81 @@ +import React from 'react' + +import PaginationItem from 'src/addons/Pagination/PaginationItem' +import * as common from 'test/specs/commonTests' +import { sandbox } from 'test/utils' + +describe('PaginationItem', () => { + common.isConformant(PaginationItem) + common.implementsCreateMethod(PaginationItem) + + describe('disabled', () => { + it('is false by default', () => { + shallow() + .should.have.prop('disabled', false) + }) + + it('is true when "type" is "ellipsisItem"', () => { + shallow() + .should.have.prop('disabled', true) + }) + }) + + describe('onClick', () => { + it('is called with (e, props) when clicked', () => { + const event = { target: null } + const onClick = sandbox.spy() + + shallow() + .simulate('click', event) + + onClick.should.have.been.calledOnce() + onClick.should.have.been.calledWithMatch(event, { onClick }) + }) + + it('is called with (e, props) when "Enter" is pressed', () => { + const event = { key: 'Enter', target: null } + const onClick = sandbox.spy() + + shallow() + .simulate('keyDown', event) + + onClick.should.have.been.calledOnce() + onClick.should.have.been.calledWithMatch(event, { onClick }) + }) + + it('is omitted when "type" is "ellipsisItem"', () => { + const event = { target: null } + const onClick = sandbox.spy() + + shallow() + .simulate('click', event) + + onClick.should.have.been.not.called() + }) + }) + + describe('onKeyDown', () => { + it('is called with (e, props) when clicked', () => { + const event = { key: 'Enter', target: null } + const onKeyDown = sandbox.spy() + + shallow() + .simulate('keyDown', event) + + onKeyDown.should.have.been.calledOnce() + onKeyDown.should.have.been.calledWithMatch(event, { onKeyDown }) + }) + }) + + describe('tabIndex', () => { + it('is 0 by default', () => { + shallow() + .should.have.prop('tabIndex', 0) + }) + + it('is -1 when "type" is "ellipsisItem"', () => { + shallow() + .should.have.prop('tabIndex', -1) + }) + }) +}) diff --git a/test/specs/lib/createPaginationItems/createPaginationItems-test.js b/test/specs/lib/createPaginationItems/createPaginationItems-test.js new file mode 100644 index 0000000000..255a304b02 --- /dev/null +++ b/test/specs/lib/createPaginationItems/createPaginationItems-test.js @@ -0,0 +1,36 @@ +import createPaginationItems from 'src/lib/createPaginationItems' + +describe('createPaginationItems', () => { + it('creates an array of item objects', () => { + createPaginationItems({ + activePage: 15, + boundaryRange: 2, + showEllipsis: true, + showFirstAndLast: true, + showPreviousAndNext: true, + siblingRange: 2, + totalPages: 30, + }).should.deep.equal([ + { active: false, type: 'firstItem', value: 1 }, + { active: false, type: 'prevItem', value: 14 }, + + { active: false, type: 'pageItem', value: 1 }, + { active: false, type: 'pageItem', value: 2 }, + + { active: false, type: 'ellipsisItem', value: 12 }, + { active: false, type: 'pageItem', value: 13 }, + { active: false, type: 'pageItem', value: 14 }, + { active: true, type: 'pageItem', value: 15 }, + { active: false, type: 'pageItem', value: 16 }, + { active: false, type: 'pageItem', value: 17 }, + { active: false, type: 'ellipsisItem', value: 18 }, + + { active: false, type: 'pageItem', value: 29 }, + { active: false, type: 'pageItem', value: 30 }, + + { active: false, type: 'nextItem', value: 16 }, + { active: false, type: 'lastItem', value: 30 }, + ]) + }) +}) + diff --git a/test/specs/lib/createPaginationItems/itemFactories-test.js b/test/specs/lib/createPaginationItems/itemFactories-test.js new file mode 100644 index 0000000000..42184d1beb --- /dev/null +++ b/test/specs/lib/createPaginationItems/itemFactories-test.js @@ -0,0 +1,103 @@ +import { + createEllipsisItem, + createFirstPage, + createLastItem, + createNextItem, + createPageFactory, + createPrevItem, +} from 'src/lib/createPaginationItems/itemFactories' + +// +// export const createPageFactory = current => page => ({ +// active: current === page, +// type: 'pageItem', +// value: page, +// }) +// + +describe('itemFactories', () => { + describe('createEllipsisItem', () => { + it('"active" is always false', () => { + createEllipsisItem(0).should.have.property('active', false) + }) + it('"type" matches "ellipsisItem"', () => { + createEllipsisItem(0).should.have.property('type', 'ellipsisItem') + }) + it('"value" matches passed argument', () => { + createEllipsisItem(5).should.have.property('value', 5) + }) + }) + + describe('createFirstPage', () => { + it('"active" is always false', () => { + createFirstPage().should.have.property('active', false) + }) + it('"type" matches "firstItem"', () => { + createFirstPage().should.have.property('type', 'firstItem') + }) + it('"value" always returns 1', () => { + createFirstPage().should.have.property('value', 1) + }) + }) + + describe('createPrevItem', () => { + it('"active" is always false', () => { + createPrevItem(1).should.have.property('active', false) + }) + it('"type" matches "prevItem"', () => { + createPrevItem(1).should.have.property('type', 'prevItem') + }) + it('"value" returns previous page number or 1', () => { + createPrevItem(1).should.have.property('value', 1) + createPrevItem(2).should.have.property('value', 1) + createPrevItem(3).should.have.property('value', 2) + }) + }) + + describe('createPageFactory', () => { + const pageFactory = createPageFactory(1) + + it('returns function', () => { + pageFactory.should.be.a('function') + }) + it('"active" is true when pageNumber is equal to activePage', () => { + pageFactory(1).should.have.property('active', true) + }) + it('"active" is false when pageNumber is not equal to activePage', () => { + pageFactory(2).should.have.property('active', false) + }) + it('"type" of created item matches "pageItem"', () => { + pageFactory(2).should.have.property('type', 'pageItem') + }) + it('"value" returns pageNumber', () => { + pageFactory(1).should.have.property('value', 1) + pageFactory(2).should.have.property('value', 2) + }) + }) + + describe('createNextItem', () => { + it('"active" is always false', () => { + createNextItem(0, 0).should.have.property('active', false) + }) + it('"type" matches "nextItem"', () => { + createNextItem(0, 0).should.have.property('type', 'nextItem') + }) + it('"value" returns the smallest of the arguments', () => { + createNextItem(1, 3).should.have.property('value', 2) + createNextItem(2, 3).should.have.property('value', 3) + createNextItem(3, 3).should.have.property('value', 3) + }) + }) + + describe('createLastItem', () => { + it('"active" is always false', () => { + createLastItem(0).should.have.property('active', false) + }) + it('"type" matches "lastItem"', () => { + createLastItem(0).should.have.property('type', 'lastItem') + }) + it('"value" matches passed argument', () => { + createLastItem(2).should.have.property('value', 2) + }) + }) +}) diff --git a/test/specs/lib/createPaginationItems/paginationUtils-test.js b/test/specs/lib/createPaginationItems/paginationUtils-test.js new file mode 100644 index 0000000000..01b2595299 --- /dev/null +++ b/test/specs/lib/createPaginationItems/paginationUtils-test.js @@ -0,0 +1,33 @@ +import { createInnerPrefix, createInnerSuffix } from 'src/lib/createPaginationItems/suffixFactories' +import { sandbox } from 'test/utils' + +describe('suffixFactories', () => { + describe('createInnerPrefix', () => { + it('returns ellipsisItem when is outside innerGroup', () => { + createInnerPrefix(5, 10, () => {}) + .should.have.property('type', 'ellipsisItem') + }) + + it('calls pageFactory when position matches border of a group', () => { + const pageFactory = sandbox.spy() + createInnerPrefix(5, 7, pageFactory) + + pageFactory.should.have.been.calledOnce() + }) + }) + + describe('createInnerSuffix', () => { + it('returns ellipsisItem when is outside innerGroup', () => { + createInnerSuffix(5, 10, () => {}) + .should.have.property('type', 'ellipsisItem') + }) + + it('calls pageFactory when position matches border of a group', () => { + const pageFactory = sandbox.spy() + createInnerSuffix(5, 7, pageFactory) + + pageFactory.should.have.been.calledOnce() + }) + }) +}) + diff --git a/test/specs/lib/createPaginationItems/rangeFactories-test.js b/test/specs/lib/createPaginationItems/rangeFactories-test.js new file mode 100644 index 0000000000..042472cf2f --- /dev/null +++ b/test/specs/lib/createPaginationItems/rangeFactories-test.js @@ -0,0 +1,14 @@ +import { createSimpleRange } from 'src/lib/createPaginationItems/rangeFactories' +import { sandbox } from 'test/utils' + +describe('rangeFactories', () => { + describe('createSimpleRange', () => { + it('creates range and calls pageFactory', () => { + const pageFactory = sandbox.spy() + createSimpleRange(5, 10, pageFactory) + + pageFactory.should.have.callCount(6) + }) + }) +}) + diff --git a/test/specs/lib/createPaginationItems/suffixFactories-test.js b/test/specs/lib/createPaginationItems/suffixFactories-test.js new file mode 100644 index 0000000000..01b2595299 --- /dev/null +++ b/test/specs/lib/createPaginationItems/suffixFactories-test.js @@ -0,0 +1,33 @@ +import { createInnerPrefix, createInnerSuffix } from 'src/lib/createPaginationItems/suffixFactories' +import { sandbox } from 'test/utils' + +describe('suffixFactories', () => { + describe('createInnerPrefix', () => { + it('returns ellipsisItem when is outside innerGroup', () => { + createInnerPrefix(5, 10, () => {}) + .should.have.property('type', 'ellipsisItem') + }) + + it('calls pageFactory when position matches border of a group', () => { + const pageFactory = sandbox.spy() + createInnerPrefix(5, 7, pageFactory) + + pageFactory.should.have.been.calledOnce() + }) + }) + + describe('createInnerSuffix', () => { + it('returns ellipsisItem when is outside innerGroup', () => { + createInnerSuffix(5, 10, () => {}) + .should.have.property('type', 'ellipsisItem') + }) + + it('calls pageFactory when position matches border of a group', () => { + const pageFactory = sandbox.spy() + createInnerSuffix(5, 7, pageFactory) + + pageFactory.should.have.been.calledOnce() + }) + }) +}) +