@@ -15,8 +15,9 @@ import { sortBy, cloneDeep, isEqual } from '@microsoft/sp-lodash-subset';
15
15
import uniqBy = require( 'lodash/uniqBy' ) ;
16
16
import TermParent from './TermParent' ;
17
17
import FieldErrorMessage from './ErrorMessage' ;
18
-
18
+ import { initializeIcons } from '@uifabric/icons' ;
19
19
import * as telemetry from '../../common/telemetry' ;
20
+ import { EmptyGuid } from '../../common/Constants' ;
20
21
21
22
/**
22
23
* Image URLs / Base64
@@ -27,6 +28,8 @@ export const GROUP_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ
27
28
export const TERMSET_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACaSURBVDhPrZLRCcAgDERdpZMIjuQA7uWH4CqdxMY0EQtNjKWB0A/77sxF55SKMTalk8a61lqCFqsLiwKac84ZRUUBi7MoYHVmAfjfjzE6vJqZQfie0AcwBQVW8ATi7AR7zGGGNSE6Q2cyLSPIjRswjO7qKhcPDN2hK46w05wZMcEUIG+HrzzcrRsQBIJ5hS8C9fGAPmRwu/9RFxW6L8CM4Ry8AAAAAElFTkSuQmCC' ; // /_layouts/15/Images/EMMTermSet.png
28
29
export const TERM_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACzSURBVDhPY2AYNKCoqIgTiOcD8X8S8F6wB4Aa1IH4akNDw+mPHz++/E8EuHTp0jmQRSDNCcXFxa/XrVt3gAh9KEpgBvx/9OjRLVI1g9TDDYBp3rlz5//Kysr/IJoYgGEASPPatWsbQDQxAMOAbdu2gZ0FookBcAOePHlyhxgN6GqQY+Hdhg0bDpJqCNgAaDrQAnJuNDY2nvr06dMbYgw6e/bsabgBUEN4yEiJ2wdNViLfIQC3sTh2vtJcswAAAABJRU5ErkJggg==' ;
29
30
31
+ initializeIcons ( ) ;
32
+
30
33
/**
31
34
* Renders the controls for PropertyFieldTermPicker component
32
35
*/
@@ -89,7 +92,7 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
89
92
} ;
90
93
}
91
94
92
- if ( nextProps . errorMessage ) {
95
+ if ( nextProps . errorMessage !== this . props . errorMessage ) {
93
96
if ( ! newState ) {
94
97
newState = { } ;
95
98
}
@@ -230,11 +233,20 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
230
233
*/
231
234
private termsChanged ( term : ITerm , checked : boolean ) : void {
232
235
233
- let activeNodes = this . state . activeNodes ;
236
+ let activeNodes = this . state . activeNodes . slice ( ) ;
234
237
if ( typeof term === 'undefined' || term === null ) {
235
238
return ;
236
239
}
237
240
241
+ const {
242
+ allowMultipleSelections,
243
+ selectChildrenIfParentSelected
244
+ } = this . props ;
245
+
246
+ const {
247
+ termSetAndTerms
248
+ } = this . state ;
249
+
238
250
// Term item to add to the active nodes array
239
251
const termItem = {
240
252
name : term . Name ,
@@ -243,24 +255,58 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
243
255
termSet : term . TermSet . Id
244
256
} ;
245
257
258
+ // Check if we need to process child terms
259
+ let children : ITerm [ ] = [ ] ;
260
+ if ( allowMultipleSelections && selectChildrenIfParentSelected ) {
261
+ if ( term . Id === term . TermSet . Id ) {
262
+ children = termSetAndTerms . Terms || [ ] ;
263
+ } else {
264
+ children = termSetAndTerms . Terms ? termSetAndTerms . Terms . filter ( t => {
265
+ return t . PathOfTerm . indexOf ( `${ term . PathOfTerm } ` ) !== - 1 ;
266
+ } ) : [ ] ;
267
+ }
268
+ }
269
+
246
270
// Check if the term is checked or unchecked
247
271
if ( checked ) {
248
272
// Check if it is allowed to select multiple terms
249
- if ( this . props . allowMultipleSelections ) {
273
+ if ( allowMultipleSelections ) {
250
274
// Add the checked term
251
275
activeNodes . push ( termItem ) ;
252
- // Filter out the duplicate terms
253
- activeNodes = uniqBy ( activeNodes , 'key' ) ;
254
276
} else {
255
277
// Only store the current selected item
256
278
activeNodes = [ termItem ] ;
257
279
}
280
+
281
+ if ( children . length ) {
282
+ activeNodes . push ( ...children . map ( c => {
283
+ return {
284
+ name : c . Name ,
285
+ key : c . Id ,
286
+ path : c . PathOfTerm ,
287
+ termSet : c . TermSet . Id
288
+ } ;
289
+ } ) ) ;
290
+ }
291
+
292
+ // Filter out the duplicate terms
293
+ activeNodes = uniqBy ( activeNodes , 'key' ) ;
258
294
} else {
259
295
// Remove the term from the list of active nodes
260
296
activeNodes = activeNodes . filter ( item => item . key !== term . Id ) ;
297
+
298
+ if ( children . length ) {
299
+ const childIds = children . map ( c => c . Id ) ;
300
+ activeNodes = activeNodes . filter ( item => childIds . indexOf ( item . key ) === - 1 ) ;
301
+ }
261
302
}
262
303
// Sort all active nodes
263
304
activeNodes = sortBy ( activeNodes , 'path' ) ;
305
+
306
+ if ( this . props . onPanelSelectionChange ) {
307
+ this . props . onPanelSelectionChange ( this . state . activeNodes . slice ( ) , activeNodes ) ;
308
+ }
309
+
264
310
// Update the current state
265
311
this . setState ( {
266
312
activeNodes : activeNodes
@@ -313,22 +359,51 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
313
359
return input ;
314
360
}
315
361
362
+ private async validateOnGetErrorMessage ( targetValue : string ) : Promise < boolean > {
363
+ const errorMessage = await this . props . onGetErrorMessage (
364
+ [
365
+ {
366
+ key : EmptyGuid ,
367
+ name : targetValue ,
368
+ path : targetValue ,
369
+ termSet : this . termsService . cleanGuid ( this . props . termsetNameOrID )
370
+ }
371
+ ]
372
+ ) ;
373
+
374
+ if ( ! ! errorMessage ) {
375
+ this . setState ( {
376
+ errorMessage : errorMessage
377
+ } ) ;
378
+ } else {
379
+ this . setState ( {
380
+ errorMessage : null
381
+ } ) ;
382
+ }
383
+
384
+ return ! errorMessage ;
385
+ }
386
+
316
387
/**
317
388
* Triggers when taxonomy picker control loses focus
318
389
*/
319
- private onBlur ( event : React . FocusEvent < HTMLElement | Autofill > ) : void {
390
+ private async onBlur ( event : React . FocusEvent < HTMLElement | Autofill > ) : Promise < void > {
320
391
const { validateInput } = this . props ;
321
392
if ( ! ! validateInput ) {
322
393
// Perform validation of input text, only if taxonomy picker is configured with validateInput={true} property.
323
394
const target : HTMLInputElement = event . target as HTMLInputElement ;
324
395
const targetValue = ! ! target ? target . value : null ;
325
- if ( ! ! targetValue ) {
326
- this . invalidTerm = targetValue ;
327
- }
328
- else {
329
- this . invalidTerm = null ;
396
+ if ( ! ! this . props . onGetErrorMessage && ! ! targetValue ) {
397
+ await this . validateOnGetErrorMessage ( targetValue ) ;
398
+ } else {
399
+ if ( ! ! targetValue ) {
400
+ this . invalidTerm = targetValue ;
401
+ }
402
+ else {
403
+ this . invalidTerm = null ;
404
+ }
405
+ this . validateInputText ( ) ;
330
406
}
331
- this . validateInputText ( ) ;
332
407
}
333
408
}
334
409
@@ -404,6 +479,9 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
404
479
if ( typeof result === 'string' ) {
405
480
if ( ! result ) {
406
481
this . validated ( value ) ;
482
+ this . setState ( {
483
+ errorMessage : undefined
484
+ } ) ;
407
485
}
408
486
else {
409
487
this . setState ( {
@@ -417,6 +495,9 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
417
495
418
496
if ( ! resolvedResult ) {
419
497
this . validated ( value ) ;
498
+ this . setState ( {
499
+ errorMessage : undefined
500
+ } ) ;
420
501
}
421
502
else {
422
503
this . setState ( {
0 commit comments