UNPKG

@microsoft/sp-webpart-base

Version:

SharePoint Framework support for building web parts

299 lines 15.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var sp_component_base_1 = require("@microsoft/sp-component-base"); var sp_telemetry_1 = require("@ms/sp-telemetry"); var sp_core_library_1 = require("@microsoft/sp-core-library"); var Strings_resx_1 = tslib_1.__importDefault(require("./loc/Strings.resx")); var ErrorMessage_1 = tslib_1.__importDefault(require("./ErrorMessage")); var FriendlyErrorMessage_1 = tslib_1.__importDefault(require("./FriendlyErrorMessage")); var ClassicPageUtils_1 = tslib_1.__importDefault(require("./classicPages/ClassicPageUtils")); var SpinnerFactory_1 = tslib_1.__importDefault(require("./SpinnerFactory")); var KillSwitches_1 = require("../common/KillSwitches"); /** * This class provides the default implementation for displaying loading indicator and error messages * for web parts. The web part host can decide to provide custom implementation of how web parts display * loading indicators and error messages. * * @internal */ var ClientSideWebPartStatusRenderer = /** @class */ (function () { function ClientSideWebPartStatusRenderer() { this._errorId = 'cswp-error'; this._activeIndicatorCache = new Map(); } /** * Returns additional time out before showing the spinner * * loadingDelayed - Time when asked Viewport loader to check and load web part post module load. * inViewportLoaded - Time when web part was allowed to load by viewport loader. * * 1. If both loadingDelayed and inViewportLoaded are defined return the ViewportWait, * difference between them would reflect total time in the waiting queue * (i.e, inViewportLoaded - loadingDelayed). * 2. If only loadingDelayed is defined (i.e, the rendering of the webpart is still in the queue) * return a timeout of 500ms. * 3. If both loadingDelayed and inViewportLoaded are undefined return 0 (i.e, let the things be as they are). * * @param webPartTag - event identifier used in written telemetry data for first party web * parts, e.g., 'WebPart.NewsWebPart.8dd9dec2-c6b3-4d4a-819e-2a5431e901f2'. */ ClientSideWebPartStatusRenderer._getAdditionalTimeOut = function (webPartTag) { var loadingDelayed = sp_telemetry_1._PerformanceLogger.readComponentBreakdown(webPartTag, 'loadingDelayed'); var inViewportLoaded = sp_telemetry_1._PerformanceLogger.readComponentBreakdown(webPartTag, 'inViewPortLoading'); var timeout = 0; if (loadingDelayed && inViewportLoaded) { timeout = inViewportLoaded - loadingDelayed; } else if (loadingDelayed) { timeout = 500; } else { timeout = 0; } return timeout; }; /** * Display a loading spinner. * * @param domElement - the web part container div. * @param loadingMessage - the message to be displayed when the loading spinner id displayed. * @param timeout - (optional) timeout to render the loading indicator. Default is 1500ms. * * @privateRemarks * First-party web parts should use _displayLoadingIndicator to log perf data. */ ClientSideWebPartStatusRenderer.prototype.displayLoadingIndicator = function (domElement, loadingMessage, timeout, clearDomElementCallback) { this._logEngagement(domElement, 'displayLoadingIndicator'); this._createLoadingIndicator(domElement, loadingMessage, timeout, undefined, undefined, undefined, clearDomElementCallback); }; /** * Display a loading indicator. * * @param domElement - the web part container div. * @param loadingMessage - the message to be displayed when the loading indicator id displayed. * @param performanceLogEventName - event identifier used in written telemetry data for first party web * parts, e.g., 'WebPart.NewsWebPart.8dd9dec2-c6b3-4d4a-819e-2a5431e901f2'. * @param reservedHeight - reserved height * @param isInternal - flag to indicate if web part is internal or external. * @param timeout - (optional) timeout to render the loading indicator. Default is 1500ms. * * @internal */ ClientSideWebPartStatusRenderer.prototype._displayLoadingIndicator = function (domElement, loadingMessage, performanceLogEventName, reservedHeight, isInternal, timeout, clearDomElementCallback) { this._createLoadingIndicator(domElement, loadingMessage, reservedHeight, timeout, performanceLogEventName, isInternal, clearDomElementCallback); }; /** * Clear the loading indicator. * * @param domElement - the web part container div. */ ClientSideWebPartStatusRenderer.prototype.clearLoadingIndicator = function (domElement) { sp_core_library_1.Validate.isNotNullOrUndefined(domElement, 'domElement'); if (this._activeIndicatorCache.has(domElement)) { var cacheEntry = this._getCacheEntry(domElement); if (cacheEntry.loadingTimer) { window.clearTimeout(cacheEntry.loadingTimer); } if (cacheEntry.placeholder) { // This is needed because some web parts (e.g. Yammer web part) render within the onInit() phase if (cacheEntry.placeholder.parentElement) { cacheEntry.placeholder.parentElement.removeChild(cacheEntry.placeholder); } } this._activeIndicatorCache.delete(domElement); } }; ClientSideWebPartStatusRenderer.prototype.renderError = function (domElement, error, options) { if (typeof options === 'function') { options = { clearDomElementCallback: options }; } this._renderError(domElement, error, options); }; /** * Clear the web part error message. * @param domElement - the web part container div. */ ClientSideWebPartStatusRenderer.prototype.clearError = function (domElement) { sp_core_library_1.Validate.isNotNullOrUndefined(domElement, 'domElement'); if (!this._activeIndicatorCache.has(domElement)) { return; } var cacheEntry = this._activeIndicatorCache.get(domElement); if (cacheEntry && cacheEntry.isErrorBeingRendered) { cacheEntry.isErrorBeingRendered = false; var divErr = domElement.querySelector("div[data-sp-id='".concat(this._errorId, "']")); if (divErr) { divErr.style.display = 'none'; divErr.removeAttribute('data-automation-id'); } } }; ClientSideWebPartStatusRenderer.prototype._createLoadingIndicator = function (domElement, loadingMessage, reservedHeight, timeout, performanceLogEventName, isInternal, clearDomElementCallback) { var _this = this; sp_core_library_1.Validate.isNotNullOrUndefined(domElement, 'domElement'); if (!timeout || (timeout && isNaN(timeout))) { timeout = 1500; // milliseconds } // In most cases, we do not want to display the loading indicator immediately. We want to delay the // display of loading indicator to the point when the user will start noticing the slowness in the UI. var cacheEntry = this._getCacheEntry(domElement); // Clear if any earlier loading timer if (cacheEntry.loadingTimer) { window.clearTimeout(cacheEntry.loadingTimer); } cacheEntry.loadingTimer = window.setTimeout(function () { if (performanceLogEventName && isInternal) { var additionalTimeOut = ClientSideWebPartStatusRenderer._getAdditionalTimeOut(performanceLogEventName); cacheEntry.loadingTimer = window.setTimeout(function () { _this._showLoadingIndicator(domElement, loadingMessage, reservedHeight, performanceLogEventName, isInternal, clearDomElementCallback); }, additionalTimeOut); } else { _this._showLoadingIndicator(domElement, loadingMessage, reservedHeight, performanceLogEventName, isInternal, clearDomElementCallback); } }, timeout); }; ClientSideWebPartStatusRenderer.prototype._renderError = function (domElement, error, options) { var _a = options || {}, message = _a.message, clearDomElementCallback = _a.clearDomElementCallback; sp_core_library_1.Validate.isNotNullOrUndefined(domElement, 'domElement'); sp_core_library_1.Validate.isNotNullOrUndefined(error, 'error'); this._logEngagement(domElement, 'renderError'); var errorText = ''; var newLineSeparator = '\r\n'; var extraMessage = message ? newLineSeparator + newLineSeparator + message : ''; if (error instanceof sp_core_library_1.SPError) { errorText = error.toStringForUI(); if (!(0, KillSwitches_1.isGraphMetadataPropertyKSActivated)()) { errorText += extraMessage; } } else { var vanillaError = error; var stack = vanillaError.stack; var callStack = stack ? sp_core_library_1.Text.format(Strings_resx_1.default.WebpartErrorCallStackText, newLineSeparator, stack) : ''; if ((0, KillSwitches_1.isGraphMetadataPropertyKSActivated)()) { errorText = sp_core_library_1.Text.format(Strings_resx_1.default.WebpartErrorErrorText, newLineSeparator, "".concat(vanillaError.message || error), callStack, ''); } else { errorText = sp_core_library_1.Text.format(Strings_resx_1.default.WebpartErrorErrorText, newLineSeparator, "".concat(vanillaError.message || error), callStack, extraMessage); } } var cacheEntry = this._getCacheEntry(domElement); cacheEntry.isErrorBeingRendered = true; var errorComponent; if (!DEBUG) { errorComponent = new FriendlyErrorMessage_1.default({ errorMessage: errorText }).render(); } else { errorComponent = new ErrorMessage_1.default({ errorMessage: errorText }).render(); } var divErr = domElement.querySelector("div[data-sp-id='".concat(this._errorId, "']")); if (divErr) { divErr.style.display = 'block'; } else { divErr = document.createElement('div'); divErr.setAttribute('data-sp-id', this._errorId); if (clearDomElementCallback) { clearDomElementCallback(domElement); } this._clearChildren(domElement); domElement.appendChild(divErr); ClassicPageUtils_1.default.disableAutomaticPostbacks(domElement, sp_core_library_1.Environment.type); } // clearError removes the data-automation-id divErr.setAttribute('data-automation-id', 'webPartError'); divErr.innerHTML = ''; divErr.appendChild(errorComponent); }; /** * Show the loading indicator * * @param domElement - the web part container div. * @param loadingMessage - the message to be displayed when the loading Indicator id displayed. * @param reservedHeight - the height of the loading indicator. * @param performanceLogEventName - event identifier used in written telemetry data for first party web * parts, e.g., 'WebPart.NewsWebPart.8dd9dec2-c6b3-4d4a-819e-2a5431e901f2'. */ ClientSideWebPartStatusRenderer.prototype._showLoadingIndicator = function (domElement, loadingMessage, reservedHeight, performanceLogEventName, isInternal, clearDomElementCallback) { if (performanceLogEventName && isInternal) { sp_telemetry_1._PerformanceLogger.markComponent(performanceLogEventName, 'displaySpinner'); } // This would set style of the loading Indicator and display it. this._renderLoadingIndicator(domElement, loadingMessage, reservedHeight, performanceLogEventName, clearDomElementCallback); }; ClientSideWebPartStatusRenderer.prototype._renderLoadingIndicator = function (domElement, loadingMessage, reservedHeight, performanceLogEventName, clearDomElementCallback) { if (!this._activeIndicatorCache.has(domElement)) { return; } var cacheEntry = this._getCacheEntry(domElement); // Error is being rendered, don't render loading indicator if (cacheEntry.isErrorBeingRendered) { return; } cacheEntry.placeholder = this._createLoadingIndicatorElement(domElement, loadingMessage, reservedHeight, performanceLogEventName, clearDomElementCallback); }; ClientSideWebPartStatusRenderer.prototype._getCacheEntry = function (domElement) { if (this._activeIndicatorCache.has(domElement)) { return this._activeIndicatorCache.get(domElement); } var cacheEntry = { loadingTimer: undefined, placeholder: undefined, isErrorBeingRendered: false }; this._activeIndicatorCache.set(domElement, cacheEntry); return cacheEntry; }; ClientSideWebPartStatusRenderer.prototype._createLoadingIndicatorElement = function (domElement, loadingMessage, reservedHeight, performanceLogEventName, clearDomElementCallback) { if (clearDomElementCallback) { clearDomElementCallback(domElement); } this._clearChildren(domElement); var titleMessage = sp_core_library_1.Text.format(Strings_resx_1.default.LoadingStatus, loadingMessage); var loadingIndicatorContainerDiv = performanceLogEventName ? sp_component_base_1._ShimmerFactory.createShimmer(domElement.clientWidth, reservedHeight, performanceLogEventName, titleMessage) : this._createSpinnerElement(titleMessage, reservedHeight); loadingIndicatorContainerDiv.style.display = 'block'; return domElement.appendChild(loadingIndicatorContainerDiv); }; ClientSideWebPartStatusRenderer.prototype._createSpinnerElement = function (titleMessage, reservedHeight) { var spinnerElement = SpinnerFactory_1.default.createSpinner(titleMessage); if (reservedHeight !== undefined) { var parentElement = document.createElement('div'); parentElement.style.maxHeight = "".concat(reservedHeight, "px"); parentElement.appendChild(spinnerElement); spinnerElement = parentElement; } return spinnerElement; }; ClientSideWebPartStatusRenderer.prototype._clearChildren = function (element) { // We need to ensure that there are no child nodes. This works for all browsers. while (element.hasChildNodes()) { if (element.lastChild) { element.removeChild(element.lastChild); } } }; ClientSideWebPartStatusRenderer.prototype._logEngagement = function (domElement, logFeature) { var isWebPartRoot = false; var spWebPartId = domElement.getAttribute('data-sp-web-part-id') || ''; if (spWebPartId) { isWebPartRoot = true; } sp_telemetry_1._EngagementLogger.log({ name: "ClientSideWebPartStatusRenderer.".concat(logFeature), isIntentional: true, extraData: { spWebPartId: spWebPartId, isWebPartRoot: isWebPartRoot } }); }; return ClientSideWebPartStatusRenderer; }()); exports.default = ClientSideWebPartStatusRenderer; //# sourceMappingURL=ClientSideWebPartStatusRenderer.js.map