synapse-react-client
Version:
[](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [](https://badge.fury.io/js/synaps
758 lines • 44.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DOWNLOAD_OPTIONS_CONTAINER_CLASS = exports.SORT_STATE = exports.EMPTY_HEADER = void 0;
var tslib_1 = require("tslib");
var lodash_es_1 = require("lodash-es");
var React = (0, tslib_1.__importStar)(require("react"));
var react_bootstrap_1 = require("react-bootstrap");
var sql_parser_1 = require("sql-parser");
var utils_1 = require("../../utils");
var getUserData_1 = require("../../utils/functions/getUserData");
var sqlFunctions_1 = require("../../utils/functions/sqlFunctions");
var synapseTypes_1 = require("../../utils/synapseTypes/");
var HasAccess_1 = (0, tslib_1.__importDefault)(require("../HasAccess"));
var TotalQueryResults_1 = (0, tslib_1.__importDefault)(require("../TotalQueryResults"));
var unCamelCase_1 = require("./../../utils/functions/unCamelCase");
var SynapseTableConstants_1 = require("./SynapseTableConstants");
var file_dotted_svg_1 = (0, tslib_1.__importDefault)(require("../../assets/icons/file-dotted.svg"));
var SynapseTableCell_1 = require("../synapse_table_functions/SynapseTableCell");
var getUniqueEntities_1 = require("../synapse_table_functions/getUniqueEntities");
var getColumnIndiciesWithType_1 = require("../synapse_table_functions/getColumnIndiciesWithType");
var Checkbox_1 = require("../widgets/Checkbox");
var EnumFacetFilter_1 = require("../widgets/query-filter/EnumFacetFilter");
var QueryFilter_1 = require("../widgets/query-filter/QueryFilter");
var column_resizer_1 = (0, tslib_1.__importDefault)(require("column-resizer"));
var ModalDownload_1 = (0, tslib_1.__importDefault)(require("../ModalDownload"));
var LoadingScreen_1 = (0, tslib_1.__importDefault)(require("../LoadingScreen"));
var utils_2 = require("../row_renderers/utils");
var SearchResultsNotFound_1 = (0, tslib_1.__importDefault)(require("./SearchResultsNotFound"));
var SynapseConstants_1 = require("../../utils/SynapseConstants");
var AddToDownloadListV2_1 = (0, tslib_1.__importDefault)(require("../AddToDownloadListV2"));
var SynapseContext_1 = require("../../utils/SynapseContext");
var getEndpoint_1 = require("../../utils/functions/getEndpoint");
var DirectDownload_1 = (0, tslib_1.__importDefault)(require("../DirectDownload"));
exports.EMPTY_HEADER = {
id: '',
name: '',
type: 'org.sagebionetworks.repo.model.FileEntity',
versionNumber: -1,
versionLabel: '',
benefactorId: -1,
createdBy: '',
createdOn: '',
modifiedBy: '',
modifiedOn: '',
};
// Hold constants for next and previous button actions
var NEXT = 'NEXT';
var PREVIOUS = 'PREVIOUS';
exports.SORT_STATE = ['', 'DESC', 'ASC'];
exports.DOWNLOAD_OPTIONS_CONTAINER_CLASS = 'SRC-download-options-container';
var RESIZER_OPTIONS = {
resizeMode: 'overflow',
partialRefresh: 'true',
liveDrag: true,
headerOnly: 'true',
};
var SynapseTable = /** @class */ (function (_super) {
(0, tslib_1.__extends)(SynapseTable, _super);
function SynapseTable(props) {
var _this = _super.call(this, props) || this;
_this.tableElement = undefined;
_this.showGroupRowData = function (selectedRow) {
// magic happens - parse query, deep copy query bundle request, modify, encode, send to Synapse.org. Easy!
var queryCopy = _this.props.getLastQueryRequest().query;
var parsed = _this.getSqlUnderlyingDataForRow(selectedRow, queryCopy.sql);
queryCopy.sql = parsed.newSql;
var queryJSON = JSON.stringify(queryCopy);
// encode this copy of the query (json)
var encodedQuery = btoa(queryJSON);
return getEndpoint_1.PRODUCTION_ENDPOINT_CONFIG.PORTAL + "#!Synapse:" + parsed.synId + "/tables/query/" + encodedQuery;
};
_this.renderTable = function (headers, columnModels, facets, rows) {
var _a, _b;
var lastQueryRequest = (_b = (_a = _this.props).getLastQueryRequest) === null || _b === void 0 ? void 0 : _b.call(_a);
// handle displaying the previous button -- if offset is zero then it
// shouldn't be displayed
var pastZero = lastQueryRequest.query.offset > 0;
var _c = _this.props, hasMoreData = _c.hasMoreData, showAccessColumn = _c.showAccessColumn, showDownloadColumn = _c.showDownloadColumn, isRowSelectionVisible = _c.isRowSelectionVisible;
var nextBtn = (React.createElement(react_bootstrap_1.Button, { variant: "secondary", className: "pill-xl", onClick: _this.handlePaginationClick(NEXT), style: {
marginRight: 0,
marginBottom: '20px',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
}, type: "button" }, "Next"));
var previousBtn = (React.createElement(react_bootstrap_1.Button, { variant: "secondary", className: "pill-xl", onClick: _this.handlePaginationClick(PREVIOUS), type: "button", style: {
marginRight: !hasMoreData && pastZero ? 0 : '10px',
marginBottom: '20px',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
} }, "Previous"));
var isShowingAccessColumn = showAccessColumn && _this.state.isEntityView;
var isLoggedIn = !!_this.context.accessToken;
var isShowingAddToV2DownloadListColumn = _this.state.isFileView && !_this.props.hideDownload && isLoggedIn;
var isShowingDirectDownloadColumn = _this.state.isFileView && showDownloadColumn && isLoggedIn;
/* min height ensure if no rows are selected that a dropdown menu is still accessible */
var tableEntityId = lastQueryRequest === null || lastQueryRequest === void 0 ? void 0 : lastQueryRequest.entityId;
return (React.createElement("div", { style: { minHeight: '400px' }, className: "SRC-overflowAuto", "data-testid": "SynapseTable" },
React.createElement("table", { ref: function (node) { return (_this.tableElement = node); }, className: "table table-striped table-condensed" },
React.createElement("thead", { className: "SRC_bordered" },
React.createElement("tr", null, _this.createTableHeader(headers, columnModels, facets, isShowingAccessColumn, isShowingDirectDownloadColumn, isShowingAddToV2DownloadListColumn, isRowSelectionVisible, lastQueryRequest))),
React.createElement("tbody", null, _this.createTableRows(rows, headers, isShowingAccessColumn, isShowingDirectDownloadColumn, isShowingAddToV2DownloadListColumn, isRowSelectionVisible, tableEntityId))),
React.createElement("div", { className: "bootstrap-4-backport", style: { textAlign: 'right' } },
pastZero && previousBtn,
hasMoreData && nextBtn)));
};
/**
* Handle a click on next or previous
*
* @memberof SynapseTable
*/
_this.handlePaginationClick = function (eventType) { return function (_event) {
var _a, _b;
var queryRequest = _this.props.getLastQueryRequest();
var currentOffset = queryRequest.query.offset;
// if its a "previous" click subtract from the offset
// otherwise its next and we paginate forward
if (eventType === PREVIOUS) {
currentOffset -= (_a = queryRequest.query.limit) !== null && _a !== void 0 ? _a : SynapseConstants_1.DEFAULT_PAGE_SIZE;
}
if (eventType === NEXT) {
currentOffset += (_b = queryRequest.query.limit) !== null && _b !== void 0 ? _b : SynapseConstants_1.DEFAULT_PAGE_SIZE;
}
queryRequest.query.offset = currentOffset;
_this.props.executeQueryRequest(queryRequest);
}; };
/**
* Handle a column having been selected
*
* @memberof SynapseTable
*/
_this.handleColumnSortPress = function (dict) { return function (_) {
// by using Synthetic event we can use the handler on both key press and mouse click
var columnIconSortState = (0, lodash_es_1.cloneDeep)(_this.state.columnIconSortState);
if (columnIconSortState.length === 0) {
columnIconSortState = Array(_this.getLengthOfPropsData()).fill(0);
}
// get currently sorted items and remove/insert/update this selection
var sortedColumnSelection = (0, lodash_es_1.cloneDeep)(_this.state.sortedColumnSelection);
var index = _this.findSelectionIndex(sortedColumnSelection, dict.name);
// if its present then remove it
if (index !== -1) {
sortedColumnSelection.splice(index, 1);
}
columnIconSortState[dict.index] =
(columnIconSortState[dict.index] + 1) % SynapseTableConstants_1.ICON_STATE.length;
if (columnIconSortState[dict.index] > 0) {
sortedColumnSelection.unshift({
column: dict.name,
direction: exports.SORT_STATE[columnIconSortState[dict.index]],
});
}
var queryRequest = _this.props.getLastQueryRequest();
queryRequest.query.sort = sortedColumnSelection;
queryRequest.query.offset = 0;
_this.props.executeQueryRequest(queryRequest);
_this.setState({
columnIconSortState: columnIconSortState,
sortedColumnSelection: sortedColumnSelection,
});
}; };
/**
* Handles the toggle of a column select, this will cause the table to
* either show the column or hide depending on the prior state of the column
*
* @memberof SynapseTable
*/
_this.toggleColumnSelection = function (columnName) {
var isColumnSelected = (0, lodash_es_1.cloneDeep)(_this.props.isColumnSelected);
if (isColumnSelected.includes(columnName)) {
isColumnSelected = isColumnSelected.filter(function (el) { return el !== columnName; });
}
else {
isColumnSelected.push(columnName);
}
_this.props.updateParentState({ isColumnSelected: isColumnSelected });
};
_this.applyChangesFromQueryFilter = function (facets) {
var queryRequest = _this.props.getLastQueryRequest();
queryRequest.query.selectedFacets = facets;
queryRequest.query.offset = 0;
_this.props.executeQueryRequest(queryRequest);
};
_this.componentDidMount = _this.componentDidMount.bind(_this);
_this.componentWillUnmount = _this.componentWillUnmount.bind(_this);
_this.componentDidUpdate = _this.componentDidUpdate.bind(_this);
_this.shouldComponentUpdate = _this.shouldComponentUpdate.bind(_this);
_this.handleColumnSortPress = _this.handleColumnSortPress.bind(_this);
_this.handlePaginationClick = _this.handlePaginationClick.bind(_this);
_this.findSelectionIndex = _this.findSelectionIndex.bind(_this);
_this.toggleColumnSelection = _this.toggleColumnSelection.bind(_this);
_this.advancedSearch = _this.advancedSearch.bind(_this);
_this.getLengthOfPropsData = _this.getLengthOfPropsData.bind(_this);
_this.configureFacetDropdown = _this.configureFacetDropdown.bind(_this);
_this.enableResize = _this.enableResize.bind(_this);
_this.disableResize = _this.disableResize.bind(_this);
// store the offset and sorted selection that is currently held
_this.state = {
/* columnIconSortState tells what icon to display for a table
header. There are three states for a particular header-
0 - show descending icon but *deselected*
1 - show descending icon selected
2 - show ascending icon selected
*/
columnIconSortState: [],
isExportTableDownloadOpen: false,
isExpanded: false,
isColumnSelectionOpen: false,
isEntityView: false,
isFileView: false,
// sortedColumnSelection contains the columns which are
// selected currently and their sort status as eithet
// off, desc, or asc.
sortedColumnSelection: [],
mapEntityIdToHeader: {},
mapUserIdToHeader: {},
isFetchingEntityHeaders: false,
isFetchingEntityVersion: false,
};
_this.getEntityHeadersInData = _this.getEntityHeadersInData.bind(_this);
return _this;
}
SynapseTable.prototype.componentWillUnmount = function () {
this.disableResize();
};
SynapseTable.prototype.componentDidMount = function () {
this.getEntityHeadersInData(true);
this.enableResize();
};
SynapseTable.prototype.shouldComponentUpdate = function (nextProps, nextState, nextContext) {
this.disableResize();
return _super.prototype.shouldComponentUpdate
? _super.prototype.shouldComponentUpdate.call(this, nextProps, nextState, nextContext)
: true;
};
SynapseTable.prototype.componentDidUpdate = function (prevProps) {
this.getEntityHeadersInData(false);
this.getTableConcreteType(prevProps);
this.enableResize();
};
SynapseTable.prototype.getTableConcreteType = function (prevProps) {
var _a;
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var token, data, currentTableId, previousTableId, entityData, isEntityView, isFileView;
return (0, tslib_1.__generator)(this, function (_b) {
switch (_b.label) {
case 0:
token = this.context.accessToken;
data = this.props.data;
if (!data || this.state.isFetchingEntityVersion) {
return [2 /*return*/];
}
currentTableId = data === null || data === void 0 ? void 0 : data.queryResult.queryResults.tableId;
previousTableId = (_a = prevProps.data) === null || _a === void 0 ? void 0 : _a.queryResult.queryResults.tableId;
if (!(currentTableId && previousTableId !== currentTableId)) return [3 /*break*/, 2];
this.setState({
isFetchingEntityVersion: true,
});
return [4 /*yield*/, utils_1.SynapseClient.getEntity(token, currentTableId)];
case 1:
entityData = _b.sent();
isEntityView = entityData.concreteType.includes('EntityView');
isFileView = isEntityView
? (entityData.viewTypeMask & 1) != 0
: false;
this.setState({
isEntityView: isEntityView,
isFileView: isFileView,
isFetchingEntityVersion: false,
});
_b.label = 2;
case 2: return [2 /*return*/];
}
});
});
};
SynapseTable.prototype.enableResize = function () {
if (!this.resizer) {
if (this.tableElement) {
this.resizer = new column_resizer_1.default(this.tableElement, RESIZER_OPTIONS);
}
}
else {
this.resizer.reset(RESIZER_OPTIONS);
}
};
SynapseTable.prototype.disableResize = function () {
if (this.resizer) {
this.resizer.reset({ disable: true });
}
};
SynapseTable.prototype.getEntityHeadersInData = function (forceRefresh) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var data, mapEntityIdToHeader, mapUserIdToHeader, entityIdColumnIndicies, userIdColumnIndicies, distinctEntityIds, queryResult, queryResults, rows, distinctUserIds, referenceList, data_1, results, err_1, userPorfileIds, ids, data_2, err_2, data_3, err_3;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
data = this.props.data;
if (!data) {
return [2 /*return*/];
}
else if (this.state.isFetchingEntityHeaders && !forceRefresh) {
return [2 /*return*/];
}
mapEntityIdToHeader = (0, lodash_es_1.cloneDeep)(this.state.mapEntityIdToHeader);
mapUserIdToHeader = (0, lodash_es_1.cloneDeep)(this.state.mapUserIdToHeader);
entityIdColumnIndicies = (0, getColumnIndiciesWithType_1.getColumnIndiciesWithType)(this.props.data, synapseTypes_1.ColumnType.ENTITYID);
userIdColumnIndicies = (0, getColumnIndiciesWithType_1.getColumnIndiciesWithType)(this.props.data, synapseTypes_1.ColumnType.USERID);
distinctEntityIds = (0, getUniqueEntities_1.getUniqueEntities)(data, mapEntityIdToHeader, entityIdColumnIndicies);
// also include row entity ids if this is a view (it's possible that the ID column was not selected)
if (this.state.isEntityView) {
queryResult = data.queryResult;
queryResults = queryResult.queryResults;
rows = queryResults.rows;
rows.forEach(function (row) {
var rowSynapseId = "syn" + row.rowId;
distinctEntityIds.add(rowSynapseId);
});
}
distinctUserIds = (0, getUniqueEntities_1.getUniqueEntities)(data, mapUserIdToHeader, userIdColumnIndicies);
if (distinctEntityIds.size === 0 && distinctUserIds.size === 0) {
return [2 /*return*/];
}
this.setState({
isFetchingEntityHeaders: true,
});
if (!(distinctEntityIds.size > 0)) return [3 /*break*/, 4];
referenceList = Array.from(distinctEntityIds).map(function (id) {
return { targetId: id };
});
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
// initialize mapEntityIdToHeader
referenceList.forEach(function (el) {
mapEntityIdToHeader[el.targetId] = exports.EMPTY_HEADER;
});
return [4 /*yield*/, utils_1.SynapseClient.getEntityHeaders(referenceList, this.context.accessToken)];
case 2:
data_1 = _a.sent();
results = data_1.results;
results.forEach(function (el) {
mapEntityIdToHeader[el.id] = el;
});
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
console.error('Error on retrieving entity header list , ', err_1);
return [3 /*break*/, 4];
case 4:
userPorfileIds = [];
if (!(distinctUserIds.size > 0)) return [3 /*break*/, 8];
ids = Array.from(distinctUserIds);
_a.label = 5;
case 5:
_a.trys.push([5, 7, , 8]);
return [4 /*yield*/, utils_1.SynapseClient.getGroupHeadersBatch(ids, this.context.accessToken)];
case 6:
data_2 = _a.sent();
data_2.children.forEach(function (el) {
if (el.isIndividual) {
userPorfileIds.push(el.ownerId);
}
else {
mapUserIdToHeader[el.ownerId] = el;
}
});
return [3 /*break*/, 8];
case 7:
err_2 = _a.sent();
console.error('Error on getGroupHeaders batch: ', err_2);
return [3 /*break*/, 8];
case 8:
if (!(userPorfileIds.length > 0)) return [3 /*break*/, 12];
_a.label = 9;
case 9:
_a.trys.push([9, 11, , 12]);
return [4 /*yield*/, (0, getUserData_1.getUserProfileWithProfilePicAttached)(userPorfileIds)];
case 10:
data_3 = _a.sent();
data_3.list.forEach(function (el) {
mapUserIdToHeader[el.ownerId] = el;
});
return [3 /*break*/, 12];
case 11:
err_3 = _a.sent();
console.error('Error on getUserProfile : ', err_3);
return [3 /*break*/, 12];
case 12:
this.setState({
mapEntityIdToHeader: mapEntityIdToHeader,
mapUserIdToHeader: mapUserIdToHeader,
isFetchingEntityHeaders: false,
});
return [2 /*return*/];
}
});
});
};
/**
* Display the view
*/
SynapseTable.prototype.render = function () {
var _this = this;
if (this.props.isLoadingNewData) {
return LoadingScreen_1.default;
}
else if (!this.props.data) {
return React.createElement(React.Fragment, null);
}
// unpack all the data
var _a = this.props, data = _a.data, _b = _a.isLoading, isLoading = _b === void 0 ? true : _b, unitDescription = _a.unitDescription, showBarChart = _a.showBarChart, topLevelControlsState = _a.topLevelControlsState;
var queryResult = data.queryResult, _c = data.columnModels, columnModels = _c === void 0 ? [] : _c;
var queryResults = queryResult.queryResults;
var rows = queryResults.rows;
var headers = queryResults.headers;
var _d = data.facets, facets = _d === void 0 ? [] : _d;
var _e = this.state, isExpanded = _e.isExpanded, isExportTableDownloadOpen = _e.isExportTableDownloadOpen;
var queryRequest = this.props.getLastQueryRequest();
var showFacetFilter = topLevelControlsState.showFacetFilter;
var className = '';
if (showBarChart) {
className = 'SRC-marginBottomTop';
}
var hasResults = data.queryResult.queryResults.rows.length > 0;
// Show the No Results UI if the current page has no rows, and this is the first page of data (offset === 0).
if (!hasResults && queryRequest.query.offset === 0) {
if (queryRequest.query.additionalFilters) {
return React.createElement(SearchResultsNotFound_1.default, null);
}
else {
return (React.createElement("div", { className: "text-center SRCBorderedPanel SRCBorderedPanel--padded2x" },
React.createElement("img", { src: file_dotted_svg_1.default, alt: "no data" }),
React.createElement("div", { style: { marginTop: '20px', fontStyle: 'italic' } }, "This table is currently empty")));
}
}
var table = (React.createElement("div", null, this.renderTable(headers, columnModels, facets, rows)));
var content = (React.createElement(React.Fragment, null,
React.createElement("div", { className: className },
isExportTableDownloadOpen && (React.createElement(ModalDownload_1.default, { onClose: function () {
_this.setState({
isExportTableDownloadOpen: false,
});
}, queryBundleRequest: queryRequest })),
!showFacetFilter &&
unitDescription &&
!(0, sqlFunctions_1.isGroupByInSql)(queryRequest.query.sql) && (React.createElement("div", { className: "SRC-centerContent text-left", style: { minHeight: '20px' } },
React.createElement(TotalQueryResults_1.default, { isLoading: isLoading, style: { fontSize: 15 }, unitDescription: unitDescription, lastQueryRequest: queryRequest, frontText: 'Showing', applyChanges: function (newFacets) {
return _this.applyChangesFromQueryFilter(newFacets);
} }))),
React.createElement("div", null, table))));
return (React.createElement(React.Fragment, null,
isExpanded && (React.createElement(react_bootstrap_1.Modal, { animation: false, show: true,
// @ts-ignore
onHide: function () { return _this.setState({ isExpanded: false }); }, dialogClassName: 'modal-90w' },
React.createElement(react_bootstrap_1.Modal.Header
// @ts-ignore
, {
// @ts-ignore
onHide: function () { return _this.setState({ isExpanded: false }); }, closeButton: true }),
React.createElement(react_bootstrap_1.Modal.Body, null, content))),
!isExpanded && content));
};
/**
* Return the select column indexes for columns that use the aggregate count function.
* If sql does not have a GROUP BY, this returns an empty array.
* @param originalSql
*/
SynapseTable.prototype.getCountFunctionColumnIndexes = function (originalSql) {
var indexes = [];
if ((0, sqlFunctions_1.isGroupByInSql)(originalSql)) {
var tokens = sql_parser_1.lexer.tokenize(originalSql);
var selectIndex = tokens.findIndex(function (el) { return el[0] === 'SELECT'; });
var fromIndex = tokens.findIndex(function (el) { return el[0] === 'FROM'; });
var columnIndex = 0;
for (var index = selectIndex + 1; index < fromIndex - selectIndex - 1; index += 1) {
var token = tokens[index];
if (token[0] === 'FUNCTION' && token[1].toLowerCase() === 'count') {
// found a count column!
indexes.push(columnIndex);
}
else if (token[0] === 'SEPARATOR') {
// next column
columnIndex += 1;
}
}
}
return indexes;
};
SynapseTable.prototype.getSqlUnderlyingDataForRow = function (selectedRow, originalSql) {
var tokens = sql_parser_1.lexer.tokenize(originalSql);
var selectIndex = tokens.findIndex(function (el) { return el[0] === 'SELECT'; });
var fromIndex = tokens.findIndex(function (el) { return el[0] === 'FROM'; });
// gather all of the column names literals between select and from (and their indices)
var columnReferences = [];
var columnIndex = 0;
var foundFunctionForColumn = false;
for (var index = selectIndex + 1; index < fromIndex - selectIndex - 1; index += 1) {
var token = tokens[index];
// parsing error. concat function is reported as a LITERAL instead of a function
if (token[0] === 'FUNCTION' ||
token[1].toLocaleLowerCase() === 'concat') {
foundFunctionForColumn = true;
}
else if (token[0] === 'LITERAL' && !foundFunctionForColumn) {
// found a column
columnReferences.push({ index: columnIndex, name: token[1] });
}
else if (token[0] === 'SEPARATOR') {
// next column
columnIndex += 1;
// reset "found function"
foundFunctionForColumn = false;
}
}
// remove all tokens after (and including) group
tokens = tokens.slice(0, tokens.findIndex(function (el) { return el[0] === 'GROUP'; }));
// replace all columns with *
tokens.splice(selectIndex + 1, fromIndex - selectIndex - 1, [
'STAR',
'*',
'1',
]);
// add new items to where clause, but only if the column name corresponds to a real column in the table/view!
// use row.values
if (this.props.data === undefined) {
return { synId: '', newSql: '' };
}
var whereIndex = tokens.findIndex(function (el) { return el[0] === 'WHERE'; });
if (whereIndex === -1) {
// does not contain a where clause
tokens.push(['WHERE', 'WHERE', '1']);
}
else {
// alreay contains a where clause, add the first AND
tokens.push(['CONDITIONAL', 'AND', '1']);
}
// look for headers in column models, if they match then add a where clause
columnReferences.forEach(function (value, index) {
var rowValue = selectedRow.values[value.index];
// PORTALS-712: support null values
if (rowValue) {
tokens.push(['LITERAL', value.name, '1'], ['OPERATOR', '=', '1'], ['STRING', rowValue, '1'], ['CONDITIONAL', 'AND', '1']);
}
else {
tokens.push(['LITERAL', value.name, '1'], ['OPERATOR', 'IS', '1'], ['BOOLEAN', 'null', '1'], ['CONDITIONAL', 'AND', '1']);
}
});
// remove the last AND
tokens.pop();
// remove backtick from output sql (for table name): `syn1234` becomes syn1234
var synId = tokens[tokens.findIndex(function (el) { return el[0] === 'FROM'; }) + 1][1];
tokens.push(['EOF', '', '1']);
return { synId: synId, newSql: (0, sqlFunctions_1.formatSQLFromParser)(tokens) };
};
SynapseTable.prototype.createTableRows = function (rows, headers, isShowingAccessColumn, isShowingDownloadColumn, isShowingAddToV2DownloadListColumn, isRowSelectionVisible, tableEntityId) {
var _this = this;
var rowsFormatted = [];
var _a = this.props, data = _a.data, isColumnSelected = _a.isColumnSelected, selectedRowIndices = _a.selectedRowIndices, updateParentState = _a.updateParentState, _b = _a.columnLinks, columnLinks = _b === void 0 ? [] : _b;
var _c = data, _d = _c.selectColumns, selectColumns = _d === void 0 ? [] : _d, _e = _c.columnModels, columnModels = _e === void 0 ? [] : _e;
var _f = this.state, mapEntityIdToHeader = _f.mapEntityIdToHeader, mapUserIdToHeader = _f.mapUserIdToHeader;
// find column indices that are COUNT type
var countColumnIndexes = this.getCountFunctionColumnIndexes(this.props.getLastQueryRequest().query.sql);
rows.forEach(function (row, rowIndex) {
var _a, _b;
var entityVersionNumber = (_a = row.versionNumber) === null || _a === void 0 ? void 0 : _a.toString();
var rowSynapseId = "syn" + row.rowId;
var rowContent = row.values.map(function (columnValue, colIndex) {
var columnName = headers[colIndex].name;
var isColumnActive = isColumnSelected.includes(columnName);
var columnLinkConfig = columnLinks.find(function (el) {
return el.matchColumnName === columnName;
});
var index = _this.findSelectionIndex(_this.state.sortedColumnSelection, columnName);
var isCountColumn = countColumnIndexes.includes(colIndex);
var isBold = index === -1 ? '' : 'SRC-boldText';
if (isColumnActive) {
return (React.createElement("td", { className: "SRC_noBorderTop SRC-synapseTableTd", key: "(" + rowIndex + columnValue + colIndex + ")" },
isCountColumn && (React.createElement("a", { href: _this.showGroupRowData(row), target: "_blank", rel: "noopener noreferrer" },
React.createElement("p", { className: isBold }, columnValue))),
!isCountColumn && (React.createElement(SynapseTableCell_1.SynapseTableCell, { columnType: headers[colIndex].columnType, columnValue: columnValue, isBold: isBold, mapEntityIdToHeader: mapEntityIdToHeader, mapUserIdToHeader: mapUserIdToHeader, rowIndex: rowIndex, columnLinkConfig: columnLinkConfig, columnName: columnName, tableEntityId: tableEntityId, rowData: row.values, selectColumns: selectColumns, columnModels: columnModels }))));
}
return React.createElement("td", { className: "SRC-hidden", key: "(" + rowIndex + "," + colIndex + ")" });
});
// also push the access column value if we are showing user access for individual items (still shown if not logged in)
if (isShowingAccessColumn) {
rowContent.unshift(React.createElement("td", { key: rowSynapseId, className: "SRC_noBorderTop" },
React.createElement(HasAccess_1.default, { key: rowSynapseId, entityId: rowSynapseId, entityVersionNumber: entityVersionNumber })));
}
var isFileEntity = ((_b = mapEntityIdToHeader[rowSynapseId]) === null || _b === void 0 ? void 0 : _b.type) ==
'org.sagebionetworks.repo.model.FileEntity';
if (isShowingDownloadColumn) {
// SWC-5790: If this is a FileEntity, the download icon should just go to entity page
rowContent.unshift(React.createElement("td", { className: "SRC_noBorderTop direct-download" }, isFileEntity && (React.createElement(DirectDownload_1.default, { key: 'direct-download-' + rowSynapseId, associatedObjectId: rowSynapseId, entityVersionNumber: entityVersionNumber }))));
}
if (isShowingAddToV2DownloadListColumn) {
rowContent.unshift(React.createElement("td", { className: "SRC_noBorderTop add-to-download-list-v2" }, isFileEntity && (React.createElement(AddToDownloadListV2_1.default, { key: 'add-to-download-list-v2-' + rowSynapseId, entityId: rowSynapseId, entityVersionNumber: parseInt(entityVersionNumber) }))));
}
if (isRowSelectionVisible && selectedRowIndices) {
rowContent.unshift(React.createElement("td", { key: "(" + rowIndex + ",rowSelectColumn)", className: "SRC_noBorderTop" },
React.createElement(Checkbox_1.Checkbox, { label: "", checked: selectedRowIndices.includes(rowIndex), onChange: function (checked) {
var cloneSelectedRowIndices = (0, tslib_1.__spreadArray)([], selectedRowIndices, true);
if (checked) {
cloneSelectedRowIndices.push(rowIndex);
}
else {
var index = cloneSelectedRowIndices.indexOf(rowIndex);
if (index > -1) {
cloneSelectedRowIndices.splice(index, 1);
}
}
// update parent state on change
updateParentState({
selectedRowIndices: cloneSelectedRowIndices,
});
} })));
}
var rowFormatted = React.createElement("tr", { key: row.rowId }, rowContent);
rowsFormatted.push(rowFormatted);
});
return rowsFormatted;
};
SynapseTable.prototype.isSortableColumn = function (column) {
switch (column) {
case synapseTypes_1.ColumnType.USERID:
case synapseTypes_1.ColumnType.ENTITYID:
case synapseTypes_1.ColumnType.FILEHANDLEID:
return false;
default:
return true;
}
};
SynapseTable.prototype.createTableHeader = function (headers, columnModels, facets, isShowingAccessColumn, isShowingDownloadColumn, isShowingAddToV2DownloadListColumn, isRowSelectionVisible, lastQueryRequest) {
var _this = this;
var _a = this.state, sortedColumnSelection = _a.sortedColumnSelection, columnIconSortState = _a.columnIconSortState;
var _b = this.props, _c = _b.facetAliases, facetAliases = _c === void 0 ? {} : _c, isColumnSelected = _b.isColumnSelected, lockedFacet = _b.lockedFacet;
var tableColumnHeaderElements = headers.map(function (column, index) {
var _a;
var isHeaderSelected = isColumnSelected.includes(column.name);
if (isHeaderSelected) {
// for background color
var isSelected = _this.findSelectionIndex(sortedColumnSelection, column.name) !== -1;
// for icon state
var columnIndex = columnIconSortState[index] === undefined
? 0
: columnIconSortState[index];
// we have to figure out if the current column is a facet selection
var facetIndex = facets.findIndex(function (facetColumnResult) {
return facetColumnResult.columnName === column.name;
});
// the header must be included in the facets and it has to be enumerable for current rendering capabilities
var isFacetSelection = facetIndex !== -1 && facets[facetIndex].facetType === 'enumeration';
var facet = facets[facetIndex];
var isSelectedSpanClass = isSelected
? 'SRC-primary-background-color SRC-anchor-light'
: '';
var isSelectedIconClass = isSelected
? 'SRC-selected-table-icon tool-icon'
: 'SRC-primary-text-color tool-icon';
var sortSpanBackgoundClass = "SRC-tableHead SRC-hand-cursor SRC-sortPadding SRC-primary-background-color-hover " + isSelectedSpanClass;
var displayColumnName = (0, unCamelCase_1.unCamelCase)(column.name, facetAliases);
var columnModel = columnModels.find(function (el) { return el.name === column.name; });
var isLockedFacetColumn = column.name.toLowerCase() === ((_a = lockedFacet === null || lockedFacet === void 0 ? void 0 : lockedFacet.facet) === null || _a === void 0 ? void 0 : _a.toLowerCase()); // used in details page to disable filter the column
return (React.createElement("th", { key: column.name },
React.createElement("div", { className: "SRC-split" },
React.createElement("span", { style: { whiteSpace: 'nowrap' } }, displayColumnName),
React.createElement("div", { className: "SRC-centerContent" },
isFacetSelection &&
!isLockedFacetColumn &&
_this.configureFacetDropdown(facet, columnModel, lastQueryRequest, facetAliases),
_this.isSortableColumn(column.columnType) && (React.createElement("span", { tabIndex: 0, className: sortSpanBackgoundClass, onKeyPress: _this.handleColumnSortPress({
index: index,
name: column.name,
}), onClick: _this.handleColumnSortPress({
index: index,
name: column.name,
}) },
React.createElement(utils_2.Icon, { type: SynapseTableConstants_1.ICON_STATE[columnIndex], cssClass: isSelectedIconClass })))))));
}
else {
return React.createElement("th", { className: "SRC-hidden", key: column.name });
}
});
// also push the access column if we are showing user access for individual items (must be logged in)
if (isShowingAccessColumn) {
tableColumnHeaderElements.unshift(React.createElement("th", { key: "accessColumn" },
React.createElement("div", { className: "SRC-centerContent" },
React.createElement("span", { style: { whiteSpace: 'nowrap' } }, "Access"))));
}
// add direct download column if logged in
if (isShowingDownloadColumn) {
tableColumnHeaderElements.unshift(React.createElement("th", { key: "downloadColumn" },
React.createElement("div", { className: "SRC-centerContent" }, "\u00A0")));
}
if (isShowingAddToV2DownloadListColumn) {
tableColumnHeaderElements.unshift(React.createElement("th", { key: "addToV2DownloadListColumn" },
React.createElement("div", { className: "SRC-centerContent" }, "\u00A0")));
}
if (isRowSelectionVisible) {
tableColumnHeaderElements.unshift(React.createElement("th", { key: "rowSelectionColumn" },
React.createElement("div", { className: "SRC-centerContent" })));
}
return tableColumnHeaderElements;
};
/**
* Utility to search through array of objects and find object with key "column"
* equal to input parameter "name"
*
* @param {*} sortedColumnSelection
* @param {*} name
* @returns -1 if not present, otherwise the index of the object
* @memberof SynapseTable
*/
SynapseTable.prototype.findSelectionIndex = function (sortedColumnSelection, name) {
if (sortedColumnSelection.length !== 0) {
// find if the current selection exists already and remove it
return sortedColumnSelection.findIndex(function (el) { return el.column === name; });
}
return -1;
};
// Direct user to corresponding query on synapse
SynapseTable.prototype.advancedSearch = function (event) {
event && event.preventDefault();
var lastQueryRequest = this.props.getLastQueryRequest();
var query = lastQueryRequest.query;
// base 64 encode the json of the query and go to url with the encoded object
var encodedQuery = btoa(JSON.stringify(query));
var synTable = lastQueryRequest.entityId;
window.open(getEndpoint_1.PRODUCTION_ENDPOINT_CONFIG.PORTAL + "#!Synapse:" + synTable + "/tables/query/" + encodedQuery, '_blank');
};
SynapseTable.prototype.getLengthOfPropsData = function () {
var data = this.props.data;
return data.queryResult.queryResults.headers.length;
};
/**
* Show the dropdown menu for a column that has been faceted
*
* @param {number} index this is column index of the query table data
* @param {string} columnName this is the name of the column
* @param {FacetColumnResult[]} facetColumnResults
* @param {number} facetIndex
* @returns
* @memberof SynapseTable
*/
SynapseTable.prototype.configureFacetDropdown = function (facetColumnResult, columnModel, lastQueryRequest, facetAliases) {
var _this = this;
return (React.createElement(EnumFacetFilter_1.EnumFacetFilter, { containerAs: "Dropdown", facetValues: facetColumnResult.facetValues, columnModel: columnModel, facetAliases: facetAliases, onChange: function (facetNamesMap) {
(0, QueryFilter_1.applyMultipleChangesToValuesColumn)(lastQueryRequest, facetColumnResult, _this.applyChangesFromQueryFilter, facetNamesMap);
}, onClear: function () {
(0, QueryFilter_1.applyChangesToValuesColumn)(lastQueryRequest, facetColumnResult, _this.applyChangesFromQueryFilter);
} }));
};
SynapseTable.contextType = SynapseContext_1.SynapseContext;
return SynapseTable;
}(React.Component));
exports.default = SynapseTable;
//# sourceMappingURL=SynapseTable.js.map