diff --git a/README.md b/README.md
index 78d0fc5..c25b83b 100644
--- a/README.md
+++ b/README.md
@@ -67,14 +67,66 @@ or load from a CDN:
};
```
+## Example with Header mapping
+
+```jsx
+// import jsonToCsvExport from "json-to-csv-export";
+() => {
+ const mockData = [
+ {
+ id: 1,
+ firstName: "Sarajane",
+ lastName: "Wheatman",
+ email: "swheatman0@google.nl",
+ language: "Zulu",
+ ip: "40.98.252.240",
+ },
+ {
+ id: 2,
+ firstName: "Linell",
+ lastName: "Humpherston",
+ email: "lhumpherston1@google.com.br",
+ language: "Czech",
+ ip: "82.225.151.150",
+ },
+ ];
+
+ const headers = [
+ { key: "id", label: "Identifier" },
+ { key: "firstName", label: "First Name" },
+ { key: "lastName", label: "Last Name" },
+ { key: "email", label: "Email Address" },
+ { key: "language", label: "Language" },
+ { key: "ip", label: "IP Address" },
+ ];
+
+ return (
+
+ );
+ };
+```
+
+
+
+
## Properties
-| # | Property | Type | Requirement | Default | Description |
-| --- | --------- | -------- | ----------- | ------------------------- | ---------------------------------------------------------------------------------------- |
-| 1 | data | [] | required | | array of objects |
-| 2 | filename | string | optional | "export.csv" | The filename. The .csv extention will be added if not included in file name |
-| 3 | delimiter | string | optional | "," | fields separator |
-| 4 | headers | string[] | optional | provided data object keys | List of columns that will be used in the final CSV file. Recommended for large datasets! |
+```typescript
+interface HeaderMapping {
+ label: string;
+ key: string;
+}
+```
+
+
+| # | Property | Type | Requirement | Default | Description |
+| - | --------- | -------------------------------- | ----------- | ------------------------- | ---------------------------------------------------------------------------------------- |
+| 1 | data | [] | required | | array of objects |
+| 2 | filename | string | optional | "export.csv" | The filename. The .csv extention will be added if not included in file name |
+| 3 | delimiter | string | optional | "," | fields separator |
+| 4 | headers | string[] OR
HeaderMapping[] | optional | provided data object keys | List of columns that will be used in the final CSV file. Recommended for large datasets! |
## Migration from version 1.x to 2.x
diff --git a/src/generate.ts b/src/generate.ts
index 4bdb365..e114c5e 100644
--- a/src/generate.ts
+++ b/src/generate.ts
@@ -1,11 +1,15 @@
+import { HeaderMapping } from ".";
+
+
export const csvGenerateRow = (
row: any,
- headerKeys: string[],
+ headerKeys: string[] | HeaderMapping[],
delimiter: string,
) => {
const needsQuoteWrapping = new RegExp(`["${delimiter}\r\n]`);
return headerKeys
- .map((fieldName) => {
+ .map((header) => {
+ const fieldName = typeof header === "string" ? header : header.key;
let value = row[fieldName];
if (typeof value === "number" || typeof value === "boolean")
return `${value}`;
@@ -13,7 +17,7 @@ export const csvGenerateRow = (
if (typeof value !== "string") {
value = String(value);
}
- /* RFC-4180
+ /* RFC-4180
6. Fields containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes.
7. If double-quotes are used to enclose fields, then a double-quote inside a field must be escaped by preceding it with
another double quote. For example: "aaa","b""bb","ccc"
@@ -42,13 +46,16 @@ const getAllUniqueKeys = (data: any[]): string[] => {
export const csvGenerate = (
data: any[],
- headers: string[] | undefined,
+ headers: string[] | HeaderMapping[] | undefined,
delimiter: string,
) => {
const headerKeys = headers ?? getAllUniqueKeys(data);
+ const headerRow = headerKeys.map((header) =>
+ typeof header === "string" ? header : header.label
+ );
const csv = data.map((row) => csvGenerateRow(row, headerKeys, delimiter));
- csv.unshift(headerKeys.join(delimiter));
+ csv.unshift(headerRow.join(delimiter));
return csv.join("\r\n");
-};
+};
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index 29db31c..73fab00 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,11 +1,15 @@
import { csvGenerate } from "./generate";
+export interface HeaderMapping {
+ label: string;
+ key: string;
+}
interface CsvDownloadProps {
data: any[];
filename?: string;
/** Cell delimiter to use. Defaults to comma for RFC-4180 compliance. */
delimiter?: string;
- headers?: string[];
+ headers?: string[] | HeaderMapping[];
}
const CSV_FILE_TYPE = "text/csv;charset=utf-8;";
@@ -20,7 +24,7 @@ const csvDownload = ({
if (data.length === 0) {
triggerCsvDownload(
- headers ? headers.join(delimiter) : "",
+ headers ? headers.map(h => typeof h === "string" ? h : h.label).join(delimiter) : "",
formattedFilename,
);
return;
@@ -32,7 +36,6 @@ const csvDownload = ({
};
const triggerCsvDownload = (csvAsString: string, fileName: string) => {
- // BOM support for special characters in Excel
const byteOrderMark = "\ufeff";
const blob = new Blob([byteOrderMark, csvAsString], {
@@ -54,4 +57,4 @@ const getFilename = (providedFilename: string): string => {
: `${providedFilename}.csv`;
};
-export default csvDownload;
+export default csvDownload;
\ No newline at end of file
diff --git a/test/generate.spec.ts b/test/generate.spec.ts
index c825df2..220bdf7 100644
--- a/test/generate.spec.ts
+++ b/test/generate.spec.ts
@@ -1,3 +1,4 @@
+import { HeaderMapping } from "../src";
import { csvGenerateRow, csvGenerate } from "../src/generate";
describe("csvGenerateRow", () => {
@@ -103,4 +104,19 @@ describe("csvGenerate", () => {
expectedCsv
);
});
+
+ test("generates CSV with HeaderMapping", () => {
+ const mockData = [
+ { id: 1, name: "Alice", age: null },
+ { id: 2, name: "Bob", age: undefined },
+ { id: 3, name: "Charlie", age: "" },
+ ];
+ const headers: HeaderMapping[] = [
+ { key: "id", label: "ID" },
+ { key: "name", label: "Full Name" },
+ { key: "age", label: "Age" },
+ ];
+ const expectedCsv = `ID,Full Name,Age\r\n1,Alice,\r\n2,Bob,\r\n3,Charlie,`;
+ expect(csvGenerate(mockData, headers, ",")).toEqual(expectedCsv);
+ });
});
diff --git a/test/index.spec.ts b/test/index.spec.ts
index a492cff..6cf9361 100644
--- a/test/index.spec.ts
+++ b/test/index.spec.ts
@@ -1,4 +1,4 @@
-import csvDownload from "../src/index";
+import csvDownload, { HeaderMapping } from "../src/index";
import mockData from "./__mocks__/mockData";
// current version of JSDom doesn't support Blob.text(), so this is a FileReader-based workaround.
@@ -111,4 +111,33 @@ describe("csvDownload", () => {
generatedCsvString.includes(`Blanch,belby0@bing.com,Elby,1`)
).toBeTruthy();
});
+
+ test("downloads CSV with HeaderMapping",async () => {
+ const headers: HeaderMapping[] = [
+ { key: "First Name", label: "First Name Label" },
+ { key: "Last Name", label: "Last Name Label" },
+ { key: "Email", label: "Email Address Label" },
+ { key: "Gender", label: "Gender Label" },
+ { key: "IP Address", label: "IP Address Label" },
+ ];
+
+ const expectedCsv = `First Name,Last Name,Email Address,Gender,IP Address\r\nPaulie,Steffens,psteffenso@washingtonpost.com,Female,115.83.208.158`;
+
+
+ csvDownload({
+ data: mockData,
+ headers,
+ filename: "test-customHeaders.csv",
+ });
+ expect(link.download).toEqual("test-customHeaders.csv");
+ expect(capturedBlob).not.toBe(null);
+ const generatedCsvString = await getBlobAsText(capturedBlob as Blob);
+ expect(
+ generatedCsvString.startsWith(`First Name Label,Last Name Label,Email Address Label,Gender Label,IP Address Label`)
+ ).toBeTruthy();
+ expect(
+ generatedCsvString.includes(`Blanch,Elby,belby0@bing.com,Female,112.81.107.207`)
+ ).toBeTruthy();
+
+ });
});