From 2ab6b49ecab754862c31b8d6f2e78818006da188 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:45:07 +0100 Subject: [PATCH] [DataGrid] Fix cell value formatting on copy (@sai6855) (#12483) Co-authored-by: sai chand <60743144+sai6855@users.noreply.github.com> Co-authored-by: Andrew Cherniavskyi --- .../cellSelection/useGridCellSelection.ts | 1 + .../features/clipboard/useGridClipboard.ts | 2 + .../export/serializers/csvSerializer.ts | 49 ++++++++++++++----- .../features/export/useGridCsvExport.tsx | 1 + .../grid/x-data-grid/src/models/gridExport.ts | 6 +++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts b/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts index 5b1e3978e43e..0ffe30c6ef99 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts @@ -565,6 +565,7 @@ export const useGridCellSelection = ( cellData = serializeCellValue(cellParams, { delimiterCharacter: clipboardCopyCellDelimiter, ignoreValueFormatter, + shouldAppendQuotes: true, }); } else { cellData = ''; diff --git a/packages/grid/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts index 38cc084886e0..17a633ae926a 100644 --- a/packages/grid/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts @@ -90,6 +90,7 @@ export const useGridClipboard = ( includeHeaders: false, // TODO: make it configurable delimiter: clipboardCopyCellDelimiter, + shouldAppendQuotes: false, }); } else { const focusedCell = gridFocusCellSelector(apiRef); @@ -98,6 +99,7 @@ export const useGridClipboard = ( textToCopy = serializeCellValue(cellParams, { delimiterCharacter: clipboardCopyCellDelimiter, ignoreValueFormatter, + shouldAppendQuotes: false, }); } } diff --git a/packages/grid/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts index 16c0038558eb..88b45fa2db8f 100644 --- a/packages/grid/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts @@ -5,11 +5,14 @@ import type { GridStateColDef } from '../../../../models/colDef/gridColDef'; import type { GridApiCommunity } from '../../../../models/api/gridApiCommunity'; import { buildWarning } from '../../../../utils/warning'; -function sanitizeCellValue(value: any, delimiterCharacter: string) { +function sanitizeCellValue(value: any, delimiterCharacter: string, shouldAppendQuotes: boolean) { if (typeof value === 'string') { // Make sure value containing delimiter or line break won't be split into multiple rows if ([delimiterCharacter, '\n', '\r', '"'].some((delimiter) => value.includes(delimiter))) { - return `"${value.replace(/"/g, '""')}"`; + if (shouldAppendQuotes) { + return `"${value.replace(/"/g, '""')}"`; + } + return `${value.replace(/"/g, '""')}`; } return value; @@ -20,9 +23,13 @@ function sanitizeCellValue(value: any, delimiterCharacter: string) { export const serializeCellValue = ( cellParams: GridCellParams, - options: { delimiterCharacter: string; ignoreValueFormatter: boolean }, + options: { + delimiterCharacter: string; + ignoreValueFormatter: boolean; + shouldAppendQuotes: boolean; + }, ) => { - const { delimiterCharacter, ignoreValueFormatter } = options; + const { delimiterCharacter, ignoreValueFormatter, shouldAppendQuotes } = options; let value: any; if (ignoreValueFormatter) { const columnType = cellParams.colDef.type; @@ -39,7 +46,7 @@ export const serializeCellValue = ( value = cellParams.formattedValue; } - return sanitizeCellValue(value, delimiterCharacter); + return sanitizeCellValue(value, delimiterCharacter, shouldAppendQuotes); }; const objectFormattedValueWarning = buildWarning([ @@ -49,7 +56,8 @@ const objectFormattedValueWarning = buildWarning([ type CSVRowOptions = { delimiterCharacter: string; - sanitizeCellValue?: (value: any, delimiterCharacter: string) => any; + sanitizeCellValue?: (value: any, delimiterCharacter: string, shouldAppendQuotes: boolean) => any; + shouldAppendQuotes: boolean; }; class CSVRow { options: CSVRowOptions; @@ -69,7 +77,11 @@ class CSVRow { if (value === null || value === undefined) { this.rowString += ''; } else if (typeof this.options.sanitizeCellValue === 'function') { - this.rowString += this.options.sanitizeCellValue(value, this.options.delimiterCharacter); + this.rowString += this.options.sanitizeCellValue( + value, + this.options.delimiterCharacter, + this.options.shouldAppendQuotes, + ); } else { this.rowString += value; } @@ -87,14 +99,16 @@ const serializeRow = ({ getCellParams, delimiterCharacter, ignoreValueFormatter, + shouldAppendQuotes, }: { id: GridRowId; columns: GridStateColDef[]; getCellParams: (id: GridRowId, field: string) => GridCellParams; delimiterCharacter: string; ignoreValueFormatter: boolean; + shouldAppendQuotes: boolean; }) => { - const row = new CSVRow({ delimiterCharacter }); + const row = new CSVRow({ delimiterCharacter, shouldAppendQuotes }); columns.forEach((column) => { const cellParams = getCellParams(id, column.field); @@ -103,7 +117,13 @@ const serializeRow = ({ objectFormattedValueWarning(); } } - row.addValue(serializeCellValue(cellParams, { delimiterCharacter, ignoreValueFormatter })); + row.addValue( + serializeCellValue(cellParams, { + delimiterCharacter, + ignoreValueFormatter, + shouldAppendQuotes, + }), + ); }); return row.getRowString(); @@ -117,6 +137,7 @@ interface BuildCSVOptions { includeColumnGroupsHeaders: NonNullable; ignoreValueFormatter: boolean; apiRef: React.MutableRefObject; + shouldAppendQuotes: boolean; } export function buildCSV(options: BuildCSVOptions): string { @@ -128,6 +149,7 @@ export function buildCSV(options: BuildCSVOptions): string { includeColumnGroupsHeaders, ignoreValueFormatter, apiRef, + shouldAppendQuotes, } = options; const CSVBody = rowIds @@ -139,6 +161,7 @@ export function buildCSV(options: BuildCSVOptions): string { getCellParams: apiRef.current.getCellParams, delimiterCharacter, ignoreValueFormatter, + shouldAppendQuotes, })}\r\n`, '', ) @@ -168,7 +191,11 @@ export function buildCSV(options: BuildCSVOptions): string { }, {}); for (let i = 0; i < maxColumnGroupsDepth; i += 1) { - const headerGroupRow = new CSVRow({ delimiterCharacter, sanitizeCellValue }); + const headerGroupRow = new CSVRow({ + delimiterCharacter, + sanitizeCellValue, + shouldAppendQuotes, + }); headerRows.push(headerGroupRow); filteredColumns.forEach((column) => { const columnGroupId = (columnGroupPathsLookup[column.field] || [])[i]; @@ -178,7 +205,7 @@ export function buildCSV(options: BuildCSVOptions): string { } } - const mainHeaderRow = new CSVRow({ delimiterCharacter, sanitizeCellValue }); + const mainHeaderRow = new CSVRow({ delimiterCharacter, sanitizeCellValue, shouldAppendQuotes }); filteredColumns.forEach((column) => { mainHeaderRow.addValue(column.headerName || column.field); }); diff --git a/packages/grid/x-data-grid/src/hooks/features/export/useGridCsvExport.tsx b/packages/grid/x-data-grid/src/hooks/features/export/useGridCsvExport.tsx index 3f54ba00d065..ac6ecb4dbfc8 100644 --- a/packages/grid/x-data-grid/src/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/export/useGridCsvExport.tsx @@ -53,6 +53,7 @@ export const useGridCsvExport = ( includeColumnGroupsHeaders: options.includeColumnGroupsHeaders ?? true, ignoreValueFormatter, apiRef, + shouldAppendQuotes: options.shouldAppendQuotes ?? true, }); }, [logger, apiRef, ignoreValueFormatter], diff --git a/packages/grid/x-data-grid/src/models/gridExport.ts b/packages/grid/x-data-grid/src/models/gridExport.ts index 46c95e2a8062..1b0366be669a 100644 --- a/packages/grid/x-data-grid/src/models/gridExport.ts +++ b/packages/grid/x-data-grid/src/models/gridExport.ts @@ -95,6 +95,12 @@ export interface GridCsvExportOptions extends GridFileExportOptions { * @returns {GridRowId[]} The list of row ids to export. */ getRowsToExport?: (params: GridCsvGetRowsToExportParams) => GridRowId[]; + /** + * @ignore + * If `false`, the quotes will not be appended to the cell value. + * @default true + */ + shouldAppendQuotes?: boolean; } /**