Merge branch 'master' into UpdateBigCalendar

This commit is contained in:
Abderahman88 2020-10-21 21:52:55 +01:00
commit 94312db65f
97 changed files with 32793 additions and 9962 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

32
samples/react-avatar/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.11.0",
"libraryName": "react-avatar",
"libraryId": "b96dfed7-ec48-4082-ba50-f6c7b09143c7",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -2,8 +2,7 @@
## Summary
This is a sample web part that helps user create their avatar and save as profile picture. User can even download their avatar as PNG file. This webpart can be useful in Intranet to help user create their avatar and save it as profile picture.
This is a sample web part that helps user create their avatar and save as profile picture. User can even download their avatar as PNG file. This web part can be useful in Intranet to help user create their avatar and save it as profile picture.
##
![directory](/samples/react-avatar/assets/reactAvatarOutcome.gif)
@ -16,7 +15,8 @@ This is a sample web part that helps user create their avatar and save as profil
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.10.0-green.svg)
![SPFx 1.11](https://img.shields.io/badge/version-1.11.0-green.svg)
## Applies to
@ -55,6 +55,7 @@ Web Part Title | Text| no|
## Solution
The web part Use avataaars library for creating avatars and MS Graph with User.ReadWrite and User.ReadWriteAll for saving avatar as current users Profile Picture.FileSaver for downloading avatar image as png file.
Solution|Author(s)
@ -66,9 +67,11 @@ react Avatar|Kunj Sangani
Version|Date|Comments
-------|----|--------
1.0.0|August 1, 2020|Initial release
1.0.1|October 20, 2020|Update to SPFx 1.11.0
## 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.**
---
@ -81,9 +84,6 @@ Version|Date|Comments
- `gulp build`
- `gulp bundle --ship`
- `gulp package-solution --ship`
- `Add to AppCatalog and deploy`
- Add to AppCatalog and deploy
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-avatar" />

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-avatars-client-side-solution",
"id": "b96dfed7-ec48-4082-ba50-f6c7b09143c7",
"version": "1.0.0.0",
"version": "1.0.1.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"webApiPermissionRequests": [
@ -11,7 +11,14 @@
"resource": "Microsoft Graph",
"scope": "User.ReadWrite"
}
]
],
"developer": {
"name": "Contoso",
"privacyUrl": "https://contoso.com/privacy",
"termsOfUseUrl": "https://contoso.com/terms-of-use",
"websiteUrl": "https://contoso.com/my-app",
"mpnId": "000000"
}
},
"paths": {
"zippedPackage": "solution/react-avatars.sppkg"

File diff suppressed because it is too large Load Diff

View File

@ -13,18 +13,14 @@
},
"dependencies": {
"@material-ui/core": "^4.11.0",
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"@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",
"avataaars": "^1.2.1",
"file-saver": "^2.0.2",
"office-ui-fabric-react": "6.189.2",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
@ -32,14 +28,14 @@
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"gulp": "~3.9.1",
"@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/mocha": "2.2.38",
"ajv": "~5.2.2"
"ajv": "~5.2.2",
"gulp": "~3.9.1"
}
}

View File

@ -29,7 +29,8 @@
]
},
"include": [
"src/**/*.ts"
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",

View File

@ -104,13 +104,15 @@ Start Date | Date | yes | Event Date
End Date| Date| yes | Event Date
## Solution
The Web Part Use PnPjs library, Office-ui-fabric-react components. react Big-Calendar Compoment
The Web Part Use PnPjs library, Office-ui-fabric-react components. react Big-Calendar Component
Solution|Author(s)
--------|---------
Calendar Web Part|João Mendes
Calendar Web Part|Mohamed Derhalli
Calendar Web Part (Upgrade)|Hugo Bernier ([@bernier](https://twitter.com/bernierh), [Tahoe Ninjas](https://tahoeninjas.blog/))
Calendar Web Part|Nanddeep Nachan ([@NanddeepNachan](https://twitter.com/NanddeepNachan))
## Version history
@ -120,6 +122,7 @@ Version|Date|Comments
1.0.1|June 10, 2019|update add recurrence events
1.0.2|April 25, 2020|Update styles according to the applied theme
1.0.3|June 06, 2020|Upgrade to SPFx 1.10.0
1.0.4|October 18, 2020|Added support for all-day events
## Disclaimer

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-calendar-client-side-solution",
"id": "3a13208b-3874-4036-9262-4edd22e88187",
"version": "1.0.0.1",
"version": "1.0.4.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false

View File

@ -1,7 +1,7 @@
{
"name": "react-calendar",
"main": "lib/index.js",
"version": "1.0.2",
"version": "1.0.4",
"private": true,
"engines": {
"node": ">=0.10.0"

View File

@ -1,33 +1,34 @@
import { IEventData } from '../../services/IEventData';
import { IUserPermissions } from '../../services/IUserPermissions';
import { DayOfWeek} from 'office-ui-fabric-react/lib/DatePicker';
import { IDropdownOption } from 'office-ui-fabric-react/';
import { DayOfWeek } from 'office-ui-fabric-react/lib/DatePicker';
import { IDropdownOption } from 'office-ui-fabric-react/';
export interface IEventState {
showPanel: boolean;
eventData:IEventData;
eventData: IEventData;
firstDayOfWeek?: DayOfWeek;
startSelectedHour: IDropdownOption ;
startSelectedMin: IDropdownOption ;
endSelectedHour: IDropdownOption ;
endSelectedMin: IDropdownOption ;
startSelectedHour: IDropdownOption;
startSelectedMin: IDropdownOption;
endSelectedHour: IDropdownOption;
endSelectedMin: IDropdownOption;
startDate?: Date;
endDate?: Date;
editorState?: any;
selectedUsers: string[];
locationLatitude: number;
locationLongitude: number;
errorMessage?:string;
hasError?:boolean;
errorMessage?: string;
hasError?: boolean;
disableButton?: boolean;
isSaving?:boolean;
isDeleting?:boolean;
displayDialog:boolean;
isSaving?: boolean;
isDeleting?: boolean;
displayDialog: boolean;
userPermissions?: IUserPermissions;
isloading:boolean;
isloading: boolean;
isAllDayEvent: boolean;
siteRegionalSettings: any;
recurrenceSeriesEdited?:boolean;
showRecurrenceSeriesInfo:boolean;
newRecurrenceEvent:boolean;
recurrenceAction:string;
recurrenceDescription?:string;
recurrenceSeriesEdited?: boolean;
showRecurrenceSeriesInfo: boolean;
newRecurrenceEvent: boolean;
recurrenceAction: string;
recurrenceDescription?: string;
}

View File

@ -79,7 +79,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
navigator.geolocation.getCurrentPosition((position) => {
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
});
} else {
/* geolocation IS NOT available */
@ -104,10 +103,11 @@ export class Event extends React.Component<IEventProps, IEventState> {
isSaving: false,
displayDialog: false,
isloading: false,
isAllDayEvent: this.props.event && this.props.event.fAllDayEvent,
siteRegionalSettings: undefined,
recurrenceSeriesEdited: false,
showRecurrenceSeriesInfo:false,
newRecurrenceEvent:false,
showRecurrenceSeriesInfo: false,
newRecurrenceEvent: false,
recurrenceAction: 'display',
userPermissions: { hasPermissionAdd: false, hasPermissionDelete: false, hasPermissionEdit: false, hasPermissionView: false },
};
@ -148,13 +148,15 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
private async onSave() {
let eventData: IEventData = this.state.eventData;
let panelMode = this.props.panelMode;
let startDate: string = null;
let endDate: string = null;
eventData.fRecurrence = false;
// set All day event
eventData.fAllDayEvent = this.state.isAllDayEvent;
// if there are new Event recurrence or Edited recurrence series
if (this.state.recurrenceSeriesEdited || this.state.newRecurrenceEvent) {
eventData.RecurrenceData = this.returnedRecurrenceInfo.recurrenceData;
@ -163,16 +165,17 @@ export class Event extends React.Component<IEventProps, IEventState> {
if (eventData.EventType == "0" && this.state.newRecurrenceEvent) {
eventData.EventType = "1";
eventData.fRecurrence= true;
eventData.UID = getGUID();
}
if (eventData.EventType == "1" && this.state.recurrenceSeriesEdited) {
eventData.fRecurrence= true;
eventData.fRecurrence = true;
eventData.UID = getGUID();
}
} else {
if (this.state.eventData.EventType == '1'){ // recurrence exception
if (eventData.EventType == "1" && this.state.recurrenceSeriesEdited) {
eventData.fRecurrence = true;
eventData.UID = getGUID();
}
}
else {
if (this.state.eventData.EventType == '1') { // recurrence exception
eventData.RecurrenceID = eventData.EventDate.toString();
eventData.MasterSeriesItemID = eventData.ID.toString();
eventData.EventType = "4";
@ -185,20 +188,19 @@ export class Event extends React.Component<IEventProps, IEventState> {
endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`;
}
// Start Date
const startTime = `${this.state.startSelectedHour.key}:${this.state.startSelectedMin.key}`;
const startDateTime = `${startDate} ${startTime}`;
const start = moment(startDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.EventDate = new Date(start);
// End Date
const endTime = `${this.state.endSelectedHour.key}:${this.state.endSelectedMin.key}`;
const endDateTime = `${endDate} ${endTime}`;
const end = moment(endDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.EndDate = new Date(end);
// get Geolocation
eventData.geolocation = { Latitude: this.latitude, Longitude: this.longitude };
const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude);
eventData.location = locationInfo ? locationInfo.display_name : 'N/A';
@ -213,7 +215,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
try {
for (const user of this.attendees) {
const userInfo: any = await this.spService.getUserByLoginName(user.id, this.props.siteUrl);
eventData.attendes.push(Number(userInfo.Id));
}
@ -255,7 +256,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @param {number} [eventId]
* @memberof Event
*/
private async renderEventData(eventId?: number) {
private async renderEventData(eventId?: number) {
this.setState({ isloading: true });
const event: IEventData = !eventId ? this.props.event : await this.spService.getEvent(this.props.siteUrl, this.props.listId, eventId);
@ -479,7 +480,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.setState({ isDeleting: false });
this.props.onDissmissPanel(true);
} catch (error) {
this.setState({ hasError: true, errorMessage: error.message, isDeleting: false, displayDialog:false });
this.setState({ hasError: true, errorMessage: error.message, isDeleting: false, displayDialog: false });
}
}
@ -616,7 +617,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
}
return format("{}{} {} {}",
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency): format(strings.everyNthFormat, frequency),
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency) : format(strings.everyNthFormat, frequency),
strings.weekLabel,
strings.onLabel,
days.join(", "));
@ -634,7 +635,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
const day: number = parseInt(rule["day"]);
return format("{}{} {}",
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency): format(strings.everyNthFormat, frequency),
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency) : format(strings.everyNthFormat, frequency),
strings.monthLabel,
format(strings.onTheDayFormat, day)
);
@ -676,7 +677,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
switch (key) {
case "monthFrequency":
const frequency = parseInt(rule[key]);
switch(frequency) {
switch (frequency) {
case 1:
frequencyFormat = format(strings.everyFormat, frequency);
break;
@ -719,7 +720,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
let month: string;
let day: string;
for (let key of keys) {
switch(key) {
switch (key) {
case "yearFrequency":
const frequency = parseInt(rule[key]);
const frequencyFormat = frequency == 1 ? strings.everyFormat : frequency == 2 ? strings.everySecondFormat : strings.everyNthFormat;
@ -770,7 +771,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
let order: string;
let dayTypeString: string;
for (let key of keys) {
switch(key) {
switch (key) {
case "yearFrequency":
const frequency = parseInt(rule[key]);
const frequencyFormat = frequency === 1 ? strings.everyFormat : frequency === 2 ? strings.everySecondFormat : strings.everyNthFormat;
@ -814,37 +815,36 @@ export class Event extends React.Component<IEventProps, IEventState> {
});
const recurrenceInfo: any = await promise;
if(recurrenceInfo != null)
{
let keys = Object.keys(recurrenceInfo.recurrence.rule[0].repeat[0]);
const recurrenceTypes = ["daily", "weekly", "monthly", "monthlyByDay", "yearly", "yearlyByDay"];
for (var key of keys) {
const rule = recurrenceInfo.recurrence.rule[0].repeat[0][key][0]['$'];
switch(recurrenceTypes.indexOf(key)) {
case 0:
return this.parseDailyRule(rule);
break;
case 1:
return this.parseWeeklyRule(rule);
break;
case 2:
return this.parseMonthlyRule(rule);
break;
case 3:
return this.parseMonthlyByDayRule(rule);
break;
case 4:
return this.parseYearlyRule(rule);
break;
case 5:
return this.parseYearlyByDayRule(rule);
break;
default:
continue;
if (recurrenceInfo != null) {
let keys = Object.keys(recurrenceInfo.recurrence.rule[0].repeat[0]);
const recurrenceTypes = ["daily", "weekly", "monthly", "monthlyByDay", "yearly", "yearlyByDay"];
for (var key of keys) {
const rule = recurrenceInfo.recurrence.rule[0].repeat[0][key][0]['$'];
switch (recurrenceTypes.indexOf(key)) {
case 0:
return this.parseDailyRule(rule);
break;
case 1:
return this.parseWeeklyRule(rule);
break;
case 2:
return this.parseMonthlyRule(rule);
break;
case 3:
return this.parseMonthlyByDayRule(rule);
break;
case 4:
return this.parseYearlyRule(rule);
break;
case 5:
return this.parseYearlyByDayRule(rule);
break;
default:
continue;
}
}
}
}
}
/**
@ -898,17 +898,17 @@ export class Event extends React.Component<IEventProps, IEventState> {
<div>
{
(this.state.eventData && (this.state.eventData.EventType !== "0" && this.state.showRecurrenceSeriesInfo !== true)) ?
<div>
<h2 style={{ display: 'inline-block', verticalAlign: 'top' }}>{ strings.recurrenceEventLabel }</h2>
{ this.state.recurrenceDescription ? <span style={{ display: 'block' }} >{ this.state.recurrenceDescription }</span> : null }
<div>
<h2 style={{ display: 'inline-block', verticalAlign: 'top' }}>{strings.recurrenceEventLabel}</h2>
{this.state.recurrenceDescription ? <span style={{ display: 'block' }} >{this.state.recurrenceDescription}</span> : null}
<DefaultButton
style={{ display: 'inline-block', marginLeft: '330px', verticalAlign: 'top', width: 'auto' }}
iconProps={{ iconName: 'RecurringEvent' }}
allowDisabledFocus={true}
onClick={this.onEditRecurrence}
>
{ strings.editRecurrenceSeries }
</DefaultButton>
{strings.editRecurrenceSeries}
</DefaultButton>
</div>
: ''
@ -948,62 +948,66 @@ export class Event extends React.Component<IEventProps, IEventState> {
hidden={this.state.showRecurrenceSeriesInfo}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
<Dropdown
selectedKey={this.state.startSelectedHour.key}
onChange={this.onStartChangeHour}
label={strings.StartHourLabel}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '01', text: '01' },
{ key: '02', text: '02' },
{ key: '03', text: '03' },
{ key: '04', text: '04' },
{ key: '05', text: '05' },
{ key: '06', text: '06' },
{ key: '07', text: '07' },
{ key: '08', text: '08' },
{ key: '09', text: '09' },
{ key: '10', text: '10' },
{ key: '11', text: '11' },
{ key: '12', text: '12' },
{ key: '13', text: '13' },
{ key: '14', text: '14' },
{ key: '15', text: '15' },
{ key: '16', text: '16' },
{ key: '17', text: '17' },
{ key: '18', text: '18' },
{ key: '19', text: '19' },
{ key: '20', text: '20' },
{ key: '21', text: '21' },
{ key: '22', text: '22' },
{ key: '23', text: '23' }
]}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', }}>
<Dropdown
label={strings.StartMinLabel}
selectedKey={this.state.startSelectedMin.key}
onChange={this.onStartChangeMin}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '05', text: '05' },
{ key: '10', text: '10' },
{ key: '15', text: '15' },
{ key: '20', text: '20' },
{ key: '25', text: '25' },
{ key: '30', text: '30' },
{ key: '35', text: '35' },
{ key: '40', text: '40' },
{ key: '45', text: '45' },
{ key: '50', text: '50' },
{ key: '55', text: '55' }
]}
/>
</div>
{!this.state.isAllDayEvent &&
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
<Dropdown
selectedKey={this.state.startSelectedHour.key}
onChange={this.onStartChangeHour}
label={strings.StartHourLabel}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '01', text: '01' },
{ key: '02', text: '02' },
{ key: '03', text: '03' },
{ key: '04', text: '04' },
{ key: '05', text: '05' },
{ key: '06', text: '06' },
{ key: '07', text: '07' },
{ key: '08', text: '08' },
{ key: '09', text: '09' },
{ key: '10', text: '10' },
{ key: '11', text: '11' },
{ key: '12', text: '12' },
{ key: '13', text: '13' },
{ key: '14', text: '14' },
{ key: '15', text: '15' },
{ key: '16', text: '16' },
{ key: '17', text: '17' },
{ key: '18', text: '18' },
{ key: '19', text: '19' },
{ key: '20', text: '20' },
{ key: '21', text: '21' },
{ key: '22', text: '22' },
{ key: '23', text: '23' }
]}
/>
</div>
}
{!this.state.isAllDayEvent &&
<div style={{ display: 'inline-block', verticalAlign: 'top', }}>
<Dropdown
label={strings.StartMinLabel}
selectedKey={this.state.startSelectedMin.key}
onChange={this.onStartChangeMin}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '05', text: '05' },
{ key: '10', text: '10' },
{ key: '15', text: '15' },
{ key: '20', text: '20' },
{ key: '25', text: '25' },
{ key: '30', text: '30' },
{ key: '35', text: '35' },
{ key: '40', text: '40' },
{ key: '45', text: '45' },
{ key: '50', text: '50' },
{ key: '55', text: '55' }
]}
/>
</div>
}
<br />
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
<DatePicker
@ -1020,75 +1024,94 @@ export class Event extends React.Component<IEventProps, IEventState> {
hidden={this.state.showRecurrenceSeriesInfo}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
<Dropdown
selectedKey={this.state.endSelectedHour.key}
onChange={this.onEndChangeHour}
label={strings.EndHourLabel}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '01', text: '01' },
{ key: '02', text: '02' },
{ key: '03', text: '03' },
{ key: '04', text: '04' },
{ key: '05', text: '05' },
{ key: '06', text: '06' },
{ key: '07', text: '07' },
{ key: '08', text: '08' },
{ key: '09', text: '09' },
{ key: '10', text: '10' },
{ key: '11', text: '11' },
{ key: '12', text: '12' },
{ key: '13', text: '13' },
{ key: '14', text: '14' },
{ key: '15', text: '15' },
{ key: '16', text: '16' },
{ key: '17', text: '17' },
{ key: '18', text: '18' },
{ key: '19', text: '19' },
{ key: '20', text: '20' },
{ key: '21', text: '21' },
{ key: '22', text: '22' },
{ key: '23', text: '23' }
]}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', }}>
<Dropdown
label={strings.EndMinLabel}
selectedKey={this.state.endSelectedMin.key}
onChange={this.onEndChangeMin}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '05', text: '05' },
{ key: '10', text: '10' },
{ key: '15', text: '15' },
{ key: '20', text: '20' },
{ key: '25', text: '25' },
{ key: '30', text: '30' },
{ key: '35', text: '35' },
{ key: '40', text: '40' },
{ key: '45', text: '45' },
{ key: '50', text: '50' },
{ key: '55', text: '55' },
{ key: '59', text: '59' }
]}
/>
</div>
{!this.state.isAllDayEvent &&
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
<Dropdown
selectedKey={this.state.endSelectedHour.key}
onChange={this.onEndChangeHour}
label={strings.EndHourLabel}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '01', text: '01' },
{ key: '02', text: '02' },
{ key: '03', text: '03' },
{ key: '04', text: '04' },
{ key: '05', text: '05' },
{ key: '06', text: '06' },
{ key: '07', text: '07' },
{ key: '08', text: '08' },
{ key: '09', text: '09' },
{ key: '10', text: '10' },
{ key: '11', text: '11' },
{ key: '12', text: '12' },
{ key: '13', text: '13' },
{ key: '14', text: '14' },
{ key: '15', text: '15' },
{ key: '16', text: '16' },
{ key: '17', text: '17' },
{ key: '18', text: '18' },
{ key: '19', text: '19' },
{ key: '20', text: '20' },
{ key: '21', text: '21' },
{ key: '22', text: '22' },
{ key: '23', text: '23' }
]}
/>
</div>
}
{!this.state.isAllDayEvent &&
<div style={{ display: 'inline-block', verticalAlign: 'top', }}>
<Dropdown
label={strings.EndMinLabel}
selectedKey={this.state.endSelectedMin.key}
onChange={this.onEndChangeMin}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
options={[
{ key: '00', text: '00' },
{ key: '05', text: '05' },
{ key: '10', text: '10' },
{ key: '15', text: '15' },
{ key: '20', text: '20' },
{ key: '25', text: '25' },
{ key: '30', text: '30' },
{ key: '35', text: '35' },
{ key: '40', text: '40' },
{ key: '45', text: '45' },
{ key: '50', text: '50' },
{ key: '55', text: '55' },
{ key: '59', text: '59' }
]}
/>
</div>
}
<Label>{this.state.siteRegionalSettings ? this.state.siteRegionalSettings.Description : ''}</Label>
<br />
{
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '200px' }}>
<Toggle
defaultChecked={this.state.eventData && this.state.eventData.fAllDayEvent}
inlineLabel={true}
label={strings.allDayEventLabel}
onText={strings.onLabel}
offText={strings.offLabel}
onChange={(ev, checked: boolean) => {
ev.preventDefault();
this.setState({ isAllDayEvent: checked });
}}
/>
</div>
<br />
{
this.state.eventData && (this.state.eventData.EventType == "0") ?
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '200px' }}>
<Toggle
defaultChecked={false}
inlineLabel={true}
label={ strings.ifRecurrenceLabel }
onText={ strings.onLabel }
offText={ strings.offLabel }
label={strings.ifRecurrenceLabel}
onText={strings.onLabel}
offText={strings.offLabel}
onChange={(ev, checked: boolean) => {
ev.preventDefault();
this.setState({ showRecurrenceSeriesInfo: checked, newRecurrenceEvent: checked });
@ -1109,12 +1132,11 @@ export class Event extends React.Component<IEventProps, IEventState> {
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.returnRecurrenceInfo}
>
</EventRecurrenceInfo>
)
}
< Label > {strings.eventDescriptionLabel }</Label>
<Label>{strings.eventDescriptionLabel}</Label>
<div className={styles.description}>
<Editor
@ -1125,7 +1147,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
</div>
<div>
<PeoplePicker
webAbsoluteUrl={this.props.siteUrl}
context={this.props.context}
titleText={strings.AttendeesLabel}

View File

@ -1,25 +1,25 @@
export interface IEventData {
Id?:number;
ID?:number;
Id?: number;
ID?: number;
title: string;
Description?: any;
location?:string;
location?: string;
EventDate: Date;
EndDate: Date;
color?:string;
color?: string;
ownerInitial?: string;
ownerPhoto?:string;
ownerEmail?:string;
ownerName?:string;
ownerPhoto?: string;
ownerEmail?: string;
ownerName?: string;
fAllDayEvent?: boolean;
attendes?: number[];
geolocation?: {Longitude:number, Latitude: number};
geolocation?: { Longitude: number, Latitude: number };
Category?: string;
Duration?: number;
RecurrenceData?:string;
fRecurrence?:string | boolean;
EventType?:string;
UID?:string;
RecurrenceData?: string;
fRecurrence?: string | boolean;
EventType?: string;
UID?: string;
RecurrenceID?: string;
MasterSeriesItemID?: string;
}

View File

@ -76,7 +76,6 @@ export default class spservices {
let results = null;
try {
const web = new Web(siteUrl);
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
results = await web.lists.getById(listId).items.add({
@ -87,7 +86,7 @@ export default class spservices {
EventDate: new Date(moment(newEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(newEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: newEvent.location,
fAllDayEvent: false,
fAllDayEvent: newEvent.fAllDayEvent,
fRecurrence: newEvent.fRecurrence,
Category: newEvent.Category,
EventType: newEvent.EventType,
@ -96,7 +95,8 @@ export default class spservices {
MasterSeriesItemID: newEvent.MasterSeriesItemID,
RecurrenceID: newEvent.RecurrenceID ? moment(newEvent.RecurrenceID).add(siteTimeZoneHours, 'hours').toISOString() : undefined,
});
} catch (error) {
}
catch (error) {
return Promise.reject(error);
}
return results;
@ -117,13 +117,13 @@ export default class spservices {
try {
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
const web = new Web(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
const event = await web.lists.getById(listId).items.usingCaching().getById(eventId)
.select("RecurrenceID", "MasterSeriesItemID", "Id", "ID", "ParticipantsPickerId", "EventType", "Title", "Description", "EventDate", "EndDate", "Location", "Author/SipAddress", "Author/Title", "Geolocation", "fAllDayEvent", "fRecurrence", "RecurrenceData", "RecurrenceData", "Duration", "Category", "UID")
.expand("Author")
.get();
returnEvent = {
Id: event.ID,
ID: event.ID,
@ -139,7 +139,7 @@ export default class spservices {
color: '',
ownerName: event.Author.Title,
attendes: event.ParticipantsPickerId,
fAllDayEvent: false,
fAllDayEvent: event.fAllDayEvent,
geolocation: { Longitude: event.Geolocation ? event.Geolocation.Longitude : 0, Latitude: event.Geolocation ? event.Geolocation.Latitude : 0 },
Category: event.Category,
Duration: event.Duration,
@ -149,13 +149,13 @@ export default class spservices {
RecurrenceID: event.RecurrenceID,
MasterSeriesItemID: event.MasterSeriesItemID,
};
} catch (error) {
}
catch (error) {
return Promise.reject(error);
}
return returnEvent;
}
/**
*
* @param {IEventData} newEvent
@ -167,13 +167,12 @@ export default class spservices {
public async updateEvent(updateEvent: IEventData, siteUrl: string, listId: string) {
let results = null;
try {
// delete all recursive extentions before update recurrence event
// delete all recursive extentions before update recurrence event
if (updateEvent.EventType.toString() == "1") await this.deleteRecurrenceExceptions(updateEvent, siteUrl, listId);
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
const web = new Web(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
let newItem: any = {
Title: updateEvent.title,
@ -183,12 +182,13 @@ export default class spservices {
EventDate: new Date(moment(updateEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(updateEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: updateEvent.location,
fAllDayEvent: false,
fAllDayEvent: updateEvent.fAllDayEvent,
fRecurrence: updateEvent.fRecurrence,
Category: updateEvent.Category,
RecurrenceData: updateEvent.RecurrenceData ? await this.deCodeHtmlEntities(updateEvent.RecurrenceData) : "",
EventType: updateEvent.EventType,
};
if (updateEvent.UID) {
newItem.UID = updateEvent.UID;
}
@ -197,7 +197,8 @@ export default class spservices {
}
results = await web.lists.getById(listId).items.getById(updateEvent.Id).update(newItem);
} catch (error) {
}
catch (error) {
return Promise.reject(error);
}
return results;
@ -489,6 +490,7 @@ export default class spservices {
if (results && results.Row.length > 0) {
let event: any = '';
for (event of results.Row) {
const initialsArray: string[] = event.Author[0].title.split(' ');
const initials: string = initialsArray[0].charAt(0) + initialsArray[initialsArray.length - 1].charAt(0);
@ -501,22 +503,20 @@ export default class spservices {
const CategoryColorValue: any[] = categoryColor.filter((value) => {
return value.category == event.Category;
});
const isAllDayEvent: boolean = event.fAllDayEvent === "Yes";
for (const attendee of event.ParticipantsPicker) {
attendees.push(parseInt(attendee.id));
}
events.push({
Id: event.ID,
ID: event.ID,
EventType: event.EventType,
title: await this.deCodeHtmlEntities(event.Title),
Description: event.Description,
EventDate: new Date(moment(event.EventDate).subtract((siteTimeZoneHours), 'hour').toISOString()),
EndDate: new Date(moment(event.EndDate).subtract(siteTimeZoneHours, 'hour').toISOString()),
EventDate: isAllDayEvent ? new Date(moment(event.EventDate).toISOString()) : new Date(moment(event.EventDate).subtract((siteTimeZoneHours), 'hour').toISOString()),
EndDate: isAllDayEvent ? new Date(moment(event.EndDate).toISOString()) : new Date(moment(event.EndDate).subtract(siteTimeZoneHours, 'hour').toISOString()),
location: event.Location,
ownerEmail: event.Author[0].email,
ownerPhoto: userPictureUrl ?
@ -525,7 +525,7 @@ export default class spservices {
color: CategoryColorValue.length > 0 ? CategoryColorValue[0].color : '#1a75ff', // blue default
ownerName: event.Author[0].title,
attendes: attendees,
fAllDayEvent: false,
fAllDayEvent: isAllDayEvent,
geolocation: { Longitude: parseFloat(geolocation[0]), Latitude: parseFloat(geolocation[1]) },
Category: event.Category,
Duration: event.Duration,
@ -540,6 +540,7 @@ export default class spservices {
let parseEvt: parseRecurrentEvent = new parseRecurrentEvent();
events = parseEvt.parseEvents(events, null, null);
}
// Return Data
return events;
} catch (error) {

View File

@ -115,6 +115,7 @@ define([], function () {
showMore: "more",
recurrenceEventLabel: "Recurrence Event",
editRecurrenceSeries: "Edit Recurrence Series",
allDayEventLabel: "All Day Event ?",
ifRecurrenceLabel: "Recurrence ?",
onLabel: "On",
offLabel: "Off",

View File

@ -115,6 +115,7 @@ declare interface ICalendarWebPartStrings {
showMore: string;
recurrenceEventLabel: string;
editRecurrenceSeries: string;
allDayEventLabel: string;
ifRecurrenceLabel: string;
onLabel: string;
offLabel: string;

View File

@ -114,6 +114,7 @@ define([], function () {
showMore: "mer",
recurrenceEventLabel: "Återkommande händelse",
editRecurrenceSeries: "Redigera serie",
allDayEventLabel: "All Day Event ?",
ifRecurrenceLabel: "Återkommande ?",
onLabel: "På",
offLabel: "Av",

View File

@ -44,6 +44,14 @@ Site Url of library | Text| yes|
Picture Library| Choice/Dropdown | yes| this is filled with all Picture Libraries (BaseTemplate : 109)
number images to load | number| yes | number between 1 and 200
### react-slick Props
For all available props, go [here](https://react-slick.neostack.com/docs/api/).
### react-slick Methods
For all available methods, go [here](https://react-slick.neostack.com/docs/api#methods)
## Solution
The web part Use PnPjs library, Microsoft Graph API, Office-ui-fabric-react components, react-slick Component

View File

@ -1,74 +1,74 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.carousel {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}
:global {
.slick-next,
.slick-prev {
background: $ms-color-themePrimary !important;
}
.slick-dots {
position: initial !important;
}
}
}

View File

@ -173,7 +173,7 @@ export default class Carousel extends React.Component<ICarouselProps, ICarouselS
}
public render(): React.ReactElement<ICarouselProps> {
const sliderSettings = {
dots: false,
dots: true,
infinite: true,
speed: 500,
slidesToShow: 1,
@ -181,7 +181,7 @@ export default class Carousel extends React.Component<ICarouselProps, ICarouselS
lazyLoad: 'progressive',
autoplaySpeed: 3000,
initialSlide: this.state.photoIndex,
arrows: false,
arrows: true,
draggable: true,
adaptiveHeight: true,
useCSS: true,
@ -216,7 +216,7 @@ export default class Carousel extends React.Component<ICarouselProps, ICarouselS
<Label style={{ width: '250px', margin: 'auto', fontSize: FontSizes.size20 }}>No images in the library</Label>
</div>
:
<div style={{ width: '100%', height: '100%', overflow: 'hidden' }}>
<div style={{ width: '100%', height: '100%'}}>
<div style={{ width: '100%'}}>
<Slider

View File

@ -33,7 +33,6 @@
## Used SharePoint Framework Version
![SPFx 1.11](https://img.shields.io/badge/version-1.11-green.svg)
## Applies to
@ -47,21 +46,18 @@
Property |Type|Required| comments
--------------------|----|--------|----------
Title | Text| no|WebPart Title
searchFirstName | boolean|no| Lastname or Firstname search query
Properties to search | text | no | By default **FirstName,LastName,WorkEmail,Department** are used for search. You can add custom properties separated by comma.
Results per page | number | Number of people result to be displayed per page. Max of **20** is allowed, default of **10** is set.
Title | Text| No|WebPart Title
searchFirstName | boolean|No| Lastname or Firstname search query
Properties to search | text | No | By default **FirstName,LastName,WorkEmail,Department** are used for search. You can add custom properties separated by comma.
Properties to sent as clear text | text | No | By default if the search key has empty spaces, its replaced with **+** before sending it to the search query. The search properties mentioned here will be sent without the empty space replacemnt.
Results per page | number | Yes | Number of people result to be displayed per page. Max of **20** is allowed, default of **10** is set.
## Solution
The web part use PnPjs library, Office-ui-fabric-react components
Solution|Author(s)
--------|---------
Directory Web Part| João Mendes
Directory Web Part|João Mendes
Directory Web Part| Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at))
Directory Web Part| Sudharsan K ([@sudharsank](https://twitter.com/sudharsank))
@ -71,7 +67,8 @@ 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)
2.0.0|Sep 18 2020|React hooks, paging, dynamic search props, result alignment using office ui fabric stack.
2.0.0.0|Sep 18 2020|React hooks, paging, dynamic search props, result alignment using office ui fabric stack.
3.0.0.0|Oct 17 2020|Minor fixes and add the additional web part property.
## Disclaimer
@ -89,8 +86,4 @@ Version|Date|Comments
- `gulp package-solution --ship`
- `Add to AppCatalog and deploy`
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-directory" />

View File

@ -10,7 +10,7 @@
},
"name": "Search Directory",
"id": "5b62bc16-3a71-461d-be2f-16bfcb011e8a",
"version": "2.0.0.0",
"version": "3.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false

View File

@ -19,6 +19,7 @@ export interface IDirectoryWebPartProps {
title: string;
searchFirstName: boolean;
searchProps: string;
clearTextSearchProps: string;
pageSize: number;
}
@ -47,6 +48,7 @@ export default class DirectoryWebPart extends BaseClientSideWebPart<
this.properties.title = value;
},
searchProps: this.properties.searchProps,
clearTextSearchProps: this.properties.clearTextSearchProps,
pageSize: this.properties.pageSize
}
);
@ -91,6 +93,13 @@ export default class DirectoryWebPart extends BaseClientSideWebPart<
multiline: false,
resizable: false
}),
PropertyPaneTextField('clearTextSearchProps', {
label: strings.ClearTextSearchPropsLabel,
description: strings.ClearTextSearchPropsDesc,
value: this.properties.clearTextSearchProps,
multiline: false,
resizable: false
}),
PropertyPaneSlider('pageSize', {
label: 'Results per page',
showValue: true,

View File

@ -19,6 +19,7 @@ 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) => {
@ -131,11 +132,36 @@ const DirectoryHook: React.FC<IDirectoryProps> = (props) => {
props.searchProps.split(',') : ['FirstName', 'LastName', 'WorkEmail', 'Department'];
let qryText: string = '';
let finalSearchText: string = searchText ? searchText.replace(/ /g, '+') : searchText;
searchProps.map((srchprop, index) => {
if (index == searchProps.length - 1)
qryText += `${srchprop}:${finalSearchText}*`;
else qryText += `${srchprop}:${finalSearchText}* OR `;
});
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,
@ -252,12 +278,10 @@ const DirectoryHook: React.FC<IDirectoryProps> = (props) => {
}, [state.users, props.pageSize]);
useEffect(() => {
console.log("Alpha change");
if (alphaKey.length > 0 && alphaKey != "0") _searchByAlphabets(false);
}, [alphaKey]);
useEffect(() => {
console.log("yes");
_loadAlphabets();
_searchByAlphabets(true);
}, [props]);

View File

@ -7,5 +7,6 @@ export interface IDirectoryProps {
searchFirstName: boolean;
updateProperty: (value: string) => void;
searchProps?: string;
clearTextSearchProps?: string;
pageSize?: number;
}

View File

@ -10,6 +10,8 @@ define([], function() {
"LoadingText": "Searching for user. Please wait...",
"SearchPropsLabel": "Properties to search",
"SearchPropsDesc": "Enter the properties separated by comma to be used for search",
"ClearTextSearchPropsLabel":"Properties whose values are not replaced",
"ClearTextSearchPropsDesc":"Enter the properties separated by comma to be sent as it is without replacing space with '+'",
"PagingLabel": "Results per page"
}
});

View File

@ -9,6 +9,8 @@ declare interface IDirectoryWebPartStrings {
LoadingText: string;
SearchPropsLabel: string;
SearchPropsDesc: string;
ClearTextSearchPropsLabel: string;
ClearTextSearchPropsDesc: string;
PagingLabel: string;
}

View File

@ -15,7 +15,12 @@
{
"resource": "Microsoft Graph",
"scope": "Directory.AccessAsUser.All"
},
{
"resource": "Microsoft Graph",
"scope": "User.Read"
}
]
},
"paths": {

View File

@ -59,6 +59,8 @@ react-outlook-add-todo-task|Luis Mañez (MVP, [ClearPeople](http://www.clearpeop
Version|Date|Comments
-------|----|--------
1.0.0|Jun 3, 2020|Initial release
1.1.0|Sep 3, 2020|To Do item body coming from email body
1.2.0|Oct 16, 2020|To Do item body included a "open in outlook" link
## Disclaimer

View File

@ -162,8 +162,8 @@ export default class CreateTask extends React.Component<
status: "notStarted",
title: taskTitle,
body: {
content: this.props.context.item.body,
contentType: "text",
content: this._composeBody(this.props.context.item.body),
contentType: "html",
},
};
@ -180,4 +180,10 @@ export default class CreateTask extends React.Component<
console.log(e);
}
}
private _composeBody(emailBody: string): string {
const id: string = encodeURIComponent(this.props.context.item.id);
const link: string = `<a href='https://outlook.office365.com/owa/?ItemID=${id}&exvsurl=1&viewmodel=ReadMessageItem'>Open in Outlook</a>`;
return `${emailBody}<p style='font-size: large; padding-top: 10px;'>${link}</p>`;
}
}

View File

@ -16,7 +16,7 @@
"environment": "spo",
"framework": "react",
"isCreatingSolution": true,
"version": "1.10.0",
"version": "1.11.0",
"libraryName": "react-personal-greeting",
"libraryId": "5e7ea24d-fccc-4d96-a56c-488564d9c61c",
"packageManager": "npm",

View File

@ -8,7 +8,7 @@ The web part pulls in the current user's name and displays it on the page. The g
## Used SharePoint Framework Version
![1.10.0](https://img.shields.io/badge/version-1.10.0-green.svg)
![1.11.0](https://img.shields.io/badge/version-1.11.0-green.svg)
## Applies to
@ -30,6 +30,7 @@ react-personal-greeting|Zach Roberts - [SPODev](https://spodev.com)
Version|Date|Comments
-------|----|--------
1.1|September 24, 2020| Updated SPFX version and added font-size
1.0|April 14, 2020|Initial release
## Disclaimer
@ -46,8 +47,8 @@ Version|Date|Comments
* `gulp build`
* `gulp bundle --ship`
* `gulp package-solution --ship`
* add the webpart to your tenant app store
* add the app to a SharePoint site and then add the webpart to the page
* add the web part to your tenant app store
* add the app to a SharePoint site and then add the web part to the page
## Features
@ -55,8 +56,8 @@ Version|Date|Comments
This Web Part illustrates the following concepts on top of the SharePoint Framework:
* Using the SPFx context to gather the current user's display name.
* Adjusting the styles of the component in the webpart using the props adjusted through the property pane.
* PnP SPFx Placeholder - This component allows you to have a placeholder visble under certain conditions if your web parts requires some setup.
* PnP SPFx Color Picker - This component adds an awesome color picker to the property pane, great for adjusting colors in your webpart.
* Adjusting the styles of the component in the web part using the props adjusted through the property pane.
* PnP SPFx Placeholder - This component allows you to have a placeholder visible under certain conditions if your web parts requires some setup.
* PnP SPFx Color Picker - This component adds an awesome color picker to the property pane, great for adjusting colors in your web part.
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-personal-greeting" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -3,9 +3,16 @@
"solution": {
"name": "react-personal-greeting-client-side-solution",
"id": "5e7ea24d-fccc-4d96-a56c-488564d9c61c",
"version": "1.0.0.0",
"version": "1.1.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false
"isDomainIsolated": false,
"developer": {
"name": "",
"mpnId": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": ""
}
},
"paths": {
"zippedPackage": "solution/react-personal-greeting.sppkg"

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "react-personal-greeting",
"version": "0.0.1",
"version": "1.1.0",
"private": true,
"main": "lib/index.js",
"engines": {
@ -14,19 +14,15 @@
"postversion": "gulp dist"
},
"dependencies": {
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@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": "^2.0.3",
"@pnp/spfx-controls-react": "1.17.0",
"@pnp/spfx-property-controls": "1.17.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"office-ui-fabric-react": "6.189.2",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
@ -36,10 +32,10 @@
"devDependencies": {
"@microsoft/microsoft-graph-types": "^1.12.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@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/mocha": "2.2.38",
"ajv": "~5.2.2",

View File

@ -19,7 +19,7 @@
"group": { "default": "Other" },
"title": { "default": "Personal Greeting" },
"description": { "default": "Personal Greeting description" },
"officeFabricIconFontName": "Page",
"officeFabricIconFontName": "TextDocumentShared",
"properties": {
"description": "Personal Greeting"
}

View File

@ -4,11 +4,11 @@ import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneDropdown
PropertyPaneDropdown,
IPropertyPaneDropdownOption
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart, WebPartContext } from '@microsoft/sp-webpart-base';
import * as strings from 'PersonalGreetingWebPartStrings';
import PersonalGreeting from './components/PersonalGreeting';
import { IPersonalGreetingProps } from './components/IPersonalGreetingProps';
import { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
@ -18,8 +18,56 @@ export interface IPersonalGreetingWebPartProps {
context: WebPartContext;
position: string;
textColor: string;
fontSize: number;
}
const fontSizeOptions: IPropertyPaneDropdownOption[] = [
{
key: 12,
text: '12'
},
{
key: 14,
text: '16'
},
{
key: 18,
text: '18'
},
{
key: 20,
text: '20'
},
{
key: 24,
text: '24'
},
{
key: 28,
text: '28'
},
{
key: 32,
text: '32'
},
{
key: 36,
text: '36'
},
{
key: 42,
text: '42'
},
{
key: 46,
text: '46'
},
{
key: 68,
text: '68'
},
];
export default class PersonalGreetingWebPart extends BaseClientSideWebPart <IPersonalGreetingWebPartProps> {
public render(): void {
@ -29,7 +77,8 @@ export default class PersonalGreetingWebPart extends BaseClientSideWebPart <IPer
greetingText: this.properties.greetingText,
context: this.context,
position: this.properties.position,
textColor: this.properties.textColor
textColor: this.properties.textColor,
fontSize: this.properties.fontSize
}
);
@ -76,6 +125,11 @@ export default class PersonalGreetingWebPart extends BaseClientSideWebPart <IPer
}
]
}),
PropertyPaneDropdown('fontSize', {
label: 'Font Size',
options: fontSizeOptions,
selectedKey: 20
}),
PropertyFieldColorPicker('textColor', {
label: 'Text Color',
properties: this.properties,

View File

@ -6,4 +6,5 @@ export interface IPersonalGreetingProps {
context: WebPartContext;
position: string;
textColor: string;
fontSize: number;
}

View File

@ -7,7 +7,6 @@
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.title {
font-size: x-large;
font-weight: 375;
}
}

View File

@ -1,25 +1,22 @@
import * as React from 'react';
import styles from './PersonalGreeting.module.scss';
import { IPersonalGreetingProps } from './IPersonalGreetingProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { CommandBar, ICommandBarItemProps } from 'office-ui-fabric-react/lib/CommandBar';
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
export default class PersonalGreeting extends React.Component<IPersonalGreetingProps, {}> {
public render(): React.ReactElement<IPersonalGreetingProps> {
const custstyles = {
const custStyles = {
'text-align': this.props.position,
'color': this.props.textColor
'color': this.props.textColor,
'fontSize': this.props.fontSize
} as React.CSSProperties;
return (
<div className={ styles.personalGreeting }>
{this.props.greetingText == null ?
<Placeholder iconName='Edit' iconText='Configure the web part' description='Please configure the web part' buttonLabel='Configure' onConfigure={this._onConfigure} />
: <div className={ styles.title } style={custstyles}>{this.props.greetingText} {this.props.context.pageContext.user.displayName}</div>
// : <h2>{this.props.greetingText} {this.props.context.pageContext.user.displayName} </h2>
: <div className={ styles.title } style={custStyles}>{this.props.greetingText} {this.props.context.pageContext.user.displayName}</div>
}
</div>
);

View File

@ -30,7 +30,8 @@
"esModuleInterop": true
},
"include": [
"src/**/*.ts"
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",

View File

@ -2,7 +2,7 @@
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.10.0",
"version": "1.11.0",
"libraryName": "react-simple-poll",
"libraryId": "890ced10-dacc-4d0d-b9df-355a289980b3",
"packageManager": "npm",

View File

@ -1,6 +1,7 @@
# React Quick Poll
## Summary
> This component is developed for the users who really need to create a **_Poll_** within a minute and with less maintenance. **_'QuickPoll'_** list will be created automatically to store the user response.
> Following are some of the features of this component.
* **_Easy_** to setup with most of the configurations are optional.
@ -22,7 +23,7 @@
* **_Start Date_** - Date when the end user can start seeing the poll question.
* **_End Date_** - Last day of the poll question visible to the end user.
3. **_Success Message_** - Message to be displayed to the user after successful submission. It is optional, if not provided the default message '**Thank you for your submission**' will be displayed.
3. **_Success Message_** - Message to be displayed to the user after a successful submission. It is optional, if not provided the default message '**Thank you for your submission**' will be displayed.
4. **_Response Message_** - Message to be displayed to the user with the user response, once the user has submitted. It is optional, if not provided the default message '**You voted for: ~User Response~**' will be displayed below the chart.
@ -36,33 +37,37 @@
* Make sure the **Multi Choice** option is chosen wisely, do not change once the user started to response to the poll.
## Preview
![Advanced-Comments-Box](./assets/react-quick-poll.gif)
![React-Quick-Poll](./assets/react-quick-poll.gif)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-GA-green.svg)
![SPFx 1.11](https://img.shields.io/badge/version-1.11-green.svg)
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
## Prerequisites
> **@microsoft/generator-sharepoint - 1.10.0**
> **@microsoft/generator-sharepoint - 1.11.0**
## Solution
Solution|Author(s)
--------|---------
react-quick-poll | Sudharsan K.([@sudharsank](https://twitter.com/sudharsank), [Know More](http://windowssharepointserver.blogspot.com/))
react-quick-poll | Sudharsan K.([@sudharsank](https://twitter.com/sudharsank), [Know More](https://spknowledge.com/))
## Version history
Version|Date|Comments
-------|----|--------
1.0.0.1|Feb 24 2020|Initial release
2.0.0.0|Oct 17 2020|Initial release
## 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.**
## Minimal Path to Awesome
@ -71,26 +76,27 @@ Version|Date|Comments
- in the command line run:
- `npm install`
- `gulp bundle --ship && gulp package-solution --ship`
- Add the .sppkg file to the app catalog and add the **'_Quick Poll_'** web part to the page.
- Add the `.sppkg` file to the app catalog and add the **'_Quick Poll_'** web part to the page.
## Features
- Used [PnP Property Pane Controls](https://sharepoint.github.io/sp-dev-fx-property-controls/) to create the property pane controls
* [PropertyFieldToggleWithCallout](https://sharepoint.github.io/sp-dev-fx-property-controls/controls/PropertyFieldToggleWithCallout/)
* [PropertyFieldCollectionData](https://sharepoint.github.io/sp-dev-fx-property-controls/controls/PropertyFieldCollectionData/)
* [PropertyFieldChoiceGroupWithCallout](https://sharepoint.github.io/sp-dev-fx-property-controls/controls/PropertyFieldChoiceGroupWithCallout/)
* PropertyPaneTextField (From base property controls)
- Used [PnP Reusable REact Controls](https://sharepoint.github.io/sp-dev-fx-controls-react/)
- Used [PnP Reusable React Controls](https://sharepoint.github.io/sp-dev-fx-controls-react/)
* [Placeholder](https://sharepoint.github.io/sp-dev-fx-controls-react/controls/Placeholder/)
* [ChartControl](https://sharepoint.github.io/sp-dev-fx-controls-react/controls/ChartControl/)
- Used few styles and controls (Text, MessageBar, ProgressIndicator, PrimaryButton, ChoiceGroup, List, Checkbox) from [Office UI Fabric](https://developer.microsoft.com/en-us/fabric)
- Used [PnP](https://pnp.github.io/pnpjs/) for communication with SharePoint.
- Used [Moment.js](https://momentjs.com/) for datetime formatting.
#### Local Mode
### Local Mode
This solution doesn't work on local mode.
#### SharePoint Mode
### SharePoint Mode
If you want to try on a real environment, open:
[O365 Workbench](https://your-domain.sharepoint.com/_layouts/15/workbench.aspx)
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-quick-poll" />

View File

@ -3,9 +3,16 @@
"solution": {
"name": "React Quick Poll",
"id": "890ced10-dacc-4d0d-b9df-355a289980b3",
"version": "1.0.0.1",
"version": "2.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false
"isDomainIsolated": false,
"developer": {
"name": "Sudharsan K.",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "https://spknowledge.com/",
"mpnId": "000000"
}
},
"paths": {
"zippedPackage": "solution/react-quick-poll.sppkg"

File diff suppressed because it is too large Load Diff

View File

@ -12,20 +12,17 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@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/polyfill-ie11": "^2.0.1-5",
"@pnp/sp": "^2.0.2",
"@pnp/spfx-controls-react": "^1.16.0",
"@pnp/spfx-property-controls": "^1.16.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"moment": "^2.24.0",
"office-ui-fabric-react": "6.189.2",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
@ -33,14 +30,14 @@
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"gulp": "~3.9.1",
"@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/mocha": "2.2.38",
"ajv": "~5.2.2"
"ajv": "~5.2.2",
"gulp": "~3.9.1"
}
}

View File

@ -2,8 +2,8 @@ import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version, ServiceScope } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField,
IPropertyPaneConfiguration,
PropertyPaneTextField,
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { CalloutTriggers } from '@pnp/spfx-property-controls/lib/PropertyFieldHeader';
@ -11,6 +11,7 @@ import { PropertyFieldToggleWithCallout } from '@pnp/spfx-property-controls/lib/
import { PropertyFieldChoiceGroupWithCallout } from '@pnp/spfx-property-controls/lib/PropertyFieldChoiceGroupWithCallout';
import { PropertyFieldCollectionData, CustomCollectionFieldType } from '@pnp/spfx-property-controls/lib/PropertyFieldCollectionData';
import { DateTimePicker, DateConvention, TimeConvention } from '@pnp/spfx-controls-react/lib/DateTimePicker';
import "@pnp/polyfill-ie11";
import { sp } from "@pnp/sp/presets/all";
import * as strings from 'SimplePollWebPartStrings';
import SimplePoll from './components/SimplePoll';
@ -21,262 +22,265 @@ import { ChartType } from '@pnp/spfx-controls-react/lib/ChartControl';
export interface ISimplePollWebPartProps {
pollQuestions: any[];
MsgAfterSubmission: string;
BtnSubmitVoteText: string;
chartType: ChartType;
ResponseMsgToUser: string;
pollBasedOnDate: boolean;
NoPollMsg: string;
pollQuestions: any[];
MsgAfterSubmission: string;
BtnSubmitVoteText: string;
chartType: ChartType;
ResponseMsgToUser: string;
pollBasedOnDate: boolean;
NoPollMsg: string;
}
export default class SimplePollWebPart extends BaseClientSideWebPart<ISimplePollWebPartProps> {
private helper: SPHelper = null;
private userinfo: IUserInfo = null;
protected async onInit(): Promise<void> {
await super.onInit();
sp.setup(this.context);
this.helper = new SPHelper();
this.userinfo = await this.helper.getCurrentUserInfo();
}
private helper: SPHelper = null;
private userinfo: IUserInfo = null;
protected async onInit(): Promise<void> {
await super.onInit();
sp.setup({
ie11: true,
spfxContext: this.context
});
this.helper = new SPHelper();
this.userinfo = await this.helper.getCurrentUserInfo();
}
public render(): void {
const element: React.ReactElement<ISimplePollProps> = React.createElement(
SimplePoll,
{
pollQuestions: this.properties.pollQuestions,
SuccessfullVoteSubmissionMsg: this.properties.MsgAfterSubmission,
ResponseMsgToUser: this.properties.ResponseMsgToUser,
BtnSubmitVoteText: this.properties.BtnSubmitVoteText,
chartType: this.properties.chartType ? this.properties.chartType : ChartType.Doughnut,
pollBasedOnDate: this.properties.pollBasedOnDate,
NoPollMsg: this.properties.NoPollMsg,
currentUserInfo: this.userinfo,
openPropertyPane: this.openPropertyPane
}
);
ReactDom.render(element, this.domElement);
}
protected get disableReactivePropertyChanges() {
return false;
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
private openPropertyPane = (): void => {
this.context.propertyPane.open();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
public render(): void {
const element: React.ReactElement<ISimplePollProps> = React.createElement(
SimplePoll,
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyFieldToggleWithCallout('pollBasedOnDate', {
calloutTrigger: CalloutTriggers.Hover,
key: 'pollBasedOnDateFieldId',
label: strings.PollDateLabel,
calloutContent: React.createElement('div', {}, strings.PollDateCalloutText),
onText: 'Yes',
offText: 'No',
checked: this.properties.pollBasedOnDate
}),
PropertyFieldCollectionData("pollQuestions", {
key: "pollQuestions",
label: strings.PollQuestionsLabel,
panelHeader: strings.PollQuestionsPanelHeader,
manageBtnLabel: strings.PollQuestionsManageButton,
enableSorting: true,
value: this.properties.pollQuestions,
fields: [
{
id: "QTitle",
title: strings.Q_Title_Title,
type: CustomCollectionFieldType.custom,
required: true,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement("div", null,
React.createElement("textarea",
{
style: { width: "250px", height: "70px" },
placeholder: strings.Q_Title_Placeholder,
key: itemId,
value: value,
onChange: (event: React.FormEvent<HTMLTextAreaElement>) => {
onUpdate(field.id, event.currentTarget.value);
},
})
)
);
}
},
{
id: "QOptions",
title: strings.Q_Options_Title,
type: CustomCollectionFieldType.custom,
required: true,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement("div", null,
React.createElement("textarea",
{
style: { width: "250px", height: "70px" },
placeholder: strings.Q_Options_Placeholder,
key: itemId,
value: value,
onChange: (event: React.FormEvent<HTMLTextAreaElement>) => {
onUpdate(field.id, event.currentTarget.value);
},
})
)
);
}
},
{
id: "QMultiChoice",
title: strings.MultiChoice_Title,
type: CustomCollectionFieldType.boolean,
defaultValue: false
},
{
id: "QStartDate",
title: strings.Q_StartDate_Title,
type: CustomCollectionFieldType.custom,
required: false,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement(DateTimePicker, {
key: itemId,
showLabels: false,
dateConvention: DateConvention.Date,
showGoToToday: true,
showMonthPickerAsOverlay: true,
value: value ? new Date(value) : null,
disabled: !this.properties.pollBasedOnDate,
onChange: (date: Date) => {
onUpdate(field.id, date);
}
})
);
}
},
{
id: "QEndDate",
title: strings.Q_EndDate_Title,
type: CustomCollectionFieldType.custom,
required: false,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement(DateTimePicker, {
key: itemId,
showLabels: false,
dateConvention: DateConvention.Date,
showGoToToday: true,
showMonthPickerAsOverlay: true,
value: value ? new Date(value) : null,
disabled: !this.properties.pollBasedOnDate,
onChange: (date: Date) => {
onUpdate(field.id, date);
}
})
);
}
}
],
disabled: false
}),
PropertyPaneTextField('MsgAfterSubmission', {
label: strings.MsgAfterSubmissionLabel,
description: strings.MsgAfterSubmissionDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.MsgAfterSubmissionPlaceholder,
value: this.properties.MsgAfterSubmission
}),
PropertyPaneTextField('ResponseMsgToUser', {
label: strings.ResponseMsgToUserLabel,
description: strings.ResponseMsgToUserDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.ResponseMsgToUserPlaceholder,
value: this.properties.ResponseMsgToUser
}),
PropertyPaneTextField('BtnSubmitVoteText', {
label: strings.BtnSumbitVoteLabel,
description: strings.BtnSumbitVoteDescription,
maxLength: 50,
multiline: false,
resizable: false,
placeholder: strings.BtnSumbitVotePlaceholder,
value: this.properties.BtnSubmitVoteText
}),
PropertyPaneTextField('NoPollMsg', {
label: strings.NoPollMsgLabel,
description: strings.NoPollMsgDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.NoPollMsgPlaceholder,
value: this.properties.NoPollMsg
}),
PropertyFieldChoiceGroupWithCallout('chartType', {
calloutContent: React.createElement('div', {}, strings.ChartFieldCalloutText),
calloutTrigger: CalloutTriggers.Hover,
key: 'choice_charttype',
label: strings.ChartFieldLabel,
options: [
{
key: 'pie',
text: 'Pie',
checked: this.properties.chartType === ChartType.Pie,
iconProps: { officeFabricIconFontName: 'PieSingle' }
}, {
key: 'doughnut',
text: 'Doughnut',
checked: this.properties.chartType === ChartType.Doughnut,
iconProps: { officeFabricIconFontName: 'DonutChart' }
}, {
key: 'bar',
text: 'Bar',
checked: this.properties.chartType === ChartType.Bar,
iconProps: { officeFabricIconFontName: 'BarChartVertical' }
}, {
key: 'horizontalBar',
text: 'Horizontal Bar',
checked: this.properties.chartType === ChartType.HorizontalBar,
iconProps: { officeFabricIconFontName: 'BarChartHorizontal' }
}, {
key: 'line',
text: 'Line',
checked: this.properties.chartType === ChartType.Line,
iconProps: { officeFabricIconFontName: 'LineChart' }
}]
})
]
pollQuestions: this.properties.pollQuestions,
SuccessfullVoteSubmissionMsg: this.properties.MsgAfterSubmission,
ResponseMsgToUser: this.properties.ResponseMsgToUser,
BtnSubmitVoteText: this.properties.BtnSubmitVoteText,
chartType: this.properties.chartType ? this.properties.chartType : ChartType.Doughnut,
pollBasedOnDate: this.properties.pollBasedOnDate,
NoPollMsg: this.properties.NoPollMsg,
currentUserInfo: this.userinfo,
openPropertyPane: this.openPropertyPane
}
]
}
]
};
}
);
ReactDom.render(element, this.domElement);
}
protected get disableReactivePropertyChanges() {
return false;
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
private openPropertyPane = (): void => {
this.context.propertyPane.open();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyFieldToggleWithCallout('pollBasedOnDate', {
calloutTrigger: CalloutTriggers.Hover,
key: 'pollBasedOnDateFieldId',
label: strings.PollDateLabel,
calloutContent: React.createElement('div', {}, strings.PollDateCalloutText),
onText: 'Yes',
offText: 'No',
checked: this.properties.pollBasedOnDate
}),
PropertyFieldCollectionData("pollQuestions", {
key: "pollQuestions",
label: strings.PollQuestionsLabel,
panelHeader: strings.PollQuestionsPanelHeader,
manageBtnLabel: strings.PollQuestionsManageButton,
enableSorting: true,
value: this.properties.pollQuestions,
fields: [
{
id: "QTitle",
title: strings.Q_Title_Title,
type: CustomCollectionFieldType.custom,
required: true,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement("div", null,
React.createElement("textarea",
{
style: { width: "220px", height: "70px" },
placeholder: strings.Q_Title_Placeholder,
key: itemId,
value: value,
onChange: (event: React.FormEvent<HTMLTextAreaElement>) => {
onUpdate(field.id, event.currentTarget.value);
},
})
)
);
}
},
{
id: "QOptions",
title: strings.Q_Options_Title,
type: CustomCollectionFieldType.custom,
required: true,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement("div", null,
React.createElement("textarea",
{
style: { width: "220px", height: "70px" },
placeholder: strings.Q_Options_Placeholder,
key: itemId,
value: value,
onChange: (event: React.FormEvent<HTMLTextAreaElement>) => {
onUpdate(field.id, event.currentTarget.value);
},
})
)
);
}
},
{
id: "QMultiChoice",
title: strings.MultiChoice_Title,
type: CustomCollectionFieldType.boolean,
defaultValue: false
},
{
id: "QStartDate",
title: strings.Q_StartDate_Title,
type: CustomCollectionFieldType.custom,
required: false,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement(DateTimePicker, {
key: itemId,
showLabels: false,
dateConvention: DateConvention.Date,
showGoToToday: true,
showMonthPickerAsOverlay: true,
value: value ? new Date(value) : null,
disabled: !this.properties.pollBasedOnDate,
onChange: (date: Date) => {
onUpdate(field.id, date);
}
})
);
}
},
{
id: "QEndDate",
title: strings.Q_EndDate_Title,
type: CustomCollectionFieldType.custom,
required: false,
onCustomRender: (field, value, onUpdate, item, itemId) => {
return (
React.createElement(DateTimePicker, {
key: itemId,
showLabels: false,
dateConvention: DateConvention.Date,
showGoToToday: true,
showMonthPickerAsOverlay: true,
value: value ? new Date(value) : null,
disabled: !this.properties.pollBasedOnDate,
onChange: (date: Date) => {
onUpdate(field.id, date);
}
})
);
}
}
],
disabled: false
}),
PropertyPaneTextField('MsgAfterSubmission', {
label: strings.MsgAfterSubmissionLabel,
description: strings.MsgAfterSubmissionDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.MsgAfterSubmissionPlaceholder,
value: this.properties.MsgAfterSubmission
}),
PropertyPaneTextField('ResponseMsgToUser', {
label: strings.ResponseMsgToUserLabel,
description: strings.ResponseMsgToUserDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.ResponseMsgToUserPlaceholder,
value: this.properties.ResponseMsgToUser
}),
PropertyPaneTextField('BtnSubmitVoteText', {
label: strings.BtnSumbitVoteLabel,
description: strings.BtnSumbitVoteDescription,
maxLength: 50,
multiline: false,
resizable: false,
placeholder: strings.BtnSumbitVotePlaceholder,
value: this.properties.BtnSubmitVoteText
}),
PropertyPaneTextField('NoPollMsg', {
label: strings.NoPollMsgLabel,
description: strings.NoPollMsgDescription,
maxLength: 150,
multiline: true,
rows: 3,
resizable: false,
placeholder: strings.NoPollMsgPlaceholder,
value: this.properties.NoPollMsg
}),
PropertyFieldChoiceGroupWithCallout('chartType', {
calloutContent: React.createElement('div', {}, strings.ChartFieldCalloutText),
calloutTrigger: CalloutTriggers.Hover,
key: 'choice_charttype',
label: strings.ChartFieldLabel,
options: [
{
key: 'pie',
text: 'Pie',
checked: this.properties.chartType === ChartType.Pie,
iconProps: { officeFabricIconFontName: 'PieSingle' }
}, {
key: 'doughnut',
text: 'Doughnut',
checked: this.properties.chartType === ChartType.Doughnut,
iconProps: { officeFabricIconFontName: 'DonutChart' }
}, {
key: 'bar',
text: 'Bar',
checked: this.properties.chartType === ChartType.Bar,
iconProps: { officeFabricIconFontName: 'BarChartVertical' }
}, {
key: 'horizontalBar',
text: 'Horizontal Bar',
checked: this.properties.chartType === ChartType.HorizontalBar,
iconProps: { officeFabricIconFontName: 'BarChartHorizontal' }
}, {
key: 'line',
text: 'Line',
checked: this.properties.chartType === ChartType.Line,
iconProps: { officeFabricIconFontName: 'LineChart' }
}]
})
]
}
]
}
]
};
}
}

View File

@ -6,96 +6,101 @@ import { IOptionsContainerProps } from './IOptionsContainerProps';
import * as _ from 'lodash';
export interface IOptionsContainerState {
selChoices?: string[];
selChoices?: string[];
}
export default class OptionsContainer extends React.Component<IOptionsContainerProps, IOptionsContainerState> {
constructor(props: IOptionsContainerProps) {
super(props);
this.state = {
selChoices: []
};
}
constructor(props: IOptionsContainerProps) {
super(props);
this.state = {
selChoices: []
};
}
public render(): JSX.Element {
const { disabled, selectedKey, label, options, onChange, multiSelect } = this.props;
return (
<div>
{multiSelect ? (
<div style={{ paddingTop: "15px" }}>
<List items={this.getOptions()} onRenderCell={this._onRenderCell} />
</div>
) : (
<ChoiceGroup disabled={disabled}
selectedKey={this._getSelectedKey()}
options={this.onRenderChoiceOptions()} required={true} label=""
onChange={this._onChange}
/>
)
public render(): JSX.Element {
const { disabled, selectedKey, label, options, onChange, multiSelect } = this.props;
return (
<div>
{multiSelect ? (
<div style={{ paddingTop: "15px" }}>
<List items={this.getOptions()} onRenderCell={this._onRenderCell} />
</div>
) : (
<ChoiceGroup disabled={disabled}
selectedKey={this._getSelectedKey()}
options={this.onRenderChoiceOptions()} required={true} label=""
onChange={this._onChange}
/>
)
}
</div>
);
}
private getOptions = (): string[] => {
let tempChoices: string[] = [];
if (this.props.options.indexOf(',') >= 0) {
let tmpChoices = this.props.options.split(',');
tmpChoices.map(choice => {
if (choice && choice.trim().length > 0) tempChoices.push(choice);
});
} else tempChoices.push(this.props.options);
return tempChoices;
}
private _onRenderCell = (item: any, index: number | undefined): JSX.Element => {
if (item && item.length > 0) {
return (
<div style={{ marginBottom: "15px" }}>
<Checkbox label={item} onChange={this._makeChangeHandler(item)} />
</div>
);
}
</div>
);
}
private getOptions = (): string[] => {
let tempChoices: string[] = [];
if (this.props.options.indexOf(',') >= 0) {
tempChoices = this.props.options.split(',');
} else tempChoices.push(this.props.options);
return tempChoices;
}
private _onRenderCell = (item: any, index: number | undefined): JSX.Element => {
return (
<div style={{ marginBottom: "15px" }}>
<Checkbox label={item} onChange={this._makeChangeHandler(item)} />
</div>
);
}
private onRenderChoiceOptions(): IChoiceGroupOption[] {
let choices: IChoiceGroupOption[] = [];
let tempChoices: string[] = this.getOptions();
if (tempChoices.length > 0) {
tempChoices.map((choice: string) => {
choices.push({
key: choice.trim(),
text: choice.trim()
});
});
} else {
choices.push({
key: '0',
text: "Sorry, no choices found",
disabled: true,
});
}
return choices;
}
private _getSelectedKey = (): string => {
return this.props.selectedKey();
}
private _onChange = (ev: React.FormEvent<HTMLInputElement>, option: any): void => {
this.props.onChange(ev, option, false);
}
private _makeChangeHandler = (item: string) => {
return (ev: any, checked: boolean) => this._onCheckboxChange(ev, checked, item);
}
private _onCheckboxChange = (ev: any, isChecked: boolean, item: string) => {
let finalSel: string[] = this.state.selChoices;
if (finalSel.length > 0) {
if (isChecked) {
finalSel.push(item);
} else finalSel = _.filter(finalSel, (o) => { return o !== item; });
} else {
if (isChecked) finalSel.push(item);
private onRenderChoiceOptions(): IChoiceGroupOption[] {
let choices: IChoiceGroupOption[] = [];
let tempChoices: string[] = this.getOptions();
if (tempChoices.length > 0) {
tempChoices.map((choice: string) => {
choices.push({
key: choice.trim(),
text: choice.trim()
});
});
} else {
choices.push({
key: '0',
text: "Sorry, no choices found",
disabled: true,
});
}
return choices;
}
private _getSelectedKey = (): string => {
return this.props.selectedKey();
}
private _onChange = (ev: React.FormEvent<HTMLInputElement>, option: any): void => {
this.props.onChange(ev, option, false);
}
private _makeChangeHandler = (item: string) => {
return (ev: any, checked: boolean) => this._onCheckboxChange(ev, checked, item);
}
private _onCheckboxChange = (ev: any, isChecked: boolean, item: string) => {
let finalSel: string[] = this.state.selChoices;
if (finalSel.length > 0) {
if (isChecked) {
finalSel.push(item);
} else finalSel = _.filter(finalSel, (o) => { return o !== item; });
} else {
if (isChecked) finalSel.push(item);
}
this.setState({ selChoices: finalSel });
this.props.onChange(ev, { key: finalSel }, true);
}
this.setState({ selChoices: finalSel });
this.props.onChange(ev, { key: finalSel }, true);
}
}

View File

@ -29,7 +29,8 @@
]
},
"include": [
"src/**/*.ts"
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",

View File

@ -0,0 +1,344 @@
# Upgrade project react-simple-poll to v1.11.0
Date: 10/16/2020
## Findings
Following is the list of steps required to upgrade your project to SharePoint Framework version 1.11.0. [Summary](#Summary) of the modifications is included at the end of the report.
### FN001001 @microsoft/sp-core-library | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-core-library
Execute the following command:
```sh
npm i -SE @microsoft/sp-core-library@1.11.0
```
File: [./package.json](./package.json)
### FN001002 @microsoft/sp-lodash-subset | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-lodash-subset
Execute the following command:
```sh
npm i -SE @microsoft/sp-lodash-subset@1.11.0
```
File: [./package.json](./package.json)
### FN001003 @microsoft/sp-office-ui-fabric-core | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-office-ui-fabric-core
Execute the following command:
```sh
npm i -SE @microsoft/sp-office-ui-fabric-core@1.11.0
```
File: [./package.json](./package.json)
### FN001004 @microsoft/sp-webpart-base | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-webpart-base
Execute the following command:
```sh
npm i -SE @microsoft/sp-webpart-base@1.11.0
```
File: [./package.json](./package.json)
### FN001005 @types/react | Required
Remove SharePoint Framework dependency package @types/react
Execute the following command:
```sh
npm un -S @types/react
```
File: [./package.json](./package.json)
### FN001006 @types/react-dom | Required
Remove SharePoint Framework dependency package @types/react-dom
Execute the following command:
```sh
npm un -S @types/react-dom
```
File: [./package.json](./package.json)
### FN001007 @types/webpack-env | Required
Remove SharePoint Framework dependency package @types/webpack-env
Execute the following command:
```sh
npm un -S @types/webpack-env
```
File: [./package.json](./package.json)
### FN001010 @types/es6-promise | Required
Remove SharePoint Framework dependency package @types/es6-promise
Execute the following command:
```sh
npm un -S @types/es6-promise
```
File: [./package.json](./package.json)
### FN001021 @microsoft/sp-property-pane | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-property-pane
Execute the following command:
```sh
npm i -SE @microsoft/sp-property-pane@1.11.0
```
File: [./package.json](./package.json)
### FN001022 office-ui-fabric-react | Required
Upgrade SharePoint Framework dependency package office-ui-fabric-react
Execute the following command:
```sh
npm i -SE office-ui-fabric-react@6.214.0
```
File: [./package.json](./package.json)
### FN002001 @microsoft/sp-build-web | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-build-web
Execute the following command:
```sh
npm i -DE @microsoft/sp-build-web@1.11.0
```
File: [./package.json](./package.json)
### FN002002 @microsoft/sp-module-interfaces | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-module-interfaces
Execute the following command:
```sh
npm i -DE @microsoft/sp-module-interfaces@1.11.0
```
File: [./package.json](./package.json)
### FN002003 @microsoft/sp-webpart-workbench | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-webpart-workbench
Execute the following command:
```sh
npm i -DE @microsoft/sp-webpart-workbench@1.11.0
```
File: [./package.json](./package.json)
### FN002009 @microsoft/sp-tslint-rules | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-tslint-rules
Execute the following command:
```sh
npm i -DE @microsoft/sp-tslint-rules@1.11.0
```
File: [./package.json](./package.json)
### FN002013 @types/webpack-env | Required
Install SharePoint Framework dev dependency package @types/webpack-env
Execute the following command:
```sh
npm i -DE @types/webpack-env@1.13.1
```
File: [./package.json](./package.json)
### FN002014 @types/es6-promise | Required
Install SharePoint Framework dev dependency package @types/es6-promise
Execute the following command:
```sh
npm i -DE @types/es6-promise@0.0.33
```
File: [./package.json](./package.json)
### FN002015 @types/react | Required
Install SharePoint Framework dev dependency package @types/react
Execute the following command:
```sh
npm i -DE @types/react@16.8.8
```
File: [./package.json](./package.json)
### FN002016 @types/react-dom | Required
Install SharePoint Framework dev dependency package @types/react-dom
Execute the following command:
```sh
npm i -DE @types/react-dom@16.8.3
```
File: [./package.json](./package.json)
### FN006004 package-solution.json developer | Optional
In package-solution.json add developer section
In file [./config/package-solution.json](./config/package-solution.json) update the code as follows:
```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"
}
}
}
```
File: [./config/package-solution.json](./config/package-solution.json)
### FN010001 .yo-rc.json version | Recommended
Update version in .yo-rc.json
In file [./.yo-rc.json](./.yo-rc.json) update the code as follows:
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0"
}
}
```
File: [./.yo-rc.json](./.yo-rc.json)
### FN012012 tsconfig.json include property | Required
Update tsconfig.json include property
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"include": [
"src/**/*.tsx"
]
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN017001 Run npm dedupe | Optional
If, after upgrading npm packages, when building the project you have errors similar to: "error TS2345: Argument of type 'SPHttpClientConfiguration' is not assignable to parameter of type 'SPHttpClientConfiguration'", try running 'npm dedupe' to cleanup npm packages.
Execute the following command:
```sh
npm dedupe
```
File: [./package.json](./package.json)
## Summary
### Execute script
```sh
npm i -SE @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-webpart-base@1.11.0 @microsoft/sp-property-pane@1.11.0 office-ui-fabric-react@6.214.0
npm i -DE @microsoft/sp-build-web@1.11.0 @microsoft/sp-module-interfaces@1.11.0 @microsoft/sp-webpart-workbench@1.11.0 @microsoft/sp-tslint-rules@1.11.0 @types/webpack-env@1.13.1 @types/es6-promise@0.0.33 @types/react@16.8.8 @types/react-dom@16.8.3
npm un -S @types/react @types/react-dom @types/webpack-env @types/es6-promise
npm dedupe
```
### Modify files
#### [./config/package-solution.json](./config/package-solution.json)
In package-solution.json add developer section:
```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"
}
}
}
```
#### [./.yo-rc.json](./.yo-rc.json)
Update version in .yo-rc.json:
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0"
}
}
```
#### [./tsconfig.json](./tsconfig.json)
Update tsconfig.json include property:
```json
{
"include": [
"src/**/*.tsx"
]
}
```

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

32
samples/react-tailwindcss/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0",
"libraryName": "react-tailwindcss",
"libraryId": "62daa1f6-c9b4-4664-b101-37ccbddb47da",
"environment": "spo",
"packageManager": "npm",
"isCreatingSolution": true,
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,81 @@
---
page_type: sample
products:
- office-sp
languages:
- javascript
- typescript
extensions:
contentType: samples
technologies:
- SharePoint Framework
- TailWind CSS
platforms:
- React
createdDate: 10/17/2020 12:00:00 AM
---
# react-tailwindcss
## Summary
This project shows how to integrate [Tailwind CSS](https://tailwindcss.com/) framework into a SPFx React project by:
- Create a custom Tailwind CSS config file
- Create a custom Gulp Task to process the Tailwind CSS generated file and optimize it
- Using CSS custom properties to manage in Tailwind CSS Classes the Theme Variant into sections
- Avoid writing SASS but only use Tailwind CSS classes to style components
![WebPart](./assets/react-tailwindcss-overview.gif)
## Used SharePoint Framework Version
![SPFx 1.11](https://img.shields.io/badge/version-1.11-green.svg)
## Applies to
- [SharePoint Framework](https://aka.ms/spfx)
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
## Prerequisites
- Basic knowledge of [Tailwind CSS](https://tailwindcss.com/)
- Basic knowledge of [GulpJs](https://gulpjs.com/)
## Solution
Solution|Author(s)
--------|---------
react-tailwindcss | Fabio Franzini, [@franzinifabio](https://twitter.com/franzinifabio)
## Version history
Version|Date|Comments
-------|----|--------
1.0|October 17, 2020|Initial release
## 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.**
---
## Minimal Path to Awesome
- Clone this repository
- Ensure that you are at the solution folder
- in the command-line run:
- **npm install**
- **gulp serve**
## References
- [Getting started with Tailwind CSS](https://tailwindcss.com/)
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-tailwindcss" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"hello-tailwind-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/HelloTailwindCss/HelloTailwindCssWebPart.js",
"manifest": "./src/webparts/HelloTailwindCss/HelloTailwindCssWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"HelloTailwindCssWebPartStrings": "lib/webparts/HelloTailwindCss/loc/{locale}.js"
}
}

View File

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

View File

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

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-tailwindcss-client-side-solution",
"id": "62daa1f6-c9b4-4664-b101-37ccbddb47da",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/react-tailwindcss.sppkg"
}
}

View File

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

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

24
samples/react-tailwindcss/gulpfile.js vendored Normal file
View File

@ -0,0 +1,24 @@
'use strict';
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
// Step 1 + './src/tailwind.css' + './tailwind.config.js'
let tailwindBuild = build.subTask('build-tailwind', (gulp, buildOptions, done) => {
const postcss = require('gulp-postcss');
gulp.src(`${buildOptions.srcFolder}/tailwind.css`)
.pipe(postcss([
require('tailwindcss')('./tailwind.config.js'),
require('gulp-autoprefixer')
]))
.pipe(gulp.dest(buildOptions.libFolder));
done();
});
build.rig.addPostBuildTask(tailwindBuild);
// End Step 1
build.initialize(require('gulp'));

18661
samples/react-tailwindcss/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
{
"name": "react-tailwindcss",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "npm run tailwind:css && gulp bundle",
"clean": "gulp clean",
"test": "gulp test",
"build:tailwind": "postcss tailwind.source.css -o src/tailwind.css"
},
"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.11.0",
"@microsoft/sp-webpart-base": "1.11.0",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"devDependencies": {
"@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.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"ajv": "~5.2.2",
"gulp": "~3.9.1",
"gulp-autoprefixer": "^7.0.1",
"gulp-postcss": "^8.0.0",
"gulp-sass": "^4.1.0",
"tailwindcss": "^1.8.10"
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,136 @@
@import "tailwindcss/components";
@import "tailwindcss/utilities";
:root {
--tw-fui-themeDarker: #004578;
--tw-fui-themeDark: #005a9e;
--tw-fui-themeDarkAlt: #106ebe;
--tw-fui-themePrimary: #0078d4;
--tw-fui-themeSecondary: #2b88d8;
--tw-fui-themeTertiary: #71afe5;
--tw-fui-themeLight: #c7e0f4;
--tw-fui-themeLighter: #deecf9;
--tw-fui-themeLighterAlt: #eff6fc;
--tw-fui-black: #000000;
--tw-fui-blackTranslucent40: rgba(0, 0, 0, .4);
--tw-fui-neutralDark: #212121;
--tw-fui-neutralPrimary: #333333;
--tw-fui-neutralPrimaryAlt: #3c3c3c;
--tw-fui-neutralSecondary: #666666;
--tw-fui-neutralSecondaryAlt: #767676;
--tw-fui-neutralTertiary: #a6a6a6;
--tw-fui-neutralTertiaryAlt: #c8c8c8;
--tw-fui-neutralQuaternary: #d0d0d0;
--tw-fui-neutralQuaternaryAlt: #dadada;
--tw-fui-neutralLight: #eaeaea;
--tw-fui-neutralLighter: #f4f4f4;
--tw-fui-neutralLighterAlt: #f8f8f8;
--tw-fui-accent: #0078d4;
--tw-fui-white: #ffffff;
--tw-fui-whiteTranslucent40: rgba(255, 255, 255, .4);
--tw-fui-yellow: #ffb900;
--tw-fui-yellowLight: #fff100;
--tw-fui-orange: #d83b01;
--tw-fui-orangeLight: #ea4300;
--tw-fui-orangeLighter: #ff8c00;
--tw-fui-redDark: #a80000;
--tw-fui-red: #e81123;
--tw-fui-magentaDark: #5c005c;
--tw-fui-magenta: #b4009e;
--tw-fui-magentaLight: #e3008c;
--tw-fui-purpleDark: #32145a;
--tw-fui-purple: #5c2d91;
--tw-fui-purpleLight: #b4a0ff;
--tw-fui-blueDark: #002050;
--tw-fui-blueMid: #00188f;
--tw-fui-blue: #0078d4;
--tw-fui-blueLight: #00bcf2;
--tw-fui-tealDark: #004b50;
--tw-fui-teal: #008272;
--tw-fui-tealLight: #00b294;
--tw-fui-greenDark: #004b1c;
--tw-fui-green: #107c10;
--tw-fui-greenLight: #bad80a;
/* ***** */
--tw-fui-bodyBackground: #ffffff;
--tw-fui-bodyStandoutBackground: #f8f8f8;
--tw-fui-bodyFrameBackground: #ffffff;
--tw-fui-bodyFrameDivider: #eaeaea;
--tw-fui-bodyText: #333333;
--tw-fui-bodyTextChecked: #000000;
--tw-fui-bodySubtext: #666666;
--tw-fui-bodyDivider: #eaeaea;
--tw-fui-disabledBackground: #f4f4f4;
--tw-fui-disabledText: #a6a6a6;
--tw-fui-disabledSubtext: #d0d0d0;
--tw-fui-disabledBodyText: #a6a6a6;
--tw-fui-disabledBodySubtext: #c8c8c8;
--tw-fui-focusBorder: #666666;
--tw-fui-variantBorder: #eaeaea;
--tw-fui-variantBorderHovered: #a6a6a6;
--tw-fui-defaultStateBackground: #f8f8f8;
--tw-fui-errorText: #a80000;
--tw-fui-warningText: #333333;
--tw-fui-errorBackground: rgba(232, 17, 35, .2);
--tw-fui-blockingBackground: rgba(234, 67, 0, .2);
--tw-fui-warningBackground: rgba(255, 185, 0, .2);
--tw-fui-warningHighlight: #ffb900;
--tw-fui-successBackground: rgba(186, 216, 10, .2);
--tw-fui-inputBorder: #a6a6a6;
--tw-fui-inputBorderHovered: #333333;
--tw-fui-inputBackground: #ffffff;
--tw-fui-inputBackgroundChecked: #0078d4;
--tw-fui-inputBackgroundCheckedHovered: #106ebe;
--tw-fui-inputForegroundChecked: #ffffff;
--tw-fui-inputFocusBorderAlt: #0078d4;
--tw-fui-smallInputBorder: #666666;
--tw-fui-inputText: #333333;
--tw-fui-inputTextHovered: #212121;
--tw-fui-inputPlaceholderText: #666666;
--tw-fui-buttonBackground: #f4f4f4;
--tw-fui-buttonBackgroundChecked: #c8c8c8;
--tw-fui-buttonBackgroundHovered: #eaeaea;
--tw-fui-buttonBackgroundCheckedHovered: #eaeaea;
--tw-fui-buttonBackgroundPressed: #eaeaea;
--tw-fui-buttonBackgroundDisabled: #f4f4f4;
--tw-fui-buttonBorder: transparent;
--tw-fui-buttonText: #333333;
--tw-fui-buttonTextHovered: #212121;
--tw-fui-buttonTextChecked: #212121;
--tw-fui-buttonTextCheckedHovered: #000000;
--tw-fui-buttonTextPressed: #212121;
--tw-fui-buttonTextDisabled: #a6a6a6;
--tw-fui-buttonBorderDisabled: transparent;
--tw-fui-primaryButtonBackground: #0078d4;
--tw-fui-primaryButtonBackgroundHovered: #106ebe;
--tw-fui-primaryButtonBackgroundPressed: #005a9e;
--tw-fui-primaryButtonBackgroundDisabled: #f4f4f4;
--tw-fui-primaryButtonBorder: transparent;
--tw-fui-primaryButtonText: #ffffff;
--tw-fui-primaryButtonTextHovered: #ffffff;
--tw-fui-primaryButtonTextPressed: #ffffff;
--tw-fui-primaryButtonTextDisabled: #d0d0d0;
--tw-fui-accentButtonBackground: #0078d4;
--tw-fui-accentButtonText: #ffffff;
--tw-fui-menuBackground: #ffffff;
--tw-fui-menuDivider: #c8c8c8;
--tw-fui-menuIcon: #0078d4;
--tw-fui-menuHeader: #0078d4;
--tw-fui-menuItemBackgroundHovered: #f4f4f4;
--tw-fui-menuItemBackgroundPressed: #eaeaea;
--tw-fui-menuItemText: #333333;
--tw-fui-menuItemTextHovered: #212121;
--tw-fui-listBackground: #ffffff;
--tw-fui-listText: #333333;
--tw-fui-listItemBackgroundHovered: #f4f4f4;
--tw-fui-listItemBackgroundChecked: #eaeaea;
--tw-fui-listItemBackgroundCheckedHovered: #dadada;
--tw-fui-listHeaderBackgroundHovered: #f4f4f4;
--tw-fui-listHeaderBackgroundPressed: #eaeaea;
--tw-fui-actionLink: #333333;
--tw-fui-actionLinkHovered: #212121;
--tw-fui-link: #0078d4;
--tw-fui-linkHovered: #004578;
--tw-fui-listTextColor: #333333;
--tw-fui-menuItemBackgroundChecked: #eaeaea;
}

View File

@ -0,0 +1,27 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "acebbfec-5fc8-42b0-94e5-cfbc43222cae",
"alias": "HelloTailwindCssWebPart",
"componentType": "WebPart",
"supportsThemeVariants": true,
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Hello TailwindCss" },
"description": { "default": "Hello TailwindCss description" },
"officeFabricIconFontName": "Duststorm",
"properties": {
"description": "Hello TailwindCss"
}
}]
}

View File

@ -0,0 +1,223 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'HelloTailwindCssWebPartStrings';
import HelloTailwindCss from './components/HelloTailwindCss';
import { IHelloTailwindCssProps } from './components/IHelloTailwindCssProps';
import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme } from '@microsoft/sp-component-base';
export interface IHelloTailwindCssWebPartProps {
description: string;
}
export default class HelloTailwindCssWebPart extends BaseClientSideWebPart<IHelloTailwindCssWebPartProps> {
private _themeProvider: ThemeProvider;
private _themeVariant: IReadonlyTheme | undefined;
protected onInit(): Promise<void> {
// Consume the new ThemeProvider service
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);
// If it exists, get the theme variant
this._themeVariant = this._themeProvider.tryGetTheme();
// Register a handler to be notified if the theme variant changes
this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent);
// Apply theme variant as CSS Variables at current DOM node
this._applyThemeVariant();
return super.onInit();
}
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme;
this._applyThemeVariant();
this.render();
}
private _applyThemeVariant() {
let style = this.domElement.style;
style.setProperty('--tw-fui-themeDarker', this._themeVariant.palette.themeDarker);
style.setProperty('--tw-fui-themeDark', this._themeVariant.palette.themeDark);
style.setProperty('--tw-fui-themeDarkAlt', this._themeVariant.palette.themeDarkAlt);
style.setProperty('--tw-fui-themePrimary', this._themeVariant.palette.themePrimary);
style.setProperty('--tw-fui-themeSecondary', this._themeVariant.palette.themeSecondary);
style.setProperty('--tw-fui-themeTertiary', this._themeVariant.palette.themeTertiary);
style.setProperty('--tw-fui-themeLight', this._themeVariant.palette.themeLight);
style.setProperty('--tw-fui-themeLighter', this._themeVariant.palette.themeLighter);
style.setProperty('--tw-fui-themeLighterAlt', this._themeVariant.palette.themeLighterAlt);
style.setProperty('--tw-fui-black', this._themeVariant.palette.black);
style.setProperty('--tw-fui-blackTranslucent40', this._themeVariant.palette.blackTranslucent40);
style.setProperty('--tw-fui-neutralDark', this._themeVariant.palette.neutralDark);
style.setProperty('--tw-fui-neutralPrimary', this._themeVariant.palette.neutralPrimary);
style.setProperty('--tw-fui-neutralPrimaryAlt', this._themeVariant.palette.neutralPrimaryAlt);
style.setProperty('--tw-fui-neutralSecondary', this._themeVariant.palette.neutralSecondary);
style.setProperty('--tw-fui-neutralSecondaryAlt', this._themeVariant.palette.neutralSecondaryAlt);
style.setProperty('--tw-fui-neutralTertiary', this._themeVariant.palette.neutralTertiary);
style.setProperty('--tw-fui-neutralTertiaryAlt', this._themeVariant.palette.neutralTertiaryAlt);
style.setProperty('--tw-fui-neutralQuaternary', this._themeVariant.palette.neutralQuaternary);
style.setProperty('--tw-fui-neutralQuaternaryAlt', this._themeVariant.palette.neutralQuaternaryAlt);
style.setProperty('--tw-fui-neutralLight', this._themeVariant.palette.neutralLight);
style.setProperty('--tw-fui-neutralLighter', this._themeVariant.palette.neutralLighter);
style.setProperty('--tw-fui-neutralLighterAlt', this._themeVariant.palette.neutralLighterAlt);
style.setProperty('--tw-fui-accent', this._themeVariant.palette.accent);
style.setProperty('--tw-fui-white', this._themeVariant.palette.white);
style.setProperty('--tw-fui-whiteTranslucent40', this._themeVariant.palette.whiteTranslucent40);
style.setProperty('--tw-fui-yellow', this._themeVariant.palette.yellow);
style.setProperty('--tw-fui-yellowLight', this._themeVariant.palette.yellowLight);
style.setProperty('--tw-fui-orange', this._themeVariant.palette.orange);
style.setProperty('--tw-fui-orangeLight', this._themeVariant.palette.orangeLight);
style.setProperty('--tw-fui-orangeLighter', this._themeVariant.palette.orangeLighter);
style.setProperty('--tw-fui-redDark', this._themeVariant.palette.redDark);
style.setProperty('--tw-fui-red', this._themeVariant.palette.red);
style.setProperty('--tw-fui-magentaDark', this._themeVariant.palette.magentaDark);
style.setProperty('--tw-fui-magenta', this._themeVariant.palette.magenta);
style.setProperty('--tw-fui-magentaLight', this._themeVariant.palette.magentaLight);
style.setProperty('--tw-fui-purpleDark', this._themeVariant.palette.purpleDark);
style.setProperty('--tw-fui-purple', this._themeVariant.palette.purple);
style.setProperty('--tw-fui-purpleLight', this._themeVariant.palette.purpleLight);
style.setProperty('--tw-fui-blueDark', this._themeVariant.palette.blueDark);
style.setProperty('--tw-fui-blueMid', this._themeVariant.palette.blueMid);
style.setProperty('--tw-fui-blue', this._themeVariant.palette.blue);
style.setProperty('--tw-fui-blueLight', this._themeVariant.palette.blueLight);
style.setProperty('--tw-fui-tealDark', this._themeVariant.palette.tealDark);
style.setProperty('--tw-fui-teal', this._themeVariant.palette.teal);
style.setProperty('--tw-fui-tealLight', this._themeVariant.palette.tealLight);
style.setProperty('--tw-fui-greenDark', this._themeVariant.palette.greenDark);
style.setProperty('--tw-fui-green', this._themeVariant.palette.green);
style.setProperty('--tw-fui-greenLight', this._themeVariant.palette.greenLight);
style.setProperty('--tw-fui-bodyBackground', this._themeVariant.semanticColors.bodyBackground);
style.setProperty('--tw-fui-bodyStandoutBackground', this._themeVariant.semanticColors.bodyStandoutBackground);
style.setProperty('--tw-fui-bodyFrameBackground', this._themeVariant.semanticColors.bodyFrameBackground);
style.setProperty('--tw-fui-bodyFrameDivider', this._themeVariant.semanticColors.bodyFrameDivider);
style.setProperty('--tw-fui-bodyText', this._themeVariant.semanticColors.bodyText);
style.setProperty('--tw-fui-bodyTextChecked', this._themeVariant.semanticColors.bodyTextChecked);
style.setProperty('--tw-fui-bodySubtext', this._themeVariant.semanticColors.bodySubtext);
style.setProperty('--tw-fui-bodyDivider', this._themeVariant.semanticColors.bodyDivider);
style.setProperty('--tw-fui-disabledBackground', this._themeVariant.semanticColors.disabledBackground);
style.setProperty('--tw-fui-disabledText', this._themeVariant.semanticColors.disabledText);
style.setProperty('--tw-fui-disabledSubtext', this._themeVariant.semanticColors.disabledSubtext);
style.setProperty('--tw-fui-disabledBodyText', this._themeVariant.semanticColors.disabledBodyText);
style.setProperty('--tw-fui-disabledBodySubtext', this._themeVariant.semanticColors.disabledBodySubtext);
style.setProperty('--tw-fui-focusBorder', this._themeVariant.semanticColors.focusBorder);
style.setProperty('--tw-fui-variantBorder', this._themeVariant.semanticColors.variantBorder);
style.setProperty('--tw-fui-variantBorderHovered', this._themeVariant.semanticColors.variantBorderHovered);
style.setProperty('--tw-fui-defaultStateBackground', this._themeVariant.semanticColors.defaultStateBackground);
style.setProperty('--tw-fui-errorText', this._themeVariant.semanticColors.errorText);
style.setProperty('--tw-fui-warningText', this._themeVariant.semanticColors.warningText);
style.setProperty('--tw-fui-errorBackground', this._themeVariant.semanticColors.errorBackground);
style.setProperty('--tw-fui-blockingBackground', this._themeVariant.semanticColors.blockingBackground);
style.setProperty('--tw-fui-warningBackground', this._themeVariant.semanticColors.warningBackground);
style.setProperty('--tw-fui-warningHighlight', this._themeVariant.semanticColors.warningHighlight);
style.setProperty('--tw-fui-successBackground', this._themeVariant.semanticColors.successBackground);
style.setProperty('--tw-fui-inputBorder', this._themeVariant.semanticColors.inputBorder);
style.setProperty('--tw-fui-inputBorderHovered', this._themeVariant.semanticColors.inputBorderHovered);
style.setProperty('--tw-fui-inputBackground', this._themeVariant.semanticColors.inputBackground);
style.setProperty('--tw-fui-inputBackgroundChecked', this._themeVariant.semanticColors.inputBackgroundChecked);
style.setProperty('--tw-fui-inputBackgroundCheckedHovered', this._themeVariant.semanticColors.inputBackgroundCheckedHovered);
style.setProperty('--tw-fui-inputForegroundChecked', this._themeVariant.semanticColors.inputForegroundChecked);
style.setProperty('--tw-fui-inputFocusBorderAlt', this._themeVariant.semanticColors.inputFocusBorderAlt);
style.setProperty('--tw-fui-smallInputBorder', this._themeVariant.semanticColors.smallInputBorder);
style.setProperty('--tw-fui-inputText', this._themeVariant.semanticColors.inputText);
style.setProperty('--tw-fui-inputTextHovered', this._themeVariant.semanticColors.inputTextHovered);
style.setProperty('--tw-fui-inputPlaceholderText', this._themeVariant.semanticColors.inputPlaceholderText);
style.setProperty('--tw-fui-buttonBackground', this._themeVariant.semanticColors.buttonBackground);
style.setProperty('--tw-fui-buttonBackgroundChecked', this._themeVariant.semanticColors.buttonBackgroundChecked);
style.setProperty('--tw-fui-buttonBackgroundHovered', this._themeVariant.semanticColors.buttonBackgroundHovered);
style.setProperty('--tw-fui-buttonBackgroundCheckedHovered', this._themeVariant.semanticColors.buttonBackgroundCheckedHovered);
style.setProperty('--tw-fui-buttonBackgroundPressed', this._themeVariant.semanticColors.buttonBackgroundPressed);
style.setProperty('--tw-fui-buttonBackgroundDisabled', this._themeVariant.semanticColors.buttonBackgroundDisabled);
style.setProperty('--tw-fui-buttonBorder', this._themeVariant.semanticColors.buttonBorder);
style.setProperty('--tw-fui-buttonText', this._themeVariant.semanticColors.buttonText);
style.setProperty('--tw-fui-buttonTextHovered', this._themeVariant.semanticColors.buttonTextHovered);
style.setProperty('--tw-fui-buttonTextChecked', this._themeVariant.semanticColors.buttonTextChecked);
style.setProperty('--tw-fui-buttonTextCheckedHovered', this._themeVariant.semanticColors.buttonTextCheckedHovered);
style.setProperty('--tw-fui-buttonTextPressed', this._themeVariant.semanticColors.buttonTextPressed);
style.setProperty('--tw-fui-buttonTextDisabled', this._themeVariant.semanticColors.buttonTextDisabled);
style.setProperty('--tw-fui-buttonBorderDisabled', this._themeVariant.semanticColors.buttonBorderDisabled);
style.setProperty('--tw-fui-primaryButtonBackground', this._themeVariant.semanticColors.primaryButtonBackground);
style.setProperty('--tw-fui-primaryButtonBackgroundHovered', this._themeVariant.semanticColors.primaryButtonBackgroundHovered);
style.setProperty('--tw-fui-primaryButtonBackgroundPressed', this._themeVariant.semanticColors.primaryButtonBackgroundPressed);
style.setProperty('--tw-fui-primaryButtonBackgroundDisabled', this._themeVariant.semanticColors.primaryButtonBackgroundDisabled);
style.setProperty('--tw-fui-primaryButtonBorder', this._themeVariant.semanticColors.primaryButtonBorder);
style.setProperty('--tw-fui-primaryButtonText', this._themeVariant.semanticColors.primaryButtonText);
style.setProperty('--tw-fui-primaryButtonTextHovered', this._themeVariant.semanticColors.primaryButtonTextHovered);
style.setProperty('--tw-fui-primaryButtonTextPressed', this._themeVariant.semanticColors.primaryButtonTextPressed);
style.setProperty('--tw-fui-primaryButtonTextDisabled', this._themeVariant.semanticColors.primaryButtonTextDisabled);
style.setProperty('--tw-fui-accentButtonBackground', this._themeVariant.semanticColors.accentButtonBackground);
style.setProperty('--tw-fui-accentButtonText', this._themeVariant.semanticColors.accentButtonText);
style.setProperty('--tw-fui-menuBackground', this._themeVariant.semanticColors.menuBackground);
style.setProperty('--tw-fui-menuDivider', this._themeVariant.semanticColors.menuDivider);
style.setProperty('--tw-fui-menuIcon', this._themeVariant.semanticColors.menuIcon);
style.setProperty('--tw-fui-menuHeader', this._themeVariant.semanticColors.menuHeader);
style.setProperty('--tw-fui-menuItemBackgroundHovered', this._themeVariant.semanticColors.menuItemBackgroundHovered);
style.setProperty('--tw-fui-menuItemBackgroundPressed', this._themeVariant.semanticColors.menuItemBackgroundPressed);
style.setProperty('--tw-fui-menuItemText', this._themeVariant.semanticColors.menuItemText);
style.setProperty('--tw-fui-menuItemTextHovered', this._themeVariant.semanticColors.menuItemTextHovered);
style.setProperty('--tw-fui-listBackground', this._themeVariant.semanticColors.listBackground);
style.setProperty('--tw-fui-listText', this._themeVariant.semanticColors.listText);
style.setProperty('--tw-fui-listItemBackgroundHovered', this._themeVariant.semanticColors.listItemBackgroundHovered);
style.setProperty('--tw-fui-listItemBackgroundChecked', this._themeVariant.semanticColors.listItemBackgroundChecked);
style.setProperty('--tw-fui-listItemBackgroundCheckedHovered', this._themeVariant.semanticColors.listItemBackgroundCheckedHovered);
style.setProperty('--tw-fui-listHeaderBackgroundHovered', this._themeVariant.semanticColors.listHeaderBackgroundHovered);
style.setProperty('--tw-fui-listHeaderBackgroundPressed', this._themeVariant.semanticColors.listHeaderBackgroundPressed);
style.setProperty('--tw-fui-actionLink', this._themeVariant.semanticColors.actionLink);
style.setProperty('--tw-fui-actionLinkHovered', this._themeVariant.semanticColors.actionLinkHovered);
style.setProperty('--tw-fui-link', this._themeVariant.semanticColors.link);
style.setProperty('--tw-fui-linkHovered', this._themeVariant.semanticColors.linkHovered);
style.setProperty('--tw-fui-listTextColor', this._themeVariant.semanticColors.listTextColor);
style.setProperty('--tw-fui-menuItemBackgroundChecked', this._themeVariant.semanticColors.menuItemBackgroundChecked);
}
public render(): void {
const element: React.ReactElement<IHelloTailwindCssProps> = React.createElement(
HelloTailwindCss,
{
description: this.properties.description
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,26 @@
import * as React from 'react';
import { escape } from '@microsoft/sp-lodash-subset';
import './../../../tailwind.css';
import User, { IUserProps } from './User';
export interface IDocumentCardProps {
title: string;
documentImageUrl: string;
lastEditUserInfo: IUserProps;
}
export default class DocumentCard extends React.Component<IDocumentCardProps, {}> {
public render(): React.ReactElement<IDocumentCardProps> {
return (
<div className="max-w-sm overflow-hidden bg-white text-black border border-solid border-gray-400 hover:outline-none hover:shadow-lg hover:cursor-pointer">
<img className="w-full border-0 border-solid border-b border-gray-400" src={this.props.documentImageUrl} alt="Document Preview" />
<div className="px-4 py-3 pt-1">
<div className="text-lg mb-2">{this.props.title}</div>
<User profileImageUrl={this.props.lastEditUserInfo.profileImageUrl}
line1={this.props.lastEditUserInfo.line1}
line2={this.props.lastEditUserInfo.line1} />
</div>
</div>
);
}
}

View File

@ -0,0 +1,32 @@
import * as React from 'react';
import { IHelloTailwindCssProps } from './IHelloTailwindCssProps';
import { escape } from '@microsoft/sp-lodash-subset';
import './../../../tailwind.css';
import DocumentCard from './DocumentCard';
import HorizontalCard from './HorizontalCard';
import { Stack } from 'office-ui-fabric-react';
export default class HelloTailwindCss extends React.Component<IHelloTailwindCssProps, {}> {
public render(): React.ReactElement<IHelloTailwindCssProps> {
return (
<Stack gap="20">
<DocumentCard title="Revenue stream proposal fiscal year 2016 version02.pptx"
documentImageUrl="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/document-preview.png"
lastEditUserInfo={{
profileImageUrl: "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/persona-female.png",
line1: "Annie Lindqvist",
line2: "Created a few minutes ago"
}}
/>
<HorizontalCard title="Can coffee make you a better developer?"
description="Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus quia, nulla! Maiores et perferendis eaque, exercitationem praesentium nihil."
documentImageUrl="//tailwindcss.com/img/card-left.jpg"
lastEditUserInfo={{
profileImageUrl: "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/persona-female.png",
line1: "Annie Lindqvist",
line2: "Aug 18"
}}
/></Stack>
);
}
}

View File

@ -0,0 +1,33 @@
import * as React from 'react';
import { escape } from '@microsoft/sp-lodash-subset';
import './../../../tailwind.css';
import User, { IUserProps } from './User';
export interface IHorizontalCardProps {
title: string;
description: string;
documentImageUrl: string;
lastEditUserInfo: IUserProps;
}
const rootClassName = "sp-col-1/3:max-w-sm sp-col-1:max-w-full sp-col-1:flex overflow-hidden shadow hover:outline-none hover:shadow-lg hover:cursor-pointer";
export default class HorizontalCard extends React.Component<IHorizontalCardProps, {}> {
public render(): React.ReactElement<IHorizontalCardProps> {
return (
<div className={rootClassName}>
<div className="h-48 sp-col-1:h-auto sp-col-1:w-48 flex-none bg-cover text-center overflow-hidden" style={{ backgroundImage: "url('//tailwindcss.com/img/card-left.jpg')" }} title="Woman holding a mug">
</div>
<div className="border-r border-b border-l border-gray-400 sp-col-1:border-l-0 sp-col-1:border-t bg-buttonBackground text-buttonText p-4 flex flex-col justify-between leading-normal">
<div className="mb-8">
<div className="font-bold text-xl mb-2">{this.props.title}</div>
<p className="text-base text-buttonTextDisabled">{this.props.description}</p>
</div>
<User profileImageUrl={this.props.lastEditUserInfo.profileImageUrl}
line1={this.props.lastEditUserInfo.line1}
line2={this.props.lastEditUserInfo.line1} />
</div>
</div>
);
}
}

View File

@ -0,0 +1,3 @@
export interface IHelloTailwindCssProps {
description: string;
}

View File

@ -0,0 +1,23 @@
import * as React from 'react';
import { escape } from '@microsoft/sp-lodash-subset';
import './../../../tailwind.css';
export interface IUserProps {
profileImageUrl: string;
line1: string;
line2: string;
}
export default class User extends React.Component<IUserProps, {}> {
public render(): React.ReactElement<IUserProps> {
return (
<div className="flex mt-4">
<img className="h-8 w-8 rounded-full mr-2" src={this.props.profileImageUrl} />
<div className="text-left">
<div className="font-bold text-xs">{this.props.line1}</div>
<div className="text-xs text-buttonTextDisabled">{this.props.line2}</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface IHelloTailwindCssWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'HelloTailwindCssWebPartStrings' {
const strings: IHelloTailwindCssWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,289 @@
const plugin = require('tailwindcss/plugin');
const customSpSectionVariant = ['sp-col-1', 'sp-col-1/2', 'sp-col-1/3', 'sp-col-2/3'];
module.exports = {
purge: {
enabled: true,
content: ['./src/**/*.tsx'],
options: {
whitelistPatterns: [/^CanvasSection*/]
}
},
theme: {
extend: {
colors: {
themeDarker: 'var(--tw-fui-themeDarker)',
themeDark: 'var(--tw-fui-themeDark)',
themeDarkAlt: 'var(--tw-fui-themeDarkAlt)',
themePrimary: 'var(--tw-fui-themePrimary)',
themeSecondary: 'var(--tw-fui-themeSecondary)',
themeTertiary: 'var(--tw-fui-themeTertiary)',
themeLight: 'var(--tw-fui-themeLight)',
themeLighter: 'var(--tw-fui-themeLighter)',
themeLighterAlt: 'var(--tw-fui-themeLighterAlt)',
//black: 'var(--tw-fui-black)',
blackTranslucent40: 'var(--tw-fui-blackTranslucent40)',
neutralDark: 'var(--tw-fui-neutralDark)',
neutralPrimary: 'var(--tw-fui-neutralPrimary)',
neutralPrimaryAlt: 'var(--tw-fui-neutralPrimaryAlt)',
neutralSecondary: 'var(--tw-fui-neutralSecondary)',
neutralSecondaryAlt: 'var(--tw-fui-neutralSecondaryAlt)',
neutralTertiary: 'var(--tw-fui-neutralTertiary)',
neutralTertiaryAlt: 'var(--tw-fui-neutralTertiaryAlt)',
neutralQuaternary: 'var(--tw-fui-neutralQuaternary)',
neutralQuaternaryAlt: 'var(--tw-fui-neutralQuaternaryAlt)',
neutralLight: 'var(--tw-fui-neutralLight)',
neutralLighter: 'var(--tw-fui-neutralLighter)',
neutralLighterAlt: 'var(--tw-fui-neutralLighterAlt)',
accent: 'var(--tw-fui-accent)',
//white: 'var(--tw-fui-white)',
whiteTranslucent40: 'var(--tw-fui-whiteTranslucent40)',
yellow: 'var(--tw-fui-yellow)',
yellowLight: 'var(--tw-fui-yellowLight)',
orange: 'var(--tw-fui-orange)',
orangeLight: 'var(--tw-fui-orangeLight)',
orangeLighter: 'var(--tw-fui-orangeLighter)',
redDark: 'var(--tw-fui-redDark)',
red: 'var(--tw-fui-red)',
magentaDark: 'var(--tw-fui-magentaDark)',
magenta: 'var(--tw-fui-magenta)',
magentaLight: 'var(--tw-fui-magentaLight)',
purpleDark: 'var(--tw-fui-purpleDark)',
purple: 'var(--tw-fui-purple)',
purpleLight: 'var(--tw-fui-purpleLight)',
blueDark: 'var(--tw-fui-blueDark)',
blueMid: 'var(--tw-fui-blueMid)',
blue: 'var(--tw-fui-blue)',
blueLight: 'var(--tw-fui-blueLight)',
tealDark: 'var(--tw-fui-tealDark)',
teal: 'var(--tw-fui-teal)',
tealLight: 'var(--tw-fui-tealLight)',
greenDark: 'var(--tw-fui-greenDark)',
green: 'var(--tw-fui-green)',
greenLight: 'var(--tw-fui-greenLight)',
/* ***** */
bodyBackground: 'var(--tw-fui-bodyBackground)',
bodyStandoutBackground: 'var(--tw-fui-bodyStandoutBackground)',
bodyFrameBackground: 'var(--tw-fui-bodyFrameBackground)',
bodyFrameDivider: 'var(--tw-fui-bodyFrameDivider)',
bodyText: 'var(--tw-fui-bodyText)',
bodyTextChecked: 'var(--tw-fui-bodyTextChecked)',
bodySubtext: 'var(--tw-fui-bodySubtext)',
bodyDivider: 'var(--tw-fui-bodyDivider)',
disabledBackground: 'var(--tw-fui-disabledBackground)',
disabledText: 'var(--tw-fui-disabledText)',
disabledSubtext: 'var(--tw-fui-disabledSubtext)',
disabledBodyText: 'var(--tw-fui-disabledBodyText)',
disabledBodySubtext: 'var(--tw-fui-disabledBodySubtext)',
focusBorder: 'var(--tw-fui-focusBorder)',
variantBorder: 'var(--tw-fui-variantBorder)',
variantBorderHovered: 'var(--tw-fui-variantBorderHovered)',
defaultStateBackground: 'var(--tw-fui-defaultStateBackground)',
errorText: 'var(--tw-fui-errorText)',
warningText: 'var(--tw-fui-warningText)',
errorBackground: 'var(--tw-fui-errorBackground)',
blockingBackground: 'var(--tw-fui-blockingBackground)',
warningBackground: 'var(--tw-fui-warningBackground)',
warningHighlight: 'var(--tw-fui-warningHighlight)',
successBackground: 'var(--tw-fui-successBackground)',
inputBorder: 'var(--tw-fui-inputBorder)',
inputBorderHovered: 'var(--tw-fui-inputBorderHovered)',
inputBackground: 'var(--tw-fui-inputBackground)',
inputBackgroundChecked: 'var(--tw-fui-inputBackgroundChecked)',
inputBackgroundCheckedHovered: 'var(--tw-fui-inputBackgroundCheckedHovered)',
inputForegroundChecked: 'var(--tw-fui-inputForegroundChecked)',
inputFocusBorderAlt: 'var(--tw-fui-inputFocusBorderAlt)',
smallInputBorder: 'var(--tw-fui-smallInputBorder)',
inputText: 'var(--tw-fui-inputText)',
inputTextHovered: 'var(--tw-fui-inputTextHovered)',
inputPlaceholderText: 'var(--tw-fui-inputPlaceholderText)',
buttonBackground: 'var(--tw-fui-buttonBackground)',
buttonBackgroundChecked: 'var(--tw-fui-buttonBackgroundChecked)',
buttonBackgroundHovered: 'var(--tw-fui-buttonBackgroundHovered)',
buttonBackgroundCheckedHovered: 'var(--tw-fui-buttonBackgroundCheckedHovered)',
buttonBackgroundPressed: 'var(--tw-fui-buttonBackgroundPressed)',
buttonBackgroundDisabled: 'var(--tw-fui-buttonBackgroundDisabled)',
buttonBorder: 'var(--tw-fui-buttonBorder)',
buttonText: 'var(--tw-fui-buttonText)',
buttonTextHovered: 'var(--tw-fui-buttonTextHovered)',
buttonTextChecked: 'var(--tw-fui-buttonTextChecked)',
buttonTextCheckedHovered: 'var(--tw-fui-buttonTextCheckedHovered)',
buttonTextPressed: 'var(--tw-fui-buttonTextPressed)',
buttonTextDisabled: 'var(--tw-fui-buttonTextDisabled)',
buttonBorderDisabled: 'var(--tw-fui-buttonBorderDisabled)',
primaryButtonBackground: 'var(--tw-fui-primaryButtonBackground)',
primaryButtonBackgroundHovered: 'var(--tw-fui-primaryButtonBackgroundHovered)',
primaryButtonBackgroundPressed: 'var(--tw-fui-primaryButtonBackgroundPressed)',
primaryButtonBackgroundDisabled: 'var(--tw-fui-primaryButtonBackgroundDisabled)',
primaryButtonBorder: 'var(--tw-fui-primaryButtonBorder)',
primaryButtonText: 'var(--tw-fui-primaryButtonText)',
primaryButtonTextHovered: 'var(--tw-fui-primaryButtonTextHovered)',
primaryButtonTextPressed: 'var(--tw-fui-primaryButtonTextPressed)',
primaryButtonTextDisabled: 'var(--tw-fui-primaryButtonTextDisabled)',
accentButtonBackground: 'var(--tw-fui-accentButtonBackground)',
accentButtonText: 'var(--tw-fui-accentButtonText)',
menuBackground: 'var(--tw-fui-menuBackground)',
menuDivider: 'var(--tw-fui-menuDivider)',
menuIcon: 'var(--tw-fui-menuIcon)',
menuHeader: 'var(--tw-fui-menuHeader)',
menuItemBackgroundHovered: 'var(--tw-fui-menuItemBackgroundHovered)',
menuItemBackgroundPressed: 'var(--tw-fui-menuItemBackgroundPressed)',
menuItemText: 'var(--tw-fui-menuItemText)',
menuItemTextHovered: 'var(--tw-fui-menuItemTextHovered)',
listBackground: 'var(--tw-fui-listBackground)',
listText: 'var(--tw-fui-listText)',
listItemBackgroundHovered: 'var(--tw-fui-listItemBackgroundHovered)',
listItemBackgroundChecked: 'var(--tw-fui-listItemBackgroundChecked)',
listItemBackgroundCheckedHovered: 'var(--tw-fui-listItemBackgroundCheckedHovered)',
listHeaderBackgroundHovered: 'var(--tw-fui-listHeaderBackgroundHovered)',
listHeaderBackgroundPressed: 'var(--tw-fui-listHeaderBackgroundPressed)',
actionLink: 'var(--tw-fui-actionLink)',
actionLinkHovered: 'var(--tw-fui-actionLinkHovered)',
link: 'var(--tw-fui-link)',
linkHovered: 'var(--tw-fui-linkHovered)',
listTextColor: 'var(--tw-fui-listTextColor)',
menuItemBackgroundChecked: 'var(--tw-fui-menuItemBackgroundChecked)',
}
},
},
variants: {
accessibility: customSpSectionVariant.concat(['hover', 'focus']),
alignContent: customSpSectionVariant.concat(['hover', 'focus']),
alignItems: customSpSectionVariant.concat(['hover', 'focus']),
alignSelf: customSpSectionVariant.concat(['hover', 'focus']),
appearance: customSpSectionVariant.concat(['hover', 'focus']),
backgroundAttachment: customSpSectionVariant.concat(['hover', 'focus']),
backgroundClip: customSpSectionVariant.concat(['hover', 'focus']),
backgroundColor: customSpSectionVariant.concat(['hover', 'focus']),
backgroundImage: customSpSectionVariant.concat(['hover', 'focus']),
gradientColorStops: customSpSectionVariant.concat(['hover', 'focus']),
backgroundOpacity: customSpSectionVariant.concat(['hover', 'focus']),
backgroundPosition: customSpSectionVariant.concat(['hover', 'focus']),
backgroundRepeat: customSpSectionVariant.concat(['hover', 'focus']),
backgroundSize: customSpSectionVariant.concat(['hover', 'focus']),
borderCollapse: customSpSectionVariant.concat(['hover', 'focus']),
borderColor: customSpSectionVariant.concat(['hover', 'focus']),
borderOpacity: customSpSectionVariant.concat(['hover', 'focus']),
borderRadius: customSpSectionVariant.concat(['hover', 'focus']),
borderStyle: customSpSectionVariant.concat(['hover', 'focus']),
borderWidth: customSpSectionVariant.concat(['hover', 'focus']),
boxShadow: customSpSectionVariant.concat(['hover', 'focus']),
boxSizing: customSpSectionVariant.concat(['hover', 'focus']),
container: customSpSectionVariant.concat(['hover', 'focus']),
cursor: customSpSectionVariant.concat(['hover', 'focus']),
display: customSpSectionVariant.concat(['hover', 'focus']),
divideColor: customSpSectionVariant.concat(['hover', 'focus']),
divideOpacity: customSpSectionVariant.concat(['hover', 'focus']),
divideStyle: customSpSectionVariant.concat(['hover', 'focus']),
divideWidth: customSpSectionVariant.concat(['hover', 'focus']),
fill: customSpSectionVariant.concat(['hover', 'focus']),
flex: customSpSectionVariant.concat(['hover', 'focus']),
flexDirection: customSpSectionVariant.concat(['hover', 'focus']),
flexGrow: customSpSectionVariant.concat(['hover', 'focus']),
flexShrink: customSpSectionVariant.concat(['hover', 'focus']),
flexWrap: customSpSectionVariant.concat(['hover', 'focus']),
float: customSpSectionVariant.concat(['hover', 'focus']),
clear: customSpSectionVariant.concat(['hover', 'focus']),
fontFamily: customSpSectionVariant.concat(['hover', 'focus']),
fontSize: customSpSectionVariant.concat(['hover', 'focus']),
fontSmoothing: customSpSectionVariant.concat(['hover', 'focus']),
fontVariantNumeric: customSpSectionVariant.concat(['hover', 'focus']),
fontStyle: customSpSectionVariant.concat(['hover', 'focus']),
fontWeight: customSpSectionVariant.concat(['hover', 'focus']),
height: customSpSectionVariant.concat(['hover', 'focus']),
inset: customSpSectionVariant.concat(['hover', 'focus']),
justifyContent: customSpSectionVariant.concat(['hover', 'focus']),
justifyItems: customSpSectionVariant.concat(['hover', 'focus']),
justifySelf: customSpSectionVariant.concat(['hover', 'focus']),
letterSpacing: customSpSectionVariant.concat(['hover', 'focus']),
lineHeight: customSpSectionVariant.concat(['hover', 'focus']),
listStylePosition: customSpSectionVariant.concat(['hover', 'focus']),
listStyleType: customSpSectionVariant.concat(['hover', 'focus']),
margin: customSpSectionVariant.concat(['hover', 'focus']),
maxHeight: customSpSectionVariant.concat(['hover', 'focus']),
maxWidth: customSpSectionVariant.concat(['hover', 'focus']),
minHeight: customSpSectionVariant.concat(['hover', 'focus']),
minWidth: customSpSectionVariant.concat(['hover', 'focus']),
objectFit: customSpSectionVariant.concat(['hover', 'focus']),
objectPosition: customSpSectionVariant.concat(['hover', 'focus']),
opacity: customSpSectionVariant.concat(['hover', 'focus']),
order: customSpSectionVariant.concat(['hover', 'focus']),
outline: customSpSectionVariant.concat(['hover', 'focus']),
overflow: customSpSectionVariant.concat(['hover', 'focus']),
overscrollBehavior: customSpSectionVariant.concat(['hover', 'focus']),
padding: customSpSectionVariant.concat(['hover', 'focus']),
placeContent: customSpSectionVariant.concat(['hover', 'focus']),
placeItems: customSpSectionVariant.concat(['hover', 'focus']),
placeSelf: customSpSectionVariant.concat(['hover', 'focus']),
placeholderColor: customSpSectionVariant.concat(['focus']),
placeholderOpacity: customSpSectionVariant.concat(['focus']),
pointerEvents: customSpSectionVariant.concat(['hover', 'focus']),
position: customSpSectionVariant.concat(['hover', 'focus']),
resize: customSpSectionVariant.concat(['hover', 'focus']),
space: customSpSectionVariant.concat(['hover', 'focus']),
stroke: customSpSectionVariant.concat(['hover', 'focus']),
strokeWidth: customSpSectionVariant.concat(['hover', 'focus']),
tableLayout: customSpSectionVariant.concat(['hover', 'focus']),
textAlign: customSpSectionVariant.concat(['hover', 'focus']),
textColor: customSpSectionVariant.concat(['hover', 'focus']),
textOpacity: customSpSectionVariant.concat(['hover', 'focus']),
textDecoration: customSpSectionVariant.concat(['hover', 'focus']),
textTransform: customSpSectionVariant.concat(['hover', 'focus']),
userSelect: customSpSectionVariant.concat(['hover', 'focus']),
verticalAlign: customSpSectionVariant.concat(['hover', 'focus']),
visibility: customSpSectionVariant.concat(['hover', 'focus']),
whitespace: customSpSectionVariant.concat(['hover', 'focus']),
width: customSpSectionVariant.concat(['hover', 'focus']),
wordBreak: customSpSectionVariant.concat(['hover', 'focus']),
zIndex: customSpSectionVariant.concat(['hover', 'focus']),
gap: customSpSectionVariant.concat(['hover', 'focus']),
gridAutoFlow: customSpSectionVariant.concat(['hover', 'focus']),
gridTemplateColumns: customSpSectionVariant.concat(['hover', 'focus']),
gridColumn: customSpSectionVariant.concat(['hover', 'focus']),
gridColumnStart: customSpSectionVariant.concat(['hover', 'focus']),
gridColumnEnd: customSpSectionVariant.concat(['hover', 'focus']),
gridTemplateRows: customSpSectionVariant.concat(['hover', 'focus']),
gridRow: customSpSectionVariant.concat(['hover', 'focus']),
gridRowStart: customSpSectionVariant.concat(['hover', 'focus']),
gridRowEnd: customSpSectionVariant.concat(['hover', 'focus']),
transform: customSpSectionVariant.concat(['hover', 'focus']),
transformOrigin: customSpSectionVariant.concat(['hover', 'focus']),
scale: customSpSectionVariant.concat(['hover', 'focus']),
rotate: customSpSectionVariant.concat(['hover', 'focus']),
translate: customSpSectionVariant.concat(['hover', 'focus']),
skew: customSpSectionVariant.concat(['hover', 'focus']),
transitionProperty: customSpSectionVariant.concat(['hover', 'focus']),
transitionTimingFunction: customSpSectionVariant.concat(['hover', 'focus']),
transitionDuration: customSpSectionVariant.concat(['hover', 'focus']),
transitionDelay: customSpSectionVariant.concat(['hover', 'focus']),
animation: customSpSectionVariant
},
plugins: [
plugin(function ({ addVariant, e }) {
addVariant('sp-col-1', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.CanvasSection.CanvasSection-col.CanvasSection-xl12 .${e(`sp-col-1${separator}${className}`)}`
})
}),
addVariant('sp-col-1/2', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.CanvasSection.CanvasSection-col.CanvasSection-xl6 .${e(`sp-col-1/2${separator}${className}`)}`
})
}),
addVariant('sp-col-1/3', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.CanvasSection.CanvasSection-col.CanvasSection-xl4 .${e(`sp-col-1/3${separator}${className}`)}`
})
}),
addVariant('sp-col-2/3', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.CanvasSection.CanvasSection-col.CanvasSection-xl8 .${e(`sp-col-2/3${separator}${className}`)}`
})
})
})
],
future: {
removeDeprecatedGapUtilities: true,
purgeLayersByDefault: true
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

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

View File

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

View File

@ -16,7 +16,7 @@
"environment": "spo",
"framework": "react",
"isCreatingSolution": true,
"version": "1.7.1",
"version": "1.11.0",
"libraryName": "react-tenant-properties",
"libraryId": "d5bc38a6-0b5c-4644-9087-efa6de87ece1",
"packageManager": "npm",

View File

@ -1,28 +1,35 @@
# React Tenant Properties Web Part
## Summary
This web part allows tenant administrators to manage tenant properties through a graphical interface.
We can create, edit or delete tenant properties.
Only users with Tenant Admin Role are allowed to managed tenant properties.
#### User without Tenant Admin Role got this message
### User without Tenant Admin Role got this message
![tenant properties](./assets/TenantProperties5.jpg)
#### List tenant properties
### List tenant properties
![tenant properties](./assets/TenantProperties1.jpg)
#### Add Tenant property
### Add Tenant property
![tenant properties](./assets/TenantProperties2.jpg)
#### Edit tenant property
### Edit tenant property
![tenant properties](./assets/TenantProperties3.jpg)
#### Delete tenant property
![tenant properties](./assets/TenantProperties4.jpg)
### Delete tenant property
![tenant properties](./assets/TenantProperties4.jpg)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.7.1-green.svg)
![SPFx 1.11](https://img.shields.io/badge/version-1.11.0-green.svg)
## Applies to
@ -38,6 +45,7 @@ WebPart Title| Text| no|
## Solution
The Web Part Use MSGraph API and need to SharePoint Administrator approve de scope "Directory.ReadWrite.All" in SharePoint Admin Center.
Solution|Author(s)
@ -49,8 +57,10 @@ Tenant Properties WebPart|João Mendes
Version|Date|Comments
-------|----|--------
1.0.0|Mar 08, 2019|Initial release
1.0.1|October 20, 2020|Update to SPFx 1.11.0
## 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.**
---
@ -63,7 +73,7 @@ Version|Date|Comments
- `gulp build`
- `gulp bundle --ship`
- `gulp package-solution --ship`
- `Add to AppCatalog and deploy`
- `Approve API permission on SharePoint Admin Center`
- Add to AppCatalog and deploy
- Approve API permission on SharePoint Admin Center
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-tenant-properties" />

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-tenant-properties-client-side-solution",
"id": "d5bc38a6-0b5c-4644-9087-efa6de87ece1",
"version": "1.0.0.0",
"version": "1.0.1.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"webApiPermissionRequests": [
@ -11,7 +11,14 @@
"resource": "Microsoft Graph",
"scope": "Directory.ReadWrite.All"
}
]
],
"developer": {
"name": "Contoso",
"privacyUrl": "https://contoso.com/privacy",
"termsOfUseUrl": "https://contoso.com/terms-of-use",
"websiteUrl": "https://contoso.com/my-app",
"mpnId": "000000"
}
},
"paths": {
"zippedPackage": "solution/react-tenant-properties.sppkg"

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
"name": "react-tenant-properties",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
@ -15,30 +16,29 @@
},
"dependencies": {
"@microsoft/office-ui-fabric-react-bundle": "^1.7.1",
"@microsoft/sp-core-library": "1.7.1",
"@microsoft/sp-lodash-subset": "1.7.1",
"@microsoft/sp-office-ui-fabric-core": "1.7.1",
"@microsoft/sp-webpart-base": "1.7.1",
"@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.0",
"@pnp/spfx-controls-react": "1.12.0",
"@pnp/spfx-property-controls": "1.14.1",
"@types/es6-promise": "0.0.33",
"@types/jquery": "^3.3.29",
"@types/react": "16.4.2",
"@types/react-dom": "16.0.5",
"@types/webpack-env": "1.13.1",
"jquery": "^3.3.1",
"react": "16.3.2",
"react-dom": "16.3.2"
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"resolutions": {
"@types/react": "16.4.2"
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1",
"@microsoft/sp-module-interfaces": "1.7.1",
"@microsoft/sp-tslint-rules": "1.7.1",
"@microsoft/sp-webpart-workbench": "1.7.1",
"@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/mocha": "2.2.38",
"@voitanos/jest-preset-spfx-react16": "^1.1.0",

View File

@ -12,6 +12,7 @@
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other

View File

@ -1,11 +1,8 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
import * as strings from 'TenantPropertiesWebPartStrings';
import TenantProperties from './components/TenantProperties';

View File

@ -1,5 +1,6 @@
import {IListViewItems } from "./IListViewItems";
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { SyntheticEvent } from "react";
export enum panelMode {
"New",
"edit",
@ -10,6 +11,6 @@ export interface ITenantPropertyPanelProps {
mode:panelMode;
showPanel: boolean;
TenantProperty: IListViewItems ;
onDismiss(refresh?:boolean) : void;
onDismiss(ev?: SyntheticEvent<HTMLElement, Event>, refresh?: boolean) : void;
context: WebPartContext;
}

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';
.tenantProperties {
.container {

View File

@ -19,6 +19,7 @@ import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBa
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import * as strings from 'TenantPropertiesWebPartStrings';
import { SyntheticEvent } from 'react';
// ListView Columns
const viewFields: IViewField[] = [
@ -97,7 +98,7 @@ export default class TenantProperties extends React.Component<ITenantPropertiesP
}
// Panel Dismiss CallBack
// @param refresh refresh list?
public async onDismissPanel(refresh?: boolean) {
public async onDismissPanel(ev?: SyntheticEvent<HTMLElement, Event>, refresh?: boolean) {
this.setState({
showPanel: false
});

View File

@ -58,7 +58,9 @@ export default class TenantPropertyPanel extends React.Component<ITenantProperty
Description: this.state.tenantProperty.tenantPropertyDescription,
Comment: this.state.tenantProperty.tenantPropertyComment
});
result ? this.props.onDismiss(true) : null;
if (result) {
this.props.onDismiss(null, true);
}
}catch(error){
this.setState({errorMessage:error});
}
@ -72,7 +74,9 @@ export default class TenantPropertyPanel extends React.Component<ITenantProperty
Description: this.state.tenantProperty.tenantPropertyDescription,
Comment: this.state.tenantProperty.tenantPropertyComment
});
result ? this.props.onDismiss(true) : null;
if (result) {
this.props.onDismiss(null, true);
}
}catch(error){
this.setState({errorMessage:error});
}
@ -86,7 +90,9 @@ export default class TenantPropertyPanel extends React.Component<ITenantProperty
Description: this.state.tenantProperty.tenantPropertyDescription,
Comment: this.state.tenantProperty.tenantPropertyComment
});
result ? this.props.onDismiss(true) : null;
if (result) {
this.props.onDismiss(null, true);
}
}catch(error){
this.setState({errorMessage:error});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

View File

@ -1,4 +1,5 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
@ -10,6 +11,9 @@
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
@ -25,7 +29,7 @@
]
},
"include": [
"src/**/*.ts", "src/webparts/tenantProperties/components/TenantPropertyPanel.tsx"
"src/**/*.ts"
],
"exclude": [
"node_modules",

View File

@ -0,0 +1,653 @@
# Upgrade project react-tenant-properties to v1.11.0
Date: 10/20/2020
## Findings
Following is the list of steps required to upgrade your project to SharePoint Framework version 1.11.0. [Summary](#Summary) of the modifications is included at the end of the report.
### FN001001 @microsoft/sp-core-library | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-core-library
Execute the following command:
```sh
npm i -SE @microsoft/sp-core-library@1.11.0
```
File: [./package.json](./package.json)
### FN001002 @microsoft/sp-lodash-subset | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-lodash-subset
Execute the following command:
```sh
npm i -SE @microsoft/sp-lodash-subset@1.11.0
```
File: [./package.json](./package.json)
### FN001003 @microsoft/sp-office-ui-fabric-core | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-office-ui-fabric-core
Execute the following command:
```sh
npm i -SE @microsoft/sp-office-ui-fabric-core@1.11.0
```
File: [./package.json](./package.json)
### FN001004 @microsoft/sp-webpart-base | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-webpart-base
Execute the following command:
```sh
npm i -SE @microsoft/sp-webpart-base@1.11.0
```
File: [./package.json](./package.json)
### FN001005 @types/react | Required
Remove SharePoint Framework dependency package @types/react
Execute the following command:
```sh
npm un -S @types/react
```
File: [./package.json](./package.json)
### FN001006 @types/react-dom | Required
Remove SharePoint Framework dependency package @types/react-dom
Execute the following command:
```sh
npm un -S @types/react-dom
```
File: [./package.json](./package.json)
### FN001007 @types/webpack-env | Required
Remove SharePoint Framework dependency package @types/webpack-env
Execute the following command:
```sh
npm un -S @types/webpack-env
```
File: [./package.json](./package.json)
### FN001010 @types/es6-promise | Required
Remove SharePoint Framework dependency package @types/es6-promise
Execute the following command:
```sh
npm un -S @types/es6-promise
```
File: [./package.json](./package.json)
### FN001021 @microsoft/sp-property-pane | Required
Install SharePoint Framework dependency package @microsoft/sp-property-pane
Execute the following command:
```sh
npm i -SE @microsoft/sp-property-pane@1.11.0
```
File: [./package.json](./package.json)
### FN001022 office-ui-fabric-react | Required
Install SharePoint Framework dependency package office-ui-fabric-react
Execute the following command:
```sh
npm i -SE office-ui-fabric-react@6.214.0
```
File: [./package.json](./package.json)
### FN002001 @microsoft/sp-build-web | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-build-web
Execute the following command:
```sh
npm i -DE @microsoft/sp-build-web@1.11.0
```
File: [./package.json](./package.json)
### FN002002 @microsoft/sp-module-interfaces | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-module-interfaces
Execute the following command:
```sh
npm i -DE @microsoft/sp-module-interfaces@1.11.0
```
File: [./package.json](./package.json)
### FN002003 @microsoft/sp-webpart-workbench | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-webpart-workbench
Execute the following command:
```sh
npm i -DE @microsoft/sp-webpart-workbench@1.11.0
```
File: [./package.json](./package.json)
### FN002009 @microsoft/sp-tslint-rules | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-tslint-rules
Execute the following command:
```sh
npm i -DE @microsoft/sp-tslint-rules@1.11.0
```
File: [./package.json](./package.json)
### FN002013 @types/webpack-env | Required
Install SharePoint Framework dev dependency package @types/webpack-env
Execute the following command:
```sh
npm i -DE @types/webpack-env@1.13.1
```
File: [./package.json](./package.json)
### FN002014 @types/es6-promise | Required
Install SharePoint Framework dev dependency package @types/es6-promise
Execute the following command:
```sh
npm i -DE @types/es6-promise@0.0.33
```
File: [./package.json](./package.json)
### FN002015 @types/react | Required
Install SharePoint Framework dev dependency package @types/react
Execute the following command:
```sh
npm i -DE @types/react@16.8.8
```
File: [./package.json](./package.json)
### FN002016 @types/react-dom | Required
Install SharePoint Framework dev dependency package @types/react-dom
Execute the following command:
```sh
npm i -DE @types/react-dom@16.8.3
```
File: [./package.json](./package.json)
### FN006004 package-solution.json developer | Optional
In package-solution.json add developer section
In file [./config/package-solution.json](./config/package-solution.json) update the code as follows:
```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"
}
}
}
```
File: [./config/package-solution.json](./config/package-solution.json)
### FN010001 .yo-rc.json version | Recommended
Update version in .yo-rc.json
In file [./.yo-rc.json](./.yo-rc.json) update the code as follows:
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0"
}
}
```
File: [./.yo-rc.json](./.yo-rc.json)
### FN012012 tsconfig.json include property | Required
Update tsconfig.json include property
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"include": [
"src/**/*.tsx"
]
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN002012 @microsoft/rush-stack-compiler-3.3 | Required
Install SharePoint Framework dev dependency package @microsoft/rush-stack-compiler-3.3
Execute the following command:
```sh
npm i -DE @microsoft/rush-stack-compiler-3.3@0.3.5
```
File: [./package.json](./package.json)
### FN012017 tsconfig.json extends property | Required
Update tsconfig.json extends property
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json"
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN016004 Property pane property import change to @microsoft/sp-property-pane | Required
Refactor the code to import property pane property from the @microsoft/sp-property-pane npm package instead of the @microsoft/sp-webpart-base package
In file [src/webparts/tenantProperties/TenantPropertiesWebPart.ts](src/webparts/tenantProperties/TenantPropertiesWebPart.ts) update the code as follows:
```ts
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
```
File: [src/webparts/tenantProperties/TenantPropertiesWebPart.ts:4:1](src/webparts/tenantProperties/TenantPropertiesWebPart.ts)
### FN020001 @types/react | Required
Add resolution for package @types/react
In file [./package.json](./package.json) update the code as follows:
```json
{
"resolutions": {
"@types/react": "16.8.8"
}
}
```
File: [./package.json](./package.json)
### FN021001 main | Required
Add package.json property
In file [./package.json](./package.json) update the code as follows:
```json
{
"main": "lib/index.js"
}
```
File: [./package.json](./package.json)
### FN001008 react | Required
Upgrade SharePoint Framework dependency package react
Execute the following command:
```sh
npm i -SE react@16.8.5
```
File: [./package.json](./package.json)
### FN001009 react-dom | Required
Upgrade SharePoint Framework dependency package react-dom
Execute the following command:
```sh
npm i -SE react-dom@16.8.5
```
File: [./package.json](./package.json)
### FN022001 Scss file import | Required
Remove scss file import
In file [src/webparts/tenantProperties/components/TenantProperties.module.scss](src/webparts/tenantProperties/components/TenantProperties.module.scss) update the code as follows:
```scss
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss'
```
File: [src/webparts/tenantProperties/components/TenantProperties.module.scss](src/webparts/tenantProperties/components/TenantProperties.module.scss)
### FN022002 Scss file import | Optional
Add scss file import
In file [src/webparts/tenantProperties/components/TenantProperties.module.scss](src/webparts/tenantProperties/components/TenantProperties.module.scss) update the code as follows:
```scss
@import '~office-ui-fabric-react/dist/sass/References.scss'
```
File: [src/webparts/tenantProperties/components/TenantProperties.module.scss](src/webparts/tenantProperties/components/TenantProperties.module.scss)
### FN011011 Web part manifest supportedHosts | Required
Update the supportedHosts property in the manifest
In file [src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json](src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json) update the code as follows:
```json
{
"supportedHosts": ["SharePointWebPart"]
}
```
File: [src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json](src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json)
### FN012014 tsconfig.json compiler options inlineSources | Required
Update tsconfig.json inlineSources value
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"compilerOptions": {
"inlineSources": false
}
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN012015 tsconfig.json compiler options strictNullChecks | Required
Update tsconfig.json strictNullChecks value
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"compilerOptions": {
"strictNullChecks": false
}
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN012016 tsconfig.json compiler options noUnusedLocals | Required
Update tsconfig.json noUnusedLocals value
In file [./tsconfig.json](./tsconfig.json) update the code as follows:
```json
{
"compilerOptions": {
"noUnusedLocals": false
}
}
```
File: [./tsconfig.json](./tsconfig.json)
### FN018001 Web part Microsoft Teams tab resources folder | Optional
Create folder for Microsoft Teams tab resources
Execute the following command:
```sh
mkdir /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams
```
File: [teams](teams)
### FN018003 Web part Microsoft Teams tab small icon | Optional
Create Microsoft Teams tab small icon for the web part
Execute the following command:
```sh
cp /home/jfmr/.nvm/versions/node/v10.20.1/lib/node_modules/@pnp/cli-microsoft365/dist/m365/spfx/commands/project/project-upgrade/assets/tab20x20.png /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams/fe85bc01-3650-4533-9c42-5c078fdd579c_outline.png
```
File: [teams/fe85bc01-3650-4533-9c42-5c078fdd579c_outline.png](teams/fe85bc01-3650-4533-9c42-5c078fdd579c_outline.png)
### FN018004 Web part Microsoft Teams tab large icon | Optional
Create Microsoft Teams tab large icon for the web part
Execute the following command:
```sh
cp /home/jfmr/.nvm/versions/node/v10.20.1/lib/node_modules/@pnp/cli-microsoft365/dist/m365/spfx/commands/project/project-upgrade/assets/tab96x96.png /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams/fe85bc01-3650-4533-9c42-5c078fdd579c_color.png
```
File: [teams/fe85bc01-3650-4533-9c42-5c078fdd579c_color.png](teams/fe85bc01-3650-4533-9c42-5c078fdd579c_color.png)
### FN017001 Run npm dedupe | Optional
If, after upgrading npm packages, when building the project you have errors similar to: "error TS2345: Argument of type 'SPHttpClientConfiguration' is not assignable to parameter of type 'SPHttpClientConfiguration'", try running 'npm dedupe' to cleanup npm packages.
Execute the following command:
```sh
npm dedupe
```
File: [./package.json](./package.json)
## Summary
### Execute script
```sh
npm i -SE @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-webpart-base@1.11.0 @microsoft/sp-property-pane@1.11.0 office-ui-fabric-react@6.214.0 react@16.8.5 react-dom@16.8.5
npm i -DE @microsoft/sp-build-web@1.11.0 @microsoft/sp-module-interfaces@1.11.0 @microsoft/sp-webpart-workbench@1.11.0 @microsoft/sp-tslint-rules@1.11.0 @types/webpack-env@1.13.1 @types/es6-promise@0.0.33 @types/react@16.8.8 @types/react-dom@16.8.3 @microsoft/rush-stack-compiler-3.3@0.3.5
npm un -S @types/react @types/react-dom @types/webpack-env @types/es6-promise
npm dedupe
mkdir /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams
cp /home/jfmr/.nvm/versions/node/v10.20.1/lib/node_modules/@pnp/cli-microsoft365/dist/m365/spfx/commands/project/project-upgrade/assets/tab20x20.png /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams/fe85bc01-3650-4533-9c42-5c078fdd579c_outline.png
cp /home/jfmr/.nvm/versions/node/v10.20.1/lib/node_modules/@pnp/cli-microsoft365/dist/m365/spfx/commands/project/project-upgrade/assets/tab96x96.png /home/jfmr/dev/github/sp-dev-fx-webparts/samples/react-tenant-properties/teams/fe85bc01-3650-4533-9c42-5c078fdd579c_color.png
```
### Modify files
#### [./config/package-solution.json](./config/package-solution.json)
In package-solution.json add developer section:
```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"
}
}
}
```
#### [./.yo-rc.json](./.yo-rc.json)
Update version in .yo-rc.json:
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.11.0"
}
}
```
#### [./tsconfig.json](./tsconfig.json)
Update tsconfig.json include property:
```json
{
"include": [
"src/**/*.tsx"
]
}
```
Update tsconfig.json extends property:
```json
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json"
}
```
Update tsconfig.json inlineSources value:
```json
{
"compilerOptions": {
"inlineSources": false
}
}
```
Update tsconfig.json strictNullChecks value:
```json
{
"compilerOptions": {
"strictNullChecks": false
}
}
```
Update tsconfig.json noUnusedLocals value:
```json
{
"compilerOptions": {
"noUnusedLocals": false
}
}
```
#### [src/webparts/tenantProperties/TenantPropertiesWebPart.ts](src/webparts/tenantProperties/TenantPropertiesWebPart.ts)
Refactor the code to import property pane property from the @microsoft/sp-property-pane npm package instead of the @microsoft/sp-webpart-base package:
```ts
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
```
#### [./package.json](./package.json)
Add resolution for package @types/react:
```json
{
"resolutions": {
"@types/react": "16.8.8"
}
}
```
Add package.json property:
```json
{
"main": "lib/index.js"
}
```
#### [src/webparts/tenantProperties/components/TenantProperties.module.scss](src/webparts/tenantProperties/components/TenantProperties.module.scss)
Remove scss file import:
```scss
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss'
```
Add scss file import:
```scss
@import '~office-ui-fabric-react/dist/sass/References.scss'
```
#### [src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json](src/webparts/tenantProperties/TenantPropertiesWebPart.manifest.json)
Update the supportedHosts property in the manifest:
```json
{
"supportedHosts": ["SharePointWebPart"]
}
```