Merge branch 'pnp:main' into main

This commit is contained in:
Takashi Shinohara 2022-01-06 15:27:06 +09:00 committed by GitHub
commit e48e244ad0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
106 changed files with 224951 additions and 26535 deletions

View File

@ -142,7 +142,7 @@ body:
required: true
attributes:
label: Node.js version
placeholder: v10.23.1
placeholder: E.g. v10.23.1
description: |
Which version of Node.js are you using?

View File

@ -9,12 +9,12 @@ An SPFx web part that displays a Scalable Vector Graphics (SVG) image using prop
# Compatibility
![SPFx 1.3.4](https://img.shields.io/badge/SPFx-1.3.4-green.svg)
![Node.js v6](https://img.shields.io/badge/Node.js-v6-green.svg)
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
![Node.js v6](https://img.shields.io/badge/Node.js-v14-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Compatible with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.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)
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg)
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
## Which PnP SPFx controls are being used in this sample?
@ -38,6 +38,7 @@ js-propertycontrols-svg | Chris Kent ([thechriskent.com](https://thechriskent.co
Version|Date|Comments
-------|----|--------
1.1|December 30, 2021|Update to SPFx 1.13.0
1.0|November 12, 2017|Initial release

View File

@ -1,5 +1,5 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"svg-hero-web-part": {

View File

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

View File

@ -1,45 +0,0 @@
{
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"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-case": true,
"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,
"valid-typeof": true,
"variable-name": false,
"whitespace": false
}
}
}

View File

@ -2,5 +2,15 @@
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.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -11,20 +11,22 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "~1.3.4",
"@microsoft/sp-lodash-subset": "~1.3.4",
"@microsoft/sp-office-ui-fabric-core": "~1.3.4",
"@microsoft/sp-webpart-base": "~1.3.4",
"@pnp/spfx-property-controls": "1.0.0",
"@types/webpack-env": ">=1.12.1 <1.14.0"
"@microsoft/sp-core-library": "1.13.0",
"@microsoft/sp-lodash-subset": "1.13.0",
"@microsoft/sp-office-ui-fabric-core": "1.13.0",
"@microsoft/sp-webpart-base": "1.13.0",
"@pnp/spfx-property-controls": "3.3.0"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.3.4",
"@microsoft/sp-module-interfaces": "~1.3.4",
"@microsoft/sp-webpart-workbench": "~1.3.4",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"ajv": "~5.2.2"
"microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@microsoft/sp-build-web": "1.13.0",
"@microsoft/sp-module-interfaces": "1.13.0",
"@microsoft/sp-tslint-rules": "1.13.0",
"@types/chai": "4.3.0",
"@types/mocha": "9.0.0",
"@types/webpack-env": "1.13.0",
"ajv": "~5.2.2",
"gulp": "4.0.2"
}
}
}

View File

@ -0,0 +1,98 @@
{
"bundles": {
"svg-hero-web-part": {
"dependencies": [
{
"componentId": "73e1dc6c-8441-42cc-ad47-4bd3659f8a3a",
"componentName": "@microsoft/sp-lodash-subset",
"componentVersion": "1.13.0",
"isDirectDependency": true
},
{
"componentId": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"componentName": "@microsoft/sp-core-library",
"componentVersion": "1.13.0",
"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.192",
"isDirectDependency": false
},
{
"componentId": "974a7777-0990-4136-8fa6-95d80114c2e0",
"componentName": "@microsoft/sp-webpart-base",
"componentVersion": "1.13.0",
"isDirectDependency": true
},
{
"componentId": "8217e442-8ed3-41fd-957d-b112e841286a",
"componentName": "@ms/sp-telemetry",
"componentVersion": "0.19.42",
"isDirectDependency": false
},
{
"componentId": "467dc675-7cc5-4709-8aac-78e3b71bd2f6",
"componentName": "@microsoft/sp-component-base",
"componentVersion": "1.13.0",
"isDirectDependency": false
},
{
"componentId": "e40f8203-b39d-425a-a957-714852e33b79",
"componentName": "@microsoft/sp-dynamic-data",
"componentVersion": "1.13.0",
"isDirectDependency": false
},
{
"componentId": "78359e4b-07c2-43c6-8d0b-d060b4d577e8",
"componentName": "@microsoft/sp-diagnostics",
"componentVersion": "1.13.0",
"isDirectDependency": false
},
{
"componentId": "1c4541f7-5c31-41aa-9fa8-fbc9dc14c0a8",
"componentName": "@microsoft/sp-page-context",
"componentVersion": "1.13.0",
"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.13.0",
"isDirectDependency": false
},
{
"componentId": "1c6c9123-7aac-41f3-a376-3caea41ed83f",
"componentName": "@microsoft/sp-loader",
"componentVersion": "1.13.0",
"isDirectDependency": false
},
{
"componentId": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"componentName": "react",
"componentVersion": "16.13.1",
"isDirectDependency": true
},
{
"componentId": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"componentName": "react-dom",
"componentVersion": "16.13.1",
"isDirectDependency": true
}
]
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,36 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"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",
"es6-collections",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.promise"
]
}
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": []
}

View File

@ -1,3 +1,29 @@
{
"rulesDirectory": "./config"
}
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}

View File

@ -49,6 +49,7 @@ Version|Date|Comments
-------|----|--------
1.0.0|March 30, 2021|Initial release
1.0.1|September 22, 2021|Added support for multi-language sites
1.0.2|December 24, 2021|Fix retrieving fields from SitePages
## Minimal Path to Awesome

View File

@ -15,7 +15,7 @@
"- Improved support for dates"
],
"creationDateTime": "2021-03-30",
"updateDateTime": "2021-09-22",
"updateDateTime": "2021-12-24",
"products": [
"SharePoint"
],

View File

@ -3,7 +3,7 @@
"solution": {
"name": "Advanced Page Properties",
"id": "daae06a2-8599-445c-93c0-3bd739305f56",
"version": "1.0.1.0",
"version": "1.0.2.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {

View File

@ -68,7 +68,7 @@ export default class AdvancedPagePropertiesWebPart extends BaseClientSideWebPart
private async getPageProperties(): Promise<void> {
Log.Write("Getting Site Page fields...");
const list = await sp.web.lists.ensureSiteAssetsLibrary();
const list = await sp.web.lists.ensureSitePagesLibrary();
const fi = await list.fields();
this.availableProperties = [];

View File

@ -42,9 +42,9 @@ const AdvancedPageProperties: React.FunctionComponent<IAdvancedPagePropertiesPro
// Get the value(s) for the field from the list item itself
var allValues: any = {};
const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary();
const sitePagesList = await sp.web.lists.ensureSitePagesLibrary();
if (props.context.pageContext.listItem !== undefined && props.context.pageContext.listItem !== null) {
allValues = await siteAssetsList.items.getById(props.context.pageContext.listItem.id).select(...props.selectedProperties).get();
allValues = await sitePagesList.items.getById(props.context.pageContext.listItem.id).select(...props.selectedProperties).get();
}
for (let i = 0; i < props.selectedProperties.length; i++) {
@ -53,7 +53,7 @@ const AdvancedPageProperties: React.FunctionComponent<IAdvancedPagePropertiesPro
Log.Write(`Selected Property: ${prop}`);
// Get field information, in case anything is needed in conjunction with value types
const field = await siteAssetsList.fields.getByInternalNameOrTitle(prop)();
const field = await sitePagesList.fields.getByInternalNameOrTitle(prop)();
// Establish the values array
var values: any[] = [];

View File

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

View File

@ -9,6 +9,7 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
#*.sppkg

View File

@ -22,7 +22,7 @@
"framework": "react",
"plusBeta": true,
"isCreatingSolution": true,
"version": "1.10.0",
"version": "1.12.1",
"libraryName": "react-calendar",
"libraryId": "3a13208b-3874-4036-9262-4edd22e88187",
"packageManager": "npm",

View File

@ -143,7 +143,7 @@ Version|Date|Comments
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
1.0.15|November 22, 2021|Fix All Day event issue that makes All Day events date expanded to another day instead of being full day event.
1.0.16|December 21, 2021|Upgraded to SPFx 1.12.1
## Minimal Path to Awesome

View File

@ -9,7 +9,7 @@
"This Web Part allows you to manage events in a calendar. Uses a list of existing calendars on any website. The location and name of the list and the dates of the events to be displayed are defined in the properties of the web part."
],
"created": "2020-12-04",
"modified": "2021-11-22",
"modified": "2021-12-21",
"products": [
"SharePoint"
],
@ -20,7 +20,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.10.0"
"value": "1.12.1"
},
{
"key": "SPFX-TEAMSTAB",

View File

@ -1,4 +1,4 @@
{
"$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",
"workingDir": "./temp/deploy/",
"workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-calendar",
"accessKey": "<!-- ACCESS KEY -->"

View File

@ -13,13 +13,20 @@ const gulpSequence = require('gulp-sequence');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
// Create clean distrubution package
gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
// Create clean development package
gulp.task('dev', gulpSequence('clean', 'bundle', 'package-solution'));
/**
* Webpack Bundle Anlayzer
* Reference and gulp task

File diff suppressed because it is too large Load Diff

View File

@ -15,35 +15,30 @@
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
},
"dependencies": {
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@microsoft/sp-core-library": "1.12.1",
"@microsoft/sp-lodash-subset": "1.12.1",
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
"@microsoft/sp-property-pane": "1.12.1",
"@microsoft/sp-webpart-base": "1.12.1",
"@pnp/pnpjs": "^1.3.0",
"@pnp/spfx-controls-react": "1.12.0",
"@pnp/spfx-property-controls": "1.14.1",
"@types/draft-js": "^0.10.30",
"@types/es6-promise": "0.0.33",
"@types/globalize": "0.0.34",
"@types/jquery": "^3.3.29",
"@types/react": "16.8.8",
"@types/react-big-calendar": "^0.24.6",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"@uifabric/fluent-theme": "^0.16.7",
"browserslist": "^4.12.0",
"caniuse-lite": "^1.0.30001079",
"draft-js": "^0.10.5",
"draftjs-to-html": "^0.8.4",
"globalize": "^1.4.2",
"immutable": "^4.0.0-rc.12",
"jquery": "^3.3.1",
"moment": "^2.24.0",
"office-ui-fabric-react": "6.189.2",
"react": "16.8.5",
"office-ui-fabric-react": "7.156.0",
"react": "16.9.0",
"react-big-calendar": "^0.24.6",
"react-dom": "16.8.5",
"react-dom": "16.9.0",
"react-draft-wysiwyg": "^1.13.2",
"spfx-uifabric-themes": "^0.6.0",
"string-format": "^2.0.0",
@ -57,16 +52,19 @@
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
"@microsoft/rush-stack-compiler-3.2": "0.3.17",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
"@microsoft/sp-build-web": "1.12.1",
"@microsoft/sp-module-interfaces": "1.12.1",
"@microsoft/sp-tslint-rules": "1.12.1",
"@microsoft/sp-webpart-workbench": "1.12.1",
"@types/es6-promise": "0.0.33",
"@types/react": "16.9.36",
"@types/react-dom": "16.9.8",
"@types/webpack-env": "1.13.1",
"@types/xml2js": "^0.4.4",
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
"ajv": "~5.2.2",
"gulp": "~3.9.1",
"gulp": "4.0.2",
"gulp-sequence": "1.0.0",
"gulp-stylelint": "^8.0.0",
"jest": "^23.6.0",
@ -75,5 +73,14 @@
"stylelint-config-standard": "^18.2.0",
"stylelint-scss": "^3.5.4",
"webpack-bundle-analyzer": "^3.1.0"
},
"solution": {
"developer": {
"name": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": ""
}
}
}

View File

@ -123,6 +123,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.onSelectDateStart = this.onSelectDateStart.bind(this);
this.onUpdateCoordinates = this.onUpdateCoordinates.bind(this);
this.onGetErrorMessageTitle = this.onGetErrorMessageTitle.bind(this);
this.onChangeEventTitle = this.onChangeEventTitle.bind(this);
this.getPeoplePickerItems = this.getPeoplePickerItems.bind(this);
this.hidePanel = this.hidePanel.bind(this);
this.onDelete = this.onDelete.bind(this);
@ -410,10 +411,21 @@ export class Event extends React.Component<IEventProps, IEventState> {
if (value.length === 0) {
returnMessage = strings.EventTitleErrorMessage;
} else {
this.setState({ eventData: { ...this.state.eventData, title: value }, disableButton: false, errorMessage: '' });
this.setState({disableButton: false, errorMessage: '' });
}
return returnMessage;
}
/**
*
* @private
* @memberof Event
*/
private onChangeEventTitle (event:any) {
const eventTitle = event.target.value;
this.setState({eventData: {...this.state.eventData, title: eventTitle}});
}
/**
*
* @private
@ -923,6 +935,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
<TextField
label={strings.EventTitleLabel}
value={this.state.eventData ? this.state.eventData.title : ''}
onChange={this.onChangeEventTitle}
onGetErrorMessage={this.onGetErrorMessageTitle}
deferredValidationTime={500}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}

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

View File

@ -1,5 +1,5 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,

View File

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

View File

@ -9,6 +9,7 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
*.sppkg

View File

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

View File

@ -10,10 +10,10 @@
"environment": "spo",
"framework": "react",
"isCreatingSolution": true,
"version": "1.7.1",
"version": "1.13.1",
"libraryName": "my-teams",
"libraryId": "9bf27890-01d8-4041-8030-72831ed570aa",
"packageManager": "npm",
"componentType": "webpart"
}
}
}

View File

@ -10,13 +10,11 @@ The web part can be configured to open the team on the web browser or client app
# Compatibility
![SPFx 1.7.1](https://img.shields.io/badge/SPFx-1.7.1-green.svg)
![Node.js v8](https://img.shields.io/badge/Node.js-v8-green.svg)
![SPFx 1.13.1](https://img.shields.io/badge/SPFx-1.13.1-green.svg)
![Node.js v14](https://img.shields.io/badge/Node.js-v14-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Compatible with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Compatible-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
@ -24,7 +22,7 @@ The web part can be configured to open the team on the web browser or client app
## Prerequisites
- Office 365 subscription with SharePoint Online licence
- Microsoft 365 subscription with SharePoint Online license
- SharePoint Framework [development environment](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) already set up.
## Solution
@ -32,12 +30,15 @@ The web part can be configured to open the team on the web browser or client app
| Solution | Author(s) |
| -------------- | -------------- |
| react-my-teams | Joel Rodrigues |
| react-my-teams | [Yves Habersaat](https://github.com/yhabersaat) |
## Version history
| Version | Date | Comments |
| ------- | ----------------- | --------------- |
| 1.0 | February 26, 2019 | Initial release |
| Version | Date | Comments |
| ------- | ----------------- | ------------------------- |
| 1.1 | December 18, 2021 | Upgraded for SPFx v1.13.1 |
| 1.0 | February 26, 2019 | Initial release |
## Disclaimer

View File

@ -9,7 +9,7 @@
"This sample uses Microsoft Graph to list the Teams the current user is a member of. When the user clicks on one of the teams, the web part retrieves information about the default channel (General) and opens it. The web part can be configured to open the team on the web browser or client app."
],
"creationDateTime": "2019-02-26",
"updateDateTime": "2019-02-26",
"updateDateTime": "2021-12-18",
"products": [
"SharePoint"
],
@ -20,7 +20,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.7.1"
"value": "1.13.1"
}
],
"thumbnails": [
@ -38,6 +38,12 @@
"pictureUrl": "https://github.com/joelfmrodrigues.png",
"name": "Joel Rodrigues",
"twitter": "joelfmrodrigues"
},
{
"gitHubAccount": "yhabersaat",
"pictureUrl": "https://github.com/yhabersaat.png",
"name": "Yves Habersaat",
"twitter": "yhabersaat"
}
],
"references": [
@ -48,4 +54,4 @@
}
]
}
]
]

View File

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

View File

@ -3,7 +3,7 @@
"solution": {
"name": "my-teams-client-side-solution",
"id": "9bf27890-01d8-4041-8030-72831ed570aa",
"version": "1.0.0.0",
"version": "1.1.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"webApiPermissionRequests": [

View File

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

View File

@ -3,8 +3,8 @@
// check if gulp dist was called
if (process.argv.indexOf('dist') !== -1) {
// add ship options to command call
process.argv.push('--ship');
// add ship options to command call
process.argv.push('--ship');
}
const path = require('path');
@ -25,5 +25,13 @@ gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
* 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);

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,7 @@
"name": "my-teams",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
@ -12,30 +10,27 @@
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
},
"dependencies": {
"@microsoft/sp-core-library": "1.7.1",
"@microsoft/sp-lodash-subset": "1.7.1",
"@microsoft/sp-office-ui-fabric-core": "1.7.1",
"@microsoft/sp-webpart-base": "1.7.1",
"@types/es6-promise": "0.0.33",
"@types/react": "16.4.2",
"@types/react-dom": "16.0.5",
"@types/webpack-env": "1.13.1",
"react": "16.3.2",
"react-dom": "16.3.2"
"@microsoft/sp-core-library": "1.13.1",
"@microsoft/sp-lodash-subset": "1.13.1",
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
"@microsoft/sp-property-pane": "1.13.1",
"@microsoft/sp-webpart-base": "1.13.1",
"office-ui-fabric-react": "7.174.1",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"resolutions": {
"@types/react": "16.4.2"
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1",
"@microsoft/sp-module-interfaces": "1.7.1",
"@microsoft/sp-tslint-rules": "1.7.1",
"@microsoft/sp-webpart-workbench": "1.7.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@microsoft/sp-build-web": "1.13.1",
"@microsoft/sp-module-interfaces": "1.13.1",
"@microsoft/sp-tslint-rules": "1.13.1",
"@types/es6-promise": "0.0.33",
"@types/webpack-env": "1.13.1",
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
"ajv": "~5.2.2",
"gulp": "~3.9.1",
"gulp-sequence": "^1.0.0",
"jest": "^23.6.0"
}

View File

@ -13,6 +13,8 @@
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },

View File

@ -1,12 +1,8 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version, Environment, EnvironmentType } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneToggle
} from '@microsoft/sp-webpart-base';
import { Version } from '@microsoft/sp-core-library';
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneToggle } from "@microsoft/sp-property-pane";
import * as strings from 'MyTeamsWebPartStrings';
import { MyTeams, IMyTeamsProps } from './components/myTeams';
import { MSGraphClient } from '@microsoft/sp-http';
@ -22,14 +18,8 @@ export default class MyTeamsWebPart extends BaseClientSideWebPart<IMyTeamsWebPar
private _teamsService: ITeamsService;
public async onInit(): Promise<void> {
if (DEBUG && Environment.type === EnvironmentType.Local) {
console.log("Mock data service not implemented yet");
} else {
this._graphClient = await this.context.msGraphClientFactory.getClient();
this._teamsService = new TeamsService(this._graphClient);
}
this._graphClient = await this.context.msGraphClientFactory.getClient();
this._teamsService = new TeamsService(this._graphClient);
return super.onInit();
}

View File

@ -1,4 +1,4 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
@import '~office-ui-fabric-react/dist/sass/References.scss';
.myTeams {
.container {

View File

@ -3,7 +3,6 @@ import { FocusZone } from 'office-ui-fabric-react/lib/FocusZone';
import { List } from 'office-ui-fabric-react/lib/List';
import styles from '../myTeams/MyTeams.module.scss';
import { IMyTeamsProps, IMyTeamsState } from '.';
import { escape } from '@microsoft/sp-lodash-subset';
import { ITeam, IChannel } from '../../../../shared/interfaces';
export class MyTeams extends React.Component<IMyTeamsProps, IMyTeamsState> {

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

View File

@ -1,4 +1,5 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
@ -10,25 +11,25 @@
"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",
"dom",
"es2015.collection"
"es2015.collection",
"es2015.promise"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
"src/**/*.ts",
"src/**/*.tsx"
]
}

View File

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

View File

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

View File

@ -9,6 +9,7 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
*.sppkg

View File

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

View File

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

View File

@ -2,11 +2,11 @@
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.9.1",
"version": "1.13.1",
"libraryName": "react-quotes",
"libraryId": "aa9211b8-e9b8-44bf-a804-af2db58bc03e",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}
}

View File

@ -9,12 +9,11 @@ This web part displays a quote of the day by querying a third-party api or can d
# Compatibility
![SPFx 1.9.1](https://img.shields.io/badge/SPFx-1.9.1-green.svg)
![Node.js v10 | v8](https://img.shields.io/badge/Node.js-v10%20%7C%20v8-green.svg)
![SPFx 1.13.1](https://img.shields.io/badge/SPFx-1.13.1-green.svg)
![Node.js v14](https://img.shields.io/badge/Node.js-v14-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Does not work with with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.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
@ -25,12 +24,14 @@ This web part displays a quote of the day by querying a third-party api or can d
Solution|Author(s)
--------|---------
react-quotes | Zach Roberts
react-quotes | [Zach Roberts](https://github.com/zachroberts8668)
react-quotes | [Yves Habersaat](https://github.com/yhabersaat)
## Version history
Version|Date|Comments
-------|----|--------
1.1| December 31, 2021| Upgraded for SPFx v1.13.1
1.0| November 11, 2019| Initial Release
## Minimal Path to Awesome
@ -47,9 +48,27 @@ Version|Date|Comments
This web part loads a random quote from a third-party api (https://favqs.com/api). Additionally a quote can be entered manually and the text color of the quote and author can be adjusted through the web part properties.
## Help
We do not support samples, but 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're having issues building the solution, please run [spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/) from within the solution folder to diagnose incompatibility issues with your environment.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3A%22sample%3A%20react-quotes%22) 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=react-quotes) 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%2Csample%3A%20react-quotes&template=bug-report.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%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%3Aquestion%2Csample%3A%20react-quotes&template=question.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%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%3Aenhancement%2Csample%3A%20react-quotes&template=suggestion.yml&sample=react-quotes&authors=@zachroberts8668 @yhabersaat&title=react-quotes%20-%20).
## 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.**
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/readme-template" />
<img src="https://pnptelemetry.azurewebsites.net/sp-dev-fx-webparts/samples/react-quotes" />

View File

@ -9,7 +9,7 @@
"This webpart displays a quote of the day by querying a third-party api or can display a quote entered manually into the webpart property pane."
],
"creationDateTime": "2019-11-11",
"updateDateTime": "2019-11-11",
"updateDateTime": "2022-01-01",
"products": [
"SharePoint"
],
@ -20,7 +20,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.9.1"
"value": "1.13.1"
},
{
"key": "PNPCONTROLS",
@ -41,6 +41,11 @@
"company": "",
"pictureUrl": "https://github.com/zachroberts8668.png",
"name": "Zach Roberts"
},
{
"gitHubAccount": "yhabersaat",
"pictureUrl": "https://github.com/yhabersaat",
"name": "Yves Habersaat"
}
],
"references": [

View File

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

View File

@ -3,9 +3,16 @@
"solution": {
"name": "react-quotes-client-side-solution",
"id": "aa9211b8-e9b8-44bf-a804-af2db58bc03e",
"version": "1.0.0.0",
"version": "1.1.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false
"isDomainIsolated": false,
"developer": {
"name": "Zach Roberts",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/react-quotes.sppkg"

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -12,32 +12,27 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.9.1",
"@microsoft/sp-lodash-subset": "1.9.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
"@microsoft/sp-webpart-base": "1.9.1",
"@microsoft/sp-core-library": "1.13.1",
"@microsoft/sp-lodash-subset": "1.13.1",
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
"@microsoft/sp-property-pane": "1.13.1",
"@microsoft/sp-webpart-base": "1.13.1",
"@pnp/spfx-property-controls": "1.16.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"axios": "^0.19.0",
"office-ui-fabric-react": "6.189.2",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"resolutions": {
"@types/react": "16.8.8"
"office-ui-fabric-react": "7.174.1",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.9.1",
"@microsoft/sp-tslint-rules": "1.9.1",
"@microsoft/sp-module-interfaces": "1.9.1",
"@microsoft/sp-webpart-workbench": "1.9.1",
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
"@types/react": "16.8.8",
"@types/react-dom": "^17.0.11",
"@microsoft/sp-build-web": "1.13.1",
"@microsoft/sp-module-interfaces": "1.13.1",
"@microsoft/sp-tslint-rules": "1.13.1",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@types/es6-promise": "0.0.33",
"@types/webpack-env": "1.13.1",
"ajv": "~5.2.2",
"gulp": "4.0.2"
}
}

View File

@ -1,17 +1,12 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'ReactQuotesWebPartStrings';
import ReactQuotes from './components/ReactQuotes';
import { IReactQuotesProps } from './components/IReactQuotesProps';
import { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
import { PropertyPaneToggle } from '@microsoft/sp-property-pane';
import { PropertyFieldColorPicker } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
import { PropertyPaneToggle, IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
export interface IReactQuotesWebPartProps {
description: string;
@ -25,7 +20,7 @@ export interface IReactQuotesWebPartProps {
export default class ReactQuotesWebPart extends BaseClientSideWebPart<IReactQuotesWebPartProps> {
public render(): void {
const element: React.ReactElement<IReactQuotesProps > = React.createElement(
const element: React.ReactElement<IReactQuotesProps> = React.createElement(
ReactQuotes,
{
description: this.properties.description,

View File

@ -2,13 +2,12 @@ import * as React from 'react';
import styles from './ReactQuotes.module.scss';
import { IReactQuotesProps } from './IReactQuotesProps';
import { IReactQuotesState } from './IReactQuotesState';
import { escape } from '@microsoft/sp-lodash-subset';
import { Spinner } from 'office-ui-fabric-react/lib/spinner';
import { Spinner } from 'office-ui-fabric-react/lib/Spinner';
import axios from 'axios';
export default class ReactQuotes extends React.Component<IReactQuotesProps, IReactQuotesState> {
constructor(props: IReactQuotesProps){
constructor(props: IReactQuotesProps) {
super(props);
this.state = {
@ -20,26 +19,26 @@ export default class ReactQuotes extends React.Component<IReactQuotesProps, IRea
public render(): React.ReactElement<IReactQuotesProps> {
return (
<div className={ styles.reactQuotes }>
<div className={styles.reactQuotes}>
<h2>{this.props.description}</h2>
{this.state.loading !== true ?
//If state is loading show spinner otherwise show quote
<div>
{this.props.manual !== true ?
//If manual quote is not on then show quote generated from third party
<div>
<h3 style={{color: this.props.quoteColor}}>"<i>{this.state.quote}</i>"</h3>
<h5 style={{color: this.props.authorColor}}>- {this.state.author}</h5>
{this.props.manual !== true ?
//If manual quote is not on then show quote generated from third party
<div>
<h3 style={{ color: this.props.quoteColor }}>"<i>{this.state.quote}</i>"</h3>
<h5 style={{ color: this.props.authorColor }}>- {this.state.author}</h5>
</div>
: //Show Manual quote
<div>
<h3 style={{ color: this.props.quoteColor }}>"<i>{this.props.manualQuote}</i>"</h3>
<h5 style={{ color: this.props.authorColor }}>- {this.props.manualAuthor}</h5>
</div>
}
</div>
: //Show Manual quote
:
<div>
<h3 style={{color: this.props.quoteColor}}>"<i>{this.props.manualQuote}</i>"</h3>
<h5 style={{color: this.props.authorColor}}>- {this.props.manualAuthor}</h5>
</div>
}
</div>
:
<div>
<Spinner label="Loading quote..." />
</div>
}
@ -47,9 +46,9 @@ export default class ReactQuotes extends React.Component<IReactQuotesProps, IRea
);
}
public componentDidMount() {
console.log(this.props.manual);
this.setState({loading: true});
public componentDidMount() {
console.log(this.props.manual);
this.setState({ loading: true });
axios.get('https://favqs.com/api/qotd').then(res => {
const quote = res.data.quote.body;
const author = res.data.quote.author;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 542 B

View File

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

View File

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

View File

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

View File

@ -0,0 +1,13 @@
{
"@microsoft/generator-sharepoint": {
"plusBeta": false,
"isCreatingSolution": true,
"environment": "spo",
"version": "1.13.1",
"libraryName": "react-taxonomy-file-explorer",
"libraryId": "5697a573-1bd1-4ff3-96f1-82263c8eb008",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,114 @@
# React Taxonomy File Explorer
## Summary
This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer. Benefit: Due to multiple selection in the managed metadata column the same file can occur more than once.
Additionally with drag and drop options the file can be changed:
- By replacing the managed metadata column with the target term (Move)
- By adding the target term to the managed metadata column (Link)
- By copying the file to a new one with (only) the target term in the managed metadata column (Copy)
In action this looks like:
Link:
![Adding the target term to the managed metadata column (Link)](./assets/03Link.gif)
Move:
![Replacing the managed metadata column with the target term (Move)](./assets/04Move.gif)
Copy:
![Copying the file to a new one with (only) the target term in the managed metadata column (Copy)](./assets/05Copy.gif)
For further details see the author's [blog post](https://mmsharepoint.wordpress.com/2021/12/23/a-sharepoint-file-explorer-based-on-managed-metadata-and-spfx/)
## Compatibility
![SPFx 1.13.0](https://img.shields.io/badge/SPFx-1.13.0-green.svg)
![Node.js v14 | v12 | v10](https://img.shields.io/badge/Node.js-v14%20%7C%20v12%20%7C%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
- [SharePoint Framework](https://aka.ms/spfx)
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
## Prerequisites
- A hierarchical Termset bound to a managed metadata column
- A document library using that managed metadata column and several documents with selected terms
## Solution
Solution|Author(s)
--------|---------
react-taxonomy-file-explorer| [Markus Moeller](https://github.com/mmsharepoint) ([@moeller2_0](http://www.twitter.com/moeller2_0))
## Version history
Version|Date|Comments
-------|----|--------
1.0|December 26, 2021|Initial release
## Minimal Path to Awesome
- Clone this repository
- Ensure that you are at the solution folder
- in the command-line run:
- `npm install`
- `gulp serve`
- Instantiate the web part in the online workbench on a site where prerequisites are met:
- A hierarchical Termset bound to a managed metadata column
- A document library using that managed metadata column and several documents with selected terms
- In the web part properties set the library name and the internal field name of the managed metadata column
- Done!
## Features
This web part illustrates the following concepts:
- Use HTML5 drag and drop event handling
- Use options by modifier key pressed
- Update Managed Metadata columns with [PnPJS](https://pnp.github.io/pnpjs/)
- Build trees with recursive components
- Traverse trees with callback functions
- [FluentUI React File Type Icons](https://www.npmjs.com/package/@fluentui/react-file-type-icons)
- [FluentUI Contextual Menu](https://developer.microsoft.com/en-us/fluentui#/controls/web/contextualmenu)
## References
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
## Help
We do not support samples, but 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're having issues building the solution, please run [spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/) from within the solution folder to diagnose incompatibility issues with your environment.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3A%22sample%3A%20react-taxonomy-file-explorer") 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=react-taxonomy-file-explorer) 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%2Csample%3A%20react-taxonomy-file-explorer&template=bug-report.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%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%3Aquestion%2Csample%3A%20react-taxonomy-file-explorer&template=question.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%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%3Aenhancement%2Csample%3A%20react-taxonomy-file-explorer&template=suggestion.yml&sample=react-taxonomy-file-explorer&authors=@mmsharepoint&title=react-taxonomy-file-explorer%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-taxonomy-file-explorer" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -0,0 +1,62 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-taxonomy-file-explorer",
"source": "pnp",
"title": "Taxonomy File Explorer",
"shortDescription": "This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-taxonomy-file-explorer",
"longDescription": [
"This solution renders a given Termset as a Tree and incorporates files similar than a folder structure in file explorer."
],
"creationDateTime": "2021-12-26",
"updateDateTime": "2021-12-26",
"products": [
"SharePoint",
"Office"
],
"metadata": [
{
"key": "CLIENT-SIDE-DEV",
"value": "React"
},
{
"key": "SPFX-VERSION",
"value": "1.13.0"
}
],
"thumbnails": [
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/03Link.gif",
"alt": "Linking to a file"
},
{
"type": "image",
"order": 101,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/04Move.gif",
"alt": "Moving a file"
},
{
"type": "image",
"order": 102,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-taxonomy-file-explorer/assets/04Move.gif",
"alt": "Copying a file"
}
],
"authors": [
{
"gitHubAccount": "mmsharepoint",
"pictureUrl": "https://github.com/mmsharepoint.png",
"name": "Markus Moeller"
}
],
"references": [
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
}
]
}
]

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"taxonomy-file-explorer-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/taxonomyFileExplorer/TaxonomyFileExplorerWebPart.js",
"manifest": "./src/webparts/taxonomyFileExplorer/TaxonomyFileExplorerWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"TaxonomyFileExplorerWebPartStrings": "lib/webparts/taxonomyFileExplorer/loc/{locale}.js"
}
}

View File

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

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-taxonomy-file-explorer-client-side-solution",
"id": "5697a573-1bd1-4ff3-96f1-82263c8eb008",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": "Undefined-1.13.1"
}
},
"paths": {
"zippedPackage": "solution/react-taxonomy-file-explorer.sppkg"
}
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
{
"name": "react-taxonomy-file-explorer",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@fluentui/react-file-type-icons": "^8.5.7",
"@microsoft/sp-core-library": "1.13.1",
"@microsoft/sp-lodash-subset": "1.13.1",
"@microsoft/sp-office-ui-fabric-core": "1.13.1",
"@microsoft/sp-property-pane": "1.13.1",
"@microsoft/sp-webpart-base": "1.13.1",
"@pnp/sp": "^2.11.0",
"office-ui-fabric-react": "7.174.1",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"devDependencies": {
"@types/react": "16.9.51",
"@types/react-dom": "16.9.8",
"@microsoft/sp-build-web": "1.13.1",
"@microsoft/sp-tslint-rules": "1.13.1",
"@microsoft/sp-module-interfaces": "1.13.1",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"gulp": "~4.0.2",
"ajv": "~5.2.2",
"@types/webpack-env": "1.13.1"
}
}

View File

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

View File

@ -0,0 +1,8 @@
export interface IFileItem {
id: string;
title: string;
extension: string;
url: string;
termGuid: string[];
taxValue: string[];
}

View File

@ -0,0 +1,9 @@
import { IFileItem } from "./IFileItem";
export interface ITermNode {
name: string;
guid: string;
childDocuments: number;
children: ITermNode[];
subFiles: IFileItem[];
}

View File

@ -0,0 +1,97 @@
import { sp } from "@pnp/sp";
import { IFileItem } from "../model/IFileItem";
export class SPService {
private listName: string;
private fieldName: string;
constructor (listname: string, fieldname: string) {
this.listName = listname;
this.fieldName = fieldname;
}
public async getItems (termsetID: string): Promise<IFileItem[]> {
const items: any[] = await sp.web.lists.getByTitle(this.listName).items.select('Id', this.fieldName).expand('File').get();
const files: IFileItem[] = [];
items.forEach(i => {
const nameparts = i.File.Name.split('.');
const file: IFileItem = {
title: i.File.Name,
extension: nameparts[nameparts.length - 1],
id: i.Id,
termGuid: [""],
taxValue: [""],
url: i.File.LinkingUrl
};
if (Array.isArray(i[this.fieldName])) {
const termguids: string[] = [];
const taxvalues: string[] = [];
i[this.fieldName].forEach(f => {
termguids.push(f.TermGuid.toLowerCase());
taxvalues.push(`${f.Label}|${f.TermGuid}`);
});
file.termGuid = termguids;
file.taxValue = taxvalues;
}
else {
file.termGuid = i[this.fieldName] ? [i[this.fieldName].TermGuid.toLowerCase()]:[""];
file.taxValue = i[this.fieldName] ? [`${i[this.fieldName].Label.toLowerCase()}|${i[this.fieldName].TermGuid.toLowerCase()}`]:[""];
}
files.push(file);
});
return files;
}
public async updateTaxonomyItemByAdd (file: IFileItem, fieldName: string, newTaxonomyValue: string) {
const itemID: number = parseInt(file.id);
let fieldValues = file.taxValue.join(';');
fieldValues += `;${newTaxonomyValue}`;
// https://blog.aterentiev.com/how-to-easily-update-managed-metadata
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
ErrorMessage: null,
FieldName: fieldName,
FieldValue: fieldValues,
HasException: false
}]);
}
public async updateTaxonomyItemByReplace (file: IFileItem, fieldName: string, newTaxonomyValue: string) {
const itemID: number = parseInt(file.id);
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
ErrorMessage: null,
FieldName: fieldName,
FieldValue: newTaxonomyValue,
HasException: false
}]);
}
public async newTaxonomyItemByCopy (file: IFileItem, fieldName: string, newTaxonomyValue: string): Promise<IFileItem> {
const fileUrl: URL = new URL(file.url);
const currentFileNamePart = file.title.replace(`.${file.extension}`, '');
const newFilename = `${currentFileNamePart}_Copy.${file.extension}`;
const destinationUrl = decodeURI(fileUrl.pathname).replace(file.title, newFilename);
await sp.web.getFileByServerRelativePath(decodeURI(fileUrl.pathname)).copyByPath(destinationUrl, false, true);
const newFileItemPromise = await sp.web.getFileByServerRelativePath(destinationUrl).getItem();
const newFileItem = await newFileItemPromise.get();
console.log(newFileItem);
const itemID: number = parseInt(newFileItem.Id);
await sp.web.lists.getByTitle(this.listName).items.getById(itemID).validateUpdateListItem([{
ErrorMessage: null,
FieldName: fieldName,
FieldValue: newTaxonomyValue,
HasException: false
}]);
const newFile: IFileItem = {
extension: file.extension,
id: itemID.toString(),
taxValue: [newTaxonomyValue],
termGuid: [newTaxonomyValue.split('|')[1]],
title: newFilename,
url: fileUrl.host + '/' + destinationUrl
};
return newFile;
}
}

View File

@ -0,0 +1,76 @@
import { sp } from "@pnp/sp";
import { IOrderedTermInfo } from "@pnp/sp/taxonomy";
import { IFileItem } from "../model/IFileItem";
import { ITermNode } from "../model/ITermNode";
export class TaxonomyService {
public async getTermsetInfo (fieldName: string): Promise<string> {
const mmFieldInfo = await sp.web.fields.getByInternalNameOrTitle(fieldName).get();
const parser = new DOMParser();
const xmlField = parser.parseFromString(mmFieldInfo.SchemaXml, "text/xml");
const properties = xmlField.getElementsByTagName("ArrayOfProperty")[0].childNodes;
let termsetID: string = "";
properties.forEach(prop => {
if (prop.childNodes[0].textContent == "TermSetId") {
termsetID = prop.childNodes[1].textContent;
}
});
return termsetID;
}
public async getTermset (termsetID: string) {
// list all the terms available in this term set by term set id
const termset: IOrderedTermInfo[] = await sp.termStore.sets.getById(termsetID).getAllChildrenAsOrderedTree();
const termnodes: ITermNode[] = [];
termset.forEach(async ti => {
const tn = this.getTermnode(ti);
termnodes.push(tn);
});
return termnodes;
}
public incorporateFiles (terms: ITermNode[], files: IFileItem[]): ITermNode[] {
terms.forEach(term => {
term = this.incorporateFilesIntoTerm(term, files);
});
return terms;
}
private getTermnode (term: IOrderedTermInfo): ITermNode {
const node: ITermNode = {
guid: term.id,
childDocuments: 0,
name: term.defaultLabel,
children: [],
subFiles: []
};
if (term.childrenCount > 0) {
const ctnodes: ITermNode[] = [];
term.children.forEach(ct => {
const ctnode: ITermNode = this.getTermnode(ct);
node.childDocuments += ctnode.childDocuments;
ctnodes.push(ctnode);
});
node.children = ctnodes;
}
return node;
}
private incorporateFilesIntoTerm (term: ITermNode, files: IFileItem[]): ITermNode {
term.childDocuments = 0;
term.subFiles = [];
if (term.children.length > 0) {
term.children.forEach(ct => {
ct = this.incorporateFilesIntoTerm(ct, files);
term.childDocuments += ct.childDocuments;
});
}
files.forEach(fi => {
if (fi.termGuid.indexOf(term.guid.toLowerCase()) > -1) {
term.childDocuments++;
term.subFiles.push(fi);
}
});
return term;
}
}

View File

@ -0,0 +1,29 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "e086d23e-079e-42c1-aecf-d042a264ddac",
"alias": "TaxonomyFileExplorerWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"],
"supportsThemeVariants": true,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Taxonomy File Explorer" },
"description": { "default": "A structural view on files, based on Managed Metadata column" },
"officeFabricIconFontName": "BulletedTreeList",
"properties": {
"listName": "Documents",
"fieldName": "FolderStructure"
}
}]
}

View File

@ -0,0 +1,73 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { sp } from "@pnp/sp/presets/all";
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'TaxonomyFileExplorerWebPartStrings';
import { TaxonomyFileExplorer } from './components/TaxonomyFileExplorer';
import { ITaxonomyFileExplorerProps } from './components/ITaxonomyFileExplorerProps';
export interface ITaxonomyFileExplorerWebPartProps {
fieldName: string;
listName: string;
}
export default class TaxonomyFileExplorerWebPart extends BaseClientSideWebPart<ITaxonomyFileExplorerWebPartProps> {
protected onInit(): Promise<void> {
return super.onInit().then(_ => {
sp.setup({
spfxContext: this.context
});
});
}
public render(): void {
const element: React.ReactElement<ITaxonomyFileExplorerProps> = React.createElement(
TaxonomyFileExplorer,
{
fieldName: this.properties.fieldName,
listName: this.properties.listName
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListnameFieldLabel
}),
PropertyPaneTextField('fieldName', {
label: strings.FieldnameFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,14 @@
.fileLabel {
padding-left: 8px;
li {
list-style: none;
}
.filelink {
margin-left: 6px;
text-decoration: none;
font-style: italic;
font-size: 0.9em;
color: "[theme: themePrimary, default: #0078d7]";
}
}

View File

@ -0,0 +1,20 @@
import * as React from 'react';
import styles from './FileLabel.module.scss';
import { getFileTypeIconProps, initializeFileTypeIcons } from '@fluentui/react-file-type-icons';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { IFileLabelProps } from './IFileLabelProps';
initializeFileTypeIcons(undefined);
export const FileLabel: React.FC<IFileLabelProps> = (props) => {
const drag = (ev) => {
ev.dataTransfer.setData("text/plain", JSON.stringify(props.file));
};
return (
<li className={styles.fileLabel} draggable={true} onDragStart={drag}>
<Icon {...getFileTypeIconProps({ extension: props.file.extension, size: 16 })} />
<a className={styles.filelink} draggable={false} href={props.file.url}>{props.file.title}</a>
</li>
);
};

View File

@ -0,0 +1,5 @@
import { IFileItem } from "../../../model/IFileItem";
export interface IFileLabelProps {
file: IFileItem;
}

View File

@ -0,0 +1,4 @@
export interface ITaxonomyFileExplorerProps {
fieldName: string;
listName: string;
}

View File

@ -0,0 +1,12 @@
import { IFileItem } from "../../../model/IFileItem";
import { ITermNode } from "../../../model/ITermNode";
export interface ITermLabelProps {
node: ITermNode;
selectedNode: string;
renderFiles: (files: IFileItem[]) => void;
resetChecked: (s: string) => void;
addTerm: (file: IFileItem, newValue: string) => void;
replaceTerm: (file: IFileItem, newValue: string) => void;
copyFile: (file: IFileItem, newValue: string) => void;
}

View File

@ -0,0 +1,25 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.taxonomyFileExplorer {
.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;
color: "[theme: themePrimary, default: #0078d7]";
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg6;
}
ul {
list-style: none;
padding-inline-start: 0px;
}
}

View File

@ -0,0 +1,116 @@
import * as React from 'react';
import styles from './TaxonomyFileExplorer.module.scss';
import { ITaxonomyFileExplorerProps } from './ITaxonomyFileExplorerProps';
import { IFileItem } from '../../../model/IFileItem';
import { ITermNode } from '../../../model/ITermNode';
import { TaxonomyService } from '../../../services/TaxonomyService';
import { SPService } from '../../../services/SPService';
import { FileLabel } from './FileLabel';
import { TermLabel } from './TermLabel';
export const TaxonomyFileExplorer: React.FC<ITaxonomyFileExplorerProps> = (props) => {
const [spSvc, setSpSvc] = React.useState<SPService>();
const [fileItems, setFileItems] = React.useState<IFileItem[]>([]);
const [terms, setTerms] = React.useState<ITermNode[]>([]);
const [shownFiles, setShownFiles] = React.useState<IFileItem[]>([]);
const [selectedTermnode, setSelectedTermnode] = React.useState<string>("");
const buildTree = async () => {
const taxSvc: TaxonomyService = new TaxonomyService();
const termsetID = await taxSvc.getTermsetInfo(props.fieldName);
let termnodetree: ITermNode[];
const termnodetreeStr = sessionStorage.getItem(`Termtree_${termsetID}`);
if (termnodetreeStr === null) {
termnodetree = await taxSvc.getTermset(termsetID);
sessionStorage.setItem(`Termtree_${termsetID}`, JSON.stringify(termnodetree));
}
else {
termnodetree = JSON.parse(termnodetreeStr);
}
const spSrvc: SPService = new SPService(props.listName, props.fieldName);
const files = await spSrvc.getItems(termsetID);
setSpSvc(spSrvc);
updateFiles(files, termnodetree);
};
const updateFiles = (files: IFileItem[], termnodetree: ITermNode[]) => {
const taxSvc: TaxonomyService = new TaxonomyService();
termnodetree = taxSvc.incorporateFiles(termnodetree, files);
setFileItems(files);
setTerms(termnodetree);
};
const renderFiles = (files: IFileItem[]) => {
setShownFiles(files);
};
const resetChecked = (newNodeID: string) => {
setSelectedTermnode(newNodeID);
};
const reloadFiles = (file: IFileItem) => {
const newFiles: IFileItem[] = [];
fileItems.forEach(fi => {
if (fi.id === file.id && fi.url === file.url) {
newFiles.push(file);
}
else {
newFiles.push(fi);
}
});
updateFiles(newFiles, terms);
};
const loadNewFiles = (file: IFileItem) => {
const newFiles: IFileItem[] = [file].concat(fileItems);
updateFiles(newFiles, terms);
};
const addTerm = (file: IFileItem, newTaxonomyValue: string) => {
spSvc.updateTaxonomyItemByAdd(file, props.fieldName, newTaxonomyValue);
reloadFiles(file);
};
const replaceTerm = (file: IFileItem, newTaxonomyValue: string) => {
spSvc.updateTaxonomyItemByReplace(file, props.fieldName, newTaxonomyValue);
reloadFiles(file);
};
const copyFile = async (file: IFileItem, newTaxonomyValue: string) => {
const newFile = await spSvc.newTaxonomyItemByCopy(file, props.fieldName, newTaxonomyValue);
loadNewFiles(newFile);
};
React.useEffect(() => {
buildTree();
}, []);
return (
<div className={ styles.taxonomyFileExplorer }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
<ul>
{terms.map(nc => { return <TermLabel node={nc}
renderFiles={renderFiles}
resetChecked={resetChecked}
selectedNode={selectedTermnode}
addTerm={addTerm}
replaceTerm={replaceTerm}
copyFile={copyFile} />; })}
</ul>
</div>
<div className={ styles.column }>
{shownFiles.length > 0 &&
<ul>
{shownFiles.map(f => {
return <FileLabel file={f} />;
})}
</ul>}
</div>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,31 @@
.termLabel {
li {
list-style: none;
}
.liFilled {
border-left-color: "[theme: themePrimary, default: #0078d7]";
border-left-style: dotted;
border-left-width: 1px;
}
ul {
padding-inline-start: 16px;
}
.label {
margin-bottom: 4px;
cursor: pointer;
}
.checkedLabel {
border: 1px dotted "[theme: themePrimary, default: #0078d7]";
}
.icon {
margin-right: 6px;
cursor: pointer;
}
.emptyicon {
margin-right: 15px;
}
.fileCount {
font-weight: 600;
padding-left: 18px;
}
}

View File

@ -0,0 +1,119 @@
import * as React from 'react';
import styles from './TermLabel.module.scss';
import { ContextualMenu, IContextualMenuItem } from "office-ui-fabric-react/lib/ContextualMenu";
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { ITermLabelProps } from "./ITermLabelProps";
import { IFileItem } from '../../../model/IFileItem';
export const TermLabel: React.FC<ITermLabelProps> = (props) => {
const linkRef = React.useRef(null);
const [showChildren, setShowChildren] = React.useState<boolean>(true);
const [countDocuments, setCountDocuments] = React.useState<number>(props.node.childDocuments);
const [showContextualMenu, setShowContextualMenu] = React.useState<boolean>(false);
const [droppedFile, setDroppedFile] = React.useState<IFileItem>();
const toggleIcon = () => {
setShowChildren(!showChildren);
};
const nodeSelected = () => {
props.resetChecked(props.node.guid);
props.renderFiles(props.node.subFiles);
};
const hideContextualMenu = () => {
setShowContextualMenu(false);
};
const drop = (ev) => {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
const file: IFileItem = JSON.parse(data);
setDroppedFile(file);
if (ev.ctrlKey) {
setShowContextualMenu(true);
}
else {
addNewTerm(file); // Default option: Simply add the new (target) term to existing ones
}
};
const dragOver = (ev) => {
ev.preventDefault();
};
const addNewTerm = (file: IFileItem) => {
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
file.termGuid.push(props.node.guid);
file.taxValue.push(newTaxonomyValue);
console.log(file);
props.addTerm(file, newTaxonomyValue);
};
const replaceByNewTerm = (file: IFileItem) => {
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
file.termGuid = [props.node.guid];
file.taxValue = [newTaxonomyValue];
console.log(file);
props.addTerm(file, newTaxonomyValue);
};
const copyWithNewTerm = (file: IFileItem) => {
const newTaxonomyValue = `${props.node.name}|${props.node.guid}`;
console.log(file);
props.copyFile(file, newTaxonomyValue);
};
const currentExpandIcon = showChildren? <Icon className={styles.icon} iconName="ChevronDown" onClick={toggleIcon} />:<Icon className={styles.icon} iconName="ChevronRight" onClick={toggleIcon} />;
const menuItems: IContextualMenuItem[] = [
{
key: 'copyItem',
text: 'Create new file with term (Copy)',
onClick: () => copyWithNewTerm(droppedFile)
},
{
key: 'moveItem',
text: 'Replace with new term (Move)',
onClick: () => replaceByNewTerm(droppedFile)
},
{
key: 'linkItem',
text: 'Add new term (Link)',
onClick: () => addNewTerm(droppedFile)
}];
React.useEffect(() => {
if (props.selectedNode===props.node.guid) {
props.renderFiles(props.node.subFiles);
}
if (props.node.childDocuments !== countDocuments) {
setCountDocuments(props.node.childDocuments);
}
}, [props.node.subFiles]);
return (
<li className={styles.termLabel}>
<div ref={linkRef} className={`${styles.label} ${props.selectedNode===props.node.guid ? styles.checkedLabel : ""}`} onClick={nodeSelected} onDrop={drop} onDragOver={dragOver}>
<label>
{props.node.children.length > 0 ? currentExpandIcon : <i className={styles.emptyicon}>&nbsp;</i>}
<Icon className={styles.icon} iconName="FabricFolder" />
{props.node.name}{countDocuments>0?<span className={styles.fileCount}>{countDocuments}</span>:""}
</label>
</div>
<ContextualMenu
items={menuItems}
hidden={!showContextualMenu}
target={linkRef}
onItemClick={hideContextualMenu}
onDismiss={hideContextualMenu}
/>
{showChildren && <ul className={`${props.node.children.length > 0 ? styles.liFilled : ""}`}>
{props.node.children.map(nc => { return <TermLabel node={nc}
renderFiles={props.renderFiles}
resetChecked={props.resetChecked}
selectedNode={props.selectedNode}
addTerm={props.addTerm}
replaceTerm={props.replaceTerm}
copyFile={props.copyFile} />; })}
</ul>}
</li>
);
};

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