Skip to content

Commit 4e77195

Browse files
author
Devang Bhavsar
committed
Fix for #728: Show an error message for an invalid/unresolved input inside taxonomy picker control (v1-release)
1 parent 6fa9cb9 commit 4e77195

File tree

6 files changed

+77
-7
lines changed

6 files changed

+77
-7
lines changed

src/controls/taxonomyPicker/ITaxonomyPicker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export interface ITaxonomyPickerProps {
8484
*/
8585
validateOnLoad?: boolean;
8686

87+
/**
88+
* Specifies if the input text will be validated, when the component focus is changed
89+
*/
90+
validateInput?: boolean;
91+
8792
/**
8893
* The method is used to get the validation error message and determine whether the input value is valid or not.
8994
* Mutually exclusive with the static string errorMessage (it will take precedence over this).

src/controls/taxonomyPicker/TaxonomyPicker.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import { PrimaryButton, DefaultButton, IconButton } from 'office-ui-fabric-react/lib/Button';
33
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
4+
import { Autofill } from 'office-ui-fabric-react/lib/components/Autofill/Autofill';
45
import { Spinner, SpinnerType } from 'office-ui-fabric-react/lib/Spinner';
56
import { Label } from 'office-ui-fabric-react/lib/Label';
67
import TermPicker from './TermPicker';
@@ -32,6 +33,7 @@ export const TERM_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQC
3233
export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxonomyPickerState> {
3334
private termsService: SPTermStorePickerService;
3435
private previousValues: IPickerTerms = [];
36+
private invalidTerm: string = null;
3537
private cancel: boolean = true;
3638

3739
/**
@@ -55,6 +57,9 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
5557
this.onSave = this.onSave.bind(this);
5658
this.termsChanged = this.termsChanged.bind(this);
5759
this.termsFromPickerChanged = this.termsFromPickerChanged.bind(this);
60+
this.onInputChange = this.onInputChange.bind(this);
61+
this.onBlur = this.onBlur.bind(this);
62+
5863
this.termsService = new SPTermStorePickerService(this.props, this.props.context);
5964
}
6065

@@ -101,7 +106,6 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
101106
* it checks, if all entries still exist in term store. if allowMultipleSelections is true. it have to validate all values
102107
*/
103108
private async validateTerms(): Promise<void> {
104-
105109
const {
106110
hideDeprecatedTags,
107111
hideTagsNotAvailableForTagging,
@@ -141,8 +145,6 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
141145
* Loads the list from SharePoint current web site
142146
*/
143147
private loadTermStores(): void {
144-
145-
146148
if (this.props.termActions && this.props.termActions.initialize) {
147149
this.props.termActions.initialize(this.termsService);
148150
// this.props.termActions.actions.forEach(x => {
@@ -276,7 +278,59 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
276278
this.validate(terms);
277279
}
278280

281+
/**
282+
* Shows an error message for any invalid input inside taxonomy picker control
283+
*/
284+
private validateInputText(): void {
285+
// Show error message, if any unresolved value exists inside taxonomy picker control
286+
if (!!this.invalidTerm) {
287+
// An unresolved value exists
288+
this.setState({
289+
errorMessage: strings.TaxonomyPickerInvalidTerms.replace('{0}', this.invalidTerm)
290+
});
291+
}
292+
else {
293+
// There are no unresolved values
294+
this.setState({
295+
errorMessage: null
296+
});
297+
}
298+
}
299+
300+
/**
301+
* Triggers when input of taxonomy picker control changes
302+
*/
303+
private onInputChange(input: string): string {
304+
if (!input) {
305+
const { validateInput } = this.props;
306+
if (!!validateInput) {
307+
// Perform validation of input text, only if taxonomy picker is configured with validateInput={true} property.
308+
this.invalidTerm = null;
309+
this.validateInputText();
310+
}
311+
}
312+
return input;
313+
}
279314

315+
/**
316+
* Triggers when taxonomy picker control loses focus
317+
*/
318+
private onBlur(event: React.FocusEvent<HTMLElement | Autofill>): void {
319+
const { validateInput } = this.props;
320+
if (!!validateInput) {
321+
// Perform validation of input text, only if taxonomy picker is configured with validateInput={true} property.
322+
const target: HTMLInputElement = event.target as HTMLInputElement;
323+
const targetValue = !!target ? target.value : null;
324+
if (!!targetValue) {
325+
this.invalidTerm = targetValue;
326+
}
327+
else {
328+
this.invalidTerm = null;
329+
}
330+
this.validateInputText();
331+
}
332+
}
333+
280334
/**
281335
* Gets the given node position in the active nodes collection
282336
* @param node
@@ -318,7 +372,6 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
318372
}
319373

320374
private validate = async (value: IPickerTerms): Promise<void> => {
321-
322375
//
323376
// checking if there are any invalid nodes left after initial validation
324377
//
@@ -390,7 +443,8 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
390443
disabled,
391444
isTermSetSelectable,
392445
allowMultipleSelections,
393-
disabledTermIds, disableChildrenOfDisabledParents,
446+
disabledTermIds,
447+
disableChildrenOfDisabledParents,
394448
placeholder,
395449
panelTitle,
396450
anchorId,
@@ -419,6 +473,8 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
419473
value={activeNodes}
420474
isTermSetSelectable={isTermSetSelectable}
421475
onChanged={this.termsFromPickerChanged}
476+
onInputChange={this.onInputChange}
477+
onBlur={this.onBlur}
422478
allowMultipleSelections={allowMultipleSelections}
423479
disabledTermIds={disabledTermIds}
424480
disableChildrenOfDisabledParents={disableChildrenOfDisabledParents}

src/controls/taxonomyPicker/TermPicker.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Icon } from 'office-ui-fabric-react/lib/Icon';
1010
import { ExtensionContext } from '@microsoft/sp-extension-base';
1111
import { ITermSet } from "../../services/ISPTermStorePickerService";
1212
import { EmptyGuid } from '../../Common';
13+
import { Autofill } from 'office-ui-fabric-react/lib/components/Autofill/Autofill';
1314

1415
export class TermBasePicker extends BasePicker<IPickerTerm, IBasePickerProps<IPickerTerm>>
1516
{
@@ -32,6 +33,8 @@ export interface ITermPickerProps {
3233
placeholder?: string;
3334

3435
onChanged: (items: IPickerTerm[]) => void;
36+
onInputChange: (input: string) => string;
37+
onBlur: (ev: React.FocusEvent<HTMLElement | Autofill>) => void;
3538
}
3639

3740
export default class TermPicker extends React.Component<ITermPickerProps, ITermPickerState> {
@@ -183,22 +186,23 @@ export default class TermPicker extends React.Component<ITermPickerProps, ITermP
183186
}
184187
}
185188

186-
187189
/**
188190
* gets the text from an item
189191
*/
190192
private onGetTextFromItem(item: any): any {
191193
return item.name;
192194
}
193195

194-
/**
196+
/**
195197
* Render method
196198
*/
197199
public render(): JSX.Element {
198200
const {
199201
disabled,
200202
value,
201203
onChanged,
204+
onInputChange,
205+
onBlur,
202206
allowMultipleSelections,
203207
placeholder
204208
} = this.props;
@@ -218,6 +222,8 @@ export default class TermPicker extends React.Component<ITermPickerProps, ITermP
218222
defaultSelectedItems={value}
219223
selectedItems={terms}
220224
onChange={onChanged}
225+
onInputChange={onInputChange}
226+
onBlur={onBlur}
221227
itemLimit={!allowMultipleSelections ? 1 : undefined}
222228
className={styles.termBasePicker}
223229
inputProps={{

src/loc/en-us.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ define([], () => {
4646
"TaxonomyPickerInLabel": "in",
4747
"TaxonomyPickerTermSetLabel": "Term Set",
4848
"TaxonomyPickerTermsNotFound": "The next selected term(s) could not be found in the term store: {0}",
49+
"TaxonomyPickerInvalidTerms": "Please fix invalid term(s): {0}",
4950

5051
peoplePickerComponentTooltipMessage: "People Picker",
5152
peoplePickerComponentErrorMessage: "Required Field",

src/loc/mystrings.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ declare interface IControlStrings {
2727
TaxonomyPickerInLabel: string;
2828
TaxonomyPickerTermSetLabel: string;
2929
TaxonomyPickerTermsNotFound: string;
30+
TaxonomyPickerInvalidTerms: string;
3031

3132
ListItemPickerSelectValue: string;
3233

src/webparts/controlsTest/components/ControlsTest.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
720720
onChange={this.onServicePickerChange}
721721
isTermSetSelectable={false}
722722
placeholder="Select service"
723+
// validateInput={true} /* Uncomment this to enable validation of input text */
723724
required={true}
724725
errorMessage='this field is required'
725726
onGetErrorMessage={(value) => { return 'comment errorMessage to see this one'; }}

0 commit comments

Comments
 (0)