Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[data grid] Option to customize CSV export column headers independent of rendered ones #12789

Open
ianroberts opened this issue Apr 15, 2024 · 3 comments
Labels
component: data grid This is the name of the generic UI component, not the React module! customization: extend Logic customizability enhancement This is not a bug, nor a new feature feature: Export

Comments

@ianroberts
Copy link

ianroberts commented Apr 15, 2024

Summary

Summary

Exporting a data grid as CSV uses the column headers exactly as they appear in the rendered grid. In most cases this is the correct behaviour, but I have a use case where it would be useful to be able to use different column headings in the CSV compared to the interactive table. Specifically my grid is used as a more human-friendly editor for a dataset, then the CSV export of that dataset is passed to another tool that expects particular column headers. As such I want the column headers in the editor to be "friendly" names like "User's Full Name" (taken from an i18n message bundle), but the CSV to export using the machine readable field names like userFullName.

Desired behaviour

Currently the CSV exporter is hard-coded to always prefer the column's headerName, if one is set:

const mainHeaderRow = new CSVRow({ delimiterCharacter, sanitizeCellValue, shouldAppendQuotes });
filteredColumns.forEach((column) => {
mainHeaderRow.addValue(column.headerName || column.field);
});
headerRows.push(mainHeaderRow);

Ideally there would be a third option as part of the GridColDef besides field and headerName to separately specify a csvHeaderName, or alternatively allow headerName to be an object as an alternative to a plain string, along the lines of

const col = {
  field: "fullName",
  headerName: {
    grid: t("field.names.fullName"),  // resolves to "User's Full Name" in English
    csv: "userFullName",
  }
}

Workarounds

I tried specifying the headerName as the CSV name and then supplying a renderHeader function to do the "pretty" names, but then the "columns" selector and the "filters" popup use the CSV names instead of the human-facing ones.

It may be possible to use a custom action instead of the default Export action, that calls the API to generate a CSV without headers and then prepends the custom headers, but this would require duplicating much of the logic for determining which columns to export and in which order.

Examples

No response

Motivation

No response

Search keywords: csv column header

@ianroberts ianroberts added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Apr 15, 2024
@michelengelen michelengelen changed the title [DataGrid] Option to customize CSV export column headers independent of rendered ones [data grid] Option to customize CSV export column headers independent of rendered ones Apr 16, 2024
@michelengelen
Copy link
Member

Hey @ianroberts ... afaik this is currently not possible.
Another approach to this could be to add a new option to the GridCsvExportOptions. Something like useFieldForHeaderName which then simply returns the field name.
WDYT @mui/xgrid ?

@michelengelen michelengelen added component: data grid This is the name of the generic UI component, not the React module! status: waiting for author Issue with insufficient information feature: Export customization: extend Logic customizability and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Apr 16, 2024
@ianroberts
Copy link
Author

Something like useFieldForHeaderName which then simply returns the field name.

This would work nicely for my case, the only reason I suggested a separate csvHeaderName is that it's a bit more flexible - if you want the CSV header to be the field name then you can easily set the two properties to the same value, but if you want the CSV header to be something different then useFieldForHeaderName wouldn't be sufficient.

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels Apr 16, 2024
@michelengelen
Copy link
Member

True that it does provide a bit more flexibilty. I just suggested that to not add something that would impact a lot of places in the code.
We could also do a hybrid approach where we provide the GridCsvExportOptions with a getter function when needed: getHeaderName or similar.
I will add this to our board for the team to have a look.

Here is a proposal how this could look like:

diff --git a/packages/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts b/packages/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts
index 88eea0e80..bda5a31e3 100644
--- a/packages/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts
+++ b/packages/x-data-grid/src/hooks/features/export/serializers/csvSerializer.ts
@@ -135,6 +135,8 @@ interface BuildCSVOptions {
   delimiterCharacter: NonNullable<GridCsvExportOptions['delimiter']>;
   includeHeaders: NonNullable<GridCsvExportOptions['includeHeaders']>;
   includeColumnGroupsHeaders: NonNullable<GridCsvExportOptions['includeColumnGroupsHeaders']>;
+  getHeaderName: NonNullable<GridCsvExportOptions['getHeaderName']>;
+  getColumnGroupHeaderName: NonNullable<GridCsvExportOptions['getColumnGroupHeaderName']>;
   ignoreValueFormatter: boolean;
   apiRef: React.MutableRefObject<GridApiCommunity>;
   shouldAppendQuotes: boolean;
@@ -146,7 +148,9 @@ export function buildCSV(options: BuildCSVOptions): string {
     rowIds,
     delimiterCharacter,
     includeHeaders,
+    getHeaderName,
     includeColumnGroupsHeaders,
+    getColumnGroupHeaderName,
     ignoreValueFormatter,
     apiRef,
     shouldAppendQuotes,
@@ -200,14 +204,20 @@ export function buildCSV(options: BuildCSVOptions): string {
       filteredColumns.forEach((column) => {
         const columnGroupId = (columnGroupPathsLookup[column.field] || [])[i];
         const columnGroup = columnGroupLookup[columnGroupId];
-        headerGroupRow.addValue(columnGroup ? columnGroup.headerName || columnGroup.groupId : '');
+        headerGroupRow.addValue(
+          columnGroup
+            ? getColumnGroupHeaderName?.(columnGroup) ||
+                columnGroup.headerName ||
+                columnGroup.groupId
+            : '',
+        );
       });
     }
   }

   const mainHeaderRow = new CSVRow({ delimiterCharacter, sanitizeCellValue, shouldAppendQuotes });
   filteredColumns.forEach((column) => {
-    mainHeaderRow.addValue(column.headerName || column.field);
+    mainHeaderRow.addValue(getHeaderName?.(column) || column.headerName || column.field);
   });
   headerRows.push(mainHeaderRow);

diff --git a/packages/x-data-grid/src/models/gridExport.ts b/packages/x-data-grid/src/models/gridExport.ts
index 07aea4967..6a469c1b4 100644
--- a/packages/x-data-grid/src/models/gridExport.ts
+++ b/packages/x-data-grid/src/models/gridExport.ts
@@ -1,4 +1,6 @@
 import * as React from 'react';
+import { GridColDef } from '@mui/x-data-grid/models/colDef';
+import { GridColumnGroup } from '@mui/x-data-grid/models/gridColumnGrouping';
 import { GridRowId } from './gridRows';
 import type { GridApiCommon } from './api';
 import type { GridApiCommunity } from './api/gridApiCommunity';
@@ -83,12 +85,24 @@ export interface GridCsvExportOptions extends GridFileExportOptions {
    * @default true
    */
   includeHeaders?: boolean;
+  /**
+   * Can be used to get a custom headerName value for a column.
+   * @param {GridColDef} colDef The column definition.
+   * @returns {string} The header name.
+   */
+  getHeaderName?: (colDef: GridColDef) => string;
   /**
    * If `true`, the CSV will include the column groups.
    * @see See {@link https://mui.com/x/react-data-grid/column-groups/ column groups docs} for more details.
    * @default true
    */
   includeColumnGroupsHeaders?: boolean;
+  /**
+   * Can be used to get a custom headerName value for a column.
+   * @param {Omit<GridColumnGroup, 'children'>} columnGroup The column definition.
+   * @returns {string} The header name.
+   */
+  getColumnGroupHeaderName?: (columnGroup: Omit<GridColumnGroup, 'children'>) => string;
   /**
    * Function that returns the list of row ids to export on the order they should be exported.
    * @param {GridCsvGetRowsToExportParams} params With all properties from [[GridCsvGetRowsToExportParams]].

@mui/xgrid WDYT?

@michelengelen michelengelen added enhancement This is not a bug, nor a new feature and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! customization: extend Logic customizability enhancement This is not a bug, nor a new feature feature: Export
Projects
None yet
Development

No branches or pull requests

2 participants