From e924e100b81744d31b30d2c28db213152c9ecfc9 Mon Sep 17 00:00:00 2001 From: Valeras Narbutas Date: Wed, 13 Dec 2023 18:37:26 +0200 Subject: [PATCH 01/46] fix bug in readme display --- docs/documentation/docs/guides/contributing.md | 5 +++-- docs/documentation/docs/guides/submitting-pr.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/documentation/docs/guides/contributing.md b/docs/documentation/docs/guides/contributing.md index 77e421f22..3caa904d0 100644 --- a/docs/documentation/docs/guides/contributing.md +++ b/docs/documentation/docs/guides/contributing.md @@ -27,7 +27,7 @@ Typos are embarrassing! Most PR's that fix typos will be accepted immediately. I Before contributing: - ensure that the **dev** branch on your fork is in sync with the original **sp-dev-fx-controls-react** repository - ```sh + ```bash # assuming you are in the folder of your locally cloned fork.... git checkout dev @@ -39,7 +39,8 @@ Before contributing: ``` - create a feature branch for your change. If you'll get stuck on an issue or merging your PR will take a while, this will allow you to have a clean dev branch that you can use for contributing other changes - ```sh + + ```bash git checkout -b my-contribution ``` diff --git a/docs/documentation/docs/guides/submitting-pr.md b/docs/documentation/docs/guides/submitting-pr.md index 6cf972aed..403789035 100644 --- a/docs/documentation/docs/guides/submitting-pr.md +++ b/docs/documentation/docs/guides/submitting-pr.md @@ -7,7 +7,7 @@ We appreciate your initiative and would love to integrate your work with the res - [AC: Keep Your Forked Git Repo Updated with Changes from the Original Upstream Repo](http://www.andrewconnell.com/blog/keep-your-forked-git-repo-updated-with-changes-from-the-original-upstream-repo) - Looking for a quick cheat sheet? Look no further: - ```sh + ```bash # assuming you are in the folder of your locally cloned fork.... git checkout dev From c63df4affd70d3386a4944a03fcf6c5549e992ea Mon Sep 17 00:00:00 2001 From: Nishkalank Date: Tue, 23 Jan 2024 17:07:25 +0100 Subject: [PATCH 02/46] Update Folderpicker documentation --- docs/documentation/docs/controls/FolderPicker.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/documentation/docs/controls/FolderPicker.md b/docs/documentation/docs/controls/FolderPicker.md index a4e3df00e..b3d335495 100644 --- a/docs/documentation/docs/controls/FolderPicker.md +++ b/docs/documentation/docs/controls/FolderPicker.md @@ -42,6 +42,19 @@ import { FolderPicker, IFolder } from "@pnp/spfx-controls-react/lib/FolderPicker canCreateFolders={true} /> ``` +- To use the `FolderExplorer` control to fetch folders from different sitecollection in your code as follows: + +```TypeScript + +``` + - The `onSelect` change event returns the selected folder and can be implemented as follows: ```TypeScript From ff369dcaa15e236d9f1bfcc7b3c0c438502a7db8 Mon Sep 17 00:00:00 2001 From: guidozam Date: Wed, 24 Jan 2024 12:50:33 +0100 Subject: [PATCH 03/46] Added customIcons property on DynamicForm. --- .../docs/controls/DynamicForm.md | 1 + src/controls/dynamicForm/DynamicForm.tsx | 43 ++++++++-------- src/controls/dynamicForm/IDynamicFormProps.ts | 14 +++++- .../dynamicForm/dynamicField/DynamicField.tsx | 49 ++++++++++--------- .../dynamicField/IDynamicFieldProps.ts | 31 ++++++------ .../controlsTest/components/ControlsTest.tsx | 18 ++++--- 6 files changed, 89 insertions(+), 67 deletions(-) diff --git a/docs/documentation/docs/controls/DynamicForm.md b/docs/documentation/docs/controls/DynamicForm.md index ce2326d4d..6eb1b8413 100644 --- a/docs/documentation/docs/controls/DynamicForm.md +++ b/docs/documentation/docs/controls/DynamicForm.md @@ -62,6 +62,7 @@ The `DynamicForm` can be configured with the following properties: | fieldOverrides | {[columnInternalName: string] : {(fieldProperties: IDynamicFieldProps): React.ReactElement\}} | no | Key value pair for fields you want to override. Key is the internal field name, value is the function to be called for the custom element to render. | | respectEtag | boolean | no | Specifies if the form should respect the ETag of the item. Default - `true` | | validationErrorDialogProps | IValidationErrorDialogProps | no | Specifies validation error dialog properties | +| customIcons | { [ columnInternalName: string ]: string } | no | Specifies custom icons for the form. The key of this dictionary is the column internal name, the value is the Fluent UI icon name. | ## Validation Error Dialog Properties `IValidationErrorDialogProps` | Property | Type | Required | Description | diff --git a/src/controls/dynamicForm/DynamicForm.tsx b/src/controls/dynamicForm/DynamicForm.tsx index b4cffa172..53eb12f1c 100644 --- a/src/controls/dynamicForm/DynamicForm.tsx +++ b/src/controls/dynamicForm/DynamicForm.tsx @@ -148,7 +148,7 @@ export class DynamicForm extends React.Component< */ public render(): JSX.Element { const { customFormatting, fieldCollection, hiddenByFormula, infoErrorMessages, isSaving } = this.state; - + const customFormattingDisabled = this.props.useCustomFormatting === false; // Custom Formatting - Header @@ -177,7 +177,7 @@ export class DynamicForm extends React.Component< footerContent = this._customFormatter.renderCustomFormatContent(customFormatting.footer, this.getFormValuesForValidation(), true) as JSX.Element; } - // Content Type + // Content Type let contentTypeId = this.props.contentTypeId; if (this.state.contentTypeId !== undefined) contentTypeId = this.state.contentTypeId; @@ -301,7 +301,7 @@ export class DynamicForm extends React.Component< ); } - private updateFormMessages(type: MessageBarType, message: string): void { + private updateFormMessages(type: MessageBarType, message: string): void { const { infoErrorMessages } = this.state; const newMessages = infoErrorMessages.slice(); newMessages.push({ type, message }); @@ -320,7 +320,7 @@ export class DynamicForm extends React.Component< validationErrorDialogProps, returnListItemInstanceOnSubmit } = this.props; - + let contentTypeId = this.props.contentTypeId; if (this.state.contentTypeId !== undefined) contentTypeId = this.state.contentTypeId; @@ -491,7 +491,7 @@ export class DynamicForm extends React.Component< } else { objects[columnInternalName] = null; } - } + } } } @@ -615,7 +615,7 @@ export class DynamicForm extends React.Component< } console.log("Error", error); } - } + } this.setState({ isSaving: false, @@ -646,7 +646,7 @@ export class DynamicForm extends React.Component< returnListItemInstanceOnSubmit } = this.props; - + if (selectedFile !== undefined) { try { const idField = "ID"; @@ -660,14 +660,14 @@ export class DynamicForm extends React.Component< "_" ) // Replace not allowed chars in folder name : ""; // Empty string will be replaced by SPO with Folder Item ID - + const fileCreatedResult = await library.rootFolder.files.addChunked(encodeURI(itemTitle), await selectedFile.downloadFileContent()); const fields = await fileCreatedResult.file.listItemAllFields(); - + if (fields[idField]) { // Read the ID of the just created folder or Document Set const folderId = fields[idField]; - + // Set the content type ID for the target item objects[contentTypeIdField] = contentTypeId; // Update the just created folder or Document Set @@ -784,7 +784,7 @@ export class DynamicForm extends React.Component< fieldCollection: fieldCol, validationErrors }, () => { - if (validate) this.performValidation(); + if (validate) this.performValidation(); }); }; @@ -821,7 +821,7 @@ export class DynamicForm extends React.Component< * @param formulas A Record / dictionary-like object, where key is internal column name and value is an object with ValidationFormula and ValidationMessage properties * @param returnMessages Determines whether a Record of error messages is returned or an array of column names that have failed validation * @param requireValue Set to true if the formula should only be evaluated when the field has a value - * @returns + * @returns */ private evaluateFormulas = ( formulas: Record>, @@ -853,12 +853,12 @@ export class DynamicForm extends React.Component< } /** - * Used for validation. Returns a Record of field values, where key is internal column name and value is the field value. + * Used for validation. Returns a Record of field values, where key is internal column name and value is the field value. * Expands certain properties and stores many of them as primitives (strings, numbers or bools) so the expression evaluator * can process them. For example: a User column named Person will have values stored as Person, Person.email, Person.title etc. - * This is so the expression evaluator can process expressions like '=[$Person.title] == "Contoso Employee 1138"' + * This is so the expression evaluator can process expressions like '=[$Person.title] == "Contoso Employee 1138"' * @param fieldCollection Optional. Could be used to compare field values in state with previous state. - * @returns + * @returns */ private getFormValuesForValidation = (fieldCollection?: IDynamicFieldProps[]): Context => { const { fieldCollection: fieldColFromState } = this.state; @@ -909,10 +909,11 @@ export class DynamicForm extends React.Component< listItemId, disabledFields, respectETag, + customIcons, onListItemLoaded, } = this.props; let contentTypeId = this.props.contentTypeId; - + try { // Fetch form rendering information from SharePoint @@ -987,13 +988,14 @@ export class DynamicForm extends React.Component< numberFields, listId, listItemId, - disabledFields + disabledFields, + customIcons ); // Get installed languages for Currency fields let installedLanguages: IInstalledLanguageInfo[]; if (tempFields.filter(f => f.fieldType === "Currency").length > 0) { - installedLanguages = await sp.web.regionalSettings.getInstalledLanguages(); + installedLanguages = await sp.web.regionalSettings.getInstalledLanguages(); } this.setState({ @@ -1026,10 +1028,10 @@ export class DynamicForm extends React.Component< * @param listId SharePoint List ID * @param listItemId SharePoint List Item ID * @param disabledFields Fields that should be disabled due to configuration - * @returns + * @returns */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async buildFieldCollection(listInfo: IRenderListDataAsStreamClientFormResult, contentTypeName: string, item: any, numberFields: ISPField[], listId: string, listItemId: number, disabledFields: string[]): Promise { + private async buildFieldCollection(listInfo: IRenderListDataAsStreamClientFormResult, contentTypeName: string, item: any, numberFields: ISPField[], listId: string, listItemId: number, disabledFields: string[], customIcons: {[key: string]: string}): Promise { const tempFields: IDynamicFieldProps[] = []; let order: number = 0; const hiddenFields = this.props.hiddenFields !== undefined ? this.props.hiddenFields : []; @@ -1286,6 +1288,7 @@ export class DynamicForm extends React.Component< minimumValue: minValue, maximumValue: maxValue, showAsPercentage: showAsPercentage, + customIcon: customIcons ? customIcons[field.InternalName] : undefined }); // This may not be necessary now using RenderListDataAsStream diff --git a/src/controls/dynamicForm/IDynamicFormProps.ts b/src/controls/dynamicForm/IDynamicFormProps.ts index 43da7003e..218bce6da 100644 --- a/src/controls/dynamicForm/IDynamicFormProps.ts +++ b/src/controls/dynamicForm/IDynamicFormProps.ts @@ -51,7 +51,13 @@ export interface IDynamicFormProps { /** * Key value pair for fields you want to override. Key is the internal field name, value is the function to be called for the custom element to render */ - fieldOverrides?: {[columnInternalName: string] : {(fieldProperties: IDynamicFieldProps): React.ReactElement}}; + fieldOverrides?: { + [columnInternalName: string]: { + ( + fieldProperties: IDynamicFieldProps + ): React.ReactElement; + }; + }; /** * Specifies if onSubmitted event should pass PnPJS list item (IItem) as a second parameter. Default - true @@ -110,4 +116,10 @@ export interface IDynamicFormProps { * Only used when enableFileSelection is true */ supportedFileExtensions?: string[]; + + /** + * Specify a set of custom icons to be used. + * The key is the field internal name and the value is the Fluent UI icon name. + */ + customIcons?: { [columnInternalName: string]: string }; } diff --git a/src/controls/dynamicForm/dynamicField/DynamicField.tsx b/src/controls/dynamicForm/dynamicField/DynamicField.tsx index dc4deb6f4..7ee2e9786 100644 --- a/src/controls/dynamicForm/dynamicField/DynamicField.tsx +++ b/src/controls/dynamicForm/dynamicField/DynamicField.tsx @@ -79,7 +79,8 @@ export class DynamicField extends React.Component
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
{ this.onChange(newText); }} disabled={disabled} onBlur={this.onBlur} - errorMessage={errorText || customNumberErrorMessage} - min={minimumValue} + errorMessage={errorText || customNumberErrorMessage} + min={minimumValue} max={maximumValue} /> {descriptionEl} ; @@ -294,7 +295,7 @@ export class DynamicField extends React.Component
- + {labelEl}
{ this.onChange(newText); }} disabled={disabled} onBlur={this.onBlur} - errorMessage={errorText || customNumberErrorMessage} - min={minimumValue} + errorMessage={errorText || customNumberErrorMessage} + min={minimumValue} max={maximumValue} /> {descriptionEl} ; @@ -315,7 +316,7 @@ export class DynamicField extends React.Component
- + {labelEl}
{ @@ -348,7 +349,7 @@ export class DynamicField extends React.Component
- + {labelEl}
cv.secondaryText) : (value ? value : defaultValue); return
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
- + {labelEl}
@@ -516,7 +517,7 @@ export class DynamicField extends React.Component
- + {labelEl}
@@ -687,7 +688,7 @@ export class DynamicField extends React.Component void; // eslint-disable-line @typescript-eslint/no-explicit-any /** Represents the value of the field as updated by the user. Only updated by fields when changed. */ newValue?: any; // eslint-disable-line @typescript-eslint/no-explicit-any - + /** Represents a stringified value of the field. Used in custom formatting and validation. */ stringValue: any; // eslint-disable-line @typescript-eslint/no-explicit-any @@ -50,7 +50,7 @@ export interface IDynamicFieldProps { /** If validation raises an error message, it can be stored against the field here for display by DynamicField */ validationErrorMessage?: string; - + /** Field Term Set ID, used in Taxonomy / Metadata fields */ fieldTermSetId?: string; @@ -64,16 +64,16 @@ export interface IDynamicFieldProps { lookupField?: string; // changedValue: any; // eslint-disable-line @typescript-eslint/no-explicit-any - + /** Equivalent to HiddenListInternalName, used for Taxonomy Metadata fields */ hiddenFieldName?: string; - + /** Order of the field in the form */ Order: number; - + /** Used for files / image uploads */ additionalData?: FieldChangeAdditionalData; - + // Related to various field types options?: IDropdownOption[]; isRichText?: boolean; @@ -84,4 +84,5 @@ export interface IDynamicFieldProps { maximumValue?: number; minimumValue?: number; showAsPercentage?: boolean; + customIcon?: string; } diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index 603c9c5bd..7e7ac2138 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -818,6 +818,9 @@ export default class ControlsTest extends React.Component @@ -2589,5 +2593,5 @@ export default class ControlsTest extends React.Component { // console.log('selected folder', folder); - // + // } From 7697fc3dc86dba6a130b1709481a49003d88bb81 Mon Sep 17 00:00:00 2001 From: guidozam Date: Mon, 29 Jan 2024 16:57:41 +0100 Subject: [PATCH 04/46] Added orderBy to DynamicField props for lookup fields --- .../dynamicForm/dynamicField/DynamicField.tsx | 14 ++++--- .../dynamicField/IDynamicFieldProps.ts | 38 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/controls/dynamicForm/dynamicField/DynamicField.tsx b/src/controls/dynamicForm/dynamicField/DynamicField.tsx index dc4deb6f4..c8b009d96 100644 --- a/src/controls/dynamicForm/dynamicField/DynamicField.tsx +++ b/src/controls/dynamicForm/dynamicField/DynamicField.tsx @@ -79,7 +79,8 @@ export class DynamicField extends React.Component { this.onChange(newValue, true); }} context={context} + orderBy={orderBy} /> {descriptionEl} {errorTextEl} @@ -283,8 +285,8 @@ export class DynamicField extends React.Component { this.onChange(newText); }} disabled={disabled} onBlur={this.onBlur} - errorMessage={errorText || customNumberErrorMessage} - min={minimumValue} + errorMessage={errorText || customNumberErrorMessage} + min={minimumValue} max={maximumValue} /> {descriptionEl}
; @@ -306,8 +308,8 @@ export class DynamicField extends React.Component { this.onChange(newText); }} disabled={disabled} onBlur={this.onBlur} - errorMessage={errorText || customNumberErrorMessage} - min={minimumValue} + errorMessage={errorText || customNumberErrorMessage} + min={minimumValue} max={maximumValue} /> {descriptionEl}
; @@ -687,7 +689,7 @@ export class DynamicField extends React.Component void; // eslint-disable-line @typescript-eslint/no-explicit-any + onChanged?: ( + columnInternalName: string, + newValue: any, + validate: boolean, + additionalData?: FieldChangeAdditionalData + ) => void; // eslint-disable-line @typescript-eslint/no-explicit-any /** Represents the value of the field as updated by the user. Only updated by fields when changed. */ newValue?: any; // eslint-disable-line @typescript-eslint/no-explicit-any - + /** Represents a stringified value of the field. Used in custom formatting and validation. */ stringValue: any; // eslint-disable-line @typescript-eslint/no-explicit-any @@ -50,7 +55,7 @@ export interface IDynamicFieldProps { /** If validation raises an error message, it can be stored against the field here for display by DynamicField */ validationErrorMessage?: string; - + /** Field Term Set ID, used in Taxonomy / Metadata fields */ fieldTermSetId?: string; @@ -64,16 +69,16 @@ export interface IDynamicFieldProps { lookupField?: string; // changedValue: any; // eslint-disable-line @typescript-eslint/no-explicit-any - + /** Equivalent to HiddenListInternalName, used for Taxonomy Metadata fields */ hiddenFieldName?: string; - + /** Order of the field in the form */ Order: number; - + /** Used for files / image uploads */ additionalData?: FieldChangeAdditionalData; - + // Related to various field types options?: IDropdownOption[]; isRichText?: boolean; @@ -84,4 +89,5 @@ export interface IDynamicFieldProps { maximumValue?: number; minimumValue?: number; showAsPercentage?: boolean; + orderBy?: string; } From bfd935e1e7b40e199bb36fa6a8d75eb1bca0afe9 Mon Sep 17 00:00:00 2001 From: Nishkalank Date: Tue, 30 Jan 2024 20:20:50 +0100 Subject: [PATCH 05/46] FIxing issue 1738 --- src/controls/dynamicForm/DynamicForm.tsx | 43 +++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/controls/dynamicForm/DynamicForm.tsx b/src/controls/dynamicForm/DynamicForm.tsx index b4cffa172..48ead2275 100644 --- a/src/controls/dynamicForm/DynamicForm.tsx +++ b/src/controls/dynamicForm/DynamicForm.tsx @@ -412,29 +412,33 @@ export class DynamicForm extends React.Component< columnInternalName, hiddenFieldName, } = field; + let fieldcolumnInternalName = columnInternalName; + if (fieldcolumnInternalName.startsWith('_x') || fieldcolumnInternalName.startsWith('_')) { + fieldcolumnInternalName = `OData_${fieldcolumnInternalName}`; + } if (field.newValue !== null && field.newValue !== undefined) { let value = field.newValue; if (["Lookup", "LookupMulti", "User", "UserMulti"].indexOf(fieldType) < 0) { - objects[columnInternalName] = value; + objects[fieldcolumnInternalName] = value; } // Choice fields if (fieldType === "Choice") { - objects[columnInternalName] = field.newValue.key; + objects[fieldcolumnInternalName] = field.newValue.key; } if (fieldType === "MultiChoice") { - objects[columnInternalName] = { results: field.newValue }; + objects[fieldcolumnInternalName] = { results: field.newValue }; } // Lookup fields if (fieldType === "Lookup") { if (value && value.length > 0) { - objects[`${columnInternalName}Id`] = value[0].key; + objects[`${fieldcolumnInternalName}Id`] = value[0].key; } else { - objects[`${columnInternalName}Id`] = null; + objects[`${fieldcolumnInternalName}Id`] = null; } } if (fieldType === "LookupMulti") { @@ -442,7 +446,7 @@ export class DynamicForm extends React.Component< field.newValue.forEach((element) => { value.push(element.key); }); - objects[`${columnInternalName}Id`] = { + objects[`${fieldcolumnInternalName}Id`] = { results: value.length === 0 ? null : value, }; } @@ -450,10 +454,10 @@ export class DynamicForm extends React.Component< // User fields if (fieldType === "User") { - objects[`${columnInternalName}Id`] = field.newValue.length === 0 ? null : field.newValue; + objects[`${fieldcolumnInternalName}Id`] = field.newValue.length === 0 ? null : field.newValue; } if (fieldType === "UserMulti") { - objects[`${columnInternalName}Id`] = { + objects[`${fieldcolumnInternalName}Id`] = { results: field.newValue.length === 0 ? null : field.newValue, }; } @@ -461,7 +465,7 @@ export class DynamicForm extends React.Component< // Taxonomy / Managed Metadata fields if (fieldType === "TaxonomyFieldType") { - objects[columnInternalName] = { + objects[fieldcolumnInternalName] = { __metadata: { type: "SP.Taxonomy.TaxonomyFieldValue" }, Label: value[0]?.name ?? "", TermGuid: value[0]?.key ?? "11111111-1111-1111-1111-111111111111", @@ -477,19 +481,19 @@ export class DynamicForm extends React.Component< // Other fields if (fieldType === "Location") { - objects[columnInternalName] = JSON.stringify(field.newValue); + objects[fieldcolumnInternalName] = JSON.stringify(field.newValue); } if (fieldType === "Thumbnail") { if (additionalData) { const uploadedImage = await this.uploadImage(additionalData); - objects[columnInternalName] = JSON.stringify({ + objects[fieldcolumnInternalName] = JSON.stringify({ type: "thumbnail", fileName: uploadedImage.Name, serverRelativeUrl: uploadedImage.ServerRelativeUrl, id: uploadedImage.UniqueId, }); } else { - objects[columnInternalName] = null; + objects[fieldcolumnInternalName] = null; } } } @@ -1061,9 +1065,14 @@ export class DynamicForm extends React.Component< // eslint-disable-next-line @typescript-eslint/no-explicit-any const selectedTags: any = []; + let fieldName = field.InternalName; + if (fieldName.startsWith('_x') || fieldName.startsWith('_')) { + fieldName = `OData_${fieldName}`; + } + // If a SharePoint Item was loaded, get the field value from it - if (item !== null && item[field.InternalName]) { - value = item[field.InternalName]; + if (item !== null && item[fieldName]) { + value = item[fieldName]; stringValue = value.toString(); } else { defaultValue = field.DefaultValue; @@ -1220,8 +1229,10 @@ export class DynamicForm extends React.Component< // Setup DateTime fields if (field.FieldType === "DateTime") { - if (item !== null && item[field.InternalName]) { - value = new Date(item[field.InternalName]); + + if (item !== null && item[fieldName]) { + + value = new Date(item[fieldName]); stringValue = value.toISOString(); } else if (defaultValue === "[today]") { defaultValue = new Date(); From 9277bd2249845c27e64f3cb77d91d4afcdfd455c Mon Sep 17 00:00:00 2001 From: matteo <> Date: Wed, 31 Jan 2024 00:08:22 +0100 Subject: [PATCH 06/46] calendar restricted dates --- .../dateTimePicker/DateTimePicker.tsx | 24 ++++++++++++++++++- .../dateTimePicker/IDateTimePickerProps.ts | 6 +++++ .../controlsTest/components/ControlsTest.tsx | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/controls/dateTimePicker/DateTimePicker.tsx b/src/controls/dateTimePicker/DateTimePicker.tsx index 247a228f0..033e3e4e3 100644 --- a/src/controls/dateTimePicker/DateTimePicker.tsx +++ b/src/controls/dateTimePicker/DateTimePicker.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import { isEqual } from '@microsoft/sp-lodash-subset'; import { TimeConvention, DateConvention } from "./DateTimeConventions"; +import { Calendar } from "@fluentui/react"; import { DatePicker } from "@fluentui/react/lib/DatePicker"; import { Label } from "@fluentui/react/lib/Label"; import { IconButton } from "@fluentui/react/lib/Button"; @@ -246,7 +247,8 @@ export class DateTimePicker extends React.Component { + return ( + + ); + }; + // Check if the time element needs to be rendered let timeElm: JSX.Element =
; @@ -341,6 +362,7 @@ export class DateTimePicker extends React.Component this.handleTextChange(e, newValue, textErrorMessage) }} diff --git a/src/controls/dateTimePicker/IDateTimePickerProps.ts b/src/controls/dateTimePicker/IDateTimePickerProps.ts index 1cd15fd7e..67b73557c 100644 --- a/src/controls/dateTimePicker/IDateTimePickerProps.ts +++ b/src/controls/dateTimePicker/IDateTimePickerProps.ts @@ -153,4 +153,10 @@ export interface IDateTimePickerProps { * Icon used for clearDate iconbutton. Defaults to RemoveEvent */ showClearDateIcon?: string; + + /** + * If set the Calendar will not allow selection of dates in this array. + */ + restrictedDates?: Date[]; + } diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index 1c726f203..ac0146eca 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -1449,6 +1449,8 @@ export default class ControlsTest extends React.Component + + console.log("DateTimePicker value:", value)} placeholder="Pick a date" restrictedDates={[new Date(2024,1,15), new Date(2024,1,16), new Date(2024,1,17)]}/>