diff --git a/packages/meta/src/pagination/Pagination.tsx b/packages/meta/src/pagination/Pagination.tsx new file mode 100644 index 0000000..62ce511 --- /dev/null +++ b/packages/meta/src/pagination/Pagination.tsx @@ -0,0 +1,151 @@ +import * as React from 'react'; +import DoubleLeftOutlined from '@ant-design/icons/DoubleLeftOutlined'; +import DoubleRightOutlined from '@ant-design/icons/DoubleRightOutlined'; +import LeftOutlined from '@ant-design/icons/LeftOutlined'; +import RightOutlined from '@ant-design/icons/RightOutlined'; +import classNames from 'classnames'; +import type { PaginationLocale, PaginationProps as RcPaginationProps } from 'rc-pagination'; +import RcPagination from 'rc-pagination'; +import enUS from 'rc-pagination/lib/locale/en_US'; + +import { ConfigContext } from '../config-provider'; +import useSize from '../config-provider/hooks/useSize'; +import useBreakpoint from '../grid/hooks/useBreakpoint'; +import { useLocale } from '../locale'; +import { useToken } from '../theme/internal'; +import { MiddleSelect, MiniSelect } from './Select'; +import useStyle from './style'; +import BorderedStyle from './style/bordered'; + +export interface PaginationProps extends RcPaginationProps { + showQuickJumper?: boolean | { goButton?: React.ReactNode }; + size?: 'default' | 'small'; + responsive?: boolean; + role?: string; + totalBoundaryShowSizeChanger?: number; + rootClassName?: string; +} + +export type PaginationPosition = 'top' | 'bottom' | 'both'; + +export type PaginationAlign = 'start' | 'center' | 'end'; + +export interface PaginationConfig extends Omit { + position?: PaginationPosition; + align?: PaginationAlign; +} + +export type { PaginationLocale }; + +const Pagination: React.FC = (props) => { + const { + prefixCls: customizePrefixCls, + selectPrefixCls: customizeSelectPrefixCls, + className, + rootClassName, + style, + size: customizeSize, + locale: customLocale, + selectComponentClass, + responsive, + showSizeChanger, + ...restProps + } = props; + const { xs } = useBreakpoint(responsive); + const [, token] = useToken(); + + const { getPrefixCls, direction, pagination = {} } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('pagination', customizePrefixCls); + + // Style + const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls); + + const mergedShowSizeChanger = showSizeChanger ?? pagination.showSizeChanger; + + const iconsProps = React.useMemo>(() => { + const ellipsis = •••; + const prevIcon = ( + + ); + const nextIcon = ( + + ); + const jumpPrevIcon = ( + +
+ {direction === 'rtl' ? ( + + ) : ( + + )} + {ellipsis} +
+
+ ); + const jumpNextIcon = ( + +
+ {direction === 'rtl' ? ( + + ) : ( + + )} + {ellipsis} +
+
+ ); + return { prevIcon, nextIcon, jumpPrevIcon, jumpNextIcon }; + }, [direction, prefixCls]); + + const [contextLocale] = useLocale('Pagination', enUS); + + const locale = { ...contextLocale, ...customLocale }; + + const mergedSize = useSize(customizeSize); + + const isSmall = mergedSize === 'small' || !!(xs && !mergedSize && responsive); + + const selectPrefixCls = getPrefixCls('select', customizeSelectPrefixCls); + + const extendedClassName = classNames( + { + [`${prefixCls}-mini`]: isSmall, + [`${prefixCls}-rtl`]: direction === 'rtl', + [`${prefixCls}-bordered`]: token.wireframe, + }, + pagination?.className, + className, + rootClassName, + hashId, + cssVarCls, + ); + + const mergedStyle: React.CSSProperties = { ...pagination?.style, ...style }; + + return wrapCSSVar( + <> + {token.wireframe && } + + , + ); +}; + +if (process.env.NODE_ENV !== 'production') { + Pagination.displayName = 'Pagination'; +} + +export default Pagination; diff --git a/packages/meta/src/pagination/Select.tsx b/packages/meta/src/pagination/Select.tsx new file mode 100644 index 0000000..d4bb919 --- /dev/null +++ b/packages/meta/src/pagination/Select.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +import type { SelectProps } from '../select'; +import Select from '../select'; + +type CompoundedComponent = React.FC & { + Option: typeof Select.Option; +}; + +const MiniSelect: CompoundedComponent = (props) => ; + +MiniSelect.Option = Select.Option; +MiddleSelect.Option = Select.Option; + +export { MiniSelect, MiddleSelect }; diff --git a/packages/meta/src/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap b/packages/meta/src/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap new file mode 100644 index 0000000..a8fd2d0 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -0,0 +1,6343 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders components/pagination/demo/all.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/all.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/basic.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/basic.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/changer.tsx extend context correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/changer.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/component-token.tsx extend context correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/component-token.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/controlled.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/controlled.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/itemRender.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/itemRender.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/jump.tsx extend context correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/jump.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/mini.tsx extend context correctly 1`] = ` +Array [ + , +
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
  • +
  • + + 4 + +
  • +
  • + + 5 + +
  • +
  • + +
  • +
  • + +
    + Go to + + Page +
    +
  • +
, + , +
    +
  • + Total 50 items +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
  • +
  • + + 4 + +
  • +
  • + + 5 + +
  • +
  • + +
  • +
  • + +
    + Go to + + Page +
    +
  • +
, +] +`; + +exports[`renders components/pagination/demo/mini.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/more.tsx extend context correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/more.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/simple.tsx extend context correctly 1`] = ` +Array [ +
    +
  • + +
  • +
  • + + + / + + 5 +
  • +
  • + +
  • +
, +
, +
    +
  • + +
  • +
  • + + + / + + 5 +
  • +
  • + +
  • +
, +] +`; + +exports[`renders components/pagination/demo/simple.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/total.tsx extend context correctly 1`] = ` +Array [ +
    +
  • + Total 85 items +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
  • +
  • + + 4 + +
  • +
  • + + 5 + +
  • +
  • + +
  • +
  • + +
  • +
, +
, +
    +
  • + 1-20 of 85 items +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
  • +
  • + + 4 + +
  • +
  • + + 5 + +
  • +
  • + +
  • +
  • + +
  • +
, +] +`; + +exports[`renders components/pagination/demo/total.tsx extend context correctly 2`] = `[]`; + +exports[`renders components/pagination/demo/wireframe.tsx extend context correctly 1`] = ` +Array [ + , +
, + , +
, + , +
, + , +] +`; + +exports[`renders components/pagination/demo/wireframe.tsx extend context correctly 2`] = `[]`; diff --git a/packages/meta/src/pagination/__tests__/__snapshots__/demo.test.ts.snap b/packages/meta/src/pagination/__tests__/__snapshots__/demo.test.ts.snap new file mode 100644 index 0000000..4b6e151 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/__snapshots__/demo.test.ts.snap @@ -0,0 +1,4568 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders components/pagination/demo/all.tsx correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/basic.tsx correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/changer.tsx correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/component-token.tsx correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/controlled.tsx correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/itemRender.tsx correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/jump.tsx correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/mini.tsx correctly 1`] = ` +Array [ + , + , + , +
    +
  • + Total 50 items +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
  • +
  • + + 4 + +
  • +
  • + + 5 + +
  • +
  • + +
  • +
  • + +
    + Go to + + Page +
    +
  • +
, +] +`; + +exports[`renders components/pagination/demo/more.tsx correctly 1`] = ` + +`; + +exports[`renders components/pagination/demo/simple.tsx correctly 1`] = ` +Array [ +
    +
  • + +
  • +
  • + + + / + + 5 +
  • +
  • + +
  • +
, +
, +
    +
  • + +
  • +
  • + + + / + + 5 +
  • +
  • + +
  • +
, +] +`; + +exports[`renders components/pagination/demo/total.tsx correctly 1`] = ` +Array [ + , +
, + , +] +`; + +exports[`renders components/pagination/demo/wireframe.tsx correctly 1`] = ` +Array [ + , +
, + , +
, + , +
, + , +] +`; diff --git a/packages/meta/src/pagination/__tests__/__snapshots__/index.test.tsx.snap b/packages/meta/src/pagination/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000..c03f1c9 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,398 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pagination ConfigProvider should be rendered correctly in RTL 1`] = ` + +`; + +exports[`Pagination ConfigProvider should be rendered correctly when componentSize is large 1`] = ` + +`; + +exports[`Pagination rtl render component should be rendered correctly in RTL direction 1`] = ` +
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
+`; diff --git a/packages/meta/src/pagination/__tests__/demo-extend.test.ts b/packages/meta/src/pagination/__tests__/demo-extend.test.ts new file mode 100644 index 0000000..78e4fdb --- /dev/null +++ b/packages/meta/src/pagination/__tests__/demo-extend.test.ts @@ -0,0 +1,3 @@ +import { extendTest } from '../../../tests/shared/demoTest'; + +extendTest('pagination'); diff --git a/packages/meta/src/pagination/__tests__/demo.test.ts b/packages/meta/src/pagination/__tests__/demo.test.ts new file mode 100644 index 0000000..4a989e4 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/demo.test.ts @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('pagination'); diff --git a/packages/meta/src/pagination/__tests__/image.test.ts b/packages/meta/src/pagination/__tests__/image.test.ts new file mode 100644 index 0000000..a9f56d1 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/image.test.ts @@ -0,0 +1,5 @@ +import { imageDemoTest } from '../../../tests/shared/imageTest'; + +describe('Pagination image', () => { + imageDemoTest('pagination'); +}); diff --git a/packages/meta/src/pagination/__tests__/index.test.tsx b/packages/meta/src/pagination/__tests__/index.test.tsx new file mode 100644 index 0000000..3658615 --- /dev/null +++ b/packages/meta/src/pagination/__tests__/index.test.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import type { OptionFC } from 'rc-select/lib/Option'; + +import type { PaginationProps } from '..'; +import Pagination from '..'; +import mountTest from '../../../tests/shared/mountTest'; +import rtlTest from '../../../tests/shared/rtlTest'; +import { fireEvent, render } from '../../../tests/utils'; +import ConfigProvider from '../../config-provider'; +import Select from '../../select'; + +describe('Pagination', () => { + mountTest(Pagination); + rtlTest(Pagination); + + it('should pass disabled to prev and next buttons', () => { + const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => { + if (type === 'prev') { + return ; + } + if (type === 'next') { + return ; + } + return originalElement; + }; + const { container } = render( + , + ); + expect(container.querySelector('button')?.disabled).toBe(true); + }); + + it('should automatically be small when size is not specified', async () => { + const { container } = render(); + expect(container.querySelector('ul')?.className.includes('ant-pagination-mini')).toBe(true); + }); + + // https://github.com/ant-design/ant-design/issues/24913 + // https://github.com/ant-design/ant-design/issues/24501 + it('should onChange called when pageSize change', () => { + const onChange = jest.fn(); + const onShowSizeChange = jest.fn(); + const { container } = render( + , + ); + + fireEvent.mouseDown(container.querySelector('.ant-select-selector')!); + + expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4); + fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]); + expect(onChange).toHaveBeenCalledWith(1, 20); + }); + + it('should support custom selectComponentClass', () => { + const CustomSelect: React.FC<{ className?: string }> & { Option: OptionFC } = ({ + className, + ...props + }) =>