diff --git a/docs/screenshots/views/metadata/CanonicalDatasetHandlerViewView.png b/docs/screenshots/views/metadata/CanonicalDatasetHandlerViewView.png new file mode 100644 index 000000000..2ca4f06a6 Binary files /dev/null and b/docs/screenshots/views/metadata/CanonicalDatasetHandlerViewView.png differ diff --git a/src/js/views/CanonicalDatasetHandlerView.js b/src/js/views/CanonicalDatasetHandlerView.js index 632067307..3f3ac5885 100644 --- a/src/js/views/CanonicalDatasetHandlerView.js +++ b/src/js/views/CanonicalDatasetHandlerView.js @@ -11,15 +11,12 @@ define([ // The URI for the prov:wasDerivedFrom annotation const PROV_WAS_DERIVED_FROM = "http://www.w3.org/ns/prov#wasDerivedFrom"; - // What to call the field that links to the original dataset - const CANONICAL_LABEL = "Canonical Dataset"; - // The text to display in the info tooltip to explain what the canonical - // dataset field means - const CANONICAL_TOOLTIP_TEXT = - "The original dataset this version was derived from. This dataset is essentially a duplicate of the original."; + // The text to show in the alert box at the top of the MetadataView + const ALERT_TEXT = + "This version of the dataset is a replica or minor variant of the original dataset:"; // The text to display in the info tooltip to explain what the info icon means const INFO_ICON_TOOLTIP_TEXT = - "This dataset is essentially a duplicate of of another, original dataset."; + "This dataset is replica or minor variant of another, original dataset."; // In the citation modal, the heading to use for the dataone version citation const CITATION_TITLE_DATAONE = "This Version of the Dataset"; // In the citation modal, the heading to use for the canonical dataset @@ -30,20 +27,23 @@ define([ // The bootstrap icon name to use for the info icon const INFO_ICON_NAME = "icon-copy"; + // Class names used in this view + const CLASS_NAMES = { + alertBox: ["alert", "alert-info", "alert-block"], // TODO: need alert-block? + alertIcon: ["icon", "icon-info-sign", "icon-on-left"], + alertCitation: "canonical-citation", + }; + // The following properties are used to identify parts of the MetadataView. // If the MetadataView changes, these properties may need to be updated. - - // The name of the property on the MetadataView that contains subviews - const SUBVIEW_PROP = "subviews"; - // The name of the property on the MetadataView that contains the citation - // modal - const CITATION_MODAL_PROP = "citationModal"; - // Class names used in the MetadataView that we also need to use in this view - const METADATA_VIEW_CLASS_NAMES = { - fieldItem: "control-group", - fieldLabel: "control-label", - fieldValue: ["controls", "controls-well"], - fieldInfoIcon: ["tooltip-this", "icon", "icon-info-sign"], + const METADATA_VIEW = { + // The selector for the container that contains the info icons and metrics buttons + controlsSelector: "#metadata-controls-container", + // The name of the property on the MetadataView that contains the citation + // modal + citationModalProp: "citationModal", + // The name of the property on the MetadataView that contains subviews + subviewProp: "subviews", }; /** @@ -72,32 +72,6 @@ define([ */ metdataView: null, - /** - * The value of the label to insert the canonical dataset field before. - * @type {string} - */ - insertFieldBefore: "Identifier", - - /** - * Creates a field item for the MetadataView. - * @param {string} label - The label for the field. - * @param {string} value - The value for the field. - * @param {string} tooltipText - The text to display in the info tooltip. - * @param {object} classes - The classes to apply to the field item. - * @returns {string} The HTML for the field item. - */ - fieldItemTemplate(label, value, tooltipText, classes) { - return `
- -
${value}
-
`; - }, - /** * Initialize the CanonicalDatasetHandlerView. * @param {object} options - A set of options to initialize the view with. @@ -120,8 +94,8 @@ define([ this.reset(); const hasCanonical = this.detectCanonicalDataset(); if (!hasCanonical) return this; - this.fieldItem = this.addFieldItem(); this.infoIcon = this.addInfoIcon(); + this.alertBox = this.addAlertBox(); this.getCitationInfo(); this.modifyCitationModal(); this.hideAnnotations(); @@ -133,8 +107,8 @@ define([ * made by this view. */ reset() { - this.fieldItem?.remove(); this.infoIcon?.remove(); + this.alertBox?.remove(); this.showAnnotations(); this.citationModel.reset(); this.canonicalUri = null; @@ -214,7 +188,7 @@ define([ * @returns {AnnotationView[]} An array of AnnotationView instances. */ getAnnotationViews() { - return this.metadataView[SUBVIEW_PROP].filter( + return this.metadataView[METADATA_VIEW.subviewProp].filter( (view) => view?.type === ANNO_VIEW_TYPE, ); }, @@ -252,7 +226,7 @@ define([ this.stopListening(this.crossRef); this.listenToOnce(this.crossRef, "sync", () => { view.citationModel.setSourceModel(this.crossRef); - view.updateFieldItemWithCitation(); + view.updateAlertBox(); }); this.crossRef.fetch(); }, @@ -293,62 +267,67 @@ define([ }, /** - * Add a row in the MetadataView to display the canonical dataset URI - * @returns {Element} The field item element that was added to the view. + * Adds an alert box to the top of the MetadataView to indicate that the + * dataset being displayed is a replica or minor variant of the original + * dataset. + * @returns {Element} The alert box element that was added to the view. */ - addFieldItem() { - const { canonicalUri, fieldItemTemplate } = this; - const itemHTML = fieldItemTemplate( - CANONICAL_LABEL, - `${canonicalUri}`, - CANONICAL_TOOLTIP_TEXT, - METADATA_VIEW_CLASS_NAMES, + addAlertBox() { + const controls = this.metadataView.el.querySelector( + METADATA_VIEW.controlsSelector, ); - // Vivify the item so we can reference it later - const range = document.createRange(); - const fragment = range.createContextualFragment(itemHTML); - const item = fragment.firstElementChild; + const alertBox = document.createElement("section"); + alertBox.classList.add(...CLASS_NAMES.alertBox); - // Find the parent item that contains the field name the view should - // be inserted before - const labels = Array.from( - this.metadataView.el.querySelectorAll("label"), - ); - const insertBeforeLabel = labels.find( - (label) => label.textContent.trim() === this.insertFieldBefore, - ); - // Insert the new field item before the label - insertBeforeLabel.parentElement.before(item); - return item; - }, + const icon = document.createElement("i"); + icon.classList.add(...CLASS_NAMES.alertIcon); - /** - * Replaces the DOI in the field item with a full citation for the - * canonical dataset. - */ - updateFieldItemWithCitation() { - const { citationModel, fieldItem } = this; - if (!fieldItem) return; + const heading = document.createElement("h5"); + heading.textContent = ALERT_TEXT; + + // Add a div that will contain the citation information + const citeContainer = document.createElement("div"); + citeContainer.classList.add(CLASS_NAMES.alertCitation); + // Just add the URI for now + citeContainer.textContent = this.canonicalUri; + + heading.prepend(icon); + alertBox.append(heading, citeContainer); + + alertBox.style.marginTop = "-1rem"; + heading.style.marginTop = "0"; + citeContainer.style.marginLeft = "1rem"; + + this.alertBox = alertBox; + // Insert the citation view before the metadata controls + controls.before(alertBox); + + return alertBox; + }, + + /** Updates the citation information in the alert box. */ + updateAlertBox() { + const alertBox = this.alertBox || this.addAlertBox(); + const citeContainer = alertBox.querySelector( + `.${CLASS_NAMES.alertCitation}`, + ); + const { citationModel } = this; const citationView = new CitationView({ model: citationModel, - className: "canonical-citation", + // Don't use styles from default class + className: "", createTitleLink: false, openLinkInNewTab: true, }).render(); - - // Replace the DOI with the citation - const fieldValueEl = fieldItem.querySelector( - `.${METADATA_VIEW_CLASS_NAMES.fieldValue.join(".")}`, - ); - fieldValueEl.innerHTML = ""; - fieldValueEl.appendChild(citationView.el); + citeContainer.innerHTML = ""; + citeContainer.appendChild(citationView.el); }, /** Open the citation modal. */ openCitationModal() { - this.metadataView[CITATION_MODAL_PROP].show(); + this.metadataView[METADATA_VIEW.citationModalProp].show(); }, /** @@ -359,7 +338,8 @@ define([ modifyCitationModal() { const view = this; // The CitationModalView is recreated each time it is shown. - const citationModalView = this.metadataView[CITATION_MODAL_PROP]; + const citationModalView = + this.metadataView[METADATA_VIEW.citationModalProp]; this.listenToOnce(citationModalView, "rendered", () => { citationModalView.canonicalDatasetMods = true; // Add heading for each citation diff --git a/src/js/views/MetadataView.js b/src/js/views/MetadataView.js index 93ee05c63..82cd90078 100644 --- a/src/js/views/MetadataView.js +++ b/src/js/views/MetadataView.js @@ -172,7 +172,7 @@ define([ // If we re-render before the first render is complete the view breaks this.stopListening(this, "renderComplete", this.render); this.listenToOnce(this, "renderComplete", this.render); - return; + return this; } this.isRendering = true; this.stopListening();