@@ -15,8 +15,9 @@ import { sortBy, cloneDeep, isEqual } from '@microsoft/sp-lodash-subset';
1515import uniqBy = require( 'lodash/uniqBy' ) ;
1616import TermParent from './TermParent' ;
1717import FieldErrorMessage from './ErrorMessage' ;
18-
18+ import { initializeIcons } from '@uifabric/icons' ;
1919import * as telemetry from '../../common/telemetry' ;
20+ import { EmptyGuid } from '../../common/Constants' ;
2021
2122/**
2223 * Image URLs / Base64
@@ -27,6 +28,8 @@ export const GROUP_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ
2728export const TERMSET_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACaSURBVDhPrZLRCcAgDERdpZMIjuQA7uWH4CqdxMY0EQtNjKWB0A/77sxF55SKMTalk8a61lqCFqsLiwKac84ZRUUBi7MoYHVmAfjfjzE6vJqZQfie0AcwBQVW8ATi7AR7zGGGNSE6Q2cyLSPIjRswjO7qKhcPDN2hK46w05wZMcEUIG+HrzzcrRsQBIJ5hS8C9fGAPmRwu/9RFxW6L8CM4Ry8AAAAAElFTkSuQmCC' ; // /_layouts/15/Images/EMMTermSet.png
2829export const TERM_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACzSURBVDhPY2AYNKCoqIgTiOcD8X8S8F6wB4Aa1IH4akNDw+mPHz++/E8EuHTp0jmQRSDNCcXFxa/XrVt3gAh9KEpgBvx/9OjRLVI1g9TDDYBp3rlz5//Kysr/IJoYgGEASPPatWsbQDQxAMOAbdu2gZ0FookBcAOePHlyhxgN6GqQY+Hdhg0bDpJqCNgAaDrQAnJuNDY2nvr06dMbYgw6e/bsabgBUEN4yEiJ2wdNViLfIQC3sTh2vtJcswAAAABJRU5ErkJggg==' ;
2930
31+ initializeIcons ( ) ;
32+
3033/**
3134 * Renders the controls for PropertyFieldTermPicker component
3235 */
@@ -89,7 +92,7 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
8992 } ;
9093 }
9194
92- if ( nextProps . errorMessage ) {
95+ if ( nextProps . errorMessage !== this . props . errorMessage ) {
9396 if ( ! newState ) {
9497 newState = { } ;
9598 }
@@ -230,11 +233,20 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
230233 */
231234 private termsChanged ( term : ITerm , checked : boolean ) : void {
232235
233- let activeNodes = this . state . activeNodes ;
236+ let activeNodes = this . state . activeNodes . slice ( ) ;
234237 if ( typeof term === 'undefined' || term === null ) {
235238 return ;
236239 }
237240
241+ const {
242+ allowMultipleSelections,
243+ selectChildrenIfParentSelected
244+ } = this . props ;
245+
246+ const {
247+ termSetAndTerms
248+ } = this . state ;
249+
238250 // Term item to add to the active nodes array
239251 const termItem = {
240252 name : term . Name ,
@@ -243,24 +255,58 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
243255 termSet : term . TermSet . Id
244256 } ;
245257
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+
246270 // Check if the term is checked or unchecked
247271 if ( checked ) {
248272 // Check if it is allowed to select multiple terms
249- if ( this . props . allowMultipleSelections ) {
273+ if ( allowMultipleSelections ) {
250274 // Add the checked term
251275 activeNodes . push ( termItem ) ;
252- // Filter out the duplicate terms
253- activeNodes = uniqBy ( activeNodes , 'key' ) ;
254276 } else {
255277 // Only store the current selected item
256278 activeNodes = [ termItem ] ;
257279 }
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' ) ;
258294 } else {
259295 // Remove the term from the list of active nodes
260296 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+ }
261302 }
262303 // Sort all active nodes
263304 activeNodes = sortBy ( activeNodes , 'path' ) ;
305+
306+ if ( this . props . onPanelSelectionChange ) {
307+ this . props . onPanelSelectionChange ( this . state . activeNodes . slice ( ) , activeNodes ) ;
308+ }
309+
264310 // Update the current state
265311 this . setState ( {
266312 activeNodes : activeNodes
@@ -313,22 +359,51 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
313359 return input ;
314360 }
315361
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+
316387 /**
317388 * Triggers when taxonomy picker control loses focus
318389 */
319- private onBlur ( event : React . FocusEvent < HTMLElement | Autofill > ) : void {
390+ private async onBlur ( event : React . FocusEvent < HTMLElement | Autofill > ) : Promise < void > {
320391 const { validateInput } = this . props ;
321392 if ( ! ! validateInput ) {
322393 // Perform validation of input text, only if taxonomy picker is configured with validateInput={true} property.
323394 const target : HTMLInputElement = event . target as HTMLInputElement ;
324395 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 ( ) ;
330406 }
331- this . validateInputText ( ) ;
332407 }
333408 }
334409
@@ -404,6 +479,9 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
404479 if ( typeof result === 'string' ) {
405480 if ( ! result ) {
406481 this . validated ( value ) ;
482+ this . setState ( {
483+ errorMessage : undefined
484+ } ) ;
407485 }
408486 else {
409487 this . setState ( {
@@ -417,6 +495,9 @@ export class TaxonomyPicker extends React.Component<ITaxonomyPickerProps, ITaxon
417495
418496 if ( ! resolvedResult ) {
419497 this . validated ( value ) ;
498+ this . setState ( {
499+ errorMessage : undefined
500+ } ) ;
420501 }
421502 else {
422503 this . setState ( {
0 commit comments