mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Changed NewsletterList to use new Table
component (#17458)
refs https://github.com/TryGhost/Product/issues/3601 - `NewsletterList` now uses the new `Table` component, along with its subcomponents `TableRow` and `TableCell` to be able to show more relevant data
This commit is contained in:
parent
0aa5815b26
commit
5d5d33b930
5 changed files with 261 additions and 14 deletions
|
@ -0,0 +1,38 @@
|
||||||
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
|
import Table from './Table';
|
||||||
|
import TableCell from './TableCell';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Global / Table',
|
||||||
|
component: Table,
|
||||||
|
tags: ['autodocs']
|
||||||
|
} satisfies Meta<typeof Table>;
|
||||||
|
|
||||||
|
const tableRows = (
|
||||||
|
<>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Jamie Larson</TableCell>
|
||||||
|
<TableCell>jamie@example.com</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Jamie Larson</TableCell>
|
||||||
|
<TableCell>jamie@example.com</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Jamie Larson</TableCell>
|
||||||
|
<TableCell>jamie@example.com</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof Table>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
children: tableRows
|
||||||
|
},
|
||||||
|
decorators: [(_story: any) => (<div style={{maxWidth: '600px'}}>{_story()}</div>)]
|
||||||
|
};
|
35
apps/admin-x-settings/src/admin-x-ds/global/Table.tsx
Normal file
35
apps/admin-x-settings/src/admin-x-ds/global/Table.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import Heading from './Heading';
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
interface TableProps {
|
||||||
|
/**
|
||||||
|
* If the table is the primary content on a page (e.g. Members table) then you can set a pagetitle to be consistent
|
||||||
|
*/
|
||||||
|
pageTitle?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
borderTop?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Table: React.FC<TableProps> = ({children, borderTop, pageTitle, className}) => {
|
||||||
|
const tableClasses = clsx(
|
||||||
|
(borderTop || pageTitle) && 'border-t border-grey-300',
|
||||||
|
'w-full',
|
||||||
|
pageTitle ? 'mb-0 mt-14' : 'my-0',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{pageTitle && <Heading>{pageTitle}</Heading>}
|
||||||
|
<table className={tableClasses}>
|
||||||
|
<tbody>
|
||||||
|
{children}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Table;
|
22
apps/admin-x-settings/src/admin-x-ds/global/TableCell.tsx
Normal file
22
apps/admin-x-settings/src/admin-x-ds/global/TableCell.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
interface TableCellProps {
|
||||||
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableCell: React.FC<TableCellProps> = ({className, children}) => {
|
||||||
|
const tableCellClasses = clsx(
|
||||||
|
'!py-3 !pl-0 !pr-6 align-top',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td className={tableCellClasses}>
|
||||||
|
{children}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableCell;
|
47
apps/admin-x-settings/src/admin-x-ds/global/TableRow.tsx
Normal file
47
apps/admin-x-settings/src/admin-x-ds/global/TableRow.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
interface TableRowProps {
|
||||||
|
id?: string;
|
||||||
|
action?: React.ReactNode;
|
||||||
|
hideActions?: boolean;
|
||||||
|
className?: string;
|
||||||
|
testId?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden for the last item in the table
|
||||||
|
*/
|
||||||
|
separator?: boolean;
|
||||||
|
|
||||||
|
bgOnHover?: boolean;
|
||||||
|
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableRow: React.FC<TableRowProps> = ({id, action, hideActions, className, testId, separator, bgOnHover = true, onClick, children}) => {
|
||||||
|
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
onClick?.(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
separator = (separator === undefined) ? true : separator;
|
||||||
|
const tableRowClasses = clsx(
|
||||||
|
'group',
|
||||||
|
bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50',
|
||||||
|
onClick && 'cursor-pointer',
|
||||||
|
separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr className={tableRowClasses} data-testid={testId} id={id} onClick={handleClick}>
|
||||||
|
{children}
|
||||||
|
{action &&
|
||||||
|
<td className={`px-6 py-3 text-center ${hideActions ? 'invisible group-hover:visible' : ''}`}>
|
||||||
|
{action}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRow;
|
|
@ -1,14 +1,64 @@
|
||||||
import Button from '../../../../admin-x-ds/global/Button';
|
import Button from '../../../../admin-x-ds/global/Button';
|
||||||
import List from '../../../../admin-x-ds/global/List';
|
// import List from '../../../../admin-x-ds/global/List';
|
||||||
import ListItem from '../../../../admin-x-ds/global/ListItem';
|
// import ListItem from '../../../../admin-x-ds/global/ListItem';
|
||||||
import NewsletterDetailModal from './NewsletterDetailModal';
|
import NewsletterDetailModal from './NewsletterDetailModal';
|
||||||
import NiceModal from '@ebay/nice-modal-react';
|
import NiceModal from '@ebay/nice-modal-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Table from '../../../../admin-x-ds/global/Table';
|
||||||
|
import TableCell from '../../../../admin-x-ds/global/TableCell';
|
||||||
|
import TableRow from '../../../../admin-x-ds/global/TableRow';
|
||||||
|
|
||||||
interface NewslettersListProps {
|
interface NewslettersListProps {
|
||||||
tab?: string;
|
tab?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should create a NewsletterItem component based on TableRow and then loop through newsletters
|
||||||
|
//
|
||||||
|
// interface NewsletterItemProps {
|
||||||
|
// name: string;
|
||||||
|
// description: string;
|
||||||
|
// subscribers: number;
|
||||||
|
// emailsSent: number;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const NewsletterItem: React.FC<NewsletterItemProps> = ({name, description, subscribers, emailsSent}) => {
|
||||||
|
// const action = tab === 'active-newsletters' ? (
|
||||||
|
// <Button color='green' label='Archive' link />
|
||||||
|
// ) : (
|
||||||
|
// <Button color='green' label='Activate' link />
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <TableRow
|
||||||
|
// action={action}
|
||||||
|
// onClick={() => {
|
||||||
|
// NiceModal.show(NewsletterDetailModal);
|
||||||
|
// }}>
|
||||||
|
// hideActions
|
||||||
|
// separator
|
||||||
|
// >
|
||||||
|
// <TableCell>
|
||||||
|
// <div className={`flex grow flex-col`}>
|
||||||
|
// <span className='font-medium'>{name}</span>
|
||||||
|
// <span className='whitespace-nowrap text-xs text-grey-700'>{description}</span>
|
||||||
|
// </div>
|
||||||
|
// </TableCell>
|
||||||
|
// <TableCell>
|
||||||
|
// <div className={`flex grow flex-col`}>
|
||||||
|
// <span>{subscribers}</span>
|
||||||
|
// <span className='whitespace-nowrap text-xs text-grey-700'>Subscribers</span>
|
||||||
|
// </div>
|
||||||
|
// </TableCell>
|
||||||
|
// <TableCell>
|
||||||
|
// <div className={`flex grow flex-col`}>
|
||||||
|
// <span>{emailsSent}</span>
|
||||||
|
// <span className='whitespace-nowrap text-xs text-grey-700'>Emails sent</span>
|
||||||
|
// </div>
|
||||||
|
// </TableCell>
|
||||||
|
// </TableRow>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
const NewslettersList: React.FC<NewslettersListProps> = ({
|
const NewslettersList: React.FC<NewslettersListProps> = ({
|
||||||
tab
|
tab
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -19,26 +69,81 @@ const NewslettersList: React.FC<NewslettersListProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<Table>
|
||||||
<ListItem
|
<TableRow
|
||||||
action={action}
|
action={action}
|
||||||
detail='This one is pretty good'
|
|
||||||
title='Amazing newsletter'
|
|
||||||
hideActions
|
hideActions
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
NiceModal.show(NewsletterDetailModal);
|
NiceModal.show(NewsletterDetailModal);
|
||||||
}}
|
}}>
|
||||||
/>
|
<TableCell>
|
||||||
<ListItem
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span className='font-medium'>Amazing newsletter</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>This one is pretty good</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span>259</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>Subscribers</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span>14</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>Emails sent</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
action={action}
|
action={action}
|
||||||
detail='This one is just spam'
|
|
||||||
title='Awful newsletter'
|
|
||||||
hideActions
|
hideActions
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
NiceModal.show(NewsletterDetailModal);
|
NiceModal.show(NewsletterDetailModal);
|
||||||
}}
|
}}>
|
||||||
/>
|
<TableCell>
|
||||||
</List>
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span className='line-clamp-1 font-medium'>Crappy newsletter</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>This one is just spam</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span>145</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>Subscribers</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className={`flex grow flex-col`}>
|
||||||
|
<span>754</span>
|
||||||
|
<span className='whitespace-nowrap text-xs text-grey-700'>Emails sent</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
// Newsletter list previously used the List component, can be removed
|
||||||
|
//
|
||||||
|
// <List>
|
||||||
|
// <ListItem
|
||||||
|
// action={action}
|
||||||
|
// detail='This one is pretty good'
|
||||||
|
// title='Amazing newsletter'
|
||||||
|
// hideActions
|
||||||
|
// onClick={() => {
|
||||||
|
// NiceModal.show(NewsletterDetailModal);
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// <ListItem
|
||||||
|
// action={action}
|
||||||
|
// detail='This one is just spam'
|
||||||
|
// title='Awful newsletter'
|
||||||
|
// hideActions
|
||||||
|
// onClick={() => {
|
||||||
|
// NiceModal.show(NewsletterDetailModal);
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// </List>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue