Skip to content

Commit c1c6c69

Browse files
authored
feat: add csv export components (#802)
Signed-off-by: Abdelsalem <[email protected]>
1 parent 3723e9e commit c1c6c69

File tree

8 files changed

+182
-0
lines changed

8 files changed

+182
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { useCallback } from 'react';
9+
import { CsvExportProps } from './csv-export.type';
10+
import { useCsvExport } from './use-csv-export';
11+
import { ExportCsvButton } from './export-csv-button';
12+
13+
export function CsvExport({
14+
gridRef,
15+
columns,
16+
tableNamePrefix = '',
17+
tableName,
18+
disabled,
19+
skipColumnHeaders = false,
20+
language,
21+
exportDataAsCsv,
22+
}: CsvExportProps): JSX.Element {
23+
const { downloadCSVData } = useCsvExport();
24+
const download = useCallback(() => {
25+
downloadCSVData({ gridRef, columns, tableName, tableNamePrefix, skipColumnHeaders, language, exportDataAsCsv });
26+
}, [downloadCSVData, gridRef, columns, tableName, tableNamePrefix, skipColumnHeaders, language, exportDataAsCsv]);
27+
28+
return <ExportCsvButton disabled={disabled} onClick={download} />;
29+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { RefObject } from 'react';
9+
import { AgGridReact } from 'ag-grid-react';
10+
import { ColDef, CsvExportParams } from 'ag-grid-community';
11+
import { GsLang } from '../../utils';
12+
13+
export type CsvDownloadProps = {
14+
gridRef: RefObject<AgGridReact>;
15+
columns: ColDef[];
16+
tableName: string;
17+
tableNamePrefix?: string;
18+
skipColumnHeaders?: boolean;
19+
language: GsLang;
20+
exportDataAsCsv: (params?: CsvExportParams) => void;
21+
};
22+
23+
export type CsvExportProps = CsvDownloadProps & {
24+
disabled: boolean;
25+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { Box, CircularProgress, IconButton } from '@mui/material';
9+
import { FormattedMessage } from 'react-intl';
10+
import { GetApp as GetAppIcon, Check as CheckIcon } from '@mui/icons-material';
11+
12+
export interface ExportButtonProps {
13+
disabled?: boolean;
14+
onClick: () => void;
15+
isDownloadLoading?: boolean; // used mostly for previous Edge versions where download from backend file takes some time to begin
16+
isDownloadSuccessful?: boolean;
17+
}
18+
19+
export function ExportCsvButton({
20+
onClick,
21+
disabled = false,
22+
isDownloadLoading: isCsvLoading = false,
23+
isDownloadSuccessful = false,
24+
}: Readonly<ExportButtonProps>) {
25+
return (
26+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
27+
<FormattedMessage id="MuiVirtualizedTable/exportCSV" />
28+
<Box sx={{ position: 'relative' }}>
29+
<IconButton disabled={disabled} aria-label="exportCSVButton" onClick={onClick}>
30+
{isDownloadSuccessful ? <CheckIcon /> : <GetAppIcon />}
31+
</IconButton>
32+
{isCsvLoading && (
33+
<CircularProgress
34+
size={30}
35+
// style from MUI documentation to wrap icon with circular progress
36+
sx={{
37+
position: 'absolute',
38+
top: '50%',
39+
left: '50%',
40+
marginTop: '-15px',
41+
marginLeft: '-15px',
42+
}}
43+
/>
44+
)}
45+
</Box>
46+
</Box>
47+
);
48+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
export * from './csv-export';
9+
export * from './csv-export.type';
10+
export * from './export-csv-button';
11+
export * from './use-csv-export';
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { useCallback } from 'react';
9+
import { ProcessCellForExportParams, ProcessHeaderForExportParams } from 'ag-grid-community';
10+
import { useIntl, IntlShape } from 'react-intl';
11+
import { CsvDownloadProps } from './csv-export.type';
12+
import { LANG_FRENCH } from '../../utils';
13+
14+
const NA_VALUE = 'N/A';
15+
16+
const formatNAValue = (value: string, intl: IntlShape): string => {
17+
return value === NA_VALUE ? intl.formatMessage({ id: 'export/undefined' }) : value;
18+
};
19+
20+
export const useCsvExport = () => {
21+
const intl = useIntl();
22+
23+
const getCSVFilename = useCallback((tableName: string) => {
24+
return tableName
25+
.trim()
26+
.replace(/[\\/:"*?<>|\s]/g, '-') // Removes the filesystem sensible characters
27+
.substring(0, 27); // Best practice : limits the filename size to 31 characters (27+'.csv')
28+
}, []);
29+
30+
const downloadCSVData = useCallback(
31+
(props: CsvDownloadProps) => {
32+
const hasColId = (colId: string | undefined): colId is string => {
33+
return colId !== undefined;
34+
};
35+
36+
const processCell = (params: ProcessCellForExportParams): string => {
37+
if (params.column.getColId() === 'limitName') {
38+
return formatNAValue(params.value, intl);
39+
}
40+
41+
// If the language is in French, we change the decimal separator
42+
if (props.language === LANG_FRENCH && typeof params.value === 'number') {
43+
return params.value.toString().replace('.', ',');
44+
}
45+
return params.value;
46+
};
47+
const prefix = props.tableNamePrefix ?? '';
48+
49+
props.exportDataAsCsv({
50+
suppressQuotes: false,
51+
columnSeparator: props.language === LANG_FRENCH ? ';' : ',',
52+
columnKeys: props.columns.map((col) => col.colId).filter(hasColId),
53+
skipColumnHeaders: props.skipColumnHeaders,
54+
processHeaderCallback: (params: ProcessHeaderForExportParams) =>
55+
params.column.getColDef().headerComponentParams?.displayName ??
56+
params.column.getColDef().headerName ??
57+
params.column.getColId(),
58+
fileName: prefix.concat(getCSVFilename(props.tableName)),
59+
processCellCallback: processCell,
60+
});
61+
},
62+
[getCSVFilename, intl]
63+
);
64+
65+
return { downloadCSVData };
66+
};

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './announcement';
88
export * from './authentication';
99
export * from './cardErrorBoundary';
1010
export * from './checkBoxList';
11+
export * from './csvDownloader';
1112
export * from './customAGGrid';
1213
export * from './dialogs';
1314
export * from './directoryItemSelector/DirectoryItemSelector';

src/translations/en/tableEn.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
export const tableEn = {
99
'MuiVirtualizedTable/exportCSV': 'Download CSV',
10+
'export/undefined': 'Undefined',
1011
};

src/translations/fr/tableFr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
export const tableFr = {
99
'MuiVirtualizedTable/exportCSV': 'Télécharger un CSV',
10+
'export/undefined': 'Non défini',
1011
};

0 commit comments

Comments
 (0)