mirror of
https://github.com/pnp/sp-dev-fx-webparts.git
synced 2025-02-18 19:07:12 +00:00
Add 'samples/react-feedback/' from commit 'f9606eeed86b7a180e44f69e992f99d98b79094f'
git-subtree-dir: samples/react-feedback git-subtree-mainline: 1b413fddabfc9681ed79daf1895538934affc4bd git-subtree-split: f9606eeed86b7a180e44f69e992f99d98b79094f
This commit is contained in:
commit
3812eb20a6
25
samples/react-feedback/.editorconfig
Normal file
25
samples/react-feedback/.editorconfig
Normal file
@ -0,0 +1,25 @@
|
||||
# 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
|
32
samples/react-feedback/.gitignore
vendored
Normal file
32
samples/react-feedback/.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
5
samples/react-feedback/.vscode/extensions.json
vendored
Normal file
5
samples/react-feedback/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"msjsdiag.debugger-for-chrome"
|
||||
]
|
||||
}
|
43
samples/react-feedback/.vscode/launch.json
vendored
Normal file
43
samples/react-feedback/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
/**
|
||||
* Install Chrome Debugger Extension for Visual Studio Code to debug your components with the
|
||||
* Chrome browser: https://aka.ms/spfx-debugger-extensions
|
||||
*/
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"name": "Local workbench",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "https://localhost:4321/temp/workbench.html",
|
||||
"webRoot": "${workspaceRoot}",
|
||||
"sourceMaps": true,
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack:///.././src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../../src/*": "${webRoot}/src/*",
|
||||
"webpack:///../../../../../src/*": "${webRoot}/src/*"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"--remote-debugging-port=9222"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Hosted workbench",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "https://enter-your-SharePoint-site/_layouts/workbench.aspx",
|
||||
"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",
|
||||
"-incognito"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
13
samples/react-feedback/.vscode/settings.json
vendored
Normal file
13
samples/react-feedback/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
// Configure glob patterns for excluding files and folders in the file explorer.
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/bower_components": true,
|
||||
"**/coverage": true,
|
||||
"**/lib-amd": true,
|
||||
"src/**/*.scss.ts": true
|
||||
},
|
||||
"typescript.tsdk": ".\\node_modules\\typescript\\lib"
|
||||
}
|
12
samples/react-feedback/.yo-rc.json
Normal file
12
samples/react-feedback/.yo-rc.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.11.0",
|
||||
"libraryName": "feedback-webpart",
|
||||
"libraryId": "d5e255e1-7071-49a8-b50d-b06c80e4ac02",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
66
samples/react-feedback/README.md
Normal file
66
samples/react-feedback/README.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Feedback
|
||||
|
||||
## Summary
|
||||
|
||||
This is an application that supports Feedback through a web part that can be used directly on a Modern Sharepoint Site page. This webpart can be added to any site page or article. This allows users to send categorized feedback via email to users in the "Feedback Owners" group.
|
||||
|
||||

|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||

|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
|
||||
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Office 365 subscription with SharePoint Online
|
||||
* SharePoint Framework [development environment](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) set up
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-feedback | Perry Kankam
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|December 15, 2020|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.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
*To really get the full experience go to the workbench on a SharePoint Site [Your site url]/_layouts/15/workbench.aspx and that's where the magic will happen but this requires that you deploy and activate features to provision the required SharePoint assets*
|
||||
|
||||
* Clone this repository
|
||||
* in the command line run:
|
||||
* `npm install`
|
||||
* `gulp serve`
|
||||
* Add "Feedback Owners" Sharepoint group. This is where you'll add all users who should receive this feedback.
|
||||
* Run one of the following custom commands to clean, build, bundle and package the solution.
|
||||
* If you want to be able to debug using your local code using gulp serve
|
||||
`gulp package`
|
||||
* Navigate to the output `feedback-webpart.sppkg` (found in the `/sharepoint/solution` folder)
|
||||
* Upload it to an application catalog (either a tenant or site collection one)
|
||||
* In your site collection go to **Site Contents** and click **New** > **App**
|
||||
* Find and add the **Feedback Application** App
|
||||
* wait for it to finish installing and activating features on the **Site Contents** page
|
||||
* Go to a site page like home, edit the page and find and add the **Feedback** web part
|
||||
* If you deployed a shippable (SharePoint Online) version you don't need to do anything else
|
||||
* If you deployed a debug (http://localhost:4321) version you'll need to ensure gulp serve is running
|
||||
|
||||
## Features
|
||||
This sample illustrates the following concepts:
|
||||
- Used [@pnp/polyfill-ie11](https://pnp.github.io/pnpjs/concepts/polyfill/)
|
||||
- Used [PnP](https://pnp.github.io/pnpjs/) for communication with SharePoint.
|
||||
- Used [@pnp/logging](https://pnp.github.io/pnpjs/logging/)
|
BIN
samples/react-feedback/assets/feedbackwebpart.gif
Normal file
BIN
samples/react-feedback/assets/feedbackwebpart.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 962 KiB |
18
samples/react-feedback/config/config.json
Normal file
18
samples/react-feedback/config/config.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"feedback-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/feedback/FeedbackWebPart.js",
|
||||
"manifest": "./src/webparts/feedback/FeedbackWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"FeedbackWebPartStrings": "lib/webparts/feedback/loc/{locale}.js"
|
||||
}
|
||||
}
|
4
samples/react-feedback/config/copy-assets.json
Normal file
4
samples/react-feedback/config/copy-assets.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
7
samples/react-feedback/config/deploy-azure-storage.json
Normal file
7
samples/react-feedback/config/deploy-azure-storage.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "feedback-webpart",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
20
samples/react-feedback/config/package-solution.json
Normal file
20
samples/react-feedback/config/package-solution.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "feedback-webpart-client-side-solution",
|
||||
"id": "d5e255e1-7071-49a8-b50d-b06c80e4ac02",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": ""
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/feedback-webpart.sppkg"
|
||||
}
|
||||
}
|
10
samples/react-feedback/config/serve.json
Normal file
10
samples/react-feedback/config/serve.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
4
samples/react-feedback/config/write-manifests.json
Normal file
4
samples/react-feedback/config/write-manifests.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
12
samples/react-feedback/gulpfile.js
vendored
Normal file
12
samples/react-feedback/gulpfile.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
build.initialize(gulp);
|
||||
|
||||
var runSequence = require('run-sequence');
|
||||
gulp.task('package', function (cb) {
|
||||
runSequence('clean', 'build', 'bundle', 'package-solution', cb);
|
||||
});
|
18544
samples/react-feedback/package-lock.json
generated
Normal file
18544
samples/react-feedback/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
samples/react-feedback/package.json
Normal file
37
samples/react-feedback/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "feedback-webpart",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.11.0",
|
||||
"@microsoft/sp-lodash-subset": "1.11.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
|
||||
"@microsoft/sp-property-pane": "1.11.0",
|
||||
"@microsoft/sp-webpart-base": "1.11.0",
|
||||
"@pnp/polyfill-ie11": "^2.0.2",
|
||||
"@pnp/sp": "^2.0.13",
|
||||
"run-sequence": "^2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.11.0",
|
||||
"@microsoft/sp-tslint-rules": "1.11.0",
|
||||
"@microsoft/sp-module-interfaces": "1.11.0",
|
||||
"@microsoft/sp-webpart-workbench": "1.11.0",
|
||||
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@types/es6-promise": "0.0.33"
|
||||
}
|
||||
}
|
1
samples/react-feedback/src/index.ts
Normal file
1
samples/react-feedback/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
4
samples/react-feedback/src/models/IArticleInfo.ts
Normal file
4
samples/react-feedback/src/models/IArticleInfo.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IArticleInfo {
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
46
samples/react-feedback/src/services/base.service.ts
Normal file
46
samples/react-feedback/src/services/base.service.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { HttpRequestError } from '@pnp/odata';
|
||||
import { LogHelper } from "../utilities";
|
||||
|
||||
|
||||
export class BaseService {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public handleHttpError(methodName: string, error: HttpRequestError): void {
|
||||
this.logError(methodName, error);
|
||||
}
|
||||
|
||||
public logError(methodName: string, error: Error) {
|
||||
LogHelper.exception(this.constructor.name, methodName, error);
|
||||
}
|
||||
|
||||
public logPnpError(methodName: string, error: HttpRequestError | any): string {
|
||||
let msg: string;
|
||||
if (error instanceof HttpRequestError) {
|
||||
if (error.message) {
|
||||
msg = error.message;
|
||||
LogHelper.error(this.constructor.name, methodName, msg);
|
||||
}
|
||||
else {
|
||||
LogHelper.exception(this.constructor.name, methodName, error);
|
||||
}
|
||||
}
|
||||
else if (error.data != null && error.data.responseBody && error.data.responseBody.error && error.data.responseBody.error.message) {
|
||||
// for email exceptions they weren't coming in as "instanceof HttpRequestError"
|
||||
msg = error.data.responseBody.error.message.value;
|
||||
LogHelper.error(this.constructor.name, methodName, msg);
|
||||
}
|
||||
else if (error instanceof Error) {
|
||||
if (error.message.indexOf('[412] Precondition Failed') !== -1) {
|
||||
msg = 'Save Conflict. Your changes conflict with those made concurrently by another user. If you want your changes to be applied, resubmit your changes.';
|
||||
LogHelper.error(this.constructor.name, methodName, msg);
|
||||
}
|
||||
else if (error.message !== 'Unexpected token < in JSON at position 0') {
|
||||
// 'Unexpected token < in JSON at position 0' will be thrown if XML file is read; this was issue in MDF project
|
||||
msg = error.message;
|
||||
LogHelper.error(this.constructor.name, methodName, msg);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
99
samples/react-feedback/src/services/feedback.service.ts
Normal file
99
samples/react-feedback/src/services/feedback.service.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { sp } from '@pnp/sp/presets/all';
|
||||
import { IPrincipalInfo } from "@pnp/sp";
|
||||
import "@pnp/sp/sputilities";
|
||||
import "@pnp/polyfill-ie11";
|
||||
import { IEmailProperties } from "@pnp/sp/sputilities";
|
||||
import "@pnp/sp/webs";
|
||||
import "@pnp/sp/site-users/web";
|
||||
import "@pnp/sp/lists";
|
||||
import "@pnp/sp/items";
|
||||
import "@pnp/sp/site-groups/web";
|
||||
// import { EmailProperties } from '@pnp/sp';
|
||||
import { BaseService } from './base.service';
|
||||
import { LogHelper } from '../utilities';
|
||||
import { IArticleInfo } from '../models/IArticleInfo';
|
||||
|
||||
export class FeedbackService extends BaseService {
|
||||
|
||||
public async getTitle(listitemid): Promise<any> {
|
||||
var item = await sp.web.lists.getByTitle("Site Pages").items.getById(listitemid).select("Title").get();
|
||||
|
||||
var itemTitle = item["Title"];
|
||||
return itemTitle;
|
||||
}
|
||||
|
||||
public async getArticleInfo(listitemid): Promise<IArticleInfo> {
|
||||
var item = await sp.web.lists.getByTitle("Site Pages").items.getById(listitemid).select("Title", "EncodedAbsUrl").get();
|
||||
|
||||
let articleInfo: IArticleInfo = {
|
||||
title: item["Title"],
|
||||
url: item["EncodedAbsUrl"]
|
||||
};
|
||||
|
||||
return articleInfo;
|
||||
}
|
||||
|
||||
public async sendEmailToFeedbackSender(articleInfo: IArticleInfo, feedback, listitemid, currentUserEmail): Promise<any> {
|
||||
|
||||
if (feedback.indexOf("\n") > -1) {
|
||||
feedback = feedback.replace(/\n/g, '<br/>');
|
||||
}
|
||||
|
||||
if (currentUserEmail) {
|
||||
console.log("Email sending to User: " + currentUserEmail);
|
||||
const emailProps: IEmailProperties = {
|
||||
To: [currentUserEmail],
|
||||
Subject: (articleInfo.title == "Home" || listitemid == 1) ? "Feedback has been provided on the Homepage" : "Feedback for " + articleInfo.title,
|
||||
Body: (articleInfo.title == "Home" || listitemid == 1) ? "The feedback you provided on the Homepage has successfully been submitted.</br></br>\"" + feedback + "\"<br/>"
|
||||
: "The feedback you provided on \"" + articleInfo.title + "\" has been successfully submitted.</br></br>\"" + feedback + "\"<br/><br>Article can be found here: <a href=\"" + articleInfo.url + "\">" + articleInfo.url + "</a></br>"
|
||||
};
|
||||
await sp.utility.sendEmail(emailProps)
|
||||
.catch(e => {
|
||||
super.handleHttpError('sendEmail', e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public async sendEmailToOwnerGroup(feedback, listitemid, category, currentUserName, currentUserEmail) {
|
||||
let articleInfo = await this.getArticleInfo(listitemid);
|
||||
|
||||
let principals: IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals(["Feedback Owners"]);
|
||||
|
||||
if (feedback.indexOf("\n") > -1) {
|
||||
feedback = feedback.replace(/\n/g, '<br/>');
|
||||
}
|
||||
|
||||
var emails: string[] = [];
|
||||
for (var i = 0; i < principals.length; i++) {
|
||||
if (principals[i].Email) {
|
||||
emails.push(principals[i].Email);
|
||||
}
|
||||
else {
|
||||
LogHelper.warning("FeedbackService", "sendEmailToOwnerGroup", `No email for ${principals[i].LoginName}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Owner Emails: " + emails.join(";"));
|
||||
|
||||
if (emails && emails.length > 0) {
|
||||
const emailProps: IEmailProperties = {
|
||||
To: emails,
|
||||
Subject: (articleInfo.title == "Home") ? "Feedback has been provided on the Homepage (" + category + ")" : "Feedback for " + articleInfo.title + " (" + category + ")",
|
||||
Body: (articleInfo.title == "Home" || listitemid == 1) ? "\"" + feedback + "\"<br/><br/>Submitted by: " + currentUserName + " <a href=\"mailto:" + currentUserEmail + "\">" + currentUserEmail + "</a><br/>"
|
||||
: "\"" + feedback + "\"<br/><br/>Submitted by: " + currentUserName + " <a href=\"mailto:" + currentUserEmail + "\">" + currentUserEmail + "</a><<br/><br/>Article can be found here: <a href=\"" + articleInfo.url + "\">" + articleInfo.url + "</a>"
|
||||
};
|
||||
|
||||
await sp.utility.sendEmail(emailProps)
|
||||
.catch(e => {
|
||||
super.handleHttpError('sendEmail', e);
|
||||
throw e;
|
||||
});
|
||||
LogHelper.info("FeedbackService", "sendEmailToOwnerGroup", `Email Sent`);
|
||||
}
|
||||
|
||||
this.sendEmailToFeedbackSender(articleInfo, feedback, listitemid, currentUserEmail);
|
||||
}
|
||||
|
||||
}
|
36
samples/react-feedback/src/utilities/LogHelper.ts
Normal file
36
samples/react-feedback/src/utilities/LogHelper.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Logger, LogLevel } from "@pnp/logging";
|
||||
|
||||
export class LogHelper {
|
||||
|
||||
public static verbose(className: string, methodName: string, message: string) {
|
||||
message = this.formatMessage(className, methodName, message);
|
||||
Logger.write(message, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
public static info(className: string, methodName: string, message: string) {
|
||||
message = this.formatMessage(className, methodName, message);
|
||||
Logger.write(message, LogLevel.Info);
|
||||
}
|
||||
|
||||
public static warning(className: string, methodName: string, message: string) {
|
||||
message = this.formatMessage(className, methodName, message);
|
||||
Logger.write(message, LogLevel.Warning);
|
||||
}
|
||||
|
||||
public static error(className: string, methodName: string, message: string) {
|
||||
message = this.formatMessage(className, methodName, message);
|
||||
Logger.write(message, LogLevel.Error);
|
||||
}
|
||||
|
||||
public static exception(className: string, methodName: string, error: Error) {
|
||||
error.message = this.formatMessage(className, methodName, error.message);
|
||||
Logger.error(error);
|
||||
}
|
||||
|
||||
private static formatMessage(className: string, methodName: string, message: string): string {
|
||||
let d = new Date();
|
||||
let dateStr = d.getDate() + '-' + (d.getMonth() + 1) + '-' + d.getFullYear() + ' ' +
|
||||
d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + '.' + d.getMilliseconds();
|
||||
return `${dateStr} ${className} > ${methodName} > ${message}`;
|
||||
}
|
||||
}
|
51
samples/react-feedback/src/utilities/_variables.scss
Normal file
51
samples/react-feedback/src/utilities/_variables.scss
Normal file
@ -0,0 +1,51 @@
|
||||
$ms-greenLight : "[theme:greenLight, default:#bad80a]";
|
||||
$ms-neutralSecondaryAlt : "[theme:info, default:#767676]";
|
||||
$ms-neutralLight : "[theme:infoBackground, default:#eaeaea]";
|
||||
$ms-magenta : "[theme:magenta, default:#b4009e]";
|
||||
$ms-magentaDark : "[theme:magentaDark, default:#5c005c]";
|
||||
$ms-magentaLight : "[theme:magentaLight, default:#e3008c]";
|
||||
$ms-neutralDark : "[theme:neutralDark, default:#212121]";
|
||||
$ms-neutralLight : "[theme:neutralLight, default:#eaeaea]";
|
||||
$ms-neutralLighter : "[theme:neutralLighter, default:#f4f4f4]";
|
||||
$ms-neutralLighterAlt : "[theme:neutralLighterAlt, default:#f8f8f8]";
|
||||
$ms-neutralPrimary : "[theme:neutralPrimary, default:#333333]";
|
||||
$ms-neutralPrimaryAlt : "[theme:neutralPrimaryAlt, default:#3C3C3C]";
|
||||
$ms-neutralQuaternary : "[theme:neutralPrimaryTranslucent50, default:#d0d0d0]";
|
||||
$ms-neutralQuaternaryAlt : "[theme:neutralQuaternary, default:#dadada]";
|
||||
$ms-neutralSecondary : "[theme:neutralQuaternaryAlt, default:#666666]";
|
||||
$ms-neutralSecondaryAlt : "[theme:neutralSecondary, default:#767676]";
|
||||
$ms-neutralTertiary : "[theme:neutralSecondaryAlt, default:#a6a6a6]";
|
||||
$ms-neutralTertiaryAlt : "[theme:neutralTertiary, default:#c8c8c8]";
|
||||
$ms-white : "[theme:neutralTertiaryAlt, default:#ffffff]";
|
||||
$ms-orange : "[theme:orange, default:#d83b01]";
|
||||
$ms-orangeLight : "[theme:orangeLight, default:#ea4300]";
|
||||
$ms-orangeLighter : "[theme:orangeLighter, default:#ff8c00]";
|
||||
$ms-primaryBackground : "[theme:primaryBackground, default:#0078d7]";
|
||||
$ms-primaryText : "[theme:primaryText, default:#0078d7]";
|
||||
$ms-purple : "[theme:purple, default:#5c2d91]";
|
||||
$ms-purpleDark : "[theme:purpleDark, default:#32145a]";
|
||||
$ms-purpleLight : "[theme:purpleLight, default:#b4a0ff]";
|
||||
$ms-red : "[theme:red, default:#e81123]";
|
||||
$ms-redDark : "[theme:redDark, default:#a80000]";
|
||||
$ms-success : "[theme:success, default:#107c10]";
|
||||
$ms-successBackground : "[theme:successBackground, default:#dff6dd]";
|
||||
$ms-teal : "[theme:teal, default:#008272]";
|
||||
$ms-tealDark : "[theme:tealDark, default:#004b50]";
|
||||
$ms-tealLight : "[theme:tealLight, default:#00b294]";
|
||||
$ms-themeAccent : "[theme:themeAccent, default:inherit]";
|
||||
$ms-themeAccentTranslucent10 : "[theme:themeAccentTranslucent10, default:inherit]";
|
||||
$ms-themeDark : "[theme:themeDark, default:#005a9e]";
|
||||
$ms-themeDarkAlt : "[theme:themeDarkAlt, default:#106ebe]";
|
||||
$ms-themeDarker : "[theme:themeDarker, default:#004578]";
|
||||
$ms-themeLight : "[theme:themeLight, default:#b3d6f2]";
|
||||
$ms-themeLightAlt : "[theme:themeLightAlt, default:inherit]";
|
||||
$ms-themeLighter : "[theme:themeLighter, default:#deecf9]";
|
||||
$ms-themeLighterAlt : "[theme:themeLighterAlt, default:#eff6fc]";
|
||||
$ms-themePrimary : "[theme:themePrimary, default:#0078d7]";
|
||||
$ms-themeSecondary : "[theme:themeSecondary, default:#2488d8]";
|
||||
$ms-themeTertiary : "[theme:themeTertiary, default:#69afe5]";
|
||||
$ms-themeTertiaryAlt : "[theme:themeTertiaryAlt, default:#c8c8c8]";
|
||||
$ms-white : "[theme:white, default:#ffffff]";
|
||||
$ms-whiteTranslucent40 : "[theme:whiteTranslucent40, default:rgba(255,255,255,.4)]";
|
||||
$ms-yellow : "[theme:yellow, default:#ffb900]";
|
||||
$ms-yellowLight : "[theme:yellowLight, default:#fff100]";
|
1
samples/react-feedback/src/utilities/index.ts
Normal file
1
samples/react-feedback/src/utilities/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './LogHelper';
|
25
samples/react-feedback/src/webparts/BaseWebPart.ts
Normal file
25
samples/react-feedback/src/webparts/BaseWebPart.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import { sp } from "@pnp/sp";
|
||||
import { Logger, ConsoleListener, LogLevel } from "@pnp/logging";
|
||||
|
||||
export default class BaseWebPart<TProperties> extends BaseClientSideWebPart<TProperties> {
|
||||
|
||||
protected async onInit(): Promise<void> {
|
||||
return super.onInit().then(_ => {
|
||||
sp.setup({
|
||||
ie11: true,
|
||||
spfxContext: this.context,
|
||||
});
|
||||
|
||||
// subscribe a listener
|
||||
Logger.subscribe(new ConsoleListener());
|
||||
|
||||
// set the active log level -- eventually make this a web part property
|
||||
Logger.activeLogLevel = LogLevel.Error;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
export class DropdownOptions {
|
||||
public static Options = [
|
||||
{
|
||||
key: "general",
|
||||
text: "General"
|
||||
},
|
||||
{
|
||||
key: "typo",
|
||||
text: "Typo/Edit/Broken Link"
|
||||
},
|
||||
{
|
||||
key: "suggestion",
|
||||
text: "Suggestion"
|
||||
}
|
||||
];
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "58f4aa4b-7256-42d7-880c-ba4f51d3f6ab",
|
||||
"alias": "FeedbackWebPart",
|
||||
"componentType": "WebPart",
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": [
|
||||
"SharePointWebPart"
|
||||
],
|
||||
"preconfiguredEntries": [
|
||||
{
|
||||
"groupId": "75e22ed5-fa14-4829-850a-c890608aca2d",
|
||||
"group": {
|
||||
"default": "Communication and collaboration"
|
||||
},
|
||||
"title": {
|
||||
"default": "Feedback"
|
||||
},
|
||||
"description": {
|
||||
"default": "Feedback webpart to submit feedback on pages/articles."
|
||||
},
|
||||
"officeFabricIconFontName": "Feedback",
|
||||
"properties": {
|
||||
"buttonLabel": "Submit Feedback",
|
||||
"feedbackCategory": "general",
|
||||
"showCategory": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
|
||||
.feedback {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
111
samples/react-feedback/src/webparts/feedback/FeedbackWebPart.ts
Normal file
111
samples/react-feedback/src/webparts/feedback/FeedbackWebPart.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import "@pnp/polyfill-ie11";
|
||||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { ThemeProvider, IReadonlyTheme, ThemeChangedEventArgs } from '@microsoft/sp-component-base';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneDropdown,
|
||||
PropertyPaneLabel,
|
||||
PropertyPaneToggle
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import styles from './FeedbackWebPart.module.scss';
|
||||
import * as strings from 'FeedbackWebPartStrings';
|
||||
import BaseWebPart from '../BaseWebPart';
|
||||
import {Container} from './components/Container/Container';
|
||||
import {IContainerProps} from './components/Container/IContainerProps';
|
||||
import {DropdownOptions} from './DropdownOptions';
|
||||
|
||||
export interface IFeedbackWebPartProps {
|
||||
buttonLabel: string;
|
||||
feedbackCategory: string;
|
||||
showCategory: boolean;
|
||||
}
|
||||
|
||||
|
||||
export default class FeedbackWebPart extends BaseWebPart<IFeedbackWebPartProps> {
|
||||
|
||||
private themeProvider: ThemeProvider;
|
||||
private themeVariant: IReadonlyTheme | undefined;
|
||||
|
||||
protected onInit(): Promise<void> {
|
||||
// Consume the new ThemeProvider service
|
||||
this.themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);
|
||||
|
||||
// If it exists, get the theme variant
|
||||
this.themeVariant = this.themeProvider.tryGetTheme();
|
||||
|
||||
// Register a handler to be notified if the theme variant changes
|
||||
this.themeProvider.themeChangedEvent.add(this, this.handleThemeChangedEvent);
|
||||
return super.onInit();
|
||||
}
|
||||
|
||||
public async render(): Promise<void> {
|
||||
var showCategory = escape(this.properties.showCategory.toString()) == "false" ? false : true;
|
||||
const element: React.ReactElement<IContainerProps> = React.createElement(
|
||||
Container,
|
||||
{
|
||||
buttonLabel: escape(this.properties.buttonLabel),
|
||||
showCategory: showCategory,
|
||||
themeVariant: this.themeVariant,
|
||||
listitemid: this.context.pageContext.listItem.id, //Replace with "1" if you're running this in a workbench
|
||||
selectedCategory: escape(this.properties.feedbackCategory),
|
||||
currentUser: this.context.pageContext.user
|
||||
}
|
||||
);
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
private handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
|
||||
this.themeVariant = args.theme;
|
||||
this.render();
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPane_Description
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.PropertyPane_GroupName_Settings,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('buttonLabel', {
|
||||
label: strings.PropertyPane_Label_ButtonText
|
||||
}),
|
||||
PropertyPaneToggle('showCategory', {
|
||||
label: strings.FeedbackCategoryToggle_Label,
|
||||
onText: 'On',
|
||||
offText: 'Off'
|
||||
}),
|
||||
PropertyPaneDropdown('feedbackCategory', {
|
||||
label: strings.FeedbackCategory_Label,
|
||||
selectedKey: "general",
|
||||
options: DropdownOptions.Options})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.PropertyPane_GroupName_About,
|
||||
groupFields: [
|
||||
PropertyPaneLabel('versionNumber', {
|
||||
text: strings.PropertyPane_Label_VersionInfo + this.manifest.version
|
||||
})
|
||||
]}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
import * as React from 'react';
|
||||
import "@pnp/polyfill-ie11";
|
||||
import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Dialog, DialogFooter, DialogType } from 'office-ui-fabric-react/lib/Dialog';
|
||||
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
|
||||
import { IContainerProps } from './IContainerProps';
|
||||
import { useConstCallback } from '@uifabric/react-hooks';
|
||||
import { TextField } from 'office-ui-fabric-react/lib/TextField';
|
||||
import { Dropdown, DropdownMenuItemType, IDropdownStyles, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||
import { FeedbackService } from '../../../../services/feedback.service';
|
||||
import { DropdownOptions } from '../../DropdownOptions';
|
||||
import * as strings from 'FeedbackWebPartStrings';
|
||||
import { IArticleInfo } from '../../../../models/IArticleInfo';
|
||||
|
||||
const buttonStyles = { root: { marginRight: 8 } };
|
||||
const dialogContentProps = {
|
||||
type: DialogType.normal,
|
||||
title: 'Feedback successfully submitted.',
|
||||
};
|
||||
const dialogModalProps = {
|
||||
isBlocking: false,
|
||||
styles: { main: { maxWidth: 450 } },
|
||||
};
|
||||
const options: IDropdownOption[] = DropdownOptions.Options;
|
||||
|
||||
|
||||
export const Container: React.FunctionComponent<IContainerProps> = props => {
|
||||
//This is for IE 11 "find" issue
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.find
|
||||
if (!Array.prototype.find) {
|
||||
Object.defineProperty(Array.prototype, 'find', {
|
||||
value: function(predicate) {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
|
||||
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
// tslint:disable-next-line: use-named-parameter
|
||||
var thisArg = arguments[1];
|
||||
|
||||
// 5. Let k be 0.
|
||||
var k = 0;
|
||||
|
||||
// 6. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let Pk be ! ToString(k).
|
||||
// b. Let kValue be ? Get(O, Pk).
|
||||
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
|
||||
// d. If testResult is true, return kValue.
|
||||
var kValue = o[k];
|
||||
if (predicate.call(thisArg, kValue, k, o)) {
|
||||
return kValue;
|
||||
}
|
||||
// e. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 7. Return undefined.
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let defaultCategoryValue = DropdownOptions.Options.find(o => o.key === props.selectedCategory);
|
||||
const feedbackService = new FeedbackService();
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [isDialogVisible, setIsDialogVisible] = React.useState(false);
|
||||
const [pageTitle, setPageTitle] = React.useState("");
|
||||
const [articleInfo, setArticleInfo] = React.useState<IArticleInfo>();
|
||||
const [txtValue, setTxtValue] = React.useState("");
|
||||
const [categoryValue, setCategoryValue] = React.useState((defaultCategoryValue) ? defaultCategoryValue.text : "");
|
||||
|
||||
React.useEffect(()=>{
|
||||
|
||||
feedbackService.getTitle(props.listitemid).then((res) => {
|
||||
setPageTitle(res);
|
||||
});
|
||||
feedbackService.getArticleInfo(props.listitemid).then((res) => {
|
||||
setArticleInfo(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
const openPanel = React.useCallback(() => setIsOpen(true), [isOpen]);
|
||||
const dismissPanel = React.useCallback(() => setIsOpen(false), [isOpen]);
|
||||
|
||||
const hideDialog = React.useCallback(() => setIsDialogVisible(false), [isDialogVisible]);
|
||||
const hideDialogAndPanel = () => {
|
||||
setIsOpen(false);
|
||||
setIsDialogVisible(false);
|
||||
};
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
let newDefaultCategory;
|
||||
if (!props.showCategory){
|
||||
newDefaultCategory = DropdownOptions.Options.find(o => o.key === props.selectedCategory);
|
||||
}
|
||||
if (props.listitemid == null){
|
||||
console.log("List item ID is null. Please run this on a site page.")
|
||||
}
|
||||
feedbackService.sendEmailToOwnerGroup(txtValue, props.listitemid, (newDefaultCategory) ? newDefaultCategory.text : categoryValue, props.currentUser.displayName, props.currentUser.email);
|
||||
dismissPanel();
|
||||
setIsDialogVisible(true);
|
||||
setCategoryValue(defaultCategoryValue.text);
|
||||
setTxtValue("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DefaultButton style={{
|
||||
color: props.themeVariant.semanticColors.buttonText
|
||||
}} text={unescape(props.buttonLabel)} onClick={openPanel} />
|
||||
<Panel
|
||||
isLightDismiss
|
||||
isOpen={isOpen}
|
||||
type={PanelType.medium}
|
||||
onDismiss={dismissPanel}
|
||||
headerText={strings.PanelHeaderText + pageTitle}
|
||||
closeButtonAriaLabel="Close"
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<p>{strings.Feedback_Instructions}</p>
|
||||
<Dropdown label={(props.showCategory) ? strings.FeedbackCategory_Label : ""} options={options} defaultSelectedKey={props.selectedCategory} hidden={(!props.showCategory)}
|
||||
onChange={(event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number) => {setCategoryValue(option.text);}}/>
|
||||
<TextField name="feedbackTxt" multiline rows={8} value={txtValue} label={strings.FeedbackBox_Label}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) =>
|
||||
{setTxtValue(newValue);}}/>
|
||||
<br></br>
|
||||
<div>
|
||||
<PrimaryButton type="submit" styles={buttonStyles} disabled={(txtValue.length > 10) ? false : true}>
|
||||
{strings.ButtonText_Submit}
|
||||
</PrimaryButton>
|
||||
<DefaultButton onClick={dismissPanel}>{strings.ButtonText_Cancel}</DefaultButton>
|
||||
</div>
|
||||
</form>
|
||||
</Panel>
|
||||
<Dialog
|
||||
hidden={!isDialogVisible}
|
||||
onDismiss={hideDialog}
|
||||
dialogContentProps={dialogContentProps}
|
||||
modalProps={dialogModalProps}
|
||||
>
|
||||
<DialogFooter>
|
||||
<PrimaryButton onClick={hideDialogAndPanel} text={strings.ButtonText_Ok} />
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
import { IReadonlyTheme } from "@microsoft/sp-component-base";
|
||||
|
||||
export interface IContainerProps {
|
||||
buttonLabel: string;
|
||||
showCategory: boolean;
|
||||
listitemid: number;
|
||||
selectedCategory: string;
|
||||
currentUser: any;
|
||||
themeVariant: IReadonlyTheme;
|
||||
}
|
18
samples/react-feedback/src/webparts/feedback/loc/en-us.js
vendored
Normal file
18
samples/react-feedback/src/webparts/feedback/loc/en-us.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
define([], function() {
|
||||
return {
|
||||
"PropertyPane_Description": "Configure feedback properties.",
|
||||
"PropertyPane_Label_ButtonText": "Button Text",
|
||||
"PropertyPane_GroupName_Settings": "Settings",
|
||||
"PropertyPane_GroupName_About": "About Webpart",
|
||||
"PropertyPane_Label_VersionInfo": "Version: ",
|
||||
|
||||
"ButtonText_Cancel": "Cancel",
|
||||
"ButtonText_Ok": "OK",
|
||||
"ButtonText_Submit": "Submit",
|
||||
"PanelHeaderText": "Provide feedback on ",
|
||||
"FeedbackBox_Label": "Feedback:",
|
||||
"FeedbackCategory_Label": "Feedback Category:",
|
||||
"FeedbackCategoryToggle_Label": "Show Feedback Category",
|
||||
"Feedback_Instructions": "Provide feedback and click the Submit button below."
|
||||
}
|
||||
});
|
20
samples/react-feedback/src/webparts/feedback/loc/mystrings.d.ts
vendored
Normal file
20
samples/react-feedback/src/webparts/feedback/loc/mystrings.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
declare interface IFeedbackWebPartStrings {
|
||||
PropertyPane_Description: string;
|
||||
PropertyPane_Label_ButtonText: string;
|
||||
PropertyPane_GroupName_Settings: string;
|
||||
PropertyPane_GroupName_About: string;
|
||||
PropertyPane_Label_VersionInfo: string;
|
||||
ButtonText_Cancel: string;
|
||||
ButtonText_Ok: string;
|
||||
ButtonText_Submit: string;
|
||||
PanelHeaderText: string;
|
||||
FeedbackBox_Label: string;
|
||||
FeedbackCategory_Label: string;
|
||||
FeedbackCategoryToggle_Label: string;
|
||||
Feedback_Instructions: string;
|
||||
}
|
||||
|
||||
declare module 'FeedbackWebPartStrings' {
|
||||
const strings: IFeedbackWebPartStrings;
|
||||
export = strings;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 383 B |
40
samples/react-feedback/tsconfig.json
Normal file
40
samples/react-feedback/tsconfig.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
//"es5",
|
||||
"es2017",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
30
samples/react-feedback/tslint.json
Normal file
30
samples/react-feedback/tslint.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
"forin": false,
|
||||
"label-position": false,
|
||||
"member-access": true,
|
||||
"no-arg": false,
|
||||
"no-console": false,
|
||||
"no-construct": false,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": false,
|
||||
"no-function-expression": true,
|
||||
"no-internal-module": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-unnecessary-semicolons": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user