From 90be09b3c6a77a81748f737ea8529cf64a908137 Mon Sep 17 00:00:00 2001 From: Ed Leeks Date: Wed, 10 Feb 2021 09:49:22 +0000 Subject: [PATCH 1/2] feat(pager): add props to conditional render elements and smart render buttons Adds `show...` props to support conditional rendering of elements within `Pager`. Adds support for smart rendering navigartion controls, if there is zero or one page all buttons are hidden, if there is only two the `First` and `Last` buttons are hidden. Adds example of responsive `Pager` using the `show...` props and the `useMediaQuery` hook fix #3425 --- .../__snapshots__/pager.spec.js.snap | 1075 ----------------- .../pager-navigation-link.component.js | 8 +- .../pager-navigation.component.js | 142 ++- .../__internal__/pager-navigation.spec.js | 74 +- .../pager/__internal__/pager.stories.mdx | 148 --- src/components/pager/index.d.ts | 4 +- src/components/pager/index.js | 2 +- .../{__internal__ => }/pager.component.js | 128 +- .../pager/{__internal__ => }/pager.d.ts | 6 + .../pager/{__internal__ => }/pager.spec.js | 169 ++- .../pager/{__internal__ => }/pager.stories.js | 27 +- src/components/pager/pager.stories.mdx | 251 ++++ .../pager/{__internal__ => }/pager.style.js | 112 +- 13 files changed, 673 insertions(+), 1473 deletions(-) delete mode 100644 src/components/pager/__internal__/__snapshots__/pager.spec.js.snap delete mode 100644 src/components/pager/__internal__/pager.stories.mdx rename src/components/pager/{__internal__ => }/pager.component.js (62%) rename src/components/pager/{__internal__ => }/pager.d.ts (70%) rename src/components/pager/{__internal__ => }/pager.spec.js (70%) rename src/components/pager/{__internal__ => }/pager.stories.js (67%) create mode 100644 src/components/pager/pager.stories.mdx rename src/components/pager/{__internal__ => }/pager.style.js (63%) diff --git a/src/components/pager/__internal__/__snapshots__/pager.spec.js.snap b/src/components/pager/__internal__/__snapshots__/pager.spec.js.snap deleted file mode 100644 index 5b654f4e5d..0000000000 --- a/src/components/pager/__internal__/__snapshots__/pager.spec.js.snap +++ /dev/null @@ -1,1075 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pager renders the Pager correctly with the Mint Theme 1`] = ` -.c12 { - background: transparent; - border: none; - color: rgba(0,0,0,0.9); - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; - font-size: 14px; - outline: none; - padding: 0; - width: 30px; -} - -.c12:-webkit-autofill { - -webkit-box-shadow: 0 0 0px 1000px #fff inset; - -webkit-box-border: none; -} - -.c12::-webkit-input-placeholder { - color: rgba(0,0,0,0.55); -} - -.c12::-moz-placeholder { - color: rgba(0,0,0,0.55); -} - -.c12:-ms-input-placeholder { - color: rgba(0,0,0,0.55); -} - -.c12::placeholder { - color: rgba(0,0,0,0.55); -} - -.c12:invalid, -.c12:required { - box-shadow: none; -} - -.c21 { - color: rgba(0,0,0,0.9); - display: block; - font-weight: 600; -} - -.c20 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin-bottom: 8px; -} - -.c8 { - -webkit-flex: 0 0 70%; - -ms-flex: 0 0 70%; - flex: 0 0 70%; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - position: relative; -} - -.c10 { - -webkit-align-items: stretch; - -webkit-box-align: stretch; - -ms-flex-align: stretch; - align-items: stretch; - background: #fff; - border: 1px solid #668592; - box-sizing: border-box; - cursor: text; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-wrap: wrap; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - width: 100%; - margin: 0; - min-height: 40px; - padding-left: 11px; - padding-right: 11px; -} - -.c10 .c11:-webkit-autofill { - margin-top: 2px; -} - -.c10 input::-ms-clear { - display: none; -} - -.c10 input::-webkit-contacts-auto-fill-button { - display: none !important; -} - -.c14 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - cursor: pointer; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - margin-right: -11px; - margin-left: -11px; - width: 40px; -} - -.c14:focus { - outline: solid 3px #FFB500; -} - -.c3 { - position: relative; -} - -.c3 .c11 { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.c3 .c9 { - cursor: pointer; - padding-right: 0; -} - -.c3 .c13 { - margin-right: 0; -} - -.c15 { - display: inline-block; - position: relative; - color: rgba(0,0,0,0.65); - background-color: transparent; - vertical-align: middle; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - height: 24px; - width: 24px; -} - -.c15:hover { - color: rgba(0,0,0,0.90); - background-color: transparent; -} - -.c15::before { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-family: CarbonIcons; - content: "\\e910"; - font-size: 16px; - font-style: normal; - font-weight: normal; - line-height: 16px; - vertical-align: middle; - display: block; -} - -.c6 + .c5 { - margin-top: 16px; -} - -.c7 { - display: block; -} - -.c4 { - height: 26px; -} - -.c4 .c9 { - padding-left: 0; -} - -.c0 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0px 24px; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - border-top-width: 0; - font-size: 13px; - height: 39px; - max-height: 39px; - border-width: 1px 1px 1px 1px; - border-style: none solid solid solid; - border-color: #CCD6DB; - background-color: #FAFBFB; - border-top: none; -} - -.c0 .carbon-input-icon { - border: none; - background: none; -} - -.c0 .carbon-input-icon:hover { - background: none; - color: #000000; -} - -.c0 .common-input__field:focus-within { - outline: 3px solid #FFB500; -} - -.c0 .common-input__input { - border: 1px solid #668592; -} - -.c0 .common-input__input:active, -.c0 .common-input__input:hover, -.c0 .common-input__input:focus { - border: 1px solid #668592; -} - -.c0 .common-input__input:focus { - border: 1px solid #668592; - color: #000000; -} - -.c0 .common-input__input:focus ~ label .carbon-input-icon, -.c0 .common-input__input:focus ~ label .carbon-input-icon:hover { - border: none; - background: none; - color: #000000; -} - -.c0 .common-input__input:hover ~ label .carbon-input-icon { - border: none; - background: none; - color: #000000; -} - -.c0 .common-input__input:hover ~ label .carbon-input-icon:hover { - border: none; - background: none; -} - -.c1 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 30%; - -ms-flex: 1 1 30%; - flex: 1 1 30%; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c1 .c9 { - width: 55px; - height: 26px; - min-height: 26px; - min-width: 10px; - margin: 0px 4px; -} - -.c1 .c9 .c11 { - font-size: 14px; - padding-right: 0px; - padding-left: 8px; - height: 22px; - width: 13px; -} - -.c1 .c9 .c13 { - margin-left: 0; - width: 20px; - height: 24px; -} - -.c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c16 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c16.c16 .c9 { - padding: 0; - margin: 8px 4px 0 4px; - height: 26px; - line-height: 26px; - min-height: 24px; -} - -.c16.c16 .c9 .c11 { - text-align: center; - height: 24px; - padding: 0; -} - -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0 12px; -} - -.c17 { - padding: 0 10px; - font-size: 13px; - border-width: 0; - background-color: transparent; - cursor: pointer; - margin-left: 7px; - margin-right: 7px; - color: rgba(0,0,0,0.90); - color: rgba(0,0,0,0.3); - cursor: not-allowed; -} - -.c17:focus { - outline: solid 3px #FFB500; - padding: 5px 10px; -} - -.c22 { - padding: 0 10px; - font-size: 13px; - border-width: 0; - background-color: transparent; - cursor: pointer; - margin-left: 7px; - margin-right: 7px; - color: rgba(0,0,0,0.90); -} - -.c22:hover { - -webkit-text-decoration: underline; - text-decoration: underline; - padding: 5px 10px; -} - -.c22:focus { - outline: solid 3px #FFB500; - padding: 5px 10px; -} - -.c19 { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - white-space: nowrap; -} - -.c23 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 30%; - -ms-flex: 1 1 30%; - flex: 1 1 30%; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - -ms-flex-pack: end; - justify-content: flex-end; -} - -
-
-
- - Show - -
-
-
-
-
- - - - -
-
-
-
-
- - items - -
-
-
- - -
- - Page - -
- -
- - of - 10 - -
- - -
-
- 100 - - items -
-
-`; - -exports[`Pager renders the Pager without pageSizeSelection 1`] = ` -.c15 { - background: transparent; - border: none; - color: rgba(0,0,0,0.9); - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; - font-size: 14px; - outline: none; - padding: 0; - width: 30px; -} - -.c15:-webkit-autofill { - -webkit-box-shadow: 0 0 0px 1000px #fff inset; - -webkit-box-border: none; -} - -.c15::-webkit-input-placeholder { - color: rgba(0,0,0,0.55); -} - -.c15::-moz-placeholder { - color: rgba(0,0,0,0.55); -} - -.c15:-ms-input-placeholder { - color: rgba(0,0,0,0.55); -} - -.c15::placeholder { - color: rgba(0,0,0,0.55); -} - -.c15:invalid, -.c15:required { - box-shadow: none; -} - -.c7 { - color: rgba(0,0,0,0.9); - display: block; - font-weight: 600; -} - -.c6 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin-bottom: 8px; -} - -.c11 { - -webkit-flex: 0 0 70%; - -ms-flex: 0 0 70%; - flex: 0 0 70%; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - position: relative; -} - -.c13 { - -webkit-align-items: stretch; - -webkit-box-align: stretch; - -ms-flex-align: stretch; - align-items: stretch; - background: #fff; - border: 1px solid #668592; - box-sizing: border-box; - cursor: text; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-wrap: wrap; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - width: 100%; - margin: 0; - min-height: 40px; - padding-left: 11px; - padding-right: 11px; -} - -.c13 .c14:-webkit-autofill { - margin-top: 2px; -} - -.c13 input::-ms-clear { - display: none; -} - -.c13 input::-webkit-contacts-auto-fill-button { - display: none !important; -} - -.c17 .c14 { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.c17 .c12 { - cursor: pointer; - padding-right: 0; -} - -.c9 + .c8 { - margin-top: 16px; -} - -.c10 { - display: block; -} - -.c18 .c12 { - padding-left: 0; -} - -.c0 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0px 24px; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - border-top-width: 0; - font-size: 13px; - height: 39px; - max-height: 39px; - border-width: 1px 1px 1px 1px; - border-style: none solid solid solid; - border-color: #CCD6DB; - background-color: #FAFBFB; - border-top: none; -} - -.c0 .carbon-input-icon { - border: none; - background: none; -} - -.c0 .carbon-input-icon:hover { - background: none; - color: #000000; -} - -.c0 .common-input__field:focus-within { - outline: 3px solid #FFB500; -} - -.c0 .common-input__input { - border: 1px solid #668592; -} - -.c0 .common-input__input:active, -.c0 .common-input__input:hover, -.c0 .common-input__input:focus { - border: 1px solid #668592; -} - -.c0 .common-input__input:focus { - border: 1px solid #668592; - color: #000000; -} - -.c0 .common-input__input:focus ~ label .carbon-input-icon, -.c0 .common-input__input:focus ~ label .carbon-input-icon:hover { - border: none; - background: none; - color: #000000; -} - -.c0 .common-input__input:hover ~ label .carbon-input-icon { - border: none; - background: none; - color: #000000; -} - -.c0 .common-input__input:hover ~ label .carbon-input-icon:hover { - border: none; - background: none; -} - -.c1 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 30%; - -ms-flex: 1 1 30%; - flex: 1 1 30%; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c1 .c12 { - width: 55px; - height: 26px; - min-height: 26px; - min-width: 10px; - margin: 0px 4px; -} - -.c1 .c12 .c14 { - font-size: 14px; - padding-right: 0px; - padding-left: 8px; - height: 22px; - width: 13px; -} - -.c1 .c12 .c19 { - margin-left: 0; - width: 20px; - height: 24px; -} - -.c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.c2.c2 .c12 { - padding: 0; - margin: 8px 4px 0 4px; - height: 26px; - line-height: 26px; - min-height: 24px; -} - -.c2.c2 .c12 .c14 { - text-align: center; - height: 24px; - padding: 0; -} - -.c4 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0 12px; -} - -.c3 { - padding: 0 10px; - font-size: 13px; - border-width: 0; - background-color: transparent; - cursor: pointer; - margin-left: 7px; - margin-right: 7px; - color: rgba(0,0,0,0.90); - color: rgba(0,0,0,0.3); - cursor: not-allowed; -} - -.c3:focus { - outline: solid 3px #FFB500; - padding: 5px 10px; -} - -.c5 { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - white-space: nowrap; -} - -.c16 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1 1 30%; - -ms-flex: 1 1 30%; - flex: 1 1 30%; - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - -ms-flex-pack: end; - justify-content: flex-end; -} - -
-
-
- - -
- - Page - -
- -
- - of - 1 - -
- - -
-
- 100 - - items -
-
-`; diff --git a/src/components/pager/__internal__/pager-navigation-link.component.js b/src/components/pager/__internal__/pager-navigation-link.component.js index 1698ad4cea..39aca03a68 100644 --- a/src/components/pager/__internal__/pager-navigation-link.component.js +++ b/src/components/pager/__internal__/pager-navigation-link.component.js @@ -1,7 +1,7 @@ import React, { useRef, useEffect, useCallback } from "react"; import PropTypes from "prop-types"; import I18n from "i18n-js"; -import { StyledPagerLinkStyles } from "./pager.style"; +import { StyledPagerLinkStyles } from "../pager.style"; const PagerNavigationLink = ({ type, @@ -10,7 +10,6 @@ const PagerNavigationLink = ({ pageSize, onClick, onPagination, - ...props }) => { const linkRef = useRef(); const navLinkConfig = { @@ -33,10 +32,6 @@ const PagerNavigationLink = ({ }; const disabled = useCallback(() => { - if (pageCount === 1 || pageCount === 0) { - return true; - } - if (currentPage === 1) { return type === "previous" || type === "first"; } @@ -67,7 +62,6 @@ const PagerNavigationLink = ({ disabled={disabled()} onClick={handleOnCLick} ref={linkRef} - {...props} > {text} diff --git a/src/components/pager/__internal__/pager-navigation.component.js b/src/components/pager/__internal__/pager-navigation.component.js index b50e64f00b..8b6437f2b8 100644 --- a/src/components/pager/__internal__/pager-navigation.component.js +++ b/src/components/pager/__internal__/pager-navigation.component.js @@ -5,7 +5,7 @@ import { StyledPagerNavigation, StyledPagerNavInner, StyledPagerNoSelect, -} from "./pager.style"; +} from "../pager.style"; import NumberInput from "../../../__experimental__/components/number"; import Events from "../../../utils/helpers/events"; import createGuid from "../../../utils/helpers/guid"; @@ -22,10 +22,20 @@ const PagerNavigation = ({ onLast, onPagination, pageCount, - ...props + showFirstAndLastButtons = true, + showPreviousAndNextButtons = true, + showPageCount = true, }) => { const guid = useRef(createGuid()); const currentPageId = `Pager_${guid.current}`; + const hasOnePage = pageCount <= 1; + const hasTwoPages = pageCount === 2; + const pagerNavigationProps = { + currentPage, + pageSize, + pageCount, + onPagination, + }; const handlePageInputChange = (ev) => { if (pageCount === 0) { @@ -55,59 +65,71 @@ const PagerNavigation = ({ setCurrentPage(e.target.value); }; - const pagerNavigationLinkProps = { - currentPage, - pageSize, - pageCount, - onPagination, - }; + const renderButtonsBeforeCount = () => ( + <> + {!hasTwoPages && showFirstAndLastButtons && ( + + )} + {showPreviousAndNextButtons && ( + + )} + + ); + + const renderButtonsAfterCount = () => ( + <> + {showPreviousAndNextButtons && ( + + )} + {!hasTwoPages && showFirstAndLastButtons && ( + + )} + + ); return ( - - - - - - {I18n.t("pager.page_x", { defaultValue: "Page " })} - - + + {I18n.t("pager.of_y", { defaultValue: " of " })} + {pageCount} + + + )} + {!hasOnePage && renderButtonsAfterCount()} ); }; @@ -122,16 +144,22 @@ PagerNavigation.propTypes = { pageCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** Sets the current page being shown */ setCurrentPage: PropTypes.func, - /** onFirst Callback trigered when first link is triggered */ + /** onFirst Callback triggered when first link is triggered */ onFirst: PropTypes.func, - /** onPrevious Callback trigered when previous link is triggered */ + /** onPrevious Callback triggered when previous link is triggered */ onPrevious: PropTypes.func, - /** onNext Callback trigered when next link is triggered */ + /** onNext Callback triggered when next link is triggered */ onNext: PropTypes.func, - /** onLast Callback trigered when last link is triggered */ + /** onLast Callback triggered when last link is triggered */ onLast: PropTypes.func, - /** onPagination Callback trigered when a change is triggered */ + /** onPagination Callback triggered when a change is triggered */ onPagination: PropTypes.func, + /** Should the `First` and `Last` navigation buttons be shown */ + showFirstAndLastButtons: PropTypes.bool, + /** Should the `Next` and `Previous` navigation buttons be shown */ + showPreviousAndNextButtons: PropTypes.bool, + /** Should the page count input be shown */ + showPageCount: PropTypes.bool, }; export default PagerNavigation; diff --git a/src/components/pager/__internal__/pager-navigation.spec.js b/src/components/pager/__internal__/pager-navigation.spec.js index 287edc769e..d91760ebe4 100644 --- a/src/components/pager/__internal__/pager-navigation.spec.js +++ b/src/components/pager/__internal__/pager-navigation.spec.js @@ -1,8 +1,7 @@ import React from "react"; import { shallow, mount } from "enzyme"; -import "jest-styled-components"; import PagerNavigation from "./pager-navigation.component"; -import { StyledPagerLinkStyles } from "./pager.style"; +import { StyledPagerLinkStyles, StyledPagerNavInner } from "../pager.style"; import { assertStyleMatch } from "../../../__spec_helper__/test-utils"; import StyledInputPresentation from "../../../__experimental__/components/input/input-presentation.style"; import StyledInput from "../../../__experimental__/components/input/input.style"; @@ -13,7 +12,7 @@ const pageSizeSelectionOptions = [ { id: "50", name: 50 }, ]; -function render(props, renderType = shallow) { +function render(props = {}, renderType = shallow) { props.setCurrentThemeName = () => {}; return renderType(); } @@ -25,8 +24,6 @@ describe("Pager Navigation", () => { onPagination: () => true, pageSize: 10, pageCount: 10, - showPageSizeSelection: true, - pageSizeSelectionOptions, }; it("renders the Pager Navigation correctly with the Mint Theme", () => { @@ -75,7 +72,7 @@ describe("Pager Navigation", () => { describe("onClick", () => { it("does not trigger if link is disabled", () => { - const onLast = jest.fn(); + const onFirst = jest.fn(); const wrapper = render( { onPagination: () => true, @@ -83,16 +80,16 @@ describe("Pager Navigation", () => { showPageSizeSelection: true, pageSizeSelectionOptions, currentPage: 1, - pageCount: 1, + pageCount: 3, totalRecords: 1, - onLast, + onFirst, }, mount ); const navLinks = wrapper.find(StyledPagerLinkStyles); - const last = navLinks.last(); - last.simulate("click"); - expect(onLast).toBeCalledTimes(0); + const first = navLinks.first(); + first.simulate("click"); + expect(onFirst).toBeCalledTimes(0); }); }); @@ -181,4 +178,59 @@ describe("Pager Navigation", () => { expect(onPagination).toHaveBeenCalledWith(0, 10, "input"); }); }); + + describe("conditional rendering", () => { + it("hides the page count input when 'showPageCount' is false", () => { + const wrapper = render( + { + ...props, + currentPage: 1, + setCurrentPage: () => {}, + pageCount: 3, + showPageCount: false, + }, + mount + ); + + const navButtons = wrapper.find("button"); + expect(navButtons.length).toEqual(4); + expect(wrapper.find(StyledPagerNavInner).exists()).toBeFalsy(); + }); + + it("hides the `First` and `Last` buttons when 'showFirstAndLastButtons' is false", () => { + const wrapper = render( + { + ...props, + currentPage: 1, + setCurrentPage: () => {}, + pageCount: 3, + showFirstAndLastButtons: false, + }, + mount + ); + + const navButtons = wrapper.find("button"); + expect(navButtons.length).toEqual(2); + expect(navButtons.first().text()).toEqual("Previous"); + expect(navButtons.last().text()).toEqual("Next"); + }); + + it("hides the `Previous` and `Next` buttons when 'showPreviousAndNextButtons' is false", () => { + const wrapper = render( + { + ...props, + currentPage: 1, + setCurrentPage: () => {}, + pageCount: 3, + showPreviousAndNextButtons: false, + }, + mount + ); + + const navButtons = wrapper.find("button"); + expect(navButtons.length).toEqual(2); + expect(navButtons.first().text()).toEqual("First"); + expect(navButtons.last().text()).toEqual("Last"); + }); + }); }); diff --git a/src/components/pager/__internal__/pager.stories.mdx b/src/components/pager/__internal__/pager.stories.mdx deleted file mode 100644 index d25b3aa497..0000000000 --- a/src/components/pager/__internal__/pager.stories.mdx +++ /dev/null @@ -1,148 +0,0 @@ -import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks'; -import Pager from '..'; -import { useState } from 'react'; -import { - text, - boolean, - select, - withKnobs, - object -} from '@storybook/addon-knobs'; -import { action } from '@storybook/addon-actions'; - - - -# Pager -Pagination Component. - -### Default usage: - - - - {() => { - const totalRecords = text('totalRecords', '100'); - const showPageSizeSelection = boolean( - 'showPageSizeSelection', - Pager.defaultProps.showPageSizeSelection - ); - const pageSize = select('pageSize', { - one: 1, - 10: 10, - 25: 25, - 50: 50, - 100: 100 - }, Pager.defaultProps.pageSize); - const currentPage = text('currentPage', '1'); - const pageSizeSelectionOptions = object('pageSizeSelectionOptions', - [ - { id: '1', name: 1 }, - { id: '10', name: 10 }, - { id: '25', name: 25 }, - { id: '50', name: 50 }, - { id: '100', name: 100 } - ] - ); - return ( - {} } - pageSizeSelectionOptions={ pageSizeSelectionOptions } - /> - ); - }} - - - -### With disabled Page Size Selection: - - - - {} } - /> - - - -### Loading state -When no totalRecords provided. - - - - {} } - /> - - - -### Custom pageSizeSelectionOptions; -Shows 1, 2 or 5 items per page. - - - - {} } - totalRecords={ 100 } - showPageSizeSelection={ true } - pageSizeSelectionOptions={ - [ - { id: '1', name: 1 }, - { id: '2', name: 2 }, - { id: '5', name: 5 } - ] - } - pageSize={ 1 } - /> - - - -### With currentPage pre-set to last page; -Due to the fact that it is last page, next and last links are disabled. - - - - {} } - totalRecords={ 100 } - showPageSizeSelection={ true } - currentPage={ 10 } - /> - - - -### With currentPage pre-set to page 5 - - - - {} } - totalRecords={ 100 } - showPageSizeSelection={ true } - currentPage={ 5 } - /> - - - -### Alternate background - - - - {} } - variant="alternate" - /> - - - -## Props: - -### Pager - - diff --git a/src/components/pager/index.d.ts b/src/components/pager/index.d.ts index dce98230af..203d5f7bd1 100644 --- a/src/components/pager/index.d.ts +++ b/src/components/pager/index.d.ts @@ -1,2 +1,2 @@ -export { default } from './__internal__/pager'; -export * from './__internal__/pager'; +export { default } from './pager'; +export * from './pager'; diff --git a/src/components/pager/index.js b/src/components/pager/index.js index e7b24943c0..4e0c737c6c 100644 --- a/src/components/pager/index.js +++ b/src/components/pager/index.js @@ -1 +1 @@ -export { default } from "./__internal__/pager.component"; +export { default } from "./pager.component"; diff --git a/src/components/pager/__internal__/pager.component.js b/src/components/pager/pager.component.js similarity index 62% rename from src/components/pager/__internal__/pager.component.js rename to src/components/pager/pager.component.js index 6a9d43643b..eeed1bd594 100644 --- a/src/components/pager/__internal__/pager.component.js +++ b/src/components/pager/pager.component.js @@ -1,8 +1,8 @@ import React, { useState, useCallback, useEffect } from "react"; import PropTypes from "prop-types"; import I18n from "i18n-js"; -import PagerNavigation from "./pager-navigation.component"; -import Option from "../../select/option/option.component"; +import PagerNavigation from "./__internal__/pager-navigation.component"; +import Option from "../select/option/option.component"; import { StyledPagerContainer, StyledPagerSizeOptions, @@ -12,36 +12,48 @@ import { } from "./pager.style"; const Pager = ({ - currentPage, - pageSizeSelectionOptions, - pageSize, - showPageSizeSelection, - totalRecords, + currentPage = 1, + pageSizeSelectionOptions = [ + { id: "10", name: 10 }, + { id: "25", name: 25 }, + { id: "50", name: 50 }, + { id: "100", name: 100 }, + ], + pageSize = 10, + showPageSizeSelection = false, + totalRecords = 0, onPagination, onNext, onFirst, onPrevious, onLast, - variant, - ...props + showPageSizeLabelBefore = true, + showPageSizeLabelAfter = true, + showTotalRecords = true, + showFirstAndLastButtons = true, + showPreviousAndNextButtons = true, + showPageCount = true, + variant = "default", + ...rest }) => { - const [pageCount, setPageCount] = useState(1); - const [page, setPage] = useState(""); + const [page, setPage] = useState(currentPage); const [currentPageSize, setCurrentPageSize] = useState(pageSize); + const getPageCount = useCallback(() => { + if (Number(totalRecords) < 0 || Number.isNaN(Number(totalRecords))) { + return 1; + } + return Math.ceil(totalRecords / currentPageSize); + }, [totalRecords, currentPageSize]); + + const [pageCount, setPageCount] = useState(getPageCount()); + useEffect(() => { setCurrentPageSize(Number(pageSize)); }, [pageSize]); useEffect(() => { - let maxPage; - - if (Number(totalRecords) < 0 || Number.isNaN(Number(totalRecords))) { - maxPage = 1; - } else { - maxPage = Math.ceil(totalRecords / currentPageSize); - } - + const maxPage = getPageCount(); setPageCount(maxPage); if (Number(currentPage) > maxPage) { @@ -49,7 +61,7 @@ const Pager = ({ } else { setPage(Number(currentPage)); } - }, [currentPageSize, pageCount, currentPage, totalRecords]); + }, [currentPageSize, pageCount, currentPage, totalRecords, getPageCount]); /** Term used to describe table data */ const records = (count) => @@ -136,24 +148,31 @@ const Pager = ({ ); }; - const pageSizeOptions = () => { + const renderPageSizeOptions = () => { const show = I18n.t("pager.show", { defaultValue: "Show" }); - const elem = ( - - {show} - {sizeSelector()} - {records(currentPageSize)} - - ); - return showPageSizeSelection ? elem : null; + return ( + showPageSizeSelection && ( + + {showPageSizeLabelBefore && {show}} + {sizeSelector()} + {showPageSizeLabelAfter && {records(currentPageSize)}} + + ) + ); }; + const renderTotalRecords = () => + showTotalRecords && ( + <> + {totalRecords} {records(totalRecords)} + + ); + return ( - - {pageSizeOptions()} + + {renderPageSizeOptions()} - - {totalRecords} {records(totalRecords)} - + {renderTotalRecords()} ); }; @@ -174,13 +194,13 @@ const Pager = ({ Pager.propTypes = { /** Function called when pager changes (PageSize, Current Page) */ onPagination: PropTypes.func.isRequired, - /** Callback function for next link */ + /** Callback function for next link */ onNext: PropTypes.func, - /** Callback function for first link */ + /** Callback function for first link */ onFirst: PropTypes.func, - /** Callback function for previous link */ + /** Callback function for previous link */ onPrevious: PropTypes.func, - /** Callback function for last link */ + /** Callback function for last link */ onLast: PropTypes.func, /** Current visible page */ currentPage: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), @@ -188,8 +208,6 @@ Pager.propTypes = { totalRecords: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** Pagination page size */ pageSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** Should the page size selection dropdown be shown */ - showPageSizeSelection: PropTypes.bool, /** Set of page size options */ pageSizeSelectionOptions: PropTypes.arrayOf( PropTypes.shape({ @@ -197,22 +215,22 @@ Pager.propTypes = { name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), }) ), + /** Should the page size selection dropdown be shown */ + showPageSizeSelection: PropTypes.bool, + /** Should the label before the page size selection dropdown be shown */ + showPageSizeLabelBefore: PropTypes.bool, + /** Should the label after the page size selection dropdown be shown */ + showPageSizeLabelAfter: PropTypes.bool, + /** Should the total records label be shown */ + showTotalRecords: PropTypes.bool, + /** Should the `First` and `Last` navigation button be shown */ + showFirstAndLastButtons: PropTypes.bool, + /** Should the `Previous` and `Next` navigation button be shown */ + showPreviousAndNextButtons: PropTypes.bool, + /** Should the page count input be shown */ + showPageCount: PropTypes.bool, /** What variant the Pager background should be */ variant: PropTypes.oneOf(["default", "alternate"]), }; -Pager.defaultProps = { - currentPage: 1, - pageSize: 10, - totalRecords: 0, - showPageSizeSelection: false, - pageSizeSelectionOptions: [ - { id: "10", name: 10 }, - { id: "25", name: 25 }, - { id: "50", name: 50 }, - { id: "100", name: 100 }, - ], - variant: "default", -}; - export default Pager; diff --git a/src/components/pager/__internal__/pager.d.ts b/src/components/pager/pager.d.ts similarity index 70% rename from src/components/pager/__internal__/pager.d.ts rename to src/components/pager/pager.d.ts index 30d612bdd1..d2e074ab9b 100644 --- a/src/components/pager/__internal__/pager.d.ts +++ b/src/components/pager/pager.d.ts @@ -11,6 +11,12 @@ export interface PagerPropTypes { pageSize?: number | string; showPageSizeSelection?: boolean; pageSizeSelectionOptions?: object; + showPageSizeLabelBefore?: boolean; + showPageSizeLabelAfter?: boolean; + showTotalRecords?: boolean; + showFirstAndLastButtons?: boolean; + showPreviousAndNextButtons?: boolean; + showPageCount?: boolean; variant?: "default" | "alternate"; } diff --git a/src/components/pager/__internal__/pager.spec.js b/src/components/pager/pager.spec.js similarity index 70% rename from src/components/pager/__internal__/pager.spec.js rename to src/components/pager/pager.spec.js index e27ad97d3a..9ff699b9a5 100644 --- a/src/components/pager/__internal__/pager.spec.js +++ b/src/components/pager/pager.spec.js @@ -1,20 +1,21 @@ import React from "react"; import { mount } from "enzyme"; import { act } from "react-dom/test-utils"; -import { ThemeProvider } from "styled-components"; -import TestRenderer from "react-test-renderer"; import I18n from "i18n-js"; -import guid from "../../../utils/helpers/guid"; -import { assertStyleMatch } from "../../../__spec_helper__/test-utils"; -import baseTheme from "../../../style/themes/base"; -import mintTheme from "../../../style/themes/mint"; +import guid from "../../utils/helpers/guid"; +import { assertStyleMatch } from "../../__spec_helper__/test-utils"; +import baseTheme from "../../style/themes/base"; import Pager from "./pager.component"; -import Select from "../../select/simple-select/simple-select.component"; -import SelectList from "../../select/select-list/select-list.component"; -import { StyledPagerLinkStyles } from "./pager.style"; -import NumberInput from "../../../__experimental__/components/number"; - -jest.mock("../../../utils/helpers/guid"); +import Select from "../select/simple-select/simple-select.component"; +import SelectList from "../select/select-list/select-list.component"; +import { + StyledPagerLinkStyles, + StyledPagerSizeOptionsInner, + StyledPagerSummary, +} from "./pager.style"; +import NumberInput from "../../__experimental__/components/number"; + +jest.mock("../../utils/helpers/guid"); guid.mockImplementation(() => "guid-12345"); const pageSizeSelectionOptions = [ @@ -24,12 +25,8 @@ const pageSizeSelectionOptions = [ { id: "100", name: 100 }, ]; -function render(props, renderType = mount) { - return renderType( - - - - ); +function render(props = {}, renderType = mount) { + return renderType(); } describe("Pager", () => { @@ -41,8 +38,13 @@ describe("Pager", () => { pageSizeSelectionOptions, }; + it("sets total records to 0 by default", () => { + const wrapper = render(); + expect(wrapper.find(StyledPagerSummary).text()).toBe("0 items"); + }); + describe("Navigate correctly on link click", () => { - let onPagination, onNext, onPrevious, onFirst, onLast; + let wrapper, onPagination, onNext, onPrevious, onFirst, onLast; beforeEach(() => { onPagination = jest.fn(); @@ -52,6 +54,8 @@ describe("Pager", () => { onLast = jest.fn(); }); + afterEach(() => wrapper.unmount()); + const getWrapper = (otherProps) => render( { @@ -67,7 +71,7 @@ describe("Pager", () => { ); it("disables the next and last link if on last page", () => { - const wrapper = getWrapper({ currentPage: 10 }); + wrapper = getWrapper({ currentPage: 10 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const last = navLinks.last(); const next = navLinks.at(2); @@ -77,7 +81,7 @@ describe("Pager", () => { }); it("disables the prev and first link if on first page", () => { - const wrapper = getWrapper({ currentPage: 1 }); + wrapper = getWrapper({ currentPage: 1 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const first = navLinks.first(); const prev = navLinks.at(1); @@ -86,22 +90,22 @@ describe("Pager", () => { expect(onPagination).not.toHaveBeenCalled(); }); - it("disables navigation if theres only one page", () => { - const wrapper = getWrapper({ currentPage: 1, totalRecords: 5 }); + it("does not render the navigation buttons if theres only one page", () => { + wrapper = getWrapper({ currentPage: 1, totalRecords: 5 }); const navLinks = wrapper.find(StyledPagerLinkStyles); - const first = navLinks.first(); - const prev = navLinks.at(1); - const next = navLinks.at(2); - const last = navLinks.last(); - first.simulate("click"); - prev.simulate("click"); - next.simulate("click"); - last.simulate("click"); - expect(onPagination).not.toHaveBeenCalled(); + expect(navLinks.exists()).toBeFalsy(); + }); + + it("does not render the 'First' and 'Last' navigation buttons if theres only two pages", () => { + wrapper = getWrapper({ currentPage: 1, totalRecords: 20 }); + const navLinks = wrapper.find(StyledPagerLinkStyles); + expect(navLinks.length).toEqual(2); + expect(navLinks.first().find("button").text()).toEqual("Previous"); + expect(navLinks.last().find("button").text()).toEqual("Next"); }); it("changes page correctly on clicking first link", () => { - const wrapper = getWrapper({ currentPage: 10 }); + wrapper = getWrapper({ currentPage: 10 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const first = navLinks.first(); first.simulate("click"); @@ -109,7 +113,7 @@ describe("Pager", () => { }); it("changes page correctly on clicking prev link", () => { - const wrapper = getWrapper({ currentPage: 3 }); + wrapper = getWrapper({ currentPage: 3 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const prev = navLinks.at(1); prev.simulate("click"); @@ -117,7 +121,7 @@ describe("Pager", () => { }); it("changes page correctly on clicking next link", () => { - const wrapper = getWrapper({ currentPage: 3 }); + wrapper = getWrapper({ currentPage: 3 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const next = navLinks.at(2); next.simulate("click"); @@ -125,7 +129,7 @@ describe("Pager", () => { }); it("next link is disabled on when on last page", () => { - const wrapper = getWrapper({ currentPage: 10 }); + wrapper = getWrapper({ currentPage: 10 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const next = navLinks.at(2); assertStyleMatch( @@ -137,7 +141,7 @@ describe("Pager", () => { }); it("changes page correctly on clicking last link", () => { - const wrapper = getWrapper({ currentPage: 3 }); + wrapper = getWrapper({ currentPage: 3 }); const navLinks = wrapper.find(StyledPagerLinkStyles); const last = navLinks.last(); last.simulate("click"); @@ -204,25 +208,86 @@ describe("Pager", () => { expect(input.prop("value")).toBe("10"); }); - it("renders the Pager without pageSizeSelection", () => { - const wrapper = render( - { + describe("conditional rendering of elements", () => { + let wrapper; + + afterEach(() => wrapper.unmount()); + + it("does not renders pageSizeSelection by default", () => { + wrapper = render({ totalRecords: 100, pageSize: 10, - showPageSizeSelection: false, onPagination: () => true, - }, - TestRenderer.create - ); - expect(wrapper).toMatchSnapshot(); - }); + }); + expect(wrapper.find(StyledPagerSizeOptionsInner).exists()).toBeFalsy(); + }); - it("renders the Pager correctly with the Mint Theme", () => { - const wrapper = render( - { ...props, theme: mintTheme, onPagination: () => true }, - TestRenderer.create - ); - expect(wrapper).toMatchSnapshot(); + it("renders the pageSize Select when 'showPageSizeSelection' is true", () => { + wrapper = render({ ...props, onPagination: () => true }); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.firstChild.textContent + ).toEqual( + wrapper.find(StyledPagerSizeOptionsInner).find("span").first().text() + ); + expect(wrapper.find(StyledPagerSizeOptionsInner).exists()).toBeTruthy(); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.lastChild.textContent + ).toEqual( + wrapper.find(StyledPagerSizeOptionsInner).find("span").last().text() + ); + }); + + it("does not render the label before pageSize Select when 'showPageSizeLabelBefore' is false", () => { + wrapper = render({ + ...props, + showPageSizeLabelBefore: false, + onPagination: () => true, + }); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.firstChild + ).toEqual( + wrapper.find("div[data-component='simple-select']").getDOMNode() + ); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.lastChild.textContent + ).toEqual( + wrapper.find(StyledPagerSizeOptionsInner).find("span").last().text() + ); + }); + + it("does not render the label after pageSize Select when 'showPageSizeLabelAfter' is false", () => { + wrapper = render({ + ...props, + showPageSizeLabelAfter: false, + onPagination: () => true, + }); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.firstChild.textContent + ).toEqual( + wrapper.find(StyledPagerSizeOptionsInner).find("span").first().text() + ); + expect( + wrapper.find("div[data-component='simple-select']").getDOMNode() + .parentElement.lastChild + ).toEqual( + wrapper.find("div[data-component='simple-select']").getDOMNode() + ); + }); + + it("does not render the total number of records 'showTotalRecords' is false", () => { + wrapper = render({ + ...props, + showTotalRecords: false, + onPagination: () => true, + }); + + expect(wrapper.find(StyledPagerSummary).text()).toBe(""); + }); }); describe("callbacks work as expected", () => { diff --git a/src/components/pager/__internal__/pager.stories.js b/src/components/pager/pager.stories.js similarity index 67% rename from src/components/pager/__internal__/pager.stories.js rename to src/components/pager/pager.stories.js index db9aa3579b..a3baf9933c 100644 --- a/src/components/pager/__internal__/pager.stories.js +++ b/src/components/pager/pager.stories.js @@ -1,7 +1,7 @@ import React from "react"; import { text, boolean, select, withKnobs } from "@storybook/addon-knobs"; import { action } from "@storybook/addon-actions"; -import Pager from ".."; +import Pager from "."; export default { title: "Design System/Pager/Test", @@ -40,20 +40,17 @@ export const Default = () => { }; const totalRecords = text("totalRecords", "100"); - const showPageSizeSelection = boolean( - "showPageSizeSelection", - Pager.defaultProps.showPageSizeSelection - ); + const showPageSizeSelection = boolean("showPageSizeSelection", false); const pageSize = select( "pageSize", { - one: 1, + 1: 1, 10: 10, 25: 25, 50: 50, 100: 100, }, - Pager.defaultProps.pageSize + 10 ); const currentPage = text("currentPage", "1"); const variant = select( @@ -64,6 +61,16 @@ export const Default = () => { }, "default" ); + const showPageSizeLabelBefore = boolean("showPageSizeLabelBefore", true); + const showPageSizeLabelAfter = boolean("showPageSizeLabelAfter", true); + const showTotalRecords = boolean("showTotalRecords", true); + const showFirstAndLastButtons = boolean("showFirstAndLastButtons", true); + const showPreviousAndNextButtons = boolean( + "showPreviousAndNextButtons", + true + ); + const showPageCount = boolean("showPageCount", true); + return ( { { id: "100", name: 100 }, ]} variant={variant} + showPageSizeLabelBefore={showPageSizeLabelBefore} + showPageSizeLabelAfter={showPageSizeLabelAfter} + showTotalRecords={showTotalRecords} + showFirstAndLastButtons={showFirstAndLastButtons} + showPreviousAndNextButtons={showPreviousAndNextButtons} + showPageCount={showPageCount} /> ); }; diff --git a/src/components/pager/pager.stories.mdx b/src/components/pager/pager.stories.mdx new file mode 100644 index 0000000000..34fc8f7491 --- /dev/null +++ b/src/components/pager/pager.stories.mdx @@ -0,0 +1,251 @@ +import { Meta, Props, Preview, Story } from "@storybook/addon-docs/blocks"; +import Pager from "."; +import { useState } from "react"; +import { + text, + boolean, + select, + withKnobs, + object +} from "@storybook/addon-knobs"; +import { action } from "@storybook/addon-actions"; +import useMediaQuery from "../../hooks/useMediaQuery/index.js" + + + +# Pager +Pagination Component. + +## Quick Start + +```jsx +import Pager from "carbon-react/lib/components/pager" +``` + +### Default + + + + {() => { + const totalRecords = text("totalRecords", "100"); + const showPageSizeSelection = boolean( + "showPageSizeSelection", + false + ); + const pageSize = select("pageSize", { + one: 1, + 10: 10, + 25: 25, + 50: 50, + 100: 100 + }, 10); + const currentPage = text("currentPage", "1"); + const pageSizeSelectionOptions = object("pageSizeSelectionOptions", + [ + { id: "1", name: 1 }, + { id: "10", name: 10 }, + { id: "25", name: 25 }, + { id: "50", name: 50 }, + { id: "100", name: 100 } + ] + ); + return ( + {} } + pageSizeSelectionOptions={ pageSizeSelectionOptions } + /> + ); + }} + + + +### With disabled Page Size Selection + + + + {} } + /> + + + +### Hiding Pager elements +Unlike with hiding the page size select component, the remaining elements that make up the `Pager` are rendered by +default: in order for them to be hidden you must pass `false` to the corresponding `show...` prop (see prop table below). +In the example below the `First` and `Last` buttons and `totalRecords` label have been hidden by setting the +`showFirstAndLastButtons` and `showTotalRecords` props to false. + + + + {} } + showFirstAndLastButtons={ false } + showTotalRecords={ false } + showPageSizeSelection={ true } + /> + + + +### Smart elements +The examples below demonstrate the `Pager`'s smart functionality. In the first there are only enough records for one page, +and as such the navigation buttons are not neccessary and therefore not rendered. In the second example there are only enough +records for two pages, the `First` and `Last` buttons are not needed and as a result will not render. + + + + <> + {} } + /> +
+ {} } + /> + +
+
+ +### Loading state +When no totalRecords provided. + + + + {} } + /> + + + +### Custom pageSizeSelectionOptions +Shows 1, 2 or 5 items per page. + + + + {} } + totalRecords={ 100 } + showPageSizeSelection={ true } + pageSizeSelectionOptions={ + [ + { id: "1", name: 1 }, + { id: "2", name: 2 }, + { id: "5", name: 5 } + ] + } + pageSize={ 1 } + /> + + + +### With currentPage pre-set to last page +Due to the fact that it is last page, next and last links are disabled. + + + + {} } + totalRecords={ 100 } + showPageSizeSelection={ true } + currentPage={ 10 } + /> + + + +### With currentPage pre-set to page 5 + + + + {} } + totalRecords={ 100 } + showPageSizeSelection={ true } + currentPage={ 5 } + /> + + + +### Custom responsive example +The`show...` props can also be used to implement responsive behaviour in the `Pager`. Below is an example that will +conditionally render the interanl elements as the screen size is adjusted. This example is best viewed in the Canvas tab +using full-screen mode with device or viewport emulation. + + + + {() => { + const query1 = useMediaQuery('(max-width: 1000px)'); + const query2 = useMediaQuery('(max-width: 900px)'); + const query3 = useMediaQuery('(max-width: 800px)'); + const query4 = useMediaQuery('(max-width: 700px)'); + const query5 = useMediaQuery('(max-width: 600px)'); + const responsiveProps = () => { + if (query5) { + return { + showPageSizeSelection: false, + showTotalRecords: false, + showFirstAndLastButtons: false + }; + } + if (query4) { + return { + showFirstAndLastButtons: false, + showTotalRecords: false, + showPageSizeLabelBefore: false, + showPageSizeLabelAfter: false + }; + } + if (query3) { + return { + showFirstAndLastButtons: false, + showTotalRecords: false, + showPageSizeLabelBefore: false, + showPageSizeLabelAfter: false + }; + } + if (query2) { + return { + showFirstAndLastButtons: false, + showTotalRecords: false + }; + } + if (query1) { + return { + showPageSizeSelection: true, + showFirstAndLastButtons: false }; + } + return { + showPageSizeSelection: true + }; + } + return ( + {} } + { ...responsiveProps() } + pageSizeSelectionOptions={ [ + { id: "1", name: 1 }, + { id: "10", name: 10 }, + { id: "25", name: 25 }, + { id: "50", name: 50 }, + { id: "100", name: 100 } + ] } + /> + ); + }} + + + +## Props + +### Pager + + diff --git a/src/components/pager/__internal__/pager.style.js b/src/components/pager/pager.style.js similarity index 63% rename from src/components/pager/__internal__/pager.style.js rename to src/components/pager/pager.style.js index 3fa70e63f0..b507de66ac 100644 --- a/src/components/pager/__internal__/pager.style.js +++ b/src/components/pager/pager.style.js @@ -1,9 +1,9 @@ import styled, { css } from "styled-components"; -import StyledInput from "../../../__experimental__/components/input/input.style"; -import StyledInputPresentation from "../../../__experimental__/components/input/input-presentation.style"; -import InputIconToggleStyle from "../../../__experimental__/components/input-icon-toggle/input-icon-toggle.style"; -import Select from "../../select/simple-select/simple-select.component"; -import baseTheme from "../../../style/themes/base"; +import StyledInput from "../../__experimental__/components/input/input.style"; +import StyledInputPresentation from "../../__experimental__/components/input/input-presentation.style"; +import InputIconToggleStyle from "../../__experimental__/components/input-icon-toggle/input-icon-toggle.style"; +import Select from "../select/simple-select/simple-select.component"; +import baseTheme from "../../style/themes/base"; const StyledSelect = styled(Select)` height: 26px; @@ -22,71 +22,67 @@ const StyledPagerContainer = styled.div` font-size: 13px; height: 39px; max-height: 39px; + border-top: none; - ${({ theme, variant }) => { - return ( - theme.table && - theme.colors && - css` - border-width: 1px 1px 1px 1px; - border-style: none solid solid solid; - border-color: ${theme.table.secondary}; - background-color: ${variant === "alternate" - ? theme.pager.alternate - : theme.table.zebra}; - - .carbon-input-icon { - border: none; + ${({ theme, variant }) => + theme.table && + theme.colors && + css` + border-width: 1px 1px 1px 1px; + border-style: none solid solid solid; + border-color: ${theme.table.secondary}; + background-color: ${variant === "alternate" + ? theme.pager.alternate + : theme.table.zebra}; + + .carbon-input-icon { + border: none; + background: none; + + &:hover { background: none; - - &:hover { - background: none; - color: ${theme.colors.black}; - } + color: ${theme.colors.black}; } + } - .common-input__field:focus-within { - outline: 3px solid ${theme.colors.focus}; - } + .common-input__field:focus-within { + outline: 3px solid ${theme.colors.focus}; + } - .common-input__input { - border: 1px solid ${theme.colors.border}; + .common-input__input { + border: 1px solid ${theme.colors.border}; - &:active, - &:hover, - &:focus { - border: 1px solid ${theme.colors.border}; - } + &:active, + &:hover, + &:focus { + border: 1px solid ${theme.colors.border}; + } - &:focus { - border: 1px solid ${theme.colors.border}; - color: ${theme.colors.black}; - } + &:focus { + border: 1px solid ${theme.colors.border}; + color: ${theme.colors.black}; + } - &:focus ~ label .carbon-input-icon, - &:focus ~ label .carbon-input-icon:hover { - border: none; - background: none; - color: ${theme.colors.black}; - } + &:focus ~ label .carbon-input-icon, + &:focus ~ label .carbon-input-icon:hover { + border: none; + background: none; + color: ${theme.colors.black}; + } - &:hover ~ label .carbon-input-icon { - border: none; - background: none; - color: ${theme.colors.black}; - } + &:hover ~ label .carbon-input-icon { + border: none; + background: none; + color: ${theme.colors.black}; + } - &:hover ~ label .carbon-input-icon:hover { - border: none; - background: none; - } + &:hover ~ label .carbon-input-icon:hover { + border: none; + background: none; } - ` - ); - }} - border-top: none; + } + `} `; - const StyledPagerSizeOptions = styled.div` display: flex; flex: 1 1 30%; From a9a8767b16314a63c751703b5f8c8a7818cd4516 Mon Sep 17 00:00:00 2001 From: Ed Leeks Date: Wed, 10 Feb 2021 09:56:49 +0000 Subject: [PATCH 2/2] chore(pager): update pager type definitions to include callback params fix #3561 --- src/components/pager/pager.d.ts | 10 +++++----- src/components/pager/pager.stories.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/pager/pager.d.ts b/src/components/pager/pager.d.ts index d2e074ab9b..311ec218b5 100644 --- a/src/components/pager/pager.d.ts +++ b/src/components/pager/pager.d.ts @@ -1,11 +1,11 @@ import * as React from 'react'; export interface PagerPropTypes { - onPagination: () => void; - onNext?: () => void; - onFirst?: () => void; - onPrevious?: () => void; - onLast?: () => void; + onPagination: (pageSize: number, currentPage: number) => void; + onNext?: (ev: React.MouseEvent) => void; + onFirst?: (ev: React.MouseEvent) => void; + onPrevious?: (ev: React.MouseEvent) => void; + onLast?: (ev: React.MouseEvent) => void; currentPage?: number | string; totalRecords?: number | string; pageSize?: number | string; diff --git a/src/components/pager/pager.stories.js b/src/components/pager/pager.stories.js index a3baf9933c..5376c7d311 100644 --- a/src/components/pager/pager.stories.js +++ b/src/components/pager/pager.stories.js @@ -44,7 +44,7 @@ export const Default = () => { const pageSize = select( "pageSize", { - 1: 1, + one: 1, 10: 10, 25: 25, 50: 50,