diff --git a/samples/angular-greeting/README.md b/samples/angular-greeting/README.md index ad9193f39..fa1075c36 100644 --- a/samples/angular-greeting/README.md +++ b/samples/angular-greeting/README.md @@ -1,3 +1,20 @@ +--- +page_type: sample +products: +- office-365 +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJs + createdDate: 2/15/2020 12:00:00 AM +--- + # AngularJS Greeting client-side web part ## Summary @@ -53,3 +70,5 @@ This web part illustrates the following concepts on top of the SharePoint Framew * using non-reactive web part property pane * using conditional rendering for one-time web part setup * passing web part configuration to AngularJS and reacting to configuration changes + + diff --git a/samples/angular-migration/README.md b/samples/angular-migration/README.md index 611e84bf4..ffefd1925 100644 --- a/samples/angular-migration/README.md +++ b/samples/angular-migration/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 10/21/2016 12:00:00 AM +--- # Migrating existing Angular applications to SharePoint Framework sample ## Summary diff --git a/samples/angular-msgraph/README.md b/samples/angular-msgraph/README.md index 06ba33773..05837b228 100644 --- a/samples/angular-msgraph/README.md +++ b/samples/angular-msgraph/README.md @@ -1,22 +1,22 @@ ---- -page_type: sample -products: -- office-sp -- ms-graph -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - - Microsoft Graph - services: - - SharePoint - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +- ms-graph +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + - Microsoft Graph + services: + - SharePoint + platforms: + - AngularJS + createdDate: 2/16/2017 12:00:00 AM +--- ## Angular MS Graph Web Part Built with Angular v1.x ## Summary @@ -94,4 +94,4 @@ Version|Date|Comments 1. Replace the **aad** and **redirect_uri** placeholder values with the application ID and redirect url of your registered Azure application in the GraphHelper.ts file Under src -> angularMsGraph -> GraphHelper.ts - \ No newline at end of file + diff --git a/samples/angular-multipage/README.md b/samples/angular-multipage/README.md index 8509730ab..458ca2fbe 100644 --- a/samples/angular-multipage/README.md +++ b/samples/angular-multipage/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- -# Angular multi-page client-side web part +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 11/1/2016 12:00:00 AM +--- +# Angular multi-page client-side web part ## Summary @@ -98,4 +98,4 @@ This sample illustrates the following concepts on top of the SharePoint Framewor - reading and updating SharePoint list items using Angular - showing charts using [Chart.js](http://www.chartjs.org) and [Angular Chart directives](https://jtblin.github.io/angular-chart.js/) - \ No newline at end of file + diff --git a/samples/angular-ngofficeuifabric-file-upload/README.md b/samples/angular-ngofficeuifabric-file-upload/README.md index a931d5bfc..e4fae1a25 100644 --- a/samples/angular-ngofficeuifabric-file-upload/README.md +++ b/samples/angular-ngofficeuifabric-file-upload/README.md @@ -1,68 +1,68 @@ ---- -page_type: sample -products: -- office-sp -- office-365 -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- -# Spfx Webpart: File Upload using AngularJs - -## Summary -File Update/Delete webpart using AngularJs and ngOfficeUIFabric with the SharePoint Framework. - -![File Upload using Angular](http://i.imgur.com/U5qg4II.png) - -Edit webpart properties to set Document library Name. Initially, It has been set to `Documents`. - -## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/version-GA-green.svg) - -## Applies to - -* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) -* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant) - -## Solution - -Solution|Author(s) ---------|--------- -angular-ngofficeuifabric-file-upload | Atish Kumar Dipongkor (MVP, Office Development), Gautam Sheth(SharePoint Consultant,RapidCircle,@gautamdsheth) - -## Version history - -Version|Date|Comments --------|----|-------- -1.0|November 24, 2016|Initial release -2.0|May 26, 2017|GA release -2.1|July 19, 2017|Bug fix - -## Disclaimer -**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** - ---- - -## Minimal Path to Awesome - -- Clone this repository -- in the command line run: - - `npm install` - - `tsd install` - - `gulp serve --nobrowser` - -## Features -This Web Part illustrates the following concepts on top of the SharePoint Framework & AngularJs: - -- `BaseService`: By injecting this Angular Service, GET, POST, UPDATE & DELETE requests can be made easily. It's a resuable service. -- `CustomFileChange`: It's a custom Angular directive. It binds the file with model on file change event. -- `IsoToDateString`: It's a custom Angular filter. It formats ISO date string to `{0:yyyy}-{0:MM}-{0:dd}` format. - - +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 6/19/2017 12:00:00 AM +--- +# Spfx Webpart: File Upload using AngularJs + +## Summary +File Update/Delete webpart using AngularJs and ngOfficeUIFabric with the SharePoint Framework. + +![File Upload using Angular](http://i.imgur.com/U5qg4II.png) + +Edit webpart properties to set Document library Name. Initially, It has been set to `Documents`. + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/version-GA-green.svg) + +## Applies to + +* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) +* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant) + +## Solution + +Solution|Author(s) +--------|--------- +angular-ngofficeuifabric-file-upload | Atish Kumar Dipongkor (MVP, Office Development), Gautam Sheth(SharePoint Consultant,RapidCircle,@gautamdsheth) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|November 24, 2016|Initial release +2.0|May 26, 2017|GA release +2.1|July 19, 2017|Bug fix + +## Disclaimer +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +--- + +## Minimal Path to Awesome + +- Clone this repository +- in the command line run: + - `npm install` + - `tsd install` + - `gulp serve --nobrowser` + +## Features +This Web Part illustrates the following concepts on top of the SharePoint Framework & AngularJs: + +- `BaseService`: By injecting this Angular Service, GET, POST, UPDATE & DELETE requests can be made easily. It's a resuable service. +- `CustomFileChange`: It's a custom Angular directive. It binds the file with model on file change event. +- `IsoToDateString`: It's a custom Angular filter. It formats ISO date string to `{0:yyyy}-{0:MM}-{0:dd}` format. + + diff --git a/samples/angular-ngofficeuifabric-todo/README.md b/samples/angular-ngofficeuifabric-todo/README.md index 2b5bd0ba1..2c952b237 100644 --- a/samples/angular-ngofficeuifabric-todo/README.md +++ b/samples/angular-ngofficeuifabric-todo/README.md @@ -1,23 +1,23 @@ ---- -page_type: sample -products: -- office-sp -- office-365 -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - - Office UI Fabric - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM - scenarios: - - Embed ---- -# Angular & ngOfficeUIFabric Client-Side Web Part +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + - Office UI Fabric + platforms: + - AngularJS + createdDate: 8/29/2016 12:00:00 AM + scenarios: + - Embed +--- +# Angular & ngOfficeUIFabric Client-Side Web Part ## Summary diff --git a/samples/angular-search/README.md b/samples/angular-search/README.md index 8cb567889..5a23ef88f 100644 --- a/samples/angular-search/README.md +++ b/samples/angular-search/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- -# Search Client-Side Web Part Built with Angular v1.x +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 2/4/2017 12:00:00 AM +--- +# Search Client-Side Web Part Built with Angular v1.x ## Summary This is a sample search web part that illustrates how you can use Angular within the new SharePoint Framework @@ -72,4 +72,4 @@ Version|Date|Comments > and not by id because then I would get everything that inherits from that content type. I only want the > the results for a specific content type and not everything that inherits that content type as well - \ No newline at end of file + diff --git a/samples/angular-todo/README.md b/samples/angular-todo/README.md index c073486d2..9fbf829fc 100644 --- a/samples/angular-todo/README.md +++ b/samples/angular-todo/README.md @@ -1,3 +1,19 @@ +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 3/10/2017 12:00:00 AM +--- # Angular client-side web part ## Summary @@ -51,4 +67,4 @@ This web part illustrates the following concepts on top of the SharePoint Framew * using conditional rendering for one-time web part setup * passing web part configuration to Angular and reacting to configuration changes - \ No newline at end of file + diff --git a/samples/angular2-prototype/README.md b/samples/angular2-prototype/README.md index 702b0bd0c..805755386 100644 --- a/samples/angular2-prototype/README.md +++ b/samples/angular2-prototype/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - AngularJS - createdDate: 1/1/2016 12:00:00 AM ---- -# Angular2 Web Part Prototype +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 8/14/2017 12:00:00 AM +--- +# Angular2 Web Part Prototype ## Note to developers > This web part sample is currently in prototype phase and subject to change. @@ -83,7 +83,7 @@ Though this is not a best practice, it helps build web parts successfully. We are trying to find better solutions to this problem. Specially, how to avoid creating a separate NgModule for each web part. ### Adding functionality -To add functionality to this web part prototype the main file to edit is TodoWebPart.ts, here there are comments to help you alter the prototype. +To add functionality to this web part prototype the main file to edit is `TodoWebPart.ts`, here there are comments to help you alter the prototype. ### Web part concepts The web part displays a title, button to add to dos and a button to print the to do items to the console. @@ -92,4 +92,4 @@ This web part illustrates the following concepts on top of the SharePoint Framew - Changing a property (the title) of a web part using the PropertyPane - Manipulating properties in the Angular2 component class and saving to web part’s property bag - \ No newline at end of file + diff --git a/samples/angularelements-helloworld/README.md b/samples/angularelements-helloworld/README.md index 870b5aad1..971323b20 100644 --- a/samples/angularelements-helloworld/README.md +++ b/samples/angularelements-helloworld/README.md @@ -1,3 +1,18 @@ +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 6/1/2018 12:00:00 AM +--- # Angular Elements in SharePoint Framework ## Summary diff --git a/samples/angularelements-html-templatefile/README.md b/samples/angularelements-html-templatefile/README.md index 3b39fdf0a..cfa5b1e5f 100644 --- a/samples/angularelements-html-templatefile/README.md +++ b/samples/angularelements-html-templatefile/README.md @@ -1,3 +1,20 @@ +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - AngularJS + createdDate: 1/8/2019 12:00:00 AM +--- + # Angular Elements with HTML Template File in SharePoint Framework ## Summary @@ -48,7 +65,7 @@ This web part illustrates the following concepts on top of the SharePoint Framew ## Implementation The below piece of code in gulpfile.js is the key to update the build pipeline: -``` +```typescript //************START: Added to handle Template file url ************/ var inlineNgxTemplate = require('gulp-inline-ngx-template'); diff --git a/samples/bootstrap-slider/README.md b/samples/bootstrap-slider/README.md index 7ca444da5..da58718d6 100644 --- a/samples/bootstrap-slider/README.md +++ b/samples/bootstrap-slider/README.md @@ -1,19 +1,20 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - jQuery - createdDate: 5/1/2017 12:00:00 AM ---- -## Bootstrap Slider Built with jQueryr v1.x and Boostrap v3.x +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - jQuery + createdDate: 5/1/2017 12:00:00 AM +--- +## Bootstrap Slider Built with jQuery v1.x and Boostrap v3.x ## Summary Sample bootstrap slider which pulls the slides from a list inside the SharePoint site. The list is automatically deployed once the app is installed in the SharePoint site. @@ -63,7 +64,7 @@ Version|Date|Comments - add the app to your SharePoint Online site - When the app is finished installing you should see a **SPFx List** in the **Site Contents** of the site ![Deployed List](./assets/List.png) -- Add items to the **SPFx List** inorder for slides to display in the webpart +- Add items to the **SPFx List** in order for slides to display in the webpart diff --git a/samples/handlebarsjs-webpack-loader/README.md b/samples/handlebarsjs-webpack-loader/README.md index 83c71266c..354053b78 100644 --- a/samples/handlebarsjs-webpack-loader/README.md +++ b/samples/handlebarsjs-webpack-loader/README.md @@ -1,19 +1,21 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - createdDate: 3/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - Handlebars + createdDate: 3/5/2017 12:00:00 AM +--- ## SPFx Sample with Handlebars.js -This sample demonstrate how to set up SPFX to use [Handlebars](http://handlebarsjs.com) through [webpack loader](https://webpack.github.io/docs/loaders.html). +This sample demonstrate how to set up SPFx to use [Handlebars](http://handlebarsjs.com) through [webpack loader](https://webpack.github.io/docs/loaders.html). ## Used SharePoint Framework Version diff --git a/samples/jquery-cdn/README.md b/samples/jquery-cdn/README.md index d5fe010fd..52d75f04b 100644 --- a/samples/jquery-cdn/README.md +++ b/samples/jquery-cdn/README.md @@ -1,21 +1,22 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - JQuery - createdDate: 1/1/2016 12:00:00 AM - scenarios: - - Embed ---- -# Using jQuery loaded from CDN +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - JQuery + createdDate: 9/16/2016 12:00:00 AM + scenarios: + - Embed +--- +# Using jQuery loaded from CDN ## Summary @@ -65,4 +66,4 @@ This web part illustrates the following concepts on top of the SharePoint Framew - using non-reactive web part Property Pane - using conditional rendering for one-time web part setup - \ No newline at end of file + diff --git a/samples/js-advanced-commenting/README.md b/samples/js-advanced-commenting/README.md index 546b72342..8ccab2adb 100644 --- a/samples/js-advanced-commenting/README.md +++ b/samples/js-advanced-commenting/README.md @@ -40,7 +40,7 @@ ![Advanced-Comments-Box](./assets/Advanced-Comments-Box.gif) ## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/version-GA-green.svg) +![1.9.1](https://img.shields.io/badge/version-1.9.1-green.svg) ## Applies to @@ -86,3 +86,5 @@ This solution doesn't work on local mode. #### SharePoint Mode If you want to try on a real environment, open: [O365 Workbench](https://your-domain.sharepoint.com/_layouts/15/workbench.aspx) + + diff --git a/samples/js-display-list/README.md b/samples/js-display-list/README.md index bfc361388..69531261e 100644 --- a/samples/js-display-list/README.md +++ b/samples/js-display-list/README.md @@ -1,22 +1,21 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - createdDate: 1/1/2016 12:00:00 AM ---- -# Display List JavaScript Client-Side Web Part +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + createdDate: 5/15/2017 12:00:00 AM +--- +# Display List JavaScript Client-Side Web Part ## Summary This simplistic sample Web Part demonstrates the use of JavaScript in a SharePoint Framework web part. The properties pane for this web part display a drop down list of lists in the current web. Once the user selects one of the lists, the web part display the contents of the list. - ![Screeshot of the Display List web part](./assets/display-list-preview.png). > Does only show data when hosted in SharePoint. No mock data at this point for local testing the rendering. @@ -71,4 +70,4 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew * Logging * Rendering error messages. - \ No newline at end of file + diff --git a/samples/js-employee-spotlight/README.md b/samples/js-employee-spotlight/README.md index f62fa4a70..840eed219 100644 --- a/samples/js-employee-spotlight/README.md +++ b/samples/js-employee-spotlight/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 8/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 8/1/2017 12:00:00 AM +--- # Display Employee Spotlight JavaScript Client-Side Web Part ## Summary diff --git a/samples/js-skype-status/README.md b/samples/js-skype-status/README.md index 9aa8e140d..867b8823b 100644 --- a/samples/js-skype-status/README.md +++ b/samples/js-skype-status/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -- skype -- office-skype-business -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - createdDate: 12/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +- skype +- office-skype-business +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + createdDate: 12/1/2017 12:00:00 AM +--- # JavaScript Skype Status WebPart ## Summary @@ -46,7 +46,7 @@ Version|Date|Comments 1.4|March 27th, 2019|Upgrade to SPFx 1.8.0 1.3|November 18th, 2018|Upgrade to SPFx 1.7.0 1.2|July 4th, 2018|Fixed a bug when subscribing to the current user's status -1.1|June 22nd, 2018|Upgraded to SPFX 1.5 +1.1|June 22nd, 2018|Upgraded to SPFx 1.5 1.0|December 1, 2017|Initial release ## Disclaimer diff --git a/samples/js-solution-editions/README.md b/samples/js-solution-editions/README.md index e4ee3fd0b..a31e185c5 100644 --- a/samples/js-solution-editions/README.md +++ b/samples/js-solution-editions/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - createdDate: 9/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + createdDate: 9/1/2017 12:00:00 AM +--- # Handling Multiple Editions of SPFx Solution ## Summary -This sample shows possible approach of handling multiple editions (e.g. trial, lite, full) of SharePoint Framework solution. +This sample shows a possible approach of handling multiple editions (e.g. trial, lite, full) of SharePoint Framework solution. ## Used SharePoint Framework Version ![drop](https://img.shields.io/badge/drop-ga-green.svg) @@ -40,33 +40,33 @@ Version|Date|Comments ## Description ### Use Case -You are an ISV and developing some product that has multiple editions, let's say, trial, lite, full. You want to have separate package file (sppkg) for each edition and also reference different CDNs based on the edition. +You are an ISV and developing some product that has multiple editions, let's say, trial, lite, full. You want to have a separate package file (`.sppkg`) for each edition and also reference different CDNs based on the edition. ### Problems to address Thinking about the use case in details we can point several problems that should be addressed: -- When we're creating a new version we should create 3 separate sppkg files -- Each sppkg file should contain manifest that references different CDN endpoints +- When we're creating a new version we should create 3 separate `.sppkg` files +- Each `.sppkg` file should contain manifest that references different CDN endpoints - It should be easy to upgrade customer from trial to lite and then to full; or directly from trial to full - You should know current edition in the code to execute the logic based on the edition's restrictions ### Approach -This sample shows the approach that is based on custom Gulp task that should be run before bundling and packaging the solution. +This sample shows the approach that is based on a custom Gulp task that should be run before bundling and packaging the solution. The name of the task is `change-build-edition`. Parameter: `edition`. The task updates SPFx solution configuration files to contain edition-specific information: - deploy-azure-storage.json is updated to contain correct `container` value - package-solution.json is updated to contain correct `solution.version` and `paths.zippedPackage` values. In this sample I'm using version's revision - 4th digit - to specify the edition: 0 for trial, 1 for lite, 2 for full. It allows to easily update customers. zippedPackage path is modified to create sppkg in subfolder based on edition configuration. -- write-manifests.json is updated to contain correct CDN endpoint URL. +- write-manifests.json is updated to contain the correct CDN endpoint URL. -Additionally, web part's source code folder contains `custom-config.json` file with `edition` property: +Additionally, the web part's source code folder contains `custom-config.json` file with `edition` property: ``` { "edition": "full" } ``` -This file is modified by custom task as well to contain correct edition. +This file is modified by a custom task as well to contain the correct edition. Later `custom-config.json` is referenced (`require('./custom-config.json')`) in web part code to provide custom logic based on current edition. -Use following commands to build specific edition version: +Use the following commands to build specific edition version: ``` gulp change-build-edition --edition lite gulp bundle --ship diff --git a/samples/js-theme-manager-2019/README.md b/samples/js-theme-manager-2019/README.md index f7dedc7bf..fc30022e5 100644 --- a/samples/js-theme-manager-2019/README.md +++ b/samples/js-theme-manager-2019/README.md @@ -5,9 +5,9 @@ This sample web part provides a user interface for applying a Modern Experience The Theme Palette can be generated using the UI Fabric Theme Generator at: https://fabricweb.z5.web.core.windows.net/pr-deploy-site/refs/pull/9318/merge/theming-designer/index.html. -

The following feature is available within this sample:

+### The following feature is available within this sample: -Apply a theme:
+#### Apply a theme: By providing a Site Collection URL, along with a theme name and palette, the theme will be applied to the Site Collection directly without being added to the tenant Company Theme options.
NOTE: This is a great option to provide theme management of a Site Collection without adding a theme to the "Company Themes" choices within the "Change the Look" options at the tenant level. The web part could be added to a Site Collection App Catalog to ensure availability of the web part is only available to those approved for theme management. ![preview](./assets/apply-a-theme.png) diff --git a/samples/js-theme-manager/README.md b/samples/js-theme-manager/README.md index 244294e0b..d4e2f644d 100644 --- a/samples/js-theme-manager/README.md +++ b/samples/js-theme-manager/README.md @@ -5,21 +5,21 @@ This sample web part provides a user interface for creating, updating, deleting The Theme Palette can be generated using the UI Fabric Theme Generator at: https://developer.microsoft.com/en-us/fabric#/styles/themegenerator -

The following four features are available within this sample:

+### The following four features are available within this sample: -Create a theme:
+#### Create a theme: Using a provided theme name and theme color palette a Modern Experience them is created and available at the tenant level. ![preview](./assets/create-a-theme.png) -Update a theme:
+#### Update a theme: By selecting a pre-existing theme from the dropdown, the theme at the tenant level will be updated with the palette provided in the Theme Palette texbox. ![preview](./assets/update-a-theme.png) -Delete a theme:
+#### Delete a theme: By selecting a pre-existing theme from the dropdown, the theme will be deleted from the tenant level. ![preview](./assets/delete-a-theme.png) -Appply a theme:
+#### Apply a theme: By providing a Site Collection URL, along with a theme name and palette, the theme will be applied to the Site Collection directly without being added to the tenant Company Theme options.
NOTE: This is a great option to provide theme management of a Site Collection without adding a theme to the "Company Themes" choices within the "Change the Look" options at the tenant level. The web part could be added to a Site Collection App Catalog to ensure availability of the web part is only available to those approved for theme management. ![preview](./assets/apply-a-theme.png) diff --git a/samples/js-workbench-customizer/README.md b/samples/js-workbench-customizer/README.md index d778d73f6..4a844a7be 100644 --- a/samples/js-workbench-customizer/README.md +++ b/samples/js-workbench-customizer/README.md @@ -19,8 +19,8 @@ The easiest way to use the solution is to package it up and deploy to the App Ca Alternatively, you can add the output files for the web part to a custom SPFx project and the web part will also be served and available both from the local or hosted Workbench page: * Generate the output files for the solution by executing `gulp bundle --ship` -* Copy all files from js-workbench-customizer\dist to the dist folder of your custom solution -* Copy the workbenchCustomizer folder from js-workbench-customizer\lib\webparts to the corresponding webparts folder of your custom solution +* Copy all files from `js-workbench-customizer\dist` to the `dist` folder of your custom solution +* Copy the `workbenchCustomizer` folder from `js-workbench-customizer\lib\webparts` to the corresponding webparts folder of your custom solution Note: This approach will not "pollute" your solution with additional resources or dependencies as the SPFx toolchain will ignore those additional files by default when you package your solution. You can also clean everything by running `gulp clean` as both the lib and dist folders are deleted and recreated again. Git will also ignore dist and lib folders by default, so the files will never be added to source control. @@ -34,7 +34,7 @@ Note: This approach will not "pollute" your solution with additional resources o ## Prerequisites -* Office 365 subscription with SharePoint Online licence +* Office 365 subscription with SharePoint Online license * SharePoint Framework [development environment](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) already set up. ## Solution diff --git a/samples/knockout-taxonomy/README.md b/samples/knockout-taxonomy/README.md index 927d8518a..b97be6d99 100644 --- a/samples/knockout-taxonomy/README.md +++ b/samples/knockout-taxonomy/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - knockout - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - knockout + createdDate: 1/1/2016 12:00:00 AM +--- # Taxonomy Web Part ## Summary @@ -66,4 +66,4 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew - creating custom Knockout components - styling HTML elements to match Fabric UI experience - \ No newline at end of file + diff --git a/samples/react-3rd-party-api/README.md b/samples/react-3rd-party-api/README.md index 7d72a2067..ecdef98cc 100644 --- a/samples/react-3rd-party-api/README.md +++ b/samples/react-3rd-party-api/README.md @@ -2,12 +2,12 @@ ## Summary -This sub folders contains a client-side project that shows how to consume a 3rd party API within SharePoint Framework. +This sub folder contains a client-side project that shows how to consume a 3rd party API within SharePoint Framework. ![The UI of the sample application](images/react-3rd-party-api-ui-sample.gif) In subfolder _Server-API_ you can find the sample REST API built using ASP.NET MVC. -You will need to publish the web application on an hosting environment (for example an Azure App Service), configure CORS, and configure the application in the Azure AD tenant under the cover of your target SharePoint Online tenant. +You will need to publish the web application on a hosting environment (for example an Azure App Service), configure CORS, and configure the application in the Azure AD tenant under the cover of your target SharePoint Online tenant. ## Solution @@ -62,7 +62,7 @@ Now run the following command to install the npm packages: npm install ``` -This will install the required npm packages and depedencies to build and run the client-side project. +This will install the required npm packages and dependencies to build and run the client-side project. Once the npm packages are installed, run the command to preview your web parts in SharePoint Workbench: diff --git a/samples/react-aad-implicitflow/README.md b/samples/react-aad-implicitflow/README.md index 407ad6050..1a2a30194 100644 --- a/samples/react-aad-implicitflow/README.md +++ b/samples/react-aad-implicitflow/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - - OAuth 2.0 - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + - OAuth 2.0 + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM +--- # Azure Active Directory implicit flow authentication samples ## Summary @@ -64,7 +64,7 @@ Version|Date|Comments - enable OAuth implicit flow - grant the application the **Microsoft Graph/Read user calendars** permission - copy the application's ID -- in the **src/webparts/upcomingMeetings/AdalConfig.ts** file in the **clientId** property enter the application ID registered in Azure +- in the `src/webparts/upcomingMeetings/AdalConfig.ts` file in the `clientId` property enter the application ID registered in Azure - in the command line execute - `npm i` - `gulp serve --nobrowser` @@ -82,4 +82,4 @@ Sample web part in this solution illustrates the following concepts on top of th - using the ADAL JS library with SharePoint Framework web parts built using React - passing web part properties to React components -![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-aad-implicitflow) \ No newline at end of file +![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-aad-implicitflow) diff --git a/samples/react-aad-webapi/README.md b/samples/react-aad-webapi/README.md index 845e280d9..88a98e020 100644 --- a/samples/react-aad-webapi/README.md +++ b/samples/react-aad-webapi/README.md @@ -84,13 +84,13 @@ Version|Date|Comments ### Configure the web part -- in the command line change the working directory to the **webpart** folder +- in the command line change the working directory to the `webpart` folder - in the command line run `npm i` -- in your code editor open the **webpart** folder -- in the **./src/webparts/recentOrders/AdalConfig.ts** file - - replace the empty GUID in the **clientId** property with the application ID of the **Orders** application - - in the **endpoints** property, replace the URL of the API with the URL of your API App and the empty GUID with the application ID of the **Orders API** app. -- in the **./src/webparts/recentOrders/components/RecentOrders.tsx** file +- in your code editor open the `webpart` folder +- in the `./src/webparts/recentOrders/AdalConfig.ts` file + - replace the empty GUID in the `clientId` property with the application ID of the **Orders** application + - in the `endpoints` property, replace the URL of the API with the URL of your API App and the empty GUID with the application ID of the **Orders API** app. +- in the `./src/webparts/recentOrders/components/RecentOrders.tsx` file - in line 155 replace the empty GUID with the application ID of the **Orders API** application - in line 185 replace the URL with the URL of your API App - in the command line execute `gulp serve` diff --git a/samples/react-accordion/README.md b/samples/react-accordion/README.md index 00c276dce..6319f8bea 100644 --- a/samples/react-accordion/README.md +++ b/samples/react-accordion/README.md @@ -67,8 +67,8 @@ This sample illustrates the following concepts on top of the SharePoint Framewor ### General - performing SharePoint GET operation in React using inbuilt SP Http Client -- Using Fabric UI button component for pagination -- optimizing REST responses for performance using nometadata option of JSON light +- Using Fabric UI button component for pagination +- optimizing REST responses for performance using `nometadata` option of JSON light - using PnP Webpart title control of @pnp/spfx-controls-react library - showing SharePoint list data in Accordion format using React Accessible Accordion plugin - searching in the fetched data by making use of Search Box from Office Fabric UI diff --git a/samples/react-adaptive-cards-image-gallery/README.md b/samples/react-adaptive-cards-image-gallery/README.md index 892750928..fb19e8843 100644 --- a/samples/react-adaptive-cards-image-gallery/README.md +++ b/samples/react-adaptive-cards-image-gallery/README.md @@ -58,8 +58,8 @@ Version|Date|Comments ## Minimal Path to Awesome - Clone this repo -- npm i -- gulp serve --nobrowser +- `npm i` +- `gulp serve --nobrowser` - Open workbench on your tenant, i.e. https://contoso.sharepoint.com/sites/salesteam/_layouts/15/workbench.aspx - Search and add web part "Adaptive Cards Image Gallery" diff --git a/samples/react-app-settings/README.md b/samples/react-app-settings/README.md index d368c9c57..9aeab9591 100644 --- a/samples/react-app-settings/README.md +++ b/samples/react-app-settings/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 8/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 8/1/2017 12:00:00 AM +--- # SPFx React app settings webpart # ## Summary @@ -75,5 +75,3 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew - The use of app settings and passing the app settings to React components. - - diff --git a/samples/react-carousel/README.md b/samples/react-carousel/README.md index fce771c42..fd63830d3 100644 --- a/samples/react-carousel/README.md +++ b/samples/react-carousel/README.md @@ -2,13 +2,13 @@ ## Summary -This web part show images and videos in carousel +This web part show images and videos in carousel -It uses Microsoft Graph API to get image/video url and use PnPjs to load files from Picture library the images/videos are loading in lazy mode, progressively. +It uses Microsoft Graph API to get image/video url and use PnPjs to load files from Picture library the images/videos are loading in lazy mode, progressively. ## -![callendar](/samples/react-carousel/assets/carousel.gif) +![calendar](/samples/react-carousel/assets/carousel.gif) diff --git a/samples/react-chartcontrol/README.md b/samples/react-chartcontrol/README.md index d416de5ab..d2fc17070 100644 --- a/samples/react-chartcontrol/README.md +++ b/samples/react-chartcontrol/README.md @@ -205,7 +205,7 @@ Version|Date|Comments * in the command line run: * `npm install` * `gulp serve` -* Insert the one of the webs part on a page +* Insert one of the webs part on a page ## Features diff --git a/samples/react-check-flows/README.md b/samples/react-check-flows/README.md index 35ddf86c2..ac425bdf6 100644 --- a/samples/react-check-flows/README.md +++ b/samples/react-check-flows/README.md @@ -33,3 +33,4 @@ react-check-flows|Aakash Bhardwaj **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.** --- + \ No newline at end of file diff --git a/samples/react-check-user-group/README.md b/samples/react-check-user-group/README.md index 7b64dc6a0..7063c1496 100644 --- a/samples/react-check-user-group/README.md +++ b/samples/react-check-user-group/README.md @@ -16,7 +16,7 @@ This web part uses Microsoft Graph API to get all the Office 365 or AAD Security This extension illustrates the following concepts: -* Requesting **Directory.Read.All** permission scope for Microsoft Graph through the webApiPermissionRequests property in package-solution.json +* Requesting **Directory.Read.All** permission scope for Microsoft Graph through the `webApiPermissionRequests` property in `package-solution.json` * Using MSGraphClient to call the **/groups/{groupId}/members** API to get all the members in a group * Using MSGraphClient to call the **/users/${email}/memberOf** API to get all the groups a user is member of * Exporting the results to a CSV file using [**react-csv**](https://www.npmjs.com/package/react-csv) third party package @@ -42,3 +42,4 @@ react-check-user-group | [Aakash Bhardwaj](https://twitter.com/aakash_316) **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.** --- + diff --git a/samples/react-components-dynamicloading/README.md b/samples/react-components-dynamicloading/README.md index aa14237b7..fd4ea5ae3 100644 --- a/samples/react-components-dynamicloading/README.md +++ b/samples/react-components-dynamicloading/README.md @@ -1,7 +1,7 @@ # Code Splitting in SharePoint Framework ## Summary -Load React components and third party packages on demand in SPFx +Load React components and third-party packages on demand in SPFx ![Dynamic Loading of React components](./assets/cs3.gif) @@ -40,4 +40,4 @@ Version|Date|Comments ## Features More details included in this blog post: https://www.vrdmn.com/2018/10/code-splitting-in-sharepoint-framework.html - \ No newline at end of file + diff --git a/samples/react-content-query-webpart/README.md b/samples/react-content-query-webpart/README.md index 0eeec450c..a47a72384 100644 --- a/samples/react-content-query-webpart/README.md +++ b/samples/react-content-query-webpart/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 8/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 8/1/2017 12:00:00 AM +--- # React Content Query WebPart ## Summary @@ -276,7 +276,7 @@ Both functions provide the following parameters : Parameter | Description ----------------------|------------- -**wpContext** | Represents the context of the WebPart who called the function, which exposes all kinds of usefull informations such as **wpContext.domElement** which represents the HTML element on which the current WebPart is being rendered. +**wpContext** | Represents the context of the WebPart who called the function, which exposes all kinds of useful informations such as **wpContext.domElement** which represents the HTML element on which the current WebPart is being rendered. **handlebarsContext** | Represents the handlebars context used for generating the template of the current WebPart. Can be used for adding handlebar block helpers in the **onPreRender** function for example. diff --git a/samples/react-covid19-info/README.md b/samples/react-covid19-info/README.md index 11b352fac..cd12e4ed4 100644 --- a/samples/react-covid19-info/README.md +++ b/samples/react-covid19-info/README.md @@ -68,7 +68,7 @@ Version|Date|Comments This Web Part illustrates the following concepts on top of the SharePoint Framework: * Using external APIs using httpClient -* [Office Fabric UI REact](https://developer.microsoft.com/en-us/fabric#/) +* [Office Fabric UI React](https://developer.microsoft.com/en-us/fabric#/) * [SPFx Controls React](https://sharepoint.github.io/sp-dev-fx-controls-react/) * [SPFx Property Controls](https://sharepoint.github.io/sp-dev-fx-property-controls/) * [Recharts](http://recharts.org/en-US/) diff --git a/samples/react-custompropertypanecontrols/README.md b/samples/react-custompropertypanecontrols/README.md index 4d953bbec..740359aa0 100644 --- a/samples/react-custompropertypanecontrols/README.md +++ b/samples/react-custompropertypanecontrols/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM +--- # Custom property pane controls built in React ## Summary @@ -87,6 +87,6 @@ Sample web parts in this solution illustrate the following concepts on top of th - displaying a custom loading indicator - reacting to web part property changes - updating properties of web part property pane controls -- refreshing the web part property pane from web part +- refreshing the web part property pane from the web part -![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-custompropertypanecontrols) \ No newline at end of file +![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-custompropertypanecontrols) diff --git a/samples/react-designpatterns-typescript/AbstractFactory/README.md b/samples/react-designpatterns-typescript/AbstractFactory/README.md index 09f5951eb..550af1ec7 100644 --- a/samples/react-designpatterns-typescript/AbstractFactory/README.md +++ b/samples/react-designpatterns-typescript/AbstractFactory/README.md @@ -1,381 +1,381 @@ -# Abstract Factory Design Pattern - -## Summary -The abstract factory pattern will allow to define an interface for the creation of objects without specifying their concrete classes. The objective of this pattern is that a class depends on the behavior of the abstract factory, which in turn will be implemented by different concrete classes that are changed at runtime based on some kind of configuration or predefined parameter. - - -## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/version-GA-green.svg) - -## Applies to - -* [SharePoint Framework](https:/dev.office.com/sharepoint) -* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) - - -## Prerequisites - -> N/A - -## Solution - -Solution|Author(s) ---------|--------- -designpatterns-typescript\abstractfactory | [@levalencia](https://www.twitter.com/levalencia) - -## Version history - -Version|Date|Comments --------|----|-------- -1.0|May 15, 2018|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.** - ---- - - -### Abstract factory pattern - -A very good real life scenario where this pattern can be used is in Data Application Layers scenario, more than often developers and architects are faced with requirements where an application needs to be able to access different databases or event different database servers which have different drivers, but the users want to to that without changing a lot of code, something that can be switched from an easy parameter somewhere. - -For the sake of simplicity lets suppose you work at Company A, and company A acquired company B, at company A you have a webpart developed that brings Customer Information from Sharepoint List, but at Company B which was acquired and in the process of merging, they have Product Information in their own CRM which exposes data via REST APIs or just a JSON file. - -The users wants to see their products in the same Sharepoint page using the same webpart, meaning that the webpart needs to be added twice with different parameters to the same page and users can search for customers information on both data sources, with the same source code. - -### Project Structure -![](http://www.luisevalencia.com/content/images/2018/01/2018-01-04-12_33_49-TypescriptDesignPatterns02AbstractFactoryWebPart.ts---TypescriptDesignPatterns02.png) - -As seen above we have a Factory component and in there we have all files that our project needs, lets discuss them one by one. - -##### Customer.ts -Our model or data access object, nothing to fancy, the idea is to show the pattern, not complex Data Transfer Objects. - -```typescript -class Customer{ - public id: string; - public firstName: string; - public lastName: string; -} - -export default Customer; - -``` - -#### DatasourcesEnum.ts - -Yay!, we have Enums on typescript, and this will allows to ease work with selections on dropdowns, checkboxlists, etc. In this case is just a dropdown list with 2 options, but I guess you see the benefit here. - -```typescript - -enum DataSources { - SharepointList = "SharepointList", - JsonData = "JsonData" -} - -export default DataSources; - -``` - -##### DaoFactory.ts -This is the abstract class DAO Factory that would need to be implemented, for the ease of sake, I am doing only one DAO, Customers, but you can use the same pattern for many different DTOs as well on the same class. - -```typescript -import ICustomerDAO from "./ICustomerDAO"; - -import DataSources from "./DatasourcesEnum"; - -abstract class DAOFactory { - - public abstract getCustomerDAO(): ICustomerDAO; - - public static getDAOFactory(whichFactory: DataSources): DAOFactory { - switch (whichFactory) { - case DataSources.SharepointList: - return new SharepointListDAOFactory(); - case DataSources.JsonData: - return new JsonDAOFactory(); - default : - return null; - } - } -} - -export default DAOFactory; -import SharepointListDAOFactory from "./SharepointListDAOFactory"; -import JsonDAOFactory from "./JsonDAOFactory"; - -``` - -##### JsoDAOFactory.ts -This class is just the implementation of the factory method -```typescript -import DAOFactory from "./DaoFactory"; -import JsonCustomerDAO from "./JsonCustomerDAO"; -import ICustomerDao from "./ICustomerDao"; - -class JsonDAOFactory extends DAOFactory { - public getCustomerDAO(): ICustomerDao{ - return new JsonCustomerDAO(); - } -} - -export default JsonDAOFactory; -``` - -##### SharepointListDAOFactory.ts -This class is just the implementation of the factory method -```typescript -import DaoFactory from "./DaoFactory"; -import ICustomerDao from "./ICustomerDao"; -import SharepointCustomerDao from "./SharepointCustomerDAO"; - -class SharepointListDAOFactory extends DaoFactory { - public getCustomerDAO(): ICustomerDao{ - return new SharepointCustomerDao(); - } -} - -export default SharepointListDAOFactory; - -``` - - -##### ICustomerDao.ts -Now, this is the customer interface which defines the methods that would need to be implemented and that depends on the data source endpoint, database or driver, or whatever. - -```typescript -import Customer from "./Customer"; - - interface ICustomerDao { - insertCustomer(): number; - deleteCustomer(): boolean; - findCustomer(): Customer; - updateCustomer(): boolean; - listCustomers(): Customer[]; -} - -export default ICustomerDao; -``` - -##### JsonCustomerDAO.ts - -Implementation on these methods are left to the reader, but the main idea here is to implement based on the datasource the Data Access Logic here and return the strongly typed objects where needed. - -```typescript -import ICustomerDao from "./ICustomerDao"; -import Customer from "./Customer"; - - class JsonCustomerDAO implements ICustomerDao{ - public insertCustomer(): number { - // implementation to be done by reader - return 1; - } - - public deleteCustomer(): boolean { - // implementation to be done by reader - return true; - } - - public findCustomer(): Customer { - // implementation to be done by reader - return new Customer(); - } - - public updateCustomer(): boolean { - // implementation to be done by reader - return true; - } - - public listCustomers(): Customer[] { - // implementation to be done by reader - let c1: Customer= new Customer(); - let c2: Customer= new Customer(); - let list: Array = [c1, c2 ]; - return list; - } -} - -export default JsonCustomerDAO; -``` -##### SharepointCustomerDAO.ts - -Implementation on these methods are left to the reader, but the main idea here is to implement based on the datasource the Data Access Logic here and return the strongly typed objects where needed. - -```typescript -import ICustomerDao from "./ICustomerDao"; -import Customer from "./Customer"; - - class SharepointCustomerDao implements ICustomerDao { - public insertCustomer(): number { - // implementation to be done by reader - return 1; - } - - public deleteCustomer(): boolean { - // implementation to be done by reader - return true; - } - - public findCustomer(): Customer { - // implementation to be done by reader - return new Customer(); - } - - public updateCustomer(): boolean { - // implementation to be done by reader - return true; - } - - public listCustomers(): Customer[] { - // implementation to be done by reader - let c1: Customer = new Customer(); - let c2: Customer = new Customer(); - let list: Array = [c1, c2 ]; - return list; - } -} - -export default SharepointCustomerDao; -``` - -##### The component -This is where we actually see the entire benefit of the abstract factory pattern, as you can see the code is really short here and easy to read, no custom business logic, and everything so easy to maintain. - -We create a private property of type ICustomerDao to be instantiated on the setDaos method based on the input of the user in the property pane. This method is only called in the constructor once. - -And then in the render method we just get the Customer items from the datasource, and as you can see, its totally generic, no custom logic based on the datasource selected. - -```typescript -import * as React from 'react'; -import { IAbstractfactoryProps } from "./IAbstractFactoryProps"; -import { IAbstractFactoryState } from "./IAbstractFactoryState"; -import styles from './Abstractfactory.module.scss'; -import { escape } from '@microsoft/sp-lodash-subset'; -import DaoFactory from "./DaoFactory"; -import ICustomerDao from "./ICustomerDao"; -import DataSources from "./DatasourcesEnum"; - -export default class Abstractfactory extends React.Component { - private customerDao: ICustomerDao; - - constructor(props: IAbstractfactoryProps, state: IAbstractFactoryState) { - super(props); - this.setInitialState(); - this.setDaos(props.datasource); - } - - public render(): React.ReactElement { - this.state = { - items: this.customerDao.listCustomers(), - }; - - return null; - } - - public setInitialState(): void { - this.state = { - items: [] - }; - } - - private setDaos(datasource: string): void { - const data: any = datasource === "Sharepoint" ? DataSources.SharepointList : DataSources.JsonData; - this.customerDao = DaoFactory.getDAOFactory(data).getCustomerDAO(); - } -} - - -``` - -And just for your understanding, I show below the props and states clases - -##### IAbstractfactoryProps.ts -```typescript -export interface IAbstractfactoryProps { - datasource: string; -} - - -``` -##### IAbstractFactoryState.ts -```typescript -import Customer from "./Customer"; - -export interface IAbstractFactoryState { - items: Customer[]; - } -``` - -And finally the webpart code -##### AbstractFactoryWebPart.ts -```typescript -import * as React from 'react'; -import * as ReactDom from 'react-dom'; -import { Version } from '@microsoft/sp-core-library'; -import { - BaseClientSideWebPart, - IPropertyPaneConfiguration, - PropertyPaneDropdown -} from "@microsoft/sp-webpart-base"; - -import * as strings from 'AbstractfactoryWebPartStrings'; -import Abstractfactory from './components/Abstractfactory'; -import { IAbstractfactoryProps } from './components/IAbstractfactoryProps'; -import { IAbstractfactoryWebPartProps } from "./IAbstractfactoryWebPartProps"; - - - - -export default class AbstractfactoryWebPart extends BaseClientSideWebPart { - - public render(): void { - const element: React.ReactElement = React.createElement( - Abstractfactory, - { - datasource: this.properties.datasource - } - ); - - ReactDom.render(element, this.domElement); - } - - protected get dataVersion(): Version { - return Version.parse('1.0'); - } - - protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { - return { - pages: [ - { - header: { - description: strings.PropertyPaneDescription - }, - groups: [ - { - groupName: strings.BasicGroupName, - groupFields: [ - PropertyPaneDropdown("datasource", { - label: "DataSource", - options: [ - { key: "1", text: "Sharepoint"}, - { key: "2", text: "JSON" } - ], - selectedKey: "1", - }) - ] - } - ] - } - ] - }; - } -} - - -``` - ->Conclusion: -We all know that Sharepoint Framework Projects are transpiled and bundled into one single JS file, however regardless of that for those of us who have worked in huge projects and are only User Interface Developers, we know that we can do better than what the standard samples show us in the standard documentation, with the above post I wanted to show you how simple is to create maintenable code, code that anyone can read, and later modify. - - \ No newline at end of file +# Abstract Factory Design Pattern + +## Summary +The abstract factory pattern will allow to define an interface for the creation of objects without specifying their concrete classes. The objective of this pattern is that a class depends on the behavior of the abstract factory, which in turn will be implemented by different concrete classes that are changed at runtime based on some kind of configuration or predefined parameter. + + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/version-GA-green.svg) + +## Applies to + +* [SharePoint Framework](https:/dev.office.com/sharepoint) +* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) + + +## Prerequisites + +> N/A + +## Solution + +Solution|Author(s) +--------|--------- +designpatterns-typescript\abstractfactory | [@levalencia](https://www.twitter.com/levalencia) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|May 15, 2018|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.** + +--- + + +### Abstract factory pattern + +A very good real life scenario where this pattern can be used is in Data Application Layers scenario, more than often developers and architects are faced with requirements where an application needs to be able to access different databases or event different database servers which have different drivers, but the users want to to that without changing a lot of code, something that can be switched from an easy parameter somewhere. + +For the sake of simplicity lets suppose you work at Company A, and company A acquired company B, at company A you have a webpart developed that brings Customer Information from SharePoint List, but at Company B which was acquired and in the process of merging, they have Product Information in their own CRM which exposes data via REST APIs or just a JSON file. + +The users wants to see their products in the same SharePoint page using the same webpart, meaning that the webpart needs to be added twice with different parameters to the same page and users can search for customers information on both data sources, with the same source code. + +### Project Structure +![](http://www.luisevalencia.com/content/images/2018/01/2018-01-04-12_33_49-TypescriptDesignPatterns02AbstractFactoryWebPart.ts---TypescriptDesignPatterns02.png) + +As seen above we have a Factory component and in there we have all files that our project needs, lets discuss them one by one. + +##### Customer.ts +Our model or data access object, nothing to fancy, the idea is to show the pattern, not complex Data Transfer Objects. + +```typescript +class Customer{ + public id: string; + public firstName: string; + public lastName: string; +} + +export default Customer; + +``` + +#### DatasourcesEnum.ts + +Yay!, we have Enums on typescript, and this will allows to ease work with selections on dropdowns, checkboxlists, etc. In this case is just a dropdown list with 2 options, but I guess you see the benefit here. + +```typescript + +enum DataSources { + SharepointList = "SharepointList", + JsonData = "JsonData" +} + +export default DataSources; + +``` + +##### DaoFactory.ts +This is the abstract class DAO Factory that would need to be implemented, for the ease of sake, I am doing only one DAO, Customers, but you can use the same pattern for many different DTOs as well on the same class. + +```typescript +import ICustomerDAO from "./ICustomerDAO"; + +import DataSources from "./DatasourcesEnum"; + +abstract class DAOFactory { + + public abstract getCustomerDAO(): ICustomerDAO; + + public static getDAOFactory(whichFactory: DataSources): DAOFactory { + switch (whichFactory) { + case DataSources.SharepointList: + return new SharepointListDAOFactory(); + case DataSources.JsonData: + return new JsonDAOFactory(); + default : + return null; + } + } +} + +export default DAOFactory; +import SharepointListDAOFactory from "./SharepointListDAOFactory"; +import JsonDAOFactory from "./JsonDAOFactory"; + +``` + +##### JsoDAOFactory.ts +This class is just the implementation of the factory method +```typescript +import DAOFactory from "./DaoFactory"; +import JsonCustomerDAO from "./JsonCustomerDAO"; +import ICustomerDao from "./ICustomerDao"; + +class JsonDAOFactory extends DAOFactory { + public getCustomerDAO(): ICustomerDao{ + return new JsonCustomerDAO(); + } +} + +export default JsonDAOFactory; +``` + +##### SharepointListDAOFactory.ts +This class is just the implementation of the factory method +```typescript +import DaoFactory from "./DaoFactory"; +import ICustomerDao from "./ICustomerDao"; +import SharepointCustomerDao from "./SharepointCustomerDAO"; + +class SharepointListDAOFactory extends DaoFactory { + public getCustomerDAO(): ICustomerDao{ + return new SharepointCustomerDao(); + } +} + +export default SharepointListDAOFactory; + +``` + + +##### ICustomerDao.ts +Now, this is the customer interface which defines the methods that would need to be implemented and that depends on the data source endpoint, database or driver, or whatever. + +```typescript +import Customer from "./Customer"; + + interface ICustomerDao { + insertCustomer(): number; + deleteCustomer(): boolean; + findCustomer(): Customer; + updateCustomer(): boolean; + listCustomers(): Customer[]; +} + +export default ICustomerDao; +``` + +##### JsonCustomerDAO.ts + +Implementation on these methods are left to the reader, but the main idea here is to implement based on the datasource the Data Access Logic here and return the strongly typed objects where needed. + +```typescript +import ICustomerDao from "./ICustomerDao"; +import Customer from "./Customer"; + + class JsonCustomerDAO implements ICustomerDao{ + public insertCustomer(): number { + // implementation to be done by reader + return 1; + } + + public deleteCustomer(): boolean { + // implementation to be done by reader + return true; + } + + public findCustomer(): Customer { + // implementation to be done by reader + return new Customer(); + } + + public updateCustomer(): boolean { + // implementation to be done by reader + return true; + } + + public listCustomers(): Customer[] { + // implementation to be done by reader + let c1: Customer= new Customer(); + let c2: Customer= new Customer(); + let list: Array = [c1, c2 ]; + return list; + } +} + +export default JsonCustomerDAO; +``` +##### SharepointCustomerDAO.ts + +Implementation on these methods are left to the reader, but the main idea here is to implement based on the datasource the Data Access Logic here and return the strongly typed objects where needed. + +```typescript +import ICustomerDao from "./ICustomerDao"; +import Customer from "./Customer"; + + class SharepointCustomerDao implements ICustomerDao { + public insertCustomer(): number { + // implementation to be done by reader + return 1; + } + + public deleteCustomer(): boolean { + // implementation to be done by reader + return true; + } + + public findCustomer(): Customer { + // implementation to be done by reader + return new Customer(); + } + + public updateCustomer(): boolean { + // implementation to be done by reader + return true; + } + + public listCustomers(): Customer[] { + // implementation to be done by reader + let c1: Customer = new Customer(); + let c2: Customer = new Customer(); + let list: Array = [c1, c2 ]; + return list; + } +} + +export default SharepointCustomerDao; +``` + +##### The component +This is where we actually see the entire benefit of the abstract factory pattern, as you can see the code is really short here and easy to read, no custom business logic, and everything so easy to maintain. + +We create a private property of type ICustomerDao to be instantiated on the setDaos method based on the input of the user in the property pane. This method is only called in the constructor once. + +And then in the render method we just get the Customer items from the datasource, and as you can see, its totally generic, no custom logic based on the datasource selected. + +```typescript +import * as React from 'react'; +import { IAbstractfactoryProps } from "./IAbstractFactoryProps"; +import { IAbstractFactoryState } from "./IAbstractFactoryState"; +import styles from './Abstractfactory.module.scss'; +import { escape } from '@microsoft/sp-lodash-subset'; +import DaoFactory from "./DaoFactory"; +import ICustomerDao from "./ICustomerDao"; +import DataSources from "./DatasourcesEnum"; + +export default class Abstractfactory extends React.Component { + private customerDao: ICustomerDao; + + constructor(props: IAbstractfactoryProps, state: IAbstractFactoryState) { + super(props); + this.setInitialState(); + this.setDaos(props.datasource); + } + + public render(): React.ReactElement { + this.state = { + items: this.customerDao.listCustomers(), + }; + + return null; + } + + public setInitialState(): void { + this.state = { + items: [] + }; + } + + private setDaos(datasource: string): void { + const data: any = datasource === "Sharepoint" ? DataSources.SharepointList : DataSources.JsonData; + this.customerDao = DaoFactory.getDAOFactory(data).getCustomerDAO(); + } +} + + +``` + +And just for your understanding, I show below the props and states clases + +##### IAbstractfactoryProps.ts +```typescript +export interface IAbstractfactoryProps { + datasource: string; +} + + +``` +##### IAbstractFactoryState.ts +```typescript +import Customer from "./Customer"; + +export interface IAbstractFactoryState { + items: Customer[]; + } +``` + +And finally the webpart code +##### AbstractFactoryWebPart.ts +```typescript +import * as React from 'react'; +import * as ReactDom from 'react-dom'; +import { Version } from '@microsoft/sp-core-library'; +import { + BaseClientSideWebPart, + IPropertyPaneConfiguration, + PropertyPaneDropdown +} from "@microsoft/sp-webpart-base"; + +import * as strings from 'AbstractfactoryWebPartStrings'; +import Abstractfactory from './components/Abstractfactory'; +import { IAbstractfactoryProps } from './components/IAbstractfactoryProps'; +import { IAbstractfactoryWebPartProps } from "./IAbstractfactoryWebPartProps"; + + + + +export default class AbstractfactoryWebPart extends BaseClientSideWebPart { + + public render(): void { + const element: React.ReactElement = React.createElement( + Abstractfactory, + { + datasource: this.properties.datasource + } + ); + + ReactDom.render(element, this.domElement); + } + + protected get dataVersion(): Version { + return Version.parse('1.0'); + } + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + return { + pages: [ + { + header: { + description: strings.PropertyPaneDescription + }, + groups: [ + { + groupName: strings.BasicGroupName, + groupFields: [ + PropertyPaneDropdown("datasource", { + label: "DataSource", + options: [ + { key: "1", text: "Sharepoint"}, + { key: "2", text: "JSON" } + ], + selectedKey: "1", + }) + ] + } + ] + } + ] + }; + } +} + + +``` + +>Conclusion: +We all know that SharePoint Framework Projects are transpiled and bundled into one single JS file, however regardless of that for those of us who have worked in huge projects and are only User Interface Developers, we know that we can do better than what the standard samples show us in the standard documentation, with the above post I wanted to show you how simple is to create maintenable code, code that anyone can read, and later modify. + + diff --git a/samples/react-designpatterns-typescript/Builder/README.md b/samples/react-designpatterns-typescript/Builder/README.md index 3745c4283..a7c0b8091 100644 --- a/samples/react-designpatterns-typescript/Builder/README.md +++ b/samples/react-designpatterns-typescript/Builder/README.md @@ -1,480 +1,480 @@ -# Builder Design Pattern - -## Summary -Builder pattern builds a complex object using simple objects and using a step by step approach. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. - - -## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/version-GA-green.svg) - -## Applies to - -* [SharePoint Framework](https:/dev.office.com/sharepoint) -* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) - - -## Prerequisites - -> N/A - -## Solution - -Solution|Author(s) ---------|--------- -designpatterns-typescript\builder | [@levalencia](https://www.twitter.com/levalencia) - -## Version history - -Version|Date|Comments --------|----|-------- -1.0|May 15, 2018|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.** - ---- - - -## Builder pattern - -A Builder class builds the final object step by step. This builder is independent of other objects. - -For this pattern, we have taken an existing example https://www.tutorialspoint.com/design_pattern/builder_pattern.htm and translated it to Typescript. Data Access implementation details are left to the reader. - -The idea on this example is to show how you can build a Complex object from single objects, a Meal from (burger, fries, soda). Suppose you have a Sharepoint List for Burgers, another list for Sodas, another one for desserts, and you want to build different Meals (Menus), so this would be a perfect sample. - -### UML -This is more or less the diagram of the classes were are coding below. - -![](http://www.luisevalencia.com/content/images/2018/02/builder_pattern_uml_diagram.jpg/content/images/2018/02/builder_pattern_uml_diagram.jpg) - - -### Project structure -We have created a component with all the needed class, lets discuss them one by one. - -![](http://www.luisevalencia.com/content/images/2018/02/builder_pattern_uml_diagram.jpg/content/images/2018/02/typescript.png) - - -### IItem.ts - -This interface is the one that every item needs to implement to come with a common structure for all products. - -```typescript -import IPacking from "./IPacking"; - -interface IItem { - name(): string; - packing(): IPacking; - price(): number; -} - -export default IItem; -``` - -### IPacking.ts - -This interface is the one that all packaging will use, eg: Bottle, Wrapper, etc, its the way to define common behavior and properties for each product packing. - -```typescript -interface IPacking { - pack(): string; -} - -export default IPacking; -``` - -### Bottle.ts -This is one type of packing, it implements the IPacking interface. - -```typescript -import IPacking from "./IPacking"; - -class Bottle implements IPacking { - public pack(): string { - return "Bottle"; - } -} - -export default Bottle; -``` - - -### Wrapper.ts - -```typescript -import IPacking from "./IPacking"; - -class Wrapper implements IPacking { - public pack(): string { - return "Wrapper"; - } -} - -export default Wrapper; -``` - -### Burger.ts -This is an abstract class from which all our specific burgers need to implement, its there to have a common structure for name, packing and pricing. - -```typescript -import IItem from "./IItem"; -import Wrapper from "./Wrapper"; -import IPacking from "./IPacking"; - -abstract class Burger implements IItem { - public name(): string { - throw new Error("Method not implemented."); - } - - public packing(): IPacking { - return new Wrapper(); - } - - public abstract price(): number ; - -} - -export default Burger; -``` - -### ChickenBurger.ts -```typescript -import Burger from "./Burger"; - -class ChickenBurger extends Burger { - public price(): number { - return 15; - } - - public name(): string { - return "Chicken Burger"; - } -} - -export default ChickenBurger; - -``` - -### VegBurger.ts -```typescript -import Burger from "./Burger"; - -class VegBurger extends Burger { - public price(): number { - return 11; - } - - public name(): string { - return "Veg Burger"; - } -} - -export default VegBurger; - -``` -### Colddrink.ts -```typescript -import IItem from "./IItem"; -import IPacking from "./IPacking"; -import Bottle from "./Bottle"; - -abstract class ColdDrink implements IItem { - public name(): string { - throw new Error("Method not implemented."); - } - public packing(): IPacking { - return new Bottle(); - } - - public abstract price(): number ; - -} - -export default ColdDrink; - -``` -### Coke.ts -```typescript -import ColdDrink from "./ColdDrink"; - -class Coke extends ColdDrink { - public price(): number { - return 2.5; - } - - public name(): string { - return "Coca Cola"; - } -} - -export default Coke; - - -``` -### Pepsi.ts -```typescript -import ColdDrink from "./ColdDrink"; - -class Pepsi extends ColdDrink { - public price(): number { - return 1.5; - } - - public name(): string { - return "Pepsi Cola"; - } -} - -export default Pepsi; - -``` -### Meal.ts - -This class will represent a full meal behavior, here we have the methods to add items to the Meal, get the cost and show the items belonging to the Meal. - -```typescript -import IItem from "./IItem"; - -class Meal { - private items: IItem[] = []; - - public addItem(item: IItem): void { - this.items.push(item); - } - - public getCost(): number { - let cost: number = 0; - for(let item of this.items) { - cost+= item.price(); - } - - return cost; - } - - public showItems(): string { - let returnStr: string = ""; - for(let item of this.items) { - returnStr +="Item:" + item.name(); - returnStr +=", Packing:" + item.packing().pack(); - returnStr +=", Price: " + item.price(); - } - - returnStr += ", Total: " + this.getCost(); - return returnStr; - } -} - -export default Meal; -``` - -### MealBuilder.ts - -Mealbuilder its just the class that uses the classes explained before to construct any type of meal, for sake of simplicity, we created only 2 meals here. - -```typescript -import Meal from "./Meal"; -import VegBurger from "./VegBurger"; -import Coke from "./Coke"; -import ChickenBurger from "./ChickenBurger"; - -class MealBuilder { - public prepareVegMeal(): Meal { - let meal: Meal= new Meal(); - meal.addItem(new VegBurger()); - meal.addItem(new Coke()); - return meal; - } - - public prepareNonVegMeal(): Meal { - let meal: Meal= new Meal(); - meal.addItem(new ChickenBurger()); - meal.addItem(new Coke()); - return meal; - } -} - -export default MealBuilder; -``` - -### IBuilderProps.ts -We created a selectedMeal string property to take the decision on which meal to build. - -```typescript -export interface IBuilderProps { - selectedMeal: string; -} - -``` - -### Builder.tsx - -This is our component class, here we have a constructor and in the constructor we call the setMeal method, with the selected meal option as a parameter, and then we can define which meal to prepare. Once the meal is prepared, in the render method we can use the showItems method -```typescript -import * as React from 'react'; -import styles from './Builder.module.scss'; -import { IBuilderProps } from './IBuilderProps'; -import { escape } from '@microsoft/sp-lodash-subset'; -import MealBuilder from "./MealBuilder"; -import Meal from "./Meal"; -import { IPropertyPaneConfiguration } from "@microsoft/sp-webpart-base"; -import { - PropertyPaneDropdown -} from "@microsoft/sp-webpart-base"; -import {Version} from "@microsoft/sp-core-library"; -import { IBuilderState } from './IBuilderState'; - -export default class Builder extends React.Component { - - private mealBuilder: MealBuilder ; - private items: string; - private meal: Meal; - - constructor(props: IBuilderProps, state: IBuilderState) { - super(props); - this.setInitialState(); - //this.setMeal = this.setMeal.bind(this); - this.mealBuilder = new MealBuilder(); - this.setMeal(props.selectedMeal); - } - - public setInitialState(): void { - this.state = { - items: "" - }; - } - - public componentWillReceiveProps(nextProps: IBuilderProps): void { - if(nextProps.selectedMeal !== this.props.selectedMeal) { - this.setMeal(nextProps.selectedMeal); - } - } - - public render(): React.ReactElement { - return ( -
-
-
-
- Welcome to Mac Luis! -

You have selected the following.

-
{this.state.items}
-
-
-
-
- ); - } - protected get dataVersion(): Version { - return Version.parse("1.0"); - } - - private setMeal(selectedMeal: string): void { - if(selectedMeal===undefined){ - this.meal = this.mealBuilder.prepareVegMeal(); - } - - if(selectedMeal === "0") { - this.meal = this.mealBuilder.prepareVegMeal(); - } - - if(selectedMeal === "1") { - this.meal = this.mealBuilder.prepareNonVegMeal(); - } - - this.state = { - items: this.meal.showItems(), - }; - } -} - - - -``` - -And finally -### BuilderWebPart.ts - -Here what we do is just to use our component and sending the parameter of the selected meal, which is just a normal dropdown with 2 hardcoded values. - - -```typescript -import * as React from 'react'; -import * as ReactDom from 'react-dom'; -import { Version } from '@microsoft/sp-core-library'; -import { - BaseClientSideWebPart, - IPropertyPaneConfiguration, - PropertyPaneTextField, - PropertyPaneDropdown -} from "@microsoft/sp-webpart-base"; - - -import * as strings from 'BuilderWebPartStrings'; -import Builder from './components/Builder'; -import { IBuilderProps } from './components/IBuilderProps'; - -export interface IBuilderWebPartProps { - selectedMeal: string; -} - -export default class BuilderWebPart extends BaseClientSideWebPart { - - public render(): void { - const element: React.ReactElement = React.createElement( - Builder, - { - - selectedMeal: this.properties.selectedMeal - } - ); - - ReactDom.render(element, this.domElement); - } - - protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { - this.properties[this.properties.selectedMeal] = newValue; - this.render(); - super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); - } - - protected get dataVersion(): Version { - return Version.parse("1.0"); - } - - protected get disableReactivePropertyChanges(): boolean { - return true; - } - - protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { - return { - pages: [ - { - header: { - description: "Header" - }, - groups: [ - { - groupName: "Group", - groupFields: [ - PropertyPaneDropdown("selectedMeal", { - label: "Select meal", - options: [ - { key: "0", text: "Veg" }, - { key: "1", text: "Nonveg" } - ], - selectedKey: 0 - }) - ] - } - ] - } - ] - }; - } -} - -``` - -Data source implementation is left to the reader - - \ No newline at end of file +# Builder Design Pattern + +## Summary +Builder pattern builds a complex object using simple objects and using a step by step approach. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. + + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/version-GA-green.svg) + +## Applies to + +* [SharePoint Framework](https:/dev.office.com/sharepoint) +* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) + + +## Prerequisites + +> N/A + +## Solution + +Solution|Author(s) +--------|--------- +designpatterns-typescript\builder | [@levalencia](https://www.twitter.com/levalencia) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|May 15, 2018|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.** + +--- + + +## Builder pattern + +A Builder class builds the final object step by step. This builder is independent of other objects. + +For this pattern, we have taken an existing example https://www.tutorialspoint.com/design_pattern/builder_pattern.htm and translated it to Typescript. Data Access implementation details are left to the reader. + +The idea on this example is to show how you can build a Complex object from single objects, a Meal from (burger, fries, soda). Suppose you have a SharePoint List for Burgers, another list for Sodas, another one for desserts, and you want to build different Meals (Menus), so this would be a perfect sample. + +### UML +This is more or less the diagram of the classes were are coding below. + +![](http://www.luisevalencia.com/content/images/2018/02/builder_pattern_uml_diagram.jpg/content/images/2018/02/builder_pattern_uml_diagram.jpg) + + +### Project structure +We have created a component with all the needed class, lets discuss them one by one. + +![](http://www.luisevalencia.com/content/images/2018/02/builder_pattern_uml_diagram.jpg/content/images/2018/02/typescript.png) + + +### IItem.ts + +This interface is the one that every item needs to implement to come with a common structure for all products. + +```typescript +import IPacking from "./IPacking"; + +interface IItem { + name(): string; + packing(): IPacking; + price(): number; +} + +export default IItem; +``` + +### IPacking.ts + +This interface is the one that all packaging will use, eg: Bottle, Wrapper, etc, its the way to define common behavior and properties for each product packing. + +```typescript +interface IPacking { + pack(): string; +} + +export default IPacking; +``` + +### Bottle.ts +This is one type of packing, it implements the IPacking interface. + +```typescript +import IPacking from "./IPacking"; + +class Bottle implements IPacking { + public pack(): string { + return "Bottle"; + } +} + +export default Bottle; +``` + + +### Wrapper.ts + +```typescript +import IPacking from "./IPacking"; + +class Wrapper implements IPacking { + public pack(): string { + return "Wrapper"; + } +} + +export default Wrapper; +``` + +### Burger.ts +This is an abstract class from which all our specific burgers need to implement, its there to have a common structure for name, packing and pricing. + +```typescript +import IItem from "./IItem"; +import Wrapper from "./Wrapper"; +import IPacking from "./IPacking"; + +abstract class Burger implements IItem { + public name(): string { + throw new Error("Method not implemented."); + } + + public packing(): IPacking { + return new Wrapper(); + } + + public abstract price(): number ; + +} + +export default Burger; +``` + +### ChickenBurger.ts +```typescript +import Burger from "./Burger"; + +class ChickenBurger extends Burger { + public price(): number { + return 15; + } + + public name(): string { + return "Chicken Burger"; + } +} + +export default ChickenBurger; + +``` + +### VegBurger.ts +```typescript +import Burger from "./Burger"; + +class VegBurger extends Burger { + public price(): number { + return 11; + } + + public name(): string { + return "Veg Burger"; + } +} + +export default VegBurger; + +``` +### Colddrink.ts +```typescript +import IItem from "./IItem"; +import IPacking from "./IPacking"; +import Bottle from "./Bottle"; + +abstract class ColdDrink implements IItem { + public name(): string { + throw new Error("Method not implemented."); + } + public packing(): IPacking { + return new Bottle(); + } + + public abstract price(): number ; + +} + +export default ColdDrink; + +``` +### Coke.ts +```typescript +import ColdDrink from "./ColdDrink"; + +class Coke extends ColdDrink { + public price(): number { + return 2.5; + } + + public name(): string { + return "Coca Cola"; + } +} + +export default Coke; + + +``` +### Pepsi.ts +```typescript +import ColdDrink from "./ColdDrink"; + +class Pepsi extends ColdDrink { + public price(): number { + return 1.5; + } + + public name(): string { + return "Pepsi Cola"; + } +} + +export default Pepsi; + +``` +### Meal.ts + +This class will represent a full meal behavior, here we have the methods to add items to the Meal, get the cost and show the items belonging to the Meal. + +```typescript +import IItem from "./IItem"; + +class Meal { + private items: IItem[] = []; + + public addItem(item: IItem): void { + this.items.push(item); + } + + public getCost(): number { + let cost: number = 0; + for(let item of this.items) { + cost+= item.price(); + } + + return cost; + } + + public showItems(): string { + let returnStr: string = ""; + for(let item of this.items) { + returnStr +="Item:" + item.name(); + returnStr +=", Packing:" + item.packing().pack(); + returnStr +=", Price: " + item.price(); + } + + returnStr += ", Total: " + this.getCost(); + return returnStr; + } +} + +export default Meal; +``` + +### MealBuilder.ts + +Mealbuilder its just the class that uses the classes explained before to construct any type of meal, for sake of simplicity, we created only 2 meals here. + +```typescript +import Meal from "./Meal"; +import VegBurger from "./VegBurger"; +import Coke from "./Coke"; +import ChickenBurger from "./ChickenBurger"; + +class MealBuilder { + public prepareVegMeal(): Meal { + let meal: Meal= new Meal(); + meal.addItem(new VegBurger()); + meal.addItem(new Coke()); + return meal; + } + + public prepareNonVegMeal(): Meal { + let meal: Meal= new Meal(); + meal.addItem(new ChickenBurger()); + meal.addItem(new Coke()); + return meal; + } +} + +export default MealBuilder; +``` + +### IBuilderProps.ts +We created a selectedMeal string property to take the decision on which meal to build. + +```typescript +export interface IBuilderProps { + selectedMeal: string; +} + +``` + +### Builder.tsx + +This is our component class, here we have a constructor and in the constructor we call the setMeal method, with the selected meal option as a parameter, and then we can define which meal to prepare. Once the meal is prepared, in the render method we can use the showItems method +```typescript +import * as React from 'react'; +import styles from './Builder.module.scss'; +import { IBuilderProps } from './IBuilderProps'; +import { escape } from '@microsoft/sp-lodash-subset'; +import MealBuilder from "./MealBuilder"; +import Meal from "./Meal"; +import { IPropertyPaneConfiguration } from "@microsoft/sp-webpart-base"; +import { + PropertyPaneDropdown +} from "@microsoft/sp-webpart-base"; +import {Version} from "@microsoft/sp-core-library"; +import { IBuilderState } from './IBuilderState'; + +export default class Builder extends React.Component { + + private mealBuilder: MealBuilder ; + private items: string; + private meal: Meal; + + constructor(props: IBuilderProps, state: IBuilderState) { + super(props); + this.setInitialState(); + //this.setMeal = this.setMeal.bind(this); + this.mealBuilder = new MealBuilder(); + this.setMeal(props.selectedMeal); + } + + public setInitialState(): void { + this.state = { + items: "" + }; + } + + public componentWillReceiveProps(nextProps: IBuilderProps): void { + if(nextProps.selectedMeal !== this.props.selectedMeal) { + this.setMeal(nextProps.selectedMeal); + } + } + + public render(): React.ReactElement { + return ( +
+
+
+
+ Welcome to Mac Luis! +

You have selected the following.

+
{this.state.items}
+
+
+
+
+ ); + } + protected get dataVersion(): Version { + return Version.parse("1.0"); + } + + private setMeal(selectedMeal: string): void { + if(selectedMeal===undefined){ + this.meal = this.mealBuilder.prepareVegMeal(); + } + + if(selectedMeal === "0") { + this.meal = this.mealBuilder.prepareVegMeal(); + } + + if(selectedMeal === "1") { + this.meal = this.mealBuilder.prepareNonVegMeal(); + } + + this.state = { + items: this.meal.showItems(), + }; + } +} + + + +``` + +And finally +### BuilderWebPart.ts + +Here what we do is just to use our component and sending the parameter of the selected meal, which is just a normal dropdown with 2 hardcoded values. + + +```typescript +import * as React from 'react'; +import * as ReactDom from 'react-dom'; +import { Version } from '@microsoft/sp-core-library'; +import { + BaseClientSideWebPart, + IPropertyPaneConfiguration, + PropertyPaneTextField, + PropertyPaneDropdown +} from "@microsoft/sp-webpart-base"; + + +import * as strings from 'BuilderWebPartStrings'; +import Builder from './components/Builder'; +import { IBuilderProps } from './components/IBuilderProps'; + +export interface IBuilderWebPartProps { + selectedMeal: string; +} + +export default class BuilderWebPart extends BaseClientSideWebPart { + + public render(): void { + const element: React.ReactElement = React.createElement( + Builder, + { + + selectedMeal: this.properties.selectedMeal + } + ); + + ReactDom.render(element, this.domElement); + } + + protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { + this.properties[this.properties.selectedMeal] = newValue; + this.render(); + super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); + } + + protected get dataVersion(): Version { + return Version.parse("1.0"); + } + + protected get disableReactivePropertyChanges(): boolean { + return true; + } + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + return { + pages: [ + { + header: { + description: "Header" + }, + groups: [ + { + groupName: "Group", + groupFields: [ + PropertyPaneDropdown("selectedMeal", { + label: "Select meal", + options: [ + { key: "0", text: "Veg" }, + { key: "1", text: "Nonveg" } + ], + selectedKey: 0 + }) + ] + } + ] + } + ] + }; + } +} + +``` + +Data source implementation is left to the reader + + diff --git a/samples/react-designpatterns-typescript/FactoryMethod/README.md b/samples/react-designpatterns-typescript/FactoryMethod/README.md index 729abfb41..3c5c85e9f 100644 --- a/samples/react-designpatterns-typescript/FactoryMethod/README.md +++ b/samples/react-designpatterns-typescript/FactoryMethod/README.md @@ -1,702 +1,702 @@ -# Factory method design Pattern - -## Summary -In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor. - -Another definition which I liked more and fits perfectly my sample is the following, taken from: https://www.javatpoint.com/factory-method-design-pattern - ->A Factory Pattern or Factory Method Pattern says that just define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate. In other words, subclasses are responsible to create the instance of the class. - - -## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/version-GA-green.svg) - -## Applies to - -* [SharePoint Framework](https:/dev.office.com/sharepoint) -* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) - - -## Prerequisites - -> N/A - -## Solution - -Solution|Author(s) ---------|--------- -designpatterns-typescript\factorymethod | [@levalencia](https://www.twitter.com/levalencia) - -## Version history - -Version|Date|Comments --------|----|-------- -1.0|May 15, 2018|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.** - ---- - - -### Factory method pattern - -##### Advantage of Factory Design Pattern - ->Factory Method Pattern allows the sub-classes to choose the type of objects to create. -It promotes the loose-coupling by eliminating the need to bind application-specific classes into the code. That means the code interacts solely with the resultant interface or abstract class, so that it will work with any classes that implement that interface or that extends that abstract class. - -##### When to use Factory Method Design Pattern - -1. When a class doesn't know what sub-classes will be required to create -2. When a class wants that its sub-classes specify the objects to be created. -3. When the parent classes choose the creation of objects to its sub-classes. - -So lets start this journey explaining how I saw this factory method pattern could be applied. - -In a Sharepoint Site we can have multiple lists and all those lists could have different columns or fields, why not create a generic way to build the list item objets depending on the selected list? well in plain english, a webpart where you can select the list, and based on the selected list it will render all the columns, sure you can do this in many ways and probably with lots of switches/if statements, etc, but I wanted a more elegant solution and I believe this sample will do just that. - - -##### Diagram -![](http://www.luisevalencia.com/content/images/2017/11/DesignPatterns.png) - -So instead of classes we have interfaces for the generic list item (IListItem), and then other interfaces that extends the base one to add more fields depending on the list, news, announcements and directory. - -On the right side of the diagram we have an IFactory interface that declares the signature of the method getItems and what it should return (any), remember at the end all items will be of type IListItem because they extend that interface but in order to make it work we will have to use an array of any[]. - -And finally on the FactoryMethod react component we use the ListItem Factory to get the items, from the caller point of view, we dont care what it will return, its the factory method responsibility to actually create the logic inside to know which instance types it should return. - -##### Project structure -![](http://www.luisevalencia.com/content/images/2017/11/estructura.png) - -###### Models - -The models just define the type of objects we want to return from our factory method, better for strong typing our return objects. - -```typescript -export interface IListItem { - [key: string]: any; - id: string; - title: string; - modified: Date; - created: Date; - modifiedby: string; - createdby: string; -} - -import {IListItem} from "./IListItem"; - -export interface IAnnouncementListItem extends IListItem { - announcementBody: string; - expiryDate: Date; -} - -import {IListItem} from "./IListItem"; - -export interface IDirectoryListItem extends IListItem { - firstName: string; - lastName: string; - mobileNumber: string; - internalNumber: string; -} - - -import {IListItem} from "./IListItem"; - -export interface INewsListItem extends IListItem { - newsheader: string; - newsbody: string; - expiryDate: Date; -} - - - -``` - -###### Factory classes - -So the Factory interface is quite simple, we just have a method to implement in the extended classes. - -Its in this method below where we actually have a switch statement to get a different list with a different URL (Select columns), and then on the return we get different concrete types by mapping the json result to a different instance of the needed type. - -```typescript -import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; -import { IWebPartContext } from "@microsoft/sp-webpart-base"; -import { IListItem} from "./models/IListItem"; -import { IFactory } from "./IFactory"; -import { INewsListItem } from "./models/INewsListItem"; -import { IDirectoryListItem } from "./models/IDirectoryListItem"; -import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; - -export class ListItemFactory implements IFactory { - // private _listItems: IListItem[]; - public getItems(requester: SPHttpClient, siteUrl: string, listName: string): Promise { - switch(listName) { - case "GenericList": - let items: IListItem[]; - // tslint:disable-next-line:max-line-length - return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`, - SPHttpClient.configurations.v1, - { - headers: { - "Accept": "application/json;odata=nometadata", - "odata-version": "" - } - }) - .then((response: SPHttpClientResponse): Promise<{ value: IListItem[] }> => { - return response.json(); - }) - .then((json: { value: IListItem[] }) => { - console.log(JSON.stringify(json.value)); - return items=json.value.map((v,i)=>( - { - // key: v.id, - id: v.Id, - title: v.Title, - created: v.Created, - createdby: v.Author.Title, - modified: v.Modified, - modifiedby: v.Editor.Title - } - )); - }); - case "News": - let newsitems: INewsListItem[]; - // tslint:disable-next-line:max-line-length - return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,newsheader,newsbody,expiryDate,Author/Title,Editor/Title&$expand=Author,Editor`, - SPHttpClient.configurations.v1, - { - headers: { - "Accept": "application/json;odata=nometadata", - "odata-version": "" - } - }) - .then((response: SPHttpClientResponse): Promise<{ value: INewsListItem[] }> => { - return response.json(); - }) - .then((json: { value: INewsListItem[] }) => { - return newsitems=json.value.map((v,i)=>( - { - id: v.Id, - title: v.Title, - created: v.Created, - createdby: v.Author.Title, - modified: v.Modified, - modifiedby: v.Editor.Title, - newsheader: v.newsheader, - newsbody: v.newsbody, - expiryDate: v.expiryDate - } - )); - }); - case "Announcements": - let announcementitems: IAnnouncementListItem[]; - return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,announcementBody,expiryDate,Author/Title,Editor/Title&$expand=Author,Editor`, - SPHttpClient.configurations.v1, - { - headers: { - "Accept": "application/json;odata=nometadata", - "odata-version": "" - } - }) - .then((response: SPHttpClientResponse): Promise<{ value: IAnnouncementListItem[] }> => { - return response.json(); - }) - .then((json: { value: IAnnouncementListItem[] }) => { - return announcementitems=json.value.map((v,i)=>( - { - id: v.Id, - title: v.Title, - created: v.Created, - createdby: v.Author.Title, - modified: v.Modified, - modifiedby: v.Editor.Title, - announcementBody: v.announcementBody, - expiryDate: v.expiryDate - } - )); - }); - case "Directory": - let directoryitems: IDirectoryListItem[]; - return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,firstName,lastName,mobileNumber,internalNumber,Author/Title,Editor/Title&$expand=Author,Editor`, - SPHttpClient.configurations.v1, - { - headers: { - "Accept": "application/json;odata=nometadata", - "odata-version": "" - } - }) - .then((response: SPHttpClientResponse): Promise<{ value: IDirectoryListItem[] }> => { - return response.json(); - }) - .then((json: { value: IDirectoryListItem[] }) => { - return directoryitems=json.value.map((v,i)=>( - { - id: v.Id, - title: v.Title, - created: v.Created, - createdby: v.Author.Title, - modified: v.Modified, - modifiedby: v.Editor.Title, - firstName: v.firstName, - lastName: v.lastName, - mobileNumber: v.mobileNumber, - internalNumber: v.internalNumber - } - )); - }); - default: - break; - } - } -} - -``` - -###### Props and states - -Properties we pass from the main webpart to the component are defined in the props interface, things like SPHttpclient are important here, state is where we actually store our returned information from the server, because listitems could be of different types, I created a wrapper Interface and depending on the type of list, then the state would be read from a different state property DetailsListItemState, DetailsNewsListItemState, etc. - -We will see later in the series how could achieve this with shorter code, but for this pattern its what I need so far. - -```typescript -import { SPHttpClient } from "@microsoft/sp-http"; -import IDataProvider from "./dataproviders/IDataProvider"; - -export interface IFactoryMethodProps { - listName: string; - spHttpClient: SPHttpClient; - siteUrl: string; - dataProvider: IDataProvider; -} - -import { IListItem } from "./models/IListItem"; -import { INewsListItem } from "./models/INewsListItem"; -import { IDirectoryListItem } from "./models/IDirectoryListItem"; -import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; -import { - IColumn -} from "office-ui-fabric-react/lib/DetailsList"; - -export interface IFactoryMethodState { - type: string; - status: string; - DetailsListItemState: IDetailsListItemState; - DetailsNewsListItemState: IDetailsNewsListItemState; - DetailsDirectoryListItemState : IDetailsDirectoryListItemState; - DetailsAnnouncementListItemState : IDetailsAnnouncementListItemState; -} - -export interface IDetailsListItemState { - columns: IColumn[]; - items: IListItem[]; -} - -export interface IDetailsNewsListItemState { - columns: IColumn[]; - items: INewsListItem[]; -} - -export interface IDetailsDirectoryListItemState { - columns: IColumn[]; - items: IDirectoryListItem[]; -} - -export interface IDetailsAnnouncementListItemState { - columns: IColumn[]; - items: IAnnouncementListItem[]; -} - -``` - -###### The component - -The component has a lot of UI logic, but the real Factory method magic is only in the readItemsAndSetStatus method, where we use the Factory class to get the items and then set the corresponding state, this is pretty nice as we avoid lots of code with the setstateLine and the getitems method here. - -Some code I think its really self explanatory so I dont explain it, if needed put a comment below with a question to further explain something. - -If you are new to React then I really recommend to read this from the official documentation, which explains the component lifecycle, after reading this, it will clarify concepts for you: https://reactjs.org/docs/react-component.html - -```Typescript -//#region Imports -//#region Imports -import * as React from "react"; -import styles from "./FactoryMethod.module.scss"; -import { IFactoryMethodProps } from "./IFactoryMethodProps"; -import { - IDetailsGenericListItemState, - IDetailsNewsListItemState, - IDetailsDirectoryListItemState, - IDetailsAnnouncementListItemState, - IFactoryMethodState -} from "./IFactoryMethodState"; -import { IListItem } from "./models/IListItem"; -import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; -import { INewsListItem } from "./models/INewsListItem"; -import { IDirectoryListItem } from "./models/IDirectoryListItem"; -import { escape } from "@microsoft/sp-lodash-subset"; -import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; -import { ListItemFactory} from "./ListItemFactory"; -import { TextField } from "office-ui-fabric-react/lib/TextField"; -import { - DetailsList, - DetailsListLayoutMode, - Selection, - buildColumns, - IColumn -} from "office-ui-fabric-react/lib/DetailsList"; -import { MarqueeSelection } from "office-ui-fabric-react/lib/MarqueeSelection"; -import { autobind } from "office-ui-fabric-react/lib/Utilities"; -import PropTypes from "prop-types"; -//#endregion - - -export default class FactoryMethod extends React.Component { - constructor(props: IFactoryMethodProps, state: any) { - super(props); - this.setInitialState(); - } - - // lifecycle help here: https://staminaloops.github.io/undefinedisnotafunction/understanding-react/ - - //#region Mouting events lifecycle - - // the data returned from render is neither a string nor a DOM node. - // it's a lightweight description of what the DOM should look like. - // inspects this.state and this.props and create the markup. - // when your data changes, the render method is called again. - // react diff the return value from the previous call to render with - // the new one, and generate a minimal set of changes to be applied to the DOM. - public render(): React.ReactElement { - switch(this.props.listName) { - case "GenericList": - // tslint:disable-next-line:max-line-length - return ; - case "News": - // tslint:disable-next-line:max-line-length - return ; - case "Announcements": - // tslint:disable-next-line:max-line-length - return ; - case "Directory": - // tslint:disable-next-line:max-line-length - return ; - default: - return null; - } - } - - // invoked once, only on the client (not on the server), immediately AFTER the initial rendering occurs. - public componentDidMount(): void { - // you can access any refs to your children - // (e.g., to access the underlying DOM representation - ReactDOM.findDOMNode). - // the componentDidMount() method of child components is invoked before that of parent components. - // if you want to integrate with other JavaScript frameworks, - // set timers using setTimeout or setInterval, - // or send AJAX requests, perform those operations in this method. - this._configureWebPart = this._configureWebPart.bind(this); - this.readItemsAndSetStatus("GenericList"); - } - - //#endregion - - //#region Props changes lifecycle events (after a property changes from parent component) - public componentWillReceiveProps(nextProps: IFactoryMethodProps): void { - if(nextProps.listName !== this.props.listName) { - this.readItemsAndSetStatus(nextProps.listName); - } - } - - //#endregion - - //#region private methods - private _configureWebPart(): void { - this.props.configureStartCallback(); - } - - public setInitialState(): void { - this.state = { - status: this.listNotConfigured(this.props) - ? "Please configure list in Web Part properties" - : "Ready", - columns:[], - hasError: false, - DetailsGenericListItemState:{ - items:[] - }, - DetailsNewsListItemState:{ - items:[] - }, - DetailsDirectoryListItemState:{ - items:[] - }, - DetailsAnnouncementListItemState:{ - items:[] - }, - }; - } - - // reusable inline component - private ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => ( -
- - -
- ) - - // read items using factory method pattern and sets state accordingly - private readItemsAndSetStatus(listName: string): void { - this.setState({ - status: "Loading all items..." - }); - - const factory: ListItemFactory = new ListItemFactory(); - factory.getItems(this.props.spHttpClient, this.props.siteUrl, listName) - .then((items: any[]) => { - const keyPart: string = listName === "GenericList" ? "Generic" : listName; - - var myItems = null; - switch(listName) { - case "GenericList": - myItems = items as IListItem[]; - break; - case "News": - myItems = items as INewsListItem[]; - break; - case "Announcements": - myItems = items as IAnnouncementListItem[]; - break; - case "Directory": - myItems = items as IDirectoryListItem[]; - break; - } - - // the explicit specification of the type argument `keyof {}` is bad and - // it should not be required. - this.setState({ - status: `Successfully loaded ${myItems.length} items`, - ["Details" + keyPart + "ListItemState"] : { - items - }, - columns: buildColumns(myItems) - }); - }); - } - - private listNotConfigured(props: IFactoryMethodProps): boolean { - return props.listName === undefined || - props.listName === null || - props.listName.length === 0; - } - - //#endregion -} -``` -###### The webpart - -And finally the webpart code is below, with self explanatory comments for the reader to understand the events lifecycle. - - -```Typescript -import * as React from "react"; -import * as ReactDom from "react-dom"; -import { Version } from "@microsoft/sp-core-library"; -import { - BaseClientSideWebPart, - IPropertyPaneConfiguration, - PropertyPaneTextField, - PropertyPaneDropdown, - IPropertyPaneDropdownOption, - IPropertyPaneField, - PropertyPaneLabel -} from "@microsoft/sp-webpart-base"; - -import * as strings from "FactoryMethodWebPartStrings"; -import FactoryMethod from "./components/FactoryMethod"; -import { IFactoryMethodProps } from "./components/IFactoryMethodProps"; -import { IFactoryMethodWebPartProps } from "./IFactoryMethodWebPartProps"; -import * as lodash from "@microsoft/sp-lodash-subset"; -import List from "./components/models/List"; -import { Environment, EnvironmentType } from "@microsoft/sp-core-library"; -import IDataProvider from "./components/dataproviders/IDataProvider"; -import MockDataProvider from "./test/MockDataProvider"; -import SharePointDataProvider from "./components/dataproviders/SharepointDataProvider"; - -export default class FactoryMethodWebPart extends BaseClientSideWebPart { - private _dropdownOptions: IPropertyPaneDropdownOption[]; - private _selectedList: List; - private _disableDropdown: boolean; - private _dataProvider: IDataProvider; - private _factorymethodContainerComponent: FactoryMethod; - - protected onInit(): Promise { - this.context.statusRenderer.displayLoadingIndicator(this.domElement, "Todo"); - - /* - Create the appropriate data provider depending on where the web part is running. - The DEBUG flag will ensure the mock data provider is not bundled with the web part when you package the - solution for distribution, that is, using the --ship flag with the package-solution gulp command. - */ - if (DEBUG && Environment.type === EnvironmentType.Local) { - this._dataProvider = new MockDataProvider(); - } else { - this._dataProvider = new SharePointDataProvider(); - this._dataProvider.webPartContext = this.context; - } - - this.openPropertyPane = this.openPropertyPane.bind(this); - - /* - Get the list of tasks lists from the current site and populate the property pane dropdown field with the values. - */ - this.loadLists() - .then(() => { - /* - If a list is already selected, then we would have stored the list Id in the associated web part property. - So, check to see if we do have a selected list for the web part. If we do, then we set that as the selected list - in the property pane dropdown field. - */ - if (this.properties.spListIndex) { - this.setSelectedList(this.properties.spListIndex.toString()); - this.context.statusRenderer.clearLoadingIndicator(this.domElement); - } - }); - - return super.onInit(); - } - - // render method of the webpart, actually calls Component - public render(): void { - const element: React.ReactElement = React.createElement( - FactoryMethod, - { - spHttpClient: this.context.spHttpClient, - siteUrl: this.context.pageContext.web.absoluteUrl, - listName: this._dataProvider.selectedList === undefined ? "GenericList" : this._dataProvider.selectedList.Title, - dataProvider: this._dataProvider, - configureStartCallback: this.openPropertyPane - } - ); - - // reactDom.render(element, this.domElement); - this._factorymethodContainerComponent = ReactDom.render(element, this.domElement); - - } - - // loads lists from the site and fill the dropdown. - private loadLists(): Promise { - return this._dataProvider.getLists() - .then((lists: List[]) => { - // disable dropdown field if there are no results from the server. - this._disableDropdown = lists.length === 0; - if (lists.length !== 0) { - this._dropdownOptions = lists.map((list: List) => { - return { - key: list.Id, - text: list.Title - }; - }); - } - }); - } - - protected get dataVersion(): Version { - return Version.parse("1.0"); - } - - protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { - /* - Check the property path to see which property pane feld changed. If the property path matches the dropdown, then we set that list - as the selected list for the web part. - */ - if (propertyPath === "spListIndex") { - this.setSelectedList(newValue); - } - - /* - Finally, tell property pane to re-render the web part. - This is valid for reactive property pane. - */ - super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); - } - - // sets the selected list based on the selection from the dropdownlist - private setSelectedList(value: string): void { - const selectedIndex: number = lodash.findIndex(this._dropdownOptions, - (item: IPropertyPaneDropdownOption) => item.key === value - ); - - const selectedDropDownOption: IPropertyPaneDropdownOption = this._dropdownOptions[selectedIndex]; - - if (selectedDropDownOption) { - this._selectedList = { - Title: selectedDropDownOption.text, - Id: selectedDropDownOption.key.toString() - }; - - this._dataProvider.selectedList = this._selectedList; - } - } - - - // we add fields dynamically to the property pane, in this case its only the list field which we will render - private getGroupFields(): IPropertyPaneField[] { - const fields: IPropertyPaneField[] = []; - - // we add the options from the dropdownoptions variable that was populated during init to the dropdown here. - fields.push(PropertyPaneDropdown("spListIndex", { - label: "Select a list", - disabled: this._disableDropdown, - options: this._dropdownOptions - })); - - /* - When we do not have any lists returned from the server, we disable the dropdown. If that is the case, - we also add a label field displaying the appropriate message. - */ - if (this._disableDropdown) { - fields.push(PropertyPaneLabel(null, { - text: "Could not find tasks lists in your site. Create one or more tasks list and then try using the web part." - })); - } - - return fields; - } - - private openPropertyPane(): void { - this.context.propertyPane.open(); - } - - protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { - return { - pages: [ - { - header: { - description: strings.PropertyPaneDescription - }, - groups: [ - { - groupName: strings.BasicGroupName, - /* - Instead of creating the fields here, we call a method that will return the set of property fields to render. - */ - groupFields: this.getGroupFields() - } - ] - } - ] - }; - } -} - - -``` - - - \ No newline at end of file +# Factory method design Pattern + +## Summary +In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor. + +Another definition which I liked more and fits perfectly my sample is the following, taken from: https://www.javatpoint.com/factory-method-design-pattern + +>A Factory Pattern or Factory Method Pattern says that just define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate. In other words, subclasses are responsible to create the instance of the class. + + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/version-GA-green.svg) + +## Applies to + +* [SharePoint Framework](https:/dev.office.com/sharepoint) +* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) + + +## Prerequisites + +> N/A + +## Solution + +Solution|Author(s) +--------|--------- +designpatterns-typescript\factorymethod | [@levalencia](https://www.twitter.com/levalencia) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|May 15, 2018|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.** + +--- + + +### Factory method pattern + +##### Advantage of Factory Design Pattern + +>Factory Method Pattern allows the sub-classes to choose the type of objects to create. +It promotes the loose-coupling by eliminating the need to bind application-specific classes into the code. That means the code interacts solely with the resultant interface or abstract class, so that it will work with any classes that implement that interface or that extends that abstract class. + +##### When to use Factory Method Design Pattern + +1. When a class doesn't know what sub-classes will be required to create +2. When a class wants that its sub-classes specify the objects to be created. +3. When the parent classes choose the creation of objects to its sub-classes. + +So lets start this journey explaining how I saw this factory method pattern could be applied. + +In a SharePoint Site we can have multiple lists and all those lists could have different columns or fields, why not create a generic way to build the list item objets depending on the selected list? well in plain english, a webpart where you can select the list, and based on the selected list it will render all the columns, sure you can do this in many ways and probably with lots of switches/if statements, etc, but I wanted a more elegant solution and I believe this sample will do just that. + + +##### Diagram +![](http://www.luisevalencia.com/content/images/2017/11/DesignPatterns.png) + +So instead of classes we have interfaces for the generic list item (IListItem), and then other interfaces that extends the base one to add more fields depending on the list, news, announcements and directory. + +On the right side of the diagram we have an IFactory interface that declares the signature of the method getItems and what it should return (any), remember at the end all items will be of type IListItem because they extend that interface but in order to make it work we will have to use an array of any[]. + +And finally on the FactoryMethod react component we use the ListItem Factory to get the items, from the caller point of view, we don't care what it will return, its the factory method responsibility to actually create the logic inside to know which instance types it should return. + +##### Project structure +![](http://www.luisevalencia.com/content/images/2017/11/estructura.png) + +###### Models + +The models just define the type of objects we want to return from our factory method, better for strong typing our return objects. + +```typescript +export interface IListItem { + [key: string]: any; + id: string; + title: string; + modified: Date; + created: Date; + modifiedby: string; + createdby: string; +} + +import {IListItem} from "./IListItem"; + +export interface IAnnouncementListItem extends IListItem { + announcementBody: string; + expiryDate: Date; +} + +import {IListItem} from "./IListItem"; + +export interface IDirectoryListItem extends IListItem { + firstName: string; + lastName: string; + mobileNumber: string; + internalNumber: string; +} + + +import {IListItem} from "./IListItem"; + +export interface INewsListItem extends IListItem { + newsheader: string; + newsbody: string; + expiryDate: Date; +} + + + +``` + +###### Factory classes + +So the Factory interface is quite simple, we just have a method to implement in the extended classes. + +Its in this method below where we actually have a switch statement to get a different list with a different URL (Select columns), and then on the return we get different concrete types by mapping the json result to a different instance of the needed type. + +```typescript +import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; +import { IWebPartContext } from "@microsoft/sp-webpart-base"; +import { IListItem} from "./models/IListItem"; +import { IFactory } from "./IFactory"; +import { INewsListItem } from "./models/INewsListItem"; +import { IDirectoryListItem } from "./models/IDirectoryListItem"; +import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; + +export class ListItemFactory implements IFactory { + // private _listItems: IListItem[]; + public getItems(requester: SPHttpClient, siteUrl: string, listName: string): Promise { + switch(listName) { + case "GenericList": + let items: IListItem[]; + // tslint:disable-next-line:max-line-length + return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`, + SPHttpClient.configurations.v1, + { + headers: { + "Accept": "application/json;odata=nometadata", + "odata-version": "" + } + }) + .then((response: SPHttpClientResponse): Promise<{ value: IListItem[] }> => { + return response.json(); + }) + .then((json: { value: IListItem[] }) => { + console.log(JSON.stringify(json.value)); + return items=json.value.map((v,i)=>( + { + // key: v.id, + id: v.Id, + title: v.Title, + created: v.Created, + createdby: v.Author.Title, + modified: v.Modified, + modifiedby: v.Editor.Title + } + )); + }); + case "News": + let newsitems: INewsListItem[]; + // tslint:disable-next-line:max-line-length + return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,newsheader,newsbody,expiryDate,Author/Title,Editor/Title&$expand=Author,Editor`, + SPHttpClient.configurations.v1, + { + headers: { + "Accept": "application/json;odata=nometadata", + "odata-version": "" + } + }) + .then((response: SPHttpClientResponse): Promise<{ value: INewsListItem[] }> => { + return response.json(); + }) + .then((json: { value: INewsListItem[] }) => { + return newsitems=json.value.map((v,i)=>( + { + id: v.Id, + title: v.Title, + created: v.Created, + createdby: v.Author.Title, + modified: v.Modified, + modifiedby: v.Editor.Title, + newsheader: v.newsheader, + newsbody: v.newsbody, + expiryDate: v.expiryDate + } + )); + }); + case "Announcements": + let announcementitems: IAnnouncementListItem[]; + return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,announcementBody,expiryDate,Author/Title,Editor/Title&$expand=Author,Editor`, + SPHttpClient.configurations.v1, + { + headers: { + "Accept": "application/json;odata=nometadata", + "odata-version": "" + } + }) + .then((response: SPHttpClientResponse): Promise<{ value: IAnnouncementListItem[] }> => { + return response.json(); + }) + .then((json: { value: IAnnouncementListItem[] }) => { + return announcementitems=json.value.map((v,i)=>( + { + id: v.Id, + title: v.Title, + created: v.Created, + createdby: v.Author.Title, + modified: v.Modified, + modifiedby: v.Editor.Title, + announcementBody: v.announcementBody, + expiryDate: v.expiryDate + } + )); + }); + case "Directory": + let directoryitems: IDirectoryListItem[]; + return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,firstName,lastName,mobileNumber,internalNumber,Author/Title,Editor/Title&$expand=Author,Editor`, + SPHttpClient.configurations.v1, + { + headers: { + "Accept": "application/json;odata=nometadata", + "odata-version": "" + } + }) + .then((response: SPHttpClientResponse): Promise<{ value: IDirectoryListItem[] }> => { + return response.json(); + }) + .then((json: { value: IDirectoryListItem[] }) => { + return directoryitems=json.value.map((v,i)=>( + { + id: v.Id, + title: v.Title, + created: v.Created, + createdby: v.Author.Title, + modified: v.Modified, + modifiedby: v.Editor.Title, + firstName: v.firstName, + lastName: v.lastName, + mobileNumber: v.mobileNumber, + internalNumber: v.internalNumber + } + )); + }); + default: + break; + } + } +} + +``` + +###### Props and states + +Properties we pass from the main webpart to the component are defined in the props interface, things like SPHttpclient are important here, state is where we actually store our returned information from the server, because listitems could be of different types, I created a wrapper Interface and depending on the type of list, then the state would be read from a different state property DetailsListItemState, DetailsNewsListItemState, etc. + +We will see later in the series how could achieve this with shorter code, but for this pattern its what I need so far. + +```typescript +import { SPHttpClient } from "@microsoft/sp-http"; +import IDataProvider from "./dataproviders/IDataProvider"; + +export interface IFactoryMethodProps { + listName: string; + spHttpClient: SPHttpClient; + siteUrl: string; + dataProvider: IDataProvider; +} + +import { IListItem } from "./models/IListItem"; +import { INewsListItem } from "./models/INewsListItem"; +import { IDirectoryListItem } from "./models/IDirectoryListItem"; +import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; +import { + IColumn +} from "office-ui-fabric-react/lib/DetailsList"; + +export interface IFactoryMethodState { + type: string; + status: string; + DetailsListItemState: IDetailsListItemState; + DetailsNewsListItemState: IDetailsNewsListItemState; + DetailsDirectoryListItemState : IDetailsDirectoryListItemState; + DetailsAnnouncementListItemState : IDetailsAnnouncementListItemState; +} + +export interface IDetailsListItemState { + columns: IColumn[]; + items: IListItem[]; +} + +export interface IDetailsNewsListItemState { + columns: IColumn[]; + items: INewsListItem[]; +} + +export interface IDetailsDirectoryListItemState { + columns: IColumn[]; + items: IDirectoryListItem[]; +} + +export interface IDetailsAnnouncementListItemState { + columns: IColumn[]; + items: IAnnouncementListItem[]; +} + +``` + +###### The component + +The component has a lot of UI logic, but the real Factory method magic is only in the readItemsAndSetStatus method, where we use the Factory class to get the items and then set the corresponding state, this is pretty nice as we avoid lots of code with the setstateLine and the getitems method here. + +Some code I think its really self explanatory so I dont explain it, if needed put a comment below with a question to further explain something. + +If you are new to React then I really recommend to read this from the official documentation, which explains the component lifecycle, after reading this, it will clarify concepts for you: https://reactjs.org/docs/react-component.html + +```Typescript +//#region Imports +//#region Imports +import * as React from "react"; +import styles from "./FactoryMethod.module.scss"; +import { IFactoryMethodProps } from "./IFactoryMethodProps"; +import { + IDetailsGenericListItemState, + IDetailsNewsListItemState, + IDetailsDirectoryListItemState, + IDetailsAnnouncementListItemState, + IFactoryMethodState +} from "./IFactoryMethodState"; +import { IListItem } from "./models/IListItem"; +import { IAnnouncementListItem } from "./models/IAnnouncementListItem"; +import { INewsListItem } from "./models/INewsListItem"; +import { IDirectoryListItem } from "./models/IDirectoryListItem"; +import { escape } from "@microsoft/sp-lodash-subset"; +import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; +import { ListItemFactory} from "./ListItemFactory"; +import { TextField } from "office-ui-fabric-react/lib/TextField"; +import { + DetailsList, + DetailsListLayoutMode, + Selection, + buildColumns, + IColumn +} from "office-ui-fabric-react/lib/DetailsList"; +import { MarqueeSelection } from "office-ui-fabric-react/lib/MarqueeSelection"; +import { autobind } from "office-ui-fabric-react/lib/Utilities"; +import PropTypes from "prop-types"; +//#endregion + + +export default class FactoryMethod extends React.Component { + constructor(props: IFactoryMethodProps, state: any) { + super(props); + this.setInitialState(); + } + + // lifecycle help here: https://staminaloops.github.io/undefinedisnotafunction/understanding-react/ + + //#region Mouting events lifecycle + + // the data returned from render is neither a string nor a DOM node. + // it's a lightweight description of what the DOM should look like. + // inspects this.state and this.props and create the markup. + // when your data changes, the render method is called again. + // react diff the return value from the previous call to render with + // the new one, and generate a minimal set of changes to be applied to the DOM. + public render(): React.ReactElement { + switch(this.props.listName) { + case "GenericList": + // tslint:disable-next-line:max-line-length + return ; + case "News": + // tslint:disable-next-line:max-line-length + return ; + case "Announcements": + // tslint:disable-next-line:max-line-length + return ; + case "Directory": + // tslint:disable-next-line:max-line-length + return ; + default: + return null; + } + } + + // invoked once, only on the client (not on the server), immediately AFTER the initial rendering occurs. + public componentDidMount(): void { + // you can access any refs to your children + // (e.g., to access the underlying DOM representation - ReactDOM.findDOMNode). + // the componentDidMount() method of child components is invoked before that of parent components. + // if you want to integrate with other JavaScript frameworks, + // set timers using setTimeout or setInterval, + // or send AJAX requests, perform those operations in this method. + this._configureWebPart = this._configureWebPart.bind(this); + this.readItemsAndSetStatus("GenericList"); + } + + //#endregion + + //#region Props changes lifecycle events (after a property changes from parent component) + public componentWillReceiveProps(nextProps: IFactoryMethodProps): void { + if(nextProps.listName !== this.props.listName) { + this.readItemsAndSetStatus(nextProps.listName); + } + } + + //#endregion + + //#region private methods + private _configureWebPart(): void { + this.props.configureStartCallback(); + } + + public setInitialState(): void { + this.state = { + status: this.listNotConfigured(this.props) + ? "Please configure list in Web Part properties" + : "Ready", + columns:[], + hasError: false, + DetailsGenericListItemState:{ + items:[] + }, + DetailsNewsListItemState:{ + items:[] + }, + DetailsDirectoryListItemState:{ + items:[] + }, + DetailsAnnouncementListItemState:{ + items:[] + }, + }; + } + + // reusable inline component + private ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => ( +
+ + +
+ ) + + // read items using factory method pattern and sets state accordingly + private readItemsAndSetStatus(listName: string): void { + this.setState({ + status: "Loading all items..." + }); + + const factory: ListItemFactory = new ListItemFactory(); + factory.getItems(this.props.spHttpClient, this.props.siteUrl, listName) + .then((items: any[]) => { + const keyPart: string = listName === "GenericList" ? "Generic" : listName; + + var myItems = null; + switch(listName) { + case "GenericList": + myItems = items as IListItem[]; + break; + case "News": + myItems = items as INewsListItem[]; + break; + case "Announcements": + myItems = items as IAnnouncementListItem[]; + break; + case "Directory": + myItems = items as IDirectoryListItem[]; + break; + } + + // the explicit specification of the type argument `keyof {}` is bad and + // it should not be required. + this.setState({ + status: `Successfully loaded ${myItems.length} items`, + ["Details" + keyPart + "ListItemState"] : { + items + }, + columns: buildColumns(myItems) + }); + }); + } + + private listNotConfigured(props: IFactoryMethodProps): boolean { + return props.listName === undefined || + props.listName === null || + props.listName.length === 0; + } + + //#endregion +} +``` +###### The webpart + +And finally the webpart code is below, with self explanatory comments for the reader to understand the events lifecycle. + + +```Typescript +import * as React from "react"; +import * as ReactDom from "react-dom"; +import { Version } from "@microsoft/sp-core-library"; +import { + BaseClientSideWebPart, + IPropertyPaneConfiguration, + PropertyPaneTextField, + PropertyPaneDropdown, + IPropertyPaneDropdownOption, + IPropertyPaneField, + PropertyPaneLabel +} from "@microsoft/sp-webpart-base"; + +import * as strings from "FactoryMethodWebPartStrings"; +import FactoryMethod from "./components/FactoryMethod"; +import { IFactoryMethodProps } from "./components/IFactoryMethodProps"; +import { IFactoryMethodWebPartProps } from "./IFactoryMethodWebPartProps"; +import * as lodash from "@microsoft/sp-lodash-subset"; +import List from "./components/models/List"; +import { Environment, EnvironmentType } from "@microsoft/sp-core-library"; +import IDataProvider from "./components/dataproviders/IDataProvider"; +import MockDataProvider from "./test/MockDataProvider"; +import SharePointDataProvider from "./components/dataproviders/SharepointDataProvider"; + +export default class FactoryMethodWebPart extends BaseClientSideWebPart { + private _dropdownOptions: IPropertyPaneDropdownOption[]; + private _selectedList: List; + private _disableDropdown: boolean; + private _dataProvider: IDataProvider; + private _factorymethodContainerComponent: FactoryMethod; + + protected onInit(): Promise { + this.context.statusRenderer.displayLoadingIndicator(this.domElement, "Todo"); + + /* + Create the appropriate data provider depending on where the web part is running. + The DEBUG flag will ensure the mock data provider is not bundled with the web part when you package the + solution for distribution, that is, using the --ship flag with the package-solution gulp command. + */ + if (DEBUG && Environment.type === EnvironmentType.Local) { + this._dataProvider = new MockDataProvider(); + } else { + this._dataProvider = new SharePointDataProvider(); + this._dataProvider.webPartContext = this.context; + } + + this.openPropertyPane = this.openPropertyPane.bind(this); + + /* + Get the list of tasks lists from the current site and populate the property pane dropdown field with the values. + */ + this.loadLists() + .then(() => { + /* + If a list is already selected, then we would have stored the list Id in the associated web part property. + So, check to see if we do have a selected list for the web part. If we do, then we set that as the selected list + in the property pane dropdown field. + */ + if (this.properties.spListIndex) { + this.setSelectedList(this.properties.spListIndex.toString()); + this.context.statusRenderer.clearLoadingIndicator(this.domElement); + } + }); + + return super.onInit(); + } + + // render method of the webpart, actually calls Component + public render(): void { + const element: React.ReactElement = React.createElement( + FactoryMethod, + { + spHttpClient: this.context.spHttpClient, + siteUrl: this.context.pageContext.web.absoluteUrl, + listName: this._dataProvider.selectedList === undefined ? "GenericList" : this._dataProvider.selectedList.Title, + dataProvider: this._dataProvider, + configureStartCallback: this.openPropertyPane + } + ); + + // reactDom.render(element, this.domElement); + this._factorymethodContainerComponent = ReactDom.render(element, this.domElement); + + } + + // loads lists from the site and fill the dropdown. + private loadLists(): Promise { + return this._dataProvider.getLists() + .then((lists: List[]) => { + // disable dropdown field if there are no results from the server. + this._disableDropdown = lists.length === 0; + if (lists.length !== 0) { + this._dropdownOptions = lists.map((list: List) => { + return { + key: list.Id, + text: list.Title + }; + }); + } + }); + } + + protected get dataVersion(): Version { + return Version.parse("1.0"); + } + + protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { + /* + Check the property path to see which property pane feld changed. If the property path matches the dropdown, then we set that list + as the selected list for the web part. + */ + if (propertyPath === "spListIndex") { + this.setSelectedList(newValue); + } + + /* + Finally, tell property pane to re-render the web part. + This is valid for reactive property pane. + */ + super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); + } + + // sets the selected list based on the selection from the dropdownlist + private setSelectedList(value: string): void { + const selectedIndex: number = lodash.findIndex(this._dropdownOptions, + (item: IPropertyPaneDropdownOption) => item.key === value + ); + + const selectedDropDownOption: IPropertyPaneDropdownOption = this._dropdownOptions[selectedIndex]; + + if (selectedDropDownOption) { + this._selectedList = { + Title: selectedDropDownOption.text, + Id: selectedDropDownOption.key.toString() + }; + + this._dataProvider.selectedList = this._selectedList; + } + } + + + // we add fields dynamically to the property pane, in this case its only the list field which we will render + private getGroupFields(): IPropertyPaneField[] { + const fields: IPropertyPaneField[] = []; + + // we add the options from the dropdownoptions variable that was populated during init to the dropdown here. + fields.push(PropertyPaneDropdown("spListIndex", { + label: "Select a list", + disabled: this._disableDropdown, + options: this._dropdownOptions + })); + + /* + When we do not have any lists returned from the server, we disable the dropdown. If that is the case, + we also add a label field displaying the appropriate message. + */ + if (this._disableDropdown) { + fields.push(PropertyPaneLabel(null, { + text: "Could not find tasks lists in your site. Create one or more tasks list and then try using the web part." + })); + } + + return fields; + } + + private openPropertyPane(): void { + this.context.propertyPane.open(); + } + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + return { + pages: [ + { + header: { + description: strings.PropertyPaneDescription + }, + groups: [ + { + groupName: strings.BasicGroupName, + /* + Instead of creating the fields here, we call a method that will return the set of property fields to render. + */ + groupFields: this.getGroupFields() + } + ] + } + ] + }; + } +} + + +``` + + + diff --git a/samples/react-designpatterns-typescript/README.MD b/samples/react-designpatterns-typescript/README.MD index 5a8b577ff..75eba7680 100644 --- a/samples/react-designpatterns-typescript/README.MD +++ b/samples/react-designpatterns-typescript/README.MD @@ -1,4 +1,4 @@ -# Design Patterns applied to Sharepoint Framework +# Design Patterns applied to SharePoint Framework ## Summary In software engineering, a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern isn't a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations. diff --git a/samples/react-dev-radar/README.md b/samples/react-dev-radar/README.md index 70166ed74..f069cd824 100644 --- a/samples/react-dev-radar/README.md +++ b/samples/react-dev-radar/README.md @@ -59,3 +59,4 @@ Version|Date|Comments - `gulp package-solution --ship` - `Add to AppCatalog and deploy` + diff --git a/samples/react-display-hierarchy/README.md b/samples/react-display-hierarchy/README.md index 9b8869915..98510cc25 100644 --- a/samples/react-display-hierarchy/README.md +++ b/samples/react-display-hierarchy/README.md @@ -5,7 +5,7 @@ At the time of developing this sample, the Office 365 UI fabric does not have an ![Web part preview][figure1] -The web part is configured to render the mock data when added to local SharePoint workbench. +The web part is configured to render the mock data when added to the local SharePoint workbench. ![Local SharePoint Workbench Run][figure2] When added to SharePoint site, the source list containing hierarchical information can be configured from web part properties. @@ -60,9 +60,9 @@ Version|Date|Comments ## Minimal Path to Awesome - Clone this repo -- npm i -- gulp serve --nobrowser -- Open workbench on your tennant, ie. https://contoso.sharepoint.com/sites/salestesm/_layouts/15/workbench.aspx +- `npm i` +- `gulp serve --nobrowser` +- Open workbench on your tenant, ie. https://contoso.sharepoint.com/sites/salestesm/_layouts/15/workbench.aspx - Search and add web part "Display Hierarchy" ## Features diff --git a/samples/react-enhanced-list-formatting/README.md b/samples/react-enhanced-list-formatting/README.md index e48367c97..fac9ffebe 100644 --- a/samples/react-enhanced-list-formatting/README.md +++ b/samples/react-enhanced-list-formatting/README.md @@ -172,3 +172,5 @@ This package produces the following: * gulp package-solution - Packages the solution * gulp dev -- Builds a clean instance of the solution for development purposes * gulp dist -- Builds a clean instance of the solution for distribution purposes + + diff --git a/samples/react-events-dynamicdata/README.md b/samples/react-events-dynamicdata/README.md index cad03ce9b..6cff639ec 100644 --- a/samples/react-events-dynamicdata/README.md +++ b/samples/react-events-dynamicdata/README.md @@ -38,7 +38,7 @@ Version|Date|Comments * `npm install` * `gulp bundle --ship` * `gulp package-solution --ship` -* from the _sharepoint/solution_ folder, deploy the .sppkg file to the App catalog in your tenant +* from the `sharepoint/solution` folder, deploy the `.sppkg` file to the App catalog in your tenant * in the site where you want to test this solution * add the app named _react-events-dynamicdata-client-side-solution_ * edit a page diff --git a/samples/react-feature-framework/README.md b/samples/react-feature-framework/README.md index e9a0707d5..dd33122d8 100644 --- a/samples/react-feature-framework/README.md +++ b/samples/react-feature-framework/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 3/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 3/1/2017 12:00:00 AM +--- # Deployment of SharePoint assets as part of SPFx package ## Summary @@ -28,7 +28,7 @@ Demonstrated assets getting deployed to SharePoint site are following When you install SPFx so site, web scoped feature is activated and associated element.xml files are being processed. Technically you could also provision SharePoint assets using JavaScript, but it is limited to context of the current user using that component. If the user doesn't have sufficient permissions to create or modify SharePoint items, the JavaScript code will not provision those items. -More details on the capability is available from following article +More details on the capability is available from the following article * [Provision SharePoint assets with your solution package](https://dev.office.com/sharepoint/docs/spfx/toolchain/provision-sharepoint-assets) @@ -63,19 +63,19 @@ Version|Date|Comments ## Minimal Path to Awesome -- Clone this repositoru +- Clone this repository - Move to /samples/react-feature-framework folder - in the command line run: - `npm install` - `gulp bundle` - `gulp package-solution` -- Install `react-feature-framework.sppkg` from /sharepoint/solution to app catalog in your tenant +- Install `react-feature-framework.sppkg` from `/sharepoint/solution` to app catalog in your tenant - Install solution to SharePoint site Following items are being provisioned - SPFxAmount and SPFxCostCenter fields under 'SPFx Columns' group - visible in the Site Column Gallery - Cost Center content type using 'SPFx Content Types' group using custom fields - visible in Site Content Types Gallery -- List called 'SPFx List' using custom schema.xml file using custom content tyep - visible in site contents page +- List called 'SPFx List' using custom schema.xml file using custom content type - visible in site contents page - \ No newline at end of file + diff --git a/samples/react-functional-component-with-data-fetch/README.md b/samples/react-functional-component-with-data-fetch/README.md index 77f5dbdb2..47fa00c61 100644 --- a/samples/react-functional-component-with-data-fetch/README.md +++ b/samples/react-functional-component-with-data-fetch/README.md @@ -109,7 +109,7 @@ Notice that we just take the teamsList (our state variable) and use a *map* func ## Team.tsx Component -The Teams.tsx component is only responsible for rendering an individual team, and the structure of the component follows a similar pattern to that of TeamTracker.tsx. Notice first that we use a different approach to using the props by using the object destructuring operator to unpack the individual values from the outset, which can sometimes make the subsequent code a little clearer: +The `Teams.tsx` component is only responsible for rendering an individual team, and the structure of the component follows a similar pattern to that of TeamTracker.tsx. Notice first that we use a different approach to using the props by using the object destructuring operator to unpack the individual values from the outset, which can sometimes make the subsequent code a little clearer: ``` export default function Team({ channelID, displayName, showChannels }) { @@ -126,12 +126,13 @@ Again we use state to manage a list of channels and initialise it to an empty ar }, [showChannels]); ``` -The rest of Team.tsx simply returns the rendering of the component with the name of the Team and a list of channels. If the *channelsList* is empty it will just render an empty <ul>. +The rest of `Team.tsx` simply returns the rendering of the component with the name of the Team and a list of channels. If the *channelsList* is empty it will just render an empty <ul>. If this were a real application, rather than a demonstration, you would need to decide whether it was efficient to make multiple graph calls, or whether to batch the calls in some way, which would probably make the code a little more complicated. If you end up with a large hierarchy of nested components you might also use the *useContext* hook to manage data that you retrieve at a higher level, to be referenced in lower level components without having to pass everything down through props. ## Building and testing -In the react-functional-component directory, run **npm install** to resolve all the dependencies. Once this has completed you can run **gulp serve --nobrowser** to test the web part in the workbench of your tenant (*https://mytenant.sharepoint.com/_layouts/15/workbench.aspx*). You could run it in the local workbench, but the PnPJS promise will never return and so you will just see the loading spinner. +In the `react-functional-component` directory, run `npm install` to resolve all the dependencies. Once this has completed you can run `gulp serve --nobrowser` to test the web part in the workbench of your tenant (*https://mytenant.sharepoint.com/_layouts/15/workbench.aspx*). You could run it in the local workbench, but the PnPJS promise will never return and so you will just see the loading spinner. + diff --git a/samples/react-functional-component/README.md b/samples/react-functional-component/README.md index c83b1c35a..c3134948a 100644 --- a/samples/react-functional-component/README.md +++ b/samples/react-functional-component/README.md @@ -59,23 +59,24 @@ It also introduces React Functional Components which offers a simpler way of bui ## HelloWorldWebPart.ts Simplification -A number of simplifications have been made to the HelloWorldWebPart.ts file to make it easier to follow. +A number of simplifications have been made to the `HelloWorldWebPart.ts` file to make it easier to follow. The use of an external string collection has been removed. This is only needed in multilingual situations and can be added as and when needed. For a first web part there is really no need to have the student wondering where these strings are defined. For this sample they are simply hard coded into the file to make it clear how the property pane configuration works. -The external interface to define the properties is moved from a separate file and inline into HelloWorldWebPart.ts. This interface is used by the web part and the component on the assumption that all the properties will be passed to the component as props. Adding more properties is simply a matter of adding them to the IHelloWorldProps interface, adding a section to the getPropertyPaneConfiguration return value and adding a default to the manifest file if needed. The property will then be available to the component through its **props** collection. +The external interface to define the properties is moved from a separate file and inline into `HelloWorldWebPart.ts`. This interface is used by the web part and the component on the assumption that all the properties will be passed to the component as props. Adding more properties is simply a matter of adding them to the IHelloWorldProps interface, adding a section to the getPropertyPaneConfiguration return value and adding a default to the manifest file if needed. The property will then be available to the component through its **props** collection. ## Functional Component -The HelloWorld.tsx React Component has been refactored as a pure functional component. This simplifies the code structure and will also gain you additional kudos when talking to computer scientists and functional code enthusiasts. The structure is a simple JavaScript function with the name of the component, a single argument containing the React props, and a simple return of the component rendering. Because it is just a function, there is no need to worry about **this** or **that**, constructors etc. +The `HelloWorld.tsx` React Component has been refactored as a pure functional component. This simplifies the code structure and will also gain you additional kudos when talking to computer scientists and functional code enthusiasts. The structure is a simple JavaScript function with the name of the component, a single argument containing the React props, and a simple return of the component rendering. Because it is just a function, there is no need to worry about **this** or **that**, constructors etc. -In addition the React elements returned have been simplified. In particular the "Learn more" button, which was constructed from HTML primitives in the Yeoman-generated sample, has been replaced by an Office-UI-Fabric PrimaryButton component. This also means that it has been possible to greatly simplify the SASS file HelloWorld.module.scss. +In addition the React elements returned have been simplified. In particular the "Learn more" button, which was constructed from HTML primitives in the Yeoman-generated sample, has been replaced by an Office-UI-Fabric PrimaryButton component. This also means that it has been possible to greatly simplify the SASS file `HelloWorld.module.scss`. ## Adding State -You may be wondering how maintaining state, side effects or other complexities can be accomodated with functional components like the one used. This can be achieved using a fairly new feature called [React Hooks](https://reactjs.org/docs/hooks-intro.html) and will be demonstrated using [another sample](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-functional-stateful-component). +You may be wondering how maintaining state, side effects or other complexities can be accommodated with functional components like the one used. This can be achieved by using a fairly new feature called [React Hooks](https://reactjs.org/docs/hooks-intro.html) and will be demonstrated using [another sample](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-functional-stateful-component). ## Building and testing In the react-functional-component directory run **npm install** to resolve all the dependencies. Once this has completed you can run **gulp serve** to test the web part in the local workbench. + diff --git a/samples/react-functional-servicescope-hooks/README.md b/samples/react-functional-servicescope-hooks/README.md index 287063d04..4d1c0debc 100644 --- a/samples/react-functional-servicescope-hooks/README.md +++ b/samples/react-functional-servicescope-hooks/README.md @@ -1,4 +1,4 @@ -# Consuming SPFX Service Scopes using React Hooks +# Consuming SPFx Service Scopes using React Hooks ## Summary @@ -47,7 +47,7 @@ Version|Date|Comments * At the command line run: * `gulp serve` * Navigate to remote workbench `https://tenant.sharepoint.com/_layouts/15/workbench.aspx` -* Add `HelloWorld` web part to workbench +* Add `HelloWorld` web part to the workbench ## Features diff --git a/samples/react-github-badge/report.md b/samples/react-github-badge/report.md deleted file mode 100644 index bbe1c817a..000000000 --- a/samples/react-github-badge/report.md +++ /dev/null @@ -1,280 +0,0 @@ -# Upgrade project C:\github\sp-dev-fx-webparts\samples\react-github-badge to v1.8.2 - -Date: 2019-6-6 - -## Findings - -Following is the list of steps required to upgrade your project to SharePoint Framework version 1.8.2. [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 @microsoft/sp-core-library@1.8.2 -SE -``` - -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 @microsoft/sp-lodash-subset@1.8.2 -SE -``` - -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 @microsoft/sp-office-ui-fabric-core@1.8.2 -SE -``` - -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 @microsoft/sp-webpart-base@1.8.2 -SE -``` - -File: [./package.json](./package.json) - -### FN001005 @types/react | Required - -Upgrade SharePoint Framework dependency package @types/react - -Execute the following command: - -```sh -npm i @types/react@16.7.22 -SE -``` - -File: [./package.json](./package.json) - -### FN001006 @types/react-dom | Required - -Upgrade SharePoint Framework dependency package @types/react-dom - -Execute the following command: - -```sh -npm i @types/react-dom@16.8.0 -SE -``` - -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 @microsoft/sp-property-pane@1.8.2 -SE -``` - -File: [./package.json](./package.json) - -### FN001022 office-ui-fabric-react | Required - -Install SharePoint Framework dependency package office-ui-fabric-react - -Execute the following command: - -```sh -npm i office-ui-fabric-react@6.143.0 -SE -``` - -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 @microsoft/sp-build-web@1.8.2 -DE -``` - -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 @microsoft/sp-module-interfaces@1.8.2 -DE -``` - -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 @microsoft/sp-webpart-workbench@1.8.2 -DE -``` - -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 @microsoft/sp-tslint-rules@1.8.2 -DE -``` - -File: [./package.json](./package.json) - -### FN002010 @microsoft/rush-stack-compiler-2.7 | Required - -Remove SharePoint Framework dev dependency package @microsoft/rush-stack-compiler-2.7 - -Execute the following command: - -```sh -npm un @microsoft/rush-stack-compiler-2.7 -D -``` - -File: [./package.json](./package.json) - -### FN002011 @microsoft/rush-stack-compiler-2.9 | Required - -Install SharePoint Framework dev dependency package @microsoft/rush-stack-compiler-2.9 - -Execute the following command: - -```sh -npm i @microsoft/rush-stack-compiler-2.9@0.7.7 -DE -``` - -File: [./package.json](./package.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.8.2" - } -} -``` - -File: [./.yo-rc.json](./.yo-rc.json) - -### FN012017 tsconfig.json extends property | Required - -Update tsconfig.json extends property - -In file [./tsconfig.json](./tsconfig.json) update the code as follows: - -```json -{ - "extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json" -} -``` - -File: [./tsconfig.json](./tsconfig.json) - -### FN020001 @types/react | Required - -Add resolution for package @types/react - -In file [./package.json](./package.json) update the code as follows: - -```json -{ - "resolutions": { - "@types/react": "16.7.22" - } -} -``` - -File: [./package.json](./package.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 @microsoft/sp-core-library@1.8.2 @microsoft/sp-lodash-subset@1.8.2 @microsoft/sp-office-ui-fabric-core@1.8.2 @microsoft/sp-webpart-base@1.8.2 @types/react@16.7.22 @types/react-dom@16.8.0 @microsoft/sp-property-pane@1.8.2 office-ui-fabric-react@6.143.0 -SE -npm i @microsoft/sp-build-web@1.8.2 @microsoft/sp-module-interfaces@1.8.2 @microsoft/sp-webpart-workbench@1.8.2 @microsoft/sp-tslint-rules@1.8.2 @microsoft/rush-stack-compiler-2.9@0.7.7 -DE -npm un @microsoft/rush-stack-compiler-2.7 -D -npm dedupe -``` - -### Modify files - -#### [./.yo-rc.json](./.yo-rc.json) - -Update version in .yo-rc.json: - -```json -{ - "@microsoft/generator-sharepoint": { - "version": "1.8.2" - } -} -``` - -#### [./tsconfig.json](./tsconfig.json) - -Update tsconfig.json extends property: - -```json -{ - "extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json" -} -``` - -#### [./package.json](./package.json) - -Add resolution for package @types/react: - -```json -{ - "resolutions": { - "@types/react": "16.7.22" - } -} -``` diff --git a/samples/react-google-fit/README.md b/samples/react-google-fit/README.md index fe29ba1ec..ca102ff06 100644 --- a/samples/react-google-fit/README.md +++ b/samples/react-google-fit/README.md @@ -2,7 +2,7 @@ ### Summary -This sample demonstrates integration of Google Fit information with SharePoint Framework. The Google Fitness REST APIs allows developers to extend it further and create their own dashboards. Google Fitness REST APIs are useful if you have fitness app and you want to integrate your data with google fit or if you just want to collect Fitness data and display some information to the users. This web part helps to display the key fitness information (activity time spent, distance travelled, calories burned, step count) from the Google fit data source. +This sample demonstrates the integration of Google Fit information with SharePoint Framework. The Google Fitness REST APIs allows developers to extend it further and create their own dashboards. Google Fitness REST APIs are useful if you have fitness app and you want to integrate your data with google fit or if you just want to collect Fitness data and display some information to the users. This web part helps to display the key fitness information (activity time spent, distance travelled, calories burned, step count) from the Google fit data source. ![Web part preview][figure1] @@ -75,7 +75,7 @@ Version|Date|Comments - Clone this repo - npm i - gulp serve --nobrowser -- Open workbench on your tennant, i.e. https://contoso.sharepoint.com/sites/salesteam/_layouts/15/workbench.aspx +- Open workbench on your tenant, i.e. https://contoso.sharepoint.com/sites/salesteam/_layouts/15/workbench.aspx - Search and add web part "Google Fit Activity Viewer" ## Features diff --git a/samples/react-graph-evalurl/README.md b/samples/react-graph-evalurl/README.md index 92c67edb1..9e1159d9e 100644 --- a/samples/react-graph-evalurl/README.md +++ b/samples/react-graph-evalurl/README.md @@ -7,15 +7,15 @@ This sample contains a class that evaluates the url input of a text field agains * Web Site * Lists and Document libraries -The web part shows all those three example and the reuslt returned by the Microsoft Graph. +The web part shows all those three examples and the results returned by the Microsoft Graph. ![Evaluation Client searching for suitable site collection][figure1] ### Usage -To evaluate the web part the input for site collection, web site or list and document library simply place a URL from your tennant in one of the text fields shown above. After the text field loses `onblur` its focus the evaluation happens automatically in the background and showing the debug information in the debugging information. +To evaluate the web part the input for site collection, web site or list and document library simply place a URL from your tenant in one of the text fields shown above. After the text field loses `onblur` its focus the evaluation happens automatically in the background and showing the debug information in the debugging information. -![Evaluation of web site with current debug informations][figure2] +![Evaluation of web site with current debug information][figure2] ### Project Setup and important files @@ -76,12 +76,12 @@ Version|Date|Comments - clone this repo - `$ npm i` - `$ gulp serve --nobrowser` -- Open workbench on your tennant, ie. https://contoso.sharepoint.com/sites/salestesm/_layouts/15/workbench.aspx +- Open workbench on your tenant, ie. https://contoso.sharepoint.com/sites/salestesm/_layouts/15/workbench.aspx - Search and add web part "Graph - Eval Url" ## Features -This sample web part shows how URLs in SharePoint can be checked and evaluated agains the Microsoft Graph. This scenario and the introduced class can be use to evluate not only user input but can also be used in the configuration panel of web parts. The benefit is that the URL of the target location remains visible and can be use for debugging reasons to. +This sample web part shows how URLs in SharePoint can be checked and evaluated against the Microsoft Graph. This scenario and the introduced class can be use to evluate not only user input but can also be used in the configuration panel of web parts. The benefit is that the URL of the target location remains visible and can be use for debugging reasons to. The class returns all the Graph objects identified during the evaluation process. It can also be used in backend code. - using React for building SharePoint Framework Client-side Web Parts diff --git a/samples/react-graph-reports/README.md b/samples/react-graph-reports/README.md index c14baaf55..cf10ab3f4 100644 --- a/samples/react-graph-reports/README.md +++ b/samples/react-graph-reports/README.md @@ -1,7 +1,7 @@ ## react-graph-reports ## Summary -This sample describe a SPFX application which retrieve an office 365 usage reports using Microsoft Graph API. This application also use chartjs library to generate graph. +This sample describe a SPFx application which retrieve an office 365 usage reports using Microsoft Graph API. This application also use chartjs library to generate graph. ![Office 365 Usage Reports using Microsoft Graph API](./assets/react-graph-reports.gif) @@ -47,7 +47,7 @@ If you have not previously granted the required Microsoft Graph permissions, you - Run `gulp bundle --ship` - Run `gulp package-solution --ship` -- Install the .sppkg file (under .\sharepoint\solution) to the SP app catalog +- Install the `.sppkg` file (under `.\sharepoint\solution`) to the SP app catalog - Approve the API permissions in the new SP admin center ## Features @@ -56,6 +56,6 @@ Here are main features for this application - Material UI Components (Tab) - Microsoft Graph API to retrieve Office 365 Usage reports - ChartJS integrations -- Dependency Injection in SPFX using Service Scope +- Dependency Injection in SPFx using Service Scope diff --git a/samples/react-image-gallery/README.md b/samples/react-image-gallery/README.md index 1642a589f..b7277e094 100644 --- a/samples/react-image-gallery/README.md +++ b/samples/react-image-gallery/README.md @@ -1,7 +1,7 @@ # Filterable Image Gallery Web Part ## Summary -This sample describe a SPFX application which implement an image gallery with taxonomy base filtering and typed search. This application also implement pagination. +This sample describes an SPFx application which implements an image gallery with taxonomy base filtering and typed search. This application also implements pagination. ![Filterable Image Gallery web part built on the SharePoint Framework using React](./assets/image-gallery.gif) @@ -45,11 +45,11 @@ Version|Date|Comments - Also fill in Title field for each image, this is require for typed search functionality ## Features -Here are main features for this application +Here are the main features for this application -- Taxonomy based filtering +- Taxonomy-based filtering - Typed Search -- Right side popup panel -- Server side pagination using REST API +- Right-side popup panel +- Server-side pagination using REST API diff --git a/samples/react-image-magnifier/README.md b/samples/react-image-magnifier/README.md index de2fa9357..6509480f1 100644 --- a/samples/react-image-magnifier/README.md +++ b/samples/react-image-magnifier/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 10/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 10/1/2017 12:00:00 AM +--- # Image Magnifier ## Summary @@ -60,7 +60,7 @@ Version|Date|Comments ## Features This Web Part illustrates the following concepts on top of the SharePoint Framework: -- Image Magnifier - How obtain best resolution from an image. +- Image Magnifier - How to obtain the best resolution from an image. - How to leverage the capabilities of the property pane. - \ No newline at end of file + diff --git a/samples/react-ioc-tests/README.md b/samples/react-ioc-tests/README.md index 60fe2ee20..00e353866 100644 --- a/samples/react-ioc-tests/README.md +++ b/samples/react-ioc-tests/README.md @@ -7,9 +7,9 @@ Following this pattern greatly improves the modularity, maintainability, and tes The example includes 100% test coverage, using Jest and Enzyme, of .ts and .tsx files, excluding *WebPart.ts files. A dependency resolver class specific to a web part class is used to map web part properties to component properties and create any dependent services/providers. -This pattern is implemented to seperate testable logic from the untestable *WebPart.ts file and hence ensure that all relevant web part logic is tested. To this end, a Service class is paired with a ServiceExecutor class which should extract the actual service requests from other service logic ensuring that the Service class remains fully testable. *Executor classes are excluded from test coverage as they cannot be unit tested by design - as they are points integration. +This pattern is implemented to separate testable logic from the untestable *WebPart.ts file and hence ensure that all relevant web part logic is tested. To this end, a Service class is paired with a ServiceExecutor class which should extract the actual service requests from other service logic ensuring that the Service class remains fully testable. *Executor classes are excluded from test coverage as they cannot be unit tested by design - as they are points integration. -Included in the coverage is a cache and logger class, along with a service class that fetches data asychronously using @pnp/sp 2.x +Included in the coverage is a cache and logger class, along with a service class that fetches data asynchronously using @pnp/sp 2.x The example also includes a pipeline definition .yaml file for Azure DevOps CI build pipeline (/pipelines/) which includes the build, running tests, and posting the test coverage results. @@ -70,4 +70,4 @@ https://github.com/estruyf/spfx-testing-jest - \ No newline at end of file + diff --git a/samples/react-item-History/README.md b/samples/react-item-History/README.md index b4e5cea8d..fa7e075da 100644 --- a/samples/react-item-History/README.md +++ b/samples/react-item-History/README.md @@ -44,13 +44,14 @@ Version|Date|Comments ## Features This listview command is used to show the past versions of the selected list item in a grid. -Add-PnPCustomAction ` - -Name 'Item History(GRID)' ` - -Title 'Item History(GRID)' ` - -Location 'ClientSideExtension.ListViewCommandSet.CommandBar' ` - -ClientSideComponentId "f6b9bab2-00a1-4ff1-8bc2-04fea3d64fed" ` - -RegistrationType List ` - -RegistrationId "101" ` +```bash +Add-PnPCustomAction + -Name 'Item History(GRID)' + -Title 'Item History(GRID)' + -Location 'ClientSideExtension.ListViewCommandSet.CommandBar' + -ClientSideComponentId "f6b9bab2-00a1-4ff1-8bc2-04fea3d64fed" + -RegistrationType List + -RegistrationId "101" -ClientSideComponentProperties "{}" - +``` diff --git a/samples/react-jest-testing/README.md b/samples/react-jest-testing/README.md index a59c0e9b5..05fab7fc0 100644 --- a/samples/react-jest-testing/README.md +++ b/samples/react-jest-testing/README.md @@ -102,7 +102,7 @@ Version|Date|Comments - `npm i` - `npm test` **(NOT gulp test)** -Note: You migh want to switch to a test fake offline provider to use the full functionality of the web part when on local workbench. You can do so by replacing the real pnpjs provider in IceCreamShopWebPart.ts with its fake one at line 37. Just replace IceCreamPnPJsProvider() with IceCreamFakeProvider(). +Note: You might want to switch to a test fake offline provider to use the full functionality of the web part when on local workbench. You can do so by replacing the real pnpjs provider in IceCreamShopWebPart.ts with its fake one at line 37. Just replace IceCreamPnPJsProvider() with IceCreamFakeProvider(). ``` iceCreamProvider: new IceCreamPnPJsProvider(sp), //new IceCreamFakeProvider() // replace with Fake provider when offline workbench. ``` diff --git a/samples/react-list-form/README.md b/samples/react-list-form/README.md index 47e741d59..9fd080f7a 100755 --- a/samples/react-list-form/README.md +++ b/samples/react-list-form/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 12/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 12/1/2017 12:00:00 AM +--- # React List Form WebPart ## Summary @@ -65,6 +65,6 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew - Using SharePoint REST services to retrieve and update schema and data for lists and fields. - Using Office UI Fabric React components and styles for building user experience consistent with SharePoint and Office. - Integrating drag and drop to provide better user experience for configuring web parts visually. -- Using custom drop down property editors in the property pane. +- Using custom drop-down property editors in the property pane. diff --git a/samples/react-manage-o365-groups/README.md b/samples/react-manage-o365-groups/README.md index a47a247eb..28d4ff6b0 100644 --- a/samples/react-manage-o365-groups/README.md +++ b/samples/react-manage-o365-groups/README.md @@ -204,3 +204,5 @@ This sample illustrates the following concepts on top of the SharePoint Framewor - Using Office UI Fabric controls for building SharePoint Framework client-side web parts - Passing web part properties to React components - Call MS Flow (Power Automate) flow from SharePoint Framework web part + + diff --git a/samples/react-manage-spfx-solutions-alm/README.md b/samples/react-manage-spfx-solutions-alm/README.md index 501bc42d6..f4bd0c421 100644 --- a/samples/react-manage-spfx-solutions-alm/README.md +++ b/samples/react-manage-spfx-solutions-alm/README.md @@ -14,7 +14,7 @@ This web part acts as a centralized place where admin can manage SPFx solutions **Note:** -This webpart is mainly build for tenant and sharepoint admins, since most of the operation requires admininstrator privileges. +This webpart is mainly build for tenant and SharePoint admins, since most of the operation requires administrator privileges. ## Used SharePoint Framework Version @@ -39,4 +39,5 @@ React-Manage-SPFx-Solutions-ALM|Ramakrishnan Raman **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.** ---- \ No newline at end of file +--- + diff --git a/samples/react-modern-charts/README.md b/samples/react-modern-charts/README.md index 64bf1cf7a..9af505a86 100644 --- a/samples/react-modern-charts/README.md +++ b/samples/react-modern-charts/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 3/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 3/1/2017 12:00:00 AM +--- # Modern Charts ## Summary @@ -77,14 +77,14 @@ Version|Date|Comments ## Prerequisites -- SharePoint Online tenant with Office Graph content enabled +- SharePoint Online tenant with Office Graph content-enabled ## Minimal Path to Awesome - clone this repo -- `$ npm i` -- `$ gulp serve` -- if deploying to Office 365, upadte the CDN path in write-manifests.json +- `npm i` +- `gulp serve` +- if deploying to Office 365, update the CDN path in write-manifests.json ## Features diff --git a/samples/react-msgraph-extension/README.md b/samples/react-msgraph-extension/README.md index c55996943..6b60e22de 100644 --- a/samples/react-msgraph-extension/README.md +++ b/samples/react-msgraph-extension/README.md @@ -1,7 +1,7 @@ ## react-msgraph-extension ## Summary -This sample shows how to managed Microsoft Graph Open Extension in SPFX. This application uses **User** Resource to create Open Extension. +This sample shows how to managed Microsoft Graph Open Extension in SPFx. This application uses **User** Resource to create Open Extension. ## ScreenShots @@ -53,7 +53,7 @@ If you have not previously granted the required Microsoft Graph permissions, you - Run `gulp bundle --ship` - Run `gulp package-solution --ship` -- Install the .sppkg file (under .\sharepoint\solution) to the SP app catalog +- Install the `.sppkg` file (under `.\sharepoint\solution`) to the SP app catalog - Approve the API permissions in the new SP admin center ## Features diff --git a/samples/react-multilist-grid/README.md b/samples/react-multilist-grid/README.md index 08c01877f..81e29601f 100644 --- a/samples/react-multilist-grid/README.md +++ b/samples/react-multilist-grid/README.md @@ -1,7 +1,7 @@ -# SPFX React Grid +# SPFx React Grid ## Summary -React-multilist-grid is an SPFX webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from lists that reside in multiple webs and multiple sites in a single grid, similar to Quick Edit mode. The lists do not to be of the same type – you just need to create column mappings to tell the webpart which fields to show in which columns of the grid. +React-multilist-grid is an SPFx webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from lists that reside in multiple webs and multiple sites in a single grid, similar to Quick Edit mode. The lists do not to be of the same type – you just need to create column mappings to tell the webpart which fields to show in which columns of the grid. The configuration panel of the webpart is show below. It has two buttons—one to configure the columns that will be displayed on the grid, and another to configure the lists that contain the data to be edited. ![config panel](./src/images/Configuration.PNG) @@ -108,7 +108,7 @@ Version|Date|Comments > Include any additional steps as needed. ## Features -An SPFX Webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from multiple Webs and Multiple Sites in a single grid. +An SPFx Webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from multiple Webs and Multiple Sites in a single grid. Usage: diff --git a/samples/react-multimedia-gallery/README.md b/samples/react-multimedia-gallery/README.md index 8805576e3..04ef0e761 100644 --- a/samples/react-multimedia-gallery/README.md +++ b/samples/react-multimedia-gallery/README.md @@ -51,7 +51,7 @@ number images to load | number| yes | number between 1 and 200 ## Solution -The web part Use PnPjs library, Microsoft Graph API, Office-ui-fabric-react components, react-slick Compoment +The web part Use PnPjs library, Microsoft Graph API, Office-ui-fabric-react components, react-slick Component Solution|Author(s) --------|--------- diff --git a/samples/react-my-teams/README.md b/samples/react-my-teams/README.md index acecb3c2d..7aafe7d61 100644 --- a/samples/react-my-teams/README.md +++ b/samples/react-my-teams/README.md @@ -51,7 +51,7 @@ gulp serve --nobrowser Navigate to the hosted version of SharePoint workbench, eg. https://contoso.sharepoint.com/_layouts/15/workbench.aspx -### Grant the service principal permission to the MicroSoft Graph API +### Grant the service principal permission to the Microsoft Graph API Once installed, the solution will request the required permissions via the Office 365 admin portal. If you prefer to approve the permissions in advance, for example when testing the solution in the Workbench page without installing it, you can do so using Office 365 CLI: diff --git a/samples/react-officegraph/README.md b/samples/react-officegraph/README.md index aa3802280..b9c11985b 100644 --- a/samples/react-officegraph/README.md +++ b/samples/react-officegraph/README.md @@ -1,104 +1,104 @@ ---- -page_type: sample -products: -- office-sp -- office-365 -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM - scenarios: - - Embed ---- -# React & Office Graph Web Part samples - -## Summary - -Sample SharePoint Framework Client-Side Web Parts built using React showing interacting with the Office Graph. - -### Trending in this site - -Sample SharePoint Framework Client-Side Web Part built using React showing documents trending in the current site. - -![Trending in this site Web Part in the SharePoint Workbench](./assets/trendinginthissite-preview.png) - -### Working with - -Sample SharePoint Framework Client-Side Web Part built using React showing people with whom the current user has recently been working with. - -![Working with Web Part in the SharePoint Workbench](./assets/working-with-preview.png) - -### My recent documents - -Sample SharePoint Framework Client-Side Web Part built using React showing documents recently viewed or modified by the current user. - -![Working with Web Part in the SharePoint Workbench](./assets/my-recent-documents-preview.png) - -### Trending in the sites I follow - -Sample SharePoint Framework Client-Side Web Part built using React showing documents trending in the sites followed by the current user. - -![Working with Web Part in the SharePoint Workbench](./assets/trending-in-sites-i-follow-preview.png) - -## Used SharePoint Framework Version -![drop](https://img.shields.io/badge/drop-ga-green.svg) - -## Applies to - -* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) -* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant) - -## Solution - -Solution|Author(s) ---------|--------- -react-officegraph|Waldek Mastykarz (MVP, Rencore, @waldekm), Gautam Sheth(SharePoint Consultant,Rapid Circle,@gautamdsheth) - -## Version history - -Version|Date|Comments --------|----|-------- -2.0 |May 31, 2017| GA release -1.3.0|September 20, 2016|Added the Trending in the sites I follow sample -1.2.0|September 20, 2016|Added the My recent documents sample -1.1.0|September 19, 2016|Added the Working with sample -1.0.0|September 9, 2016|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.** - ---- - -## Prerequisites - -- SharePoint Online tenant with Office Graph content -- Site Collection created under the **/sites/** Managed Path - -## Minimal Path to Awesome - -- clone this repo -- `$ npm i` -- `$ gulp serve --nobrowser` -- create a copy of the **./temp/workbench.html** file and change its extension to **.aspx** -- in the contents of the **workbench.aspx** file change the URL of the **webAbsoluteUrl** property to the URL of your site, eg. `https://contoso.sharepoint.com/sites/my-team` -- upload the **workbench.aspx** file to the Document Library in your site -- navigate to the workbench page uploaded to your SharePoint site, ie. _https://contoso.sharepoint.com/sites/my-team/documents/workbench.aspx_ - -## Features - -Sample Web Parts in this solution illustrate the following concepts on top of the SharePoint Framework: - -- using React for building SharePoint Framework Client-Side Web Parts -- using Office UI Fabric React components for building user experience consistent with SharePoint and Office -- communicating with SharePoint using its REST API -- communicating with the Office Graph via the SharePoint Search REST API -- passing Web Part properties to React components -- using ES6 Promises with vanilla-JavaScript web requests - -![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-officegraph) \ No newline at end of file +--- +page_type: sample +products: +- office-sp +- office-365 +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM + scenarios: + - Embed +--- +# React & Office Graph Web Part samples + +## Summary + +Sample SharePoint Framework Client-Side Web Parts built using React showing interacting with the Office Graph. + +### Trending in this site + +Sample SharePoint Framework Client-Side Web Part built using React showing documents trending in the current site. + +![Trending in this site Web Part in the SharePoint Workbench](./assets/trendinginthissite-preview.png) + +### Working with + +Sample SharePoint Framework Client-Side Web Part built using React showing people with whom the current user has recently been working with. + +![Working with Web Part in the SharePoint Workbench](./assets/working-with-preview.png) + +### My recent documents + +Sample SharePoint Framework Client-Side Web Part built using React showing documents recently viewed or modified by the current user. + +![Working with Web Part in the SharePoint Workbench](./assets/my-recent-documents-preview.png) + +### Trending in the sites I follow + +Sample SharePoint Framework Client-Side Web Part built using React showing documents trending in the sites followed by the current user. + +![Working with Web Part in the SharePoint Workbench](./assets/trending-in-sites-i-follow-preview.png) + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/drop-ga-green.svg) + +## Applies to + +* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) +* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant) + +## Solution + +Solution|Author(s) +--------|--------- +react-officegraph|Waldek Mastykarz (MVP, Rencore, @waldekm), Gautam Sheth(SharePoint Consultant,Rapid Circle,@gautamdsheth) + +## Version history + +Version|Date|Comments +-------|----|-------- +2.0 |May 31, 2017| GA release +1.3.0|September 20, 2016|Added the Trending in the sites I follow sample +1.2.0|September 20, 2016|Added the My recent documents sample +1.1.0|September 19, 2016|Added the Working with sample +1.0.0|September 9, 2016|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.** + +--- + +## Prerequisites + +- SharePoint Online tenant with Office Graph content +- Site Collection created under the **/sites/** Managed Path + +## Minimal Path to Awesome + +- clone this repo +- `$ npm i` +- `$ gulp serve --nobrowser` +- create a copy of the **./temp/workbench.html** file and change its extension to **.aspx** +- in the contents of the **workbench.aspx** file change the URL of the **webAbsoluteUrl** property to the URL of your site, eg. `https://contoso.sharepoint.com/sites/my-team` +- upload the **workbench.aspx** file to the Document Library in your site +- navigate to the workbench page uploaded to your SharePoint site, i.e. _https://contoso.sharepoint.com/sites/my-team/documents/workbench.aspx_ + +## Features + +Sample Web Parts in this solution illustrate the following concepts on top of the SharePoint Framework: + +- using React for building SharePoint Framework Client-Side Web Parts +- using Office UI Fabric React components for building user experience consistent with SharePoint and Office +- communicating with SharePoint using its REST API +- communicating with the Office Graph via the SharePoint Search REST API +- passing Web Part properties to React components +- using ES6 Promises with vanilla-JavaScript web requests + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-officegraph) diff --git a/samples/react-offline-first/README.md b/samples/react-offline-first/README.md index be4db0a8d..48d9aad3e 100644 --- a/samples/react-offline-first/README.md +++ b/samples/react-offline-first/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 3/1/2018 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 3/1/2018 12:00:00 AM +--- # Offline First React Webpart built using LocalForage, Whatwg-Fetch, ES6-Promise ## Summary @@ -47,7 +47,7 @@ react-offline-first |[Austin Breslin](https://github.com/AustinBreslinDev) Version |Date | Comments ------- |---- | -------- 0.0.1 |17/Feb/18 | Got the HTTP Requests to work. -0.0.2 |17/Feb/18 | Seperated interfaces into their own files. +0.0.2 |17/Feb/18 | Separated interfaces into their own files. 0.0.3 |18/Feb/18 | Updated JSDocs, and and created readme. ## Disclaimer @@ -88,6 +88,6 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew HTTP requests. - Async looping, can be found in the Queue System. - Performance techniques for browsers, async looping does not block render, -using local storage first reduces the time to draw the inital page load. +using local storage first reduces the time to draw the initial page load. - \ No newline at end of file + diff --git a/samples/react-pnp-controls-list-view-fields/README.md b/samples/react-pnp-controls-list-view-fields/README.md index 92e68b146..90e0917a7 100644 --- a/samples/react-pnp-controls-list-view-fields/README.md +++ b/samples/react-pnp-controls-list-view-fields/README.md @@ -38,15 +38,15 @@ Version|Date|Comments * `npm install` * `gulp bundle --ship` * `gulp package-solution --ship` -* from the _sharepoint/solution_ folder, deploy the .sppkg file to the App catalog in your tenant +* from the `sharepoint/solution` folder, deploy the `.sppkg` file to the App catalog in your tenant * create Term Store group as shown on the screenshot below:\ ![Term Store](./assets/taxonomy.png) * in the site where you want to test this solution * create Country list as a standard Custom list and add few items in there * create Journeys list with the next columns: * Title - Single line of text - * JorneyDate - Date and Time - * VisitedCountries - Lookup with multiselection. Referenced list - Contry + * JourneyDate - Date and Time + * VisitedCountries - Lookup with multiselection. Referenced list - Country * Experience - Choice with next choices: Bad, Normal, Good * Picture - Hyperlink or Picture, display as Picture * JourneyType - Managed Metadata connected to the Term Set created above @@ -57,3 +57,5 @@ Version|Date|Comments ## Features This sample demonstrates how to combine different parts of PnP Reusable React controls to create rich solutions. It also partially answers the question from [Issue 493](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/493) in PnP Reusable Controls repo. + + diff --git a/samples/react-project-online/README.md b/samples/react-project-online/README.md index 59a04efc1..05be3728b 100644 --- a/samples/react-project-online/README.md +++ b/samples/react-project-online/README.md @@ -1,19 +1,19 @@ ---- -page_type: sample -products: -- office-sp -- office-project -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 11/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +- office-project +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 11/1/2017 12:00:00 AM +--- # React Project Online ## Summary @@ -34,7 +34,7 @@ The web part is currently returning project tasks as a simple proof of concept. ## Prerequisites -- Office 365 subscription with SharePoint Online and Project Online licence +- Office 365 subscription with SharePoint Online and Project Online license - SharePoint Framework [development environment](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) already set up. -Project site with some tasks available. diff --git a/samples/react-property-bag-editor/README.md b/samples/react-property-bag-editor/README.md index 1b02eac85..9c43a948b 100644 --- a/samples/react-property-bag-editor/README.md +++ b/samples/react-property-bag-editor/README.md @@ -133,7 +133,7 @@ This webpart displays a list of all sites that meet the criteria specified in th ![PropertyBagFilteredSiteListDisplay](./src/images/PropertyBagFilteredSiteListDisplay.PNG) -Additionally, it lets the user narrow down the list of sites displayed by applying metadata filters that are set up by the administrator in the Property Pane( Businsess Unit and Continent in the example above): +Additionally, it lets the user narrow down the list of sites displayed by applying metadata filters that are set up by the administrator in the Property Pane( Business Unit and Continent in the example above): ![PropertyBagFilteredSiteListConfig](./src/images/PropertyBagFilteredSiteListConfigy.PNG) @@ -148,6 +148,6 @@ In the PropertyPane, an administrator just needs to specify which Managed Proper ![PropertyBagGlobalNavConfig](./src/images/PropertyBagGlobalNavConfig.PNG) -If desired, the admin can also specify which site templates should be included in the menu, as wall as any additional filters. Additional Filteres can be specified in the format 'ManagedPropertyName=value'; +If desired, the admin can also specify which site templates should be included in the menu, as wall as any additional filters. Additional Filters can be specified in the format 'ManagedPropertyName=value'; diff --git a/samples/react-quick-poll/README.md b/samples/react-quick-poll/README.md index 85a75cfff..56c4743df 100644 --- a/samples/react-quick-poll/README.md +++ b/samples/react-quick-poll/README.md @@ -22,7 +22,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 successfull 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 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. @@ -91,4 +91,6 @@ This solution doesn't work on local mode. #### SharePoint Mode If you want to try on a real environment, open: -[O365 Workbench](https://your-domain.sharepoint.com/_layouts/15/workbench.aspx) \ No newline at end of file +[O365 Workbench](https://your-domain.sharepoint.com/_layouts/15/workbench.aspx) + + diff --git a/samples/react-recaptcha/README.md b/samples/react-recaptcha/README.md index 824ebe012..1b2c13054 100644 --- a/samples/react-recaptcha/README.md +++ b/samples/react-recaptcha/README.md @@ -72,6 +72,6 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew * Using react framework in SPFx webpart * Using [PnP Placeholder control](https://sharepoint.github.io/sp-dev-fx-controls-react/controls/Placeholder/) to configure webpart. * Using [react-google-recaptcha](https://github.com/dozoisch/react-google-recaptcha) npm package in SPFx webpart -* Validate if captcha is resolved before submiting data. +* Validate if captcha is resolved before submitting data. - \ No newline at end of file + diff --git a/samples/react-rss-reader/README.md b/samples/react-rss-reader/README.md index 59af9174f..f5cf10be2 100644 --- a/samples/react-rss-reader/README.md +++ b/samples/react-rss-reader/README.md @@ -101,7 +101,7 @@ Show Publication Date | Display the publication date Show Description | Display the content or description of each feed listing Description Character Limit | The maximum number of description characters to display Link Target | The "target" of a listing link, default: _blank -Date Format | The Momment based format format of the listing date, i.e. DD/MM/YYYY (European), default: MM/DD/YYYY +Date Format | The Moment based format format of the listing date, i.e. DD/MM/YYYY (European), default: MM/DD/YYYY Title Color | Color override for a listing title Background Color | Color override for the webpart background diff --git a/samples/react-rxjs-event-emitter/README.md b/samples/react-rxjs-event-emitter/README.md index 0451dc7f6..25a1bd206 100644 --- a/samples/react-rxjs-event-emitter/README.md +++ b/samples/react-rxjs-event-emitter/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: tools - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 9/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: tools + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 9/1/2017 12:00:00 AM +--- # SPFx ReactiveX (RxJs) Event Emitter Sample # ## Summary @@ -76,5 +76,3 @@ There is a very good [blog post](https://blog.mastykarz.nl/dll-code-sharepoint-f - - diff --git a/samples/react-script-editor-onprem/README.md b/samples/react-script-editor-onprem/README.md index 2278ec1af..555b48db5 100644 --- a/samples/react-script-editor-onprem/README.md +++ b/samples/react-script-editor-onprem/README.md @@ -158,12 +158,12 @@ If you however want to allow the web part for non-script sites like Office 365 G ``` ### Deploy tenant wide -By default you have to install this web part per site collection where you want it availble. If you want it enabled by default on all sites you have to edit `package-solution.json` with the following change: +By default you have to install this web part per site collection where you want it available. If you want it enabled by default on all sites you have to edit `package-solution.json` with the following change: ``` "skipFeatureDeployment": true ``` -In order to make it availble to absolutely all sites you need apply the _Deploy to non-script sites / modern team site_ change as well. +In order to make it available to absolutely all sites you need apply the _Deploy to non-script sites / modern team site_ change as well. ## Features This web part illustrates the following concepts on top of the SharePoint Framework: diff --git a/samples/react-script-editor/README.md b/samples/react-script-editor/README.md index 825c9f64d..fd9122248 100644 --- a/samples/react-script-editor/README.md +++ b/samples/react-script-editor/README.md @@ -198,7 +198,7 @@ By default you have to install this web part per site collection where you want "skipFeatureDeployment": true ``` -In order to make it availble to absolutely all sites you need apply the _Deploy to non-script sites / modern team site_ change as well. +In order to make it available to absolutely all sites you need apply the _Deploy to non-script sites / modern team site_ change as well. ## Features This web part illustrates the following concepts on top of the SharePoint Framework: diff --git a/samples/react-search/README.md b/samples/react-search/README.md index 1370b3c71..535b739f9 100644 --- a/samples/react-search/README.md +++ b/samples/react-search/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM +--- # Search Client-Side Web Part built with React and Flux ## Summary @@ -40,7 +40,7 @@ react-search-wp|Elio Struyf (MVP, U2U, [@eliostruyf](https://twitter.com/eliostr Version|Date|Comments -------|----|-------- 0.0.4|September 08, 2016|Initial release -0.0.5|September 27, 2016|Updates for drop 4. Added the abilty to use various search tokens. Plus a logging field to watch search calls. +0.0.5|September 27, 2016|Updates for drop 4. Added the ability to use various search tokens. Plus a logging field to watch search calls. 0.0.6|October 19, 2016|Updates for drop 5. 0.1.0|January 18, 2017|Updates to support RC0. 1.0.0|February 28, 2017|Updates to support GA. @@ -64,7 +64,7 @@ Version|Date|Comments The search web part is a sample client-side web part built on the SharePoint Framework. The web part makes use of React and the Flux pattern. -The web part has built in templating support for internal (created within the project) and external (loaded from a URL) templates. +The web part has built-in templating support for internal (created within the project) and external (loaded from a URL) templates. When adding your query you are able to make use of the following tokens: {Today}, {Today+Number}, {Today-Number}, {CurrentDisplayLanguage}, {User}, {User.Name}, {User.Email}, {Site}, {SiteCollection}. @@ -92,4 +92,4 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew - loading external templates to render - loading scripts and stylesheets which are required in the external template (example: loading jQuery or a custom stylesheet). - \ No newline at end of file + diff --git a/samples/react-securitygrid/README.md b/samples/react-securitygrid/README.md index c43fe5d17..69acfdaf4 100644 --- a/samples/react-securitygrid/README.md +++ b/samples/react-securitygrid/README.md @@ -1,29 +1,29 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 12/1/2017 12:00:00 AM ---- -# SPFX React Grid +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 12/1/2017 12:00:00 AM +--- +# SPFx React Grid ## Summary -React-securitygrid is an SPFX webpart that uses React and Office-UI-Fabric to render a grid showing which users have access to which lists/libraries/folders/files on a Web as shown here: +React-securitygrid is an SPFx webpart that uses React and Office-UI-Fabric to render a grid showing which users have access to which lists/libraries/folders/files on a Web as shown here: ![config panel](./src/images/MainDisplay.PNG) -Empty libraries are displayed withh a black folder icon, those with items are displayed with a white folder. The user can expand a list or library by clicking on the desired row. For deeply nested folders the Title column can be resized by drag and drop. The display shows a 'filled-in' circle if the user has the selected permission to the given list, library, file or folder. (NOTE:The grid does not currently take into account access give via membership in an active directory group). The user must have permissions to access lists and enumerate permissions in order to view the grid. +Empty libraries are displayed with a black folder icon, those with items are displayed with a white folder. The user can expand a list or library by clicking on the desired row. For deeply nested folders the Title column can be resized by drag and drop. The display shows a 'filled-in' circle if the user has the selected permission to the given list, library, file or folder. (NOTE:The grid does not currently take into account access give via membership in an active directory group). The user must have permissions to access lists and enumerate permissions in order to view the grid. -The user can change the permission being tested by cliking the Permission in the command bar and selecting a new Permission: +The user can change the permission being tested by clicking the Permission in the command bar and selecting a new Permission: ![permission panel](./src/images/selectPermissionsPopout.PNG) @@ -58,8 +58,8 @@ Display Settings The Initial Title column width determines the initial width of the Title column(it can be resized). -The second configuarion pannel allows the owner to configure the List Settings -![List Confoguration panel](./src/images/ListConfiguration.PNG) +The second configuration panel allows the owner to configure the List Settings +![List Configuration panel](./src/images/ListConfiguration.PNG) List Settings diff --git a/samples/react-skype-status/README.md b/samples/react-skype-status/README.md index f7b207139..cd3dd7e5d 100644 --- a/samples/react-skype-status/README.md +++ b/samples/react-skype-status/README.md @@ -1,27 +1,27 @@ ---- -page_type: sample -products: -- office-sp -- skype -- office-skype-business -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 12/1/2017 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +- skype +- office-skype-business +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 12/1/2017 12:00:00 AM +--- # React Skype Status WebPart ## Summary This sample demonstrates how to use the *UCWA JS SDK* for skype with the SharePoint Framework. It shows how to subscribe to status change of the different people of the organization but you can get much more information, checkout the [documentation](https://msdn.microsoft.com/en-us/skype/websdk/docs/generalreference?f=255&MSPPError=-2147217396) It allows leverages the *Office UI Fabric* Persona card to display comprehensive information about the user. -Allows you to properly display and subscribe to change of users' availibility. +Allows you to properly display and subscribe to change of users' availability. See the demo: ![demo](./images/demo.gif) @@ -45,7 +45,7 @@ react-skype-status|[Vincent Biret](https://github.com/baywet) Version|Date|Comments -------|----|-------- 1.4|March 27th, 2019|Upgrade to SPFx 1.8.0 -1.3|November 18th, 2018|Upgrade to SPFX 1.7.0 +1.3|November 18th, 2018|Upgrade to SPFx 1.7.0 1.2|July 4th, 2018|Fixed a bug when subscribing to the current user's status 1.1|June 25th, 2018|Upgrade to 1.5 1.0|December 1, 2017|Initial release @@ -58,7 +58,7 @@ Version|Date|Comments ## Minimal Path to Awesome ### Authentication Because the Skype API's are secured, you first need to register an Azure Active Directory application. -To do that go to `portal.azure.com` and sign in as your tenant adminstrator. +To do that go to `portal.azure.com` and sign in as your tenant administrator. ![azure active directory](./images/1.PNG) Click on `Azure Active Directory`. ![app registrations](./images/2.PNG) diff --git a/samples/react-tinymce/README.md b/samples/react-tinymce/README.md index b66db3265..090b5a65b 100644 --- a/samples/react-tinymce/README.md +++ b/samples/react-tinymce/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - react - createdDate: 3/1/2018 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - react + createdDate: 3/1/2018 12:00:00 AM +--- # React TinyMCE Editor WebPart Integration with SharePoint ## Summary @@ -67,7 +67,7 @@ Description of the web part with possible additional details than in short summa This Web Part illustrates the following concepts on top of the SharePoint Framework: - Rich text editing, that has more features than the Out of the box. -- Uses an Open Source, community driven Editor. +- Uses an Open Source, community-driven Editor. - Can be extended to accept copy/paste from using Paste Plugin. - Can be extended to accept images using the manager in the Editor. - Can write your own Styles for all content on your site. diff --git a/samples/react-todo-basic/README.md b/samples/react-todo-basic/README.md index 24f3761fb..b7d56886f 100644 --- a/samples/react-todo-basic/README.md +++ b/samples/react-todo-basic/README.md @@ -1,18 +1,18 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: tools - technologies: - - SharePoint Framework - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: tools + technologies: + - SharePoint Framework + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM +--- ## Summary A simple todo web part built using react to showcase some of the SharePoint Framework developer features, utilities and best practices in building react based web parts. @@ -359,5 +359,3 @@ private _resolveBatch(batch: SPHttpClientBatch, promises: Promise<{}>[]): Promis ``` - - diff --git a/samples/react-tour-pnpjs/README.md b/samples/react-tour-pnpjs/README.md index 11e3de0c6..8f659866a 100644 --- a/samples/react-tour-pnpjs/README.md +++ b/samples/react-tour-pnpjs/README.md @@ -1,4 +1,4 @@ -# react-tour-pnpjs - SharePoint modern page tutorial: SPFX Tour sample WebPart. +# react-tour-pnpjs - SharePoint modern page tutorial: SPFx Tour sample WebPart. ## Summary @@ -67,4 +67,3 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew * How to include external React Component [ReactTourJS](https://reactour.js.org/) - diff --git a/samples/react-twitter/README.md b/samples/react-twitter/README.md index ea12e5280..febe3e122 100644 --- a/samples/react-twitter/README.md +++ b/samples/react-twitter/README.md @@ -49,3 +49,5 @@ Version|Date|Comments This sample illustrates how to use [react-twitter-embed](https://www.npmjs.com/package/react-twitter-embed) module to display Twitter timeline of specific profile, likes, list, collection or url. It also uses [PnP React Controls](https://github.com/SharePoint/sp-dev-fx-controls-react) and [PnP Property Controls](https://github.com/SharePoint/sp-dev-fx-property-controls) for rich web part and property pane components. + + diff --git a/samples/react-videolibrary/README.md b/samples/react-videolibrary/README.md index dede1d67a..3506b95dc 100644 --- a/samples/react-videolibrary/README.md +++ b/samples/react-videolibrary/README.md @@ -1,24 +1,24 @@ ---- -page_type: sample -products: -- office-sp -languages: -- javascript -- typescript -extensions: - contentType: samples - technologies: - - SharePoint Framework - platforms: - - React - createdDate: 1/1/2016 12:00:00 AM ---- +--- +page_type: sample +products: +- office-sp +languages: +- javascript +- typescript +extensions: + contentType: samples + technologies: + - SharePoint Framework + platforms: + - React + createdDate: 1/1/2016 12:00:00 AM +--- # React Video Library ## Summary -A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick) +A set of 3 SPFx webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick) to display videos stored on an Office 365 Video Channel. The idea being to display a carousel of the thumbnail images, and then -when a user clicks on one of the thumbnails, replace the tumbnail with a video player, or an Iframe playing the video. +when a user clicks on one of the thumbnails, replace the thumbnail with a video player, or an Iframe playing the video. All 3 webparts share a common utility class (O365Vutilities) that is used to talk to the tenants Video Service through its rest API (https://msdn.microsoft.com/en-us/office/office365/api/video-rest-operations) @@ -104,7 +104,7 @@ Version|Date|Comments > Include any additional steps as needed. ## Features -A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick) +A set of 3 SPFx webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick) to display videos stored on an O365 Video Channel. diff --git a/samples/react-zpl-viewer/README.md b/samples/react-zpl-viewer/README.md index bd504940f..87b2a36b8 100644 --- a/samples/react-zpl-viewer/README.md +++ b/samples/react-zpl-viewer/README.md @@ -25,7 +25,7 @@ react-zpl-viewer | Zach Roberts [spodev](https://spodev.com) Version|Date|Comments -------|----|-------- -1.0|Feburary 13, 2020|Initial release +1.0|February 13, 2020|Initial release ## Disclaimer