diff --git a/packages/semantic-ui/src/components/DataTable.js b/packages/semantic-ui/src/components/DataTable.js index 61ba61f3..d8501319 100644 --- a/packages/semantic-ui/src/components/DataTable.js +++ b/packages/semantic-ui/src/components/DataTable.js @@ -633,6 +633,7 @@ DataTable.defaultProps = { buttons: [], count: 0, className: '', + csvExportButton: undefined, expandableRows: false, expandPanel: undefined, filters: undefined, diff --git a/packages/semantic-ui/src/components/List.js b/packages/semantic-ui/src/components/List.js index 71829f39..95b9daae 100644 --- a/packages/semantic-ui/src/components/List.js +++ b/packages/semantic-ui/src/components/List.js @@ -79,6 +79,16 @@ type Props = { */ className?: string, + /** + * If provided, a CSV export button will be rendered in the list header. + */ + csvExportButton?: { + basic: boolean, + color: string, + location: string, + onClick?: () => void + }, + /** * If provided, a "delete all" button will be rendered in the list header. */ @@ -208,6 +218,7 @@ type State = { }; const BUTTON_KEY_ADD = 'add'; +const BUTTON_KEY_CSV_EXPORT = 'csv-export'; const BUTTON_KEY_DELETE_ALL = 'delete-all'; /** @@ -229,6 +240,7 @@ const useList = (WrappedComponent: ComponentType) => ( }, buttons: [], className: '', + csvExportButton: undefined, filters: undefined, modal: undefined, page: 1, @@ -269,6 +281,7 @@ const useList = (WrappedComponent: ComponentType) => ( const { addButton = {}, + csvExportButton = {}, deleteButton = {}, modal, selectable @@ -288,6 +301,13 @@ const useList = (WrappedComponent: ComponentType) => ( }); } + // Add the CSV export button to the list if the csvExport prop is passed + if (csvExportButton.location === location && !selectable) { + buttons.push({ + render: this.renderCsvExportButton.bind(this) + }); + } + // Resolve the array of other buttons buttons.push(..._.filter(this.props.buttons, (button) => { let include = false; @@ -331,6 +351,36 @@ const useList = (WrappedComponent: ComponentType) => ( this.setState({ selectedItem: copy, modalEdit: true }); } + /** + * Generates and downloads a CSV file containing all + * the data in the table. + * + * @param items + */ + onCsvExportButton() { + let csv = `${this.props.columns.map((col) => `"${col.label}"`).join(',')}\n`; + + this.props.items.forEach((item) => { + csv = csv.concat(`${this.props.columns.map((col) => { + if (col.resolve) { + return `"${col.resolve(item)}"`; + } + return `"${item[col.name]}"`; + }).join(',')}\n`); + }); + + const element = document.createElement('a'); + element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(csv)}`); + element.setAttribute('download', `${this.props.collectionName || 'table'}.csv`); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); + } + /** * Deletes the currently selected item and clears the state. * @@ -533,6 +583,29 @@ const useList = (WrappedComponent: ComponentType) => ( ); } + /** + * Renders the CSV export button. + * + * @returns {null|*} + */ + renderCsvExportButton() { + if (!this.props.csvExportButton) { + return null; + } + + return ( + + ); + } + /** * Renders the delete all button. * diff --git a/packages/semantic-ui/src/components/ListTable.js b/packages/semantic-ui/src/components/ListTable.js index 6826ba9d..ec458b1a 100644 --- a/packages/semantic-ui/src/components/ListTable.js +++ b/packages/semantic-ui/src/components/ListTable.js @@ -14,6 +14,15 @@ type Props = DataListProps & DataTableProps & { */ configurable?: boolean, + /** + * If provided, a CSV export button will be rendered in the list header. + */ + csvExportButton?: { + color: string, + location: string, + onClick?: () => void + }, + /** * The name of the default sort column. */ diff --git a/packages/semantic-ui/src/i18n/en.json b/packages/semantic-ui/src/i18n/en.json index 110d2b36..b4ee3791 100644 --- a/packages/semantic-ui/src/i18n/en.json +++ b/packages/semantic-ui/src/i18n/en.json @@ -233,6 +233,7 @@ "List": { "buttons": { "add": "Add", + "csvExport": "CSV Export", "deleteAll": "Delete all" }, "deleteAllContent": "Are you sure you want to remove all records? This action cannot be undone.", diff --git a/packages/storybook/src/semantic-ui/DataTable.stories.js b/packages/storybook/src/semantic-ui/DataTable.stories.js index 0b0fd256..75b22655 100644 --- a/packages/storybook/src/semantic-ui/DataTable.stories.js +++ b/packages/storybook/src/semantic-ui/DataTable.stories.js @@ -233,6 +233,27 @@ export const Pagination = useDragDrop(() => ( /> )); +export const CsvExport = useDragDrop(() => ( + + }, + ]} + columns={columns} + csvExportButton={{ + color: 'blue', + location: 'bottom' + }} + items={items} + onColumnClick={action('column-click')} + onCopy={action('copy')} + onDelete={action('delete')} + onSave={action('save')} + /> +)); + export const CustomDeleteModal = useDragDrop(() => ( { ); }); +export const CsvExport = useDragDrop(() => ( + Api.onLoad(_.extend(params, { + items, + perPage: number('Per page', 10) + }))} + onDelete={action('delete')} + onSave={action('save')} + searchable={boolean('Searchable', true)} + /> +)); + export const CustomizableColumns = useDragDrop(() => (