import * as React from 'react' ; import * as ReactDom from 'react-dom' ; import { Version , ServiceKey , Text } from '@microsoft/sp-core-library' ; import { GlobalSettings } from 'office-ui-fabric-react' ; import { IWebPartPropertiesMetadata } from '@microsoft/sp-webpart-base' ; import { uniqBy } from '@microsoft/sp-lodash-subset' ; import { DynamicProperty } from "@microsoft/sp-component-base" ; import * as webPartStrings from 'SearchBoxWebPartStrings' ; import { IPropertyPaneConfiguration , IPropertyPaneField , PropertyPaneSlider , PropertyPaneDropdown , PropertyPaneDynamicField , PropertyPaneDynamicFieldSet , PropertyPaneTextField , PropertyPaneToggle , DynamicDataSharedDepth , IPropertyPanePage , IPropertyPaneGroup } from "@microsoft/sp-property-pane" ; import SearchBoxContainer from './components/SearchBoxContainer' ; import { ISearchBoxContainerProps } from './components/ISearchBoxContainerProps' ; import { DynamicDataService } from '../../services/dynamicDataService/DynamicDataService' ; import { IDynamicDataCallables , IDynamicDataPropertyDefinition } from '@microsoft/sp-dynamic-data' ; import IDynamicDataService from '../../services/dynamicDataService/IDynamicDataService' ; import { ComponentType } from '../../common/ComponentType' ; import { ISearchBoxWebPartProps } from './ISearchBoxWebPartProps' ; import { UrlHelper , PageOpenBehavior , QueryPathBehavior } from '../../helpers/UrlHelper' ; import * as commonStrings from 'CommonStrings' ; import { ServiceScope } from '@microsoft/sp-core-library' ; import { ISuggestionProviderDefinition , BaseSuggestionProvider } from '@pnp/modern-search-extensibility' ; import { AvailableSuggestionProviders , BuiltinSuggestionProviderKeys } from '../../providers/AvailableSuggestionProviders' ; import { ISuggestionProvider } from '@pnp/modern-search-extensibility' ; import { ServiceScopeHelper } from '../../helpers/ServiceScopeHelper' ; import { Toggle , IToggleProps , MessageBar , MessageBarType , Link } from "office-ui-fabric-react" ; import { ISuggestionProviderConfiguration } from '../../providers/ISuggestionProviderConfiguration' ; import { IExtensibilityConfiguration } from '../../models/common/IExtensibilityConfiguration' ; import { Constants } from '../../common/Constants' ; import { ITokenService } from '@pnp/modern-search-extensibility' ; import { BuiltinTokenNames , TokenService } from '../../services/tokenService/TokenService' ; import { BaseWebPart } from '../../common/BaseWebPart' ; import { DynamicPropertyHelper } from '../../helpers/DynamicPropertyHelper' ; export default class SearchBoxWebPart extends BaseWebPart < ISearchBoxWebPartProps > implements IDynamicDataCallables { /**
     * The error message
     */ private errorMessage : string = undefined ; /**
     * Dynamically loaded components for property pane
     */ private _propertyFieldCollectionData : any = null ; private _propertyPanePropertyEditor = null ; private _customCollectionFieldType : any = null ; /**
     * The dynamic data service instance
     */ private dynamicDataService : IDynamicDataService ; /**
     * The search query text present in the search box
     */ private _searchQueryText : string = '' ; private webPartInstanceServiceScope : ServiceScope ; /**
     * The available custom suggestions providers
     */ private availableCustomProviders : ISuggestionProviderDefinition [ ] = AvailableSuggestionProviders . BuiltinSuggestionProviders ; /**
     * The current selected suggestion providers
     */ private _selectedCustomProviders : ISuggestionProvider [ ] = [ ] ; private _pushStateCallback = null ; /**
     * The token service instance
     */ private tokenService : ITokenService ; constructor ( ) { super ( ) ; this . _bindHashChange = this . _bindHashChange . bind ( this ) ; } protected async onInit ( ) { this . initializeProperties ( ) ; await this . initializeBaseWebPart ( ) ; this . initializeWebPartServices ( ) ; await this . loadExtensions ( this . properties . extensibilityLibraryConfiguration ) ; this . _bindHashChange ( ) ; this . _handleQueryStringChange ( ) ; this . context . dynamicDataSourceManager . initializeSource ( this ) ; return super . onInit ( ) ; } public async render ( ) { try { this . errorMessage = undefined ; this . _selectedCustomProviders = await this . initializeSuggestionProviders ( this . properties . suggestionProviderConfiguration ) ; } catch ( error ) { this . errorMessage = error . message ? error . message : error ; } if ( this . context . propertyPane && this . context . propertyPane . isPropertyPaneOpen ( ) ) { this . context . propertyPane . refresh ( ) ; } return this . renderCompleted ( ) ; } protected renderCompleted ( ) { if ( ! this . domElement ) { return ; } let renderRootElement = null ; let inputValue = "" ; if ( this . properties . queryText ) { try { inputValue = DynamicPropertyHelper . tryGetValueSafe ( this . properties . queryText ) ; if ( inputValue !== undefined && typeof ( inputValue ) === 'string' ) { inputValue = decodeURIComponent ( inputValue ) ; } } catch ( error ) { } } if ( inputValue && typeof ( inputValue ) === 'string' ) { this . _searchQueryText = decodeURIComponent ( inputValue ) ; GlobalSettings . setValue ( BuiltinTokenNames . inputQueryText , this . _searchQueryText ) ; this . context . dynamicDataSourceManager . notifyPropertyChanged ( ComponentType . SearchBox ) ; } renderRootElement = React . createElement ( SearchBoxContainer , { domElement : this . domElement , enableQuerySuggestions : this . properties . enableQuerySuggestions , inputValue : this . _searchQueryText , openBehavior : this . properties . openBehavior , pageUrl : this . properties . pageUrl , placeholderText : this . properties . placeholderText , queryPathBehavior : this . properties . queryPathBehavior , queryStringParameter : this . properties . queryStringParameter , inputTemplate : this . properties . inputTemplate , searchInNewPage : this . properties . searchInNewPage , themeVariant : this . _themeVariant , onSearch : this . _onSearch , suggestionProviders : this . _selectedCustomProviders , numberOfSuggestionsPerGroup : this . properties . numberOfSuggestionsPerGroup , tokenService : this . tokenService } as ISearchBoxContainerProps ) ; if ( this . errorMessage ) { renderRootElement = React . createElement ( MessageBar , { messageBarType : MessageBarType . error , } , this . errorMessage , React . createElement ( Link , { target : '_blank' , href : this . properties . documentationLink } , commonStrings . General . Resources . PleaseReferToDocumentationMessage ) ) ; } ReactDom . render ( renderRootElement , this . domElement ) ; super . renderCompleted ( ) ; } protected onDispose ( ) { if ( this . _pushStateCallback ) { window . history . pushState = this . _pushStateCallback ; } ReactDom . unmountComponentAtNode ( this . domElement ) ; } protected get isRenderAsync ( ) : boolean { return true ; } protected get dataVersion ( ) : Version { return Version . parse ( '1.0' ) ; } protected getPropertyPaneConfiguration ( ) { let propertyPanePages = [ ] ; let providerOptionGroups = [ ] ; let extensibilityConfigurationGroups = [ ] ; if ( this . _selectedCustomProviders . length > 0 && ! this . errorMessage ) { this . _selectedCustomProviders . forEach ( provider => { providerOptionGroups = providerOptionGroups . concat ( provider . getPropertyPaneGroupsConfiguration ( ) ) ; } ) ; } propertyPanePages . push ( { groups : [ { groupName : webPartStrings . PropertyPane . SearchBoxSettingsGroup . GroupName , groupFields : this . _getSearchBoxSettingsFields ( ) } ] , displayGroupsAsAccordion : true } , { groups : [ { groupName : webPartStrings . PropertyPane . QuerySuggestionsGroup . GroupName , groupFields : this . _getSearchQuerySuggestionsFields ( ) } , ... providerOptionGroups ] , displayGroupsAsAccordion : true } , { groups : [ { groupName : webPartStrings . PropertyPane . AvailableConnectionsGroup . GroupName , groupFields : this . _getSearchAvailableConnectionsFields ( ) } ] , displayGroupsAsAccordion : true } ) ; extensibilityConfigurationGroups . push ( { groupName : commonStrings . PropertyPane . InformationPage . Extensibility . GroupName , groupFields : this . getExtensibilityFields ( ) } ) ; propertyPanePages . push ( { displayGroupsAsAccordion : true , groups : [ ... this . getPropertyPaneWebPartInfoGroups ( ) , ... extensibilityConfigurationGroups , { groupName : commonStrings . PropertyPane . InformationPage . ImportExport , groupFields : [ this . _propertyPanePropertyEditor ( { webpart : this , key : 'propertyEditor' } ) ] } ] } ) ; return { pages : propertyPanePages } ; } protected async loadPropertyPaneResources ( ) { const { PropertyFieldCollectionData , CustomCollectionFieldType } = await import ( '@pnp/spfx-property-controls/lib/PropertyFieldCollectionData' ) ; this . _propertyFieldCollectionData = PropertyFieldCollectionData ; this . _customCollectionFieldType = CustomCollectionFieldType ; const { PropertyPanePropertyEditor } = await import ( '@pnp/spfx-property-controls/lib/PropertyPanePropertyEditor' ) ; this . _propertyPanePropertyEditor = PropertyPanePropertyEditor ; } protected get propertiesMetadata ( ) : IWebPartPropertiesMetadata { return { 'queryText' : { dynamicPropertyType : 'string' } } ; } protected async onPropertyPaneFieldChanged ( propertyPath , oldValue , newValue ) { if ( ! this . properties . useDynamicDataSource ) { this . properties . queryText . setValue ( '' ) ; } if ( propertyPath . localeCompare ( 'enableQuerySuggestions' ) === 0 && ! newValue ) { this . properties . suggestionProviderConfiguration . forEach ( provider => { provider . enabled = false ; } ) ; } if ( propertyPath . localeCompare ( 'extensibilityLibraryConfiguration' ) === 0 ) { const cleanConfiguration = uniqBy ( this . properties . extensibilityLibraryConfiguration , 'id' ) ; this . availableCustomProviders = AvailableSuggestionProviders . BuiltinSuggestionProviders ; await this . loadExtensions ( cleanConfiguration ) ; } this . _bindHashChange ( ) ; } protected async onPropertyPaneConfigurationStart ( ) { await this . loadPropertyPaneResources ( ) ; } public getPropertyDefinitions ( ) { return [ { id : ComponentType . SearchBox , title : webPartStrings . General . DynamicPropertyDefinition } ] ; } public getPropertyValue ( propertyId ) { switch ( propertyId ) { case ComponentType . SearchBox : return this . _searchQueryText ; } } /**
     * Determines the group fields for the search query options inside the property pane
     */ private _getSearchAvailableConnectionsFields ( ) { let searchAvailabeConnectionsConfigFields = [ PropertyPaneToggle ( 'useDynamicDataSource' , { label : webPartStrings . PropertyPane . AvailableConnectionsGroup . UseDynamicDataSourceLabel , } ) ] ; if ( this . properties . useDynamicDataSource ) { searchAvailabeConnectionsConfigFields . push ( PropertyPaneDynamicFieldSet ( { label : webPartStrings . PropertyPane . AvailableConnectionsGroup . QueryKeywordsPropertyLabel , fields : [ PropertyPaneDynamicField ( 'queryText' , { label : webPartStrings . PropertyPane . AvailableConnectionsGroup . QueryKeywordsPropertyLabel , } ) ] , sharedConfiguration : { depth : DynamicDataSharedDepth . Source , } } ) ) ; } return searchAvailabeConnectionsConfigFields ; } private _getSearchQuerySuggestionsFields ( ) { let searchQuerySuggestionsFields = [ PropertyPaneToggle ( "enableQuerySuggestions" , { label : webPartStrings . PropertyPane . QuerySuggestionsGroup . EnableQuerySuggestions } ) ] ; if ( this . properties . enableQuerySuggestions ) { searchQuerySuggestionsFields . push ( this . _propertyFieldCollectionData ( 'suggestionProviderConfiguration' , { manageBtnLabel : webPartStrings . PropertyPane . QuerySuggestionsGroup . EditSuggestionProvidersLabel , key : 'suggestionProviderConfiguration' , panelHeader : webPartStrings . PropertyPane . QuerySuggestionsGroup . EditSuggestionProvidersLabel , panelDescription : webPartStrings . PropertyPane . QuerySuggestionsGroup . SuggestionProvidersDescription , disableItemCreation : true , disableItemDeletion : true , disabled : ! this . properties . enableQuerySuggestions , label : webPartStrings . PropertyPane . QuerySuggestionsGroup . SuggestionProvidersLabel , value : this . properties . suggestionProviderConfiguration , fields : [ { id : 'enabled' , title : webPartStrings . PropertyPane . QuerySuggestionsGroup . EnabledPropertyLabel , type : this . _customCollectionFieldType . custom , onCustomRender : ( field , value , onUpdate , item , itemId ) => { return ( React . createElement ( "div" , null , React . createElement ( Toggle , { key : itemId , checked : value , onChange : ( evt , checked ) => { onUpdate ( field . id , checked ) ; } , offText : commonStrings . General . OffTextLabel , onText : commonStrings . General . OnTextLabel } ) ) ) ; } } , { id : 'name' , title : webPartStrings . PropertyPane . QuerySuggestionsGroup . ProviderNamePropertyLabel , type : this . _customCollectionFieldType . custom , onCustomRender : ( field , value ) => { return ( React . createElement ( "div" , { style : { 'fontWeight' : 600 } } , value ) ) ; } } , { id : 'description' , title : webPartStrings . PropertyPane . QuerySuggestionsGroup . ProviderDescriptionPropertyLabel , type : this . _customCollectionFieldType . custom , onCustomRender : ( field , value ) => { return ( React . createElement ( "div" , null , value ) ) ; } } ] } ) , PropertyPaneSlider ( 'numberOfSuggestionsPerGroup' , { min : 1 , max : 20 , showValue : true , step : 1 , label : webPartStrings . PropertyPane . QuerySuggestionsGroup . NumberOfSuggestionsToShow } ) ) ; } return searchQuerySuggestionsFields ; } /**
     * Determines the group fields for the search options inside the property pane
     */ private _getSearchBoxSettingsFields ( ) { let searchBehaviorOptionsFields = [ PropertyPaneTextField ( 'placeholderText' , { label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . PlaceholderTextLabel } ) , PropertyPaneToggle ( "searchInNewPage" , { label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . SearchInNewPageLabel } ) ] ; if ( this . properties . searchInNewPage ) { searchBehaviorOptionsFields = searchBehaviorOptionsFields . concat ( [ PropertyPaneTextField ( 'inputTemplate' , { label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . QueryInputTransformationLabel , multiline : true , placeholder : `{${ BuiltinTokenNames . inputQueryText }}` } ) , PropertyPaneTextField ( 'pageUrl' , { disabled : ! this . properties . searchInNewPage , label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . PageUrlLabel , onGetErrorMessage : this . _validatePageUrl . bind ( this ) , validateOnFocusOut : true , validateOnFocusIn : true , placeholder : 'https://...' } ) , PropertyPaneDropdown ( 'openBehavior' , { label : commonStrings . General . PageOpenBehaviorLabel , options : [ { key : PageOpenBehavior . Self , text : commonStrings . General . SameTabOpenBehavior } , { key : PageOpenBehavior . NewTab , text : commonStrings . General . NewTabOpenBehavior } ] , disabled : ! this . properties . searchInNewPage , selectedKey : this . properties . openBehavior } ) , PropertyPaneDropdown ( 'queryPathBehavior' , { label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . QueryPathBehaviorLabel , options : [ { key : QueryPathBehavior . URLFragment , text : webPartStrings . PropertyPane . SearchBoxSettingsGroup . UrlFragmentQueryPathBehavior } , { key : QueryPathBehavior . QueryParameter , text : webPartStrings . PropertyPane . SearchBoxSettingsGroup . QueryStringQueryPathBehavior } ] , disabled : ! this . properties . searchInNewPage , selectedKey : this . properties . queryPathBehavior } ) ] ) ; } if ( this . properties . searchInNewPage && this . properties . queryPathBehavior === QueryPathBehavior . QueryParameter ) { searchBehaviorOptionsFields = searchBehaviorOptionsFields . concat ( [ PropertyPaneTextField ( 'queryStringParameter' , { disabled : ! this . properties . searchInNewPage || this . properties . searchInNewPage && this . properties . queryPathBehavior !== QueryPathBehavior . QueryParameter , label : webPartStrings . PropertyPane . SearchBoxSettingsGroup . QueryStringParameterName , onGetErrorMessage : ( value ) => { if ( this . properties . queryPathBehavior === QueryPathBehavior . QueryParameter ) { if ( value === null || value . trim ( ) . length === 0 ) { return webPartStrings . PropertyPane . SearchBoxSettingsGroup . QueryParameterNotEmpty ; } } return '' ; } } ) ] ) ; } return searchBehaviorOptionsFields ; } private getExtensibilityFields ( ) { let extensibilityFields = [ this . _propertyFieldCollectionData ( 'extensibilityLibraryConfiguration' , { manageBtnLabel : commonStrings . PropertyPane . InformationPage . Extensibility . ManageBtnLabel , key : 'extensibilityLibraryConfiguration' , enableSorting : true , panelHeader : webPartStrings . PropertyPane . InformationPage . Extensibility . PanelHeader , panelDescription : webPartStrings . PropertyPane . InformationPage . Extensibility . PanelDescription , label : commonStrings . PropertyPane . InformationPage . Extensibility . FieldLabel , value : this . properties . extensibilityLibraryConfiguration , fields : [ { id : 'name' , title : commonStrings . PropertyPane . InformationPage . Extensibility . Columns . Name , type : this . _customCollectionFieldType . string } , { id : 'id' , title : commonStrings . PropertyPane . InformationPage . Extensibility . Columns . Id , type : this . _customCollectionFieldType . string , onGetErrorMessage : this . _validateGuid . bind ( this ) } , { id : 'enabled' , title : commonStrings . PropertyPane . InformationPage . Extensibility . Columns . Enabled , type : this . _customCollectionFieldType . custom , required : true , onCustomRender : ( field , value , onUpdate , item , itemId ) => { return ( React . createElement ( "div" , null , React . createElement ( Toggle , { key : itemId , checked : value , offText : commonStrings . General . OffTextLabel , onText : commonStrings . General . OnTextLabel , onChange : ( ( evt , checked ) => { onUpdate ( field . id , checked ) ; } ) . bind ( this ) } as IToggleProps ) ) ) ; } } ] } ) ] ; return extensibilityFields ; } param value private _validatePageUrl ( value ) { if ( ( ! ( /^(https?):\/\/[^\s/$.?#].[^\s]*/ ) . test ( value ) || ! value ) && this . properties . searchInNewPage ) { return webPartStrings . PropertyPane . SearchBoxSettingsGroup . UrlErrorMessage ; } return '' ; } /**
     * Initializes required Web Part properties
     */ private initializeProperties ( ) { this . properties . queryText = this . properties . queryText ? this . properties . queryText : new DynamicProperty < string > ( this . context . dynamicDataProvider ) ; this . properties . inputTemplate = this . properties . inputTemplate ? this . properties . inputTemplate : `{${ BuiltinTokenNames . inputQueryText }}` ; this . properties . openBehavior = this . properties . openBehavior ? this . properties . openBehavior : PageOpenBehavior . Self ; this . properties . queryPathBehavior = this . properties . queryPathBehavior ? this . properties . queryPathBehavior : QueryPathBehavior . URLFragment ; this . properties . suggestionProviderConfiguration = this . properties . suggestionProviderConfiguration ? this . properties . suggestionProviderConfiguration : [ ] ; this . properties . numberOfSuggestionsPerGroup = this . properties . numberOfSuggestionsPerGroup ? this . properties . numberOfSuggestionsPerGroup : 10 ; this . properties . providerProperties = this . properties . providerProperties ? this . properties . providerProperties : { } ; this . properties . extensibilityLibraryConfiguration = this . properties . extensibilityLibraryConfiguration ? this . properties . extensibilityLibraryConfiguration : [ { name : commonStrings . General . Extensibility . DefaultExtensibilityLibraryName , enabled : true , id : Constants . DEFAULT_EXTENSIBILITY_LIBRARY_COMPONENT_ID } ] ; } private initializeWebPartServices ( ) { this . tokenService = this . context . serviceScope . consume < ITokenService > ( TokenService . ServiceKey ) ; this . webPartInstanceServiceScope = this . context . serviceScope . startNewChild ( ) ; this . dynamicDataService = this . webPartInstanceServiceScope . createDefaultAndProvide ( DynamicDataService . ServiceKey ) ; this . dynamicDataService . dynamicDataProvider = this . context . dynamicDataProvider ; this . webPartInstanceServiceScope . finish ( ) ; } /**
     * Handler used to notify data source subscribers when the input query is updated
     */ private _onSearch = ( searchQuery ) : void => { this . _searchQueryText = searchQuery ; GlobalSettings . setValue ( BuiltinTokenNames . inputQueryText , searchQuery ) ; this . context . dynamicDataSourceManager . notifyPropertyChanged ( ComponentType . SearchBox ) ; this . context . dynamicDataSourceManager . notifySourceChanged ( ) ; if ( this . properties . useDynamicDataSource && this . properties . queryText && this . properties . queryText . reference ) { const refChunks = this . properties . queryText . reference . split ( ':' ) ; if ( refChunks . length >= 3 ) { const paramType = refChunks [ 2 ] ; if ( paramType === 'fragment' ) { window . history . pushState ( undefined , undefined , `#${ searchQuery }` ) ; } else if ( paramType . indexOf ( 'queryParameters' ) !== - 1 ) { const paramChunks = paramType . split ( '.' ) ; const queryTextParam = paramChunks . length === 2 ? paramChunks [ 1 ] : 'q' ; const newUrl = UrlHelper . addOrReplaceQueryStringParam ( window . location . href , queryTextParam , searchQuery ) ; if ( window . location . href !== newUrl ) { window . history . pushState ( { path : newUrl } , undefined , newUrl ) ; } } } } } ; /**
     * Subscribes to URL hash change if the dynamic property is set to the default 'URL Fragment' property
     */ private _bindHashChange ( ) { if ( this . properties . queryText . tryGetSource ( ) && this . properties . queryText . reference . localeCompare ( 'PageContext:UrlData:fragment' ) === 0 ) { window . addEventListener ( 'hashchange' , this . render ) ; } else { window . removeEventListener ( 'hashchange' , this . render ) ; } } private async initializeSuggestionProviders ( suggestionProviderConfiguration ) { const promises = [ ] ; let selectedProviders = [ ] ; suggestionProviderConfiguration . forEach ( configuration => { if ( configuration . enabled ) { promises . push ( this . getSuggestionProviderInstance ( configuration . key , this . availableCustomProviders ) ) ; } } ) ; if ( promises . length > 0 ) { selectedProviders = await Promise . all ( promises ) ; } else { selectedProviders = [ ] ; } return selectedProviders ; } param providerKey param suggestionProviderDefinitions returns private async getSuggestionProviderInstance ( providerKey , suggestionProviderDefinitions ) { let suggestionsProvider = undefined ; let serviceKey = undefined ; if ( providerKey ) { switch ( providerKey ) { case BuiltinSuggestionProviderKeys . SharePointStaticSuggestions : const { SharePointSuggestionProvider } = await import ( '../../providers/SharePointSuggestionProvider' ) ; serviceKey = ServiceKey . create < ISuggestionProvider > ( 'ModernSearchSharePointStaticSuggestionProvider' , SharePointSuggestionProvider ) ; break ; default : const matchingDefinitions = suggestionProviderDefinitions . filter ( ( provider ) => { return provider . key === providerKey ; } ) ; if ( matchingDefinitions . length > 0 ) { serviceKey = matchingDefinitions [ 0 ] . serviceKey ; } else { throw new Error ( Text . format ( commonStrings . General . Extensibility . ProviderDefinitionNotFound , providerKey ) ) ; } break ; } return new Promise < ISuggestionProvider > ( ( resolve , reject ) => { const childServiceScope = ServiceScopeHelper . registerChildServices ( this . webPartInstanceServiceScope , [ serviceKey ] ) ; childServiceScope . whenFinished ( async ( ) => { suggestionsProvider = childServiceScope . consume < ISuggestionProvider > ( serviceKey ) ; const isValidProvider = ( providerInstance ) : providerInstance is BaseSuggestionProvider < any > => { return ( ( providerInstance as BaseSuggestionProvider < any > ) . getPropertyPaneGroupsConfiguration !== undefined && ( providerInstance as BaseSuggestionProvider < any > ) . getSuggestions !== undefined && ( providerInstance as BaseSuggestionProvider < any > ) . onPropertyUpdate !== undefined && ( providerInstance as BaseSuggestionProvider < any > ) . onInit !== undefined ) ; } ; if ( ! isValidProvider ( suggestionsProvider ) ) { reject ( new Error ( Text . format ( commonStrings . General . Extensibility . InvalidProviderInstance , providerKey ) ) ) ; } if ( suggestionsProvider ) { suggestionsProvider . properties = this . properties . providerProperties ; suggestionsProvider . context = this . context ; await suggestionsProvider . onInit ( ) ; resolve ( suggestionsProvider ) ; } } ) ; } ) ; } } /**
     * Loads extensions from registered extensibility librairies
     */ private async loadExtensions ( librariesConfiguration ) { const customSuggestionProviderConfiguration = [ ] ; const extensibilityLibraries = await this . extensibilityService . loadExtensibilityLibraries ( librariesConfiguration ) ; if ( extensibilityLibraries . length > 0 ) { extensibilityLibraries . forEach ( extensibilityLibrary => { this . availableCustomProviders = this . availableCustomProviders . concat ( extensibilityLibrary . getCustomSuggestionProviders ( ) ) ; } ) ; } this . availableCustomProviders . forEach ( provider => { if ( ! this . properties . suggestionProviderConfiguration . some ( p => p . key === provider . key ) ) { customSuggestionProviderConfiguration . push ( { key : provider . key , description : provider . description , enabled : false , name : provider . name } ) ; } } ) ; this . properties . suggestionProviderConfiguration = this . properties . suggestionProviderConfiguration . concat ( customSuggestionProviderConfiguration ) ; } param value private _validateGuid ( value ) { if ( value . length > 0 ) { if ( ! ( /^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$/ ) . test ( value ) ) { return 'Invalid GUID' ; } } return '' ; } /**
     * Subscribes to URL query string change events using SharePoint page router
     */ private _handleQueryStringChange ( ) { if ( /^(PageContext:SearchData:searchQuery)|(PageContext:UrlData:queryParameters)/ . test ( this . properties . queryText . reference ) ) { ( ( h ) => { this . _pushStateCallback = history . pushState ; h . pushState = this . pushStateHandler . bind ( this ) ; } ) ( window . history ) ; } } private pushStateHandler ( state , key , path ) { this . _pushStateCallback . apply ( history , [ state , key , path ] ) ; if ( this . properties . queryText . isDisposed ) { return ; } const source = this . properties . queryText . tryGetSource ( ) ; if ( source && source . id === ComponentType . PageEnvironment ) { this . render ( ) ; } } }