Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save AndroConsis/b17d1d94deb87478a1284337b4f24de0 to your computer and use it in GitHub Desktop.
Save AndroConsis/b17d1d94deb87478a1284337b4f24de0 to your computer and use it in GitHub Desktop.
Implementing a SharePoint File Picker Modal in React using Ant Design
import { useMsal } from '@azure/msal-react';
import { Breadcrumb, Divider, Layout, List, Modal, Space, Typography } from 'antd';
import React, { useEffect, useState } from 'react';
import { Loader } from '../../../components';
import axios from 'axios';
import FolderOutlined from "@ant-design/icons/FolderOutlined";
import InfoCircleOutlined from "@ant-design/icons/InfoCircleOutlined";
import { filterSharepointFilesByType } from '../../../utils/util';
const { Text, Link } = Typography;
const SelectSharePointFile = ({
open,
onOk,
onCancel
}) => {
const { instance, accounts } = useMsal();
const [loading, setLoading] = useState(true);
const [accessToken, setAccessToken] = useState("");
const [driveItems, setDriveItems] = useState([]);
const [navigationStack, setNavigationStack] = useState([]);
const [selected, setSelected] = useState({});
useEffect(() => {
if (open) {
fetchFiles();
}
return setNavigationStack([]) && setSelected({});
}, [open]);
const getToken = async () => {
if (accessToken) return accessToken;
const silentRequest = {
account: accounts[0]
};
const silentToken = await instance.acquireTokenSilent(silentRequest);
setAccessToken(silentToken.accessToken);
return silentToken.accessToken;
}
const fetchApi = async (url) => {
const token = await getToken();
return axios.get(url, {
"headers": {
"Authorization": "Bearer " + token
}
});
}
const fetchMyFiles = async () => {
let files = [];
const response = await Promise.allSettled([
fetchApi("https://graph.microsoft.com/v1.0/me/drive/root/children"),
fetchApi("https://graph.microsoft.com/v1.0/me/drive/sharedWithMe?allowexternal=true"),
fetchApi("https://graph.microsoft.com/v1.0/me/drive/recent")
]);
for (const child of response) {
if (child.status == "fulfilled") {
const { value = [] } = child.value.data;
files = [...files, ...value];
}
};
const uniqueFiles = files.reduce((acc, item) => {
if (!acc.some(accItem => accItem.id.toString() === item.id.toString())) {
acc.push(item);
}
return acc;
}, []);
const sortedFiles = uniqueFiles.sort((a, b) => {
if ('folder' in a && 'folder' in b) return 0;
if ('folder' in a) return -1;
if ('folder' in b) return 1;
return 0;
});
return sortedFiles;
}
const fetchFolderFiles = async (driveId, id) => {
const response = await fetchApi(`https://graph.microsoft.com/v1.0/me/drives/${driveId}/items/${id}/children`);
return response.data.value;
}
const fetchFiles = async (driveId, id, name = "My Files") => {
setLoading(true)
let files = [];
if (driveId) {
files = await fetchFolderFiles(driveId, id);
} else {
files = await fetchMyFiles();
}
files = filterSharepointFilesByType(files);
setNavigationStack(prevState => [...prevState, { name, value: files }]);
setDriveItems(files);
setLoading(false);
}
const openFolder = (item) => {
if (item.remoteItem) {
item = item.remoteItem;
}
setLoading(true);
fetchFiles(item.parentReference.driveId, item.id, item.name);
}
const onSelect = (item) => {
setSelected(item);
}
const onBreadCrumbClick = (item, nvStack) => {
let index = nvStack.findIndex(obj => obj.name == item.name);
if (index !== -1) {
setNavigationStack(nvStack => {
nvStack.length = index + 1;
return nvStack;
});
setDriveItems(nvStack[nvStack.length - 1]?.value);
}
}
const onConfirmation = () => {
onOk(selected);
}
return <Modal
title="Select Sharepoint File"
centered
open={open}
onOk={onConfirmation}
onCancel={onCancel}
okText={"Select"}
okButtonProps={{
disabled: !selected.id
}}
width={700}
>
<Layout>
<div style={{
height: '30rem',
background: "white"
}}>
<Breadcrumb>
{
navigationStack.map((nv, index) => <Breadcrumb.Item
key={nv.name}>
{index == (navigationStack.length - 1)
? nv.name
: <a onClick={() => onBreadCrumbClick(nv, navigationStack)}>{nv.name}</a>}
</Breadcrumb.Item>)
}
</Breadcrumb>
<Divider style={{ margin: '10px 0' }} />
{loading ?
<Loader /> : <FilePicker
files={driveItems}
openFolder={openFolder}
onSelect={onSelect}
selected={selected}
/>}
</div>
</Layout>
</Modal>
}
const FilePicker = ({ files, openFolder, onSelect, selected }) => {
return <List
size='default'
dataSource={files}
style={{
height: "27rem",
overflowY: "auto"
}}
renderItem={(item) => {
const isFolder = !!item.folder;
return <List.Item
key={item.id}
className={`file-list-item ${item.id == selected.id ? "selected" : ""}`}
onClick={() => isFolder ? openFolder(item) : onSelect(item)}
>
{
isFolder ? <div>
<FolderOutlined /> {item.name}
</div>
:
<Space
align='center'>
{item.name}
<Link
href={item.webUrl}
target="_blank">
<InfoCircleOutlined />
</Link>
</Space>
}
</List.Item>
}}
>
</List>
}
export default SelectSharePointFile;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment