Add kind picker to the CatalogPage
Signed-off-by: Tim Hansen <timbonicus@gmail.com>
This commit is contained in:
@@ -34,15 +34,14 @@ export const CustomCatalogPage = ({
|
||||
}: CatalogPageProps) => {
|
||||
return (
|
||||
<PageWithHeader title={`${orgName} Catalog`} themeId="home">
|
||||
<Content>
|
||||
<ContentHeader title="Components">
|
||||
<CreateButton title="Create Component" to={link} />
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
</ContentHeader>
|
||||
<EntityListProvider>
|
||||
<EntityListProvider>
|
||||
<Content>
|
||||
<ContentHeader titleComponent={<CatalogKindHeader />}>
|
||||
<CreateButton title="Create Component" to={link} />
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
</ContentHeader>
|
||||
<FilteredEntityLayout>
|
||||
<FilterContainer>
|
||||
<EntityKindPicker initialFilter="component" hidden />
|
||||
<EntityTypePicker />
|
||||
<UserListPicker initialFilter={initiallySelectedFilter} />
|
||||
<EntityTagPicker />
|
||||
@@ -51,8 +50,8 @@ export const CustomCatalogPage = ({
|
||||
<CatalogTable columns={columns} actions={actions} />
|
||||
</EntityListContainer>
|
||||
</FilteredEntityLayout>
|
||||
</EntityListProvider>
|
||||
</Content>
|
||||
</Content>
|
||||
</EntityListProvider>
|
||||
</PageWithHeader>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('<ContentHeader/>', () => {
|
||||
|
||||
it('should render with titleComponent', async () => {
|
||||
const title = 'Custom title';
|
||||
const titleComponent = () => <h1>{title}</h1>;
|
||||
const titleComponent = <h1>{title}</h1>;
|
||||
const rendered = await renderInTestApp(
|
||||
<ContentHeader titleComponent={titleComponent} />,
|
||||
);
|
||||
|
||||
@@ -77,7 +77,7 @@ const ContentHeaderTitle = ({
|
||||
|
||||
type ContentHeaderProps = {
|
||||
title?: ContentHeaderTitleProps['title'];
|
||||
titleComponent?: ComponentType;
|
||||
titleComponent?: JSX.Element;
|
||||
description?: string;
|
||||
textAlign?: 'left' | 'right' | 'center';
|
||||
};
|
||||
@@ -92,7 +92,7 @@ export const ContentHeader = ({
|
||||
const classes = useStyles({ textAlign })();
|
||||
|
||||
const renderedTitle = TitleComponent ? (
|
||||
<TitleComponent />
|
||||
TitleComponent
|
||||
) : (
|
||||
<ContentHeaderTitle title={title} className={classes.title} />
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
@@ -26,6 +26,7 @@ import { EntityKindFilter, EntityTypeFilter } from '../../filters';
|
||||
|
||||
import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { renderWithEffects } from '@backstage/test-utils';
|
||||
|
||||
const entities: Entity[] = [
|
||||
{
|
||||
@@ -79,7 +80,7 @@ const apis = ApiRegistry.from([
|
||||
|
||||
describe('<EntityTypePicker/>', () => {
|
||||
it('renders available entity types', async () => {
|
||||
const rendered = render(
|
||||
const rendered = await renderWithEffects(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ filters: { kind: new EntityKindFilter('component') } }}
|
||||
@@ -104,7 +105,7 @@ describe('<EntityTypePicker/>', () => {
|
||||
|
||||
it('sets the selected type filter', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
const rendered = await renderWithEffects(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
|
||||
@@ -36,7 +36,7 @@ export const EntityTypePicker = () => {
|
||||
}
|
||||
}, [error, alertApi]);
|
||||
|
||||
if (!availableTypes || error) return null;
|
||||
if (availableTypes.length === 0 || error) return null;
|
||||
|
||||
const items = [
|
||||
{ value: 'all', label: 'All' },
|
||||
|
||||
@@ -22,6 +22,7 @@ export {
|
||||
} from './useEntityListProvider';
|
||||
export type { DefaultEntityFilters } from './useEntityListProvider';
|
||||
export { useEntityTypeFilter } from './useEntityTypeFilter';
|
||||
export { useEntityKinds } from './useEntityKinds';
|
||||
export { useOwnUser } from './useOwnUser';
|
||||
export { useRelatedEntities } from './useRelatedEntities';
|
||||
export { useStarredEntities } from './useStarredEntities';
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2021 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 { useEffect, useState } from 'react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { catalogApiRef } from '../api';
|
||||
|
||||
export function useEntityKinds() {
|
||||
const [kinds, setKinds] = useState(['component']);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadKinds() {
|
||||
const entities = await catalogApi
|
||||
.getEntities({ fields: ['kind'] })
|
||||
.then(response => response.items);
|
||||
setKinds([...new Set(entities.map(e => e.kind))].sort());
|
||||
}
|
||||
loadKinds();
|
||||
}, [catalogApi]);
|
||||
|
||||
return kinds;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2021 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, useState } from 'react';
|
||||
import {
|
||||
capitalize,
|
||||
createStyles,
|
||||
InputBase,
|
||||
makeStyles,
|
||||
MenuItem,
|
||||
Select,
|
||||
Theme,
|
||||
} from '@material-ui/core';
|
||||
import {
|
||||
EntityKindFilter,
|
||||
useEntityKinds,
|
||||
useEntityListProvider,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
...theme.typography.h4,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
type CatalogKindHeaderProps = {
|
||||
initialFilter?: string;
|
||||
};
|
||||
|
||||
export const CatalogKindHeader = ({
|
||||
initialFilter = 'Component',
|
||||
}: CatalogKindHeaderProps) => {
|
||||
const classes = useStyles();
|
||||
const allKinds = useEntityKinds();
|
||||
const { updateFilters, queryParameters } = useEntityListProvider();
|
||||
|
||||
const [selectedKind, setSelectedKind] = useState(
|
||||
[queryParameters.kind].flat()[0] ?? initialFilter,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
kind: selectedKind ? new EntityKindFilter(selectedKind) : undefined,
|
||||
});
|
||||
}, [selectedKind, updateFilters]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
input={<InputBase value={selectedKind} />}
|
||||
value={selectedKind}
|
||||
onChange={e => setSelectedKind(e.target.value as string)}
|
||||
classes={classes}
|
||||
>
|
||||
{allKinds.map(kind => (
|
||||
<MenuItem value={kind}>{capitalize(kind)}s</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
EntityListContainer,
|
||||
FilterContainer,
|
||||
} from '../FilteredEntityLayout';
|
||||
import { CatalogKindHeader } from './CatalogKindHeader';
|
||||
|
||||
export type CatalogPageProps = {
|
||||
initiallySelectedFilter?: UserListFilterKind;
|
||||
@@ -61,18 +62,17 @@ export const CatalogPage = ({
|
||||
|
||||
return (
|
||||
<PageWithHeader title={`${orgName} Catalog`} themeId="home">
|
||||
<Content>
|
||||
<ContentHeader title="Components">
|
||||
<CreateButton
|
||||
title="Create Component"
|
||||
to={createComponentLink && createComponentLink()}
|
||||
/>
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
</ContentHeader>
|
||||
<EntityListProvider>
|
||||
<EntityListProvider>
|
||||
<Content>
|
||||
<ContentHeader titleComponent={<CatalogKindHeader />}>
|
||||
<CreateButton
|
||||
title="Create Component"
|
||||
to={createComponentLink && createComponentLink()}
|
||||
/>
|
||||
<SupportButton>All your software catalog entities</SupportButton>
|
||||
</ContentHeader>
|
||||
<FilteredEntityLayout>
|
||||
<FilterContainer>
|
||||
<EntityKindPicker initialFilter="component" hidden />
|
||||
<EntityTypePicker />
|
||||
<UserListPicker initialFilter={initiallySelectedFilter} />
|
||||
<EntityOwnerPicker />
|
||||
@@ -83,8 +83,8 @@ export const CatalogPage = ({
|
||||
<CatalogTable columns={columns} actions={actions} />
|
||||
</EntityListContainer>
|
||||
</FilteredEntityLayout>
|
||||
</EntityListProvider>
|
||||
</Content>
|
||||
</Content>
|
||||
</EntityListProvider>
|
||||
</PageWithHeader>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user