Skip to content

Commit

Permalink
Add downloadCSV property for downloading content as CSV
Browse files Browse the repository at this point in the history
Change-type: minor
  • Loading branch information
myarmolinsky committed Apr 26, 2023
1 parent 0cb659c commit f615ad2
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 0 deletions.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
"README.md"
],
"dependencies": {
"@fortawesome/fontawesome": "^1.2.0-6",
"@fortawesome/fontawesome-common-types": "^6.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@types/history": "^4.7.11",
"json-e": "^4.4.3",
"lodash": "^4.17.21",
Expand Down
82 changes: 82 additions & 0 deletions src/AutoUI/Actions/DownloadCSV.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { AutoUIBaseResource } from '../schemaOps';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button } from 'rendition';
import { faDownload } from '@fortawesome/free-solid-svg-icons';
import { useTranslation } from '../../hooks/useTranslation';

// Source: https://stackoverflow.com/questions/41028114/how-to-convert-array-of-nested-objects-to-csv
const pivot = (arr: object[]) => {
const mp = new Map();

const setValue = (a: string[], path: string[], val: any) => {
if (Object(val) !== val) {
// primitive value
const pathStr = path.join('.');
const i = (mp.has(pathStr) ? mp : mp.set(pathStr, mp.size)).get(pathStr);
a[i] = val;
} else {
for (const key of Object.keys(val)) {
setValue(a, key === '0' ? path : path.concat(key), val[key]);
}
}
return a;
};

const result = arr.map((obj) => setValue([], [], obj));
return [[...mp.keys()], ...result];
};

// Source: https://stackoverflow.com/questions/41028114/how-to-convert-array-of-nested-objects-to-csv
const toCsv = (arr: any[][]) => {
return arr
.map((row) =>
row.map((val) => (isNaN(val) ? JSON.stringify(val) : +val)).join(','),
)
.join('\n');
};

const download = (data: object[], fileName: string) => {
const csvData = toCsv(pivot(data));
const CSVFile = new Blob([csvData], { type: 'text/csv' });
const tempLink = document.createElement('a');
tempLink.download = `${fileName}.csv`;
const url = window.URL.createObjectURL(CSVFile);
tempLink.href = url;
tempLink.style.display = 'none';
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
};

interface DownloadCSVProps<T extends AutoUIBaseResource<T>> {
downloadCSV?: {
getData: ($filter: any) => Promise<object[]>;
fileName: string;
};
$filter: any;
}

export const DownloadCSV = <T extends AutoUIBaseResource<T>>({
downloadCSV,
$filter,
}: DownloadCSVProps<T>) => {
const { t } = useTranslation();

if (!downloadCSV) {
return null;
}

const { getData, fileName } = downloadCSV;

return (
<Button
onClick={async () => {
const data = await getData($filter);
download(data, fileName);
}}
tooltip={t('actions.download_csv')}
icon={<FontAwesomeIcon icon={faDownload} />}
/>
);
};
17 changes: 17 additions & 0 deletions src/AutoUI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
} from '../oData/jsonToOData';
import { CollectionLensRendererProps } from './Lenses/types';
import pickBy from 'lodash/pickBy';
import { DownloadCSV } from './Actions/DownloadCSV';

const DEFAULT_ITEMS_PER_PAGE = 50;

Expand Down Expand Up @@ -129,7 +130,13 @@ export interface AutoUIProps<T> extends Omit<BoxProps, 'onChange'> {
lensContext?: object;
/** Loading property to show the Spinner */
loading?: boolean;
/** Property to use as the key for each rendered entity */
rowKey?: keyof T;
/** Passes $filter and expects all data */
downloadCSV?: {
getData: ($filter: any) => Promise<object[]>;
fileName: string;
};
}

export const AutoUI = <T extends AutoUIBaseResource<T>>({
Expand All @@ -148,6 +155,7 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
lensContext,
loading,
rowKey,
downloadCSV,
...boxProps
}: AutoUIProps<T>) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -340,6 +348,11 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
[actions, sdk?.tags],
);

const pineClientfilters = React.useMemo(
() => convertToPineClientFilter([], filters),
[filters],
);

const internalOnChange = (
updatedFilters: JSONSchema[],
sortInfo: TableSortOptions<T> | null,
Expand Down Expand Up @@ -447,6 +460,10 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
);
}}
/>
<DownloadCSV
downloadCSV={downloadCSV}
$filter={pineClientfilters}
/>
</>
)}
</HeaderGrid>
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useTranslation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type TFunction = (str: string, options?: any) => string;

const translationMap = {
'actions.manage_tags': 'Manage tags',
'actions.download_csv': 'Download CSV',
'info.update_item_no_permissions':
"You don't have permission to {{action}} the selected {{resource}}",
'info.ongoing_action_wait': 'There is an ongoing action, please wait',
Expand Down

0 comments on commit f615ad2

Please sign in to comment.