This commit is contained in:
mgwojciechavenga 2022-10-19 11:05:21 +02:00
commit d4aa25b1cc
2926 changed files with 1996167 additions and 2405253 deletions

View File

@ -11,7 +11,7 @@ body:
Thank you for reporting a bug! Use the sections below to submit a bug ONLY if it's related to samples in this repo. If you have an issue or question about the SharePoint Framework or its documentation, please submit it at https://aka.ms/spdev-issues.
> **IMPORTANT**
> Before you submit an issue, please run **[spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/)** from within the solution folder to diagnose incompatibility issues with your environment.
> Before you submit an issue, please follow the **[troubleshooting steps](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples)**.
- Follow our guidance on [How To Create Good Issues](https://github.com/pnp/sp-dev-fx-webparts/wiki/How-to-Create-Good-Issues).
- All bug reports are welcome - we want to fix them!
@ -22,6 +22,15 @@ body:
Please provide the following details about the issue you encountered. *Issues that do not provide the information requested in this form will be flagged as **incomplete** & automatically closed.*
- type: dropdown
id: disclaimer
validations:
required: true
attributes:
label: Disclaimer
description: I have read and understood the **[support guidelines](https://github.com/pnp/sp-dev-fx-webparts/blob/main/SUPPORT.md)**, and followed the steps listed in **[troubleshooting steps](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples)** and **[how to create good issues](https://github.com/pnp/sp-dev-fx-webparts/wiki/How-to-Create-Good-Issues)** before submitting this issue. I understand that my issue may be closed if I do not follow the guidelines.
options:
- "Yes"
- type: input
id: sample
validations:
@ -136,17 +145,16 @@ body:
- label: not applicable
- label: other (enter in the "Additional environment details" area below)
- type: input
id: Node
- type: textarea
id: spfxdoctor
validations:
required: true
attributes:
label: Node.js version
placeholder: E.g. v10.23.1
label: Paste the results of SPFx doctor
description: |
Which version of Node.js are you using?
Run `m365 spfx doctor` and paste the results.
TIP: To find out, run `node -v`
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)
- type: textarea
id: moar

View File

@ -18,6 +18,16 @@ body:
- Screenshots are always helpful (just paste any images right here in the question)
- If you're trying to start a conversation on a topic, consider using [Discussions](https://github.com/pnp/sp-dev-fx-webparts/discussions). We want to close every issue as quickly as possible, but discussions are intended for longer conversations.
- type: dropdown
id: disclaimer
validations:
required: true
attributes:
label: Disclaimer
description: I have read and understood the **[support guidelines](https://github.com/pnp/sp-dev-fx-webparts/blob/main/SUPPORT.md)** before submitting this issue. I understand that my issue may be closed if I do not follow the guidelines.
options:
- "Yes"
- type: input
id: sample
validations:

View File

@ -16,6 +16,15 @@ body:
- If you have multiple requests submit them separately so we can track resolution.
- We accept pull requests. If you know how to make it - go ahead! If you're unsure how to do a pull request, consider signing up for a free [Sharing is Caring First-Time Contributor](http://aka.ms/sharing-is-caring#pnp-sic-first-time-contributor) session.
- type: dropdown
id: disclaimer
validations:
required: true
attributes:
label: Disclaimer
description: I have read and understood the **[support guidelines](https://github.com/pnp/sp-dev-fx-webparts/blob/main/SUPPORT.md)** before submitting this issue. I understand that my issue may be closed if I do not follow the guidelines.
options:
- "Yes"
- type: textarea
id: description
attributes:

View File

@ -16,6 +16,15 @@ body:
- If you have multiple suggestions please submit them separately so we can track resolution.
- Screenshots are always helpful (just paste any images right here in the suggestion)
- type: dropdown
id: disclaimer
validations:
required: true
attributes:
label: Disclaimer
description: I have read and understood the **[support guidelines](https://github.com/pnp/sp-dev-fx-webparts/blob/main/SUPPORT.md)** before submitting this issue. I understand that my issue may be closed if I do not follow the guidelines.
options:
- "Yes"
- type: input
id: sample
validations:

View File

@ -2,6 +2,7 @@
> If you aren't familiar with how to contribute to open-source repositories using GitHub, or if you find the instructions on this page confusing, [sign up](https://forms.office.com/Pages/ResponsePage.aspx?id=KtIy2vgLW0SOgZbwvQuRaXDXyCl9DkBHq4A2OG7uLpdUREZVRDVYUUJLT1VNRDM4SjhGMlpUNzBORy4u) for one of our [Sharing is Caring](https://pnp.github.io/sharing-is-caring/#pnp-sic-events) events. It's completely free, and we'll guide you through the process.
> To submit a pull request with multiple authors, make sure that at least one commit is a co-authored commit by adding a `Co-authored-by:` trailer to the commit's message. E.g.: `Co-authored-by: name <name@example.com>`
| Q | A |
| --------------- | --------------------------------------- |

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": []
}

View File

@ -63,9 +63,9 @@ status:node-compatibility:
comment: |+
Thank you for your issue. It looks like the web part sample you're trying to use is not compatible with the version of Node.js that you're running in your environment.
Please refer to the [SPFx Compatibility Matrix](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/compatibility) to make sure that you're using a version of Node.js that is compatible with the version of SPFx the sample uses.
You should refer to [the troubleshooting instructons](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples) **before** opening an issue to make sure that you have eliminated the most common causes.
We'll close this issue, but if you run into further issues after you have made changes to your environment, please do not hesitate to open a new issue so we can help you further.
We'll close this issue, but if you run into further issues after you have made changes to your environment and followed the [troubreshooting steps](https://github.com/pnp/sp-dev-fx-webparts/wiki/Troubleshooting-issues-with-samples), please do not hesitate to open a new issue so we can help you further.
# Close the issue
close: true

View File

@ -0,0 +1,34 @@
name: 'Close stale issues'
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
permissions:
issues: write
jobs:
destale-inator:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
name: 'Close invalid authors'
with:
days-before-issue-stale: 3
days-before-issue-close: 1
days-before-pr-stale: -1
operations-per-run: 1000
stale-issue-message: 'Please note that this issue will be closed within 24 hours because it has been flagged as having invalid authors. [Read the guidance](https://github.com/pnp/sp-dev-fx-webparts/wiki/How-to-create-good-issues#mention-authors) and @mention the authors so they can respond to you.'
only-issue-labels: 'status:wrong-author'
close-issue-message: 'Closing this issue due to lack of response. Feel free to open a new issue, but make sure to follow the guidance on [how to create good issues](https://github.com/pnp/sp-dev-fx-webparts/wiki/How-to-create-good-issues#mention-authors) and @mention the authors so they can respond to you.'
- uses: actions/stale@v4
name: 'Close stale issues'
with:
days-before-issue-stale: 90
days-before-issue-close: 7
days-before-pr-stale: -1
stale-issue-label: 'status:no-recent-activity'
exempt-all-issue-assignees: true
exempt-issue-labels: 'type:sample-request'
remove-issue-stale-when-updated: true
operations-per-run: 1000
stale-issue-message: 'This issue has been marked as stale due to no progress in over 90 days. If you are still experiencing this issue, please provide additional information so we can help you resolve it.'
close-issue-message: 'Closing this issue due to lack of activity. If you are still encountering this issue, please do not hesitate to open a new issue. Make sure to follow the guidance on [how to create good issues](https://github.com/pnp/sp-dev-fx-webparts/wiki/How-to-create-good-issues) to increase your chances of getting the help you deserve.'

View File

@ -1,67 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '33 2 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

13
.github/workflows/merge-dependabot.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: auto-merge
on:
pull_request:
jobs:
auto-merge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
github-token: ${{ secrets.mytoken }}

59
.github/workflows/merge-sample-json.yml vendored Normal file
View File

@ -0,0 +1,59 @@
name: Merge sample metadata
on:
push:
branches:
# When someone push to `main` branch
- main
paths:
- 'samples/**/assets/sample.json'
jobs:
build:
name: 'Merge all sample.json files to samples.json'
runs-on: ubuntu-latest
steps:
- name: Checkout main branch
uses: actions/checkout@v2
with:
ref: main
path: main
- name: Checkout gh-pages branch
uses: actions/checkout@v2
with:
ref: gh-pages
path: gh-pages
- name: Checkout Docs branch
uses: actions/checkout@v2
with:
ref: Docs
path: Docs
- name: Join samples
run: |
jq -s '[.[][]]' ./main/samples/**/assets/sample.json > ./Docs/docs/samples.json
- name: Validate JSON
uses: docker://orrosenblatt/validate-json-action:latest
env:
INPUT_SCHEMA: ./main/templates/metadata-schema.json
INPUT_JSONS: ./Docs/docs/samples.json
- name: copy file to gh-pages
uses: canastro/copy-file-action@master
with:
source: "./Docs/docs/samples.json"
target: "./gh-pages/samples.json"
- name: Commit samples.json to gh-pages
uses: EndBug/add-and-commit@v6
with:
cwd: ./gh-pages
branch: gh-pages
add: 'samples.json --force'
push: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: Commit samples.json to Docs
uses: EndBug/add-and-commit@v6
with:
cwd: ./Docs
branch: Docs
add: 'docs/samples.json --force'
push: true
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,44 +0,0 @@
# This workflow integrates a collection of open source static analysis tools
# with GitHub code scanning. For documentation, or to provide feedback, visit
# https://github.com/github/ossar-action
name: OSSAR
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '36 20 * * 2'
jobs:
OSSAR-Scan:
# OSSAR runs on windows-latest.
# ubuntu-latest and macos-latest support coming soon
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Ensure a compatible version of dotnet is installed.
# The [Microsoft Security Code Analysis CLI](https://aka.ms/mscadocs) is built with dotnet v3.1.201.
# A version greater than or equal to v3.1.201 of dotnet must be installed on the agent in order to run this action.
# GitHub hosted runners already have a compatible version of dotnet installed and this step may be skipped.
# For self-hosted runners, ensure dotnet version 3.1.201 or later is installed by including this action:
# - name: Install .NET
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: '3.1.x'
# Run open source static analysis tools
- name: Run OSSAR
uses: github/ossar-action@v1
id: ossar
# Upload results to the Security tab
- name: Upload OSSAR results
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: ${{ steps.ossar.outputs.sarifFile }}

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 }}

20
.gitignore vendored
View File

@ -33,10 +33,22 @@ obj
*.scss.ts
packages/
samples/react-teams-send-notification/MyNotes.md
samples/react-teams-send-notification/teams/teams.zip
samples/react-find-parker/release/component-dependency-audit/react-find-parker.json
samples/react-find-parker/release/webpack-stats/react-find-parker.stats.json
# Include distributable workflow actions
!.github/actions/**/dist
# .CER Certificates
*.cer
# .PEM Certificates
*.pem
# Exclude build artifacts from samples
samples/**/dist/
samples/**/lib/
samples/**/solution/
samples/**/temp/
samples/**/release/
samples/**/node_modules

View File

@ -56,10 +56,10 @@ When you submit a new sample, please follow these guidelines:
### README.md
* You will need to have a `README.md` file for your contribution, which is based on [the provided template](../main/samples/README-template.md) under the `samples` folder. Please copy this template to your project and update it accordingly. Your `README.md` must be named exactly `README.md` -- with capital letters -- as this is the information we use to make your sample public.
* You will need to have a `README.md` file for your contribution, which is based on [the provided template](../main/templates/README-template.md) under the `samples` folder. Please copy this template to your project and update it accordingly. Your `README.md` must be named exactly `README.md` -- with capital letters -- as this is the information we use to make your sample public.
* You will need to have a screenshot picture of your sample in action in the `README.md` file ("pics or it didn't happen"). The preview image must be located in the `assets` folder in the root of your sample folder.
* All screen shots must be located in the `assets` folder. Do not point to your own repository or any other external source
* The README template contains a specific tracking image at the end of the file with an `img` element pointing to `https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/readme-template`. This is a transparent image which is used to track how many visits each sample receives in GitHub.
* The README template contains a specific tracking image at the end of the file with an `img` element pointing to `https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/YOUR-SOLUTION-NAME`. This is a transparent image which is used to track how many visits each sample receives in GitHub.
* Update the image `src` attribute according with the repository name and folder information. For example, if your sample is named `react-todo` in the `samples` folder, you should update the `src` attribute to `https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/react-todo`
* Update the image `src` attribute according with the repository name and folder information.
* If you find an existing sample which is similar to yours, please extend the existing one rather than submitting a new similar sample

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

@ -1,8 +1,8 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-aad-api-spo-cookie",
"source": "pnp",
"title": "Call custom APIs secured with Azure Active Directory without ADAL JS",
"source": "pnp",
"shortDescription": "Sample SharePoint Framework client-side web part showing how to access a custom API secured with Azure Active Directory (AAD) without using ADAL JS.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/aad-api-spo-cookie",
"longDescription": [
@ -84,4 +84,4 @@
}
]
}
]
]

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://docs.microsoft.com/en-us/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://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development

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

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.0.0",
"image": "docker.io/m365pnp/spfx:ga",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -30,3 +30,7 @@ obj
# Styles Generated Code
*.scss.ts
# .CER Certificates
*.cer
# .PEM Certificates
*.pem

View File

@ -33,6 +33,7 @@ Sample SharePoint Framework client-side web part illustrating communication with
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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

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

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.12.1",
"image": "docker.io/m365pnp/spfx:1.12.1",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
// Not needed for SPFx>= 1.12.1
// "5432": {
// "protocol": "https",
// "label": "Workbench",
// "onAutoForward": "silent"
// },
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,33 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
# Convert the generated PEM certificate to a CER certificate
openssl x509 -inform PEM -in ~/.rushstack/rushstack-serve.pem -outform DER -out ./spfx-dev-cert.cer
# Copy the PEM ecrtificate for non-Windows hosts
cp ~/.rushstack/rushstack-serve.pem ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -16,6 +16,7 @@ Final outcome:
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams")
![Workbench Local | Hosted](https://img.shields.io/badge/Workbench-Local%20%7C%20Hosted-green.svg)
![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg)
## Applies to
@ -57,6 +58,8 @@ No pre-requisites
* `npm install`
* `gulp serve`
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
## Features

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

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.0.0",
"image": "docker.io/m365pnp/spfx:ga",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -30,3 +30,7 @@ obj
# Styles Generated Code
*.scss.ts
# .CER Certificates
*.cer
# .PEM Certificates
*.pem

View File

@ -47,6 +47,7 @@ display all the lists associated with the site followed by all the items inside
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 0.4.0",
"image": "docker.io/m365pnp/spfx:0.4.0",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -34,6 +34,7 @@ This sample contains a poll web part allowing users to vote and view the results
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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
@ -61,6 +62,8 @@ Version|Date|Comments
### Poll web part
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
- create list for the poll
- in SharePoint create a new list
- in the list add new column called `NumVotes` of type **Number** (you can change the name later if you want)

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.0.0",
"image": "docker.io/m365pnp/spfx:ga",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -34,6 +34,7 @@ Edit web part properties to set Document library Name. Initially, It has been se
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg )
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "Requires access to SharePoint document libraries")
![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
@ -63,6 +64,8 @@ Version|Date|Comments
- `tsd install`
- `gulp serve --nobrowser`
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
## Features
This Web Part illustrates the following concepts on top of the SharePoint Framework & AngularJs:

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 0.1.2",
"image": "docker.io/m365pnp/spfx:0.1.2",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -41,6 +41,7 @@ The logic for querying the SharePoint Content Types in the properties of the web
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.0.0",
"image": "docker.io/m365pnp/spfx:ga",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -31,6 +31,7 @@ Sample Web Part illustrating using Angular with the SharePoint Framework.
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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
@ -59,6 +60,8 @@ Version|Date|Comments
* `npm i`
* `gulp serve`
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
## Features
The To do web part is a sample client-side web part built on the SharePoint Framework built using Angular.

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 0.1.2",
"image": "docker.io/m365pnp/spfx:0.1.2",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -38,6 +38,7 @@ Sample To Do Web Part built with Angular2. This sample illustrates how you can u
![Compatible with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![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)
@ -75,6 +76,8 @@ Version|Date|Comments
- Open the workbench
- Test out the web part
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
## Features
This is a sample client-side web part built on the SharePoint Framework using Angular2 for building the UI component of the web part.

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.4.1",
"image": "docker.io/m365pnp/spfx:1.4.1",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -29,6 +29,7 @@ Set of sample web parts illustrating how to use Angular Elements in the SharePoi
![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 Compatible](https://img.shields.io/badge/Local%20Workbench-Compatbile-green.svg)
![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
@ -56,6 +57,8 @@ Version|Date|Comments
* `npm i`
* `gulp serve`
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.
## Features
This sample contains a set of web parts that illustrate using Angular Elements in the SharePoint Framework.

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

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.4.1",
"image": "docker.io/m365pnp/spfx:1.4.1",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729,
5432
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
"5432": {
"protocol": "https",
"label": "Workbench",
"onAutoForward": "silent"
},
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,31 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer
cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

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