Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0b53845

Browse files
implement export csv
1 parent 5913226 commit 0b53845

File tree

12 files changed

+252
-2
lines changed

12 files changed

+252
-2
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* eslint react/prop-types: 0 */
2+
import React from 'react';
3+
4+
import BootstrapTable from 'react-bootstrap-table-next';
5+
import ToolkitContext, { CSVExport } from 'react-bootstrap-table2-toolkit';
6+
import Code from 'components/common/code-block';
7+
import { productsGenerator } from 'utils/common';
8+
9+
const products = productsGenerator();
10+
11+
const columns = [{
12+
dataField: 'id',
13+
text: 'Product ID'
14+
}, {
15+
dataField: 'name',
16+
text: 'Product Name'
17+
}, {
18+
dataField: 'price',
19+
text: 'Product Price'
20+
}];
21+
22+
const sourceCode = `\
23+
import BootstrapTable from 'react-bootstrap-table-next';
24+
import ToolkitContext, { Search } from 'react-bootstrap-table2-toolkit';
25+
26+
const { SearchBar, searchFactory } = Search;
27+
const columns = [{
28+
dataField: 'id',
29+
text: 'Product ID'
30+
}, {
31+
dataField: 'name',
32+
text: 'Product Name'
33+
}, {
34+
dataField: 'price',
35+
text: 'Product Price'
36+
}];
37+
38+
<ToolkitContext.Provider
39+
keyField="id"
40+
data={ products }
41+
columns={ columns }
42+
>
43+
<ToolkitContext.Consumer>
44+
{
45+
props => (
46+
<div>
47+
<h3>Input something at below input field:</h3>
48+
<SearchBar { ...props.searchProps } />
49+
<hr />
50+
<BootstrapTable
51+
{ ...props.baseProps }
52+
search={ searchFactory({
53+
...props.searchProps
54+
}) }
55+
/>
56+
</div>
57+
)
58+
}
59+
</ToolkitContext.Consumer>
60+
</ToolkitContext.Provider>
61+
`;
62+
63+
export default () => (
64+
<div>
65+
<ToolkitContext.Provider
66+
keyField="id"
67+
data={ products }
68+
columns={ columns }
69+
exportCSV
70+
>
71+
<ToolkitContext.Consumer>
72+
{
73+
props => (
74+
<div>
75+
<h3>Input something at below input field:</h3>
76+
<CSVExport { ...props.csvProps } />
77+
<hr />
78+
<BootstrapTable { ...props.baseProps } />
79+
</div>
80+
)
81+
}
82+
</ToolkitContext.Consumer>
83+
</ToolkitContext.Provider>
84+
<Code>{ sourceCode }</Code>
85+
</div>
86+
);

‎packages/react-bootstrap-table2-example/stories/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ import FullyCustomSearch from 'examples/search/fully-custom-search';
128128
import SearchFormattedData from 'examples/search/search-formatted';
129129
import CustomSearchValue from 'examples/search/custom-search-value';
130130

131+
// CSV
132+
import ExportCSV from 'examples/csv';
133+
131134
// loading overlay
132135
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
133136
import TableOverlay from 'examples/loading-overlay/table-overlay';
@@ -279,6 +282,9 @@ storiesOf('Table Search', module)
279282
.add('Search Fromatted Value', () => <SearchFormattedData />)
280283
.add('Custom Search Value', () => <CustomSearchValue />);
281284

285+
storiesOf('Export CSV', module)
286+
.add('Basic Export CSV', () => <ExportCSV />);
287+
282288
storiesOf('EmptyTableOverlay', module)
283289
.add('Empty Table Overlay', () => <EmptyTableOverlay />)
284290
.add('Table Overlay', () => <TableOverlay />);

‎packages/react-bootstrap-table2-toolkit/context.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

22
import React from 'react';
33
import PropTypes from 'prop-types';
4+
import statelessDrcorator from './statelessOp';
45

56
import createContext from './src/search/context';
67

78
const ToolkitContext = React.createContext();
89

9-
class ToolkitProvider extends React.Component {
10+
class ToolkitProvider extends statelessDrcorator(React.Component) {
1011
static propTypes = {
1112
keyField: PropTypes.string.isRequired,
1213
data: PropTypes.array.isRequired,
@@ -17,11 +18,21 @@ class ToolkitProvider extends React.Component {
1718
PropTypes.shape({
1819
searchFormatted: PropTypes.bool
1920
})
21+
]),
22+
exportCSV: PropTypes.oneOfType([
23+
PropTypes.bool,
24+
PropTypes.shape({
25+
fileName: PropTypes.string,
26+
separator: PropTypes.string,
27+
ignoreHeader: PropTypes.bool,
28+
noAutoBOM: PropTypes.bool
29+
})
2030
])
2131
}
2232

2333
static defaultProps = {
24-
search: null
34+
search: false,
35+
exportCSV: false
2536
}
2637

2738
constructor(props) {
@@ -53,6 +64,9 @@ class ToolkitProvider extends React.Component {
5364
searchProps: {
5465
onSearch: this.onSearch
5566
},
67+
csvProps: {
68+
onExport: this.handleExportCSV
69+
},
5670
baseProps
5771
} }
5872
>

‎packages/react-bootstrap-table2-toolkit/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ import ToolkitProvider from './provider';
44
export default ToolkitProvider;
55
export const ToolkitContext = Context;
66
export { default as Search } from './src/search';
7+
export { default as CSVExport } from './src/csv';

‎packages/react-bootstrap-table2-toolkit/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,8 @@
4343
"prop-types": "^15.0.0",
4444
"react": "^16.3.0",
4545
"react-dom": "^16.3.0"
46+
},
47+
"dependencies": {
48+
"file-saver": "1.3.8"
4649
}
4750
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const ExportCSVButton = (props) => {
5+
const {
6+
onExport,
7+
children,
8+
...rest
9+
} = props;
10+
11+
return (
12+
<button
13+
type="button"
14+
onClick={ onExport }
15+
{ ...rest }
16+
>
17+
{ children }
18+
</button>
19+
);
20+
};
21+
22+
ExportCSVButton.propTypes = {
23+
children: PropTypes.node.isRequired,
24+
onExport: PropTypes.func.isRequired,
25+
className: PropTypes.string,
26+
style: PropTypes.object
27+
};
28+
ExportCSVButton.defaultProps = {
29+
className: 'react-bs-table-csv-btn btn btn-default',
30+
style: {}
31+
};
32+
33+
export default ExportCSVButton;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* eslint no-unneeded-ternary: 0 */
2+
import FileSaver from 'file-saver';
3+
4+
export const getMetaInfo = columns =>
5+
columns
6+
.map(column => ({
7+
field: column.dataField,
8+
type: column.csvType || String,
9+
formatter: column.csvFormatter,
10+
formatExtraData: column.formatExtraData,
11+
header: column.csvText || column.text,
12+
export: column.csvExport === false ? false : true,
13+
row: Number(column.row) || 0,
14+
rowSpan: Number(column.rowSpan) || 1,
15+
colSpan: Number(column.colSpan) || 1
16+
}))
17+
.filter(_ => _.export);
18+
19+
export const transform = (
20+
data,
21+
meta,
22+
{
23+
separator,
24+
ignoreHeader
25+
}
26+
) => {
27+
const visibleColumns = meta.filter(m => m.export);
28+
let content = '';
29+
// extract csv header
30+
if (!ignoreHeader) {
31+
content += visibleColumns.map(m => `"${m.header}"`).join(separator);
32+
content += '\n';
33+
}
34+
// extract csv body
35+
if (data.length === 0) return content;
36+
content += data
37+
.map((row, rowIndex) =>
38+
visibleColumns.map((m) => {
39+
let cellContent = row[m.field];
40+
if (m.formatter) {
41+
cellContent = m.formatter(cellContent, row, rowIndex, m.formatExtraData);
42+
}
43+
if (m.type === String) {
44+
return `"${cellContent}"`;
45+
}
46+
return cellContent;
47+
}).join(separator)).join('\n');
48+
49+
return content;
50+
};
51+
52+
export const save = (
53+
content,
54+
{
55+
noAutoBOM,
56+
fileName
57+
}
58+
) => {
59+
FileSaver.saveAs(
60+
new Blob(['\ufeff', content], { type: 'text/plain;charset=utf-8' }),
61+
fileName,
62+
noAutoBOM
63+
);
64+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import ExportCSVButton from './button';
2+
3+
export default { ExportCSVButton };
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { getMetaInfo, transform, save } from '../csv/exporter';
2+
3+
const csvDefaultOptions = {
4+
fileName: 'spreadsheet.csv',
5+
separator: ',',
6+
ignoreHeader: false,
7+
noAutoBOM: true
8+
};
9+
10+
export default Base =>
11+
class CSVOperation extends Base {
12+
handleExportCSV = () => {
13+
const { columns, data, exportCSV } = this.props;
14+
const meta = getMetaInfo(columns);
15+
const options = exportCSV === true ?
16+
csvDefaultOptions :
17+
{
18+
...csvDefaultOptions,
19+
...exportCSV
20+
};
21+
const content = transform(data, meta, options);
22+
save(content, options);
23+
}
24+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import csvOperation from './csv';
2+
3+
export default {
4+
csvOperation
5+
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /