From 8654cb106a92517ba1f8530df4616b5e7a9fe676 Mon Sep 17 00:00:00 2001 From: jazairi <16103405+jazairi@users.noreply.github.com> Date: Thu, 4 Jun 2026 09:24:34 -0700 Subject: [PATCH 1/5] Render fulfillment container only when applicable Why these changes are being introduced: The fulfillment link container currently renders as an empty div when no links are available. This pollutes the DOM and affects spacing. Relevant ticket(s): - [USE-611](https://mitlibraries.atlassian.net/browse/USE-611) How this addresses that need: This removes the parent div if no fulfillment links are present. Side effects of this change: Primo link removal moves further up in the control flow, because that check is not applicable if the parent element is gone. --- .../controllers/content_loader_controller.js | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/app/javascript/controllers/content_loader_controller.js b/app/javascript/controllers/content_loader_controller.js index 429ea148..3440d4c0 100644 --- a/app/javascript/controllers/content_loader_controller.js +++ b/app/javascript/controllers/content_loader_controller.js @@ -12,16 +12,23 @@ export default class extends Controller { .then(response => response.text()) .then(html => { const parentElement = this.element.parentElement; - // Replace the entire element with the fetched HTML - this.element.outerHTML = html; - // Hide primo links if libkey link is present - if (parentElement.querySelector('.libkey-link')) { - const resultGet = parentElement.closest('.result-get'); - if (resultGet) { - const primoLinks = resultGet.querySelectorAll('.primo-link'); - // removing instead of hiding to avoid layout issues when selecting which link to highlight - primoLinks.forEach(link => link.remove()); + // Strip HTML comments and trim whitespace + const cleanedHtml = html.replace(//g, '').trim(); + // Replace the entire element with the fetched HTML, or remove if empty + if (cleanedHtml) { + this.element.outerHTML = cleanedHtml; + // Hide primo links if libkey link is present + if (parentElement.querySelector('.libkey-link')) { + const resultGet = parentElement.closest('.result-get'); + if (resultGet) { + const primoLinks = resultGet.querySelectorAll('.primo-link'); + // removing instead of hiding to avoid layout issues when selecting which link to highlight + primoLinks.forEach(link => link.remove()); + } } + } else { + // Remove result-get container (no fulfillment links) + parentElement.remove(); } }) } From 97e04c52ebcc1bea08b1f07dc244e76daed624f7 Mon Sep 17 00:00:00 2001 From: jazairi <16103405+jazairi@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:50:13 -0700 Subject: [PATCH 2/5] Address security issue identified by CodeQL --- .../controllers/content_loader_controller.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/javascript/controllers/content_loader_controller.js b/app/javascript/controllers/content_loader_controller.js index 3440d4c0..87f090d1 100644 --- a/app/javascript/controllers/content_loader_controller.js +++ b/app/javascript/controllers/content_loader_controller.js @@ -7,13 +7,25 @@ export default class extends Controller { this.load() } + stripHtmlComments(input) { + let previous + let output = input + + do { + previous = output + output = output.replace(//g, '') + } while (output !== previous) + + return output + } + load() { fetch(this.urlValue) .then(response => response.text()) .then(html => { const parentElement = this.element.parentElement; // Strip HTML comments and trim whitespace - const cleanedHtml = html.replace(//g, '').trim(); + const cleanedHtml = this.stripHtmlComments(html).trim(); // Replace the entire element with the fetched HTML, or remove if empty if (cleanedHtml) { this.element.outerHTML = cleanedHtml; From 0fb3f130bc7eabe2ec71a1ee6d9ed70b8af021a2 Mon Sep 17 00:00:00 2001 From: jazairi <16103405+jazairi@users.noreply.github.com> Date: Thu, 4 Jun 2026 12:50:43 -0700 Subject: [PATCH 3/5] Remove semicolons --- .../controllers/content_loader_controller.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/javascript/controllers/content_loader_controller.js b/app/javascript/controllers/content_loader_controller.js index 87f090d1..cb421fbf 100644 --- a/app/javascript/controllers/content_loader_controller.js +++ b/app/javascript/controllers/content_loader_controller.js @@ -23,24 +23,24 @@ export default class extends Controller { fetch(this.urlValue) .then(response => response.text()) .then(html => { - const parentElement = this.element.parentElement; + const parentElement = this.element.parentElement // Strip HTML comments and trim whitespace - const cleanedHtml = this.stripHtmlComments(html).trim(); + const cleanedHtml = this.stripHtmlComments(html).trim() // Replace the entire element with the fetched HTML, or remove if empty if (cleanedHtml) { - this.element.outerHTML = cleanedHtml; + this.element.outerHTML = cleanedHtml // Hide primo links if libkey link is present if (parentElement.querySelector('.libkey-link')) { - const resultGet = parentElement.closest('.result-get'); + const resultGet = parentElement.closest('.result-get') if (resultGet) { - const primoLinks = resultGet.querySelectorAll('.primo-link'); + const primoLinks = resultGet.querySelectorAll('.primo-link') // removing instead of hiding to avoid layout issues when selecting which link to highlight - primoLinks.forEach(link => link.remove()); + primoLinks.forEach(link => link.remove()) } } } else { // Remove result-get container (no fulfillment links) - parentElement.remove(); + parentElement.remove() } }) } From c8f65a3222d96f60f95d889f835e68f3afd68830 Mon Sep 17 00:00:00 2001 From: jazairi <16103405+jazairi@users.noreply.github.com> Date: Thu, 4 Jun 2026 17:01:41 -0700 Subject: [PATCH 4/5] Safely remove container --- app/javascript/controllers/content_loader_controller.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/javascript/controllers/content_loader_controller.js b/app/javascript/controllers/content_loader_controller.js index cb421fbf..67fbba52 100644 --- a/app/javascript/controllers/content_loader_controller.js +++ b/app/javascript/controllers/content_loader_controller.js @@ -39,8 +39,12 @@ export default class extends Controller { } } } else { - // Remove result-get container (no fulfillment links) - parentElement.remove() + // Remove only this loader element + this.element.remove(); + // Remove result-get container if it's now empty (no fulfillment links and no other content) + if (!parentElement.textContent.trim()) { + parentElement.remove(); + } } }) } From 57a4d80280923cb47e44a6c9b437c588423e23f5 Mon Sep 17 00:00:00 2001 From: jazairi <16103405+jazairi@users.noreply.github.com> Date: Fri, 5 Jun 2026 09:04:57 -0700 Subject: [PATCH 5/5] Disable annotations in development and simplify cleanup logic --- .../controllers/content_loader_controller.js | 24 ++++--------------- config/environments/development.rb | 3 ++- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/app/javascript/controllers/content_loader_controller.js b/app/javascript/controllers/content_loader_controller.js index 67fbba52..3db0192b 100644 --- a/app/javascript/controllers/content_loader_controller.js +++ b/app/javascript/controllers/content_loader_controller.js @@ -7,28 +7,14 @@ export default class extends Controller { this.load() } - stripHtmlComments(input) { - let previous - let output = input - - do { - previous = output - output = output.replace(//g, '') - } while (output !== previous) - - return output - } - load() { fetch(this.urlValue) .then(response => response.text()) .then(html => { const parentElement = this.element.parentElement - // Strip HTML comments and trim whitespace - const cleanedHtml = this.stripHtmlComments(html).trim() // Replace the entire element with the fetched HTML, or remove if empty - if (cleanedHtml) { - this.element.outerHTML = cleanedHtml + if (html.trim()) { + this.element.outerHTML = html // Hide primo links if libkey link is present if (parentElement.querySelector('.libkey-link')) { const resultGet = parentElement.closest('.result-get') @@ -39,11 +25,9 @@ export default class extends Controller { } } } else { - // Remove only this loader element - this.element.remove(); - // Remove result-get container if it's now empty (no fulfillment links and no other content) + this.element.remove() if (!parentElement.textContent.trim()) { - parentElement.remove(); + parentElement.remove() } } }) diff --git a/config/environments/development.rb b/config/environments/development.rb index 9b673600..2d0b3b37 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -68,7 +68,8 @@ # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - config.action_view.annotate_rendered_view_with_filenames = true + # Disabled because annotation comments pollute dynamically-loaded HTML fragments (e.g. ThirdIron responses) + # config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true