diff --git a/.changeset/curly-beans-brake.md b/.changeset/curly-beans-brake.md
new file mode 100644
index 0000000000..4afb11578d
--- /dev/null
+++ b/.changeset/curly-beans-brake.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs': minor
+---
+
+Add pagination support to TechDocs Index Page and make it the default
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index ccf06b17bd..7e914bdc9b 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -164,7 +164,10 @@ const routes = (
/>
}
/>
- } />
+ }
+ />
}
diff --git a/plugins/techdocs/report.api.md b/plugins/techdocs/report.api.md
index 2cc3934f62..c577de5eac 100644
--- a/plugins/techdocs/report.api.md
+++ b/plugins/techdocs/report.api.md
@@ -12,6 +12,7 @@ import { Config } from '@backstage/config';
import { CSSProperties } from '@material-ui/styles/withStyles';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
+import { EntityListPagination } from '@backstage/plugin-catalog-react';
import { EntityOwnerPickerProps } from '@backstage/plugin-catalog-react';
import { FetchApi } from '@backstage/core-plugin-api';
import { IdentityApi } from '@backstage/core-plugin-api';
@@ -309,6 +310,7 @@ export type TechDocsIndexPageProps = {
columns?: TableColumn[];
actions?: TableProps['actions'];
ownerPickerMode?: EntityOwnerPickerProps['mode'];
+ pagination?: EntityListPagination;
};
// @public @deprecated (undocumented)
diff --git a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
index 2fbd5bb50a..f9757f29cc 100644
--- a/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
+++ b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
@@ -46,7 +46,13 @@ export type DefaultTechDocsHomeProps = TechDocsIndexPageProps;
* @public
*/
export const DefaultTechDocsHome = (props: TechDocsIndexPageProps) => {
- const { initialFilter = 'owned', columns, actions, ownerPickerMode } = props;
+ const {
+ initialFilter = 'owned',
+ columns,
+ actions,
+ ownerPickerMode,
+ pagination,
+ } = props;
return (
@@ -55,7 +61,7 @@ export const DefaultTechDocsHome = (props: TechDocsIndexPageProps) => {
Discover documentation in your ecosystem.
-
+
diff --git a/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.test.tsx b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.test.tsx
new file mode 100644
index 0000000000..d7ab3827e1
--- /dev/null
+++ b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.test.tsx
@@ -0,0 +1,168 @@
+/*
+ * 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 React, { ReactNode } from 'react';
+import { fireEvent, screen, waitFor } from '@testing-library/react';
+import { CursorPaginatedDocsTable } from './CursorPaginatedDocsTable';
+import { DocsTableRow } from './types';
+import { renderInTestApp } from '@backstage/test-utils';
+import {
+ DefaultEntityFilters,
+ EntityKindFilter,
+ EntityListContextProps,
+} from '@backstage/plugin-catalog-react';
+import { MockEntityListContextProvider } from '@backstage/plugin-catalog-react/testUtils';
+
+describe('CursorPaginatedDocsTable', () => {
+ const data = new Array(100).fill(0).map((_, index) => {
+ const name = `techdocs-${index}`;
+ return {
+ entity: {
+ apiVersion: '1',
+ kind: 'TestKind',
+ metadata: {
+ name,
+ },
+ },
+ resolved: {
+ docsUrl: 'https://example.com',
+ ownedByRelationsTitle: 'owned',
+ ownedByRelations: [],
+ },
+ } as DocsTableRow;
+ });
+
+ const columns = [
+ {
+ title: 'Title',
+ field: 'entity.metadata.name',
+ searchable: true,
+ },
+ ];
+
+ const wrapInContext = (
+ node: ReactNode,
+ value?: Partial>,
+ ) => {
+ return (
+
+ {node}
+
+ );
+ };
+
+ it('should display all the items', async () => {
+ await renderInTestApp(
+ wrapInContext(),
+ );
+
+ for (const item of data) {
+ expect(screen.queryByText(item.entity.metadata.name)).toBeInTheDocument();
+ }
+ });
+
+ it('should display and invoke the next button', async () => {
+ const { rerender } = await renderInTestApp(
+ wrapInContext(
+ ,
+ ),
+ );
+
+ expect(
+ screen.queryAllByRole('button', { name: 'Next Page' })[0],
+ ).toBeDisabled();
+
+ const fn = jest.fn();
+
+ rerender(
+ wrapInContext(
+ ,
+ ),
+ );
+
+ const nextButton = screen.queryAllByRole('button', {
+ name: 'Next Page',
+ })[0];
+ expect(nextButton).toBeEnabled();
+
+ fireEvent.click(nextButton);
+ expect(fn).toHaveBeenCalled();
+ });
+
+ it('should display and invoke the prev button', async () => {
+ const { rerender } = await renderInTestApp(
+ wrapInContext(
+ ,
+ ),
+ );
+
+ expect(
+ screen.queryAllByRole('button', { name: 'Next Page' })[0],
+ ).toBeDisabled();
+
+ const fn = jest.fn();
+
+ rerender(
+ wrapInContext(
+ ,
+ ),
+ );
+
+ const prevButton = screen.queryAllByRole('button', {
+ name: 'Previous Page',
+ })[0];
+ expect(prevButton).toBeEnabled();
+
+ fireEvent.click(prevButton);
+ expect(fn).toHaveBeenCalled();
+ });
+
+ it('should display entity names when loading has finished and no error occurred', async () => {
+ await renderInTestApp(
+ e.entity),
+ totalItems: data.length,
+ filters: {
+ kind: new EntityKindFilter('techdocs'),
+ },
+ }}
+ >
+
+ ,
+ );
+
+ expect(screen.getByText(/techdocs-0/)).toBeInTheDocument();
+ expect(screen.getByText(/techdocs-50/)).toBeInTheDocument();
+ expect(screen.getByText(/techdocs-99/)).toBeInTheDocument();
+ await waitFor(() => {
+ expect(screen.getByText(/My title/)).toBeInTheDocument();
+ });
+ });
+});
diff --git a/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx
new file mode 100644
index 0000000000..48a1ecddce
--- /dev/null
+++ b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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 React from 'react';
+
+import { Table, TableProps } from '@backstage/core-components';
+import { DocsTableRow } from './types';
+
+type PaginatedDocsTableProps = {
+ prev?(): void;
+ next?(): void;
+} & TableProps;
+
+/**
+ * @internal
+ */
+
+export function CursorPaginatedDocsTable(props: PaginatedDocsTableProps) {
+ const {
+ actions,
+ columns,
+ data,
+ next,
+ prev,
+ title,
+ isLoading,
+ options,
+ ...restProps
+ } = props;
+
+ return (
+ {
+ if (page > 0) {
+ next?.();
+ } else {
+ prev?.();
+ }
+ }}
+ /* this will enable the prev button accordingly */
+ page={prev ? 1 : 0}
+ /* this will enable the next button accordingly */
+ totalCount={next ? Number.MAX_VALUE : Number.MAX_SAFE_INTEGER}
+ localization={{ pagination: { labelDisplayedRows: '' } }}
+ isLoading={isLoading}
+ {...restProps}
+ />
+ );
+}
diff --git a/plugins/techdocs/src/home/components/Tables/DocsTable.tsx b/plugins/techdocs/src/home/components/Tables/DocsTable.tsx
index b1537affdb..29dbb3fd4f 100644
--- a/plugins/techdocs/src/home/components/Tables/DocsTable.tsx
+++ b/plugins/techdocs/src/home/components/Tables/DocsTable.tsx
@@ -18,11 +18,7 @@ import React from 'react';
import useCopyToClipboard from 'react-use/esm/useCopyToClipboard';
import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
-import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';
-import {
- getEntityRelations,
- humanizeEntityRef,
-} from '@backstage/plugin-catalog-react';
+import { Entity } from '@backstage/catalog-model';
import { rootDocsRouteRef } from '../../../routes';
import {
EmptyState,
@@ -33,9 +29,9 @@ import {
TableProps,
} from '@backstage/core-components';
import { actionFactories } from './actions';
-import { columnFactories } from './columns';
-import { toLowerMaybe } from '../../../helpers';
+import { columnFactories, defaultColumns } from './columns';
import { DocsTableRow } from './types';
+import { entitiesToDocsMapper } from './helpers';
/**
* Props for {@link DocsTable}.
@@ -51,14 +47,6 @@ export type DocsTableProps = {
options?: TableOptions;
};
-const defaultColumns: TableColumn[] = [
- columnFactories.createTitleColumn({ hidden: true }),
- columnFactories.createNameColumn(),
- columnFactories.createOwnerColumn(),
- columnFactories.createKindColumn(),
- columnFactories.createTypeColumn(),
-];
-
/**
* Component which renders a table documents
*
@@ -71,26 +59,11 @@ export const DocsTable = (props: DocsTableProps) => {
const config = useApi(configApiRef);
if (!entities) return null;
- const documents = entities.map(entity => {
- const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
- return {
- entity,
- resolved: {
- docsUrl: getRouteToReaderPageFor({
- namespace: toLowerMaybe(
- entity.metadata.namespace ?? 'default',
- config,
- ),
- kind: toLowerMaybe(entity.kind, config),
- name: toLowerMaybe(entity.metadata.name, config),
- }),
- ownedByRelations,
- ownedByRelationsTitle: ownedByRelations
- .map(r => humanizeEntityRef(r, { defaultKind: 'group' }))
- .join(', '),
- },
- };
- });
+ const documents = entitiesToDocsMapper(
+ entities,
+ getRouteToReaderPageFor,
+ config,
+ );
const defaultActions: TableProps['actions'] = [
actionFactories.createCopyDocsUrlAction(copyToClipboard),
diff --git a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx
index 563bec4d22..08702d2fde 100644
--- a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx
+++ b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx
@@ -24,14 +24,19 @@ import {
TableProps,
WarningPanel,
} from '@backstage/core-components';
+import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
import {
useEntityList,
useStarredEntities,
} from '@backstage/plugin-catalog-react';
import { DocsTable } from './DocsTable';
+import { OffsetPaginatedDocsTable } from './OffsetPaginatedDocsTable';
+import { CursorPaginatedDocsTable } from './CursorPaginatedDocsTable';
import { actionFactories } from './actions';
-import { columnFactories } from './columns';
+import { columnFactories, defaultColumns } from './columns';
import { DocsTableRow } from './types';
+import { rootDocsRouteRef } from '../../../routes';
+import { entitiesToDocsMapper } from './helpers';
/**
* Props for {@link EntityListDocsTable}.
@@ -51,9 +56,12 @@ export type EntityListDocsTableProps = {
*/
export const EntityListDocsTable = (props: EntityListDocsTableProps) => {
const { columns, actions, options } = props;
- const { loading, error, entities, filters } = useEntityList();
+ const { loading, error, entities, filters, paginationMode, pageInfo } =
+ useEntityList();
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
const [, copyToClipboard] = useCopyToClipboard();
+ const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
+ const config = useApi(configApiRef);
const title = capitalize(filters.user?.value ?? 'all');
@@ -65,6 +73,38 @@ export const EntityListDocsTable = (props: EntityListDocsTableProps) => {
),
];
+ const documents = entitiesToDocsMapper(
+ entities,
+ getRouteToReaderPageFor,
+ config,
+ );
+
+ if (paginationMode === 'cursor') {
+ return (
+
+ );
+ } else if (paginationMode === 'offset') {
+ return (
+
+ );
+ }
+
if (error) {
return (
{
+ const data = new Array(100).fill(0).map((_, index) => {
+ const name = `tectdocs-${index}`;
+ return {
+ entity: {
+ apiVersion: '1',
+ kind: 'TestKind',
+ metadata: {
+ name,
+ },
+ },
+ resolved: {
+ docsUrl: 'https://example.com',
+ ownedByRelationsTitle: 'owned',
+ ownedByRelations: [],
+ },
+ } as DocsTableRow;
+ });
+
+ const columns = [
+ {
+ title: 'Title',
+ field: 'entity.metadata.name',
+ searchable: true,
+ },
+ ];
+
+ const wrapInContext = (
+ node: ReactNode,
+ value?: Partial>,
+ ) => {
+ return (
+
+ {node}
+
+ );
+ };
+
+ it('should display all the items', async () => {
+ await renderInTestApp(
+ wrapInContext(
+ ,
+ {
+ setOffset: jest.fn(),
+ limit: Number.MAX_SAFE_INTEGER,
+ offset: 0,
+ totalItems: data.length,
+ },
+ ),
+ );
+
+ for (const item of data) {
+ expect(screen.queryByText(item.entity.metadata.name)).toBeInTheDocument();
+ }
+ });
+
+ it('should display and invoke the next and previous buttons', async () => {
+ const offsetFn = jest.fn();
+
+ await renderInTestApp(
+ wrapInContext(
+ ,
+ { setOffset: offsetFn, limit: 10, totalItems: data.length, offset: 0 },
+ ),
+ );
+
+ expect(offsetFn).toHaveBeenNthCalledWith(1, 0);
+ const nextButton = screen.queryAllByRole('button', {
+ name: 'Next Page',
+ })[0];
+ expect(nextButton).toBeEnabled();
+
+ fireEvent.click(nextButton);
+ expect(offsetFn).toHaveBeenNthCalledWith(2, 10);
+
+ const prevButton = screen.queryAllByRole('button', {
+ name: 'Previous Page',
+ })[0];
+ expect(prevButton).toBeEnabled();
+
+ fireEvent.click(prevButton);
+ expect(offsetFn).toHaveBeenNthCalledWith(3, 0);
+ });
+});
diff --git a/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx
new file mode 100644
index 0000000000..9f4572117d
--- /dev/null
+++ b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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 React, { useEffect } from 'react';
+
+import { Table, TableProps } from '@backstage/core-components';
+import { DocsTableRow } from './types';
+import {
+ EntityTextFilter,
+ useEntityList,
+} from '@backstage/plugin-catalog-react';
+
+/**
+ * @internal
+ */
+export function OffsetPaginatedDocsTable(props: TableProps) {
+ const { actions, columns, data, isLoading, options } = props;
+ const { updateFilters, setLimit, setOffset, limit, totalItems, offset } =
+ useEntityList();
+ const [page, setPage] = React.useState(
+ offset && limit ? Math.floor(offset / limit) : 0,
+ );
+
+ useEffect(() => {
+ if (totalItems && page * limit >= totalItems) {
+ setOffset!(Math.max(0, totalItems - limit));
+ } else {
+ setOffset!(Math.max(0, page * limit));
+ }
+ }, [setOffset, page, limit, totalItems]);
+
+ return (
+
+ columns={columns}
+ data={data}
+ options={{
+ paginationPosition: 'both',
+ pageSizeOptions: [5, 10, 20, 50, 100],
+ pageSize: limit,
+ emptyRowsWhenPaging: false,
+ actionsColumnIndex: -1,
+ ...options,
+ }}
+ actions={actions}
+ onSearchChange={(searchText: string) =>
+ updateFilters({
+ text: searchText ? new EntityTextFilter(searchText) : undefined,
+ })
+ }
+ page={page}
+ onPageChange={newPage => {
+ setPage(newPage);
+ }}
+ onRowsPerPageChange={pageSize => {
+ setLimit(pageSize);
+ }}
+ totalCount={totalItems}
+ localization={{ pagination: { labelDisplayedRows: '' } }}
+ isLoading={isLoading}
+ />
+ );
+}
diff --git a/plugins/techdocs/src/home/components/Tables/columns.tsx b/plugins/techdocs/src/home/components/Tables/columns.tsx
index ef22f6d288..9947e1de9e 100644
--- a/plugins/techdocs/src/home/components/Tables/columns.tsx
+++ b/plugins/techdocs/src/home/components/Tables/columns.tsx
@@ -85,3 +85,11 @@ export const columnFactories = {
};
},
};
+
+export const defaultColumns: TableColumn[] = [
+ columnFactories.createTitleColumn({ hidden: true }),
+ columnFactories.createNameColumn(),
+ columnFactories.createOwnerColumn(),
+ columnFactories.createKindColumn(),
+ columnFactories.createTypeColumn(),
+];
diff --git a/plugins/techdocs/src/home/components/Tables/helpers.ts b/plugins/techdocs/src/home/components/Tables/helpers.ts
new file mode 100644
index 0000000000..4c6383c35e
--- /dev/null
+++ b/plugins/techdocs/src/home/components/Tables/helpers.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { RELATION_OWNED_BY, Entity } from '@backstage/catalog-model';
+import {
+ getEntityRelations,
+ humanizeEntityRef,
+} from '@backstage/plugin-catalog-react';
+import { toLowerMaybe } from '../../../helpers';
+import { ConfigApi, RouteFunc } from '@backstage/core-plugin-api';
+
+type getRouteFunc = RouteFunc<{
+ namespace: string;
+ kind: string;
+ name: string;
+}>;
+
+export function entitiesToDocsMapper(
+ entities: Entity[],
+ getRouteToReaderPageFor: getRouteFunc,
+ config: ConfigApi,
+) {
+ return entities.map(entity => {
+ const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
+ return {
+ entity,
+ resolved: {
+ docsUrl: getRouteToReaderPageFor({
+ namespace: toLowerMaybe(
+ entity.metadata.namespace ?? 'default',
+ config,
+ ),
+ kind: toLowerMaybe(entity.kind, config),
+ name: toLowerMaybe(entity.metadata.name, config),
+ }),
+ ownedByRelations,
+ ownedByRelationsTitle: ownedByRelations
+ .map(r => humanizeEntityRef(r, { defaultKind: 'group' }))
+ .join(', '),
+ },
+ };
+ });
+}
diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx
index 409d68a593..585248c6c9 100644
--- a/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx
+++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.test.tsx
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-import { catalogApiRef } from '@backstage/plugin-catalog-react';
+import {
+ catalogApiRef,
+ starredEntitiesApiRef,
+ MockStarredEntitiesApi,
+} from '@backstage/plugin-catalog-react';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
import { screen } from '@testing-library/react';
@@ -37,7 +41,10 @@ const mockCatalogApi = catalogApiMock({
});
describe('TechDocsCustomHome', () => {
- const apiRegistry = TestApiRegistry.from([catalogApiRef, mockCatalogApi]);
+ const apiRegistry = TestApiRegistry.from(
+ [catalogApiRef, mockCatalogApi],
+ [starredEntitiesApiRef, new MockStarredEntitiesApi()],
+ );
it('should render a TechDocs home page', async () => {
const tabsConfig = [
diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
index 56d086acfc..d89bc5ee1b 100644
--- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
+++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
@@ -23,6 +23,7 @@ import {
catalogApiRef,
CatalogApi,
useEntityOwnership,
+ EntityListProvider,
} from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { DocsTable } from './Tables';
@@ -127,7 +128,9 @@ const CustomPanel = ({
) : null}
>
);
diff --git a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx
index adbb68d3e9..cc89d22c08 100644
--- a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx
+++ b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx
@@ -18,6 +18,7 @@ import React from 'react';
import { useOutlet } from 'react-router-dom';
import { TableColumn, TableProps } from '@backstage/core-components';
import {
+ EntityListPagination,
EntityOwnerPickerProps,
UserListFilterKind,
} from '@backstage/plugin-catalog-react';
@@ -34,6 +35,7 @@ export type TechDocsIndexPageProps = {
columns?: TableColumn[];
actions?: TableProps['actions'];
ownerPickerMode?: EntityOwnerPickerProps['mode'];
+ pagination?: EntityListPagination;
};
export const TechDocsIndexPage = (props: TechDocsIndexPageProps) => {