@mui/x-data-grid-premium 6.19.11 → 6.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/components/GridExcelExportMenuItem.js +1 -0
- package/hooks/features/cellSelection/useGridCellSelection.js +6 -3
- package/hooks/features/export/serializer/excelSerializer.d.ts +3 -8
- package/hooks/features/export/serializer/excelSerializer.js +14 -4
- package/hooks/features/export/useGridExcelExport.js +9 -3
- package/index.js +1 -1
- package/legacy/components/GridExcelExportMenuItem.js +1 -0
- package/legacy/hooks/features/cellSelection/useGridCellSelection.js +6 -3
- package/legacy/hooks/features/export/serializer/excelSerializer.js +14 -4
- package/legacy/hooks/features/export/useGridExcelExport.js +7 -3
- package/legacy/index.js +1 -1
- package/legacy/utils/releaseInfo.js +1 -1
- package/modern/components/GridExcelExportMenuItem.js +1 -0
- package/modern/hooks/features/cellSelection/useGridCellSelection.js +6 -3
- package/modern/hooks/features/export/serializer/excelSerializer.js +14 -4
- package/modern/hooks/features/export/useGridExcelExport.js +5 -2
- package/modern/index.js +1 -1
- package/modern/utils/releaseInfo.js +1 -1
- package/node/components/GridExcelExportMenuItem.js +1 -0
- package/node/hooks/features/cellSelection/useGridCellSelection.js +6 -3
- package/node/hooks/features/export/serializer/excelSerializer.js +14 -4
- package/node/hooks/features/export/useGridExcelExport.js +5 -2
- package/node/index.js +1 -1
- package/node/utils/releaseInfo.js +1 -1
- package/package.json +3 -3
- package/utils/releaseInfo.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,60 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 6.20.0
|
|
7
|
+
|
|
8
|
+
_May 24, 2024_
|
|
9
|
+
|
|
10
|
+
We'd like to offer a big thanks to the 2 contributors who made this release possible. Here are some highlights ✨:
|
|
11
|
+
|
|
12
|
+
- 🐞 Bugfixes
|
|
13
|
+
|
|
14
|
+
### Data Grid
|
|
15
|
+
|
|
16
|
+
#### `@mui/x-data-grid@6.20.0`
|
|
17
|
+
|
|
18
|
+
- [DataGrid] Escape formulas in CSV and Excel export (#13190) @cherniavskii
|
|
19
|
+
|
|
20
|
+
#### `@mui/x-data-grid-pro@6.20.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
21
|
+
|
|
22
|
+
Same changes as in `@mui/x-data-grid@6.20.0`.
|
|
23
|
+
|
|
24
|
+
#### `@mui/x-data-grid-premium@6.20.0` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
25
|
+
|
|
26
|
+
Same changes as in `@mui/x-data-grid-pro@6.20.0`.
|
|
27
|
+
|
|
28
|
+
### Date Pickers
|
|
29
|
+
|
|
30
|
+
#### `@mui/x-date-pickers@6.20.0`
|
|
31
|
+
|
|
32
|
+
- [pickers] Fix `disableOpenPicker` prop behavior (#13221) @LukasTy
|
|
33
|
+
|
|
34
|
+
#### `@mui/x-date-pickers-pro@6.20.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
35
|
+
|
|
36
|
+
Same changes as in `@mui/x-date-pickers@6.20.0`.
|
|
37
|
+
|
|
38
|
+
## 6.19.12
|
|
39
|
+
|
|
40
|
+
_May 17, 2024_
|
|
41
|
+
|
|
42
|
+
We'd like to offer a big thanks to the 2 contributors who made this release possible. Here are some highlights ✨:
|
|
43
|
+
|
|
44
|
+
- 🐞 Bugfixes
|
|
45
|
+
|
|
46
|
+
### Date Pickers
|
|
47
|
+
|
|
48
|
+
#### `@mui/x-date-pickers@6.19.12`
|
|
49
|
+
|
|
50
|
+
- [pickers] Fix `AdapterMomentJalaali` regression (#13150) @LukasTy
|
|
51
|
+
|
|
52
|
+
#### `@mui/x-date-pickers-pro@6.19.12` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
53
|
+
|
|
54
|
+
Same changes as in `@mui/x-date-pickers@6.19.12`.
|
|
55
|
+
|
|
56
|
+
### Docs
|
|
57
|
+
|
|
58
|
+
- [docs] Use MUI X v6 in Codesandbox and Stackblitz demos (#12838) @cherniavskii
|
|
59
|
+
|
|
6
60
|
## 6.19.11
|
|
7
61
|
|
|
8
62
|
_Apr 18, 2024_
|
|
@@ -32,6 +32,7 @@ process.env.NODE_ENV !== "production" ? GridExcelExportMenuItem.propTypes = {
|
|
|
32
32
|
allColumns: PropTypes.bool,
|
|
33
33
|
columnsStyles: PropTypes.object,
|
|
34
34
|
disableToolbarButton: PropTypes.bool,
|
|
35
|
+
escapeFormulas: PropTypes.bool,
|
|
35
36
|
exceljsPostProcess: PropTypes.func,
|
|
36
37
|
exceljsPreProcess: PropTypes.func,
|
|
37
38
|
fields: PropTypes.arrayOf(PropTypes.string),
|
|
@@ -455,9 +455,12 @@ export const useGridCellSelection = (apiRef, props) => {
|
|
|
455
455
|
if (fieldsMap[field]) {
|
|
456
456
|
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
457
457
|
cellData = serializeCellValue(cellParams, {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
458
|
+
csvOptions: {
|
|
459
|
+
delimiter: clipboardCopyCellDelimiter,
|
|
460
|
+
shouldAppendQuotes: false,
|
|
461
|
+
escapeFormulas: false
|
|
462
|
+
},
|
|
463
|
+
ignoreValueFormatter
|
|
461
464
|
});
|
|
462
465
|
} else {
|
|
463
466
|
cellData = '';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as Excel from 'exceljs';
|
|
2
2
|
import { GridRowId, GridColDef } from '@mui/x-data-grid-pro';
|
|
3
3
|
import { GridStateColDef, GridColumnGroupLookup } from '@mui/x-data-grid/internals';
|
|
4
|
-
import {
|
|
4
|
+
import { ColumnsStylesInterface, GridExcelExportOptions } from '../gridExcelExportInterface';
|
|
5
5
|
import { GridPrivateApiPremium } from '../../../../models/gridApiPremium';
|
|
6
6
|
interface SerializedRow {
|
|
7
7
|
row: Record<string, undefined | number | boolean | string | Date>;
|
|
@@ -16,7 +16,7 @@ export declare const serializeRow: (id: GridRowId, columns: GridStateColDef[], a
|
|
|
16
16
|
[field: string]: {
|
|
17
17
|
address: string;
|
|
18
18
|
};
|
|
19
|
-
}) => SerializedRow;
|
|
19
|
+
}, options: Pick<BuildExcelOptions, 'escapeFormulas'>) => SerializedRow;
|
|
20
20
|
export declare const serializeColumn: (column: GridColDef, columnsStyles: ColumnsStylesInterface) => {
|
|
21
21
|
key: string;
|
|
22
22
|
headerText: string;
|
|
@@ -42,14 +42,9 @@ type ValueOptionsData = Record<string, {
|
|
|
42
42
|
address: string;
|
|
43
43
|
}>;
|
|
44
44
|
export declare function getDataForValueOptionsSheet(columns: GridStateColDef[], valueOptionsSheetName: string, api: GridPrivateApiPremium): Promise<ValueOptionsData>;
|
|
45
|
-
interface BuildExcelOptions {
|
|
45
|
+
interface BuildExcelOptions extends Pick<GridExcelExportOptions, 'exceljsPreProcess' | 'exceljsPostProcess'>, Pick<Required<GridExcelExportOptions>, 'valueOptionsSheetName' | 'includeHeaders' | 'includeColumnGroupsHeaders' | 'escapeFormulas'> {
|
|
46
46
|
columns: GridStateColDef[];
|
|
47
47
|
rowIds: GridRowId[];
|
|
48
|
-
includeHeaders: boolean;
|
|
49
|
-
includeColumnGroupsHeaders: boolean;
|
|
50
|
-
valueOptionsSheetName: string;
|
|
51
|
-
exceljsPreProcess?: (processInput: GridExceljsProcessInput) => Promise<void>;
|
|
52
|
-
exceljsPostProcess?: (processInput: GridExceljsProcessInput) => Promise<void>;
|
|
53
48
|
columnsStyles?: ColumnsStylesInterface;
|
|
54
49
|
}
|
|
55
50
|
export declare function buildExcel(options: BuildExcelOptions, api: GridPrivateApiPremium): Promise<Excel.Workbook>;
|
|
@@ -27,7 +27,7 @@ const getFormattedValueOptions = (colDef, valueOptions, api) => {
|
|
|
27
27
|
}
|
|
28
28
|
return valueOptionsFormatted.map(option => typeof option === 'object' ? option.label : option);
|
|
29
29
|
};
|
|
30
|
-
export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
30
|
+
export const serializeRow = (id, columns, api, defaultValueOptionsFormulae, options) => {
|
|
31
31
|
const row = {};
|
|
32
32
|
const dataValidation = {};
|
|
33
33
|
const mergedCells = [];
|
|
@@ -53,6 +53,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
const cellParams = api.getCellParams(id, column.field);
|
|
56
|
+
let cellValue;
|
|
56
57
|
switch (cellParams.colDef.type) {
|
|
57
58
|
case 'singleSelect':
|
|
58
59
|
{
|
|
@@ -96,7 +97,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
96
97
|
}
|
|
97
98
|
case 'boolean':
|
|
98
99
|
case 'number':
|
|
99
|
-
|
|
100
|
+
cellValue = api.getCellParams(id, column.field).value;
|
|
100
101
|
break;
|
|
101
102
|
case 'date':
|
|
102
103
|
case 'dateTime':
|
|
@@ -116,7 +117,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
116
117
|
case 'actions':
|
|
117
118
|
break;
|
|
118
119
|
default:
|
|
119
|
-
|
|
120
|
+
cellValue = api.getCellParams(id, column.field).formattedValue;
|
|
120
121
|
if (process.env.NODE_ENV !== 'production') {
|
|
121
122
|
if (String(cellParams.formattedValue) === '[object Object]') {
|
|
122
123
|
warnInvalidFormattedValue();
|
|
@@ -124,6 +125,15 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
124
125
|
}
|
|
125
126
|
break;
|
|
126
127
|
}
|
|
128
|
+
if (typeof cellValue === 'string' && options.escapeFormulas) {
|
|
129
|
+
// See https://owasp.org/www-community/attacks/CSV_Injection
|
|
130
|
+
if (['=', '+', '-', '@', '\t', '\r'].includes(cellValue[0])) {
|
|
131
|
+
cellValue = `'${cellValue}`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (typeof cellValue !== 'undefined') {
|
|
135
|
+
row[column.field] = cellValue;
|
|
136
|
+
}
|
|
127
137
|
});
|
|
128
138
|
return {
|
|
129
139
|
row,
|
|
@@ -316,7 +326,7 @@ export async function buildExcel(options, api) {
|
|
|
316
326
|
const valueOptionsData = await getDataForValueOptionsSheet(columns, valueOptionsSheetName, api);
|
|
317
327
|
createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
|
|
318
328
|
rowIds.forEach(id => {
|
|
319
|
-
const serializedRow = serializeRow(id, columns, api, valueOptionsData);
|
|
329
|
+
const serializedRow = serializeRow(id, columns, api, valueOptionsData, options);
|
|
320
330
|
addSerializedRowToWorksheet(serializedRow, worksheet);
|
|
321
331
|
});
|
|
322
332
|
if (exceljsPostProcess) {
|
|
@@ -17,7 +17,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
17
17
|
export const useGridExcelExport = (apiRef, props) => {
|
|
18
18
|
const logger = useGridLogger(apiRef, 'useGridExcelExport');
|
|
19
19
|
const getDataAsExcel = React.useCallback((options = {}) => {
|
|
20
|
-
var _options$getRowsToExp, _options$includeHeade, _options$includeColum;
|
|
20
|
+
var _options$getRowsToExp, _options$includeHeade, _options$includeColum, _options$escapeFormul;
|
|
21
21
|
logger.debug(`Get data as excel`);
|
|
22
22
|
const getRowsToExport = (_options$getRowsToExp = options.getRowsToExport) != null ? _options$getRowsToExp : defaultGetRowsToExport;
|
|
23
23
|
const exportedRowIds = getRowsToExport({
|
|
@@ -35,7 +35,8 @@ export const useGridExcelExport = (apiRef, props) => {
|
|
|
35
35
|
valueOptionsSheetName: (options == null ? void 0 : options.valueOptionsSheetName) || 'Options',
|
|
36
36
|
columnsStyles: options == null ? void 0 : options.columnsStyles,
|
|
37
37
|
exceljsPreProcess: options == null ? void 0 : options.exceljsPreProcess,
|
|
38
|
-
exceljsPostProcess: options == null ? void 0 : options.exceljsPostProcess
|
|
38
|
+
exceljsPostProcess: options == null ? void 0 : options.exceljsPostProcess,
|
|
39
|
+
escapeFormulas: (_options$escapeFormul = options.escapeFormulas) != null ? _options$escapeFormul : true
|
|
39
40
|
}, apiRef.current);
|
|
40
41
|
}, [logger, apiRef]);
|
|
41
42
|
const exportDataAsExcel = React.useCallback(async (options = {}) => {
|
|
@@ -86,7 +87,12 @@ export const useGridExcelExport = (apiRef, props) => {
|
|
|
86
87
|
});
|
|
87
88
|
const valueOptionsData = await getDataForValueOptionsSheet(exportedColumns, valueOptionsSheetName, apiRef.current);
|
|
88
89
|
const serializedColumns = serializeColumns(exportedColumns, options.columnsStyles || {});
|
|
89
|
-
const serializedRows = exportedRowIds.map(id =>
|
|
90
|
+
const serializedRows = exportedRowIds.map(id => {
|
|
91
|
+
var _options$escapeFormul2;
|
|
92
|
+
return serializeRow(id, exportedColumns, apiRef.current, valueOptionsData, {
|
|
93
|
+
escapeFormulas: (_options$escapeFormul2 = options.escapeFormulas) != null ? _options$escapeFormul2 : true
|
|
94
|
+
});
|
|
95
|
+
});
|
|
90
96
|
const columnGroupPaths = exportedColumns.reduce((acc, column) => {
|
|
91
97
|
acc[column.field] = apiRef.current.unstable_getColumnGroupPath(column.field);
|
|
92
98
|
return acc;
|
package/index.js
CHANGED
|
@@ -30,6 +30,7 @@ process.env.NODE_ENV !== "production" ? GridExcelExportMenuItem.propTypes = {
|
|
|
30
30
|
allColumns: PropTypes.bool,
|
|
31
31
|
columnsStyles: PropTypes.object,
|
|
32
32
|
disableToolbarButton: PropTypes.bool,
|
|
33
|
+
escapeFormulas: PropTypes.bool,
|
|
33
34
|
exceljsPostProcess: PropTypes.func,
|
|
34
35
|
exceljsPreProcess: PropTypes.func,
|
|
35
36
|
fields: PropTypes.arrayOf(PropTypes.string),
|
|
@@ -441,9 +441,12 @@ export var useGridCellSelection = function useGridCellSelection(apiRef, props) {
|
|
|
441
441
|
if (fieldsMap[field]) {
|
|
442
442
|
var cellParams = apiRef.current.getCellParams(rowId, field);
|
|
443
443
|
cellData = serializeCellValue(cellParams, {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
csvOptions: {
|
|
445
|
+
delimiter: clipboardCopyCellDelimiter,
|
|
446
|
+
shouldAppendQuotes: false,
|
|
447
|
+
escapeFormulas: false
|
|
448
|
+
},
|
|
449
|
+
ignoreValueFormatter: ignoreValueFormatter
|
|
447
450
|
});
|
|
448
451
|
} else {
|
|
449
452
|
cellData = '';
|
|
@@ -52,7 +52,7 @@ var getFormattedValueOptions = function getFormattedValueOptions(colDef, valueOp
|
|
|
52
52
|
return _typeof(option) === 'object' ? option.label : option;
|
|
53
53
|
});
|
|
54
54
|
};
|
|
55
|
-
export var serializeRow = function serializeRow(id, columns, api, defaultValueOptionsFormulae) {
|
|
55
|
+
export var serializeRow = function serializeRow(id, columns, api, defaultValueOptionsFormulae, options) {
|
|
56
56
|
var row = {};
|
|
57
57
|
var dataValidation = {};
|
|
58
58
|
var mergedCells = [];
|
|
@@ -78,6 +78,7 @@ export var serializeRow = function serializeRow(id, columns, api, defaultValueOp
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
var cellParams = api.getCellParams(id, column.field);
|
|
81
|
+
var cellValue;
|
|
81
82
|
switch (cellParams.colDef.type) {
|
|
82
83
|
case 'singleSelect':
|
|
83
84
|
{
|
|
@@ -123,7 +124,7 @@ export var serializeRow = function serializeRow(id, columns, api, defaultValueOp
|
|
|
123
124
|
}
|
|
124
125
|
case 'boolean':
|
|
125
126
|
case 'number':
|
|
126
|
-
|
|
127
|
+
cellValue = api.getCellParams(id, column.field).value;
|
|
127
128
|
break;
|
|
128
129
|
case 'date':
|
|
129
130
|
case 'dateTime':
|
|
@@ -143,7 +144,7 @@ export var serializeRow = function serializeRow(id, columns, api, defaultValueOp
|
|
|
143
144
|
case 'actions':
|
|
144
145
|
break;
|
|
145
146
|
default:
|
|
146
|
-
|
|
147
|
+
cellValue = api.getCellParams(id, column.field).formattedValue;
|
|
147
148
|
if (process.env.NODE_ENV !== 'production') {
|
|
148
149
|
if (String(cellParams.formattedValue) === '[object Object]') {
|
|
149
150
|
warnInvalidFormattedValue();
|
|
@@ -151,6 +152,15 @@ export var serializeRow = function serializeRow(id, columns, api, defaultValueOp
|
|
|
151
152
|
}
|
|
152
153
|
break;
|
|
153
154
|
}
|
|
155
|
+
if (typeof cellValue === 'string' && options.escapeFormulas) {
|
|
156
|
+
// See https://owasp.org/www-community/attacks/CSV_Injection
|
|
157
|
+
if (['=', '+', '-', '@', '\t', '\r'].includes(cellValue[0])) {
|
|
158
|
+
cellValue = "'".concat(cellValue);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (typeof cellValue !== 'undefined') {
|
|
162
|
+
row[column.field] = cellValue;
|
|
163
|
+
}
|
|
154
164
|
});
|
|
155
165
|
return {
|
|
156
166
|
row: row,
|
|
@@ -392,7 +402,7 @@ function _buildExcel() {
|
|
|
392
402
|
valueOptionsData = _context5.sent;
|
|
393
403
|
createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
|
|
394
404
|
rowIds.forEach(function (id) {
|
|
395
|
-
var serializedRow = serializeRow(id, columns, api, valueOptionsData);
|
|
405
|
+
var serializedRow = serializeRow(id, columns, api, valueOptionsData, options);
|
|
396
406
|
addSerializedRowToWorksheet(serializedRow, worksheet);
|
|
397
407
|
});
|
|
398
408
|
if (!exceljsPostProcess) {
|
|
@@ -20,7 +20,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
20
20
|
export var useGridExcelExport = function useGridExcelExport(apiRef, props) {
|
|
21
21
|
var logger = useGridLogger(apiRef, 'useGridExcelExport');
|
|
22
22
|
var getDataAsExcel = React.useCallback(function () {
|
|
23
|
-
var _options$getRowsToExp, _options$includeHeade, _options$includeColum;
|
|
23
|
+
var _options$getRowsToExp, _options$includeHeade, _options$includeColum, _options$escapeFormul;
|
|
24
24
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
25
25
|
logger.debug("Get data as excel");
|
|
26
26
|
var getRowsToExport = (_options$getRowsToExp = options.getRowsToExport) != null ? _options$getRowsToExp : defaultGetRowsToExport;
|
|
@@ -39,7 +39,8 @@ export var useGridExcelExport = function useGridExcelExport(apiRef, props) {
|
|
|
39
39
|
valueOptionsSheetName: (options == null ? void 0 : options.valueOptionsSheetName) || 'Options',
|
|
40
40
|
columnsStyles: options == null ? void 0 : options.columnsStyles,
|
|
41
41
|
exceljsPreProcess: options == null ? void 0 : options.exceljsPreProcess,
|
|
42
|
-
exceljsPostProcess: options == null ? void 0 : options.exceljsPostProcess
|
|
42
|
+
exceljsPostProcess: options == null ? void 0 : options.exceljsPostProcess,
|
|
43
|
+
escapeFormulas: (_options$escapeFormul = options.escapeFormulas) != null ? _options$escapeFormul : true
|
|
43
44
|
}, apiRef.current);
|
|
44
45
|
}, [logger, apiRef]);
|
|
45
46
|
var exportDataAsExcel = React.useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
@@ -139,7 +140,10 @@ export var useGridExcelExport = function useGridExcelExport(apiRef, props) {
|
|
|
139
140
|
valueOptionsData = _context2.sent;
|
|
140
141
|
serializedColumns = serializeColumns(exportedColumns, options.columnsStyles || {});
|
|
141
142
|
serializedRows = exportedRowIds.map(function (id) {
|
|
142
|
-
|
|
143
|
+
var _options$escapeFormul2;
|
|
144
|
+
return serializeRow(id, exportedColumns, apiRef.current, valueOptionsData, {
|
|
145
|
+
escapeFormulas: (_options$escapeFormul2 = options.escapeFormulas) != null ? _options$escapeFormul2 : true
|
|
146
|
+
});
|
|
143
147
|
});
|
|
144
148
|
columnGroupPaths = exportedColumns.reduce(function (acc, column) {
|
|
145
149
|
acc[column.field] = apiRef.current.unstable_getColumnGroupPath(column.field);
|
package/legacy/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ponyfillGlobal } from '@mui/utils';
|
|
2
2
|
export var getReleaseInfo = function getReleaseInfo() {
|
|
3
|
-
var releaseInfo = "
|
|
3
|
+
var releaseInfo = "MTcxNjUwMTYwMDAwMA==";
|
|
4
4
|
if (process.env.NODE_ENV !== 'production') {
|
|
5
5
|
// A simple hack to set the value in the test environment (has no build step).
|
|
6
6
|
// eslint-disable-next-line no-useless-concat
|
|
@@ -32,6 +32,7 @@ process.env.NODE_ENV !== "production" ? GridExcelExportMenuItem.propTypes = {
|
|
|
32
32
|
allColumns: PropTypes.bool,
|
|
33
33
|
columnsStyles: PropTypes.object,
|
|
34
34
|
disableToolbarButton: PropTypes.bool,
|
|
35
|
+
escapeFormulas: PropTypes.bool,
|
|
35
36
|
exceljsPostProcess: PropTypes.func,
|
|
36
37
|
exceljsPreProcess: PropTypes.func,
|
|
37
38
|
fields: PropTypes.arrayOf(PropTypes.string),
|
|
@@ -446,9 +446,12 @@ export const useGridCellSelection = (apiRef, props) => {
|
|
|
446
446
|
if (fieldsMap[field]) {
|
|
447
447
|
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
448
448
|
cellData = serializeCellValue(cellParams, {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
449
|
+
csvOptions: {
|
|
450
|
+
delimiter: clipboardCopyCellDelimiter,
|
|
451
|
+
shouldAppendQuotes: false,
|
|
452
|
+
escapeFormulas: false
|
|
453
|
+
},
|
|
454
|
+
ignoreValueFormatter
|
|
452
455
|
});
|
|
453
456
|
} else {
|
|
454
457
|
cellData = '';
|
|
@@ -26,7 +26,7 @@ const getFormattedValueOptions = (colDef, valueOptions, api) => {
|
|
|
26
26
|
}
|
|
27
27
|
return valueOptionsFormatted.map(option => typeof option === 'object' ? option.label : option);
|
|
28
28
|
};
|
|
29
|
-
export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
29
|
+
export const serializeRow = (id, columns, api, defaultValueOptionsFormulae, options) => {
|
|
30
30
|
const row = {};
|
|
31
31
|
const dataValidation = {};
|
|
32
32
|
const mergedCells = [];
|
|
@@ -52,6 +52,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
const cellParams = api.getCellParams(id, column.field);
|
|
55
|
+
let cellValue;
|
|
55
56
|
switch (cellParams.colDef.type) {
|
|
56
57
|
case 'singleSelect':
|
|
57
58
|
{
|
|
@@ -95,7 +96,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
95
96
|
}
|
|
96
97
|
case 'boolean':
|
|
97
98
|
case 'number':
|
|
98
|
-
|
|
99
|
+
cellValue = api.getCellParams(id, column.field).value;
|
|
99
100
|
break;
|
|
100
101
|
case 'date':
|
|
101
102
|
case 'dateTime':
|
|
@@ -115,7 +116,7 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
115
116
|
case 'actions':
|
|
116
117
|
break;
|
|
117
118
|
default:
|
|
118
|
-
|
|
119
|
+
cellValue = api.getCellParams(id, column.field).formattedValue;
|
|
119
120
|
if (process.env.NODE_ENV !== 'production') {
|
|
120
121
|
if (String(cellParams.formattedValue) === '[object Object]') {
|
|
121
122
|
warnInvalidFormattedValue();
|
|
@@ -123,6 +124,15 @@ export const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
123
124
|
}
|
|
124
125
|
break;
|
|
125
126
|
}
|
|
127
|
+
if (typeof cellValue === 'string' && options.escapeFormulas) {
|
|
128
|
+
// See https://owasp.org/www-community/attacks/CSV_Injection
|
|
129
|
+
if (['=', '+', '-', '@', '\t', '\r'].includes(cellValue[0])) {
|
|
130
|
+
cellValue = `'${cellValue}`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (typeof cellValue !== 'undefined') {
|
|
134
|
+
row[column.field] = cellValue;
|
|
135
|
+
}
|
|
126
136
|
});
|
|
127
137
|
return {
|
|
128
138
|
row,
|
|
@@ -304,7 +314,7 @@ export async function buildExcel(options, api) {
|
|
|
304
314
|
const valueOptionsData = await getDataForValueOptionsSheet(columns, valueOptionsSheetName, api);
|
|
305
315
|
createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
|
|
306
316
|
rowIds.forEach(id => {
|
|
307
|
-
const serializedRow = serializeRow(id, columns, api, valueOptionsData);
|
|
317
|
+
const serializedRow = serializeRow(id, columns, api, valueOptionsData, options);
|
|
308
318
|
addSerializedRowToWorksheet(serializedRow, worksheet);
|
|
309
319
|
});
|
|
310
320
|
if (exceljsPostProcess) {
|
|
@@ -34,7 +34,8 @@ export const useGridExcelExport = (apiRef, props) => {
|
|
|
34
34
|
valueOptionsSheetName: options?.valueOptionsSheetName || 'Options',
|
|
35
35
|
columnsStyles: options?.columnsStyles,
|
|
36
36
|
exceljsPreProcess: options?.exceljsPreProcess,
|
|
37
|
-
exceljsPostProcess: options?.exceljsPostProcess
|
|
37
|
+
exceljsPostProcess: options?.exceljsPostProcess,
|
|
38
|
+
escapeFormulas: options.escapeFormulas ?? true
|
|
38
39
|
}, apiRef.current);
|
|
39
40
|
}, [logger, apiRef]);
|
|
40
41
|
const exportDataAsExcel = React.useCallback(async (options = {}) => {
|
|
@@ -85,7 +86,9 @@ export const useGridExcelExport = (apiRef, props) => {
|
|
|
85
86
|
});
|
|
86
87
|
const valueOptionsData = await getDataForValueOptionsSheet(exportedColumns, valueOptionsSheetName, apiRef.current);
|
|
87
88
|
const serializedColumns = serializeColumns(exportedColumns, options.columnsStyles || {});
|
|
88
|
-
const serializedRows = exportedRowIds.map(id => serializeRow(id, exportedColumns, apiRef.current, valueOptionsData
|
|
89
|
+
const serializedRows = exportedRowIds.map(id => serializeRow(id, exportedColumns, apiRef.current, valueOptionsData, {
|
|
90
|
+
escapeFormulas: options.escapeFormulas ?? true
|
|
91
|
+
}));
|
|
89
92
|
const columnGroupPaths = exportedColumns.reduce((acc, column) => {
|
|
90
93
|
acc[column.field] = apiRef.current.unstable_getColumnGroupPath(column.field);
|
|
91
94
|
return acc;
|
package/modern/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ponyfillGlobal } from '@mui/utils';
|
|
2
2
|
export const getReleaseInfo = () => {
|
|
3
|
-
const releaseInfo = "
|
|
3
|
+
const releaseInfo = "MTcxNjUwMTYwMDAwMA==";
|
|
4
4
|
if (process.env.NODE_ENV !== 'production') {
|
|
5
5
|
// A simple hack to set the value in the test environment (has no build step).
|
|
6
6
|
// eslint-disable-next-line no-useless-concat
|
|
@@ -41,6 +41,7 @@ process.env.NODE_ENV !== "production" ? GridExcelExportMenuItem.propTypes = {
|
|
|
41
41
|
allColumns: _propTypes.default.bool,
|
|
42
42
|
columnsStyles: _propTypes.default.object,
|
|
43
43
|
disableToolbarButton: _propTypes.default.bool,
|
|
44
|
+
escapeFormulas: _propTypes.default.bool,
|
|
44
45
|
exceljsPostProcess: _propTypes.default.func,
|
|
45
46
|
exceljsPreProcess: _propTypes.default.func,
|
|
46
47
|
fields: _propTypes.default.arrayOf(_propTypes.default.string),
|
|
@@ -456,9 +456,12 @@ const useGridCellSelection = (apiRef, props) => {
|
|
|
456
456
|
if (fieldsMap[field]) {
|
|
457
457
|
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
458
458
|
cellData = (0, _internals.serializeCellValue)(cellParams, {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
459
|
+
csvOptions: {
|
|
460
|
+
delimiter: clipboardCopyCellDelimiter,
|
|
461
|
+
shouldAppendQuotes: false,
|
|
462
|
+
escapeFormulas: false
|
|
463
|
+
},
|
|
464
|
+
ignoreValueFormatter
|
|
462
465
|
});
|
|
463
466
|
} else {
|
|
464
467
|
cellData = '';
|
|
@@ -40,7 +40,7 @@ const getFormattedValueOptions = (colDef, valueOptions, api) => {
|
|
|
40
40
|
}
|
|
41
41
|
return valueOptionsFormatted.map(option => typeof option === 'object' ? option.label : option);
|
|
42
42
|
};
|
|
43
|
-
const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
43
|
+
const serializeRow = (id, columns, api, defaultValueOptionsFormulae, options) => {
|
|
44
44
|
const row = {};
|
|
45
45
|
const dataValidation = {};
|
|
46
46
|
const mergedCells = [];
|
|
@@ -66,6 +66,7 @@ const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
const cellParams = api.getCellParams(id, column.field);
|
|
69
|
+
let cellValue;
|
|
69
70
|
switch (cellParams.colDef.type) {
|
|
70
71
|
case 'singleSelect':
|
|
71
72
|
{
|
|
@@ -109,7 +110,7 @@ const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
109
110
|
}
|
|
110
111
|
case 'boolean':
|
|
111
112
|
case 'number':
|
|
112
|
-
|
|
113
|
+
cellValue = api.getCellParams(id, column.field).value;
|
|
113
114
|
break;
|
|
114
115
|
case 'date':
|
|
115
116
|
case 'dateTime':
|
|
@@ -129,7 +130,7 @@ const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
129
130
|
case 'actions':
|
|
130
131
|
break;
|
|
131
132
|
default:
|
|
132
|
-
|
|
133
|
+
cellValue = api.getCellParams(id, column.field).formattedValue;
|
|
133
134
|
if (process.env.NODE_ENV !== 'production') {
|
|
134
135
|
if (String(cellParams.formattedValue) === '[object Object]') {
|
|
135
136
|
warnInvalidFormattedValue();
|
|
@@ -137,6 +138,15 @@ const serializeRow = (id, columns, api, defaultValueOptionsFormulae) => {
|
|
|
137
138
|
}
|
|
138
139
|
break;
|
|
139
140
|
}
|
|
141
|
+
if (typeof cellValue === 'string' && options.escapeFormulas) {
|
|
142
|
+
// See https://owasp.org/www-community/attacks/CSV_Injection
|
|
143
|
+
if (['=', '+', '-', '@', '\t', '\r'].includes(cellValue[0])) {
|
|
144
|
+
cellValue = `'${cellValue}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (typeof cellValue !== 'undefined') {
|
|
148
|
+
row[column.field] = cellValue;
|
|
149
|
+
}
|
|
140
150
|
});
|
|
141
151
|
return {
|
|
142
152
|
row,
|
|
@@ -320,7 +330,7 @@ async function buildExcel(options, api) {
|
|
|
320
330
|
const valueOptionsData = await getDataForValueOptionsSheet(columns, valueOptionsSheetName, api);
|
|
321
331
|
createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
|
|
322
332
|
rowIds.forEach(id => {
|
|
323
|
-
const serializedRow = serializeRow(id, columns, api, valueOptionsData);
|
|
333
|
+
const serializedRow = serializeRow(id, columns, api, valueOptionsData, options);
|
|
324
334
|
addSerializedRowToWorksheet(serializedRow, worksheet);
|
|
325
335
|
});
|
|
326
336
|
if (exceljsPostProcess) {
|
|
@@ -42,7 +42,8 @@ const useGridExcelExport = (apiRef, props) => {
|
|
|
42
42
|
valueOptionsSheetName: options?.valueOptionsSheetName || 'Options',
|
|
43
43
|
columnsStyles: options?.columnsStyles,
|
|
44
44
|
exceljsPreProcess: options?.exceljsPreProcess,
|
|
45
|
-
exceljsPostProcess: options?.exceljsPostProcess
|
|
45
|
+
exceljsPostProcess: options?.exceljsPostProcess,
|
|
46
|
+
escapeFormulas: options.escapeFormulas ?? true
|
|
46
47
|
}, apiRef.current);
|
|
47
48
|
}, [logger, apiRef]);
|
|
48
49
|
const exportDataAsExcel = React.useCallback(async (options = {}) => {
|
|
@@ -93,7 +94,9 @@ const useGridExcelExport = (apiRef, props) => {
|
|
|
93
94
|
});
|
|
94
95
|
const valueOptionsData = await (0, _excelSerializer.getDataForValueOptionsSheet)(exportedColumns, valueOptionsSheetName, apiRef.current);
|
|
95
96
|
const serializedColumns = (0, _excelSerializer.serializeColumns)(exportedColumns, options.columnsStyles || {});
|
|
96
|
-
const serializedRows = exportedRowIds.map(id => (0, _excelSerializer.serializeRow)(id, exportedColumns, apiRef.current, valueOptionsData
|
|
97
|
+
const serializedRows = exportedRowIds.map(id => (0, _excelSerializer.serializeRow)(id, exportedColumns, apiRef.current, valueOptionsData, {
|
|
98
|
+
escapeFormulas: options.escapeFormulas ?? true
|
|
99
|
+
}));
|
|
97
100
|
const columnGroupPaths = exportedColumns.reduce((acc, column) => {
|
|
98
101
|
acc[column.field] = apiRef.current.unstable_getColumnGroupPath(column.field);
|
|
99
102
|
return acc;
|
package/node/index.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getReleaseInfo = void 0;
|
|
7
7
|
var _utils = require("@mui/utils");
|
|
8
8
|
const getReleaseInfo = () => {
|
|
9
|
-
const releaseInfo = "
|
|
9
|
+
const releaseInfo = "MTcxNjUwMTYwMDAwMA==";
|
|
10
10
|
if (process.env.NODE_ENV !== 'production') {
|
|
11
11
|
// A simple hack to set the value in the test environment (has no build step).
|
|
12
12
|
// eslint-disable-next-line no-useless-concat
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/x-data-grid-premium",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.20.0",
|
|
4
4
|
"description": "The Premium plan edition of the data grid component (MUI X).",
|
|
5
5
|
"author": "MUI Team",
|
|
6
6
|
"main": "./node/index.js",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@babel/runtime": "^7.23.2",
|
|
35
35
|
"@mui/utils": "^5.14.16",
|
|
36
|
-
"@mui/x-data-grid": "6.
|
|
37
|
-
"@mui/x-data-grid-pro": "6.
|
|
36
|
+
"@mui/x-data-grid": "6.20.0",
|
|
37
|
+
"@mui/x-data-grid-pro": "6.20.0",
|
|
38
38
|
"@mui/x-license-pro": "6.10.2",
|
|
39
39
|
"@types/format-util": "^1.0.3",
|
|
40
40
|
"clsx": "^2.0.0",
|
package/utils/releaseInfo.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ponyfillGlobal } from '@mui/utils';
|
|
2
2
|
export const getReleaseInfo = () => {
|
|
3
|
-
const releaseInfo = "
|
|
3
|
+
const releaseInfo = "MTcxNjUwMTYwMDAwMA==";
|
|
4
4
|
if (process.env.NODE_ENV !== 'production') {
|
|
5
5
|
// A simple hack to set the value in the test environment (has no build step).
|
|
6
6
|
// eslint-disable-next-line no-useless-concat
|