- Published on
PnP Listview and contextual menu in the SharePoint Framework (SPFx) web part
This article explains how to implement the PnP ListView and Contextual Menu in the SharePoint Framework (SPFx) web part.
It allows you to display files in a list view and add actions such as open, download, and delete using the contextual menu options.
📦 Install dependencies
npm install @pnp/sp --save
npm install office-ui-fabric-react --save
npm install moment --save
🧩 filemenu.tsx
import * as React from 'react';
import { IconButton } from 'office-ui-fabric-react';
import { ContextualMenuItemType } from 'office-ui-fabric-react/lib/ContextualMenu';
import { sp } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import { autobind } from 'office-ui-fabric-react/lib/Utilities';
export interface IfilemenuProps {
item: any;
context: any;
ondatachange: any;
}
export class filemenu extends React.Component<IfilemenuProps, {}> {
public constructor(props: IfilemenuProps) {
super(props);
sp.setup({
spfxContext: this.props.context
});
this.state = {
panelOpen: false
};
}
public render() {
return (
<div>
<IconButton id='ContextualMenuButton1'
text=''
width='30'
split={false}
iconProps={{ iconName: 'MoreVertical' }}
menuIconProps={{ iconName: '' }}
menuProps={{
shouldFocusOnMount: true,
items: [
{
key: 'action1',
name: 'Open in new tab',
onClick: this.handleClick.bind(this, "open", this.props.item)
},
{
key: 'divider_1',
itemType: ContextualMenuItemType.Divider
},
{
key: 'action2',
name: 'Download',
onClick: this.handleClick.bind(this, "download", this.props.item)
},
{
key: 'action3',
name: 'Delete',
onClick: this.handleClick.bind(this, "delete", this.props.item)
},
{
key: 'disabled',
name: 'Disabled action',
disabled: true,
onClick: () => console.error('Disabled action should not be clickable.')
}
]
}} />
</div>
);
}
@autobind
private async handleClick(actionType: string, seletedfile: any, event) {
if (actionType === 'open') {
window.open(
window.location.protocol + "//" + window.location.host + seletedfile.ServerRelativeUrl + "?web=1",
'_blank'
);
}
else if (actionType === 'download') {
window.open(
window.location.protocol + "//" + window.location.host + seletedfile.ServerRelativeUrl + "?web=0",
'_blank'
);
}
else if (actionType === 'delete') {
let list = sp.web.lists.getByTitle("Policies");
await list.items.getById(seletedfile["ListItemAllFields.ID"]).delete();
this.props.ondatachange();
}
}
}
🧩 SpfxPnpListviewContextualmenu.tsx
import * as React from 'react';
import { IViewField } from "@pnp/spfx-controls-react/lib/ListView";
import { filemenu, IfilemenuProps } from './filemenu';
import { sp } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import * as moment from 'moment';
export default class SpfxPnpListviewContextualmenu extends React.Component<ISpfxPnpListviewContextualmenuProps, ISpfxPnpListviewContextualmenuState> {
constructor(props: ISpfxPnpListviewContextualmenuProps, state: ISpfxPnpListviewContextualmenuState) {
super(props);
sp.setup({
spfxContext: this.props.context
});
var _viewFields: IViewField[] = [
{
name: "Name",
linkPropertyName: "ServerRelativeUrl",
displayName: "Name",
sorting: true,
minWidth: 250,
},
{
name: "",
sorting: false,
maxWidth: 40,
render: (rowitem: any) => {
const element: React.ReactElement<IfilemenuProps> = React.createElement(
filemenu,
{
item: rowitem,
context: this.props.context,
ondatachange: this._getfiles
}
);
return element;
}
},
{
name: "Author.Title",
displayName: "Author",
sorting: false,
minWidth: 180,
render: (item: any) => {
const authoremail = item['Author.UserPrincipalName'];
return <a href={'mailto:' + authoremail}>{item['Author.Title']}</a>;
}
},
{
name: "TimeCreated",
displayName: "Created",
minWidth: 150,
render: (item: any) => {
const created = item["TimeCreated"];
if (created) {
const createdDate = moment(created);
return <span>{createdDate.format('DD/MM/YYYY HH:mm:ss')}</span>;
}
}
}
];
this.state = { items: [], viewFields: _viewFields };
this._getfiles();
}
}
🚀 Deploy the solution
Build, bundle, and package the SPFx web part:
gulp build
gulp bundle --ship
gulp package-solution --ship
Upload the .sppkg
file to your App Catalog, deploy it, and add the PnP ListView Contextual Menu web part to your SharePoint page.
📂 GitHub Source
View full SPFx project on GitHub:PnP Listview and contextual menu in the SharePoint Framework (SPFx) web part
✅ Summary
This example shows how to combine PnP ListView and Fluent UI Contextual Menu for SharePoint to display, open, download, and delete files easily.
It provides a powerful and modern user experience for document libraries using the SPFx framework.
Author
- Ravichandran@Hi_Ravichandran