-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
POC: Export to CSV, XLSX, PDF #2355
Changes from all commits
69e6e41
6608893
4ce510f
eaa8d91
64d885e
773b31d
c6a3e09
9a56915
c679831
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { cloneElement } from 'react'; | ||
import type { ReactElement } from 'react'; | ||
import { renderToStaticMarkup } from 'react-dom/server'; | ||
import XLSX from 'xlsx'; | ||
import { jsPDF } from 'jspdf'; | ||
import autoTable from 'jspdf-autotable'; | ||
|
||
import type { DataGridProps } from '../../src'; | ||
|
||
export function exportToCsv<R, SR>(gridElement: ReactElement<DataGridProps<R, SR>>, fileName: string) { | ||
const { head, body, foot } = getGridContent(gridElement); | ||
const content = [...head, ...body, ...foot] | ||
.map(cells => cells.map(serialiseCellValue).join(',')) | ||
.join('\n'); | ||
|
||
downloadFile(fileName, new Blob([content], { type: 'text/csv;charset=utf-8;' })); | ||
} | ||
|
||
export function exportToXlsx<R, SR>(gridElement: ReactElement<DataGridProps<R, SR>>, fileName: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/SheetJS/sheetjs |
||
const { head, body, foot } = getGridContent(gridElement); | ||
const wb = XLSX.utils.book_new(); | ||
const ws = XLSX.utils.aoa_to_sheet([...head, ...body, ...foot]); | ||
XLSX.utils.book_append_sheet(wb, ws, 'Sheet 1'); | ||
XLSX.writeFile(wb, fileName); | ||
} | ||
|
||
|
||
export function exportToPdf<R, SR>(gridElement: ReactElement<DataGridProps<R, SR>>, fileName: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/simonbengtsson/jsPDF-AutoTable |
||
const doc = new jsPDF({ | ||
orientation: 'l', | ||
unit: 'px' | ||
}); | ||
|
||
const { head, body, foot } = getGridContent(gridElement); | ||
autoTable(doc, { | ||
head, | ||
body, | ||
foot, | ||
horizontalPageBreak: true, | ||
styles: { cellPadding: 1.5, fontSize: 8, cellWidth: 'wrap' }, | ||
tableWidth: 'wrap' | ||
}); | ||
doc.save(fileName); | ||
} | ||
|
||
function getGridContent<R, SR>(gridElement: ReactElement<DataGridProps<R, SR>>) { | ||
const grid = document.createElement('div'); | ||
grid.innerHTML = renderToStaticMarkup(cloneElement(gridElement, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're rendering images, it might end up downloading a lot of images at once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good to know. We can add it if needed or I will create a followup PR |
||
enableVirtualization: false | ||
})); | ||
|
||
return { | ||
head: getRows('.rdg-header-row'), | ||
body: getRows('.rdg-row:not(.rdg-summary-row)'), | ||
foot: getRows('.rdg-summary-row') | ||
}; | ||
|
||
function getRows(selector: string) { | ||
return Array.from( | ||
grid.querySelectorAll<HTMLDivElement>(selector) | ||
).map(gridRow => { | ||
return Array.from( | ||
gridRow.querySelectorAll<HTMLDivElement>('.rdg-cell') | ||
).map(gridCell => gridCell.innerText); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the cell ends up empty when we display an image, a checkbox, or a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. We can manually update cell value during export or provide a flag to the formatter so it can return a print friendly value |
||
}); | ||
} | ||
} | ||
|
||
function serialiseCellValue(value: unknown) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we handle There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can. It works for now so may be next PR if needed |
||
if (typeof value === 'string') { | ||
const formattedValue = value.replace(/"/g, '""'); | ||
return formattedValue.includes(',') ? `"${formattedValue}"` : formattedValue; | ||
} | ||
return value; | ||
} | ||
|
||
function downloadFile(fileName: string, data: Blob) { | ||
const downloadLink = document.createElement('a'); | ||
downloadLink.download = fileName; | ||
const url = URL.createObjectURL(data); | ||
downloadLink.href = url; | ||
downloadLink.click(); | ||
URL.revokeObjectURL(url); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about the prop name. Any suggessions?
disableVirtualization
makes more sense but I kept this to be consistent withenableFilterRow
. We can also consider forwarding this prop to the formatters to users can use it during printing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prop name is good to me. I prefer avoiding "negative" prop/variable names as they make reading logic harder.
What use case do you have in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If for example there is a cell that shows a complex data like an icon with a tooltip. It may not work out of the box and it would be helpful to provide an alternate formatter for printing. We can revisit if needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like it should be a separate thing, and it could be implemented by the user via a provider.