Merge pull request #1038 from ejazhussain/react-msgraph-extension

React msgraph extension
This commit is contained in:
Laura Kokkarinen 2019-11-03 17:20:22 +02:00 committed by GitHub
commit 62980c4d6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 19651 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.9.1",
"libraryName": "react-msgraph-extension",
"libraryId": "7ecf1b13-cb5b-451d-bfda-b11fa316d6c1",
"environment": "spo",
"packageManager": "npm",
"isCreatingSolution": true,
"isDomainIsolated": true,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,68 @@
## react-msgraph-extension
## Summary
This sample shows how to managed Microsoft Graph Open Extension in SPFX. This application uses **User** Resource to create Open Extension.
## ScreenShots
### Create a new Microsoft Graph Open Extension
![Create a new Microsoft Graph Open Extension](./assets/create-graph-extension.png)
### Get an existing Microsoft Graph Open Extension
![Get existing Microsoft Graph Open Extension](./assets/get-graph-extension.png)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.9.1-green.svg)
## Applies to
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
## Prerequisites
> You need following set of permissions in order to manage Microsoft Open Graph Extension.Find out more about consuming the [Microsoft Graph API in the SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/use-aad-tutorial)<br><br>![Microsoft Graph API Permissions](./assets/graph-extension-user-permissions.png)
## Solution
Solution|Author(s)
--------|---------
react-msgraph-extension | Ejaz Hussain
## Version history
Version|Date|Comments
-------|----|--------
1.0|October 20, 2019|Initial release
## 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.**
---
## Minimal Path to Awesome
- Clone this repository
- in the command line run:
- `npm install`
- `gulp serve`
If you have not previously granted the required Microsoft Graph permissions, you need to:
- Run `gulp bundle --ship`
- Run `gulp package-solution --ship`
- Install the .sppkg file (under .\sharepoint\solution) to the SP app catalog
- Approve the API permissions in the new SP admin center
## Features
Here are main features for this application
- Create a new Open Graph Extension
- Get an existing Graph Open Extension
- Update an existing Open Graph Extension
- Remove an existing Open Graph Extension
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-msgraph-extension" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"graphextension-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/graphextension/GraphextensionWebPart.js",
"manifest": "./src/webparts/graphextension/GraphextensionWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"GraphextensionWebPartStrings": "lib/webparts/graphextension/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-msgraph-extension",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,24 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-msgraph-extension-client-side-solution",
"id": "7ecf1b13-cb5b-451d-bfda-b11fa316d6c1",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "User.ReadBasic.All"
},
{
"resource": "Microsoft Graph",
"scope": "User.ReadWrite.All"
}
]
},
"paths": {
"zippedPackage": "solution/react-msgraph-extension.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,7 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
{
"name": "react-msgraph-extension",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.9.1",
"@microsoft/sp-lodash-subset": "1.9.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
"@microsoft/sp-webpart-base": "1.9.1",
"@pnp/common": "^1.3.6",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"office-ui-fabric-react": "6.189.2",
"react": "16.8.5",
"react-dom": "16.8.5",
"react-toastify": "^5.4.0",
"semantic-ui-react": "^0.88.1"
},
"resolutions": {
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.9.1",
"@microsoft/sp-tslint-rules": "1.9.1",
"@microsoft/sp-module-interfaces": "1.9.1",
"@microsoft/sp-webpart-workbench": "1.9.1",
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1,5 @@
export default class Constants {
public static readonly ExtensionName = "com.ejazhussain.settings";
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,4 @@
export interface IFormSchema {
Theme?: string;
Tags?: string;
}

View File

@ -0,0 +1,13 @@
export default class IPersona {
public name: string;
public email: string;
public phone: string;
public photo: string;
constructor(name: string, email: string, phone: string, photo: string) {
this.name = name;
this.email = email;
this.phone = phone;
this.photo = photo;
}
}

View File

@ -0,0 +1,164 @@
import { MSGraphClient } from "@microsoft/sp-http";
import IPersona from "./../models/IPersona";
import Constants from '../common/constants';
export class MsGraphService {
private context = null;
/**
*
*/
constructor(context: any) {
this.context = context;
}
public async PatchExtension(userSettings: any): Promise<any> {
try {
let result = await this.PATCH(`/me/extensions/${Constants.ExtensionName}`, JSON.stringify(userSettings));
return result;
}
catch (error) {
console.log("Error in PatchExtension:", error);
return null;
}
}
public async CreateExtension(userSettings: any): Promise<any> {
try {
let result = await this.POST(`/me/extensions`, JSON.stringify(userSettings));
return result;
}
catch (error) {
console.log("Error in CreateExtension:", error);
return null;
}
}
public async GetExtension(): Promise<any> {
try {
let result = await this.GET(`/me/extensions/${Constants.ExtensionName}`);
return result;
}
catch (error) {
console.log("Error in GetExtension:", error);
return null;
}
}
public async DeleteExtension(): Promise<any> {
try {
let result = await this.DELETE(`/me/extensions/${Constants.ExtensionName}`);
return result;
}
catch (error) {
console.log("Error in DeleteExtension:", error);
return null;
}
}
public async GetUserProfile(): Promise<any> {
try {
let userResponse: any = await this.GET("/me");
let photoResponse: any = await this.GET("/me/photo/$value", "blob");
let user = {
name: userResponse.displayName,
email: userResponse.mail,
phone: userResponse.businessPhones[0],
photo: window.URL.createObjectURL(photoResponse)
} as IPersona;
return user;
}
catch (error) {
console.log("Error in GetUserProfile:", error);
return null;
}
}
private GET(query: string, responseType?: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.context.msGraphClientFactory.getClient().then((client: MSGraphClient): void => {
client.api(query).responseType(responseType)
.get((error, response) => {
if (error) {
reject(error);
return;
}
resolve(response);
});
});
});
}
private POST(query: string, content: string) {
return new Promise<any>((resolve, reject) => {
this.context.msGraphClientFactory.getClient().then((client: MSGraphClient): void => {
client.api(query)
.post(content, (error, response) => {
if (error) {
reject(error);
}
resolve(response);
});
});
});
}
private PATCH(query: string, content: string) {
return new Promise<any>((resolve, reject) => {
this.context.msGraphClientFactory.getClient().then((client: MSGraphClient): void => {
client.api(query)
.patch(content, (error, response, rawResponse) => {
if (error) {
reject(error);
}
resolve(rawResponse);
});
});
});
}
private DELETE(query: string) {
return new Promise<any>((resolve, reject) => {
this.context.msGraphClientFactory.getClient().then((client: MSGraphClient): void => {
client.api(query)
.delete((error, response, rawResponse) => {
if (error) {
reject(error);
}
resolve(rawResponse);
});
});
});
}
}

View File

@ -0,0 +1,27 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "8cc62b32-de8c-4600-bdee-325dc55f587f",
"alias": "GraphextensionWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "SpfxSolutions" },
"title": { "default": "Graph Extension" },
"description": { "default": "This solution provide working demo to create, get and update microsoft graph extensions" },
"officeFabricIconFontName": "Emoji2",
"properties": {
"description": "graphextension"
}
}]
}

View File

@ -0,0 +1,69 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'GraphextensionWebPartStrings';
import Graphextension from './components/Graphextension';
import { IGraphextensionProps } from './components/IGraphextensionProps';
import { SPComponentLoader } from '@microsoft/sp-loader';
import { ToastContainer, toast } from 'react-toastify';
export interface IGraphextensionWebPartProps {
description: string;
}
export default class GraphextensionWebPart extends BaseClientSideWebPart<IGraphextensionWebPartProps> {
protected onInit(): Promise<void> {
//toast.configure()
SPComponentLoader.loadCss("https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css");
return super.onInit();
}
public render(): void {
const element: React.ReactElement<IGraphextensionProps > = React.createElement(
Graphextension,
{
webpartContext: this.context
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,96 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.graphextension {
.container {
width: 100%;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-neutralDark;
//background-color: $ms-color-themeDark;
padding: 20px;
}
.column12 {
@include ms-Grid-col;
@include ms-lg12;
// @include ms-xl8;
// @include ms-xlPush2;
// @include ms-lgPush1;
}
.column10 {
@include ms-Grid-col;
@include ms-lg10;
}
.column8 {
@include ms-Grid-col;
@include ms-lg8;
}
.column6 {
@include ms-Grid-col;
@include ms-lg6;
}
.column4 {
@include ms-Grid-col;
@include ms-lg4;
}
.column2 {
@include ms-Grid-col;
@include ms-lg2;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
margin:20px 0;
min-width: 80px;
//background-color: $ms-color-themePrimary;
//border-color: $ms-color-themePrimary;
//color: $ms-color-white;
// Basic Button
// outline: transparent;
// position: relative;
// font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
// -webkit-font-smoothing: antialiased;
// font-size: $ms-font-size-m;
// font-weight: $ms-font-weight-regular;
// border-width: 0;
// text-align: center;
// cursor: pointer;
// display: inline-block;
// padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,279 @@
import * as React from 'react';
import styles from './Graphextension.module.scss';
import { IGraphextensionProps } from './IGraphextensionProps';
import { escape, isEmpty } from '@microsoft/sp-lodash-subset';
import { MsGraphService } from '../../../services/MSGraphService';
import { Tab, Header } from 'semantic-ui-react';
import { TextField, MaskedTextField } from 'office-ui-fabric-react/lib/TextField';
import { Stack, IStackProps } from 'office-ui-fabric-react/lib/Stack';
import { IFormSchema } from '../../../models/IFormSchema';
import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react';
import Constants from './../../../common/constants';
import { ToastContainer, toast } from 'react-toastify';
import { string } from 'prop-types';
require('./ReactToastify.css');
import { stringIsNullOrEmpty } from "@pnp/common";
export interface IGraphextensionState {
schemaForm?: IFormSchema;
onSuccess?: Boolean;
onFail?: Boolean;
response?: any;
}
export default class Graphextension extends React.Component<IGraphextensionProps, IGraphextensionState> {
//MS Graph service
private graphService: MsGraphService;
constructor(props: IGraphextensionProps, state: IGraphextensionState) {
super(props);
this.state = {
schemaForm: {} as IFormSchema
};
this.onChangeValue = this.onChangeValue.bind(this);
this.onCreateExtension = this.onCreateExtension.bind(this);
this.onGetExtension = this.onGetExtension.bind(this);
this.onPatchExtension = this.onPatchExtension.bind(this);
this.onDeleteExtension = this.onDeleteExtension.bind(this);
this.onTabChange = this.onTabChange.bind(this);
this.graphService = new MsGraphService(this.props.webpartContext);
}
public componentDidMount() {
}
private onChangeValue(event: any, newValue?: string) {
this.setState({
schemaForm: {
...this.state.schemaForm,
[event.target.name]: newValue
}
});
}
private async onPatchExtension() {
if (this.state.schemaForm != null && !isEmpty(this.state.schemaForm)) {
let result = await this.graphService.GetExtension();
let userSettings = {
"@odata.type": "microsoft.graph.openTypeExtension",
"extensionName": Constants.ExtensionName,
"Theme": !stringIsNullOrEmpty(this.state.schemaForm.Theme) ? this.state.schemaForm.Theme : result.Theme,
"Tags": !stringIsNullOrEmpty(this.state.schemaForm.Tags) ? this.state.schemaForm.Tags : result.Tags
};
//Patch Extesion
let response = await this.graphService.PatchExtension(userSettings);
if (response != null && response.ok) {
toast.success("Graph Extension Successfully Updated!", {
position: toast.POSITION.BOTTOM_CENTER
});
}
else {
toast.error("Error in patching graph extension !", {
position: toast.POSITION.BOTTOM_CENTER
})
}
}
}
private async onCreateExtension() {
if (this.state.schemaForm != null && !isEmpty(this.state.schemaForm)) {
let userSettings = {
"@odata.type": "microsoft.graph.openTypeExtension",
"extensionName": Constants.ExtensionName,
"Theme": this.state.schemaForm.Theme,
"Tags": this.state.schemaForm.Tags
};
//Create Extesion
let response = await this.graphService.CreateExtension(userSettings);
if (response != null) {
toast.success("Graph Extension created !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
else {
toast.error("Error in creating graph extension !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
}
}
private async onDeleteExtension() {
//Delete Extesion
let response = await this.graphService.DeleteExtension();
if (response != null && response.ok) {
toast.success("Graph extension removed !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
else {
toast.error("Error in removing graph extension !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
}
private async onGetExtension() {
//Get Extesion
let response = await this.graphService.GetExtension();
if (response != null) {
this.setState({
response
});
toast.success("Graph Extension retrieved !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
else {
this.setState({
response: null
});
toast.error("Error in retrieving graph extension !", {
position: toast.POSITION.BOTTOM_CENTER
});
}
}
private onTabChange(e: any, data: any): void {
console.log(data);
this.setState({
response: null,
schemaForm: null
});
}
public render(): React.ReactElement<IGraphextensionProps> {
const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 },
styles: { root: { width: 300 } }
};
const panes = [
{
menuItem: 'POST', render: () =>
<Tab.Pane>
<Header as='h3'>Create open extension</Header>
<Stack horizontal tokens={{ childrenGap: 50 }} styles={{ root: { width: 650 } }}>
<Stack {...columnProps}>
<TextField
name="Theme"
label="Theme"
value={this.state.schemaForm ? this.state.schemaForm.Theme : ""}
onChange={this.onChangeValue} />
<TextField
name="Tags"
label="Tags"
value={this.state.schemaForm ? this.state.schemaForm.Tags : ""}
onChange={this.onChangeValue} />
</Stack>
<Stack {...columnProps}>
{this.state.schemaForm != null ?
<pre>{JSON.stringify(this.state.schemaForm, null, 2)}</pre>
: ""}
</Stack>
</Stack>
<PrimaryButton className={styles.button} text="Create Extension" onClick={this.onCreateExtension} allowDisabledFocus />
</Tab.Pane>
},
{
menuItem: 'GET', render: () =>
<Tab.Pane>
<Header as='h3'>Get open extension</Header>
<PrimaryButton className={styles.button} text="Get Extension" onClick={this.onGetExtension} allowDisabledFocus />
<Stack horizontal tokens={{ childrenGap: 50 }} styles={{ root: { width: 650 } }}>
<Stack {...columnProps}>
{this.state.response != null ? <pre>{JSON.stringify(this.state.response, null, 2)}</pre> : ""}
</Stack>
</Stack>
</Tab.Pane>
},
{
menuItem: 'PATCH', render: () =>
<Tab.Pane>
<Header as='h3'>Patch Extension</Header>
<Stack horizontal tokens={{ childrenGap: 50 }} styles={{ root: { width: 650 } }}>
<Stack {...columnProps}>
<TextField
name="Theme"
label="Theme"
value={this.state.schemaForm ? this.state.schemaForm.Theme : ""}
onChange={this.onChangeValue} />
<TextField
name="Tags"
label="Tags"
value={this.state.schemaForm ? this.state.schemaForm.Tags : ""}
onChange={this.onChangeValue} />
</Stack>
<Stack {...columnProps}>
{this.state.schemaForm != null ?
<pre>{JSON.stringify(this.state.schemaForm, null, 2)}</pre>
: ""}
</Stack>
</Stack>
<PrimaryButton className={styles.button} text="Patch Extension" onClick={this.onPatchExtension} allowDisabledFocus />
</Tab.Pane>
},
{
menuItem: 'DELETE', render: () => <Tab.Pane>
<Header as='h3'>Delete Extension</Header>
<PrimaryButton className={styles.button} text="Delete Extension" onClick={this.onDeleteExtension} allowDisabledFocus />
<Stack horizontal tokens={{ childrenGap: 50 }} styles={{ root: { width: 650 } }}>
<Stack {...columnProps}>
</Stack>
</Stack>
</Tab.Pane>
}
];
return (
<div className={styles.graphextension}>
<div className={styles.container}>
<div className={styles.row}>
<div className={styles.column12}>
<Header as='h1'>Graph Open Extension Demo</Header>
</div>
</div>
<div className={styles.row}>
<div className={styles.column12}>
<Tab panes={panes} onTabChange={this.onTabChange}></Tab>
<ToastContainer></ToastContainer>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,5 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IGraphextensionProps {
webpartContext:WebPartContext;
}

View File

@ -0,0 +1,624 @@
.Toastify__toast-container {
z-index: 9999;
-webkit-transform: translate3d(0, 0, 9999px);
position: fixed;
padding: 4px;
width: 320px;
box-sizing: border-box;
color: #fff;
}
.Toastify__toast-container--top-left {
top: 1em;
left: 1em;
}
.Toastify__toast-container--top-center {
top: 1em;
left: 50%;
margin-left: -160px;
}
.Toastify__toast-container--top-right {
top: 1em;
right: 1em;
}
.Toastify__toast-container--bottom-left {
bottom: 1em;
left: 1em;
}
.Toastify__toast-container--bottom-center {
bottom: 1em;
left: 50%;
margin-left: -160px;
}
.Toastify__toast-container--bottom-right {
bottom: 1em;
right: 1em;
}
@media only screen and (max-width: 480px) {
.Toastify__toast-container {
width: 100vw;
padding: 0;
left: 0;
margin: 0;
}
.Toastify__toast-container--top-left,
.Toastify__toast-container--top-center,
.Toastify__toast-container--top-right {
top: 0;
}
.Toastify__toast-container--bottom-left,
.Toastify__toast-container--bottom-center,
.Toastify__toast-container--bottom-right {
bottom: 0;
}
.Toastify__toast-container--rtl {
right: 0;
left: initial;
}
}
.Toastify__toast {
position: relative;
min-height: 64px;
box-sizing: border-box;
margin-bottom: 1rem;
padding: 8px;
border-radius: 1px;
box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1), 0 2px 15px 0 rgba(0, 0, 0, 0.05);
display: -ms-flexbox;
display: flex;
-ms-flex-pack: justify;
justify-content: space-between;
max-height: 800px;
overflow: hidden;
font-family: sans-serif;
cursor: pointer;
direction: ltr;
}
.Toastify__toast--rtl {
direction: rtl;
}
.Toastify__toast--default {
background: #fff;
color: #aaa;
}
.Toastify__toast--info {
background: #3498db;
}
.Toastify__toast--success {
background: #28a745 !important;
}
.Toastify__toast--warning {
background: #ffc107!important;
}
.Toastify__toast--error {
background: #dc3545 !important;
}
.Toastify__toast-body {
margin: auto 0;
-ms-flex: 1;
flex: 1;
}
@media only screen and (max-width: 480px) {
.Toastify__toast {
margin-bottom: 0;
}
}
.Toastify__close-button {
color: #fff;
font-weight: bold;
font-size: 14px;
background: transparent;
outline: none;
border: none;
padding: 0;
cursor: pointer;
opacity: 0.7;
transition: 0.3s ease;
-ms-flex-item-align: start;
align-self: flex-start;
}
.Toastify__close-button--default {
color: #000;
opacity: 0.3;
}
.Toastify__close-button:hover,
.Toastify__close-button:focus {
opacity: 1;
}
@keyframes Toastify__trackProgress {
0% {
transform: scaleX(1);
}
100% {
transform: scaleX(0);
}
}
.Toastify__progress-bar {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 5px;
z-index: 9999;
opacity: 0.7;
background-color: rgba(255, 255, 255, 0.7);
transform-origin: left;
}
.Toastify__progress-bar--animated {
animation: Toastify__trackProgress linear 1 forwards;
}
.Toastify__progress-bar--controlled {
transition: transform .2s;
}
.Toastify__progress-bar--rtl {
right: 0;
left: initial;
transform-origin: right;
}
.Toastify__progress-bar--default {
background: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55);
}
@keyframes Toastify__bounceInRight {
from,
60%,
75%,
90%,
to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
from {
opacity: 0;
transform: translate3d(3000px, 0, 0);
}
60% {
opacity: 1;
transform: translate3d(-25px, 0, 0);
}
75% {
transform: translate3d(10px, 0, 0);
}
90% {
transform: translate3d(-5px, 0, 0);
}
to {
transform: none;
}
}
@keyframes Toastify__bounceOutRight {
20% {
opacity: 1;
transform: translate3d(-20px, 0, 0);
}
to {
opacity: 0;
transform: translate3d(2000px, 0, 0);
}
}
@keyframes Toastify__bounceInLeft {
from,
60%,
75%,
90%,
to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0% {
opacity: 0;
transform: translate3d(-3000px, 0, 0);
}
60% {
opacity: 1;
transform: translate3d(25px, 0, 0);
}
75% {
transform: translate3d(-10px, 0, 0);
}
90% {
transform: translate3d(5px, 0, 0);
}
to {
transform: none;
}
}
@keyframes Toastify__bounceOutLeft {
20% {
opacity: 1;
transform: translate3d(20px, 0, 0);
}
to {
opacity: 0;
transform: translate3d(-2000px, 0, 0);
}
}
@keyframes Toastify__bounceInUp {
from,
60%,
75%,
90%,
to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
from {
opacity: 0;
transform: translate3d(0, 3000px, 0);
}
60% {
opacity: 1;
transform: translate3d(0, -20px, 0);
}
75% {
transform: translate3d(0, 10px, 0);
}
90% {
transform: translate3d(0, -5px, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes Toastify__bounceOutUp {
20% {
transform: translate3d(0, -10px, 0);
}
40%,
45% {
opacity: 1;
transform: translate3d(0, 20px, 0);
}
to {
opacity: 0;
transform: translate3d(0, -2000px, 0);
}
}
@keyframes Toastify__bounceInDown {
from,
60%,
75%,
90%,
to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0% {
opacity: 0;
transform: translate3d(0, -3000px, 0);
}
60% {
opacity: 1;
transform: translate3d(0, 25px, 0);
}
75% {
transform: translate3d(0, -10px, 0);
}
90% {
transform: translate3d(0, 5px, 0);
}
to {
transform: none;
}
}
@keyframes Toastify__bounceOutDown {
20% {
transform: translate3d(0, 10px, 0);
}
40%,
45% {
opacity: 1;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 0;
transform: translate3d(0, 2000px, 0);
}
}
.Toastify__bounce-enter--top-left,
.Toastify__bounce-enter--bottom-left {
animation-name: Toastify__bounceInLeft;
}
.Toastify__bounce-enter--top-right,
.Toastify__bounce-enter--bottom-right {
animation-name: Toastify__bounceInRight;
}
.Toastify__bounce-enter--top-center {
animation-name: Toastify__bounceInDown;
}
.Toastify__bounce-enter--bottom-center {
animation-name: Toastify__bounceInUp;
}
.Toastify__bounce-exit--top-left,
.Toastify__bounce-exit--bottom-left {
animation-name: Toastify__bounceOutLeft;
}
.Toastify__bounce-exit--top-right,
.Toastify__bounce-exit--bottom-right {
animation-name: Toastify__bounceOutRight;
}
.Toastify__bounce-exit--top-center {
animation-name: Toastify__bounceOutUp;
}
.Toastify__bounce-exit--bottom-center {
animation-name: Toastify__bounceOutDown;
}
@keyframes Toastify__zoomIn {
from {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
50% {
opacity: 1;
}
}
@keyframes Toastify__zoomOut {
from {
opacity: 1;
}
50% {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
to {
opacity: 0;
}
}
.Toastify__zoom-enter {
animation-name: Toastify__zoomIn;
}
.Toastify__zoom-exit {
animation-name: Toastify__zoomOut;
}
@keyframes Toastify__flipIn {
from {
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
animation-timing-function: ease-in;
opacity: 0;
}
40% {
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
animation-timing-function: ease-in;
}
60% {
transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
opacity: 1;
}
80% {
transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
}
to {
transform: perspective(400px);
}
}
@keyframes Toastify__flipOut {
from {
transform: perspective(400px);
}
30% {
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
opacity: 1;
}
to {
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
opacity: 0;
}
}
.Toastify__flip-enter {
animation-name: Toastify__flipIn;
}
.Toastify__flip-exit {
animation-name: Toastify__flipOut;
}
@keyframes Toastify__slideInRight {
from {
transform: translate3d(110%, 0, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes Toastify__slideInLeft {
from {
transform: translate3d(-110%, 0, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes Toastify__slideInUp {
from {
transform: translate3d(0, 110%, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes Toastify__slideInDown {
from {
transform: translate3d(0, -110%, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes Toastify__slideOutRight {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(110%, 0, 0);
}
}
@keyframes Toastify__slideOutLeft {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(-110%, 0, 0);
}
}
@keyframes Toastify__slideOutDown {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(0, 500px, 0);
}
}
@keyframes Toastify__slideOutUp {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(0, -500px, 0);
}
}
.Toastify__slide-enter--top-left,
.Toastify__slide-enter--bottom-left {
animation-name: Toastify__slideInLeft;
}
.Toastify__slide-enter--top-right,
.Toastify__slide-enter--bottom-right {
animation-name: Toastify__slideInRight;
}
.Toastify__slide-enter--top-center {
animation-name: Toastify__slideInDown;
}
.Toastify__slide-enter--bottom-center {
animation-name: Toastify__slideInUp;
}
.Toastify__slide-exit--top-left,
.Toastify__slide-exit--bottom-left {
animation-name: Toastify__slideOutLeft;
}
.Toastify__slide-exit--top-right,
.Toastify__slide-exit--bottom-right {
animation-name: Toastify__slideOutRight;
}
.Toastify__slide-exit--top-center {
animation-name: Toastify__slideOutUp;
}
.Toastify__slide-exit--bottom-center {
animation-name: Toastify__slideOutDown;
}
/*# sourceMappingURL=ReactToastify.css.map */

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface IGraphextensionWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'GraphextensionWebPartStrings' {
const strings: IGraphextensionWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,38 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}