Merge branch 'master' into issue1520
This commit is contained in:
commit
fe4d149ea3
|
@ -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
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"solution": {
|
||||
"name": "react-calendar-client-side-solution",
|
||||
"id": "3a13208b-3874-4036-9262-4edd22e88187",
|
||||
"version": "1.0.0.0",
|
||||
"version": "1.0.4.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,42 +165,42 @@ 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";
|
||||
eventData.fRecurrence = true;
|
||||
eventData.UID = getGUID();
|
||||
panelMode = IPanelModelEnum.add;
|
||||
eventData.RecurrenceData = await this.returnExceptionRecurrenceInfo(eventData.RecurrenceData);
|
||||
eventData.RecurrenceData = await this.returnExceptionRecurrenceInfo(eventData.RecurrenceData);
|
||||
}
|
||||
startDate = `${moment(this.state.startDate).format('YYYY/MM/DD')}`;
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,7 +581,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
private parseDailyRule(rule): string {
|
||||
const keys = Object.keys(rule);
|
||||
if (keys.indexOf("weekday") !== -1 && rule["weekday"] === "TRUE")
|
||||
return format("{} {}", format(strings.everyFormat, 1), strings.weekDayLabel);
|
||||
return format("{} {}", format(strings.everyFormat, 1), strings.weekDayLabel);
|
||||
|
||||
if (keys.indexOf("dayFrequency") !== -1) {
|
||||
const dayFrequency: number = parseInt(rule["dayFrequency"]);
|
||||
|
@ -609,16 +610,16 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
"fr": strings.Friday,
|
||||
"sa": strings.Saturday,
|
||||
"su": strings.Sunday
|
||||
};
|
||||
};
|
||||
let days: string[] = [];
|
||||
for (let key of keys) {
|
||||
days.push(dayMap[key]);
|
||||
}
|
||||
|
||||
return format("{}{} {} {}",
|
||||
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency): format(strings.everyNthFormat, frequency),
|
||||
strings.weekLabel,
|
||||
strings.onLabel,
|
||||
return format("{}{} {} {}",
|
||||
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)
|
||||
);
|
||||
|
@ -649,17 +650,17 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
private parseMonthlyByDayRule(rule): string {
|
||||
let keys: string[] = Object.keys(rule);
|
||||
const dayTypeMap: any = {
|
||||
"day": strings.weekDayLabel,
|
||||
"weekend_day": strings.weekEndDay,
|
||||
"mo": strings.Monday,
|
||||
"tu": strings.Tuesday,
|
||||
"we": strings.Wednesday,
|
||||
"th": strings.Thursday,
|
||||
"fr": strings.Friday,
|
||||
"sa": strings.Saturday,
|
||||
"day": strings.weekDayLabel,
|
||||
"weekend_day": strings.weekEndDay,
|
||||
"mo": strings.Monday,
|
||||
"tu": strings.Tuesday,
|
||||
"we": strings.Wednesday,
|
||||
"th": strings.Thursday,
|
||||
"fr": strings.Friday,
|
||||
"sa": strings.Saturday,
|
||||
"su": strings.Sunday
|
||||
};
|
||||
|
||||
|
||||
const orderType: any = {
|
||||
"first": strings.firstLabel,
|
||||
"second": strings.secondLabel,
|
||||
|
@ -671,22 +672,22 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
let order: string;
|
||||
let dayType: string;
|
||||
let frequencyFormat: string;
|
||||
|
||||
|
||||
for (let key of keys) {
|
||||
switch (key) {
|
||||
case "monthFrequency":
|
||||
const frequency = parseInt(rule[key]);
|
||||
switch(frequency) {
|
||||
case 1:
|
||||
switch (frequency) {
|
||||
case 1:
|
||||
frequencyFormat = format(strings.everyFormat, frequency);
|
||||
break;
|
||||
case 2:
|
||||
case 2:
|
||||
frequencyFormat = format(strings.everySecondFormat, frequency);
|
||||
break;
|
||||
default:
|
||||
frequencyFormat = format(strings.everyNthFormat, frequency);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "weekDayOfMonth":
|
||||
order = orderType[rule[key]];
|
||||
|
@ -697,10 +698,10 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
}
|
||||
}
|
||||
|
||||
return format("{} {} {} {} {}{}",
|
||||
frequencyFormat,
|
||||
strings.monthLabel.toLowerCase(),
|
||||
strings.onTheLabel,
|
||||
return format("{} {} {} {} {}{}",
|
||||
frequencyFormat,
|
||||
strings.monthLabel.toLowerCase(),
|
||||
strings.onTheLabel,
|
||||
order,
|
||||
dayType,
|
||||
strings.theSuffix);
|
||||
|
@ -719,12 +720,12 @@ 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;
|
||||
frequencyString = format(frequencyFormat, frequency);
|
||||
break;
|
||||
break;
|
||||
case "month":
|
||||
month = months[parseInt(rule[key]) - 1];
|
||||
break;
|
||||
|
@ -755,14 +756,14 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
"last": strings.lastLabel
|
||||
};
|
||||
const dayTypeMap: any = {
|
||||
"day": strings.weekDayLabel,
|
||||
"weekend_day": strings.weekEndDay,
|
||||
"mo": strings.Monday,
|
||||
"tu": strings.Tuesday,
|
||||
"we": strings.Wednesday,
|
||||
"th": strings.Thursday,
|
||||
"fr": strings.Friday,
|
||||
"sa": strings.Saturday,
|
||||
"day": strings.weekDayLabel,
|
||||
"weekend_day": strings.weekEndDay,
|
||||
"mo": strings.Monday,
|
||||
"tu": strings.Tuesday,
|
||||
"we": strings.Wednesday,
|
||||
"th": strings.Thursday,
|
||||
"fr": strings.Friday,
|
||||
"sa": strings.Saturday,
|
||||
"su": strings.Sunday
|
||||
};
|
||||
let frequencyString: string;
|
||||
|
@ -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;
|
||||
|
@ -779,7 +780,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
case "weekDayOfMonth":
|
||||
order = orderMap[rule[key]];
|
||||
break;
|
||||
case "month":
|
||||
case "month":
|
||||
month = months[parseInt(rule[key]) - 1];
|
||||
break;
|
||||
default:
|
||||
|
@ -787,7 +788,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
break;
|
||||
}
|
||||
|
||||
return format("{} {} {}",
|
||||
return format("{} {} {}",
|
||||
frequencyString,
|
||||
strings.yearLabel,
|
||||
format(strings.onTheDayTypeFormat, order, dayTypeString.toLowerCase(), strings.theSuffix)
|
||||
|
@ -812,39 +813,38 @@ export class Event extends React.Component<IEventProps, IEventState> {
|
|||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -114,6 +114,7 @@ define([], function () {
|
|||
showMore: "more",
|
||||
recurrenceEventLabel: "Recurrence Event",
|
||||
editRecurrenceSeries: "Edit Recurrence Series",
|
||||
allDayEventLabel: "All Day Event ?",
|
||||
ifRecurrenceLabel: "Recurrence ?",
|
||||
onLabel: "On",
|
||||
offLabel: "Off",
|
||||
|
|
|
@ -114,6 +114,7 @@ declare interface ICalendarWebPartStrings {
|
|||
showMore: string;
|
||||
recurrenceEventLabel: string;
|
||||
editRecurrenceSeries: string;
|
||||
allDayEventLabel: string;
|
||||
ifRecurrenceLabel: string;
|
||||
onLabel: string;
|
||||
offLabel: string;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {
|
||||
|
@ -124,18 +125,43 @@ const DirectoryHook: React.FC<IDirectoryProps> = (props) => {
|
|||
};
|
||||
|
||||
let _searchUsers = async (searchText: string) => {
|
||||
try {
|
||||
try {
|
||||
setstate({ ...state, searchText: searchText, isLoading: true });
|
||||
if (searchText.length > 0) {
|
||||
let searchProps: string[] = props.searchProps && props.searchProps.length > 0 ?
|
||||
props.searchProps.split(',') : ['FirstName', 'LastName', 'WorkEmail', 'Department'];
|
||||
let qryText: string = '';
|
||||
let finalSearchText: string = searchText ? searchText.replace(/ /g, '+') : searchText;
|
||||
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]);
|
||||
|
|
|
@ -7,5 +7,6 @@ export interface IDirectoryProps {
|
|||
searchFirstName: boolean;
|
||||
updateProperty: (value: string) => void;
|
||||
searchProps?: string;
|
||||
clearTextSearchProps?: string;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,6 +9,8 @@ declare interface IDirectoryWebPartStrings {
|
|||
LoadingText: string;
|
||||
SearchPropsLabel: string;
|
||||
SearchPropsDesc: string;
|
||||
ClearTextSearchPropsLabel: string;
|
||||
ClearTextSearchPropsDesc: string;
|
||||
PagingLabel: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"@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",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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' }
|
||||
}]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,8 @@
|
|||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue