feat: Add user menu on users table (#1222)

This commit is contained in:
Bruno Quaresma
2022-05-02 14:58:49 +00:00
committed by GitHub
parent a71f3934c5
commit 75343288ff
5 changed files with 116 additions and 7 deletions
+19 -5
View File
@@ -40,17 +40,21 @@ export interface TableProps<T> {
* Optional empty state UI when the data is empty
*/
emptyState?: React.ReactElement
/**
* Optional element to render row actions like delete, update, etc
*/
rowMenu?: (data: T) => React.ReactElement
}
export const Table = <T,>({ columns, data, emptyState, title }: TableProps<T>): React.ReactElement => {
export const Table = <T,>({ columns, data, emptyState, title, rowMenu }: TableProps<T>): React.ReactElement => {
const columnNames = columns.map(({ name }) => name)
const body = renderTableBody(data, columns, emptyState)
const body = renderTableBody(data, columns, emptyState, rowMenu)
return (
<MuiTable>
<TableHead>
{title && <TableTitle title={title} />}
<TableHeaders columns={columnNames} />
<TableHeaders columns={columnNames} hasMenu={!!rowMenu} />
</TableHead>
{body}
</MuiTable>
@@ -60,7 +64,12 @@ export const Table = <T,>({ columns, data, emptyState, title }: TableProps<T>):
/**
* Helper function to render the table data, falling back to an empty state if available
*/
const renderTableBody = <T,>(data: T[], columns: Column<T>[], emptyState?: React.ReactElement) => {
const renderTableBody = <T,>(
data: T[],
columns: Column<T>[],
emptyState?: React.ReactElement,
rowMenu?: (data: T) => React.ReactElement,
) => {
if (data.length > 0) {
const rows = data.map((item: T, index) => {
const cells = columns.map((column) => {
@@ -70,7 +79,12 @@ const renderTableBody = <T,>(data: T[], columns: Column<T>[], emptyState?: React
return <TableCell key={String(column.key)}>{String(item[column.key]).toString()}</TableCell>
}
})
return <TableRow key={index}>{cells}</TableRow>
return (
<TableRow key={index}>
{cells}
{rowMenu && <TableCell>{rowMenu(item)}</TableCell>}
</TableRow>
)
})
return <TableBody>{rows}</TableBody>
} else {
@@ -5,9 +5,10 @@ import React from "react"
export interface TableHeadersProps {
columns: string[]
hasMenu?: boolean
}
export const TableHeaders: React.FC<TableHeadersProps> = ({ columns }) => {
export const TableHeaders: React.FC<TableHeadersProps> = ({ columns, hasMenu }) => {
const styles = useStyles()
return (
<TableRow className={styles.root}>
@@ -16,6 +17,8 @@ export const TableHeaders: React.FC<TableHeadersProps> = ({ columns }) => {
{c}
</TableCell>
))}
{/* 1% is a trick to make the table cell width fit the content */}
{hasMenu && <TableCell width="1%" />}
</TableRow>
)
}
@@ -0,0 +1,24 @@
import { ComponentMeta, Story } from "@storybook/react"
import React from "react"
import { TableRowMenu, TableRowMenuProps } from "./TableRowMenu"
export default {
title: "components/TableRowMenu",
component: TableRowMenu,
} as ComponentMeta<typeof TableRowMenu>
type DataType = {
id: string
}
const Template: Story<TableRowMenuProps<DataType>> = (args) => <TableRowMenu {...args} />
export const Example = Template.bind({})
Example.args = {
data: { id: "123" },
menuItems: [
{ label: "Suspend", onClick: (data) => alert(data.id) },
{ label: "Update", onClick: (data) => alert(data.id) },
{ label: "Delete", onClick: (data) => alert(data.id) },
],
}
@@ -0,0 +1,46 @@
import IconButton from "@material-ui/core/IconButton"
import Menu, { MenuProps } from "@material-ui/core/Menu"
import MenuItem from "@material-ui/core/MenuItem"
import MoreVertIcon from "@material-ui/icons/MoreVert"
import React from "react"
export interface TableRowMenuProps<TData> {
data: TData
menuItems: Array<{
label: string
onClick: (data: TData) => void
}>
}
export const TableRowMenu = <T,>({ data, menuItems }: TableRowMenuProps<T>): JSX.Element => {
const [anchorEl, setAnchorEl] = React.useState<MenuProps["anchorEl"]>(null)
const handleClick = (event: React.MouseEvent) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
return (
<>
<IconButton size="small" aria-label="more" aria-controls="long-menu" aria-haspopup="true" onClick={handleClick}>
<MoreVertIcon />
</IconButton>
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{menuItems.map((item) => (
<MenuItem
key={item.label}
onClick={() => {
handleClose()
item.onClick(data)
}}
>
{item.label}
</MenuItem>
))}
</Menu>
</>
)
}
+23 -1
View File
@@ -2,6 +2,7 @@ import React from "react"
import { UserResponse } from "../../api/types"
import { EmptyState } from "../EmptyState/EmptyState"
import { Column, Table } from "../Table/Table"
import { TableRowMenu } from "../TableRowMenu/TableRowMenu"
import { UserCell } from "../UserCell/UserCell"
const Language = {
@@ -9,6 +10,7 @@ const Language = {
usersTitle: "All users",
emptyMessage: "No users found",
usernameLabel: "User",
suspendMenuItem: "Suspend",
}
const emptyState = <EmptyState message={Language.emptyMessage} />
@@ -28,5 +30,25 @@ export interface UsersTableProps {
}
export const UsersTable: React.FC<UsersTableProps> = ({ users }) => {
return <Table columns={columns} data={users} title={Language.usersTitle} emptyState={emptyState} />
return (
<Table
columns={columns}
data={users}
title={Language.usersTitle}
emptyState={emptyState}
rowMenu={(user) => (
<TableRowMenu
data={user}
menuItems={[
{
label: Language.suspendMenuItem,
onClick: () => {
// TO-DO: Add suspend action here
},
},
]}
/>
)}
/>
)
}