UNPKG

@microsoft/sp-webpart-base

Version:

SharePoint Framework support for building web parts

467 lines 21.7 kB
import type { IA11yCheckResult } from '@msinternal/sp-a11y-checker-util'; import { DisplayMode } from '@microsoft/sp-core-library'; import type { IClientSideWebPartManifest, IAdaptiveCardExtensionManifest } from '@microsoft/sp-module-interfaces'; import type { _IPropertyPaneConsumer as IPropertyPaneConsumer, _PropertyPaneAction as PropertyPaneAction } from '@microsoft/sp-property-pane'; import type { ITopActions } from '@microsoft/sp-top-actions'; import type { IRenderProps } from '@msinternal/sp-renderable-components-base'; import BaseClientSideWebPart from './BaseClientSideWebPart'; import type IWebPartData from './IWebPartData'; import type IWebPartHost from '../components/host/IWebPartHost'; import type IWebPartManagerContext from './IWebPartManagerContext'; import WebPartContext from './WebPartContext'; import type { ITranspileContext } from './transpile/ITranspileContext'; import { WebPartIdleLoad } from './idleLoad/WebPartIdleLoad'; /** * The ClientSideWebPartManager is expected to be the public interface to client-side web parts. Each * host is expected to create an instance of this class and manage loading of all web parts in that host * through that one instance. e.g. On one page, if there are multiple Canvas objects, each one could have * one instance of this class. If the page hosts web parts without the Canvas, then the page can have an * instance of this class. Overall this class is expected to provide the following purpose: * * - Orchestrates loading of one or more web parts in a host. * - It takes care of loading web part dependencies asynchronously. Each web part loads completely * independently of the other web parts in the host. * - Keep a reference to all the loaded web parts and help provide bulk operations on the web parts. * - Help manage memory leak type issues in one place. * - Integrate the web parts with the PropertyPane and other external entities. * - Help troubleshoot web parts during escalations. * * @internal */ export default class ClientSideWebPartManager { /** * Reference to the instances of all ClientSideWebPartManager instances in the app. */ private static readonly _webPartManagerList; private static readonly _1PIsolatedDomainRegex; private static readonly _logSource; private static _manifestRequestPromiseCache; /** * A promise that resolves to the component responsible for managing web part prefetching. * * note: You must call `enablePrefetching` prior to reading this property. */ webPartPrefetching: WebPartIdleLoad | undefined; /** * Reference to the PropertyPane controller. * Note, all instances of ClientSideWebPartManager share the same PropertyPane controller. */ private _propertyPane; /** * Dictionary of all the active web parts in this instance of the manager. */ private _webParts; /** * Host for this instance of the web part manager. * Note, at the current time there is no support for multiple hosts and the host is not expected to change. */ private readonly _host; /** * Timer to mark the canvas dirty. */ private _dirtyBitTimer; /** * Web part status renderer instance scoped to this web part manager instance. */ private _statusRenderer; /** * Page context reference. */ private readonly _pageContext; /** * Teams apps client instance. */ private _deferredTeamsAppsClient; private _toolboxTeamsAppComponentsManifests; /** * Promise for the async call to get edit-time web parts. */ private _toolboxManifestsPromise; /** * Promise for the async call to get edit-time AdaptiveCardExtensions * This currently requires a separate call to a different endpoint (GetAdaptiveCardExtensions) * When the webpart and ACE endpoints are merged, this will be removed */ private _toolboxACEManifestsPromise; /** * Promise for the async call to get edit-time Teams ACEs. */ private _toolboxTeamsACEManifestsPromise; /** * Dictionary of all the iframed web parts in this instance of the manager. */ private _iframedWebpartInstanceIds; /** * Reference to IframedWebPartController instance. * The instance is chunked loaded for the first time in 'this.loadWebPart' * and should be checked for existence before using. */ private _iframedWebPartController; private _propertyPaneLoader; private _propertyPaneConsumerQueue; private _displayMode; /** * Create the web part tag. Web part tag is a unique tag per web part instance and is used for logging and telemetry. */ static createWebPartTag(manifest: IClientSideWebPartManifest<any>, instanceId: string): string; private static _instanceOfBase; private static _getWebPartTag; private static _isLoadingIsolatedWebPart; private static _getWebPartTitle; private static _loadDynamicComponentDependencies; /** * Returns true if the maintenanceMode query string parameter is provided. */ static get isMaintenanceMode(): boolean; /** * Initialize the ClientSideWebPartManager. * * @param host - Reference to the host. A web part host is a component that is deemed capable of hosting a web * part. Any component that implements the IWebPartHost is allowed to host a web part. */ constructor(host: IWebPartHost); /** * Enables prefetching for the specified set of web parts. * * @privateRemarks * This is a prerequiste for prefetching and it does not initiate prefetching. * You must call `prefetchWebParts` to start the prefetching process (see example below). * * Example: * ``` * ClientSideWebpartManager.webPartPrefetching?.then((prefetching) => * prefetching.prefetchWebParts() * ); * ``` * @param controls - The web parts to enable prefetching for. * @beta */ enablePrefetching(controls: IWebPartData[]): void; /** * Load a web part in the provided DOM element. Does the following steps * * 1. Validate params. * 2. Validate the web part manifest. * 3. Perform an async import of the web part modules from the provided manifest * - i.e ClientSideWebPartManager._loadComponentModules * 4. Instantiate and initialize the web part object * - i.e. ClientSideWebPartManager._initializeWebPart * 5. Render the web part in the provided DOM element * - i.e. ClientSideWebPartManager._renderWebPart * * If an error happens during any of the above steps, catch the error and log it. * * @param webPartManagerContext - web part manager context. */ loadWebPart(webPartManagerContext: IWebPartManagerContext): Promise<void>; /** * Set a IPropertyPaneConsumer object into the set of the Client-side Web Part Manager's * managed web parts. * * @param id - A unique instance id. * @param control - A component which wants to use the property Pane. * * @internal */ setPropertyPaneConsumer(id: string, control: IPropertyPaneConsumer): void; /** * Fetch web part manifests. This makes a REST call to load the current site's web parts into the module loader. * * Previously, this method only fetched WebPart manifests through a REST call to GetClientSideWebParts * Then, an extra call to fetch AdaptiveCardExtension manifests via GetAdaptiveCardExtensions was added when MEEDashboard is enabled * * To consolidate these calls into a single request, we use a new endpoint, GetClientSideComponentsByComponentType * * If the CallGetClientSideComponentsByComponentType flight is enabled * - call GetClientSideComponentsByComponentType(["WebPart", "AdaptiveCardExtension"]) if MEEDashboard flight enabled * - call GetClientSideWebParts, since GetClientSideComponentsByComponentType server endpoint checks for MEEDashboard flight: Workitem 1119156 * If the CallGetClientSideComponentsByComponentType flight is disabled * - call GetClientSideWebParts and GetAdaptiveCardExtensions if MEEDashboard flight is enabled * - call GetClientSideWebParts, only, if MEEDashboard flight is disabled * * This method will also initiate fetching of Teams-connected manifests from Teams App Catalog. */ fetchWebPartManifests(): Promise<void>; /** * Get list of active web part manifests. * * @param includeAdaptiveCardExtensions - Include ACE manifests (casted as webpart manifests) in the returned array. * * @returns - array of manifests. */ getWebPartManifests(includeAdaptiveCardExtensions?: boolean): IClientSideWebPartManifest<unknown>[]; /** * Get list of Adaptive Card Extension manifests. * * @returns - array of ACE manifests */ getAdaptiveCardExtensionManifests(): IAdaptiveCardExtensionManifest<unknown>[]; /** * Set the display mode of the specified web part. If no web part id is specified, switch mode of all web parts. * If the display mode passed is same as the current mode, no change is applied. * * @param displayMode - the new DisplayMode. * @param instanceId - instance id of the web part. */ setDisplayMode(displayMode: DisplayMode, instanceId?: string): void; /** * @remarks * Only use this API if you need to immediately call property pane APIs after switching the mode. * * @internal */ _setDisplayMode(displayMode: DisplayMode, instanceId?: string): Promise<void>; /** * Serialize the specified web part. If no web part is specified, serialize all web parts. * * @param instanceId - instance id of the web part. */ serialize(instanceId?: string): Map<string, IWebPartData | undefined>; /** * Set the web part data for the specified web part using data that is not persisted. * * _Warning: This method can throw and Promise.catch will not catch it. You should wrap the usage in a try/catch._ * * @param webPartData - the new webPartData. * @param instanceId - instance id of the web part. * @returns - A promise that resolves immediately. */ setWebPartData(webPartData: IWebPartData, instanceId: string): Promise<void>; /** * Set the web part data for the specified web part. * * The setWebPartData API has 3 possible scenarios when the web part data is updated externally. * - Web part declares useFallbackWhenPropertiesUpdatedExternally manifest field as `true`, web part manager will * trigger the default fallback logic which disposes the web part and reload it using the given context. * - Web part overrides `onAfterPropertiesUpdatedExternally` life cycle events, web part manager will first * deserialize the web part data, then invokes the `onAfterPropertiesUpdatedExternally` event to allow web part to * do their customized handling logic. * - If useFallbackWhenPropertiesUpdatedExternally manifest field is `false` or `undefined`, and web part does not * override `onAfterPropertiesUpdatedExternally` life cycle events, web part manager will first deserialize the * web part data, then invokes the `_refresh` life cycle to re-render the web part. * * _Warning: This method can throw and Promise.catch will not catch it. You should wrap the usage in a try/catch._ * * @param context - the new context. * @param instanceId - instance id of the web part. * @param shouldFallback - whether it should fallback to dispose & reload logic. * @returns - A promise that resolves immediately if fallback is not used, or returns the loadWebPart promise * when fallback is used. */ setWebPartData(context: IWebPartManagerContext, instanceId: string, shouldFallback?: boolean): Promise<void>; /** * Dispose of the current webpart manager and all of the webparts it has loaded. */ dispose(): void; /** * Dispose the specified web part. If no web part is specified, dispose all web parts. * * @param instanceId - instance id of the web part. */ disposeWebparts(instanceId?: string): void; tryGeneratePreviewImageUrl(instanceIds?: string[]): string | undefined; /** * Gets the top action configuration for the webpart with the specified instanceId * @param instanceId - the specified instanceId * @returns undefined if the webpart isn't loaded yet or the webpart doesn't support top actions. */ getWebpartTopActions(instanceId: string): ITopActions | undefined; /** * Gets the accessibility results for the webpart with the specified instanceId * @param instanceId - the specified instanceId * @returns undefined if the webpart isn't loaded yet or the webpart doesn't support accessibility assistant. */ getWebpartA11yResult(instanceId: string): Promise<(IA11yCheckResult | undefined)[]>; /** * Calls the preOnClickA11yResult method for the webpart with the specified instanceId * to help users redirect to the specific item editing configuration pane to fix the accessibility issues. * @param result - the accessibility result to be passed to the webpart */ preOnClickA11yResult(result: IA11yCheckResult): void; /** * Request property pane or content panel to perform the given action. * * @param instanceId - web part instance id. * @param propertyPaneAction - indicates what action needs to be performed on the property pane. * @param renderedByWebPart - indicates whether the the property pane rendered by a web part or not. * @param context - pass additional context to property pane */ requestPropertyPaneAction(instanceId: string, propertyPaneAction?: PropertyPaneAction, renderedByWebPart?: boolean, context?: any): void; /** * Returns true if the current property pane or content panel source is a web part and not the Canvas or any other source. */ isPropertyPaneRenderedByWebPart(): boolean; /** * Returns the state of the PropertyPane if it is open or not. * Also true if Property Pane is rendered by Content Panel. * * @param instanceId - optional param to check isopen based on the supported consumer (content panel or property pane). * see IConfigurableOptionsController.isOpen(instanceId) for more details */ isPropertyPaneOpen(instanceId?: string): boolean; /** * Returns the state of the ContentPanel if it is open or not. * Returns true if the Content Panel is open, regardless if the Content Panel is rendering the property pane. */ isContentPanelOpen(): boolean; /** * Method to handle the web part delete action from the host. There is a key distinction between delete and dispose. * Delete implies that the web part has been deleted from the page and the web part should dispose all the server * side or other external resources attached to the web part. Dispose implies that an in-place navigation is * happening and the web part manager should delete the web part from its cache. * * @param instanceId - instance id of the webpart which is deleted. */ onWebPartDelete(instanceId: string): void; /** * Render an error message in the web part container div. Also logs the error message to the IWebPartHost logger. */ renderError(domElement: HTMLElement, error: Error): void; /** * Notify webparts that their container has resized. * * @param instanceId - if specified only notify one webpart that its container has resized */ notifyWebPartContainerResize(instanceId?: string): void; /** * Notify web parts to check dirty bit without waiting for dirty bit timer to run. * @see ClientSideWebPartManager._startDirtyBitTimer for more details about the dirty bit timer. */ notifyWebPartsSetDirtyBit(): void; /** * Used to ensure the next request for webpart manifests makes a call to the server. */ clearManifestPromise(): void; /** * Find the render props for the web part with the given instance id. * @param webPartManagerContext - web part manager context. * @param request - transpile context. * @returns render props for the web part. * * @internal */ getWebPartTranspileRenderProps(webPartManagerContext: IWebPartManagerContext, request: ITranspileContext): Promise<IRenderProps[]>; /** * Find the instance of the web part with the given instance id within the manager instance. * @param instanceId - Instance Id of the web part * @returns Instance of the web part, if it exists in this manager. * * @internal */ getWebPartByInstanceId(instanceId: string): BaseClientSideWebPart<unknown & {}> | undefined; /** ------------------- PROTECTED ------------------- **/ /** * Generate web part context. */ protected _getWebPartContext(context: IWebPartManagerContext): WebPartContext; /** ------------------- PRIVATE ------------------- **/ private _getLoadedWebPart; /** * Load a web part in the provided DOM element. Does the following steps * * 1. Validate params. * 2. Validate the web part manifest. * 3. Perform an async import of the web part modules from the provided manifest * - i.e ClientSideWebPartManager._loadComponentModules * 4. Instantiate and initialize the web part object * - i.e. ClientSideWebPartManager._initializeWebPart * 5. Render the web part in the provided DOM element * - i.e. ClientSideWebPartManager._renderWebPart * * If an error happens during any of the above steps, catch the error and log it. * * @param webPartManagerContext - web part manager context. */ private _loadWebPartOld; private _isIsolatedWebPart; private _isACE; private _isPrefab; /** * Load a web part in the provided DOM element. Does the following steps * * 1. Validate params. * 2. Validate the web part manifest. * 3. Perform an async import of the web part modules from the provided manifest * - i.e ClientSideWebPartManager._loadComponentModules * 4. Instantiate and initialize the web part object * - i.e. ClientSideWebPartManager._initializeWebPart * * If an error happens during any of the above steps, catch the error and log it. * * @param webPartManagerContext - web part manager context. */ private _loadAndInitWebPart; private _initializeWebPart; private _fetchManifestsWithCache; private _fetchAdaptiveCardExtensionManifests; private _fetchTeamsACEManifests; private _clearManifestCacheItem; /** * Workaround for a server issue - disambiguate manifest locales if they haven't already been disambiguated * (VSO#243888) tracks fixing this issue. */ private _disambiguateWebPartManifestLocales; private _loadIsolatedWebPart; private _waitForTeamsAppSync; private _loadComponentModules; /** * Execute the provided callback for the list of provided web part ids. If no list if provided, * execute the callback on all web parts. */ private _executeForIdsOrAll; /** * This is a temporary implementation of updating the host that a web part has updated properties. * Currently we run a timer that regularly checks for updated properties and raises the dirty bit * handler to the host. todo (VSO SPPPLAT#200728) tracks fixing this scenario in a better way. */ private _startDirtyBitTimer; private _notifyWebPartsSetDirtyBit; private _deleteWebPart; /** * Get web part manifest instance from manifest. Promotes the pre-configured entries. */ private _getManifestInstance; /** * If non-non parameter passed, return array else return undefined. */ private _getArrayOrUndefined; private _initialzeOnResizeEventHandler; /** * Window onresize event handler. */ private _onContainerResize; /** * Listener for window post message that the property pane. * * todo (SPPPLAT Bug #299413): Implement missing schema for property pane post messages * todo (SPPPLAT PBI #687467): Refactor IframedWebPartContoller */ private _onPropertyPaneNotifications; private _renderWebPart; private _loadPropertyPaneModule; private _getDataUpdatedEventName; /** * Registers a component as a property pane consumer. * @param instanceId - Instance id of the consumer, to be registered with the property pane. */ private _registerWebPartAsPropertyPaneConsumer; private _registerPendingPropertyPaneConsumers; private _closeIsolatedPropertyPaneIfRequired; /** * Load the fabric core library, for third-party web parts if required. * @param context - Current context of the web part manager */ private _loadLegacyFabricCssIfRequired; private get _isDebugSession(); /** * Loads the property pane module asynchronously, if not already loaded. */ private _loadPropertyPaneModuleOld; private _validateIfWPIsNotDisposed; private _swapManifestForWebparts; private _doesUserHasPermissions; private get _teamsAppsClient(); private _isTitleAreaBannerWebPart; private _loadWebPartPrefetching; } //# sourceMappingURL=ClientSideWebPartManager.d.ts.map