Refacto table to make it dynamic for the CM migration

Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
soupette 2021-08-31 22:03:52 +02:00
parent 8b278955bc
commit c3d639e912
5 changed files with 189 additions and 87 deletions

View File

@ -10,33 +10,29 @@ import {
VisuallyHidden,
} from '@strapi/parts';
import { SortIcon, useQueryParams } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
const TableHead = () => {
const TableHead = ({ headers, withMainAction, withBulkActions }) => {
const [{ query }, setQuery] = useQueryParams();
const sort = query.sort;
const [sortBy, sortOrder] = sort.split(':');
const headers = [
{ label: 'Name', value: 'firstname', isSortable: true },
{ label: 'Email', value: 'email', isSortable: true },
{ label: 'Roles', value: 'roles', isSortable: false },
{ label: 'Username', value: 'username', isSortable: true },
{ label: 'Active User', value: 'isActive', isSortable: false },
];
return (
<Thead>
<Tr>
<Th>
<BaseCheckbox aria-label="Select all entries" />
</Th>
{headers.map(({ label, value, isSortable }) => {
const isSorted = sortBy === value;
{withMainAction && (
<Th>
<BaseCheckbox aria-label="Select all entries" />
</Th>
)}
{headers.map(({ name, metadatas: { sortable: isSortable, label } }) => {
const isSorted = sortBy === name;
const isUp = sortOrder === 'ASC';
const handleClickSort = (shouldAllowClick = true) => {
if (isSortable && shouldAllowClick) {
const nextSortOrder = isSorted && sortOrder === 'ASC' ? 'DESC' : 'ASC';
const nextSort = `${value}:${nextSortOrder}`;
const nextSort = `${name}:${nextSortOrder}`;
setQuery({
sort: nextSort,
@ -46,7 +42,7 @@ const TableHead = () => {
return (
<Th
key={value}
key={name}
action={
isSorted ? (
<IconButton
@ -73,12 +69,26 @@ const TableHead = () => {
);
})}
<Th>
<VisuallyHidden>Actions</VisuallyHidden>
</Th>
{withBulkActions && (
<Th>
<VisuallyHidden>Actions</VisuallyHidden>
</Th>
)}
</Tr>
</Thead>
);
};
TableHead.defaultProps = {
headers: [],
withBulkActions: false,
withMainAction: false,
};
TableHead.propTypes = {
headers: PropTypes.array,
withBulkActions: PropTypes.bool,
withMainAction: PropTypes.bool,
};
export default TableHead;

View File

@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import { BaseCheckbox, Box, IconButton, Tbody, Td, Text, Tr, Row } from '@strapi/parts';
import { EditIcon, DeleteIcon } from '@strapi/icons';
import { useHistory } from 'react-router-dom';
const TableRows = ({ canUpdate, canDelete, headers, withMainAction, withBulkActions, rows }) => {
const {
push,
location: { pathname },
} = useHistory();
return (
<Tbody>
{rows.map(data => {
return (
<Tr key={data.id}>
{withMainAction && (
<Td>
<BaseCheckbox aria-label="Select all entries" />
</Td>
)}
{headers.map(({ key, cellFormatter, name, ...rest }) => {
return (
<Td key={key}>
{typeof cellFormatter === 'function' ? (
cellFormatter(data, { key, name, ...rest })
) : (
<Text textColor="neutral800">{data[name] || '-'}</Text>
)}
</Td>
);
})}
{withBulkActions && (
<Td>
<Row>
{canUpdate && (
<IconButton
onClick={() => push(`${pathname}/${data.id}`)}
label="Edit"
noBorder
icon={<EditIcon />}
/>
)}
{canDelete && (
<Box paddingLeft={1}>
<IconButton
onClick={() => console.log('delete')}
label="Delete"
noBorder
icon={<DeleteIcon />}
/>
</Box>
)}
</Row>
</Td>
)}
</Tr>
);
})}
</Tbody>
);
};
TableRows.defaultProps = {
canDelete: false,
canUpdate: false,
rows: [],
withBulkActions: false,
withMainAction: false,
};
TableRows.propTypes = {
canDelete: PropTypes.bool,
canUpdate: PropTypes.bool,
headers: PropTypes.array.isRequired,
rows: PropTypes.array,
withBulkActions: PropTypes.bool,
withMainAction: PropTypes.bool,
};
export default TableRows;

View File

@ -1,25 +1,17 @@
import React from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import {
BaseCheckbox,
Box,
IconButton,
Table as TableCompo,
Tbody,
Td,
Text,
Tr,
Row,
} from '@strapi/parts';
import { EditIcon, DeleteIcon } from '@strapi/icons';
import { EmptyBodyTable, Status, useQueryParams } from '@strapi/helper-plugin';
import { Table as TableCompo } from '@strapi/parts';
import { EmptyBodyTable, useQueryParams } from '@strapi/helper-plugin';
import TableHead from './TableHead';
import TableRows from './TableRows';
const Table = ({ canDelete, canUpdate, rows }) => {
const Table = ({ canDelete, canUpdate, headers, rows, withBulkActions, withMainAction }) => {
const [entriesToDelete, setEntriesToDelete] = useState([]);
const [{ query }] = useQueryParams();
const ROW_COUNT = rows.length + 1;
const COL_COUNT = 7;
const hasFilters = query.filters !== undefined;
const content = hasFilters
? {
id: 'content-manager.components.TableEmpty.withFilters',
@ -30,75 +22,42 @@ const Table = ({ canDelete, canUpdate, rows }) => {
return (
<TableCompo colCount={COL_COUNT} rowCount={ROW_COUNT}>
<TableHead />
<TableHead
headers={headers}
withMainAction={withMainAction}
withBulkActions={withBulkActions}
/>
{!rows.length ? (
<EmptyBodyTable colSpan={COL_COUNT} content={content} />
) : (
<Tbody>
{rows.map(entry => (
<Tr key={entry.id}>
<Td>
<BaseCheckbox aria-label={`Select ${entry.email}`} />
</Td>
<Td>
<Text textColor="neutral800">
{entry.firstname} {entry.lastname}
</Text>
</Td>
<Td>{entry.email}</Td>
<Td>
<Text textColor="neutral800">{entry.roles.map(role => role.name).join(',\n')}</Text>
</Td>
<Td>
<Text textColor="neutral800">{entry.username || '-'}</Text>
</Td>
<Td>
<Row>
<Status
isActive={entry.isActive}
variant={entry.isActive ? 'success' : 'danger'}
/>
<Text textColor="neutral800">{entry.isActive ? 'Active' : 'Inactive'}</Text>
</Row>
</Td>
<Td>
<Row>
{canUpdate && (
<IconButton
onClick={() => console.log('edit')}
label="Edit"
noBorder
icon={<EditIcon />}
/>
)}
{canDelete && (
<Box paddingLeft={1}>
<IconButton
onClick={() => console.log('delete')}
label="Delete"
noBorder
icon={<DeleteIcon />}
/>
</Box>
)}
</Row>
</Td>
</Tr>
))}
</Tbody>
<TableRows
canDelete={canDelete}
canUpdate={canUpdate}
data={rows}
headers={headers}
rows={rows}
withBulkActions={withBulkActions}
withMainAction={withMainAction}
/>
)}
</TableCompo>
);
};
Table.defaultProps = {
headers: [],
rows: [],
withBulkActions: false,
withMainAction: false,
};
Table.propTypes = {
canDelete: PropTypes.bool.isRequired,
canUpdate: PropTypes.bool.isRequired,
headers: PropTypes.array,
rows: PropTypes.array,
withBulkActions: PropTypes.bool,
withMainAction: PropTypes.bool,
};
export default Table;

View File

@ -17,9 +17,9 @@ import { useIntl } from 'react-intl';
import { useQuery } from 'react-query';
import get from 'lodash/get';
import adminPermissions from '../../../permissions';
import Table from './Table';
import fetchData from './utils/api';
import tableHeaders from './utils/tableHeaders';
const ListPage = () => {
const {
@ -269,7 +269,10 @@ const ListPage = () => {
canCreate={canCreate}
canDelete={canDelete}
canUpdate={canUpdate}
headers={tableHeaders}
rows={data?.results}
withBulkActions
withMainAction={canDelete}
/>
)}
</CustomContentLayout>

View File

@ -0,0 +1,47 @@
import React from 'react';
import { Text, Row } from '@strapi/parts';
import { Status } from '@strapi/helper-plugin';
const tableHeaders = [
{
name: 'firstname',
key: 'firstname',
metadatas: { label: 'Name', sortable: true },
},
{
key: 'email',
name: 'email',
metadatas: { label: 'Email', sortable: true },
},
{
key: 'roles',
name: 'roles',
metadatas: { label: 'Roles', sortable: false },
/* eslint-disable react/prop-types */
cellFormatter: ({ roles }) => {
return <Text textColor="neutral800">{roles.map(role => role.name).join(',\n')}</Text>;
},
/* eslint-enable react/prop-types */
},
{
key: 'username',
name: 'username',
metadatas: { label: 'Username', sortable: true },
},
{
key: 'isActive',
name: 'isActive',
metadatas: { label: 'Active User', sortable: false },
// eslint-disable-next-line react/prop-types
cellFormatter: ({ isActive }) => {
return (
<Row>
<Status isActive={isActive} variant={isActive ? 'success' : 'danger'} />
<Text textColor="neutral800">{isActive ? 'Active' : 'Inactive'}</Text>
</Row>
);
},
},
];
export default tableHeaders;