Add support for custom empty state of Table components
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Add support for custom empty state of `Table` components.
|
||||
|
||||
You can now optionally pass `emptyComponent` to `Table` that is displayed
|
||||
if the table has now rows.
|
||||
@@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { Table, SubvalueCell, TableColumn } from './';
|
||||
import { Link } from '../Link';
|
||||
import { SubvalueCell, Table, TableColumn } from './';
|
||||
import { TableFilter } from './Table';
|
||||
|
||||
export default {
|
||||
@@ -23,7 +25,16 @@ export default {
|
||||
component: Table,
|
||||
};
|
||||
|
||||
const containerStyle = { width: 850 };
|
||||
const useStyles = makeStyles(theme => ({
|
||||
container: {
|
||||
width: 850,
|
||||
},
|
||||
empty: {
|
||||
padding: theme.spacing(2),
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
const generateTestData: (number: number) => Array<{}> = (rows = 10) => {
|
||||
const data: Array<{}> = [];
|
||||
@@ -43,6 +54,7 @@ const generateTestData: (number: number) => Array<{}> = (rows = 10) => {
|
||||
const testData10 = generateTestData(10);
|
||||
|
||||
export const DefaultTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -66,7 +78,7 @@ export const DefaultTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false }}
|
||||
data={testData10}
|
||||
@@ -77,7 +89,8 @@ export const DefaultTable = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const SubtitleTable = () => {
|
||||
export const EmptyTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -101,7 +114,49 @@ export const SubtitleTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false }}
|
||||
data={[]}
|
||||
columns={columns}
|
||||
emptyComponent={
|
||||
<div className={classes.empty}>
|
||||
No data was added yet,
|
||||
<Link to="http://backstage.io/">learn how to add data</Link>.
|
||||
</div>
|
||||
}
|
||||
title="Backstage Table"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SubtitleTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
field: 'col1',
|
||||
highlight: true,
|
||||
},
|
||||
{
|
||||
title: 'Column 2',
|
||||
field: 'col2',
|
||||
},
|
||||
{
|
||||
title: 'Numeric value',
|
||||
field: 'number',
|
||||
type: 'numeric',
|
||||
},
|
||||
{
|
||||
title: 'A Date',
|
||||
field: 'date',
|
||||
type: 'date',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false }}
|
||||
data={testData10}
|
||||
@@ -114,6 +169,7 @@ export const SubtitleTable = () => {
|
||||
};
|
||||
|
||||
export const HiddenSearchTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -137,7 +193,7 @@ export const HiddenSearchTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false, search: false }}
|
||||
data={testData10}
|
||||
@@ -148,6 +204,7 @@ export const HiddenSearchTable = () => {
|
||||
};
|
||||
|
||||
export const SubvalueTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -181,13 +238,14 @@ export const SubvalueTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table options={{ paging: false }} data={testData10} columns={columns} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DenseTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -211,7 +269,7 @@ export const DenseTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false, padding: 'dense' }}
|
||||
data={testData10}
|
||||
@@ -223,6 +281,7 @@ export const DenseTable = () => {
|
||||
};
|
||||
|
||||
export const FilterTable = () => {
|
||||
const classes = useStyles();
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Column 1',
|
||||
@@ -261,7 +320,7 @@ export const FilterTable = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div className={classes.container}>
|
||||
<Table
|
||||
options={{ paging: false, padding: 'dense' }}
|
||||
data={testData10}
|
||||
|
||||
@@ -53,4 +53,16 @@ describe('<Table />', () => {
|
||||
);
|
||||
expect(rendered.getByText('subtitle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders custom empty component if empty', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Table
|
||||
subtitle="subtitle"
|
||||
emptyComponent={<div>EMPTY</div>}
|
||||
columns={minProps.columns}
|
||||
data={[]}
|
||||
/>,
|
||||
);
|
||||
expect(rendered.getByText('EMPTY')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,6 +42,7 @@ import MTable, {
|
||||
Column,
|
||||
Icons,
|
||||
MaterialTableProps,
|
||||
MTableBody,
|
||||
MTableHeader,
|
||||
MTableToolbar,
|
||||
Options,
|
||||
@@ -202,6 +203,7 @@ export interface TableProps<T extends object = {}>
|
||||
subtitle?: string;
|
||||
filters?: TableFilter[];
|
||||
initialState?: TableState;
|
||||
emptyComponent?: JSX.Element;
|
||||
onStateChange?: (state: TableState) => any;
|
||||
}
|
||||
|
||||
@@ -212,6 +214,7 @@ export function Table<T extends object = {}>({
|
||||
subtitle,
|
||||
filters,
|
||||
initialState,
|
||||
emptyComponent,
|
||||
onStateChange,
|
||||
...props
|
||||
}: TableProps<T>) {
|
||||
@@ -423,6 +426,23 @@ export function Table<T extends object = {}>({
|
||||
],
|
||||
);
|
||||
|
||||
const Body = useCallback(
|
||||
bodyProps => {
|
||||
if (emptyComponent && data.length === 0) {
|
||||
return (
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={columns.length}>{emptyComponent}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
|
||||
return <MTableBody {...bodyProps} />;
|
||||
},
|
||||
[data, emptyComponent, columns],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={tableClasses.root}>
|
||||
{filtersOpen && data && filters?.length && (
|
||||
@@ -438,6 +458,7 @@ export function Table<T extends object = {}>({
|
||||
<MTableHeader classes={headerClasses} {...headerProps} />
|
||||
),
|
||||
Toolbar,
|
||||
Body,
|
||||
}}
|
||||
options={{ ...defaultOptions, ...options }}
|
||||
columns={MTColumns}
|
||||
|
||||
Reference in New Issue
Block a user