diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index ab18605dd9e86..e7f4b0737df0d 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -18,6 +18,7 @@ import { GridColDef } from '@material-ui/data-grid'; | cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | | description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | | disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | +| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | | editable? | boolean | false
| If `true`, the cells of the column are editable. | | field | string | | The column identifier. It's used to map with GridRowData values. | | filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | diff --git a/docs/src/pages/components/data-grid/export/export.md b/docs/src/pages/components/data-grid/export/export.md index 7c3629d1a63c8..0370e48af1f41 100644 --- a/docs/src/pages/components/data-grid/export/export.md +++ b/docs/src/pages/components/data-grid/export/export.md @@ -14,6 +14,29 @@ To enable the CSV export you need to compose a toolbar containing the `GridToolb {{"demo": "pages/components/data-grid/export/ExportSelectorGrid.js", "bg": "inline"}} +### Customize exported columns + +By default, the CSV will only contain the visible columns of the grid. +To include or hide other columns, there are two ways: + +1. Define the exact columns to be exported with the `fields` attribute in the `csvOptions` prop of [`GridToolbarExport`](/components/data-grid/components/#toolbar). + +```jsx + +``` + +Set `allColumns` in `csvOptions` to true to include hidden columns, instead of only the visible ones. + +```jsx + +``` + +2. Set the `disableExport` attribute to true in each `GridColDef`. + +```jsx + +``` + ## 🚧 Print > ⚠️ This feature isn't implemented yet. It's coming. diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index c4f3c619d88e3..a298326655c52 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridSelector } from '../core/useGridSelector'; -import { visibleGridColumnsSelector } from '../columns'; +import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; import { visibleSortedGridRowsSelector } from '../filter'; import { gridSelectionStateSelector } from '../selection'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; @@ -10,10 +10,12 @@ import { GridExportCsvOptions } from '../../../models/gridExport'; import { useLogger } from '../../utils/useLogger'; import { exportAs } from '../../../utils'; import { buildCSV } from './serializers/csvSerializer'; +import { GridColDef } from '../../../models'; export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useLogger('useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + const columns = useGridSelector(apiRef, allGridColumnsSelector); const visibleSortedRows = useGridSelector(apiRef, visibleSortedGridRowsSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); @@ -21,15 +23,27 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { (options?: GridExportCsvOptions): string => { logger.debug(`Get data as CSV`); + let exportedColumns: GridColDef[]; + + if (options?.fields) { + exportedColumns = options.fields + .map((field) => columns.find((column) => column.field === field)) + .filter((column): column is GridColDef => !!column); + } else { + const validColumns = options?.allColumns ? columns : visibleColumns; + + exportedColumns = validColumns.filter((column) => !column.disableExport); + } + return buildCSV({ - columns: visibleColumns, + columns: exportedColumns, rows: visibleSortedRows, selectedRowIds: selection, getCellParams: apiRef.current.getCellParams, delimiterCharacter: options?.delimiter || ',', }); }, - [logger, visibleColumns, visibleSortedRows, selection, apiRef], + [logger, visibleColumns, columns, visibleSortedRows, selection, apiRef], ); const exportDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index c2670a8b952f1..360de0f4d572c 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -143,6 +143,11 @@ export interface GridColDef { * Allows setting the filter operators for this column. */ filterOperators?: GridFilterOperator[]; + /** + * If `true`, this column will not be included in exports. + * @default false + */ + disableExport?: boolean; } export interface GridColumnProp extends Omit { diff --git a/packages/grid/_modules_/grid/models/gridExport.ts b/packages/grid/_modules_/grid/models/gridExport.ts index fbd0fee21f3f2..f7d1bd8881035 100644 --- a/packages/grid/_modules_/grid/models/gridExport.ts +++ b/packages/grid/_modules_/grid/models/gridExport.ts @@ -23,6 +23,16 @@ export interface GridExportCsvOptions { * @default false */ utf8WithBom?: boolean; + /** + * The columns exported in the CSV. + * This should only be used if you want to restrict the columns exports. + */ + fields?: string[]; + /** + * If `true`, the hidden columns will also be exported. + * @default false + */ + allColumns?: boolean; } /** diff --git a/packages/grid/x-grid/src/tests/export.XGrid.test.tsx b/packages/grid/x-grid/src/tests/export.XGrid.test.tsx index b23c58c1fff1d..cdfe15d860cab 100644 --- a/packages/grid/x-grid/src/tests/export.XGrid.test.tsx +++ b/packages/grid/x-grid/src/tests/export.XGrid.test.tsx @@ -1,4 +1,4 @@ -import { GridApiRef, useGridApiRef, XGrid } from '@material-ui/x-grid'; +import { GridApiRef, GridColumns, useGridApiRef, XGrid } from '@material-ui/x-grid'; import { expect } from 'chai'; import * as React from 'react'; import { createClientRenderStrictMode } from 'test/utils'; @@ -15,177 +15,341 @@ describe(' - Export', () => { let apiRef: GridApiRef; - const columns = [{ field: 'id' }, { field: 'brand', headerName: 'Brand' }]; - - it('getDataAsCsv should work with basic strings', () => { - const TestCaseCSVExport = () => { - apiRef = useGridApiRef(); - return ( -
- -
- ); - }; - - render(); - expect(apiRef.current.getDataAsCsv()).to.equal( - ['id,Brand', '0,Nike', '1,Adidas', '2,Puma'].join('\r\n'), - ); - apiRef.current.updateRows([ - { - id: 1, - brand: 'Adidas,Reebok', - }, - ]); - expect(apiRef.current.getDataAsCsv()).to.equal( - ['id,Brand', '0,Nike', '1,"Adidas,Reebok"', '2,Puma'].join('\r\n'), - ); - }); + const columns: GridColumns = [{ field: 'id' }, { field: 'brand', headerName: 'Brand' }]; + + describe('getDataAsCsv', () => { + it('should work with basic strings', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; - it('getDataAsCsv should work with comma', () => { - const TestCaseCSVExport = () => { - apiRef = useGridApiRef(); - return ( -
- -
+ render(); + expect(apiRef.current.getDataAsCsv()).to.equal( + ['id,Brand', '0,Nike', '1,Adidas', '2,Puma'].join('\r\n'), ); - }; + apiRef.current.updateRows([ + { + id: 1, + brand: 'Adidas,Reebok', + }, + ]); + expect(apiRef.current.getDataAsCsv()).to.equal( + ['id,Brand', '0,Nike', '1,"Adidas,Reebok"', '2,Puma'].join('\r\n'), + ); + }); - render(); - expect(apiRef.current.getDataAsCsv()).to.equal( - ['id,Brand', '0,Nike', '1,"Adidas,Puma"'].join('\r\n'), - ); - }); + it('should work with comma', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; - it('getDataAsCsv should apply valueFormatter correctly', () => { - const TestCaseCSVExport = () => { - apiRef = useGridApiRef(); - return ( -
- (params.value === 'Nike' ? 'Jordan' : params.value), - }, - ]} - rows={[ - { - id: 0, - brand: 'Nike', - }, - { - id: 1, - brand: 'Adidas', - }, - ]} - /> -
+ render(); + expect(apiRef.current.getDataAsCsv()).to.equal( + ['id,Brand', '0,Nike', '1,"Adidas,Puma"'].join('\r\n'), ); - }; + }); - render(); - expect(apiRef.current.getDataAsCsv()).to.equal( - ['id,Brand', '0,Jordan', '1,Adidas'].join('\r\n'), - ); - }); + it('should apply valueFormatter correctly', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ (params.value === 'Nike' ? 'Jordan' : params.value), + }, + ]} + rows={[ + { + id: 0, + brand: 'Nike', + }, + { + id: 1, + brand: 'Adidas', + }, + ]} + /> +
+ ); + }; - it('getDataAsCsv should work with double quotes', () => { - const TestCaseCSVExport = () => { - apiRef = useGridApiRef(); - return ( -
- -
+ render(); + expect(apiRef.current.getDataAsCsv()).to.equal( + ['id,Brand', '0,Jordan', '1,Adidas'].join('\r\n'), ); - }; + }); - render(); - expect(apiRef.current.getDataAsCsv()).to.equal( - ['id,Brand', '0,Nike', '1,Samsung 24"" (inches)'].join('\r\n'), - ); - }); + it('should work with double quotes', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; - it('getDataAsCsv should allow to change the delimiter', () => { - const TestCaseCSVExport = () => { - apiRef = useGridApiRef(); - return ( -
- -
+ render(); + expect(apiRef.current.getDataAsCsv()).to.equal( + ['id,Brand', '0,Nike', '1,Samsung 24"" (inches)'].join('\r\n'), ); - }; - - render(); - expect( - apiRef.current.getDataAsCsv({ - delimiter: ';', - }), - ).to.equal(['id;Brand', '0;Nike', '1;Adidas'].join('\r\n')); + }); + + it('should allow to change the delimiter', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect( + apiRef.current.getDataAsCsv({ + delimiter: ';', + }), + ).to.equal(['id;Brand', '0;Nike', '1;Adidas'].join('\r\n')); + }); + + it('should not export hidden column', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect(apiRef.current.getDataAsCsv()).to.equal(['id', '0', '1'].join('\r\n')); + }); + + it('should export hidden column if params.allColumns = true', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect( + apiRef.current.getDataAsCsv({ + allColumns: true, + }), + ).to.equal(['id,Brand', '0,Nike', '1,Adidas'].join('\r\n')); + }); + + it('should not export columns with column.disableExport = true', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect( + apiRef.current.getDataAsCsv({ + fields: ['brand'], + }), + ).to.equal(['Brand', 'Nike', 'Adidas'].join('\r\n')); + }); + + it('should only export columns in params.fields if defined', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect( + apiRef.current.getDataAsCsv({ + fields: ['brand'], + }), + ).to.equal(['Brand', 'Nike', 'Adidas'].join('\r\n')); + }); + + it('should export column defined in params.fields even if column.hide=true or column.disableExport=true', () => { + const TestCaseCSVExport = () => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + render(); + expect( + apiRef.current.getDataAsCsv({ + fields: ['id', 'brand'], + }), + ).to.equal(['id,Brand', '0,Nike', '1,Adidas'].join('\r\n')); + }); }); });