Merge pull request #1798 from chandaniprajapati/react-datatable-fluentui

react-datatable: fluent ui design and added some advanced features
This commit is contained in:
Hugo Bernier 2021-04-05 00:01:03 -04:00 committed by GitHub
commit 6da1e3d6a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 897 additions and 412 deletions

View File

@ -42,6 +42,9 @@ Version|Date|Comments
1.0|February 19, 2021|Initial release
1.1|February 24, 2021|Added support for large lists
1.2|March 01, 2021|Fixed search issue for number field
1.3|March 31,2021| Changed UI as per SharePoint list, Set themeing as per current SharePoint site theme, Created custom pagination by using reusable controls, Added features to export CSV based on the filter if the filter is available, Added hyperlink feature for image and link column in export to pdf and also set alternative row formatting in generated pdf as per property pane configuration odd/even row color, fixed object issue (for people/hyperlink, etc) in export to CSV.
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because it is too large Load Diff

View File

@ -25,9 +25,8 @@
"@pnp/spfx-controls-react": "^2.4.0",
"@pnp/spfx-property-controls": "^2.3.0",
"export-to-csv": "^0.2.1",
"jspdf": "^2.3.0",
"jspdf-autotable": "^3.5.13",
"office-ui-fabric-react": "6.214.0",
"pdfmake": "^0.1.70",
"react": "16.8.5",
"react-dom": "16.8.5"
},

View File

@ -0,0 +1,9 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.exportToCsv {
.btnCSV {
background-color: "[theme:themePrimary, default:#0078d7]";
border-color: "[theme: themePrimary, default: #0078d7]";
color: "[theme:white, default:white]"
}
}

View File

@ -1,20 +1,23 @@
import * as React from 'react';
import * as strings from 'ReactDatatableWebPartStrings';
import { ExportToCsv } from 'export-to-csv';
import { Button } from '@material-ui/core';
import GetAppSharpIcon from '@material-ui/icons/GetAppSharp';
import { IIconProps, PrimaryButton } from 'office-ui-fabric-react';
import styles from './ExportListItemsToCSV.module.scss';
interface IExportToCSV {
columnHeader: Array<string>;
listItems: any[];
listName: string;
description: string;
dataSource: ()=> any[];
}
export function ExportListItemsToCSV(props: IExportToCSV) {
let { columnHeader, listItems, listName, description } = props;
const downloadIcon: IIconProps = { iconName: 'Download' };
let { columnHeader, listName, dataSource } = props;
function generateCSV() {
let colHeader = columnHeader;
const options = {
filename: listName,
@ -30,15 +33,15 @@ export function ExportListItemsToCSV(props: IExportToCSV) {
headers: colHeader
};
const csvExporter = new ExportToCsv(options);
csvExporter.generateCsv(listItems);
csvExporter.generateCsv(dataSource());
}
return (
<Button variant="contained"
<PrimaryButton
text={strings.DownloadAsCSVLabel}
iconProps={downloadIcon}
onClick={() => generateCSV()}
startIcon={<GetAppSharpIcon />}>
{strings.DownloadAsCSVLabel}
</Button>
className={styles.btnCSV}
/>
);
}

View File

@ -0,0 +1,14 @@
export function csvCellFormatter(value: any, type: string) {
if (!value) {
return value;
}
switch (type) {
case 'SP.FieldUrl':
value = value.props.children;
break;
default:
break;
}
return value;
}

View File

@ -0,0 +1,9 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.exportToPdf {
.btnPDF {
background-color: "[theme:themePrimary, default:#0078d7]";
border-color: "[theme: themePrimary, default: #0078d7]";
color: "[theme:white, default:white]"
}
}

View File

@ -1,29 +1,70 @@
import * as React from 'react';
import * as strings from 'ReactDatatableWebPartStrings';
import jsPDF from 'jspdf';
import autoTable from 'jspdf-autotable';
import { Button } from '@material-ui/core';
import GetAppSharpIcon from '@material-ui/icons/GetAppSharp';
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;
import { IIconProps, PrimaryButton } from 'office-ui-fabric-react';
import styles from './ExportListItemsToPDF.module.scss';
import { isNullOrUndefined } from '../../utilities/utilities';
import { IPropertyPaneDropdownOption } from '@microsoft/sp-property-pane';
interface IExportToPDF {
htmlElementForPDF: string;
listName: string;
title: string;
columns: any[];
oddRowColor?: string;
evenRowColor?: string;
dataSource: ()=> any[];
}
export function ExportListItemsToPDF(props: IExportToPDF) {
let { htmlElementForPDF, listName } = props;
const downloadIcon: IIconProps = { iconName: 'Download' };
let { title, listName, columns, oddRowColor, evenRowColor, dataSource } = props;
function genearatePDF() {
const doc = new jsPDF();
autoTable(doc, { html: htmlElementForPDF, theme: 'grid' });
doc.save(`${listName}.pdf`);
}
let dataTableRows = dataSource().map(lItem => columns.reduce((arr, c) => [...arr, isNullOrUndefined(lItem[c]) ? '' : lItem[c]], []));
let data = {
content: [
{
text: title,
fontSize: 16,
alignment: 'center',
margin: [0, 0, 0, 15]
},
{
style: 'tableExample',
table: {
widths: new Array(columns.length).fill("auto"),
headerRows: 1,
body: [
columns.map(c=> ({text: c, bold: true})),
...dataTableRows
]
},
layout: {
fillColor: function (rowIndex: number) {
if (oddRowColor && evenRowColor)
return (rowIndex % 2 === 0) ? evenRowColor : oddRowColor;
else
return (rowIndex % 2 === 0) ? '#CCCCCC' : null;
}
}
}
]
};
pdfMake.createPdf(data).download(`${listName}.pdf`);
}
return (
<Button variant="contained"
<PrimaryButton
text={strings.DownloadAsPDFLabel}
iconProps={downloadIcon}
onClick={() => genearatePDF()}
startIcon={<GetAppSharpIcon />}>
{strings.DownloadAsPDFLabel}
</Button>
className={styles.btnPDF}
/>
);
}

View File

@ -0,0 +1,15 @@
export function pdfCellFormatter(value: any, type: string) {
if (!value) {
return value;
}
switch (type) {
case 'SP.FieldUrl':
let { children: text, href: link } = value.props;
value = { text, link, color: 'blue' };
break;
default:
break;
}
return value;
}

View File

@ -0,0 +1,28 @@
.pagination{
padding-top: 10px;
text-align: center;
float: right;
.rowsPerPage{
float: left;
}
.buttonStyle {
min-width: auto;
text-align: center;
border: 2px solid white;
&:not([class*="ms-Button--primary"]){
background-color: #f0f0f0;
&:hover{
background-color: #f7f7f7;
}
}
}
.buttonStyle:disabled{
color: lightgray;
i{
color: lightgray
}
}
}

View File

@ -1,44 +1,177 @@
import TablePagination from '@material-ui/core/TablePagination';
import * as React from 'react';
import * as React from "react";
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import styles from "./Pagination.module.scss";
import { isEqual } from "lodash";
import { Dropdown, DropdownMenuItemType, IDropdownOption, IDropdownProps } from 'office-ui-fabric-react/lib/Dropdown';
interface IPagination {
colSpan: number;
export interface IPaginationProps {
/**
* The page initial selected
*/
currentPage: number;
/**
* The total items for which you want to generate pagination
*/
totalItems: number;
onPaginationUpdate: (pageNo: number, pageSize: number) => void;
/**
* When the page number change send the page number selected
*/
onChange: (pageNo: number, rowsPerPage: number) => void;
/**
* The number of pages showing before the icon
*/
limiter?: number;
/**
* Hide the quick jump to the first page
*/
hideFirstPageJump?: boolean;
/**
* Hide the quick jump to the last page
*/
hideLastPageJump?: boolean;
/**
* Limitir icon, by default is More icon
*/
limiterIcon?: string;
}
export function Pagination(props: IPagination) {
export interface IPaginationState {
totalPages: number;
currentPage: number;
paginationElements: number[];
limiter: number;
rowsPerPage?: number;
}
export class Pagination extends React.Component<IPaginationProps, IPaginationState> {
constructor(props: Readonly<IPaginationProps>) {
super(props);
this.state = {
currentPage: props.currentPage,
paginationElements : [],
limiter: props.limiter ? props.limiter : 3,
totalPages: 0,
rowsPerPage: 10
};
}
let { colSpan, totalItems, onPaginationUpdate } = props;
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
public componentDidMount(){
let totalPages = this.getTotalPages(this.props.totalItems);
const paginationElements = this.preparePaginationElements(totalPages);
this.setState({totalPages, paginationElements});
}
React.useEffect(() => {
onPaginationUpdate(page, rowsPerPage);
}, [page, rowsPerPage]);
private getTotalPages(totalItems: number) {
return totalItems ? Math.ceil(totalItems / this.state.rowsPerPage) : 0;
}
const handlePageChange = (event, newPage: number) => {
setPage(newPage);
};
public componentDidUpdate(prevProps: IPaginationProps) {
let { currentPage, paginationElements, totalPages } = this.state;
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
if (prevProps.totalItems !== this.props.totalItems) {
totalPages = this.getTotalPages(this.props.totalItems);
paginationElements = this.preparePaginationElements(totalPages);
currentPage = (currentPage > totalPages) ? totalPages : currentPage;
}
if (this.props.currentPage !== prevProps.currentPage) {
currentPage = this.props.currentPage > totalPages ? totalPages : this.props.currentPage;
}
return (
<TablePagination
rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
colSpan={colSpan}
count={totalItems}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { 'aria-label': 'rows per page' },
native: true,
}}
onChangePage={handlePageChange}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
);
if (!isEqual(this.state.currentPage, currentPage) || !isEqual(this.state.paginationElements, paginationElements) || !isEqual(this.state.totalPages, totalPages)) {
this.setState({
paginationElements,
currentPage,
totalPages
});
}
}
public render(): React.ReactElement<IPaginationProps> {
return (
<div className={`${styles.pagination} pagination-container`}>
{!this.props.hideFirstPageJump &&
<DefaultButton
disabled={this.props.currentPage === 1}
className={`${styles.buttonStyle} pagination-button pagination-button_first`}
onClick={() => this.onClick(1)}
iconProps={{ iconName: "DoubleChevronLeft" }}> First
</DefaultButton>
}
<DefaultButton
disabled={this.props.currentPage === 1}
className={`${styles.buttonStyle} pagination-button pagination-button_prev`}
onClick={() => this.onClick(this.state.currentPage - 1)}
iconProps={{ iconName: "ChevronLeft" }}> Prev
</DefaultButton>
{this.state.paginationElements.map((pageNumber) => this.renderPageNumber(pageNumber))}
<DefaultButton
disabled={this.state.totalPages === this.props.currentPage}
className={`${styles.buttonStyle} pagination-button pagination-button_next`}
onClick={() => this.onClick(this.state.currentPage + 1)}
iconProps={{ iconName: "ChevronRight" }}> Next
</DefaultButton>
{!this.props.hideLastPageJump &&
<DefaultButton
disabled={this.state.totalPages === this.props.currentPage}
className={`${styles.buttonStyle} pagination-button pagination-button_last`}
onClick={() => this.onClick(this.state.totalPages)}
iconProps={{ iconName: "DoubleChevronRight" }}> Last
</DefaultButton>
}
</div>
);
}
private preparePaginationElements = (totalPages: number) => {
let paginationElementsArray = [];
for (let i = 0; i < totalPages; i++) {
paginationElementsArray.push(i + 1);
}
return paginationElementsArray;
}
private onClick = (page: number) => {
this.setState({ currentPage: page });
this.props.onChange(page, this.state.rowsPerPage);
}
private renderPageNumber(pageNumber) {
if (pageNumber === this.state.currentPage) {
return (
<PrimaryButton
className={styles.buttonStyle}
onClick={() => this.onClick(pageNumber)}
text={pageNumber}>
</PrimaryButton>
);
} else {
if (!(pageNumber < this.state.currentPage - this.state.limiter || pageNumber > this.state.currentPage + this.state.limiter)) {
return (
<DefaultButton
className={styles.buttonStyle}
onClick={() => this.onClick(pageNumber)}
text={pageNumber}>
</DefaultButton>);
}
else if (!(pageNumber < this.state.currentPage - this.state.limiter - 1 || pageNumber > this.state.currentPage + this.state.limiter + 1)) {
if (this.props.limiterIcon) {
return (<DefaultButton
className={styles.buttonStyle}
onClick={() => this.onClick(pageNumber)}
iconProps={{ iconName: this.props.limiterIcon ? this.props.limiterIcon : "More" }}>
</DefaultButton>);
}
else {
return (<DefaultButton
className={styles.buttonStyle}
onClick={() => this.onClick(pageNumber)}
iconProps={{ iconName: this.props.limiterIcon ? this.props.limiterIcon : "More" }}>
</DefaultButton>);
}
}
else {
return;
}
}
}
}

View File

@ -17,6 +17,9 @@ export class SPService {
for (var i = 0; i < selectedFields.length; i++) {
switch (selectedFields[i].fieldType) {
case 'SP.FieldUser':
selectQuery.push(`${selectedFields[i].key}/Title,${selectedFields[i].key}/Name`);
expandQuery.push(selectedFields[i].key);
break;
case 'SP.FieldLookup':
selectQuery.push(`${selectedFields[i].key}/Title`);
expandQuery.push(selectedFields[i].key);
@ -59,4 +62,14 @@ export class SPService {
Promise.reject(err);
}
}
public async getUserProfileUrl(loginName: string, propertyName: string) {
try {
const profileUrl = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName);
return profileUrl;
}
catch (err) {
Promise.reject(err);
}
}
}

View File

@ -0,0 +1,3 @@
/** utility function to check null or undefined */
export const isNullOrUndefined = (value: any) => value === null || value === undefined;

View File

@ -20,7 +20,7 @@
"default": "Other"
},
"title": {
"default": "react-datatable"
"default": "Datatable"
},
"description": {
"default": "Shows the sharepoint list items in data table format with some advanced features."

View File

@ -1,12 +1,16 @@
import { ColDef } from "@material-ui/data-grid";
import {
IColumn,
} from 'office-ui-fabric-react/lib/DetailsList';
export interface IReactDatatableState {
listItems: any[];
columns: ColDef[];
page: number;
rowsPerPage: number;
searchText: string;
contentType: string;
sortingFields: string;
sortDirection: 'asc'|'desc';
columns: IColumn[];
page: number;
rowsPerPage?: number;
searchText: string;
contentType: string;
sortingFields: string;
pageOfItems: any[];
sortDirection: 'asc' | 'desc';
}

View File

@ -3,8 +3,16 @@
.reactDatatable {
.dataTableUtilities{
margin-bottom: 10px;
margin-top: 10px;
.downloadButtons > *{
margin-right: 10px;
}
}
}
.txtSearchBox{
min-width: 250px;
}
.colHeader{
font-weight: 600;
font-size: 12px;
}
}

View File

@ -6,34 +6,18 @@ import * as strings from 'ReactDatatableWebPartStrings';
import { SPService } from '../../../shared/service/SPService';
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
import { DisplayMode } from '@microsoft/sp-core-library';
import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import { Alert, AlertTitle } from '@material-ui/lab';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableFooter from '@material-ui/core/TableFooter';
import TableRow from '@material-ui/core/TableRow';
import { Grid, InputAdornment, Link, TextField } from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { Grid } from '@material-ui/core';
import { Link, Text } from 'office-ui-fabric-react';
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { withStyles, Theme, createStyles, makeStyles } from '@material-ui/core/styles';
import TableSortLabel from '@material-ui/core/TableSortLabel';
import { ExportListItemsToCSV } from '../../../shared/common/ExportListItemsToCSV/ExportListItemsToCSV';
import { ExportListItemsToPDF } from '../../../shared/common/ExportListItemsToPDF/ExportListItemsToPDF';
import { Pagination } from '../../../shared/common/Pagination/Pagination';
import { DetailsList, DetailsListLayoutMode, DetailsRow, IDetailsRowStyles, IDetailsListProps, IColumn, MessageBar, SelectionMode } from 'office-ui-fabric-react';
import { pdfCellFormatter } from '../../../shared/common/ExportListItemsToPDF/ExportListItemsToPDFFormatter';
import { csvCellFormatter } from '../../../shared/common/ExportListItemsToCSV/ExportListItemsToCSVFormatter';
import { IPropertyPaneDropdownOption } from '@microsoft/sp-property-pane';
const StyledTableCell = withStyles((theme: Theme) =>
createStyles({
head: {
backgroundColor: theme.palette.grey[200],
},
body: {
fontSize: 14,
},
}),
)(TableCell);
export default class ReactDatatable extends React.Component<IReactDatatableProps, IReactDatatableState> {
@ -44,12 +28,13 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
this.state = {
listItems: [],
columns: [],
page: 0,
rowsPerPage: 5,
page: 1,
searchText: '',
rowsPerPage: 10,
sortingFields: '',
sortDirection: 'asc',
contentType: ''
contentType: '',
pageOfItems: []
};
this._services = new SPService(this.props.context);
this._onConfigure = this._onConfigure.bind(this);
@ -80,7 +65,16 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
return ob;
}, {})
}));
let dataGridColumns = [...fields].map(f => ({ field: f.key as string, headerName: f.text }));
let dataGridColumns: IColumn[] = [...fields].map(f => ({
key: f.key as string,
name: f.text,
fieldName: f.key as string,
isResizable: true,
onColumnClick: this.props.sortBy && this.props.sortBy.filter(field => field === f.key).length ? this.handleSorting(f.key as string) : undefined,
minWidth: 70,
maxWidth: 100,
headerClassName: styles.colHeader
}));
this.setState({ listItems: listItems, columns: dataGridColumns });
}
}
@ -88,6 +82,7 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
private _onConfigure() {
this.props.context.propertyPane.open();
}
public formatColumnValue(value: any, type: string) {
if (!value) {
@ -129,6 +124,34 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
}
return value;
}
public formatValueForExportingData(value: any, type?: string) {
if (!value) {
return value;
}
switch (type) {
case 'SP.FieldUser':
let userName = value['Title'];
value = userName;
break;
case 'SP.FieldUrl':
let url = value['Url'];
let description = value['Description'];
value = <a href={url}>{description}</a>;
break;
default:
break;
}
return value;
}
private exportDataFormatter(fields: Array<IPropertyPaneDropdownOption & { fieldType: string }>, listItems: any[], cellFormatterFn: (value: any, type: string) => any){
return listItems && listItems.map(item => ({
...fields.reduce((ob, f) => {
ob[f.text] = item[f.key] ? cellFormatterFn(item[f.key], f.fieldType) : '-';
return ob;
}, {})
}));
}
private handlePaginationChange(pageNo: number, pageSize: number) {
this.setState({ page: pageNo, rowsPerPage: pageSize });
@ -174,22 +197,40 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
private paginateFn = (filterItem: any[]) => {
let { rowsPerPage, page } = this.state;
return (rowsPerPage > 0
? filterItem.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
? filterItem.slice((page - 1) * rowsPerPage, (page - 1) * rowsPerPage + rowsPerPage)
: filterItem
);
}
private handleSorting = (property: string) => (event: React.MouseEvent<unknown>) => {
private handleSorting = (property: string) => (event: React.MouseEvent<unknown>, column: IColumn) => {
property = column.key;
let { sortingFields, sortDirection } = this.state;
const isAsc = sortingFields === property && sortDirection === 'asc';
this.setState({ sortDirection: (isAsc ? 'desc' : 'asc'), sortingFields: property });
const isAsc = sortingFields && sortingFields === property && sortDirection === 'asc';
let updateColumns = this.state.columns.map(c => {
return c.key === property ? { ...c, isSorted: true, isSortedDescending: (isAsc ? false : true) } : { ...c, isSorted: false, isSortedDescending: true };
});
this.setState({ sortDirection: (isAsc ? 'desc' : 'asc'), sortingFields: property, columns: updateColumns });
}
private _onRenderRow: IDetailsListProps['onRenderRow'] = props => {
const customStyles: Partial<IDetailsRowStyles> = {};
if (props) {
if (props.itemIndex % 2 === 0) {
customStyles.root = { backgroundColor: this.props.evenRowColor };
}
else {
customStyles.root = { backgroundColor: this.props.oddRowColor };
}
return <DetailsRow {...props} styles={customStyles} />;
}
return null;
}
public render(): React.ReactElement<IReactDatatableProps> {
let filteredItems = this.filterListItems();
let { list, fields, enableDownloadAsCsv, enableDownloadAsPdf, enablePagination, displayMode, enableSearching, title, evenRowColor, oddRowColor, sortBy } = this.props;
let { sortingFields, sortDirection, columns, listItems } = this.state;
filteredItems = enablePagination ? this.paginateFn(filteredItems) : filteredItems;
let { list, fields, enableDownloadAsCsv, enableDownloadAsPdf, enablePagination, displayMode, enableSearching, title, evenRowColor, oddRowColor } = this.props;
let { columns } = this.state;
let filteredPageItems = enablePagination ? this.paginateFn(filteredItems) : filteredItems;
return (
<div className={styles.reactDatatable}>
@ -201,104 +242,65 @@ export default class ReactDatatable extends React.Component<IReactDatatableProps
description={strings.ConfigureWebpartDescription}
buttonLabel={strings.ConfigureWebpartButtonLabel}
hideButton={displayMode === DisplayMode.Read}
onConfigure={this._onConfigure} /> : <>
<WebPartTitle
title={title}
displayMode={DisplayMode.Read}
updateProperty={() => { }}>
</WebPartTitle>
{ list && fields && fields.length ?
<div>
<Grid container className={styles.dataTableUtilities}>
<Grid item xs={6} className={styles.downloadButtons}>
{
enableDownloadAsCsv
onConfigure={this._onConfigure} /> : <><>
<WebPartTitle
title={title}
displayMode={DisplayMode.Read}
updateProperty={() => { }}>
</WebPartTitle>
{list && fields && fields.length ?
<div>
<Grid container className={styles.dataTableUtilities}>
<Grid item xs={6} className={styles.downloadButtons}>
{enableDownloadAsCsv
? <ExportListItemsToCSV
columnHeader={columns.map(c => c.headerName)}
columnHeader={columns.map(c => c.name)}
listName={list}
description={title}
listItems={listItems}
/> : <></>
}
{
enableDownloadAsPdf
dataSource={()=> this.exportDataFormatter(fields, filteredItems, csvCellFormatter)}
/> : <></>}
{enableDownloadAsPdf
? <ExportListItemsToPDF
listName={list}
htmlElementForPDF='#dataTable'
/>
: <></>
}
</Grid>
<Grid container justify='flex-end' xs={6}>
{
enableSearching ?
title={title}
columns={columns.map(c => c.name)}
oddRowColor={oddRowColor}
evenRowColor={evenRowColor}
dataSource={()=> this.exportDataFormatter(fields, filteredItems, pdfCellFormatter)} />
: <></>}
</Grid>
<Grid container justify='flex-end' xs={6}>
{enableSearching ?
<TextField
onChange={this.handleSearch.bind(this)}
size="small"
label="Search"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<SearchIcon />
</InputAdornment>
),
}}
/>
: <></>
}
placeholder="Search"
className={styles.txtSearchBox} />
: <></>}
</Grid>
</Grid>
</Grid>
<div id="generateTable">
<TableContainer component={Paper} >
<Table aria-label="customized table" id="dataTable" >
<TableHead>
<TableRow>
{columns.map((c) => (
<StyledTableCell key={c.headerName}>
{
(sortBy && sortBy.indexOf(c.field) !== -1)
? <TableSortLabel
active={sortingFields === c.field}
direction={sortingFields === c.field ? sortDirection : 'asc'}
onClick={this.handleSorting(c.field)}
>
{c.headerName}
</TableSortLabel>
: c.headerName
}
</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{filteredItems.map((row, index) => (
<TableRow
style={{ backgroundColor: ((index + 1) % 2 === 0) ? evenRowColor : oddRowColor }} >
{columns.map((c) => (
<StyledTableCell >{row[c.field]}</StyledTableCell >
))}
</TableRow>
))}
</TableBody>
{enablePagination ?
<React.Fragment>
<TableFooter>
<TableRow>
<Pagination
colSpan={columns.length}
onPaginationUpdate={this.handlePaginationChange.bind(this)}
totalItems={listItems.length} />
</TableRow>
</TableFooter>
</React.Fragment> : <></>
}
</Table>
</TableContainer>
</div>
</div> : <Alert severity="info">
{strings.ListFieldValidation}</Alert>
}</>
<div id="generateTable">
<DetailsList
items={filteredPageItems}
columns={columns}
selectionMode={SelectionMode.none}
layoutMode={DetailsListLayoutMode.justified}
isHeaderVisible={true}
onRenderRow={this._onRenderRow}
/>
<div>
{this.props.enablePagination ?
<Pagination
currentPage={this.state.page}
totalItems={filteredItems.length}
onChange={this.handlePaginationChange.bind(this)}
/>
: <></>}
</div>
</div>
</div> : <MessageBar>
{strings.ListFieldValidation}
</MessageBar>}</>
</>
}
</div >
);

View File

@ -7,7 +7,7 @@ define([], function () {
"ConfigureWebpartDescription": "Please configure the web part.",
"ListFieldValidation": "Please select the list fields.",
"ListPickerLabel": "Select a list",
"MultiSelectFieldLabel": "Multi select field",
"MultiSelectFieldLabel": "Select list fields",
"SortingToggleLabel": "Enable Sort",
"SortByLabel": "Default Sort By",
"SearchingToggleLabel": "Enable Search",

View File

@ -11,6 +11,7 @@
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"downlevelIteration": true,
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
@ -25,7 +26,9 @@
"lib": [
"es5",
"dom",
"es2015.collection"
"es2015",
"es2015.collection",
"ES2017"
]
},
"include": [
@ -36,4 +39,4 @@
"node_modules",
"lib"
]
}
}