mirror of
https://github.com/pnp/sp-dev-fx-webparts.git
synced 2025-03-03 10:19:14 +00:00
Add events on people picked can now be used as a standalone component, minor bug fixes. (#382)
This commit is contained in:
parent
d16af92f89
commit
3283f093de
@ -3,7 +3,7 @@
|
||||
## Summary
|
||||
SharePoint Framework solution with the Office UI Fabric People Picker, the client web part across the SharePoint Rest API is able to retrieve people and groups.
|
||||
|
||||

|
||||

|
||||
|
||||
## Used SharePoint Framework Version
|
||||

|
||||
@ -25,7 +25,7 @@ Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|May 21, 2017|Initial release
|
||||
1.0.1|Sep 28, 2017|Updated to GA Version, New properties that allow to specify the number of items to display and which entities retrieve (User, SharePoint Groups, Distribution Lists, Security Groups).
|
||||
1.0.2|Oct 25, 2017|fixed a broken link on readme file.
|
||||
1.0.2|Dec 06, 2017|Minor bug fixes, Add events on people picked can now be used as a standalone component (Thanks to [@MikeMyers](https://github.com/thespooler) for contributing.
|
||||
|
||||
## 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.**
|
||||
@ -51,4 +51,4 @@ https://localhost:4321/temp/workbench.html
|
||||
If you want to try on a real environment, open:
|
||||
https://your-domain.sharepoint.com/_layouts/15/workbench.aspx
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-peoplepicker" />
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-peoplepicker" />
|
||||
|
@ -4,11 +4,9 @@ import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneDropdown,
|
||||
PropertyPaneToggle,
|
||||
PropertyPaneSlider,
|
||||
IWebPartContext
|
||||
PropertyPaneSlider
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'officeUiFabricPeoplePickerStrings';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import { SPHttpClient } from '@microsoft/sp-http';
|
||||
import { SharePointUserPersona } from '../models/OfficeUiFabricPeoplePicker';
|
||||
|
||||
export interface IOfficeUiFabricPeoplePickerProps {
|
||||
description: string;
|
||||
@ -10,4 +11,5 @@ export interface IOfficeUiFabricPeoplePickerProps {
|
||||
principalTypeSecurityGroup: boolean;
|
||||
principalTypeDistributionList: boolean;
|
||||
numberOfItems: number;
|
||||
onChange?: (items: SharePointUserPersona[]) => void;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
.helloWorld {
|
||||
|
||||
.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 {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.listItem {
|
||||
max-width: 715px;
|
||||
margin: 5px auto 5px auto;
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.button {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +1,41 @@
|
||||
import * as React from 'react';
|
||||
import { css } from 'office-ui-fabric-react';
|
||||
import styles from './OfficeUiFabricPeoplePicker.module.scss';
|
||||
import { IOfficeUiFabricPeoplePickerProps } from './IOfficeUiFabricPeoplePickerProps';
|
||||
|
||||
import {
|
||||
CompactPeoplePicker,
|
||||
IBasePickerSuggestionsProps,
|
||||
ListPeoplePicker,
|
||||
NormalPeoplePicker
|
||||
} from 'office-ui-fabric-react/lib/Pickers';
|
||||
import { IPersonaProps } from 'office-ui-fabric-react/lib/Persona';
|
||||
import {
|
||||
assign,
|
||||
autobind
|
||||
} from 'office-ui-fabric-react/lib/Utilities';
|
||||
import { people } from './PeoplePickerExampleData';
|
||||
import { IContextualMenuItem } from 'office-ui-fabric-react/lib/ContextualMenu';
|
||||
import {
|
||||
SPHttpClient,
|
||||
SPHttpClientBatch,
|
||||
SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentType
|
||||
} from '@microsoft/sp-core-library';
|
||||
import { Promise } from 'es6-promise';
|
||||
import * as lodash from 'lodash';
|
||||
import {
|
||||
IClientPeoplePickerSearchUser,
|
||||
IEnsurableSharePointUser,
|
||||
IEnsureUser,
|
||||
IOfficeUiFabricPeoplePickerState,
|
||||
SharePointUserPersona } from '../models/OfficeUiFabricPeoplePicker';
|
||||
import { IPersonaWithMenu } from 'office-ui-fabric-react/lib/components/pickers/PeoplePicker/PeoplePickerItems/PeoplePickerItem.Props';
|
||||
|
||||
const suggestionProps: IBasePickerSuggestionsProps = {
|
||||
suggestionsHeaderText: 'Suggested People',
|
||||
noResultsFoundText: 'No results found',
|
||||
loadingText: 'Loading'
|
||||
};
|
||||
import {
|
||||
BaseComponent,
|
||||
assign,
|
||||
autobind
|
||||
} from 'office-ui-fabric-react/lib//Utilities';
|
||||
import { people } from './PeoplePickerExampleData';
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
import { IPersonaWithMenu } from 'office-ui-fabric-react/lib/components/pickers/PeoplePicker/PeoplePickerItems/PeoplePickerItem.Props';
|
||||
import { IContextualMenuItem } from 'office-ui-fabric-react/lib/ContextualMenu';
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
|
||||
export interface IOfficeUiFabricPeoplePickerState {
|
||||
currentPicker?: number | string;
|
||||
delayResults?: boolean;
|
||||
}
|
||||
export interface IPeopleSearchProps {
|
||||
JobTitle: string;
|
||||
PictureURL: string;
|
||||
PreferredName: string;
|
||||
}
|
||||
export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeUiFabricPeoplePickerProps, IOfficeUiFabricPeoplePickerState> {
|
||||
private _peopleList;
|
||||
private contextualMenuItems: IContextualMenuItem[] = [
|
||||
@ -78,16 +79,18 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
|
||||
this.state = {
|
||||
currentPicker: 1,
|
||||
delayResults: false
|
||||
delayResults: false,
|
||||
selectedItems: []
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IOfficeUiFabricPeoplePickerProps> {
|
||||
if (this.props.typePicker == "Normal") {
|
||||
return (
|
||||
<NormalPeoplePicker
|
||||
onResolveSuggestions={this._onFilterChanged}
|
||||
onChange={this._onChange.bind(this) }
|
||||
onResolveSuggestions={this._onFilterChanged }
|
||||
getTextFromItem={(persona: IPersonaProps) => persona.primaryText}
|
||||
pickerSuggestionsProps={suggestionProps}
|
||||
className={'ms-PeoplePicker'}
|
||||
@ -97,7 +100,8 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
} else {
|
||||
return (
|
||||
<CompactPeoplePicker
|
||||
onResolveSuggestions={this._onFilterChanged}
|
||||
onChange={this._onChange.bind(this) }
|
||||
onResolveSuggestions={this._onFilterChanged }
|
||||
getTextFromItem={(persona: IPersonaProps) => persona.primaryText}
|
||||
pickerSuggestionsProps={suggestionProps}
|
||||
className={'ms-PeoplePicker'}
|
||||
@ -107,11 +111,21 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
}
|
||||
}
|
||||
|
||||
private _onChange(items:any[]) {
|
||||
this.setState({
|
||||
selectedItems: items
|
||||
});
|
||||
if (this.props.onChange)
|
||||
{
|
||||
this.props.onChange(items);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private _onFilterChanged(filterText: string, currentPersonas: IPersonaProps[], limitResults?: number) {
|
||||
if (filterText) {
|
||||
if (filterText.length > 2) {
|
||||
return this._searchPeople(filterText, this._peopleList);
|
||||
return this._searchPeople(filterText, this._peopleList);
|
||||
}
|
||||
} else {
|
||||
return [];
|
||||
@ -164,8 +178,8 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
* Returns people results after a REST API call
|
||||
*/
|
||||
private _searchPeople(terms: string, results: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> {
|
||||
//return new Promise<IPersonaProps[]>((resolve, reject) => setTimeout(() => resolve(results), 2000));
|
||||
if (this.props.siteUrl.toLowerCase().indexOf("wwww.contoso.com") >= 0) {
|
||||
|
||||
if (DEBUG && Environment.type === EnvironmentType.Local) {
|
||||
// If the running environment is local, load the data from the mock
|
||||
return this.searchPeopleFromMock();
|
||||
} else {
|
||||
@ -183,7 +197,7 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
if (this.props.principalTypeDistributionList === true) {
|
||||
principalType += 2;
|
||||
}
|
||||
const data = {
|
||||
const userQueryParams = {
|
||||
'queryParams': {
|
||||
'AllowEmailAddresses': true,
|
||||
'AllowMultipleEntities': false,
|
||||
@ -198,42 +212,46 @@ export default class OfficeUiFabricPeoplePicker extends React.Component<IOfficeU
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise<IPersonaProps[]>((resolve, reject) =>
|
||||
return new Promise<SharePointUserPersona[]>((resolve, reject) =>
|
||||
this.props.spHttpClient.post(userRequestUrl,
|
||||
SPHttpClient.configurations.v1,
|
||||
{
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
SPHttpClient.configurations.v1, { body: JSON.stringify(userQueryParams) })
|
||||
.then((response: SPHttpClientResponse) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((response: any): void => {
|
||||
let relevantResults: any = JSON.parse(response.value);
|
||||
let resultCount: number = relevantResults.length;
|
||||
let people = [];
|
||||
let persona: IPersonaProps = {};
|
||||
if (resultCount > 0) {
|
||||
for (var index = 0; index < resultCount; index++) {
|
||||
var p = relevantResults[index];
|
||||
let account = p.Key.substr(p.Key.lastIndexOf('|') + 1);
|
||||
.then((response: {value: string}) => {
|
||||
let userQueryResults: IClientPeoplePickerSearchUser[] = JSON.parse(response.value);
|
||||
let persons = userQueryResults.map(p => new SharePointUserPersona(p as IEnsurableSharePointUser));
|
||||
return persons;
|
||||
})
|
||||
.then((persons) => {
|
||||
const batch = this.props.spHttpClient.beginBatch();
|
||||
const ensureUserUrl = `${this.props.siteUrl}/_api/web/ensureUser`;
|
||||
const batchPromises: Promise<IEnsureUser>[] = persons.map(p => {
|
||||
var userQuery = JSON.stringify({logonName: p.User.Key});
|
||||
return batch.post(ensureUserUrl, SPHttpClientBatch.configurations.v1, {
|
||||
body: userQuery
|
||||
})
|
||||
.then((response: SPHttpClientResponse) => response.json())
|
||||
.then((json: IEnsureUser) => json);
|
||||
});
|
||||
|
||||
persona.primaryText = p.DisplayText;
|
||||
persona.imageUrl = `/_layouts/15/userphoto.aspx?size=S&accountname=${account}`;
|
||||
persona.imageShouldFadeIn = true;
|
||||
persona.secondaryText = p.EntityData.Title;
|
||||
people.push(persona);
|
||||
}
|
||||
}
|
||||
resolve(people);
|
||||
var users = batch.execute().then(() => Promise.all(batchPromises).then(values => {
|
||||
values.forEach(v => {
|
||||
let userPersona = lodash.find(persons, o => o.User.Key == v.LoginName);
|
||||
if (userPersona && userPersona.User)
|
||||
{
|
||||
let user = userPersona.User;
|
||||
lodash.assign(user, v);
|
||||
userPersona.User = user;
|
||||
}
|
||||
});
|
||||
|
||||
resolve(persons);
|
||||
}));
|
||||
}, (error: any): void => {
|
||||
reject(this._peopleList = []);
|
||||
})
|
||||
);
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private _filterPersonasByText(filterText: string): IPersonaProps[] {
|
||||
|
@ -33,4 +33,4 @@ export const people: IPersonaProps[] = [
|
||||
tertiaryText: 'In a meeting',
|
||||
optionalText: 'Available at 4:00pm'
|
||||
},
|
||||
];
|
||||
];
|
||||
|
@ -0,0 +1,79 @@
|
||||
import { IPersonaProps, IPersona } from "office-ui-fabric-react";
|
||||
|
||||
export interface IOfficeUiFabricPeoplePickerState {
|
||||
currentPicker?: number | string;
|
||||
delayResults?: boolean;
|
||||
selectedItems: any[];
|
||||
}
|
||||
export interface IPeopleSearchProps {
|
||||
JobTitle: string;
|
||||
PictureURL: string;
|
||||
PreferredName: string;
|
||||
}
|
||||
|
||||
export interface IUserEntityData {
|
||||
IsAltSecIdPresent: string;
|
||||
ObjectId: string;
|
||||
Title: string;
|
||||
Email: string;
|
||||
MobilePhone: string;
|
||||
OtherMails: string;
|
||||
Department: string;
|
||||
}
|
||||
|
||||
export interface IClientPeoplePickerSearchUser {
|
||||
Key: string;
|
||||
Description: string;
|
||||
DisplayText: string;
|
||||
EntityType: string;
|
||||
ProviderDisplayName: string;
|
||||
ProviderName: string;
|
||||
IsResolved: boolean;
|
||||
EntityData: IUserEntityData;
|
||||
MultipleMatches: any[];
|
||||
}
|
||||
|
||||
export interface IEnsureUser {
|
||||
Email: string;
|
||||
Id: number;
|
||||
IsEmailAuthenticationGuestUser: boolean;
|
||||
IsHiddenInUI: boolean;
|
||||
IsShareByEmailGuestUser: boolean;
|
||||
IsSiteAdmin: boolean;
|
||||
LoginName: string;
|
||||
PrincipalType: number;
|
||||
Title: string;
|
||||
UserId: {
|
||||
NameId: string;
|
||||
NameIdIssuer: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IEnsurableSharePointUser
|
||||
extends IClientPeoplePickerSearchUser, IEnsureUser {}
|
||||
|
||||
export class SharePointUserPersona implements IPersona {
|
||||
private _user:IEnsurableSharePointUser;
|
||||
public get User(): IEnsurableSharePointUser {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
public set User(user: IEnsurableSharePointUser) {
|
||||
this._user = user;
|
||||
this.primaryText = user.Title;
|
||||
this.secondaryText = user.EntityData.Title;
|
||||
this.tertiaryText = user.EntityData.Department;
|
||||
this.imageShouldFadeIn = true;
|
||||
this.imageUrl = `/_layouts/15/userphoto.aspx?size=S&accountname=${this.User.Key.substr(this.User.Key.lastIndexOf('|') + 1)}`;
|
||||
}
|
||||
|
||||
constructor (user: IEnsurableSharePointUser) {
|
||||
this.User = user;
|
||||
}
|
||||
|
||||
public primaryText: string;
|
||||
public secondaryText: string;
|
||||
public tertiaryText: string;
|
||||
public imageUrl: string;
|
||||
public imageShouldFadeIn: boolean;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user