Merge branch 'main' of https://github.com/pnp/sp-dev-fx-webparts into patch-2693

This commit is contained in:
Abderahman88 2022-10-25 23:11:43 +02:00
commit 1ca000ecf8
1379 changed files with 1589876 additions and 836330 deletions

View File

@ -145,6 +145,24 @@ body:
- label: not applicable
- label: other (enter in the "Additional environment details" area below)
- type: input
id: installed-node
validations:
required: true
attributes:
label: What version of Node.js is currently installed on your workstation?
description: |
Run `node -v` and paste the results.
- type: input
id: required-node
validations:
required: true
attributes:
label: What version of Node.js is required by the sample?
description: |
You can find the compatible versions of React in the sample's `README.md`, in the **Compatibility** section.
- type: textarea
id: spfxdoctor
validations:
@ -152,7 +170,7 @@ body:
attributes:
label: Paste the results of SPFx doctor
description: |
Run `m365 spfx doctor -o json` and paste the results.
Run `m365 spfx doctor -o text` and paste the results.
Follow [the steps](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples#run-spfx-doctor) listed in [troubleshooting issues with samples](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples)

969
.github/fabricbot.json vendored Normal file
View File

@ -0,0 +1,969 @@
{
"version": "1.0",
"tasks": [
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "IssueResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"name": "isEvent",
"parameters": {
"eventName": "issues"
}
},
{
"name": "isAction",
"parameters": {
"action": "opened"
}
},
{
"operator": "not",
"operands": [
{
"name": "isAssignedToSomeone",
"parameters": {}
}
]
}
]
},
"taskName": "Auto-label incoming issues as Needs Triage",
"actions": [
{
"name": "addReply",
"parameters": {
"comment": "Thank you for reporting this issue. We will be triaging your incoming issue as soon as possible."
}
},
{
"name": "addLabels",
"parameters": {
"labels": [
"Needs: Triage :mag:"
]
}
}
]
},
"id": "_FrLNVmyb"
},
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "IssueResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"operator": "not",
"operands": [
{
"name": "isActivitySender",
"parameters": {
"user": "msft-github-bot"
}
}
]
},
{
"operator": "not",
"operands": [
{
"name": "isAction",
"parameters": {
"action": "closed"
}
}
]
},
{
"name": "hasLabel",
"parameters": {
"label": "no-recent-activity"
}
}
]
},
"taskName": "Remove no recent activity label",
"actions": [
{
"name": "removeLabel",
"parameters": {
"label": "no-recent-activity"
}
}
]
},
"id": "XgQc3JGUWy"
},
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "IssueResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"name": "isEvent",
"parameters": {
"eventName": "issue_comment"
}
},
{
"name": "isIssue",
"parameters": {}
},
{
"name": "isActivitySender",
"parameters": {
"user": {
"type": "author"
}
}
},
{
"name": "hasLabel",
"parameters": {
"label": "Needs: Author Feedback"
}
}
]
},
"taskName": "Add needs attention label to issues",
"actions": [
{
"name": "addLabels",
"parameters": {
"labels": [
"Needs: Attention :wave:"
]
}
}
]
},
"id": "qncidkvqrI"
},
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "IssueResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"name": "isActivitySender",
"parameters": {
"user": {
"type": "author"
}
}
},
{
"operator": "not",
"operands": [
{
"name": "isAction",
"parameters": {
"action": "closed"
}
}
]
},
{
"name": "hasLabel",
"parameters": {
"label": "Needs: Author Feedback"
}
}
]
},
"taskName": "Remove needs author feedback label from issues and pull requests",
"actions": [
{
"name": "removeLabel",
"parameters": {
"label": "Needs: Author Feedback"
}
}
]
},
"id": "KfryfubNkp"
},
{
"taskType": "scheduled",
"capabilityId": "ScheduledSearch",
"subCapability": "ScheduledSearch",
"version": "1.0",
"config": {
"taskName": "Close stale issues",
"frequency": [
{
"weekDay": 0,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 1,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 2,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 3,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 4,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 5,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 6,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
}
],
"searchTerms": [
{
"name": "isIssue",
"parameters": {}
},
{
"name": "isOpen",
"parameters": {}
},
{
"name": "hasLabel",
"parameters": {
"label": "Needs: Author Feedback"
}
},
{
"name": "hasLabel",
"parameters": {
"label": "no-recent-activity"
}
},
{
"name": "noActivitySince",
"parameters": {
"days": 7
}
}
],
"actions": [
{
"name": "closeIssue",
"parameters": {}
},
{
"name": "addReply",
"parameters": {
"comment": "Closing issue due no response from original author. If this issue is still occurring, please open a new issue with additional details. Notice that if you have included another related issue as additional comment on this, please open that also as separate issue, so that we can track it independently. "
}
}
]
},
"id": "xhkCxOyLg3"
},
{
"taskType": "scheduled",
"capabilityId": "ScheduledSearch",
"subCapability": "ScheduledSearch",
"version": "1.0",
"config": {
"frequency": [
{
"weekDay": 0,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 1,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 2,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 3,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 4,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 5,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 6,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
}
],
"searchTerms": [
{
"name": "isIssue",
"parameters": {}
},
{
"name": "isOpen",
"parameters": {}
},
{
"name": "hasLabel",
"parameters": {
"label": "Needs: Author Feedback"
}
},
{
"name": "noActivitySince",
"parameters": {
"days": 7
}
},
{
"name": "noLabel",
"parameters": {
"label": "no-recent-activity"
}
}
],
"actions": [
{
"name": "addLabels",
"parameters": {
"labels": [
"no-recent-activity"
]
}
},
{
"name": "addReply",
"parameters": {
"comment": "This issue has been automatically marked as stale because it has marked as requiring author feedback but has not had any activity for **7 days**. It will be closed if no further activity occurs **within next 7 days of this comment**. Thank you for your contributions to SharePoint Developer activities."
}
}
],
"taskName": "Mark issue with no-recent-activity label if there's no actions in 7 days"
},
"id": "pWfvfK_kKh"
},
{
"taskType": "scheduled",
"capabilityId": "ScheduledSearch",
"subCapability": "ScheduledSearch",
"version": "1.1",
"config": {
"taskName": "Closed answered issues in 7 days",
"frequency": [
{
"weekDay": 0,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 1,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 2,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 3,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 4,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 5,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
},
{
"weekDay": 6,
"hours": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]
}
],
"searchTerms": [
{
"name": "isIssue",
"parameters": {}
},
{
"name": "isOpen",
"parameters": {}
},
{
"name": "hasLabel",
"parameters": {
"label": "status:answered"
}
},
{
"name": "noActivitySince",
"parameters": {
"days": 3
}
}
],
"actions": [
{
"name": "closeIssue",
"parameters": {}
},
{
"name": "addReply",
"parameters": {
"comment": "Closing issue as \"answered\". If you encounter similar issue(s), please open up a NEW issue. Thank you."
}
}
]
},
"id": "FPQDPkT-gN"
}
],
"userGroups": []
}

18
.github/workflows/sample-scanner.yml vendored Normal file
View File

@ -0,0 +1,18 @@
# This is a basic workflow to help you get started with Actions
name: Sample Scanner
# Controls when the workflow will run
on:
workflow_dispatch:
jobs:
sample-scan:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: Sample Scanner
uses: pnp/action-sample-scanner@main
with:
dirs: '["samples"]'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -37,9 +37,9 @@ Please use following logic on submitting your questions or issues to right locat
## Additional resources
* [Overview of the SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [SharePoint Framework development tools and libraries](https://docs.microsoft.com/sharepoint/dev/spfx/tools-and-libraries)
* [Getting Started](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [Overview of the SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [SharePoint Framework development tools and libraries](https://learn.microsoft.com/sharepoint/dev/spfx/tools-and-libraries)
* [Getting Started](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Using the samples

View File

@ -31,8 +31,8 @@ Sample web part showing the list of latest orders retrieved from a custom API se
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -17,17 +17,17 @@
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net452" />
<package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.0" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.Owin" version="3.1.0" targetFramework="net452" />
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net452" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security" version="3.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.Security.ActiveDirectory" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security.Cookies" version="3.1.0" targetFramework="net452" />
<package id="Microsoft.Owin.Security.Cookies" version="4.2.2" targetFramework="net452" />
<package id="Microsoft.Owin.Security.Jwt" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security.OAuth" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security.OpenIdConnect" version="3.1.0" targetFramework="net452" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
<package id="Modernizr" version="2.6.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net452" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
<package id="Respond" version="1.2.0" targetFramework="net452" />
<package id="System.IdentityModel.Tokens.Jwt" version="4.0.0" targetFramework="net452" />

View File

@ -80,7 +80,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

33
samples/ace-strategy-pattern/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,16 @@
!dist
config
gulpfile.js
release
src
temp
tsconfig.json
tslint.json
*.log
.yo-rc.json
.vscode

View File

@ -0,0 +1,17 @@
{
"@microsoft/generator-sharepoint": {
"plusBeta": false,
"isCreatingSolution": true,
"version": "1.14.0",
"libraryName": "ace-strategy-pattern",
"libraryId": "7008c810-9588-43c3-b75c-e750cfe57fff",
"environment": "spo",
"packageManager": "npm",
"solutionName": "ace-strategy-pattern",
"solutionShortDescription": "ace-strategy-pattern description",
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"componentType": "adaptiveCardExtension",
"aceTemplateType": "Basic"
}
}

View File

@ -0,0 +1,114 @@
# ace-strategy-pattern
## Summary
This sample shows how to avoid conditions hell in quick view actions.
With more complex cards it can get extremely difficult to handle all the actions as adding more and more conditions to target action id impacts code readability.
In this sample we are exposing organization news in adaptive card.
To start we introduce Next and Previous action to navigate between loaded news.
We also want to add possibility to like and comment displayed news.
Commenting is actually a two-action operation as first- we have to show the input and then submit value provided in the input.
Finally we add support for posting this news in a Teams channel - this requires calls to get Teams, then Channels for selected Team and finally post the news in selected channel. This is how it would look like.
![](./doc-resources/QuickViewBeforeRefactoring.PNG)
Note we are already abstracting all the logic in a NewsManager, which hides away significant chunk of the code.
With few simple tricks we can refactor to this
![](./doc-resources/QuickViewAfterRefactoring.PNG)
With a better isolation and testability of our actions.
## Compatibility
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
![Node.js v14 | v12](https://img.shields.io/badge/Node.js-v14%20%7C%20v12-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
![Local Workbench Unsupported](https://img.shields.io/badge/Local%20Workbench-Unsupported-red.svg "Local workbench is no longer available as of SPFx 1.13 and above")
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg)
## Applies to
- [SharePoint Framework](https://aka.ms/spfx)
- [Microsoft 365 tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Prerequisites
None
## Solution
Solution|Author(s)
--------|---------
ace-strategy-pattern | [Marcin Wojciechowski](https://github.com/mgwojciech) [@mgwojciech](https://twitter.com/mgwojciech)
## Version history
Version|Date|Comments
-------|----|--------
1.0|May 27, 2022|Initial commit
## 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 (or [download this solution as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/ace-strategy-pattern) then unzip it)
* From your command line, change your current directory to the directory containing this sample (`ace-strategy-pattern`, located under `samples`)
* in the command line run:
* `npm install`
* `gulp serve`
- Add Graph Auto Batching
To run tests
* Clone this repository (or [download this solution as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/ace-strategy-pattern) then unzip it)
* From your command line, change your current directory to the directory containing this sample (`ace-strategy-pattern`, located under `samples`)
* in the command line run:
* `npm install`
* `npx jest`
## Features
To understand this sample we need to understand the root problem. First of all, I hope we can all agree, multiple if statements have negative impact on code readability and extensibility. At some point if You need to extend such method You cannot be sure where to put new if statement and it's difficult to controls state changes. Additionally - as it's very difficult to correctly mock objects from SPFx, we hit our test coverage, making actions almost impossible to test, or making our tests performing heavy mocking on SPFx objects (such as QuickView).
To address this we need an interface that can abstract our operations. To perform any action we effectively need only action object, current state and setState method.
We can pass all of this as a method arguments, which we do in src\manager\viewManager\viewActions\IViewActionHandler.ts.
To avoid SPFx objects reference let's create new interface which will represent a contract we have with SPFx Quick view:
src\manager\viewManager\IView.ts
With that in place we can implement a specific ActionHandlers such as
src\manager\viewManager\viewActions\NavigateAction.ts and src\manager\viewManager\viewActions\PostInTeamsAction.ts
Finally we bring it together in src\manager\viewManager\viewActions\ViewActionExecutor.ts where we register actions we want to support in our view.
Everything is handled by src\manager\viewManager\NewsQuickViewManager.ts
Note how now, we can test in isolation sharing news in Teams without any dependency on SPFx
tests\manager\viewManager\viewActions\PostInTeamsAction.test.ts
Hope You'll enjoy it.
> Notice that better pictures and documentation will increase the sample usage and the value you are providing for others. Thanks for your submissions advance.
> Share your web part with others through Microsoft 365 Patterns and Practices program to get visibility and exposure. More details on the community, open-source projects and other activities from http://aka.ms/m365pnp.
## References
- [Getting started with SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://learn.microsoft.com/sharepoint/dev/spfx/build-for-teams-overview)
- [Use Microsoft Graph in your solution](https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"complex-card-adaptive-card-extension": {
"components": [
{
"entrypoint": "./lib/adaptiveCardExtensions/complexCard/ComplexCardAdaptiveCardExtension.js",
"manifest": "./src/adaptiveCardExtensions/complexCard/ComplexCardAdaptiveCardExtension.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"ComplexCardAdaptiveCardExtensionStrings": "lib/adaptiveCardExtensions/complexCard/loc/{locale}.js"
}
}

View File

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

View File

@ -0,0 +1,40 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "ace-strategy-pattern-client-side-solution",
"id": "7008c810-9588-43c3-b75c-e750cfe57fff",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": "Undefined-1.14.0"
},
"metadata": {
"shortDescription": {
"default": "ace-strategy-pattern description"
},
"longDescription": {
"default": "ace-strategy-pattern description"
},
"screenshotPaths": [],
"videoUrl": "",
"categories": []
},
"features": [
{
"title": "ace-strategy-pattern Feature",
"description": "The feature that activates elements of the ace-strategy-pattern solution.",
"id": "828528d0-7849-4e3f-9a1d-e9322e84963e",
"version": "1.0.0.0"
}
]
},
"paths": {
"zippedPackage": "solution/ace-strategy-pattern.sppkg"
}
}

View File

@ -0,0 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -0,0 +1,16 @@
'use strict';
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(require('gulp'));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
{
"name": "ace-strategy-pattern",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.14.0",
"@microsoft/sp-property-pane": "1.14.0",
"@microsoft/sp-adaptive-card-extension-base": "1.14.0"
},
"devDependencies": {
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@microsoft/sp-build-web": "1.14.0",
"@microsoft/sp-module-interfaces": "1.14.0",
"@microsoft/sp-tslint-rules": "1.14.0",
"@types/chai": "^4.3.1",
"@types/jest": "^27.5.1",
"@types/webpack-env": "1.13.1",
"ajv": "~5.2.2",
"chai": "^4.3.6",
"gulp": "~4.0.2",
"jest": "^27.5.1",
"ts-jest": "^27.1.5"
},
"jest": {
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test))\\.(ts?|tsx?)$"
}
}

View File

@ -0,0 +1,27 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "28954e80-6946-4786-beea-bf33eca037e1",
"alias": "ComplexCardAdaptiveCardExtension",
"componentType": "AdaptiveCardExtension",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["Dashboard"],
"supportsThemeVariants": true,
"preconfiguredEntries": [{
"groupId": "bd067b1e-3ad5-4d5d-a5fe-505f07d7f59c", // Dashboard
"group": { "default": "Dashboard" },
"title": { "default": "Complex Card" },
"description": { "default": "Complex Card description" },
"officeFabricIconFontName": "SharePointLogo",
"properties": {
"title": "Complex Card"
}
}]
}

View File

@ -0,0 +1,80 @@
import { IPropertyPaneConfiguration } from '@microsoft/sp-property-pane';
import { BaseAdaptiveCardExtension } from '@microsoft/sp-adaptive-card-extension-base';
import { CardView } from './cardView/CardView';
import { QuickView } from './quickView/QuickView';
import { ComplexCardPropertyPane } from './ComplexCardPropertyPane';
import { INews, NewsProvider } from '../../dal/NewsProvider';
import { SPFxHttpClient } from '../../dal/http/SPFxHttpClient';
import { SPPFxSPHttpClient } from '../../dal/http/SPPFxSPHttpClient';
import { NewsManager } from "../../manager/NewsManager";
import { IComment, SocialInfoHelper } from '../../dal/SocialInfoHelper';
import { ITeam, ITeamsChannel, TeamsHelper } from '../../dal/TeamsHelper';
export interface IComplexCardAdaptiveCardExtensionProps {
title: string;
}
export interface IComplexCardAdaptiveCardExtensionState {
news: INews[];
selectedNewsIndex: number;
selectedNewsComments?:IComment[];
joinedTeams?: ITeam[];
selectedTeamIndex?: number;
selectedTeamChannels?: ITeamsChannel[];
commentInputVisible?: boolean;
showTeams?: boolean;
showChannels?: boolean;
selectedTeamId?: string;
selectedChannelId?: string;
}
const CARD_VIEW_REGISTRY_ID: string = 'ComplexCard_CARD_VIEW';
export const QUICK_VIEW_REGISTRY_ID: string = 'ComplexCard_QUICK_VIEW';
export default class ComplexCardAdaptiveCardExtension extends BaseAdaptiveCardExtension<
IComplexCardAdaptiveCardExtensionProps,
IComplexCardAdaptiveCardExtensionState
> {
private _deferredPropertyPane: ComplexCardPropertyPane | undefined;
public async onInit(): Promise<void> {
let tempGraphClient = await this.context.aadHttpClientFactory.getClient("https://graph.microsoft.com");
let graphClient = new SPFxHttpClient(tempGraphClient);
let spHttpClient = new SPPFxSPHttpClient(this.context.spHttpClient);
let newsProvider = new NewsProvider(graphClient);
let socialInfoHelper = new SocialInfoHelper(spHttpClient);
let teamsHelper = new TeamsHelper(graphClient);
let newsManager = new NewsManager(newsProvider, socialInfoHelper, teamsHelper);
let news = await newsManager.getNews();
let newsComments = await newsManager.loadComments(news[0]);
this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView());
this.quickViewNavigator.register(QUICK_VIEW_REGISTRY_ID, () => new QuickView(newsManager));
this.state = {
news,
selectedNewsIndex: 0,
selectedNewsComments: newsComments
};
return Promise.resolve();
}
protected loadPropertyPaneResources(): Promise<void> {
return import(
/* webpackChunkName: 'ComplexCard-property-pane'*/
'./ComplexCardPropertyPane'
)
.then(
(component) => {
this._deferredPropertyPane = new component.ComplexCardPropertyPane();
}
);
}
protected renderCard(): string | undefined {
return CARD_VIEW_REGISTRY_ID;
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return this._deferredPropertyPane!.getPropertyPaneConfiguration();
}
}

View File

@ -0,0 +1,23 @@
import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
import * as strings from 'ComplexCardAdaptiveCardExtensionStrings';
export class ComplexCardPropertyPane {
public getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: { description: strings.PropertyPaneDescription },
groups: [
{
groupFields: [
PropertyPaneTextField('title', {
label: strings.TitleFieldLabel
})
]
}
]
}
]
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,8 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.3125 13.0625C13.9196 13.0625 16.8438 10.1384 16.8438 6.53125C16.8438 2.92414 13.9196 0 10.3125 0C6.70539 0 3.78125 2.92414 3.78125 6.53125C3.78125 10.1384 6.70539 13.0625 10.3125 13.0625Z" fill="#036C70"/>
<path d="M16.5 17.875C19.5376 17.875 22 15.4126 22 12.375C22 9.33743 19.5376 6.875 16.5 6.875C13.4624 6.875 11 9.33743 11 12.375C11 15.4126 13.4624 17.875 16.5 17.875Z" fill="#1A9BA1"/>
<path d="M11 22C13.468 22 15.4688 19.9993 15.4688 17.5312C15.4688 15.0632 13.468 13.0625 11 13.0625C8.53198 13.0625 6.53125 15.0632 6.53125 17.5312C6.53125 19.9993 8.53198 22 11 22Z" fill="#37C6D0"/>
<path opacity="0.5" d="M13.75 5.50464H3.88472C3.82303 5.84353 3.78843 6.1868 3.78125 6.53119C3.78125 8.26338 4.46936 9.92463 5.69421 11.1495C6.91906 12.3743 8.5803 13.0624 10.3125 13.0624C10.5137 13.0624 10.7035 13.021 10.9001 13.0032C10.9031 13.0259 10.9038 13.0493 10.907 13.0718C10.1797 13.0855 9.46672 13.2769 8.83032 13.6293C8.19392 13.9817 7.6534 14.4844 7.25589 15.0937C6.85838 15.703 6.61595 16.4002 6.54975 17.1246C6.48355 17.8491 6.59559 18.5787 6.87609 19.2499H12.3743C12.7356 19.2499 13.0932 19.1788 13.427 19.0406C13.7607 18.9023 14.0639 18.6997 14.3194 18.4443C14.5748 18.1889 14.7774 17.8856 14.9156 17.5519C15.0539 17.2182 15.125 16.8605 15.125 16.4993V6.87964C15.125 6.51497 14.9801 6.16523 14.7223 5.90737C14.4644 5.6495 14.1147 5.50464 13.75 5.50464Z" fill="black"/>
<path d="M12.375 4.125H1.375C0.615608 4.125 0 4.74061 0 5.5V16.5C0 17.2594 0.615608 17.875 1.375 17.875H12.375C13.1344 17.875 13.75 17.2594 13.75 16.5V5.5C13.75 4.74061 13.1344 4.125 12.375 4.125Z" fill="#038387"/>
<path d="M5.07712 10.8554C4.80603 10.6695 4.58101 10.4241 4.41928 10.138C4.26016 9.83564 4.18125 9.49752 4.19007 9.15597C4.17534 8.6949 4.32648 8.24384 4.61603 7.88472C4.91574 7.53092 5.3077 7.26716 5.74833 7.12277C6.24641 6.95324 6.76967 6.8695 7.29578 6.87512C7.98742 6.84915 8.67801 6.94905 9.33393 7.16996V8.65529C9.04944 8.47693 8.73813 8.34545 8.41191 8.26587C8.05945 8.17641 7.69716 8.13156 7.33353 8.13236C6.94967 8.11836 6.5685 8.20157 6.2254 8.37427C6.0939 8.43448 5.98253 8.53131 5.90461 8.65316C5.82669 8.77501 5.78552 8.91673 5.78603 9.06137C5.78391 9.24141 5.85046 9.4155 5.97214 9.54823C6.11501 9.70135 6.28496 9.82675 6.47341 9.9181C6.68388 10.0258 6.99754 10.1686 7.41439 10.3465C7.46064 10.3616 7.50568 10.3802 7.54914 10.4021C7.96131 10.5685 8.35821 10.7705 8.73535 11.0057C9.04484 11.1955 9.29704 11.4657 9.46511 11.7875C9.63317 12.1093 9.7108 12.4707 9.68973 12.8331C9.71074 13.3069 9.57003 13.7738 9.29071 14.1571C9.01622 14.5043 8.64457 14.7618 8.22312 14.897C7.73228 15.056 7.2186 15.133 6.7027 15.1251C6.24096 15.1271 5.77996 15.088 5.32516 15.0082C4.93957 14.9448 4.56359 14.8326 4.20624 14.6745V13.1057C4.54746 13.3572 4.93095 13.5456 5.33855 13.662C5.74129 13.7918 6.16102 13.8611 6.58408 13.8679C6.97637 13.8933 7.36787 13.8075 7.71359 13.6204C7.83325 13.5482 7.93154 13.4455 7.99837 13.3228C8.06519 13.2001 8.09814 13.0618 8.09382 12.9221C8.09573 12.7231 8.01926 12.5312 7.88091 12.3881C7.71009 12.2151 7.51135 12.0722 7.29303 11.9654C7.04326 11.8356 6.67394 11.665 6.18507 11.4536C5.79647 11.2922 5.42526 11.0917 5.07712 10.8554Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,41 @@
import {
BaseBasicCardView,
IBasicCardParameters,
IExternalLinkCardAction,
IQuickViewCardAction,
ICardButton
} from '@microsoft/sp-adaptive-card-extension-base';
import * as strings from 'ComplexCardAdaptiveCardExtensionStrings';
import { IComplexCardAdaptiveCardExtensionProps, IComplexCardAdaptiveCardExtensionState, QUICK_VIEW_REGISTRY_ID } from '../ComplexCardAdaptiveCardExtension';
export class CardView extends BaseBasicCardView<IComplexCardAdaptiveCardExtensionProps, IComplexCardAdaptiveCardExtensionState> {
public get cardButtons(): [ICardButton] | [ICardButton, ICardButton] | undefined {
return [
{
title: strings.QuickViewButton,
action: {
type: 'QuickView',
parameters: {
view: QUICK_VIEW_REGISTRY_ID
}
}
}
];
}
public get data(): IBasicCardParameters {
return {
primaryText: strings.PrimaryText,
title: this.properties.title
};
}
public get onCardSelection(): IQuickViewCardAction | IExternalLinkCardAction | undefined {
return {
type: 'ExternalLink',
parameters: {
target: 'https://www.bing.com'
}
};
}
}

View File

@ -0,0 +1,11 @@
define([], function() {
return {
"PropertyPaneDescription": "Write 1-3 sentences describing the functionality of this component.",
"TitleFieldLabel": "Card title",
"Title": "Adaptive Card Extension",
"SubTitle": "Quick view",
"PrimaryText": "SPFx Adaptive Card Extension",
"Description": "Create your SPFx Adaptive Card Extension solution!",
"QuickViewButton": "Quick view"
}
});

View File

@ -0,0 +1,14 @@
declare interface IComplexCardAdaptiveCardExtensionStrings {
PropertyPaneDescription: string;
TitleFieldLabel: string;
Title: string;
SubTitle: string;
PrimaryText: string;
Description: string;
QuickViewButton: string;
}
declare module 'ComplexCardAdaptiveCardExtensionStrings' {
const strings: IComplexCardAdaptiveCardExtensionStrings;
export = strings;
}

View File

@ -0,0 +1,56 @@
import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from '@microsoft/sp-adaptive-card-extension-base';
import * as strings from 'ComplexCardAdaptiveCardExtensionStrings';
import { IComment } from '../../../dal/SocialInfoHelper';
import { NewsManager } from '../../../manager/NewsManager';
import { INewsView } from '../../../manager/viewManager/INewsView';
import { NewsQuickViewManager } from '../../../manager/viewManager/NewsQuickViewManager';
import { IComplexCardAdaptiveCardExtensionProps, IComplexCardAdaptiveCardExtensionState } from '../ComplexCardAdaptiveCardExtension';
export interface IQuickViewData extends IComplexCardAdaptiveCardExtensionState {
subTitle: string;
title: string;
description: string;
newsThumbnail: string;
joinedTeamsOptions: {title: string, value: string}[];
channelsOptions: {title: string, value: string}[];
}
export class QuickView extends BaseAdaptiveCardView<
IComplexCardAdaptiveCardExtensionProps,
IComplexCardAdaptiveCardExtensionState,
IQuickViewData
> implements INewsView {
protected viewManager: NewsQuickViewManager;
constructor(protected newsManager: NewsManager) {
super();
this.viewManager = new NewsQuickViewManager(newsManager);
}
public get data(): IQuickViewData {
let news = this.state.news[this.state.selectedNewsIndex];
let joinedTeamsOptions = this.state.joinedTeams ? this.state.joinedTeams.map((team)=>({
title: team.displayName,
value: team.id
})) : [];
let channelsOptions = this.state.selectedTeamChannels ? this.state.selectedTeamChannels.map((team)=>({
title: team.displayName,
value: team.id
})) : [];
return {
...this.state,
subTitle: `By ${news.author} on ${(new Date(news.firstPublishedDateOWSDATE)).toLocaleDateString()}`,
title: `${news.title} (${this.state.selectedNewsIndex + 1}/${this.state.news.length})`,
description: news.description,
newsThumbnail: news.pictureThumbnailURL,
joinedTeamsOptions,
channelsOptions
};
}
public get template(): ISPFxAdaptiveCard {
return require('./template/QuickViewTemplate.json');
}
public onAction(action: IActionArguments): void {
this.viewManager.handleAction(action as any, this);
}
}

View File

@ -0,0 +1,103 @@
import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from '@microsoft/sp-adaptive-card-extension-base';
import * as strings from 'ComplexCardAdaptiveCardExtensionStrings';
import { IComment } from '../../../dal/SocialInfoHelper';
import { NewsManager } from '../../../manager/NewsManager';
import { IComplexCardAdaptiveCardExtensionProps, IComplexCardAdaptiveCardExtensionState } from '../ComplexCardAdaptiveCardExtension';
export interface IQuickViewData extends IComplexCardAdaptiveCardExtensionState {
subTitle: string;
title: string;
description: string;
newsThumbnail: string;
joinedTeamsOptions: {title: string, value: string}[];
channelsOptions: {title: string, value: string}[];
}
export class QuickViewBeforeRefactoring extends BaseAdaptiveCardView<
IComplexCardAdaptiveCardExtensionProps,
IComplexCardAdaptiveCardExtensionState,
IQuickViewData
> {
constructor(protected newsManager: NewsManager) {
super();
}
public get data(): IQuickViewData {
let news = this.state.news[this.state.selectedNewsIndex];
let joinedTeamsOptions = this.state.joinedTeams ? this.state.joinedTeams.map((team)=>({
title: team.displayName,
value: team.id
})) : [];
let channelsOptions = this.state.selectedTeamChannels ? this.state.selectedTeamChannels.map((team)=>({
title: team.displayName,
value: team.id
})) : [];
return {
...this.state,
subTitle: `By ${news.author} on ${(new Date(news.firstPublishedDateOWSDATE)).toLocaleDateString()}`,
title: `${news.title} (${this.state.selectedNewsIndex + 1}/${this.state.news.length})`,
description: news.description,
newsThumbnail: news.pictureThumbnailURL,
joinedTeamsOptions,
channelsOptions
};
}
public get template(): ISPFxAdaptiveCard {
return require('./template/QuickViewTemplate.json');
}
public onAction(action: IActionArguments): void {
if (action.id === "next") {
let newIndex = (this.state.selectedNewsIndex + 1) % this.state.news.length;
this.setState({ selectedNewsIndex: newIndex, selectedNewsComments:[] });
this.newsManager.loadComments(this.state.news[newIndex]).then((comments) => {
this.setState({ selectedNewsComments: comments });
});
}
if (action.id === "previous") {
let newIndex = (this.state.selectedNewsIndex - 1) % this.state.news.length;
this.setState({ selectedNewsIndex: newIndex, selectedNewsComments:[] });
this.newsManager.loadComments(this.state.news[newIndex]).then((comments) => {
this.setState({ selectedNewsComments: comments });
});
}
if(action.id === "showAddCommentInput"){
this.setState({commentInputVisible: true});
}
if(action.id === "addComment"){
let commentText = (action as any).data.newCommentInput;
this.newsManager.commentNews(this.state.news[this.state.selectedNewsIndex], commentText).then(()=>{
let comments = this.state.selectedNewsComments;
comments.unshift({
author:{
name: "You"
} as any,
text: commentText
})
this.setState({selectedNewsComments:comments, commentInputVisible: false});
});
}
if(action.id === "likePost"){
this.newsManager.likeNews(this.state.news[this.state.selectedNewsIndex]).then(()=>{
});
}
if(action.id === "loadTeams"){
this.newsManager.getJoinedTeams().then((teams)=>{
this.setState({joinedTeams:teams, showTeams: true});
});
}
if(action.id === "showSelectChannel"){
let teamId = (action as any).data.selectTeamsDD;
this.newsManager.getChannels(teamId).then((channels)=>{
this.setState({selectedTeamChannels: channels, showChannels: true, selectedTeamId: teamId});
});
}
if(action.id === "shareInSelectedChannel"){
let channelId = (action as any).data.selectChannelDD;
this.newsManager.shareNews(this.state.news[this.state.selectedNewsIndex], this.state.selectedTeamId, channelId).then(()=>{
this.setState({selectedTeamId: null,selectedTeamChannels: [], showTeams: false, showChannels: false});
});
}
}
}

View File

@ -0,0 +1,155 @@
{
"schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.2",
"body": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "${title}"
},
{
"type": "Image",
"size": "Stretch",
"url": "${newsThumbnail}"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "${subTitle}",
"wrap": true
}
]
}
]
},
{
"type": "TextBlock",
"text": "${description}",
"wrap": true
},
{
"type": "ColumnSet",
"$data": "${selectedNewsComments}",
"columns": [
{
"type": "Column",
"width": 40,
"items": [
{
"type": "TextBlock",
"text": "${author.name}",
"wrap": true,
"size": "Small",
"isSubtle": true
}
]
},
{
"type": "Column",
"width": 50,
"items": [
{
"type": "TextBlock",
"text": "${text}",
"wrap": true,
"size": "Default"
}
]
}
],
"separator": true
},
{
"type": "Container",
"$when": "${commentInputVisible == true}",
"items": [
{
"type": "Input.Text",
"placeholder": "Your comment",
"label":"Comment",
"errorMessage": "Please enter comment",
"id":"newCommentInput",
"isRequired": true,
"inlineAction": {
"type": "Action.Submit",
"title": "Add",
"id":"addComment"
}
}
]
},
{
"type": "Input.ChoiceSet",
"$when": "${showTeams == true}",
"id": "selectTeamsDD",
"choices": "${joinedTeamsOptions}",
"placeholder": "Select a team",
"value":"${selectedTeamId}",
"separator": true,
"wrap": true
},
{
"type": "Input.ChoiceSet",
"$when": "${showChannels == true}",
"id": "selectChannelDD",
"choices": "${channelsOptions}",
"placeholder": "Select a team",
"separator": true,
"wrap": true
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Share in selected chanel",
"$when": "${showChannels == true}",
"id": "shareInSelectedChannel"
},
{
"type": "Action.Submit",
"title": "Select channel",
"$when": "${showTeams == true}",
"id": "showSelectChannel"
},
{
"type": "Action.Submit",
"title": "Like this post",
"$when": "${showTeams != true}",
"id": "likePost",
"data": "likePost"
},
{
"type": "Action.Submit",
"$when": "${showAddCommentInput != true && showTeams != true}",
"title": "Add a comment",
"id": "showAddCommentInput",
"data": "showAddCommentInput"
},
{
"type": "Action.Submit",
"title": "Share in teams",
"$when": "${showTeams != true}",
"id": "loadTeams",
"data": "loadTeams"
},
{
"type": "Action.Submit",
"title": "Previous",
"id": "previous",
"data":"previous"
},
{
"type": "Action.Submit",
"title": "Next",
"id": "next",
"data": "Next"
}
]
}

View File

@ -0,0 +1,68 @@
import { IHttpClient } from "./http/IHttpClient";
export interface INews {
author: string;
description: string;
listID: string;
listItemID: string;
path: string;
pictureThumbnailURL: string;
spWebUrl: string;
title: string;
uniqueId: string;
firstPublishedDateOWSDATE: string;
}
export class NewsProvider {
protected baseQuery = "ContentTypeId:0x0101009D1CB255DA76424F860D91F20E6C4118* AND PromotedState:2";
public static readonly baseMaxResults = 32;
protected readonly selectedField = ["Title", "Description", "PictureThumbnailURL", "Path", "UniqueId", "SPWebUrl", "ListItemID", "ListID", "Author", "FirstPublishedDateOWSDATE"];
constructor(protected graphClient: IHttpClient, public maxResults?: number, public query?: string) {
this.query = query || this.baseQuery;
this.maxResults = maxResults || NewsProvider.baseMaxResults;
}
public pollData(lastPoll: Date): Promise<any[]> {
return this.getData();
}
public getDataById(id: any): Promise<any> {
return;
}
public async getData(): Promise<INews[]> {
try {
let searchResults = await this.graphClient.post("https://graph.microsoft.com/v1.0/search/query", {
body: JSON.stringify({
"requests": [
{
"entityTypes": [
"listItem"
],
"query": {
"queryString": `${this.query}`
},
"fields": this.selectedField
}
]
})
});
if (searchResults.ok) {
let results = await searchResults.json();
return this.parseNews(results.value[0].hitsContainers[0].hits.map(hit => hit.resource.fields));
}
}
catch (err) {
throw err;
}
}
protected parseNews(news: any): INews[] {
return news.sort((a: INews, b: INews) => {
return a.firstPublishedDateOWSDATE < b.firstPublishedDateOWSDATE
? 1
: a.firstPublishedDateOWSDATE === b.firstPublishedDateOWSDATE
? 0
: -1;
});
}
}

View File

@ -0,0 +1,30 @@
import { IHttpClient } from "./http/IHttpClient";
import { INews } from "./NewsProvider";
export interface IComment{
text: string;
author: {
email: string;
name: string;
}
}
export class SocialInfoHelper {
constructor(protected spHttpClient: IHttpClient) {
}
public async likeNews(news: INews): Promise<void> {
await this.spHttpClient.post(`${news.spWebUrl}/_api/web/lists('${news.listID}')/GetItemById(${news.listItemID})/like`, { body: "{}" });
}
public async commentNews(news: INews, comment: string): Promise<void> {
await this.spHttpClient.post(`${news.spWebUrl}/_api/web/lists('${news.listID}')/GetItemById(${news.listItemID})/GetComments`, {
body: JSON.stringify({
text: comment
})
});
}
public async loadComments(news: INews): Promise<IComment[]> {
let response = await this.spHttpClient.get(`${news.spWebUrl}/_api/web/lists('${news.listID}')/GetItemById(${news.listItemID})/GetComments`);
return (await response.json()).value;
}
}

View File

@ -0,0 +1,51 @@
import { Guid } from "@microsoft/sp-core-library";
import { IHttpClient } from "./http/IHttpClient";
export interface ITeam{
id: string;
displayName: string;
}
export interface ITeamsChannel{
id: string;
displayName: string;
}
export class TeamsHelper{
constructor(protected graphClient:IHttpClient){
}
public async getTeams():Promise<ITeam[]>{
let response = await this.graphClient.get("https://graph.microsoft.com/v1.0/me/joinedTeams");
if(response.ok){
return (await response.json()).value;
}
else throw new Error("Error getting teams, " + response.statusText);
}
public async getTeamsChannels(teamId: string):Promise<ITeamsChannel[]>{
let response = await this.graphClient.get(`https://graph.microsoft.com/v1.0/teams/${teamId}/channels`);
if(response.ok){
return (await response.json()).value;
}
else throw new Error("Error getting teams, " + response.statusText);
}
public async postNewsCard(newsCard: any, teamId: string, channelId: string){
let attachmentId = Guid.newGuid().toString();
let messageBody = {
subject: newsCard.title,
body:{
contentType:"html",
content: `<attachment id="${attachmentId}"></attachment>`
},
attachments:[{
id: attachmentId,
contentType: "application/vnd.microsoft.card.adaptive",
contentUrl: null,
content: JSON.stringify(newsCard)
}]
}
return await this.graphClient.post(`https://graph.microsoft.com/v1.0/teams/${teamId}/channels/${channelId}/messages`, {
body: JSON.stringify(messageBody)
});
}
}

View File

@ -0,0 +1,16 @@
export interface IHttpClientResponse {
json: () => Promise<any>;
text: () => Promise<string>;
blob: () => Promise<Blob>;
ok: boolean;
status: number;
statusText?: string;
}
export interface IHttpClient {
get(url: string, options?: RequestInit): Promise<IHttpClientResponse>;
post(url: string, options?: RequestInit): Promise<IHttpClientResponse>;
patch(url: string, options?: RequestInit): Promise<IHttpClientResponse>;
put(url: string, options?: RequestInit): Promise<IHttpClientResponse>;
delete(url: string): Promise<IHttpClientResponse>;
}

View File

@ -0,0 +1,60 @@
import { IHttpClient, IHttpClientResponse } from "./IHttpClient";
import { AadHttpClient } from "@microsoft/sp-http";
export class SPFxHttpClient implements IHttpClient {
constructor(protected httpClient: AadHttpClient) {
}
public get(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.get(url, AadHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
Accept: "application/json",
ConsistencyLevel: "eventual",
"Content-Type": "application/json"
}
});
}
public post(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.post(url, AadHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
Accept: "application/json",
ConsistencyLevel: "eventual",
"Content-Type": "application/json"
}
});
}
public patch(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, AadHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
Accept: "application/json",
ConsistencyLevel: "eventual",
"Content-Type": "application/json"
},
method: "PATCH"
});
}
public put(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, AadHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
Accept: "application/json",
ConsistencyLevel: "eventual",
"Content-Type": "application/json"
},
method: "PUT"
});
}
public delete(url: string): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, AadHttpClient.configurations.v1, {
method: "DELETE"
});
}
}

View File

@ -0,0 +1,43 @@
import { IHttpClient, IHttpClientResponse } from "./IHttpClient";
import { SPHttpClient } from "@microsoft/sp-http";
export class SPPFxSPHttpClient implements IHttpClient {
constructor(protected httpClient: SPHttpClient) {
}
public get(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.get(url, SPHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
}
});
}
public post(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.post(url, SPHttpClient.configurations.v1, options);
}
public patch(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, SPHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
},
method: "PATCH"
});
}
public put(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, SPHttpClient.configurations.v1, {
...options,
headers: {
...options?.headers,
},
method: "PUT"
});
}
public delete(url: string): Promise<IHttpClientResponse> {
return this.httpClient.fetch(url, SPHttpClient.configurations.v1, {
method: "DELETE"
});
}
}

View File

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

View File

@ -0,0 +1,68 @@
import { IHttpClient } from "../dal/http/IHttpClient";
import { INews, NewsProvider } from "../dal/NewsProvider";
import { IComment, SocialInfoHelper } from "../dal/SocialInfoHelper";
import { ITeam, ITeamsChannel, TeamsHelper } from "../dal/TeamsHelper";
export class NewsManager{
constructor(protected newsProvider: NewsProvider, protected socialInfoHelper: SocialInfoHelper, protected teamsHelper: TeamsHelper){
}
public async getNews(): Promise<INews[]>{
return this.newsProvider.getData();
}
public async likeNews(news: INews): Promise<void>{
await this.socialInfoHelper.likeNews(news);
}
public async commentNews(news: INews, comment: string): Promise<void>{
await this.socialInfoHelper.commentNews(news, comment);
}
public async loadComments(news:INews):Promise<IComment[]>{
return this.socialInfoHelper.loadComments(news);
}
public async getJoinedTeams():Promise<ITeam[]>{
return this.teamsHelper.getTeams();
}
public async getChannels(teamId:string):Promise<ITeamsChannel[]>{
return this.teamsHelper.getTeamsChannels(teamId);
}
public async shareNews(news:INews, teamId:string, channelId:string):Promise<void>{
let newsACE = {
"schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.2",
"body": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": news.title
},
{
"type": "Image",
"size": "Stretch",
"url": news.pictureThumbnailURL
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": `By ${news.author} on ${(new Date(news.firstPublishedDateOWSDATE)).toLocaleDateString()}`,
"wrap": true
}
]
}
]
},
{
"type": "TextBlock",
"text": news.description,
"wrap": true
}]
}
await this.teamsHelper.postNewsCard(newsACE, teamId, channelId);
}
}

View File

@ -0,0 +1,5 @@
import { IComplexCardAdaptiveCardExtensionState } from "../../adaptiveCardExtensions/complexCard/ComplexCardAdaptiveCardExtension";
import { IView } from "./IView";
export interface INewsView extends IView<IComplexCardAdaptiveCardExtensionState> {
}

View File

@ -0,0 +1,4 @@
export interface IView<T>{
state: T;
setState(state: Partial<T>): void;
}

View File

@ -0,0 +1,14 @@
import { NewsManager } from "../NewsManager";
import { INewsView } from "./INewsView";
import { ViewActionExecutor } from "./viewActions/ViewActionExecutor";
export class NewsQuickViewManager{
protected actionExecutor: ViewActionExecutor;
constructor(protected newsManger: NewsManager){
this.actionExecutor = new ViewActionExecutor(this.newsManger);
}
public handleAction(action: {id: string, data?: any}, quickView: INewsView){
this.actionExecutor.handleAction(action, quickView);
}
}

View File

@ -0,0 +1,30 @@
import { NewsManager } from "../../NewsManager";
import { INewsView } from "../INewsView";
import { IViewActionHandler } from "./IViewActionHandler";
export class AddCommentAction implements IViewActionHandler{
constructor(protected newsManager: NewsManager) {
}
shouldHandleAction(action: { id: string; data?: any; }): boolean {
return action.id === "showAddCommentInput" || action.id === "addComment";
}
handleAction(action: { id: string; data?: any; }, quickView: INewsView): void {
if(action.id === "showAddCommentInput"){
quickView.setState({commentInputVisible: true});
}
if(action.id === "addComment"){
let commentText = (action as any).data.newCommentInput;
this.newsManager.commentNews(quickView.state.news[quickView.state.selectedNewsIndex], commentText).then(()=>{
let comments = quickView.state.selectedNewsComments;
comments.unshift({
author:{
name: "You"
} as any,
text: commentText
})
quickView.setState({selectedNewsComments:comments, commentInputVisible: false});
});
}
}
}

View File

@ -0,0 +1,6 @@
import { INewsView } from "../INewsView";
export interface IViewActionHandler {
shouldHandleAction(action: { id: string, data?: any }): boolean;
handleAction(action: { id: string, data?: any }, quickView: INewsView): void;
}

View File

@ -0,0 +1,16 @@
import { NewsManager } from "../../NewsManager";
import { INewsView } from "../INewsView";
import { IViewActionHandler } from "./IViewActionHandler";
export class LikePostAction implements IViewActionHandler{
constructor(protected newsManager: NewsManager) {
}
shouldHandleAction(action: { id: string; data?: any; }): boolean {
return action.id === "likePost";
}
handleAction(action: { id: string; data?: any; }, quickView: INewsView): void {
this.newsManager.likeNews(quickView.state.news[quickView.state.selectedNewsIndex]).then(()=>{
});
}
}

View File

@ -0,0 +1,19 @@
import { NewsManager } from "../../NewsManager";
import { INewsView } from "../INewsView";
import { IViewActionHandler } from "./IViewActionHandler";
export class NavigateAction implements IViewActionHandler {
constructor(protected newsManager: NewsManager) {
}
shouldHandleAction(action: { id: string; data?: any; }): boolean {
return action.id === "next" || action.id === "previous";
}
handleAction(action: { id: string; data?: any; }, quickView: INewsView): void {
let newIndex = action.id === "next" ? (quickView.state.selectedNewsIndex + 1) % quickView.state.news.length : (quickView.state.selectedNewsIndex - 1) % quickView.state.news.length;
quickView.setState({ selectedNewsIndex: newIndex, selectedNewsComments: [] });
this.newsManager.loadComments(quickView.state.news[newIndex]).then((comments) => {
quickView.setState({ selectedNewsComments: comments });
});
}
}

View File

@ -0,0 +1,31 @@
import { NewsManager } from "../../NewsManager";
import { INewsView } from "../INewsView";
import { IViewActionHandler } from "./IViewActionHandler";
export class PostInTeamsAction implements IViewActionHandler {
constructor(protected newsManager: NewsManager) {
}
shouldHandleAction(action: { id: string; data?: any; }): boolean {
return action.id === "loadTeams" || action.id === "showSelectChannel" || action.id === "shareInSelectedChannel";
}
handleAction(action: { id: string; data?: any; }, quickView: INewsView): void {
if (action.id === "loadTeams") {
this.newsManager.getJoinedTeams().then((teams) => {
quickView.setState({ joinedTeams: teams, showTeams: true });
});
}
if (action.id === "showSelectChannel") {
let teamId = (action as any).data.selectTeamsDD;
this.newsManager.getChannels(teamId).then((channels) => {
quickView.setState({ selectedTeamChannels: channels, showChannels: true, selectedTeamId: teamId });
});
}
if (action.id === "shareInSelectedChannel") {
let channelId = (action as any).data.selectChannelDD;
this.newsManager.shareNews(quickView.state.news[quickView.state.selectedNewsIndex], quickView.state.selectedTeamId, channelId).then(() => {
quickView.setState({ selectedTeamId: null, selectedTeamChannels: [], showTeams: false, showChannels: false });
});
}
}
}

View File

@ -0,0 +1,25 @@
import { INewsView } from "../INewsView";
import { AddCommentAction } from "./AddCommentAction";
import { IViewActionHandler } from "./IViewActionHandler";
import { LikePostAction } from "./LikePostAction";
import { NavigateAction } from "./NavigateAction";
import { PostInTeamsAction } from "./PostInTeamsAction";
export class ViewActionExecutor {
public actions: IViewActionHandler[];
constructor(newsManager) {
this.actions = [
new NavigateAction(newsManager),
new AddCommentAction(newsManager),
new LikePostAction(newsManager),
new PostInTeamsAction(newsManager)
];
}
public handleAction(action: { id: string; data?: any; }, quickView: INewsView): void {
this.actions.forEach((handler) => {
if (handler.shouldHandleAction(action)) {
handler.handleAction(action, quickView);
}
});
}
}

View File

@ -0,0 +1,286 @@
///<reference types="jest" />
import { NewsProvider } from "../../src/dal/NewsProvider";
import { MockGraphClient } from "../mocks/MockGraphClient";
describe("NewsProvider", () => {
it("should return news", async () => {
let graphClient = new MockGraphClient();
graphClient.responses.set("https://graph.microsoft.com/v1.0/search/query",{
"value": [
{
"searchTerms": [],
"hitsContainers": [
{
"hits": [
{
"hitId": "66A0EED6-FA43-49FD-AA03-EF82B4040140",
"rank": 1,
"summary": " daf0b71c-6de8-4ef7-b511-faae7c388708 Another post<ddd/>Another post<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test Post",
"description": "Another post",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/SitePages/Test-Post.aspx",
"uniqueId": "{66A0EED6-FA43-49FD-AA03-EF82B4040140}",
"spWebUrl": "https://test.sharepoint.com",
"listItemID": "15",
"listID": "274163ca-a930-455a-9e14-40906b4edd5d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2021-06-15T12:52:29Z"
}
}
},
{
"hitId": "B4FDA1D9-F775-48AC-AC81-31F6795223EA",
"rank": 2,
"summary": "",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test news (review) 15-05-2020",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-news-(review)-15-05-2020.aspx",
"uniqueId": "{B4FDA1D9-F775-48AC-AC81-31F6795223EA}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "17",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-05-15T11:23:19Z"
}
}
},
{
"hitId": "F20028EB-924F-4DAE-A263-82C75D33B7EE",
"rank": 3,
"summary": " How do you get started? Select 'Edit' to start working with this basic two-column template with an emphasis on text and examples of text formatting. With your page in edit mode, <ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "News from Test team",
"description": "How do you get started? Select 'Edit' to start working with this basic two-column template with an emphasis on text and examples of text formatting. With your page in edit mode, select this paragraph and replace it with your own text. Then, select t…",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/sites/Testteam646/SitePages/News-from-Test-team.aspx",
"uniqueId": "{F20028EB-924F-4DAE-A263-82C75D33B7EE}",
"spWebUrl": "https://test.sharepoint.com/sites/Testteam646",
"listItemID": "2",
"listID": "cfb20819-517b-4f63-96b9-6fd954f22933",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-04-23T07:32:58Z"
}
}
},
{
"hitId": "62EF2409-F0E9-457B-810F-75165D1228B6",
"rank": 4,
"summary": " Some test content of my test news<ddd/>Some test content of my test news<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Some test News",
"description": "Some test content of my test news",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=1b6898259ab84db389681cdffce4d117&ext=jpeg",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Some-test-News.aspx",
"uniqueId": "{62EF2409-F0E9-457B-810F-75165D1228B6}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "100",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2021-03-25T10:05:21Z"
}
}
},
{
"hitId": "92B0C0A1-BF46-4345-AD62-9A86ABE6BFFB",
"rank": 5,
"summary": " Publish something about CSGO. Today for the very first time we will be joined by my good friend Mateusz! e84a8ca2-f63c-4fb9-bc0b-d8eef5ccb22b<ddd/>Publish something about CS:GO. Today<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-30-10-2020-2",
"description": "Publish something about CS:GO. Today for the very first time we will be joined by my good friend Mateusz!",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=51b1a4ea93664b56aa174ce2a039ebea&ext=jpeg",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-30-10-2020-2.aspx",
"uniqueId": "{92B0C0A1-BF46-4345-AD62-9A86ABE6BFFB}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "80",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-10-30T12:26:47Z"
}
}
},
{
"hitId": "86FDD130-9219-4C4E-A3F5-63CB1C14A216",
"rank": 6,
"summary": " I'm going to play BG again. I already started last weekend and its amazing. Hope I'll get few hours this weekend as well. Just between You and me, I'm thinking about blowing<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-30-10-2020-1",
"description": "I'm going to play BG again. I already started last weekend and its amazing. Hope I'll get few hours this weekend as well. Just between You and me, I'm thinking about blowing todays CS for BG...difficult decisions ahead.",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=bc31bba470754c1aaa91d0a85d08df4a&ext=jpeg",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-30-10-2020-1.aspx",
"uniqueId": "{86FDD130-9219-4C4E-A3F5-63CB1C14A216}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "79",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-10-30T08:40:30Z"
}
}
},
{
"hitId": "B5EB893D-CD84-4029-B323-95D2B5594C53",
"rank": 7,
"summary": " Some test news content. Another paragraph. And one more, just for fun. And one more to check if label is passed<ddd/>Some test news content. Another paragraph. And one more, just for<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-12-11-2020-1",
"description": "Some test news content. Another paragraph. And one more, just for fun. And one more to check if label is passed",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=5800091587144706be5dc504509f80f9&ext=jpeg",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-12-11-2020-1.aspx",
"uniqueId": "{B5EB893D-CD84-4029-B323-95D2B5594C53}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "83",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-11-12T06:53:16Z"
}
}
},
{
"hitId": "4F5CC501-FD89-46AE-A48B-158FAC9B96B6",
"rank": 8,
"summary": " Hm...that might be a time to play sc again. <ddd/>Hm...that might be a time to play sc again.<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-17-11-2020-1",
"description": "Hm...that might be a time to play sc again.",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=9d181eecec514fc0b897d638bb843e3a&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=1e6009a7d2d34e758bd439a8c33282ca&ext=jpeg",
"path": "https://test.sharepoint.com/sites/tea-point-about-us/SitePages/news-Test-17-11-2020-1.aspx",
"uniqueId": "{4F5CC501-FD89-46AE-A48B-158FAC9B96B6}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point-about-us",
"listItemID": "27",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-11-17T10:40:44Z"
}
}
},
{
"hitId": "591442E1-B64D-46C9-81CF-4672C63A5D8C",
"rank": 9,
"summary": " 6409567c-ede1-4834-af41-55ca074c4048 0767823f-7fc4-48f8-9e92-3678d353f033<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-22-4-2020-NotHighlighted",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-22-4-2020-NotHighlighted.aspx",
"uniqueId": "{591442E1-B64D-46C9-81CF-4672C63A5D8C}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "13",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-04-22T07:23:27Z"
}
}
},
{
"hitId": "EB99702E-59C0-45E1-919E-F87E407A888C",
"rank": 10,
"summary": "Hello! This is a Text web part in one of two columns in this section . You can click inside this text block when in Edit mode to make changes. Next to this paragraph is a column<ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test Post in teams",
"description": "Hello! This is a Text web part in one of two columns in this section . You can click inside this text block when in Edit mode to make changes. Next to this paragraph is a column that contains an image web part. Click the image, and you can use the t…",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/sites/test/SitePages/Test-Post.aspx",
"uniqueId": "{EB99702E-59C0-45E1-919E-F87E407A888C}",
"spWebUrl": "https://test.sharepoint.com/sites/test",
"listItemID": "2",
"listID": "57d636f3-6d70-4ba3-8b47-f7775e401972",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-03-30T13:43:21Z"
}
}
},
{
"hitId": "AD6384BD-C232-4FCD-AE4E-BC0F61F91B77",
"rank": 11,
"summary": "",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-News-22-04-2020",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=cd7e4c54b5c54fc3b285a5e5135e5017&ext=png&ow=1110&oh=200",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-News-22-04-2020.aspx",
"uniqueId": "{AD6384BD-C232-4FCD-AE4E-BC0F61F91B77}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "12",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-04-22T07:22:08Z"
}
}
},
{
"hitId": "FB9CDB0F-7422-46C8-9499-BFE6DFE3BF93",
"rank": 12,
"summary": "",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test-Highlighted2",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/getpreview.ashx?guidSite=6939921445424f93b658328a712a834f&guidWeb=f5ad2fe7d9f14b968dce884fa6cc49cd&guidFile=ac954ab7f92849d8900e00cd5c0e8385&ext=jpeg&ow=2560&oh=1884",
"path": "https://test.sharepoint.com/sites/tea-point/SitePages/news-Test-Highlighted2.aspx",
"uniqueId": "{FB9CDB0F-7422-46C8-9499-BFE6DFE3BF93}",
"spWebUrl": "https://test.sharepoint.com/sites/tea-point",
"listItemID": "15",
"listID": "59930aeb-e78b-4bb4-a78c-5b7485d1445d",
"author": "Test User",
"firstPublishedDateOWSDATE": "2020-04-22T07:32:16Z"
}
}
},
{
"hitId": "C4F91266-AC1C-46F9-AE04-22E41E1584C8",
"rank": 13,
"summary": "How do you get started? Select 'Edit' to start working with this basic two-column template with an emphasis on text and examples of text formatting. With your page in edit mode, <ddd/>",
"resource": {
"@odata.type": "#microsoft.graph.listItem",
"fields": {
"title": "Test news in Test Team!",
"description": "How do you get started? Select 'Edit' to start working with this basic two-column template with an emphasis on text and examples of text formatting. With your page in edit mode, select this paragraph and replace it with your own text. Then, select t…",
"pictureThumbnailURL": "https://test.sharepoint.com/_layouts/15/images/sitepagethumbnail.png",
"path": "https://test.sharepoint.com/sites/Testteam646/SitePages/Test-news-in-Test-Team!.aspx",
"uniqueId": "{C4F91266-AC1C-46F9-AE04-22E41E1584C8}",
"spWebUrl": "https://test.sharepoint.com/sites/Testteam646",
"listItemID": "3",
"listID": "cfb20819-517b-4f63-96b9-6fd954f22933",
"author": "Test User",
"firstPublishedDateOWSDATE": "2021-04-01T06:31:49Z"
}
}
}
],
"total": 26,
"moreResultsAvailable": false
}
]
}
],
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.searchResponse)"
})
const newsProvider = new NewsProvider(graphClient);
const news = await newsProvider.getData();
expect(news.length).toBeGreaterThan(0);
expect(news[0].title).toBe("Test Post");
});
});

View File

@ -0,0 +1,89 @@
///<reference types="jest" />
import { PostInTeamsAction } from "../../../../src/manager/viewManager/viewActions/PostInTeamsAction";
describe("PostInTeamsAction", () => {
test("should load teams to state", async () => {
let mockTeams = [{
id: "1",
displayName: "team1"
}, {
id: "2",
displayName: "team2"
}, {
id: "3",
displayName: "team3"
}];
let mockManager = {
getJoinedTeams: () => Promise.resolve(mockTeams)
}
let actionHandler = new PostInTeamsAction(mockManager as any);
let quickView = {
setState: jest.fn(),
state: {}
}
let action = {
id: "loadTeams"
}
let spy = jest.spyOn(quickView, "setState");
await actionHandler.handleAction(action, quickView as any);
expect(spy).toBeCalledWith({ joinedTeams: mockTeams, showTeams: true });
});
test("should load channels to state", async () => {
let mockChannels = [{
id: "1",
displayName: "team1"
}, {
id: "2",
displayName: "team2"
}, {
id: "3",
displayName: "team3"
}];
let mockManager = {
getChannels: () => Promise.resolve(mockChannels)
}
let actionHandler = new PostInTeamsAction(mockManager as any);
let quickView = {
setState: jest.fn(),
state: {}
}
let action = {
id: "showSelectChannel",
data:{
selectTeamsDD: "1"
}
}
let spy = jest.spyOn(quickView, "setState");
await actionHandler.handleAction(action, quickView as any);
expect(spy).toBeCalledWith({ selectedTeamChannels: mockChannels, showChannels: true, selectedTeamId: "1" });
});
test("should post to selected channel", async () => {
let mockManager = {
shareNews: () => Promise.resolve()
}
let actionHandler = new PostInTeamsAction(mockManager as any);
let newsToShare = {
id: "test-news-1",
};
let quickView = {
setState: jest.fn(),
state: {
selectedTeamId: "1",
selectedNewsIndex: 0,
news:[newsToShare]
}
}
let action = {
id: "shareInSelectedChannel",
data:{
selectChannelDD: "2"
}
}
let spy = jest.spyOn(mockManager, "shareNews");
await actionHandler.handleAction(action, quickView as any);
expect(spy).toBeCalledWith(newsToShare, "1", "2");
});
});

View File

@ -0,0 +1,34 @@
import { IHttpClient, IHttpClientResponse } from "../../src/dal/http/IHttpClient";
export class MockGraphClient implements IHttpClient{
public responses: Map<string,any> = new Map<string,any>();
get(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.returnMock(url);
}
private returnMock(url: string) {
let responseBody = this.responses.get(url);
let response = {
ok: true,
statusText: "OK",
status: 200,
json: () => Promise.resolve(responseBody),
text: () => Promise.resolve(JSON.stringify(responseBody)),
blob: () => Promise.resolve(new Blob([JSON.stringify(responseBody)], { type: "application/json" }))
};
return Promise.resolve(response);
}
post(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
return this.returnMock(url);
}
patch(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
throw new Error("Method not implemented.");
}
put(url: string, options?: RequestInit): Promise<IHttpClientResponse> {
throw new Error("Method not implemented.");
}
delete(url: string): Promise<IHttpClientResponse> {
throw new Error("Method not implemented.");
}
}

View File

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

View File

@ -38,8 +38,8 @@ Sample SharePoint Framework client-side web part illustrating communication with
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -23,11 +23,11 @@
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.ActiveDirectory" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="4.2.2" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Facebook" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Google" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Jwt" version="3.0.1" targetFramework="net45" />
@ -36,7 +36,7 @@
<package id="Microsoft.Owin.Security.Twitter" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Modernizr" version="2.6.2" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Respond" version="1.2.0" targetFramework="net45" />
<package id="System.IdentityModel.Tokens.Jwt" version="4.0.0" targetFramework="net45" />

View File

@ -43,7 +43,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -21,8 +21,8 @@ Final outcome:
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Microsoft 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Microsoft 365 tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## Solution

View File

@ -49,7 +49,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
},
"devDependencies": {
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-build-web": "1.15.2",
"@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-tslint-rules": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.12.1",

View File

@ -27,8 +27,8 @@ Application after migration:
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -21,8 +21,8 @@ Sample SharePoint Framework client-side web part for managing tasks stored in a
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -50,7 +50,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -42,8 +42,8 @@ y luego mostrará todas las listas asociadas con el sitio seguidos de todos los
## Se aplica a
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Espacio empresarial de desarrollador de Office 365](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Espacio empresarial de desarrollador de Office 365](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solución
@ -94,4 +94,4 @@ Versión | Fecha |Comentarios
1. Reemplace los valores de los marcadores de posición **aad** y **redirect_uri** con el Id.
de la aplicación y la dirección URL de redireccionamiento de la aplicación de Azure registrada en el archivo GraphHelper.ts en src -> angularMsGraph -> GraphHelper.ts
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />

View File

@ -42,8 +42,8 @@ puis affiche toutes les listes associées au site, suivies de tous les élément
## Sapplique à
* [Version préliminaire pour développeurs de SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Client de développeur Office 365](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [Version préliminaire pour développeurs de SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Client de développeur Office 365](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -1,4 +1,4 @@
---
---
page_type: sample
products:
- office-sp
@ -42,8 +42,8 @@ extensions:
## 適用対象
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 Developer のテナント](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 Developer のテナント](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## 解決方法
@ -94,4 +94,4 @@ angular-msgraph|David Hartman ([Slalom](https://slalom.com))
1. **aad** および **redirect_uri** プレースホルダー値をアプリケーション ID に置き換え、
GraphHelper.ts ファイルの登録済み Azure アプリケーションの URL を src -> angularMsGraph -> GraphHelper.ts でリダイレクトします。
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />

View File

@ -42,8 +42,8 @@ e exibirá todas as listas associadas ao site seguido por todos os itens da list
## Aplicável a
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Locatário de desenvolvedor do Office 365](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Locatário de desenvolvedor do Office 365](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solução
@ -94,4 +94,4 @@ Versão | Data | Comentários
1. Substitua os valores de espaço reservado **aad** e **redirect_uri** pela ID do aplicativo e URL de redirecionamento do aplicativo registrado do Azure no arquivo GraphHelper.ts Em
src -> angularMsGraph -> GraphHelper.ts
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />

View File

@ -1,4 +1,4 @@
---
---
page_type: sample
products:
- office-sp
@ -42,8 +42,8 @@ extensions:
## Область применения
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Клиент Office 365 для разработчиков](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Клиент Office 365 для разработчиков](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Решение
@ -93,4 +93,4 @@ Angular-MSGraph|Дэвид Хартман ([Slalom](https://slalom.com))
## Настройка приложения
1. Замените **aad** и **redirect_uri** на идентификатор приложения и URL-адрес перенаправления приложения, зарегистрированного в Azure, в файле GraphHelper.ts (src -> angularMsGraph -> GraphHelper.ts)
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />

View File

@ -42,8 +42,8 @@ extensions:
## 适用于
* [SharePoint Framework 开发人员预览版](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 开发人员租户](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework 开发人员预览版](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 开发人员租户](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## 解决方案
@ -94,4 +94,4 @@ angular-msgraph|David Hartman ([Slalom](https://slalom.com))
1. 使用 src -> angularMsGraph -> GraphHelper.ts 下 GraphHelper.
ts 文件中的已注册 Azure 应用的应用程序 ID 和重定向 url 替换 **aad****redirect\_uri** 占位符值
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/angular-msgraph" />

View File

@ -52,8 +52,8 @@ display all the lists associated with the site followed by all the items inside
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -67,7 +67,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -39,8 +39,8 @@ This sample contains a poll web part allowing users to vote and view the results
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -44,7 +44,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -38,8 +38,8 @@ Edit web part properties to set Document library Name. Initially, It has been se
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -57,7 +57,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -32,8 +32,8 @@ You can find a video recording walk-through this sample from [SharePoint PnP You
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -51,7 +51,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -46,8 +46,8 @@ The logic for querying the SharePoint Content Types in the properties of the web
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -43,7 +43,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -36,8 +36,8 @@ Sample Web Part illustrating using Angular with the SharePoint Framework.
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -44,7 +44,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -44,8 +44,8 @@ Sample To Do Web Part built with Angular2. This sample illustrates how you can u
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Prerequisites

View File

@ -43,7 +43,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -34,8 +34,8 @@ Set of sample web parts illustrating how to use Angular Elements in the SharePoi
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -51,7 +51,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -35,17 +35,17 @@
}
},
"@angular/core": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-6.0.3.tgz",
"integrity": "sha512-YJk0kS/V9C2JFKMHfiw6TNxmfkYWGW4HzqGOm/VoPkvj9O4Erkz/OtOOc8hYGmXFmQz1UpOAByzY2XIlEi72XQ==",
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.5.tgz",
"integrity": "sha512-XAXWQi7R3ucZXQwx9QK5jSKJeQyRJ53u2dQDpr7R5stzeCy1a5hrNOkZLg9zOTTPcth/6+FrOrRZP9SMdxtw3w==",
"requires": {
"tslib": "1.9.1"
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.1.tgz",
"integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg=="
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},

View File

@ -13,7 +13,7 @@
"dependencies": {
"@angular/common": "^6.0.3",
"@angular/compiler": "^6.0.3",
"@angular/core": "^6.0.3",
"@angular/core": "^11.0.5",
"@angular/elements": "^6.0.3",
"@angular/platform-browser": "^6.0.3",
"@angular/platform-browser-dynamic": "^6.0.3",

View File

@ -34,8 +34,8 @@ A sample web part illustrating how to use Angular Elements in the SharePoint Fra
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -44,7 +44,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
"dependencies": {
"@angular/common": "^6.0.3",
"@angular/compiler": "^6.0.3",
"@angular/core": "^6.0.3",
"@angular/core": "^11.0.5",
"@angular/elements": "^6.0.3",
"@angular/platform-browser": "^6.0.3",
"@angular/platform-browser-dynamic": "^6.0.3",
@ -34,9 +34,9 @@
"zone.js": "^0.8.26"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.1",
"@microsoft/sp-build-web": "~1.15.2",
"@microsoft/sp-module-interfaces": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.12.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"ajv": "~5.2.2",

View File

@ -40,8 +40,8 @@ Sample bootstrap slider which pulls the slides from a list inside the SharePoint
## Applies to
* [SharePoint Framework Developer GA](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer GA](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -55,7 +55,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

View File

@ -30,8 +30,8 @@ This sample demonstrate how to set up SPFx to use [Handlebars](http://handlebars
## Applies to
* [SharePoint Framework Developer](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -44,7 +44,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -13,9 +13,9 @@
"handlebars": "^4.7.7"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.1",
"@microsoft/sp-build-web": "~1.15.2",
"@microsoft/sp-module-interfaces": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.12.1",
"@types/chai": "~4.1.2",
"@types/es6-collections": "^0.5.31",
"@types/mocha": "~2.2.48",

View File

@ -38,8 +38,8 @@ This is a sample web Part that illustrates the use of jQuery and its plugins loa
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
* [SharePoint Framework Developer Preview](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution

View File

@ -44,7 +44,7 @@
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
"url": "https://learn.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}

Some files were not shown because too many files have changed in this diff Show More