Backend API
import React from 'react';
import { jsPDF } from 'jspdf';
import DataGrid, {
Column, Summary, GroupPanel, Grouping, SortByGroupSummaryInfo, TotalItem, Export,
} from 'devextreme-react/data-grid';
import type { DataGridTypes } from 'devextreme-react/data-grid';
import { exportDataGrid } from 'devextreme-react/common/export/pdf';
import type { DataGridExportOptions } from 'devextreme-react/common/export/pdf';
import { companies } from './data.ts';
type CustomizeCellOptions = Parameters<Required<DataGridExportOptions>['customizeCell']>[number];
type CustomDrawCellOptions = Parameters<Required<DataGridExportOptions>['customDrawCell']>[number];
const exportFormats = ['pdf'];
const onExporting = (e: DataGridTypes.ExportingEvent) => {
const doc = new jsPDF();
exportDataGrid({
jsPDFDocument: doc,
component: e.component,
columnWidths: [40, 40, 30, 30, 40],
customizeCell({ gridCell, pdfCell }: CustomizeCellOptions) {
if (gridCell?.rowType === 'data' && gridCell?.column?.dataField === 'Phone' && pdfCell?.text) {
pdfCell.text = pdfCell.text.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
} else if (gridCell?.rowType === 'group' && pdfCell) {
pdfCell.backgroundColor = '#BEDFE6';
} else if (gridCell?.rowType === 'totalFooter' && pdfCell?.font) {
pdfCell.font.style = 'italic';
}
},
customDrawCell(options: CustomDrawCellOptions) {
const { gridCell, pdfCell, rect } = options;
if (gridCell && pdfCell && rect && gridCell.rowType === 'data' && gridCell.column?.dataField === 'Website') {
options.cancel = true;
doc.setFontSize(11);
doc.setTextColor('#0000FF');
const textHeight = pdfCell.text ? doc.getTextDimensions(pdfCell.text).h : 0;
doc.textWithLink('website',
rect.x + (pdfCell.padding?.left ?? 0),
rect.y + rect.h / 2 + textHeight / 2,
{ url: pdfCell.text });
}
},
}).then(() => {
doc.save('Companies.pdf');
});
};
const renderGridCell = (data: DataGridTypes.ColumnCellTemplateData) => (
<a href={ data.text } target='_blank' rel='noopener noreferrer'>Website</a>
);
const phoneNumberFormat = (value: number) => {
const valueStr = String(value);
const USNumber = valueStr.match(/(\d{3})(\d{3})(\d{4})/);
return USNumber ? `(${USNumber[1]}) ${USNumber[2]}-${USNumber[3]}` : '';
};
const App = () => (
<div>
<DataGrid
id="gridContainer"
dataSource={companies}
keyExpr="ID"
width="100%"
showBorders={true}
onExporting={onExporting}>
<Export enabled={true} formats={exportFormats} />
<GroupPanel visible={true} />
<Grouping autoExpandAll={true} />
<SortByGroupSummaryInfo summaryItem="count" />
<Column dataField="Name" width={190} />
<Column dataField="Address" width={200} />
<Column dataField="City" />
<Column dataField="State" groupIndex={0} />
<Column dataField="Phone" format={phoneNumberFormat} />
<Column dataField="Website" alignment="center" width={100} cellRender={(e: DataGridTypes.ColumnCellTemplateData) => renderGridCell(e)} />
<Summary>
<TotalItem
column="Name"
summaryType="count"
displayFormat="Total count: {0}"
/>
</Summary>
</DataGrid>
</div>
);
export default App;
import React from 'react';
import { jsPDF } from 'jspdf';
import DataGrid, {
Column,
Summary,
GroupPanel,
Grouping,
SortByGroupSummaryInfo,
TotalItem,
Export,
} from 'devextreme-react/data-grid';
import { exportDataGrid } from 'devextreme-react/common/export/pdf';
import { companies } from './data.js';
const exportFormats = ['pdf'];
const onExporting = (e) => {
const doc = new jsPDF();
exportDataGrid({
jsPDFDocument: doc,
component: e.component,
columnWidths: [40, 40, 30, 30, 40],
customizeCell({ gridCell, pdfCell }) {
if (
gridCell?.rowType === 'data' &&
gridCell?.column?.dataField === 'Phone' &&
pdfCell?.text
) {
pdfCell.text = pdfCell.text.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
} else if (gridCell?.rowType === 'group' && pdfCell) {
pdfCell.backgroundColor = '#BEDFE6';
} else if (gridCell?.rowType === 'totalFooter' && pdfCell?.font) {
pdfCell.font.style = 'italic';
}
},
customDrawCell(options) {
const { gridCell, pdfCell, rect } = options;
if (
gridCell &&
pdfCell &&
rect &&
gridCell.rowType === 'data' &&
gridCell.column?.dataField === 'Website'
) {
options.cancel = true;
doc.setFontSize(11);
doc.setTextColor('#0000FF');
const textHeight = pdfCell.text ? doc.getTextDimensions(pdfCell.text).h : 0;
doc.textWithLink(
'website',
rect.x + (pdfCell.padding?.left ?? 0),
rect.y + rect.h / 2 + textHeight / 2,
{ url: pdfCell.text },
);
}
},
}).then(() => {
doc.save('Companies.pdf');
});
};
const renderGridCell = (data) => (
<a
href={data.text}
target="_blank"
rel="noopener noreferrer"
>
Website
</a>
);
const phoneNumberFormat = (value) => {
const valueStr = String(value);
const USNumber = valueStr.match(/(\d{3})(\d{3})(\d{4})/);
return USNumber ? `(${USNumber[1]}) ${USNumber[2]}-${USNumber[3]}` : '';
};
const App = () => (
<div>
<DataGrid
id="gridContainer"
dataSource={companies}
keyExpr="ID"
width="100%"
showBorders={true}
onExporting={onExporting}
>
<Export
enabled={true}
formats={exportFormats}
/>
<GroupPanel visible={true} />
<Grouping autoExpandAll={true} />
<SortByGroupSummaryInfo summaryItem="count" />
<Column
dataField="Name"
width={190}
/>
<Column
dataField="Address"
width={200}
/>
<Column dataField="City" />
<Column
dataField="State"
groupIndex={0}
/>
<Column
dataField="Phone"
format={phoneNumberFormat}
/>
<Column
dataField="Website"
alignment="center"
width={100}
cellRender={(e) => renderGridCell(e)}
/>
<Summary>
<TotalItem
column="Name"
summaryType="count"
displayFormat="Total count: {0}"
/>
</Summary>
</DataGrid>
</div>
);
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
export const companies = [{
ID: 1,
Name: 'Super Mart of the West',
Address: '702 SW 8th Street',
City: 'Bentonville',
State: 'Arkansas',
Zipcode: 72716,
Phone: '8005552797',
Fax: '(800) 555-2171',
Website: 'http://nowebsitesupermart.dx',
}, {
ID: 2,
Name: 'Electronics Depot',
Address: '2455 Paces Ferry Road NW',
City: 'Atlanta',
State: 'Georgia',
Zipcode: 30339,
Phone: '8005953232',
Fax: '(800) 595-3231',
Website: 'http://nowebsitedepot.dx',
}, {
ID: 3,
Name: 'K&S Music',
Address: '1000 Nicllet Mall',
City: 'Minneapolis',
State: 'Minnesota',
Zipcode: 55403,
Phone: '6123046073',
Fax: '(612) 304-6074',
Website: 'http://nowebsitemusic.dx',
}, {
ID: 4,
Name: 'Tom\'s Club',
Address: '999 Lake Drive',
City: 'Issaquah',
State: 'Washington',
Zipcode: 98027,
Phone: '8009552292',
Fax: '(800) 955-2293',
Website: 'http://nowebsitetomsclub.dx',
}, {
ID: 5,
Name: 'E-Mart',
Address: '3333 Beverly Rd',
City: 'Hoffman Estates',
State: 'Illinois',
Zipcode: 60179,
Phone: '8472862500',
Fax: '(847) 286-2501',
Website: 'http://nowebsiteemart.dx',
}, {
ID: 6,
Name: 'Walters',
Address: '200 Wilmot Rd',
City: 'Deerfield',
State: 'Illinois',
Zipcode: 60015,
Phone: '8479402500',
Fax: '(847) 940-2501',
Website: 'http://nowebsitewalters.dx',
}, {
ID: 7,
Name: 'StereoShack',
Address: '400 Commerce S',
City: 'Fort Worth',
State: 'Texas',
Zipcode: 76102,
Phone: '8178200741',
Fax: '(817) 820-0742',
Website: 'http://nowebsiteshack.dx',
}, {
ID: 8,
Name: 'Circuit Town',
Address: '2200 Kensington Court',
City: 'Oak Brook',
State: 'Illinois',
Zipcode: 60523,
Phone: '8009552929',
Fax: '(800) 955-9392',
Website: 'http://nowebsitecircuittown.dx',
}, {
ID: 9,
Name: 'Premier Buy',
Address: '7601 Penn Avenue South',
City: 'Richfield',
State: 'Minnesota',
Zipcode: 55423,
Phone: '6122911000',
Fax: '(612) 291-2001',
Website: 'http://nowebsitepremierbuy.dx',
}, {
ID: 10,
Name: 'ElectrixMax',
Address: '263 Shuman Blvd',
City: 'Naperville',
State: 'Illinois',
Zipcode: 60563,
Phone: '6304387800',
Fax: '(630) 438-7801',
Website: 'http://nowebsiteelectrixmax.dx',
}, {
ID: 11,
Name: 'Video Emporium',
Address: '1201 Elm Street',
City: 'Dallas',
State: 'Texas',
Zipcode: 75270,
Phone: '2148543000',
Fax: '(214) 854-3001',
Website: 'http://nowebsitevideoemporium.dx',
}, {
ID: 12,
Name: 'Screen Shop',
Address: '1000 Lowes Blvd',
City: 'Mooresville',
State: 'North Carolina',
Zipcode: 28117,
Phone: '8004456937',
Fax: '(800) 445-6938',
Website: 'http://nowebsitescreenshop.dx',
}];
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
export const companies = [
{
ID: 1,
Name: 'Super Mart of the West',
Address: '702 SW 8th Street',
City: 'Bentonville',
State: 'Arkansas',
Zipcode: 72716,
Phone: '8005552797',
Fax: '(800) 555-2171',
Website: 'http://nowebsitesupermart.dx',
},
{
ID: 2,
Name: 'Electronics Depot',
Address: '2455 Paces Ferry Road NW',
City: 'Atlanta',
State: 'Georgia',
Zipcode: 30339,
Phone: '8005953232',
Fax: '(800) 595-3231',
Website: 'http://nowebsitedepot.dx',
},
{
ID: 3,
Name: 'K&S Music',
Address: '1000 Nicllet Mall',
City: 'Minneapolis',
State: 'Minnesota',
Zipcode: 55403,
Phone: '6123046073',
Fax: '(612) 304-6074',
Website: 'http://nowebsitemusic.dx',
},
{
ID: 4,
Name: "Tom's Club",
Address: '999 Lake Drive',
City: 'Issaquah',
State: 'Washington',
Zipcode: 98027,
Phone: '8009552292',
Fax: '(800) 955-2293',
Website: 'http://nowebsitetomsclub.dx',
},
{
ID: 5,
Name: 'E-Mart',
Address: '3333 Beverly Rd',
City: 'Hoffman Estates',
State: 'Illinois',
Zipcode: 60179,
Phone: '8472862500',
Fax: '(847) 286-2501',
Website: 'http://nowebsiteemart.dx',
},
{
ID: 6,
Name: 'Walters',
Address: '200 Wilmot Rd',
City: 'Deerfield',
State: 'Illinois',
Zipcode: 60015,
Phone: '8479402500',
Fax: '(847) 940-2501',
Website: 'http://nowebsitewalters.dx',
},
{
ID: 7,
Name: 'StereoShack',
Address: '400 Commerce S',
City: 'Fort Worth',
State: 'Texas',
Zipcode: 76102,
Phone: '8178200741',
Fax: '(817) 820-0742',
Website: 'http://nowebsiteshack.dx',
},
{
ID: 8,
Name: 'Circuit Town',
Address: '2200 Kensington Court',
City: 'Oak Brook',
State: 'Illinois',
Zipcode: 60523,
Phone: '8009552929',
Fax: '(800) 955-9392',
Website: 'http://nowebsitecircuittown.dx',
},
{
ID: 9,
Name: 'Premier Buy',
Address: '7601 Penn Avenue South',
City: 'Richfield',
State: 'Minnesota',
Zipcode: 55423,
Phone: '6122911000',
Fax: '(612) 291-2001',
Website: 'http://nowebsitepremierbuy.dx',
},
{
ID: 10,
Name: 'ElectrixMax',
Address: '263 Shuman Blvd',
City: 'Naperville',
State: 'Illinois',
Zipcode: 60563,
Phone: '6304387800',
Fax: '(630) 438-7801',
Website: 'http://nowebsiteelectrixmax.dx',
},
{
ID: 11,
Name: 'Video Emporium',
Address: '1201 Elm Street',
City: 'Dallas',
State: 'Texas',
Zipcode: 75270,
Phone: '2148543000',
Fax: '(214) 854-3001',
Website: 'http://nowebsitevideoemporium.dx',
},
{
ID: 12,
Name: 'Screen Shop',
Address: '1000 Lowes Blvd',
City: 'Mooresville',
State: 'North Carolina',
Zipcode: 28117,
Phone: '8004456937',
Fax: '(800) 445-6938',
Website: 'http://nowebsitescreenshop.dx',
},
];
<!DOCTYPE html>
<html lang="en">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/26.1.5/css/dx.light.css" />
<link rel="stylesheet" type="text/css" href="./bundle.css" />
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
<script src="bundles/vendor/react.vendor.js"></script>
<script src="./bundle.js"></script>
</body>
</html>
#gridContainer {
height: 436px;
}
In this demo, the customizeCell function changes the following elements:
- Background color and font weight of group row cells
- Font style of a footer cell
The customDrawCell function allows you to draw cells in a PDF document. The following parameters are available when you use this function:
- doc
An instance of the jsPDFDocument object. - rect
A cell’s bounds. - gridCell
Contains information about the source DataGrid cell. - pdfCell
Contains settings applied to a cell in a PDF document. - cancel
Set this parameter to true to prevent default painting logic.
This demo uses the customDrawCell function to paint cells in the "Website" column.