diff --git a/packages/react-bootstrap-table2-example/examples/csv/index.js b/packages/react-bootstrap-table2-example/examples/csv/index.js
new file mode 100644
index 0000000..e8069c1
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/csv/index.js
@@ -0,0 +1,86 @@
+/* eslint react/prop-types: 0 */
+import React from 'react';
+
+import BootstrapTable from 'react-bootstrap-table-next';
+import ToolkitContext, { CSVExport } from 'react-bootstrap-table2-toolkit';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator();
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price'
+}];
+
+const sourceCode = `\
+import BootstrapTable from 'react-bootstrap-table-next';
+import ToolkitContext, { Search } from 'react-bootstrap-table2-toolkit';
+
+const { SearchBar, searchFactory } = Search;
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price'
+}];
+
+
+
+ {
+ props => (
+
+
Input something at below input field:
+
+
+
+
+ )
+ }
+
+
+`;
+
+export default () => (
+
+
+
+ {
+ props => (
+
+
Input something at below input field:
+
+
+
+
+ )
+ }
+
+
+
{ sourceCode }
+
+);
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index d556b1f..04d589e 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -133,6 +133,9 @@ import FullyCustomSearch from 'examples/search/fully-custom-search';
import SearchFormattedData from 'examples/search/search-formatted';
import CustomSearchValue from 'examples/search/custom-search-value';
+// CSV
+import ExportCSV from 'examples/csv';
+
// loading overlay
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
import TableOverlay from 'examples/loading-overlay/table-overlay';
@@ -289,6 +292,9 @@ storiesOf('Table Search', module)
.add('Search Fromatted Value', () => )
.add('Custom Search Value', () => );
+storiesOf('Export CSV', module)
+ .add('Basic Export CSV', () => );
+
storiesOf('EmptyTableOverlay', module)
.add('Empty Table Overlay', () => )
.add('Table Overlay', () => );
diff --git a/packages/react-bootstrap-table2-toolkit/context.js b/packages/react-bootstrap-table2-toolkit/context.js
index a561203..e55be86 100644
--- a/packages/react-bootstrap-table2-toolkit/context.js
+++ b/packages/react-bootstrap-table2-toolkit/context.js
@@ -1,12 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
+import statelessDrcorator from './statelessOp';
import createContext from './src/search/context';
const ToolkitContext = React.createContext();
-class ToolkitProvider extends React.Component {
+class ToolkitProvider extends statelessDrcorator(React.Component) {
static propTypes = {
keyField: PropTypes.string.isRequired,
data: PropTypes.array.isRequired,
@@ -17,11 +18,21 @@ class ToolkitProvider extends React.Component {
PropTypes.shape({
searchFormatted: PropTypes.bool
})
+ ]),
+ exportCSV: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.shape({
+ fileName: PropTypes.string,
+ separator: PropTypes.string,
+ ignoreHeader: PropTypes.bool,
+ noAutoBOM: PropTypes.bool
+ })
])
}
static defaultProps = {
- search: null
+ search: false,
+ exportCSV: false
}
constructor(props) {
@@ -53,6 +64,9 @@ class ToolkitProvider extends React.Component {
searchProps: {
onSearch: this.onSearch
},
+ csvProps: {
+ onExport: this.handleExportCSV
+ },
baseProps
} }
>
diff --git a/packages/react-bootstrap-table2-toolkit/index.js b/packages/react-bootstrap-table2-toolkit/index.js
index 836f07d..f9553ac 100644
--- a/packages/react-bootstrap-table2-toolkit/index.js
+++ b/packages/react-bootstrap-table2-toolkit/index.js
@@ -4,3 +4,4 @@ import ToolkitProvider from './provider';
export default ToolkitProvider;
export const ToolkitContext = Context;
export { default as Search } from './src/search';
+export { default as CSVExport } from './src/csv';
diff --git a/packages/react-bootstrap-table2-toolkit/package.json b/packages/react-bootstrap-table2-toolkit/package.json
index efdbc64..6338d8a 100644
--- a/packages/react-bootstrap-table2-toolkit/package.json
+++ b/packages/react-bootstrap-table2-toolkit/package.json
@@ -43,5 +43,8 @@
"prop-types": "^15.0.0",
"react": "^16.3.0",
"react-dom": "^16.3.0"
+ },
+ "dependencies": {
+ "file-saver": "1.3.8"
}
}
diff --git a/packages/react-bootstrap-table2-toolkit/src/csv/button.js b/packages/react-bootstrap-table2-toolkit/src/csv/button.js
new file mode 100644
index 0000000..d0ee010
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/src/csv/button.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const ExportCSVButton = (props) => {
+ const {
+ onExport,
+ children,
+ ...rest
+ } = props;
+
+ return (
+
+ );
+};
+
+ExportCSVButton.propTypes = {
+ children: PropTypes.node.isRequired,
+ onExport: PropTypes.func.isRequired,
+ className: PropTypes.string,
+ style: PropTypes.object
+};
+ExportCSVButton.defaultProps = {
+ className: 'react-bs-table-csv-btn btn btn-default',
+ style: {}
+};
+
+export default ExportCSVButton;
diff --git a/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js
new file mode 100644
index 0000000..0cb9252
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js
@@ -0,0 +1,64 @@
+/* eslint no-unneeded-ternary: 0 */
+import FileSaver from 'file-saver';
+
+export const getMetaInfo = columns =>
+ columns
+ .map(column => ({
+ field: column.dataField,
+ type: column.csvType || String,
+ formatter: column.csvFormatter,
+ formatExtraData: column.formatExtraData,
+ header: column.csvText || column.text,
+ export: column.csvExport === false ? false : true,
+ row: Number(column.row) || 0,
+ rowSpan: Number(column.rowSpan) || 1,
+ colSpan: Number(column.colSpan) || 1
+ }))
+ .filter(_ => _.export);
+
+export const transform = (
+ data,
+ meta,
+ {
+ separator,
+ ignoreHeader
+ }
+) => {
+ const visibleColumns = meta.filter(m => m.export);
+ let content = '';
+ // extract csv header
+ if (!ignoreHeader) {
+ content += visibleColumns.map(m => `"${m.header}"`).join(separator);
+ content += '\n';
+ }
+ // extract csv body
+ if (data.length === 0) return content;
+ content += data
+ .map((row, rowIndex) =>
+ visibleColumns.map((m) => {
+ let cellContent = row[m.field];
+ if (m.formatter) {
+ cellContent = m.formatter(cellContent, row, rowIndex, m.formatExtraData);
+ }
+ if (m.type === String) {
+ return `"${cellContent}"`;
+ }
+ return cellContent;
+ }).join(separator)).join('\n');
+
+ return content;
+};
+
+export const save = (
+ content,
+ {
+ noAutoBOM,
+ fileName
+ }
+) => {
+ FileSaver.saveAs(
+ new Blob(['\ufeff', content], { type: 'text/plain;charset=utf-8' }),
+ fileName,
+ noAutoBOM
+ );
+};
diff --git a/packages/react-bootstrap-table2-toolkit/src/csv/index.js b/packages/react-bootstrap-table2-toolkit/src/csv/index.js
new file mode 100644
index 0000000..4c60453
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/src/csv/index.js
@@ -0,0 +1,3 @@
+import ExportCSVButton from './button';
+
+export default { ExportCSVButton };
diff --git a/packages/react-bootstrap-table2-toolkit/src/op/csv.js b/packages/react-bootstrap-table2-toolkit/src/op/csv.js
new file mode 100644
index 0000000..71cf3b2
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/src/op/csv.js
@@ -0,0 +1,24 @@
+import { getMetaInfo, transform, save } from '../csv/exporter';
+
+const csvDefaultOptions = {
+ fileName: 'spreadsheet.csv',
+ separator: ',',
+ ignoreHeader: false,
+ noAutoBOM: true
+};
+
+export default Base =>
+ class CSVOperation extends Base {
+ handleExportCSV = () => {
+ const { columns, data, exportCSV } = this.props;
+ const meta = getMetaInfo(columns);
+ const options = exportCSV === true ?
+ csvDefaultOptions :
+ {
+ ...csvDefaultOptions,
+ ...exportCSV
+ };
+ const content = transform(data, meta, options);
+ save(content, options);
+ }
+ };
diff --git a/packages/react-bootstrap-table2-toolkit/src/op/index.js b/packages/react-bootstrap-table2-toolkit/src/op/index.js
new file mode 100644
index 0000000..847e057
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/src/op/index.js
@@ -0,0 +1,5 @@
+import csvOperation from './csv';
+
+export default {
+ csvOperation
+};
diff --git a/packages/react-bootstrap-table2-toolkit/statelessOp.js b/packages/react-bootstrap-table2-toolkit/statelessOp.js
new file mode 100644
index 0000000..b39287d
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/statelessOp.js
@@ -0,0 +1,4 @@
+import Operation from './src/op';
+
+export default Base =>
+ class StatelessOperation extends Operation.csvOperation(Base) {};
diff --git a/packages/react-bootstrap-table2-toolkit/yarn.lock b/packages/react-bootstrap-table2-toolkit/yarn.lock
new file mode 100644
index 0000000..bcdf61c
--- /dev/null
+++ b/packages/react-bootstrap-table2-toolkit/yarn.lock
@@ -0,0 +1,7 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+file-saver@1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"