Updated sample, container, linting issues

This commit is contained in:
Hugo Bernier 2023-03-12 14:35:31 -04:00
parent 643bf996f9
commit e31c94ac76
13 changed files with 54601 additions and 938 deletions

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.16.1",
"image": "docker.io/m365pnp/spfx:1.16.1",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
// Not needed for SPFx>= 1.12.1
// "5432": {
// "protocol": "https",
// "label": "Workbench",
// "onAutoForward": "silent"
// },
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,33 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
# Convert the generated PEM certificate to a CER certificate
openssl x509 -inform PEM -in ~/.rushstack/rushstack-serve.pem -outform DER -out ./spfx-dev-cert.cer
# Copy the PEM ecrtificate for non-Windows hosts
cp ~/.rushstack/rushstack-serve.pem ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -9,7 +9,7 @@
"This is sample web part using SPFx and MSGraph to fetch the users information based on First Name, Last Name and Email Address. web part will fetch data from directory using Graph API and display in details list." "This is sample web part using SPFx and MSGraph to fetch the users information based on First Name, Last Name and Email Address. web part will fetch data from directory using Graph API and display in details list."
], ],
"creationDateTime": "2020-11-07", "creationDateTime": "2020-11-07",
"updateDateTime": "2021-08-07", "updateDateTime": "2023-03-10",
"products": [ "products": [
"SharePoint" "SharePoint"
], ],
@ -20,7 +20,7 @@
}, },
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.11.0" "value": "1.16.1"
} }
], ],
"thumbnails": [ "thumbnails": [

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,16 @@
import { IMSGraphService } from "./IMSGraphService"; import { IMSGraphService } from "./IMSGraphService";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { IUserProperties } from "./IUserProperties"; import { IUserProperties } from "./IUserProperties";
import { MSGraphClient,MSGraphClientFactory } from "@microsoft/sp-http"; import { MSGraphClient } from "@microsoft/sp-http";
import { Log } from "@microsoft/sp-core-library"; import { Log } from "@microsoft/sp-core-library";
const LOG_SOURCE = "MSGraphService"; const LOG_SOURCE = "MSGraphService";
export class MSGraphService implements IMSGraphService{ export class MSGraphService implements IMSGraphService{
public async getUserProperties(email:string,client:MSGraphClient):Promise<IUserProperties[]>{ public async getUserProperties(email:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = []; const userProperties:IUserProperties[] = [];
try { try {
//let client:MSGraphClient = await context.msGraphClientFactory.getClient().then(); //let client:MSGraphClient = await context.msGraphClientFactory.getClient().then();
let endPoint:string = `/Users/${email}`; const endPoint = `/Users/${email}`;
let response = await client.api(`${endPoint}`).version("v1.0").get(); const response = await client.api(`${endPoint}`).version("v1.0").get();
if(response){ if(response){
userProperties.push({ userProperties.push({
businessPhone:response.businessPhones[0], businessPhone:response.businessPhones[0],
@ -31,12 +30,13 @@ export class MSGraphService implements IMSGraphService{
} }
public async getUserPropertiesByLastName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{ public async getUserPropertiesByLastName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = []; const userProperties:IUserProperties[] = [];
try { try {
let res = await client.api("users") const res = await client.api("users")
.version("v1.0") .version("v1.0")
.filter(`(startswith(surname,'${escape(searchFor)}'))`).get(); .filter(`(startswith(surname,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){ if(res.value.length !== 0){
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
res.value.map((_userProperty:any,_index:any)=>{ res.value.map((_userProperty:any,_index:any)=>{
if(_userProperty.mail !== null){ if(_userProperty.mail !== null){
userProperties.push({ userProperties.push({
@ -60,12 +60,13 @@ export class MSGraphService implements IMSGraphService{
} }
public async getUserPropertiesByFirstName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{ public async getUserPropertiesByFirstName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = []; const userProperties:IUserProperties[] = [];
try { try {
let res = await client.api("users") const res = await client.api("users")
.version("v1.0") .version("v1.0")
.filter(`(startswith(givenName,'${escape(searchFor)}'))`).get(); .filter(`(startswith(givenName,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){ if(res.value.length !== 0){
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
res.value.map((_userProperty:any,_index:any)=>{ res.value.map((_userProperty:any,_index:any)=>{
if(_userProperty.mail !== null){ if(_userProperty.mail !== null){
userProperties.push({ userProperties.push({
@ -88,12 +89,13 @@ export class MSGraphService implements IMSGraphService{
} }
public async getUserPropertiesBySearch(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{ public async getUserPropertiesBySearch(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = []; const userProperties:IUserProperties[] = [];
try { try {
let res = await client.api("users") const res = await client.api("users")
.version("v1.0") .version("v1.0")
.filter(`(startswith(displayName,'${escape(searchFor)}'))`).get(); .filter(`(startswith(displayName,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){ if(res.value.length !== 0){
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
res.value.map((_userProperty:any,_index:any)=>{ res.value.map((_userProperty:any,_index:any)=>{
userProperties.push({ userProperties.push({
businessPhone:_userProperty.businessPhones[0], businessPhone:_userProperty.businessPhones[0],
@ -113,4 +115,4 @@ export class MSGraphService implements IMSGraphService{
return userProperties; return userProperties;
} }
} }

View File

@ -6,10 +6,8 @@ import { autobind } from "office-ui-fabric-react/lib/Utilities";
import { PeoplePicker, PrincipalType } from '@pnp/spfx-controls-react/lib/PeoplePicker'; import { PeoplePicker, PrincipalType } from '@pnp/spfx-controls-react/lib/PeoplePicker';
import { } from "@pnp/spfx-controls-react/"; import { } from "@pnp/spfx-controls-react/";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack'; import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; import { DetailsList, DetailsListLayoutMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 }; const stackTokens = { childrenGap: 50 };
const iconProps = { iconName: 'Calendar' };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } }; const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
const columnProps: Partial<IStackProps> = { const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 }, tokens: { childrenGap: 15 },
@ -28,20 +26,9 @@ export class ByEmail extends React.Component<ByEmailProps,ByEmailState>{
}; };
} }
@autobind
private _getPeoplePickerItems(items: any[]) {
if(items.length == 1){
this.getUsers(items[0].secondaryText !== ""?items[0].secondaryText:items[0].id.split('|').pop());
}
else
{
this.setState({
userProperties:[]
});
}
}
@autobind @autobind
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async getUsers(email:string) : Promise<any>{ private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{ this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance await this.props.MSGraphServiceInstance
@ -67,7 +54,7 @@ export class ByEmail extends React.Component<ByEmailProps,ByEmailState>{
public render(): React.ReactElement<ByEmailProps> { public render(): React.ReactElement<ByEmailProps> {
return ( return (
<div className={styles.telephonedirectory}> <div className={styles.telephonedirectory}>
<div> <div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}> <Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}> <Stack {...columnProps}>
@ -82,10 +69,9 @@ export class ByEmail extends React.Component<ByEmailProps,ByEmailState>{
resolveDelay={1000} /> resolveDelay={1000} />
</Stack> </Stack>
</Stack> </Stack>
<div> <div/>
</div>
<div id='detailedList'> <div id='detailedList'>
{this.state.userProperties.length !== 0 && {this.state.userProperties.length !== 0 &&
<DetailsList <DetailsList
items={this.state.userProperties} items={this.state.userProperties}
columns={this.props.columns} columns={this.props.columns}
@ -98,4 +84,4 @@ export class ByEmail extends React.Component<ByEmailProps,ByEmailState>{
</div> </div>
); );
} }
} }

View File

@ -1,5 +1,4 @@
import { IUserProperties } from "../../../../Services/IUserProperties"; import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByEmailState{ export interface ByEmailState{
@ -7,4 +6,4 @@ export interface ByEmailState{
userProperties:IUserProperties[]; userProperties:IUserProperties[];
searchFor: string; searchFor: string;
isDataFound:boolean; isDataFound:boolean;
} }

View File

@ -7,7 +7,7 @@ import * as strings from 'TelephonedirectoryWebPartStrings';
import { Log } from "@microsoft/sp-core-library"; import { Log } from "@microsoft/sp-core-library";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack'; import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { TextField } from "office-ui-fabric-react/lib/TextField"; import { TextField } from "office-ui-fabric-react/lib/TextField";
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; import { DetailsList, DetailsListLayoutMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName"; const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 }; const stackTokens = { childrenGap: 50 };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } }; const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
@ -39,6 +39,7 @@ export class ByFirstName extends React.Component<ByFirstNameProps,ByFirstNameSta
} }
@autobind @autobind
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async getUsers(email:string) : Promise<any>{ private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{ this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance await this.props.MSGraphServiceInstance
@ -72,7 +73,7 @@ export class ByFirstName extends React.Component<ByFirstNameProps,ByFirstNameSta
public render(): React.ReactElement<ByFirstNameProps> { public render(): React.ReactElement<ByFirstNameProps> {
return ( return (
<div className={styles.telephonedirectory}> <div className={styles.telephonedirectory}>
<div> <div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}> <Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}> <Stack {...columnProps}>
@ -85,10 +86,10 @@ export class ByFirstName extends React.Component<ByFirstNameProps,ByFirstNameSta
/> />
</Stack> </Stack>
</Stack> </Stack>
<div> <div/>
</div>
<div id='detailedList'> <div id='detailedList'>
{this.state.userProperties.length !== 0 && {this.state.userProperties.length !== 0 &&
<DetailsList <DetailsList
items={this.state.userProperties} items={this.state.userProperties}
columns={this.props.Columns} columns={this.props.Columns}

View File

@ -1,5 +1,4 @@
import { IUserProperties } from "../../../../Services/IUserProperties"; import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByFirstNameState{ export interface ByFirstNameState{
@ -7,4 +6,4 @@ export interface ByFirstNameState{
userProperties:IUserProperties[]; userProperties:IUserProperties[];
searchFor: string; searchFor: string;
isDataFound:boolean; isDataFound:boolean;
} }

View File

@ -7,10 +7,9 @@ import * as strings from 'TelephonedirectoryWebPartStrings';
import { Log } from "@microsoft/sp-core-library"; import { Log } from "@microsoft/sp-core-library";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack'; import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { TextField } from "office-ui-fabric-react/lib/TextField"; import { TextField } from "office-ui-fabric-react/lib/TextField";
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; import { DetailsList, DetailsListLayoutMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName"; const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 }; const stackTokens = { childrenGap: 50 };
const iconProps = { iconName: 'Calendar' };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } }; const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
const columnProps: Partial<IStackProps> = { const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 }, tokens: { childrenGap: 15 },
@ -42,6 +41,7 @@ export class ByLastName extends React.Component<ByLastNameProps,ByLastNameState>
} }
@autobind @autobind
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async getUsers(email:string) : Promise<any>{ private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{ this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance await this.props.MSGraphServiceInstance
@ -75,7 +75,7 @@ export class ByLastName extends React.Component<ByLastNameProps,ByLastNameState>
public render(): React.ReactElement<ByLastNameProps> { public render(): React.ReactElement<ByLastNameProps> {
return ( return (
<div className={styles.telephonedirectory}> <div className={styles.telephonedirectory}>
<div> <div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}> <Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}> <Stack {...columnProps}>
@ -88,10 +88,9 @@ export class ByLastName extends React.Component<ByLastNameProps,ByLastNameState>
/> />
</Stack> </Stack>
</Stack> </Stack>
<div> <div/>
</div>
<div id='detailedList'> <div id='detailedList'>
{this.state.userProperties.length !== 0 && {this.state.userProperties.length !== 0 &&
<DetailsList <DetailsList
items={this.state.userProperties} items={this.state.userProperties}
columns={this.props.columns} columns={this.props.columns}

View File

@ -1,8 +1,7 @@
import { IUserProperties } from "../../../../Services/IUserProperties"; import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByLastNameState{ export interface ByLastNameState{
loading:boolean; loading:boolean;
userProperties:IUserProperties[]; userProperties:IUserProperties[];
searchFor: string; searchFor: string;
isDataFound:boolean; isDataFound:boolean;
} }

View File

@ -1,8 +1,7 @@
import { IUserProperties } from "../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList"; import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ITelephoneDirectoryState{ export interface ITelephoneDirectoryState{
loading:boolean; loading:boolean;
columns:IColumn[]; columns:IColumn[];
selectedKey:string; selectedKey:string;
} }

View File

@ -9,7 +9,6 @@ import { ByLastName } from "./ByLastName/ByLastName";
import { ByEmail } from "./ByEmail/ByEmail"; import { ByEmail } from "./ByEmail/ByEmail";
import { Pivot, PivotItem,PivotLinkFormat, PivotLinkSize } from 'office-ui-fabric-react/lib/Pivot'; import { Pivot, PivotItem,PivotLinkFormat, PivotLinkSize } from 'office-ui-fabric-react/lib/Pivot';
import { IColumn } from 'office-ui-fabric-react/lib/DetailsList'; import { IColumn } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "TelephoneDirectory";
export default class Telephonedirectory extends React.Component<ITelephoneDirectoryProps, ITelephoneDirectoryState> { export default class Telephonedirectory extends React.Component<ITelephoneDirectoryProps, ITelephoneDirectoryState> {
private headers = [ private headers = [
{ label: 'Name', key: 'displayName' }, { label: 'Name', key: 'displayName' },
@ -88,7 +87,7 @@ export default class Telephonedirectory extends React.Component<ITelephoneDirect
isResizable: true isResizable: true
} }
]; ];
this.state={ this.state={
loading:false, loading:false,
selectedKey:"byFirstName", selectedKey:"byFirstName",
@ -112,7 +111,7 @@ export default class Telephonedirectory extends React.Component<ITelephoneDirect
<WebPartTitle displayMode={this.props.DisplayMode} <WebPartTitle displayMode={this.props.DisplayMode}
title={this.props.WebpartTitle} title={this.props.WebpartTitle}
updateProperty={this.props.updateProperty} /> updateProperty={this.props.updateProperty} />
<Pivot headersOnly={true} <Pivot headersOnly={true}
selectedKey ={this.state.selectedKey} selectedKey ={this.state.selectedKey}
onLinkClick = {this._handleLinkClick} onLinkClick = {this._handleLinkClick}
@ -121,36 +120,36 @@ export default class Telephonedirectory extends React.Component<ITelephoneDirect
<PivotItem <PivotItem
headerText='Search User By First Name' headerText='Search User By First Name'
itemKey='byFirstName' itemKey='byFirstName'
itemIcon="Group" ></PivotItem> itemIcon="Group" />
<PivotItem <PivotItem
headerText='Search User By Last Name' headerText='Search User By Last Name'
itemKey='byLastName' itemKey='byLastName'
itemIcon="Group"></PivotItem> itemIcon="Group"/>
<PivotItem <PivotItem
headerText='Search User By Email' headerText='Search User By Email'
itemKey="byEmail" itemKey="byEmail"
itemIcon="Group"></PivotItem> itemIcon="Group"/>
</Pivot><br/> </Pivot><br/>
{this.state.selectedKey === "byFirstName" && {this.state.selectedKey === "byFirstName" &&
<ByFirstName <ByFirstName
MSGraphClient={this.props.MsGraphClient} MSGraphClient={this.props.MsGraphClient}
MSGraphServiceInstance={this.props.MSGraphServiceInstance} MSGraphServiceInstance={this.props.MSGraphServiceInstance}
context={this.props.context} context={this.props.context}
Columns={this.state.columns}></ByFirstName> Columns={this.state.columns}/>
} }
{this.state.selectedKey === "byLastName" && {this.state.selectedKey === "byLastName" &&
<ByLastName <ByLastName
MSGraphClient = {this.props.MsGraphClient} MSGraphClient = {this.props.MsGraphClient}
MSGraphServiceInstance= {this.props.MSGraphServiceInstance} MSGraphServiceInstance= {this.props.MSGraphServiceInstance}
context={this.props.context} context={this.props.context}
columns={this.state.columns}></ByLastName> columns={this.state.columns}/>
} }
{this.state.selectedKey === "byEmail" && {this.state.selectedKey === "byEmail" &&
<ByEmail <ByEmail
MSGraphClient = {this.props.MsGraphClient} MSGraphClient = {this.props.MsGraphClient}
MSGraphServiceInstance= {this.props.MSGraphServiceInstance} MSGraphServiceInstance= {this.props.MSGraphServiceInstance}
context={this.props.context} context={this.props.context}
columns={this.state.columns}></ByEmail> columns={this.state.columns}/>
} }
</div> </div>
</div> </div>