Skip to content

Commit

Permalink
Add UTF-8 BOM to CSV exports
Browse files Browse the repository at this point in the history
  • Loading branch information
canac committed Nov 4, 2024
1 parent 1c203d1 commit 5368f53
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { Settings } from 'luxon';
import { SnackbarProvider } from 'notistack';
import { buildURI } from 'react-csv/lib/core';
import { I18nextProvider } from 'react-i18next';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
Expand All @@ -25,6 +26,8 @@ import { AccountTransactions } from './AccountTransactions';
import { financialAccountEntriesMock } from './AccountTransactionsMocks';
import { FinancialAccountEntriesQuery } from './financialAccountTransactions.generated';

jest.mock('react-csv/lib/core');

const accountListId = 'account-list-1';
const financialAccountId = 'financialAccountId';
const defaultSearchTerm = '';
Expand Down Expand Up @@ -291,6 +294,9 @@ describe('Financial Account Transactions', () => {
});

it('should export CSV', async () => {
const mockBlobUrl = 'blob:';
buildURI.mockReturnValue(mockBlobUrl);

const { getByRole } = render(<Components />);

await waitFor(() => {
Expand Down Expand Up @@ -318,24 +324,12 @@ describe('Financial Account Transactions', () => {
['8/8/2024', 'description2', 'category1Name', '15008', ''],
['8/7/2024', 'description3', 'category2Name', '', '36.2'],
];

const csvContent =
'data:text/csv;charset=utf-8,' +
csvContentArray
.map((row) =>
row
.map((field) => `"${String(field).replace(/"/g, '""')}"`)
.join(','),
)
.join('\n');
expect(buildURI).toHaveBeenCalledWith(csvContentArray, true);

await waitFor(() => {
expect(createElementSpy).toHaveBeenCalledWith('a');
expect(appendChildSpy).toHaveBeenCalled();
expect(setAttributeSpy).toHaveBeenCalledWith(
'href',
encodeURI(csvContent),
);
expect(setAttributeSpy).toHaveBeenCalledWith('href', mockBlobUrl);
expect(clickSpy).toHaveBeenCalled();
expect(removeChildSpy).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useContext, useEffect, useMemo } from 'react';
import { Box, CircularProgress } from '@mui/material';
import { styled } from '@mui/material/styles';
import { DateTime } from 'luxon';
import { buildURI } from 'react-csv/lib/core';
import { useTranslation } from 'react-i18next';
import { Panel } from 'pages/accountLists/[accountListId]/reports/helpers';
import { headerHeight } from 'src/components/Shared/Header/ListHeader';
Expand Down Expand Up @@ -115,10 +116,10 @@ export const AccountTransactions: React.FC = () => {
t('Outflow'),
t('Inflow'),
];
const convertDataToArray = data.financialAccountEntries.entries.reduce(
(array, entry) => {
const csvLines = data.financialAccountEntries.entries.reduce(
(csvLines, entry) => {
return [
...array,
...csvLines,
[
entry.entryDate
? dateFormatShort(DateTime.fromISO(entry.entryDate), locale)
Expand All @@ -138,20 +139,11 @@ export const AccountTransactions: React.FC = () => {
);

// Convert Array to CSV format
const csvContent =
'data:text/csv;charset=utf-8,' +
convertDataToArray
.map((row) =>
row
.map((field) => `"${String(field).replace(/"/g, '""')}"`)
.join(','),
)
.join('\n');
const csvBlob = buildURI(csvLines, true);

// Create a link and trigger download
const encodedUri = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('href', csvBlob);
link.setAttribute('download', `${appName}-entries-export${dateRange}.csv`);
document.body.appendChild(link);
link.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const FourteenMonthReportActions: React.FC<
// This has to be a useEffect instead of a useMemo to prevent hydration errors because the
// server isn't able to calculate a blob URL.
useEffect(() => {
const csvBlob = buildURI(csvData);
const csvBlob = buildURI(csvData, true);
setCsvBlob(csvBlob);

return () => URL.revokeObjectURL(csvBlob);
Expand Down

0 comments on commit 5368f53

Please sign in to comment.