refactor(EntityListDocsTable): support custom columns and actions
Signed-off-by: Phil Kuang <pkuang@factset.com>
This commit is contained in:
@@ -18,28 +18,34 @@ import React from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import {
|
||||
formatEntityRefTitle,
|
||||
getEntityRelations,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
import {
|
||||
Button,
|
||||
EmptyState,
|
||||
Link,
|
||||
SubvalueCell,
|
||||
Table,
|
||||
TableColumn,
|
||||
TableProps,
|
||||
} from '@backstage/core-components';
|
||||
import * as actionFactories from './actions';
|
||||
import * as columnFactories from './columns';
|
||||
import { DocsTableRow } from './types';
|
||||
|
||||
export const DocsTable = ({
|
||||
entities,
|
||||
title,
|
||||
loading,
|
||||
columns,
|
||||
actions,
|
||||
}: {
|
||||
entities: Entity[] | undefined;
|
||||
title?: string | undefined;
|
||||
loading?: boolean | undefined;
|
||||
columns?: TableColumn<DocsTableRow>[];
|
||||
actions?: TableProps<DocsTableRow>['actions'];
|
||||
}) => {
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
@@ -47,6 +53,8 @@ export const DocsTable = ({
|
||||
if (!entities) return null;
|
||||
|
||||
const documents = entities.map(entity => {
|
||||
const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
|
||||
|
||||
return {
|
||||
entity,
|
||||
resolved: {
|
||||
@@ -55,32 +63,18 @@ export const DocsTable = ({
|
||||
kind: entity.kind,
|
||||
name: entity.metadata.name,
|
||||
}),
|
||||
ownedByRelations,
|
||||
ownedByRelationsTitle: ownedByRelations
|
||||
.map(r => formatEntityRefTitle(r, { defaultKind: 'group' }))
|
||||
.join(', '),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Document',
|
||||
field: 'name',
|
||||
highlight: true,
|
||||
render: (row: DocsTableRow): React.ReactNode => (
|
||||
<SubvalueCell
|
||||
value={
|
||||
<Link to={row.resolved.docsUrl}>{row.entity.metadata.name}</Link>
|
||||
}
|
||||
subvalue={row.entity.metadata.description}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Owner',
|
||||
field: 'entity.spec.owner',
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
field: 'entity.spec.type',
|
||||
},
|
||||
const defaultColumns: TableColumn<DocsTableRow>[] = [
|
||||
columnFactories.createNameColumn(),
|
||||
columnFactories.createOwnerColumn(),
|
||||
columnFactories.createTypeColumn(),
|
||||
];
|
||||
|
||||
const defaultActions: TableProps<DocsTableRow>['actions'] = [
|
||||
@@ -99,7 +93,7 @@ export const DocsTable = ({
|
||||
actionsColumnIndex: -1,
|
||||
}}
|
||||
data={documents}
|
||||
columns={columns}
|
||||
columns={columns || defaultColumns}
|
||||
actions={actions || defaultActions}
|
||||
title={
|
||||
title
|
||||
|
||||
@@ -17,35 +17,40 @@
|
||||
import React from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { capitalize } from 'lodash';
|
||||
import { CodeSnippet, WarningPanel } from '@backstage/core-components';
|
||||
import {
|
||||
favoriteEntityIcon,
|
||||
favoriteEntityTooltip,
|
||||
CodeSnippet,
|
||||
TableColumn,
|
||||
TableProps,
|
||||
WarningPanel,
|
||||
} from '@backstage/core-components';
|
||||
import {
|
||||
useEntityListProvider,
|
||||
useStarredEntities,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import * as actionFactories from './actions';
|
||||
import { DocsTable } from './DocsTable';
|
||||
import * as actionFactories from './actions';
|
||||
import * as columnFactories from './columns';
|
||||
import { DocsTableRow } from './types';
|
||||
|
||||
export const EntityListDocsTable = () => {
|
||||
export const EntityListDocsTable = ({
|
||||
columns,
|
||||
actions,
|
||||
}: {
|
||||
columns?: TableColumn<DocsTableRow>[];
|
||||
actions?: TableProps<DocsTableRow>['actions'];
|
||||
}) => {
|
||||
const { loading, error, entities, filters } = useEntityListProvider();
|
||||
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
const title = capitalize(filters.user?.value ?? 'all');
|
||||
|
||||
const actions = [
|
||||
const defaultActions = [
|
||||
actionFactories.createCopyDocsUrlAction(copyToClipboard),
|
||||
({ entity }: DocsTableRow) => {
|
||||
const isStarred = isStarredEntity(entity);
|
||||
return {
|
||||
cellStyle: { paddingLeft: '1em' },
|
||||
icon: () => favoriteEntityIcon(isStarred),
|
||||
tooltip: favoriteEntityTooltip(isStarred),
|
||||
onClick: () => toggleStarredEntity(entity),
|
||||
};
|
||||
},
|
||||
actionFactories.createStarEntityAction(
|
||||
isStarredEntity,
|
||||
toggleStarredEntity,
|
||||
),
|
||||
];
|
||||
|
||||
if (error) {
|
||||
@@ -64,7 +69,11 @@ export const EntityListDocsTable = () => {
|
||||
title={title}
|
||||
entities={entities}
|
||||
loading={loading}
|
||||
actions={actions}
|
||||
actions={actions || defaultActions}
|
||||
columns={columns}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
EntityListDocsTable.columns = columnFactories;
|
||||
EntityListDocsTable.actions = actionFactories;
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
SupportButton,
|
||||
TableColumn,
|
||||
TableProps,
|
||||
} from '@backstage/core-components';
|
||||
import {
|
||||
EntityListContainer,
|
||||
@@ -29,13 +31,23 @@ import {
|
||||
EntityListProvider,
|
||||
EntityOwnerPicker,
|
||||
EntityTagPicker,
|
||||
UserListFilterKind,
|
||||
UserListPicker,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { EntityListDocsTable } from './EntityListDocsTable';
|
||||
import { TechDocsHomeLayout } from './TechDocsHomeLayout';
|
||||
import { TechDocsPicker } from './TechDocsPicker';
|
||||
import { DocsTableRow } from './types';
|
||||
|
||||
export const TechDocsHome = () => {
|
||||
export const TechDocsHome = ({
|
||||
initialFilter = 'all',
|
||||
columns,
|
||||
actions,
|
||||
}: {
|
||||
initialFilter?: UserListFilterKind;
|
||||
columns?: TableColumn<DocsTableRow>[];
|
||||
actions?: TableProps<DocsTableRow>['actions'];
|
||||
}) => {
|
||||
return (
|
||||
<TechDocsHomeLayout>
|
||||
<Content>
|
||||
@@ -48,12 +60,12 @@ export const TechDocsHome = () => {
|
||||
<FilteredEntityLayout>
|
||||
<FilterContainer>
|
||||
<TechDocsPicker />
|
||||
<UserListPicker initialFilter="all" />
|
||||
<UserListPicker initialFilter={initialFilter} />
|
||||
<EntityOwnerPicker />
|
||||
<EntityTagPicker />
|
||||
</FilterContainer>
|
||||
<EntityListContainer>
|
||||
<EntityListDocsTable />
|
||||
<EntityListDocsTable actions={actions} columns={columns} />
|
||||
</EntityListContainer>
|
||||
</FilteredEntityLayout>
|
||||
</EntityListProvider>
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
import React from 'react';
|
||||
import ShareIcon from '@material-ui/icons/Share';
|
||||
import {
|
||||
favoriteEntityIcon,
|
||||
favoriteEntityTooltip,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { DocsTableRow } from './types';
|
||||
|
||||
export function createCopyDocsUrlAction(copyToClipboard: Function) {
|
||||
@@ -25,8 +29,26 @@ export function createCopyDocsUrlAction(copyToClipboard: Function) {
|
||||
tooltip: 'Click to copy documentation link to clipboard',
|
||||
onClick: () =>
|
||||
copyToClipboard(
|
||||
`${window.location.href.split('/?')[0]}/${row.resolved.docsUrl}`,
|
||||
`${window.location.origin}${window.location.pathname.replace(
|
||||
/\/?$/,
|
||||
'/',
|
||||
)}${row.resolved.docsUrl}`,
|
||||
),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function createStarEntityAction(
|
||||
isStarredEntity: Function,
|
||||
toggleStarredEntity: Function,
|
||||
) {
|
||||
return ({ entity }: DocsTableRow) => {
|
||||
const isStarred = isStarredEntity(entity);
|
||||
return {
|
||||
cellStyle: { paddingLeft: '1em' },
|
||||
icon: () => favoriteEntityIcon(isStarred),
|
||||
tooltip: favoriteEntityTooltip(isStarred),
|
||||
onClick: () => toggleStarredEntity(entity),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 from 'react';
|
||||
import { Link, SubvalueCell, TableColumn } from '@backstage/core-components';
|
||||
import { EntityRefLinks } from '@backstage/plugin-catalog-react';
|
||||
import { DocsTableRow } from './types';
|
||||
|
||||
export function createNameColumn(): TableColumn<DocsTableRow> {
|
||||
return {
|
||||
title: 'Document',
|
||||
field: 'entity.metadata.name',
|
||||
highlight: true,
|
||||
render: (row: DocsTableRow) => (
|
||||
<SubvalueCell
|
||||
value={
|
||||
<Link to={row.resolved.docsUrl}>{row.entity.metadata.name}</Link>
|
||||
}
|
||||
subvalue={row.entity.metadata.description}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function createOwnerColumn(): TableColumn<DocsTableRow> {
|
||||
return {
|
||||
title: 'Owner',
|
||||
field: 'resolved.ownedByRelationsTitle',
|
||||
render: ({ resolved }) => (
|
||||
<EntityRefLinks
|
||||
entityRefs={resolved.ownedByRelations}
|
||||
defaultKind="group"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function createTypeColumn(): TableColumn<DocsTableRow> {
|
||||
return {
|
||||
title: 'Type',
|
||||
field: 'entity.spec.type',
|
||||
};
|
||||
}
|
||||
@@ -18,3 +18,4 @@ export { EntityListDocsTable } from './EntityListDocsTable';
|
||||
export { TechDocsHomeLayout } from './TechDocsHomeLayout';
|
||||
export { TechDocsPicker } from './TechDocsPicker';
|
||||
export type { PanelType } from './TechDocsCustomHome';
|
||||
export type { DocsTableRow } from './types';
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Entity, EntityName } from '@backstage/catalog-model';
|
||||
|
||||
export type DocsTableRow = {
|
||||
entity: Entity;
|
||||
resolved: {
|
||||
docsUrl: string;
|
||||
ownedByRelationsTitle: string;
|
||||
ownedByRelations: EntityName[];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ export * from './api';
|
||||
export { techdocsApiRef, techdocsStorageApiRef } from './api';
|
||||
export type { TechDocsApi, TechDocsStorageApi } from './api';
|
||||
export { TechDocsClient, TechDocsStorageClient } from './client';
|
||||
export type { PanelType } from './home/components';
|
||||
export type { DocsTableRow, PanelType } from './home/components';
|
||||
export {
|
||||
EntityListDocsTable,
|
||||
TechDocsHomeLayout,
|
||||
@@ -30,6 +30,7 @@ export {
|
||||
DocsTable,
|
||||
EntityTechdocsContent,
|
||||
TechDocsCustomHome,
|
||||
TechDocsHome,
|
||||
TechdocsPage,
|
||||
techdocsPlugin as plugin,
|
||||
techdocsPlugin,
|
||||
|
||||
@@ -113,6 +113,14 @@ export const TechDocsCustomHome = techdocsPlugin.provide(
|
||||
}),
|
||||
);
|
||||
|
||||
export const TechDocsHome = techdocsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./home/components/TechDocsHome').then(m => m.TechDocsHome),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
export const TechDocsReaderPage = techdocsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
|
||||
Reference in New Issue
Block a user