Upgrade to 1.17.1 and linter changes

This commit is contained in:
Peter Paul Kirschner 2023-06-20 13:51:35 +02:00
parent 2512a1cbed
commit 221117cda9
29 changed files with 49877 additions and 19141 deletions

View File

@ -0,0 +1,5 @@
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
parserOptions: { tsconfigRootDir: __dirname }
};

View File

@ -9,9 +9,11 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
*.sppkg
.heft
# Coverage directory used by tools like istanbul
coverage

View File

@ -0,0 +1,16 @@
!dist
config
gulpfile.js
release
src
temp
tsconfig.json
tslint.json
*.log
.yo-rc.json
.vscode

View File

@ -1,6 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0",
"plusBeta": false,
"nodeVersion": "18.16.0",
"sdksVersions": {
"@microsoft/microsoft-graph-client": "3.0.2",
"@microsoft/teams-js": "2.9.1"
},
"version": "1.17.1",
"libraryName": "react-directory",
"libraryId": "5b62bc16-3a71-461d-be2f-16bfcb011e8a",
"environment": "spo",
@ -8,6 +14,9 @@
"framework": "react",
"isCreatingSolution": true,
"isDomainIsolated": false,
"componentType": "webpart"
"componentType": "webpart",
"solutionName": "\\",
"solutionShortDescription": "\\ description",
"skipFeatureDeployment": true
}
}

View File

@ -36,8 +36,8 @@ Search People from Organization Directory and show live persona card on hover.
| Every SPFx version is only compatible with specific version(s) of Node.js. In order to be able to build this sample, please ensure that the version of Node on your workstation matches one of the versions listed in this section. This sample will not work on a different version of Node.|
|Refer to <https://aka.ms/spfx-matrix> for more information on SPFx compatibility. |
![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg)
![Node.js v10](https://img.shields.io/badge/Node.js-v10-green.svg)
![SPFx 1.17.1](https://img.shields.io/badge/SPFx-1.17.1-green.svg)
![Node.js v16](https://img.shields.io/badge/Node.js-v16-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
@ -82,6 +82,7 @@ Version|Date|Comments
3.0.1|March 4 2021|Bugfix 'Sort People by'
3.0.2|Oct 3 2022|Minor styling fixes and people container position
3.0.3|Oct 4 2022|Fix for LivePersonaCard
3.0.4|Jun 20 2023|Upgrade to SPFx 1.17.1
## Minimal Path to Awesome

View File

@ -1,4 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -1,7 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-directory",
"container": "",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -10,10 +10,32 @@
},
"name": "Search Directory",
"id": "5b62bc16-3a71-461d-be2f-16bfcb011e8a",
"version": "3.0.3.0",
"version": "3.0.4.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false
"isDomainIsolated": false,
"metadata": {
"shortDescription": {
"default": "react-directory description"
},
"longDescription": {
"default": "react-directory description"
},
"screenshotPaths": [],
"videoUrl": "",
"categories": []
},
"features": [
{
"title": "react-directory DirectoryWebPart Feature",
"description": "The feature that activates DirectoryWebPart from the react-directory solution.",
"id": "fae479bf-405f-4f80-a086-eea22eff3d6f",
"version": "3.0.4.0",
"componentIds": [
"fae479bf-405f-4f80-a086-eea22eff3d6f"
]
}
]
},
"paths": {
"zippedPackage": "solution/react-directory.sppkg"

View File

@ -0,0 +1,3 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json"
}

View File

@ -1,10 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx",
}

View File

@ -1,29 +1,20 @@
'use strict';
// check if gulp dist was called
if (process.argv.indexOf('dist') !== -1) {
// add ship options to command call
process.argv.push('--ship');
}
const path = require('path');
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
const gulpSequence = require('gulp-sequence');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
// Create clean distrubution package
gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
// Create clean development package
gulp.task('dev', gulpSequence('clean', 'bundle', 'package-solution'));
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
/**
* Custom Framework Specific gulp tasks
*/
build.initialize(gulp);
build.initialize(require('gulp'));

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +1,48 @@
{
"name": "react-directory",
"version": "2.0.0",
"version": "3.0.4",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
"node": ">=16.13.0 <17.0.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"preversion": "node ./tools/pre-version.js",
"postversion": "gulp dist",
"test": "./node_modules/.bin/jest --config ./config/jest.config.json",
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
"test": "gulp test"
},
"dependencies": {
"@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.16.1",
"@microsoft/sp-webpart-base": "1.16.1",
"@pnp/pnpjs": "^1.3.3",
"@pnp/spfx-controls-react": "1.13.2",
"@pnp/spfx-property-controls": "1.15.0",
"@types/jquery": "^3.3.30",
"@uifabric/fluent-theme": "^0.16.9",
"jquery": "^3.5.0",
"lodash": "^4.17.21",
"office-ui-fabric-react": "6.214.0",
"react": "16.7.0",
"react-dom": "16.7.0",
"@microsoft/sp-core-library": "1.17.1",
"@microsoft/sp-lodash-subset": "1.17.1",
"@microsoft/sp-office-ui-fabric-core": "1.17.1",
"@microsoft/sp-property-pane": "1.17.1",
"@microsoft/sp-webpart-base": "1.17.1",
"@pnp/sp": "2.5.0",
"@pnp/spfx-controls-react": "3.14.0",
"@pnp/spfx-property-controls": "^3.13.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-js-pagination": "^3.0.3",
"throttle-debounce": "^2.3.0"
},
"resolutions": {
"@types/react": "16.8.8"
"throttle-debounce": "^5.0.0",
"tslib": "2.3.1"
},
"devDependencies": {
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.16.1",
"@microsoft/sp-module-interfaces": "1.11.0",
"@microsoft/sp-tslint-rules": "1.11.0",
"@microsoft/sp-webpart-workbench": "1.12.1",
"@types/chai": "3.4.34",
"@types/es6-promise": "0.0.33",
"@types/mocha": "2.2.38",
"@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": "~6.12.3",
"gulp": "~4.0.2",
"gulp-sequence": "1.0.0",
"jest": "^29.3.1",
"jest-junit": "^6.3.0",
"typescript": "~3.3.x"
},
"jest-junit": {
"output": "temp/test/junit/junit.xml",
"usePathForSuiteName": "true"
"@microsoft/eslint-config-spfx": "1.17.1",
"@microsoft/eslint-plugin-spfx": "1.17.1",
"@microsoft/rush-stack-compiler-4.5": "0.2.2",
"@microsoft/sp-build-web": "1.17.1",
"@microsoft/sp-module-interfaces": "1.17.1",
"@rushstack/eslint-config": "2.5.1",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/react-js-pagination": "^3.0.4",
"@types/throttle-debounce": "^5.0.0",
"@types/webpack-env": "~1.15.2",
"ajv": "^6.12.5",
"eslint": "8.7.0",
"eslint-plugin-react-hooks": "4.3.0",
"gulp": "4.0.2"
}
}

View File

@ -1,8 +1,9 @@
import { PeoplePickerEntity } from '@pnp/sp';
import { SearchResults } from '@pnp/sp/search';
export interface ISPServices {
searchUsers(searchString: string, searchFirstName: boolean);
searchUsersNew(searchString: string, srchQry: string, isInitialSearch: boolean, pageNumber?: number);
searchUsers(searchString: string, searchFirstName: boolean): Promise<SearchResults>;
searchUsersNew(searchString: string, srchQry: string, isInitialSearch: boolean): Promise<SearchResults>;
}

View File

@ -1,210 +0,0 @@
{
"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

@ -1,143 +0,0 @@
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;
BaseOfficeLocation: 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`,
BaseOfficeLocation: 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);
});
}
public async searchUsersNew(searchString: string, srchQry: string, isInitialSearch: boolean, pageNumber?: number) {
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 = !!isInitialSearch ?
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

@ -1,89 +1,95 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
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 { sp } from '@pnp/sp';
import { SearchResults, ISearchQuery, SortDirection } from '@pnp/sp/search';
import { ISPServices } from "./ISPServices";
export class spservices implements ISPServices {
constructor(private context: WebPartContext) {
sp.setup({
spfxContext: this.context
});
}
constructor(private context: WebPartContext) {
sp.setup({
spfxContext: {
pageContext: {
web: {
absoluteUrl: this.context.pageContext.web.absoluteUrl
}
}
}
});
}
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"];
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 {
if (!searchString) return undefined;
const users = await sp.searchWithCaching(<ISearchQuery>{
Querytext: _search,
RowLimit: 500,
EnableInterleaving: true,
SelectProperties: searchProperties,
SourceId: 'b09a7990-05ea-4af9-81ef-edfab16c4e31',
SortList: [{ "Property": "LastName", "Direction": SortDirection.Ascending }],
});
return users;
} catch (error) {
Promise.reject(error);
}
}
public async _getImageBase64(pictureUrl: string): Promise<string> {
return new Promise((resolve) => {
const image = new Image();
image.addEventListener("load", () => {
const tempCanvas = document.createElement("canvas");
tempCanvas.width = image.width,
tempCanvas.height = image.height,
tempCanvas.getContext("2d").drawImage(image, 0, 0);
let base64Str;
try {
if (!searchString) return undefined;
let users = await sp.searchWithCaching(<SearchQuery>{
Querytext: _search,
RowLimit: 500,
EnableInterleaving: true,
SelectProperties: searchProperties,
SourceId: 'b09a7990-05ea-4af9-81ef-edfab16c4e31',
SortList: [{ "Property": "LastName", "Direction": SortDirection.Ascending }],
});
return users;
} catch (error) {
Promise.reject(error);
base64Str = tempCanvas.toDataURL("image/png");
} catch (e) {
return "";
}
}
resolve(base64Str);
});
image.src = pictureUrl;
});
}
public async _getImageBase64 (pictureUrl: string): Promise<string> {
return new Promise((resolve, reject) => {
let image = new Image();
image.addEventListener("load", () => {
let tempCanvas = document.createElement("canvas");
tempCanvas.width = image.width,
tempCanvas.height = image.height,
tempCanvas.getContext("2d").drawImage(image, 0, 0);
let base64Str;
try {
base64Str = tempCanvas.toDataURL("image/png");
} catch (e) {
return "";
}
resolve(base64Str);
});
image.src = pictureUrl;
});
public async searchUsersNew(searchString: string, srchQry: string, isInitialSearch: boolean): Promise<SearchResults> {
let qrytext = '';
if (isInitialSearch) qrytext = `FirstName:${searchString}* OR LastName:${searchString}*`;
else {
if (srchQry) qrytext = srchQry;
else {
if (searchString) qrytext = searchString;
}
if (qrytext.length <= 0) qrytext = `*`;
}
public async searchUsersNew(searchString: string, srchQry: string, isInitialSearch: boolean, pageNumber?: number): Promise<SearchResults> {
let qrytext: string = '';
if (isInitialSearch) qrytext = `FirstName:${searchString}* OR LastName:${searchString}*`;
else {
if (srchQry) qrytext = srchQry;
else {
if (searchString) qrytext = searchString;
}
if (qrytext.length <= 0) qrytext = `*`;
}
const searchProperties: string[] = ["FirstName", "LastName", "PreferredName", "WorkEmail", "OfficeNumber", "PictureURL", "WorkPhone", "MobilePhone", "JobTitle", "Department", "Skills", "PastProjects", "BaseOfficeLocation", "SPS-UserType", "GroupId"];
try {
let users = await sp.search(<SearchQuery>{
Querytext: qrytext,
RowLimit: 500,
EnableInterleaving: true,
SelectProperties: searchProperties,
SourceId: 'b09a7990-05ea-4af9-81ef-edfab16c4e31',
SortList: [{ "Property": "LastName", "Direction": SortDirection.Ascending }],
});
if (users && users.PrimarySearchResults.length > 0) {
for (let index = 0; index < users.PrimarySearchResults.length; index++) {
let user: any = users.PrimarySearchResults[index];
if (user.PictureURL) {
user = { ...user, PictureURL: `/_layouts/15/userphoto.aspx?size=M&accountname=${user.WorkEmail}` };
users.PrimarySearchResults[index] = user;
}
}
}
return users;
} catch (error) {
Promise.reject(error);
const searchProperties: string[] = ["FirstName", "LastName", "PreferredName", "WorkEmail", "OfficeNumber", "PictureURL", "WorkPhone", "MobilePhone", "JobTitle", "Department", "Skills", "PastProjects", "BaseOfficeLocation", "SPS-UserType", "GroupId"];
try {
const users = await sp.search(<ISearchQuery>{
Querytext: qrytext,
RowLimit: 500,
EnableInterleaving: true,
SelectProperties: searchProperties,
SourceId: 'b09a7990-05ea-4af9-81ef-edfab16c4e31',
SortList: [{ "Property": "LastName", "Direction": SortDirection.Ascending }],
});
if (users && users.PrimarySearchResults.length > 0) {
for (let index = 0; index < users.PrimarySearchResults.length; index++) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let user:any = users.PrimarySearchResults[index];
if (user.PictureURL) {
user = { ...user, PictureURL: `/_layouts/15/userphoto.aspx?size=M&accountname=${user.WorkEmail}` };
users.PrimarySearchResults[index] = user;
}
}
}
return users;
} catch (error) {
Promise.reject(error);
}
}
}

View File

@ -1,4 +1,4 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
@import '~@fluentui/react/dist/sass/References.scss';
.directory {
.dropDownSortBy {

View File

@ -6,374 +6,327 @@ import { spservices } from "../../../SPServices/spservices";
import { IDirectoryState } from "./IDirectoryState";
import * as strings from "DirectoryWebPartStrings";
import {
Spinner, SpinnerSize, MessageBar, MessageBarType, SearchBox, Icon, Label,
Pivot, PivotItem, PivotLinkFormat, PivotLinkSize, Dropdown, IDropdownOption
Spinner, SpinnerSize, MessageBar, MessageBarType, SearchBox, Icon, Label,
Pivot, PivotItem, PivotLinkFormat, PivotLinkSize, Dropdown, IDropdownOption
} from "office-ui-fabric-react";
import { Stack, IStackTokens } from 'office-ui-fabric-react/lib/Stack';
import { debounce } from "throttle-debounce";
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { Stack, IStackTokens } from 'office-ui-fabric-react/lib/Stack';
//import { debounce } from "throttle-debounce";
import { WebPartTitle } from "@pnp/spfx-controls-react";
import { ISPServices } from "../../../SPServices/ISPServices";
import { Environment, EnvironmentType } from "@microsoft/sp-core-library";
import { spMockServices } from "../../../SPServices/spMockServices";
import { IDirectoryProps } from './IDirectoryProps';
import Paging from './Pagination/Paging';
const slice: any = require('lodash/slice');
const filter: any = require('lodash/filter');
const wrapStackTokens: IStackTokens = { childrenGap: 30 };
const DirectoryHook: React.FC<IDirectoryProps> = (props) => {
let _services: ISPServices = null;
if (Environment.type === EnvironmentType.Local) {
_services = new spMockServices();
} else {
_services = new spservices(props.context);
const _services: ISPServices = new spservices(props.context);
const [az, setaz] = useState<string[]>([]);
const [alphaKey, setalphaKey] = useState<string>('A');
const [state, setstate] = useState<IDirectoryState>({
users: [],
isLoading: true,
errorMessage: "",
hasError: false,
indexSelectedKey: "A",
searchString: "LastName",
searchText: ""
});
const orderOptions: IDropdownOption[] = [
{ key: "FirstName", text: "First Name" },
{ key: "LastName", text: "Last Name" },
{ key: "Department", text: "Department" },
{ key: "Location", text: "Location" },
{ key: "JobTitle", text: "Job Title" }
];
const color = props.context.microsoftTeams ? "white" : "";
// Paging
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [pagedItems, setPagedItems] = useState<any[]>([]);
const [pageSize, setPageSize] = useState<number>(props.pageSize ? props.pageSize : 10);
const [currentPage, setCurrentPage] = useState<number>(1);
const _onPageUpdate = async (pageno?: number) => {
const currentPge = (pageno) ? pageno : currentPage;
const startItem = ((currentPge - 1) * pageSize);
const endItem = currentPge * pageSize;
const filItems = state.users.slice(startItem, endItem);
setCurrentPage(currentPge);
setPagedItems(filItems);
};
const diretoryGrid =
pagedItems && pagedItems.length > 0
// eslint-disable-next-line @typescript-eslint/no-explicit-any
? pagedItems.map((user: any, i) => {
return (
<PersonaCard
context={props.context}
key={"PersonaCard" + i}
profileProperties={{
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 _loadAlphabets = () => {
const alphabets: string[] = [];
for (let i = 65; i < 91; i++) {
alphabets.push(
String.fromCharCode(i)
);
}
const [az, setaz] = useState<string[]>([]);
const [alphaKey, setalphaKey] = useState<string>('A');
const [state, setstate] = useState<IDirectoryState>({
users: [],
isLoading: true,
errorMessage: "",
hasError: false,
indexSelectedKey: "A",
searchString: "LastName",
searchText: ""
setaz(alphabets);
};
const _alphabetChange = async (item?: PivotItem) => {
setstate({ ...state, searchText: "", indexSelectedKey: item.props.itemKey, isLoading: true });
setalphaKey(item.props.itemKey);
setCurrentPage(1);
};
const _searchByAlphabets = async (initialSearch: boolean) => {
setstate({ ...state, isLoading: true, searchText: '' });
let users = null;
if (initialSearch) {
if (props.searchFirstName)
users = await _services.searchUsersNew('', `FirstName:a*`, false);
else users = await _services.searchUsersNew('a', '', true);
} else {
if (props.searchFirstName)
users = await _services.searchUsersNew('', `FirstName:${alphaKey}*`, false);
else users = await _services.searchUsersNew(`${alphaKey}`, '', true);
}
setstate({
...state,
searchText: '',
indexSelectedKey: initialSearch ? 'A' : state.indexSelectedKey,
users:
users && users.PrimarySearchResults
? users.PrimarySearchResults
: null,
isLoading: false,
errorMessage: "",
hasError: false
});
const orderOptions: IDropdownOption[] = [
{ key: "FirstName", text: "First Name" },
{ key: "LastName", text: "Last Name" },
{ key: "Department", text: "Department" },
{ key: "Location", text: "Location" },
{ key: "JobTitle", text: "Job Title" }
];
const color = props.context.microsoftTeams ? "white" : "";
// Paging
const [pagedItems, setPagedItems] = useState<any[]>([]);
const [pageSize, setPageSize] = useState<number>(props.pageSize ? props.pageSize : 10);
const [currentPage, setCurrentPage] = useState<number>(1);
};
const _onPageUpdate = async (pageno?: number) => {
var currentPge = (pageno) ? pageno : currentPage;
var startItem = ((currentPge - 1) * pageSize);
var endItem = currentPge * pageSize;
let filItems = slice(state.users, startItem, endItem);
setCurrentPage(currentPge);
setPagedItems(filItems);
};
const diretoryGrid =
pagedItems && pagedItems.length > 0
? pagedItems.map((user: any) => {
return (
<PersonaCard
context={props.context}
profileProperties={{
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 _loadAlphabets = () => {
let alphabets: string[] = [];
for (let i = 65; i < 91; i++) {
alphabets.push(
String.fromCharCode(i)
);
}
setaz(alphabets);
};
const _alphabetChange = async (item?: PivotItem, ev?: React.MouseEvent<HTMLElement>) => {
setstate({ ...state, searchText: "", indexSelectedKey: item.props.itemKey, isLoading: true });
setalphaKey(item.props.itemKey);
setCurrentPage(1);
};
const _searchByAlphabets = async (initialSearch: boolean) => {
setstate({ ...state, isLoading: true, searchText: '' });
let users = null;
if (initialSearch) {
if (props.searchFirstName)
users = await _services.searchUsersNew('', `FirstName:a*`, false);
else users = await _services.searchUsersNew('a', '', true);
const _searchUsers = async (searchText: string) => {
try {
setstate({ ...state, searchText: searchText, isLoading: true });
if (searchText.length > 0) {
const searchProps: string[] = props.searchProps && props.searchProps.length > 0 ?
props.searchProps.split(',') : ['FirstName', 'LastName', 'WorkEmail', 'Department'];
let qryText = '';
const finalSearchText: string = searchText ? searchText.replace(/ /g, '+') : searchText;
if (props.clearTextSearchProps) {
const tmpCTProps: string[] = props.clearTextSearchProps.indexOf(',') >= 0 ? props.clearTextSearchProps.split(',') : [props.clearTextSearchProps];
if (tmpCTProps.length > 0) {
searchProps.map((srchprop, index) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ctPresent: any[] = tmpCTProps.filter( (o) => { return o.toLowerCase() == srchprop.toLowerCase(); });
if (ctPresent.length > 0) {
if (index == searchProps.length - 1) {
qryText += `${srchprop}:${searchText}*`;
} else qryText += `${srchprop}:${searchText}* OR `;
} else {
if (index == searchProps.length - 1) {
qryText += `${srchprop}:${finalSearchText}*`;
} else qryText += `${srchprop}:${finalSearchText}* OR `;
}
});
} else {
searchProps.map((srchprop, index) => {
if (index == searchProps.length - 1)
qryText += `${srchprop}:${finalSearchText}*`;
else qryText += `${srchprop}:${finalSearchText}* OR `;
});
}
} else {
if (props.searchFirstName)
users = await _services.searchUsersNew('', `FirstName:${alphaKey}*`, false);
else users = await _services.searchUsersNew(`${alphaKey}`, '', true);
searchProps.map((srchprop, index) => {
if (index == searchProps.length - 1)
qryText += `${srchprop}:${finalSearchText}*`;
else qryText += `${srchprop}:${finalSearchText}* OR `;
});
}
console.log(qryText);
const users = await _services.searchUsersNew('', qryText, false);
setstate({
...state,
searchText: '',
indexSelectedKey: initialSearch ? 'A' : state.indexSelectedKey,
users:
users && users.PrimarySearchResults
? users.PrimarySearchResults
: null,
isLoading: false,
errorMessage: "",
hasError: false
...state,
searchText: searchText,
indexSelectedKey: '0',
users:
users && users.PrimarySearchResults
? users.PrimarySearchResults
: null,
isLoading: false,
errorMessage: "",
hasError: false
});
};
let _searchUsers = async (searchText: string) => {
try {
setstate({ ...state, searchText: searchText, isLoading: true });
if (searchText.length > 0) {
let searchProps: string[] = props.searchProps && props.searchProps.length > 0 ?
props.searchProps.split(',') : ['FirstName', 'LastName', 'WorkEmail', 'Department'];
let qryText: string = '';
let finalSearchText: string = searchText ? searchText.replace(/ /g, '+') : searchText;
if (props.clearTextSearchProps) {
let tmpCTProps: string[] = props.clearTextSearchProps.indexOf(',') >= 0 ? props.clearTextSearchProps.split(',') : [props.clearTextSearchProps];
if (tmpCTProps.length > 0) {
searchProps.map((srchprop, index) => {
let ctPresent: any[] = filter(tmpCTProps, (o) => { return o.toLowerCase() == srchprop.toLowerCase(); });
if (ctPresent.length > 0) {
if(index == searchProps.length - 1) {
qryText += `${srchprop}:${searchText}*`;
} else qryText += `${srchprop}:${searchText}* OR `;
} else {
if(index == searchProps.length - 1) {
qryText += `${srchprop}:${finalSearchText}*`;
} else qryText += `${srchprop}:${finalSearchText}* OR `;
}
});
} else {
searchProps.map((srchprop, index) => {
if (index == searchProps.length - 1)
qryText += `${srchprop}:${finalSearchText}*`;
else qryText += `${srchprop}:${finalSearchText}* OR `;
});
}
} else {
searchProps.map((srchprop, index) => {
if (index == searchProps.length - 1)
qryText += `${srchprop}:${finalSearchText}*`;
else qryText += `${srchprop}:${finalSearchText}* OR `;
});
}
console.log(qryText);
const users = await _services.searchUsersNew('', qryText, false);
setstate({
...state,
searchText: searchText,
indexSelectedKey: '0',
users:
users && users.PrimarySearchResults
? users.PrimarySearchResults
: null,
isLoading: false,
errorMessage: "",
hasError: false
});
setalphaKey('0');
} else {
setstate({ ...state, searchText: '' });
_searchByAlphabets(true);
}
} catch (err) {
setstate({ ...state, errorMessage: err.message, hasError: true });
}
};
const _searchBoxChanged = (newvalue: string): void => {
setCurrentPage(1);
_searchUsers(newvalue);
};
_searchUsers = debounce(500, _searchUsers);
const _sortPeople = async (sortField: string) => {
let _users = [...state.users];
_users = _users.sort((a: any, b: any) => {
switch (sortField) {
// Sorte by FirstName
case "FirstName":
const aFirstName = a.FirstName ? a.FirstName : "";
const bFirstName = b.FirstName ? b.FirstName : "";
if (aFirstName.toUpperCase() < bFirstName.toUpperCase()) {
return -1;
}
if (aFirstName.toUpperCase() > bFirstName.toUpperCase()) {
return 1;
}
return 0;
break;
// Sort by LastName
case "LastName":
const aLastName = a.LastName ? a.LastName : "";
const bLastName = b.LastName ? b.LastName : "";
if (aLastName.toUpperCase() < bLastName.toUpperCase()) {
return -1;
}
if (aLastName.toUpperCase() > bLastName.toUpperCase()) {
return 1;
}
return 0;
break;
// Sort by Location
case "Location":
const aBaseOfficeLocation = a.BaseOfficeLocation
? a.BaseOfficeLocation
: "";
const bBaseOfficeLocation = b.BaseOfficeLocation
? b.BaseOfficeLocation
: "";
if (
aBaseOfficeLocation.toUpperCase() <
bBaseOfficeLocation.toUpperCase()
) {
return -1;
}
if (
aBaseOfficeLocation.toUpperCase() >
bBaseOfficeLocation.toUpperCase()
) {
return 1;
}
return 0;
break;
// Sort by JobTitle
case "JobTitle":
const aJobTitle = a.JobTitle ? a.JobTitle : "";
const bJobTitle = b.JobTitle ? b.JobTitle : "";
if (aJobTitle.toUpperCase() < bJobTitle.toUpperCase()) {
return -1;
}
if (aJobTitle.toUpperCase() > bJobTitle.toUpperCase()) {
return 1;
}
return 0;
break;
// Sort by Department
case "Department":
const aDepartment = a.Department ? a.Department : "";
const bDepartment = b.Department ? b.Department : "";
if (aDepartment.toUpperCase() < bDepartment.toUpperCase()) {
return -1;
}
if (aDepartment.toUpperCase() > bDepartment.toUpperCase()) {
return 1;
}
return 0;
break;
default:
break;
}
});
setstate({ ...state, users: _users, searchString: sortField });
};
useEffect(() => {
setPageSize(props.pageSize);
if (state.users) _onPageUpdate();
}, [state.users, props.pageSize]);
useEffect(() => {
if (alphaKey.length > 0 && alphaKey != "0") _searchByAlphabets(false);
}, [alphaKey]);
useEffect(() => {
_loadAlphabets();
setalphaKey('0');
} else {
setstate({ ...state, searchText: '' });
_searchByAlphabets(true);
}, [props]);
}
} catch (err) {
setstate({ ...state, errorMessage: err.message, hasError: true });
}
};
return (
<div className={styles.directory}>
<WebPartTitle displayMode={props.displayMode} title={props.title}
updateProperty={props.updateProperty} />
<div className={styles.searchBox}>
<SearchBox placeholder={strings.SearchPlaceHolder} className={styles.searchTextBox}
onSearch={_searchUsers}
value={state.searchText}
onChange={_searchBoxChanged} />
<div>
<Pivot className={styles.alphabets} linkFormat={PivotLinkFormat.tabs}
selectedKey={state.indexSelectedKey} onLinkClick={_alphabetChange}
linkSize={PivotLinkSize.normal} >
{az.map((index: string) => {
return (
<PivotItem headerText={index} itemKey={index} key={index} />
);
})}
</Pivot>
</div>
</div>
{state.isLoading ? (
<div style={{ marginTop: '10px' }}>
<Spinner size={SpinnerSize.large} label={strings.LoadingText} />
</div>
) : (
<>
{state.hasError ? (
<div style={{ marginTop: '10px' }}>
<MessageBar messageBarType={MessageBarType.error}>
{state.errorMessage}
</MessageBar>
</div>
) : (
<>
{!pagedItems || pagedItems.length == 0 ? (
<div className={styles.noUsers}>
<Icon
iconName={"ProfileSearch"}
style={{ fontSize: "54px", color: color }}
/>
<Label>
<span style={{ marginLeft: 5, fontSize: "26px", color: color }}>
{strings.DirectoryMessage}
</span>
</Label>
</div>
) : (
<>
<div style={{ width: '100%', display: 'inline-block' }}>
<Paging
totalItems={state.users.length}
itemsCountPerPage={pageSize}
onPageUpdate={_onPageUpdate}
currentPage={currentPage} />
</div>
<div className={styles.dropDownSortBy}>
<Stack horizontal horizontalAlign="center" wrap tokens={wrapStackTokens}>
<Dropdown
placeholder={strings.DropDownPlaceHolderMessage}
label={strings.DropDownPlaceLabelMessage}
options={orderOptions}
selectedKey={state.searchString}
onChange={(ev: any, value: IDropdownOption) => {
_sortPeople(value.key.toString());
}}
styles={{ dropdown: { width: 200 } }}
/>
</Stack>
</div>
<Stack horizontal horizontalAlign={props.useSpaceBetween?"space-between":"center"} wrap tokens={wrapStackTokens}>
{diretoryGrid}
</Stack>
<div style={{ width: '100%', display: 'inline-block' }}>
<Paging
totalItems={state.users.length}
itemsCountPerPage={pageSize}
onPageUpdate={_onPageUpdate}
currentPage={currentPage} />
</div>
</>
)}
</>
)}
</>
)}
const _searchBoxChanged = (newvalue: string): void => {
setCurrentPage(1);
_searchUsers(newvalue);
};
//_searchUsers = debounce(500, _searchUsers);
const _sortPeople = async (sortField: string) => {
let _users = [...state.users];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_users = _users.sort((a: any, b: any) => {
switch (sortField) {
// Sort by Location
case "Location":
if ((a.BaseOfficeLocation||"").toUpperCase() < (b.BaseOfficeLocation||"").toUpperCase()) {
return -1;
}
if ((a.BaseOfficeLocation||"").toUpperCase() > (b.BaseOfficeLocation||"").toUpperCase()) {
return 1;
}
return 0;
break;
break;
default:
if ((a[sortField]||"").toUpperCase() < (a[sortField]||"").toUpperCase()) {
return -1;
}
if ((a[sortField]||"").toUpperCase() > (a[sortField]||"").toUpperCase()) {
return 1;
}
return 0;
break;
}
});
setstate({ ...state, users: _users, searchString: sortField });
};
useEffect(() => {
setPageSize(props.pageSize);
if (state.users) _onPageUpdate();
}, [state.users, props.pageSize]);
useEffect(() => {
if (alphaKey.length > 0 && alphaKey != "0") _searchByAlphabets(false);
}, [alphaKey]);
useEffect(() => {
_loadAlphabets();
_searchByAlphabets(true);
}, [props]);
return (
<div className={styles.directory}>
<WebPartTitle displayMode={props.displayMode} title={props.title}
updateProperty={props.updateProperty} />
<div className={styles.searchBox}>
<SearchBox placeholder={strings.SearchPlaceHolder} className={styles.searchTextBox}
onSearch={_searchUsers}
value={state.searchText}
onChange={(ev,newVal) =>_searchBoxChanged(newVal)} />
<div>
<Pivot className={styles.alphabets} linkFormat={PivotLinkFormat.tabs}
selectedKey={state.indexSelectedKey} onLinkClick={_alphabetChange}
linkSize={PivotLinkSize.normal} >
{az.map((index: string) => {
return (
<PivotItem headerText={index} itemKey={index} key={index} />
);
})}
</Pivot>
</div>
);
</div>
{state.isLoading ? (
<div style={{ marginTop: '10px' }}>
<Spinner size={SpinnerSize.large} label={strings.LoadingText} />
</div>
) : (
<>
{state.hasError ? (
<div style={{ marginTop: '10px' }}>
<MessageBar messageBarType={MessageBarType.error}>
{state.errorMessage}
</MessageBar>
</div>
) : (
<>
{!pagedItems || pagedItems.length == 0 ? (
<div className={styles.noUsers}>
<Icon
iconName={"ProfileSearch"}
style={{ fontSize: "54px", color: color }}
/>
<Label>
<span style={{ marginLeft: 5, fontSize: "26px", color: color }}>
{strings.DirectoryMessage}
</span>
</Label>
</div>
) : (
<>
<div style={{ width: '100%', display: 'inline-block' }}>
<Paging
totalItems={state.users.length}
itemsCountPerPage={pageSize}
onPageUpdate={_onPageUpdate}
currentPage={currentPage} />
</div>
<div className={styles.dropDownSortBy}>
<Stack horizontal horizontalAlign="center" wrap tokens={wrapStackTokens}>
<Dropdown
placeholder={strings.DropDownPlaceHolderMessage}
label={strings.DropDownPlaceLabelMessage}
options={orderOptions}
selectedKey={state.searchString}
onChange={(ev, value) => {
_sortPeople(value.key.toString());
}}
styles={{ dropdown: { width: 200 } }}
/>
</Stack>
</div>
<Stack horizontal horizontalAlign={props.useSpaceBetween ? "space-between" : "center"} wrap tokens={wrapStackTokens}>
{diretoryGrid}
</Stack>
<div style={{ width: '100%', display: 'inline-block' }}>
<Paging
totalItems={state.users.length}
itemsCountPerPage={pageSize}
onPageUpdate={_onPageUpdate}
currentPage={currentPage} />
</div>
</>
)}
</>
)}
</>
)}
</div>
);
};
export default DirectoryHook;

View File

@ -1,5 +1,6 @@
export interface IDirectoryState {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
users: any;
isLoading: boolean;
errorMessage: string;

View File

@ -1,3 +1,5 @@
@import '~@fluentui/react/dist/sass/References.scss';
.paginationContainer {
text-align: right;
font-size: 14px;

View File

@ -33,10 +33,10 @@ const Paging: React.FC<IPagingProps> = (props) => {
<div className={styles.searchWp__paginationContainer__pagination}>
<Pagination
activePage={currentPage}
firstPageText={<i className='ms-Icon ms-Icon--DoubleChevronLeft' aria-hidden='true'></i>}
lastPageText={<i className='ms-Icon ms-Icon--DoubleChevronRight' aria-hidden='true'></i>}
prevPageText={<i className='ms-Icon ms-Icon--ChevronLeft' aria-hidden='true'></i>}
nextPageText={<i className='ms-Icon ms-Icon--ChevronRight' aria-hidden='true'></i>}
firstPageText={<i className='ms-Icon ms-Icon--DoubleChevronLeft' aria-hidden='true' />}
lastPageText={<i className='ms-Icon ms-Icon--DoubleChevronRight' aria-hidden='true' />}
prevPageText={<i className='ms-Icon ms-Icon--ChevronLeft' aria-hidden='true' />}
nextPageText={<i className='ms-Icon ms-Icon--ChevronRight' aria-hidden='true' />}
activeLinkClass={styles.active}
itemsCountPerPage={props.itemsCountPerPage}
totalItemsCount={props.totalItems}

View File

@ -1,4 +1,5 @@
export interface IPersonaCardState {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
livePersonaCard: any;
pictureUrl: string;
}

View File

@ -1,6 +1,6 @@
export interface IUserProperties {
Department: string;
MobilePhone?: String;
MobilePhone?: string;
PictureUrl: string;
Title: string;
DisplayName: string;

View File

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

View File

@ -2,9 +2,7 @@ import * as React from 'react';
import styles from './PersonaCard.module.scss';
import { IPersonaCardProps } from './IPersonaCardProps';
import { IPersonaCardState } from './IPersonaCardState';
import {
Log, Environment, EnvironmentType,
} from '@microsoft/sp-core-library';
import { Log } from '@microsoft/sp-core-library';
import { SPComponentLoader } from '@microsoft/sp-loader';
import {
@ -15,14 +13,13 @@ import {
Icon,
} from 'office-ui-fabric-react';
const EXP_SOURCE: string = 'SPFxDirectory';
const LIVE_PERSONA_COMPONENT_ID: string =
'914330ee-2df2-4f6e-a858-30c23a812408';
const EXP_SOURCE = 'SPFxDirectory';
const LIVE_PERSONA_COMPONENT_ID = '914330ee-2df2-4f6e-a858-30c23a812408';
export class PersonaCard extends React.Component<
IPersonaCardProps,
IPersonaCardState
> {
> {
constructor(props: IPersonaCardProps) {
super(props);
@ -34,26 +31,15 @@ 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
);
const livePersonaCard: any = sharedLibrary.LivePersonaCard;
this.setState({ livePersonaCard: livePersonaCard });
}
const sharedLibrary = await this._loadSPComponentById(
LIVE_PERSONA_COMPONENT_ID
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const livePersonaCard: any = sharedLibrary.LivePersonaCard;
this.setState({ livePersonaCard: livePersonaCard });
}
/**
*
*
* @param {IPersonaCardProps} prevProps
* @param {IPersonaCardState} prevState
* @memberof PersonaCard
*/
public componentDidUpdate(
prevProps: IPersonaCardProps,
prevState: IPersonaCardState
): void { }
/**
*
@ -111,8 +97,8 @@ export class PersonaCard extends React.Component<
</span>
</div>
) : (
''
)}
''
)}
{this.props.profileProperties.Location ? (
<div className={styles.textOverflow}>
<Icon iconName="Poi" style={{ fontSize: '12px' }} />
@ -122,8 +108,8 @@ export class PersonaCard extends React.Component<
</span>
</div>
) : (
''
)}
''
)}
</Persona>
</div>
</DocumentCard>
@ -133,8 +119,10 @@ export class PersonaCard extends React.Component<
* Load SPFx component by id, SPComponentLoader is used to load the SPFx components
* @param componentId - componentId, guid of the component library
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async _loadSPComponentById(componentId: string): Promise<any> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const component: any = await SPComponentLoader.loadComponentById(
componentId
);

View File

@ -1,11 +1,10 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.5/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule" : true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
@ -14,27 +13,27 @@
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noImplicitAny": true,
"noUnusedLocals": false,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
"es2015.collection",
"es2015.promise"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -1,30 +0,0 @@
{
"extends": "@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
}
}