Merge pull request #1399 from petkir/petkir-react-directory-1133

This commit is contained in:
Hugo Bernier 2020-07-19 22:23:39 -04:00 committed by GitHub
commit e3a7c1eda0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 30979 additions and 3560 deletions

View File

@ -1,6 +1,6 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.8.2",
"version": "1.11.0",
"libraryName": "react-directory",
"libraryId": "5b62bc16-3a71-461d-be2f-16bfcb011e8a",
"environment": "spo",

View File

@ -31,7 +31,7 @@
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
![drop](https://img.shields.io/badge/version-1.11-green.svg)
## Applies to
@ -44,24 +44,26 @@
Property |Type|Required| comments
--------------------|----|--------|----------
Web Part Title | Text| no|
Title | Text| no|WebPart Title
searchFirstName | boolean|no| Lastname or Firstname search query
## Solution
The web part Use PnPjs library, Office-ui-fabric-react components
The web part use PnPjs library, Office-ui-fabric-react components
Solution|Author(s)
--------|---------
Directory Web Part|João Mendes
Directory Web Part| Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at))
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|July 29, 2019|Initial release
1.0.1|July 19, 2020|Bugfix and mock-service for workbench (```LivePersonaCard``` not supported in workbench)
## Disclaimer

View File

@ -1,9 +1,16 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"developer": {
"name": "Contoso",
"privacyUrl": "https://contoso.com/privacy",
"termsOfUseUrl": "https://contoso.com/terms-of-use",
"websiteUrl": "https://contoso.com/my-app",
"mpnId": "000000"
},
"name": "Search Directory",
"id": "5b62bc16-3a71-461d-be2f-16bfcb011e8a",
"version": "1.0.0.0",
"version": "1.0.1.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
{
"name": "react-directory",
"version": "0.0.1",
"version": "1.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
@ -14,37 +15,36 @@
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
},
"dependencies": {
"@microsoft/sp-core-library": "1.8.2",
"@microsoft/sp-lodash-subset": "1.8.2",
"@microsoft/sp-office-ui-fabric-core": "1.8.2",
"@microsoft/sp-property-pane": "1.8.2",
"@microsoft/sp-webpart-base": "1.8.2",
"@microsoft/sp-core-library": "1.11.0",
"@microsoft/sp-lodash-subset": "1.11.0",
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
"@microsoft/sp-property-pane": "1.11.0",
"@microsoft/sp-webpart-base": "1.11.0",
"@pnp/pnpjs": "^1.3.3",
"@pnp/spfx-controls-react": "1.13.2",
"@pnp/spfx-property-controls": "1.15.0",
"@types/es6-promise": "0.0.33",
"@types/jquery": "^3.3.30",
"@types/react": "16.7.22",
"@types/react-dom": "16.8.0",
"@types/webpack-env": "1.13.1",
"@uifabric/fluent-theme": "^0.16.9",
"jquery": "^3.4.1",
"office-ui-fabric-react": "^6.182.0",
"office-ui-fabric-react": "6.214.0",
"react": "16.7.0",
"react-dom": "16.7.0"
},
"resolutions": {
"@types/react": "16.7.22"
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/rush-stack-compiler-3.3": "~0.2.x",
"@microsoft/sp-build-web": "1.8.2",
"@microsoft/sp-module-interfaces": "1.8.2",
"@microsoft/sp-tslint-rules": "1.8.2",
"@microsoft/sp-webpart-workbench": "1.8.2",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.11.0",
"@microsoft/sp-module-interfaces": "1.11.0",
"@microsoft/sp-tslint-rules": "1.11.0",
"@microsoft/sp-webpart-workbench": "1.11.0",
"@types/chai": "3.4.34",
"@types/es6-promise": "0.0.33",
"@types/mocha": "2.2.38",
"@types/react": "^16.7.22",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
"ajv": "~5.2.2",
"gulp": "~3.9.1",

View File

@ -0,0 +1,7 @@
import { PeoplePickerEntity } from '@pnp/sp';
export interface ISPServices {
searchUsers(searchString: string, searchFirstName: boolean);
}

View File

@ -0,0 +1,210 @@
{
"odata.metadata": "https://contoso.sharepoint.com/_api/$metadata#Microsoft.Office.Server.Search.REST.SearchResult",
"ElapsedTime": 34,
"PrimaryQueryResult": {
"CustomResults": [],
"QueryId": "70699796-a977-4437-b771-92a074e322e6",
"QueryRuleId": "00000000-0000-0000-0000-000000000000",
"RefinementResults": null,
"RelevantResults": {
"GroupTemplateId": null,
"ItemTemplateId": null,
"Properties": [],
"ResultTitle": null,
"ResultTitleUrl": null,
"RowCount": 1,
"Table": {
"Rows": [{
"Cells": [{
"Key": "Rank",
"Value": "0",
"ValueType": "Edm.Double"
}, {
"Key": "DocId",
"Value": "17646696630634",
"ValueType": "Edm.Int64"
}, {
"Key": "FirstName",
"Value": "Peter Paul",
"ValueType": "Edm.String"
}, {
"Key": "LastName",
"Value": "Kirschner",
"ValueType": "Edm.String"
}, {
"Key": "PreferredName",
"Value": "Peter Paul Kirschner",
"ValueType": "Edm.String"
}, {
"Key": "WorkEmail",
"Value": "petkir@pkirschner.onmicrosoft.com",
"ValueType": "Edm.String"
}, {
"Key": "OfficeNumber",
"Value": null,
"ValueType": "Null"
}, {
"Key": "PictureURL",
"Value": "",
"ValueType": "Edm.String"
}, {
"Key": "WorkPhone",
"Value": "4250000000",
"ValueType": "Edm.String"
}, {
"Key": "MobilePhone",
"Value": null,
"ValueType": "Null"
}, {
"Key": "JobTitle",
"Value": null,
"ValueType": "Null"
}, {
"Key": "Department",
"Value": null,
"ValueType": "Null"
}, {
"Key": "Skills",
"Value": "",
"ValueType": "Edm.String"
}, {
"Key": "PastProjects",
"Value": "",
"ValueType": "Edm.String"
}, {
"Key": "BaseOfficeLocation",
"Value": null,
"ValueType": "Null"
}, {
"Key": "SPS-UserType",
"Value": "0",
"ValueType": "Edm.Int64"
}, {
"Key": "GroupId",
"Value": null,
"ValueType": "Null"
}, {
"Key": "SiteId",
"Value": null,
"ValueType": "Null"
}, {
"Key": "WebId",
"Value": null,
"ValueType": "Null"
}, {
"Key": "UniqueId",
"Value": null,
"ValueType": "Null"
}, {
"Key": "contentclass",
"Value": "urn:content-class:SPSPeople",
"ValueType": "Edm.String"
}, {
"Key": "PartitionId",
"Value": "92ab9c96-469b-4d78-8b8c-961c4df9356b",
"ValueType": "Edm.Guid"
}, {
"Key": "UrlZone",
"Value": "0",
"ValueType": "Edm.Int32"
}, {
"Key": "Culture",
"Value": "en-US",
"ValueType": "Edm.String"
}, {
"Key": "ResultTypeId",
"Value": "0",
"ValueType": "Edm.Int32"
}, {
"Key": "EditProfileUrl",
"Value": null,
"ValueType": "Null"
}, {
"Key": "ProfileViewsLastMonth",
"Value": null,
"ValueType": "Null"
}, {
"Key": "ProfileViewsLastWeek",
"Value": null,
"ValueType": "Null"
}, {
"Key": "ProfileQueriesFoundYou",
"Value": null,
"ValueType": "Null"
}, {
"Key": "RenderTemplateId",
"Value": "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js",
"ValueType": "Edm.String"
}, {
"Key": "GeoLocationSource",
"Value": "EUR",
"ValueType": "Edm.String"
}]
}]
},
"TotalRows": 1,
"TotalRowsIncludingDuplicates": 1
},
"SpecialTermResults": null
},
"Properties": [{
"Key": "RowLimit",
"Value": "500",
"ValueType": "Edm.Int32"
}, {
"Key": "SourceId",
"Value": "b09a7990-05ea-4af9-81ef-edfab16c4e31",
"ValueType": "Edm.Guid"
}, {
"Key": "CorrelationId",
"Value": "e83b679f-b0c6-2000-3de3-321f0ebd7f6d",
"ValueType": "Edm.Guid"
}, {
"Key": "WasGroupRestricted",
"Value": "false",
"ValueType": "Edm.Boolean"
}, {
"Key": "WordBreakerLanguage",
"Value": "default",
"ValueType": "Edm.String"
}, {
"Key": "IsPartialUpnDocIdMapping",
"Value": "false",
"ValueType": "Edm.Boolean"
}, {
"Key": "EnableInterleaving",
"Value": "true",
"ValueType": "Edm.Boolean"
}, {
"Key": "IsMissingUnifiedGroups",
"Value": "false",
"ValueType": "Edm.Boolean"
}, {
"Key": "Constellation",
"Value": "i31EEE",
"ValueType": "Edm.String"
}, {
"Key": "MultiGeoSearchStatus",
"Value": "Full",
"ValueType": "Edm.String"
}, {
"Key": "HasParseException",
"Value": "false",
"ValueType": "Edm.Boolean"
}, {
"Key": "IsPartial",
"Value": "false",
"ValueType": "Edm.Boolean"
}, {
"Key": "InternalRequestId",
"Value": "e44dc9d5-b4dc-4f4e-84f1-eef4eb163ad4",
"ValueType": "Edm.String"
}, {
"Key": "SerializedQuery",
"Value": "<Query Culture=\"en-US\" EnableStemming=\"True\" EnablePhonetic=\"False\" EnableNicknames=\"False\" IgnoreAllNoiseQuery=\"True\" SummaryLength=\"180\" MaxSnippetLength=\"180\" DesiredSnippetLength=\"90\" KeywordInclusion=\"0\" QueryText=\"LastName:kir*\" QueryTemplate=\"\" TrimDuplicates=\"True\" Site=\"3ea90dc6-5d70-4967-80a0-e07cbae5867f\" Web=\"ea51dacc-87ca-49cd-9f28-13fd2cb3b96b\" KeywordType=\"True\" HiddenConstraints=\"\" />",
"ValueType": "Edm.String"
}],
"SecondaryQueryResults": [],
"SpellingSuggestion": null,
"TriggeredRules": []
}

View File

@ -0,0 +1,112 @@
import { ISPServices } from "./ISPServices";
import * as mockdata from './MockDataSearch.json';
import { cloneDeep } from '@microsoft/sp-lodash-subset';
interface MinimalMockUser {
FirstName: string;
LastName: string;
Department: string;
Location: string;
Title: string;
PreferredName: string;
WorkPhone: string;
}
/*
DisplayName: user.PreferredName,
Title: user.JobTitle,
PictureUrl: user.PictureURL,
Email: user.WorkEmail,
Department: user.Department,
WorkPhone: user.WorkPhone,
Location: user.OfficeNumber
? user.OfficeNumber
: user.BaseOfficeLocation
*/
const sampleUserFirstNameLetter: string[] = [
"A",
"C",
"D",
"F",
"H",
"J",
"L",
"N",
"P",
"R",
"T",
"V",
"X",
"Z",
];
const sampleUserLastNameLetter: string[] = [
"B",
"E",
"G",
"I",
"K",
"M",
"O",
"Q",
"S",
"U",
"W",
"Y",
];
export class spMockServices implements ISPServices {
private sampleData: MinimalMockUser[] = [];
constructor() {
sampleUserLastNameLetter.forEach(lastNameL => {
sampleUserFirstNameLetter.forEach(firstNameL => {
const usercount = Math.floor(Math.random() * (5)) + 1;
for (let i = 0; i < usercount; i++) {
this.sampleData.push({
FirstName: `${firstNameL}FirstName${i}`,
LastName: `${lastNameL}LastName${i}`,
PreferredName: `${firstNameL}FirstName${i} ${lastNameL}LastName${i}`,
Department: i % 2 === 0 ? `${lastNameL}Department` : `${firstNameL}Department`,
Location: i % 3 === 0 ? `${lastNameL}Location` : `${firstNameL}Location`,
Title: i % 2 === 0 ? `${lastNameL}JobTitle` : `${firstNameL}JobTitle`,
WorkPhone: '' + Math.floor(Math.random() * 1234) + 54678900
});
}
});
});
}
public async searchUsers(searchString: string, searchFirstName: boolean) {
let filtervalue = searchString.trim().toLowerCase();
if (searchString.length > 0 && filtervalue.lastIndexOf("*") == searchString.length - 1) {
// remove last '*'
filtervalue = filtervalue.substring(0, searchString.length - 1);
}
if (!filtervalue || filtervalue.length === 0) {
throw new Error("No valid Input.");
}
const searchresult = !!searchFirstName ?
this.sampleData.filter(p => p.FirstName.toLowerCase().indexOf(filtervalue) === 0) :
this.sampleData.filter(p => p.LastName.toLowerCase().indexOf(filtervalue) === 0);
const timeout = Math.floor(Math.random() * (1000)) + 1;
const resultdata = {
ElapsedTime: timeout,
RowCount: searchresult.length,
TotalRows: searchresult.length,
PrimarySearchResults: searchresult
};
return new Promise((resolve) => {
setTimeout(() => {
resolve(resultdata);
}, timeout);
});
}
}

View File

@ -3,9 +3,10 @@ import { graph } from "@pnp/graph";
import { sp, PeoplePickerEntity, ClientPeoplePickerQueryParameters, SearchQuery, SearchResults, SearchProperty, SortDirection } from '@pnp/sp';
import { PrincipalType } from "@pnp/sp/src/sitegroups";
import { isRelativeUrl } from "office-ui-fabric-react";
import { ISPServices } from "./ISPServices";
export class spservices {
export class spservices implements ISPServices {
@ -17,44 +18,8 @@ export class spservices {
});
}
public async getUserProperties(user: string): Promise<any> {
try {
let currentUserProperties: any = await sp.profiles.getPropertiesFor(user);
return currentUserProperties;
} catch (error) {
Promise.reject(error);
}
}
/**
* async GetUserProfileProperty
user:string */
public async getUserProfileProperty(user: string, property: string): Promise<string> {
try {
let UserProperty: string = await sp.profiles.getUserProfilePropertyFor(user, property);
console.log(UserProperty);
return UserProperty;
} catch (error) {
Promise.reject(error);
}
}
/**
*
* @param {string} searchUser
* @memberof spservices
*/
public async getUsers(searchUser: string): Promise<PeoplePickerEntity[]> {
try {
let users: PeoplePickerEntity[] = await sp.profiles.clientPeoplePickerSearchUser({ QueryString: searchUser, MaximumEntitySuggestions: 100, PrincipalType: 1 });
return users;
} catch (error) {
Promise.reject(error);
}
}
public async searchUsers(searchString: string, searchFirstName:boolean) {
public async searchUsers(searchString: string, searchFirstName: boolean): Promise<SearchResults> {
const _search = !searchFirstName ? `LastName:${searchString}*` : `FirstName:${searchString}*`;
const searchProperties: string[] = ["FirstName", "LastName", "PreferredName", "WorkEmail", "OfficeNumber", "PictureURL", "WorkPhone", "MobilePhone", "JobTitle", "Department", "Skills", "PastProjects", "BaseOfficeLocation", "SPS-UserType", "GroupId"];
try {

View File

@ -1,4 +1,4 @@
@import "~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss";
@import '~office-ui-fabric-react/dist/sass/References.scss';
.directory {
.dropDownSortBy {

View File

@ -1,11 +1,9 @@
import * as React from "react";
import styles from "./Directory.module.scss";
import { IDirectoryProps } from "./IDirectoryProps";
import { escape } from "@microsoft/sp-lodash-subset";
import { PersonaCard } from "./PersonaCard/PersonaCard";
import { spservices } from "../../../SPServices/spservices";
import { IDirectoryState } from "./IDirectoryState";
import { DisplayMode } from "@microsoft/sp-core-library";
import * as strings from "DirectoryWebPartStrings";
import {
Spinner,
@ -20,15 +18,13 @@ import {
PivotLinkFormat,
PivotLinkSize,
Dropdown,
DropdownMenuItemType,
IDropdownStyles,
IDropdownOption
} from "office-ui-fabric-react";
import { IProfileProperties } from "../../../SPServices/IProfileProperties";
import { PeoplePickerEntity, Search, SearchResult } from "@pnp/sp";
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { Root } from "@pnp/graph";
import { IUserProperties } from "./PersonaCard/IUserProperties";
import { ISPServices } from "../../../SPServices/ISPServices";
import { Environment, EnvironmentType } from "@microsoft/sp-core-library";
import { spMockServices } from "../../../SPServices/spMockServices";
const az: string[] = [
"A",
@ -69,7 +65,7 @@ export default class Directory extends React.Component<
IDirectoryProps,
IDirectoryState
> {
private _services: spservices = null;
private _services: ISPServices = null;
constructor(props: IDirectoryProps) {
super(props);
@ -80,14 +76,19 @@ export default class Directory extends React.Component<
errorMessage: "",
hasError: false,
indexSelectedKey: "A",
searchString: "LastName"
searchString: "LastName",
searchText: ""
};
if (Environment.type === EnvironmentType.Local) {
this._services = new spMockServices();
} else {
this._services = new spservices(this.props.context);
}
// Register event handlers
this._searchUsers = this._searchUsers.bind(this);
this._selectedIndex = this._selectedIndex.bind(this);
this._sortPeople = this._sortPeople.bind(this);
this._searchBoxChanged = this._searchBoxChanged.bind(this);
}
/**
@ -124,7 +125,9 @@ export default class Directory extends React.Component<
image.src = pictureUrl;
});
}
private _searchBoxChanged(newvalue: string): void {
this.setState({ searchText: newvalue }, () => this._searchUsers(newvalue));
}
private async _searchUsers(searchText: string) {
searchText = searchText.trim().length > 0 ? searchText : "A";
@ -139,7 +142,7 @@ export default class Directory extends React.Component<
searchText,
this.props.searchFirstName
);
debugger;
if (users && users.PrimarySearchResults.length > 0) {
for (let index = 0; index < users.PrimarySearchResults.length; index++) {
let user: any = users.PrimarySearchResults[index];
@ -279,7 +282,7 @@ export default class Directory extends React.Component<
* @memberof Directory
*/
private _selectedIndex(item?: PivotItem, ev?: React.MouseEvent<HTMLElement>) {
this._searchUsers(item.props.itemKey);
this.setState({ searchText: "" }, () => this._searchUsers(item.props.itemKey));
}
/**
*
@ -336,7 +339,8 @@ export default class Directory extends React.Component<
onClear={() => {
this._searchUsers("A");
}}
onChange={this._searchUsers}
value={this.state.searchText}
onChange={this._searchBoxChanged}
/>
<div>
<Pivot

View File

@ -1,5 +1,4 @@
import { IProfileProperties } from "./../../../SPServices/IProfileProperties";
import { PeoplePickerEntity, SearchResult, SearchResults } from "@pnp/pnpjs";
export interface IDirectoryState {
users: any;
isLoading: boolean;
@ -7,4 +6,5 @@ export interface IDirectoryState {
hasError: boolean;
indexSelectedKey: string;
searchString: string;
searchText: string;
}

View File

@ -1,7 +1,6 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { ApplicationCustomizerContext } from "@microsoft/sp-application-base";
import { IUserProperties } from "./IUserProperties";
import { PeoplePickerEntity, SearchResult, SearchResults } from "@pnp/pnpjs";
export interface IPersonaCardProps {
context: WebPartContext | ApplicationCustomizerContext;

View File

@ -1,4 +1,4 @@
@import "~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss";
@import '~office-ui-fabric-react/dist/sass/References.scss';
.personaContainer {
display: inline-block;

View File

@ -3,24 +3,14 @@ import styles from './PersonaCard.module.scss';
import { IPersonaCardProps } from './IPersonaCardProps';
import { IPersonaCardState } from './IPersonaCardState';
import {
Version,
Environment,
EnvironmentType,
ServiceScope,
Log,
Text,
Log, Environment, EnvironmentType,
} from '@microsoft/sp-core-library';
import { SPComponentLoader } from '@microsoft/sp-loader';
import {
Persona,
PersonaCoin,
PersonaInitialsColor,
PersonaSize,
IPersonaStyles,
Label,
DocumentCard,
IDocumentCardStyles,
DocumentCardType,
Icon,
} from 'office-ui-fabric-react';
@ -44,6 +34,7 @@ export class PersonaCard extends React.Component<
* @memberof PersonaCard
*/
public async componentDidMount() {
if (Environment.type !== EnvironmentType.Local) {
const sharedLibrary = await this._loadSPComponentById(
LIVE_PERSONA_COMPONENT_ID
);
@ -51,6 +42,7 @@ export class PersonaCard extends React.Component<
console.log(livePersonaCard);
this.setState({ livePersonaCard: livePersonaCard });
}
}
/**
*

View File

@ -5,6 +5,7 @@
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule" : true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
@ -29,7 +30,8 @@
]
},
"include": [
"src/**/*.ts"
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",

19055
samples/react-script-editor/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff