Merge branch 'main' into React-List-Items-Menu-SPfx-Upgrade

This commit is contained in:
Hugo Bernier 2021-10-31 17:44:27 -04:00 committed by GitHub
commit 5e32931fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 69190 additions and 33842 deletions

View File

@ -128,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. 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/YOUR-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). 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). 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 | Jeremy Coleman (MCP, PC Professional, Inc.)
js-modern-calendar | Nanddeep Nachan ([@NanddeepNachan](twitter.com/NanddeepNachan)) js-modern-calendar | Nanddeep Nachan ([@NanddeepNachan](twitter.com/NanddeepNachan))
js-modern-calendar | Ravi Chandra ([Ravikadri](https://github.com/Ravikadri)) 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 ## Version history
@ -55,6 +56,7 @@ Version|Date|Comments
1.0.0.0|February 11, 2017|Initial release 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.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.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 ## Disclaimer

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# react-at-a-glance # At a glance
## Summary ## 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). The idea is based of the *At a glance* section of a news in the BBC news app (beta).
### Highlights ### Highlights
- Usage of `SPHttpClient` or `PnP JS` - Usage of `SPHttpClient` or `PnPjs`
- Conditional property enabling - Conditional property enabling
- Usage of regex to get the sentences from article content - Usage of regex to get the sentences from article content
- Usage of Carousel for mobile view - 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) ![article-content-desktop](./assets/article-content-desktop.png)
## Compatibility ## Compatibility
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg) ![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 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) ![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams") ![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")
![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench") ![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 ## Applies to
@ -62,17 +64,18 @@ react-at-a-glance | [Anoop Tatti](https://github.com/anoopt) ([https://linktr.ee
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0|September 07, 2021|Initial release 1.0|September 07, 2021|Initial release
1.1|October 20, 2021|Minor CSS changes
## Minimal Path to Awesome ## Minimal Path to Awesome
- Clone this repository - Clone this repository
- Ensure that you are at the solution folder - Ensure that you are at the solution folder
- in the command-line run: - in the command-line run:
- **npm install** - `npm install`
- **gulp serve** - `gulp serve`
- Open a news article - Open a news article
- Add `?loadSPFX=true&debugManifestsFile=https://localhost:4321/temp/manifests.js` to the URL - 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 ## 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." "The idea is based of the At a glance section of a news in the BBC news app."
], ],
"creationDateTime": "2021-09-07", "creationDateTime": "2021-09-07",
"updateDateTime": "2021-09-07", "updateDateTime": "2021-10-20",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"

View File

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

View File

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

View File

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

View File

@ -140,6 +140,7 @@ Version|Date|Comments
1.0.11|May 10, 2021|Optimized page refresh using local storage 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.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.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 ## Minimal Path to Awesome

View File

@ -8,8 +8,8 @@
"longDescription": [ "longDescription": [
"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." "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."
], ],
"creationDateTime": "2020-12-04", "created": "2020-12-04",
"updateDateTime": "2021-10-02", "modified": "2021-10-16",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"

View File

@ -369,6 +369,8 @@ export default class parseRecurrentEvent {
} }
} }
if (e.fRecurrence === "1" && e.MasterSeriesItemID !== "") { 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); const ni = this.cloneObj(e);
er.push(ni); er.push(ni);
} }

View File

@ -10,7 +10,7 @@ I got the idea from this great article [Use Power Automate to Notify of Upcoming
## Compatibility ## Compatibility
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg) ![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) ![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") ![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") ![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 Version|Date|Comments
-------|----|-------- -------|----|--------
1.0|September 17, 2021|Initial release 1.0|September 17, 2021|Initial release
1.1|October 10, 2021|Add pagination
## Minimal Path to Awesome ## Minimal Path to Awesome
@ -39,14 +40,14 @@ Version|Date|Comments
- Clone this repository - Clone this repository
- Ensure that you are at the solution folder - Ensure that you are at the solution folder
- In the command-line run: - In the command-line run:
- **npm install** - `npm install`
- **gulp bundle** - `gulp bundle`
- **gulp package-solution** - `gulp package-solution`
- Deploy the package to your app catalog - Deploy the package to your app catalog
- Approve the API permission request from the SharePoint admin - Approve the API permission request from the SharePoint admin
- Add the web part to a page - Add the web part to a page
- In the command-line run: - In the command-line run:
- **gulp serve --nobrowser** - `gulp serve --nobrowser`
## Features ## 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." "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", "creationDateTime": "2021-09-17",
"updateDateTime": "2021-09-17", "updateDateTime": "2021-10-10",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"
@ -22,6 +22,10 @@
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.12.1" "value": "1.12.1"
},
{
"key": "PNPCONTROLS",
"value": "Pagination"
} }
], ],
"thumbnails": [ "thumbnails": [

View File

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

View File

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

View File

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

View File

@ -5,7 +5,9 @@ import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp
import { IApplications, IApplication, IFormattedApplication } from '../../../models/IApplication'; import { IApplications, IApplication, IFormattedApplication } from '../../../models/IApplication';
import { IGraphAppSecretExpirationState } from './GraphAppSecretExpirationState'; import { IGraphAppSecretExpirationState } from './GraphAppSecretExpirationState';
import * as moment from 'moment'; 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({ const stackItemHidden = mergeStyles({
display: 'none', display: 'none',
@ -57,9 +59,13 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
super(props); super(props);
this.state = { this.state = {
applications: [], applications: [],
filteredApplications: [],
filterValue: "",
searchFilter: "",
error: undefined, error: undefined,
loading: true, 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'); var today = (moment(Date.now())).format('DD-MMM-YYYY');
try { try {
applications.forEach(app => { applications.forEach(app => {
app.passwordCredentials.forEach(pswd => {
if (app.passwordCredentials.length > 0) { let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays();
app.passwordCredentials.forEach(pswd => { let formatedApp: IFormattedApplication = {
let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays(); applicationId: app.appId,
let formatedApp: IFormattedApplication = { displayName: app.displayName,
applicationId: app.appId, type: "Secret",
displayName: app.displayName, expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'),
type: "Secret", daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'), };
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0 displayedApplication.push(formatedApp);
}; });
displayedApplication.push(formatedApp); app.keyCredentials.forEach(keyCred => {
}); let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays();
} let formatedApp: IFormattedApplication = {
if (app.keyCredentials.length > 0) { applicationId: app.appId,
app.keyCredentials.forEach(keyCred => { displayName: app.displayName,
let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays(); type: "Certificate",
let formatedApp: IFormattedApplication = { expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'),
applicationId: app.appId, daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
displayName: app.displayName, };
type: "Certificate", displayedApplication.push(formatedApp);
expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'), });
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
};
displayedApplication.push(formatedApp);
});
}
}); });
this.setState({ this.setState({
applications: displayedApplication applications: displayedApplication,
filteredApplications: displayedApplication
}); });
} catch (error) { } catch (error) {
console.log(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 = () => { private _groupView = () => {
if (this.state.groupByFields.length === 0) { if (this.state.groupByFields.length === 0) {
let groupByFields: IGrouping[] = [ let groupByFields: IGrouping[] = [
@ -177,21 +221,31 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
<p className={styles.title}>Application list :</p> <p className={styles.title}>Application list :</p>
<div className={this.state.loading ? "" : stackItemHidden}> <div className={this.state.loading ? "" : stackItemHidden}>
<Spinner label="Loading..." ariaLive="assertive" labelPosition="left" /> <Spinner label="Loading..." ariaLive="assertive" labelPosition="left" />
<br/> <br />
</div> </div>
<div className={this.state.loading ? stackItemHidden : ""}> <div className={this.state.loading ? stackItemHidden : ""}>
<DefaultButton text={this.state.groupByFields.length === 0 ? "Group by App ID" : "Ungroup"} onClick={this._groupView} /> <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 <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} viewFields={_viewFields}
iconFieldName="ServerRelativeUrl" iconFieldName="ServerRelativeUrl"
compact={true} compact={true}
selectionMode={SelectionMode.none} selectionMode={SelectionMode.none}
selection={this._getSelection} selection={this._getSelection}
groupByFields={this.state.groupByFields} groupByFields={this.state.groupByFields}
showFilter={true} showFilter={false}
filterPlaceHolder="Search..." /> filterPlaceHolder="Search..." />
</div> </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>
</div> </div>
); );

View File

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

View File

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

View File

@ -26,11 +26,13 @@ Web part pulls all Microsoft 365 Groups and Teams that the logged in user has ac
## Compatibility ## Compatibility
![SPFx 1.10](https://img.shields.io/badge/SPFx-1.10.0-green.svg) ![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-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) ![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) ![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams") ![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%202019-Not%20compatible-red.svg)
![Workbench Hosted: Only after API permissions granted](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Only after API permissions granted") ![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 ## 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) 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 ## 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.0 | April 16, 2021 | Initial release |
| 1.0.1 | August 1, 2021 | Fixed references to Office.com | | 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 # 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." "Web part pulls all Microsoft 365 Groups and Teams that the logged in user has access to view."
], ],
"creationDateTime": "2021-05-06", "creationDateTime": "2021-05-06",
"updateDateTime": "2021-08-01", "updateDateTime": "2021-10-08",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"
@ -21,7 +21,7 @@
}, },
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.10.0" "value": "1.12.1"
} }
], ],
"thumbnails": [ "thumbnails": [

View File

@ -1,7 +1,16 @@
'use strict'; 'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web'); 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.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" "test": "gulp test"
}, },
"dependencies": { "dependencies": {
"@microsoft/sp-core-library": "1.9.1", "@microsoft/sp-core-library": "1.12.1",
"@microsoft/sp-lodash-subset": "1.9.1", "@microsoft/sp-lodash-subset": "1.12.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1", "@microsoft/sp-office-ui-fabric-core": "1.12.1",
"@microsoft/sp-webpart-base": "1.9.1", "@microsoft/sp-webpart-base": "1.12.1",
"@types/es6-promise": "0.0.33", "@types/es6-promise": "0.0.33",
"@types/react": "16.8.8", "@types/react": "16.8.8",
"@types/react-dom": "16.8.3", "@types/react-dom": "16.8.3",
@ -28,10 +28,10 @@
"@types/react": "16.8.8" "@types/react": "16.8.8"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/sp-build-web": "1.9.1", "@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-tslint-rules": "1.9.1", "@microsoft/sp-tslint-rules": "1.12.1",
"@microsoft/sp-module-interfaces": "1.9.1", "@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.9.1", "@microsoft/sp-webpart-workbench": "1.12.1",
"@microsoft/rush-stack-compiler-2.9": "0.7.16", "@microsoft/rush-stack-compiler-2.9": "0.7.16",
"gulp": "~3.9.1", "gulp": "~3.9.1",
"@types/chai": "3.4.34", "@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. // 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 // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false, "requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"], "supportedHosts": ["SharePointWebPart", "TeamsTab"],
"preconfiguredEntries": [{ "preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other "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 lib
solution solution
temp temp
release
*.sppkg *.sppkg
# Coverage directory used by tools like istanbul # 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 * Chrome browser: https://aka.ms/spfx-debugger-extensions
*/ */
"version": "0.2.0", "version": "0.2.0",
"configurations": [{ "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"
]
},
{ {
"name": "Hosted workbench", "name": "Hosted workbench",
"type": "chrome", "type": "chrome",

View File

@ -2,7 +2,7 @@
"@microsoft/generator-sharepoint": { "@microsoft/generator-sharepoint": {
"isCreatingSolution": true, "isCreatingSolution": true,
"environment": "spo", "environment": "spo",
"version": "1.10.0", "version": "1.13.0",
"libraryName": "react-kanban-board", "libraryName": "react-kanban-board",
"libraryId": "cccbd72b-7b89-4128-9348-0a4850ded8fd", "libraryId": "cccbd72b-7b89-4128-9348-0a4850ded8fd",
"packageManager": "npm", "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) ![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 ## Applies to
@ -49,9 +57,10 @@ The Task list can be chosen using the web part properties (BaseTemplate 171 or 1
Solution|Author(s) Solution|Author(s)
--------|--------- --------|---------
react-kanban-board | [Ram](https://twitter.com/ram_meenavalli) react-kanban-board | [Ram Prasad Meenavalli](https://github.com/RamPrasadMeenavalli) ([@ram_meenavalli](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 | [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 ([@petkir_at](https://twitter.com/petkir_at)) 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 ## Version history
@ -60,6 +69,7 @@ Version|Date|Comments
1.0.0.0|July 17, 2019|Initial release 1.0.0.0|July 17, 2019|Initial release
1.0.1.0|April 21, 2020|Added support for Teams hosts 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 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) [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)--> <!---* [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--> <!---* [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 ## Minimal Path to Awesome
* Clone this repository * 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 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" /> <img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-kanban-board" />

View File

@ -21,11 +21,15 @@
}, },
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.10.0" "value": "1.13.0"
}, },
{ {
"key": "SPFX-TEAMSTAB", "key": "SPFX-TEAMSTAB",
"value": "true" "value": "true"
},
{
"key": "PNPCONTROLS",
"value": "PropertyFieldListPicker, PropertyFieldOrder, WebPartTitle, Placeholder"
} }
], ],
"thumbnails": [ "thumbnails": [
@ -57,6 +61,13 @@
"pictureUrl": "https://github.com/petkir.png", "pictureUrl": "https://github.com/petkir.png",
"name": "Peter Paul Kirschner", "name": "Peter Paul Kirschner",
"twitter": "petkir_at" "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": [ "references": [

View File

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

View File

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

View File

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

View File

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

View File

@ -6,50 +6,13 @@ const gulp = require('gulp');
const build = require('@microsoft/sp-build-web'); 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.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/ var getTasks = build.rig.getTasks;
// Stefan rocks! build.rig.getTasks = function () {
let syncVersionsSubtask = build.subTask('version-sync', function (gulp, buildOptions, done) { var result = getTasks.call(build.rig);
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);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(gulp); build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,39 @@
{ {
"name": "react-kanban-board", "name": "react-kanban-board",
"main": "lib/index.js", "main": "lib/index.js",
"version": "2.0.0", "version": "3.0.0",
"private": true, "private": true,
"engines": { "engines": "undefined",
"node": ">=0.10.0"
},
"scripts": { "scripts": {
"build": "gulp bundle", "build": "gulp bundle",
"clean": "gulp clean", "clean": "gulp clean",
"test": "gulp test" "test": "gulp test"
}, },
"dependencies": { "dependencies": {
"@microsoft/sp-core-library": "1.10.0", "@microsoft/sp-core-library": "1.13.0",
"@microsoft/sp-lodash-subset": "1.10.0", "@microsoft/sp-lodash-subset": "1.13.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0", "@microsoft/sp-office-ui-fabric-core": "1.13.0",
"@microsoft/sp-property-pane": "1.10.0", "@microsoft/sp-property-pane": "1.13.0",
"@microsoft/sp-webpart-base": "1.10.0", "@microsoft/sp-webpart-base": "1.13.0",
"@pnp/common": "^1.3.3", "@pnp/sp": "2.10.0",
"@pnp/logging": "^1.3.3", "@pnp/spfx-controls-react": "^3.5.0-beta.2d993b2",
"@pnp/odata": "^1.3.3", "@pnp/spfx-property-controls": "^3.3.0-beta.d48002e",
"@pnp/polyfill-ie11": "^2.0.2", "office-ui-fabric-react": "7.174.1",
"@pnp/sp": "^1.3.3", "react": "16.13.1",
"@pnp/spfx-controls-react": "1.19.0", "react-dom": "16.13.1"
"@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"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/rush-stack-compiler-2.9": "0.7.7", "@microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/rush-stack-compiler-3.3": "0.3.5", "@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@microsoft/sp-build-web": "1.10.0", "@microsoft/sp-build-web": "1.13.0",
"@microsoft/sp-module-interfaces": "1.10.0", "@microsoft/sp-module-interfaces": "1.13.0",
"@microsoft/sp-tslint-rules": "1.10.0", "@microsoft/sp-tslint-rules": "1.13.0",
"@microsoft/sp-webpart-workbench": "1.10.0", "@types/react": "16.9.51",
"@types/chai": "3.4.34", "@types/react-dom": "16.9.8",
"@types/mocha": "2.2.38", "@types/webpack-env": "1.13.1",
"ajv": "~5.2.2", "ajv": "~5.2.2",
"autoprefixer": "^9.8.4", "autoprefixer": "^9.8.4",
"gulp": "~3.9.1", "gulp": "4.0.2",
"react-html-parser": "^2.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 { private onDragStart(event, taskId: string, bucket: string): void {
console.log('onDragStart');
const taskitem = this.props.tasks.filter(p => p.taskId === taskId); const taskitem = this.props.tasks.filter(p => p.taskId === taskId);
if (taskitem.length === 1) { if (taskitem.length === 1) {
event.dataTransfer.setData("text", taskId); event.dataTransfer.setData("text", taskId);
@ -353,6 +354,7 @@ const hasprocessIndicator = buckets.filter((b)=> b.showPercentageHeadline).lengt
private onDragOver(event, targetbucket: string): void { private onDragOver(event, targetbucket: string): void {
event.preventDefault(); event.preventDefault();
console.log('onDragOver');
if (this.dragelement.bucket !== targetbucket) { if (this.dragelement.bucket !== targetbucket) {
const index = findIndex(this.props.buckets, element => element.bucket == targetbucket); const index = findIndex(this.props.buckets, element => element.bucket == targetbucket);

View File

@ -4,7 +4,7 @@
import * as React from 'react'; import * as React from 'react';
import styles from './KanbanTaskManagedProp.module.scss'; import styles from './KanbanTaskManagedProp.module.scss';
import { IKanbanTaskManagedProps, KanbanTaskMamagedPropertyType } from './IKanbanTask'; 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 ReactHtmlParser from 'react-html-parser';
import { Persona, PersonaSize, IPersonaProps } from 'office-ui-fabric-react'; import { Persona, PersonaSize, IPersonaProps } from 'office-ui-fabric-react';
@ -67,10 +67,8 @@ export default class KanbanTaskManagedProp extends React.Component<IKanbanTaskMa
size={PersonaSize.size32} size={PersonaSize.size32}
hidePersonaDetails={false} hidePersonaDetails={false}
/>)) />))
) ))
)
} }
};
</span>); </span>);
break; break;
case KanbanTaskMamagedPropertyType.complex: 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 { PropertyFieldListPicker, PropertyFieldListPickerOrderBy } from '@pnp/spfx-property-controls/lib/PropertyFieldListPicker';
import { PropertyFieldOrder } from '@pnp/spfx-property-controls/lib/PropertyFieldOrder'; import { PropertyFieldOrder } from '@pnp/spfx-property-controls/lib/PropertyFieldOrder';
import * as strings from 'KanbanBoardWebPartStrings'; import * as strings from 'KanbanBoardWebPartStrings';
import "@pnp/polyfill-ie11";
import { sp } from '@pnp/sp'; import { sp } from '@pnp/sp';
import PropertyPaneBucketConfigComponent from './components/PropertyPaneBucketConfig'; import PropertyPaneBucketConfigComponent from './components/PropertyPaneBucketConfig';
@ -113,7 +112,7 @@ export default class KanbanBoardWebPart extends BaseClientSideWebPart<IKanbanBoa
onListsRetrieved: (lists) => { onListsRetrieved: (lists) => {
//TODO Check from TS Definition it should be a string but i get a number //TODO Check from TS Definition it should be a string but i get a number
// with Typesafe equal it fails // with Typesafe equal it fails
if (Environment.type == EnvironmentType.Local || Environment.type == EnvironmentType.Test) { if (Environment.type == EnvironmentType.Test) {
return lists; return lists;
} else { } else {
const alists = lists.filter((l: any) => { const alists = lists.filter((l: any) => {

View File

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

View File

@ -1,8 +1,16 @@
import { ISPKanbanService } from "./ISPKanbanService"; import { ISPKanbanService } from "./ISPKanbanService";
import "@pnp/polyfill-ie11";
import { sp } from '@pnp/sp'; 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 { IKanbanTask, KanbanTaskMamagedPropertyType } from "../../../kanban";
import * as strings from 'KanbanBoardWebPartStrings'; import * as strings from 'KanbanBoardWebPartStrings';
import { IFieldInfo } from "@pnp/sp/fields";
interface IFieldChoiceInfo extends IFieldInfo {
Choices: string[];
}
export default class SPKanbanService implements ISPKanbanService { export default class SPKanbanService implements ISPKanbanService {
@ -50,7 +58,7 @@ export default class SPKanbanService implements ISPKanbanService {
} }
public getBuckets(listId: string, ): Promise<string[]> { public getBuckets(listId: string, ): Promise<string[]> {
return sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get() 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; 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": { "compilerOptions": {
"target": "es5", "target": "es5",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
@ -19,20 +19,18 @@
"./node_modules/@microsoft" "./node_modules/@microsoft"
], ],
"types": [ "types": [
"es6-promise",
"webpack-env" "webpack-env"
], ],
"lib": [ "lib": [
"es5", "es5",
"dom", "dom",
"es2015.collection" "es2015.collection",
"es2015.promise"
] ]
}, },
"include": [ "include": [
"src/**/*.ts", "src/webparts/kanbanBoard/components/bucketOrder.tsx" "src/**/*.ts",
"src/**/*.tsx"
], ],
"exclude": [ "exclude": []
"node_modules",
"lib"
]
} }

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": { "rules": {
"class-name": false, "class-name": false,
"export-name": false, "export-name": false,
@ -17,7 +17,6 @@
"no-switch-case-fall-through": true, "no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true, "no-unnecessary-semicolons": true,
"no-unused-expression": true, "no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true, "no-with-statement": true,
"semicolon": true, "semicolon": true,
"trailing-comma": false, "trailing-comma": false,

View File

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

View File

@ -30,7 +30,7 @@ When the user clicks on the header it dynamically load documents.
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview) * [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) * [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## WebPart Properties ## Web Part Properties
Property |Type|Required| comments Property |Type|Required| comments
--------------------|----|--------|---------- --------------------|----|--------|----------
@ -56,7 +56,8 @@ Version|Date|Comments
1.0.0|November 20, 2020|Initial release 1.0.0|November 20, 2020|Initial release
1.0.1|February 18, 2021|Added support for metadata columns 1.0.1|February 18, 2021|Added support for metadata columns
1.0.2|February 21, 2021|Fixed `gulp build` issues 1.0.2|February 21, 2021|Fixed `gulp build` issues
1.0.3|October 31🦇, 2021|Upgraded to SPFx 1.13 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 ## Minimal Path to Awesome

View File

@ -34,6 +34,14 @@
{ {
"key": "SPFX-TEAMSPERSONALAPP", "key": "SPFX-TEAMSPERSONALAPP",
"value": "true" "value": "true"
},
{
"key": "PNPCONTROLS",
"value": "PropertyFieldListPicker, PropertyFieldMessage, PropertyFieldSpinner"
},
{
"key": "REACT-HOOKS",
"value": "true"
} }
], ],
"thumbnails": [ "thumbnails": [

View File

@ -3,7 +3,7 @@
"solution": { "solution": {
"name": "react-list-items-menu-client-side-solution", "name": "react-list-items-menu-client-side-solution",
"id": "8b4a758d-a968-4e7c-a949-b42e7dd5ad14", "id": "8b4a758d-a968-4e7c-a949-b42e7dd5ad14",
"version": "1.0.2.0", "version": "1.0.3.0",
"includeClientSideAssets": true, "includeClientSideAssets": true,
"skipFeatureDeployment": true, "skipFeatureDeployment": true,
"isDomainIsolated": false, "isDomainIsolated": false,

View File

@ -24269,6 +24269,11 @@
} }
} }
}, },
"date-fns": {
"version": "2.25.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.25.0.tgz",
"integrity": "sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w=="
},
"dateformat": { "dateformat": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "react-list-items-menu", "name": "react-list-items-menu",
"version": "1.0.2", "version": "1.0.3",
"private": true, "private": true,
"main": "lib/index.js", "main": "lib/index.js",
"engines": { "engines": {
@ -27,6 +27,7 @@
"@pnp/spfx-property-controls": "1.19.0", "@pnp/spfx-property-controls": "1.19.0",
"@types/jquery": "^3.5.0", "@types/jquery": "^3.5.0",
"@uifabric/file-type-icons": "^7.6.11", "@uifabric/file-type-icons": "^7.6.11",
"date-fns": "^2.25.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"jsstore": "^3.10.3", "jsstore": "^3.10.3",
"moment": "^2.29.1", "moment": "^2.29.1",

View File

@ -158,3 +158,11 @@ export const convertTimeTo24h = (
resolve(hourInTimeFormat); 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

@ -69,7 +69,86 @@ export const ListItemsMenu: React.FunctionComponent<IListItemsMenuProps> = (
const stateRef = React.useRef(state); // Use to access state on eventListenners const stateRef = React.useRef(state); // Use to access state on eventListenners
// On component did mount only if listId or Field Change 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,
onHeaderClick: _onGroupHeaderClick,
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 component did mount only if listId or Field Change
React.useEffect(() => { React.useEffect(() => {
(async () => { (async () => {
if (!props.listId || !props.fieldName) { if (!props.listId || !props.fieldName) {
@ -142,7 +221,7 @@ export const ListItemsMenu: React.FunctionComponent<IListItemsMenuProps> = (
setState(stateRef.current); setState(stateRef.current);
} }
})(); })();
}, [props.listId, props.fieldName]); }, [props.listId, props.fieldName]); */
// On Header click get Items for the header // On Header click get Items for the header
const _onGroupHeaderClick = async ( const _onGroupHeaderClick = async (

View File

@ -9,7 +9,8 @@ import moment from "moment";
import { sp } from "@pnp/sp"; import { sp } from "@pnp/sp";
import { IFieldInfo } from "@pnp/sp/fields"; import { IFieldInfo } from "@pnp/sp/fields";
import { IListInfo } from "@pnp/sp/lists"; import { IListInfo } from "@pnp/sp/lists";
import { checkIfValidDate } from "../Utils/Utils";
import {format , parseISO} from 'date-fns';
export const useList = () => { export const useList = () => {
// Run on useList hook // Run on useList hook
(async () => {})(); (async () => {})();
@ -71,12 +72,14 @@ export const useList = () => {
): Promise<any[]> => { ): Promise<any[]> => {
const _field: any = await getField(listId, groupByField); const _field: any = await getField(listId, groupByField);
if (checkIfValidDate(groupFieldValue)) {
groupFieldValue = format(new Date(groupFieldValue), "yyyy-MM-dd");
}
switch (_field.fieldType) { switch (_field.fieldType) {
case "DateTime": case "DateTime":
groupFieldValue = groupFieldValue =
groupFieldValue != "Unassigned" groupFieldValue != "Unassigned" ? format(parseISO(groupFieldValue), "yyyy-MM-dd") : "Unassigned";
? moment(groupFieldValue).format("YYYY-MM-DD")
: "Unassigned";
break; break;
case "AllDayEvent": case "AllDayEvent":
groupFieldValue = groupFieldValue === "No" ? "0" : "1"; groupFieldValue = groupFieldValue === "No" ? "0" : "1";
@ -84,7 +87,6 @@ export const useList = () => {
default: default:
break; break;
} }
let _viewXml = `<View Scope='Recursive'> let _viewXml = `<View Scope='Recursive'>
<Query> <Query>
<OrderBy> <OrderBy>

View File

@ -47,9 +47,17 @@ Current Data Functions:
Built with SharePoint Framework GA, Office Graph, React and Chart.JS Built with SharePoint Framework GA, Office Graph, React and Chart.JS
## Used SharePoint Framework Version
![version](https://img.shields.io/badge/version-1.10.0-green.svg) ## Compatibility
![SPFx 1.10](https://img.shields.io/badge/SPFx-1.10.0-green.svg)
![Node.js LSTS 8 | LTS 10](https://img.shields.io/badge/Node.js-LTS%208%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 Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
## Applies to ## Applies to
@ -60,24 +68,20 @@ Built with SharePoint Framework GA, Office Graph, React and Chart.JS
Solution|Author(s) Solution|Author(s)
--------|--------- --------|---------
react-modern-charts|Jeremy Coleman (MCP, PC Professional, Inc.) react-modern-charts|[Jeremy Coleman](https://github.com/jcoleman-pcprofessional) (MCP, PC Professional, Inc.)
react-modern-charts|Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at)) react-modern-charts|[Peter Paul Kirschner](https://github.com/petkir) ([@petkir_at](https://twitter.com/petkir_at))
react-modern-charts|[Abderahman Moujahid](https://github.com/Abderahman88)
## Version history ## Version history
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0.0.4|October 19, 2021| Fix values of managed metadata fields
1.0.0.3|July 30, 2020| Support for Managed Metadata Field(Single) as Label 1.0.0.3|July 30, 2020| Support for Managed Metadata Field(Single) as Label
1.0.0.2|February 09, 2020| Upgrade to SPFx 1.10.0 1.0.0.2|February 09, 2020| Upgrade to SPFx 1.10.0
1.0.0.1|April 25, 2018|Update to SPFx 1.4.1 1.0.0.1|April 25, 2018|Update to SPFx 1.4.1
1.0.0.0|February 11, 2017|Initial release 1.0.0.0|February 11, 2017|Initial release
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Prerequisites ## Prerequisites
- SharePoint Online tenant with Office Graph content-enabled - SharePoint Online tenant with Office Graph content-enabled
@ -99,4 +103,18 @@ Sample Web Parts in this solution illustrate the following concepts on top of th
- passing Web Part properties to React components - passing Web Part properties to React components
- building dynamic web part properties - building dynamic web part properties
## 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.
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-modern-charts&authors=@jcoleman-pcprofessional%20@petkir%20@Abderahman88&title=react-modern-charts%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-modern-charts&authors=@jcoleman-pcprofessional%20@petkir%20@Abderahman88&title=react-modern-charts%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-modern-charts&authors=@jcoleman-pcprofessional%20@petkir%20@Abderahman88&title=react-modern-charts%20-%20).
![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-modern-charts) ![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-modern-charts)

View File

@ -9,7 +9,7 @@
"This web part uses the Chart.js library to visualize SharePoint list data." "This web part uses the Chart.js library to visualize SharePoint list data."
], ],
"creationDateTime": "2020-07-30", "creationDateTime": "2020-07-30",
"updateDateTime": "2020-07-30", "updateDateTime": "2021-10-19",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"

View File

@ -3,7 +3,7 @@
"solution": { "solution": {
"name": "modern-charts-client-side-solution", "name": "modern-charts-client-side-solution",
"id": "f8a78a9a-a93e-4843-89e5-7b871d9b9fa2", "id": "f8a78a9a-a93e-4843-89e5-7b871d9b9fa2",
"version": "1.0.0.3", "version": "1.0.0.4",
"isDomainIsolated": false, "isDomainIsolated": false,
"includeClientSideAssets": true "includeClientSideAssets": true
}, },

View File

@ -208,8 +208,11 @@ export default class ModernChartsWebPart extends BaseClientSideWebPart<IModernCh
private getUnique(data: Array<Object>, config: ChartConfiguration): Object { private getUnique(data: Array<Object>, config: ChartConfiguration): Object {
const chLabels: Object = { unique: [], labels: [] }; const chLabels: Object = { unique: [], labels: [] };
data.forEach((item) => { data.forEach((item) => {
if (chLabels['unique'].indexOf(item[config.unique]) == -1 && item[config.unique] != null && item[config.unique] != "") { var uniqueItem = item[config.unique];
chLabels['unique'].push(item[config.unique]); var isTaxonomyField = item[config.unique].hasOwnProperty('TermGuid');
if ((isTaxonomyField ? !chLabels['unique'].some(field => field['TermGuid'] === uniqueItem['TermGuid']) : chLabels['unique'].indexOf(uniqueItem) == -1) && uniqueItem != null && uniqueItem != "") {
chLabels['unique'].push(uniqueItem);
//if term use VAlue //if term use VAlue
chLabels['labels'].push(this.getLabel(item, config.col1)); chLabels['labels'].push(this.getLabel(item, config.col1));
} }
@ -234,14 +237,13 @@ export default class ModernChartsWebPart extends BaseClientSideWebPart<IModernCh
} }
private getValues(data: Array<Object>, unique: Array<string>, config: ChartConfiguration): Array<Array<any>> { private getValues(data: Array<Object>, unique: Array<string>, config: ChartConfiguration): Array<Array<any>> {
const values: Object = {}; const values: Object = {};
const vals: Array<Array<any>> = [[]]; const vals: Array<Array<any>> = [[]];
unique.forEach((col, i) => { unique.forEach((col, i) => {
values[col] = []; values[col] = [];
vals[i] = []; vals[i] = [];
data.forEach((item, _i) => { data.forEach((item, _i) => {
if (item[config.unique] == col) { if (item[config.unique].hasOwnProperty('TermGuid') ? item[config.unique]["TermGuid"] == col["TermGuid"] : item[config.unique] == col) {
vals[i].push(item[config.col2]); vals[i].push(item[config.col2]);
} }
}); });

View File

@ -1,72 +1,96 @@
import * as React from 'react'; import * as React from "react";
import styles from './ModernCharts.module.scss'; import styles from "./ModernCharts.module.scss";
import { IModernChartsProps } from '../IModernChartsWebPartProps'; import { IModernChartsProps } from "../IModernChartsWebPartProps";
import { MChart } from '../IModernChartsWebPartProps'; import { MChart } from "../IModernChartsWebPartProps";
import 'chart.js'; import "chart.js";
import { Doughnut } from 'react-chartjs-2'; import { Doughnut } from "react-chartjs-2";
import { Line } from 'react-chartjs-2'; import { Line } from "react-chartjs-2";
import { Pie } from 'react-chartjs-2'; import { Pie } from "react-chartjs-2";
import { Bar } from 'react-chartjs-2'; import { Bar } from "react-chartjs-2";
import { HorizontalBar } from 'react-chartjs-2'; import { HorizontalBar } from "react-chartjs-2";
import { Radar } from 'react-chartjs-2'; import { Radar } from "react-chartjs-2";
import { Polar } from 'react-chartjs-2'; import { Polar } from "react-chartjs-2";
import ChartOptions from '../ChartOptions'; import ChartOptions from "../ChartOptions";
import { import {
DocumentCard, DocumentCard,
DocumentCardTitle, DocumentCardTitle,
DocumentCardLocation, DocumentCardLocation,
DocumentCardPreview, DocumentCardPreview,
IDocumentCardPreviewProps IDocumentCardPreviewProps,
} from 'office-ui-fabric-react/lib/DocumentCard'; } from "office-ui-fabric-react/lib/DocumentCard";
export default class ModernCharts extends React.Component<IModernChartsProps, {}> {
export default class ModernCharts extends React.Component<
IModernChartsProps,
{}
> {
public render(): JSX.Element { public render(): JSX.Element {
const charts: JSX.Element[] = this.props.charts.map((chart: MChart, i: number) => { const charts: JSX.Element[] = this.props.charts.map(
return ( (chart: MChart, i: number) => {
<DocumentCard onClickHref='#' className={styles.docContainer + ' ms-Grid-col ms-u-sm12 ms-u-md12 ms-u-lg' + chart.config.size} key={chart.key}> return (
<div className={styles.chartCard}> <DocumentCard
{this.chart(ChartOptions.Data(chart), ChartOptions.Options(), chart.config.type)} onClickHref="#"
</div> className={
<DocumentCardLocation location={chart.config.description} /> styles.docContainer +
<DocumentCardTitle title={chart.config.title} /> " ms-Grid-col ms-u-sm12 ms-u-md12 ms-u-lg" +
</DocumentCard> chart.config.size
); }
}); key={chart.key}
>
return ( <div className={styles.chartCard}>
<div className={styles.chartjs + ' ms-Grid'}> {this.chart(
<div className={'ms-Grid-row'}> ChartOptions.Data(chart),
{charts} ChartOptions.Options(),
</div> chart.config.type
<div style={{ clear: 'both' }} /> )}
</div> </div>
<DocumentCardLocation location={chart.config.description} />
<DocumentCardTitle title={chart.config.title} />
</DocumentCard>
);
}
); );
return (
<div className={styles.chartjs + " ms-Grid"}>
<div className={"ms-Grid-row"}>{charts}</div>
<div style={{ clear: "both" }} />
</div>
);
} }
public chart(data: Object, options: Object, type: string) { public chart(data: Object, options: Object, type: string) {
var tChart: any; var tChart: any;
switch (type) { switch (type) {
case 'doughnut': case "doughnut":
tChart = <Doughnut data={data} options={options} />; tChart = <Doughnut data={data} options={options} />;
return tChart; return tChart;
case 'line': case "line":
debugger; return (
return <Line data={data} options={options} legend={{ display: false }} />; <Line data={data} options={options} legend={{ display: false }} />
case 'pie': );
case "pie":
tChart = <Pie data={data} options={options} />; tChart = <Pie data={data} options={options} />;
return tChart; return tChart;
case 'bar': case "bar":
tChart = <Bar data={data} options={options} legend={{ display: false }} />; tChart = (
<Bar data={data} options={options} legend={{ display: false }} />
);
return tChart; return tChart;
case 'horizontalbar': case "horizontalbar":
tChart = <HorizontalBar data={data} options={options} legend={{ display: false }} />; tChart = (
<HorizontalBar
data={data}
options={options}
legend={{ display: false }}
/>
);
return tChart; return tChart;
case 'radar': case "radar":
tChart = <Radar data={data} options={options} legend={{ display: false }} />; tChart = (
<Radar data={data} options={options} legend={{ display: false }} />
);
return tChart; return tChart;
case 'polar': case "polar":
tChart = <Polar data={data} options={options} />; tChart = <Polar data={data} options={options} />;
return tChart; return tChart;
default: default:

View File

@ -1,6 +1,6 @@
{ {
"name": "react-my-task", "name": "react-my-task",
"version": "0.0.1", "version": "1.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -60,6 +60,8 @@ This extension illustrates the following concepts:
We do not support samples, but we do use GitHub to track issues and constantly want to improve these samples. 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-organization-chart)
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-organisation-chart&authors=@joaojmendes&title=react-organisation-chart%20-%20). 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-organisation-chart&authors=@joaojmendes&title=react-organisation-chart%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-organisation-chart&authors=@joaojmendes&title=react-organisation-chart%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-organisation-chart&authors=@joaojmendes&title=react-organisation-chart%20-%20).

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

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

View File

@ -18,7 +18,7 @@
"environment": "spo", "environment": "spo",
"framework": "react", "framework": "react",
"isCreatingSolution": true, "isCreatingSolution": true,
"version": "1.9.1", "version": "1.12.1",
"libraryName": "navigator", "libraryName": "navigator",
"libraryId": "065ee566-e00d-4058-bbfd-356c8d9a8005", "libraryId": "065ee566-e00d-4058-bbfd-356c8d9a8005",
"packageManager": "npm", "packageManager": "npm",

View File

@ -8,22 +8,29 @@ When added to a Vertical Section it can be used as a Contents table for the page
![Page Navigator](./assets/PageNavigator.gif) ![Page Navigator](./assets/PageNavigator.gif)
## Used SharePoint Framework Version ## Compatibility
![version](https://img.shields.io/badge/version-1.9.1-green.svg) ![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 "The solution requires access to the page structure")
![Hosted Workbench Partially](https://img.shields.io/badge/Hosted%20Workbench-Partially-yellow.svg "The solution needs to run on a hosted page to work as intended")
## Version history ## Version history
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0|September 5, 2019|Initial release 1.0|September 5, 2019|Initial release
1.1|October 20, 2021|SPFx Upgraded to 1.12.1 and code refactored
## Minimal Path to Awesome ## Minimal Path to Awesome
- git clone the repo - `git clone` the repo
- npm i - `npm i`
- gulp bundle --ship - `gulp bundle --ship`
- gulp package-solution --ship - `gulp package-solution --ship`
- Add the app package to Site Collection App Catalog and Install the App - Add the app package to Site Collection App Catalog and Install the App
- Add the web part to a page in the Site Collection - Add the web part to a page in the Site Collection
@ -31,10 +38,20 @@ Version|Date|Comments
Solution|Author(s) Solution|Author(s)
--------|--------- --------|---------
react-page-navigator|Aakash Bhardwaj react-page-navigator|[Aakash Bhardwaj](https://github.com/aakashbhardwaj619)
## Disclaimer ## 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.** **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.
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-page-navigator&authors=@aakashbhardwaj619&title=react-page-navigator%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-page-navigator&authors=@aakashbhardwaj619&title=react-page-navigator%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-page-navigator&authors=@aakashbhardwaj619&title=react-page-navigator%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-page-navigator" /> <img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-page-navigator" />

View File

@ -9,7 +9,7 @@
"This web part fetches all the automatically added Header anchor tags in a SharePoint page and displays them in a Navigation component." "This web part fetches all the automatically added Header anchor tags in a SharePoint page and displays them in a Navigation component."
], ],
"creationDateTime": "2019-09-05", "creationDateTime": "2019-09-05",
"updateDateTime": "2019-09-05", "updateDateTime": "2021-10-20",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"
@ -21,7 +21,7 @@
}, },
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.9.1" "value": "1.12.1"
} }
], ],
"thumbnails": [ "thumbnails": [

View File

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

View File

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

View File

@ -1,9 +1,16 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": { "solution": {
"developer": {
"name": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": ""
},
"name": "react-page-navigator", "name": "react-page-navigator",
"id": "065ee566-e00d-4058-bbfd-356c8d9a8005", "id": "065ee566-e00d-4058-bbfd-356c8d9a8005",
"version": "1.0.0.0", "version": "1.1.0.0",
"includeClientSideAssets": true, "includeClientSideAssets": true,
"isDomainIsolated": false "isDomainIsolated": false
}, },

View File

@ -25,5 +25,12 @@ gulp.task('dev', gulpSequence('clean', 'bundle', 'package-solution'));
* Custom Framework Specific gulp tasks * Custom Framework Specific gulp tasks
*/ */
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); build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -3,9 +3,6 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"main": "lib/index.js", "main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": { "scripts": {
"build": "gulp bundle", "build": "gulp bundle",
"clean": "gulp clean", "clean": "gulp clean",
@ -15,36 +12,35 @@
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll" "test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
}, },
"dependencies": { "dependencies": {
"@microsoft/sp-core-library": "1.9.1", "@microsoft/sp-core-library": "1.12.1",
"@microsoft/sp-lodash-subset": "1.9.1", "@microsoft/sp-lodash-subset": "1.12.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1", "@microsoft/sp-office-ui-fabric-core": "1.12.1",
"@microsoft/sp-webpart-base": "1.9.1", "@microsoft/sp-property-pane": "1.12.1",
"@microsoft/sp-webpart-base": "1.12.1",
"@pnp/pnpjs": "^1.3.5", "@pnp/pnpjs": "^1.3.5",
"@pnp/spfx-controls-react": "1.14.0", "@pnp/spfx-controls-react": "1.14.0",
"@pnp/spfx-property-controls": "1.16.0", "@pnp/spfx-property-controls": "1.16.0",
"@types/es6-promise": "0.0.33", "office-ui-fabric-react": "7.156.0",
"@types/react": "16.8.8", "react": "16.9.0",
"@types/react-dom": "16.8.3", "react-dom": "16.9.0"
"@types/webpack-env": "1.13.1",
"office-ui-fabric-react": "^6.182.0",
"react": "16.8.5",
"react-dom": "16.8.5"
}, },
"resolutions": { "resolutions": {
"@types/react": "16.8.8" "@types/react": "16.8.8"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/rush-stack-compiler-3.3": "0.1.7", "@microsoft/rush-stack-compiler-3.3": "0.1.7",
"@microsoft/sp-build-web": "1.9.1", "@microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/sp-module-interfaces": "1.9.1", "@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-tslint-rules": "1.9.1", "@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.9.1", "@microsoft/sp-tslint-rules": "1.12.1",
"@types/chai": "3.4.34", "@microsoft/sp-webpart-workbench": "1.12.1",
"@types/mocha": "2.2.38", "@types/es6-promise": "0.0.33",
"@types/react": "^16.7.22", "@types/react": "16.9.36",
"@types/react-dom": "16.9.8",
"@types/webpack-env": "1.13.1",
"@voitanos/jest-preset-spfx-react16": "^1.1.0", "@voitanos/jest-preset-spfx-react16": "^1.1.0",
"ajv": "~5.2.2", "ajv": "~5.2.2",
"gulp": "~3.9.1", "gulp": "4.0.2",
"gulp-sequence": "1.0.0", "gulp-sequence": "1.0.0",
"jest": "^23.6.0" "jest": "^23.6.0"
} }

View File

@ -3,18 +3,49 @@ import { WebPartContext } from '@microsoft/sp-webpart-base';
import { SPHttpClient } from '@microsoft/sp-http'; import { SPHttpClient } from '@microsoft/sp-http';
export class SPService { export class SPService {
/* Array to store all unique anchor URLs */
private static allUrls: string[] = [];
/**
* Returns the unique Anchor URL for a heading
* @param headingValue The text value of the heading
* @returns anchorUrl
*/
private static GetAnchorUrl(headingValue: string): string {
let urlExists = true;
// .replace(/'|?|\|/| |&/g, "-") replaces any blanks and special characters (list is for sure not complete) with "-"
// .replace(/--+/g, "-") replaces any additional - with only one -; e.g. --- get replaced with -, -- get replaced with - etc.
let anchorUrl = `#${headingValue
.replace(/\'|\?|\\|\/| |\&/g, "-")
.replace(/--+/g, "-")}`.toLowerCase();
let urlSuffix = 1;
while (urlExists === true) {
urlExists = (this.allUrls.indexOf(anchorUrl) === -1) ? false : true;
if (urlExists) {
anchorUrl = anchorUrl + `-${urlSuffix}`;
urlSuffix++;
}
}
return anchorUrl;
}
/**
* Returns the Anchor Links for Nav element
* @param context Web part context
* @returns anchorLinks
*/
public static async GetAnchorLinks(context: WebPartContext) { public static async GetAnchorLinks(context: WebPartContext) {
let anchorLinks: INavLink[] = []; const anchorLinks: INavLink[] = [];
try { try {
/* Page ID on which the web part is added */ /* Page ID on which the web part is added */
let pageId = context.pageContext.listItem.id; const pageId = context.pageContext.listItem.id;
/* Get the canvasContent1 data for the page which consists of all the HTML */ /* Get the canvasContent1 data for the page which consists of all the HTML */
let data = await context.spHttpClient.get(`${context.pageContext.web.absoluteUrl}/_api/sitepages/pages(${pageId})`, SPHttpClient.configurations.v1); const data = await context.spHttpClient.get(`${context.pageContext.web.absoluteUrl}/_api/sitepages/pages(${pageId})`, SPHttpClient.configurations.v1);
let jsonData = await data.json(); const jsonData = await data.json();
let canvasContent1 = jsonData.CanvasContent1; const canvasContent1 = jsonData.CanvasContent1;
let canvasContent1JSON: any[] = JSON.parse(canvasContent1); const canvasContent1JSON: any[] = JSON.parse(canvasContent1);
/* Initialize variables to be used for sorting and adding the Navigation links */ /* Initialize variables to be used for sorting and adding the Navigation links */
let headingIndex = 0; let headingIndex = 0;
@ -22,9 +53,6 @@ export class SPService {
let headingOrder = 0; let headingOrder = 0;
let prevHeadingOrder = 0; let prevHeadingOrder = 0;
/* Array to store all unique anchor URLs */
let allUrls: string[] = [];
/* Traverse through all the Text web parts in the page */ /* Traverse through all the Text web parts in the page */
canvasContent1JSON.map((webPart) => { canvasContent1JSON.map((webPart) => {
if (webPart.innerHTML) { if (webPart.innerHTML) {
@ -34,28 +62,14 @@ export class SPService {
/* The Header Text value */ /* The Header Text value */
// .replace(/<.+?>/gi, "") replaces in the headingValue any html tags like <strong> </strong> // .replace(/<.+?>/gi, "") replaces in the headingValue any html tags like <strong> </strong>
// .replace(/&.+;/gi, "") replaces in the headingValue any &****; tags like &nbsp; // .replace(/&.+;/gi, "") replaces in the headingValue any &****; tags like &nbsp;
let headingValue = HTMLString.substring(HTMLString.search(/<h[1-4]>/g) + 4, HTMLString.search(/<\/h[1-4]>/g)) const headingValue = HTMLString.substring(HTMLString.search(/<h[1-4]>/g) + 4, HTMLString.search(/<\/h[1-4]>/g))
.replace(/<.+?>/gi, "") .replace(/<.+?>/gi, "")
.replace(/\&.+\;/gi, ""); .replace(/\&.+\;/gi, "");
headingOrder = parseInt(HTMLString.charAt(HTMLString.search(/<h[1-4]>/g) + 2)); headingOrder = parseInt(HTMLString.charAt(HTMLString.search(/<h[1-4]>/g) + 2));
/* Check if same anchorUrl already exists */ const anchorUrl = this.GetAnchorUrl(headingValue);
let urlExists = true; this.allUrls.push(anchorUrl);
// .replace(/'|?|\|/| |&/g, "-") replaces any blanks and special characters (list is for sure not complete) with "-"
// .replace(/--+/g, "-") replaces any additional - with only one -; e.g. --- get replaced with -, -- get replaced with - etc.
let anchorUrl = `#${headingValue
.replace(/\'|\?|\\|\/| |\&/g, "-")
.replace(/--+/g, "-")}`.toLowerCase();
let urlSuffix = 1;
while (urlExists === true) {
urlExists = (allUrls.indexOf(anchorUrl) === -1) ? false : true;
if (urlExists) {
anchorUrl = anchorUrl + `-${urlSuffix}`;
urlSuffix++;
}
}
allUrls.push(anchorUrl);
/* Add links to Nav element */ /* Add links to Nav element */
if (anchorLinks.length === 0) { if (anchorLinks.length === 0) {
@ -63,33 +77,42 @@ export class SPService {
} else { } else {
if (headingOrder <= prevHeadingOrder) { if (headingOrder <= prevHeadingOrder) {
/* Adding or Promoting links */ /* Adding or Promoting links */
if (headingOrder === 2) { switch (headingOrder) {
anchorLinks.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); case 2:
headingIndex++; anchorLinks.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
subHeadingIndex = -1; headingIndex++;
} else { subHeadingIndex = -1;
if (headingOrder === 4) { break;
anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); case 4:
} else if (headingOrder === 3) { if (subHeadingIndex > -1) {
anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
} else {
anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
}
break;
default:
anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
subHeadingIndex++; subHeadingIndex = anchorLinks[headingIndex].links.length - 1;
} break;
} }
} else { } else {
/* Making sub links */ /* Making sub links */
if (headingOrder === 3) { if (headingOrder === 3) {
anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
subHeadingIndex++; subHeadingIndex = anchorLinks[headingIndex].links.length - 1;
} else { } else {
anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); if (subHeadingIndex > -1) {
anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
} else {
anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true });
}
} }
} }
} }
prevHeadingOrder = headingOrder; prevHeadingOrder = headingOrder;
/* Replace the added header links from the string so they don't get processed again */ /* Replace the added header links from the string so they don't get processed again */
HTMLString = HTMLString.replace(`<h${headingOrder}>`, ''); HTMLString = HTMLString.replace(`<h${headingOrder}>`, '').replace(`</h${headingOrder}>`, '');
HTMLString = HTMLString.replace(`</h${headingOrder}>`, '');
} }
} }
}); });
@ -97,7 +120,7 @@ export class SPService {
console.log(error); console.log(error);
} }
console.log(anchorLinks); console.log('anchorLinks', anchorLinks);
return anchorLinks; return anchorLinks;
} }
} }

View File

@ -1,12 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import * as ReactDom from 'react-dom'; import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library'; import { Version } from '@microsoft/sp-core-library';
import { import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
BaseClientSideWebPart, import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'PageNavigatorWebPartStrings'; import * as strings from 'PageNavigatorWebPartStrings';
import PageNavigator from './components/PageNavigator'; import PageNavigator from './components/PageNavigator';
import { IPageNavigatorProps } from './components/IPageNavigatorProps'; import { IPageNavigatorProps } from './components/IPageNavigatorProps';

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.7/includes/tsconfig-web.json",
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
@ -19,20 +19,18 @@
"./node_modules/@microsoft" "./node_modules/@microsoft"
], ],
"types": [ "types": [
"es6-promise",
"webpack-env" "webpack-env"
], ],
"lib": [ "lib": [
"es5", "es5",
"dom", "dom",
"es2015.collection" "es2015.collection",
"es2015.promise"
] ]
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts",
"src/**/*.tsx"
], ],
"exclude": [ "exclude": []
"node_modules",
"lib"
]
} }

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": { "rules": {
"class-name": false, "class-name": false,
"export-name": false, "export-name": false,

View File

@ -16,7 +16,8 @@ This SPFx Outlook Add-In lets users save any email attachments to a OneDrive fol
![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 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") ![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") ![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-yellow.svg "Designed to work in Outlook, but will display sample attachments while in workbench") ![Hosted Workbench Partially](https://img.shields.io/badge/Hosted%20Workbench-Partially-yellow.svg "Designed to work in Outlook, but will display sample attachments while in workbench")
![Outlook Compatible](https://img.shields.io/badge/Outlook-Compatible-green.svg)
## Features ## Features

View File

@ -165,7 +165,7 @@ In order to make it available to absolutely all sites you need apply the _Deploy
Solution|Author(s) Solution|Author(s)
--------|--------- --------|---------
react-script-editor | Mikael Svenson ([@mikaelsvenson](http://www.twitter.com/mikaelsvenson), [techmikael.com](techmikael.com)) react-script-editor | [Mikael Svenson](https://github.com/wobba) ([@mikaelsvenson](http://www.twitter.com/mikaelsvenson), [techmikael.com](techmikael.com))
## Version history ## Version history
@ -190,11 +190,6 @@ Version|Date|Comments
1.0.0.16|April 1st, 2020|Improved how script tags are handled and cleaned up on smart page navigation. 1.0.0.16|April 1st, 2020|Improved how script tags are handled and cleaned up on smart page navigation.
1.0.17.0|January 29th, 2021|Changed versioning to 3 parts. Updated npm packages, restructured documentation, minor change to webpack analyzer setup. 1.0.17.0|January 29th, 2021|Changed versioning to 3 parts. Updated npm packages, restructured documentation, minor change to webpack analyzer setup.
## 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 ## Minimal Path to Awesome
### Local testing ### Local testing
@ -204,10 +199,10 @@ Version|Date|Comments
- `gulp serve` - `gulp serve`
### Deploy ### Deploy
* gulp clean * `gulp clean`
* gulp bundle --ship * `gulp bundle --ship`
* gulp package-solution --ship * `gulp package-solution --ship`
* Upload .sppkg file from sharepoint\solution to your tenant App Catalog * Upload `.sppkg` file from `sharepoint\solution` to your tenant App Catalog
* E.g.: https://&lt;tenant&gt;.sharepoint.com/sites/AppCatalog/AppCatalog * E.g.: https://&lt;tenant&gt;.sharepoint.com/sites/AppCatalog/AppCatalog
* Add the web part to a site collection, and test it on a page * Add the web part to a site collection, and test it on a page
## Features ## Features
@ -217,4 +212,24 @@ This web part illustrates the following concepts on top of the SharePoint Framew
- Office UI Fabric - Office UI Fabric
- React - React
## 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-script-editor) 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-script-editor) 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-script-editor&authors=@wobba&title=react-script-editor%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-script-editor&authors=@wobba&title=react-script-editor%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-script-editor&authors=@wobba&title=react-script-editor%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-script-editor" /> <img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-script-editor" />

View File

@ -13,9 +13,11 @@ This is a rebuild of the awesome project created by @hugoabernier [https://githu
![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg) ![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) ![Node.js LTS 10.x](https://img.shields.io/badge/Node.js-LTS%2010.x-green.svg)
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg) ![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams") ![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")
![Workbench Local | Hosted](https://img.shields.io/badge/Workbench-Local%20%7C%20Hosted-green.svg) ![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
![Local Workbench Compatible](https://img.shields.io/badge/Local%20Workbench-Compatible-green.svg)
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
## Applies to ## Applies to
@ -37,14 +39,15 @@ react-tiles-v2 | [Omar El-Anis](https://github.com/omarelanis) @ SP Bytes www.sp
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0|July 14, 2021|Initial release 1.0|July 14, 2021|Initial release
1.1|October 14, 2021|Added sorting, static tile width and unique tile colour - [fthorild](https://github.com/fthorild)
## Minimal Path to Awesome ## Minimal Path to Awesome
- Clone this repository - Clone this repository
- Ensure that you are at the solution folder - Ensure that you are at the solution folder
- in the command-line run: - in the command-line run:
- **npm install** - `npm install`
- **gulp serve** - `gulp serve`
## Features ## Features
@ -75,10 +78,10 @@ This extension illustrates the following concepts:
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. 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.
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-tiles-v2&authors=@omarelanis&title=react-tiles-v2%20-%20). 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-tiles-v2&authors=fthorild%20@fthorild&title=react-tiles-v2%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-tiles-v2&authors=@omarelanis&title=react-tiles-v2%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-tiles-v2&authors=fthorild%20@fthorild&title=react-tiles-v2%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-tiles-v2&authors=@omarelanis&title=react-tiles-v2%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-tiles-v2&authors=fthorild%20@fthorild&title=react-tiles-v2%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-tiles-v2" /> <img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-tiles-v2" />

View File

@ -9,7 +9,7 @@
"This solution creates a customisable Tiles Web part, it uses a stored collection from the PnP `PropertyFieldCollectionData` control and allows the user to choose the colour scheme (theme or custom) and to set the size of the tiles. By default the tiles use a fluid flex layout to use the available screen area." "This solution creates a customisable Tiles Web part, it uses a stored collection from the PnP `PropertyFieldCollectionData` control and allows the user to choose the colour scheme (theme or custom) and to set the size of the tiles. By default the tiles use a fluid flex layout to use the available screen area."
], ],
"creationDateTime": "2021-07-14", "creationDateTime": "2021-07-14",
"updateDateTime": "2021-07-14", "updateDateTime": "2021-10-14",
"products": [ "products": [
"SharePoint", "SharePoint",
"Office" "Office"
@ -22,6 +22,10 @@
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.11" "value": "1.11"
},
{
"key": "PNPCONTROLS",
"value": "IconPicker, WebPartTitle, Placeholder, PropertyFieldCollectionData"
} }
], ],
"thumbnails": [ "thumbnails": [
@ -37,6 +41,12 @@
"gitHubAccount": "omarelanis", "gitHubAccount": "omarelanis",
"pictureUrl": "https://github.com/omarelanis.png", "pictureUrl": "https://github.com/omarelanis.png",
"name": "Omar El-Anis" "name": "Omar El-Anis"
},
{
"gitHubAccount": "fthorild",
"name": "Fredrik Thorild",
"company": "Sogeti Sweden",
"pictureUrl": "https://github.com/fthorild.png"
} }
], ],
"references": [ "references": [

View File

@ -3,7 +3,7 @@
"solution": { "solution": {
"name": "Tiles V2 Webpart - Modern", "name": "Tiles V2 Webpart - Modern",
"id": "d56958d6-9eaf-4500-934b-b421c22a7d1f", "id": "d56958d6-9eaf-4500-934b-b421c22a7d1f",
"version": "1.0.0.0", "version": "1.1.0.0",
"skipFeatureDeployment": true, "skipFeatureDeployment": true,
"includeClientSideAssets": true, "includeClientSideAssets": true,
"isDomainIsolated": false, "isDomainIsolated": false,

View File

@ -1,6 +1,6 @@
{ {
"name": "tiles", "name": "tiles",
"version": "0.0.1", "version": "1.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "tiles", "name": "tiles",
"version": "0.0.1", "version": "1.1.0",
"private": true, "private": true,
"main": "lib/index.js", "main": "lib/index.js",
"engines": { "engines": {

View File

@ -1,23 +1,26 @@
import * as React from 'react'; import * as React from 'react';
import * as ReactDom from 'react-dom'; import * as ReactDom from 'react-dom';
import * as strings from 'TilesWebPartStrings'; import * as strings from 'TilesWebPartStrings';
import { BaseClientSideWebPart} from '@microsoft/sp-webpart-base'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme} from '@microsoft/sp-component-base'; import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme } from '@microsoft/sp-component-base';
import { IPropertyPaneConfiguration} from "@microsoft/sp-property-pane"; import { IPropertyPaneConfiguration } from "@microsoft/sp-property-pane";
import { PropertyPaneToggle,PropertyPaneSlider } from '@microsoft/sp-property-pane'; import { PropertyPaneToggle, PropertyPaneSlider, PropertyPaneDropdown } from '@microsoft/sp-property-pane';
import { Tiles, ITilesProps, ITileInfo, LinkTarget } from './components'; import { Tiles, ITilesProps, ITileInfo, LinkTarget } from './components';
import { IconPicker } from '@pnp/spfx-controls-react/lib/IconPicker'; import { IconPicker } from '@pnp/spfx-controls-react/lib/IconPicker';
import { initializeIcons } from 'office-ui-fabric-react/lib'; import { ColorPicker, initializeIcons } from 'office-ui-fabric-react/lib';
import { SimpleColorPicker } from './components/colorpicker/SimpleColorPicker';
const ThemeColorsFromWindow: any = (window as any).__themeState__.theme; const ThemeColorsFromWindow: any = (window as any).__themeState__.theme;
export interface ITilesWebPartProps { export interface ITilesWebPartProps {
collectionData: ITileInfo[]; collectionData: ITileInfo[];
tileHeight: number; tileHeight: number;
tileWidth: number;
tileColour: string; tileColour: string;
tileFont: string; tileFont: string;
title: string; title: string;
customColour: boolean; staticWidth: boolean;
colourMode: string;
themeVariant: IReadonlyTheme | undefined; themeVariant: IReadonlyTheme | undefined;
ThemeColorsFromWindow: IReadonlyTheme | undefined; ThemeColorsFromWindow: IReadonlyTheme | undefined;
} }
@ -42,7 +45,7 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent); this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent);
if (this.context.sdks.microsoftTeams) if (this.context.sdks.microsoftTeams)
initializeIcons(); initializeIcons();
return super.onInit(); return super.onInit();
} }
@ -64,13 +67,15 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
{ {
title: this.properties.title, title: this.properties.title,
tileHeight: this.properties.tileHeight, tileHeight: this.properties.tileHeight,
tileWidth: this.properties.tileWidth,
tileColour: this.properties.tileColour, tileColour: this.properties.tileColour,
tileFont: this.properties.tileFont, tileFont: this.properties.tileFont,
customColour: this.properties.customColour, staticWidth: this.properties.staticWidth,
collectionData: this.properties.collectionData, collectionData: this.properties.collectionData,
displayMode: this.displayMode, displayMode: this.displayMode,
themeVariant: this._themeVariant, themeVariant: this._themeVariant,
ThemeColorsFromWindow: ThemeColorsFromWindow, ThemeColorsFromWindow: ThemeColorsFromWindow,
colourMode: this.properties.colourMode,
fUpdateProperty: (value: string) => { fUpdateProperty: (value: string) => {
this.properties.title = value; this.properties.title = value;
}, },
@ -83,16 +88,16 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
//executes only before property pane is loaded. //executes only before property pane is loaded.
protected async loadPropertyPaneResources(): Promise<void> { protected async loadPropertyPaneResources(): Promise<void> {
// import additional controls/components // import additional controls/components
const { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } = await import ( const { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } = await import(
/* webpackChunkName: 'pnp-propcontrols-number' */ /* webpackChunkName: 'pnp-propcontrols-number' */
'@pnp/spfx-property-controls/lib/PropertyFieldColorPicker' '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker'
); );
const { PropertyFieldNumber } = await import ( const { PropertyFieldNumber } = await import(
/* webpackChunkName: 'pnp-propcontrols-number' */ /* webpackChunkName: 'pnp-propcontrols-number' */
'@pnp/spfx-property-controls/lib/propertyFields/number' '@pnp/spfx-property-controls/lib/propertyFields/number'
); );
const { PropertyFieldCollectionData, CustomCollectionFieldType } = await import ( const { PropertyFieldCollectionData, CustomCollectionFieldType } = await import(
/* webpackChunkName: 'pnp-propcontrols-colldata' */ /* webpackChunkName: 'pnp-propcontrols-colldata' */
'@pnp/spfx-property-controls/lib/PropertyFieldCollectionData' '@pnp/spfx-property-controls/lib/PropertyFieldCollectionData'
); );
@ -107,7 +112,9 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
let tileColourplaceholder: any = []; let tileColourplaceholder: any = [];
let tileFontplaceholder: any = []; let tileFontplaceholder: any = [];
if (this.properties.customColour) { let tileStaticWidthplaceholder: any = [];
if (this.properties.colourMode === '2') {
tileColourplaceholder = this.propertyFieldColorPicker('tileColour', { tileColourplaceholder = this.propertyFieldColorPicker('tileColour', {
key: "tileColour", key: "tileColour",
label: strings.tileColour, label: strings.tileColour,
@ -120,19 +127,32 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
style: this.propertyFieldColorPickerStyle.Full, style: this.propertyFieldColorPickerStyle.Full,
iconName: 'Precipitation' iconName: 'Precipitation'
}); });
tileFontplaceholder=this.propertyFieldColorPicker('tileFont', { tileFontplaceholder = this.propertyFieldColorPicker('tileFont', {
key: "tileFont", key: "tileFont",
label: strings.tileFont, label: strings.tileFont,
selectedColor: this.properties.tileFont, selectedColor: this.properties.tileFont,
onPropertyChange: this.onPropertyPaneFieldChanged, onPropertyChange: this.onPropertyPaneFieldChanged,
properties: this.properties, properties: this.properties,
disabled: false, disabled: false,
isHidden: false, isHidden: false,
alphaSliderHidden: false, alphaSliderHidden: false,
style: this.propertyFieldColorPickerStyle.Full, style: this.propertyFieldColorPickerStyle.Full,
iconName: 'Precipitation' iconName: 'Precipitation'
}); });
}
}
if (this.properties.staticWidth) {
tileStaticWidthplaceholder = PropertyPaneSlider('tileWidth', {
label: strings.widthStaticSet,
max: 1000,
min: 10,
step: 1,
showValue: true,
value: this.properties.tileHeight
});
}
return { return {
pages: [ pages: [
{ {
@ -170,26 +190,64 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
required: true required: true
}, },
{ {
id: "icon", id: "sortOrder",
title: "Select Icon", title: strings.sortOrder,
type: this.customCollectionFieldType.number,
required: true
},
{
id: "background",
title: strings.colorSetUniqueBg,
type: this.customCollectionFieldType.custom, type: this.customCollectionFieldType.custom,
onCustomRender: (field, value, onUpdate, item, itemId, onError) => { onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
return ( return (
React.createElement(IconPicker , { React.createElement(SimpleColorPicker, {
key: itemId, key: itemId,
buttonLabel:"Select File", onChange: (colour: string) => {
onUpdate(field.id, colour);
return Event;
}
})
);
}
},
{
id: "foreground",
title: strings.colorSetUniqueFg,
type: this.customCollectionFieldType.custom,
onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
return (
React.createElement(SimpleColorPicker, {
key: itemId,
onChange: (colour: string) => {
onUpdate(field.id, colour);
return Event;
}
})
);
}
},
{
id: "icon",
title: strings.iconField,
type: this.customCollectionFieldType.custom,
onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
return (
React.createElement(IconPicker, {
key: itemId,
buttonLabel: strings.iconSelectFile,
onChange: (iconName: string) => { onChange: (iconName: string) => {
onUpdate(field.id, iconName); onUpdate(field.id, iconName);
return Event; return Event;
}, },
onSave: (iconName: string) => { onSave: (iconName: string) => {
onUpdate(field.id, iconName); onUpdate(field.id, iconName);
return Event; return Event;
} }
}) })
); );
} }
}, },
{ {
id: "target", id: "target",
title: strings.targetField, title: strings.targetField,
@ -207,24 +265,35 @@ export default class TilesWebPart extends BaseClientSideWebPart<ITilesWebPartPro
} }
] ]
}) })
]}, ]
{ },
groupName: "Tile Settings", {
groupFields: [ groupName: "Tile Settings",
groupFields: [
PropertyPaneSlider('tileHeight', { PropertyPaneSlider('tileHeight', {
label: 'Tile Height', label: strings.tilesHeight,
max: 300, max: 300,
min: 120, min: 120,
step: 1, step: 1,
showValue: true, showValue: true,
value: this.properties.tileHeight value: this.properties.tileHeight
}), }),
PropertyPaneToggle('customColour', { PropertyPaneToggle('staticWidth', {
key: 'customColourID', key: 'staticWidthID',
label: 'Theme or Custom colours', label: strings.widthAutomaticOrStatic,
onText: 'Custom Colours', onText: strings.widthStatic,
offText: 'Theme Colours', offText: strings.widthAutomatic,
checked: this.properties.customColour checked: this.properties.staticWidth
}),
tileStaticWidthplaceholder,
PropertyPaneDropdown('colourMode', {
label: strings.colourMode,
options: [
{ key: '1', text: strings.colourModeTheme },
{ key: '2', text: strings.colourModeUniform },
{ key: '3', text: strings.colourModeUnique }
],
selectedKey: '1',
}), }),
tileColourplaceholder, tileColourplaceholder,
tileFontplaceholder tileFontplaceholder

View File

@ -4,6 +4,9 @@ export interface ITileInfo {
url: string; url: string;
icon: string; icon: string;
target: LinkTarget; target: LinkTarget;
sortOrder: string;
foreground: string;
background: string;
} }
export enum LinkTarget { export enum LinkTarget {

View File

@ -1,11 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import * as strings from 'TilesWebPartStrings'; import * as strings from 'TilesWebPartStrings';
import styles from './Tiles.module.scss'; import styles from './Tiles.module.scss';
import { ITilesProps } from '.'; import { ITileInfo, ITilesProps } from '.';
import { Tile } from './tile'; import { Tile } from './tile';
import { WebPartTitle } from '@pnp/spfx-controls-react/lib/WebPartTitle'; import { WebPartTitle } from '@pnp/spfx-controls-react/lib/WebPartTitle';
import { Placeholder } from '@pnp/spfx-controls-react/lib/Placeholder'; import { Placeholder } from '@pnp/spfx-controls-react/lib/Placeholder';
import {createstyles } from './ThemeVariantTypeStyle.styles'; import { createstyles } from './ThemeVariantTypeStyle.styles';
import { css } from "@uifabric/utilities/lib/css"; import { css } from "@uifabric/utilities/lib/css";
export class Tiles extends React.Component<ITilesProps, {}> { export class Tiles extends React.Component<ITilesProps, {}> {
@ -15,25 +15,34 @@ export class Tiles extends React.Component<ITilesProps, {}> {
*/ */
public render(): React.ReactElement<ITilesProps> { public render(): React.ReactElement<ITilesProps> {
const CustomStyles = createstyles(this.props.themeVariant); const CustomStyles = createstyles(this.props.themeVariant);
return ( return (
<div className={css(styles.tiles, CustomStyles.root)}> <div className={css(styles.tiles, CustomStyles.root)
}>
<WebPartTitle displayMode={this.props.displayMode} <WebPartTitle displayMode={this.props.displayMode}
title={this.props.title} title={this.props.title}
updateProperty={this.props.fUpdateProperty} /> updateProperty={this.props.fUpdateProperty} />
{ {
this.props.collectionData && this.props.collectionData.length > 0 ? ( this.props.collectionData && this.props.collectionData.length > 0 ? (
<div className={styles.tilesList}> <div className={styles.tilesList}>
{ {
this.props.collectionData.map((tile, idx) => this.props.collectionData
<Tile key={idx} .sort((a: ITileInfo, b: ITileInfo) =>
parseInt(a.sortOrder) > parseInt(b.sortOrder) ?
1 : ((parseInt(b.sortOrder) > parseInt(a.sortOrder) ? -1 : 0)))
.map((tile, idx) =>
<Tile key={idx}
item={tile} item={tile}
tileHeight={this.props.tileHeight} tileHeight={this.props.tileHeight}
tileWidth={this.props.tileWidth}
tileColour={this.props.tileColour} tileColour={this.props.tileColour}
tileFont={this.props.tileFont} tileFont={this.props.tileFont}
customColour={this.props.customColour} staticWidth={this.props.staticWidth}
colourMode={this.props.colourMode}
themeVariant={this.props.themeVariant} themeVariant={this.props.themeVariant}
ThemeColorsFromWindow={this.props.ThemeColorsFromWindow}/>) ThemeColorsFromWindow={this.props.ThemeColorsFromWindow} />)
} }
</div> </div>
) : ( ) : (
@ -45,7 +54,7 @@ export class Tiles extends React.Component<ITilesProps, {}> {
onConfigure={this.props.fPropertyPaneOpen} /> onConfigure={this.props.fPropertyPaneOpen} />
) )
} }
</div> </div >
); );
} }
} }

View File

@ -0,0 +1,3 @@
export interface ISimpleColorPickerProps {
onChange(iconName: any): void;
}

View File

@ -0,0 +1,3 @@
export interface ISimpleColorPickerState {
val: string;
}

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import { ISimpleColorPickerProps } from './ISimpleColorPickerProps';
import { ISimpleColorPickerState } from './ISimpleColorPickerState';
export class SimpleColorPicker extends React.Component<ISimpleColorPickerProps, ISimpleColorPickerState> {
constructor(props) {
super(props);
this.state = { val: '#efefef' };
}
private handleChange = event => {
const value = event.target.value;
(async () => {
await this.setState({ val: value });
this.props.onChange(this.state.val);
})();
}
public render(): React.ReactElement<ISimpleColorPickerProps> {
return (
<div>
<input
onChange={this.handleChange}
type='color'
value={this.state.val}
/>
</div>
);
}
}

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