synapse-react-client
Version:
[](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [](https://badge.fury.io/js/synaps
195 lines • 13.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaDrivenAnnotationEditor = void 0;
var tslib_1 = require("tslib");
var rjsf_core_1 = (0, tslib_1.__importDefault)(require("@sage-bionetworks/rjsf-core"));
var isEmpty_1 = (0, tslib_1.__importDefault)(require("lodash-es/isEmpty"));
var react_1 = (0, tslib_1.__importStar)(require("react"));
var react_bootstrap_1 = require("react-bootstrap");
var react_error_boundary_1 = require("react-error-boundary");
var react_tooltip_1 = (0, tslib_1.__importDefault)(require("react-tooltip"));
var AddToList_1 = (0, tslib_1.__importDefault)(require("../../../assets/icons/AddToList"));
var ErrorUtils_1 = require("../../../utils/ErrorUtils");
var getEndpoint_1 = require("../../../utils/functions/getEndpoint");
var useEntity_1 = require("../../../utils/hooks/SynapseAPI/useEntity");
var useSchema_1 = require("../../../utils/hooks/SynapseAPI/useSchema");
var synapseTypes_1 = require("../../../utils/synapseTypes");
var LoadingScreen_1 = require("../../LoadingScreen");
var AdditionalPropertiesSchemaField_1 = require("./AdditionalPropertiesSchemaField");
var AnnotationEditorUtils_1 = require("./AnnotationEditorUtils");
var CustomAdditionalPropertiesFieldTemplate_1 = require("./CustomAdditionalPropertiesFieldTemplate");
var CustomArrayFieldTemplate_1 = require("./CustomArrayFieldTemplate");
var CustomBooleanWidget_1 = require("./CustomBooleanWidget");
var CustomDateTimeWidget_1 = require("./CustomDateTimeWidget");
var CustomDefaultTemplate_1 = require("./CustomDefaultTemplate");
var CustomObjectFieldTemplate_1 = require("./CustomObjectFieldTemplate");
var CustomSelectWidget_1 = require("./CustomSelectWidget");
var CustomTextWidget_1 = (0, tslib_1.__importDefault)(require("./CustomTextWidget"));
// patternProperties lets us define how to treat additionalProperties in a JSON schema by property name
// here we can ban properties that collide with entity properties by making their schema "not: {}"
var patternPropertiesBannedKeys = synapseTypes_1.entityJsonKeys.reduce(function (current, item) {
current["^" + item + "$"] = { not: {} };
return current;
}, {});
/**
* Renders a form for editing an entity's annotations. The component also supports supplying just a schema ID,
* but work to support annotation flows without an entity (i.e. before creating entities) is not yet complete.
*/
var SchemaDrivenAnnotationEditor = function (props) {
var _a, _b, _c;
var entityId = props.entityId, schemaId = props.schemaId, _d = props.liveValidate, liveValidate = _d === void 0 ? false : _d, _e = props.onSuccess, onSuccess = _e === void 0 ? function () {
/* no-op */
} : _e, onCancel = props.onCancel;
var handleError = (0, react_error_boundary_1.useErrorHandler)();
var formRef = (0, react_1.useRef)(null);
// Annotation fields fetched and modified via the form
var _f = react_1.default.useState(undefined), formData = _f[0], setFormData = _f[1];
// Client-side validation errors
var _g = react_1.default.useState(undefined), validationError = _g[0], setValidationError = _g[1];
// Errors from the backend response
var _h = react_1.default.useState(undefined), submissionError = _h[0], setSubmissionError = _h[1];
var _j = react_1.default.useState(false), showSubmissionError = _j[0], setShowSubmissionError = _j[1];
var _k = react_1.default.useState(false), showConfirmation = _k[0], setShowConfirmation = _k[1];
var ANNOTATION_EDITOR_TOOLTIP_ID = 'AnnotationEditorTooltip';
var _l = (0, useEntity_1.useGetJson)(entityId, {
enabled: !entityId || !formData,
onError: function (e) {
if (e.status >= 500) {
handleError((0, ErrorUtils_1.toError)(e));
}
},
}), entityJson = _l.entityMetadata, annotations = _l.annotations;
(0, react_1.useEffect)(function () {
if (annotations) {
// Put the annotations into a state variable so it can be modified by the form.
setFormData(annotations);
}
}, [annotations]);
var _m = (0, useSchema_1.useGetSchemaBinding)(entityId, {
enabled: !!entityId,
retry: false,
refetchOnWindowFocus: false,
onError: function (e) {
if (e.status >= 500) {
handleError((0, ErrorUtils_1.toError)(e));
}
},
}), schema = _m.data, isLoadingBinding = _m.isLoading;
var _o = (0, useSchema_1.useGetSchema)((_a = schemaId !== null && schemaId !== void 0 ? schemaId : schema === null || schema === void 0 ? void 0 : schema.jsonSchemaVersionInfo.$id) !== null && _a !== void 0 ? _a : '', {
enabled: !!schemaId || !!schema,
select: function (schema) {
// Have to remove the ID because of a bug in RJSF
// https://github.com/rjsf-team/react-jsonschema-form/issues/2471
delete schema.$id;
return schema;
},
onError: function (e) {
if (e.status >= 500) {
handleError((0, ErrorUtils_1.toError)(e));
}
},
}), validationSchema = _o.data, isLoadingSchema = _o.isLoading;
var isLoading = isLoadingBinding || isLoadingSchema;
var mutation = (0, useEntity_1.useUpdateViaJson)({
onSuccess: function () {
onSuccess();
},
onError: function (error) {
setSubmissionError(error);
setShowSubmissionError(true);
},
});
function submitChangedEntity() {
mutation.mutate((0, tslib_1.__assign)((0, tslib_1.__assign)({}, (0, AnnotationEditorUtils_1.dropNullishArrayValues)(formData !== null && formData !== void 0 ? formData : {})), entityJson));
}
return (react_1.default.createElement("div", { className: "bootstrap-4-backport AnnotationEditor" }, isLoading ? (react_1.default.createElement("div", { className: "LoadingPlaceholder" },
react_1.default.createElement(LoadingScreen_1.SynapseSpinner, { size: 30 }))) : (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_tooltip_1.default, { id: ANNOTATION_EDITOR_TOOLTIP_ID }),
entityJson && schema && (react_1.default.createElement(react_bootstrap_1.Alert, { dismissible: false, show: true, variant: "info", transition: false },
react_1.default.createElement("b", null, entityJson.name),
" requires scientific annotations specified by ",
react_1.default.createElement("b", null, schema.jsonSchemaVersionInfo.$id),
'. ',
react_1.default.createElement("b", null,
react_1.default.createElement("a", { href: (0, getEndpoint_1.getEndpoint)(getEndpoint_1.BackendDestinationEnum.REPO_ENDPOINT) + "/repo/v1/schema/type/registered/" + schema.jsonSchemaVersionInfo.$id, target: "_blank", rel: "noopener noreferrer" }, "View required schema")))),
entityJson && (!formData || (0, isEmpty_1.default)(formData)) && !schema && (react_1.default.createElement(react_bootstrap_1.Alert, { dismissible: false, show: true, variant: "info", transition: false },
react_1.default.createElement("b", null, entityJson.name),
" has no annotations. Click the",
' ',
react_1.default.createElement(AddToList_1.default, null),
" button to annotate.")),
react_1.default.createElement(rjsf_core_1.default, { className: "AnnotationEditorForm", liveValidate: liveValidate, noHtml5Validate: true, ArrayFieldTemplate: CustomArrayFieldTemplate_1.CustomArrayFieldTemplate, ObjectFieldTemplate: CustomObjectFieldTemplate_1.CustomObjectFieldTemplate, FieldTemplate: CustomDefaultTemplate_1.CustomDefaultTemplate, ref: formRef, disabled: mutation.isLoading,
/* Errors are displayed by an Alert component below, so we don't show the builtin ErrorList */
ErrorList: function () { return null; }, schema: (0, tslib_1.__assign)((0, tslib_1.__assign)({}, (validationSchema !== null && validationSchema !== void 0 ? validationSchema : {})), { patternProperties: (0, tslib_1.__assign)((0, tslib_1.__assign)({}, ((_b = validationSchema === null || validationSchema === void 0 ? void 0 : validationSchema.patternProperties) !== null && _b !== void 0 ? _b : {})), patternPropertiesBannedKeys), additionalProperties: (_c = validationSchema === null || validationSchema === void 0 ? void 0 : validationSchema.additionalProperties) !== null && _c !== void 0 ? _c : true }), uiSchema: {
'ui:DuplicateKeySuffixSeparator': '_',
additionalProperties: {
'ui:field': AdditionalPropertiesSchemaField_1.AdditionalPropertiesSchemaField,
'ui:FieldTemplate': CustomAdditionalPropertiesFieldTemplate_1.CustomAdditionalPropertiesFieldTemplate,
},
}, transformErrors: AnnotationEditorUtils_1.transformErrors, formData: formData, onChange: function (_a) {
var formData = _a.formData;
setFormData(formData);
setValidationError(undefined);
}, onSubmit: function (_a) {
var formData = _a.formData, errors = _a.errors;
if (errors && errors.length > 0) {
setValidationError(errors);
}
setShowSubmissionError(false);
setFormData(formData);
submitChangedEntity();
}, onError: function (errors) {
// invoked when submit is clicked and there are client-side validation errors
setValidationError(errors);
if (validationError && entityId) {
setShowConfirmation(true);
}
}, widgets: {
TextWidget: CustomTextWidget_1.default,
DateTimeWidget: CustomDateTimeWidget_1.CustomDateTimeWidget,
SelectWidget: CustomSelectWidget_1.CustomSelectWidget,
CheckboxWidget: CustomBooleanWidget_1.CustomBooleanWidget,
} },
validationError && (react_1.default.createElement(react_bootstrap_1.Alert, { className: "ErrorList", dismissible: false, show: true, variant: "danger", transition: false },
react_1.default.createElement("b", null, "Validation errors found:"),
react_1.default.createElement("ul", null, validationError.map(function (e, index) {
return (react_1.default.createElement("li", { key: index },
react_1.default.createElement("b", null, (0, AnnotationEditorUtils_1.getFriendlyPropertyName)(e) + ": "),
' ', "" + e.message));
})))),
submissionError && (react_1.default.createElement(react_bootstrap_1.Alert, { dismissible: false, show: showSubmissionError, variant: 'danger', transition: false },
"Annotations could not be updated: ",
submissionError.reason)),
react_1.default.createElement("hr", null),
react_1.default.createElement("div", { className: "SaveButtonContainer" },
react_1.default.createElement(react_bootstrap_1.Button, { variant: "primary-500", onClick: function () {
formRef.current.submit();
} }, entityId ? 'Save' : 'Validate'),
onCancel && (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement("div", { className: "Spacer" }),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "primary-500", onClick: onCancel }, "Cancel"))))),
showConfirmation && (react_1.default.createElement(ConfirmationModal, { show: true, onSave: function () {
submitChangedEntity();
setShowConfirmation(false);
}, onCancel: function () {
setShowConfirmation(false);
}, errors: validationError }))))));
};
exports.SchemaDrivenAnnotationEditor = SchemaDrivenAnnotationEditor;
var ConfirmationModal = function (_a) {
var show = _a.show, onCancel = _a.onCancel, onSave = _a.onSave, errors = _a.errors;
return (react_1.default.createElement(react_bootstrap_1.Modal, { animation: false, show: show, onHide: onCancel },
react_1.default.createElement(react_bootstrap_1.Modal.Header, { closeButton: true },
react_1.default.createElement(react_bootstrap_1.Modal.Title, null, "Update Annotations")),
react_1.default.createElement(react_bootstrap_1.Modal.Body, null,
react_1.default.createElement("div", null, "The following errors exist with the annotations you entered:"),
react_1.default.createElement("div", null,
react_1.default.createElement("ul", null, (errors !== null && errors !== void 0 ? errors : []).map(function (e, index) { return (react_1.default.createElement("li", { key: index },
react_1.default.createElement("b", null, (0, AnnotationEditorUtils_1.getFriendlyPropertyName)(e) + ": "),
" ", "" + e.message)); }))),
react_1.default.createElement("div", null, "Are you sure you want to save them?")),
react_1.default.createElement(react_bootstrap_1.Modal.Footer, null,
react_1.default.createElement(react_bootstrap_1.Button, { variant: "default", onClick: onCancel }, "Cancel"),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "primary", onClick: onSave }, "Save"))));
};
//# sourceMappingURL=SchemaDrivenAnnotationEditor.js.map