diff --git a/CHANGELOG.JSON b/CHANGELOG.JSON index 934a8e935..ee7a17952 100644 --- a/CHANGELOG.JSON +++ b/CHANGELOG.JSON @@ -3,15 +3,33 @@ { "version": "1.10.0", "changes": { - "new": [], + "new": [ + "`ListItemPicker`: New field control [#165](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/165)" + ], "enhancements": [ + "Dutch localization added [#100](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/100)", + "German localization added [#101](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/101)", + "French localization added [#102](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/102)", + "`PeoplePicker`: Move defaultSelectedUsers from ComponentWillMount to ComponentDidUpdate Lifecycle [#135](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/135)", + "`PeoplePicker`: Initialize with users from a list item [#138](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/138)", + "`PeoplePicker`: Remove Messagebar error handling to match Office UI Fabric field error styling [#140](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/140)", "`PeoplePicker`: REST API filter and nometadata header added to reduce payload [#139](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/139)", "`PeoplePicker`: Allow to set the maximum number of suggestions `suggestionsLimit` [#143](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/143) [#148](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/148)", - "`TaxonomyPicker`: retreiving the terms in the correct custom sort order [#146](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/146)" + "`TaxonomyPicker`: retreiving the terms in the correct custom sort order [#146](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/146)", + "`PeoplePicker`: Documentation format updated to make it easier to check the default values [#159](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/159)" ], "fixes": [] }, - "contributions": ["[Gautam Sheth](https://github.com/gautamdsheth)", "[Ole Bergtun](https://github.com/trillian74)", "[Tse Kit Yam](https://github.com/tsekityam)"] + "contributions": [ + "[Marc D Anderson](https://github.com/sympmarc)", + "[Ole Bergtun](https://github.com/trillian74)", + "[João Mendes](https://github.com/joaojmendes)", + "[Markus Möller](https://github.com/mmsharepoint)", + "[Asish Padhy](https://github.com/AsishP)", + "[PooLP](https://github.com/PooLP)", + "[Gautam Sheth](https://github.com/gautamdsheth)", + "[Tse Kit Yam](https://github.com/tsekityam)" + ] }, { "version": "1.9.0", diff --git a/CHANGELOG.md b/CHANGELOG.md index ae20b82d3..ca7c8e430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,26 @@ ## 1.10.0 +**New control(s)** + +- `ListItemPicker`: New field control [#165](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/165) + **Enhancements** +- Dutch localization added [#100](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/100) +- German localization added [#101](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/101) +- French localization added [#102](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/102) +- `PeoplePicker`: Move defaultSelectedUsers from ComponentWillMount to ComponentDidUpdate Lifecycle [#135](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/135) +- `PeoplePicker`: Initialize with users from a list item [#138](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/138) +- `PeoplePicker`: Remove Messagebar error handling to match Office UI Fabric field error styling [#140](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/140) - `PeoplePicker`: REST API filter and nometadata header added to reduce payload [#139](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/139) - `PeoplePicker`: Allow to set the maximum number of suggestions `suggestionsLimit` [#143](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/143) [#148](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/148) - `TaxonomyPicker`: retreiving the terms in the correct custom sort order [#146](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/146) +- `PeoplePicker`: Documentation format updated to make it easier to check the default values [#159](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/159) ### Contributors -Special thanks to our contributors (in alphabetical order): [Gautam Sheth](https://github.com/gautamdsheth), [Ole Bergtun](https://github.com/trillian74), [Tse Kit Yam](https://github.com/tsekityam). +Special thanks to our contributors (in alphabetical order): [Marc D Anderson](https://github.com/sympmarc), [Ole Bergtun](https://github.com/trillian74), [João Mendes](https://github.com/joaojmendes), [Markus Möller](https://github.com/mmsharepoint), [Asish Padhy](https://github.com/AsishP), [PooLP](https://github.com/PooLP), [Gautam Sheth](https://github.com/gautamdsheth), [Tse Kit Yam](https://github.com/tsekityam). ## 1.9.0 diff --git a/docs/documentation/docs/about/release-notes.md b/docs/documentation/docs/about/release-notes.md index ae20b82d3..ca7c8e430 100644 --- a/docs/documentation/docs/about/release-notes.md +++ b/docs/documentation/docs/about/release-notes.md @@ -2,15 +2,26 @@ ## 1.10.0 +**New control(s)** + +- `ListItemPicker`: New field control [#165](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/165) + **Enhancements** +- Dutch localization added [#100](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/100) +- German localization added [#101](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/101) +- French localization added [#102](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/102) +- `PeoplePicker`: Move defaultSelectedUsers from ComponentWillMount to ComponentDidUpdate Lifecycle [#135](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/135) +- `PeoplePicker`: Initialize with users from a list item [#138](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/138) +- `PeoplePicker`: Remove Messagebar error handling to match Office UI Fabric field error styling [#140](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/140) - `PeoplePicker`: REST API filter and nometadata header added to reduce payload [#139](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/139) - `PeoplePicker`: Allow to set the maximum number of suggestions `suggestionsLimit` [#143](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/143) [#148](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/148) - `TaxonomyPicker`: retreiving the terms in the correct custom sort order [#146](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/146) +- `PeoplePicker`: Documentation format updated to make it easier to check the default values [#159](https://github.com/SharePoint/sp-dev-fx-controls-react/pull/159) ### Contributors -Special thanks to our contributors (in alphabetical order): [Gautam Sheth](https://github.com/gautamdsheth), [Ole Bergtun](https://github.com/trillian74), [Tse Kit Yam](https://github.com/tsekityam). +Special thanks to our contributors (in alphabetical order): [Marc D Anderson](https://github.com/sympmarc), [Ole Bergtun](https://github.com/trillian74), [João Mendes](https://github.com/joaojmendes), [Markus Möller](https://github.com/mmsharepoint), [Asish Padhy](https://github.com/AsishP), [PooLP](https://github.com/PooLP), [Gautam Sheth](https://github.com/gautamdsheth), [Tse Kit Yam](https://github.com/tsekityam). ## 1.9.0 diff --git a/docs/documentation/docs/assets/ListItemPicker-selectedItems.png b/docs/documentation/docs/assets/ListItemPicker-selectedItems.png new file mode 100644 index 000000000..bca220555 Binary files /dev/null and b/docs/documentation/docs/assets/ListItemPicker-selectedItems.png differ diff --git a/docs/documentation/docs/assets/ListItemPicker-selectlist.png b/docs/documentation/docs/assets/ListItemPicker-selectlist.png new file mode 100644 index 000000000..aff7eb624 Binary files /dev/null and b/docs/documentation/docs/assets/ListItemPicker-selectlist.png differ diff --git a/docs/documentation/docs/assets/ListItemPicker-selectlist2.png b/docs/documentation/docs/assets/ListItemPicker-selectlist2.png new file mode 100644 index 000000000..c900bbe7b Binary files /dev/null and b/docs/documentation/docs/assets/ListItemPicker-selectlist2.png differ diff --git a/docs/documentation/docs/controls/ListItemPicker.md b/docs/documentation/docs/controls/ListItemPicker.md new file mode 100644 index 000000000..1aaecca45 --- /dev/null +++ b/docs/documentation/docs/controls/ListItemPicker.md @@ -0,0 +1,59 @@ +# ListItemPicker control + +This control allows you to select one or more items from a list. The item selection is based from a column value. The control will suggest items based on the inserted value. + +Here is an example of the control: + +![ListItemPicker select list items](../assets/ListItemPicker-selectlist.png) + +![ListItemPicker select list items](../assets/ListItemPicker-selectlist2.png) + +![ListItemPicker selected Items](../assets/ListItemPicker-selectedItems.png) + +## How to use this control in your solutions + +- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../#getting-started) page for more information about installing the dependency. +- Import the control into your component: + +```TypeScript +import { ListItemPicker } from '@pnp/spfx-controls-react/listItemPicker'; +``` +- Use the `ListItemPicker` control in your code as follows: + +```TypeScript + +``` + +- The `onSelectedItem` change event returns the list items selected and can be implemented as follows: + +```TypeScript +private onSelectedItem(data: { key: string; name: string }[]) { + for (const item of data) { + console.log(`Item value: ${item.name}`); + } +} +``` +## Implementation + +The `ListItemPicker` control can be configured with the following properties: + + +| Property | Type | Required | Description | +| ---- | ---- | ---- | ---- | +| columnInternalName | string | yes | InternalName of column to search and get values. | +| context | WebPartContext \| ApplicationCustomizerContext | yes | SPFx web part or extention context | +| listId | string | yes | Guid of the list. | +| itemLimit | number | yes | Number of items which can be selected | +| onSelectItem | (items: any[]) => void | yes | Callback function which returns the selected items. | +| className | string | no | ClassName for the picker. | +| webUrl | string | no | URL of the site. By default it uses the current site URL. | +| defaultSelectedItems | any[] | no | Initial items that have already been selected and should appear in the people picker. | +| suggestionsHeaderText | string | no | The text that should appear at the top of the suggestion box. | +| noResultsFoundText | string | no | The text that should appear when no results are returned. | +| disabled | boolean | no | Specifies if the control is disabled or not. | + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ListItemPicker) diff --git a/docs/documentation/docs/controls/PeoplePicker.md b/docs/documentation/docs/controls/PeoplePicker.md index 5c802f7c3..8275b0568 100644 --- a/docs/documentation/docs/controls/PeoplePicker.md +++ b/docs/documentation/docs/controls/PeoplePicker.md @@ -52,27 +52,27 @@ private _getPeoplePickerItems(items: any[]) { The People picker control can be configured with the following properties: -| Property | Type | Required | Description | -| ---- | ---- | ---- | ---- | -| context | WebPartContext | yes | Context of the current web part. | -| titleText | string | yes | Text to be displayed on the control | -| groupName | string | no | group from which users are fetched. Leave it blank if need to filter all users | -| personSelectionLimit | number | no | Defines the limit of people that can be selected in the control| -| isRequired | boolean | no | Set if the control is required or not | -| disabled | boolean | no | Set if the control is disabled or not | -| errorMessage | string | no | Specify the error message to display | -| errorMessageclassName | string | no | applies custom styling to the error message section| -| showtooltip | boolean | no | Defines if need a tooltip or not | -| tooltip | string | no | Specify the tooltip message to display | -| tooltipDirectional | DirectionalHint | no | Direction where the tooltip would be shown | -| selectedItems | function | no | get the selected users in the control | -| peoplePickerWPclassName | string | no | applies custom styling to the people picker element | -| peoplePickerCntrlclassName | string | no | applies custom styling to the people picker control only | -| defaultSelectedUsers | string[] | no | Default selected user emails | -| webAbsoluteUrl | string | no | Specify the site URL on which you want to perform the user query call. Default is the current site URL. | -| showHiddenInUI | boolean | no | Show users which are hidden from the UI. By default these users/groups hidden for the UI will not be shown. | -| principleTypes | PrincipleType[] | no | Define which type of data you want to retrieve: User, SharePoint groups, Security groups. Multiple are possible. | -| suggestionsLimit | number | no | Maximum number of suggestions to show in the full suggestion list. Default is 5. | +| Property | Type | Required | Description | Default | +| ---- | ---- | ---- | ---- | ---- | +| context | WebPartContext | yes | Context of the current web part. | | +| titleText | string | yes | Text to be displayed on the control | | +| groupName | string | no | group from which users are fetched. Leave it blank if need to filter all users | _none_ | +| personSelectionLimit | number | no | Defines the limit of people that can be selected in the control | | +| isRequired | boolean | no | Set if the control is required or not | false | +| disabled | boolean | no | Set if the control is disabled or not | false | +| errorMessage | string | no | Specify the error message to display | | +| errorMessageClassName | string | no | applies custom styling to the error message section | | +| showtooltip | boolean | no | Defines if need a tooltip or not | false | +| tooltip | string | no | Specify the tooltip message to display | | +| tooltipDirectional | DirectionalHint | no | Direction where the tooltip would be shown | | +| selectedItems | function | no | get the selected users in the control | | +| peoplePickerWPclassName | string | no | applies custom styling to the people picker element | | +| peoplePickerCntrlclassName | string | no | applies custom styling to the people picker control only | | +| defaultSelectedUsers | string[] | no | Default selected user emails | | +| webAbsoluteUrl | string | no | Specify the site URL on which you want to perform the user query call. | Current site URL | +| showHiddenInUI | boolean | no | Show users which are hidden from the UI. By default these users/groups hidden for the UI will not be shown. | false | +| principleTypes | PrincipleType[] | no | Define which type of data you want to retrieve: User, SharePoint groups, Security groups. Multiple are possible. | | +| suggestionsLimit | number | no | Maximum number of suggestions to show in the full suggestion list. | 5 | Enum `PrincipalType` diff --git a/docs/documentation/mkdocs.yml b/docs/documentation/mkdocs.yml index 861e8bef5..7332026be 100644 --- a/docs/documentation/mkdocs.yml +++ b/docs/documentation/mkdocs.yml @@ -3,6 +3,7 @@ nav: - Home: 'index.md' - Controls: - FileTypeIcon: 'controls/FileTypeIcon.md' + - ListItemPicker: 'controls/ListItemPicker.md' - ListPicker: 'controls/ListPicker.md' - ListView: 'controls/ListView.md' - "ListView: add a contextual menu": 'controls/ListView.ContextualMenu.md' diff --git a/package-lock.json b/package-lock.json index df956b13a..7037c03c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4697,7 +4697,7 @@ }, "file-type": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", "dev": true }, @@ -9303,7 +9303,7 @@ }, "into-stream": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", "dev": true, "requires": { @@ -14878,9 +14878,9 @@ } }, "sonarqube-scanner": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/sonarqube-scanner/-/sonarqube-scanner-2.1.1.tgz", - "integrity": "sha512-KhvOBP1IBXnnkVAbfdINLyDnsR6W6NsX4m5z57s4/wxlfUIeRMT8MNsqmGWRRIzYqtPPUCpHT3R3sUChGZmALg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/sonarqube-scanner/-/sonarqube-scanner-2.1.2.tgz", + "integrity": "sha512-4Sf01R7zOnR3YTofaQI5HH6UTz46l0kUov7cJvWmhD9Hi+U16komI/e61GjOyC2JrVrFiWqFsO/o15ceHbKd/Q==", "dev": true, "requires": { "download": "7.0.0", @@ -14916,9 +14916,9 @@ } }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", + "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, "read-pkg": { @@ -16210,9 +16210,9 @@ "dev": true }, "unbzip2-stream": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.0.tgz", - "integrity": "sha512-kE2WkurNnPUMcryNioS68DDbhoPB8Qxsd8btHSj+sd5Pjh2GsjmeHLzMSqV9HHziAo8FzVxVCJl9ZYhk7yY1pA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz", + "integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==", "dev": true, "requires": { "buffer": "^3.0.1", diff --git a/package.json b/package.json index 05213422b..cffff5e38 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "react": "15.4.2", "react-addons-test-utils": "15.6.0", "react-dom": "15.4.2", - "sonarqube-scanner": "2.1.1" + "sonarqube-scanner": "2.1.2" }, "repository": { "type": "git", diff --git a/src/ListItemPicker.ts b/src/ListItemPicker.ts new file mode 100644 index 000000000..be6838119 --- /dev/null +++ b/src/ListItemPicker.ts @@ -0,0 +1 @@ +export * from './controls/listItemPicker'; diff --git a/src/controls/listItemPicker/IListItemPickerProps.ts b/src/controls/listItemPicker/IListItemPickerProps.ts new file mode 100644 index 000000000..96e2499ab --- /dev/null +++ b/src/controls/listItemPicker/IListItemPickerProps.ts @@ -0,0 +1,18 @@ +import { WebPartContext } from "@microsoft/sp-webpart-base"; +import { ApplicationCustomizerContext } from "@microsoft/sp-application-base"; + +export interface IListItemPickerProps { + columnInternalName: string; + context: WebPartContext | ApplicationCustomizerContext; + listId: string; + itemLimit: number; + + className?: string; + webUrl?: string; + defaultSelectedItems?: any[]; + disabled?: boolean; + suggestionsHeaderText?:string; + noResultsFoundText?:string; + + onSelectedItem: (item:any) => void; +} diff --git a/src/controls/listItemPicker/IListItemPickerState.ts b/src/controls/listItemPicker/IListItemPickerState.ts new file mode 100644 index 000000000..9c6a3de79 --- /dev/null +++ b/src/controls/listItemPicker/IListItemPickerState.ts @@ -0,0 +1,6 @@ +export interface IListItemPickerState { + noresultsFoundText: string; + showError: boolean; + errorMessage: string; + suggestionsHeaderText:string; +} diff --git a/src/controls/listItemPicker/ListItemPicker.tsx b/src/controls/listItemPicker/ListItemPicker.tsx new file mode 100644 index 000000000..449720a39 --- /dev/null +++ b/src/controls/listItemPicker/ListItemPicker.tsx @@ -0,0 +1,137 @@ +import * as strings from 'ControlStrings'; +import * as React from "react"; +import SPservice from "../../services/SPService"; +import { escape } from "@microsoft/sp-lodash-subset"; +import { TagPicker } from "office-ui-fabric-react/lib/components/pickers/TagPicker/TagPicker"; +import { Label } from "office-ui-fabric-react/lib/Label"; +import { IListItemPickerProps, IListItemPickerState } from "."; +import * as telemetry from '../../common/telemetry'; + + +export class ListItemPicker extends React.Component { + private _spservice: SPservice; + private selectedItems: any[]; + + constructor(props: IListItemPickerProps) { + super(props); + + telemetry.track('ListItemPicker', {}); + + // States + this.state = { + noresultsFoundText: !this.props.noResultsFoundText ? strings.genericNoResultsFoundText : this.props.noResultsFoundText, + showError: false, + errorMessage: "", + suggestionsHeaderText: !this.props.suggestionsHeaderText ? strings.ListItemPickerSelectValue : this.props.suggestionsHeaderText + }; + + // Get SPService Factory + this._spservice = new SPservice(this.props.context); + + this.selectedItems = []; + } + + public componentDidUpdate(prevProps: IListItemPickerProps, prevState: IListItemPickerState): void { + if (this.props.listId !== prevProps.listId) { + this.selectedItems = []; + } + } + + /** + * Render the field + */ + public render(): React.ReactElement { + const { className, disabled, itemLimit } = this.props; + + return ( +
+ { return item.name; }} + getTextFromItem={this.getTextFromItem} + pickerSuggestionsProps={{ + suggestionsHeaderText: this.state.suggestionsHeaderText, + noResultsFoundText: this.state.noresultsFoundText + }} + defaultSelectedItems={this.props.defaultSelectedItems || []} + onChange={this.onItemChanged} + className={className} + itemLimit={itemLimit} + disabled={disabled} /> + + +
+ ); + } + + /** + * Get text from Item + */ + private getTextFromItem(item: any): string { + return item.name; + } + + /** + * On Selected Item + */ + private onItemChanged = (selectedItems: { key: string; name: string }[]): void => { + this.selectedItems = selectedItems; + this.props.onSelectedItem(selectedItems); + } + + /** + * Filter Change + */ + private onFilterChanged = async (filterText: string, tagList: { key: string; name: string }[]) => { + let resolvedSugestions: { key: string; name: string }[] = await this.loadListItems(filterText); + + // Filter out the already retrieved items, so that they cannot be selected again + if (this.selectedItems && this.selectedItems.length > 0) { + let filteredSuggestions = []; + for (const suggestion of resolvedSugestions) { + const exists = this.selectedItems.filter(sItem => sItem.key === suggestion.key); + if (!exists || exists.length === 0) { + filteredSuggestions.push(suggestion); + } + } + resolvedSugestions = filteredSuggestions; + } + + if (resolvedSugestions) { + this.setState({ + errorMessage: "", + showError: false + }); + + return resolvedSugestions; + } else { + return []; + } + } + + /** + * Function to load List Items + */ + private loadListItems = async (filterText: string): Promise<{ key: string; name: string }[]> => { + let { listId, columnInternalName, webUrl } = this.props; + let arrayItems: { key: string; name: string }[] = []; + + try { + let listItems = await this._spservice.getListItems(filterText, listId, columnInternalName, webUrl); + // Check if the list had items + if (listItems.length > 0) { + for (const item of listItems) { + arrayItems.push({ key: item.Id, name: item[columnInternalName] }); + } + } + return arrayItems; + } catch (error) { + console.log(`Error get Items`, error); + this.setState({ + showError: true, + errorMessage: error.message, + noresultsFoundText: error.message + }); + return null; + } + } +} diff --git a/src/controls/listItemPicker/index.ts b/src/controls/listItemPicker/index.ts new file mode 100644 index 000000000..1f118bfde --- /dev/null +++ b/src/controls/listItemPicker/index.ts @@ -0,0 +1,4 @@ +// A file is required to be in the root of the /src directory by the TypeScript compiler +export * from './IListItemPickerProps'; +export * from './IListItemPickerState'; +export * from './ListItemPicker'; diff --git a/src/controls/peoplepicker/IPeoplePicker.ts b/src/controls/peoplepicker/IPeoplePicker.ts index 33eb50ef1..16282355b 100644 --- a/src/controls/peoplepicker/IPeoplePicker.ts +++ b/src/controls/peoplepicker/IPeoplePicker.ts @@ -71,7 +71,7 @@ export interface IPeoplePickerProps { /** * Class Name for the Error Section */ - errorMessageclassName?: string; + errorMessageClassName?: string; /** * Default Selected User Emails */ diff --git a/src/controls/peoplepicker/PeoplePickerComponent.module.scss b/src/controls/peoplepicker/PeoplePickerComponent.module.scss index 1b3e8cef4..3c035896b 100644 --- a/src/controls/peoplepicker/PeoplePickerComponent.module.scss +++ b/src/controls/peoplepicker/PeoplePickerComponent.module.scss @@ -1,3 +1,18 @@ .defaultClass { - color : black; + color : black; +} + +.errorMessage { + font-size: 12px; + font-weight: 400; + color: #a80000; + margin: 0; + padding-top: 5px; + display: flex; + align-items: center; +} + +.errorIcon { + font-size: 14px; + margin-right: 5px; } diff --git a/src/controls/peoplepicker/PeoplePickerComponent.tsx b/src/controls/peoplepicker/PeoplePickerComponent.tsx index d831b39b1..94669b78a 100644 --- a/src/controls/peoplepicker/PeoplePickerComponent.tsx +++ b/src/controls/peoplepicker/PeoplePickerComponent.tsx @@ -16,6 +16,8 @@ import { IPersonaWithMenu } from "office-ui-fabric-react/lib/components/pickers/ import { IPersonaProps } from "office-ui-fabric-react/lib/components/Persona/Persona.types"; import { MessageBarType } from "office-ui-fabric-react/lib/components/MessageBar"; import { ValidationState } from 'office-ui-fabric-react/lib/components/pickers/BasePicker.types'; +import { Icon } from "office-ui-fabric-react/lib/components/Icon"; +import { isEqual } from "@microsoft/sp-lodash-subset"; /** * PeoplePicker component @@ -58,6 +60,34 @@ export class PeoplePicker extends React.Component = this.state.allPersons.length !==0 ? this.state.allPersons : new Array(); + + // Set Default selected persons + let defaultUsers : any = []; + let defaultPeopleList: IPersonaProps[] = []; + if (this.props.defaultSelectedUsers) { + defaultUsers = this.getDefaultUsers(userValuesArray, this.props.defaultSelectedUsers); + for (const persona of defaultUsers) { + let selectedPeople: IPersonaProps = {}; + assign(selectedPeople, persona); + defaultPeopleList.push(selectedPeople); + } + } + + this.setState({ + selectedPersons : defaultPeopleList.length !== 0 ? defaultPeopleList : [], + showmessageerror: this.props.isRequired && defaultPeopleList.length === 0 + }); + } + } + /** * Generate the user photo link * @@ -156,63 +186,53 @@ export class PeoplePicker extends React.Component resp.json()); - - // Check if items were retrieved - if (items && items.value && items.value.length > 0) { - - let userValuesArray: Array = new Array(); - - // Loop over all the retrieved items - for (let i = 0; i < items.value.length; i++) { - const item = items.value[i]; - if (!item.IsHiddenInUI || (this.props.showHiddenInUI && item.IsHiddenInUI)) { - // Check if the the type must be returned - if (!this.props.principleTypes || this.props.principleTypes.indexOf(item.PrincipalType) !== -1) { - userValuesArray.push({ - id: item.Id.toString(), - imageUrl: this.generateUserPhotoLink(item.Email), - imageInitials: "", - text: item.Title, // name - secondaryText: item.Email, // email - tertiaryText: "", // status - optionalText: "" // anything - }); + }); + + if (data.ok) { + const items: IUsers = await data.json(); + + // Check if items were retrieved + if (items && items.value && items.value.length > 0) { + + let userValuesArray: Array = new Array(); + + // Loop over all the retrieved items + for (let i = 0; i < items.value.length; i++) { + const item = items.value[i]; + if (!item.IsHiddenInUI || (this.props.showHiddenInUI && item.IsHiddenInUI)) { + // Check if the the type must be returned + if (!this.props.principleTypes || this.props.principleTypes.indexOf(item.PrincipalType) !== -1) { + userValuesArray.push({ + id: item.Id.toString(), + imageUrl: this.generateUserPhotoLink(item.Email), + imageInitials: "", + text: item.Title, // name + secondaryText: item.Email, // email + tertiaryText: "", // status + optionalText: "" // anything + }); + } } } - } - // Set Default selected persons - let defaultUsers: any = []; - let defaultPeopleList: IPersonaProps[] = []; - if (this.props.defaultSelectedUsers) { - defaultUsers = this.getDefaultUsers(userValuesArray, this.props.defaultSelectedUsers); - for (const persona of defaultUsers) { - let selectedPeople: IPersonaProps = {}; - assign(selectedPeople, persona); - defaultPeopleList.push(selectedPeople); + let personaList: IPersonaProps[] = []; + for (const persona of userValuesArray) { + let personaWithMenu: IPersonaProps = {}; + assign(personaWithMenu, persona); + personaList.push(personaWithMenu); } - } - let personaList: IPersonaProps[] = []; - for (const persona of userValuesArray) { - let personaWithMenu: IPersonaProps = {}; - assign(personaWithMenu, persona); - personaList.push(personaWithMenu); + // Update the current state + this.setState({ + allPersons : userValuesArray, + peoplePersonaMenu : personaList, + mostRecentlyUsedPersons : personaList.slice(0,5) + }); } - - // Update the current state - this.setState({ - allPersons: userValuesArray, - selectedPersons: defaultPeopleList.length != 0 ? defaultPeopleList : [], - peoplePersonaMenu: personaList, - mostRecentlyUsedPersons: personaList.slice(0, 5), - showmessageerror: this.props.isRequired && this.state.selectedPersons.length === 0 - }); } } catch (e) { console.error("Error occured while fetching the users and setting selected users."); @@ -379,7 +399,7 @@ export class PeoplePicker extends React.Component { const suggestionProps: IBasePickerSuggestionsProps = { suggestionsHeaderText: strings.peoplePickerSuggestionsHeaderText, - noResultsFoundText: strings.peoplePickerNoResultsFoundText, + noResultsFoundText: strings.genericNoResultsFoundText, loadingText: strings.peoplePickerLoadingText, resultsMaximumNumber: this.props.suggestionsLimit ? this.props.suggestionsLimit : 5 }; @@ -418,21 +438,20 @@ export class PeoplePicker extends React.Component ) : ( -
- {peoplepicker} -
- ) - } - - { - (this.props.isRequired && this.state.showmessageerror) && ( - - {this.props.errorMessage ? this.props.errorMessage : strings.peoplePickerComponentErrorMessage} - +
+ {peoplepicker} +
) } + + { + (this.props.isRequired && this.state.showmessageerror) && ( +

+ + {this.props.errorMessage ? this.props.errorMessage : strings.peoplePickerComponentErrorMessage} +

+ ) + } ); } diff --git a/src/index.ts b/src/index.ts index abfcf3225..f1fe003fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export * from './SiteBreadcrumb'; export * from './TaxonomyPicker'; export * from './WebPartTitle'; export * from './ListPicker'; +export * from './ListItemPicker'; export * from './IFrameDialog'; export * from './Common'; diff --git a/src/loc/de-de.ts b/src/loc/de-de.ts new file mode 100644 index 000000000..350f17dbd --- /dev/null +++ b/src/loc/de-de.ts @@ -0,0 +1,59 @@ +declare var define: any; + +define([], () => { + return { + 'SiteBreadcrumbLabel': 'Website Brotkrume', + + 'ListViewGroupEmptyLabel': 'Leer', + + 'WebPartTitlePlaceholder': 'Webpart Titel', + 'WebPartTitleLabel': 'Titel hinzufügen', + + "DateTime": { + "L_RelativeDateTime_AFewSecondsFuture": "In wenigen Sekunden", + "L_RelativeDateTime_AFewSeconds": "Vor wenigen Sekunden", + "L_RelativeDateTime_AboutAMinuteFuture": "In ungefähr einer Minute", + "L_RelativeDateTime_AboutAMinute": "Vor ungefähr einer Minute", + "L_RelativeDateTime_XMinutesFuture": "In {0} Minute||In {0} Minuten", + "L_RelativeDateTime_XMinutes": "Vor {0} Minute||Vor {0} Minuten", + "L_RelativeDateTime_XMinutesFutureIntervals": "1||2-", + "L_RelativeDateTime_XMinutesIntervals": "1||2-", + "L_RelativeDateTime_AboutAnHourFuture": "In ungefähr einer Stunde", + "L_RelativeDateTime_AboutAnHour": "Vor ungefähr einer Stunde", + "L_RelativeDateTime_Tomorrow": "Morgen", + "L_RelativeDateTime_Yesterday": "Gestern", + "L_RelativeDateTime_TomorrowAndTime": "Morgen um {0}", + "L_RelativeDateTime_YesterdayAndTime": "Gestern um {0}", + "L_RelativeDateTime_XHoursFuture": "In {0} Stunde||In {0} Stunden", + "L_RelativeDateTime_XHours": "Vor {0} Stunde||Vor {0} Stunden", + "L_RelativeDateTime_XHoursFutureIntervals": "1||2-", + "L_RelativeDateTime_XHoursIntervals": "1||2-", + "L_RelativeDateTime_DayAndTime": "{0} at {1}", + "L_RelativeDateTime_XDaysFuture": "{0} Tag ab jetzt||{0} Tage ab jetzt", + "L_RelativeDateTime_XDays": "Vor {0} Tag||Vor {0} Tagen", + "L_RelativeDateTime_XDaysFutureIntervals": "1||2-", + "L_RelativeDateTime_XDaysIntervals": "1||2-", + "L_RelativeDateTime_Today": "Heute" + }, + "SendEmailTo": "Email senden an {0}", + "StartChatWith": "Chat starten mit {0}", + "Contact": "Kontakt", + "UpdateProfile": "Aktualisiere Dein Profil", + + "TaxonomyPickerNoTerms": "Ausdruckssatz enthält keine Ausdrücke", + "TaxonomyPickerExpandTitle": "Ausdruckssatz erweitern", + "TaxonomyPickerMenuTermSet": "Ausdruckssatz Menü", + "TaxonomyPickerInLabel": "in", + "TaxonomyPickerTermSetLabel": "Ausdruckssatz", + + peoplePickerComponentTooltipMessage: "People Picker", + peoplePickerComponentErrorMessage: "Benutzerauswahl ist ein Pflichtfeld", + peoplePickerComponentTitleText: "Benutzer auswählen", + peoplePickerSuggestionsHeaderText: 'Vorgeschlagene Benutzer', + peoplePickerLoadingText: 'Laden', + + ListItemPickerSelectValue: 'Wähle Wert', + + genericNoResultsFoundText: 'Kein Ergebnis gefunden' + }; +}); diff --git a/src/loc/en-us.ts b/src/loc/en-us.ts index de8c5471d..4948bbc2a 100644 --- a/src/loc/en-us.ts +++ b/src/loc/en-us.ts @@ -47,10 +47,13 @@ define([], () => { "TaxonomyPickerTermSetLabel": "Term Set", peoplePickerComponentTooltipMessage: "People Picker", - peoplePickerComponentErrorMessage: "People picker is mandatory", + peoplePickerComponentErrorMessage: "Required Field", peoplePickerComponentTitleText: "Pick the user(s)", peoplePickerSuggestionsHeaderText: 'Suggested People', - peoplePickerNoResultsFoundText: 'No results found', - peoplePickerLoadingText: 'Loading' + peoplePickerLoadingText: 'Loading', + + ListItemPickerSelectValue: 'Select value', + + genericNoResultsFoundText: 'No results found' }; }); diff --git a/src/loc/fr-fr.ts b/src/loc/fr-fr.ts new file mode 100644 index 000000000..75359af7a --- /dev/null +++ b/src/loc/fr-fr.ts @@ -0,0 +1,59 @@ +declare var define: any; + +define([], () => { + return { + 'SiteBreadcrumbLabel': 'Fil d\'Ariane', + + 'ListViewGroupEmptyLabel': 'Vide', + + 'WebPartTitlePlaceholder': 'Titre du WebPart', + 'WebPartTitleLabel': 'Ajouter un titre', + + "DateTime": { + "L_RelativeDateTime_AFewSecondsFuture": "Dans quelques secondes", + "L_RelativeDateTime_AFewSeconds": "Il ya quelques secondes", + "L_RelativeDateTime_AboutAMinuteFuture": "Dans environ une minute", + "L_RelativeDateTime_AboutAMinute": "Il y a environ une minute", + "L_RelativeDateTime_XMinutesFuture": "Dans {0} minute||Dans {0} minutes", + "L_RelativeDateTime_XMinutes": "Il y a {0} minute||Il y a {0} minutes", + "L_RelativeDateTime_XMinutesFutureIntervals": "1||2-", + "L_RelativeDateTime_XMinutesIntervals": "1||2-", + "L_RelativeDateTime_AboutAnHourFuture": "Dans environ une heure", + "L_RelativeDateTime_AboutAnHour": "Il y a à peu près une heure", + "L_RelativeDateTime_Tomorrow": "Demain", + "L_RelativeDateTime_Yesterday": "Hier", + "L_RelativeDateTime_TomorrowAndTime": "Demain à {0}", + "L_RelativeDateTime_YesterdayAndTime": "Hier à {0}", + "L_RelativeDateTime_XHoursFuture": "Dans {0} heure||Dans {0} heures", + "L_RelativeDateTime_XHours": "Il y a {0} heure||Il y a {0} heures", + "L_RelativeDateTime_XHoursFutureIntervals": "1||2-", + "L_RelativeDateTime_XHoursIntervals": "1||2-", + "L_RelativeDateTime_DayAndTime": "{0} à {1}", + "L_RelativeDateTime_XDaysFuture": "{0} jours à partir de maintenant || {0} jours à partir de maintenant", + "L_RelativeDateTime_XDays": "Il y a {0} jour||Il y a {0} jours", + "L_RelativeDateTime_XDaysFutureIntervals": "1||2-", + "L_RelativeDateTime_XDaysIntervals": "1||2-", + "L_RelativeDateTime_Today": "Aujourd'hui" + }, + "SendEmailTo": "Envoyer un email à {0}", + "StartChatWith": "Démarrer une discussion avec {0}", + "Contact": "Contact", + "UpdateProfile": "Mettre à jour votre profil", + + "TaxonomyPickerNoTerms": "L'ensemble de termes ne contient aucun terme", + "TaxonomyPickerExpandTitle": "Développer cet ensemble de termes", + "TaxonomyPickerMenuTermSet": "Menu pour l'ensemble de termes", + "TaxonomyPickerInLabel": "dans", + "TaxonomyPickerTermSetLabel": "Ensemble de termes", + + peoplePickerComponentTooltipMessage: "Sélecteur de personnes", + peoplePickerComponentErrorMessage: "Le sélecteur de personnes est obligatoire", + peoplePickerComponentTitleText: "Choisissez l'utilisateur(s)", + peoplePickerSuggestionsHeaderText: 'Personnes suggérées', + peoplePickerLoadingText: 'Chargement', + + ListItemPickerSelectValue: 'Sélectionnez une valeur', + + genericNoResultsFoundText: 'Aucun résultat trouvé' + }; +}); diff --git a/src/loc/mystrings.d.ts b/src/loc/mystrings.d.ts index 453239807..2b58ffa1b 100644 --- a/src/loc/mystrings.d.ts +++ b/src/loc/mystrings.d.ts @@ -3,7 +3,7 @@ declare interface IControlStrings { peoplePickerComponentErrorMessage: string; peoplePickerComponentTitleText: string; peoplePickerSuggestionsHeaderText: string; - peoplePickerNoResultsFoundText: string; + genericNoResultsFoundText: string; peoplePickerLoadingText: string; @@ -23,6 +23,8 @@ declare interface IControlStrings { TaxonomyPickerMenuTermSet: string; TaxonomyPickerInLabel: string; TaxonomyPickerTermSetLabel: string; + + ListItemPickerSelectValue: string; } declare module 'ControlStrings' { diff --git a/src/loc/nl-nl.ts b/src/loc/nl-nl.ts new file mode 100644 index 000000000..541730aab --- /dev/null +++ b/src/loc/nl-nl.ts @@ -0,0 +1,59 @@ +declare var define: any; + +define([], () => { + return { + 'SiteBreadcrumbLabel': 'Website broodkruimelpad', + + 'ListViewGroupEmptyLabel': 'Leeg', + + 'WebPartTitlePlaceholder': 'Webonderdeel titel', + 'WebPartTitleLabel': 'Titel ingeven', + + "DateTime": { + "L_RelativeDateTime_AFewSecondsFuture": "Over een paar seconden", + "L_RelativeDateTime_AFewSeconds": "Een paar seconden geleden", + "L_RelativeDateTime_AboutAMinuteFuture": "Over ongeveer een minuut", + "L_RelativeDateTime_AboutAMinute": "Ongeveer een minuut geleden", + "L_RelativeDateTime_XMinutesFuture": "In {0} minuut||In {0} minuten", + "L_RelativeDateTime_XMinutes": "{0} minuut geleden||{0} minuten geleden", + "L_RelativeDateTime_XMinutesFutureIntervals": "1||2-", + "L_RelativeDateTime_XMinutesIntervals": "1||2-", + "L_RelativeDateTime_AboutAnHourFuture": "In ongeveer een uur", + "L_RelativeDateTime_AboutAnHour": "Ongeveer een uur geleden", + "L_RelativeDateTime_Tomorrow": "Morgen", + "L_RelativeDateTime_Yesterday": "Gisteren", + "L_RelativeDateTime_TomorrowAndTime": "Morgen om {0}", + "L_RelativeDateTime_YesterdayAndTime": "Gisteren om {0}", + "L_RelativeDateTime_XHoursFuture": "In {0} uur||In {0} uur", + "L_RelativeDateTime_XHours": "{0} uur geleden||{0} uur geleden", + "L_RelativeDateTime_XHoursFutureIntervals": "1||2-", + "L_RelativeDateTime_XHoursIntervals": "1||2-", + "L_RelativeDateTime_DayAndTime": "{0} om {1}", + "L_RelativeDateTime_XDaysFuture": "{0} dag vanaf nu||{0} dagen vanaf nu", + "L_RelativeDateTime_XDays": "{0} dag geleden||{0} dagen geleden", + "L_RelativeDateTime_XDaysFutureIntervals": "1||2-", + "L_RelativeDateTime_XDaysIntervals": "1||2-", + "L_RelativeDateTime_Today": "Vandaag" + }, + "SendEmailTo": "Stuur een mail naar {0}", + "StartChatWith": "Start een gesprek met {0}", + "Contact": "Contacteer", + "UpdateProfile": "Profiel aanpassen", + + "TaxonomyPickerNoTerms": "Termen set heeft geen termen beschikbaar", + "TaxonomyPickerExpandTitle": "Vouw de termen set uit", + "TaxonomyPickerMenuTermSet": "Menu van de termen set", + "TaxonomyPickerInLabel": "in", + "TaxonomyPickerTermSetLabel": "Termen set", + + peoplePickerComponentTooltipMessage: "Personen kiezen", + peoplePickerComponentErrorMessage: "Verplicht veld", + peoplePickerComponentTitleText: "Kies personen", + peoplePickerSuggestionsHeaderText: 'Voorgestelde personen', + peoplePickerLoadingText: 'Laden', + + ListItemPickerSelectValue: 'Selecteer veld', + + genericNoResultsFoundText: 'Geen resultaten gevonden' + }; +}); diff --git a/src/services/ISPService.ts b/src/services/ISPService.ts index c62fa8a8e..d808ccd8f 100644 --- a/src/services/ISPService.ts +++ b/src/services/ISPService.ts @@ -4,6 +4,7 @@ export enum LibsOrderBy { Id = 1, Title } + /** * Options used to sort and filter */ @@ -12,10 +13,12 @@ export interface ILibsOptions { baseTemplate?: number; includeHidden?: boolean; } + export interface ISPService { /** * Get the lists from SharePoint * @param options Options used to order and filter during the API query */ getLibs(options?: ILibsOptions): Promise; -} \ No newline at end of file + getListItems?(filterText: string, listId: string, internalColumnName: string, webUrl?: string) : Promise; +} diff --git a/src/services/SPService.ts b/src/services/SPService.ts index a68bbd60a..615f02093 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -1,7 +1,7 @@ import { ISPService, ILibsOptions, LibsOrderBy } from "./ISPService"; import { ISPLists } from "../common/SPEntities"; import { WebPartContext } from "@microsoft/sp-webpart-base"; -import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; +import { ApplicationCustomizerContext } from "@microsoft/sp-application-base"; import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; export default class SPService implements ISPService { @@ -12,7 +12,7 @@ export default class SPService implements ISPService { * Get lists or libraries * @param options */ - public getLibs(options?: ILibsOptions): Promise { + public async getLibs(options?: ILibsOptions): Promise { let filtered: boolean; let queryUrl: string = `${this._context.pageContext.web.absoluteUrl}/_api/web/lists?$select=Title,id,BaseTemplate`; @@ -30,7 +30,34 @@ export default class SPService implements ISPService { filtered = true; } - return this._context.spHttpClient.get(queryUrl, SPHttpClient.configurations.v1) - .then(response => response.json()) as Promise; + const data = await this._context.spHttpClient.get(queryUrl, SPHttpClient.configurations.v1); + if (data.ok) { + return await data.json() as Promise; + } else { + return null; + } + } + + /** + * Get List Items + */ + public async getListItems(filterText: string, listId: string, internalColumnName: string, webUrl?: string): Promise { + let returnItems: any[]; + + try { + const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; + const apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=Id,${internalColumnName}&$filter=startswith(${internalColumnName},'${filterText}')`; + const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); + if (data.ok) { + const results = await data.json(); + if (results && results.value && results.value.length > 0) { + return results.value; + } + } + + return []; + } catch (error) { + return Promise.reject(error); + } } } diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index 3bddbe607..18ff386f0 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -18,6 +18,8 @@ import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; import { SecurityTrimmedControl, PermissionLevel } from '../../../SecurityTrimmedControl'; import { SPPermission } from '@microsoft/sp-page-context'; import { PeoplePicker, PrincipalType } from '../../../PeoplePicker'; +import { getItemClassNames } from 'office-ui-fabric-react/lib/components/ContextualMenu/ContextualMenu.classNames'; +import { ListItemPicker } from "../../../ListItemPicker"; /** * Component that can be used to test out the React controls from this project @@ -30,7 +32,9 @@ export default class ControlsTest extends React.Component { return resp.json(); }) + // .then(items => { + // let emails : string[] = items.value ? items.value.map((item, key)=> { return item.Author.EMail}) : []; + // console.log(emails); + // this.setState({ + // authorEmails: emails + // }); + // }); } /** @@ -105,8 +121,11 @@ private onServicePickerChange(terms: IPickerTerms): void { * Selected lists change event * @param lists */ - private onListPickerChange (lists: string | string[]) { + private onListPickerChange = (lists: string | string[]) => { console.log("Lists:", lists); + this.setState({ + selectedList: typeof lists === "string" ? lists : lists.pop() + }); } /** @@ -121,13 +140,22 @@ private onServicePickerChange(terms: IPickerTerms): void { }); } } - /** Method that retrieves the selected items from People Picker + + /** + * Method that retrieves the selected items from People Picker * @param items */ private _getPeoplePickerItems(items: any[]) { console.log('Items:', items); } + /** + * Selected item from the list data picker + */ + private listItemPickerDataSelected(item: any) { + console.log(item); + } + /** * Renders the component */ @@ -248,6 +276,14 @@ private onServicePickerChange(terms: IPickerTerms): void { onSelectionChanged={this.onListPickerChange} /> +
Field picker list data tester: + +
+
Services tester: