UNPKG

synapse-react-client

Version:

[![Build Status](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client.svg?branch=main)](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [![npm version](https://badge.fury.io/js/synapse-react-client.svg)](https://badge.fury.io/js/synaps

195 lines 13.6 kB
"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