@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
341 lines • 17.6 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
// (C) 2007-2020 GoodData Corporation
var React = require("react");
var noop = require("lodash/noop");
var get = require("lodash/get");
var isEqual = require("lodash/isEqual");
var omit = require("lodash/omit");
var flatten = require("lodash/flatten");
var uniqBy = require("lodash/uniqBy");
var gooddata_js_1 = require("@gooddata/gooddata-js");
var typings_1 = require("@gooddata/typings");
var errorStates_1 = require("../../../constants/errorStates");
var errorHandlers_1 = require("../../../helpers/errorHandlers");
var react_intl_1 = require("react-intl");
var IntlWrapper_1 = require("../../core/base/IntlWrapper");
var LoadingComponent_1 = require("../../simple/LoadingComponent");
var ErrorComponent_1 = require("../../simple/ErrorComponent");
var RuntimeError_1 = require("../../../errors/RuntimeError");
var utils_1 = require("../../../helpers/utils");
var fixEmptyHeaderItems_1 = require("./utils/fixEmptyHeaderItems");
var escapeFileName = function (str) { return str && str.replace(/[\/\?<>\\:\*\|":]/g, ""); };
var defaultErrorHandler = function (error) {
// if error was not placed in object, we couldnt see its properties in console (ie cause, responseText etc.)
console.error("Error in execution:", { error: error }); // tslint:disable-line no-console
};
exports.commonDefaultProps = {
resultSpec: undefined,
onError: defaultErrorHandler,
onLoadingChanged: noop,
ErrorComponent: ErrorComponent_1.ErrorComponent,
LoadingComponent: LoadingComponent_1.LoadingComponent,
afterRender: noop,
pushData: noop,
locale: "en-US",
drillableItems: [],
onExportReady: noop,
onFiredDrillEvent: function () { return true; },
};
function visualizationLoadingHOC(InnerComponent, autoExecuteDataSource) {
if (autoExecuteDataSource === void 0) { autoExecuteDataSource = true; }
var LoadingHOCWrapped = /** @class */ (function (_super) {
__extends(LoadingHOCWrapped, _super);
function LoadingHOCWrapped(props) {
var _this = _super.call(this, props) || this;
_this.removePagePromise = function (promise) {
var promiseIndex = _this.pagePromises.indexOf(promise);
if (promiseIndex > -1) {
_this.pagePromises = _this.pagePromises
.slice(0, promiseIndex)
.concat(_this.pagePromises.slice(promiseIndex + 1));
}
};
_this.state = {
isLoading: false,
result: null,
error: null,
};
_this.sdk = props.sdk ? props.sdk.clone() : gooddata_js_1.factory();
utils_1.setTelemetryHeaders(_this.sdk, "LoadingHOCWrapped", props);
_this.pagePromises = [];
_this.hasUnmounted = false;
_this.onLoadingChanged = _this.onLoadingChanged.bind(_this);
_this.onDataTooLarge = _this.onDataTooLarge.bind(_this);
_this.onNegativeValues = _this.onNegativeValues.bind(_this);
_this.getPage = _this.getPage.bind(_this);
_this.cancelPagePromises = _this.cancelPagePromises.bind(_this);
_this.createExportFunction = _this.createExportFunction.bind(_this);
_this.initSubject();
return _this;
}
LoadingHOCWrapped.prototype.componentDidMount = function () {
var _a = this.props, dataSource = _a.dataSource, resultSpec = _a.resultSpec;
if (autoExecuteDataSource) {
this.initDataLoading(dataSource, resultSpec);
}
else {
// without this, the ReportVisualization component doesn't assign executionId
// and doesn't match results to executions and never stops loading
this.onLoadingChanged({ isLoading: true });
}
};
LoadingHOCWrapped.prototype.render = function () {
var _a = this.state, result = _a.result, isLoading = _a.isLoading, error = _a.error;
var intl = this.props.intl;
var getPageProperty = autoExecuteDataSource
? {}
: {
getPage: this.getPage,
cancelPagePromises: this.cancelPagePromises,
};
// lower-level components do not need projectId
var props = omit(this.props, ["projectId"]);
return (React.createElement(InnerComponent, __assign({ key: "InnerComponent" }, props, { execution: result, onDataTooLarge: this.onDataTooLarge, onNegativeValues: this.onNegativeValues, error: error, isLoading: isLoading, intl: intl }, getPageProperty)));
};
LoadingHOCWrapped.prototype.cancelPagePromises = function () {
this.pagePromises = []; // dumping this array of getPage promises will result in effectively cancelling them
};
LoadingHOCWrapped.prototype.getPage = function (resultSpec, limit, offset) {
var _this = this;
if (this.hasUnmounted) {
return Promise.resolve(null);
}
this.setState({ error: null });
var pagePromise = this.createPagePromise(resultSpec, limit, offset);
return pagePromise
.then(errorHandlers_1.checkEmptyResult)
.then(function (rawExecution) {
var emptyHeaderString = "(" + _this.props.intl.formatMessage({
id: "visualization.emptyValue",
}) + ")";
var executionResultWithResolvedEmptyValues = fixEmptyHeaderItems_1.fixEmptyHeaderItems(rawExecution.executionResult, emptyHeaderString);
var result = __assign({}, rawExecution, { executionResult: executionResultWithResolvedEmptyValues });
var supportedDrillableItems = _this.getSupportedDrillableItems(result.executionResponse);
// This returns only current page,
// gooddata-js mergePages doesn't support discontinuous page ranges yet
_this.setState({ result: result, error: null });
_this.props.pushData({
result: result,
supportedDrillableItems: supportedDrillableItems,
});
_this.onLoadingChanged({ isLoading: false });
_this.props.onExportReady(_this.createExportFunction(result)); // Pivot tables
return result;
})
.catch(function (error) {
// only trigger errors on non-cancelled promises
if (error.message !== errorStates_1.ErrorStates.CANCELLED) {
_this.pushDataForError(error);
_this.onError(errorHandlers_1.convertErrors(error));
}
});
};
LoadingHOCWrapped.prototype.isDataReloadRequired = function (nextProps) {
return (!gooddata_js_1.DataLayer.DataSourceUtils.dataSourcesMatch(this.props.dataSource, nextProps.dataSource) ||
!isEqual(this.props.resultSpec, nextProps.resultSpec));
};
LoadingHOCWrapped.prototype.componentWillReceiveProps = function (nextProps) {
if (nextProps.sdk && this.props.sdk !== nextProps.sdk) {
this.sdk = nextProps.sdk.clone();
utils_1.setTelemetryHeaders(this.sdk, "LoadingHOCWrapped", nextProps);
}
if (this.isDataReloadRequired(nextProps)) {
if (autoExecuteDataSource) {
var dataSource = nextProps.dataSource, resultSpec = nextProps.resultSpec;
this.initDataLoading(dataSource, resultSpec);
}
else {
this.onLoadingChanged({ isLoading: true });
}
}
};
LoadingHOCWrapped.prototype.componentWillUnmount = function () {
this.hasUnmounted = true;
this.subject.unsubscribe();
this.cancelPagePromises();
this.getPage = function () { return Promise.resolve(null); };
this.cancelPagePromises = noop;
this.onLoadingChanged = noop;
this.onError = noop;
};
LoadingHOCWrapped.prototype.createPagePromise = function (resultSpec, limit, offset) {
var _this = this;
var pagePromise = this.props.dataSource.getPage(resultSpec, limit, offset);
this.pagePromises.push(pagePromise);
return pagePromise.then(function (result) {
if (_this.isCancelled(pagePromise)) {
_this.removePagePromise(pagePromise);
return result;
}
else {
throw new Error(errorStates_1.ErrorStates.CANCELLED);
}
});
};
LoadingHOCWrapped.prototype.isCancelled = function (pagePromise) {
return this.pagePromises.includes(pagePromise);
};
LoadingHOCWrapped.prototype.getAllAttributeHeaders = function (response) {
return flatten(response.dimensions.map(function (dimension) { return dimension.headers; }))
.filter(function (header) { return typings_1.Execution.isAttributeHeader(header); })
.map(function (header) { return header; });
};
LoadingHOCWrapped.prototype.getAllAttributes = function (response) {
return uniqBy(this.getAllAttributeHeaders(response).map(function (attribute) { return ({
attribute: attribute.attributeHeader.formOf.uri,
displayForm: attribute.attributeHeader.uri,
}); }), function (attribute) { return attribute.displayForm; });
};
LoadingHOCWrapped.prototype.getAllMeasureHeaderItems = function (response) {
return flatten(flatten(flatten(response.dimensions).map(function (dimension) { return dimension.headers; }))
.filter(function (header) { return typings_1.Execution.isMeasureGroupHeader(header); })
.map(function (header) { return header.measureGroupHeader.items; }));
};
LoadingHOCWrapped.prototype.getSupportedDrillableItems = function (response) {
var attributes = this.getAllAttributes(response);
return this.getAllMeasureHeaderItems(response).map(function (measure) { return ({
type: "measure",
localIdentifier: measure.measureHeaderItem.localIdentifier,
title: measure.measureHeaderItem.name,
attributes: attributes,
}); });
};
LoadingHOCWrapped.prototype.initSubject = function () {
var _this = this;
this.subject = gooddata_js_1.DataLayer.createSubject(function (result) {
_this.setState({ result: result });
var supportedDrillableItems = _this.getSupportedDrillableItems(result.executionResponse);
_this.props.pushData({ result: result, supportedDrillableItems: supportedDrillableItems });
_this.onLoadingChanged({ isLoading: false });
_this.props.onExportReady(_this.createExportFunction(result)); // Charts / Tables
}, function (error) {
return _this.onError(error);
});
};
LoadingHOCWrapped.prototype.pushDataForError = function (error) {
if (gooddata_js_1.TypeGuards.isApiExecutionResponseError(error)) {
var supportedDrillableItems = this.getSupportedDrillableItems(error.executionResponse);
this.props.pushData({ supportedDrillableItems: supportedDrillableItems });
}
};
LoadingHOCWrapped.prototype.pushDataForNoContentResponse = function (error) {
var _this = this;
if (!errorHandlers_1.isApiResponseError(error.cause) || error.getMessage() !== errorStates_1.ErrorStates.NO_DATA) {
return;
}
var response = error.cause.response.json();
response.then(function (r) {
var supportedDrillableItems = _this.getSupportedDrillableItems(r.executionResponse);
_this.props.pushData({ supportedDrillableItems: supportedDrillableItems });
});
};
LoadingHOCWrapped.prototype.onLoadingChanged = function (loadingState) {
this.props.onLoadingChanged(loadingState);
var isLoading = loadingState.isLoading;
var state = { isLoading: isLoading };
if (isLoading) {
state.error = null;
}
this.setState(state);
};
LoadingHOCWrapped.prototype.onError = function (error, dataSource) {
if (dataSource === void 0) { dataSource = this.props.dataSource; }
if (gooddata_js_1.DataLayer.DataSourceUtils.dataSourcesMatch(this.props.dataSource, dataSource)) {
this.setState({ error: error.getMessage() });
this.pushDataForNoContentResponse(error);
this.onLoadingChanged({ isLoading: false });
this.props.onExportReady(this.createExportErrorFunction(error));
this.props.onError(error);
}
};
LoadingHOCWrapped.prototype.onDataTooLarge = function () {
this.onError(new RuntimeError_1.RuntimeError(errorStates_1.ErrorStates.DATA_TOO_LARGE_TO_DISPLAY));
};
LoadingHOCWrapped.prototype.onNegativeValues = function () {
this.onError(new RuntimeError_1.RuntimeError(errorStates_1.ErrorStates.NEGATIVE_VALUES));
};
LoadingHOCWrapped.prototype.initDataLoading = function (dataSource, resultSpec) {
var _this = this;
this.onLoadingChanged({ isLoading: true });
this.setState({ result: null });
var promise = dataSource
.getData(resultSpec)
.then(errorHandlers_1.checkEmptyResult)
.catch(function (error) {
_this.pushDataForError(error);
throw errorHandlers_1.convertErrors(error);
});
this.subject.next(promise);
};
LoadingHOCWrapped.prototype.createExportFunction = function (execution) {
var _this = this;
return function (exportConfig) {
var _a = _this.props, dataSource = _a.dataSource, exportTitle = _a.exportTitle, projectId = _a.projectId;
if (!execution) {
return Promise.reject(new Error("Unknown execution"));
}
if (!projectId) {
return Promise.reject(new Error("Unknown projectId"));
}
var format = exportConfig.format, includeFilterContext = exportConfig.includeFilterContext, mergeHeaders = exportConfig.mergeHeaders, customTitle = exportConfig.title;
var title = escapeFileName(customTitle || exportTitle) || "Untitled";
var exportConfigRequest = {
format: format,
mergeHeaders: mergeHeaders,
title: title,
};
if (includeFilterContext) {
exportConfigRequest.showFilters = true;
exportConfigRequest.afm = dataSource.getAfm();
}
return _this.sdk.report.exportResult(projectId, get(execution, "executionResponse.links.executionResult"), exportConfigRequest);
};
};
LoadingHOCWrapped.prototype.createExportErrorFunction = function (error) {
return function (_exportConfig) {
return Promise.reject(error);
};
};
LoadingHOCWrapped.defaultProps = InnerComponent.defaultProps;
return LoadingHOCWrapped;
}(React.Component));
var IntlLoadingHOC = react_intl_1.injectIntl(LoadingHOCWrapped);
return /** @class */ (function (_super) {
__extends(LoadingHOC, _super);
function LoadingHOC() {
return _super !== null && _super.apply(this, arguments) || this;
}
LoadingHOC.prototype.render = function () {
return (React.createElement(IntlWrapper_1.IntlWrapper, { locale: this.props.locale },
React.createElement(IntlLoadingHOC, __assign({}, this.props))));
};
return LoadingHOC;
}(React.Component));
}
exports.visualizationLoadingHOC = visualizationLoadingHOC;
//# sourceMappingURL=VisualizationLoadingHOC.js.map