UNPKG

@workday/canvas-kit-react

Version:

The parent module that contains all Workday Canvas Kit React components

237 lines (236 loc) • 10.2 kB
import * as React from 'react'; import { createComponent } from '@workday/canvas-kit-react/common'; import { Flex } from '@workday/canvas-kit-react/layout'; import { usePaginationModel, PaginationContext, } from './usePaginationModel'; import { JumpToFirstButton, StepToPreviousButton, StepToNextButton, JumpToLastButton, PaginationControls, } from './Controls'; import { PageList, PageListItem } from './PageList'; import { PageButton } from './PageButton'; import { AdditionalDetails } from './AdditionalDetails'; import { GoToForm } from './GoTo/Form'; import { GoToTextInput } from './GoTo/TextInput'; import { GoToLabel } from './GoTo/Label'; export { PaginationContext }; function useDefaultModel(model, config, fn) { return model || fn(config); } /** * `Pagination` is a container component rendered as a `<nav>` element that is responsible for creating * a `PaginationModel` and sharing it with its subcomponents using React context. * * ```tsx * <Pagination * aria-label="Pagination" * lastPage={100} * initialCurrentPage={6} * rangeSize={3} * onPageChange={pageNumber => console.log(pageNumber)} * > * {Child components} * </Pagination> * ``` * * Alternatively, you may pass in a model using the hoisted model pattern. * * ```tsx * const model = usePaginationModel({ * lastPage: 100, * initialCurrentPage: 6, * rangeSize: 3, * onPageChange: pageNumber => console.log(pageNumber), * }); * * return ( * <Pagination aria-label="Pagination" model={model}> * {Child components} * </Pagination> * ); * ``` */ export const Pagination = createComponent('nav')({ displayName: 'Pagination', Component(props, ref, Element) { const model = useDefaultModel(props.model, props, usePaginationModel); const { lastPage, firstPage, initialCurrentPage, rangeSize, onPageChange, ...elemProps } = props; return (React.createElement(PaginationContext.Provider, { value: model }, React.createElement(Flex, { ref: ref, as: Element, display: "inline-flex", flexDirection: "column", alignItems: "center", ...elemProps }))); }, subComponents: { /** * `Pagination.Controls` provides proper alignment and spacing between elements inside * `Pagination`. It does not handle any internal business logic or state. * * ```tsx * <Pagination.Controls>{Child components}</Pagination.Controls> * ``` */ Controls: PaginationControls, /** * `Pagination.JumpToFirstButton` is a {@link TertiaryButton} that subscribes to the * `Pagination` context. This allows it to know when to disable and set `currentPage` to the * first page. * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `First` or the translated equivalent. * * ```tsx * <Pagination.JumpToFirstButton aria-label="First" /> * ``` */ JumpToFirstButton, /** * `Pagination.StepToPreviousButton` is a {@link TertiaryButton} that renders an icon that * subscribes to the `Pagination` context. This allows it to know when to disable and decrement * the `currentPage`. * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `Previous` or the translated equivalent. * * ```tsx * <Pagination.StepToPreviousButton aria-label="Previous" /> * ``` */ StepToPreviousButton, /** * `Pagination.StepToNextButton` is a {@link TertiaryButton} that subscribes to the `Pagination` * context. This allows it to know when to disable and increment the `currentPage`. * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `Next` or the translated equivalent. * * ```tsx * <Pagination.StepToNextButton aria-label="Next" /> * ``` */ StepToNextButton, /** * `Pagination.JumpToLastButton` is a {@link TertiaryButton} that renders an icon that * subscribes to the `Pagination` context. This allows it to know when to disable and set * `currentPage` to the last page. * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `Last` or the translated equivalent. * * ```tsx * <Pagination.JumpToLastButton aria-label="Last" /> * ``` */ JumpToLastButton, /** * `Pagination.PageList` subscribes to the `Pagination` context. This allows it generate the * `range` of page numbers. It also handles spacing between the child elements. * * This component will accept either child elements or a render prop. In most cases, you'll want * to use the render prop so you can access the `Pagination` model in order to generate the * proper list items. * * ```tsx * <Pagination.PageList> * {({state}) => * state.range.map(pageNumber => ( * <Pagination.PageListItem key={pageNumber}> * <Pagination.PageButton aria-label={`Page ${pageNumber}`} pageNumber={pageNumber} /> * </Pagination.PageListItem> * )) * } * </Pagination.PageList> * ``` */ PageList, /** * `Pagination.PageListItem` provides a semantic child element for the `PageList` component and * is important for accessibility. It does not handle any internal business logic or state. * * ```tsx * <Pagination.PageListItem>{Child element}</Pagination.PageListItem> * ``` */ PageListItem, /** * `Pagination.PageButton` subscribes to the `Pagination` context. This allows it to update the * `currentPage` and set the `toggled` styling when it is the current item. * * `Pagination.PageButton` will render `pageNumber` as its children. * * ```tsx * <Pagination.PageButton aria-label="Page 2" pageNumber={2} /> * ``` * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `Page ${pageNumber}` or the translated equivalent. */ PageButton, /** * `Pagination.GoToForm` is a wrapper for a React context provider rendered as a `<form>` * element. Child components such as {@link GoToTextInput Pagination.GoToTextInput} and * {@link GoToLabel Pagination.GoToLabel} subscribe to that context to manage the form state and * behavior as well as update the `currentPage` in the `Pagination` component. * * ```tsx * <Pagination.GoToForm>{Child elements}</Pagination.GoToForm> * ``` */ GoToForm, /** * `Pagination.GoToTextInput` is a {@link TextInput}. * * ```tsx * <Pagination.GoToTextInput aria-label="Go to page number" /> * ``` * * Note that you must set `aria-label` to meet accessibility standards. We recommend setting it * to `Go to page number` or the translated equivalent. */ GoToTextInput, /** * `Pagination.GoToLabel` subscribes to the `Pagination` context. This allows it to pass the * `Pagination` context to child elements. * * This component will accept either child elements or a render prop. In most cases, you'll want * to use the render prop so you can access the `Pagination` model when generating the label * text. * * ```tsx * <Pagination.GoToLabel>{({state}) => `of ${state.lastPage} pages`}</Pagination.GoToLabel> * ``` */ GoToLabel, /** * `Pagination.AdditionalDetails` subscribes to the `Pagination` context. This allows it to pass * the `Pagination` context to child elements. It is also an `aria-live` region that announces * the current page update to screen readers. * * `Pagination.AdditionalDetails` must be included in your `Pagination` component to meet * accessibility standards (with one exception, see below). If you wish to prevent it from * displaying, you may set its `shouldHideDetails` prop to `true`. The visually hidden region * will still be accessible to screen readers. * * If you have multiple `Pagination` components sharing the same state and rendered on the same * page, you may do either of the following to prevent screen readers from announcing the same * update multiple times: * * - Exclude `Pagination.AdditionalDetails` from all but one of the `Pagination` components. * This is the one case where you may exclude `Pagination.AdditionalDetails` from a * `Pagination` component. * - Include `Pagination.AdditionalDetails` in every `Pagination` component (i.e., you want it * to be visible for every component), but set the `shouldAnnounceToScreenReader` prop to * `false` on all but one of them. * * This component will accept either child elements or a render prop. In most cases, you'll want * to use the render prop so you can access the `Pagination` model in order to generate the * appropriate text. * * ```tsx * <Pagination.AdditionalDetails> * {({state}) => * `${getVisibleResultsMin(state.currentPage, resultCount)}-${getVisibleResultsMax( * state.currentPage, * resultCount, * totalCount * )} of ${totalCount} results` * } * </Pagination.AdditionalDetails> * ``` */ AdditionalDetails, }, });