diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.js b/docs/data/data-grid/export/PrintExportSelectedRows.js new file mode 100644 index 000000000000..2b50951c8158 --- /dev/null +++ b/docs/data/data-grid/export/PrintExportSelectedRows.js @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { + DataGrid, + GridToolbar, + gridFilteredSortedRowIdsSelector, + selectedGridRowsSelector, +} from '@mui/x-data-grid'; + +const getSelectedRowsToExport = ({ apiRef }) => { + const selectedRowIds = selectedGridRowsSelector(apiRef); + if (selectedRowIds.size > 0) { + return Array.from(selectedRowIds.keys()); + } + + return gridFilteredSortedRowIdsSelector(apiRef); +}; + +export default function PrintExportSelectedRows() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.tsx b/docs/data/data-grid/export/PrintExportSelectedRows.tsx new file mode 100644 index 000000000000..5a0ca2049f12 --- /dev/null +++ b/docs/data/data-grid/export/PrintExportSelectedRows.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { + DataGrid, + GridToolbar, + GridPrintGetRowsToExportParams, + gridFilteredSortedRowIdsSelector, + selectedGridRowsSelector, + GridRowId, +} from '@mui/x-data-grid'; + +const getSelectedRowsToExport = ({ + apiRef, +}: GridPrintGetRowsToExportParams): GridRowId[] => { + const selectedRowIds = selectedGridRowsSelector(apiRef); + if (selectedRowIds.size > 0) { + return Array.from(selectedRowIds.keys()); + } + + return gridFilteredSortedRowIdsSelector(apiRef); +}; + +export default function PrintExportSelectedRows() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview b/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview new file mode 100644 index 000000000000..11e312d98379 --- /dev/null +++ b/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/export.md b/docs/data/data-grid/export/export.md index c442d2bded9a..65dee9725b4a 100644 --- a/docs/data/data-grid/export/export.md +++ b/docs/data/data-grid/export/export.md @@ -88,14 +88,20 @@ There are a few ways to include or hide other columns. ## Exported rows -:::warning -This section only applies to the CSV and the Excel export. -The print export always prints rows in their current state. -::: +### Print export + +The print export always prints all rows regardless of whether or not some rows are selected. +To export only selected rows via print you can use the `getRowsToExport` function. + +{{"demo": "PrintExportSelectedRows.js", "bg": "inline", "defaultCodeOpen": false}} + +### CSV and Excel export By default, the data grid exports the selected rows if there are any. If not, it exports all rows except the footers (filtered and sorted rows, according to active rules), including the collapsed ones. +### Customizing the rows to export + Alternatively, you can set the `getRowsToExport` function and export any rows you want, as in the following example. The grid exports a few [selectors](/x/react-data-grid/state/#access-the-state) that can help you get the rows for the most common use-cases: @@ -186,7 +192,7 @@ With `pageStyle` option, you can override the main content color with a [more sp ### Customize grid display -By default, the print export display all the DataGrid. It is possible to remove the footer and the toolbar by setting respectively `hideFooter` and `hideToolbar` to `true`. +By default, the print export displays all the DataGrid. It is possible to remove the footer and the toolbar by setting respectively `hideFooter` and `hideToolbar` to `true`. ```jsx ``` -For more option to customize the print export, please visit the [`printOptions` API page](/x/api/data-grid/grid-print-export-options/). +If rows are selected when exporting, the checkboxes will not be included in the printed page. To export the checkboxes you can set `includeCheckboxes` to `true`. + +```jsx + +``` + +For more options to customize the print export, please visit the [`printOptions` API page](/x/api/data-grid/grid-print-export-options/). ## Custom export format diff --git a/docs/pages/x/api/data-grid/grid-csv-export-options.md b/docs/pages/x/api/data-grid/grid-csv-export-options.md index 8a279ae4681f..e862ab57a9b3 100644 --- a/docs/pages/x/api/data-grid/grid-csv-export-options.md +++ b/docs/pages/x/api/data-grid/grid-csv-export-options.md @@ -29,7 +29,7 @@ import { GridCsvExportOptions } from '@mui/x-data-grid'; | delimiter? | string | ',' | The character used to separate fields. | | fields? | string[] | | The columns exported.
This should only be used if you want to restrict the columns exports. | | fileName? | string | `document.title` | The string used as the file name. | -| getRowsToExport? | (params: GridCsvGetRowsToExportParams) => GridRowId[] | | Function that returns the id of the rows to export on the order they should be exported. | +| getRowsToExport? | (params: GridCsvGetRowsToExportParams) => GridRowId[] | | Function that returns the list of row ids to export on the order they should be exported. | | includeColumnGroupsHeaders? | boolean | true | If `true`, the CSV will include the column groups. | | includeHeaders? | boolean | true | If `true`, the CSV will include the column headers and column groups.
Use `includeColumnGroupsHeaders` to control whether the column groups are included. | | utf8WithBom? | boolean | false | If `true`, the UTF-8 Byte Order Mark (BOM) prefixes the exported file.
This can allow Excel to automatically detect file encoding as UTF-8. | diff --git a/docs/pages/x/api/data-grid/grid-excel-export-options.md b/docs/pages/x/api/data-grid/grid-excel-export-options.md index a289f4fce216..ee4f465bcffa 100644 --- a/docs/pages/x/api/data-grid/grid-excel-export-options.md +++ b/docs/pages/x/api/data-grid/grid-excel-export-options.md @@ -27,7 +27,7 @@ import { GridExcelExportOptions } from '@mui/x-data-grid-premium'; | exceljsPreProcess? [](/x/introduction/licensing/#premium-plan) | (processInput: GridExceljsProcessInput) => Promise<void> | | Method called before adding the rows to the workbook.
Not supported when `worker` is set.
To use with web workers, use the option in `setupExcelExportWebWorker`. | | fields? [](/x/introduction/licensing/#premium-plan) | string[] | | The columns exported.
This should only be used if you want to restrict the columns exports. | | fileName? [](/x/introduction/licensing/#premium-plan) | string | `document.title` | The string used as the file name. | -| getRowsToExport? [](/x/introduction/licensing/#premium-plan) | (params: GridGetRowsToExportParams<Api>) => GridRowId[] | | Function that returns the id of the rows to export on the order they should be exported. | +| getRowsToExport? [](/x/introduction/licensing/#premium-plan) | (params: GridGetRowsToExportParams<Api>) => GridRowId[] | | Function that returns the list of row ids to export on the order they should be exported. | | includeColumnGroupsHeaders? [](/x/introduction/licensing/#premium-plan) | boolean | true | If `true`, the headers of the column groups will be added into the file. | | includeHeaders? [](/x/introduction/licensing/#premium-plan) | boolean | true | If `true`, the first row of the file will include the headers of the grid. | | valueOptionsSheetName? [](/x/introduction/licensing/#premium-plan) | string | | Name given to the worksheet containing the columns valueOptions.
valueOptions are added to this worksheet if they are provided as an array. | diff --git a/docs/pages/x/api/data-grid/grid-print-export-options.md b/docs/pages/x/api/data-grid/grid-print-export-options.md index ea8b8b8c4242..2106c9ec94a1 100644 --- a/docs/pages/x/api/data-grid/grid-print-export-options.md +++ b/docs/pages/x/api/data-grid/grid-print-export-options.md @@ -23,13 +23,15 @@ import { GridPrintExportOptions } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :---------------------------------------------------------------------------------------------- | :------------------------------------------------ | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | -| allColumns? | boolean | false | If `true`, the hidden columns will also be exported. | -| bodyClassName? | string | | One or more classes passed to the print window. | -| copyStyles? | boolean | true | If `false`, all <style> and <link type="stylesheet" /> tags from the <head> will not be copied
to the print window. | -| fields? | string[] | | The columns exported.
This should only be used if you want to restrict the columns exports. | -| fileName? | string | The title of the page. | The value to be used as the print window title. | -| hideFooter? | boolean | false | If `true`, the footer is removed for when printing. | -| hideToolbar? | boolean | false | If `true`, the toolbar is removed for when printing. | -| pageStyle? | string \| Function | | Provide Print specific styles to the print window. | +| Name | Type | Default | Description | +| :-------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | +| allColumns? | boolean | false | If `true`, the hidden columns will also be exported. | +| bodyClassName? | string | | One or more classes passed to the print window. | +| copyStyles? | boolean | true | If `false`, all <style> and <link type="stylesheet" /> tags from the <head> will not be copied
to the print window. | +| fields? | string[] | | The columns exported.
This should only be used if you want to restrict the columns exports. | +| fileName? | string | The title of the page. | The value to be used as the print window title. | +| getRowsToExport? | (params: GridPrintGetRowsToExportParams) => GridRowId[] | | Function that returns the list of row ids to export in the order they should be exported. | +| hideFooter? | boolean | false | If `true`, the footer is removed for when printing. | +| hideToolbar? | boolean | false | If `true`, the toolbar is removed for when printing. | +| includeCheckboxes? | boolean | false | If `true`, the selection checkboxes will be included when printing. | +| pageStyle? | string \| Function | | Provide Print specific styles to the print window. | diff --git a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx index 306f879977b0..87d108f92a67 100644 --- a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; import { unstable_ownerDocument as ownerDocument } from '@mui/utils'; -import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import { GridApiCommunity, GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { GridPrintExportApi } from '../../../models/api/gridPrintExportApi'; import { useGridLogger } from '../../utils/useGridLogger'; import { gridExpandedRowCountSelector } from '../filter/gridFilterSelector'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; -import { GridPrintExportOptions } from '../../../models/gridExport'; +import { GridPrintExportOptions, GridPrintGetRowsToExportParams } from '../../../models/gridExport'; +import { GridRowId, GridValidRowModel } from '../../../models/gridRows'; import { GridInitialStateCommunity } from '../../../models/gridStateCommunity'; import { gridColumnDefinitionsSelector, @@ -22,6 +23,7 @@ import { GridPrintExportMenuItem, } from '../../../components/toolbar/GridToolbarExport'; import { getTotalHeaderHeight } from '../columns/gridColumnsUtils'; +import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../colDef/gridCheckboxSelectionColDef'; function raf() { return new Promise((resolve) => { @@ -35,7 +37,13 @@ type PrintWindowOnLoad = ( printWindow: HTMLIFrameElement, options?: Pick< GridPrintExportOptions, - 'copyStyles' | 'bodyClassName' | 'pageStyle' | 'hideToolbar' | 'hideFooter' + | 'copyStyles' + | 'bodyClassName' + | 'pageStyle' + | 'hideToolbar' + | 'hideFooter' + | 'includeCheckboxes' + | 'getRowsToExport' >, ) => void; @@ -62,6 +70,7 @@ export const useGridPrintExport = ( const doc = React.useRef(null); const previousGridState = React.useRef(null); const previousColumnVisibility = React.useRef<{ [key: string]: boolean }>({}); + const previousRows = React.useRef([]); React.useEffect(() => { doc.current = ownerDocument(apiRef.current.rootElementRef!.current!); @@ -70,7 +79,7 @@ export const useGridPrintExport = ( // Returns a promise because updateColumns triggers state update and // the new state needs to be in place before the grid can be sized correctly const updateGridColumnsForPrint = React.useCallback( - (fields?: string[], allColumns?: boolean) => + (fields?: string[], allColumns?: boolean, includeCheckboxes?: boolean) => new Promise((resolve) => { const exportedColumnFields = getColumnsToExport({ apiRef, @@ -84,18 +93,34 @@ export const useGridPrintExport = ( newColumnVisibilityModel[column.field] = exportedColumnFields.includes(column.field); }); + if (includeCheckboxes) { + newColumnVisibilityModel[GRID_CHECKBOX_SELECTION_COL_DEF.field] = true; + } + apiRef.current.setColumnVisibilityModel(newColumnVisibilityModel); resolve(); }), [apiRef], ); + const updateGridRowsForPrint = React.useCallback( + ( + getRowsToExport: (params: GridPrintGetRowsToExportParams) => GridRowId[], + ) => { + const rowsToExportIds = getRowsToExport({ apiRef }); + const newRows = rowsToExportIds.map((id) => apiRef.current.getRow(id)); + apiRef.current.setRows(newRows); + }, + [apiRef], + ); + const handlePrintWindowLoad: PrintWindowOnLoad = React.useCallback( (printWindow, options): void => { const normalizeOptions = { copyStyles: true, hideToolbar: false, hideFooter: false, + includeCheckboxes: false, ...options, }; @@ -141,15 +166,27 @@ export const useGridPrintExport = ( } // Expand container height to accommodate all rows - gridClone.style.height = `${ + const computedTotalHeight = rowsMeta.currentPageTotalHeight + getTotalHeaderHeight(apiRef, props.columnHeaderHeight) + gridToolbarElementHeight + - gridFooterElementHeight - }px`; + gridFooterElementHeight; + gridClone.style.height = `${computedTotalHeight}px`; // The height above does not include grid border width, so we need to exclude it gridClone.style.boxSizing = 'content-box'; + // the footer is always being placed at the bottom of the page as if all rows are exported + // so if getRowsToExport is being used to only export a subset of rows then we need to + // adjust the footer position to be correctly placed at the bottom of the grid + if (options?.getRowsToExport) { + const gridFooterElement: HTMLElement | null = gridClone.querySelector( + `.${gridClasses.footerContainer}`, + ); + gridFooterElement!.style.position = 'absolute'; + gridFooterElement!.style.width = '100%'; + gridFooterElement!.style.top = `${computedTotalHeight - gridFooterElementHeight}px`; + } + // printDoc.body.appendChild(gridClone); should be enough but a clone isolation bug in Safari // prevents us to do it const container = document.createElement('div'); @@ -245,10 +282,12 @@ export const useGridPrintExport = ( } apiRef.current.unstable_enableVirtualization(); + apiRef.current.setRows(previousRows.current); // Clear local state previousGridState.current = null; previousColumnVisibility.current = {}; + previousRows.current = []; }, [apiRef], ); @@ -264,6 +303,7 @@ export const useGridPrintExport = ( previousGridState.current = apiRef.current.exportState(); // It appends that the visibility model is not exported, especially if columnVisibility is not controlled previousColumnVisibility.current = gridColumnVisibilityModelSelector(apiRef); + previousRows.current = apiRef.current.getSortedRows(); if (props.pagination) { const visibleRowCount = gridExpandedRowCountSelector(apiRef); @@ -279,7 +319,16 @@ export const useGridPrintExport = ( apiRef.current.forceUpdate(); } - await updateGridColumnsForPrint(options?.fields, options?.allColumns); + await updateGridColumnsForPrint( + options?.fields, + options?.allColumns, + options?.includeCheckboxes, + ); + + if (options?.getRowsToExport) { + updateGridRowsForPrint(options.getRowsToExport); + } + apiRef.current.unstable_disableVirtualization(); await raf(); // wait for the state changes to take action const printWindow = buildPrintWindow(options?.fileName); @@ -310,6 +359,7 @@ export const useGridPrintExport = ( handlePrintWindowLoad, handlePrintWindowAfterPrint, updateGridColumnsForPrint, + updateGridRowsForPrint, ], ); diff --git a/packages/grid/x-data-grid/src/models/gridExport.ts b/packages/grid/x-data-grid/src/models/gridExport.ts index 2969520913f5..46c95e2a8062 100644 --- a/packages/grid/x-data-grid/src/models/gridExport.ts +++ b/packages/grid/x-data-grid/src/models/gridExport.ts @@ -35,9 +35,9 @@ export interface GridFileExportOptions) => GridRowId[]; } @@ -52,6 +52,9 @@ export interface GridGetRowsToExportParams extends GridGetRowsToExportParams {} +export interface GridPrintGetRowsToExportParams + extends GridGetRowsToExportParams {} + /** * The options to apply on the CSV export. * @demos @@ -87,9 +90,9 @@ export interface GridCsvExportOptions extends GridFileExportOptions { */ includeColumnGroupsHeaders?: boolean; /** - * Function that returns the id of the rows to export on the order they should be exported. + * Function that returns the list of row ids to export on the order they should be exported. * @param {GridCsvGetRowsToExportParams} params With all properties from [[GridCsvGetRowsToExportParams]]. - * @returns {GridRowId[]} The id of the rows to export. + * @returns {GridRowId[]} The list of row ids to export. */ getRowsToExport?: (params: GridCsvGetRowsToExportParams) => GridRowId[]; } @@ -115,6 +118,11 @@ export interface GridPrintExportOptions extends GridExportOptions { * @default false */ hideFooter?: boolean; + /** + * If `true`, the selection checkboxes will be included when printing. + * @default false + */ + includeCheckboxes?: boolean; /** * If `false`, all