Updated orgchart to use office-ui-fabric-react and uifabric/styling (#269)
* initial checkin * basic code ready * use office-ui-fabric-react and @uifabric/styling * formatting * package-lock * updated readme * removed wip code * fix react children rendering * deleted lock file added incorrectly.
This commit is contained in:
parent
afce98b92b
commit
b2b9c6ea76
|
@ -27,6 +27,7 @@ Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
1.0|September 14, 2016|Initial release
|
1.0|September 14, 2016|Initial release
|
||||||
2.0|March 12, 2017|Updated for SPFx 1.0
|
2.0|March 12, 2017|Updated for SPFx 1.0
|
||||||
|
2.1|July 19, 2017|Use office-ui-fabric-react and uifabric/styling
|
||||||
|
|
||||||
## Disclaimer
|
## 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.**
|
**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.**
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "react-organisationchart",
|
"name": "react-organisationchart",
|
||||||
"version": "0.0.1",
|
"version": "2.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
"@types/react-addons-update": "0.14.14",
|
"@types/react-addons-update": "0.14.14",
|
||||||
"@types/react-dom": "0.14.18",
|
"@types/react-dom": "0.14.18",
|
||||||
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
||||||
|
"office-ui-fabric-react": "^4.21.1",
|
||||||
"react": "15.4.2",
|
"react": "15.4.2",
|
||||||
"react-dom": "15.4.2"
|
"react-dom": "15.4.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface IOrganisationChartWebPartProps {
|
export interface IOrganisationChartWebPartProps {
|
||||||
organisationName: string;
|
organisationName: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@import "~office-ui-fabric/dist/sass/Fabric.scss";
|
|
||||||
@import "~office-ui-fabric/dist/components/Persona/Persona.scss";
|
|
||||||
@import "~office-ui-fabric/dist/components/OrgChart/OrgChart.scss";
|
|
|
@ -1,9 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styles from './OrganisationChart.module.scss';
|
|
||||||
import { escape } from '@microsoft/sp-lodash-subset';
|
import { escape } from '@microsoft/sp-lodash-subset';
|
||||||
|
|
||||||
import { IOrganisationChartProps } from './IOrganisationChartProps';
|
import { Persona, PersonaSize, PersonaPresence } from 'office-ui-fabric-react/lib/Persona';
|
||||||
|
|
||||||
|
import { FontClassNames } from '@uifabric/styling';
|
||||||
|
|
||||||
|
import { IOrganisationChartProps } from './IOrganisationChartProps';
|
||||||
|
|
||||||
import { ServiceScope, Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
import { ServiceScope, Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||||
import { IPerson, IUserProfileService } from '../interfaces';
|
import { IPerson, IUserProfileService } from '../interfaces';
|
||||||
|
@ -16,6 +18,35 @@ export interface IOrganisationChartWebPartState {
|
||||||
reports?: IPerson[];
|
reports?: IPerson[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IPersonaListProps {
|
||||||
|
title: string;
|
||||||
|
users: IPerson[];
|
||||||
|
getProfilePhoto: (photoUrl: string) => string;
|
||||||
|
onProfileLinkClick: (profileLink: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PersonaList extends React.Component<IPersonaListProps, {}> {
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={FontClassNames.large}>{this.props.title}</div>
|
||||||
|
{this.props.users.map((user, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Persona
|
||||||
|
imageUrl={this.props.getProfilePhoto(user.PictureUrl)}
|
||||||
|
primaryText={user.DisplayName}
|
||||||
|
secondaryText={user.Title}
|
||||||
|
size={PersonaSize.regular}
|
||||||
|
presence={PersonaPresence.none}
|
||||||
|
onClick={() => this.props.onProfileLinkClick(user.UserUrl)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class OrganisationChart extends React.Component<IOrganisationChartProps, IOrganisationChartWebPartState> {
|
export default class OrganisationChart extends React.Component<IOrganisationChartProps, IOrganisationChartWebPartState> {
|
||||||
|
|
||||||
private userProfileServiceInstance: IUserProfileService;
|
private userProfileServiceInstance: IUserProfileService;
|
||||||
|
@ -48,66 +79,33 @@ export default class OrganisationChart extends React.Component<IOrganisationChar
|
||||||
|
|
||||||
public render(): React.ReactElement<IOrganisationChartProps> {
|
public render(): React.ReactElement<IOrganisationChartProps> {
|
||||||
return (
|
return (
|
||||||
<div className={styles['ms-OrgChart']}>
|
<div>
|
||||||
<div className="ms-font-xl">
|
<div className={FontClassNames.xLarge}>
|
||||||
{escape(this.props.organisationName)}
|
{escape(this.props.organisationName)}
|
||||||
</div>
|
</div>
|
||||||
<div className="ms-OrgChart-group">
|
<PersonaList
|
||||||
<div className="ms-OrgChart-groupTitle">Managers</div>
|
title="Managers"
|
||||||
<ul className={styles['ms-OrgChart-list']}>
|
users={this.state.managers}
|
||||||
{this.state.managers.map((manager, index) => (
|
getProfilePhoto={this.getProfilePhoto.bind(this)}
|
||||||
<li key={index} className={styles['ms-OrgChart-listItem']}>
|
onProfileLinkClick={this.onProfileLinkClick.bind(this)}
|
||||||
<button className={styles['ms-OrgChart-listItemBtn']} onClick={() => this.onProfileLinkClick(manager.UserUrl)}>
|
/>
|
||||||
<div className="ms-Persona">
|
<div>
|
||||||
<div className="ms-Persona-imageArea">
|
<div className={FontClassNames.large}>You</div>
|
||||||
<img className="ms-Persona-image" alt="" role="presentation" src={this.getProfilePhoto(manager.PictureUrl)}></img>
|
<Persona
|
||||||
</div>
|
imageUrl={this.getProfilePhoto(this.state.user.PictureUrl)}
|
||||||
<div className="ms-Persona-details">
|
primaryText={this.state.user.DisplayName}
|
||||||
<div className="ms-Persona-primaryText">{manager.DisplayName}</div>
|
secondaryText={this.state.user.Title}
|
||||||
<div className="ms-Persona-secondaryText">{manager.Title}</div>
|
size={PersonaSize.regular}
|
||||||
</div>
|
presence={PersonaPresence.none}
|
||||||
</div>
|
onClick={() => this.onProfileLinkClick(this.state.user.UserUrl)}
|
||||||
</button>
|
/>
|
||||||
</li>))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="ms-OrgChart-group">
|
|
||||||
<div className="ms-OrgChart-groupTitle">You</div>
|
|
||||||
<ul className={styles['ms-OrgChart-list']}>
|
|
||||||
<li className={styles['ms-OrgChart-listItem']}>
|
|
||||||
<button className={styles['ms-OrgChart-listItemBtn']} onClick={() => this.onProfileLinkClick(this.state.user.UserUrl)}>
|
|
||||||
<div className="ms-Persona">
|
|
||||||
<div className="ms-Persona-imageArea">
|
|
||||||
<img className="ms-Persona-image" alt="" role="presentation" src={this.getProfilePhoto(this.state.user.PictureUrl)}></img>
|
|
||||||
</div>
|
|
||||||
<div className="ms-Persona-details">
|
|
||||||
<div className="ms-Persona-primaryText">{this.state.user.DisplayName}</div>
|
|
||||||
<div className="ms-Persona-secondaryText">{this.state.user.Title}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="ms-OrgChart-group">
|
|
||||||
<div className="ms-OrgChart-groupTitle">Reports</div>
|
|
||||||
<ul className={styles['ms-OrgChart-list']}>
|
|
||||||
{this.state.reports.map((report, index) => (
|
|
||||||
<li key={index} className={styles['ms-OrgChart-listItem']}>
|
|
||||||
<button className={styles['ms-OrgChart-listItemBtn']} onClick={() => this.onProfileLinkClick(report.UserUrl)}>
|
|
||||||
<div className="ms-Persona">
|
|
||||||
<div className="ms-Persona-imageArea">
|
|
||||||
<img className="ms-Persona-image" alt="" role="presentation" src={this.getProfilePhoto(report.PictureUrl)}></img>
|
|
||||||
</div>
|
|
||||||
<div className="ms-Persona-details">
|
|
||||||
<div className="ms-Persona-primaryText">{report.DisplayName}</div>
|
|
||||||
<div className="ms-Persona-secondaryText">{report.Title}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</li>))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<PersonaList
|
||||||
|
title="Reports"
|
||||||
|
users={this.state.reports}
|
||||||
|
getProfilePhoto={this.getProfilePhoto.bind(this)}
|
||||||
|
onProfileLinkClick={this.onProfileLinkClick.bind(this)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,42 @@ import { IPerson, IUserProfileService } from '../interfaces';
|
||||||
import { ServiceScope, ServiceKey } from '@microsoft/sp-core-library';
|
import { ServiceScope, ServiceKey } from '@microsoft/sp-core-library';
|
||||||
|
|
||||||
export class MockUserProfileService implements IUserProfileService {
|
export class MockUserProfileService implements IUserProfileService {
|
||||||
public static readonly serviceKey: ServiceKey<IUserProfileService> = ServiceKey.create<IUserProfileService>('vrd:MockUserProfileService', MockUserProfileService);
|
public static readonly serviceKey: ServiceKey<IUserProfileService> = ServiceKey.create<IUserProfileService>('vrd:MockUserProfileService', MockUserProfileService);
|
||||||
|
|
||||||
constructor(serviceScope: ServiceScope) {
|
|
||||||
|
|
||||||
}
|
constructor(serviceScope: ServiceScope) {
|
||||||
|
|
||||||
public getPropertiesForCurrentUser(): Promise<IPerson> {
|
}
|
||||||
return new Promise<IPerson>((resolve, reject) => {
|
|
||||||
const user: IPerson = { Title: "Consultant", DisplayName: "Adam Jones", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-male.png" };
|
|
||||||
resolve(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getManagers(userLoginNames: string[]): Promise<IPerson[]> {
|
public getPropertiesForCurrentUser(): Promise<IPerson> {
|
||||||
return new Promise<IPerson[]>((resolve, reject) => {
|
return new Promise<IPerson>((resolve, reject) => {
|
||||||
const users: IPerson[] = [];
|
const user: IPerson = { Title: "Consultant", DisplayName: "Adam Jones", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-male.png" };
|
||||||
|
resolve(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
users.push({ Title: "Manager", DisplayName: "Grant Steel", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-male.png" });
|
public getManagers(userLoginNames: string[]): Promise<IPerson[]> {
|
||||||
users.push({ Title: "Head of Management", DisplayName: "Marcel Grose", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
return new Promise<IPerson[]>((resolve, reject) => {
|
||||||
|
const users: IPerson[] = [];
|
||||||
|
|
||||||
resolve(users);
|
users.push({ Title: "Manager", DisplayName: "Grant Steel", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-male.png" });
|
||||||
});
|
users.push({ Title: "Head of Management", DisplayName: "Marcel Grose", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
||||||
}
|
|
||||||
|
|
||||||
public getReports(userLoginNames: string[]): Promise<IPerson[]> {
|
resolve(users);
|
||||||
return new Promise<IPerson[]>((resolve, reject) => {
|
});
|
||||||
const users: IPerson[] = [];
|
}
|
||||||
|
|
||||||
users.push({ Title: "Developer", DisplayName: "Russel Miller", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
public getReports(userLoginNames: string[]): Promise<IPerson[]> {
|
||||||
users.push({ Title: "IT Admin", DisplayName: "Robert Fischer", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
return new Promise<IPerson[]>((resolve, reject) => {
|
||||||
|
const users: IPerson[] = [];
|
||||||
|
|
||||||
resolve(users);
|
users.push({ Title: "Developer", DisplayName: "Russel Miller", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
||||||
});
|
users.push({ Title: "IT Admin", DisplayName: "Robert Fischer", PictureUrl: "https://raw.githubusercontent.com/OfficeDev/office-ui-fabric-react/master/packages/office-ui-fabric-react/images/persona-female.png" });
|
||||||
}
|
|
||||||
|
|
||||||
public getProfilePhoto(photoUrl: string) {
|
resolve(users);
|
||||||
return photoUrl;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getProfilePhoto(photoUrl: string) {
|
||||||
|
return photoUrl;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,77 +5,77 @@ import { SPHttpClient, ISPHttpClientBatchCreationOptions, SPHttpClientResponse,
|
||||||
|
|
||||||
export class UserProfileService implements IUserProfileService {
|
export class UserProfileService implements IUserProfileService {
|
||||||
|
|
||||||
public static readonly serviceKey: ServiceKey<IUserProfileService> = ServiceKey.create<IUserProfileService>('vrd:UserProfileService', UserProfileService);
|
public static readonly serviceKey: ServiceKey<IUserProfileService> = ServiceKey.create<IUserProfileService>('vrd:UserProfileService', UserProfileService);
|
||||||
|
|
||||||
private _spHttpClient: SPHttpClient;
|
private _spHttpClient: SPHttpClient;
|
||||||
private _pageContext: PageContext;
|
private _pageContext: PageContext;
|
||||||
private _currentWebUrl: string;
|
private _currentWebUrl: string;
|
||||||
|
|
||||||
constructor(serviceScope: ServiceScope) {
|
constructor(serviceScope: ServiceScope) {
|
||||||
serviceScope.whenFinished(() => {
|
serviceScope.whenFinished(() => {
|
||||||
this._spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
|
this._spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
|
||||||
this._pageContext = serviceScope.consume(PageContext.serviceKey);
|
this._pageContext = serviceScope.consume(PageContext.serviceKey);
|
||||||
this._currentWebUrl = this._pageContext.web.absoluteUrl;
|
this._currentWebUrl = this._pageContext.web.absoluteUrl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPropertiesForCurrentUser(): Promise<IPerson> {
|
public getPropertiesForCurrentUser(): Promise<IPerson> {
|
||||||
return this._spHttpClient.get(`${this._currentWebUrl}/_api/SP.UserProfiles.PeopleManager/GetMyProperties?$select=DisplayName,Title,UserUrl,PictureUrl,DirectReports,ExtendedManagers`,
|
return this._spHttpClient.get(`${this._currentWebUrl}/_api/SP.UserProfiles.PeopleManager/GetMyProperties?$select=DisplayName,Title,UserUrl,PictureUrl,DirectReports,ExtendedManagers`,
|
||||||
SPHttpClient.configurations.v1)
|
SPHttpClient.configurations.v1)
|
||||||
.then((response: SPHttpClientResponse) => {
|
.then((response: SPHttpClientResponse) => {
|
||||||
return response.json();
|
return response.json();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getManagers(userLoginNames: string[]): Promise<IPerson[]> {
|
public getManagers(userLoginNames: string[]): Promise<IPerson[]> {
|
||||||
return this.getPropertiesForUsers(userLoginNames);
|
return this.getPropertiesForUsers(userLoginNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getReports(userLoginNames: string[]): Promise<IPerson[]> {
|
public getReports(userLoginNames: string[]): Promise<IPerson[]> {
|
||||||
return this.getPropertiesForUsers(userLoginNames);
|
return this.getPropertiesForUsers(userLoginNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPropertiesForUsers(userLoginNames: string[]): Promise<IPerson[]> {
|
private getPropertiesForUsers(userLoginNames: string[]): Promise<IPerson[]> {
|
||||||
|
|
||||||
|
|
||||||
return new Promise<IPerson[]>((resolve, reject) => {
|
return new Promise<IPerson[]>((resolve, reject) => {
|
||||||
//at least 1 login name should be supplied
|
//at least 1 login name should be supplied
|
||||||
if (userLoginNames.length > 0) {
|
if (userLoginNames.length > 0) {
|
||||||
const arrayOfPersons: IPerson[] = [];
|
const arrayOfPersons: IPerson[] = [];
|
||||||
|
|
||||||
const spBatchCreationOpts: ISPHttpClientBatchCreationOptions = { webUrl: this._currentWebUrl };
|
const spBatchCreationOpts: ISPHttpClientBatchCreationOptions = { webUrl: this._currentWebUrl };
|
||||||
|
|
||||||
const spBatch: SPHttpClientBatch = this._spHttpClient.beginBatch(spBatchCreationOpts);
|
const spBatch: SPHttpClientBatch = this._spHttpClient.beginBatch(spBatchCreationOpts);
|
||||||
|
|
||||||
const userResponses: Promise<SPHttpClientResponse>[] = [];
|
const userResponses: Promise<SPHttpClientResponse>[] = [];
|
||||||
|
|
||||||
for (const userLoginName of userLoginNames) {
|
for (const userLoginName of userLoginNames) {
|
||||||
const getUserProps: Promise<SPHttpClientResponse> = spBatch.get(`${this._currentWebUrl}/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='${encodeURIComponent(userLoginName)}'
|
const getUserProps: Promise<SPHttpClientResponse> = spBatch.get(`${this._currentWebUrl}/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='${encodeURIComponent(userLoginName)}'
|
||||||
&$select=DisplayName,Title,UserUrl,PictureUrl,DirectReports,ExtendedManagers`,
|
&$select=DisplayName,Title,UserUrl,PictureUrl,DirectReports,ExtendedManagers`,
|
||||||
SPHttpClientBatch.configurations.v1);
|
SPHttpClientBatch.configurations.v1);
|
||||||
userResponses.push(getUserProps);
|
userResponses.push(getUserProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the batch request
|
||||||
|
spBatch.execute().then(() => {
|
||||||
|
userResponses.forEach((item, index) => {
|
||||||
|
item.then((response: SPHttpClientResponse) => {
|
||||||
|
response.json().then((responseJSON: IPerson) => {
|
||||||
|
arrayOfPersons.push(responseJSON);
|
||||||
|
if (index == (userResponses.length) - 1) {
|
||||||
|
resolve(arrayOfPersons);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Make the batch request
|
});
|
||||||
spBatch.execute().then(() => {
|
});
|
||||||
userResponses.forEach((item, index) => {
|
|
||||||
item.then((response: SPHttpClientResponse) => {
|
|
||||||
response.json().then((responseJSON: IPerson) => {
|
|
||||||
arrayOfPersons.push(responseJSON);
|
|
||||||
if (index == (userResponses.length) - 1) {
|
|
||||||
resolve(arrayOfPersons);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//SharePoint does not return the userphoto if the current user has not currently signed in to their MySite (ODfB site)
|
//SharePoint does not return the userphoto if the current user has not currently signed in to their MySite (ODfB site)
|
||||||
//This method of getting the user photo works in all scenarios.
|
//This method of getting the user photo works in all scenarios.
|
||||||
public getProfilePhoto(photoUrl: string) {
|
public getProfilePhoto(photoUrl: string) {
|
||||||
return `/_layouts/15/userphoto.aspx?size=M&url=${photoUrl}`;
|
return `/_layouts/15/userphoto.aspx?size=M&url=${photoUrl}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue