refactor(EntityListDocsTable): support custom columns and actions

Signed-off-by: Phil Kuang <pkuang@factset.com>
This commit is contained in:
Phil Kuang
2021-08-04 10:01:00 -04:00
parent a440d3b389
commit 10ef115554
10 changed files with 251 additions and 51 deletions
+98 -3
View File
@@ -17,6 +17,54 @@ import { IdentityApi } from '@backstage/core-plugin-api';
import { LocationSpec } from '@backstage/catalog-model';
import { default as React_2 } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { TableColumn } from '@backstage/core-components';
import { TableProps } from '@backstage/core-components';
import { UserListFilterKind } from '@backstage/plugin-catalog-react';
// Warning: (ae-missing-release-tag) "createCopyDocsUrlAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createCopyDocsUrlAction(
copyToClipboard: Function,
): (
row: DocsTableRow,
) => {
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
// Warning: (ae-missing-release-tag) "createNameColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createNameColumn(): TableColumn<DocsTableRow>;
// Warning: (ae-missing-release-tag) "createOwnerColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createOwnerColumn(): TableColumn<DocsTableRow>;
// Warning: (ae-missing-release-tag) "createStarEntityAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
): ({
entity,
}: DocsTableRow) => {
cellStyle: {
paddingLeft: string;
};
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
// Warning: (ae-missing-release-tag) "createTypeColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createTypeColumn(): TableColumn<DocsTableRow>;
// Warning: (ae-missing-release-tag) "DocsCardGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -45,11 +93,13 @@ export const DocsTable: ({
entities,
title,
loading,
columns,
actions,
}: {
entities: Entity[] | undefined;
title?: string | undefined;
loading?: boolean | undefined;
columns?: TableColumn<DocsTableRow>[] | undefined;
actions?:
| (
| Action<DocsTableRow>
@@ -62,6 +112,18 @@ export const DocsTable: ({
| undefined;
}) => JSX.Element | null;
// Warning: (ae-missing-release-tag) "DocsTableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type DocsTableRow = {
entity: Entity;
resolved: {
docsUrl: string;
ownedByRelationsTitle: string;
ownedByRelations: EntityName[];
};
};
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "EmbeddedDocsRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -71,7 +133,17 @@ export const EmbeddedDocsRouter: (_props: Props_2) => JSX.Element;
// Warning: (ae-missing-release-tag) "EntityListDocsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityListDocsTable: () => JSX.Element;
export const EntityListDocsTable: {
({
columns,
actions,
}: {
columns?: TableColumn<DocsTableRow>[] | undefined;
actions?: TableProps<DocsTableRow>['actions'];
}): JSX.Element;
columns: typeof columnFactories;
actions: typeof actionFactories;
};
// Warning: (ae-missing-release-tag) "EntityTechdocsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -163,6 +235,28 @@ export const TechDocsCustomHome: ({
tabsConfig: TabsConfig;
}) => JSX.Element;
// Warning: (ae-missing-release-tag) "TechDocsHome" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TechDocsHome: ({
initialFilter,
columns,
actions,
}: {
initialFilter?: UserListFilterKind | undefined;
columns?: TableColumn<DocsTableRow>[] | undefined;
actions?:
| (
| Action<DocsTableRow>
| {
action: (rowData: DocsTableRow) => Action<DocsTableRow>;
position: string;
}
| ((rowData: DocsTableRow) => Action<DocsTableRow>)
)[]
| undefined;
}) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "TechDocsHomeLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -286,8 +380,9 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
// Warnings were encountered during analysis:
//
// src/plugin.d.ts:17:5 - (ae-forgotten-export) The symbol "DocsTableRow" needs to be exported by the entry point index.d.ts
// src/plugin.d.ts:23:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts
// src/home/components/EntityListDocsTable.d.ts:11:5 - (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts
// src/home/components/EntityListDocsTable.d.ts:12:5 - (ae-forgotten-export) The symbol "actionFactories" needs to be exported by the entry point index.d.ts
// src/plugin.d.ts:24:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
```
@@ -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[];
};
};
+2 -1
View File
@@ -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,
+8
View File
@@ -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: () =>