From 430d155998e61dfad139e31add0434bc1964df56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E8=89=B3=E5=85=B5?= Date: Mon, 28 Sep 2026 10:36:20 +0800 Subject: [PATCH] fix: preserve zero-valued content and valid jumper markup --- src/Options.tsx | 8 +-- src/Pager.tsx | 3 +- src/Pagination.tsx | 6 +-- tests/__snapshots__/demo.test.tsx.snap | 4 +- tests/__snapshots__/simple.test.tsx.snap | 8 +-- tests/hydration.test.tsx | 63 ++++++++++++++++++++++++ tests/renderability.test.tsx | 47 ++++++++++++++++++ 7 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 tests/hydration.test.tsx create mode 100644 tests/renderability.test.tsx diff --git a/src/Options.tsx b/src/Options.tsx index aa570dc0..7abebba2 100644 --- a/src/Options.tsx +++ b/src/Options.tsx @@ -1,4 +1,4 @@ -import { KeyCode } from '@rc-component/util'; +import { isReactRenderable, KeyCode } from '@rc-component/util'; import React from 'react'; import type { PaginationLocale } from './interface'; @@ -21,7 +21,7 @@ interface OptionsProps { selectPrefixCls?: string; pageSize: number; pageSizeOptions?: number[]; - goButton?: boolean | string; + goButton?: React.ReactNode; changeSize?: (size: number) => void; quickGo?: (value: number) => void; buildOptionText?: (value: number | string) => string; @@ -67,7 +67,7 @@ const Options: React.FC = (props) => { }; const handleBlur = (e: React.FocusEvent) => { - if (goButton || goInputText === '') { + if (isReactRenderable(goButton) || goInputText === '') { return; } setGoInputText(''); @@ -137,7 +137,7 @@ const Options: React.FC = (props) => { // >>>>> Quick Go if (quickGo) { - if (goButton) { + if (isReactRenderable(goButton)) { gotoButton = typeof goButton === 'boolean' ? ( - + diff --git a/tests/__snapshots__/simple.test.tsx.snap b/tests/__snapshots__/simple.test.tsx.snap index db9c27d9..db8a2a83 100644 --- a/tests/__snapshots__/simple.test.tsx.snap +++ b/tests/__snapshots__/simple.test.tsx.snap @@ -26,7 +26,7 @@ exports[`simple Pagination props: showQuickJumper should render normally quick-j /> 页 -
  • @@ -37,7 +37,7 @@ exports[`simple Pagination props: showQuickJumper should render normally quick-j go -
  • +
    `; @@ -54,7 +54,7 @@ exports[`simple Pagination props: showQuickJumper should render normally quick-j /> 页 -
  • @@ -63,7 +63,7 @@ exports[`simple Pagination props: showQuickJumper should render normally quick-j > 确定 -
  • +
    `; diff --git a/tests/hydration.test.tsx b/tests/hydration.test.tsx new file mode 100644 index 00000000..267edfe5 --- /dev/null +++ b/tests/hydration.test.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { act, fireEvent } from '@testing-library/react'; +import { hydrateRoot, type Root } from 'react-dom/client'; +import { renderToString } from 'react-dom/server'; +import Pagination from '../src'; + +describe('simple Pagination hydration', () => { + it.each([ + 0, + true, + , + ])( + 'hydrates a go button without replacing server nodes: %s', + async (goButton) => { + const onChange = jest.fn(); + const onRecoverableError = jest.fn(); + const node = ( + + ); + const container = document.createElement('div'); + container.innerHTML = renderToString(node); + document.body.appendChild(container); + const serverRoot = container.firstElementChild; + const serverInput = container.querySelector('input'); + let root: Root; + + try { + await act(async () => { + root = hydrateRoot(container, node, { onRecoverableError }); + }); + + expect(onRecoverableError).not.toHaveBeenCalled(); + expect(container.firstElementChild).toBe(serverRoot); + expect(container.querySelector('input')).toBe(serverInput); + expect(container.querySelector('li li')).toBeNull(); + + const button = container.querySelector( + '.rc-pagination-options .rc-pagination-simple-pager', + ).firstElementChild; + fireEvent.change(serverInput, { target: { value: '3' } }); + fireEvent.click(button); + expect(onChange).toHaveBeenLastCalledWith(3, 10); + + const quickInput = container.querySelector( + '.rc-pagination-options-quick-jumper input', + ); + fireEvent.change(quickInput, { target: { value: '4' } }); + fireEvent.keyUp(button, { key: 'Enter', keyCode: 13, which: 13 }); + expect(onChange).toHaveBeenLastCalledWith(4, 10); + } finally { + await act(async () => root?.unmount()); + container.remove(); + } + }, + ); +}); diff --git a/tests/renderability.test.tsx b/tests/renderability.test.tsx new file mode 100644 index 00000000..de2ee1f2 --- /dev/null +++ b/tests/renderability.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import Pagination from '../src'; + +describe('ReactNode renderability', () => { + it('keeps zero page content clickable', () => { + const onChange = jest.fn(); + const { container } = render( + (type === 'page' ? 0 : node)} + />, + ); + const page = container.querySelector('.rc-pagination-item-2'); + expect(page.textContent).toBe('0'); + fireEvent.click(page); + expect(onChange).toHaveBeenCalledWith(2, 10); + }); + + it.each([false, true])( + 'keeps a zero go button interactive in simple=%s', + (simple) => { + const onChange = jest.fn(); + const { container } = render( + , + ); + const input = container.querySelector('input'); + fireEvent.change(input, { target: { value: '3' } }); + const button = Array.from(container.querySelectorAll('span')).find( + (node) => node.textContent === '0' && node.childElementCount === 0, + ); + expect(button).toBeTruthy(); + if (!simple) { + fireEvent.blur(input); + expect(onChange).not.toHaveBeenCalled(); + } + fireEvent.click(button); + expect(onChange).toHaveBeenCalledWith(3, 10); + }, + ); +});