initial commit
This commit is contained in:
parent
39ea25afc2
commit
98f59421bf
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
||||
![picture of the web part in action](assets/preview.gif)
|
||||
|
||||
## Compatibility
|
||||
|
||||
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
|
||||
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
|
||||
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
|
||||
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams")
|
||||
![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
|
||||
|
||||
## 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" />
|
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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 -->"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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 -->"
|
||||
}
|
|
@ -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'));
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 |
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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…
Reference in New Issue