Skip to content

Commit

Permalink
Merge pull request #89 from SharePoint/dev
Browse files Browse the repository at this point in the history
Merge for 1.9.0 release
  • Loading branch information
estruyf authored Aug 10, 2018
2 parents dc30bfd + ff6bf11 commit bb6f47f
Show file tree
Hide file tree
Showing 24 changed files with 308 additions and 80 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
{
"versions": [
{
"version": "1.9.0",
"changes": {
"new": [],
"enhancements": [
"`PropertyFieldCollectionData`: Added custom validation for `string`, `number`, `icon`, and `URL` field types [#74](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/74)",
"`PropertyFieldCollectionData`: Add an option to specify a default value [#86](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/86)",
"`PropertyFieldCollectionData`: override placeholder for the inputs [#87](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/87)",
"`PropertyFieldCollectionData`: Hide save button when \"Add and save\" is shown [#88](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/88)"
],
"fixes": [
"`PropertyFieldMultiSelect`: fixed an issue where the control didn't retain the preselected values when dropdown options were provided async [#85](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/85)",
"`PropertyFieldOrder`: fixed an issue where items where provided async [#81](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/81)"
]
},
"contributions": []
},
{
"version": "1.8.0",
"changes": {
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Releases

## 1.9.0

**Enhancements**

- `PropertyFieldCollectionData`: Added custom validation for `string`, `number`, `icon`, and `URL` field types [#74](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/74)
- `PropertyFieldCollectionData`: Add an option to specify a default value [#86](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/86)
- `PropertyFieldCollectionData`: override placeholder for the inputs [#87](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/87)
- `PropertyFieldCollectionData`: Hide save button when "Add and save" is shown [#88](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/88)

**Fixes**

- `PropertyFieldMultiSelect`: fixed an issue where the control didn't retain the preselected values when dropdown options were provided async [#85](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/85)
- `PropertyFieldOrder`: fixed an issue where items where provided async [#81](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/81)

## 1.8.0

**Enhancements**
Expand Down
14 changes: 14 additions & 0 deletions docs/documentation/docs/about/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Releases

## 1.9.0

**Enhancements**

- `PropertyFieldCollectionData`: Added custom validation for `string`, `number`, `icon`, and `URL` field types [#74](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/74)
- `PropertyFieldCollectionData`: Add an option to specify a default value [#86](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/86)
- `PropertyFieldCollectionData`: override placeholder for the inputs [#87](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/87)
- `PropertyFieldCollectionData`: Hide save button when "Add and save" is shown [#88](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/88)

**Fixes**

- `PropertyFieldMultiSelect`: fixed an issue where the control didn't retain the preselected values when dropdown options were provided async [#85](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/85)
- `PropertyFieldOrder`: fixed an issue where items where provided async [#81](https://github.com/SharePoint/sp-dev-fx-property-controls/issues/81)

## 1.8.0

**Enhancements**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ Interface `ICustomCollectionField`
| ---- | ---- | ---- | ---- |
| id | string | yes | ID of the field. |
| title | string | yes | Title of the field. This will be used for the label in the table. |
| type | yes | CustomCollectionFieldType | Specifies the type of field to render. |
| required | no | boolean | Specify if the field is required. |
| options | no | [IDropdownOption[]](https://developer.microsoft.com/en-us/fabric#/components/dropdown) | Dropdown options. Only necessary when dropdown type is used. |
| type | CustomCollectionFieldType | yes | Specifies the type of field to render. |
| required | boolean | no | Specify if the field is required. |
| options | [IDropdownOption[]](https://developer.microsoft.com/en-us/fabric#/components/dropdown) | no | Dropdown options. Only necessary when dropdown type is used. |
| placeholder | string | no | Placehoder text which will be used for the input field. If not provided the input title will be used. |
| defaultValue | any | no | Specify a default value for the input field. |
| onGetErrorMessage | (value: any): string \| Promise<string> | no | The method is used to get the validation error message and determine whether the input value is valid or not. |

Enum `CustomCollectionFieldType`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The `PropertyFieldListPicker` control can be configured with the following prope
| showSelectAll | boolean | no | Specify if you want the Select All checkbox. By default this is set to `false` (mult-list picker only). |
| selectAllInList | boolean | no | Specify where to show the Select All checkbox. When false (default), checkbox is shown before the label, when true it is shown with the lists (mult-list picker only). |
| selectAllInListLabel | string | no | The label to use for the in list select all checkbox (mult-list picker only). |
| webAbsoluteUrl | string | no | Absolute Web Url of target site (user requires permissions) |
| onPropertyChange | function | yes | Defines a onPropertyChange function to raise when the date gets 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. |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@pnp/spfx-property-controls",
"description": "Reusable property pane controls for SharePoint Framework solutions",
"version": "1.8.0",
"version": "1.9.0",
"engines": {
"node": ">=0.10.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/common/telemetry/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const version: string = "1.8.0";
export const version: string = "1.9.0";
1 change: 1 addition & 0 deletions src/loc/en-us.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
define([], function () {
return {
propertyFieldMultiSelectNoOptions: "No options to select",
InvalidUrlError: "The provided URL is not valid",
// Common field labels
'SaveButtonLabel': 'Save',
Expand Down
1 change: 1 addition & 0 deletions src/loc/mystrings.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
declare interface IPropertyControlStrings {
propertyFieldMultiSelectNoOptions: string;
InvalidUrlError: string;
// PeoplePicker labels
PeoplePickerSuggestedContacts: string;
Expand Down
16 changes: 16 additions & 0 deletions src/propertyFields/collectionData/ICustomCollectionField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ export interface ICustomCollectionField {
* Dropdown options. Only nescessary when dropdown type is used.
*/
options?: IDropdownOption[];
/**
* Input placeholder text.
*/
placeholder?: string;
/**
* Default value for the field
*/
defaultValue?: 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, the field will show a red border
*/
onGetErrorMessage?: (value: any) => string | Promise<string>;
}

export enum CustomCollectionFieldType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
text-overflow: ellipsis;
outline: 0;
}

&.invalidField {
border-color: #a80000;
}
}

.iconField {
Expand All @@ -84,7 +88,7 @@
}
}

.urlField {
.collectionDataField {
> span {
display: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ICustomCollectionField, CustomCollectionFieldType, FieldValidator } fro
import { Dropdown } from 'office-ui-fabric-react';
import { CollectionIconField } from '../collectionIconField';
import { clone } from '@microsoft/sp-lodash-subset';
import { CollectionNumberField } from '../collectionNumberField';

export class CollectionDataItem extends React.Component<ICollectionDataItemProps, ICollectionDataItemState> {
private emptyItem: any = null;
Expand All @@ -21,7 +22,8 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
// Create an empty item with all properties
this.emptyItem = {};
for (const field of this.props.fields) {
this.emptyItem[field.id] = null;
// Assign default value or null to the emptyItem
this.emptyItem[field.id] = field.defaultValue || null;
}

this.state = {
Expand Down Expand Up @@ -171,8 +173,27 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
}
}

/**
* Allow custom field validation
*
* @param field
* @param value
*/
private fieldValidation = async (field: ICustomCollectionField, value: any): Promise<string> => {
let validation = "";
if (field.onGetErrorMessage) {
validation = await field.onGetErrorMessage(value);
}
// Store the field validation
this.validation[field.id] = validation === "";
// Trigger field change
this.onValueChanged(field.id, value);
return validation;
}

/**
* Render the field
*
* @param field
* @param item
*/
Expand All @@ -182,50 +203,55 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
return <Checkbox checked={item[field.id] ? item[field.id] : false}
onChange={(ev, value) => this.onValueChanged(field.id, value)} />;
case CustomCollectionFieldType.dropdown:
return <Dropdown placeHolder={field.title}
return <Dropdown placeHolder={field.placeholder || field.title}
options={field.options}
selectedKey={item[field.id] || null}
required={field.required}
onChanged={(opt) => this.onValueChanged(field.id, opt.key)} />;
case CustomCollectionFieldType.number:
return (
<div className={styles.numberField}>
<input type="number"
role="spinbutton"
placeholder={field.title}
aria-valuemax="99999"
aria-valuemin="-999999"
aria-valuenow={item[field.id] || ''}
value={item[field.id] || ''}
onChange={(ev) => this.onValueChanged(field.id, ev.target.value)} />
</div>
<CollectionNumberField field={field} item={item} fOnValueChange={this.onValueChanged} fValidation={this.fieldValidation} />
);
case CustomCollectionFieldType.fabricIcon:
return (
<CollectionIconField field={field} item={item} fOnValueChange={this.onValueChanged} />
<CollectionIconField field={field} item={item} fOnValueChange={this.onValueChanged} fValidation={this.fieldValidation} />
);
case CustomCollectionFieldType.url:
return <TextField placeholder={field.title}
return <TextField placeholder={field.placeholder || field.title}
value={item[field.id] ? item[field.id] : ""}
required={field.required}
className={styles.urlField}
onGetErrorMessage={(value) => {
// Check if entered value is a valid URL
const regEx: RegExp = /^((http|https)?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
const isValid = (value === null || value.length === 0 || regEx.test(value));
className={styles.collectionDataField}
onGetErrorMessage={async (value) => {
let isValid = true;
let validation = "";

// Check if custom validation is configured
if (field.onGetErrorMessage) {
// Using the custom validation
validation = await field.onGetErrorMessage(value);
isValid = validation === "";
} else {
// Check if entered value is a valid URL
const regEx: RegExp = /^((http|https)?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
isValid = (value === null || value.length === 0 || regEx.test(value));
validation = isValid ? "" : strings.InvalidUrlError;
}

// Store the field validation
this.validation[field.id] = isValid;
// Trigger field change
this.onValueChanged(field.id, value);
// Return the error message if needed
return isValid ? "" : strings.InvalidUrlError;
return validation;
}} />;
case CustomCollectionFieldType.string:
default:
return <TextField placeholder={field.title}
return <TextField placeholder={field.placeholder || field.title}
className={styles.collectionDataField}
value={item[field.id] ? item[field.id] : ""}
required={field.required}
onChanged={(value) => this.onValueChanged(field.id, value)} />;
onChanged={(value) => this.onValueChanged(field.id, value)}
onGetErrorMessage={(value: string) => this.fieldValidation(field, value)} />;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class CollectionDataViewer extends React.Component<ICollectionDataViewerP

<div className={styles.panelActions}>
{ this.state.inCreationItem && <PrimaryButton text={strings.CollectionSaveAndAddButtonLabel} onClick={this.addAndSave} disabled={!this.allItemsValid()} /> }
<PrimaryButton text={strings.SaveButtonLabel} onClick={this.onSave} disabled={!this.allItemsValid()} />
{ !this.state.inCreationItem && <PrimaryButton text={strings.SaveButtonLabel} onClick={this.onSave} disabled={!this.allItemsValid()} /> }
<DefaultButton text={strings.CancelButtonLabel} onClick={this.onCancel} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@ import { TextField } from 'office-ui-fabric-react/lib/components/TextField';
import { Icon } from 'office-ui-fabric-react/lib/components/Icon';

export class CollectionIconField extends React.Component<ICollectionIconFieldProps, {}> {
constructor(props: ICollectionIconFieldProps) {
super(props);

this.state = {
iconName: null
};
}

public render(): React.ReactElement<ICollectionIconFieldProps> {
return (
<div className={styles.iconField}>
<TextField placeholder={this.props.field.title}
<TextField placeholder={this.props.field.placeholder || this.props.field.title}
className={styles.collectionDataField}
value={this.props.item[this.props.field.id] ? this.props.item[this.props.field.id] : ""}
required={this.props.field.required}
onChanged={(value) => this.props.fOnValueChange(this.props.field.id, value)} />
onChanged={(value) => this.props.fOnValueChange(this.props.field.id, value)}
onGetErrorMessage={(value) => this.props.fValidation(this.props.field, value)} />
<Icon iconName={this.props.item[this.props.field.id] ? this.props.item[this.props.field.id] : ""} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface ICollectionIconFieldProps {
item: any;

fOnValueChange: (fieldId: string, value: any) => void;
fValidation: (field: ICustomCollectionField, value: any) => Promise<string>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as React from 'react';
import styles from '../PropertyFieldCollectionDataHost.module.scss';
import { ICollectionNumberFieldProps, ICollectionNumberFieldState } from '.';
import { ICustomCollectionField } from '..';

export class CollectionNumberField extends React.Component<ICollectionNumberFieldProps, ICollectionNumberFieldState> {
constructor(props: ICollectionNumberFieldProps) {
super(props);

this.state = {
errorMessage: ''
};
}

/**
* componentWillMount lifecycle hook
*/
public componentWillMount(): void {
this.valueChange(this.props.field, this.props.item[this.props.field.id]);
}

/**
* Value change event handler
*
* @param field
* @param value
*/
private valueChange = async (field: ICustomCollectionField, value: string | number) => {
const inputVal = typeof value === "string" ? parseInt(value) : value;
const validation = await this.props.fValidation(field, inputVal);
// Update the error message
this.setState({
errorMessage: validation
});
}

/**
* Default React render method
*/
public render(): React.ReactElement<ICollectionNumberFieldProps> {
return (
<div className={`${styles.numberField} ${this.state.errorMessage ? styles.invalidField : ""}`}>
<input type="number"
role="spinbutton"
placeholder={this.props.field.placeholder || this.props.field.title}
aria-valuemax="99999"
aria-valuemin="-999999"
aria-valuenow={this.props.item[this.props.field.id] || ''}
aria-invalid={!!this.state.errorMessage}
value={this.props.item[this.props.field.id] || ''}
onChange={(ev) => this.valueChange(this.props.field, ev.target.value)} />
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ICustomCollectionField } from "..";

export interface ICollectionNumberFieldProps {
field: ICustomCollectionField;
item: any;

fOnValueChange: (fieldId: string, value: any) => void;
fValidation: (field: ICustomCollectionField, value: any) => Promise<string>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ICollectionNumberFieldState {
errorMessage: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './CollectionNumberField';
export * from './ICollectionNumberFieldProps';
export * from './ICollectionNumberFieldState';
Loading

0 comments on commit bb6f47f

Please sign in to comment.