Updated React Graph Calendar to SPFx 1.10 + adding Personal Tab support (#1111)

Updated React Graph Calendar to SPFx 1.10 + adding Personal Tab support (#1111)
This commit is contained in:
Sébastien Levert 2020-01-09 14:10:19 -05:00 committed by Vesa Juvonen
parent e636d68221
commit 11da7ad89d
9 changed files with 4953 additions and 3999 deletions

View File

@ -1,7 +1,7 @@
# React Graph Calendar Web Part
## Summary
This is a sample web part developed using React Framework to gather events from the underlying group calendar of a Team site. This sample also demonstrates the utilization of web parts as Teams tabs and offering a visualization context to change behaviors based on the platform used (Getting the proper information from the team vs. SharePoint site, understanding the context of the theme on Teams, etc.).
This is a sample web part developed using React Framework to gather events from the underlying group calendar of a Team site. This sample also demonstrates the utilization of web parts as Teams tabs and Personal tab and offering a visualization context to change behaviors based on the platform used (Getting the proper information from the team vs. SharePoint site, understanding the context of the theme on Teams, etc.).
### Web Part in SharePoint Online
![The web part in action](./assets/react-graph-calendar-spo.gif)
@ -16,7 +16,7 @@ Webpart is developed using below technologies
* Office UI Fabric
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.9.1-green.svg)
![drop](https://img.shields.io/badge/version-1.10-green.svg)
## Applies to
@ -38,6 +38,7 @@ react-graph-calendar | [Sébastien Levert](https://www.linkedin.com/in/sebastien
Version|Date|Comments
-------|----|--------
1.0 |December 29, 2019 | Initial Release
1.1 |January 08, 2020 | Bumped to SPFx 1.10 and added the Personal Tab support
## 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.**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 MiB

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 MiB

After

Width:  |  Height:  |  Size: 8.7 MiB

View File

@ -11,6 +11,10 @@
{
"resource": "Microsoft Graph",
"scope": "Group.Read.All"
},
{
"resource": "Microsoft Graph",
"scope": "Calendars.Read"
}
]
},

File diff suppressed because it is too large Load Diff

View File

@ -17,10 +17,10 @@
"@fullcalendar/moment": "^4.3.0",
"@fullcalendar/moment-timezone": "^4.3.0",
"@fullcalendar/react": "^4.3.0",
"@microsoft/sp-core-library": "1.9.1",
"@microsoft/sp-lodash-subset": "1.9.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
"@microsoft/sp-webpart-base": "1.9.1",
"@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-webpart-base": "1.10.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
@ -35,10 +35,10 @@
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.9.1",
"@microsoft/sp-tslint-rules": "1.9.1",
"@microsoft/sp-module-interfaces": "1.9.1",
"@microsoft/sp-webpart-workbench": "1.9.1",
"@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-2.9": "0.7.16",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",

View File

@ -12,13 +12,13 @@
// 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", "TeamsTab"],
"supportedHosts": ["SharePointWebPart", "TeamsTab", "TeamsPersonalApp"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Graph Calendar" },
"description": { "default": "GraphCalendar description" },
"officeFabricIconFontName": "Page",
"description": { "default": "Graph Calendar" },
"officeFabricIconFontName": "Calendar",
"properties": {
"description": "Graph Calendar"
}

View File

@ -44,15 +44,14 @@ export default class GraphCalendarWebPart extends BaseClientSideWebPart<IGraphCa
}
// Sets the Teams context if in Teams
if (this.context.microsoftTeams) {
this.context.microsoftTeams.getContext(context => {
this._teamsContext = context;
// resolve the promise
resolve(undefined);
});
if (this.context.sdks.microsoftTeams) {
this._teamsContext = this.context.sdks.microsoftTeams.context;
// Initialize the OUIF icons if in Teams
initializeIcons();
// resolve the promise
resolve(undefined);
} else {
// resolve the promise
resolve(undefined);

View File

@ -17,6 +17,12 @@ interface IGraphCalendarState {
isEventDetailsOpen: boolean;
currentSelectedEvent: EventInput;
groupId: string;
tabType: TabType;
}
enum TabType {
TeamsTab,
PersonalTab
}
export default class GraphCalendar extends React.Component<IGraphCalendarProps, IGraphCalendarState> {
@ -45,7 +51,8 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
currentActiveEndDate: null,
isEventDetailsOpen: false,
currentSelectedEvent: null,
groupId: this._isRunningInTeams() ? this.props.teamsContext.groupId : this.props.context.pageContext.site.group ? this.props.context.pageContext.site.group.id : ""
groupId: this._isRunningInTeams() ? this.props.teamsContext.groupId : this.props.context.pageContext.site.group ? this.props.context.pageContext.site.group.id : "",
tabType: this._isRunningInTeams() ? (this._isPersonalTab() ? TabType.PersonalTab : TabType.TeamsTab) : TabType.TeamsTab
};
}
@ -73,7 +80,7 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
plugins={[ dayGridPlugin ]}
windowResize={this._handleResize.bind(this)}
datesRender={this._datesRender.bind(this)}
eventClick={this._handleEventClick.bind(this)}
eventClick={this._openEventPanel.bind(this)}
height={this.state.height}
events={this.state.events} />
{this.state.currentSelectedEvent &&
@ -81,6 +88,8 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
isOpen={this.state.isEventDetailsOpen}
type={ PanelType.smallFixedFar }
headerText={this.state.currentSelectedEvent ? this.state.currentSelectedEvent.title : ""}
onDismiss={this._closeEventPanel.bind(this)}
isLightDismiss={true}
closeButtonAriaLabel='Close'>
<h3>Start Time</h3>
<span>{moment(this.state.currentSelectedEvent.start).format('MMMM Do YYYY [at] h:mm:ss a')}</span>
@ -123,17 +132,40 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
return this.props.teamsContext;
}
/**
* Validates if the current web part is running in a Personal Tab
*/
private _isPersonalTab() {
let _isPersonalTab: Boolean = false;
if(this._isRunningInTeams() && !this.props.teamsContext.teamId) {
_isPersonalTab = true;
}
return _isPersonalTab;
}
/**
* Handles the click event and opens the OUIF Panel
* @param eventClickInfo The information about the selected event
*/
private _handleEventClick(eventClickInfo: any) {
private _openEventPanel(eventClickInfo: any) {
this.setState({
isEventDetailsOpen: true,
currentSelectedEvent: eventClickInfo.event
});
}
/**
* Handles the click event on the dismiss from the Panel and closes the OUIF Panel
*/
private _closeEventPanel() {
this.setState({
isEventDetailsOpen: true,
currentSelectedEvent: null
});
}
/**
* If the view changed, reload the events based on the active view
* @param info Information about the current active view
@ -166,15 +198,20 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
*/
private _loadEvents(startDate: Date, endDate: Date): void {
// If a Group was found, execute the query. If not, do nothing.
if(this.state.groupId) {
// If a Group was found or running in the context of a Personal tab, execute the query. If not, do nothing.
if(this.state.groupId || this.state.tabType == TabType.PersonalTab) {
this.props.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient): void => {
let apiUrl: string = `/groups/${this.state.groupId}/events`;
if(this._isPersonalTab()) {
apiUrl = '/me/events';
}
client
.api(`/groups/${this.state.groupId}/events`)
.api(apiUrl)
.version("v1.0")
.select('subject,start,end,location,bodyPreview,isAllDay')
.filter(`start/dateTime ge '${startDate.toISOString()}' and end/dateTime le '${endDate.toISOString()}'`)