diff --git a/CHANGELOG.JSON b/CHANGELOG.JSON index 5b5b221f8..671ec074e 100644 --- a/CHANGELOG.JSON +++ b/CHANGELOG.JSON @@ -1,5 +1,32 @@ { "versions": [ + { + "version": "2.9.0", + "changes": { + "new": [], + "enhancements": [ + "`FilePicker`: spanish translation for Stock Images labels [#946](https://github.com/pnp/sp-dev-fx-controls-react/pull/946)", + "`FilePicker`: Add support for a defaultFolderAbsolutePath prop [#947](https://github.com/pnp/sp-dev-fx-controls-react/pull/947)", + "`DynamicForm`: Returning PnPJS `IItem` in `onSubmitted` event based on `returnListItemInstanceOnSubmit` property [#944](https://github.com/pnp/sp-dev-fx-controls-react/pull/944)", + "`DateTimePicker`: Add property for minutes dropdown increment [#939](https://github.com/pnp/sp-dev-fx-controls-react/issues/939)", + "`DynamicForm`: Principal Types support [#956](https://github.com/pnp/sp-dev-fx-controls-react/pull/956)", + "`Dynamic Form`: Show field descriptions [#975](https://github.com/pnp/sp-dev-fx-controls-react/issues/975)" + ], + "fixes": [ + "`RichText`: Image button is checked when hyperlink is added to the text [#948](https://github.com/pnp/sp-dev-fx-controls-react/issues/948)", + "`RichText`: impossible to display link with the text equal to the url [#949](https://github.com/pnp/sp-dev-fx-controls-react/issues/949)", + "`ComboBoxListItemPicker`: defaultSelectedItems not working [#954](https://github.com/pnp/sp-dev-fx-controls-react/issues/954)", + "`PeoplePicker`: Default selected items for groups [#958](https://github.com/pnp/sp-dev-fx-controls-react/issues/958)" + ] + }, + "contributions": [ + "[Alexey Morozov](https://github.com/a1exymoroz)", + "[Daniel Stratton](https://github.com/gobigfoot)", + "[Ketill Antoníus Ágústsson](https://github.com/Katli95)", + "[Ravichandran Krishnasamy](https://github.com/ravichandran-blog)", + "[Sergio Ortega Martín](https://github.com/sortegamartin)" + ] + }, { "version": "2.8.0", "changes": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e66e03c9..fc2abfd01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Releases +## 2.9.0 + +### Enhancements + +- `FilePicker`: spanish translation for Stock Images labels [#946](https://github.com/pnp/sp-dev-fx-controls-react/pull/946) +- `FilePicker`: Add support for a defaultFolderAbsolutePath prop [#947](https://github.com/pnp/sp-dev-fx-controls-react/pull/947) +- `DynamicForm`: Returning PnPJS `IItem` in `onSubmitted` event based on `returnListItemInstanceOnSubmit` property [#944](https://github.com/pnp/sp-dev-fx-controls-react/pull/944) +- `DateTimePicker`: Add property for minutes dropdown increment [#939](https://github.com/pnp/sp-dev-fx-controls-react/issues/939) +- `DynamicForm`: Principal Types support [#956](https://github.com/pnp/sp-dev-fx-controls-react/pull/956) +- `Dynamic Form`: Show field descriptions [#975](https://github.com/pnp/sp-dev-fx-controls-react/issues/975) + +### Fixes + +- `RichText`: Image button is checked when hyperlink is added to the text [#948](https://github.com/pnp/sp-dev-fx-controls-react/issues/948) +- `RichText`: impossible to display link with the text equal to the url [#949](https://github.com/pnp/sp-dev-fx-controls-react/issues/949) +- `ComboBoxListItemPicker`: defaultSelectedItems not working [#954](https://github.com/pnp/sp-dev-fx-controls-react/issues/954) +- `PeoplePicker`: Default selected items for groups [#958](https://github.com/pnp/sp-dev-fx-controls-react/issues/958) + +### Contributors + +Special thanks to our contributors (in alphabetical order): [Alexey Morozov](https://github.com/a1exymoroz), [Daniel Stratton](https://github.com/gobigfoot), [Ketill Antoníus Ágústsson](https://github.com/Katli95), [Ravichandran Krishnasamy](https://github.com/ravichandran-blog), [Sergio Ortega Martín](https://github.com/sortegamartin). + ## 2.8.0 ### New control(s) diff --git a/docs/documentation/docs/about/release-notes.md b/docs/documentation/docs/about/release-notes.md index 8e66e03c9..fc2abfd01 100644 --- a/docs/documentation/docs/about/release-notes.md +++ b/docs/documentation/docs/about/release-notes.md @@ -1,5 +1,27 @@ # Releases +## 2.9.0 + +### Enhancements + +- `FilePicker`: spanish translation for Stock Images labels [#946](https://github.com/pnp/sp-dev-fx-controls-react/pull/946) +- `FilePicker`: Add support for a defaultFolderAbsolutePath prop [#947](https://github.com/pnp/sp-dev-fx-controls-react/pull/947) +- `DynamicForm`: Returning PnPJS `IItem` in `onSubmitted` event based on `returnListItemInstanceOnSubmit` property [#944](https://github.com/pnp/sp-dev-fx-controls-react/pull/944) +- `DateTimePicker`: Add property for minutes dropdown increment [#939](https://github.com/pnp/sp-dev-fx-controls-react/issues/939) +- `DynamicForm`: Principal Types support [#956](https://github.com/pnp/sp-dev-fx-controls-react/pull/956) +- `Dynamic Form`: Show field descriptions [#975](https://github.com/pnp/sp-dev-fx-controls-react/issues/975) + +### Fixes + +- `RichText`: Image button is checked when hyperlink is added to the text [#948](https://github.com/pnp/sp-dev-fx-controls-react/issues/948) +- `RichText`: impossible to display link with the text equal to the url [#949](https://github.com/pnp/sp-dev-fx-controls-react/issues/949) +- `ComboBoxListItemPicker`: defaultSelectedItems not working [#954](https://github.com/pnp/sp-dev-fx-controls-react/issues/954) +- `PeoplePicker`: Default selected items for groups [#958](https://github.com/pnp/sp-dev-fx-controls-react/issues/958) + +### Contributors + +Special thanks to our contributors (in alphabetical order): [Alexey Morozov](https://github.com/a1exymoroz), [Daniel Stratton](https://github.com/gobigfoot), [Ketill Antoníus Ágústsson](https://github.com/Katli95), [Ravichandran Krishnasamy](https://github.com/ravichandran-blog), [Sergio Ortega Martín](https://github.com/sortegamartin). + ## 2.8.0 ### New control(s) diff --git a/docs/documentation/docs/controls/DateTimePicker.md b/docs/documentation/docs/controls/DateTimePicker.md index 45fd9833b..226b963b9 100644 --- a/docs/documentation/docs/controls/DateTimePicker.md +++ b/docs/documentation/docs/controls/DateTimePicker.md @@ -71,6 +71,7 @@ The `DateTimePicker` control can be configured with the following properties: | placeholder | string | no | Placeholder text for the DatePicker. | | maxDate | Date | no | The maximum allowable date. | | minDate | Date | no | The minimum allowable date. | +| minutesIncrementStep | MinutesIncrement | no | Specifies minutes' increment step for `TimeDisplayControlType.Dropdow` | Enum `TimeDisplayControlType` @@ -103,4 +104,9 @@ Interface `IDateTimePickerStrings` extends [IDatePickerStrings](https://develope | amDesignator | string | no | Used as AM designator when 12-hour clock is used. | | pmDesignator | string | no | Used as PM designator when 12-hour clock is used. | +Type `MinutesIncrement` +```typescript +type MinutesIncrement = 1 | 5 | 10 | 15 | 30; +``` + ![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/DateTimePicker) diff --git a/docs/documentation/docs/controls/DynamicForm.md b/docs/documentation/docs/controls/DynamicForm.md index c7d23640b..80ca03690 100644 --- a/docs/documentation/docs/controls/DynamicForm.md +++ b/docs/documentation/docs/controls/DynamicForm.md @@ -8,7 +8,7 @@ This control can dynamically generate SharePoint list or SharePoint document lib - Import the following modules to your component: ```TypeScript -import { DynamicForm } from "fx-controls-react/lib/DynamicForm"; +import { DynamicForm } from "@pnp/spfx-controls-react/lib/DynamicForm"; ``` - Use the DynamicForm control in your code as follows: @@ -38,6 +38,7 @@ The `DynamicForm` can be configured with the following properties: | contentTypeId | string | no | content type ID | | disabled | boolean | no | Option allow to be enable or disable. Default value is `false`| | onBeforeSubmit | (listItemData: any) => Promise<boolean> | no | Before submit handler. Allows to modify the object to be submitted or cancel the submission. | -| onSubmitted | (listItem: any) => void | no | Method that returns listItem data JSON object. | +| onSubmitted | (listItemData: any, listItem?: IItem) => void | no | Method that returns listItem data JSON object and PnPJS list item instance (`IItem`). | | onSubmitError | (listItemData: any, error: Error) => void | no | Handler of submission error. | | onCancelled | () => void | no | Handler when form has been cancelled. | +| returnListItemInstanceOnSubmit | boolean | no | Specifies if `onSubmitted` event should pass PnPJS list item (`IItem`) as a second parameter. Default - `true` | diff --git a/docs/documentation/docs/controls/FilePicker.md b/docs/documentation/docs/controls/FilePicker.md index 2d4eebf36..5115219dd 100644 --- a/docs/documentation/docs/controls/FilePicker.md +++ b/docs/documentation/docs/controls/FilePicker.md @@ -75,7 +75,8 @@ The FilePicker component can be configured with the following properties: | label | string | no | Specifies the text describing the file picker. | | buttonLabel | string | no | Specifies the label of the file picker button. | | buttonIcon | string | no | In case it is provided the file picker will be rendered as an action button. | - buttonIconProps | IIconProps | no | In case it is provided the file picker will be rendered as an Icon the and all can define Properties for Icon | +| buttonIconProps | IIconProps | no | In case it is provided the file picker will be rendered as an Icon the and all can define Properties for Icon | +| defaultFolderAbsolutePath | string | no | Optional string parameter to set a default active folder/library for the SiteFilesTab. E.g. `"https://contoso.sharepoint.com/teams/siteName/documentLibrary/Folder 1/SubFolder 1"` | | onSave | (filePickerResult: IFilePickerResult[]) => void | yes | Handler when the file has been selected and picker has been closed. | | onChange | (filePickerResult: IFilePickerResult[]) => void | no | Handler when the file selection has been changed. | | onCancel | () => void | no | Handler when file picker has been cancelled. | diff --git a/package-lock.json b/package-lock.json index 2b924cad7..f2f3febd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@pnp/spfx-controls-react", - "version": "2.8.0", + "version": "2.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5955,7 +5955,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@pnp/common/-/common-2.5.0.tgz", "integrity": "sha512-ea4zTNC3sjLolrHZXP+/2SrJM+yC8PygmPW/yRfgbErdvdwYMUSogT69dW+NUaqhkfYZfkkAoWn42irlLMSpdw==", - "dev": true, "requires": { "tslib": "2.2.0" }, @@ -5963,8 +5962,7 @@ "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" } } }, @@ -5972,7 +5970,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-2.5.0.tgz", "integrity": "sha512-SnmMCN6oADjiHKAIR23FfTqXeQZeXPBnWeVfyZAgzJfRn9uEQoUlkyET3jHjl9kkrFOVkiOD1CRI7TWMIxURbA==", - "dev": true, "requires": { "tslib": "2.2.0" }, @@ -5980,8 +5977,7 @@ "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" } } }, @@ -5989,7 +5985,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-2.5.0.tgz", "integrity": "sha512-AeP01jDvnkiUVn7V+4FT07chz+G/yzrJDH0Gk+qzujJ393ZO6FwJpJEiOCRh9cxF48gqSj/f7r/IIyDHe0+IpQ==", - "dev": true, "requires": { "@pnp/common": "2.5.0", "@pnp/logging": "2.5.0", @@ -5999,8 +5994,7 @@ "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" } } }, @@ -6008,7 +6002,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-2.5.0.tgz", "integrity": "sha512-4s2p+X5qvkXR72NViKb8DIfC+pvj/a3psZ3Im5PRIan2ErMtu9ch3Lb9nkSaMCF3NTJxWOhkUQ/R6tx8ApaUkg==", - "dev": true, "requires": { "@pnp/common": "2.5.0", "@pnp/logging": "2.5.0", @@ -6019,8 +6012,7 @@ "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" } } }, diff --git a/package.json b/package.json index 38254a0f2..5d8606e98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@pnp/spfx-controls-react", "description": "Reusable React controls for SharePoint Framework solutions", - "version": "2.8.0", + "version": "2.9.0", "engines": { "node": ">=10" }, diff --git a/src/common/telemetry/version.ts b/src/common/telemetry/version.ts index 87ca4597d..c3e4503c3 100644 --- a/src/common/telemetry/version.ts +++ b/src/common/telemetry/version.ts @@ -1 +1 @@ -export const version: string = "2.8.0"; \ No newline at end of file +export const version: string = "2.9.0"; \ No newline at end of file diff --git a/src/controls/dateTimePicker/DateTimePicker.tsx b/src/controls/dateTimePicker/DateTimePicker.tsx index 8f2bf4b7d..1cfc2a027 100644 --- a/src/controls/dateTimePicker/DateTimePicker.tsx +++ b/src/controls/dateTimePicker/DateTimePicker.tsx @@ -211,7 +211,8 @@ export class DateTimePicker extends React.Component diff --git a/src/controls/dateTimePicker/IDateTimePickerProps.ts b/src/controls/dateTimePicker/IDateTimePickerProps.ts index fcc270b06..92bda7bb3 100644 --- a/src/controls/dateTimePicker/IDateTimePickerProps.ts +++ b/src/controls/dateTimePicker/IDateTimePickerProps.ts @@ -3,6 +3,8 @@ import { TimeConvention, DateConvention } from './DateTimeConventions'; import { IDateTimePickerStrings } from './IDateTimePickerStrings'; import { TimeDisplayControlType } from './TimeDisplayControlType'; +export type MinutesIncrement = 1 | 5 | 10 | 15 | 30; + /** * Public properties of the DateTimePicker custom field * @@ -102,12 +104,12 @@ export interface IDateTimePickerProps { * Specify if labels in front of date and time parts should be rendered. True by default */ showLabels?: boolean; - + /** * Placeholder text for the DatePicker */ placeholder?: string; - + /** * The minimum allowable date for the DatePicker */ @@ -119,4 +121,9 @@ export interface IDateTimePickerProps { */ maxDate?: Date; + + /** + * Specifies minutes' increment step + */ + minutesIncrementStep?: MinutesIncrement; } diff --git a/src/controls/dateTimePicker/ITimeComponentProps.ts b/src/controls/dateTimePicker/ITimeComponentProps.ts index 34f207546..a17010804 100644 --- a/src/controls/dateTimePicker/ITimeComponentProps.ts +++ b/src/controls/dateTimePicker/ITimeComponentProps.ts @@ -1,5 +1,6 @@ import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown'; import { TimeConvention } from './DateTimeConventions'; +import { MinutesIncrement } from './IDateTimePickerProps'; import { TimeDisplayControlType } from './TimeDisplayControlType'; /** @@ -9,6 +10,7 @@ export interface ITimeComponentProps { disabled?: boolean; value: number; timeDisplayControlType?: TimeDisplayControlType; + minutesIncrementStep?: MinutesIncrement; onChange: (value?: string) => void; } diff --git a/src/controls/dateTimePicker/MinutesComponent.tsx b/src/controls/dateTimePicker/MinutesComponent.tsx index 502b36793..e0073c19a 100644 --- a/src/controls/dateTimePicker/MinutesComponent.tsx +++ b/src/controls/dateTimePicker/MinutesComponent.tsx @@ -16,7 +16,7 @@ export default class MinutesComponent extends React.Component { - try { - let fieldCol = (this.state.fieldCollection || []).slice(); - let field = fieldCol.filter((element, i) => { return element.columnInternalName === internalName; })[0]; - field.newValue = newValue; - field.additionalData = additionalData; - if (field.fieldType === "User" && newValue.length !== 0) { - let result = await sp.web.ensureUser(newValue[0].secondaryText); + // try { + let fieldCol = (this.state.fieldCollection || []).slice(); + let field = fieldCol.filter((element, i) => { return element.columnInternalName === internalName; })[0]; + field.newValue = newValue; + field.additionalData = additionalData; + if (field.fieldType === "User" && newValue.length !== 0) { + // let result = await sp.web.ensureUser(newValue[0].secondaryText); + // field.newValue = result.data.Id; + + if (newValue[0].id === undefined || parseInt(newValue[0].id, 10).toString() === "NaN") { + let user: string = newValue[0].secondaryText; + if (user.indexOf('@') === -1) { + user = newValue[0].loginName; + } + let result = await sp.web.ensureUser(user); field.newValue = result.data.Id; } - else if (field.fieldType === "UserMulti" && newValue.length !== 0) { - field.newValue = []; - for (let index = 0; index < newValue.length; index++) { - const element = newValue[index]; - let user: string = element.secondaryText; - if (user.indexOf('@') === -1) { - user = element.loginName; - } - let result = await sp.web.ensureUser(user); - field.newValue.push(result.data.Id); - } + else { + field.newValue = newValue[0].id; } - this.setState({ - fieldCollection: fieldCol - }); - } catch (error) { - console.log(`Error onchange`, error); - return null; } + else if (field.fieldType === "UserMulti" && newValue.length !== 0) { + field.newValue = []; + for (let index = 0; index < newValue.length; index++) { + const element = newValue[index]; + var retrivedItem: boolean = false; + if (field.fieldDefaultValue != null) { + if (field.fieldDefaultValue.join(',').indexOf(element.text) !== -1) + field.fieldDefaultValue.forEach(item => { + if (item.split('/')[1] === element.text) { + retrivedItem = true; + field.newValue.push(item.split('/')[0]); + } + }); + } + if (!retrivedItem) { + if (element.id === undefined || parseInt(element.id, 10).toString() === "NaN") { + let user: string = element.secondaryText; + if (user.indexOf('@') === -1) { + user = element.loginName; + } + let result = await sp.web.ensureUser(user); + field.newValue.push(result.data.Id); + } + else { + field.newValue.push(element.id); + } + } + } + } + this.setState({ + fieldCollection: fieldCol + }); + // } catch (error) { + + // console.log(`Error onchange`, error); + // return null; + // } } //getting all the fields information as part of get ready process @@ -291,6 +321,7 @@ export class DynamicForm extends React.Component a.Order - b.Order); } diff --git a/src/controls/dynamicForm/IDynamicFormProps.ts b/src/controls/dynamicForm/IDynamicFormProps.ts index 1fa36c1d2..d186ada9a 100644 --- a/src/controls/dynamicForm/IDynamicFormProps.ts +++ b/src/controls/dynamicForm/IDynamicFormProps.ts @@ -1,5 +1,6 @@ import { ExtensionContext } from '@microsoft/sp-extension-base'; import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { IItem } from '@pnp/sp/items'; export interface IDynamicFormProps { /** @@ -22,7 +23,7 @@ export interface IDynamicFormProps { /** * Handler for form submitted event */ - onSubmitted?: (listItemData: any) => void; + onSubmitted?: (listItemData: any, listItem?: IItem) => void; /** * Handler of submission error */ @@ -39,6 +40,12 @@ export interface IDynamicFormProps { * Content type id of the item */ contentTypeId?: string; + + /** + * Specifies if onSubmitted event should pass PnPJS list item (IItem) as a second parameter. Default - true + */ + returnListItemInstanceOnSubmit?: boolean; + /** * Used to execute WebSearch. If not provided SearchTab will not be available. */ diff --git a/src/controls/dynamicForm/dynamicField/DynamicField.tsx b/src/controls/dynamicForm/dynamicField/DynamicField.tsx index 1b551f519..342dcffe0 100644 --- a/src/controls/dynamicForm/dynamicField/DynamicField.tsx +++ b/src/controls/dynamicForm/dynamicField/DynamicField.tsx @@ -74,7 +74,9 @@ export class DynamicField extends React.Component{labelText}; const errorText = this.getRequiredErrorText(); const errorTextEl = {errorText}; + const descriptionEl = {description}; switch (fieldType) { case 'loading': @@ -118,6 +121,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; case 'Note': @@ -134,6 +138,7 @@ export class DynamicField extends React.Component { this.onChange(newText); return newText; }} isEditMode={disabled}> + {descriptionEl} {errorTextEl} ; } @@ -153,6 +158,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; } @@ -168,6 +174,7 @@ export class DynamicField extends React.Component { this.onChange(option); }} onBlur={this.onBlur} errorMessage={errorText} /> + {descriptionEl} ; case 'MultiChoice': @@ -183,6 +190,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; case 'Location': @@ -199,6 +207,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; case 'Lookup': @@ -218,6 +227,7 @@ export class DynamicField extends React.Component { this.onChange(newValue); }} context={context} /> + {descriptionEl} {errorTextEl} ; @@ -238,6 +248,7 @@ export class DynamicField extends React.Component { this.onChange(newValue); }} context={context} /> + {descriptionEl} {errorTextEl} ; @@ -256,6 +267,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; case 'Currency': @@ -273,6 +285,7 @@ export class DynamicField extends React.Component + {descriptionEl} ; case 'DateTime': @@ -301,6 +314,7 @@ export class DynamicField extends React.Component { this.onChange(newDate); }} disabled={disabled} /> } + {descriptionEl} {errorTextEl} ; @@ -318,6 +332,7 @@ export class DynamicField extends React.Component { this.onChange(checkedvalue); }} disabled={disabled} /> + {descriptionEl} {errorTextEl} ; @@ -335,11 +350,12 @@ export class DynamicField extends React.Component { this.onChange(items); }} disabled={disabled} /> + {descriptionEl} {errorTextEl} ; @@ -357,11 +373,12 @@ export class DynamicField extends React.Component { this.onChange(items); }} disabled={disabled} /> + {descriptionEl} {errorTextEl} ; @@ -387,6 +404,7 @@ export class DynamicField extends React.Component { this.onURLChange(newText, false); }} disabled={disabled} /> + {descriptionEl} {errorTextEl} ; @@ -434,6 +452,7 @@ export class DynamicField extends React.Component} + {descriptionEl} {errorTextEl} ; @@ -457,6 +476,7 @@ export class DynamicField extends React.Component + {descriptionEl} {errorTextEl} ; @@ -479,6 +499,7 @@ export class DynamicField extends React.Component { this.onChange(newValue); }} isTermSetSelectable={false} /> + {descriptionEl} {errorTextEl} ; } diff --git a/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts b/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts index aeaa6cf31..3df0aa1bc 100644 --- a/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts +++ b/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts @@ -32,4 +32,6 @@ export interface IDynamicFieldProps { //bingAPIKey?: string; dateFormat?: DateFormat; additionalData?: FieldChangeAdditionalData; + principalType?:string; + description?: string; } diff --git a/src/controls/filePicker/FilePicker.tsx b/src/controls/filePicker/FilePicker.tsx index 9e3b51d93..b2a62be0a 100644 --- a/src/controls/filePicker/FilePicker.tsx +++ b/src/controls/filePicker/FilePicker.tsx @@ -214,6 +214,7 @@ export class FilePicker extends React.Component< )} diff --git a/src/controls/filePicker/FilePicker.types.ts b/src/controls/filePicker/FilePicker.types.ts index 84c984622..96b990124 100644 --- a/src/controls/filePicker/FilePicker.types.ts +++ b/src/controls/filePicker/FilePicker.types.ts @@ -1,11 +1,11 @@ import { WebPartContext } from "@microsoft/sp-webpart-base"; import { IBreadcrumbItem } from "office-ui-fabric-react/lib/Breadcrumb"; -import { IFile, ILibrary } from "../../services/FileBrowserService.types"; +import { IFile, IFolder, ILibrary } from "../../services/FileBrowserService.types"; import { ExtensionContext } from "@microsoft/sp-extension-base"; export interface FilePickerBreadcrumbItem extends IBreadcrumbItem { libraryData?: ILibrary; - folderData?: IFile; + folderData?: IFolder; } export interface IFilePickerTab { diff --git a/src/controls/filePicker/IFilePickerProps.ts b/src/controls/filePicker/IFilePickerProps.ts index 89af13ae5..9908790f0 100644 --- a/src/controls/filePicker/IFilePickerProps.ts +++ b/src/controls/filePicker/IFilePickerProps.ts @@ -157,4 +157,9 @@ export interface IFilePickerProps { * Specifies if Site Pages library to be visible on Sites tab */ includePageLibraries?: boolean; + + /** + * Specifies a default folder to be active in the Site Files tab + */ + defaultFolderAbsolutePath?: string; } diff --git a/src/controls/filePicker/SiteFilePickerTab/ISiteFilePickerTabProps.ts b/src/controls/filePicker/SiteFilePickerTab/ISiteFilePickerTabProps.ts index 404f9c826..654710c01 100644 --- a/src/controls/filePicker/SiteFilePickerTab/ISiteFilePickerTabProps.ts +++ b/src/controls/filePicker/SiteFilePickerTab/ISiteFilePickerTabProps.ts @@ -10,5 +10,10 @@ export interface ISiteFilePickerTabProps extends IFilePickerTab { */ breadcrumbFirstNode?: IBreadcrumbItem; + /** + * Specifies a default folder to be active in the Site Files tab + */ + defaultFolderAbsolutePath?: string; + includePageLibraries?: boolean; } diff --git a/src/controls/filePicker/SiteFilePickerTab/SiteFilePickerTab.tsx b/src/controls/filePicker/SiteFilePickerTab/SiteFilePickerTab.tsx index 220099cbe..0e7613c51 100644 --- a/src/controls/filePicker/SiteFilePickerTab/SiteFilePickerTab.tsx +++ b/src/controls/filePicker/SiteFilePickerTab/SiteFilePickerTab.tsx @@ -1,46 +1,153 @@ import * as React from 'react'; import findIndex from 'lodash/findIndex'; import { ISiteFilePickerTabProps } from './ISiteFilePickerTabProps'; -import {ISiteFilePickerTabState } from './ISiteFilePickerTabState'; +import { ISiteFilePickerTabState } from './ISiteFilePickerTabState'; import { DocumentLibraryBrowser } from '../controls/DocumentLibraryBrowser/DocumentLibraryBrowser'; import { FileBrowser } from '../controls/FileBrowser/FileBrowser'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/components/Button'; import { Breadcrumb, IBreadcrumbItem } from 'office-ui-fabric-react/lib/Breadcrumb'; -import { IFile, ILibrary } from '../../../services/FileBrowserService.types'; +import { IFile, IFolder, ILibrary } from '../../../services/FileBrowserService.types'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { IFilePickerResult, FilePickerBreadcrumbItem } from '../FilePicker.types'; +import { SPWeb } from "@microsoft/sp-page-context"; + import styles from './SiteFilePickerTab.module.scss'; import * as strings from 'ControlStrings'; import { urlCombine } from '../../../common/utilities'; +import { cloneDeep } from '@microsoft/sp-lodash-subset'; export default class SiteFilePickerTab extends React.Component { + private _defaultLibraryNamePromise: Promise = Promise.resolve(); + constructor(props: ISiteFilePickerTabProps) { super(props); // Add current site to the breadcrumb or the provided node - const breadcrumbSiteNode: FilePickerBreadcrumbItem = this.props.breadcrumbFirstNode ? this.props. breadcrumbFirstNode : { - isCurrentItem: true, + const breadcrumbSiteNode: FilePickerBreadcrumbItem = this.props.breadcrumbFirstNode ? this.props.breadcrumbFirstNode : { + isCurrentItem: false, text: props.context.pageContext.web.title, - key: props.context.pageContext.web.id.toString() + key: props.context.pageContext.web.id.toString(), + onClick: (ev, itm) => { this.onBreadcrumpItemClick(itm); } }; - breadcrumbSiteNode.onClick = () => { this.onBreadcrumpItemClick(breadcrumbSiteNode); }; + + let breadcrumbItems: FilePickerBreadcrumbItem[] = [breadcrumbSiteNode]; + + let { folderAbsPath = undefined, libraryServRelUrl = undefined, folderServRelPath = undefined, folderBreadcrumbs = [] } = props.defaultFolderAbsolutePath + ? this._parseInitialLocationState( + props.defaultFolderAbsolutePath, + props.context.pageContext.web + ) + : {}; + + breadcrumbItems.push(...folderBreadcrumbs); + + breadcrumbItems[breadcrumbItems.length - 1].isCurrentItem = true; this.state = { filePickerResult: null, - libraryAbsolutePath: undefined, - libraryUrl: urlCombine(props.context.pageContext.web.serverRelativeUrl, '/Shared%20Documents'), - libraryPath: undefined, + libraryAbsolutePath: folderAbsPath || undefined, + libraryUrl: libraryServRelUrl || urlCombine(props.context.pageContext.web.serverRelativeUrl, '/Shared%20Documents'), + libraryPath: folderServRelPath, folderName: strings.DocumentLibraries, - breadcrumbItems: [breadcrumbSiteNode] + breadcrumbItems }; } + private _parseInitialLocationState(folderAbsPath: string, { serverRelativeUrl: webServRelUrl, absoluteUrl: webAbsUrl }: SPWeb) { + // folderAbsPath: "https://tenant.sharepoint.com/teams/Test/DocLib/Folder" + + // folderServRelPath: "/teams/Test/DocLib/Folder" + let folderServRelPath = folderAbsPath && folderAbsPath.substr(folderAbsPath.indexOf(webServRelUrl)); + + // folderWebRelPath: "/DocLib/Folder" + let folderWebRelPath = folderServRelPath && folderServRelPath.substr(webServRelUrl.length); + let libInternalName = folderWebRelPath && folderWebRelPath.substring(1, Math.max(folderWebRelPath.indexOf("/", 2), 0) || undefined); + + // libraryServRelUrl: "/teams/Test/DocLib/" + let libraryServRelUrl = urlCombine(webServRelUrl, libInternalName); + + let tenantUrl = folderAbsPath.substring(0, folderAbsPath.indexOf(webServRelUrl)); + let folderBreadcrumbs: FilePickerBreadcrumbItem[] = this.parseBreadcrumbsFromPaths( + libraryServRelUrl, + folderServRelPath, + folderWebRelPath, + webAbsUrl, + tenantUrl, + libInternalName + ); + + return { libraryServRelUrl, folderServRelPath, folderAbsPath, folderBreadcrumbs }; + } + + private parseBreadcrumbsFromPaths( + libraryServRelUrl: string, + folderServRelPath: string, + folderWebRelPath: string, + webAbsUrl: string, + tenantUrl: string, + libInternalName: string + ) { + this._defaultLibraryNamePromise = this.props.fileBrowserService.getLibraryNameByInternalName(libInternalName); + let folderBreadcrumbs: FilePickerBreadcrumbItem[] = []; + folderBreadcrumbs.push({ + isCurrentItem: false, + text: libInternalName, + key: libraryServRelUrl, + libraryData: { + serverRelativeUrl: libraryServRelUrl, + absoluteUrl: urlCombine(webAbsUrl, libInternalName), + title: libInternalName + }, + onClick: (ev, itm) => { this.onBreadcrumpItemClick(itm); } + }); + + if (folderServRelPath != libraryServRelUrl) { + let folderLibRelPath = folderWebRelPath.substring(libInternalName.length + 2); + let breadcrumbFolderServRelPath = libraryServRelUrl; + + let crumbs: FilePickerBreadcrumbItem[] = folderLibRelPath.split("/").map((currFolderName => { + breadcrumbFolderServRelPath += `/${currFolderName}`; + return { + isCurrentItem: false, + text: currFolderName, + key: urlCombine(tenantUrl, breadcrumbFolderServRelPath), + folderData: { + name: currFolderName, + absoluteUrl: urlCombine(tenantUrl, breadcrumbFolderServRelPath), + serverRelativeUrl: breadcrumbFolderServRelPath, + }, + onClick: (ev, itm) => { this.onBreadcrumpItemClick(itm); } + }; + })); + + folderBreadcrumbs.push(...crumbs); + } + return folderBreadcrumbs; + } + + public componentDidMount(): void { + this._defaultLibraryNamePromise.then(docLibName => { + if (docLibName) { + let updatedBCItems = cloneDeep(this.state.breadcrumbItems); + updatedBCItems.forEach(crumb => { + if (crumb.libraryData) { + crumb.text = docLibName; + crumb.libraryData.title = docLibName; + } + }); + this.setState({ breadcrumbItems: updatedBCItems }); + } + }).catch((err) => { + console.log("[SiteFilePicker] Failed To Fetch defaultLibraryName, defaulting to internal name"); + }); + } + public render(): React.ReactElement { return ( -
+
- +
{this.state.libraryAbsolutePath === undefined && @@ -142,7 +249,7 @@ export default class SiteFilePickerTab extends React.Component { + private _handleOpenFolder = (folder: IFolder, addBreadcrumbNode: boolean) => { const { breadcrumbItems } = this.state; if (addBreadcrumbNode) { @@ -151,9 +258,9 @@ export default class SiteFilePickerTab extends React.Component { this.onBreadcrumpItemClick(itm); } }; - breadcrumbNode.onClick = () => { this.onBreadcrumpItemClick(breadcrumbNode); }; breadcrumbItems.push(breadcrumbNode); } @@ -177,9 +284,9 @@ export default class SiteFilePickerTab extends React.Component { this.onBreadcrumpItemClick(itm); } }; - breadcrumbNode.onClick = () => { this.onBreadcrumpItemClick(breadcrumbNode); }; breadcrumbItems.push(breadcrumbNode); } this.setState({ diff --git a/src/controls/listItemPicker/ComboBoxListItemPicker.tsx b/src/controls/listItemPicker/ComboBoxListItemPicker.tsx index 35890f043..39a66914e 100644 --- a/src/controls/listItemPicker/ComboBoxListItemPicker.tsx +++ b/src/controls/listItemPicker/ComboBoxListItemPicker.tsx @@ -7,11 +7,12 @@ import { ComboBox, IComboBoxOption } from "office-ui-fabric-react/lib/ComboBox"; import { ListItemRepository } from '../../common/dal/ListItemRepository'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react'; import styles from './ComboBoxListItemPicker.module.scss'; +import { cloneDeep, isEqual } from 'lodash'; export class ComboBoxListItemPicker extends React.Component { private _listItemRepo: ListItemRepository; - public selectedItems: any[]; + private _options: any[] = null; constructor(props: IComboBoxListItemPickerProps) { super(props); @@ -23,13 +24,13 @@ export class ComboBoxListItemPicker extends React.Component { - return { - key: option[keyColumnName], - text: option[columnInternalName || "Id"] - }; - }); - if (defaultSelectedItems) { - //if passed only ids - if (!isNaN(defaultSelectedItems[0])) { - this.selectedItems = options.filter(opt => defaultSelectedItems.indexOf(opt.key) >= 0); - } - else { - this.selectedItems = options.filter(opt => defaultSelectedItems.map(selected => selected[keyColumnName]).indexOf(opt.key) >= 0); - } + if (!this._options || listId !== this.props.listId) { + const listItems = await this._listItemRepo.getListItemsByFilterClause(query, + listId, + columnInternalName, + keyColumnInternalName, + webUrl, + itemLimit || 100); + + this._options = listItems.map(option => { + return { + key: option[keyColumnName], + text: option[columnInternalName || "Id"] + }; + }); } + + const selectedItems = this._getSelectedItems(props); + this.setState({ - availableOptions: options + availableOptions: this._options, + selectedItems: selectedItems, }); if (onInitialized && isInitialLoad !== false) { onInitialized(); @@ -82,9 +79,41 @@ export class ComboBoxListItemPicker extends React.Component { if (nextProps.listId !== this.props.listId) { + this.setState({ + selectedItems: [], + }); await this.loadOptions(nextProps, false); - this.selectedItems = []; } + if (!isEqual(nextProps.defaultSelectedItems, this.props.defaultSelectedItems)) { + const selectedItems = this._getSelectedItems(nextProps); + this.setState({ + selectedItems: selectedItems, + }); + } + } + + private _getSelectedItems(props: IComboBoxListItemPickerProps): any[] { + let selectedItems: any[] = []; + let keyColumnName = props.keyColumnInternalName || "Id"; + if (props.defaultSelectedItems) { + //if passed only ids + if (!isNaN(props.defaultSelectedItems[0])) { + selectedItems = this._options.filter(opt => props.defaultSelectedItems.indexOf(opt.key) >= 0); + } + else { + selectedItems = this._options.filter(opt => props.defaultSelectedItems.map(selected => selected[keyColumnName]).indexOf(opt.key) >= 0); + } + } + if (selectedItems && selectedItems.length > 0) { + selectedItems = selectedItems.map(item => { + return { + [this.props.keyColumnInternalName || "Id"]: item.key, + [this.props.columnInternalName]: item.text + }; + }); + } + + return selectedItems; } /*public componentDidUpdate(prevProps: IComboBoxListItemPickerProps, prevState: IComboBoxListItemPickerState): void { @@ -98,6 +127,7 @@ export class ComboBoxListItemPicker extends React.Component { const { className, disabled } = this.props; + const selectedKeys = this.state.selectedItems ? this.state.selectedItems.map(item => item[this.props.keyColumnInternalName || "Id"]) : []; return (
@@ -113,7 +143,7 @@ export class ComboBoxListItemPicker extends React.Component item.key) || []} + selectedKey={selectedKeys} className={className} disabled={disabled || !this.state.availableOptions} /> {!this.state.errorMessage && !this.state.availableOptions && ()} @@ -127,25 +157,30 @@ export class ComboBoxListItemPicker extends React.Component { + let selectedItems: any[] = cloneDeep(this.state.selectedItems); if (this.props.multiSelect) { if (option && option.selected) { - this.selectedItems.push({ + selectedItems.push({ [this.props.keyColumnInternalName || "Id"]: option.key, [this.props.columnInternalName]: option.text, selected: option.selected }); } else { - this.selectedItems = this.selectedItems.filter(o => o[this.props.keyColumnInternalName || "Id"] !== option.key); + selectedItems = selectedItems.filter(o => o[this.props.keyColumnInternalName || "Id"] !== option.key); } } else { - this.selectedItems.push({ + selectedItems.push({ [this.props.keyColumnInternalName || "Id"]: option.key, [this.props.columnInternalName]: option.text }); - this.selectedItems = this.selectedItems.filter(o => o[this.props.keyColumnInternalName || "Id"] === option.key); + selectedItems = selectedItems.filter(o => o[this.props.keyColumnInternalName || "Id"] === option.key); } - this.props.onSelectedItem(this.selectedItems); + this.setState({ + selectedItems: selectedItems, + }); + + this.props.onSelectedItem(selectedItems); } } diff --git a/src/controls/richText/RichText.tsx b/src/controls/richText/RichText.tsx index 6e3235da4..9d7533348 100644 --- a/src/controls/richText/RichText.tsx +++ b/src/controls/richText/RichText.tsx @@ -381,7 +381,7 @@ export class RichText extends React.Component { { - if (newValue !== this.state.insertUrl) { + if (newValue !== this.state.insertUrlText) { this.setState({ insertUrlText: newValue }); @@ -602,7 +602,7 @@ export class RichText extends React.Component { - { // if we're already in list mode, toggle off const key = item.key; const newAlignValue = (key === 'bullet' && this.state.formats.list === 'bullet') || (key === 'numbered' && this.state.formats.list === 'numbered') ? false : key; - console.log(newAlignValue); this.applyFormat("list", newAlignValue); } diff --git a/src/loc/es-es.ts b/src/loc/es-es.ts index 922da1ada..8700ecaa5 100644 --- a/src/loc/es-es.ts +++ b/src/loc/es-es.ts @@ -310,6 +310,8 @@ define([], () => { "UploadImageHeader": "Subir imagen", "UploadLinkLabel": "Subir", "WebSearchLinkLabel": "Búsqueda web", + "StockImagesLinkLabel": "Imágenes de stock", + "StockImagesHeader": "Imágenes de stock", "Yes": "Sí", "SelectedLabel": "Seleccionado", "SelectIcon": "Seleccionar icono" diff --git a/src/services/FileBrowserService.ts b/src/services/FileBrowserService.ts index c03648551..57ea0a072 100644 --- a/src/services/FileBrowserService.ts +++ b/src/services/FileBrowserService.ts @@ -80,6 +80,30 @@ export class FileBrowserService { } } + /** + * Gets document and media libraries from the site + */ + public getLibraryNameByInternalName = async (internalName: string): Promise => { + try { + const absoluteUrl = this.context.pageContext.web.absoluteUrl; + const restApi = `${absoluteUrl}/_api/web/GetFolderByServerRelativeUrl('${internalName}')/Properties?$select=vti_x005f_listtitle`; + const libraryResult = await this.context.spHttpClient.get(restApi, SPHttpClient.configurations.v1); + + if (!libraryResult || !libraryResult.ok) { + throw new Error(`Something went wrong when executing request. Status='${libraryResult.status}'`); + } + const libResults: { vti_x005f_listtitle: string } = await libraryResult.json(); + if (!libResults || !libResults.vti_x005f_listtitle) { + throw new Error(`Cannot read data from the results.`); + } + + return libResults.vti_x005f_listtitle != internalName && libResults.vti_x005f_listtitle || ""; + } catch (error) { + console.error(`[FileBrowserService.getSiteLibraryNameByInternalName]: Err='${error.message}'`); + return null; + } + } + /** * Downloads document content from SP location. */ @@ -118,7 +142,7 @@ export class FileBrowserService { } }; if (folderPath) { - body.parameters["FolderServerRelativeUrl"] = folderPath; + body.parameters["FolderServerRelativeUrl"] = folderPath; } const data: any = await this.context.spHttpClient.fetch(restApi, SPHttpClient.configurations.v1, { method: "POST", diff --git a/src/services/FileBrowserService.types.ts b/src/services/FileBrowserService.types.ts index 01a36694d..db146dd19 100644 --- a/src/services/FileBrowserService.types.ts +++ b/src/services/FileBrowserService.types.ts @@ -26,6 +26,10 @@ export interface IFile { supportsThumbnail: boolean; } +export interface IFolder extends Pick { + +} + export interface ILibrary { title: string; absoluteUrl: string; diff --git a/src/services/PeopleSearchService.ts b/src/services/PeopleSearchService.ts index ab3bd340d..4db16563a 100644 --- a/src/services/PeopleSearchService.ts +++ b/src/services/PeopleSearchService.ts @@ -228,8 +228,8 @@ export default class SPPeopleSearchService { const userResults = values.map(element => { switch (element.EntityType) { case 'User': - const accountName: string = element.Description || ""; - const email: string = element.EntityData.Email || element.Description; + const accountName: string = element.Description || ""; + const email: string = element.EntityData && element.EntityData.Email ? element.EntityData.Email : element.Description; return { id: element.Key, loginName: element.LoginName ? element.LoginName : element.Key, @@ -241,12 +241,13 @@ export default class SPPeopleSearchService { optionalText: "" // anything } as IPeoplePickerUserItem; case 'SecGroup': + const secondaryText = element.EntityData && element.EntityData.Email ? element.EntityData.Email : element.ProviderName; return { id: element.Key, loginName: element.LoginName ? element.LoginName : element.Key, imageInitials: this.getFullNameInitials(element.DisplayText), text: element.DisplayText, - secondaryText: element.ProviderName + secondaryText, } as IPeoplePickerUserItem; case 'FormsRole': return { diff --git a/src/services/SPService.ts b/src/services/SPService.ts index a5cdad5d7..187349e40 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -427,7 +427,7 @@ export default class SPService implements ISPService { public async getUsersUPNFromFieldValue(listId: string, listItemId: number, fieldName: string, webUrl?: string): Promise { try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - let apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemId})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/UserName&$expand=${fieldName}`; + let apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemId})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/Title,${fieldName}/Id&$expand=${fieldName}`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { @@ -435,7 +435,7 @@ export default class SPService implements ISPService { if (result && result[fieldName]) { let emails = []; result[fieldName].forEach(element => { - emails.push(element.UserName); + emails.push(element.Id + "/" + element.Title); }); return emails; } @@ -448,16 +448,16 @@ export default class SPService implements ISPService { } } - public async getUserUPNById(userId: number, webUrl?: string): Promise { + public async getUserUPNById(userId: number, webUrl?: string): Promise { try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - let apiUrl = `${webAbsoluteUrl}/_api/web/getuserbyid(${userId})?$select=UserPrincipalName`; + let apiUrl = `${webAbsoluteUrl}/_api/web/getuserbyid(${userId})?$select=UserPrincipalName,Title`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { const results = await data.json(); if (results) { - return results.UserPrincipalName; + return userId + "/" + results.Title; } } diff --git a/src/webparts/controlsTest/ControlsTestWebPart.ts b/src/webparts/controlsTest/ControlsTestWebPart.ts index 6f5c98e9a..5331d05a4 100644 --- a/src/webparts/controlsTest/ControlsTestWebPart.ts +++ b/src/webparts/controlsTest/ControlsTestWebPart.ts @@ -10,7 +10,7 @@ import { } from '@microsoft/sp-property-pane'; import * as strings from 'ControlsTestWebPartStrings'; -import ControlsTest from './components/ControlsTest_SingleComponent'; +import ControlsTest from './components/ControlsTest'; import { IControlsTestProps } from './components/IControlsTestProps'; import { IControlsTestWebPartProps } from './IControlsTestWebPartProps'; diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index 17af2eb48..373388216 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -167,6 +167,7 @@ import { SitePicker } from "../../../controls/sitePicker/SitePicker"; import { DynamicForm } from '../../../controls/dynamicForm'; import { LocationPicker } from "../../../controls/locationPicker/LocationPicker"; import { ILocationPickerItem } from "../../../controls/locationPicker/ILocationPicker"; +import { debounce } from "lodash"; // Used to render document card /** @@ -417,6 +418,7 @@ export default class ControlsTest extends React.Component console.log("DateTimePicker value:", value)} placeholder="Pick a date" /> - console.log("DateTimePicker value:", value)} /> + console.log("DateTimePicker value:", value)} timeDisplayControlType={TimeDisplayControlType.Dropdown} minutesIncrementStep={15} /> console.log("DateTimePicker value:", value)} /> console.log("DateTimePicker value:", value)} /> console.log("DateTimePicker value:", value)} /> @@ -1210,14 +1212,12 @@ export default class ControlsTest extends React.Component - Drag files or folder with files here...} buttonLabel='Configure' hideButton={this.props.displayMode === DisplayMode.Read} onConfigure={this._onConfigure} /> -

@@ -1322,7 +1322,7 @@ export default class ControlsTest extends React.Component
Site picker tester: - { @@ -1384,6 +1385,11 @@ export default class ControlsTest extends React.Component + { + this.setState({ + comboBoxListItemPickerIds: [{Id: 2, Title: '222'}] + }); + }} />
@@ -1531,11 +1537,16 @@ export default class ControlsTest extends React.Component

File Picker

+ { this.setState({ filePickerDefaultFolderAbsolutePath: newVal }); }, 500)} + styles={{ root: { marginBottom: 10 } }} + /> { console.log(filePickerResult); }} diff --git a/src/webparts/controlsTest/components/ControlsTest_SingleComponent.tsx b/src/webparts/controlsTest/components/ControlsTest_SingleComponent.tsx index e55683127..de1b15cee 100644 --- a/src/webparts/controlsTest/components/ControlsTest_SingleComponent.tsx +++ b/src/webparts/controlsTest/components/ControlsTest_SingleComponent.tsx @@ -133,7 +133,8 @@ export default class ControlsTest extends React.Component