Files
supabase/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfTable.tsx
T
Joshen Lim 38fa40e851 Joshenlim/fe 4445 explorer export as pdf option 2 (#50786)
## Context

Another take on [this
PR](https://github.com/supabase/supabase/pull/50733), decided that it's
better UX + DX to use a PDF library (`@react-pdf/renderer`) instead for
handling exporting Notebooks to PDF. The previous method using `print`
involved a lot of sparse patching in various places + had a lot of
limitations, not to mention its awkward UX to "Print".

There's a lot of code changes in this PR but it's because we're
constructing the PDF document based on the notebook's contents using
primitives from `@react-pdf/renderer` which gives us a lot more control
over the PDF's output.

PDF output will differ slightly from the Notebook UI in the browser
- Added a custom header in the report which covers the notebook name +
project name, and when its exported
- Empty markdown cells are skipped
- Query cells will show both SQL content + results (either table or
chart)
- Opting to skip syntax highlighting for SQL content (Can consider
separately)
- Results will only be included in the PDF if the notebook's been run
(Aligns with Copy as Markdown CTA)
- For table results, _all_ rows will be rendered (Aligns with Copy as
Markdown CTA)

Feel free to give it a spin! 🙂 🙏 

<img width="248" height="207" alt="image"
src="https://github.com/user-attachments/assets/e38c14ba-7ee8-40ce-99b1-fa126453bf0f"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Added an “Export as PDF” option for notebooks.
- PDFs include notebook details, Markdown content, SQL queries, query
results, tables, and charts.
- Query exports show errors, empty results, row counts, and applicable
row limits.
- Export progress is displayed while the PDF is generated, and failures
are reported via notifications.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-24 13:23:38 +08:00

51 lines
1.4 KiB
TypeScript

import { Text, View } from '@react-pdf/renderer'
import type { ReactElement } from 'react'
import { pdfStyles } from './theme'
import { formatCellValue } from '@/components/ui/DataGridResults/DataGridResults.utils'
interface NotebookPdfTableProps {
rows: readonly Record<string, unknown>[]
}
export function NotebookPdfTable({ rows }: NotebookPdfTableProps): ReactElement | null {
const columns = Object.keys(rows[0] ?? {})
if (columns.length === 0) return null
return (
<View style={pdfStyles.table}>
<View style={pdfStyles.tableHeaderRow}>
{columns.map((column) => (
<Text key={column} style={pdfStyles.tableHeaderCell}>
{column}
</Text>
))}
</View>
{rows.map((row, rowIndex) => (
<View
key={rowIndex}
style={
rowIndex === rows.length - 1
? [pdfStyles.tableRow, pdfStyles.tableRowLast]
: pdfStyles.tableRow
}
wrap={false}
>
{columns.map((column) => {
const value = row[column]
return value === null ? (
<Text key={column} style={pdfStyles.tableCellNull}>
NULL
</Text>
) : (
<Text key={column} style={pdfStyles.tableCell}>
{formatCellValue(value)}
</Text>
)
})}
</View>
))}
</View>
)
}