diff --git a/packages/ui/src/components/TablePagination/TablePagination.stories.tsx b/packages/ui/src/components/TablePagination/TablePagination.stories.tsx new file mode 100644 index 0000000000..b0e841ec15 --- /dev/null +++ b/packages/ui/src/components/TablePagination/TablePagination.stories.tsx @@ -0,0 +1,58 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useArgs } from '@storybook/preview-api'; +import type { Meta, StoryObj } from '@storybook/react'; +import { TablePagination } from './TablePagination'; + +const meta = { + title: 'Components/TablePagination', + component: TablePagination, + argTypes: { + pageIndex: { control: 'number' }, + pageSize: { control: 'radio', options: [5, 10, 20, 30, 40, 50] }, + rowCount: { control: 'number' }, + showPageSizeOptions: { control: 'boolean', defaultValue: true }, + setPageIndex: { action: 'setPageIndex' }, + setPageSize: { action: 'setPageSize' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + pageIndex: 0, + pageSize: 10, + rowCount: 100, + }, + render: args => { + const [{}, updateArgs] = useArgs(); + + return ( + { + updateArgs({ pageIndex: value }); + }} + setPageSize={value => { + updateArgs({ pageSize: value }); + }} + /> + ); + }, +}; diff --git a/packages/ui/src/components/TablePagination/TablePagination.styles.css b/packages/ui/src/components/TablePagination/TablePagination.styles.css new file mode 100644 index 0000000000..f7cbb44fc2 --- /dev/null +++ b/packages/ui/src/components/TablePagination/TablePagination.styles.css @@ -0,0 +1,23 @@ +.bui-DataTablePagination { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: var(--bui-space-5); +} + +.bui-DataTablePagination--left { + display: flex; + align-items: center; + justify-content: space-between; +} + +.bui-DataTablePagination--right { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--bui-space-2); +} + +.bui-DataTablePagination--select { + min-width: 10.5rem; +} diff --git a/packages/ui/src/components/TablePagination/TablePagination.tsx b/packages/ui/src/components/TablePagination/TablePagination.tsx new file mode 100644 index 0000000000..9fc9ee7207 --- /dev/null +++ b/packages/ui/src/components/TablePagination/TablePagination.tsx @@ -0,0 +1,126 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import clsx from 'clsx'; +import { Text, ButtonIcon, Select, Icon } from '../..'; +import type { TablePaginationProps } from './types'; + +/** + * Pagination controls for Table components with page navigation and size selection. + * + * @public + */ +export function TablePagination(props: TablePaginationProps) { + const { + className, + pageIndex, + pageSize, + rowCount, + onNextPage, + onPreviousPage, + onPageSizeChange, + setPageIndex, + setPageSize, + showPageSizeOptions = true, + ...rest + } = props; + + const fromCount = (pageIndex ?? 0) * (pageSize ?? 10) + 1; + const toCount = Math.min( + ((pageIndex ?? 0) + 1) * (pageSize ?? 10), + rowCount ?? 0, + ); + + const nextPage = () => { + const currentPageIndex = pageIndex ?? 0; + const currentPageSize = pageSize ?? 10; + const totalRows = rowCount ?? 0; + + // Check if there are more pages to navigate to + const maxPageIndex = Math.ceil(totalRows / currentPageSize) - 1; + + if (currentPageIndex < maxPageIndex) { + onNextPage?.(); // Analytics tracking + setPageIndex?.(currentPageIndex + 1); // Navigate to next page + } + }; + + const previousPage = () => { + const currentPageIndex = pageIndex ?? 0; + + // Check if we can go to previous page + if (currentPageIndex > 0) { + onPreviousPage?.(); // Analytics tracking + setPageIndex?.(currentPageIndex - 1); // Navigate to previous page + } + }; + + return ( +
+
+ {showPageSizeOptions && ( +