Merge pull request #22527 from sennyeya/better-catalog-pagination-info

feat(catalog): Add a title to the PaginatedCatalogTable.
This commit is contained in:
Rickard Dybeck
2024-03-11 08:31:42 -07:00
committed by GitHub
9 changed files with 69 additions and 8 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': minor
---
Number of results is now directly added as the field `totalItems` on `useEntityList`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': minor
---
Adds a title to the `PaginatedCatalogTable` for better visibility on what you're viewing.
+1
View File
@@ -306,6 +306,7 @@ export type EntityListContextProps<
next?: () => void;
prev?: () => void;
};
totalItems?: number;
};
// @public
@@ -235,6 +235,7 @@ describe('<EntityListProvider />', () => {
await waitFor(() => {
expect(result.current.entities.length).toBe(1);
});
expect(result.current.totalItems).toBe(1);
await expect(() =>
waitFor(() => {
@@ -251,6 +252,7 @@ describe('<EntityListProvider />', () => {
await waitFor(() => {
expect(result.current.backendEntities.length).toBeGreaterThan(0);
});
expect(result.current.totalItems).toBe(2);
expect(result.current.backendEntities.length).toBe(2);
expect(mockCatalogApi.getEntities).toHaveBeenCalledTimes(1);
@@ -276,6 +278,8 @@ describe('<EntityListProvider />', () => {
});
expect(result.current.backendEntities.length).toBe(2);
expect(result.current.totalItems).toBe(2);
mockCatalogApi.getEntities!.mockRejectedValueOnce('error');
act(() => {
result.current.updateFilters({ kind: new EntityKindFilter('api') });
@@ -455,6 +459,8 @@ describe('<EntityListProvider pagination />', () => {
orderFields,
});
});
expect(result.current.totalItems).toBe(10);
});
it('returns an error on catalogApi failure', async () => {
@@ -108,6 +108,8 @@ export type EntityListContextProps<
next?: () => void;
prev?: () => void;
};
totalItems?: number;
};
/**
@@ -124,6 +126,7 @@ type OutputState<EntityFilters extends DefaultEntityFilters> = {
entities: Entity[];
backendEntities: Entity[];
pageInfo?: QueryEntitiesResponse['pageInfo'];
totalItems?: number;
};
/**
@@ -223,6 +226,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
backendEntities: response.items,
entities: response.items.filter(entityFilter),
pageInfo: response.pageInfo,
totalItems: response.totalItems,
});
}
} else {
@@ -243,6 +247,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
backendEntities: response.items,
entities: response.items.filter(entityFilter),
pageInfo: response.pageInfo,
totalItems: response.totalItems,
});
}
}
@@ -262,16 +267,20 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
const response = await catalogApi.getEntities({
filter: backendFilter,
});
const entities = response.items.filter(entityFilter);
setOutputState({
appliedFilters: requestedFilters,
backendEntities: response.items,
entities: response.items.filter(entityFilter),
entities,
totalItems: entities.length,
});
} else {
const entities = outputState.backendEntities.filter(entityFilter);
setOutputState({
appliedFilters: requestedFilters,
backendEntities: outputState.backendEntities,
entities: outputState.backendEntities.filter(entityFilter),
entities,
totalItems: entities.length,
});
}
}
@@ -352,6 +361,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
loading,
error,
pageInfo,
totalItems: outputState.totalItems,
}),
[outputState, updateFilters, queryParameters, loading, error, pageInfo],
);
@@ -71,6 +71,8 @@ export function MockEntityListContextProvider<
loading: value?.loading ?? false,
queryParameters: value?.queryParameters ?? defaultValues.queryParameters,
error: value?.error,
totalItems:
value?.totalItems ?? (value?.entities ?? defaultValues.entities).length,
}),
[value, defaultValues, filters, updateFilters],
);
@@ -88,7 +88,8 @@ export const CatalogTable = (props: CatalogTableProps) => {
} = props;
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
const entityListContext = useEntityList();
const { loading, error, entities, filters, pageInfo } = entityListContext;
const { loading, error, entities, filters, pageInfo, totalItems } =
entityListContext;
const enablePagination = !!pageInfo;
const tableColumns = useMemo(
@@ -175,7 +176,7 @@ export const CatalogTable = (props: CatalogTableProps) => {
.filter(s => s)
.join(' ');
const title = `${titleDisplay} (${entities.length})`;
const title = `${titleDisplay} (${totalItems})`;
const actions = props.actions || defaultActions;
const options = {
actionsColumnIndex: -1,
@@ -216,7 +217,7 @@ export const CatalogTable = (props: CatalogTableProps) => {
pageSizeOptions: [20, 50, 100],
...options,
}}
title={`${titleDisplay} (${entities.length})`}
title={title}
data={rows}
actions={actions}
subtitle={subtitle}
@@ -14,14 +14,16 @@
* limitations under the License.
*/
import React, { ReactNode } from 'react';
import { fireEvent, render } from '@testing-library/react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { PaginatedCatalogTable } from './PaginatedCatalogTable';
import { screen } from '@testing-library/react';
import { CatalogTableRow } from './types';
import { renderInTestApp } from '@backstage/test-utils';
import {
EntityKindFilter,
MockEntityListContextProvider,
DefaultEntityFilters,
EntityListContextProps,
MockEntityListContextProvider,
} from '@backstage/plugin-catalog-react';
describe('PaginatedCatalogTable', () => {
@@ -134,4 +136,32 @@ describe('PaginatedCatalogTable', () => {
fireEvent.click(prevButton);
expect(fn).toHaveBeenCalled();
});
it('should display entity names when loading has finished and no error occurred', async () => {
await renderInTestApp(
<MockEntityListContextProvider
value={{
entities: data.map(e => e.entity),
totalItems: data.length,
filters: {
kind: new EntityKindFilter('component'),
},
}}
>
<PaginatedCatalogTable
data={data}
columns={columns}
next={undefined}
title="My title"
/>
</MockEntityListContextProvider>,
);
expect(screen.getByText(/component-0/)).toBeInTheDocument();
expect(screen.getByText(/component-50/)).toBeInTheDocument();
expect(screen.getByText(/component-99/)).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText(/My title/)).toBeInTheDocument();
});
});
});
@@ -32,11 +32,12 @@ type PaginatedCatalogTableProps = {
* @internal
*/
export function PaginatedCatalogTable(props: PaginatedCatalogTableProps) {
const { columns, data, next, prev } = props;
const { columns, data, next, prev, title, isLoading } = props;
const { updateFilters } = useEntityList();
return (
<Table
title={isLoading ? '' : title}
columns={columns}
data={data}
options={{