Move local concerns into CatalogTable
This commit is contained in:
@@ -14,18 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { FC, forwardRef } from 'react';
|
||||
import MTable, {
|
||||
MTableCell,
|
||||
MTableHeader,
|
||||
MTableToolbar,
|
||||
MaterialTableProps,
|
||||
Options,
|
||||
Column,
|
||||
} from 'material-table';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles, useTheme, Typography } from '@material-ui/core';
|
||||
|
||||
import { makeStyles, Typography, useTheme } from '@material-ui/core';
|
||||
// Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51
|
||||
import AddBox from '@material-ui/icons/AddBox';
|
||||
import ArrowUpward from '@material-ui/icons/ArrowUpward';
|
||||
@@ -42,6 +32,15 @@ import Remove from '@material-ui/icons/Remove';
|
||||
import SaveAlt from '@material-ui/icons/SaveAlt';
|
||||
import Search from '@material-ui/icons/Search';
|
||||
import ViewColumn from '@material-ui/icons/ViewColumn';
|
||||
import MTable, {
|
||||
Column,
|
||||
MaterialTableProps,
|
||||
MTableCell,
|
||||
MTableHeader,
|
||||
MTableToolbar,
|
||||
Options,
|
||||
} from 'material-table';
|
||||
import React, { forwardRef } from 'react';
|
||||
|
||||
const tableIcons = {
|
||||
Add: forwardRef((props, ref: React.Ref<SVGSVGElement>) => (
|
||||
@@ -131,10 +130,10 @@ const useToolbarStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const convertColumns = (
|
||||
columns: TableColumn[],
|
||||
function convertColumns<T extends object>(
|
||||
columns: TableColumn<T>[],
|
||||
theme: BackstageTheme,
|
||||
): TableColumn[] => {
|
||||
): TableColumn<T>[] {
|
||||
return columns.map(column => {
|
||||
const headerStyle: React.CSSProperties = {};
|
||||
const cellStyle: React.CSSProperties = {};
|
||||
@@ -150,25 +149,26 @@ const convertColumns = (
|
||||
cellStyle,
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export interface TableColumn extends Column<{}> {
|
||||
export interface TableColumn<T extends object = {}> extends Column<T> {
|
||||
highlight?: boolean;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export interface TableProps extends MaterialTableProps<{}> {
|
||||
columns: TableColumn[];
|
||||
export interface TableProps<T extends object = {}>
|
||||
extends MaterialTableProps<T> {
|
||||
columns: TableColumn<T>[];
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
export const Table: FC<TableProps> = ({
|
||||
export function Table<T extends object = {}>({
|
||||
columns,
|
||||
options,
|
||||
title,
|
||||
subtitle,
|
||||
...props
|
||||
}) => {
|
||||
}: TableProps<T>) {
|
||||
const cellClasses = useCellStyles();
|
||||
const headerClasses = useHeaderStyles();
|
||||
const toolbarClasses = useToolbarStyles();
|
||||
@@ -183,7 +183,7 @@ export const Table: FC<TableProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<MTable
|
||||
<MTable<T>
|
||||
components={{
|
||||
Cell: cellProps => (
|
||||
<MTableCell className={cellClasses.root} {...cellProps} />
|
||||
@@ -211,4 +211,4 @@ export const Table: FC<TableProps> = ({
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
*/
|
||||
|
||||
export { Table } from './Table';
|
||||
export type { TableColumn } from './Table';
|
||||
export type { TableColumn, TableProps } from './Table';
|
||||
export { SubvalueCell } from './SubvalueCell';
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
import { FilterGroup, useEntityFilterGroup } from '../../filter';
|
||||
import { useStarredEntities } from '../../hooks/useStarredEntites';
|
||||
|
||||
type CatalogFilterItem = {
|
||||
export type CatalogFilterItem = {
|
||||
id: EntityGroup;
|
||||
label: string;
|
||||
icon?: IconComponent;
|
||||
@@ -88,6 +88,9 @@ type Props = {
|
||||
initiallySelected?: EntityGroup;
|
||||
};
|
||||
|
||||
/**
|
||||
* The main filter group in the sidebar, toggling owned/starred/all.
|
||||
*/
|
||||
export const CatalogFilter = ({
|
||||
filterGroups,
|
||||
onChange,
|
||||
|
||||
@@ -14,20 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity, LocationSpec } from '@backstage/catalog-model';
|
||||
import { Content, ContentHeader, SupportButton } from '@backstage/core';
|
||||
import { rootRoute as scaffolderRootRoute } from '@backstage/plugin-scaffolder';
|
||||
import { Button, makeStyles, withStyles } from '@material-ui/core';
|
||||
import Edit from '@material-ui/icons/Edit';
|
||||
import GitHub from '@material-ui/icons/GitHub';
|
||||
import Star from '@material-ui/icons/Star';
|
||||
import StarOutline from '@material-ui/icons/StarBorder';
|
||||
import { Button, makeStyles } from '@material-ui/core';
|
||||
import React, { useState } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { EntityGroup, filterGroups } from '../../data/filters';
|
||||
import { findLocationForEntityMeta } from '../../data/utils';
|
||||
import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter';
|
||||
import { useStarredEntities } from '../../hooks/useStarredEntites';
|
||||
import { CatalogFilter } from '../CatalogFilter/CatalogFilter';
|
||||
import { CatalogTable } from '../CatalogTable/CatalogTable';
|
||||
import CatalogLayout from './CatalogLayout';
|
||||
@@ -45,61 +38,10 @@ const useStyles = makeStyles(theme => ({
|
||||
|
||||
const CatalogPageContents = () => {
|
||||
const styles = useStyles();
|
||||
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
|
||||
const { loading, error, matchingEntities } = useFilteredEntities();
|
||||
const [selectedTab, setSelectedTab] = useState<string>();
|
||||
const [selectedSidebarItem, setSelectedSidebarItem] = useState<string>();
|
||||
|
||||
const YellowStar = withStyles({
|
||||
root: {
|
||||
color: '#f3ba37',
|
||||
},
|
||||
})(Star);
|
||||
|
||||
const actions = [
|
||||
(rowData: Entity) => {
|
||||
const location = findLocationForEntityMeta(rowData.metadata);
|
||||
return {
|
||||
icon: GitHub,
|
||||
tooltip: 'View on GitHub',
|
||||
onClick: () => {
|
||||
if (!location) return;
|
||||
window.open(location.target, '_blank');
|
||||
},
|
||||
hidden: location?.type !== 'github',
|
||||
};
|
||||
},
|
||||
(rowData: Entity) => {
|
||||
const createEditLink = (location: LocationSpec): string => {
|
||||
switch (location.type) {
|
||||
case 'github':
|
||||
return location.target.replace('/blob/', '/edit/');
|
||||
default:
|
||||
return location.target;
|
||||
}
|
||||
};
|
||||
const location = findLocationForEntityMeta(rowData.metadata);
|
||||
return {
|
||||
icon: Edit,
|
||||
tooltip: 'Edit',
|
||||
iconProps: { size: 'small' },
|
||||
onClick: () => {
|
||||
if (!location) return;
|
||||
window.open(createEditLink(location), '_blank');
|
||||
},
|
||||
hidden: location?.type !== 'github',
|
||||
};
|
||||
},
|
||||
(rowData: Entity) => {
|
||||
const isStarred = isStarredEntity(rowData);
|
||||
return {
|
||||
icon: isStarred ? YellowStar : StarOutline,
|
||||
tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites',
|
||||
onClick: () => toggleStarredEntity(rowData),
|
||||
};
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<CatalogLayout>
|
||||
<CatalogTabs onChange={({ label }) => setSelectedTab(label)} />
|
||||
@@ -129,7 +71,6 @@ const CatalogPageContents = () => {
|
||||
entities={matchingEntities}
|
||||
loading={loading}
|
||||
error={error}
|
||||
actions={actions}
|
||||
/>
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { CatalogTable } from './CatalogTable';
|
||||
|
||||
@@ -66,11 +66,11 @@ describe('CatalogTable component', () => {
|
||||
/>,
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await rendered.findByText(`Owned (${entites.length})`),
|
||||
).toBeInTheDocument();
|
||||
expect(await rendered.findByText('component1')).toBeInTheDocument();
|
||||
expect(await rendered.findByText('component2')).toBeInTheDocument();
|
||||
expect(await rendered.findByText('component3')).toBeInTheDocument();
|
||||
waitFor(() => {
|
||||
expect(rendered.queryByText(/Owned \(3\)/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/component1/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/component2/)).toBeInTheDocument();
|
||||
expect(rendered.queryByText(/component3/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,16 +13,21 @@
|
||||
* 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 { Entity, LocationSpec } from '@backstage/catalog-model';
|
||||
import { Table, TableColumn, TableProps } from '@backstage/core';
|
||||
import { Link } from '@material-ui/core';
|
||||
import Edit from '@material-ui/icons/Edit';
|
||||
import GitHub from '@material-ui/icons/GitHub';
|
||||
import Star from '@material-ui/icons/Star';
|
||||
import StarOutline from '@material-ui/icons/StarBorder';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React, { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { generatePath, Link as RouterLink } from 'react-router-dom';
|
||||
import { findLocationForEntityMeta } from '../../data/utils';
|
||||
import { useStarredEntities } from '../../hooks/useStarredEntites';
|
||||
import { entityRoute } from '../../routes';
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
const columns: TableColumn<Entity>[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
field: 'metadata.name',
|
||||
@@ -63,16 +68,16 @@ type CatalogTableProps = {
|
||||
titlePreamble: string;
|
||||
loading: boolean;
|
||||
error?: any;
|
||||
actions?: any;
|
||||
};
|
||||
|
||||
export const CatalogTable: FC<CatalogTableProps> = ({
|
||||
export const CatalogTable = ({
|
||||
entities,
|
||||
loading,
|
||||
error,
|
||||
titlePreamble,
|
||||
actions,
|
||||
}) => {
|
||||
}: CatalogTableProps) => {
|
||||
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
@@ -83,8 +88,52 @@ export const CatalogTable: FC<CatalogTableProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const actions: TableProps<Entity>['actions'] = [
|
||||
(rowData: Entity) => {
|
||||
const location = findLocationForEntityMeta(rowData.metadata);
|
||||
return {
|
||||
icon: GitHub,
|
||||
tooltip: 'View on GitHub',
|
||||
onClick: () => {
|
||||
if (!location) return;
|
||||
window.open(location.target, '_blank');
|
||||
},
|
||||
hidden: location?.type !== 'github',
|
||||
};
|
||||
},
|
||||
(rowData: Entity) => {
|
||||
const createEditLink = (location: LocationSpec): string => {
|
||||
switch (location.type) {
|
||||
case 'github':
|
||||
return location.target.replace('/blob/', '/edit/');
|
||||
default:
|
||||
return location.target;
|
||||
}
|
||||
};
|
||||
const location = findLocationForEntityMeta(rowData.metadata);
|
||||
return {
|
||||
icon: Edit,
|
||||
tooltip: 'Edit',
|
||||
// iconProps: { size: 'small' },
|
||||
onClick: () => {
|
||||
if (!location) return;
|
||||
window.open(createEditLink(location), '_blank');
|
||||
},
|
||||
hidden: location?.type !== 'github',
|
||||
};
|
||||
},
|
||||
(rowData: Entity) => {
|
||||
const isStarred = isStarredEntity(rowData);
|
||||
return {
|
||||
icon: isStarred ? () => <Star htmlColor="#f3ba37" /> : StarOutline,
|
||||
tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites',
|
||||
onClick: () => toggleStarredEntity(rowData),
|
||||
};
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
<Table<Entity>
|
||||
isLoading={loading}
|
||||
columns={columns}
|
||||
options={{
|
||||
|
||||
Reference in New Issue
Block a user