UNPKG

@microsoft/sp-webpart-base

Version:

SharePoint Framework support for building web parts

649 lines 28.3 kB
import type { IA11yCheckResult } from '@msinternal/sp-a11y-checker-util'; import { BaseComponent } from '@microsoft/sp-component-base'; import { DisplayMode, Version } from '@microsoft/sp-core-library'; import type { _IPropertyPaneData as IPropertyPaneData, _IPropertyPaneConsumer as IPropertyPaneConsumer, _PropertyPaneLifeCycleEvent as PropertyPaneLifeCycleEvent } from '@microsoft/sp-property-pane'; import type { ITopActions } from '@microsoft/sp-top-actions'; import type BaseWebPartContext from './BaseWebPartContext'; import { type IPropertyPaneConfiguration, PropertyPaneFieldType } from '../SPPropertyPane'; import type { IHideOn, IModifiedMetadata, default as IWebPartData } from './IWebPartData'; import type { default as IWebPartPropertiesMetadata } from './IWebPartPropertiesMetadata'; /** * This abstract class implements the UI-agnostic base functionality for a client-side web part. * The purpose is to allow a common core between 2D and MR WebParts. * * @privateRemarks * The _refresh() and _dynamicPropertyRefresh() API are the only API that are * mandatory to be implemented by our base WebPart classes. * * @public */ export default abstract class BaseWebPart<TProperties extends {}> extends BaseComponent implements IPropertyPaneConsumer { /** * {@inheritDoc @microsoft/sp-component-base#BaseComponent.context} */ readonly context: BaseWebPartContext; /** * @internal */ protected _renderedFromPersistedData: boolean | undefined; private _displayMode; private _properties; private _initialized; /** * Keep a copy of the properties to be used for discard in non-reactive mode and this new set * of `properties('_backUpProperties')` is what will be persisted until the apply button is clicked. */ private _backupProperties; private _previousState; private _baseLogSource; private _title; private _modifiedByGraph; private _description; private _iconImageUrl; private _audiences; private _hideOn; /** * It makes sure we only log edit events once during one editing session. */ private _hasEditLogged; /** * Cache init properties value, it would be used to check whether web part has been changed or not. */ private _initPropertiesSnapshot; private _loadPropertyPaneResourcesPromise; private _emptyResolvedPromise; /** * This property points to the preview image for the web part. The base implementation returns undefined. Web parts * that want to provide a valid preview image url need to override this API. The preview image url can be used to * create a preview of the web part or of the page on which the web part is present. * virtual */ get previewImageUrl(): string | undefined; /** * This property is the current display mode of the web part. * * @readonly */ get displayMode(): DisplayMode; /** * This property holds metadata about Graph API calls that touch the web part. * @internal */ get modifiedByGraph(): IModifiedMetadata; /** * The value of this property is stored in the serialized data of the web part to allow developers to manage * versioning of their web part. The default version is 1.0 */ protected get dataVersion(): Version; /** * This property is the pointer to the custom property bag of the web part. * * @readonly */ protected get properties(): TProperties; /** * This property defines metadata for the web part property bag. The metadata can help SharePoint understand * the content of the properties better and perform relevant services on the data. * virtual * * @remarks * See {@link IWebPartPropertiesMetadata} for more information about how to define metadata */ protected get propertiesMetadata(): IWebPartPropertiesMetadata | undefined; /** * This property is used to change the web part's property pane interaction from Reactive to NonReactive. * virtual * * @remarks * The default behavior is Reactive. * * Reactive implies that changes made in the PropertyPane are transmitted to the web part instantly and the user can * see instant updates. This helps the page creator get instant feedback and decide if they should keep the new * configuration changes or not. * * NonReactive implies that the configuration changes are transmitted to the web part only after "Apply" PropertyPane * button is clicked. */ protected get disableReactivePropertyChanges(): boolean; /** * This property points to the accessible title of web part made available to screen readers. The base implementation * returns that default title in the manifest. Web parts that want to provide more descriptive title containing * contextual information need to override this API. * virtual */ protected get accessibleTitle(): string; /** * Title of the WebPart * * @readonly */ protected get title(): string; /** * Icon image URL of the WebPart * * @internal */ protected get iconImageUrl(): string | undefined; /** * Description of the WebPart * * @readonly */ protected get description(): string; /** * List of user audiences that can see the WebPart * * @internal */ protected get audiences(): string[]; /** * Whether or not to show the web part in various view contexts (e.g., mobile, email, etc.). * * @internal */ protected get hideOn(): IHideOn; /** * This property is a pointer to the current set of properties which are already or needs to be persisted. * * @readonly */ private get _persistedProperties(); /** * Constructor for the `BaseWebPart` class. * * @remarks * It is highly recommended that the web part use the OnInit API to perform any web part specific initialization. * Most of the web part features like this.context and this.properties are not available to be used before the * the onInit part of the web part loading lifecycle. */ constructor(); /** * API to get property pane configuration asynchronously. * * @internal */ _getPropertyPaneData(isDetails?: boolean, context?: any): Promise<IPropertyPaneData>; /** * API to enable asynchronous loading of property pane related resources of a web part. * * @internal */ _loadPropertyPaneResources(): Promise<void>; /** * Internal API which is invoked when a property field is changed on the property pane. * This API is invoked only for the reactive property pane. * * @remarks * If the old value is of type DynamicProperty, but the new value is a static value * (This could happen when the 'targetProperty' is same for both a DynamicField on the * Dynamic Data Widget and a TextField which represents afore mentioned Dynamic Field * when the DD connection is removed by the end user on the property pane UI. In that * case, property pane sends in the static value which corresponds to the dynamic property(DP) * present before the DD connection is removed) * then to respect the web part developer's choice of that target property being a DP, * we create a new instance of DP and assign the incoming value as the static value * then update the properties bag with the newly created DP instance. * else, update the properties bag with the new value for the property. * * @param propertyPath - JSON path of the property in the property bag. * @param newValue - New value of the property. * * @returns if property was updated * @internal */ _onPropertyPaneFieldChanged(propertyPath: string, newValue: any, fieldType?: PropertyPaneFieldType): boolean; /** * Internal API which is invoked when one of the predefined configuration events * (defined in `PropertyPaneLifeCycleEvent` enum.) is triggered. * * See PropertyPaneLifeCycleEvent for more details on the event definitions. * * @param event - Type of PropertyPaneLifeCycleEvent. * * @internal */ _onPropertyPaneLifeCycleEvent(event: PropertyPaneLifeCycleEvent): void; /** * Indicates whether the property pane is reactive or not. * * @remarks * The default behavior is Reactive. * * Reactive implies that changes made in the PropertyPane are transmitted to the web part instantly and the user can * see instant updates. This helps the page creator get instant feedback and decide if they should keep the new * configuration changes or not. * * NonReactive implies that the configuration changes are transmitted to the web part only after "Apply" PropertyPane * button is clicked. * * @internal */ _isPropertyPaneReactive(): boolean; /** * Internal API to serialize the web part properties. * * @internal */ _internalSerialize<T>(): T | undefined; /** * Internal API to dispose the web part. * * See onDispose for more details. * * @internal */ _internalDispose(): void; /** * Internal API triggered upon a resize of the DOM window's viewport * * @internal */ _internalOnAfterResize(): void; /** * Internal API to update the web part data. * * !!!WARNING!!! updating the web part data can be risky. If you end up updating the web part properties * to an invalid format, this could cause the web part to persist invalid data format. * * @internal */ _internalSetWebPartData(webPartData: IWebPartData, addedFromPersistedData?: boolean): void; /** * Internal API to switch the web part's display mode. This API updates the display mode and then re-renders the web * part in the new mode. * @internal */ _internalSetDisplayMode(newDisplayMode: DisplayMode): void; /** * Internal API to set the dirty bit on the web part host if the web part properties have changed. * @internal */ _internalSetDirtyBit(): void; /** * This is the initial entry point, which is called by the `ClientSideWebPartManager` immediately * after the web part is constructed. The initialization contract is internal. * * @param webPartContext - The web part context * @param addedFromPersistedData - Tells the Web Part it will need to reinstate some properties * from server processed content (see BaseWebPart._reInstateServerProcessedData) * * @internal */ _internalInitialize(webPartContext: BaseWebPartContext, addedFromPersistedData: boolean, mode: DisplayMode): void; /** * Internal base implementation of the web part data deserialization. * * @param data - web part data * * @internal */ _internalDeserialize(data: IWebPartData): void; /** * Returns the configuration for this webpart's top actions or undefined if the webpart * doesn't support top actions. */ getTopActionsConfiguration(): ITopActions | undefined; /** * Returns the accessibility results for the web part. * * @internal */ _checkA11y(): Promise<(IA11yCheckResult | undefined)[]>; /** * This API is called when user clicks on the violations in the accessibility assistant panel. * virtual * * @remarks * This API should be overridden to perform any actions when a violation is clicked in the accessibility assistant panel. * Usually this API is used to redirect the user to the specific item editing configuration pane to fix the violation. * * @param result - The result(violation) data that was clicked. * * @internal */ _preOnClickA11yResult(result: IA11yCheckResult): void; /** * This event method is called when the web part is initialized. * virtual * * @remarks * This API should be overridden to perform long running operations e.g. data fetching from a remote service before * the initial rendering of the web part. The loading indicator is displayed during the lifetime of this method. * This API is called only once during the lifecycle of a web part. */ protected onInit(): Promise<void>; /** * This event method is called when the display mode of a web part is changed. * virtual * * @remarks * The default implementation of this API calls * the web part render method to re-render the web part with the new display mode. If a web part developer does not * want a full re-render to happen on display mode change, they can override this API and perform specific updates * to the web part DOM to switch its display mode. * * If the web part is initialized or re-initialized when switching to a different display mode then this * lifecycle method is not called. Example: SharePoint Site Page. * * @param oldDisplayMode - The old display mode. */ protected onDisplayModeChanged(oldDisplayMode: DisplayMode): void; /** * This event method is called before the web part is serialized. * virtual * * @remarks * The default implementation is a no-op. The serialization * process serializes the web part property bag i.e. this.properties. This API gives the web part a chance to * update it's property bag before the serialization happens. Some web part's may keep their state other objects * or even in the DOM. If a web part needs to persist some of that state, it needs to override this API and update * the web part property bag to the latest state. If a web part updates the property bag with invalid property * values, those will get persisted. So that should be avoided. The web part property bag should always contain * valid property values. */ protected onBeforeSerialize(): void; /** * This API is called after the web part is deserialized to an object, right before the property bag is populated. * virtual * * @remarks * The default implementation is a no-op. A web part developer can override this API if the deserialized object * does not fully reflect the initial state of the property bag. This gives the web part developer a chance to * populate the property bag right after the data is deserialized to an object. * * An important scenario to use deserialize is upgrading. An upgraded web part may load the data * that was serialized by an older version of the web part that supported a different schema of the property bag, * resulting the deserialized object to be inconsistent with the current schema of the property bag. The developer * can use `onAfterDeserialize` to check the dataVersion and fix the property bag. * * @param deserializedObject - The object deserialized from the stored data. Note that the schema of this object * is not necessarily consistent with the current property bag, because the serialization could have been done by * an older version of the web part * @param dataVersion - The data version of the stored data being deserialized. You can use this value to determine * if the data was serialized by an older web part. Web parts can define their data version by overriding the * dataVersion property. * * @returns The property bag of the web part */ protected onAfterDeserialize(deserializedObject: any, dataVersion: Version): TProperties; /** * This API enables asynchronous loading of property pane related resources of the web part. * @virtual * * @remarks * If not overridden by the web part, base web part resolves the promise immediately. * * This method is called before any property pane APIs are called. * Other property pane APIs cannot be called until this promise is resolved. * No other property pane related events are raised except 'PropertyPaneConfigurationComplete'. * * 'PropertyPaneConfigurationComplete' event can be raised to clean up any pending resources * including 'loadPropertyPaneResources' promise. */ protected loadPropertyPaneResources(): Promise<void>; /** * This API is used to ger the configuration to build the property pane for the web part. If the web part wants * to use the PropertyPane for configuration, this API needs to be overridden and the web part needs to return * the configuration for the PropertyPane. * * This API is not invoked until the 'loadPropertyPaneResources' promise is resolved. * * See IPropertyPane and other PropertyPane integration wiki documentation for more details. * virtual */ protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration; /** * This API is used to get the configuration to build the property pane for details. * If the web part wants to use the PropertyPane for details configuration, * this API needs to be overridden and the web part needs to return the configuration for details. * * This API is not invoked until the 'loadPropertyPaneResources' promise is resolved. * * See IPropertyPane and other PropertyPane integration wiki documentation for more details. * virtual * * @param context - additional context passed to getPropertyPaneDetailsConfiguration * * @alpha */ protected getDetailsPaneConfiguration(context?: any): IPropertyPaneConfiguration; /** * This API should be used to refresh the contents of the PropertyPane. * virtual * * @remarks * This API is called at the end of the web part lifecycle on a page. It should be used to dispose any local * resources (i.e. DOM elements) that the web part is holding onto. This API is expected to be called in scenarios * like page navigation i.e. the host is transitioning from one page to another and disposes the page that is being * transitioned out. */ protected onDispose(): void; /** * This API is invoked after updating the new value of the property in the property bag when the PropertyPane * is being used in Reactive mode. * virtual * * @param propertyPath - JSON path of the property in the property bag. * In the case of custom field, if no target property is provided then a custom value is assigned, * which will be in the form of `__CustomField_<key provided when the custom field is created>`. * @param oldValue - Old value of the property. * This value could be undefined/empty in the case of custom field. * @param newValue - New value of the property. * This value could be undefined/empty in the case of custom field. */ protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void; /** * This event method is invoked when the configuration starts on the PropertyPane. * virtual * * @remarks * This event method is invoked in the following cases: * * - When the PropertyPane is opened. * * - When the user switches web parts then the new web part gets this event. */ protected onPropertyPaneConfigurationStart(): void; /** * This API is invoked when the configuration is completed on the PropertyPane. * virtual * * @remarks * This event method is invoked in the following cases: * * - When the CONFIGURATION_COMPLETE_TIMEOUT((currently the value is 5 secs) elapses after the last change. * * - When user clicks the "X" (close) button before the CONFIGURATION_COMPLETE_TIMEOUT elapses. * * - When user clicks the 'Apply' button before the CONFIGURATION_COMPLETE_TIMEOUT elapses. * * - When the user switches web parts then the current web part gets this event. */ protected onPropertyPaneConfigurationComplete(): void; /** * This API is invoked after the changes made on the PropertyPane are applied when the PropertyPane is used in * Non-Reactive mode. This API is not invoked when the PropertyPane is used in Reactive mode. * virtual */ protected onAfterPropertyPaneChangesApplied(): void; /** * This API is invoked when the PropertyPane is rendered. * virtual * @privateRemarks * From framework standpoint, we do not want to allow this event handler to be passed in, and trigger it. * This api should be deprecated and then removed as part of refactoring. */ protected onPropertyPaneRendered(): void; /** * This API is invoked after properties are updated by sources other than the property pane or the web part host * (except for isolated web parts). * virtual * * @remarks For isolated web parts, this lifecycle is invoked instead of `onPropertyPaneFieldChanged`. * * @alpha * @param prevProperties - The web part properties before the update. */ protected onAfterPropertiesUpdatedExternally(prevProperties: TProperties): void; /** * @internal */ protected _internalGetData(): Promise<void>; /** * Internal API to trigger a refresh to the WebPart's visual rendition. * * @internal */ protected abstract _refresh(): void; /** * Internal API triggered by a dynamic property's callback. * * @internal */ protected abstract _dynamicPropertyRefresh(): void; /** * Gets default accessible title in the format `"<Web part name> web part"`, such as "Image web part". * Note: pulled into own method because TypeScript doesn't allow derived classes to call super protected properties. * * @internal */ protected _getDefaultAccessibleTitle(): string; /** * Allows for asynchronous updating of properties and setting of dirty bit * * @internal */ protected _onPropertyPaneChangesApplied(): Promise<TProperties>; /** * This method consumes the properties object from the serialized data and re-applies * the field values from the serverProcessedContent. This process will result in the * properties bag as it was when the serialization happened. */ private _reInstateServerProcessedData; /** * Iterates and validates each metadata property before calling metadata processor. * * @param metadataProcessor - Callback handler to to process validated metadata. * @param properties - Optional properties to process instead of default this.properties */ private _forEachPropertyWithMetaData; /** * Validates path and iterate over one or multiple wildcard paths * * @param propPath - Original property path provided in metadata * @param properties - Properties to look for the given path. * @param propertyPathHandler - Handler to process validated and fixed path */ private _validateAndIteratePath; /** * Operations to be done after the property bag is updated. * * @param shouldRefresh - Indicating whether web part needs to be shouldRefresh or not. */ private _afterPropertyUpdated; /** * Fixes up a property pane configuration to use dynamic data. This happens in-place. * The fix-up consists on adding the necessary hooks to the web part that are required, * like the dynamic data provider, the render function and the object it refers to. * * @param configuration - Property pane configuration to fix up. */ private _fixUpDynamicDataConfiguration; /** * Adds a new group with an audiences picker to the first page of the property pane configuration. * This happens in-place. * * @param configuration - Property pane configuration to add the audience targeting picker group to */ private _addAudienceTargetingConfiguration; /** * Adds "Visibility" group (including "Show in mobile and email" toggle) to property pane. * * @param configuration - Property pane configuration to add the group / toggle to. */ private _addVisibilityConfiguration; /** * The serializer for the exposed part of the web part data * * @privateRemarks * This serialize API is a public candidate for post-GA (after removing the input parameter) */ private _serialize; /** * This function serializes the Dynamic Data objects from the web part properties into the serialized web part data. * * If there is a property "myDynamicProperty" in the property bag that points to `"PageContext:user"`, * the serialized data is updated as the following: * ``` * serializedData = { * dynamicDataPaths: { * "myDynamicProperty": "PageContext:user" * } * } * ``` */ private _serializeDynamicData; /** * This function deserializes the Dynamic Data objects from serialized web part data into web part properties. * * If the serializedData has a "dynamicDataPaths" or "dynamicDataValues" property, * it creates DynamicData objects for it. * * Example: * ``` * serializedData = { * dynamicDataPaths: { * "myDynamicProperty": "PageContext:user" * } * } * ``` * * This will create a property "myDynamicProperty" in the web parts properties, with a new DynamicData object that * points at "PageContext:user" */ private _deserializeDynamicData; /** * For each property in the serialized properties of the web part, we check if the property is declared * as dynamicProperty in the properties metadata of the web part. * - If yes, then we check * - if the value of it is an instance of DynamicProperty * - If No, then we create and assign a DynamicProperty with the type given specified in the * 'dynamicPropertyType' property * - else, it's a no-op. * - If no, then it's a no-op * * While creating the DynamicProperty, we use the pre-configured value associated with that property from * the web part's property bag as the default value. * @param deSerializedProperties - deserialized properties of the web part. */ private _initializeDynamicPropertiesIfRequired; /** * Disposing the dynamic properties, for those which are declared as dynamic properties. * * @privateRemarks * We are disposing only the declared dynamic properties because we created them on behalf of * the web part and hence it is our responsiblity to clean them. */ private _disposeDynamicPropertiesIfRequired; /** * Update the web part property if it is in the property bag. * * @param propertyPath - property path as expected by lodash update (https://lodash.com/docs#update). * (e.g. 'o.a.b', 'o[0].a.b[1].c'). These paths are similar to those in the JSONPath spec described by * Stefan Goessner at (http://goessner.net/articles/JsonPath/). Currently we plan to use lodash.get, has * and update APIs which support paths. Some day we may migrate to using actual JSONPath.js. But that * should not be an issue because the path formatting conventions are same in lodash and JSONPath. * @param newValue - new value entered by the user for the associated target property */ private _updateProperty; /** * Using 'cloneDeepWith' because the web part properties bag will now serialize the * dynamic properties as well. One of the building blocks of these properties are dataProviders, * which when serialized will result in 'Maximum call stack size exceeded' based on what kind of * sources they hold references to. * Hence using cloneDeepWith, which lets us handle the special cases and in this case if the property * in question is a 'DynamicProperty' and we call '_cloneDeep' api on the dynamicProperty. */ private _cloneProperties; } //# sourceMappingURL=BaseWebPart.d.ts.map