@microsoft/sp-webpart-base
Version:
SharePoint Framework support for building web parts
1,038 lines (1,037 loc) • 70.2 kB
JavaScript
"use strict";
// Copyright (c) Microsoft. All rights reserved.
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var sp_component_base_1 = require("@microsoft/sp-component-base");
var sp_core_library_1 = require("@microsoft/sp-core-library");
var sp_diagnostics_1 = require("@microsoft/sp-diagnostics");
var lodash = tslib_1.__importStar(require("@microsoft/sp-lodash-subset"));
var sp_edit_customer_promise_1 = require("@msinternal/sp-edit-customer-promise");
var ExecuteAndReThrow_1 = require("../utils/ExecuteAndReThrow");
var Object_1 = require("../utils/Object");
var isAmplifyHostType_1 = require("../utils/isAmplifyHostType");
var SPPropertyPane_1 = require("../SPPropertyPane");
var SPWebPartError_1 = require("./error/SPWebPartError");
var AudienceFormServiceKey_1 = require("../services/AudienceForm/AudienceFormServiceKey");
var Flights_1 = require("../common/Flights");
var Strings_resx_1 = tslib_1.__importDefault(require("./loc/Strings.resx"));
var KillSwitches_1 = require("../common/KillSwitches");
var deserializationHelpers_1 = require("./deserializationHelpers");
// Used to link property pane fields with actual properties.
// Note: A GUID uniquely identifies BaseWebPart properties to prevent name conflicts with 1p/3p web part-level properties.
var AUDIENCES_PROPERTY = '4eae8b0b-40f6-4ea9-aabb-b6834ae24532';
// Note: While the web part property is named "hideOn", we specify the "mobile" key below because
// our property pane toggle sets the `hideOn.mobile` property (may differ from future uses of
// `hideOn`, e.g., another property pane toggle controlling another view context in `hideOn`).
// Note: This serves as the source of truth for other files that use this property GUID.
var HIDE_ON_MOBILE_PROPERTY = '9caa98f0-9050-43f7-be06-9bc2bdd5e833';
var ACE_WEBPART_MANIFEST_ID = 'f3bc67ee-015d-4678-a95f-c7e90ea62c1e';
// Note: This serves as the source of truth for other files that assign this default value.
var HIDE_ON_MOBILE_DEFAULT = false;
/**
* 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
*/
var BaseWebPart = /** @class */ (function (_super) {
tslib_1.__extends(BaseWebPart, _super);
/**
* 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.
*/
function BaseWebPart() {
var _this = _super.call(this) || this;
_this._initialized = false;
_this._baseLogSource = sp_diagnostics_1._LogSource.create('BaseWebPart');
/**
* It makes sure we only log edit events once during one editing session.
*/
_this._hasEditLogged = false;
_this._emptyResolvedPromise = Promise.resolve();
_this._disposeDynamicPropertiesIfRequired = _this._disposeDynamicPropertiesIfRequired.bind(_this);
_this._onPropertyPaneFieldChanged = _this._onPropertyPaneFieldChanged.bind(_this);
// Disallow instantiation of the base class by itself
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (_this.constructor.name === 'BaseWebPart') {
throw SPWebPartError_1.SPWebPartError.create(SPWebPartError_1.SPWebPartErrorCode.BaseConstructError);
}
return _this;
}
Object.defineProperty(BaseWebPart.prototype, "previewImageUrl", {
/**
* 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: function () {
return undefined;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "displayMode", {
/**
* This property is the current display mode of the web part.
*
* @readonly
*/
get: function () {
return this._displayMode;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "modifiedByGraph", {
/**
* This property holds metadata about Graph API calls that touch the web part.
* @internal
*/
get: function () {
return this._modifiedByGraph;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "dataVersion", {
/**
* 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
*/
get: function () {
return sp_core_library_1.Version.parse('1.0');
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "properties", {
/**
* This property is the pointer to the custom property bag of the web part.
*
* @readonly
*/
get: function () {
if (this._initialized) {
return this._properties;
}
else {
throw SPWebPartError_1.SPWebPartError.create(SPWebPartError_1.SPWebPartErrorCode.NotInitializedError);
}
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "propertiesMetadata", {
/**
* 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
*/
get: function () {
return undefined;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "disableReactivePropertyChanges", {
/**
* 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.
*/
get: function () {
return false;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "accessibleTitle", {
/**
* 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
*/
get: function () {
return this._getDefaultAccessibleTitle();
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "title", {
/**
* Title of the WebPart
*
* @readonly
*/
get: function () {
return this._title;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "iconImageUrl", {
/**
* Icon image URL of the WebPart
*
* @internal
*/
get: function () {
return this._iconImageUrl;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "description", {
/**
* Description of the WebPart
*
* @readonly
*/
get: function () {
return this._description;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "audiences", {
/**
* List of user audiences that can see the WebPart
*
* @internal
*/
get: function () {
return this._audiences;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "hideOn", {
/**
* Whether or not to show the web part in various view contexts (e.g., mobile, email, etc.).
*
* @internal
*/
get: function () {
return this._hideOn;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseWebPart.prototype, "_persistedProperties", {
/**
* This property is a pointer to the current set of properties which are already or needs to be persisted.
*
* @readonly
*/
get: function () {
return this.disableReactivePropertyChanges && this._backupProperties
? this._backupProperties
: this.properties;
},
enumerable: false,
configurable: true
});
/**
* API to get property pane configuration asynchronously.
*
* @internal
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BaseWebPart.prototype._getPropertyPaneData = function (isDetails, context) {
var _this = this;
return this._loadPropertyPaneResources().then(function () {
var configuration = isDetails
? _this.getDetailsPaneConfiguration(context)
: _this.getPropertyPaneConfiguration();
var shouldAddAudiencePicker = _this.context.manifest.id === ACE_WEBPART_MANIFEST_ID ||
((0, Flights_1.isWebPartAudienceTargetingEnabled)() && !configuration.disableAudiencePicker);
if (shouldAddAudiencePicker) {
_this._addAudienceTargetingConfiguration(configuration);
}
// TODO: Create a more generic method for injecting properties into propery pane (VSO:2573145).
// Perhaps implement an api that accepts and injects a list of property pane groups.
// Adds "Visibility" group (including "Show in mobile and email" toggle) to property pane.
// Note: Only for overall web part configuration, not individual items (e.g., links in quick links), hence the `shouldAdd` check.
var shouldAddVisibilityGroup = (0, Flights_1.isWebPartHideOnMobileEnabled)() &&
!configuration.disableVisibilityGroup &&
((0, KillSwitches_1.isDisableHideOnMobileInAmplifyKSActivated)() || !(0, isAmplifyHostType_1.isAmplifyHostType)(_this.context.host.hostType));
if (shouldAddVisibilityGroup) {
_this._addVisibilityConfiguration(configuration);
}
_this._fixUpDynamicDataConfiguration(configuration);
return {
webPartId: _this.context.instanceId,
title: _this.title,
isReactive: !_this.disableReactivePropertyChanges,
configuration: configuration,
properties: _this._cloneProperties(_this.properties),
// Below methods are being overridden in the PropertyPane controller and hence
// we do not need to bind it here. We still do not want to make it optional because
// that could lead to unwanted bugs.
onPropertyPaneFieldChanged: undefined,
onConfigurationEvent: undefined,
onRendered: _this.onPropertyPaneRendered,
dynamicConfiguration: {
defaultCallback: (function () {
_this._dynamicPropertyRefresh();
}).bind(_this),
dynamicDataProvider: _this.context.dynamicDataProvider
}
};
});
};
/**
* API to enable asynchronous loading of property pane related resources of a web part.
*
* @internal
*/
BaseWebPart.prototype._loadPropertyPaneResources = function () {
// Below promise will be undefined only when the web part is opening its property pane for the first time.
if (!this._loadPropertyPaneResourcesPromise) {
this._loadPropertyPaneResourcesPromise = this.loadPropertyPaneResources();
}
return this._loadPropertyPaneResourcesPromise;
};
/**
* 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
*/
BaseWebPart.prototype._onPropertyPaneFieldChanged = function (propertyPath,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
newValue, fieldType) {
var _this = this;
var _a, _b;
var monitor;
var componentType = this.context.manifest.id === ACE_WEBPART_MANIFEST_ID ? 'ACE' : 'WebPart';
try {
var oldValue = lodash.get(this._properties, propertyPath);
// Property updates and web part re-rendering happens only in the following cases:
// case 1: when there is a change between oldValue and newValue.
// case 2: when the event is coming from a button click(this is because, in this case
// regardless of what newValue is we need to re-render the web part.)
// case 3: when the event is coming from a custom field(this is because, framework wouldn't
// know what exactly changed inside the custom field and we come to this spot in case
// of custom field only when the web part specifically requests it.)
// case 4: when the event is coming from a SortableGroup
if (!lodash.isEqual(oldValue, newValue) ||
fieldType === SPPropertyPane_1.PropertyPaneFieldType.Button ||
fieldType === SPPropertyPane_1.PropertyPaneFieldType.Custom) {
if (!(0, KillSwitches_1.isPropertyPaneUpdateASHAVetoKSActivated)() && this.manifest.isInternal) {
// create a monitor only when an update is required.
monitor = new sp_diagnostics_1._QosMonitorShareFailureWithCP("PropertyPane.".concat(componentType, ".").concat(this.manifest.alias, ".Update"), false, [
{
errorModuleName: "".concat(componentType, " PropertyPaneUpdate Failure"),
veto: 'WebPart Property Pane Failure',
logFailure: this.context.serviceScope.consume(sp_edit_customer_promise_1._EditModeCustomerPromiseHandler.serviceKey)
.logFailure
}
]);
}
// Create backupProperties if it is non-reactive property pane.
if (this.disableReactivePropertyChanges && !this._backupProperties) {
this._backupProperties = this._cloneProperties(this.properties);
}
if (oldValue instanceof sp_component_base_1.DynamicProperty && !(newValue instanceof sp_component_base_1.DynamicProperty)) {
var newDynamicProperty = new sp_component_base_1.DynamicProperty(this.context.dynamicDataProvider, (function () {
_this._dynamicPropertyRefresh();
}).bind(this));
newDynamicProperty.setValue(newValue);
newValue = newDynamicProperty;
}
if (propertyPath === AUDIENCES_PROPERTY) {
this._audiences = newValue;
if ((0, Flights_1.isWebPartAudienceTargetingEnabled)()) {
sp_diagnostics_1._EngagementLogger.logEvent("".concat(componentType, ".PropertyPane"), 'AudienceUpdated');
(_b = (_a = this.context.host).onAudiencesChanged) === null || _b === void 0 ? void 0 : _b.call(_a, this.instanceId, newValue);
}
}
else if ((0, Flights_1.isWebPartHideOnMobileEnabled)() && propertyPath === HIDE_ON_MOBILE_PROPERTY) {
// Note: We invert `newValue` since the toggle is "show" (not "hide"). Reference `hideOn`
// comment in IWebPartData.ts for why we implemented the property as "hide".
this._hideOn.mobile = !newValue;
// Example: "WebPart.ShowInMobileAndEmail.Toggle", extra data includes web part alias, 1p or 3p, and new value.
// Note: We also log this event in SPRtePropertyPaneControl.tsx for RTE.
// Note: We include `isInternal` so separating out 1p and 3p usage data is easier.
// Note: We use "ShowInMobileAndEmail" to be more aligned with the toggle's label.
sp_diagnostics_1._EngagementLogger.logEventWithLogEntry(new sp_diagnostics_1._LogEntry('WebPart', 'ShowInMobileAndEmail.Toggle', sp_diagnostics_1._LogType.Event, {
webPart: this.context.manifest.alias,
isInternal: "".concat(!!this.context.manifest.isInternal),
showInMobileAndEmail: "".concat(!this._hideOn.mobile)
}));
}
else {
this._updateProperty(propertyPath, newValue);
}
this.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
this._afterPropertyUpdated(!this.disableReactivePropertyChanges);
if (!(0, KillSwitches_1.isPropertyPaneUpdateASHAVetoKSActivated)() && monitor) {
monitor.writeSuccess();
}
return true;
}
}
catch (error) {
if (!(0, KillSwitches_1.isPropertyPaneUpdateASHAVetoKSActivated)() && monitor) {
monitor.writeUnexpectedFailure(error.message);
}
throw error;
}
return false;
};
/**
* 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
*/
BaseWebPart.prototype._onPropertyPaneLifeCycleEvent = function (event) {
var _this = this;
// Invoke the callback so that the host can handle the property pane life cycle event.
if (this.context.host.propertyPaneLifeCycleEventCallback) {
this.context.host.propertyPaneLifeCycleEventCallback(event, {
webPartData: this._internalSerialize(),
isPropertyPaneReactive: this._isPropertyPaneReactive()
});
}
switch (event) {
case 'ConfigurationStart':
this._loadPropertyPaneResources()
.then(function () { return _this.onPropertyPaneConfigurationStart(); })
.catch(function (e) {
return sp_diagnostics_1._TraceLogger.logVerboseData({ source: _this._baseLogSource, message: e.message });
});
break;
case 'ConfigurationComplete':
// Make sure we discard any backed up properties in the non-reactive mode.
if (this.disableReactivePropertyChanges && this._backupProperties) {
// resetting the properties bag.
this._properties = this._backupProperties;
this._backupProperties = undefined;
}
this.onPropertyPaneConfigurationComplete();
break;
case 'ApplyClicked':
// Updating the _backUpProperties with new set of properties.
this._backupProperties = this.properties;
this._onPropertyPaneChangesApplied()
.then(function () {
_this.onAfterPropertyPaneChangesApplied();
_this._afterPropertyUpdated(true);
})
.catch(function (err) {
sp_diagnostics_1._TraceLogger.logVerboseData({ source: _this._baseLogSource, message: err.message });
throw err;
});
break;
}
};
/**
* 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
*/
BaseWebPart.prototype._isPropertyPaneReactive = function () {
return !this.disableReactivePropertyChanges;
};
/**
* Internal API to serialize the web part properties.
*
* @internal
*/
BaseWebPart.prototype._internalSerialize = function () {
var _this = this;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var data;
(0, ExecuteAndReThrow_1.executeAndReThrow)(function () {
_this.onBeforeSerialize();
var propertiesJson = JSON.stringify(_this.properties);
// Only log edit, if properties have changed and it haven't been logged.
if (!_this._hasEditLogged && _this._initPropertiesSnapshot !== propertiesJson) {
var isInternal = !!_this.context.manifest.isInternal;
var logEntry = new sp_diagnostics_1._LogEntry(_this._baseLogSource.id, 'WebPartEdited', sp_diagnostics_1._LogType.Event, {
alias: _this.context.manifest.alias,
isInternal: isInternal.toString()
});
sp_diagnostics_1._EngagementLogger.logEventWithLogEntry(logEntry);
_this._hasEditLogged = true;
}
var serializedData = _this._serialize();
sp_core_library_1.Validate.isNotNullOrUndefined(serializedData.dataVersion, 'serialized data version');
// Avoid doing extra work if we know there is no dynamic data
if (propertiesJson && propertiesJson.indexOf(sp_component_base_1.DynamicProperty._TYPE_NAME) !== -1) {
_this._serializeDynamicData(serializedData);
}
data = tslib_1.__assign(tslib_1.__assign({
// The serialized data wrapped by the framework
id: _this.context.manifest.id, instanceId: _this.context.instanceId, title: _this.title, description: _this.description, audiences: _this.audiences }, ((0, Flights_1.isWebPartHideOnMobileEnabled)() && {
hideOn: _this.hideOn
})), {
// The serialized data provided by the web part
serverProcessedContent: serializedData.serverProcessedContent, dynamicDataPaths: serializedData.dynamicDataPaths, dynamicDataValues: serializedData.dynamicDataValues, dataVersion: serializedData.dataVersion.toString(), properties: serializedData.properties });
if (!(0, KillSwitches_1.isExemptWebPartWithDynamicDataFromLazyLoadKSActivated)()) {
data.containsDynamicDataSource = _this._containsDynamicDataSource;
}
}, SPWebPartError_1.SPWebPartError.create(SPWebPartError_1.SPWebPartErrorCode.SerializationFailed, this.context.webPartTag), this._baseLogSource);
return data;
};
/**
* Internal API to dispose the web part.
*
* See onDispose for more details.
*
* @internal
*/
BaseWebPart.prototype._internalDispose = function () {
this._disposeDynamicPropertiesIfRequired();
_super.prototype.dispose.call(this);
};
/**
* Internal API triggered upon a resize of the DOM window's viewport
*
* @internal
*/
BaseWebPart.prototype._internalOnAfterResize = function () {
/* EMPTY BLOCK */
};
/**
* 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
*/
BaseWebPart.prototype._internalSetWebPartData = function (webPartData, addedFromPersistedData) {
var oldProperties = this.properties;
// Clear _previousState to avoid the new webPartData set by host being tracked as a dirty change.
this._previousState = undefined;
if (addedFromPersistedData !== undefined) {
this._renderedFromPersistedData = addedFromPersistedData;
}
this._internalDeserialize(webPartData);
this.onAfterPropertiesUpdatedExternally(oldProperties);
};
/**
* 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
*/
BaseWebPart.prototype._internalSetDisplayMode = function (newDisplayMode) {
if (this._displayMode !== newDisplayMode) {
var oldDisplayMode = this._displayMode;
this._displayMode = newDisplayMode;
this.onDisplayModeChanged(oldDisplayMode);
}
};
/**
* Internal API to set the dirty bit on the web part host if the web part properties have changed.
* @internal
*/
BaseWebPart.prototype._internalSetDirtyBit = function () {
// This method could get invoked in classic pages in read mode also. Just ignore the call.
if (this.displayMode === sp_core_library_1.DisplayMode.Read) {
return;
}
var serializedState = this._internalSerialize();
var newState = JSON.stringify(serializedState);
// Don't set dirty bit the first time because the Canvas
// would have done it when the webpart got added.
if (!this._previousState) {
this._previousState = newState;
}
else if (this._previousState !== newState && this.context.host.setDirty) {
this.context.host.setDirty(this.context.instanceId, serializedState);
this._previousState = newState;
}
};
/**
* 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
*/
BaseWebPart.prototype._internalInitialize = function (webPartContext, addedFromPersistedData, mode) {
sp_core_library_1.Validate.isNotNullOrUndefined(webPartContext, 'webPartContext');
(0, Object_1.deepFreeze)(webPartContext.manifest);
_super.prototype._initializeContext.call(this, webPartContext);
// Bind the callbacks
this.onDispose = this.onDispose.bind(this);
this.onPropertyPaneRendered = this.onPropertyPaneRendered.bind(this);
this._initialized = true;
// Set the display mode of the web part
this._displayMode = mode;
// This is important for reinstating properties the way they were
// before serialization. It must be set before any deserialization occurs.
this._renderedFromPersistedData = addedFromPersistedData;
if (!this._renderedFromPersistedData) {
var isInternal = this.context.manifest.isInternal || false;
var logEntry = new sp_diagnostics_1._LogEntry(this._baseLogSource.id, 'WebPartAdded', sp_diagnostics_1._LogType.Event, {
alias: this.context.manifest.alias,
isInternal: isInternal.toString()
});
sp_diagnostics_1._EngagementLogger.logEventWithLogEntry(logEntry);
}
};
/**
* Internal base implementation of the web part data deserialization.
*
* @param data - web part data
*
* @internal
*/
BaseWebPart.prototype._internalDeserialize = function (data) {
data = lodash.cloneDeep(data);
if (data) {
if (!this._previousState) {
this._previousState = JSON.stringify(data);
}
if (data.title) {
this._title = data.title;
}
if (data.modifiedByGraph) {
this._modifiedByGraph = data.modifiedByGraph;
}
if (data.description) {
this._description = data.description;
}
if (data.iconImageUrl) {
this._iconImageUrl = data.iconImageUrl;
}
this._audiences = data.audiences || [];
// Note: For web part-specific default values, must define in web part's `onAfterDeserialize()`
// and property getter in SkipControlRenderHelpers.ts (reference function comment).
this._hideOn = data.hideOn || { mobile: HIDE_ON_MOBILE_DEFAULT };
// For backward-compatibility, convert non-string versions to '1.0'
// Because we used to serialize the version object in the early versions
if (typeof data.dataVersion !== 'string') {
data.dataVersion = this.dataVersion.toString();
}
// Note: it is okay not to create clones here
var deserializedData = {
properties: data.properties,
serverProcessedContent: data.serverProcessedContent,
dynamicDataPaths: data.dynamicDataPaths,
dynamicDataValues: data.dynamicDataValues,
dataVersion: sp_core_library_1.Version.tryParse(data.dataVersion)
};
var shouldProcessDynamicDataAfterServerScrub = (0, Flights_1.isServerPropertiesSanitizedEnabled)();
var deserializedPropsObject = void 0;
if (shouldProcessDynamicDataAfterServerScrub) {
deserializedPropsObject = !(0, KillSwitches_1.isRefactorReInstateServerProcessedDataKSActive)()
? (0, deserializationHelpers_1.reInstateServerProcessedData)({
deserializedProperties: deserializedData.properties,
serverProcessedContent: deserializedData.serverProcessedContent,
renderedFromPersistedData: this._renderedFromPersistedData,
propertiesMetadata: this.propertiesMetadata,
manifest: this.context.manifest
})
: this._reInstateServerProcessedData(deserializedData.properties, deserializedData.serverProcessedContent);
}
if (!(0, KillSwitches_1.isExemptWebPartWithDynamicDataFromLazyLoadKSActivated)()) {
deserializedData.containsDynamicDataSource = data.containsDynamicDataSource;
this._containsDynamicDataSource = !!deserializedData.containsDynamicDataSource;
}
this._deserializeDynamicData(deserializedData);
// Note: Initialization of DynamicProperties should always happen after the
// properties are deserialized. This ensures that all the properties have proper
// values constructed with types, if required.
this._initializeDynamicPropertiesIfRequired(deserializedData.properties);
if (!shouldProcessDynamicDataAfterServerScrub) {
deserializedPropsObject = !(0, KillSwitches_1.isRefactorReInstateServerProcessedDataKSActive)()
? (0, deserializationHelpers_1.reInstateServerProcessedData)({
deserializedProperties: deserializedData.properties,
serverProcessedContent: deserializedData.serverProcessedContent,
renderedFromPersistedData: this._renderedFromPersistedData,
propertiesMetadata: this.propertiesMetadata,
manifest: this.context.manifest
})
: this._reInstateServerProcessedData(deserializedData.properties, deserializedData.serverProcessedContent);
}
// Give the web part an opportunity to deserialize the properties. If the web part
// returns a valid property bag, use it as is else perform default deserialization.
var fixedProps = this.onAfterDeserialize(deserializedPropsObject, deserializedData.dataVersion);
// Cache the properties data just deserialized.
this._initPropertiesSnapshot = JSON.stringify(deserializedPropsObject);
if (!fixedProps) {
throw SPWebPartError_1.SPWebPartError.create(SPWebPartError_1.SPWebPartErrorCode.OnAfterDeserializeReturnedNull, this.context.webPartTag);
}
this._properties = fixedProps;
}
};
/**
* Returns the configuration for this webpart's top actions or undefined if the webpart
* doesn't support top actions.
*/
BaseWebPart.prototype.getTopActionsConfiguration = function () {
return undefined;
};
/**
* Returns the accessibility results for the web part.
*
* @internal
*/
BaseWebPart.prototype._checkA11y = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, Promise.resolve([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
*/
BaseWebPart.prototype._preOnClickA11yResult = function (result) {
return undefined;
};
/**
* 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.
*/
BaseWebPart.prototype.onInit = function () {
// Return a resolved promise by default
return Promise.resolve(undefined);
};
/**
* 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.
*/
BaseWebPart.prototype.onDisplayModeChanged = function (oldDisplayMode) {
/* EMPTY BLOCK */
};
/**
* 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.
*/
BaseWebPart.prototype.onBeforeSerialize = function () {
/* EMPTY BLOCK */
};
/**
* 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
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BaseWebPart.prototype.onAfterDeserialize = function (deserializedObject, dataVersion) {
return deserializedObject;
};
/**
* 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.
*/
BaseWebPart.prototype.loadPropertyPaneResources = function () {
return this._emptyResolvedPromise;
};
/**
* 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
*/
BaseWebPart.prototype.getPropertyPaneConfiguration = function () {
return {
pages: []
};
};
/**
* 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
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BaseWebPart.prototype.getDetailsPaneConfiguration = function (context) {
return {
pages: []
};
};
/**
* 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.
*/
BaseWebPart.prototype.onDispose = function () {
/* EMPTY BLOCK */
};
/**
* 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.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BaseWebPart.prototype.onPropertyPaneFieldChanged = function (propertyPath, oldValue, newValue) {
/* EMPTY BLOCK */
};
/**
* 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.
*/
BaseWebPart.prototype.onPropertyPaneConfigurationStart = function () {
/* EMPTY BLOCK */
};
/**
* 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.
*/
BaseWebPart.prototype.onPropertyPaneConfigurationComplete = function () {
/* EMPTY BLOCK */
};
/**
* 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
*/
BaseWebPart.prototype.onAfterPropertyPaneChangesApplied = function () {
/* EMPTY BLOCK */
};
/**
* 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.
*/
BaseWebPart.prototype.onPropertyPaneRendered = function () {
/* EMPTY BLOCK */
};
/**
* 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.
*/
BaseWebPart.prototype.onAfterPropertiesUpdatedExternally = function (prevProperties) {
return this._refresh();
};
/**
* @internal
*/
BaseWebPart.prototype._internalGetData = function () {
return this._emptyResolvedPromise;
};
/**
* 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
*/
BaseWebPart.prototype._getDefaultAccessibleTitle = function () {
return sp_core_library_1.Text.format(Strings_resx_1.default.GenericAccessibleLabelTemplate, this.title);
};
/**
* Allows for asynchronous updating of properties and setting of dirty bit
*
* @internal
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BaseWebPart.prototype._onPropertyPaneChangesApplied = function () {
return Promise.resolve(this._properties);
};
/**
* 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.
*/
BaseWebPart.prototype._reInstateServerProcessedData = function (deserializedProperties, serverProcessedContent) {
var metadata = this.propertiesMetadata;
if (!deserializedProperties) {
return {};
}
var fixedProperties = deserializedProperties;
var shouldSkip = !(0, Flights_1.isServerPropertiesSanitizedEnabled)()
? !serverProcessedContent && !this._renderedFromPersistedData
: !this._renderedFromPersistedData;
if (shouldSkip) {
return fixedProperties;
}
sp_component_base_1._PropertyMetadataProcessor.reInstateServerProcessedData(metadata, fixedProperties, serverProcessedContent, this.context.manifest);
return fixedProperties;
};
/**
* 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
*/
BaseWebPart.prototype._forEachPropertyWithMetaData = function (metadataProcessor, properties) {
if (this.propertiesMetadata) {
var _loop_1 = function (propPath, metadata) {
this_1._validateAndIteratePath(propPath, properties || this_1.properties, function (fixedPropPath, index) {
metadataProcessor(fixedPropPath, metadata, index);
});
};
var this_1 = this;
for (var _i = 0, _a = Object.entries(this.propertiesMetadata); _i < _a.length; _i++) {
var _b = _a[_i], propPath = _b[0], metadata = _b[1];
_loop_1(propPath, metadata);
}
}
};
/**
* Validates path and iterate over one or multiple wildcard paths