Merge branch 'pnp:main' into React-Calendar-recurrent-events-issue

This commit is contained in:
mohammadamer 2021-11-01 01:18:06 +02:00 committed by GitHub
commit 195e67ec28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
165 changed files with 114045 additions and 38821 deletions

View File

@ -37,6 +37,7 @@ Short summary on functionality and used technologies.
> If using SPFx 1.12.1, update the SPFx and Node.js tags accordingly:
> ![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
> ![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
>
> If using an older version of SPFx, update the SPFx and Node.js compatibility tag accordingly:
> SPFx 1.11
> ![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg)
@ -47,12 +48,13 @@ Short summary on functionality and used technologies.
> ![Node.js LTS 6.x | LTS 8.x](https://img.shields.io/badge/Node.js-LTS%206.x%20%7C%20LTS%208.x-green.svg)
>
> If you built this sample specifically for SharePoint 2016, or SharePoint 2019 support, update the SharePoint compatibility tag accordingly:
> ![SharePoint 2019 | Online](https://img.shields.io/badge/SharePoint-2019%20%7C%20Online-yellow.svg)
> ![SharePoint 2016 | 2019 | Online](https://img.shields.io/badge/SharePoint-2016%20%7C%202019%20%7C%20Online-green.svg)
> ![Works with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Compatible-green.svg)
> ![Work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Compatible-green.svg)
> If you know your web part only works on the hosted workbench, you can use this for the workbench compatibility tag:
> ![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "The solution requires access to XXX")
> ![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
>
>
> If you specifically built and tested this web part to work with Teams, use this for the Teams compatibility tag:
> ![Teams Yes: Designed for Microsoft Teams](https://img.shields.io/badge/Teams-Yes-green.svg "Designed for Microsoft Teams")
> And if you know for sure that it is NOT compatible with Teams, use this:
@ -126,6 +128,10 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3AYOUR-SOLUTION-NAME) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3AYOUR-SOLUTION-NAME) and see what the community is saying.
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=YOUR-SOLUTION-NAME&authors=@YOURGITHUBUSERNAME&title=YOUR-SOLUTION-NAME%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=YOUR-SOLUTION-NAME&authors=@YOURGITHUBUSERNAME&title=YOUR-SOLUTION-NAME%20-%20).

View File

@ -47,6 +47,7 @@ Solution|Author(s)
js-modern-calendar | Jeremy Coleman (MCP, PC Professional, Inc.)
js-modern-calendar | Nanddeep Nachan ([@NanddeepNachan](twitter.com/NanddeepNachan))
js-modern-calendar | Ravi Chandra ([Ravikadri](https://github.com/Ravikadri))
js-modern-calendar | Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at))
## Version history
@ -55,6 +56,7 @@ Version|Date|Comments
1.0.0.0|February 11, 2017|Initial release
1.0.0.1|June 05, 2020|Updated the external CDN references to public CDN references
1.0.2.0|February 9, 2021|Upgraded to SPFx 1.11 and fixed issues with missing dependencies
1.0.3.0|October 28, 2021|fixed issues with Timezones. The Browser Timezone Settings are now used
## Disclaimer

View File

@ -21,7 +21,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.6.0"
"value": "1.11.0"
}
],
"thumbnails": [
@ -45,6 +45,13 @@
"pictureUrl": "https://github.com/nanddeepn.png",
"name": "Nanddeep Nachan",
"twitter": "NanddeepNachan"
},
{
"gitHubAccount": "petkir",
"company": "Cubido Business Solutions GmbH",
"pictureUrl": "https://github.com/petkir.png",
"name": "Peter Paul Kirschner",
"twitter": "petkir_at"
}
],
"references": [

View File

@ -2,7 +2,7 @@
"solution": {
"name": "SPFx Modern Calendar",
"id": "3d593a2f-73f1-486f-9dae-555c6f6b584d",
"version": "1.0.2.0",
"version": "1.0.3.0",
"includeClientSideAssets": true,
"developer": {
"name": "",

View File

@ -1,6 +1,6 @@
{
"name": "modern-calendar",
"version": "1.0.2",
"version": "1.0.3",
"private": true,
"main": "lib/index.js",
"engines": {

View File

@ -390,10 +390,12 @@ export default class ModernCalendarWebPart extends BaseClientSideWebPart<IModern
private _renderList(items: any[]): void {
var calItems: EventObjectInput[] = items.map((list: any) => {
const start = list[this.properties.start];
const end = list[this.properties.end];
return {
title: list[this.properties.title],
start: list[this.properties.start],
end: list[this.properties.end],
start: moment.utc(start,'YYYY-MM-DD HH:mm:ss').toDate(),
end: moment.utc(end,'YYYY-MM-DD HH:mm:ss').toDate(),
id: list["Id"],
detail: list[this.properties.detail],
};
@ -405,9 +407,9 @@ export default class ModernCalendarWebPart extends BaseClientSideWebPart<IModern
events: calItems,
eventClick: (_event) => {
var eventDetail =
moment(_event["start"]).format("MM/DD/YYYY hh:mm") +
" - " +
moment(_event["end"]).format("MM/DD/YYYY hh:mm") +
moment.utc(_event["start"]).local().format('YYYY-MM-DD hh:mm A') +
" - " +
moment.utc(_event["end"]).local().format('YYYY-MM-DD hh:mm A') +
"<br>" +
_event["detail"];
swal2.default(_event.title, eventDetail, "info");

View File

@ -31,13 +31,15 @@
![Select list and other properties from property panel for use with the Accordion](./assets/AccordionSettings4.png)
![Completed properties.](./assets/AccordionSettings3.png)
## Compatibility
![SPFx 1.10.0](https://img.shields.io/badge/version-1.10.0-green.svg)
![Node.js LTS v10](https://img.shields.io/badge/Node.js-LTS%20v10-green.svg)
![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")
![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 Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "The solution requires access to a hosted SharePoint list")
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
## Applies to
@ -86,6 +88,11 @@ Please create the list as described above
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/labels/react-accordion-dynamic-section) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3Areact-accordion-dynamic-section) and see what the community is saying.
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-accordion-dynamic-section&authors=@mikezimm%20@jyasir%20@AriGunawan&title=react-accordion-dynamic-section%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-accordion-dynamic-section&authors=@mikezimm%20@jyasir%20@AriGunawan&title=react-accordion-dynamic-section%20-%20).

View File

@ -1,4 +1,4 @@
# react-at-a-glance
# At a glance
## Summary
@ -7,7 +7,7 @@ This sample shows a web part to show the first few sentences of an article in a
The idea is based of the *At a glance* section of a news in the BBC news app (beta).
### Highlights
- Usage of `SPHttpClient` or `PnP JS`
- Usage of `SPHttpClient` or `PnPjs`
- Conditional property enabling
- Usage of regex to get the sentences from article content
- Usage of Carousel for mobile view
@ -31,14 +31,16 @@ The idea is based of the *At a glance* section of a news in the BBC news app (be
![article-content-desktop](./assets/article-content-desktop.png)
## Compatibility
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
![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 Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
![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 Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "The solution requires access to a news page to work")
![Hosted Workbench Partially](https://img.shields.io/badge/Hosted%20Workbench-Partially-yellow.svg "The solution needs to run on a news page to work")
## Applies to
@ -62,17 +64,18 @@ react-at-a-glance | [Anoop Tatti](https://github.com/anoopt) ([https://linktr.ee
Version|Date|Comments
-------|----|--------
1.0|September 07, 2021|Initial release
1.1|October 20, 2021|Minor CSS changes
## Minimal Path to Awesome
- Clone this repository
- Ensure that you are at the solution folder
- in the command-line run:
- **npm install**
- **gulp serve**
- `npm install`
- `gulp serve`
- Open a news article
- Add `?loadSPFX=true&debugManifestsFile=https://localhost:4321/temp/manifests.js` to the URL
- Add the `At a glance` web part to the page
- Add the **At a glance** web part to the page
## Features

View File

@ -10,7 +10,7 @@
"The idea is based of the At a glance section of a news in the BBC news app."
],
"creationDateTime": "2021-09-07",
"updateDateTime": "2021-09-07",
"updateDateTime": "2021-10-20",
"products": [
"SharePoint",
"Office"

View File

@ -77,6 +77,10 @@ export default class NewsGlanceWebPart extends BaseClientSideWebPart<INewsGlance
let element: any;
if (this.width < 400) {
this.sentences = this.sentences.map(sentence => {
return sentence.length > 100 ? `${sentence.slice(0, 100)}...` : sentence;
});
element = React.createElement(
NewsGlanceSmall,
{

View File

@ -25,13 +25,12 @@
}
.leftItem {
max-width: 150px;
object-fit: contain;
align-self: flex-start;
max-width: 200px;
flex: 1;
}
.rightItem {
flex: 1 1 auto;
flex: 2;
}
.articleImage {

View File

@ -8,10 +8,8 @@
.carouselItem {
background-color: $ms-color-themeDark;
// display: flex;
min-height: 150px;
height: 98%;
// justify-content: center;
}
.carouselDotList {

View File

@ -140,6 +140,7 @@ Version|Date|Comments
1.0.11|May 10, 2021|Optimized page refresh using local storage
1.0.12|June 21, 2021|Fixes overlap with Year-view and the comment section by adding a vertical scrollbar.
1.0.13|October 2, 2021|Fix to make sure Today is always visible and highlighted.
1.0.14|October 16, 2021|Resolve unhandled exception that happens clicking on recurrent events
## Minimal Path to Awesome

View File

@ -9,7 +9,7 @@
"This Web Part allows you to manage events in a calendar. Uses a list of existing calendars on any website. The location and name of the list and the dates of the events to be displayed are defined in the properties of the web part."
],
"created": "2020-12-04",
"modified": "2021-10-02",
"modified": "2021-10-16",
"products": [
"SharePoint",
"Office"

View File

@ -369,6 +369,8 @@ export default class parseRecurrentEvent {
}
}
if (e.fRecurrence === "1" && e.MasterSeriesItemID !== "") {
e.EventDate = new Date(this.parseDate(e.EventDate, e.fAllDayEvent));
e.EndDate = new Date(this.parseDate(e.EndDate, e.fAllDayEvent));
const ni = this.cloneObj(e);
er.push(ni);
}

View File

@ -96,6 +96,10 @@ Version|Date|Comments
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3Areact-directory) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3Areact-directory) and see what the community is saying.
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-directory&authors=@joaojmendes%20@petkir%20@sudharsank%20@Abderahman88&title=react-directory%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-directory&authors=@joaojmendes%20@petkir%20@sudharsank%20@Abderahman88&title=react-directory%20-%20).

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,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.12.1",
"libraryName": "react-menu-accordion",
"libraryId": "f7134ff1-6e97-430c-9a73-5dc4902c75e3",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,89 @@
# Documents Links Accordion
## Summary
This web part allows user create a accordion with documents links grouped by any column of document library.
When the user clicks on the header it dynamically load documents.
![documentsLinksAccordion](./assets/documentsLinksAccordion.gif)
## Screenshots
![documentsLinksAccordion](./assets/documentsLinksAccordion1.png)
## Compatibility
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-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 Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg)
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## Web Part Properties
Property |Type|Required| comments
--------------------|----|--------|----------
Web part Title| Text| no|
Select Document Library| dropdown|yes
Select Field to Group By | dropdown|yes
## Solution
The web part Use PnPjs library, Fluent-Ui-react components
Solution|Author(s)
--------|---------
React Documents Links Accordion |[João Mendes](https://github.com/joaojmendes) ([@joaojmendes](https://twitter.com/joaojmendes))
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|October 10, 2021|Initial release
## Minimal Path to Awesome
- Clone this repository
- Move to sample folder
- in the command line run:
- `npm install`
- `gulp build`
- `gulp bundle --ship`
- `gulp package-solution --ship`
- Add to AppCatalog and deploy
## 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.**
## Help
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/labels/react-document-links-accordion) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3Areact-document-links-accordion) and see what the community is saying.
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-document-links-accordion&authors=@joaojmendes%20@Ravikadri&title=react-document-links-accordion%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-document-links-accordion&authors=@joaojmendes%20@Ravikadri&title=react-document-links-accordion%20-%20).
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=react-document-links-accordion&authors=@joaojmendes%20@Ravikadri&title=react-document-links-accordion%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-documents-links-accordion" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

View File

@ -0,0 +1,89 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-document-links-accordion",
"source": "pnp",
"title": "Documents Links Accordion",
"shortDescription": "This web part allows user create a accordion with documents links grouped by any column of document library.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-document-links-accordion",
"longDescription": [
"This web part allows user create a accordion with documents links grouped by any column of document library.",
"When the user clicks on the header it dynamically load documents."
],
"creationDateTime": "2021-10-10",
"updateDateTime": "2021-10-10",
"products": [
"SharePoint",
"Office"
],
"metadata": [
{
"key": "CLIENT-SIDE-DEV",
"value": "React"
},
{
"key": "SPFX-VERSION",
"value": "1.12.1"
},
{
"key": "SPFX-FULLPAGEAPP",
"value": "true"
},
{
"key": "SPFX-TEAMSTAB",
"value": "true"
},
{
"key": "SPFX-TEAMSPERSONALAPP",
"value": "true"
},
{
"key": "REACT-HOOKS",
"value": "true"
},
{
"key": "PNPCONTROLS",
"value": "PropertyFieldListPicker, PropertyFieldMessage, PropertyFieldSpinner, Placeholder, AccessibleAccordion"
}
],
"thumbnails": [
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-document-links-accordion/assets/documentsLinksAccordion.gif",
"alt": "Web part in action"
},
{
"type": "image",
"order": 101,
"url": "https://github.com/pnp/sp-dev-fx-webparts/blob/main/samples/react-document-links-accordion/assets/documentsLinksAccordion1.png",
"alt": "Screen shot of web part"
}
],
"authors": [
{
"gitHubAccount": "joaojmendes",
"company": "Storm Technology Ltd",
"pictureUrl": "https://github.com/joaojmendes.png",
"name": "Jo\u00E3o Mendes",
"twitter": "joaojmendes"
}
],
"references": [
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
},
{
"name": "Supporting section backgrounds",
"description": "Starting with SharePoint Framework v1.8, web parts can be made aware of any section backgrounds and use these colors to improve the appearance of a web part when hosted in a section with a different background.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/supporting-section-backgrounds"
},
{
"name": "Building Microsoft Teams Tabs using SharePoint Framework",
"description": "Starting with SharePoint Framework v1.8, you can build tabs for Microsoft Teams with the SharePoint Framework tooling and use SharePoint as a host for your solutions. As part of the SharePoint Framework v1.10 you can also publish your solution as Microsoft Teams personal app.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/integrate-with-teams-introduction"
}
]
}
]

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"documentslinksaccordion-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/DocumentsLinksAccordion/DocumentsLinksAccordionWebPart.js",
"manifest": "./src/webparts/DocumentsLinksAccordion/DocumentsLinksAccordionWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"DocumentsLinksAccordionWebPartStrings": "lib/webparts/DocumentsLinksAccordion/loc/{locale}.js",
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
}
}

View File

@ -1,4 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "./release/assets/"
}

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": "react-menu-accordion",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,21 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-documentslinks-accordion",
"id": "f7134ff1-6e97-430c-9a73-5dc4902c75e3",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/react-documentslinks-accordion.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

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

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,37 @@
{
"name": "react-documentslinks-accordion",
"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.12.1",
"@microsoft/sp-lodash-subset": "1.12.1",
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
"@microsoft/sp-property-pane": "1.12.1",
"@microsoft/sp-webpart-base": "1.12.1",
"@pnp/spfx-controls-react": "^3.3.0",
"@pnp/spfx-property-controls": "^3.2.0",
"@uifabric/file-type-icons": "^7.8.1",
"date-fns": "^2.25.0",
"office-ui-fabric-react": "7.156.0",
"react": "16.9.0",
"react-dom": "16.9.0"
},
"devDependencies": {
"@types/react": "16.9.36",
"@types/react-dom": "16.9.8",
"@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-tslint-rules": "1.12.1",
"@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.12.1",
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
"gulp": "~4.0.2",
"ajv": "~5.2.2",
"@types/webpack-env": "1.13.1"
}
}

View File

@ -0,0 +1,160 @@
import { SPComponentLoader } from "@microsoft/sp-loader";
import { sp } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/regional-settings/web";
import { IRegionalSettingsInfo } from "@pnp/sp/regional-settings";
// get all the web's regional settings
const DEFAULT_PERSONA_IMG_HASH: string = "7ad602295f8386b7615b582d87bcc294";
const DEFAULT_IMAGE_PLACEHOLDER_HASH: string = "4a48f26592f4e1498d7a478a4c48609c";
const MD5_MODULE_ID: string = "8494e7d7-6b99-47b2-a741-59873e42f16f";
const PROFILE_IMAGE_URL: string = "/_layouts/15/userphoto.aspx?size=M&accountname=";
/**
* Gets user photo
* @param userId
* @returns user photo
*/
export const getUserPhoto = async (userId): Promise<string> => {
const personaImgUrl = PROFILE_IMAGE_URL + userId;
console.log(personaImgUrl);
// tslint:disable-next-line: no-use-before-declare
const url: string = await getImageBase64(personaImgUrl);
// tslint:disable-next-line: no-use-before-declare
const newHash = await getMd5HashForUrl(url);
if (newHash !== DEFAULT_PERSONA_IMG_HASH && newHash !== DEFAULT_IMAGE_PLACEHOLDER_HASH) {
return "data:image/png;base64," + url;
} else {
return "undefined";
}
};
/**
* Get MD5Hash for the image url to verify whether user has default image or custom image
* @param url
*/
export const getMd5HashForUrl = async (url: string) => {
return new Promise(async (resolve, reject) => {
// tslint:disable-next-line: no-use-before-declare
const library: any = await loadSPComponentById(MD5_MODULE_ID);
try {
const md5Hash = library.Md5Hash;
if (md5Hash) {
const convertedHash = md5Hash(url);
resolve(convertedHash);
}
} catch (error) {
resolve(url);
}
});
};
/**
* Load SPFx component by id, SPComponentLoader is used to load the SPFx components
* @param componentId - componentId, guid of the component library
*/
export const loadSPComponentById = async (componentId: string) => {
return new Promise((resolve, reject) => {
SPComponentLoader.loadComponentById(componentId)
.then((component: any) => {
resolve(component);
})
.catch((error) => {});
});
};
/**
* Gets image base64
* @param pictureUrl
* @returns image base64
*/
export const getImageBase64 = async (pictureUrl: string): Promise<string> => {
console.log(pictureUrl);
return new Promise((resolve, reject) => {
let image = new Image();
image.addEventListener("load", () => {
let tempCanvas = document.createElement("canvas");
(tempCanvas.width = image.width),
(tempCanvas.height = image.height),
tempCanvas.getContext("2d").drawImage(image, 0, 0);
let base64Str;
try {
base64Str = tempCanvas.toDataURL("image/png");
} catch (e) {
return "";
}
base64Str = base64Str.replace(/^data:image\/png;base64,/, "");
resolve(base64Str);
});
image.src = pictureUrl;
});
};
export const zeroPad = (num, places) => {
var zero = places - num.toString().length + 1;
return Array(+(zero > 0 && zero)).join("0") + num;
};
export const getSiteRegionalSettings = async (): Promise<IRegionalSettingsInfo> => {
try {
const s = await sp.web.regionalSettings();
return s;
} catch (error) {
console.error(error);
Promise.reject(error);
}
};
export const convertTimeTo12H = (
_hour: number
): Promise<{ hourInTimeFormat: string; current12HTimeFormat: string }> => {
console.log("prm", _hour);
return new Promise((resolve, rejected) => {
let hourInTimeFormat: string = "";
let current12HTimeFormat: string = "AM";
if (_hour >= 0 && _hour <= 11) {
if (_hour === 0) {
hourInTimeFormat = "12";
} else {
hourInTimeFormat = _hour.toString();
}
current12HTimeFormat = "AM";
} else {
const _hour12h = _hour - 12;
hourInTimeFormat = _hour12h === 0 ? "12" : _hour12h.toString();
current12HTimeFormat = "PM";
}
resolve({ hourInTimeFormat, current12HTimeFormat });
});
};
export const convertTimeTo24h = (hour: number, current12HTimeFormat: string): Promise<string> => {
return new Promise((resolve, rejected) => {
let hourInTimeFormat: string = "";
if (current12HTimeFormat === "PM") {
if (hour >= 1 && hour <= 11) {
if (hour === 12) {
hourInTimeFormat = "12";
}
const _hour24h = hour + 12;
hourInTimeFormat = _hour24h.toString();
}
} else {
if (hour === 12) {
hourInTimeFormat = "00";
} else {
hourInTimeFormat = hour.toString();
}
}
resolve(hourInTimeFormat);
});
};
/* Check if string is valid date */
export const checkIfValidDate = (str:string):boolean => {
// Regular expression to check if string is valid date
const regexExp = /(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})/gi;
return regexExp.test(str);
};

View File

@ -0,0 +1,16 @@
import * as React from "react";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphClient, AadTokenProvider } from "@microsoft/sp-http";
import { IReadonlyTheme } from '@microsoft/sp-component-base';
import { IRegionalSettingsInfo} from "@pnp/sp/regional-settings";
export interface IAppContextProps {
currentUser:string;
msGraphClient:MSGraphClient;
locale:string;
themeVariant: IReadonlyTheme | undefined;
}
export const AppContext = React.createContext<IAppContextProps>(undefined);

View File

@ -0,0 +1,24 @@
{
"themePrimary": "#6264a7",
"themeLighterAlt": "#f7f7fb",
"themeLighter": "#e1e1f1",
"themeLight": "#c8c9e4",
"themeTertiary": "#989ac9",
"themeSecondary": "#7173b0",
"themeDarkAlt": "#585a95",
"themeDark": "#4a4c7e",
"themeDarker": "#37385d",
"neutralLighterAlt": "#0b0b0b",
"neutralLighter": "#151515",
"neutralLight": "#252525",
"neutralQuaternaryAlt": "#2f2f2f",
"neutralQuaternary": "#373737",
"neutralTertiaryAlt": "#595959",
"neutralTertiary": "#c8c8c8",
"neutralSecondary": "#d0d0d0",
"neutralPrimaryAlt": "#dadada",
"neutralPrimary": "#ffffff",
"neutralDark": "#f4f4f4",
"black": "#f8f8f8",
"white": "#000000"
}

View File

@ -0,0 +1,24 @@
{
"themePrimary": "#6264a7",
"themeLighterAlt": "#f7f7fb",
"themeLighter": "#e1e1f1",
"themeLight": "#c8c9e4",
"themeTertiary": "#989ac9",
"themeSecondary": "#7173b0",
"themeDarkAlt": "#585a95",
"themeDark": "#4a4c7e",
"themeDarker": "#37385d",
"neutralLighterAlt": "#2d2c2c",
"neutralLighter": "#2c2b2b",
"neutralLight": "#2a2929",
"neutralQuaternaryAlt": "#272626",
"neutralQuaternary": "#252525",
"neutralTertiaryAlt": "#242323",
"neutralTertiary": "#c8c8c8",
"neutralSecondary": "#d0d0d0",
"neutralPrimaryAlt": "#dadada",
"neutralPrimary": "#ffffff",
"neutralDark": "#f4f4f4",
"black": "#f8f8f8",
"white": "#2d2c2c"
}

View File

@ -0,0 +1,24 @@
{
"themePrimary": "#6264a7",
"themeLighterAlt": "#f7f7fb",
"themeLighter": "#e1e1f1",
"themeLight": "#c8c9e4",
"themeTertiary": "#989ac9",
"themeSecondary": "#7173b0",
"themeDarkAlt": "#585a95",
"themeDark": "#4a4c7e",
"themeDarker": "#37385d",
"neutralLighterAlt": "#ecebe9",
"neutralLighter": "#e8e7e6",
"neutralLight": "#dedddc",
"neutralQuaternaryAlt": "#cfcecd",
"neutralQuaternary": "#c6c5c4",
"neutralTertiaryAlt": "#bebdbc",
"neutralTertiary": "#b5b4b2",
"neutralSecondary": "#9d9c9a",
"neutralPrimaryAlt": "#868482",
"neutralPrimary": "#252423",
"neutralDark": "#565453",
"black": "#3e3d3b",
"white": "#f3f2f1"
}

View File

@ -0,0 +1,28 @@
import { IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker';
export const DayPickerStrings: IDatePickerStrings = {
months: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
goToToday: 'Go to today',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
prevYearAriaLabel: 'Go to previous year',
nextYearAriaLabel: 'Go to next year',
closeButtonAriaLabel: 'Close date picker',
monthPickerHeaderAriaLabel: '{0}, select to change the year',
yearPickerHeaderAriaLabel: '{0}, select to change the month',
};

View File

@ -0,0 +1,16 @@
$default-background: #f3f2f1;
$default-color: #252423;
$default-button-background: #6264a7;
$default-Button-color: #f3f2f1;
// dark theme
$dark-background: #2d2c2c;
$dark-color: #ffffff;
$dark-button-background: #6264a7;
$dark-button-color: #2d2c2c;
// contrast theme
$contrast-background: #000000;
$contrast-color: #ffffff;
$contrast-button-background: #b5c01c;
$contrast-Button-color: #000000;

View File

@ -0,0 +1,74 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.DocumentsLinksAccordion {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,356 @@
import * as React from "react";
import * as strings from "DocumentsLinksAccordionWebPartStrings";
import { filter, findIndex, uniqBy } from "lodash";
import {
Customizer,
INavLink,
INavLinkGroup,
Label,
Link,
mergeStyleSets,
MessageBar,
MessageBarType,
Spinner,
SpinnerSize,
Stack,
FontIcon,
Text,
DetailsList,
DetailsListLayoutMode,
IColumn,
SelectionMode,
IDetailsListProps,
IStackStyles,
IStyle,
IDetailsListStyles,
} from "office-ui-fabric-react";
import { getFileTypeIconProps } from "@uifabric/file-type-icons";
import { useList } from "../../hooks/useList";
import { IDocumentsLinksAccordionState } from "./IDocumentsLinksAccordionState";
import { IDocumentsLinksAccordionProps } from "./IDocumentsLinksAccordionProps";
import {
Accordion,
AccordionItem,
AccordionItemHeading,
AccordionItemButton,
AccordionItemPanel,
} from "@pnp/spfx-controls-react/lib/AccessibleAccordion";
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
const { getGroupItems, getGroupHeaders, getField } = useList();
export const DocumentsLinksAccordion: React.FunctionComponent<IDocumentsLinksAccordionProps> = (
props: IDocumentsLinksAccordionProps
) => {
const [state, setState] = React.useState<IDocumentsLinksAccordionState>({
navLinkGroups: [],
isLoading: false,
hasError: false,
errorMessage: "",
listName: "",
});
const stackItemStyles: Partial<IStackStyles> = React.useMemo(() => {
return {
root: {
padding: 7,
} as IStyle,
};
}, []);
const listViewStyles: Partial<IDetailsListStyles> = React.useMemo(() => {
return {
focusZone: {
width: "auto",
maxHeight: 450,
overflowY: "auto",
overflowX: "hidden",
"&::-webkit-scrollbar-thumb": {
backgroundColor: props.themeVariant?.palette?.neutralLighter,
},
"&::-webkit-scrollbar": {
width: "7.5px",
},
"scrollbar-color": props.themeVariant?.palette?.neutralLighter,
"scrollbar-width": "thin",
},
root: {
"&::-webkit-scrollbar-thumb": {
backgroundColor: props.themeVariant?.palette?.neutralLighter,
},
"&::-webkit-scrollbar": {
height: "7.5px",
},
"scrollbar-color": props.themeVariant?.palette?.neutralLighter,
"scrollbar-width": "thin",
},
};
}, []);
const classComponent = React.useMemo(() => {
return mergeStyleSets({
webPartTitle: {
fontWeight: 600,
overflowX: "hidden",
textOverflow: "Ellipsis",
fontSize: props.themeVariant.fonts.large.fontSize,
marginBottom: 10,
},
});
}, [props.themeVariant]);
const stateRef = React.useRef(state); // Use to access state on eventListenners
const columns: IColumn[] = React.useMemo(() => {
return [
{
key: "documentRenderLink",
name: "Document",
fieldName: "name",
minWidth: 400,
maxWidth: 800,
isResizable: false,
data: "string",
},
];
}, []);
const onRenderRow: IDetailsListProps["onRenderRow"] = (propsItem) => {
if (propsItem.item) {
const item = propsItem.item as INavLink;
return (
<Stack horizontal horizontalAlign="start" tokens={{ childrenGap: 10 }} styles={stackItemStyles}>
<FontIcon iconName={item.iconProps.iconName} />
<Link href={item.url} target="_blank">
<Text variant={"medium"}>{item.name}</Text>
</Link>
</Stack>
);
}
return null;
};
React.useEffect(() => {
(async () => {
if (!props.listId || !props.fieldName) {
return;
}
try {
let _navLinksGroups: INavLinkGroup[] = [];
stateRef.current = {
...stateRef.current,
isLoading: true,
navLinkGroups: _navLinksGroups,
};
setState(stateRef.current);
const _groupHeaders = await getGroupHeaders(props.listId, props.fieldName, props.listBaseTemplate);
const { fieldName } = props;
const _field: any = await getField(props.listId, props.fieldName);
for (const groupHeader of _groupHeaders) {
let _name: any;
switch (_field.fieldType) {
case "TaxonomyFieldType":
_name = groupHeader[fieldName]?.Label ?? "Unassigned";
break;
case "TaxonomyFieldTypeMulti":
_name = groupHeader[fieldName][0]?.Label ?? "Unassigned";
break;
case "User":
if (_name != "Unassigned") {
_name = groupHeader[fieldName][0]?.title;
}
break;
case "Lookup":
_name =
groupHeader[props.fieldName] !== "" &&
groupHeader[props.fieldName] !== undefined &&
groupHeader[props.fieldName][0].lookupValue !== ""
? groupHeader[props.fieldName][0]?.lookupValue
: "Unassigned";
break;
default:
_name =
groupHeader[props.fieldName] !== "" && groupHeader[props.fieldName] !== undefined
? groupHeader[props.fieldName]
: "Unassigned";
break;
}
_navLinksGroups.push({
name: _name,
groupData: _name,
collapseByDefault: true,
links: [],
});
// Ensure the groups name are unique!
_navLinksGroups = uniqBy(_navLinksGroups, "name");
}
stateRef.current = {
...stateRef.current,
hasError: false,
errorMessage: "",
isLoading: false,
listName: _field.fieldScope,
navLinkGroups: _navLinksGroups,
};
setState(stateRef.current);
} catch (error) {
stateRef.current = {
...stateRef.current,
hasError: true,
errorMessage: error.message,
};
setState(stateRef.current);
}
})();
}, [props.listId, props.fieldName]);
// On Header click get Items for the header
const onGroupHeaderClick = React.useCallback(
async (ev: React.MouseEvent<HTMLElement, MouseEvent>) => {
try {
const _groupName = ev.currentTarget.innerText;
const { navLinkGroups } = stateRef.current;
setState(stateRef.current);
const _navGroup = filter(navLinkGroups, { name: _groupName });
if (_navGroup?.length && _navGroup[0]?.links?.length === 0) {
const _navlinks: INavLink[] = [];
const _groupHeaderItems: any[] = await getGroupItems(
props.listId,
props.fieldName,
_groupName,
props.listBaseTemplate
);
if (_groupHeaderItems?.length) {
for (const _groupHeaderItem of _groupHeaderItems) {
if (props.listBaseTemplate === 0) {
// List
_navlinks.push({
name: _groupHeaderItem.Title,
url: `${_groupHeaderItem.FileDirRef}/dispform.aspx?ID=${_groupHeaderItem.Id}`,
iconProps: {
iconName: "TaskManager",
},
key: _groupHeaderItem.Id,
target: "_blank",
isExpanded: false,
});
}
if (props.listBaseTemplate === 1) {
// Document Library
_navlinks.push({
name: _groupHeaderItem.FileLeafRef,
url: _groupHeaderItem.FileRef,
iconProps: {
...getFileTypeIconProps({
extension: _groupHeaderItem.DocIcon,
size: 16,
imageFileType: "svg",
}),
},
key: _groupHeaderItem.title,
target: "_blank",
isExpanded: false,
});
}
}
}
// Update Navigation with Items of Group
_navGroup[0].links = _navlinks;
}
const _index = findIndex(navLinkGroups, { name: _groupName });
_navGroup[0].collapseByDefault = true;
navLinkGroups[_index] = _navGroup[0];
stateRef.current = { ...stateRef.current, navLinkGroups: navLinkGroups };
setState(stateRef.current);
} catch (error) {}
},
[props]
);
// Show Error if Exists
if (state.hasError) {
return (
<>
<MessageBar messageBarType={MessageBarType.error} isMultiline>
{state.errorMessage}
</MessageBar>
</>
);
}
// render component
if (!props.listId || !props.fieldName) {
return (
<Placeholder
iconName="Edit"
iconText={strings.PlaceHolderIconText}
description={strings.PlaceHolderDescription}
buttonLabel={strings.PlaceHolderButtonLable}
onConfigure={props.onConfigure}
/>
);
}
return (
<>
<Customizer settings={{ theme: props.themeVariant }}>
{state.isLoading ? (
<Stack horizontal horizontalAlign="center">
<Spinner size={SpinnerSize.medium}></Spinner>
</Stack>
) : (
<>
<Stack horizontalAlign="space-between" horizontal tokens={{ childrenGap: 10 }} style={{ width: "100%" }}>
<div className={classComponent.webPartTitle}>{props.title}</div>
<Link href={state.listName}>{strings.ViewAllLabel}</Link>
</Stack>
{state.navLinkGroups?.length === 0 ? (
<Label
style={{
fontWeight: 400,
fontSize: props.themeVariant.fonts.small.fontSize,
}}
>
{strings.NodocumentsLabel}
</Label>
) : (
<Accordion allowZeroExpanded allowMultipleExpanded>
{state.navLinkGroups.map((item, i) => {
return (
<AccordionItem>
<AccordionItemHeading
onClick={async (ev) => {
await onGroupHeaderClick(ev);
}}
>
<AccordionItemButton>{item.name}</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel>
<Stack>
<DetailsList
items={item.links}
columns={columns}
styles={listViewStyles}
compact={true}
selectionMode={SelectionMode.none}
setKey="none"
layoutMode={DetailsListLayoutMode.justified}
isHeaderVisible={false}
onRenderRow={onRenderRow}
/>
</Stack>
</AccordionItemPanel>
</AccordionItem>
);
})}
</Accordion>
)}
</>
)}
</Customizer>
</>
);
};

View File

@ -0,0 +1,14 @@
import { IReadonlyTheme } from "@microsoft/sp-component-base";
import { DisplayMode } from "@microsoft/sp-core-library";
export interface IDocumentsLinksAccordionProps {
title: string;
listId:string;
listBaseTemplate:number;
fieldName:string;
locale:string;
themeVariant: IReadonlyTheme | undefined;
onConfigure: () => void;
}

View File

@ -0,0 +1,9 @@
import { Nav, INavStyles, INavLinkGroup } from 'office-ui-fabric-react/lib/Nav';
export interface IDocumentsLinksAccordionState {
navLinkGroups: INavLinkGroup[];
isLoading: boolean;
hasError:boolean;
errorMessage:string;
listName:string;
}

View File

@ -0,0 +1,6 @@
export interface IListItemsMenu {
groupBy:string;
id:string;
title:string;
url:string;
}

View File

@ -0,0 +1,137 @@
import "@pnp/sp/fields";
import "@pnp/sp/items";
import "@pnp/sp/lists";
import "@pnp/sp/webs";
import { sortBy, uniqBy } from "lodash";
import { sp } from "@pnp/sp";
import { IFieldInfo } from "@pnp/sp/fields";
import { IListInfo } from "@pnp/sp/lists";
import * as moment from "moment";
import { format, parse, parseISO } from "date-fns";
import { checkIfValidDate } from "../Utils/Utils";
export const useList = () => {
// Run on useList hook
(async () => {})();
// Get List Columns
const getListColumns = async (listId: string): Promise<IFieldInfo[]> => {
const _listColumnsResults: IFieldInfo[] = await sp.web.lists.getById(listId).fields.filter("Hidden eq false").get();
const _wColumns: IFieldInfo[] = uniqBy(sortBy(_listColumnsResults, "Title"), "Title");
return _wColumns;
};
const getField = async (listId: string, field: string): Promise<any> => {
const _field: IFieldInfo = await sp.web.lists.getById(listId).fields.getByInternalNameOrTitle(field).get();
const fieldType = _field.TypeAsString;
const fieldScope = _field.Scope;
return { fieldType, fieldScope };
};
const getGroupHeaders = async (listId: string, groupByField: string, baseTemplate: number): Promise<any[]> => {
let _viewXml = `<View Scope='Recursive'>
<Query>
<GroupBy Collapse="TRUE">
<FieldRef Name="${groupByField}"/>
</GroupBy>
</Query>
<RowLimit>1000</RowLimit>
</View>`;
const _groupHeadersResults = await sp.web.lists.getById(listId).renderListDataAsStream({ ViewXml: _viewXml });
return uniqBy(_groupHeadersResults.Row, groupByField);
};
const getGroupItems = async (
listId: string,
groupByField: string,
groupFieldValue: string,
baseTemplate: number
): Promise<any[]> => {
const _field: any = await getField(listId, groupByField);
if (checkIfValidDate(groupFieldValue)) {
groupFieldValue = format(new Date(groupFieldValue), "yyyy-MM-dd");
}
switch (_field.fieldType) {
case "DateTime":
groupFieldValue =
groupFieldValue != "Unassigned" ? format(parseISO(groupFieldValue), "yyyy-MM-dd") : "Unassigned";
break;
case "AllDayEvent":
groupFieldValue = groupFieldValue === "No" ? "0" : "1";
break;
default:
break;
}
let _viewXml = `<View Scope='Recursive'>
<Query>
<OrderBy>
<FieldRef Name="${groupByField}" Ascending="FALSE"></FieldRef>
</OrderBy>
</Query>
</View>`;
if (groupFieldValue != "Unassigned") {
_viewXml = `<View Scope='Recursive'>
<Query>
<OrderBy>
<FieldRef Name="${groupByField}" Ascending="FALSE"></FieldRef>
</OrderBy>
<Where>
<Eq>
<FieldRef Name="${groupByField}"></FieldRef>
<Value Type="${_field.fieldType}" IncludeTimeValue="FALSE">${groupFieldValue}</Value>
</Eq>
</Where>
</Query>
</View>`;
}
if (groupFieldValue === "Unassigned") {
_viewXml = `<View Scope='Recursive'>
<Query>
<OrderBy>
<FieldRef Name="${groupByField}" Ascending="FALSE"></FieldRef>
</OrderBy>
<Where>
<IsNull>
<FieldRef Name="${groupByField}"></FieldRef>
</IsNull>
</Where>
</Query>
</View>`;
}
const _groupItemsResults = await sp.web.lists.getById(listId).renderListDataAsStream({ ViewXml: _viewXml });
return _groupItemsResults.Row;
};
// Get Lists
const getLists = async (baseTemplate: number): Promise<IListInfo[]> => {
let _filter: string = "Hidden eq false and ";
if (baseTemplate === 0) {
_filter = _filter + " BaseType ne 1";
} else {
_filter = _filter + " BaseType eq 1";
}
const _lists: IListInfo[] = await sp.web.lists.filter(_filter).get();
return _lists;
};
// Return functions
return {
getListColumns,
getLists,
getGroupItems,
getGroupHeaders,
getField,
};
};

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,31 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "a0215264-a8cd-4952-aecd-4e80719de202",
"alias": "DocumentsLinksAccordionWebPart",
"componentType": "WebPart",
"supportsFullBleed": true,
// 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": ["SharePointWebPart", "SharePointFullPage", "TeamsPersonalApp","TeamsTab"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "SPFx Custom Web Parts" },
"title": { "default": "Documents Links Accordion" },
"description": { "default": "Documents" },
"officeFabricIconFontName": "GroupedList",
"properties": {
"title": "Documents",
"listId": "",
"fieldName": "",
"listBasetemplate": 1
}
}]
}

View File

@ -0,0 +1,302 @@
import * as React from "react";
import * as ReactDom from "react-dom";
import {
loadTheme,
MessageBarType,
SpinnerSize
} from "office-ui-fabric-react";
import {
IReadonlyTheme,
ThemeChangedEventArgs,
ThemeProvider
} from "@microsoft/sp-component-base";
import { MSGraphClient } from "@microsoft/sp-http";
import {
IPropertyPaneConfiguration,
IPropertyPaneDropdownOption,
IPropertyPaneField,
IPropertyPaneGroup,
IPropertyPanePage,
PropertyPaneDropdown,
PropertyPaneTextField
} from "@microsoft/sp-property-pane";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { sp } from "@pnp/sp";
import {
PropertyFieldListPicker,
PropertyFieldListPickerOrderBy
} from "@pnp/spfx-property-controls/lib/PropertyFieldListPicker";
import {
PropertyFieldMessage
} from "@pnp/spfx-property-controls/lib/PropertyFieldMessage";
import {
PropertyFieldSpinner
} from "@pnp/spfx-property-controls/lib/PropertyFieldSpinner";
import { useList } from "../../hooks/useList";
export interface IListItemsMenuWebPartProps {
title: string;
listId: string;
fieldName: string;
listBasetemplate: number;
}
const teamsDefaultTheme = require("../../common/TeamsDefaultTheme.json");
const teamsDarkTheme = require("../../common/TeamsDarkTheme.json");
const teamsContrastTheme = require("../../common/TeamsContrastTheme.json");
const { getListColumns, getLists } = useList() ;
import * as strings from 'DocumentsLinksAccordionWebPartStrings';
import { IDocumentsLinksAccordionProps } from "../../components/DocumentsLinksAccordion/IDocumentsLinksAccordionProps";
import {DocumentsLinksAccordion} from "../../components/DocumentsLinksAccordion/DocumentsLinksAccordion";
import { Version } from "@microsoft/sp-core-library";
export interface IDocumentsLinksAccordionWebPartProps {
title: string;
listId: string;
fieldName: string;
listBasetemplate: number;
}
export default class DocumentsLinksAccordionWebPart extends BaseClientSideWebPart<IDocumentsLinksAccordionWebPartProps> {
private columns: IPropertyPaneDropdownOption[] = [];
private lists: IPropertyPaneDropdownOption[] = [];
private _themeProvider: ThemeProvider;
private _themeVariant: IReadonlyTheme | undefined;
private _msgGraphclient: MSGraphClient;
private _hasError: boolean = false;
private _messageError: string = undefined;
protected async onInit(): Promise<void> {
sp.setup({
spfxContext: this.context,
});
this._msgGraphclient = await this.context.msGraphClientFactory.getClient();
this._themeProvider = this.context.serviceScope.consume(
ThemeProvider.serviceKey
);
// If it exists, get the theme variant
this._themeVariant = this._themeProvider.tryGetTheme();
// Register a handler to be notified if the theme variant changes
this._themeProvider.themeChangedEvent.add(
this,
this._handleThemeChangedEvent
);
if (this.context.sdks.microsoftTeams) {
// in teams ?
const context = this.context.sdks.microsoftTeams!.context;
this._applyTheme(context.theme || "default");
this.context.sdks.microsoftTeams.teamsJs.registerOnThemeChangeHandler(
this._applyTheme
);
}
return Promise.resolve();
}
/**
* Update the current theme variant reference and re-render.
*
* @param args The new theme
*/
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme;
this.render();
}
// Apply theme id in Teams
private _applyTheme = (theme: string): void => {
this.context.domElement.setAttribute("data-theme", theme);
document.body.setAttribute("data-theme", theme);
if (theme == "dark") {
loadTheme({
palette: teamsDarkTheme,
});
}
if (theme == "default") {
loadTheme({
palette: teamsDefaultTheme,
});
}
if (theme == "contrast") {
loadTheme({
palette: teamsContrastTheme,
});
}
}
public render(): void {
const element: React.ReactElement<IDocumentsLinksAccordionProps> = React.createElement(
DocumentsLinksAccordion,
{
title: this.properties.title,
listId: this.properties.listId,
fieldName: this.properties.fieldName,
themeVariant: this._themeVariant,
locale: this.context.pageContext.cultureInfo.currentUICultureName,
listBaseTemplate: this.properties.listBasetemplate ?? 1,
onConfigure: () =>{ this.context.propertyPane.open(); },
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get disableReactivePropertyChanges() {
return true;
}
protected async onPropertyPaneConfigurationStart() {
if (
this.properties.fieldName &&
this.properties.listId &&
this.columns.length === 0
) {
await this.addListColumns(this.properties.listId);
this.context.propertyPane.refresh();
}
if ( this.properties.listId && this.lists.length === 0){
await this.addLists('1');
this.context.propertyPane.refresh();
}
}
protected async onPropertyPaneFieldChanged(
propertyPath: string,
oldValue: any,
newValue: any
) {
if (propertyPath === "listId" && newValue != oldValue) {
this.columns = [];
this.context.propertyPane.refresh();
await this.addListColumns(newValue);
this.context.propertyPane.refresh();
}
}
private async addLists(newValue: any) {
try {
this.lists = [];
const _lists = await getLists(newValue);
for (const _list of _lists) {
this.lists.push({ key: _list.Id, text: _list.Title });
}
} catch (error) {
this._hasError = true;
this._messageError =error.message;
}
}
private async addListColumns(newValue: any) {
try {
this.columns = [];
const _listColumns = await getListColumns(newValue);
for (const _column of _listColumns) {
this.columns.push({ key: _column.InternalName, text: _column.Title });
}
} catch (error) {
this._hasError = true;
this._messageError = error.message;
}
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
const _pages: IPropertyPanePage[] = [
{
header: {
description: strings.PropertyPaneDescription,
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("title", {
label: strings.DescriptionFieldLabel,
}),
],
},
],
},
];
let groups: IPropertyPaneGroup = _pages[0].groups[0] as IPropertyPaneGroup;
let groupFields: IPropertyPaneField<any>[] = groups.groupFields;
groupFields.push(
PropertyFieldListPicker("listId", {
label: "Select Document Library",
selectedList: this.properties.listId ,
includeHidden: false,
baseTemplate: 101,
orderBy: PropertyFieldListPickerOrderBy.Title,
disabled: false,
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
properties: this.properties,
context: this.context,
onGetErrorMessage: null,
deferredValidationTime: 0,
key: "listPickerFieldId",
})
);
if (this.properties.listId) {
groupFields.push(
PropertyFieldSpinner("", {
key: "sp1",
size: SpinnerSize.medium,
isVisible: (this.columns.length || this._hasError) ? false : true,
label: "Loading ...",
})
);
}
// Show Columns
if (this.columns.length > 0) {
groupFields.push(
PropertyPaneDropdown("fieldName", {
label: "Select field to group by documents",
options: this.columns,
selectedKey: this.properties.fieldName,
})
);
}
// Show Error
if (this._hasError) {
groupFields.push(
PropertyFieldMessage("", {
key: "msgError",
messageType: MessageBarType.error,
multiline: true,
text: this._messageError,
isVisible: this._hasError,
})
);
}
const _panelConfiguration: IPropertyPaneConfiguration = { pages: _pages };
return _panelConfiguration;
}
}

View File

@ -0,0 +1,12 @@
define([], function() {
return {
PlaceHolderButtonLable: "Configure",
PlaceHolderDescription: "Please configure the web part.",
PlaceHolderIconText: "Configure Documents Links Accordion Web Part",
"PropertyPaneDescription": "Show documents grouped by field",
"BasicGroupName": "Properties",
"DescriptionFieldLabel": "Title",
"NodocumentsLabel":"No documents found in library",
"ViewAllLabel": "View All"
}
});

View File

@ -0,0 +1,15 @@
declare interface IDocumentsLinksAccordionWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
ViewAllLabel:string;
NodocumentsLabel:string;
PlaceHolderButtonLable: string;
PlaceHolderDescription: string;
PlaceHolderIconText: string;
}
declare module 'DocumentsLinksAccordionWebPartStrings' {
const strings: IDocumentsLinksAccordionWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

@ -0,0 +1,35 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/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,30 @@
{
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}

View File

@ -10,7 +10,7 @@ I got the idea from this great article [Use Power Automate to Notify of Upcoming
## Compatibility
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
![Node.js LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v12%20%7C%20LTS%20v10-green.svg)
![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 Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
@ -32,6 +32,7 @@ react-graph-app-secret-expiration | [Aimery Thomas](https://github.com/a1mery) (
Version|Date|Comments
-------|----|--------
1.0|September 17, 2021|Initial release
1.1|October 10, 2021|Add pagination
## Minimal Path to Awesome
@ -39,14 +40,14 @@ Version|Date|Comments
- Clone this repository
- Ensure that you are at the solution folder
- In the command-line run:
- **npm install**
- **gulp bundle**
- **gulp package-solution**
- `npm install`
- `gulp bundle`
- `gulp package-solution`
- Deploy the package to your app catalog
- Approve the API permission request from the SharePoint admin
- Add the web part to a page
- In the command-line run:
- **gulp serve --nobrowser**
- `gulp serve --nobrowser`
## Features

View File

@ -9,7 +9,7 @@
"This sample web part shows the list of your applications registered in Azure AD along with their associated client secret/certificate expiration date."
],
"creationDateTime": "2021-09-17",
"updateDateTime": "2021-09-17",
"updateDateTime": "2021-10-10",
"products": [
"SharePoint",
"Office"
@ -22,6 +22,10 @@
{
"key": "SPFX-VERSION",
"value": "1.12.1"
},
{
"key": "PNPCONTROLS",
"value": "Pagination"
}
],
"thumbnails": [

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-graph-app-secret-expiration-client-side-solution",
"id": "b25d85a4-7310-408a-a263-25959b0a5b1b",
"version": "1.0.0.0",
"version": "1.1.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"skipFeatureDeployment": true,

View File

@ -1,6 +1,6 @@
{
"name": "react-graph-app-secret-expiration",
"version": "0.0.1",
"version": "1.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "react-graph-app-secret-expiration",
"version": "0.0.1",
"version": "1.1.0",
"private": true,
"main": "lib/index.js",
"scripts": {

View File

@ -5,7 +5,9 @@ import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp
import { IApplications, IApplication, IFormattedApplication } from '../../../models/IApplication';
import { IGraphAppSecretExpirationState } from './GraphAppSecretExpirationState';
import * as moment from 'moment';
import { DefaultButton, Spinner, mergeStyles } from '@fluentui/react';
import { DefaultButton, Spinner, mergeStyles, SearchBox } from '@fluentui/react';
import { Pagination } from "@pnp/spfx-controls-react/lib/pagination";
import * as strings from 'GraphAppSecretExpirationWebPartStrings';
const stackItemHidden = mergeStyles({
display: 'none',
@ -57,9 +59,13 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
super(props);
this.state = {
applications: [],
filteredApplications: [],
filterValue: "",
searchFilter: "",
error: undefined,
loading: true,
groupByFields: []
groupByFields: [],
page: 1
};
}
@ -113,42 +119,80 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
var today = (moment(Date.now())).format('DD-MMM-YYYY');
try {
applications.forEach(app => {
if (app.passwordCredentials.length > 0) {
app.passwordCredentials.forEach(pswd => {
let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays();
let formatedApp: IFormattedApplication = {
applicationId: app.appId,
displayName: app.displayName,
type: "Secret",
expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'),
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
};
displayedApplication.push(formatedApp);
});
}
if (app.keyCredentials.length > 0) {
app.keyCredentials.forEach(keyCred => {
let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays();
let formatedApp: IFormattedApplication = {
applicationId: app.appId,
displayName: app.displayName,
type: "Certificate",
expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'),
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
};
displayedApplication.push(formatedApp);
});
}
app.passwordCredentials.forEach(pswd => {
let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays();
let formatedApp: IFormattedApplication = {
applicationId: app.appId,
displayName: app.displayName,
type: "Secret",
expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'),
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
};
displayedApplication.push(formatedApp);
});
app.keyCredentials.forEach(keyCred => {
let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays();
let formatedApp: IFormattedApplication = {
applicationId: app.appId,
displayName: app.displayName,
type: "Certificate",
expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'),
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
};
displayedApplication.push(formatedApp);
});
});
this.setState({
applications: displayedApplication
applications: displayedApplication,
filteredApplications: displayedApplication
});
} catch (error) {
console.log(error);
}
}
private _getPage(selectedPage: number) {
this.setState({
page: selectedPage
});
}
private _filterApplication = (value, clear: boolean) => {
let searchResult: IFormattedApplication[] = [];
if (clear) {
this.state.applications.forEach(app => {
if (this._filterByProperties(app, value)) {
searchResult.push(app);
}
});
this.setState({
filteredApplications: searchResult,
filterValue: value
});
} else {
this.setState({
filteredApplications: this.state.applications,
filterValue: value,
page: 1
});
}
}
private _filterByProperties(application: IFormattedApplication, filterValue) {
if (application.applicationId.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
return true;
} else if (application.displayName.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
return true;
} else if (application.expirationDate.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
return true;
} else if (application.type.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
return true;
} else {
return false;
}
}
private _groupView = () => {
if (this.state.groupByFields.length === 0) {
let groupByFields: IGrouping[] = [
@ -177,21 +221,31 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
<p className={styles.title}>Application list :</p>
<div className={this.state.loading ? "" : stackItemHidden}>
<Spinner label="Loading..." ariaLive="assertive" labelPosition="left" />
<br/>
<br />
</div>
<div className={this.state.loading ? stackItemHidden : ""}>
<DefaultButton text={this.state.groupByFields.length === 0 ? "Group by App ID" : "Ungroup"} onClick={this._groupView} />
<SearchBox placeholder="Search" onChange={(e, text) => this._filterApplication(text, false)} onClear={() => this._filterApplication("", true)} value={this.state.filterValue} />
<ListView
items={this.state.applications}
items={this.state.filteredApplications.slice(this.state.page === 1 || this.state.filterValue !== "" ? 0 : this.state.page * 10 - 10, this.state.page * 10)}
viewFields={_viewFields}
iconFieldName="ServerRelativeUrl"
compact={true}
selectionMode={SelectionMode.none}
selection={this._getSelection}
groupByFields={this.state.groupByFields}
showFilter={true}
showFilter={false}
filterPlaceHolder="Search..." />
</div>
<Pagination
currentPage={1}
totalPages={Math.floor(this.state.filteredApplications.length / 10) + 1}
onChange={(page) => this._getPage(page)}
limiter={3} // Optional - default value 3
hideFirstPageJump // Optional
hideLastPageJump // Optional
limiterIcon={"Emoji12"} // Optional
/>
</div>
</div>
);

View File

@ -3,7 +3,11 @@ import { IApplications, IApplication,IPasswordCredential,IKeyCredential,IFormatt
export interface IGraphAppSecretExpirationState {
applications: IFormattedApplication[];
filteredApplications: IFormattedApplication[];
filterValue: string;
searchFilter: string;
groupByFields: IGrouping[];
page: number;
error: string;
loading: boolean;
}

View File

@ -10,6 +10,7 @@ node_modules
dist
lib
solution
release
temp
*.sppkg

View File

@ -26,11 +26,13 @@ Web part pulls all Microsoft 365 Groups and Teams that the logged in user has ac
## Compatibility
![SPFx 1.10](https://img.shields.io/badge/SPFx-1.10.0-green.svg)
![Node.js LTS 8.x | LTS 10.x](https://img.shields.io/badge/Node.js-LTS%208.x%20%7C%20LTS%210.x-green.svg)
![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 Hosted: Only after API permissions granted](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Only after API permissions granted")
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-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%202019-Not%20compatible-red.svg)
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%202016%20(Feature%20Pack%202)-Not%20compatible-red.svg)
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "This solution requires API permissions")
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-yellow.svg "Only after API permissions granted")
## Applies to
@ -41,7 +43,7 @@ Web part pulls all Microsoft 365 Groups and Teams that the logged in user has ac
Solution|Author(s)
--------|---------
React-Groups-Teams | [Alison Collins](https://github.com/ReactIntern) |
React-Groups-Teams | [Alison Collins](https://github.com/ReactIntern) ([Blog](https://graphgod.dev), [LinkedIn](https://www.linkedin.com/in/alison-collins-53192b219/)) |
## Version history
@ -49,7 +51,7 @@ React-Groups-Teams | [Alison Collins](https://github.com/ReactIntern) |
| ------- | ---------------- | --------------- |
| 1.0.0 | April 16, 2021 | Initial release |
| 1.0.1 | August 1, 2021 | Fixed references to Office.com |
| 1.1.0 | October 8, 2021 | Upgraded to SPFx 1.12.1 for higher compatibility and added Teams Tab deployment support. |
# Prerequisites

View File

@ -9,7 +9,7 @@
"Web part pulls all Microsoft 365 Groups and Teams that the logged in user has access to view."
],
"creationDateTime": "2021-05-06",
"updateDateTime": "2021-08-01",
"updateDateTime": "2021-10-08",
"products": [
"SharePoint",
"Office"
@ -21,7 +21,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.10.0"
"value": "1.12.1"
}
],
"thumbnails": [

View File

@ -1,7 +1,16 @@
'use strict';
const gulp = require('gulp');
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.`);
build.initialize(gulp);
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

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,10 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.9.1",
"@microsoft/sp-lodash-subset": "1.9.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
"@microsoft/sp-webpart-base": "1.9.1",
"@microsoft/sp-core-library": "1.12.1",
"@microsoft/sp-lodash-subset": "1.12.1",
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
"@microsoft/sp-webpart-base": "1.12.1",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
@ -28,10 +28,10 @@
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.9.1",
"@microsoft/sp-tslint-rules": "1.9.1",
"@microsoft/sp-module-interfaces": "1.9.1",
"@microsoft/sp-webpart-workbench": "1.9.1",
"@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-tslint-rules": "1.12.1",
"@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.12.1",
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",

View File

@ -0,0 +1,66 @@
{
"id": "5ced32db-af85-469a-a3cb-39f3986e1f1a",
"alias": "MicrosoftGroupsWebPart",
"componentType": "WebPart",
"version": "1.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart",
"TeamsTab"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "Microsoft Groups"
},
"description": {
"default": "Microsoft Groups description"
},
"officeFabricIconFontName": "Page",
"properties": {
"description": "Microsoft Groups"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"https://localhost:4321/dist/"
],
"entryModuleId": "microsoft-groups-web-part",
"scriptResources": {
"microsoft-groups-web-part": {
"type": "path",
"path": "microsoft-groups-web-part_da003a7fb7fd1f14fcb7.js"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.1"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.1"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.8.5"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.8.5"
},
"MicrosoftGroupsWebPartStrings": {
"type": "path",
"path": "MicrosoftGroupsWebPartStrings_en-us_b41dd8b4c7f5f69692bd8f24e2b83745.js"
}
}
}
}

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,98 @@
{
"bundles": {
"microsoft-groups-web-part": {
"dependencies": [
{
"componentId": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"componentName": "@microsoft/sp-core-library",
"componentVersion": "1.12.1",
"isDirectDependency": true
},
{
"componentId": "01c4df03-e775-48cb-aa14-171ee5199a15",
"componentName": "tslib",
"componentVersion": "1.10.0",
"isDirectDependency": false
},
{
"componentId": "2e09fb9b-13bb-48f2-859f-97d6fff71176",
"componentName": "@ms/odsp-core-bundle",
"componentVersion": "1.1.13",
"isDirectDependency": false
},
{
"componentId": "73e1dc6c-8441-42cc-ad47-4bd3659f8a3a",
"componentName": "@microsoft/sp-lodash-subset",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "974a7777-0990-4136-8fa6-95d80114c2e0",
"componentName": "@microsoft/sp-webpart-base",
"componentVersion": "1.12.1",
"isDirectDependency": true
},
{
"componentId": "8217e442-8ed3-41fd-957d-b112e841286a",
"componentName": "@ms/sp-telemetry",
"componentVersion": "0.19.2",
"isDirectDependency": false
},
{
"componentId": "467dc675-7cc5-4709-8aac-78e3b71bd2f6",
"componentName": "@microsoft/sp-component-base",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "e40f8203-b39d-425a-a957-714852e33b79",
"componentName": "@microsoft/sp-dynamic-data",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "78359e4b-07c2-43c6-8d0b-d060b4d577e8",
"componentName": "@microsoft/sp-diagnostics",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "1c4541f7-5c31-41aa-9fa8-fbc9dc14c0a8",
"componentName": "@microsoft/sp-page-context",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "229b8d08-79f3-438b-8c21-4613fc877abd",
"componentName": "@microsoft/load-themed-styles",
"componentVersion": "0.1.2",
"isDirectDependency": false
},
{
"componentId": "c07208f0-ea3b-4c1a-9965-ac1b825211a6",
"componentName": "@microsoft/sp-http",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "1c6c9123-7aac-41f3-a376-3caea41ed83f",
"componentName": "@microsoft/sp-loader",
"componentVersion": "1.12.1",
"isDirectDependency": false
},
{
"componentId": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"componentName": "react",
"componentVersion": "16.8.5",
"isDirectDependency": true
},
{
"componentId": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"componentName": "react-dom",
"componentVersion": "16.8.5",
"isDirectDependency": true
}
]
}
}
}

View File

@ -0,0 +1,66 @@
{
"id": "5ced32db-af85-469a-a3cb-39f3986e1f1a",
"alias": "MicrosoftGroupsWebPart",
"componentType": "WebPart",
"version": "1.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart",
"TeamsTab"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "Microsoft Groups"
},
"description": {
"default": "Microsoft Groups description"
},
"officeFabricIconFontName": "Page",
"properties": {
"description": "Microsoft Groups"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"<!-- PATH TO CDN -->"
],
"entryModuleId": "microsoft-groups-web-part",
"scriptResources": {
"microsoft-groups-web-part": {
"type": "path",
"path": "microsoft-groups-web-part_da003a7fb7fd1f14fcb7.js"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.1"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.1"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.8.5"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.8.5"
},
"MicrosoftGroupsWebPartStrings": {
"type": "path",
"path": "MicrosoftGroupsWebPartStrings_en-us_b41dd8b4c7f5f69692bd8f24e2b83745.js"
}
}
}
}

View File

@ -0,0 +1,66 @@
{
"id": "5ced32db-af85-469a-a3cb-39f3986e1f1a",
"alias": "MicrosoftGroupsWebPart",
"componentType": "WebPart",
"version": "1.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart",
"TeamsTab"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "Microsoft Groups"
},
"description": {
"default": "Microsoft Groups description"
},
"officeFabricIconFontName": "Page",
"properties": {
"description": "Microsoft Groups"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"<!-- PATH TO CDN -->"
],
"entryModuleId": "microsoft-groups-web-part",
"scriptResources": {
"microsoft-groups-web-part": {
"type": "path",
"path": "microsoft-groups-web-part_da003a7fb7fd1f14fcb7.js"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.1"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.1"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.8.5"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.8.5"
},
"MicrosoftGroupsWebPartStrings": {
"type": "path",
"path": "MicrosoftGroupsWebPartStrings_en-us_b41dd8b4c7f5f69692bd8f24e2b83745.js"
}
}
}
}

View File

@ -12,7 +12,7 @@
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"supportedHosts": ["SharePointWebPart", "TeamsTab"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other

View File

@ -1,25 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -11,6 +11,7 @@ dist
lib
solution
temp
release
*.sppkg
# Coverage directory used by tools like istanbul

View File

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

View File

@ -4,23 +4,7 @@
* Chrome browser: https://aka.ms/spfx-debugger-extensions
*/
"version": "0.2.0",
"configurations": [{
"name": "Local workbench",
"type": "chrome",
"request": "launch",
"url": "https://localhost:4321/temp/workbench.html",
"webRoot": "${workspaceRoot}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///.././src/*": "${webRoot}/src/*",
"webpack:///../../../src/*": "${webRoot}/src/*",
"webpack:///../../../../src/*": "${webRoot}/src/*",
"webpack:///../../../../../src/*": "${webRoot}/src/*"
},
"runtimeArgs": [
"--remote-debugging-port=9222"
]
},
"configurations": [
{
"name": "Hosted workbench",
"type": "chrome",

View File

@ -2,7 +2,7 @@
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.10.0",
"version": "1.13.0",
"libraryName": "react-kanban-board",
"libraryId": "cccbd72b-7b89-4128-9348-0a4850ded8fd",
"packageManager": "npm",

View File

@ -24,9 +24,17 @@ The web part uses the default columns of the SharePoint Tasks list for showing t
![picture of the web part in action](assets/kanbanofficeUI.gif)
## Used SharePoint Framework Version
![1.10.0](https://img.shields.io/badge/version-1.10.0-green.svg)
## Compatibility
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-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)
## Applies to
@ -49,9 +57,10 @@ The Task list can be chosen using the web part properties (BaseTemplate 171 or 1
Solution|Author(s)
--------|---------
react-kanban-board | [Ram](https://twitter.com/ram_meenavalli)
react-kanban-board | Daniel Westerdale ([Westerdale Solutions Ltd.](https://westerdale.blog), [@westerdaled](https://twitter.com/westerdaled?s=20))
react-kanban-board | Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at))
react-kanban-board | [Ram Prasad Meenavalli](https://github.com/RamPrasadMeenavalli) ([@ram_meenavalli](https://twitter.com/ram_meenavalli))
react-kanban-board | [Daniel Westerdale](https://github.com/westerdaled) ([Westerdale Solutions Ltd.](https://westerdale.blog), [@westerdaled](https://twitter.com/westerdaled?s=20))
react-kanban-board | [Peter Paul Kirschner](https://github.com/petkir) ([@petkir_at](https://twitter.com/petkir_at))
react-kanban-board | [Alex Terentiev](https://github.com/AJIXuMuK) ([@alexaterentiev](https://twitter.com/alexaterentiev))
## Version history
@ -60,6 +69,7 @@ Version|Date|Comments
1.0.0.0|July 17, 2019|Initial release
1.0.1.0|April 21, 2020|Added support for Teams hosts
2.0.0.0|July 10, 2020| jqwidgets replaced with a custom Kanban Board based on Office UI Component and IE11 Support
3.0.0.0|October 29, 2021| SPFx 1.13, PnPJS v2, PnP Controls v3
[Read More about the implementation of this Board](./src/kanban/README.md)
@ -80,13 +90,6 @@ Version|Date|Comments
<!---* [Jean-Philippe CIVADE](https://github.com/ewidance) for Bug Report IE11 (initiator of rewrite of this sample)-->
<!---* [RamPrasadMeenavalli](https://github.com/RamPrasadMeenavalli) for the initial Idea-->
## 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
@ -102,4 +105,26 @@ This sample highlights the following concepts
When a task is moved to different columns in the Kanban Board, the status of the respective SharePoint list item is updated using PnP JS
## 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.**
## Help
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/labels/react-kanban-board) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3Areact-kanban-board) and see what the community is saying
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-kanban-board&authors=@RamPrasadMeenavalli%20@westerdaled%20@petkir%20@AJIXuMuK&title=react-kanban-board%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-kanban-board&authors=@RamPrasadMeenavalli%20@westerdaled%20@petkir%20@AJIXuMuK&title=react-kanban-board%20-%20).
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=react-kanban-board&authors=@RamPrasadMeenavalli%20@westerdaled%20@petkir%20@AJIXuMuK&title=react-kanban-board%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-kanban-board" />

View File

@ -21,11 +21,15 @@
},
{
"key": "SPFX-VERSION",
"value": "1.10.0"
"value": "1.13.0"
},
{
"key": "SPFX-TEAMSTAB",
"value": "true"
},
{
"key": "PNPCONTROLS",
"value": "PropertyFieldListPicker, PropertyFieldOrder, WebPartTitle, Placeholder"
}
],
"thumbnails": [
@ -57,6 +61,13 @@
"pictureUrl": "https://github.com/petkir.png",
"name": "Peter Paul Kirschner",
"twitter": "petkir_at"
},
{
"gitHubAccount": "AJIXuMuK",
"company": "Sharepointalist",
"pictureUrl": "https://avatars.githubusercontent.com/u/17036219?s=460\u0026u=b8e83fb70a90eae0c0e0078c206990785e1a5b6f\u0026v=4",
"name": "Alex Terentiev",
"twitter": "alexaterentiev"
}
],
"references": [

View File

@ -1,6 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-kanban-board",
"accessKey": "<!-- ACCESS KEY -->"

View File

@ -3,10 +3,17 @@
"solution": {
"name": "react-kanban-board-client-side-solution",
"id": "cccbd72b-7b89-4128-9348-0a4850ded8fd",
"version": "2.0.0.0",
"version": "3.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false
"isDomainIsolated": false,
"developer": {
"name": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/react-kanban-board.sppkg"

View File

@ -2,9 +2,5 @@
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
}

View File

@ -6,50 +6,13 @@ const gulp = require('gulp');
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.`);
// This section is inspired by Stefan Bauer's article at https://n8d.at/how-to-version-new-sharepoint-framework-projects/
// Stefan rocks!
let syncVersionsSubtask = build.subTask('version-sync', function (gulp, buildOptions, done) {
this.log('Synching versions');
// import gulp utilits to write error messages
const gutil = require('gulp-util');
// import file system utilities form nodeJS
const fs = require('fs');
// read package.json
var pkgConfig = require('./package.json');
// read configuration of web part solution file
var pkgSolution = require('./config/package-solution.json');
// log old version
this.log('package-solution.json version:\t' + pkgSolution.solution.version);
// Generate new MS compliant version number
var newVersionNumber = pkgConfig.version.split('-')[0] + '.0';
if (pkgSolution.solution.version !== newVersionNumber) {
// assign newly generated version number to web part version
pkgSolution.solution.version = newVersionNumber;
// log new version
this.log('New package-solution.json version:\t' + pkgSolution.solution.version);
fs.writeFile('./config/package-solution.json', JSON.stringify(pkgSolution, null, 4), function (err, result) {
if (err) this.log('error', err);
});
}
else {
this.log('package-solution.json version is up-to-date');
}
done();
});
let syncVersionTask = build.task('version-sync', syncVersionsSubtask);
build.rig.addPreBuildTask(syncVersionTask);
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(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,39 @@
{
"name": "react-kanban-board",
"main": "lib/index.js",
"version": "2.0.0",
"version": "3.0.0",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"engines": "undefined",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@pnp/common": "^1.3.3",
"@pnp/logging": "^1.3.3",
"@pnp/odata": "^1.3.3",
"@pnp/polyfill-ie11": "^2.0.2",
"@pnp/sp": "^1.3.3",
"@pnp/spfx-controls-react": "1.19.0",
"@pnp/spfx-property-controls": "1.19.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"classnames": "^2.2.6",
"office-ui-fabric-react": "6.214.0",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"resolutions": {
"@types/react": "16.8.8"
"@microsoft/sp-core-library": "1.13.0",
"@microsoft/sp-lodash-subset": "1.13.0",
"@microsoft/sp-office-ui-fabric-core": "1.13.0",
"@microsoft/sp-property-pane": "1.13.0",
"@microsoft/sp-webpart-base": "1.13.0",
"@pnp/sp": "2.10.0",
"@pnp/spfx-controls-react": "^3.5.0-beta.2d993b2",
"@pnp/spfx-property-controls": "^3.3.0-beta.d48002e",
"office-ui-fabric-react": "7.174.1",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"devDependencies": {
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@microsoft/sp-build-web": "1.13.0",
"@microsoft/sp-module-interfaces": "1.13.0",
"@microsoft/sp-tslint-rules": "1.13.0",
"@types/react": "16.9.51",
"@types/react-dom": "16.9.8",
"@types/webpack-env": "1.13.1",
"ajv": "~5.2.2",
"autoprefixer": "^9.8.4",
"gulp": "~3.9.1",
"gulp": "4.0.2",
"react-html-parser": "^2.0.2"
}
}

View File

@ -331,6 +331,7 @@ const hasprocessIndicator = buckets.filter((b)=> b.showPercentageHeadline).lengt
}
private onDragStart(event, taskId: string, bucket: string): void {
console.log('onDragStart');
const taskitem = this.props.tasks.filter(p => p.taskId === taskId);
if (taskitem.length === 1) {
event.dataTransfer.setData("text", taskId);
@ -353,6 +354,7 @@ const hasprocessIndicator = buckets.filter((b)=> b.showPercentageHeadline).lengt
private onDragOver(event, targetbucket: string): void {
event.preventDefault();
console.log('onDragOver');
if (this.dragelement.bucket !== targetbucket) {
const index = findIndex(this.props.buckets, element => element.bucket == targetbucket);

View File

@ -4,7 +4,7 @@
import * as React from 'react';
import styles from './KanbanTaskManagedProp.module.scss';
import { IKanbanTaskManagedProps, KanbanTaskMamagedPropertyType } from './IKanbanTask';
import { Stack } from 'office-ui-fabric-react/lib/Stack';
import { Stack } from 'office-ui-fabric-react/lib/Stack';
import ReactHtmlParser from 'react-html-parser';
import { Persona, PersonaSize, IPersonaProps } from 'office-ui-fabric-react';
@ -67,10 +67,8 @@ export default class KanbanTaskManagedProp extends React.Component<IKanbanTaskMa
size={PersonaSize.size32}
hidePersonaDetails={false}
/>))
)
)
))
}
};
</span>);
break;
case KanbanTaskMamagedPropertyType.complex:

View File

@ -11,7 +11,6 @@ import { cloneDeep } from '@microsoft/sp-lodash-subset';
import { PropertyFieldListPicker, PropertyFieldListPickerOrderBy } from '@pnp/spfx-property-controls/lib/PropertyFieldListPicker';
import { PropertyFieldOrder } from '@pnp/spfx-property-controls/lib/PropertyFieldOrder';
import * as strings from 'KanbanBoardWebPartStrings';
import "@pnp/polyfill-ie11";
import { sp } from '@pnp/sp';
import PropertyPaneBucketConfigComponent from './components/PropertyPaneBucketConfig';
@ -113,7 +112,7 @@ export default class KanbanBoardWebPart extends BaseClientSideWebPart<IKanbanBoa
onListsRetrieved: (lists) => {
//TODO Check from TS Definition it should be a string but i get a number
// with Typesafe equal it fails
if (Environment.type == EnvironmentType.Local || Environment.type == EnvironmentType.Test) {
if (Environment.type == EnvironmentType.Test) {
return lists;
} else {
const alists = lists.filter((l: any) => {

View File

@ -1,6 +1,4 @@
import { ISPKanbanService } from "./ISPKanbanService";
import "@pnp/polyfill-ie11";
import { sp } from '@pnp/sp';
import { IKanbanTask, KanbanTaskMamagedPropertyType } from "../../../kanban";
import * as strings from 'KanbanBoardWebPartStrings';

View File

@ -1,8 +1,16 @@
import { ISPKanbanService } from "./ISPKanbanService";
import "@pnp/polyfill-ie11";
import { sp } from '@pnp/sp';
import '@pnp/sp/webs';
import '@pnp/sp/lists';
import '@pnp/sp/items';
import '@pnp/sp/fields';
import { IKanbanTask, KanbanTaskMamagedPropertyType } from "../../../kanban";
import * as strings from 'KanbanBoardWebPartStrings';
import { IFieldInfo } from "@pnp/sp/fields";
interface IFieldChoiceInfo extends IFieldInfo {
Choices: string[];
}
export default class SPKanbanService implements ISPKanbanService {
@ -50,7 +58,7 @@ export default class SPKanbanService implements ISPKanbanService {
}
public getBuckets(listId: string, ): Promise<string[]> {
return sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get()
.then(status => status.Choices.map((val, index) => {
.then((status: IFieldChoiceInfo) => status.Choices.map((val, index) => {
return val;
}));
}

View File

@ -1,5 +1,5 @@
{
"extends": "node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
@ -19,20 +19,18 @@
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
"es2015.collection",
"es2015.promise"
]
},
"include": [
"src/**/*.ts", "src/webparts/kanbanBoard/components/bucketOrder.tsx"
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"lib"
]
"exclude": []
}

View File

@ -1,5 +1,5 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
@ -17,7 +17,6 @@
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,

View File

@ -1,25 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -9,8 +9,10 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
release
*.sppkg
# Coverage directory used by tools like istanbul
@ -30,4 +32,4 @@ obj
# Styles Generated Code
*.scss.ts
*.scss.d.ts
*.scss.d.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

@ -24,7 +24,7 @@
"environment": "spo",
"framework": "react",
"isCreatingSolution": true,
"version": "1.11.0",
"version": "1.13.0",
"libraryName": "react-list-items-menu",
"libraryId": "8b4a758d-a968-4e7c-a949-b42e7dd5ad14",
"packageManager": "npm",

View File

@ -17,13 +17,12 @@ When the user clicks on the header it dynamically load documents.
## Compatibility
![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg)
![Node.js LTS 10.x](https://img.shields.io/badge/Node.js-LTS%2010.x-green.svg)
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-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")
![Teams Incompatible](https://img.shields.io/badge/Teams-Incompatible-lightgrey.svg)
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "The solution requires access to SharePoint content")
![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)
## Applies to
@ -31,11 +30,11 @@ When the user clicks on the header it dynamically load documents.
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## WebPart Properties
## Web Part Properties
Property |Type|Required| comments
--------------------|----|--------|----------
WebPart Title| Text| no|
Web Part Title| Text| no|
Select Document Library| dropdown|yes
Select Field to Group By | dropdown|yes
@ -57,6 +56,8 @@ Version|Date|Comments
1.0.0|November 20, 2020|Initial release
1.0.1|February 18, 2021|Added support for metadata columns
1.0.2|February 21, 2021|Fixed `gulp build` issues
1.0.3|October 25, 2021|Fixed bug support for metadata columns and Lookup fields
1.0.4|October 31🦇, 2021|Upgraded to SPFx 1.13
## Minimal Path to Awesome
@ -75,11 +76,15 @@ Version|Date|Comments
**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.**
## Support
We do not support samples, but we do use GitHub to track issues and constantly want to improve these samples.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/labels/react-list-items-menu) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=label%3Areact-list-items-menu) and see what the community is saying.
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-list-items-menu&authors=@joaojmendes%20@Ravikadri&title=react-list-items-menu%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-list-items-menu&authors=@joaojmendes%20@Ravikadri&title=react-list-items-menu%20-%20).

View File

@ -9,7 +9,7 @@
"Allows user create a navigation menu , grouped by any column of document library. When the user clicks on the header it dynamically load documents."
],
"creationDateTime": "2021-02-18",
"updateDateTime": "2021-02-18",
"updateDateTime": "2021-10-31",
"products": [
"SharePoint",
"Office"
@ -21,7 +21,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.11.0"
"value": "1.13.0"
},
{
"key": "SPFX-SUPPORTSTHEMEVARIANTS",
@ -34,6 +34,14 @@
{
"key": "SPFX-TEAMSPERSONALAPP",
"value": "true"
},
{
"key": "PNPCONTROLS",
"value": "PropertyFieldListPicker, PropertyFieldMessage, PropertyFieldSpinner"
},
{
"key": "REACT-HOOKS",
"value": "true"
}
],
"thumbnails": [
@ -69,6 +77,13 @@
"pictureUrl": "https://github.com/joaojmendes.png",
"name": "Jo\u00E3o Mendes",
"twitter": "joaojmendes"
},
{
"gitHubAccount": "mohammadamer",
"company": "Atea Global Services",
"pictureUrl": "https://avatars.githubusercontent.com/u/19314043?s=460&u=79acb7fd0ad466e1040ddd8a739fa93385018b81&v=4",
"name": "Mohammed Amer",
"twitter": "Mohammad3mer"
}
],
"references": [

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