Add Pagination component#30
Conversation
Composite pagination nav built from semantic HTML: a <nav aria-label> landmark holding an optional per-page selector, an optional range label, and an ordered list of page controls (prev/next arrow buttons + page numbers with first/last anchors and ellipses around a window near the current page). The four Figma truncation layouts (all-visible / near-start / middle / near-end) are derived from page/pageCount, not exposed as a variant. The current page is marked aria-current="page" and disabled; prev/next disable at the bounds and their arrows mirror under RTL. Tokens (24px slots, radius/sm pages, radius/md arrows, text/13, transparent-active selected fill) match the Figma frame.
|
📚 Storybook preview: https://pr-30-propel-storybook.vamsi-906.workers.dev |
There was a problem hiding this comment.
Pull request overview
Adds a new Pagination component to the @plane/propel component set, including Storybook stories and interaction tests, intended to match the referenced Figma spec and support optional page-size + range UI.
Changes:
- Introduces
Paginationcomponent (navlandmark + page tokens + prev/next + optional page-size selector and range label). - Adds Storybook stories covering default/variants/loading states and a basic behavioral play test.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/propel/src/components/pagination/index.tsx | Implements the new Pagination component, token generation, and accessibility labels. |
| packages/propel/src/components/pagination/pagination.stories.tsx | Adds Storybook stories and a play-test validating basic navigation/a11y behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… ellipses - slotBase: replace fixed size-6 with h-6 min-w-6 w-auto so single digits stay square (24px) but the slot grows for wider content — 100 no longer clips. - buildPageTokens: only ellipsize gaps of 2+ pages; a lone skipped page is rendered as its own number, so pageCount=8/page=4 reads 1 2 3 4 5 … 8 (page 2 no longer hidden) instead of 1 … 3 4 5 … 8. - stories: add ThreeDigit (pageCount=100/page=100) and SingleGap play test (pageCount=8/page=4) covering both cases.
The current/selected page is marked `disabled` to block re-navigation, which triggered the base `disabled:text-disabled` dim. Figma's "Selected" state (node 4762:550) keeps `text/primary`, so the current variant now applies `disabled:text-primary` to override the dim while staying on the `transparent-active` fill. Default/hover/disabled states are unchanged and still match Figma.
|
@bhaveshraja fixed: selected page number now uses text-primary (matched to Figma 4762-503). |
…ends, aria-current)
|
Added keyboard play tests: Enter/Space page activation fires onPageChange, prev/next disabled at the ends, aria-current on the selected page. |
Replace the interim native <select> with the propel Dropdown: the layer-3
pill becomes a DropdownTrigger labeled "N per page" and the menu lists the
page-size options as single-select DropdownItems (current one checked), with
selection reporting through the existing pageSize.onChange. Same pageSize API
({ value, options, onChange }); keyboard (ArrowDown/Enter opens, arrows +
Enter select) and the labeled trigger keep it accessible. Adds a play-tested
PageSizeSelector story covering open/select/keyboard.
|
Replaced the native page-size select with the propel Dropdown (composition, per #41). |

What
Adds a Pagination component to
@plane/propel, built from Figma node4762-503(Global components).It is a single composite navigation control rather than a variant matrix: the Figma "variant" axis (All pages visible / Near start / Middle / Near end) is a function of where the current page sits within the total, so it is derived at render time from
page/pageCount, never a prop.Structure (semantic HTML)
<nav aria-label="Pagination">landmark50 v per page, alayer-3pill + native<select>)1-50 of 250)<ul>of page controls: a previous button, first/last anchors with ellipses around a window of pages near the current one, and a next buttonProps
page(required),pageCount(required),onPageChange(required)loading?: renders the current page as a spinner while navigation is in flightpageSize?:{ value, options, onChange }shows/hides the per-page selectorrange?:{ current, total }shows/hides the range labellabels?: overrides for accessible names / visible selector text (i18n)No
cvadefaultVariants; the essential axes are required props.cva+cx(no tailwind-merge), semantic tokens only.Accessibility
aria-current="page"and disabledaria-hidden; lucide iconsaria-hiddenlandmark-unique)Tokens (match Figma)
24px square slots; page numbers
radius/smwith alayer-transparent-activeselected fill andlayer-transparent-hover; prev/nextradius/md16px arrows;text/13; per-page pilllayer-3+ chevron-down.Verification (LTR + RTL)
Rendered the
Variantsstory (the Figma frame) in a browser and compared both directions:1 2 3 4 5/1 2 3 … 100/1 … 44 45 46 … 100/1 … 98 99 100; selected page greyed, prev disabled at start, next disabled at end, ellipsis dots in the placeholder color.dir="rtl"): layout flips (selector + numbers right-to-left) and the prev/next arrows mirror viartl:-scale-x-100, so "previous" still points toward the start of the run.Gates
vp check,vp run -r test(axe a11y),vp run -r build(attw clean),vp run -r build-storybookall pass.Dependencies
Optionally composed by: #15 (Table). Otherwise standalone.