Add a EntityTable

This commit is contained in:
Oliver Sand
2021-02-11 13:23:52 +01:00
parent c7e85614c7
commit 6141ec38fb
6 changed files with 258 additions and 2 deletions
@@ -0,0 +1,79 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { Entity } from '@backstage/catalog-model';
import { Table, TableColumn } from '@backstage/core';
import { makeStyles } from '@material-ui/core';
import React from 'react';
import * as columnFactories from './columns';
import { systemEntityColumns } from './presets';
type Props<T extends Entity> = {
title: string;
variant?: string;
entities: T[];
emptyComponent?: JSX.Element;
columns: TableColumn<T>[];
};
const useStyles = makeStyles(theme => ({
empty: {
padding: theme.spacing(2),
display: 'flex',
justifyContent: 'center',
},
}));
export function EntityTable<T extends Entity>({
entities,
title,
emptyComponent,
variant = 'gridItem',
columns,
}: Props<T>) {
const classes = useStyles();
const tableStyle: React.CSSProperties = {
minWidth: '0',
width: '100%',
};
if (variant === 'gridItem') {
tableStyle.height = 'calc(100% - 10px)';
}
return (
<Table<T>
columns={columns}
title={title}
style={tableStyle}
emptyComponent={
emptyComponent && <div className={classes.empty}>{emptyComponent}</div>
}
options={{
// TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px;
search: false,
paging: false,
actionsColumnIndex: -1,
padding: 'dense',
}}
data={entities}
/>
);
}
EntityTable.columns = columnFactories;
EntityTable.systemEntityColumns = systemEntityColumns;
@@ -0,0 +1,128 @@
/*
* Copyright 2020 Spotify AB
*
* 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 {
Entity,
RELATION_OWNED_BY,
RELATION_PART_OF,
} from '@backstage/catalog-model';
import { TableColumn } from '@backstage/core';
import React from 'react';
import { getEntityRelations } from '../../utils';
import {
EntityRefLink,
EntityRefLinks,
formatEntityRefTitle,
} from '../EntityRefLink';
export function createEntityRefColumn<T extends Entity>({
defaultKind,
}: {
defaultKind?: string;
}): TableColumn<T> {
function formatContent(entity: T): string {
return formatEntityRefTitle(entity, {
defaultKind,
});
}
return {
title: 'Name',
highlight: true,
customFilterAndSearch(filter, entity) {
// TODO: We could implement this more efficiently, like searching over each field individually, but that migth confuse the user
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
// TODO: We could implement this more efficiently by comparing field by field.
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => (
<EntityRefLink entityRef={entity} defaultKind={defaultKind} />
),
};
}
export function createOwnerColumn<T extends Entity>(): TableColumn<T> {
function formatContent(entity: T): string {
const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
return ownedByRelations
.map(r => formatEntityRefTitle(r, { defaultKind: 'group' }))
.join(', ');
}
return {
title: 'Owner',
customFilterAndSearch(filter, entity) {
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => {
const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
return (
<EntityRefLinks entityRefs={ownedByRelations} defaultKind="group" />
);
},
};
}
export function createDomainColumn<T extends Entity>(): TableColumn<T> {
function formatContent(entity: T): string {
const partOfDomainRelations = getEntityRelations(entity, RELATION_PART_OF, {
kind: 'domain',
});
return partOfDomainRelations
.map(r => formatEntityRefTitle(r, { defaultKind: 'domain' }))
.join(', ');
}
return {
title: 'Domain',
customFilterAndSearch(filter, entity) {
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => {
const partOfDomainRelations = getEntityRelations(
entity,
RELATION_PART_OF,
{
kind: 'domain',
},
);
return (
<EntityRefLinks
entityRefs={partOfDomainRelations}
defaultKind="domain"
/>
);
},
};
}
export function createMetadataDescriptionColumn<
T extends Entity
>(): TableColumn<T> {
return {
title: 'Description',
field: 'metadata.description',
width: 'auto',
};
}
@@ -0,0 +1,16 @@
/*
* Copyright 2020 Spotify AB
*
* 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.
*/
export { EntityTable } from './EntityTable';
@@ -0,0 +1,31 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { SystemEntity } from '@backstage/catalog-model';
import { TableColumn } from '@backstage/core';
import {
createEntityRefColumn,
createMetadataDescriptionColumn,
createOwnerColumn,
createDomainColumn,
} from './columns';
export const systemEntityColumns: TableColumn<SystemEntity>[] = [
createEntityRefColumn({ defaultKind: 'system' }),
createOwnerColumn(),
createDomainColumn(),
createMetadataDescriptionColumn(),
];
@@ -15,4 +15,5 @@
*/
export * from './EntityProvider';
export * from './EntityRefLink';
export * from './EntityTable';
export * from './EntityTables';
@@ -23,7 +23,7 @@ import {
WarningPanel,
} from '@backstage/core';
import {
SystemsTable,
EntityTable,
useEntity,
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
@@ -71,7 +71,7 @@ export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => {
}
return (
<SystemsTable
<EntityTable
title="Systems"
variant={variant}
emptyComponent={
@@ -82,6 +82,7 @@ export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => {
</Link>
</div>
}
columns={EntityTable.systemEntityColumns}
entities={entities as SystemEntity[]}
/>
);