Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add component "PropertyFieldSitePicker" #208

Merged
merged 2 commits into from
Nov 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/documentation/docs/assets/sitepicker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions docs/documentation/docs/controls/PropertyFieldSitePicker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# PropertyFieldPeoplePicker control

This control generates a site picker that can be used in the property pane of your SharePoint Framework web parts.

**Searching for sites**

![Site picker](../assets/sitepicker.png)

**Selected sites**

![Site picker](../assets/sitepicker-selected.png)

## How to use this control in your solutions

1. Check that you installed the `@pnp/spfx-property-controls` dependency. Check out The [getting started](../../#getting-started) page for more information about installing the dependency.
2. Import the following modules to your component:

```TypeScript
import { PropertyFieldSitePicker } from '@pnp/spfx-property-controls/lib/PropertyFieldSitePicker';
```

3. Create a new property for your web part, for example:

```TypeScript
import { IPropertyFieldSite } from "@pnp/spfx-property-controls/lib/PropertyFieldSitePicker";

export interface IPropertyControlsTestWebPartProps {
sites: IPropertyFieldSite[];
}
```

4. Add the custom property control to the `groupFields` of the web part property pane configuration:

```TypeScript
PropertyFieldSitePicker('sites', {
label: 'Select sites',
initialSites: this.properties.sites,
context: this.context,
deferredValidationTime: 500,
multiSelect: true,
onPropertyChange: this.onPropertyPaneFieldChanged,
properties: this.properties,
key: 'sitesFieldId'
})
```

## Implementation

The `PropertyFieldSitePicker` control can be configured with the following properties:

| Property | Type | Required | Description |
| ---- | ---- | ---- | ---- |
| label | string | yes | Property field label displayed on top. |
| disabled | boolean | no | Specify if the control needs to be disabled. |
| context | WebPartContext | yes | Context of the current web part. |
| initialSites | IPropertyFieldGroupOrPerson[] | no | Intial sites to load in the site picker (optional). |
| multiSelect | boolean | no | Define if you want to allow multi sites selection. (optional, false by default). |
| onPropertyChange | function | yes | Defines a onPropertyChange function to raise when the sites get changed. |
| properties | any | yes | Parent web part properties, this object is use to update the property value. |
| key | string | yes | An unique key that indicates the identity of this control. |
| onGetErrorMessage | function | no | The method is used to get the validation error message and determine whether the input value is valid or not. See [this documentation](https://dev.office.com/sharepoint/docs/spfx/web-parts/guidance/validate-web-part-property-values) to learn how to use it. |
| deferredValidationTime | number | no | Control will start to validate after users stop typing for `deferredValidationTime` milliseconds. Default value is 200. |

Interface `IPropertyFieldSite`

| Property | Type | Required | Description |
| ---- | ---- | ---- | ---- |
| id | string | no | The ID of the site |
| title | string | no | Site's display name |
| url | string | no | URL to the site |

![](https://telemetry.sharepointpnp.com/sp-dev-fx-property-controls/wiki/PropertyFieldPeoplePicker)
1 change: 1 addition & 0 deletions docs/documentation/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nav:
- PropertyFieldTermPicker: 'controls/PropertyFieldTermPicker.md'
- PropertyPanePropertyEditor: 'controls/PropertyPanePropertyEditor.md'
- PropertyPaneWebPartInformation: 'controls/PropertyPaneWebPartInformation.md'
- PropertyFieldSitePicker: 'controls/PropertyFieldSitePicker.md'
- 'Controls with callout':
- PropertyFieldButtonWithCallout: 'controls/PropertyFieldButtonWithCallout.md'
- PropertyFieldCheckboxWithCallout: 'controls/PropertyFieldCheckboxWithCallout.md'
Expand Down
1 change: 1 addition & 0 deletions src/PropertyFieldSitePicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './propertyFields/sitePicker/index';
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './PropertyFieldTextWithCallout';
export * from './PropertyFieldToggleWithCallout';
export * from './PropertyPaneWebPartInformation';
export * from './PropertyPanePropertyEditor';
export * from './PropertyFieldSitePicker';
7 changes: 5 additions & 2 deletions src/loc/en-us.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
define([], function() {
define([], function () {
return {
ApplyButtonLabel: "Apply",
ImportButtonLabel: "Import",
Expand Down Expand Up @@ -74,6 +74,9 @@ define([], function() {
InvalidUrlError: "The provided URL is not valid",
DescriptionLabel: "Description",
MoreInfoLabel: "More info",
AboutGroupLabel: "About"
AboutGroupLabel: "About",
SitePickerSearchBoxPlaceholder: "Search...",
SitePickerNoResults: "No search results",
SitePickerSitesChosen: "Website(s) selected"
}
});
9 changes: 6 additions & 3 deletions src/loc/mystrings.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
declare interface IPropertyControlStrings {



DescriptionLabel: string;
MoreInfoLabel: string;

// PeoplePicker labels
PeoplePickerSuggestedContacts: string;
PeoplePickerSuggestedGroups: string;
Expand Down Expand Up @@ -95,6 +93,11 @@ declare interface IPropertyControlStrings {
ImportButtonLabel: string;
ExportButtonLabel: string;
JsonFileRequiredMessage: string;

// Site Picker labels
SitePickerSearchBoxPlaceholder: string;
SitePickerNoResults: string;
SitePickerSitesChosen: string;
}

declare module 'PropertyControlStrings' {
Expand Down
84 changes: 84 additions & 0 deletions src/propertyFields/sitePicker/IPropertyFieldSitePicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { IWebPartContext } from '@microsoft/sp-webpart-base';

export interface IPropertyFieldSite {
/**
* ID of the site
*/
id?: string;
/**
* Title
*/
title?: string;
/**
* Base URL
*/
url?: string;
}

export interface IPropertyFieldSitePickerProps {
/**
* Property field label
*/
label: string;
/**
* An UNIQUE key indicates the identity of this control
*/
key?: string;
/**
* Specify if the control needs to be disabled
*/
disabled?: boolean;
/**
* Web Part context
*/
context: IWebPartContext;
/**
* Intial data to load in the 'Selected sites' area (optional)
*/
initialSites: IPropertyFieldSite[];
/**
* Define if you want to allow multi site selection. True by default.
*/
multiSelect?: boolean;
/**
* Defines a onPropertyChange function to raise when the selected value changed.
* Normally this function must be always defined with the 'this.onPropertyChange'
* method of the web part object.
*/
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
/**
* Parent Web Part properties
*/
properties: any;
/**
* The method is used to get the validation error message and determine whether the input value is valid or not.
*
* When it returns string:
* - If valid, it returns empty string.
* - If invalid, it returns the error message string and the text field will
* show a red border and show an error message below the text field.
*
* When it returns Promise<string>:
* - The resolved value is display as error message.
* - The rejected, the value is thrown away.
*
*/
onGetErrorMessage?: (value: IPropertyFieldSite[]) => string | Promise<string>;
/**
* Custom Field will start to validate after users stop typing for `deferredValidationTime` milliseconds.
* Default value is 200.
*/
deferredValidationTime?: number;
}
/**
* Private properties of the PropertyFielSitePicker custom field.
* We separate public & private properties to include onRender & onDispose method waited
* by the PropertyFieldCustom, witout asking to the developer to add it when he's using
* the PropertyFieldSitePicker.
*
*/
export interface IPropertyFieldSitePickerPropsInternal extends IPropertyFieldSitePickerProps {
targetProperty: string;
onRender(elem: HTMLElement): void;
onDispose(elem: HTMLElement): void;
}
15 changes: 15 additions & 0 deletions src/propertyFields/sitePicker/IPropertyFieldSitePickerHost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IPropertyFieldSitePickerPropsInternal, IPropertyFieldSite } from './IPropertyFieldSitePicker';

/**
* PropertyFieldSitePickerHost properties interface
*/
export interface IPropertyFieldSitePickerHostProps extends IPropertyFieldSitePickerPropsInternal {
onChange: (targetProperty?: string, newValue?: any) => void;
}

export interface ISitePickerState {
siteSearchResults?: Array<IPropertyFieldSite>;
selectedSites?: Array<IPropertyFieldSite>;
isLoading: boolean;
errorMessage?: string;
}
111 changes: 111 additions & 0 deletions src/propertyFields/sitePicker/PropertyFieldSitePicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import {
IPropertyPaneField,
PropertyPaneFieldType,
IWebPartContext
} from '@microsoft/sp-webpart-base';
import { IPropertyFieldSitePickerPropsInternal, IPropertyFieldSite, IPropertyFieldSitePickerProps } from './IPropertyFieldSitePicker';
import { IPropertyFieldSitePickerHostProps } from './IPropertyFieldSitePickerHost';
import PropertyFieldSitePickerHost from './PropertyFieldSitePickerHost';

/**
* Represents a PropertyFieldSitePicker object
*/

class PropertyFieldSitePickerBuilder implements IPropertyPaneField<IPropertyFieldSitePickerPropsInternal> {

// Properties defined by IPropertyPaneField
public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;
public targetProperty: string;
public properties: IPropertyFieldSitePickerPropsInternal;

// Custom properties
private label: string;
private disabled: boolean = false;
private context: IWebPartContext;
private initialSites: IPropertyFieldSite[];
private multiSelect: boolean = false;
private onPropertyChange: (propertyPath: string, oldValue: any, newValue: any) => void;
private customProperties: any;
private key: string;
private onGetErrorMessage: (value: IPropertyFieldSite[]) => string | Promise<string>;
private deferredValidationTime: number = 200;

/**
* Constructor method
*/
public constructor(_targetProperty: string, _properties: IPropertyFieldSitePickerPropsInternal) {
this.render = this.render.bind(this);
this.label = _properties.label;
this.targetProperty = _properties.targetProperty;
this.properties = _properties;
this.properties.onDispose = this.dispose;
this.properties.onRender = this.render;
this.onPropertyChange = _properties.onPropertyChange;
this.context = _properties.context;
this.initialSites = _properties.initialSites;
this.customProperties = _properties.properties;
this.key = _properties.key;
this.onGetErrorMessage = _properties.onGetErrorMessage;

if (typeof _properties.disabled !== 'undefined') {
this.disabled = _properties.disabled;
}

if (_properties.deferredValidationTime) {
this.deferredValidationTime = _properties.deferredValidationTime;
}

if (typeof _properties.multiSelect !== "undefined") {
this.multiSelect = _properties.multiSelect;
}
}

/**
* Renders the PeoplePicker field content
*/
private render(elem: HTMLElement, ctx?: any, changeCallback?: (targetProperty?: string, newValue?: any) => void): void {
// Construct the JSX properties
const element: React.ReactElement<IPropertyFieldSitePickerHostProps> = React.createElement(PropertyFieldSitePickerHost, {
label: this.label,
disabled: this.disabled,
targetProperty: this.targetProperty,
initialSites: this.initialSites,
multiSelect: this.multiSelect,
onDispose: this.dispose,
onRender: this.render,
onChange: changeCallback,
onPropertyChange: this.onPropertyChange,
context: this.context,
properties: this.customProperties,
key: this.key,
onGetErrorMessage: this.onGetErrorMessage,
deferredValidationTime: this.deferredValidationTime
});

// Calls the REACT content generator
ReactDom.render(element, elem);
}

/**
* Disposes the current object
*/
private dispose(elem: HTMLElement): void { }
}

/**
* Helper method to create a Site Picker on the PropertyPane.
* @param targetProperty - Target property the site picker is associated to.
* @param properties - Strongly typed site Picker properties.
*/
export function PropertyFieldSitePicker(targetProperty: string, properties: IPropertyFieldSitePickerProps): IPropertyPaneField<IPropertyFieldSitePickerPropsInternal> {
// Calls the PropertyFieldSitePicker builder object
// This object will simulate a PropertyFieldCustom to manage his rendering process
return new PropertyFieldSitePickerBuilder(targetProperty, {
...properties,
targetProperty: targetProperty,
onDispose: null,
onRender: null
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.siteList {
list-style: none;
padding-left: 5px;
}

.bold {
font-weight: bold;
}
Loading