Skip to content

Commit

Permalink
Merge pull request #2 from giuleon/dev
Browse files Browse the repository at this point in the history
Add events on people picked can now be used as a standalone component
  • Loading branch information
giuleon committed Dec 6, 2017
2 parents 841d3f5 + 1cb27c1 commit d34d246
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 103 deletions.
Binary file removed Preview1.gif
Binary file not shown.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +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|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.**
Expand All @@ -50,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" />
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spfx-office-ui-fabric-people-picker",
"version": "1.0.1",
"version": "1.0.2",
"private": true,
"engines": {
"node": ">=0.10.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,4 +11,5 @@ export interface IOfficeUiFabricPeoplePickerProps {
principalTypeSecurityGroup: boolean;
principalTypeDistributionList: boolean;
numberOfItems: number;
onChange?: (items: SharePointUserPersona[]) => void;
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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';
const suggestionProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: 'Suggested People',
noResultsFoundText: 'No results found',
loadingText: 'Loading'
};
import {
BaseComponent,
assign,
autobind
} from 'office-ui-fabric-react/lib//Utilities';
} 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';
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'
};

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[] = [
Expand Down Expand Up @@ -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'}
Expand All @@ -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'}
Expand All @@ -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 [];
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit d34d246

Please sign in to comment.