Merge branch 'dev' of https://github.com/SharePoint/sp-dev-fx-webparts into dev
This commit is contained in:
commit
80f0c7d70e
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"libraryName": "component-test",
|
||||||
|
"libraryId": "7d4d5b98-cea3-4361-8e3d-5143d5c86334",
|
||||||
|
"environment": "spo"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
# SharePoint Framework PnP Controls Sample
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This is a sample project that contains a web part which makes use of the PnP SPFx Controls:
|
||||||
|
|
||||||
|
- [SharePoint Framework React Controls](https://www.npmjs.com/package/@pnp/spfx-controls-react)
|
||||||
|
- [SharePoint Framework Property Controls](https://www.npmjs.com/package/@pnp/spfx-property-controls)
|
||||||
|
|
||||||
|
![Web part outcome](./assets/webpart-outcome.gif)
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
![drop](https://img.shields.io/badge/drop-1.2.0-green.svg)
|
||||||
|
|
||||||
|
## Which PnP SPFx controls are being used in this sample?
|
||||||
|
|
||||||
|
The sample makes use of the following controls:
|
||||||
|
- PropertyFieldListPicker
|
||||||
|
- PropertyFieldTermPicker
|
||||||
|
- Placeholder
|
||||||
|
- ListView (which also uses the FileTypeIcon control)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
pnp-controls|Elio Struyf (MVP, U2U, [@eliostruyf](https://twitter.com/eliostruyf))
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
0.0.1|September 20, 2017|Initial release
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
To test out this web part, you need to have a library with a managed metadata field. In my case, I made use of a field called **Country**.
|
||||||
|
|
||||||
|
![Documents](./assets/documents.png)
|
||||||
|
|
||||||
|
Once you have such a library in place, you can copy the code and run the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
gulp serve --nobrowser
|
||||||
|
```
|
||||||
|
|
||||||
|
![](https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/pnp-controls)
|
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.5 MiB |
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"pnp-controls-web-part": {
|
||||||
|
"components": [{
|
||||||
|
"entrypoint": "./lib/webparts/pnpControls/PnPControlsWebPart.js",
|
||||||
|
"manifest": "./src/webparts/pnpControls/PnPControlsWebPart.manifest.json"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"PnPControlsWebPartStrings": "lib/webparts/pnpControls/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "temp/deploy"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./temp/deploy/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "component-test",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "component-test-client-side-solution",
|
||||||
|
"id": "7d4d5b98-cea3-4361-8e3d-5143d5c86334",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"skipFeatureDeployment": false
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/component-test.sppkg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/core-build/serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"initialPage": "https://localhost:5432/workbench",
|
||||||
|
"https": true,
|
||||||
|
"api": {
|
||||||
|
"port": 5432,
|
||||||
|
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"$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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const build = require('@microsoft/sp-build-web');
|
||||||
|
|
||||||
|
build.initialize(gulp);
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "component-test",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/sp-core-library": "~1.2.0",
|
||||||
|
"@microsoft/sp-webpart-base": "~1.2.0",
|
||||||
|
"@pnp/spfx-controls-react": "1.0.0-beta.5",
|
||||||
|
"@pnp/spfx-property-controls": "1.0.0-beta.1",
|
||||||
|
"@types/react": "15.0.38",
|
||||||
|
"@types/react-addons-shallow-compare": "0.14.17",
|
||||||
|
"@types/react-addons-test-utils": "0.14.15",
|
||||||
|
"@types/react-addons-update": "0.14.14",
|
||||||
|
"@types/react-dom": "0.14.18",
|
||||||
|
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
||||||
|
"moment": "2.18.1",
|
||||||
|
"react": "15.4.2",
|
||||||
|
"react-dom": "15.4.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/sp-build-web": "~1.2.0",
|
||||||
|
"@microsoft/sp-module-interfaces": "~1.2.0",
|
||||||
|
"@microsoft/sp-webpart-workbench": "~1.2.0",
|
||||||
|
"gulp": "~3.9.1",
|
||||||
|
"@types/chai": ">=3.4.34 <3.6.0",
|
||||||
|
"@types/mocha": ">=2.2.33 <2.6.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { ICheckedTerms } from "@pnp/spfx-property-controls/lib/PropertyFieldTermPicker";
|
||||||
|
|
||||||
|
export interface IPnPControlsWebPartProps {
|
||||||
|
lists: string | string[]; // Stores the list ID(s)
|
||||||
|
terms: ICheckedTerms; // Keeps hold of the selected terms
|
||||||
|
description: string;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "8909dae1-5562-4f0f-be6c-aa14eac34c66",
|
||||||
|
"alias": "PnPControlsWebPart",
|
||||||
|
"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,
|
||||||
|
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "8909dae1-5562-4f0f-be6c-aa14eac34c66",
|
||||||
|
"group": {
|
||||||
|
"default": "Under Development"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"default": "PnPControls"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"default": "Web part to test out the PnP SPFx controls"
|
||||||
|
},
|
||||||
|
"iconImageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAcCAYAAAATFf3WAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAA6ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNy0wOS0xOVQxMjowOTozOTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjY8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6Q29tcHJlc3Npb24+NTwvdGlmZjpDb21wcmVzc2lvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzI8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NDA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI4PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CjwcPPMAAAP9SURBVFgJnZfLi49RGMdn3GsUCymXDM0wGCHFkin+ABZYTGTkmmxZoazYSuS2UZNEYiFFJJeFRimxYGE0CwvKXe6X7+c3z1dn3jnv/H6/+db397znnOd5zvd5znnfaRob8hip6cZY+iMLc5igyWaxTZwfdoXsAfG0OEr8JQ4bJMjhd2ESvynibLE9OE+2RWR+hJiCAgFFeq2syIpj2U+ZwCUKQMCCYKvsdHG8mAMFIQA7TvwrGhaGUObTNfuU2jKBDxXhI06D2cwbMs+m+NGx9Fq4az81T3Ec80sR4F+zSCeqRCY/JKAbbEByd4jkFGVaoKYGwEV0a/Z58Gh4kDtXfCzXZhBGohzZ3IKL647rkg8d9ZgYfHeIoOzk+leT37IO5ipkE7rJmuPYNAd8KOJtLH4PuzQscc4RU3lTk5NCEYevK7dQhOREModvkwi8T6eeN4s+Ac/jk0VVB0VZ3Fc97xUXi3z31oqfxJxIjpciekUwRnwg3hPPiOdEQG7i6wbJ6QIJqJbnNWIRfZpgzT6+c75rk7W2SaRzfH7AbpGYEwyE0aJPpjJRy48FesOeCJope1t8LB4SX4k5gds0nwOdBQdF4jpEo65OWuAPRZPoQmRZH2PmTLrsZxe0Pfw5WroDEZBeqV6N34iXRTpcFyzQG96JaC499wdB6fGXCSwenQXyV4o33HHYDWLNsEBEuEMrk+iNev4iktj3j2cX5A4WBXp8OGK/yfLyEXtfHARXNGghJjgWBILz4qrKU0PDWdnV8UwONqgHufuWmyvN6Q6ycZH7kqirse7O2ZZ10C/JQsV9KOTekuSt+miBPt6PhWTLI8ORmPfLVBQ41EvyQrHvxeviVjEL34nsoia5X/jcEHmT94uvxT4RTOo3g3595BZsBzpI0ZzCLJErc1OsG+6gO3Mpk2GG5t6JiKEQrAX5Q00BneI6cawIdon4nmIg0IBqjao4pj8W6I35JOwRF4lzRF6QJyIb2Td99pE9Ch/W7orXYnxR1qj2otpvgE035UjYwHRXU0Fecwe75M9xfo44Pif4ELtTNKqKq+qgTLz+iEQ04G8nnYV+K/U4AMSwzrcyRbcGx0X2heQdEmVnT7UpnNDzjBFRBvwQPzEcvE9PjIl3wTGVNzjm4A5wZCSiG66WtaHEafl/l68wENjnpHiMgUC+muDKis4+Oluv0xnfT0S6kxZs68J5e9sj5pkswMcnUZkY6qdM4DIFzRXTfzunadwk5mLoCJtiKcrd5r4+FQGi8alZHEG5zZjnrvi+MMZvqtgq0hFIAS0i/7i7087nTmqpIgxr0TzXDCcsBrChNyExx9oXvCVr8I98s8i3EdFtYofoO0a3hiVMcRX8A255OkxXy9tuAAAAAElFTkSuQmCC",
|
||||||
|
"properties": {
|
||||||
|
"description": "PnPControls",
|
||||||
|
"fieldName": "Country"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
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 * as strings from 'PnPControlsWebPartStrings';
|
||||||
|
import PnPControls from './components/PnPControls';
|
||||||
|
import { IPnPControlsProps } from './components/IPnPControlsProps';
|
||||||
|
import { IPnPControlsWebPartProps } from './IPnPControlsWebPartProps';
|
||||||
|
|
||||||
|
import { PropertyFieldListPicker, PropertyFieldListPickerOrderBy } from '@pnp/spfx-property-controls/lib/PropertyFieldListPicker';
|
||||||
|
import { PropertyFieldTermPicker } from '@pnp/spfx-property-controls/lib/PropertyFieldTermPicker';
|
||||||
|
|
||||||
|
export default class PnPControlsWebPart extends BaseClientSideWebPart<IPnPControlsWebPartProps> {
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IPnPControlsProps> = React.createElement(
|
||||||
|
PnPControls,
|
||||||
|
{
|
||||||
|
context: this.context,
|
||||||
|
description: this.properties.description,
|
||||||
|
list: this.properties.lists || "",
|
||||||
|
terms: this.properties.terms || null
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get disableReactivePropertyChanges(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.BasicGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('description', {
|
||||||
|
label: strings.DescriptionFieldLabel
|
||||||
|
}),
|
||||||
|
PropertyFieldListPicker('lists', {
|
||||||
|
label: 'Select a list',
|
||||||
|
selectedList: this.properties.lists,
|
||||||
|
includeHidden: false,
|
||||||
|
orderBy: PropertyFieldListPickerOrderBy.Title,
|
||||||
|
disabled: false,
|
||||||
|
baseTemplate: 101,
|
||||||
|
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
|
||||||
|
properties: this.properties,
|
||||||
|
context: this.context,
|
||||||
|
onGetErrorMessage: null,
|
||||||
|
deferredValidationTime: 0,
|
||||||
|
key: 'listPickerFieldId'
|
||||||
|
}),
|
||||||
|
PropertyFieldTermPicker('terms', {
|
||||||
|
label: 'Select a term',
|
||||||
|
panelTitle: 'Select a term',
|
||||||
|
initialValues: this.properties.terms,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
excludeSystemGroup: false,
|
||||||
|
onPropertyChange: this.onPropertyPaneFieldChanged,
|
||||||
|
properties: this.properties,
|
||||||
|
context: this.context,
|
||||||
|
onGetErrorMessage: null,
|
||||||
|
deferredValidationTime: 0,
|
||||||
|
key: 'termSetsPickerFieldId'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { ICheckedTerms } from '@pnp/spfx-property-controls/lib/PropertyFieldTermPicker';
|
||||||
|
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||||
|
|
||||||
|
export interface IPnPControlsProps {
|
||||||
|
context: WebPartContext;
|
||||||
|
description: string;
|
||||||
|
list: string | string[];
|
||||||
|
terms: ICheckedTerms;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPnpControlsState {
|
||||||
|
items?: any[];
|
||||||
|
loading?: boolean;
|
||||||
|
showPlaceholder?: boolean;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
.pnpControls {}
|
|
@ -0,0 +1,160 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { IPnPControlsProps, IPnpControlsState } from './IPnPControlsProps';
|
||||||
|
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/components/Spinner';
|
||||||
|
import { Placeholder } from '@pnp/spfx-controls-react/lib/Placeholder';
|
||||||
|
import { ListView } from '@pnp/spfx-controls-react/lib/ListView';
|
||||||
|
import { SPHttpClient } from '@microsoft/sp-http';
|
||||||
|
import { IViewField } from '@pnp/spfx-controls-react/lib/controls/listView';
|
||||||
|
|
||||||
|
export default class PnPControls extends React.Component<IPnPControlsProps, IPnpControlsState> {
|
||||||
|
// Specify the fields that need to be viewed in the listview
|
||||||
|
private _viewFields: IViewField[] = [
|
||||||
|
{
|
||||||
|
name: "Id",
|
||||||
|
displayName: "ID",
|
||||||
|
maxWidth: 25,
|
||||||
|
minWidth: 25,
|
||||||
|
sorting: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File.Name",
|
||||||
|
linkPropertyName: "File.ServerRelativeUrl",
|
||||||
|
displayName: "Name",
|
||||||
|
sorting: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File.TimeCreated",
|
||||||
|
displayName: "Created",
|
||||||
|
minWidth: 150,
|
||||||
|
render: (item: any) => {
|
||||||
|
const created = item["File.TimeCreated"];
|
||||||
|
if (created) {
|
||||||
|
const createdDate = moment(created);
|
||||||
|
return <span>{createdDate.format('DD/MM/YYYY HH:mm:ss')}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param props
|
||||||
|
*/
|
||||||
|
constructor(props: IPnPControlsProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
items: [],
|
||||||
|
loading: false,
|
||||||
|
showPlaceholder: (this.props.list === null || this.props.list === "")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* componentDidMount lifecycle hook
|
||||||
|
*/
|
||||||
|
public componentDidMount() {
|
||||||
|
if (this.props.list !== null && this.props.list !== "") {
|
||||||
|
this._getListItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* componentDidUpdate lifecycle hook
|
||||||
|
* @param nextProps
|
||||||
|
* @param nextState
|
||||||
|
*/
|
||||||
|
public componentDidUpdate(prevProps: IPnPControlsProps, prevState: IPnpControlsState) {
|
||||||
|
if (this.props.list !== prevProps.list || this.props.terms !== prevProps.terms) {
|
||||||
|
if (this.props.list !== null && this.props.list !== "") {
|
||||||
|
this._getListItems();
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
showPlaceholder: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves items for the specified list
|
||||||
|
* @param listId
|
||||||
|
*/
|
||||||
|
private _getListItems() {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let restApi = `${this.props.context.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this.props.list.toString()}')/items?$expand=File`;
|
||||||
|
|
||||||
|
// Check if results need to be filtered
|
||||||
|
if (typeof this.props.terms !== "undefined" && this.props.terms !== null && this.props.terms.length > 0) {
|
||||||
|
// Get the first term (single selection)
|
||||||
|
const term = this.props.terms[0];
|
||||||
|
// Add the filter to the restApi URL
|
||||||
|
restApi += `,TaxCatchAll&$select=*,TaxCatchAll/Term&$filter=TaxCatchAll/Term eq '${term.name}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.context.spHttpClient.get(restApi, SPHttpClient.configurations.v1)
|
||||||
|
.then(resp => { return resp.json(); })
|
||||||
|
.then(items => {
|
||||||
|
console.log('List Items:', items);
|
||||||
|
this.setState({
|
||||||
|
items: items.value ? items.value : [],
|
||||||
|
loading: false,
|
||||||
|
showPlaceholder: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens the web part property pane
|
||||||
|
*/
|
||||||
|
private _configureWebPart() {
|
||||||
|
this.props.context.propertyPane.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React render method
|
||||||
|
*/
|
||||||
|
public render(): React.ReactElement<IPnPControlsProps> {
|
||||||
|
// Check if placeholder needs to be shown
|
||||||
|
if (this.state.showPlaceholder) {
|
||||||
|
return (
|
||||||
|
<Placeholder
|
||||||
|
iconName="Edit"
|
||||||
|
iconText="List view web part configuration"
|
||||||
|
description="Please configure the web part before you can show the list view."
|
||||||
|
buttonLabel="Configure"
|
||||||
|
onConfigure={this._configureWebPart.bind(this)} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
this.state.loading ?
|
||||||
|
(
|
||||||
|
<Spinner size={SpinnerSize.large} label="Retrieving results ..." />
|
||||||
|
) : (
|
||||||
|
this.state.items.length === 0 ?
|
||||||
|
(
|
||||||
|
<Placeholder
|
||||||
|
iconName="InfoSolid"
|
||||||
|
iconText="No items found"
|
||||||
|
description="The list or library you selected does not contain items." />
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<p className="ms-font-xl">{this.props.description}</p>
|
||||||
|
<ListView items={this.state.items} viewFields={this._viewFields} iconFieldName="File.ServerRelativeUrl" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
define([], function () {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Web Part Configuration",
|
||||||
|
"BasicGroupName": "PnP Controls",
|
||||||
|
"DescriptionFieldLabel": "List view title"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
declare interface IPnPControlsWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
DescriptionFieldLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'PnPControlsWebPartStrings' {
|
||||||
|
const strings: IPnPControlsWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
describe('PnPControlsWebPart', () => {
|
||||||
|
it('should do something', () => {
|
||||||
|
assert.ok(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"types": [
|
||||||
|
"es6-promise",
|
||||||
|
"es6-collections",
|
||||||
|
"webpack-env"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Type definitions for Microsoft ODSP projects
|
||||||
|
// Project: ODSP
|
||||||
|
|
||||||
|
/* Global definition for UNIT_TEST builds
|
||||||
|
Code that is wrapped inside an if(UNIT_TEST) {...}
|
||||||
|
block will not be included in the final bundle when the
|
||||||
|
--ship flag is specified */
|
||||||
|
declare const UNIT_TEST: boolean;
|
||||||
|
|
||||||
|
/* Global defintion for SPO builds */
|
||||||
|
declare const DATACENTER: boolean;
|
|
@ -0,0 +1 @@
|
||||||
|
/// <reference path="@ms/odsp.d.ts" />
|
|
@ -29,6 +29,7 @@ Version|Date|Comments
|
||||||
1.0.3|August 12, 2017|Added external scripts functionnality
|
1.0.3|August 12, 2017|Added external scripts functionnality
|
||||||
1.0.4|August 31, 2017|Fixed a bug where tenant sites/subsites were missing from the **Web Url** dropdown
|
1.0.4|August 31, 2017|Fixed a bug where tenant sites/subsites were missing from the **Web Url** dropdown
|
||||||
1.0.5|September 1st, 2017|Added a **Site Url** parameter next to the **Web Url** parameter in order to narrow down the results
|
1.0.5|September 1st, 2017|Added a **Site Url** parameter next to the **Web Url** parameter in order to narrow down the results
|
||||||
|
1.0.6|September 19, 2017| Upgraded to SharePoint drop 1.2.0 and added the site url and web url preselection when adding the WebPart for the first time on a page. Also fixed a bug with fields that had spaces in their internal names (automatically replaced with `_x0020_` by SharePoint).
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
@ -37,7 +38,8 @@ Version|Date|Comments
|
||||||
|
|
||||||
### Cross site collection
|
### Cross site collection
|
||||||
|
|
||||||
The WebPart uses the search in order to get all sites under the current domain, which makes it possible to query not only subsites but other site collections and their subsites as well.
|
The WebPart uses the search in order to get all sites under the current domain, which makes it possible to query not only subsites but other site collections and their subsites as well. By default, the current site collection and the current web on which the user is adding the WebPart will be pre-selected automatically.
|
||||||
|
|
||||||
<img src="Misc/allsites_v2.gif" />
|
<img src="Misc/allsites_v2.gif" />
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
{
|
{
|
||||||
"entries": [
|
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
{
|
"version": "2.0",
|
||||||
"entry": "./lib/webparts/contentQuery/ContentQueryWebPart.js",
|
"bundles": {
|
||||||
"manifest": "./src/webparts/contentQuery/ContentQueryWebPart.manifest.json",
|
"content-query-bundle": {
|
||||||
"outputPath": "./dist/content-query.bundle.js"
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/contentQuery/ContentQueryWebPart.js",
|
||||||
|
"manifest": "./src/webparts/contentQuery/ContentQueryWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"externals": {},
|
|
||||||
"localizedResources": {
|
"localizedResources": {
|
||||||
"contentQueryStrings": "webparts/contentQuery/loc/{locale}.js"
|
"contentQueryStrings": "lib/webparts/contentQuery/loc/{locale}.js"
|
||||||
}
|
},
|
||||||
}
|
"externals": {}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"solution": {
|
"solution": {
|
||||||
"name": "React Content Query",
|
"name": "React Content Query",
|
||||||
"id": "00406271-0276-406f-9666-512623eb6709",
|
"id": "00406271-0276-406f-9666-512623eb6709",
|
||||||
"version": "1.0.5.0"
|
"version": "1.0.6.0"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"zippedPackage": "solution/react-content-query-webpart.sppkg"
|
"zippedPackage": "solution/react-content-query-webpart.sppkg"
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
{
|
{
|
||||||
|
"cdnBasePath": "https://publiccdn.sharepointonline.com/spptechnologies.sharepoint.com/110700492eeea162ee5bad0f35b1f0061ded8bf436ce0199efe2a4d24109e1c0df1ec594/react-content-query-1.0.6"
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "react-content-query",
|
"name": "react-content-query",
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/sp-core-library": "~1.1.0",
|
"@microsoft/sp-core-library": "~1.2.0",
|
||||||
"@microsoft/sp-webpart-base": "~1.1.1",
|
"@microsoft/sp-webpart-base": "~1.2.0",
|
||||||
"@types/handlebars": "4.0.32",
|
"@types/handlebars": "4.0.32",
|
||||||
"@types/react": "0.14.46",
|
"@types/react": "15.0.38",
|
||||||
"@types/react-addons-shallow-compare": "0.14.17",
|
"@types/react-addons-shallow-compare": "0.14.17",
|
||||||
"@types/react-addons-test-utils": "0.14.15",
|
"@types/react-addons-test-utils": "0.14.15",
|
||||||
"@types/react-addons-update": "0.14.14",
|
"@types/react-addons-update": "0.14.14",
|
||||||
|
@ -23,9 +23,9 @@
|
||||||
"react-dom": "15.4.2"
|
"react-dom": "15.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/sp-build-web": "~1.1.0",
|
"@microsoft/sp-build-web": "~1.2.0",
|
||||||
"@microsoft/sp-module-interfaces": "~1.1.0",
|
"@microsoft/sp-module-interfaces": "~1.2.0",
|
||||||
"@microsoft/sp-webpart-workbench": "~1.1.0",
|
"@microsoft/sp-webpart-workbench": "~1.2.0",
|
||||||
"@types/chai": ">=3.4.34 <3.6.0",
|
"@types/chai": ">=3.4.34 <3.6.0",
|
||||||
"@types/mocha": ">=2.2.33 <2.6.0",
|
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||||
"awesome-typescript-loader": "^3.2.1",
|
"awesome-typescript-loader": "^3.2.1",
|
||||||
|
|
Binary file not shown.
|
@ -161,7 +161,7 @@ export class CamlQueryHelper {
|
||||||
}
|
}
|
||||||
else if (filter.operator == QueryFilterOperator.ContainsAny || filterUsers == null)
|
else if (filter.operator == QueryFilterOperator.ContainsAny || filterUsers == null)
|
||||||
{
|
{
|
||||||
let values = filterUsers != null ? filterUsers.map(x => Text.format("<Value Type='Integer'>{0}</Value>", x.value)).join('') : '';
|
let values = filterUsers != null ? filterUsers.map(x => Text.format("<Value Type='Integer'>{0}</Value>", x.optionalText)).join('') : '';
|
||||||
filterOutput = Text.format("<In><FieldRef Name='{0}' LookupId='TRUE' /><Values>{1}</Values></In>", filter.field.internalName, values);
|
filterOutput = Text.format("<In><FieldRef Name='{0}' LookupId='TRUE' /><Values>{1}</Values></In>", filter.field.internalName, values);
|
||||||
}
|
}
|
||||||
else if (filter.operator == QueryFilterOperator.ContainsAll)
|
else if (filter.operator == QueryFilterOperator.ContainsAll)
|
||||||
|
@ -244,8 +244,8 @@ export class CamlQueryHelper {
|
||||||
let digit = parseInt(operatorSplit[operatorSplit.length - 1].replace("[", "").replace("]", "").trim()) * addOrRemove;
|
let digit = parseInt(operatorSplit[operatorSplit.length - 1].replace("[", "").replace("]", "").trim()) * addOrRemove;
|
||||||
let dt = new Date();
|
let dt = new Date();
|
||||||
dt.setDate(dt.getDate() + digit);
|
dt.setDate(dt.getDate() + digit);
|
||||||
let formattedDate = moment(dt).format("YYYY-MM-DDTHH:mm:ss\\Z");
|
let formatDate = moment(dt).format("YYYY-MM-DDTHH:mm:ss\\Z");
|
||||||
filterValue = filterValue.replace(result, formattedDate);
|
filterValue = filterValue.replace(result, formatDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,10 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
|
|
||||||
this.searchService.getSitesStartingWith(serverUrl)
|
this.searchService.getSitesStartingWith(serverUrl)
|
||||||
.then((urls) => {
|
.then((urls) => {
|
||||||
|
// Adds the current site collection url to the ones returned by the search (in case the current site isn't indexed yet)
|
||||||
|
this.ensureUrl(urls, this.context.pageContext.site.absoluteUrl);
|
||||||
|
|
||||||
|
// Builds the IDropdownOption[] based on the urls
|
||||||
let options:IDropdownOption[] = [ { key: "", text: strings.SiteUrlFieldPlaceholder } ];
|
let options:IDropdownOption[] = [ { key: "", text: strings.SiteUrlFieldPlaceholder } ];
|
||||||
let urlOptions:IDropdownOption[] = urls.sort().map((url) => {
|
let urlOptions:IDropdownOption[] = urls.sort().map((url) => {
|
||||||
let serverRelativeUrl = !isEmpty(url.replace(serverUrl, '')) ? url.replace(serverUrl, '') : '/';
|
let serverRelativeUrl = !isEmpty(url.replace(serverUrl, '')) ? url.replace(serverUrl, '') : '/';
|
||||||
|
@ -201,6 +205,12 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
|
|
||||||
this.searchService.getWebsFromSite(siteUrl)
|
this.searchService.getWebsFromSite(siteUrl)
|
||||||
.then((urls) => {
|
.then((urls) => {
|
||||||
|
// If querying the current site, adds the current site collection url to the ones returned by the search (in case the current web isn't indexed yet)
|
||||||
|
if(siteUrl.toLowerCase().trim() === this.context.pageContext.site.absoluteUrl.toLowerCase().trim()) {
|
||||||
|
this.ensureUrl(urls, this.context.pageContext.web.absoluteUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds the IDropdownOption[] based on the urls
|
||||||
let options:IDropdownOption[] = [ { key: "", text: strings.WebUrlFieldPlaceholder } ];
|
let options:IDropdownOption[] = [ { key: "", text: strings.WebUrlFieldPlaceholder } ];
|
||||||
let urlOptions:IDropdownOption[] = urls.sort().map((url) => {
|
let urlOptions:IDropdownOption[] = urls.sort().map((url) => {
|
||||||
let siteRelativeUrl = !isEmpty(url.replace(siteUrl, '')) ? url.replace(siteUrl, '') : '/';
|
let siteRelativeUrl = !isEmpty(url.replace(siteUrl, '')) ? url.replace(siteUrl, '') : '/';
|
||||||
|
@ -372,7 +382,7 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
let users: any[] = JSON.parse(data.value);
|
let users: any[] = JSON.parse(data.value);
|
||||||
let userSuggestions:IPersonaProps[] = users.map((user) => { return {
|
let userSuggestions:IPersonaProps[] = users.map((user) => { return {
|
||||||
primaryText: user.DisplayText,
|
primaryText: user.DisplayText,
|
||||||
value: user.EntityData.SPUserID || user.EntityData.SPGroupID
|
optionalText: user.EntityData.SPUserID || user.EntityData.SPGroupID
|
||||||
}; });
|
}; });
|
||||||
resolve(this.removeUserSuggestionsDuplicates(userSuggestions, currentPersonas));
|
resolve(this.removeUserSuggestionsDuplicates(userSuggestions, currentPersonas));
|
||||||
})
|
})
|
||||||
|
@ -546,9 +556,11 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
let normalizedResult: any = {};
|
let normalizedResult: any = {};
|
||||||
|
|
||||||
for(let viewField of viewFields) {
|
for(let viewField of viewFields) {
|
||||||
|
let spacesFormattedName = viewField.replace(new RegExp("_x0020_", "g"), "_x005f_x0020_x005f_");
|
||||||
|
|
||||||
normalizedResult[viewField] = {
|
normalizedResult[viewField] = {
|
||||||
textValue: result.FieldValuesAsText[viewField],
|
textValue: result.FieldValuesAsText[spacesFormattedName],
|
||||||
htmlValue: result.FieldValuesAsHtml[viewField],
|
htmlValue: result.FieldValuesAsHtml[spacesFormattedName],
|
||||||
rawValue: result[viewField] || result[viewField + 'Id']
|
rawValue: result[viewField] || result[viewField + 'Id']
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -626,7 +638,7 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
let trimmedUsers: IPersonaProps[] = [];
|
let trimmedUsers: IPersonaProps[] = [];
|
||||||
|
|
||||||
for(let user of users) {
|
for(let user of users) {
|
||||||
let isDuplicate = currentUsers.filter((u) => { return u.value === user.value; }).length > 0;
|
let isDuplicate = currentUsers.filter((u) => { return u.optionalText === user.optionalText; }).length > 0;
|
||||||
|
|
||||||
if(!isDuplicate) {
|
if(!isDuplicate) {
|
||||||
trimmedUsers.push(user);
|
trimmedUsers.push(user);
|
||||||
|
@ -654,4 +666,19 @@ export class ContentQueryService implements IContentQueryService {
|
||||||
}
|
}
|
||||||
return trimmedTerms;
|
return trimmedTerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Makes sure the specified url is in the given collection, otherwise adds it
|
||||||
|
* @param urls : An array of urls
|
||||||
|
* @param urlToEnsure : The url that needs to be ensured
|
||||||
|
***************************************************************************/
|
||||||
|
private ensureUrl(urls: string[], urlToEnsure: string) {
|
||||||
|
urlToEnsure = urlToEnsure.toLowerCase().trim();
|
||||||
|
let urlExist = urls.filter((u) => { return u.toLowerCase().trim() === urlToEnsure; }).length > 0;
|
||||||
|
|
||||||
|
if(!urlExist) {
|
||||||
|
urls.push(urlToEnsure);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -166,7 +166,7 @@ export class SearchService {
|
||||||
let pathCell = result.Cells.filter((cell) => { return cell.Key == "Path"; })[0];
|
let pathCell = result.Cells.filter((cell) => { return cell.Key == "Path"; })[0];
|
||||||
pathIndex = result.Cells.indexOf(pathCell);
|
pathIndex = result.Cells.indexOf(pathCell);
|
||||||
}
|
}
|
||||||
urls.push(result.Cells[pathIndex].Value);
|
urls.push(result.Cells[pathIndex].Value.toLowerCase().trim());
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ export class AsyncChecklist extends React.Component<IAsyncChecklistProps, IAsync
|
||||||
* @param checked : Whether the checkbox is not checked or not
|
* @param checked : Whether the checkbox is not checked or not
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
private onCheckboxChange(ev?: React.FormEvent<HTMLInputElement>, checked?: boolean) {
|
private onCheckboxChange(ev?: React.FormEvent<HTMLInputElement>, checked?: boolean) {
|
||||||
let checkboxKey = ev.currentTarget.attributes.getNamedItem('data').value;
|
let checkboxKey = ev.currentTarget.attributes.getNamedItem('value').value;
|
||||||
let itemIndex = this.checkedItems.indexOf(checkboxKey);
|
let itemIndex = this.checkedItems.indexOf(checkboxKey);
|
||||||
|
|
||||||
if(checked) {
|
if(checked) {
|
||||||
|
@ -127,12 +127,12 @@ export class AsyncChecklist extends React.Component<IAsyncChecklistProps, IAsync
|
||||||
|
|
||||||
const checklistItems = this.state.items.map((item, index) => {
|
const checklistItems = this.state.items.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<Checkbox data={ item.id }
|
<Checkbox id={ item.id }
|
||||||
label={ item.label }
|
label={ item.label }
|
||||||
defaultChecked={ this.isCheckboxChecked(item.id) }
|
defaultChecked={ this.isCheckboxChecked(item.id) }
|
||||||
disabled={ this.props.disable }
|
disabled={ this.props.disable }
|
||||||
onChange={ this.onCheckboxChange.bind(this) }
|
onChange={ this.onCheckboxChange.bind(this) }
|
||||||
inputProps={ { data: item.id } }
|
inputProps={ { value: item.id } }
|
||||||
className={ styles.checklistItem }
|
className={ styles.checklistItem }
|
||||||
key={ index } />
|
key={ index } />
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,9 +3,11 @@ import { Text } from '@microsoft/sp-core-library
|
||||||
import { Dropdown, IDropdownOption, Spinner } from 'office-ui-fabric-react';
|
import { Dropdown, IDropdownOption, Spinner } from 'office-ui-fabric-react';
|
||||||
import { IAsyncDropdownProps } from './IAsyncDropdownProps';
|
import { IAsyncDropdownProps } from './IAsyncDropdownProps';
|
||||||
import { IAsyncDropdownState } from './IAsyncDropdownState';
|
import { IAsyncDropdownState } from './IAsyncDropdownState';
|
||||||
|
import { cloneDeep } from '@microsoft/sp-lodash-subset';
|
||||||
|
|
||||||
export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDropdownState> {
|
export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDropdownState> {
|
||||||
|
|
||||||
|
|
||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
* Component's constructor
|
* Component's constructor
|
||||||
* @param props
|
* @param props
|
||||||
|
@ -17,6 +19,7 @@ export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDr
|
||||||
this.state = {
|
this.state = {
|
||||||
processed: false,
|
processed: false,
|
||||||
options: new Array<IDropdownOption>(),
|
options: new Array<IDropdownOption>(),
|
||||||
|
selectedKey: props.selectedKey,
|
||||||
error: null
|
error: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -47,14 +50,16 @@ export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDr
|
||||||
this.setState({
|
this.setState({
|
||||||
processed: false,
|
processed: false,
|
||||||
error: null,
|
error: null,
|
||||||
options: new Array<IDropdownOption>()
|
options: new Array<IDropdownOption>(),
|
||||||
|
selectedKey: null
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.loadOptions().then((options: IDropdownOption[]) => {
|
this.props.loadOptions().then((options: IDropdownOption[]) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
processed: true,
|
processed: true,
|
||||||
error: null,
|
error: null,
|
||||||
options: options
|
options: options,
|
||||||
|
selectedKey: this.props.selectedKey
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
|
@ -67,6 +72,32 @@ export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* Temporary fix because of an issue introducted in office-ui-fabric-react 4.32.0 :
|
||||||
|
* https://github.com/OfficeDev/office-ui-fabric-react/issues/2719
|
||||||
|
* Issue has been resolved but SPFX still refers to 4.32.0, so this is a temporary fix
|
||||||
|
* while waiting for SPFX to use a more recent version of office-ui-fabric-react
|
||||||
|
*************************************************************************************/
|
||||||
|
private onChanged(option: IDropdownOption, index?: number): void {
|
||||||
|
|
||||||
|
// reset previously selected options
|
||||||
|
const options: IDropdownOption[] = this.state.options;
|
||||||
|
options.forEach((o: IDropdownOption): void => {
|
||||||
|
if (o.key !== option.key) {
|
||||||
|
o.selected = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState((prevState: IAsyncDropdownState, props: IAsyncDropdownProps): IAsyncDropdownState => {
|
||||||
|
prevState.options = options;
|
||||||
|
prevState.selectedKey = option.key;
|
||||||
|
return prevState;
|
||||||
|
});
|
||||||
|
if (this.props.onChanged) {
|
||||||
|
this.props.onChanged(option, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
* Renders the the AsyncDropdown component
|
* Renders the the AsyncDropdown component
|
||||||
*************************************************************************************/
|
*************************************************************************************/
|
||||||
|
@ -79,8 +110,8 @@ export class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDr
|
||||||
<div>
|
<div>
|
||||||
<Dropdown label={this.props.label}
|
<Dropdown label={this.props.label}
|
||||||
isDisabled={this.props.disabled}
|
isDisabled={this.props.disabled}
|
||||||
onChanged={this.props.onChanged}
|
onChanged={this.onChanged.bind(this)}
|
||||||
selectedKey={this.props.selectedKey}
|
selectedKey={this.state.selectedKey}
|
||||||
options={this.state.options} />
|
options={this.state.options} />
|
||||||
|
|
||||||
{loading}
|
{loading}
|
||||||
|
|
|
@ -3,5 +3,6 @@ import { IDropdownOption } from 'office-ui-fabric-react';
|
||||||
export interface IAsyncDropdownState {
|
export interface IAsyncDropdownState {
|
||||||
processed: boolean;
|
processed: boolean;
|
||||||
options: IDropdownOption[];
|
options: IDropdownOption[];
|
||||||
|
selectedKey: string | number;
|
||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
|
@ -19,20 +19,29 @@ $lightgray: #f5f5f5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div[class~="ms-BasePicker-text"] {
|
:global .ms-BasePicker-text {
|
||||||
background: #fff;
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .ms-Checkbox {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .ms-Persona-details {
|
||||||
|
max-width: 165px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peoplePicker {
|
.peoplePicker {
|
||||||
&.disabled {
|
&.disabled {
|
||||||
div[class~="ms-PickerPersona-container"] {
|
:global .ms-BasePicker-text {
|
||||||
display: none;
|
background-color: #ccc;
|
||||||
|
|
||||||
|
:global .ms-BasePicker-input,
|
||||||
|
:global .ms-PickerPersona-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span[class~="ms-TagItem-text"] {
|
|
||||||
max-width: 201px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
"id": "46edf08f-95c7-4ca7-9146-6471f9f471be",
|
"id": "46edf08f-95c7-4ca7-9146-6471f9f471be",
|
||||||
"alias": "ContentQueryWebPart",
|
"alias": "ContentQueryWebPart",
|
||||||
"componentType": "WebPart",
|
"componentType": "WebPart",
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"manifestVersion": 2,
|
"manifestVersion": 2,
|
||||||
|
|
||||||
"preconfiguredEntries": [{
|
"preconfiguredEntries": [{
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default class ContentQueryWebPart extends BaseClientSideWebPart<IContentQ
|
||||||
* Returns the WebPart's version
|
* Returns the WebPart's version
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
protected get dataVersion(): Version {
|
protected get dataVersion(): Version {
|
||||||
return Version.parse('1.0.5');
|
return Version.parse('1.0.6');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ export default class ContentQueryWebPart extends BaseClientSideWebPart<IContentQ
|
||||||
protected onInit(): Promise<void> {
|
protected onInit(): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
this.ContentQueryService = new ContentQueryService(this.context, this.context.spHttpClient);
|
this.ContentQueryService = new ContentQueryService(this.context, this.context.spHttpClient);
|
||||||
|
this.properties.siteUrl = this.properties.siteUrl ? this.properties.siteUrl : this.context.pageContext.site.absoluteUrl.toLowerCase().trim();
|
||||||
|
this.properties.webUrl = this.properties.webUrl ? this.properties.webUrl : this.context.pageContext.web.absoluteUrl.toLocaleLowerCase().trim();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -413,31 +415,6 @@ export default class ContentQueryWebPart extends BaseClientSideWebPart<IContentQ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* Resets dependent property panes if needed
|
|
||||||
***************************************************************************/
|
|
||||||
private resetDependentPropertyPanes(propertyPath: string): void {
|
|
||||||
if(propertyPath == ContentQueryConstants.propertySiteUrl) {
|
|
||||||
this.resetWebUrlPropertyPane();
|
|
||||||
this.resetListTitlePropertyPane();
|
|
||||||
this.resetOrderByPropertyPane();
|
|
||||||
this.resetFiltersPropertyPane();
|
|
||||||
this.resetViewFieldsPropertyPane();
|
|
||||||
}
|
|
||||||
else if(propertyPath == ContentQueryConstants.propertyWebUrl) {
|
|
||||||
this.resetListTitlePropertyPane();
|
|
||||||
this.resetOrderByPropertyPane();
|
|
||||||
this.resetFiltersPropertyPane();
|
|
||||||
this.resetViewFieldsPropertyPane();
|
|
||||||
}
|
|
||||||
else if (propertyPath == ContentQueryConstants.propertyListTitle) {
|
|
||||||
this.resetOrderByPropertyPane();
|
|
||||||
this.resetFiltersPropertyPane();
|
|
||||||
this.resetViewFieldsPropertyPane();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Validates the templateUrl property
|
* Validates the templateUrl property
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
@ -483,13 +460,38 @@ export default class ContentQueryWebPart extends BaseClientSideWebPart<IContentQ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Resets dependent property panes if needed
|
||||||
|
***************************************************************************/
|
||||||
|
private resetDependentPropertyPanes(propertyPath: string): void {
|
||||||
|
if(propertyPath == ContentQueryConstants.propertySiteUrl) {
|
||||||
|
this.resetWebUrlPropertyPane();
|
||||||
|
this.resetListTitlePropertyPane();
|
||||||
|
this.resetOrderByPropertyPane();
|
||||||
|
this.resetFiltersPropertyPane();
|
||||||
|
this.resetViewFieldsPropertyPane();
|
||||||
|
}
|
||||||
|
else if(propertyPath == ContentQueryConstants.propertyWebUrl) {
|
||||||
|
this.resetListTitlePropertyPane();
|
||||||
|
this.resetOrderByPropertyPane();
|
||||||
|
this.resetFiltersPropertyPane();
|
||||||
|
this.resetViewFieldsPropertyPane();
|
||||||
|
}
|
||||||
|
else if (propertyPath == ContentQueryConstants.propertyListTitle) {
|
||||||
|
this.resetOrderByPropertyPane();
|
||||||
|
this.resetFiltersPropertyPane();
|
||||||
|
this.resetViewFieldsPropertyPane();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Resets the List Title property pane and re-renders it
|
* Resets the List Title property pane and re-renders it
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
private resetWebUrlPropertyPane() {
|
private resetWebUrlPropertyPane() {
|
||||||
Log.verbose(this.logSource, "Resetting 'webUrl' property...", this.context.serviceScope);
|
Log.verbose(this.logSource, "Resetting 'webUrl' property...", this.context.serviceScope);
|
||||||
|
|
||||||
this.properties.webUrl = null;
|
this.properties.webUrl = "";
|
||||||
this.ContentQueryService.clearCachedWebUrlOptions();
|
this.ContentQueryService.clearCachedWebUrlOptions();
|
||||||
update(this.properties, ContentQueryConstants.propertyWebUrl, (): any => { return this.properties.webUrl; });
|
update(this.properties, ContentQueryConstants.propertyWebUrl, (): any => { return this.properties.webUrl; });
|
||||||
this.webUrlDropdown.properties.selectedKey = "";
|
this.webUrlDropdown.properties.selectedKey = "";
|
||||||
|
|
|
@ -7,10 +7,8 @@ $container-border: 1px solid #dadada;
|
||||||
border: $container-border;
|
border: $container-border;
|
||||||
padding: 20px 20px 15px 20px;
|
padding: 20px 20px 15px 20px;
|
||||||
|
|
||||||
div[class*='ms-Checkbox'] {
|
:global .ms-Checkbox {
|
||||||
&:first-child {
|
margin-top: 10px;
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.cqwpError {
|
.cqwpError {
|
||||||
|
|
Loading…
Reference in New Issue