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, VisuallyHidden,
} from '@strapi/parts'; } from '@strapi/parts';
import { SortIcon, useQueryParams } from '@strapi/helper-plugin'; import { SortIcon, useQueryParams } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
const TableHead = () => { const TableHead = ({ headers, withMainAction, withBulkActions }) => {
const [{ query }, setQuery] = useQueryParams(); const [{ query }, setQuery] = useQueryParams();
const sort = query.sort; const sort = query.sort;
const [sortBy, sortOrder] = sort.split(':'); 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 ( return (
<Thead> <Thead>
<Tr> <Tr>
<Th> {withMainAction && (
<BaseCheckbox aria-label="Select all entries" /> <Th>
</Th> <BaseCheckbox aria-label="Select all entries" />
{headers.map(({ label, value, isSortable }) => { </Th>
const isSorted = sortBy === value; )}
{headers.map(({ name, metadatas: { sortable: isSortable, label } }) => {
const isSorted = sortBy === name;
const isUp = sortOrder === 'ASC'; const isUp = sortOrder === 'ASC';
const handleClickSort = (shouldAllowClick = true) => { const handleClickSort = (shouldAllowClick = true) => {
if (isSortable && shouldAllowClick) { if (isSortable && shouldAllowClick) {
const nextSortOrder = isSorted && sortOrder === 'ASC' ? 'DESC' : 'ASC'; const nextSortOrder = isSorted && sortOrder === 'ASC' ? 'DESC' : 'ASC';
const nextSort = `${value}:${nextSortOrder}`; const nextSort = `${name}:${nextSortOrder}`;
setQuery({ setQuery({
sort: nextSort, sort: nextSort,
@ -46,7 +42,7 @@ const TableHead = () => {
return ( return (
<Th <Th
key={value} key={name}
action={ action={
isSorted ? ( isSorted ? (
<IconButton <IconButton
@ -73,12 +69,26 @@ const TableHead = () => {
); );
})} })}
<Th> {withBulkActions && (
<VisuallyHidden>Actions</VisuallyHidden> <Th>
</Th> <VisuallyHidden>Actions</VisuallyHidden>
</Th>
)}
</Tr> </Tr>
</Thead> </Thead>
); );
}; };
TableHead.defaultProps = {
headers: [],
withBulkActions: false,
withMainAction: false,
};
TableHead.propTypes = {
headers: PropTypes.array,
withBulkActions: PropTypes.bool,
withMainAction: PropTypes.bool,
};
export default TableHead; 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 PropTypes from 'prop-types';
import { import { Table as TableCompo } from '@strapi/parts';
BaseCheckbox, import { EmptyBodyTable, useQueryParams } from '@strapi/helper-plugin';
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 TableHead from './TableHead'; 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 [{ query }] = useQueryParams();
const ROW_COUNT = rows.length + 1; const ROW_COUNT = rows.length + 1;
const COL_COUNT = 7; const COL_COUNT = 7;
const hasFilters = query.filters !== undefined; const hasFilters = query.filters !== undefined;
const content = hasFilters const content = hasFilters
? { ? {
id: 'content-manager.components.TableEmpty.withFilters', id: 'content-manager.components.TableEmpty.withFilters',
@ -30,75 +22,42 @@ const Table = ({ canDelete, canUpdate, rows }) => {
return ( return (
<TableCompo colCount={COL_COUNT} rowCount={ROW_COUNT}> <TableCompo colCount={COL_COUNT} rowCount={ROW_COUNT}>
<TableHead /> <TableHead
headers={headers}
withMainAction={withMainAction}
withBulkActions={withBulkActions}
/>
{!rows.length ? ( {!rows.length ? (
<EmptyBodyTable colSpan={COL_COUNT} content={content} /> <EmptyBodyTable colSpan={COL_COUNT} content={content} />
) : ( ) : (
<Tbody> <TableRows
{rows.map(entry => ( canDelete={canDelete}
<Tr key={entry.id}> canUpdate={canUpdate}
<Td> data={rows}
<BaseCheckbox aria-label={`Select ${entry.email}`} /> headers={headers}
</Td> rows={rows}
<Td> withBulkActions={withBulkActions}
<Text textColor="neutral800"> withMainAction={withMainAction}
{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>
)} )}
</TableCompo> </TableCompo>
); );
}; };
Table.defaultProps = { Table.defaultProps = {
headers: [],
rows: [], rows: [],
withBulkActions: false,
withMainAction: false,
}; };
Table.propTypes = { Table.propTypes = {
canDelete: PropTypes.bool.isRequired, canDelete: PropTypes.bool.isRequired,
canUpdate: PropTypes.bool.isRequired, canUpdate: PropTypes.bool.isRequired,
headers: PropTypes.array,
rows: PropTypes.array, rows: PropTypes.array,
withBulkActions: PropTypes.bool,
withMainAction: PropTypes.bool,
}; };
export default Table; export default Table;

View File

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