From a3127a860cda984d0d1fe6f7c6dcff58a59db932 Mon Sep 17 00:00:00 2001 From: AlexandraGallipoliRodrigues Date: Tue, 26 May 2026 10:02:15 +0200 Subject: [PATCH 01/18] feat(Pagination): add Pagination component Adds a new Pagination component with desktop and mobile layouts, following the Mistica design specs. - Page navigation with Previous/Next controls (hidden at boundaries) - Configurable visible page window via `dynamicCount` - Optional ellipsis truncation for large page counts - Controlled and uncontrolled modes (`currentPage` / `defaultPage`) - iconOnly mode for compact layouts - Customisable navigation labels (i18n) - Hover state uses neutral `backgroundContainerHover` with 1.06 scale - Current page uses `brandLow` and `textActivated` - Filled chevrons match the Figma stroke weight - Numeric labels use medium weight for visual parity with Figma - Includes playroom snippets and unit tests Co-Authored-By: Claude Opus 4.7 (1M context) --- playroom/snippets.tsx | 21 +++ src/__tests__/pagination-test.tsx | 94 ++++++++++ src/index.tsx | 1 + src/pagination.css.ts | 242 ++++++++++++++++++++++++ src/pagination.tsx | 297 ++++++++++++++++++++++++++++++ 5 files changed, 655 insertions(+) create mode 100644 src/__tests__/pagination-test.tsx create mode 100644 src/pagination.css.ts create mode 100644 src/pagination.tsx diff --git a/playroom/snippets.tsx b/playroom/snippets.tsx index f25abb70c0..30c70cf98a 100644 --- a/playroom/snippets.tsx +++ b/playroom/snippets.tsx @@ -4583,6 +4583,27 @@ export default [ name: 'NavigationBreadcrumbs', code: '', }, + { + group: 'Pagination', + name: 'Pagination', + code: ` + setState("paginationPage", page)} + />`, + }, + { + group: 'Pagination', + name: 'Pagination iconOnly', + code: ` + setState("paginationIconPage", page)} + />`, + }, ...titlesSnippets, ...emptyStatesGroup, { diff --git a/src/__tests__/pagination-test.tsx b/src/__tests__/pagination-test.tsx new file mode 100644 index 0000000000..af96fdec11 --- /dev/null +++ b/src/__tests__/pagination-test.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import {render, screen} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import ThemeContextProvider from '../theme-context-provider'; +import {makeTheme} from './test-utils'; +import Pagination, {getPaginationItems} from '../pagination'; + +test('renders pagination navigation landmark', () => { + render( + + + + ); + + expect(screen.getByRole('navigation', {name: 'Pagination'})).toBeInTheDocument(); +}); + +test('does not render when there is a single page', () => { + render( + + + + ); + + expect(screen.queryByRole('navigation')).not.toBeInTheDocument(); +}); + +test('calls onChange when a page button is clicked (uncontrolled)', async () => { + const onChange = jest.fn(); + render( + + + + ); + + await userEvent.click(screen.getByRole('button', {name: 'Page 3'})); + + expect(onChange).toHaveBeenCalledWith(3); +}); + +test('calls onChange when Next is clicked', async () => { + const onChange = jest.fn(); + render( + + + + ); + + await userEvent.click(screen.getByRole('button', {name: 'Next'})); + + expect(onChange).toHaveBeenCalledWith(3); +}); + +test('does not change page when disabled', async () => { + const onChange = jest.fn(); + render( + + + + ); + + await userEvent.click(screen.getByRole('button', {name: 'Page 3'})); + + expect(onChange).not.toHaveBeenCalled(); +}); + +test('honors controlled currentPage', () => { + render( + + + + ); + + expect(screen.getByText('3').closest('[aria-current="page"]')).toBeInTheDocument(); +}); + +describe('getPaginationItems', () => { + test('returns an empty array for a single page', () => { + expect(getPaginationItems({totalPages: 1, currentPage: 1})).toEqual([]); + }); + + test('returns all pages when total fits without ellipsis', () => { + const items = getPaginationItems({totalPages: 5, currentPage: 3}); + expect(items.filter((i) => i.type === 'ellipsis')).toHaveLength(0); + expect(items).toHaveLength(5); + }); + + test('inserts ellipsis when middle pages are skipped', () => { + const items = getPaginationItems({totalPages: 20, currentPage: 10}); + expect(items.some((i) => i.type === 'ellipsis')).toBe(true); + expect(items[0]).toMatchObject({type: 'page', page: 1}); + expect(items[items.length - 1]).toMatchObject({type: 'page', page: 20}); + }); +}); diff --git a/src/index.tsx b/src/index.tsx index f56b0ae7ee..ff5e5beab3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -63,6 +63,7 @@ export {default as Inline} from './inline'; export {default as HorizontalScroll} from './horizontal-scroll'; export {default as Stepper} from './stepper'; export {ProgressBar, ProgressBarStepped} from './progress-bar'; +export {default as Pagination} from './pagination'; export {default as Meter} from './meter'; export {Rating, InfoRating} from './rating'; export {VerticalMosaic, HorizontalMosaic} from './mosaic'; diff --git a/src/pagination.css.ts b/src/pagination.css.ts new file mode 100644 index 0000000000..159f28101a --- /dev/null +++ b/src/pagination.css.ts @@ -0,0 +1,242 @@ +import {style} from '@vanilla-extract/css'; +import {sprinkles} from './sprinkles.css'; +import {vars as skinVars} from './skins/skin-contract.css'; +import * as mq from './media-queries.css'; + +export const container = style([ + sprinkles({ + display: 'inline-flex', + alignItems: 'center', + }), + { + gap: 4, + padding: '8px 16px', + width: 'fit-content', + maxWidth: '100%', + boxSizing: 'border-box', + + '@media': { + [mq.desktopOrBigger]: { + gap: 8, + }, + }, + }, +]); + +export const pageList = style([ + sprinkles({ + display: 'flex', + alignItems: 'center', + }), + { + gap: 4, + margin: 0, + padding: 0, + listStyle: 'none', + + '@media': { + [mq.desktopOrBigger]: { + gap: 8, + }, + }, + }, +]); + +export const pageListItem = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}); + +const interactiveArea = style([ + sprinkles({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }), + { + position: 'relative', + width: 32, + minWidth: 32, + height: 48, + padding: 0, + border: 0, + font: 'inherit', + background: 'transparent', + color: skinVars.colors.textPrimary, + borderRadius: skinVars.borderRadii.button, + WebkitTapHighlightColor: 'transparent', + boxSizing: 'border-box', + + '@media': { + [mq.desktopOrBigger]: { + height: 32, + }, + }, + }, +]); + +const pageElement = style([ + interactiveArea, + { + ':before': { + content: '""', + position: 'absolute', + top: '50%', + left: '50%', + width: 32, + height: 32, + borderRadius: '50%', + transform: 'translate(-50%, -50%) scale(0.8)', + opacity: 0, + transition: 'transform 0.2s ease-in-out, opacity 0.2s ease-in-out', + }, + + '@media': { + [mq.desktopOrBigger]: { + minWidth: 32, + }, + }, + }, +]); + +export const pageButton = style([ + pageElement, + { + cursor: 'pointer', + + selectors: { + '&:active:before': { + opacity: 1, + transform: 'translate(-50%, -50%) scale(1)', + backgroundColor: skinVars.colors.backgroundContainerPressed, + }, + '&:disabled': { + cursor: 'default', + opacity: 0.5, + }, + }, + + '@media': { + [mq.supportsHover]: { + selectors: { + '&:hover:before': { + opacity: 1, + transform: 'translate(-50%, -50%) scale(1.06)', + backgroundColor: skinVars.colors.backgroundContainerHover, + }, + '&:active:before': { + opacity: 1, + transform: 'translate(-50%, -50%) scale(1)', + backgroundColor: skinVars.colors.backgroundContainerPressed, + }, + }, + }, + [mq.touchableOnly]: { + ':before': { + transition: 'none', + }, + }, + }, + }, +]); + +export const currentPage = style([ + pageElement, + { + cursor: 'default', + color: skinVars.colors.textActivated, + + ':before': { + opacity: 1, + transform: 'translate(-50%, -50%) scale(1)', + backgroundColor: skinVars.colors.brandLow, + }, + }, +]); + +export const pageContent = style({ + position: 'relative', + zIndex: 1, +}); + +export const ellipsis = style([ + interactiveArea, + { + color: skinVars.colors.textPrimary, + cursor: 'default', + + '@media': { + [mq.desktopOrBigger]: { + width: 16, + minWidth: 16, + }, + }, + }, +]); + +export const navigationButton = style([ + sprinkles({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }), + { + gap: 4, + width: 32, + minWidth: 32, + height: 48, + padding: 0, + border: 0, + font: 'inherit', + background: 'transparent', + color: skinVars.colors.textLink, + borderRadius: skinVars.borderRadii.button, + cursor: 'pointer', + WebkitTapHighlightColor: 'transparent', + + selectors: { + '&:active': { + backgroundColor: skinVars.colors.buttonLinkBackgroundPressed, + }, + '&:disabled': { + cursor: 'default', + opacity: 0.5, + }, + }, + + '@media': { + [mq.desktopOrBigger]: { + width: 'auto', + minWidth: 32, + height: 32, + }, + [mq.supportsHover]: { + selectors: { + '&:hover': { + color: skinVars.colors.textLink, + }, + }, + }, + }, + }, +]); + +export const navigationButtonIconOnly = style({ + '@media': { + [mq.desktopOrBigger]: { + width: 32, + minWidth: 32, + }, + }, +}); + +export const navigationLabel = style({ + display: 'inline-flex', + + '@media': { + [mq.tabletOrSmaller]: { + display: 'none', + }, + }, +}); diff --git a/src/pagination.tsx b/src/pagination.tsx new file mode 100644 index 0000000000..2087edbba7 --- /dev/null +++ b/src/pagination.tsx @@ -0,0 +1,297 @@ +'use client'; + +import * as React from 'react'; +import classnames from 'classnames'; +import * as styles from './pagination.css'; +import {Text3} from './text'; +import {useTheme} from './hooks'; +import IconChevronLeftFilled from './generated/mistica-icons/icon-chevron-left-filled'; +import IconChevronRightFilled from './generated/mistica-icons/icon-chevron-right-filled'; +import {getPrefixedDataAttributes} from './utils/dom'; + +import type {DataAttributes} from './utils/types'; + +export type PaginationProps = { + totalPages: number; + currentPage?: number; + defaultPage?: number; + onChange?: (page: number) => void; + + hideNavigationControls?: boolean; + hidePageList?: boolean; + showEllipsis?: boolean; + + dynamicCount?: number; + + navLeftLabel?: string; + navRightLabel?: string; + + mode?: 'default' | 'iconOnly'; + disabled?: boolean; + + dataAttributes?: DataAttributes; + 'aria-label'?: string; +}; + +type PaginationItem = {type: 'page'; page: number; current: boolean} | {type: 'ellipsis'}; + +const clamp = (value: number, min: number, max: number): number => Math.min(Math.max(value, min), max); + +export const getPaginationItems = ({ + totalPages, + currentPage, + dynamicCount = 3, + showEllipsis = true, +}: { + totalPages: number; + currentPage: number; + dynamicCount?: number; + showEllipsis?: boolean; +}): Array => { + if (totalPages <= 1) { + return []; + } + + const activePage = clamp(currentPage, 1, totalPages); + const visibleCount = Math.max(1, Math.floor(dynamicCount)); + + if (!showEllipsis || totalPages <= visibleCount + 2) { + return Array.from({length: totalPages}, (_, index) => { + const page = index + 1; + return {type: 'page', page, current: page === activePage}; + }); + } + + const pages = new Set(); + + pages.add(1); + pages.add(totalPages); + + const leftCount = Math.floor((visibleCount - 1) / 2); + const rightCount = visibleCount - 1 - leftCount; + + let start = activePage - leftCount; + let end = activePage + rightCount; + + if (start < 2) { + end += 2 - start; + start = 2; + } + + if (end > totalPages - 1) { + start -= end - (totalPages - 1); + end = totalPages - 1; + } + + start = Math.max(2, start); + end = Math.min(totalPages - 1, end); + + for (let page = start; page <= end; page++) { + pages.add(page); + } + + const orderedPages = Array.from(pages).sort((a, b) => a - b); + const items: Array = []; + + orderedPages.forEach((page, index) => { + const previousPage = orderedPages[index - 1]; + + if (previousPage !== undefined) { + const gap = page - previousPage; + + if (gap === 2) { + const missingPage = previousPage + 1; + items.push({ + type: 'page', + page: missingPage, + current: missingPage === activePage, + }); + } else if (gap > 2) { + items.push({type: 'ellipsis'}); + } + } + + items.push({ + type: 'page', + page, + current: page === activePage, + }); + }); + + return items; +}; + +type PaginationLabelWeight = 'regular' | 'medium'; + +const PaginationLabel = ({ + children, + weight, +}: { + children: React.ReactNode; + weight?: PaginationLabelWeight; +}): JSX.Element => { + const {textPresets} = useTheme(); + + return ( + + {children} + + ); +}; + +type PageListProps = { + items: Array; + disabled?: boolean; + className?: string; + onPageClick: (page: number) => void; +}; + +const PageList = ({items, disabled, className, onPageClick}: PageListProps): JSX.Element => { + return ( +
    + {items.map((item, index) => { + if (item.type === 'ellipsis') { + return ( +
  • + +
  • + ); + } + + if (item.current) { + return ( +
  • + + + {item.page} + + +
  • + ); + } + + return ( +
  • + +
  • + ); + })} +
+ ); +}; + +export const Pagination = ({ + totalPages, + currentPage, + defaultPage = 1, + onChange, + hideNavigationControls = false, + hidePageList = false, + showEllipsis = true, + dynamicCount = 3, + navLeftLabel = 'Previous', + navRightLabel = 'Next', + mode = 'default', + disabled = false, + dataAttributes, + 'aria-label': ariaLabel = 'Pagination', +}: PaginationProps): JSX.Element | null => { + const isControlled = currentPage !== undefined; + const [internalPage, setInternalPage] = React.useState(defaultPage); + + if (totalPages <= 1) { + return null; + } + + const activePage = clamp(isControlled ? currentPage : internalPage, 1, totalPages); + + const goToPage = (page: number) => { + const nextPage = clamp(page, 1, totalPages); + + if (disabled || nextPage === activePage) { + return; + } + + if (!isControlled) { + setInternalPage(nextPage); + } + + onChange?.(nextPage); + }; + + const items = getPaginationItems({ + totalPages, + currentPage: activePage, + dynamicCount, + showEllipsis, + }); + + const showPrevious = activePage > 1; + const showNext = activePage < totalPages; + + return ( + + ); +}; + +export default Pagination; From 08e0990df04640ba7e64117e68e822e0b47401f2 Mon Sep 17 00:00:00 2001 From: AlexandraGallipoliRodrigues Date: Mon, 1 Jun 2026 09:06:24 +0200 Subject: [PATCH 02/18] feat(Pagination): align with Cyber skin and improve accessibility - Use textBrand instead of textLink for Previous/Next so the Cyber skin (where textLink is purple/accent) renders the blue from secondary - Expose Cyber theme in playroom/themes.tsx so it can be selected - Enforce 48x48px touch target on mobile per Figma accessibility spec (the visible 32px circle stays centered inside the larger hit area) - Localise aria-labels via text-tokens (paginationLabel, paginationPrevPage, paginationNextPage, paginationGoToPage) following the Carousel pattern; navLeftLabel / navRightLabel / aria-label props still override the tokens - Update tests to assert the localised aria-labels (default locale es-ES) Co-Authored-By: Claude Opus 4.7 (1M context) --- playroom/themes.tsx | 2 ++ src/__tests__/pagination-test.tsx | 8 ++++---- src/pagination.css.ts | 24 +++++++++++++++++------ src/pagination.tsx | 30 ++++++++++++++++++++--------- src/text-tokens.tsx | 32 +++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 19 deletions(-) diff --git a/playroom/themes.tsx b/playroom/themes.tsx index 3a31f74a3b..ffc5f268a2 100644 --- a/playroom/themes.tsx +++ b/playroom/themes.tsx @@ -16,6 +16,7 @@ export const O2_New: ThemeConfig = {...themes.O2_New, ...common}; export const Telefonica: ThemeConfig = {...themes.Telefonica, ...common}; export const Blau: ThemeConfig = {...themes.Blau, ...common}; export const Esimflag: ThemeConfig = {...themes.Esimflag, ...common}; +export const Cyber: ThemeConfig = {...themes.Cyber, ...common}; export const Movistar_New_iOS: ThemeConfig = {...Movistar_New, platformOverrides: {platform: 'ios'}}; export const Vivo_New_iOS: ThemeConfig = {...Vivo_New, platformOverrides: {platform: 'ios'}}; @@ -23,3 +24,4 @@ export const O2_New_iOS: ThemeConfig = {...O2_New, platformOverrides: {platform: export const Telefonica_iOS: ThemeConfig = {...Telefonica, platformOverrides: {platform: 'ios'}}; export const Blau_iOS: ThemeConfig = {...Blau, platformOverrides: {platform: 'ios'}}; export const Esimflag_iOS: ThemeConfig = {...Esimflag, platformOverrides: {platform: 'ios'}}; +export const Cyber_iOS: ThemeConfig = {...Cyber, platformOverrides: {platform: 'ios'}}; diff --git a/src/__tests__/pagination-test.tsx b/src/__tests__/pagination-test.tsx index af96fdec11..2d6290e9a3 100644 --- a/src/__tests__/pagination-test.tsx +++ b/src/__tests__/pagination-test.tsx @@ -12,7 +12,7 @@ test('renders pagination navigation landmark', () => { ); - expect(screen.getByRole('navigation', {name: 'Pagination'})).toBeInTheDocument(); + expect(screen.getByRole('navigation', {name: 'Paginación'})).toBeInTheDocument(); }); test('does not render when there is a single page', () => { @@ -33,7 +33,7 @@ test('calls onChange when a page button is clicked (uncontrolled)', async () => ); - await userEvent.click(screen.getByRole('button', {name: 'Page 3'})); + await userEvent.click(screen.getByRole('button', {name: 'Ir a la página 3'})); expect(onChange).toHaveBeenCalledWith(3); }); @@ -46,7 +46,7 @@ test('calls onChange when Next is clicked', async () => { ); - await userEvent.click(screen.getByRole('button', {name: 'Next'})); + await userEvent.click(screen.getByRole('button', {name: 'Página siguiente'})); expect(onChange).toHaveBeenCalledWith(3); }); @@ -59,7 +59,7 @@ test('does not change page when disabled', async () => { ); - await userEvent.click(screen.getByRole('button', {name: 'Page 3'})); + await userEvent.click(screen.getByRole('button', {name: 'Ir a la página 3'})); expect(onChange).not.toHaveBeenCalled(); }); diff --git a/src/pagination.css.ts b/src/pagination.css.ts index 159f28101a..daad27cfa3 100644 --- a/src/pagination.css.ts +++ b/src/pagination.css.ts @@ -48,6 +48,11 @@ export const pageListItem = style({ justifyContent: 'center', }); +/* + * Accessibility: per Figma spec, each Page Item must expose a minimum 48x48px + * interactive area on mobile (the visible 32px circle is centered within it). + * Desktop reduces back to 32x32. Width grows via @media on mobile-first base. + */ const interactiveArea = style([ sprinkles({ display: 'flex', @@ -56,8 +61,8 @@ const interactiveArea = style([ }), { position: 'relative', - width: 32, - minWidth: 32, + width: 48, + minWidth: 48, height: 48, padding: 0, border: 0, @@ -70,6 +75,8 @@ const interactiveArea = style([ '@media': { [mq.desktopOrBigger]: { + width: 32, + minWidth: 32, height: 32, }, }, @@ -175,6 +182,11 @@ export const ellipsis = style([ }, ]); +/* + * Accessibility: on mobile the chevron-only nav button must keep the 48x48 + * minimum tap area (the label is hidden under tablet breakpoint). On desktop + * the label brings its own width so we relax min-width to 32px. + */ export const navigationButton = style([ sprinkles({ display: 'flex', @@ -183,14 +195,14 @@ export const navigationButton = style([ }), { gap: 4, - width: 32, - minWidth: 32, + width: 48, + minWidth: 48, height: 48, padding: 0, border: 0, font: 'inherit', background: 'transparent', - color: skinVars.colors.textLink, + color: skinVars.colors.textBrand, borderRadius: skinVars.borderRadii.button, cursor: 'pointer', WebkitTapHighlightColor: 'transparent', @@ -214,7 +226,7 @@ export const navigationButton = style([ [mq.supportsHover]: { selectors: { '&:hover': { - color: skinVars.colors.textLink, + color: skinVars.colors.textBrand, }, }, }, diff --git a/src/pagination.tsx b/src/pagination.tsx index 2087edbba7..ece0118d35 100644 --- a/src/pagination.tsx +++ b/src/pagination.tsx @@ -8,6 +8,7 @@ import {useTheme} from './hooks'; import IconChevronLeftFilled from './generated/mistica-icons/icon-chevron-left-filled'; import IconChevronRightFilled from './generated/mistica-icons/icon-chevron-right-filled'; import {getPrefixedDataAttributes} from './utils/dom'; +import * as tokens from './text-tokens'; import type {DataAttributes} from './utils/types'; @@ -152,6 +153,10 @@ type PageListProps = { }; const PageList = ({items, disabled, className, onPageClick}: PageListProps): JSX.Element => { + const {texts, t} = useTheme(); + const goToPageLabel = (page: number) => + t(texts.paginationGoToPage || tokens.paginationGoToPage, String(page)); + return (
    {items.map((item, index) => { @@ -183,7 +188,7 @@ const PageList = ({items, disabled, className, onPageClick}: PageListProps): JSX type="button" className={styles.pageButton} disabled={disabled} - aria-label={`Page ${item.page}`} + aria-label={goToPageLabel(item.page)} onClick={() => onPageClick(item.page)} > @@ -206,15 +211,22 @@ export const Pagination = ({ hidePageList = false, showEllipsis = true, dynamicCount = 3, - navLeftLabel = 'Previous', - navRightLabel = 'Next', + navLeftLabel, + navRightLabel, mode = 'default', disabled = false, dataAttributes, - 'aria-label': ariaLabel = 'Pagination', + 'aria-label': ariaLabel, }: PaginationProps): JSX.Element | null => { const isControlled = currentPage !== undefined; const [internalPage, setInternalPage] = React.useState(defaultPage); + const {texts, t} = useTheme(); + + const resolvedAriaLabel = ariaLabel || texts.paginationLabel || t(tokens.paginationLabel); + const resolvedPrevLabel = + navLeftLabel || texts.paginationPrevPage || t(tokens.paginationPrevPage); + const resolvedNextLabel = + navRightLabel || texts.paginationNextPage || t(tokens.paginationNextPage); if (totalPages <= 1) { return null; @@ -248,7 +260,7 @@ export const Pagination = ({ return ( From c5c4061c0d050661f15024739c5efabbf121b0e5 Mon Sep 17 00:00:00 2001 From: AlexandraGallipoliRodrigues Date: Tue, 2 Jun 2026 13:32:01 +0200 Subject: [PATCH 06/18] changed text color with the updated cyber skins and modified translations --- src/pagination.css.ts | 4 ++-- src/text-tokens.tsx | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pagination.css.ts b/src/pagination.css.ts index 119a3e73cf..156d00be24 100644 --- a/src/pagination.css.ts +++ b/src/pagination.css.ts @@ -202,7 +202,7 @@ export const navigationButton = style([ border: 0, font: 'inherit', background: 'transparent', - color: skinVars.colors.textBrand, + color: skinVars.colors.textLink, borderRadius: skinVars.borderRadii.button, cursor: 'pointer', WebkitTapHighlightColor: 'transparent', @@ -226,7 +226,7 @@ export const navigationButton = style([ [mq.supportsHover]: { selectors: { '&:hover': { - color: skinVars.colors.textBrand, + color: skinVars.colors.textLink, }, }, }, diff --git a/src/text-tokens.tsx b/src/text-tokens.tsx index 5df0adca1b..52b9d51b7f 100644 --- a/src/text-tokens.tsx +++ b/src/text-tokens.tsx @@ -424,17 +424,17 @@ export const paginationLabel: TextToken = { }; export const paginationPrevPage: TextToken = { - es: 'Página anterior', - en: 'Previous page', - de: 'Vorherige Seite', - pt: 'Página anterior', + es: 'Anterior', + en: 'Previous', + de: 'Zurück', + pt: 'Anterior', }; export const paginationNextPage: TextToken = { - es: 'Página siguiente', - en: 'Next page', - de: 'Nächste Seite', - pt: 'Próxima página', + es: 'Siguiente', + en: 'Next', + de: 'Weiter', + pt: 'Próximo', }; export const paginationGoToPage: TextToken = { From 8e9de99b96e6cfbe12a164bda523a35ce8fc49c4 Mon Sep 17 00:00:00 2001 From: AlexandraGallipoliRodrigues Date: Tue, 2 Jun 2026 15:34:15 +0200 Subject: [PATCH 07/18] added screenshot tests --- ...n-compact-view-mobile-ios-small-1-snap.png | Bin 0 -> 6499 bytes ...t-components-pagination-default-1-snap.png | Bin 0 -> 5056 bytes ...t-components-pagination-default-2-snap.png | Bin 0 -> 5489 bytes ...omponents-pagination-first-page-1-snap.png | Bin 0 -> 4172 bytes ...omponents-pagination-first-page-2-snap.png | Bin 0 -> 5316 bytes ...s-pagination-icon-only-controls-1-snap.png | Bin 0 -> 430 bytes ...s-pagination-icon-only-controls-2-snap.png | Bin 0 -> 852 bytes ...components-pagination-last-page-1-snap.png | Bin 0 -> 3847 bytes ...components-pagination-last-page-2-snap.png | Bin 0 -> 5519 bytes ...-pagination-nav-only-responsive-1-snap.png | Bin 0 -> 2843 bytes ...-pagination-nav-only-responsive-2-snap.png | Bin 0 -> 852 bytes ...ts-pagination-next-chapter-link-1-snap.png | Bin 0 -> 2984 bytes ...ts-pagination-next-chapter-link-2-snap.png | Bin 0 -> 651 bytes ...omponents-pagination-pages-only-1-snap.png | Bin 0 -> 2200 bytes ...omponents-pagination-pages-only-2-snap.png | Bin 0 -> 4368 bytes ...onents-pagination-with-ellipsis-1-snap.png | Bin 0 -> 5406 bytes ...onents-pagination-with-ellipsis-2-snap.png | Bin 0 -> 6361 bytes .../pagination-screenshot-test.tsx | 46 ++++++++++ src/__stories__/pagination-story.tsx | 83 ++++++++++++++++++ src/__tests__/pagination-test.tsx | 2 +- src/pagination.css.ts | 63 ++++++++++--- src/pagination.tsx | 46 ++++++++-- 22 files changed, 219 insertions(+), 21 deletions(-) create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-compact-view-mobile-ios-small-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-default-components-pagination-default-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-default-components-pagination-default-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-first-page-components-pagination-first-page-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-first-page-components-pagination-first-page-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-icon-only-controls-components-pagination-icon-only-controls-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-icon-only-controls-components-pagination-icon-only-controls-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-nav-only-responsive-components-pagination-nav-only-responsive-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-nav-only-responsive-components-pagination-nav-only-responsive-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-pages-only-components-pagination-pages-only-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-pages-only-components-pagination-pages-only-2-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-with-ellipsis-components-pagination-with-ellipsis-1-snap.png create mode 100644 src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-with-ellipsis-components-pagination-with-ellipsis-2-snap.png create mode 100644 src/__screenshot_tests__/pagination-screenshot-test.tsx create mode 100644 src/__stories__/pagination-story.tsx diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-compact-view-mobile-ios-small-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-compact-view-mobile-ios-small-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..0a6dd214c11a4c140717aae8efdbf1156750aac2 GIT binary patch literal 6499 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 literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-default-components-pagination-default-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-default-components-pagination-default-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..ca6953988879832b2dde3aed5aefbd724e960ca5 GIT binary patch literal 5056 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 G$o~N~TKuC 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 literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-icon-only-controls-components-pagination-icon-only-controls-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-icon-only-controls-components-pagination-icon-only-controls-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca82a27ecfcbae72cc4afd623a937c751669046 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^1wd@T!3HEx?)aw;q!^2X+?^QKos)S9A8vkHGbQNBhPx^J57wP-_`t()o)B?~(Z9Lm zx6ar0H=h6E>LA5KK%QuZ{;Un=ViUK=GEL^*Sx>SJ~{RO_PWXUe(&0TR;%pohxQrQ1dKl}S(5Cc z-Ep%b=#k^RJ_}YG&7YySES28Iz7YAKlKE`^Amu`2<$7hGh&U_RkeU(0iajhkE5 zFNiBFTy*c&DV8YVoDDMFr_&fceSFOR-`sgW{A`JO`nTzGADug9z~js)=zodX;XDh6 zBU6h4qtgTiMUMsn6$cI>1r|<$rhyQyc>QG2tFwhy{}|bxEcyH6W?4+n z3k#x3|K06Rp6aJ}tB>FBZRNt>4B{U{Lv8D$N`L5CpPy1&bG0l+^-$|N<{F9nKLf9R z)3LrhCHMTTKGz*ravS=OZr?t2?n?IRXr(=?Pcs~yu6k(cJ0_9)79AU2YB_xD?dGuN z3aEK}rMe}SMPpy#Ys*6?*){fky^!59nJ=K`_0r=U=52`*mz>ue+Fp6JUH@%V&Fj>< z$gjV4yt@9r?Elx`tH1ZY@mXzjv;M&D_Equc<`hJ#-`@P~jM%aohq_ISa{Jcax|?(T z^Xf9L|G(6%h5wh;7XJ=C6}tQW-JJ79tJI_)bf>@F8r<^w@x~wL`fqKXv+5*UgzBNv zOUX5%!u%V&T3#=>{$rID2auI~t+*z1E(=iB@RjYpReelrgdWc5SpQP%fRmdKI;Vst E0F-=3Qvd(} literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..fb2082d0ba99e1dbd9918b9c62fa755364467a74 GIT binary patch literal 3847 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 literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-2-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-last-page-components-pagination-last-page-2-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6b7547baf82632ebf1bc3f70973f140d943e95 GIT binary patch literal 5519 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 Tr~t~@!3CfY)^Lic$D{uN%d~*D literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-nav-only-responsive-components-pagination-nav-only-responsive-2-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-nav-only-responsive-components-pagination-nav-only-responsive-2-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..aa4a135f6b7b575ce67f4145825aaaa8835621a8 GIT binary patch literal 852 zcmeAS@N?(olHy`uVBq!ia0vp^4}iFVgAGUu-AvsLq!^2X+?^QKos)S9Amu`2<$7hGh&U_RkeU(0iajhkE5 zFNiBFTy*c&DV8YVoDDMFr_&fceSFOR-`sgW{A`JO`nTzGADug9z~js)=zodX;XDh6 zBU6h4qtgTiMUMsn6$cI>1r|<$rhyQyc>QG2tFwhy{}|bxEcyH6W?4+n z3k#x3|K06Rp6aJ}tB>FBZRNt>4B{U{Lv8D$N`L5CpPy1&bG0l+^-$|N<{F9nKLf9R z)3LrhCHMTTKGz*ravS=OZr?t2?n?IRXr(=?Pcs~yu6k(cJ0_9)79AU2YB_xD?dGuN z3aEK}rMe}SMPpy#Ys*6?*){fky^!59nJ=K`_0r=U=52`*mz>ue+Fp6JUH@%V&Fj>< z$gjV4yt@9r?Elx`tH1ZY@mXzjv;M&D_Equc<`hJ#-`@P~jM%aohq_ISa{Jcax|?(T z^Xf9L|G(6%h5wh;7XJ=C6}tQW-JJ79tJI_)bf>@F8r<^w@x~wL`fqKXv+5*UgzBNv zOUX5%!u%V&T3#=>{$rID2auI~t+*z1E(=iB@RjYpReelrgdWc5SpQP%fRmdKI;Vst E0F-=3Qvd(} literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..1912ae2ef8f495d2968ef3aa0726b8169ba08eae GIT binary patch literal 2984 zcmb`J={FRP+s4O)1|uXUOC!rzG7QQVMz+QvyRwh5mwn&YGPZ|0stYe{5hY#~gN zeUA~PK@3J&fBuH&Joh=*=Q`J``+jqub0r$;qnH`F836zQGg@2I_`(M-Bn6_sV4Icg z#D&rL8KcwzH6y$m7lGDK9c>D_m~fC|0sz2djMh{$4bI=n53x3z6nqmTC;+AKqk>5TszsK`u0#=jaPS>QR5;vT;DOvWcfWu?L_*PkWeok*s+uM>+I zbY8^BPzOliSF*womuntaN5z70UiMA3uILEj6S6%Wqe zvlRd`knugQdEUE4(Lc9j_-bX;V zUaVxjoqK-d(j=L+o@Qwl*lFWCu^51yWv+~TmKvx?@Qfzeo44@1H1X0uH5+{4AhqfS zVxp~hDZi3ew6fPhoeZKVe-iHg03T@h0PjY6^H)5HBW)(=5N0kX2lZ%_u*&m>tCXEg z@9q2mW@msREPHz;y2hCwraa462EDb3?3iX+J~7+bqn&1K+;55R8epgY*j6GrTl)4o zJtC%JPOYL`{wLI|?@_$nr%O_$9ky~2Dy7cdt3lse12a(t+-cWg{Y_-}3@6M;mfgvGvFkj~7N5acAHYoSBQf&WfQET;+f zkJxEG9ol!Xya`7`3#J96>5Lh~L6=mmYJ zsQFpJ?)7aII*;#w_YACOt(!`n6855B+BFrYNM^1wbU<6Fpwy3%;UR#+gt)oYGCtXX z?&MtosT79F9}D9Utmo@+ef_xltOG(vlh%UExb}6zLc4@&Ir<+DkNcrGnNb-ke&}`k zM!Sf%s<^}7Dx*wwp9F1zxoAJx1WRTE#M7_TuAF4wsu)PPl%X?=75FbJK7y(DZr5)% zsVG*{+lf(1??fi5A4kno7=JifMX{DsW4^H4?XvzAWVzeHu+f=Uq**=GA3~eZd`_k!IA!?Q$t=>dl&7IC5PoKE9rJ!z?^y(?# z8Q!D1=8U9TM8d;>p%H*6>#V)N8}U(ni5Tc19Dajw3A!^8kGJP;0oI*Ir{a2EMyXdG zN%;x3XWMl!AJXe$V%z&*Q%8YOt2$SL#Xf#8(2hxG>G_#1Q+~1FG|Ex9o$Wsz(JP7Q zIeFYOBPm5&ptFRl=Wh|8&)GGj%3Vhdk7sMwK$;(MS{gc&vzqW!VY#rx2eE=KTEXcB z2b6xoXh36vpAMqq#8+~&;Pt@04T#-Ypn^Yrp}V+!QKPMBPC-Mb~dK~y(^zlmmrsx$MVOE!)M=>pR4We_Rcoo9i-SJjd<3JcT!$T;o0!1-ER-^(>CxB zz*z%vxM8+;>2hgF3$|ut-80cSKt?jVNR_0vC!}VgI_qL9d5&y*k*rmT8IVyj9bTj$ z%h399oHVWX?%;xAozF;~K)p@=`R?&rPt|4x^rqROMU~%U0VOh!#wW38O9fG`x#_?d@Tr3r3JYXb>@?P~1XaR6-L)Voi5-yLu>(D-UqunNk}GcR=vj=MV>pNcTR`{Q>n+9K5KW3T!X_tBRkW46+>*P z_430HIjwSP7nC===5G!=U=N(e71H=~8lF~2eC_c4auO!!HSG7X?&Eif)Y+%sW-6SX zR%QeQwUj{IPqmz@lbj{1rTxXK7DejOc{u8{3G@ovV77t}%6C263K3ng9Rr!_Lbz08 z$fghO&P0{`o#K|Y#yYF2a9AvAQ58ehr;B8Qm(`YbPSFZn4uv7(qQ}t-*2K=dq5XmN z2%Wme!$mlP2Rv12HsNf+2G-|M&DYmAhH(+eVGvkH2Z~~^?Ym0mRBN~t56|PLEh`P! z<7H_w<2xL_e@68q$w_5~V~EdHVQoybyw{rJFDDwD)^IR|{<@RjLR-xG51!q_*#@q0S4oQ8r2jRlN!=A&UMcrF9$QI2Zi+*{{ z$s}h>7q~DPM*NeJ$8u_q9Tf=KsQ&grj2Fq2*qNPW@DHdw(4FsbHLrk(&!K*hGY
      B~34jgA#tp@6&i=cnzOdlU(j zW4Do#CyN=3)q7Nr((kad7MWmq33Dz)2&n!|S$?!l&%Wd9H9vh=C@3}>#@)U7z`jEx ze3|bMeV;*S$BT41`DFP32Sxblqq>*C?}}sN*2`&&lQ0kwMW+j!BGGybf9E$qhsmR z=H#(lP_fgF<;A_VZZgXCdPn8EuGtC0m5y49o(f}f_u5Md|_YtHY+e|g9!8|6s- z=dT+YGf|#}&(RtP6)wu7ZmgIcbH$U)dRElP&>wGT`z5*O92g!U(e_v3Pc6A2BA{v) z%>0w)yCX*vflXWUN=oSd5ooxwQ+=E+j7;^;i2F3OyzYL=oq?9gqAqV+Mp}m|t$L@M z;zz#NS}$1c`!icKln*OKNr4t};V#?j~q{ofG<#7fo)lIK&qF4{H#t);J7qiz@b EKSdpsQUCw| literal 0 HcmV?d00001 diff --git a/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-2-snap.png b/src/__screenshot_tests__/__image_snapshots__/pagination-screenshot-test-tsx-pagination-next-chapter-link-components-pagination-next-chapter-link-2-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa1a982c6f14c87acb80945eea7f635cd2c12fd GIT binary patch literal 651 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 { + const cases: Array<[string, string, Device]> = []; + for (const story of STORIES) { + for (const device of DEVICES) { + cases.push([story.name, story.id, device]); + } + } + return cases; +}; + +test.each(getCases())('Pagination %s - %s', async (_name, id, device) => { + await openStoryPage({id, device}); + + const pagination = await screen.findByTestId('Pagination'); + const image = await pagination.screenshot(); + expect(image).toMatchImageSnapshot(); +}); + +// Compact view only renders below 375px viewport. We use MOBILE_IOS_SMALL (320px) +// to drive the vertical layout and the current ± 1 page reduction. +test('Pagination CompactView - MOBILE_IOS_SMALL', async () => { + await openStoryPage({ + id: 'components-pagination--compact-view', + device: 'MOBILE_IOS_SMALL', + }); + + const pagination = await screen.findByTestId('Pagination'); + const image = await pagination.screenshot(); + expect(image).toMatchImageSnapshot(); +}); diff --git a/src/__stories__/pagination-story.tsx b/src/__stories__/pagination-story.tsx new file mode 100644 index 0000000000..964c3ceeb7 --- /dev/null +++ b/src/__stories__/pagination-story.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import {Pagination} from '..'; + +export default { + title: 'Components/Pagination', +}; + +/** + * Mid-range page with both navigation controls. On desktop renders + * "Previous 1 2 3 4 ... 9 Next", on mobile collapses labels into chevrons. + */ +export const Default: StoryComponent = () => ; +Default.storyName = 'Default'; + +/** + * First page: the Previous control must be hidden (showPrevious logic). + */ +export const FirstPage: StoryComponent = () => ; +FirstPage.storyName = 'FirstPage'; + +/** + * Last page: the Next control must be hidden. + */ +export const LastPage: StoryComponent = () => ; +LastPage.storyName = 'LastPage'; + +/** + * Wide page count with active page in the middle: ellipses should appear on + * both sides of the visible range ("1 ... 9 10 11 ... 20"). + */ +export const WithEllipsis: StoryComponent = () => ; +WithEllipsis.storyName = 'WithEllipsis'; + +/** + * No page list, only Previous/Next. On desktop the labels are visible, + * on mobile the labels are hidden and only chevrons are shown. + */ +export const NavOnlyResponsive: StoryComponent = () => ( + +); +NavOnlyResponsive.storyName = 'NavOnlyResponsive'; + +/** + * Page list without navigation controls. Matches the product-list scenario + * from the Figma examples panel. + */ +export const PagesOnly: StoryComponent = () => ( + +); +PagesOnly.storyName = 'PagesOnly'; + +/** + * iconOnly mode forces chevron-only Previous/Next even on desktop. + * Combined with hidePageList matches the "Mapa / Listado" card scenario. + */ +export const IconOnlyControls: StoryComponent = () => ( + +); +IconOnlyControls.storyName = 'IconOnlyControls'; + +/** + * Compact view: triggered automatically when the viewport is narrower than + * 375px (high-zoom or space-limited contexts). Layout stacks vertically with + * Previous / page list / Next and only renders the current ± 1 pages. + * Only visible when the screenshot test runs this story at MOBILE_IOS_SMALL. + */ +export const CompactView: StoryComponent = () => ; +CompactView.storyName = 'CompactView'; + +/** + * Pagination acting as a single next-chapter link: page list is hidden, only + * the Next control is rendered with a custom navRightLabel ("Siguiente + * capítulo"). Matches the chapter-reader scenario from the Figma examples. + */ +export const NextChapterLink: StoryComponent = () => ( + +); +NextChapterLink.storyName = 'NextChapterLink'; diff --git a/src/__tests__/pagination-test.tsx b/src/__tests__/pagination-test.tsx index db714bd89f..bf8b9d63f2 100644 --- a/src/__tests__/pagination-test.tsx +++ b/src/__tests__/pagination-test.tsx @@ -46,7 +46,7 @@ test('calls onChange when Next is clicked', async () => { ); - await userEvent.click(screen.getByRole('button', {name: 'Página siguiente'})); + await userEvent.click(screen.getByRole('button', {name: 'Siguiente'})); expect(onChange).toHaveBeenCalledWith(3); }); diff --git a/src/pagination.css.ts b/src/pagination.css.ts index 156d00be24..9f62888e82 100644 --- a/src/pagination.css.ts +++ b/src/pagination.css.ts @@ -23,6 +23,40 @@ export const container = style([ }, ]); +/* + * Variant for the "Previous / Next only" (hidePageList) layout. Per Figma the + * gap between Previous and Next when the page list is absent is 16px (vs the + * default 4/8px gap of the regular container). + */ +export const containerNavOnly = style({ + gap: 16, + '@media': { + [mq.desktopOrBigger]: { + gap: 16, + }, + }, +}); + +/* + * Compact view (high-zoom or space-limited contexts < 375px wide): navigation + * stacks vertically with Previous on top, page list in the middle, Next at + * the bottom. The JS layer also reduces the page list to current ± 1. + */ +export const containerCompact = style([ + sprinkles({ + display: 'inline-flex', + }), + { + flexDirection: 'column', + alignItems: 'center', + gap: 4, + padding: '8px 16px', + width: 'fit-content', + maxWidth: '100%', + boxSizing: 'border-box', + }, +]); + export const pageList = style([ sprinkles({ display: 'flex', @@ -49,9 +83,13 @@ export const pageListItem = style({ }); /* - * Accessibility: per Figma spec, each Page Item must expose a minimum 48x48px - * interactive area on mobile (the visible 32px circle is centered within it). - * Desktop reduces back to 32x32. Width grows via @media on mobile-first base. + * Page items use the Figma anatomy size (32px circle) on every viewport so the + * full pagination fits on mobile screens (~375px). Vertical hit area stays at + * 48px on mobile to give a comfortable thumb target. WCAG 2.2 Target Size + * (Minimum) is satisfied through the spacing exception: 32px circles with a + * 4px gap between centers (36px apart) easily inscribe non-overlapping 24px + * circles, so the rule passes even though the literal target width is below + * 24×24 only in width. */ const interactiveArea = style([ sprinkles({ @@ -61,8 +99,8 @@ const interactiveArea = style([ }), { position: 'relative', - width: 48, - minWidth: 48, + width: 32, + minWidth: 32, height: 48, padding: 0, border: 0, @@ -75,8 +113,6 @@ const interactiveArea = style([ '@media': { [mq.desktopOrBigger]: { - width: 32, - minWidth: 32, height: 32, }, }, @@ -183,9 +219,12 @@ export const ellipsis = style([ ]); /* - * Accessibility: on mobile the chevron-only nav button must keep the 48x48 - * minimum tap area (the label is hidden under tablet breakpoint). On desktop - * the label brings its own width so we relax min-width to 32px. + * Navigation button matches the page-item layout on mobile (32 wide x 48 tall) + * so the whole pagination fits inside a 375px viewport even with the densest + * "1 ... N N+1 N+2 ... LAST" layout. The hit area still passes WCAG 2.2 via + * the spacing exception (32px button + 4px gap = 36px center-to-center, room + * to inscribe non-overlapping 24px touch circles). Desktop relaxes width to + * auto so the inline label can expand the button naturally. */ export const navigationButton = style([ sprinkles({ @@ -195,8 +234,8 @@ export const navigationButton = style([ }), { gap: 4, - width: 48, - minWidth: 48, + width: 32, + minWidth: 32, height: 48, padding: 0, border: 0, diff --git a/src/pagination.tsx b/src/pagination.tsx index 320e53a98f..954afda322 100644 --- a/src/pagination.tsx +++ b/src/pagination.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import classnames from 'classnames'; import * as styles from './pagination.css'; import {Text3} from './text'; -import {useTheme} from './hooks'; +import {useTheme, useWindowWidth} from './hooks'; import IconChevronLeftRegular from './generated/mistica-icons/icon-chevron-left-regular'; import IconChevronRightRegular from './generated/mistica-icons/icon-chevron-right-regular'; import {getPrefixedDataAttributes} from './utils/dom'; @@ -38,6 +38,30 @@ type PaginationItem = {type: 'page'; page: number; current: boolean} | {type: 'e const clamp = (value: number, min: number, max: number): number => Math.min(Math.max(value, min), max); +/** + * Viewport width (in CSS pixels) under which Pagination collapses into the + * compact view: vertical layout with current ± 1 pages and no anchors. + * Matches the "viewport < 375" rule from the Figma "Compact View" spec. + */ +const COMPACT_BREAKPOINT_PX = 375; + +const getCompactPaginationItems = ( + totalPages: number, + currentPage: number +): Array => { + if (totalPages <= 1) { + return []; + } + const activePage = clamp(currentPage, 1, totalPages); + const start = Math.max(1, activePage - 1); + const end = Math.min(totalPages, activePage + 1); + const items: Array = []; + for (let page = start; page <= end; page++) { + items.push({type: 'page', page, current: page === activePage}); + } + return items; +}; + export const getPaginationItems = ({ totalPages, currentPage, @@ -216,6 +240,8 @@ export const Pagination = ({ const isControlled = currentPage !== undefined; const [internalPage, setInternalPage] = React.useState(defaultPage); const {texts, t} = useTheme(); + const windowWidth = useWindowWidth(); + const isCompact = windowWidth > 0 && windowWidth < COMPACT_BREAKPOINT_PX; const resolvedAriaLabel = ariaLabel || texts.paginationLabel || t(tokens.paginationLabel); const resolvedPrevLabel = navLeftLabel || texts.paginationPrevPage || t(tokens.paginationPrevPage); @@ -241,12 +267,14 @@ export const Pagination = ({ onChange?.(nextPage); }; - const items = getPaginationItems({ - totalPages, - currentPage: activePage, - dynamicCount, - showEllipsis, - }); + const items = isCompact + ? getCompactPaginationItems(totalPages, activePage) + : getPaginationItems({ + totalPages, + currentPage: activePage, + dynamicCount, + showEllipsis, + }); const showPrevious = activePage > 1; const showNext = activePage < totalPages; @@ -254,7 +282,9 @@ export const Pagination = ({ return (
    ); }; @@ -240,9 +259,29 @@ export const Pagination = ({ const windowWidth = useWindowWidth(); const isCompact = windowWidth > 0 && windowWidth < COMPACT_BREAKPOINT_PX; - const resolvedAriaLabel = ariaLabel || texts.paginationLabel || t(tokens.paginationLabel); + /* + * The