mirror of
https://github.com/pnp/sp-dev-fx-webparts.git
synced 2025-03-09 06:38:52 +00:00
Merge pull request #2055 from zachroberts8668/zroberts8668
react-graph-groupviewer sample
This commit is contained in:
commit
72ea04c8d4
33
samples/react-graph-groupviewer/.gitignore
vendored
Normal file
33
samples/react-graph-groupviewer/.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
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
|
12
samples/react-graph-groupviewer/.yo-rc.json
Normal file
12
samples/react-graph-groupviewer/.yo-rc.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.12.1",
|
||||
"libraryName": "react-azure-group-view",
|
||||
"libraryId": "bcbef560-eaf3-4560-8f6f-482eceaf14a5",
|
||||
"environment": "spo",
|
||||
"packageManager": "npm",
|
||||
"isCreatingSolution": true,
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
77
samples/react-graph-groupviewer/README.md
Normal file
77
samples/react-graph-groupviewer/README.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Graph Group Viewer
|
||||
|
||||
## Summary
|
||||
|
||||
This webpart will provide the functionality to search for groups in your tenant and allow you to view the details of the selected group as well as view a list of group members and export the list of members to a CSV file.
|
||||
|
||||

|
||||
|
||||
## Compatibility
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
|
||||
* [Microsoft 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
|
||||
* [Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview)
|
||||
* [PnP React Controls](https://pnp.github.io/sp-dev-fx-controls-react/)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-graph-groupviewer | [Zach Roberts](https://github.com/zachroberts8668) ([@ZachSPODev](https://twitter.com/ZachSPODev))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|September 27, 2021|Initial release
|
||||
|
||||
## Prerequisites
|
||||
There are no prerequisites to use this webpart.
|
||||
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
* Clone this repository
|
||||
* If your SharePoint tenant does not already allow API access to the following Microsoft Graph permissions: Directory.Read.All, Group.Read.All and GroupMember.Read.All then please follow the below instructions.
|
||||
* in the command line run:
|
||||
* `npm install`
|
||||
* `gulp build --ship`
|
||||
* `gulp bundle --ship`
|
||||
* `gulp package-solution --ship`
|
||||
* browse to your SharePoint app catalog and load the SPFX package. Once loaded you will need to browse to your SharePoint Admin Center and under advanced you will need to open Api Access and allow the requests for Microsoft Graph.
|
||||
* If you have the APIs permissions already allowed you can follow the below steps.
|
||||
* in the command line run:
|
||||
* `npm install`
|
||||
* `gulp serve --nobrowser`
|
||||
* browse to your hosted workbench and add the webpart.
|
||||
|
||||
## Features
|
||||
|
||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
||||
|
||||
* Using Microsoft Graph to search for groups and return group members for the selected group.
|
||||
* Exporting loaded data to a CSV file using [CSVLink](https://github.com/react-csv/react-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.**
|
||||
|
||||
## Help
|
||||
|
||||
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
|
||||
|
||||
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-graph-groupviewer&authors=@zroberts8668&title=react-graph-groupviewer%20-%20).
|
||||
|
||||
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-graph-groupviewer&authors=@zroberts8668&title=react-graph-groupviewer%20-%20).
|
||||
|
||||
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=react-graph-groupviewer&authors=@zroberts8668&title=react-graph-groupviewer%20-%20).
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-graph-groupviewer" />
|
BIN
samples/react-graph-groupviewer/assets/preview.gif
Normal file
BIN
samples/react-graph-groupviewer/assets/preview.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
51
samples/react-graph-groupviewer/assets/sample.json
Normal file
51
samples/react-graph-groupviewer/assets/sample.json
Normal file
@ -0,0 +1,51 @@
|
||||
[
|
||||
{
|
||||
"name": "pnp-sp-dev-spfx-web-parts-react-graph-groupviewer",
|
||||
"source": "pnp",
|
||||
"title": "Graph Group Viewer",
|
||||
"shortDescription": "Search for groups in your tenant and allow you to view the details of the selected group as well as view a list of group members and export the list of members to a CSV file",
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-graph-groupviewer",
|
||||
"longDescription": [
|
||||
"This webpart will provide the functionality to search for groups in your tenant and allow you to view the details of the selected group as well as view a list of group members and export the list of members to a CSV file"
|
||||
],
|
||||
"creationDateTime": "2021-09-27",
|
||||
"updateDateTime": "2021-09-27",
|
||||
"products": [
|
||||
"SharePoint",
|
||||
"Office"
|
||||
],
|
||||
"metadata": [
|
||||
{
|
||||
"key": "CLIENT-SIDE-DEV",
|
||||
"value": "React"
|
||||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.12.1"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
{
|
||||
"type": "image",
|
||||
"order": 100,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-graph-groupviewer/assets/preview.gif",
|
||||
"alt": "Preview"
|
||||
}
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"gitHubAccount": "zachroberts8668",
|
||||
"company": "",
|
||||
"pictureUrl": "https://github.com/zachroberts8668.png",
|
||||
"name": "Zach Roberts"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"name": "Build your first SharePoint client-side web part",
|
||||
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
20
samples/react-graph-groupviewer/config/config.json
Normal file
20
samples/react-graph-groupviewer/config/config.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"azure-group-view-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/azureGroupView/AzureGroupViewWebPart.js",
|
||||
"manifest": "./src/webparts/azureGroupView/AzureGroupViewWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"AzureGroupViewWebPartStrings": "lib/webparts/azureGroupView/loc/{locale}.js",
|
||||
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js",
|
||||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js"
|
||||
}
|
||||
}
|
4
samples/react-graph-groupviewer/config/copy-assets.json
Normal file
4
samples/react-graph-groupviewer/config/copy-assets.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "./release/assets/"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-azure-group-view",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
34
samples/react-graph-groupviewer/config/package-solution.json
Normal file
34
samples/react-graph-groupviewer/config/package-solution.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-graph-groupviewer-client-side-solution",
|
||||
"id": "bcbef560-eaf3-4560-8f6f-482eceaf14a5",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": ""
|
||||
},
|
||||
"webApiPermissionRequests": [
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Directory.Read.All"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Group.Read.All"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "GroupMember.Read.All"
|
||||
}
|
||||
]
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-graph-groupviewer.sppkg"
|
||||
}
|
||||
}
|
10
samples/react-graph-groupviewer/config/serve.json
Normal file
10
samples/react-graph-groupviewer/config/serve.json
Normal 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/"
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
16
samples/react-graph-groupviewer/gulpfile.js
vendored
Normal file
16
samples/react-graph-groupviewer/gulpfile.js
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
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.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(require('gulp'));
|
22501
samples/react-graph-groupviewer/package-lock.json
generated
Normal file
22501
samples/react-graph-groupviewer/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
samples/react-graph-groupviewer/package.json
Normal file
40
samples/react-graph-groupviewer/package.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "react-graph-groupviewer",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.12.1",
|
||||
"@microsoft/sp-lodash-subset": "1.12.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
|
||||
"@microsoft/sp-property-pane": "1.12.1",
|
||||
"@microsoft/sp-webpart-base": "1.12.1",
|
||||
"@pnp/common": "^2.8.0",
|
||||
"@pnp/graph": "^2.8.0",
|
||||
"@pnp/logging": "^2.8.0",
|
||||
"@pnp/odata": "^2.8.0",
|
||||
"@pnp/spfx-controls-react": "^3.3.0",
|
||||
"@pnp/spfx-property-controls": "3.2.0",
|
||||
"office-ui-fabric-react": "7.156.0",
|
||||
"react": "16.9.0",
|
||||
"react-csv": "^2.0.3",
|
||||
"react-dom": "16.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.9.36",
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@microsoft/sp-build-web": "1.12.1",
|
||||
"@microsoft/sp-tslint-rules": "1.12.1",
|
||||
"@microsoft/sp-module-interfaces": "1.12.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.12.1",
|
||||
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
|
||||
"gulp": "~4.0.2",
|
||||
"ajv": "~5.2.2",
|
||||
"@types/webpack-env": "1.13.1"
|
||||
}
|
||||
}
|
1
samples/react-graph-groupviewer/src/index.ts
Normal file
1
samples/react-graph-groupviewer/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "bc258878-df14-4cf4-83d1-1032852bc307",
|
||||
"alias": "AzureGroupViewWebPart",
|
||||
"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": "Other" },
|
||||
"title": { "default": "React Group View" },
|
||||
"description": { "default": "Azure Group View description" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"description": "Azure Group View"
|
||||
}
|
||||
}]
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
IPropertyPaneDropdownOption,
|
||||
PropertyPaneDropdown,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import { graph } from "@pnp/graph";
|
||||
import "@pnp/graph/groups";
|
||||
import * as strings from 'AzureGroupViewWebPartStrings';
|
||||
import { AzureGroupView, IAzureGroupViewProps } from './components';
|
||||
|
||||
export interface IAzureGroupViewWebPartProps {
|
||||
description: string;
|
||||
aadGroupId: string;
|
||||
}
|
||||
|
||||
export default class AzureGroupViewWebPart extends BaseClientSideWebPart<IAzureGroupViewWebPartProps> {
|
||||
|
||||
private groupOptions: IPropertyPaneDropdownOption[] = [];
|
||||
private groupOptionsLoading: boolean = false;
|
||||
|
||||
public render(): void {
|
||||
|
||||
const element: React.ReactElement<IAzureGroupViewProps> = React.createElement(
|
||||
AzureGroupView,
|
||||
{
|
||||
description: this.properties.description,
|
||||
aadGroupId: this.properties.aadGroupId
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onInit(): Promise<void> {
|
||||
return super.onInit().then(_ => {
|
||||
graph.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.membersBtn {
|
||||
padding-top: 10px;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import * as React from 'react';
|
||||
import { IGroupDetailsProps } from './IGroupDetailsProps';
|
||||
import { IGroupDetailsState } from './IGroupDetailsState';
|
||||
import { PrimaryButton, Label, Text, Panel} from 'office-ui-fabric-react';
|
||||
import styles from './GroupDetails.module.scss';
|
||||
import { MembersList } from '../memberslist/MembersList';
|
||||
|
||||
export class GroupDetails extends React.Component<IGroupDetailsProps, IGroupDetailsState>{
|
||||
|
||||
constructor(props: IGroupDetailsProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showPanel: false
|
||||
};
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IGroupDetailsProps> {
|
||||
return (
|
||||
<div>
|
||||
<hr />
|
||||
<Text variant="large">Group Details - {this.props.selectedGroup.displayName}</Text>
|
||||
<Label>Description:</Label>
|
||||
<div>{this.props.selectedGroup.description}</div>
|
||||
<div className={styles.membersBtn}>
|
||||
<PrimaryButton iconProps={{iconName: 'People'}} text="View Members" onClick={this._handlePanel} />
|
||||
</div>
|
||||
{this.state.showPanel ?
|
||||
<MembersList closePanel={this._handlePanel} showPanel={this.state.showPanel} groupName={this.props.selectedGroup.displayName} groupId={this.props.selectedGroup.id} />
|
||||
: <div></div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private _handlePanel = (): void => {
|
||||
this.setState({
|
||||
showPanel: !this.state.showPanel
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { IGroup } from "@pnp/graph/groups";
|
||||
import { IAzureGroup } from "../../models";
|
||||
|
||||
export interface IGroupDetailsProps {
|
||||
selectedGroup?: IAzureGroup;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export interface IGroupDetailsState {
|
||||
showPanel?: boolean;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import * as React from 'react';
|
||||
import { IGroupListProps } from './IGroupListProps';
|
||||
import { IGroupListState } from './IGroupListState';
|
||||
import { ListView, IViewField, SelectionMode } from "@pnp/spfx-controls-react/lib/ListView";
|
||||
import { PrimaryButton } from 'office-ui-fabric-react';
|
||||
import { IAzureGroup } from '../../models';
|
||||
import { GroupDetails } from '../groupdetails/GroupDetails';
|
||||
|
||||
const view: IViewField[] = [
|
||||
{
|
||||
name: 'displayName',
|
||||
displayName: 'Group Name',
|
||||
minWidth: 150,
|
||||
isResizable: true
|
||||
}
|
||||
];
|
||||
|
||||
export class GroupList extends React.Component<IGroupListProps, IGroupListState> {
|
||||
|
||||
constructor(props: IGroupListProps){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedGroup: null
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IGroupListProps> {
|
||||
return (
|
||||
<div>
|
||||
<ListView
|
||||
items={this.props.results}
|
||||
viewFields={view}
|
||||
selectionMode={SelectionMode.single}
|
||||
selection={this._getSelection}
|
||||
/>
|
||||
{ this.state.selectedGroup === null ?
|
||||
<div></div>
|
||||
: <GroupDetails selectedGroup={this.state.selectedGroup} />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private _getSelection = (items: any[]) :void => {
|
||||
console.log(items);
|
||||
if(items.length !== 0){
|
||||
const selected: IAzureGroup = {
|
||||
displayName: items[0].displayName,
|
||||
description: items[0].description,
|
||||
id: items[0].id
|
||||
};
|
||||
this.setState({
|
||||
selectedGroup: selected
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { IGroup } from "@pnp/graph/groups";
|
||||
import { _Group } from "@pnp/graph/groups/types";
|
||||
|
||||
export interface IGroupListProps {
|
||||
results?: IGroup[];
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import { IAzureGroup } from "../../models";
|
||||
|
||||
export interface IGroupListState {
|
||||
selectedGroup: IAzureGroup;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export * from './GroupList';
|
||||
export * from './IGroupListProps';
|
@ -0,0 +1 @@
|
||||
export * from './main';
|
@ -0,0 +1,74 @@
|
||||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.azureGroupView {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
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-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
// Primary Button
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import styles from './AzureGroupView.module.scss';
|
||||
import { IAzureGroupViewProps } from './IAzureGroupViewProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import { PrimaryButton, TextField } from '@microsoft/office-ui-fabric-react-bundle';
|
||||
import { graph } from "@pnp/graph";
|
||||
import "@pnp/graph/groups";
|
||||
import { IAzureGroupViewState } from './IAzureGroupViewState';
|
||||
import { GroupList } from '../grouplist/GroupList';
|
||||
import { GroupDetails } from '../groupdetails/GroupDetails';
|
||||
|
||||
|
||||
export class AzureGroupView extends React.Component<IAzureGroupViewProps, IAzureGroupViewState> {
|
||||
|
||||
constructor(props: IAzureGroupViewProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchResults: null,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IAzureGroupViewProps> {
|
||||
return (
|
||||
<div className={ styles.azureGroupView }>
|
||||
<TextField label="Search Groups" onChange={(event: React.FormEvent<HTMLInputElement>, newValue: string) => {this._searchGroups(newValue);}} />
|
||||
<br />
|
||||
<GroupList results={this.state.searchResults} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private _searchGroups = (searchText: string): void => {
|
||||
graph.groups.search(`displayName:${searchText}`).top(5).get().then(groups => {
|
||||
this.setState({
|
||||
searchResults: groups
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export interface IAzureGroupViewProps {
|
||||
description: string;
|
||||
aadGroupId?: string;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from "@pnp/graph/presets/all";
|
||||
|
||||
export interface IAzureGroupViewState {
|
||||
searchResults?: any[];
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export * from './AzureGroupView';
|
||||
export * from './IAzureGroupViewProps';
|
@ -0,0 +1,5 @@
|
||||
export interface IMemberListState {
|
||||
members?: any[];
|
||||
membersCount: number;
|
||||
isLoading: boolean;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { PanelCallback } from "../../models";
|
||||
|
||||
export interface IMembersListProps {
|
||||
groupName: string;
|
||||
groupId: string;
|
||||
showPanel: boolean;
|
||||
closePanel: PanelCallback;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
.exportBtn {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
import * as React from 'react';
|
||||
import { IMembersListProps } from './IMembersListProps';
|
||||
import { IMemberListState } from './IMemberListState';
|
||||
import { Panel, PanelType, Spinner, SpinnerSize, DefaultButton } from 'office-ui-fabric-react';
|
||||
import { ListView, IViewField } from '@pnp/spfx-controls-react/lib/ListView';
|
||||
import { graph } from '@pnp/graph/presets/all';
|
||||
import { CSVLink } from 'react-csv';
|
||||
import styles from './MembersList.module.scss';
|
||||
|
||||
const view: IViewField[] = [
|
||||
{
|
||||
name: 'displayName',
|
||||
displayName: 'Name',
|
||||
minWidth: 150,
|
||||
isResizable: true
|
||||
},
|
||||
{
|
||||
name: 'mail',
|
||||
displayName: 'Email',
|
||||
minWidth: 150,
|
||||
isResizable: true
|
||||
}
|
||||
];
|
||||
|
||||
const csvHeaders = [
|
||||
{ label: 'Name', key: 'displayName'},
|
||||
{ label: 'Email', key: 'mail'}
|
||||
];
|
||||
|
||||
export class MembersList extends React.Component<IMembersListProps, IMemberListState>{
|
||||
|
||||
constructor(props: IMembersListProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
members: null,
|
||||
membersCount: 0,
|
||||
isLoading: false
|
||||
};
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IMembersListProps> {
|
||||
return (
|
||||
<div>
|
||||
<Panel type={PanelType.medium} isOpen={this.props.showPanel} headerText={`${this.props.groupName} - Members (${this.state.membersCount})`} onDismiss={this._handlePanel}>
|
||||
{this.state.isLoading ?
|
||||
<div>
|
||||
<Spinner label="Loading Members..." size={SpinnerSize.medium} />
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<div className={styles.exportBtn}>
|
||||
<CSVLink data={this.state.members} headers={csvHeaders} filename={`${this.props.groupName}-Members.csv`}>
|
||||
<DefaultButton text="Export to CSV" />
|
||||
</CSVLink>
|
||||
</div>
|
||||
<ListView
|
||||
items={this.state.members}
|
||||
viewFields={view}
|
||||
showFilter={true}
|
||||
filterPlaceHolder={"Search Members..."}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this._loadMembers();
|
||||
}
|
||||
|
||||
private _loadMembers = (): void => {
|
||||
this.setState({isLoading: true});
|
||||
this._getMembers(this.props.groupId).then(response => {
|
||||
this.setState({
|
||||
members: response,
|
||||
membersCount: response.length,
|
||||
isLoading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async _getMembers(id: string) {
|
||||
const members = await graph.groups.getById(id).members();
|
||||
console.log(members);
|
||||
return members;
|
||||
}
|
||||
|
||||
private _handlePanel = (): void => {
|
||||
this.props.closePanel();
|
||||
}
|
||||
|
||||
}
|
7
samples/react-graph-groupviewer/src/webparts/azureGroupView/loc/en-us.js
vendored
Normal file
7
samples/react-graph-groupviewer/src/webparts/azureGroupView/loc/en-us.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field"
|
||||
}
|
||||
});
|
10
samples/react-graph-groupviewer/src/webparts/azureGroupView/loc/mystrings.d.ts
vendored
Normal file
10
samples/react-graph-groupviewer/src/webparts/azureGroupView/loc/mystrings.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
declare interface IAzureGroupViewWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'AzureGroupViewWebPartStrings' {
|
||||
const strings: IAzureGroupViewWebPartStrings;
|
||||
export = strings;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface IAzureGroup {
|
||||
displayName: string;
|
||||
id: string;
|
||||
description: string;
|
||||
}
|
@ -0,0 +1 @@
|
||||
export type PanelCallback = () => void;
|
@ -0,0 +1,2 @@
|
||||
export * from './IAzureGroup';
|
||||
export * from './PanelCallback';
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 383 B |
35
samples/react-graph-groupviewer/tsconfig.json
Normal file
35
samples/react-graph-groupviewer/tsconfig.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/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": [
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
30
samples/react-graph-groupviewer/tslint.json
Normal file
30
samples/react-graph-groupviewer/tslint.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "./node_modules/@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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user