From 1a114e7b5aa3277f2bb84b88a2418aeb23a9886e Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Thu, 14 Nov 2024 23:39:58 -0700 Subject: [PATCH 01/11] feat(TechDocs): allow passing in pagination from down to the Signed-off-by: Yingbai He --- .../src/home/components/DefaultTechDocsHome.tsx | 10 ++++++++-- .../techdocs/src/home/components/TechDocsIndexPage.tsx | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) 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/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) => { From cd258e56cd2b1d67f89fa741e064bfe1c6d90e6f Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Fri, 15 Nov 2024 09:37:22 -0700 Subject: [PATCH 02/11] feat(TechDocs): add and similar to Catalog Tables Signed-off-by: Yingbai He --- .../Tables/CursorPaginatedDocsTable.tsx | 65 +++++++++++++++++ .../Tables/OffsetPaginatedDocsTable.tsx | 73 +++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx create mode 100644 plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx 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..e5ed54c624 --- /dev/null +++ b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx @@ -0,0 +1,65 @@ +/* + * 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 { 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/OffsetPaginatedDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx new file mode 100644 index 0000000000..bee52eca85 --- /dev/null +++ b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.tsx @@ -0,0 +1,73 @@ +/* + * 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 { 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, + ...options, + }} + 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} + /> + ); +} From 961058375919d1054a735b82f09d322a6b58d468 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Fri, 15 Nov 2024 09:41:25 -0700 Subject: [PATCH 03/11] feat(TechDocs): use and in Techdocs Signed-off-by: Yingbai He --- .../Tables/CursorPaginatedDocsTable.tsx | 14 +++- .../components/Tables/EntityListDocsTable.tsx | 68 ++++++++++++++++++- .../Tables/OffsetPaginatedDocsTable.tsx | 4 +- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx index e5ed54c624..48a1ecddce 100644 --- a/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx +++ b/plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.tsx @@ -29,8 +29,17 @@ type PaginatedDocsTableProps = { */ export function CursorPaginatedDocsTable(props: PaginatedDocsTableProps) { - const { columns, data, next, prev, title, isLoading, options, ...restProps } = - props; + const { + actions, + columns, + data, + next, + prev, + title, + isLoading, + options, + ...restProps + } = props; return (
{ if (page > 0) { diff --git a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx index 563bec4d22..6ed3138c01 100644 --- a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx +++ b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx @@ -24,14 +24,22 @@ import { TableProps, WarningPanel, } from '@backstage/core-components'; +import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { + getEntityRelations, + humanizeEntityRef, 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 { DocsTableRow } from './types'; +import { toLowerMaybe } from '../../../helpers'; +import { rootDocsRouteRef } from '../../../routes'; /** * Props for {@link EntityListDocsTable}. @@ -44,6 +52,14 @@ export type EntityListDocsTableProps = { options?: TableOptions; }; +const defaultColumns: TableColumn[] = [ + columnFactories.createTitleColumn({ hidden: true }), + columnFactories.createNameColumn(), + columnFactories.createOwnerColumn(), + columnFactories.createKindColumn(), + columnFactories.createTypeColumn(), +]; + /** * Component which renders a table with entities from catalog. * @@ -51,9 +67,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 +84,53 @@ export const EntityListDocsTable = (props: EntityListDocsTableProps) => { ), ]; + 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(', '), + }, + }; + }); + + if (paginationMode === 'cursor') { + return ( + + ); + } else if (paginationMode === 'offset') { + return ( + + ); + } + if (error) { return ( ) { - const { columns, data, isLoading, options } = props; + const { actions, columns, data, isLoading, options } = props; const { updateFilters, setLimit, setOffset, limit, totalItems, offset } = useEntityList(); const [page, setPage] = React.useState( @@ -51,8 +51,10 @@ export function OffsetPaginatedDocsTable(props: TableProps) { 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, From 18f8ff410abeac34d19a77a7d0a94c8ecc65a6a2 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Fri, 15 Nov 2024 16:49:09 -0700 Subject: [PATCH 04/11] feat(TechDocs): update to support the new component Signed-off-by: Yingbai He --- .../src/home/components/TechDocsCustomHome.test.tsx | 11 +++++++++-- .../src/home/components/TechDocsCustomHome.tsx | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) 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}
- + + +
); From 52c82714bc8b6569dd12f2e92a9470bc8aa545c2 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Mon, 18 Nov 2024 00:02:47 -0800 Subject: [PATCH 05/11] feat(TechDocs): add test for paginated docs tables Signed-off-by: Yingbai He --- .../Tables/CursorPaginatedDocsTable.test.tsx | 168 ++++++++++++++++++ .../Tables/OffsetPaginatedDocsTable.test.tsx | 111 ++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 plugins/techdocs/src/home/components/Tables/CursorPaginatedDocsTable.test.tsx create mode 100644 plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.test.tsx 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/OffsetPaginatedDocsTable.test.tsx b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.test.tsx new file mode 100644 index 0000000000..2932b499e1 --- /dev/null +++ b/plugins/techdocs/src/home/components/Tables/OffsetPaginatedDocsTable.test.tsx @@ -0,0 +1,111 @@ +/* + * 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 } from '@testing-library/react'; +import { DocsTableRow } from './types'; +import { renderInTestApp } from '@backstage/test-utils'; +import { + DefaultEntityFilters, + EntityListContextProps, +} from '@backstage/plugin-catalog-react'; +import { MockEntityListContextProvider } from '@backstage/plugin-catalog-react/testUtils'; +import { OffsetPaginatedDocsTable } from './OffsetPaginatedDocsTable'; + +describe('OffsetPaginatedDocsTable', () => { + 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); + }); +}); From 981a08014d07591f2ad81c9b20051edfa3420b71 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Mon, 18 Nov 2024 00:12:21 -0800 Subject: [PATCH 06/11] feat(TechDocs): add default pagination to default Docs page Signed-off-by: Yingbai He --- packages/app/src/App.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 = ( /> } /> - } /> + } + /> } From e153ca600c68578ef9480fad79d8f5caf0c57c00 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Mon, 18 Nov 2024 10:45:12 -0800 Subject: [PATCH 07/11] chore(changeset): add changeset for the add pagination support change Signed-off-by: Yingbai He --- .changeset/curly-beans-brake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/curly-beans-brake.md 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 From 21d3282cda52126619b0a6f1e73d70099d215061 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Mon, 18 Nov 2024 15:33:40 -0800 Subject: [PATCH 08/11] feat(TechDocs): update documentations for Signed-off-by: Yingbai He --- plugins/techdocs/report.api.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/techdocs/report.api.md b/plugins/techdocs/report.api.md index c58a946287..77afe5545a 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'; @@ -173,7 +174,12 @@ export const EntityListDocsTable: { toggleStarredEntity: Function, ): (row: DocsTableRow) => { cellStyle: { - paddingLeft: string; + paddingLeft: string + /** + * Props for {@link EntityListDocsTable}. + * + * @public + */; }; icon: () => React_2.JSX.Element; tooltip: string; @@ -309,6 +315,7 @@ export type TechDocsIndexPageProps = { columns?: TableColumn[]; actions?: TableProps['actions']; ownerPickerMode?: EntityOwnerPickerProps['mode']; + pagination?: EntityListPagination; }; // @public @deprecated (undocumented) From cd48d9751c77cd76195bcc42a7357c01a6701f1a Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Tue, 26 Nov 2024 11:05:49 -0800 Subject: [PATCH 09/11] chore(TechDocs): extract `defaultColumns` into the columns util file Signed-off-by: Yingbai He --- .../techdocs/src/home/components/Tables/DocsTable.tsx | 10 +--------- .../src/home/components/Tables/EntityListDocsTable.tsx | 10 +--------- .../techdocs/src/home/components/Tables/columns.tsx | 8 ++++++++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/plugins/techdocs/src/home/components/Tables/DocsTable.tsx b/plugins/techdocs/src/home/components/Tables/DocsTable.tsx index b1537affdb..850ce4250b 100644 --- a/plugins/techdocs/src/home/components/Tables/DocsTable.tsx +++ b/plugins/techdocs/src/home/components/Tables/DocsTable.tsx @@ -33,7 +33,7 @@ import { TableProps, } from '@backstage/core-components'; import { actionFactories } from './actions'; -import { columnFactories } from './columns'; +import { columnFactories, defaultColumns } from './columns'; import { toLowerMaybe } from '../../../helpers'; import { DocsTableRow } from './types'; @@ -51,14 +51,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 * diff --git a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx index 6ed3138c01..959644ca19 100644 --- a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx +++ b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx @@ -36,7 +36,7 @@ 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 { toLowerMaybe } from '../../../helpers'; import { rootDocsRouteRef } from '../../../routes'; @@ -52,14 +52,6 @@ export type EntityListDocsTableProps = { options?: TableOptions; }; -const defaultColumns: TableColumn[] = [ - columnFactories.createTitleColumn({ hidden: true }), - columnFactories.createNameColumn(), - columnFactories.createOwnerColumn(), - columnFactories.createKindColumn(), - columnFactories.createTypeColumn(), -]; - /** * Component which renders a table with entities from catalog. * 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(), +]; From 0928c8fe558d12fecfb0822bbe347d9b60e80382 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Tue, 26 Nov 2024 12:25:20 -0800 Subject: [PATCH 10/11] chore(TechDocs): extract entities to docs mapper to be shared amount DocsTable(s) Signed-off-by: Yingbai He --- .../src/home/components/Tables/DocsTable.tsx | 33 +++-------- .../components/Tables/EntityListDocsTable.tsx | 30 ++-------- .../src/home/components/Tables/helpers.ts | 56 +++++++++++++++++++ 3 files changed, 69 insertions(+), 50 deletions(-) create mode 100644 plugins/techdocs/src/home/components/Tables/helpers.ts diff --git a/plugins/techdocs/src/home/components/Tables/DocsTable.tsx b/plugins/techdocs/src/home/components/Tables/DocsTable.tsx index 850ce4250b..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, @@ -34,8 +30,8 @@ import { } from '@backstage/core-components'; import { actionFactories } from './actions'; import { columnFactories, defaultColumns } from './columns'; -import { toLowerMaybe } from '../../../helpers'; import { DocsTableRow } from './types'; +import { entitiesToDocsMapper } from './helpers'; /** * Props for {@link DocsTable}. @@ -63,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 959644ca19..08702d2fde 100644 --- a/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx +++ b/plugins/techdocs/src/home/components/Tables/EntityListDocsTable.tsx @@ -25,10 +25,7 @@ import { WarningPanel, } from '@backstage/core-components'; import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; -import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { - getEntityRelations, - humanizeEntityRef, useEntityList, useStarredEntities, } from '@backstage/plugin-catalog-react'; @@ -38,8 +35,8 @@ import { CursorPaginatedDocsTable } from './CursorPaginatedDocsTable'; import { actionFactories } from './actions'; import { columnFactories, defaultColumns } from './columns'; import { DocsTableRow } from './types'; -import { toLowerMaybe } from '../../../helpers'; import { rootDocsRouteRef } from '../../../routes'; +import { entitiesToDocsMapper } from './helpers'; /** * Props for {@link EntityListDocsTable}. @@ -76,26 +73,11 @@ export const EntityListDocsTable = (props: EntityListDocsTableProps) => { ), ]; - 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, + ); if (paginationMode === 'cursor') { return ( 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(', '), + }, + }; + }); +} From 023e598b66704e94dd97f98000d171fe4287e2c8 Mon Sep 17 00:00:00 2001 From: Yingbai He Date: Tue, 26 Nov 2024 18:03:43 -0800 Subject: [PATCH 11/11] chore(TechDocs): update api doc post changes Signed-off-by: Yingbai He --- plugins/techdocs/report.api.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/techdocs/report.api.md b/plugins/techdocs/report.api.md index 77afe5545a..a2ef766a0b 100644 --- a/plugins/techdocs/report.api.md +++ b/plugins/techdocs/report.api.md @@ -174,12 +174,7 @@ export const EntityListDocsTable: { toggleStarredEntity: Function, ): (row: DocsTableRow) => { cellStyle: { - paddingLeft: string - /** - * Props for {@link EntityListDocsTable}. - * - * @public - */; + paddingLeft: string; }; icon: () => React_2.JSX.Element; tooltip: string;