Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit 4152242

Browse files
Add async usage for xlsx
1 parent e8e588d commit 4152242

26 files changed

+248
-153
lines changed

.circleci/config.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
python -m venv venv || virtualenv venv
3232
. venv/bin/activate
3333
pip install -r dev-requirements.txt --quiet
34-
git clone --depth 1 [email protected]:plotly/dash.git dash-main
34+
git clone --depth 1 -b exp-dynamic [email protected]:plotly/dash.git dash-main
3535
pip install -e ./dash-main[dev] --quiet
3636
3737
- run:
@@ -107,7 +107,7 @@ jobs:
107107
python -m venv venv || virtualenv venv
108108
. venv/bin/activate
109109
pip install -r dev-requirements.txt --quiet
110-
git clone --depth 1 [email protected]:plotly/dash.git dash-main
110+
git clone --depth 1 -b exp-dynamic [email protected]:plotly/dash.git dash-main
111111
pip install -e ./dash-main[dev] --quiet
112112
113113
- run:
@@ -212,7 +212,7 @@ jobs:
212212
name: Install dependencies (dash)
213213
command: |
214214
. venv/bin/activate
215-
git clone --depth 1 [email protected]:plotly/dash.git dash-main
215+
git clone --depth 1 -b exp-dynamic [email protected]:plotly/dash.git dash-main
216216
pip install -e ./dash-main[dev,testing] --quiet
217217
cd dash-main/dash-renderer && npm run build && pip install -e . && cd ../..
218218

.config/webpack/base.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
21
const path = require('path');
2+
const WebpackDashDynamicImport = require('@plotly/webpack-dash-dynamic-import');
3+
4+
const basePreprocessing = require('./base.preprocessing');
35
const packagejson = require('./../../package.json');
46

57
const dashLibraryName = packagejson.name.replace(/-/g, '_');
68

9+
710
module.exports = (options = {}) => {
811
const babel = options.babel || undefined;
9-
const preprocessor = options.preprocessor || {};
12+
const preprocessor = basePreprocessing(options.preprocessor);
1013
const mode = options.mode || 'development';
1114
const ts = options.ts || {};
1215

@@ -24,6 +27,7 @@ module.exports = (options = {}) => {
2427
mode: mode,
2528
output: {
2629
path: path.resolve(__dirname, `./../../${dashLibraryName}`),
30+
chunkFilename: '[name].js',
2731
filename: '[name].js',
2832
library: dashLibraryName,
2933
libraryTarget: 'window'
@@ -87,6 +91,20 @@ module.exports = (options = {}) => {
8791
tests: path.resolve('./tests')
8892
},
8993
extensions: ['.js', '.ts', '.tsx']
90-
}
94+
},
95+
optimization: {
96+
splitChunks: {
97+
chunks: 'async',
98+
name: true,
99+
cacheGroups: {
100+
async: {
101+
102+
}
103+
}
104+
}
105+
},
106+
plugins: [
107+
new WebpackDashDynamicImport()
108+
]
91109
};
92110
};

.config/webpack/base.preprocessing.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = ({ definitions, variables, ...options } = {}) => ({
2+
...options,
3+
definitions: definitions || [],
4+
variables: Object.assign({
5+
mode: 'lazy'
6+
}, variables || {})
7+
});

.storybook/webpack.config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
let babel = require('./babel.config.js');
22
let config = require('./../.config/webpack/base.js')({
3-
babel
3+
babel,
4+
preprocessor: {
5+
variables: {
6+
mode: 'eager'
7+
}
8+
}
49
});
510

611
config.externals = {};
12+
delete config.plugins;
713

814
module.exports = config;

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## Unreleased
6+
### Changed
7+
- [#554](https://github.com/plotly/dash-table/pull/554) Async loading of `xlsx` library on export
8+
59
## [4.4.1] - 2019-08-17
610
### Fixed
711
- [#618](https://github.com/plotly/dash-table/issues/618) Fix a bug with keyboard navigation not working correctly in certain circumstances when the table contains `readonly` columns.

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
include dash_table/bundle.js
22
include dash_table/bundle.js.map
3+
include dash_table/async~*.js
4+
include dash_table/async~*.js.map
35
include dash_table/metadata.json
46
include dash_table/package-info.json
57
include LICENSE

dash_table_base/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@
4444
).format(__version__),
4545
'namespace': package_name,
4646
'dynamic': True
47+
},
48+
{
49+
'relative_package_path': 'async~export.js',
50+
'external_url': (
51+
'https://unpkg.com/dash-table@{}/dash_table/async~export.js'
52+
).format(__version__),
53+
'namespace': package_name,
54+
'async': True
55+
},
56+
{
57+
'relative_package_path': 'async~export.js.map',
58+
'external_url': (
59+
'https://unpkg.com/dash-table@{}/dash_table/async~export.js.map'
60+
).format(__version__),
61+
'namespace': package_name,
62+
'dynamic': True
4763
}
4864
]
4965

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
"preprivate::opentests": "run-s private::wait*",
1616
"preprivate::test.server": "run-s private::wait_dash*",
1717
"preprivate::test.standalone": "run-s private::wait_js",
18-
"pretest.standalone": "run-s private::build:js-test",
18+
"pretest.standalone": "run-s private::build:js-test-standalone",
1919
"private::build": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack --display-reasons --bail",
2020
"private::build:js": "run-s \"private::build -- --mode production\"",
21-
"private::build:js-dev": "run-s \"private::build -- --mode development --config webpack.dev.config.js\"",
2221
"private::build:js-test": "run-s \"private::build -- --mode development --config webpack.test.config.js\"",
22+
"private::build:js-test-standalone": "run-s \"private::build -- --mode development --config webpack.test.standalone.config.js\"",
2323
"private::build:js-test-watch": "run-s \"private::build -- --mode development --config webpack.test.config.js --watch\"",
2424
"private::build:py": "dash-generate-components src/dash-table/dash/DataTable.js dash_table -p package-info.json && cp dash_table_base/** dash_table/ && dash-generate-components src/dash-table/dash/DataTable.js dash_table -p package-info.json --r-prefix 'dash'",
2525
"private::host_dash8081": "python tests/cypress/dash/v_be_page.py",
@@ -61,6 +61,7 @@
6161
"devDependencies": {
6262
"@babel/cli": "^7.6.2",
6363
"@babel/core": "^7.6.2",
64+
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
6465
"@babel/polyfill": "^7.6.0",
6566
"@babel/preset-env": "^7.6.2",
6667
"@babel/preset-react": "^7.0.0",
@@ -70,6 +71,7 @@
7071
"@fortawesome/free-solid-svg-icons": "^5.11.2",
7172
"@fortawesome/react-fontawesome": "^0.1.4",
7273
"@percy/storybook": "^3.2.0",
74+
"@plotly/webpack-dash-dynamic-import": "^1.0.0",
7375
"@storybook/cli": "^5.1.11",
7476
"@storybook/react": "^5.1.11",
7577
"@types/d3-format": "^1.3.1",

src/dash-table/LazyLoader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default class LazyLoader {
2+
public static get xlsx() {
3+
return import(/* webpackChunkName: "export", webpackMode: "$${{mode}}" */ 'xlsx');
4+
}
5+
}

src/dash-table/components/Export/index.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import XLSX from 'xlsx';
21
import React from 'react';
32
import { IDerivedData, Columns, ExportHeaders, ExportFormat, ExportColumns } from 'dash-table/components/Table/props';
4-
import { createWorkbook, createHeadings, createWorksheet } from './utils';
3+
import { createWorkbook, createHeadings, exportWorkbook } from './utils';
54
import getHeaderRows from 'dash-table/derived/header/headerRows';
65

76
interface IExportButtonProps {
@@ -21,19 +20,16 @@ export default React.memo((props: IExportButtonProps) => {
2120

2221
const exportedColumns = export_columns === ExportColumns.Visible ? visibleColumns : columns;
2322

24-
const handleExport = () => {
23+
const handleExport = async () => {
2524
const columnID = exportedColumns.map(column => column.id);
2625
const columnHeaders = exportedColumns.map(column => column.name);
2726

2827
const maxLength = getHeaderRows(columns);
2928
const heading = (export_headers !== ExportHeaders.None) ? createHeadings(columnHeaders, maxLength) : [];
30-
const ws = createWorksheet(heading, virtual_data.data, columnID, export_headers, merge_duplicate_headers);
31-
const wb = createWorkbook(ws);
32-
if (export_format === ExportFormat.Xlsx) {
33-
XLSX.writeFile(wb, 'Data.xlsx', {bookType: 'xlsx', type: 'buffer'});
34-
} else if (export_format === ExportFormat.Csv) {
35-
XLSX.writeFile(wb, 'Data.csv', {bookType: 'csv', type: 'buffer'});
36-
}
29+
30+
const wb = await createWorkbook(heading, virtual_data.data, columnID, export_headers, merge_duplicate_headers);
31+
32+
await exportWorkbook(wb, export_format);
3733
};
3834

3935
return (<div>

src/dash-table/components/Export/utils.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as R from 'ramda';
2-
import XLSX from 'xlsx';
2+
import { WorkBook } from 'xlsx/types';
3+
34
import { Data, ExportHeaders } from 'dash-table/components/Table/props';
5+
import LazyLoader from 'dash-table/LazyLoader';
46

57
interface IMergeObject {
68
s: {r: number, c: number};
@@ -47,13 +49,9 @@ export function getMergeRanges(array: string[][]) {
4749
return R.filter((item: IMergeObject) => item.s.c !== item.e.c || item.s.r !== item.e.r, apiMergeArray);
4850
}
4951

50-
export function createWorkbook(ws: XLSX.WorkSheet) {
51-
const wb = XLSX.utils.book_new();
52-
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
53-
return wb;
54-
}
52+
export async function createWorkbook(heading: string[][], data: Data, columnID: string[], exportHeader: string, mergeDuplicateHeaders: boolean ) {
53+
const XLSX = await LazyLoader.xlsx;
5554

56-
export function createWorksheet(heading: string[][], data: Data, columnID: string[], exportHeader: ExportHeaders, mergeDuplicateHeaders: boolean) {
5755
const ws = XLSX.utils.aoa_to_sheet([]);
5856

5957
data = R.map(R.pick(columnID))(data);
@@ -73,7 +71,20 @@ export function createWorksheet(heading: string[][], data: Data, columnID: strin
7371
} else if (exportHeader === ExportHeaders.Ids) {
7472
XLSX.utils.sheet_add_json(ws, data, { header: columnID });
7573
}
76-
return ws;
74+
75+
const wb = XLSX.utils.book_new();
76+
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
77+
return wb;
78+
}
79+
80+
export async function exportWorkbook (wb: WorkBook, format: string) {
81+
const XLSX = await LazyLoader.xlsx;
82+
83+
if (format === 'xlsx') {
84+
XLSX.writeFile(wb, 'Data.xlsx', { bookType: 'xlsx', type: 'buffer' });
85+
} else if (format === 'csv') {
86+
XLSX.writeFile(wb, 'Data.csv', { bookType: 'csv', type: 'buffer' });
87+
}
7788
}
7889

7990
export function createHeadings(columnHeaders: (string | string[])[], maxLength: number) {

src/dash-table/dash/DataTable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,4 +1307,4 @@ DataTable.persistenceTransforms = {
13071307
};
13081308

13091309
DataTable.defaultProps = defaultProps;
1310-
DataTable.propTypes = propTypes;
1310+
DataTable.propTypes = propTypes;

tests/cypress/plugins/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const wp = require('@cypress/webpack-preprocessor');
22

33
module.exports = on => {
44
const options = {
5-
webpackOptions: require('../../../webpack.test.config.js')
5+
webpackOptions: require('../../../webpack.test.standalone.config.js')
66
};
77

88
on('file:preprocessor', wp(options));

tests/cypress/tests/unit/exportUtils_tests.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { transformMultiDimArray, getMergeRanges, createHeadings, createWorksheet } from 'dash-table/components/Export/utils';
21
import * as R from 'ramda';
32
import { ExportHeaders } from 'dash-table/components/Table/props';
43

4+
import { transformMultiDimArray, getMergeRanges, createHeadings, createWorkbook } from 'dash-table/components/Export/utils';
5+
56
describe('export', () => {
67

78
describe('transformMultiDimArray', () => {
@@ -225,10 +226,10 @@ describe('export', () => {
225226
];
226227

227228
const columnID = ['col1', 'col2', 'col4'];
228-
it('create sheet with column names as headers for name or display header mode', () => {
229-
const wsName = createWorksheet(Headings, data, columnID, ExportHeaders.Names, true);
230-
const wsDisplay = createWorksheet(Headings, data, columnID, ExportHeaders.Display, true);
231-
const wsDisplayNoMerge = createWorksheet(Headings, data, columnID, ExportHeaders.Display, false);
229+
it('create sheet with column names as headers for name or display header mode', async () => {
230+
const wsName = await createWorkbook(Headings, data, columnID, ExportHeaders.Names, true);
231+
const wsDisplay = await createWorkbook(Headings, data, columnID, ExportHeaders.Display, true);
232+
const wsDisplayNoMerge = await createWorkbook(Headings, data, columnID, ExportHeaders.Display, false);
232233
const expectedWS = {
233234
A1: {t: 's', v: 'rows'},
234235
A2: {t: 's', v: 'rows'},
@@ -256,12 +257,12 @@ describe('export', () => {
256257
expectedWSDisplay['!merges'] = [ {s: {r: 0, c: 0}, e: {r: 0, c: 1}},
257258
{s: {r: 1, c: 1}, e: {r: 1, c: 2}},
258259
{s: {r: 3, c: 0}, e: {r: 3, c: 2}} ];
259-
expect(wsName).to.deep.equal(expectedWS);
260-
expect(wsDisplayNoMerge).to.deep.equal(expectedWS);
261-
expect(wsDisplay).to.deep.equal(expectedWSDisplay);
260+
expect(wsName.Sheets.SheetJS).to.deep.equal(expectedWS);
261+
expect(wsDisplayNoMerge.Sheets.SheetJS).to.deep.equal(expectedWS);
262+
expect(wsDisplay.Sheets.SheetJS).to.deep.equal(expectedWSDisplay);
262263
});
263-
it('create sheet with column ids as headers', () => {
264-
const ws = createWorksheet(Headings, data, columnID, ExportHeaders.Ids, true);
264+
it('create sheet with column ids as headers', async () => {
265+
const ws = await createWorkbook(Headings, data, columnID, ExportHeaders.Ids, true);
265266
const expectedWS = {
266267
A1: {t: 's', v: 'col1'},
267268
A2: {t: 'n', v: 1},
@@ -276,10 +277,10 @@ describe('export', () => {
276277
C3: {t: 'n', v: 4},
277278
C4: {t: 'n', v: 3}};
278279
expectedWS['!ref'] = 'A1:C4';
279-
expect(ws).to.deep.equal(expectedWS);
280+
expect(ws.Sheets.SheetJS).to.deep.equal(expectedWS);
280281
});
281-
it('create sheet with no headers', () => {
282-
const ws = createWorksheet([], data, columnID, ExportHeaders.None, true);
282+
it('create sheet with no headers', async () => {
283+
const ws = await createWorkbook([], data, columnID, ExportHeaders.None, true);
283284
const expectedWS = {
284285
A1: {t: 'n', v: 1},
285286
A2: {t: 'n', v: 2},
@@ -291,15 +292,15 @@ describe('export', () => {
291292
C2: {t: 'n', v: 4},
292293
C3: {t: 'n', v: 3}};
293294
expectedWS['!ref'] = 'A1:C3';
294-
expect(ws).to.deep.equal(expectedWS);
295+
expect(ws.Sheets.SheetJS).to.deep.equal(expectedWS);
295296
});
296-
it('create sheet with undefined column for clearable columns', () => {
297+
it('create sheet with undefined column for clearable columns', async () => {
297298
const newData = [
298299
{col2: 2, col4: 3},
299300
{col2: 3, col4: 4},
300301
{col2: 2, col4: 3}
301302
];
302-
const ws = createWorksheet(Headings, newData, columnID, ExportHeaders.Display, false);
303+
const ws = await createWorkbook(Headings, newData, columnID, ExportHeaders.Display, false);
303304
const expectedWS = {A1: {t: 's', v: 'rows'},
304305
A2: {t: 's', v: 'rows'},
305306
A3: {t: 's', v: 'rows'},
@@ -319,7 +320,7 @@ describe('export', () => {
319320
C6: {t: 'n', v: 4},
320321
C7: {t: 'n', v: 3}};
321322
expectedWS['!ref'] = 'A1:C7';
322-
expect(ws).to.deep.equal(expectedWS);
323+
expect(ws.Sheets.SheetJS).to.deep.equal(expectedWS);
323324
});
324325
});
325326

tests/visual/percy-storybook/Border.defaults.percy.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ storiesOf('DashTable/Border (available space filled)', module)
9393
fixed_rows={{ headers: true }}
9494
/>))
9595
.add('with no frozen rows and frozen columns', () => (<DataTable
96-
{...props2}DataTable
96+
{...props2} DataTable
9797
fixed_columns={{ headers: true, data: 1 }}
9898
/>))
9999
.add('with frozen rows and frozen columns', () => (<DataTable

0 commit comments

Comments
 (0)