diff --git a/docs/documentation/docs/controls/TaxonomyPicker.md b/docs/documentation/docs/controls/TaxonomyPicker.md index 369cb25b3..400c1ff3f 100644 --- a/docs/documentation/docs/controls/TaxonomyPicker.md +++ b/docs/documentation/docs/controls/TaxonomyPicker.md @@ -51,6 +51,21 @@ private onTaxPickerChange(terms : IPickerTerms) { } ``` +- With the `onNewTerm` property you can capture the event, when text is in the input field and +enter/return is pressed. With a controlled TaxonomyPicker, this enables you to create the term +and have the same flow as in SharePoint Keywords fields. + +```typescript + const onNewTerm = (value: IPickerTerm): void => { + if(value?.name && EmptyGuid === value.key ){ + console.log(`TaxonmyPicker.onNewTerm name=${value.name}`, value ); + // Create keyword + } else { + console.error(`TaxonmyPicker.onNewTerm name=${value?.name}`, value ); + } + }; +``` + ## Term actions Since version `1.12.0`, you can apply term actions to all terms or specific ones. Term actions could for instance be used to retrieve the labels of the term, or retrieve other information. These term actions can be implemented as follows: @@ -158,6 +173,7 @@ The TaxonomyPicker control can be configured with the following properties: | allowMultipleSelections | boolean | no | Defines if the user can select only one or many term sets. Default value is false. | | termsetNameOrID | string | yes | The name or Id of your TermSet that you would like the Taxonomy Picker to chose terms from. | | onChange | function | no | captures the event of when the terms in the picker has changed. | +| onNewTerm | function | no | captures the event when text is in the input field and the user presses return | | isTermSetSelectable | boolean | no | Specify if the TermSet itself is selectable in the tree view. | | disabledTermIds | string[] | no | Specify which terms should be disabled in the term set so that they cannot be selected. | | disableChildrenOfDisabledParents | boolean | no | Specify if you want to disable the child terms when their parent is disabled. | diff --git a/src/controls/taxonomyPicker/ITaxonomyPicker.ts b/src/controls/taxonomyPicker/ITaxonomyPicker.ts index fe1da4b46..0f99bfb46 100644 --- a/src/controls/taxonomyPicker/ITaxonomyPicker.ts +++ b/src/controls/taxonomyPicker/ITaxonomyPicker.ts @@ -1,4 +1,4 @@ -import { IPickerTerms } from './ITermPicker'; +import { IPickerTerm, IPickerTerms } from './ITermPicker'; import { ITermSet, ITerm } from '../../services/ISPTermStorePickerService'; import { ITermActions } from './termActions/ITermsActions'; import SPTermStorePickerService from '../../services/SPTermStorePickerService'; @@ -104,6 +104,11 @@ export interface ITaxonomyPickerProps { */ onGetErrorMessage?: (value: IPickerTerms) => string | Promise; + /** + * Called when text is in the input field and the enter key is pressed. + */ + onNewTerm?: (value: IPickerTerm) => void; + /** * Static error message displayed below the text field. Use onGetErrorMessage to dynamically change the error message displayed (if any) based on the current value. errorMessage and onGetErrorMessage are mutually exclusive (errorMessage takes precedence). */ diff --git a/src/controls/taxonomyPicker/TaxonomyPicker.tsx b/src/controls/taxonomyPicker/TaxonomyPicker.tsx index c179271fc..cc34535a2 100644 --- a/src/controls/taxonomyPicker/TaxonomyPicker.tsx +++ b/src/controls/taxonomyPicker/TaxonomyPicker.tsx @@ -17,6 +17,7 @@ import TermParent from './TermParent'; import FieldErrorMessage from '../errorMessage/ErrorMessage'; import { initializeIcons } from '@uifabric/icons'; import * as telemetry from '../../common/telemetry'; +import { EmptyGuid } from '../../common/Constants'; /** * Image URLs / Base64 @@ -363,22 +364,63 @@ export class TaxonomyPicker extends React.Component { + const errorMessage = await this.props.onGetErrorMessage( + [ + { + key: EmptyGuid, + name: targetValue, + path: targetValue, + termSet: this.termsService.cleanGuid(this.props.termsetNameOrID) + } + ] + ); + + if (!!errorMessage) { + this.setState({ + errorMessage: errorMessage + }); + } + else { + this.setState({ + errorMessage: null + }); + } + return !errorMessage; + } + + private onNewTerm = (newLabel: string) => { + this.props.onNewTerm( + { + key: EmptyGuid, + name: newLabel, + path: newLabel, + termSet: this.termsService.cleanGuid(this.props.termsetNameOrID) + } + ); + } + /** * Triggers when taxonomy picker control loses focus */ - private onBlur(event: React.FocusEvent): void { + private async onBlur(event: React.FocusEvent): Promise { const { validateInput } = this.props; if (!!validateInput) { // Perform validation of input text, only if taxonomy picker is configured with validateInput={true} property. const target: HTMLInputElement = event.target as HTMLInputElement; const targetValue = !!target ? target.value : null; - if (!!targetValue) { - this.invalidTerm = targetValue; - } - else { - this.invalidTerm = null; + + if(!!this.props.onGetErrorMessage && !!targetValue) { + await this.validateOnGetErrorMessage(targetValue); + } else { + if (!!targetValue) { + this.invalidTerm = targetValue; + } + else { + this.invalidTerm = null; + } + this.validateInputText(); } - this.validateInputText(); } } @@ -534,6 +576,7 @@ export class TaxonomyPicker extends React.Component> { @@ -17,6 +18,7 @@ export class TermBasePicker extends BasePicker & LegacyRef; } export interface ITermPickerProps { @@ -30,6 +32,8 @@ export interface ITermPickerProps { disableChildrenOfDisabledParents?: boolean; placeholder?: string; + /** Called when text is in the input field and the enter key is pressed. */ + onNewTerm?: (newLabel: string) => void; onChanged: (items: IPickerTerm[]) => void; onInputChange: (input: string) => string; onBlur: (ev: React.FocusEvent) => void; @@ -201,6 +205,7 @@ export default class TermPicker extends React.Component { + const picker = this.state.elRef as unknown as TermBasePicker; + const autoFill = picker?.['input']?.current as Autofill; + if (autoFill) { + autoFill['_value'] = ''; + autoFill.setState({ displayValue: '' }); + } else { + throw new Error(`TermPicker.TermBasePicker.render.clearDisplayValue no autoFill to reset displayValue`); + } + }; + + const inputProps: IInputProps = { placeholder: placeholder }; + + if(onNewTerm) { + inputProps.onKeyDown = (e: KeyboardEvent) => { + if (e && e.key === 'Enter' && (! (e.ctrlKey || e.altKey || e.shiftKey)) && e.target?.['value'] ) { + onNewTerm(e.target['value']); + clearDisplayValue(); + } + }; + } + return (
{ + if (!this.state.elRef) { + this.setState({ elRef }); + } + }} disabled={disabled} onResolveSuggestions={this.onFilterChanged} onRenderSuggestionsItem={this.onRenderSuggestionsItem} @@ -224,9 +256,7 @@ export default class TermPicker extends React.Component
);