diff --git a/docs/src/pages/components/pagination/PaginationPropControls.tsx b/docs/src/pages/components/pagination/PaginationPropControls.tsx new file mode 100644 index 00000000000..34ce78c4fb0 --- /dev/null +++ b/docs/src/pages/components/pagination/PaginationPropControls.tsx @@ -0,0 +1,81 @@ +import { + Flex, + PaginationProps, + StepperField, + SwitchField, +} from '@aws-amplify/ui-react'; + +export interface PaginationPropControlsProps extends PaginationProps { + setCurrentPage: ( + value: React.SetStateAction + ) => void; + setTotalPages: ( + value: React.SetStateAction + ) => void; + setSiblingCount: ( + value: React.SetStateAction + ) => void; + setHasMorePages: ( + value: React.SetStateAction + ) => void; + onNext: () => void; + onPrevious: () => void; + onChange: (newPageIndex: number, prevPageIndex: number) => void; +} + +interface PaginationPropControlsInterface { + (props: PaginationPropControlsProps): JSX.Element; +} + +export const PaginationPropControls: PaginationPropControlsInterface = ({ + currentPage, + setCurrentPage, + totalPages, + setTotalPages, + siblingCount, + setSiblingCount, + hasMorePages, + setHasMorePages, +}) => { + return ( + + + + + + + + + setHasMorePages( + event.target.checked as PaginationProps['hasMorePages'] + ) + } + /> + + ); +}; diff --git a/docs/src/pages/components/pagination/demo.tsx b/docs/src/pages/components/pagination/demo.tsx index b08b089d3d7..ccf4abcd38d 100644 --- a/docs/src/pages/components/pagination/demo.tsx +++ b/docs/src/pages/components/pagination/demo.tsx @@ -1,122 +1,41 @@ -import React, { useState, useCallback } from 'react'; +import { Pagination } from '@aws-amplify/ui-react'; +import { Demo } from '@/components/Demo'; +import { PaginationPropControls } from './PaginationPropControls'; +import { usePaginationProps } from './usePaginationProps'; -import { Flex, Pagination } from '@aws-amplify/ui-react'; -import { Example } from '@/components/Example'; - -interface PaginationDemoProps { - isDemo?: boolean; - defaultCurrentPage?: number; - defaultTotalPages?: number; - defaultSiblingCount?: number; - defaultHasMorePages?: boolean; -} -export const PaginationDemo: React.FC = (props) => { - const { - isDemo = true, - defaultCurrentPage = 1, - defaultTotalPages = 10, - defaultSiblingCount = 1, - defaultHasMorePages = false, - ...rest - } = props; - - const [currentPage, setCurrentPage] = useState(defaultCurrentPage); - const [totalPages, setTotalPages] = useState(defaultTotalPages); - const [siblingCount, setSiblingCount] = useState(defaultSiblingCount); - const [hasMorePages, setHasMorePages] = useState(defaultHasMorePages); - - const onNext = useCallback(() => { - if (currentPage < totalPages) { - setCurrentPage(currentPage + 1); - } - }, [currentPage, totalPages]); +const propsToCode = (paginationProps) => { + return ( + `` + ); +}; - const onPrev = useCallback(() => { - if (currentPage > 1) { - setCurrentPage(currentPage - 1); - } - }, [currentPage]); +export const PaginationDemo = () => { + const paginationProps = usePaginationProps({ + currentPage: 1, + totalPages: 10, + siblingCount: 1, + hasMorePages: false, + }); - const onChange = useCallback((newPage, prevPage) => { - setCurrentPage(newPage); - }, []); return ( -
- {isDemo ? ( - - - - - - - - - { - const newCurrentPage = isNaN(Number(e.target.value)) - ? defaultCurrentPage - : Number(e.target.value); - setCurrentPage(newCurrentPage); - }} - /> - - - - { - const newTotalPages = isNaN(Number(e.target.value)) - ? defaultTotalPages - : Number(e.target.value); - setTotalPages(newTotalPages); - }} - /> - - - - { - const newSiblingCount = isNaN(Number(e.target.value)) - ? defaultSiblingCount - : Number(e.target.value); - setSiblingCount(newSiblingCount); - }} - /> - - - - { - setHasMorePages(!hasMorePages); - }} - /> - - - ) : null} -
+ } + > + + ); }; diff --git a/docs/src/pages/components/pagination/examples/ControlledPaginationExample.tsx b/docs/src/pages/components/pagination/examples/ControlledPaginationExample.tsx new file mode 100644 index 00000000000..5c94d5a93cf --- /dev/null +++ b/docs/src/pages/components/pagination/examples/ControlledPaginationExample.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { Pagination } from '@aws-amplify/ui-react'; + +export const ControlledPaginationExample = () => { + const [currentPageIndex, setCurrentPageIndex] = React.useState(1); + const totalPages = 5; + + const handleNextPage = () => { + console.log('handleNextPage'); + setCurrentPageIndex(currentPageIndex + 1); + }; + + const handlePreviousPage = () => { + console.log('handlePreviousPage'); + setCurrentPageIndex(currentPageIndex - 1); + }; + + const handleOnChange = (newPageIndex, prevPageIndex) => { + console.log( + `handleOnChange \n - newPageIndex: ${newPageIndex} \n - prevPageIndex: ${prevPageIndex}` + ); + setCurrentPageIndex(newPageIndex); + }; + + return ( + + ); +}; diff --git a/docs/src/pages/components/pagination/examples/DefaultPaginationExample.tsx b/docs/src/pages/components/pagination/examples/DefaultPaginationExample.tsx new file mode 100644 index 00000000000..9ca7c79085b --- /dev/null +++ b/docs/src/pages/components/pagination/examples/DefaultPaginationExample.tsx @@ -0,0 +1,7 @@ +import { Pagination, usePagination } from '@aws-amplify/ui-react'; + +export const DefaultPaginationExample = () => { + const paginationProps = usePagination({ totalPages: 8 }); + + return ; +}; diff --git a/docs/src/pages/components/pagination/examples/PaginationHasMorePagesExample.tsx b/docs/src/pages/components/pagination/examples/PaginationHasMorePagesExample.tsx index 7c4bed635a3..9501ce31591 100644 --- a/docs/src/pages/components/pagination/examples/PaginationHasMorePagesExample.tsx +++ b/docs/src/pages/components/pagination/examples/PaginationHasMorePagesExample.tsx @@ -20,24 +20,14 @@ export const PaginationHasMorePagesExample = () => { setCurrentPageIndex(currentPageIndex + 1); }; - const handlePreviousPage = () => { - if (currentPageIndex > 1) { - setCurrentPageIndex(currentPageIndex - 1); - } - }; - - const handleOnChange = (pageIndex) => { - setCurrentPageIndex(pageIndex); - }; - return ( setCurrentPageIndex(currentPageIndex - 1)} + onChange={(pageIndex) => setCurrentPageIndex(pageIndex)} /> ); }; diff --git a/docs/src/pages/components/pagination/examples/PaginationSiblingCountExample.tsx b/docs/src/pages/components/pagination/examples/PaginationSiblingCountExample.tsx new file mode 100644 index 00000000000..73ee08439b9 --- /dev/null +++ b/docs/src/pages/components/pagination/examples/PaginationSiblingCountExample.tsx @@ -0,0 +1,11 @@ +import { Pagination, usePagination } from '@aws-amplify/ui-react'; + +export const PaginationSiblingCountExample = () => { + const paginationProps = usePagination({ + totalPages: 11, + currentPage: 5, + siblingCount: 2, + }); + + return ; +}; diff --git a/docs/src/pages/components/pagination/examples/PaginationStylingExample.tsx b/docs/src/pages/components/pagination/examples/PaginationStylingExample.tsx new file mode 100644 index 00000000000..429dc0a0913 --- /dev/null +++ b/docs/src/pages/components/pagination/examples/PaginationStylingExample.tsx @@ -0,0 +1,11 @@ +import { Pagination, usePagination } from '@aws-amplify/ui-react'; + +export const PaginationStylingExample = ({ + className, + totalPages, + ...rest +}) => { + const paginationProps = usePagination({ totalPages }); + + return ; +}; diff --git a/docs/src/pages/components/pagination/examples/PaginationThemeExample.tsx b/docs/src/pages/components/pagination/examples/PaginationThemeExample.tsx new file mode 100644 index 00000000000..5047c48a41e --- /dev/null +++ b/docs/src/pages/components/pagination/examples/PaginationThemeExample.tsx @@ -0,0 +1,34 @@ +import { + AmplifyProvider, + Pagination, + usePagination, +} from '@aws-amplify/ui-react'; + +const theme = { + name: 'pagination-theme', + tokens: { + components: { + pagination: { + current: { + backgroundColor: { value: 'rebeccapurple' }, + }, + button: { + hover: { + backgroundColor: { value: '{colors.neutral.40.value}' }, + color: { value: '{colors.white.value}' }, + }, + }, + }, + }, + }, +}; + +export const PaginationThemeExample = () => { + const paginationProps = usePagination({ totalPages: 6 }); + + return ( + + + + ); +}; diff --git a/docs/src/pages/components/pagination/examples/index.ts b/docs/src/pages/components/pagination/examples/index.ts index 46770d994a9..f44ee3e5ccc 100644 --- a/docs/src/pages/components/pagination/examples/index.ts +++ b/docs/src/pages/components/pagination/examples/index.ts @@ -1 +1,6 @@ +export { ControlledPaginationExample } from './ControlledPaginationExample'; +export { DefaultPaginationExample } from './DefaultPaginationExample'; +export { PaginationStylingExample } from './PaginationStylingExample'; export { PaginationHasMorePagesExample } from './PaginationHasMorePagesExample'; +export { PaginationSiblingCountExample } from './PaginationSiblingCountExample'; +export { PaginationThemeExample } from './PaginationThemeExample'; diff --git a/docs/src/pages/components/pagination/react.mdx b/docs/src/pages/components/pagination/react.mdx index 04e6ead2710..a3380a155fa 100644 --- a/docs/src/pages/components/pagination/react.mdx +++ b/docs/src/pages/components/pagination/react.mdx @@ -1,8 +1,15 @@ -import { Flex } from '@aws-amplify/ui-react'; +import { Pagination } from '@aws-amplify/ui-react'; import { PaginationDemo } from './demo.tsx'; import { Example, ExampleCode } from '@/components/Example'; -import { PaginationHasMorePagesExample } from './examples'; import { ComponentStyleDisplay } from '@/components/ComponentStyleDisplay'; +import { + ControlledPaginationExample, + DefaultPaginationExample, + PaginationHasMorePagesExample, + PaginationSiblingCountExample, + PaginationStylingExample, + PaginationThemeExample, +} from './examples'; ## Demo @@ -10,23 +17,38 @@ import { ComponentStyleDisplay } from '@/components/ComponentStyleDisplay'; ## Usage -Import the Pagination component and styles. +Import the Pagination component. To use Pagination as an uncontrolled component, import the `usePagination` hook and pass it an object including the following properties: + +- `totalPages` (required) +- `currentPage` (optional, defaults to `1`) +- `siblingCount` (optional, defaults to `1`) +- `hasMorePages` (optional) + + + + +```jsx file=./examples/DefaultPaginationExample.tsx + +``` + + + +### Controlled component +To use Pagination as a controlled component, you'll need to handle state using these callback functions: + +- `onNext`: triggered when the next-page button `>` is pressed +- `onPrevious`: triggered when the previous-page button `<` is pressed +- `onChange`: triggered every time the page changes (e.g., when a page button is pressed directly) + + + + +```jsx file=./examples/ControlledPaginationExample.tsx -```jsx -import { Pagination } from '@aws-amplify/ui-react'; -import '@aws-amplify/ui-react/styles.css'; - -; ``` - + + ### Paginating at an API level @@ -37,7 +59,22 @@ It's common to use a paged API where the total number of pages in the dataset is ```jsx file=./examples/PaginationHasMorePagesExample.tsx -```` +``` + + + + +### Sibling Count + +`siblingCount` is an optional prop which controls the number of pages displayed on each side of the current page (defaults to `1`). For example, in the demo below, notice how page 5 has two siblings to the left (3 and 4) and two siblings to the right (6 and 7). + + + + +```jsx{8} file=./examples/PaginationSiblingCountExample.tsx + +``` + @@ -47,11 +84,48 @@ It's common to use a paged API where the total number of pages in the dataset is +### Theme + +You can customize the appearance of all Pagination components in your application with a [Theme](/theming). + + + + +```jsx file=./examples/PaginationThemeExample.tsx + +``` + + + ### Global styling To override styling on all Pagination components, you can set the Amplify CSS variables or use the built-in `.amplify-pagination` class. -(Example needed) + + + + ```css + /* styles.css */ + .amplify-pagination { + --amplify-components-pagination-current-background-color: var( + --amplify-colors-red-60 + ); + } + ``` + + + ```jsx + import { Pagination, usePagination } from '@aws-amplify/ui-react'; + import './styles.css'; + + const PaginationGlobalStylingExample = () => { + const paginationProps = usePagination({ totalPages: 8 }); + return ; + }; + ``` + + + To replace the Pagination styling, unset it: @@ -68,75 +142,52 @@ To override styling on a specific Pagination component, you can use a class sele _Using a class selector:_ -```css -/* styles.css */ -.my-custom-pagination { - --amplify-components-pagination-current-background-color: rgba( - 0, - 181, - 204, - 0.8 - ); - --amplify-components-pagination-button-hover-background-color: rgba( - 0, - 181, - 204, - 0.3 - ); -} -``` + + + + ```css + /* styles.css */ + .my-custom-pagination { + --amplify-components-pagination-current-background-color: var( + --amplify-colors-green-80 + ); + --amplify-components-pagination-button-hover-background-color: var( + --amplify-colors-blue-20 + ); + } + ``` + + + ```jsx + import { Pagination, usePagination } from '@aws-amplify/ui-react'; + import './styles.css'; -```jsx -import { Pagination } from '@aws-amplify/ui-react'; -import '@aws-amplify/ui-react/styles.css'; -import './styles.css'; - -; -``` + const PaginationClassStylingExample= () => { + const paginationProps = usePagination({ totalPages: 7 }); + return ; + }; + ``` - + + _Using style props:_ -**Modify this example to show how to use style props (no inline CSS variables)** + + + + ```jsx + import { Pagination, usePagination } from '@aws-amplify/ui-react'; -```jsx -import { Pagination } from '@aws-amplify/ui-react'; -import '@aws-amplify/ui-react/styles.css'; - -; -``` + const PaginationStylePropsExample = () => { + const paginationProps = usePagination({ totalPages: 5 }); + return ; + }; + ``` - + + ## Accessibility -By default, the root node has a role of "navigation" and each page item has an aria-label that identifies the purpose of the item ("Go to previous page", "Go to next page", "Go to page 1" etc.). -```` +By default, the root node of the Pagination component has a role of "navigation", and each page item has an `aria-label` that identifies the purpose of the item ("Go to previous page", "Go to next page", "Go to page 1", etc). diff --git a/docs/src/pages/components/pagination/usePaginationProps.tsx b/docs/src/pages/components/pagination/usePaginationProps.tsx new file mode 100644 index 00000000000..865e692a75c --- /dev/null +++ b/docs/src/pages/components/pagination/usePaginationProps.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { PaginationProps } from '@aws-amplify/ui-react'; +import { PaginationPropControlsProps } from './PaginationPropControls'; + +interface UsePaginationProps { + (initialValues: PaginationProps): PaginationPropControlsProps; +} + +export const usePaginationProps: UsePaginationProps = (initialValues) => { + const [currentPage, setCurrentPage] = React.useState< + PaginationProps['currentPage'] + >(initialValues.currentPage); + const [totalPages, setTotalPages] = React.useState< + PaginationProps['totalPages'] + >(initialValues.totalPages); + const [siblingCount, setSiblingCount] = React.useState< + PaginationProps['siblingCount'] + >(initialValues.siblingCount); + const [hasMorePages, setHasMorePages] = React.useState< + PaginationProps['hasMorePages'] + >(initialValues.hasMorePages); + + const onNext = () => { + if (currentPage < totalPages) { + setCurrentPage(currentPage + 1); + } + }; + + const onPrevious = () => { + if (currentPage > 1) { + setCurrentPage(currentPage - 1); + } + }; + + const onChange = (pageIndex) => setCurrentPage(pageIndex); + + return { + currentPage, + setCurrentPage, + totalPages, + setTotalPages, + siblingCount, + setSiblingCount, + hasMorePages, + setHasMorePages, + onNext, + onPrevious, + onChange, + }; +}; diff --git a/docs/src/styles/primitives/paginationStyles.css b/docs/src/styles/primitives/paginationStyles.css index 20fd547fdef..64057dd330c 100644 --- a/docs/src/styles/primitives/paginationStyles.css +++ b/docs/src/styles/primitives/paginationStyles.css @@ -1,19 +1,14 @@ -.amplify-pagination-demo input { - width: 150px; - font-size: 0.875rem; +.pagination-global-styling { + --amplify-components-pagination-current-background-color: var( + --amplify-colors-red-60 + ); } .my-custom-pagination { - --amplify-components-pagination-current-background-color: rgba( - 0, - 181, - 204, - 0.8 + --amplify-components-pagination-current-background-color: var( + --amplify-colors-green-80 ); - --amplify-components-pagination-button-hover-background-color: rgba( - 0, - 181, - 204, - 0.3 + --amplify-components-pagination-button-hover-background-color: var( + --amplify-colors-blue-20 ); }