Repository: phac-nml/irida-next
Pull request #1062: Import Metadata UI: Enhancements
Author: login: ChrisHuynh333
Labels: name: ready for review
description: Pull request is ready for review
Created: 2025-05-06T20:26:51Z | Merged: 2025-05-09T20:54:25Z

## What does this PR do and why?
This PR adds some additional enhancements to the import metadata UI:
- Auto-selects sample column
- Metadata selections don't reset if sample column selection is changed
- Error state if a spreadsheet is uploaded with no metadata

## Screenshots or screen recordings
Error state:
![image](https://github.com/user-attachments/assets/d1f8ee64-2516-4c41-8e3a-7f234d1d1bec)

## How to set up and validate locally
Test sample autoselection:
1. Navigate to a group or project you can import metadata to, and upload `/test/fixtures/files/metadata/valid.csv`.
2. Verify `sample_name` is auto-selected, and metadatafields 1, 2, and 3 are displayed in the selected metadata list.
3. Verify the following:
- Moving all the metadata to the `Available` list disables the submit button
- De-selecting sample ID column disables the submit button and hides the metadata listing
- Selecting a field other than `sample_name` for sample ID column leaves only the unselected metadatafield values in the metadata listing, and `sample_name` is not there.

Test Error state:
1. Upload the file `test/fixtures/files/batch_sample_import/project/valid.csv`
2. Verify the error message appears (because only sample, description, and project puid columns are available in the spreadsheet, all of which are ignored).
3. Verify the submit button is disabled.

## PR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

- [x] I have evaluated the [PR acceptance checklist](https://phac-nml.github.io/irida-next/docs/development/development_processes/code_review#acceptance-checklist) for this PR.



Diff:
diff --git a/app/javascript/controllers/metadata/file_import_controller.js b/app/javascript/controllers/metadata/file_import_controller.js
index d8d7d638bc..a3c6ece8fc 100644
--- a/app/javascript/controllers/metadata/file_import_controller.js
+++ b/app/javascript/controllers/metadata/file_import_controller.js
@@ -3,50 +3,53 @@ import * as XLSX from "xlsx";
 import { Controller } from "@hotwired/stimulus";
 
 export default class extends Controller {
+  static outlets = ["viral--sortable-lists--two-lists-selection"];
   static targets = [
     "sampleIdColumn",
     "metadataColumns",
+    "submitButton",
+    "error",
     "sortableListsTemplate",
     "sortableListsItemTemplate",
-    "submitButton",
   ];
 
-  #headers = [];
-  #disabled_classes = [
-    "bg-slate-50",
-    "border",
-    "border-slate-300",
-    "text-slate-900",
-    "text-sm",
-    "rounded-lg",
-    "block",
-    "w-full",
-    "p-2.5",
-    "dark:bg-slate-700",
-    "dark:border-slate-600",
-    "dark:placeholder-slate-400",
-    "dark:text-white",
+  #defaultSampleColumnHeaders = [
+    "sample_name",
+    "sample name",
+    "sample",
+    "sample_id",
+    "sample id",
+    "sample_puid",
+    "sample puid",
   ];
 
-  connect() {
-    this.#disableTarget(this.sampleIdColumnTarget);
-  }
+  #ignoreList = [
+    "sample_name",
+    "sample name",
+    "sample",
+    "sample_id",
+    "sample id",
+    "sample_puid",
+    "sample puid",
+    "project id",
+    "project_id",
+    "project_puid",
+    "project puid",
+    "created_at",
+    "updated_at",
+    "last_updated_at",
+    "description",
+  ];
+
+  #headers = [];
 
   changeSampleIDInput(event) {
     const { value } = event.target;
-
     if (value) {
-      if (this.hasMetadataColumnsTarget) {
-        this.#addMetadataColumns();
-      } else {
-        this.submitButtonTarget.disabled = false;
-      }
+      this.#enableDialogState();
+      this.#sortableListsConnect();
     } else {
-      if (this.hasMetadataColumnsTarget) {
-        this.#removeMetadataColumns();
-      } else {
-        this.submitButtonTarget.disabled = true;
-      }
+      this.#resetDialogState();
     }
   }
 
@@ -54,9 +57,8 @@ export default class extends Controller {
     const { files } = event.target;
 
     this.#removeSampleIDInputOptions();
-    this.#removeMetadataColumns();
-    this.submitButtonTarget.disabled = true;
-
+    this.#resetDialogState();
+    this.#disableErrorState();
     if (!files.length) {
       return;
     }
@@ -73,12 +75,6 @@ export default class extends Controller {
     };
   }
 
-  #removeMetadataColumns() {
-    if (this.hasMetadataColumnsTarget) {
-      this.metadataColumnsTarget.innerHTML = "";
-    }
-  }
-
   #removeSampleIDInputOptions() {
     this.#removeInputOptions(this.sampleIdColumnTarget);
     this.#disableTarget(this.sampleIdColumnTarget);
@@ -91,36 +87,46 @@ export default class extends Controller {
       option.text = header;
       this.sampleIdColumnTarget.append(option);
     }
+    // lower case to test for case insensitivity
+    const allHeadersToLowerCase = this.#headers.map((header) =>
+      header.toLowerCase(),
+    );
+    for (const sampleColumnHeader of this.#defaultSampleColumnHeaders) {
+      const sampleIndex = allHeadersToLowerCase.indexOf(sampleColumnHeader);
+      if (sampleIndex > -1) {
+        this.sampleIdColumnTarget.value = this.#headers[sampleIndex];
+        this.#enableDialogState();
+        this.#sortableListsConnect();
+        break;
+      }
+    }
     this.#enableTarget(this.sampleIdColumnTarget);
   }
 
   #addMetadataColumns() {
-    const ignoreList = [
-      "sample id",
-      "sample name",
-      "project id",
-      "created_at",
-      "updated_at",
-      "last_updated_at",
-    ];
-
     let columns = this.#headers.filter(
       (header) =>
-        !ignoreList.includes(header.toLowerCase()) &&
+        !this.#ignoreList.includes(header.toLowerCase()) &&
         header.toLowerCase() != this.sampleIdColumnTarget.value.toLowerCase(),
     );
 
-    this.metadataColumnsTarget.innerHTML =
-      this.sortableListsTemplateTarget.innerHTML;
-
-    columns.forEach((column) => {
-      const template =
-        this.sortableListsItemTemplateTarget.content.cloneNode(true);
-      template.querySelector("li").innerText = column;
-      template.querySelector("li").id = column.replace(/\s+/g, "-");
-      this.metadataColumnsTarget.querySelector("#selected").append(template);
-    });
-    this.submitButtonTarget.disabled = !columns.length;
+    if (columns.length > 0) {
+      this.#unhideElement(this.metadataColumnsTarget);
+      this.metadataColumnsTarget.innerHTML =
+        this.sortableListsTemplateTarget.innerHTML;
+
+      columns.forEach((column) => {
+        const template =
+          this.sortableListsItemTemplateTarget.content.cloneNode(true);
+        template.querySelector("li").innerText = column;
+        template.querySelector("li").id = column.replace(/\s+/g, "-");
+        this.metadataColumnsTarget.querySelector("#Selected").append(template);
+      });
+      this.submitButtonTarget.disabled = !columns.length;
+    } else {
+      this.#hideElement(this.metadataColumnsTarget);
+      this.#enableErrorState();
+    }
   }
 
   #removeInputOptions(target) {
@@ -131,11 +137,51 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
-    target.classList.add(...this.#disabled_classes);
+    target.setAttribute("aria-disabled", "true");
   }
 
   #enableTarget(target) {
     target.disabled = false;
-    target.classList.remove(...this.#disabled_classes);
+    target.removeAttribute("aria-disabled");
+  }
+
+  #resetDialogState() {
+    this.#disableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#hideElement(this.metadataColumnsTarget);
+      this.errorTarget.setAttribute("aria-disabled", "true");
+    }
+  }
+
+  #enableDialogState() {
+    this.#enableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#addMetadataColumns();
+    }
+  }
+
+  #disableErrorState() {
+    this.#hideElement(this.errorTarget);
+  }
+
+  #enableErrorState() {
+    this.#unhideElement(this.errorTarget);
+    this.#disableTarget(this.submitButtonTarget);
+  }
+
+  #hideElement(element) {
+    element.classList.add("hidden");
+    element.setAttribute("aria-disabled", "true");
+  }
+
+  #unhideElement(element) {
+    element.classList.remove("hidden");
+    element.removeAttribute("aria-hidden");
+  }
+
+  #sortableListsConnect() {
+    if (this.hasViralSortableListsTwoListsSelectionOutlet) {
+      this.viralSortableListsTwoListsSelectionOutlet.idempotentConnect();
+    }
   }
 }
diff --git a/app/javascript/controllers/viral/sortable_lists/two_lists_selection_controller.js b/app/javascript/controllers/viral/sortable_lists/two_lists_selection_controller.js
index 6fe705d03a..dfea744cab 100644
--- a/app/javascript/controllers/viral/sortable_lists/two_lists_selection_controller.js
+++ b/app/javascript/controllers/viral/sortable_lists/two_lists_selection_controller.js
@@ -30,7 +30,6 @@ export default class extends Controller {
     // Get a handle on the available and selected lists
     this.availableList = document.getElementById(this.availableListValue);
     this.selectedList = document.getElementById(this.selectedListValue);
-
     if (this.availableList && this.selectedList) {
       // Get a handle on the original available list
       this.#originalAvailableList = [
diff --git a/app/views/shared/metadata_templates/_edit_template_dialog.html.erb b/app/views/shared/metadata_templates/_edit_template_dialog.html.erb
index d7e450456b..a2b1f3dced 100644
--- a/app/views/shared/metadata_templates/_edit_template_dialog.html.erb
+++ b/app/views/shared/metadata_templates/_edit_template_dialog.html.erb
@@ -4,8 +4,8 @@
   <%= turbo_frame_tag "metadata_template_dialog_content" do %>
     <div
       data-controller="viral--sortable-lists--two-lists-selection"
-      data-viral--sortable-lists--two-lists-selection-selected-list-value="selected"
-      data-viral--sortable-lists--two-lists-selection-available-list-value="available"
+      data-viral--sortable-lists--two-lists-selection-selected-list-value="<%=  t("metadata_templates.edit_template_dialog.selected") %>"
+      data-viral--sortable-lists--two-lists-selection-available-list-value="<%= t("metadata_templates.edit_template_dialog.available") %>"
       data-viral--sortable-lists--two-lists-selection-field-name-value="metadata_template[fields][]"
       class="font-normal text-slate-500 dark:text-slate-400"
     >
@@ -34,7 +34,7 @@
                   description: t("metadata_templates.edit_template_dialog.description"),
                   ) do |sortable_lists| %>
             <%= sortable_lists.with_list(
-              id: "available",
+              id: t("metadata_templates.edit_template_dialog.available"),
               title: t("metadata_templates.edit_template_dialog.available"),
               list_items: @available_metadata_fields,
               group: "metadata_selection",
@@ -42,7 +42,7 @@
               list_classes: "overflow-y-auto max-w-[356px] min-w-[356px] w-full",
             ) %>
             <%= sortable_lists.with_list(
-              id: "selected",
+              id: t("metadata_templates.edit_template_dialog.selected"),
               title: t("metadata_templates.edit_template_dialog.selected"),
               list_items: @current_template_fields,
               group: "metadata_selection",
diff --git a/app/views/shared/metadata_templates/_new_template_dialog.html.erb b/app/views/shared/metadata_templates/_new_template_dialog.html.erb
index e97a20cbfe..efcd7e8904 100644
--- a/app/views/shared/metadata_templates/_new_template_dialog.html.erb
+++ b/app/views/shared/metadata_templates/_new_template_dialog.html.erb
@@ -4,8 +4,8 @@
   <%= turbo_frame_tag "metadata_template_dialog_content" do %>
     <div
       data-controller="viral--sortable-lists--two-lists-selection"
-      data-viral--sortable-lists--two-lists-selection-selected-list-value="selected"
-      data-viral--sortable-lists--two-lists-selection-available-list-value="available"
+      data-viral--sortable-lists--two-lists-selection-selected-list-value="<%= t("metadata_templates.new_template_dialog.selected") %>"
+      data-viral--sortable-lists--two-lists-selection-available-list-value="<%= t("metadata_templates.new_template_dialog.available") %>"
       data-viral--sortable-lists--two-lists-selection-field-name-value="metadata_template[fields][]"
       class="font-normal text-slate-500 dark:text-slate-400"
     >
@@ -35,7 +35,7 @@
             description: t("metadata_templates.new_template_dialog.description")
             ) do |sortable_lists| %>
               <%= sortable_lists.with_list(
-                id: "available",
+                id: t("metadata_templates.new_template_dialog.available"),
                 title: t("metadata_templates.new_template_dialog.available"),
                 list_items: @available_metadata_fields,
                 group: "metadata_selection",
@@ -43,7 +43,7 @@
                 list_classes: "overflow-y-auto max-w-[356px] min-w-[356px] w-full",
               ) %>
               <%= sortable_lists.with_list(
-                id: "selected",
+                id: t("metadata_templates.new_template_dialog.selected"),
                 title: t("metadata_templates.new_template_dialog.selected"),
                 list_items: @current_template_fields,
                 group: "metadata_selection",
diff --git a/app/views/shared/samples/metadata/file_imports/_dialog.html.erb b/app/views/shared/samples/metadata/file_imports/_dialog.html.erb
index 0f9520eeeb..15d8bccdc7 100644
--- a/app/views/shared/samples/metadata/file_imports/_dialog.html.erb
+++ b/app/views/shared/samples/metadata/file_imports/_dialog.html.erb
@@ -8,13 +8,15 @@
     <div
       <% if Flipper.enabled?(:metadata_import_field_selection) %>
       data-controller="attachment-upload metadata--file-import viral--sortable-lists--two-lists-selection"
-      data-viral--sortable-lists--two-lists-selection-selected-list-value="selected"
-      data-viral--sortable-lists--two-lists-selection-available-list-value="available"
+      data-viral--sortable-lists--two-lists-selection-selected-list-value="<%= t(".selected") %>"
+      data-viral--sortable-lists--two-lists-selection-available-list-value="<%= t(".available") %>"
       data-viral--sortable-lists--two-lists-selection-field-name-value="file_import[metadata_columns][]"
+      data-metadata--file-import-viral--sortable-lists--two-lists-selection-outlet="#import-metadata-dialog-content"
       <% else %>
       data-controller="attachment-upload metadata--file-import"
-      <% end%>
+      <% end %>
       data-attachment-upload-button-text-value='<%=t(".uploading") %>'
+      id="import-metadata-dialog-content"
     >
       <%= form_for(:file_import, url: url, method: :post) do |form| %>
         <input type="hidden" name="broadcast_target" value="<%= @broadcast_target %>"/>
@@ -30,6 +32,22 @@
               <%= t(".namespace.project.description_html") %>
             <% end %>
           </p>
+          <div
+            role="alert"
+            class="
+              flex items-center hidden p-4 mb-2 text-red-800 border-l-4 border-red-300
+              bg-red-50 dark:text-red-400 dark:bg-slate-800 dark:border-red-800
+            "
+            aria-hidden="true"
+            data-metadata--file-import-target="error"
+          >
+            <span class="inline shrink-0">
+              <%= viral_icon(name: "exclamation_circle", classes: "w-5 h-5 mr-3 stroke-2") %>
+            </span>
+            <div>
+              <div class="font-medium whitespace-break-spaces"><%= t(".no_valid_metadata") %></div>
+            </div>
+          </div>
           <div class="form-field">
             <%= form.label :file,
                        t(".file"),
@@ -60,8 +78,13 @@
                         required: true,
                         data: {
                           "metadata--file-import-target": "sampleIdColumn",
-                          action:
-                            "change->metadata--file-import#changeSampleIDInput viral--sortable-lists--two-lists-selection#idempotentConnect",
+                          action: "change->metadata--file-import#changeSampleIDInput",
+                        },
+                        class:
+                          "disabled:bg-slate-50 disabled:text-slate-900 disabled:dark:bg-slate-700 disabled:dark:placeholder-slate-400 disabled:dark:text-white",
+                        disabled: true,
+                        aria: {
+                          disabled: true,
                         } %>
           </div>
           <% if Flipper.enabled?(:metadata_import_field_selection) %>
@@ -76,14 +99,14 @@
                 title: t(".metadata_columns"),
             ) do |sortable_lists| %>
                   <%= sortable_lists.with_list(
-                    id: "available",
+                    id: t(".available"),
                     title: t(".available"),
                     group: "metadata_selection",
                     container_classes: "block mb-1 pr-2 text-sm font-medium",
                     list_classes: "overflow-y-auto max-w-[356px] min-w-[356px] w-full",
                   ) %>
                   <%= sortable_lists.with_list(
-                    id: "selected",
+                    id: t(".selected"),
                     title: t(".selected"),
                     group: "metadata_selection",
                     container_classes: "block mb-1 text-sm font-medium",
@@ -124,7 +147,10 @@
                           "metadata--file-import-target": "submitButton",
                           "attachment-upload-target": "submitButton",
                         },
-                        disabled: true %>
+                        disabled: true,
+                        aria: {
+                          disabled: true,
+                        } %>
           </div>
         </div>
       <% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 23b349e0fc..e2891fb780 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2021,7 +2021,7 @@ en:
             file: File
             file_help: CSV, TSV, XLS or XLSX.
             ignore_empty_values:
-              description: If selected, any metadata fields without an associated value will be ignored and those metadata keys will not be removed from the sample if present. However, if this not selected, any samples with the metadata key and empty value will be deleted.
+              description: If selected, any metadata fields without an associated value will be ignored and those metadata keys will not be removed from the sample if present. However, if this is not selected, any samples with the metadata key and empty value will be deleted.
             metadata_columns: Metadata Columns
             namespace:
               description: The spreadsheet is required to have a column that contains a sample identifier.
@@ -2029,6 +2029,7 @@ en:
                 description_html: The identifier is case-sensitive and must contain the <b>sample ID</b>.
               project:
                 description_html: The identifier is case-sensitive and can contain either the <b>sample name or ID</b>.
+            no_valid_metadata: The uploaded spreadsheet contains no viable metadata
             sample_id_column: Sample ID Column
             select_sample_id_column: Select a Sample ID Column
             selected: Selected
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index fa8fa98899..e10008e7e9 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -2025,6 +2025,7 @@ fr:
                 description_html: L’identificateur est sensible à la casse et doit contenir l’<b>identifiant de l’échantillon</b>.
               project:
                 description_html: L’identificateur est sensible à la casse et peut contenir le <b>nom ou l’identifiant de l’échantillon</b>.
+            no_valid_metadata: The uploaded spreadsheet contains no viable metadata
             sample_id_column: Colonne ID de l’échantillon
             select_sample_id_column: Sélectionner une colonne ID de l’échantillon
             selected: Sélectionné
diff --git a/test/fixtures/files/metadata/contains_ignored_headers.csv b/test/fixtures/files/metadata/contains_ignored_headers.csv
new file mode 100644
index 0000000000..e43d96a127
--- /dev/null
+++ b/test/fixtures/files/metadata/contains_ignored_headers.csv
@@ -0,0 +1,3 @@
+sample_puid,project_puid,description,metadatafield1,metadatafield2,metadatafield3
+INXT_SAM_AAAAAAAAAA,INXT_PRJ_PUID_1,description_1,10,20,30
+INXT_SAM_AAAAAAAAAB,INXT_PRJ_PUID_2,description_2,15,25,35
diff --git a/test/system/groups/metadata_templates_test.rb b/test/system/groups/metadata_templates_test.rb
index 7c75ab0be3..f7949e80af 100644
--- a/test/system/groups/metadata_templates_test.rb
+++ b/test/system/groups/metadata_templates_test.rb
@@ -159,14 +159,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -214,14 +214,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -258,14 +258,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -300,14 +300,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -344,14 +344,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -388,14 +388,14 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -404,14 +404,14 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.add_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
           assert_no_selector 'li'
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
@@ -420,14 +420,14 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.remove_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
           assert_selector 'li', count: 3
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
@@ -468,7 +468,7 @@ def setup
       within('div[data-controller-connected="true"] dialog') do
         assert_text I18n.t('metadata_templates.edit_template_dialog.title')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'unique.metadata.field'
@@ -476,7 +476,7 @@ def setup
         end
 
         # fields currently in template fixture
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.selected')}']" do
           assert_text 'field_1'
           assert_text 'field_2'
           assert_text 'field_3'
@@ -485,14 +485,14 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.add_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'unique.metadata.field'
           assert_no_selector 'li'
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.selected')}']" do
           assert_text 'field_1'
           assert_text 'field_2'
           assert_text 'field_3'
diff --git a/test/system/groups/samples_test.rb b/test/system/groups/samples_test.rb
index b3a71d411f..464928e9ed 100644
--- a/test/system/groups/samples_test.rb
+++ b/test/system/groups/samples_test.rb
@@ -801,7 +801,6 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
         click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
       end
       assert_text I18n.t('shared.progress_bar.in_progress')
@@ -820,14 +819,13 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -850,23 +848,9 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/invalid.txt')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
-          assert_no_text 'header'
-          assert_no_selector 'li'
-        end
-        within "ul[id='selected']" do
-          assert_text 'header'
-          assert_selector 'li', count: 1
-        end
-        click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
-
-        assert_text I18n.t('shared.progress_bar.in_progress')
-
-        perform_enqueued_jobs only: [::Samples::MetadataImportJob]
-      end
-      within %(turbo-frame[id="samples_dialog"]) do
-        assert_text I18n.t('services.spreadsheet_import.invalid_file_extension')
+        assert_no_selector '#Available'
+        assert_no_selector '#Selected'
+        assert find("input[value='#{I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')}'").disabled?
       end
     end
 
@@ -880,14 +864,13 @@ def retrieve_puids
       within('#dialog') do
         attach_file 'file_import[file]',
                     Rails.root.join('test/fixtures/files/metadata/contains_empty_values_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -929,14 +912,13 @@ def retrieve_puids
       within('#dialog') do
         attach_file 'file_import[file]',
                     Rails.root.join('test/fixtures/files/metadata/contains_empty_values_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -969,14 +951,13 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/duplicate_headers.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -999,14 +980,13 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/missing_metadata_rows.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1029,7 +1009,6 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/missing_metadata_columns.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
         assert find("input[value='#{I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')}'").disabled?
       end
     end
@@ -1049,14 +1028,13 @@ def retrieve_puids
       within('#dialog') do
         attach_file 'file_import[file]',
                     Rails.root.join('test/fixtures/files/metadata/mixed_project_samples_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1083,13 +1061,12 @@ def retrieve_puids
       within('#dialog') do
         attach_file 'file_import[file]',
                     Rails.root.join('test/fixtures/files/metadata/contains_analysis_values_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield3'
           assert_selector 'li', count: 2
@@ -1106,6 +1083,78 @@ def retrieve_puids
       end
     end
 
+    test 'uploading spreadsheet with no viable metadata should display error' do
+      group = groups(:group_twelve)
+      visit group_samples_url(group)
+      click_button I18n.t('shared.samples.actions_dropdown.label')
+      click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
+      within('#dialog') do
+        attach_file 'file_import[file]',
+                    Rails.root.join('test/fixtures/files/batch_sample_import/group/valid.csv')
+
+        assert_text I18n.t('shared.samples.metadata.file_imports.dialog.no_valid_metadata')
+        assert find("input[value='#{I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')}'").disabled?
+      end
+    end
+
+    test 'should not import metadata from ignored header values' do
+      visit group_samples_url(@group)
+
+      click_button I18n.t('shared.samples.metadata_templates.label')
+      click_button I18n.t('shared.samples.metadata_templates.fields.all')
+
+      assert_selector 'div[data-test-selector="spinner"]'
+      assert_no_selector 'div[data-test-selector="spinner"]'
+
+      # description and project_puid metadata headers do not exist
+      within('#samples-table table thead tr') do
+        assert_selector 'th', count: 9
+      end
+      within('#samples-table table thead') do
+        assert_text 'METADATAFIELD1'
+        assert_no_text 'DESCRIPTION'
+        assert_no_text 'PROJECT_PUID'
+      end
+      click_button I18n.t('shared.samples.actions_dropdown.label')
+      click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
+      within('#dialog') do
+        attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/contains_ignored_headers.csv')
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
+          assert_no_text 'metadatafield1'
+          assert_no_text 'metadatafield2'
+          assert_no_text 'metadatafield3'
+          assert_no_text 'description'
+          assert_no_text 'project_puid'
+          assert_no_selector 'li'
+        end
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
+          assert_text 'metadatafield1'
+          assert_text 'metadatafield2'
+          assert_text 'metadatafield3'
+          assert_no_text 'description'
+          assert_no_text 'project_puid'
+          assert_selector 'li', count: 3
+        end
+        click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
+      end
+      assert_text I18n.t('shared.progress_bar.in_progress')
+      perform_enqueued_jobs only: [::Samples::MetadataImportJob]
+
+      within %(turbo-frame[id="samples_dialog"]) do
+        assert_text I18n.t('shared.samples.metadata.file_imports.success.description')
+        click_on I18n.t('shared.samples.metadata.file_imports.success.ok_button')
+      end
+
+      assert_selector '#samples-table table thead tr th', count: 10
+      within('#samples-table table') do
+        within('thead') do
+          assert_text 'METADATAFIELD3'
+          assert_no_text 'DESCRIPTION'
+          assert_no_text 'PROJECT_PUID'
+        end
+      end
+    end
+
     test 'dialog close button is hidden during metadata import' do
       visit group_samples_url(@group)
       click_button I18n.t('shared.samples.actions_dropdown.label')
@@ -1115,7 +1164,6 @@ def retrieve_puids
         assert_selector 'button.dialog--close'
 
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
         click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
 
         assert_text I18n.t('shared.progress_bar.in_progress')
diff --git a/test/system/projects/metadata_templates_test.rb b/test/system/projects/metadata_templates_test.rb
index 95ee62c8ae..885a1ef9a6 100644
--- a/test/system/projects/metadata_templates_test.rb
+++ b/test/system/projects/metadata_templates_test.rb
@@ -164,13 +164,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -217,13 +217,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -258,13 +258,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -299,13 +299,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -341,13 +341,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -383,13 +383,13 @@ def setup
         assert_selector 'h1', text: I18n.t('metadata_templates.new_template_dialog.title')
         assert_text I18n.t('metadata_templates.new_template_dialog.description')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -397,13 +397,13 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.add_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
@@ -411,13 +411,13 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.remove_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.new_template_dialog.selected')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
@@ -457,14 +457,14 @@ def setup
       within('div[data-controller-connected="true"] dialog') do
         assert_text I18n.t('metadata_templates.edit_template_dialog.title')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.available')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_selector 'li', count: 2
         end
 
         # fields currently in template fixture
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.selected')}']" do
           assert_text 'field_1'
           assert_text 'field_2'
           assert_text 'field_3'
@@ -473,13 +473,13 @@ def setup
 
         click_button I18n.t('viral.sortable_lists_component.add_all')
 
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_selector 'li'
         end
 
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('metadata_templates.edit_template_dialog.selected')}']" do
           assert_text 'field_1'
           assert_text 'field_2'
           assert_text 'field_3'
diff --git a/test/system/projects/samples_test.rb b/test/system/projects/samples_test.rb
index d25fc39cf3..81d97c7375 100644
--- a/test/system/projects/samples_test.rb
+++ b/test/system/projects/samples_test.rb
@@ -1163,7 +1163,6 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
         click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
         ### ACTIONS END ###
       end
@@ -1241,14 +1240,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1329,8 +1327,7 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid.xls')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
@@ -1338,7 +1335,7 @@ class SamplesTest < ApplicationSystemTestCase
           assert_no_text 'metadatafield5'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1421,8 +1418,7 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid.xlsx')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
@@ -1430,7 +1426,7 @@ class SamplesTest < ApplicationSystemTestCase
           assert_no_text 'metadatafield5'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1483,7 +1479,6 @@ class SamplesTest < ApplicationSystemTestCase
         assert_selector 'button.dialog--close'
 
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid.xlsx')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
         click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
 
         assert_text I18n.t('shared.progress_bar.in_progress')
@@ -1501,33 +1496,16 @@ class SamplesTest < ApplicationSystemTestCase
                                                                            locale: @user.locale))
       ### SETUP END ###
 
-      ### ACTIONS START ###
+      ### ACTIONS AND VERIFY START ###
       click_button I18n.t('shared.samples.actions_dropdown.label')
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/invalid.txt')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
-          assert_no_text 'header'
-          assert_no_selector 'li'
-        end
-        within "ul[id='selected']" do
-          assert_text 'header'
-          assert_selector 'li', count: 1
-        end
-        click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
-      end
-      ### ACTIONS END ###
-
-      ### VERIFY START ###
-      assert_text I18n.t('shared.progress_bar.in_progress')
-      perform_enqueued_jobs only: [::Samples::MetadataImportJob]
-
-      within('#dialog') do
-        # error msg
-        assert_text I18n.t('services.spreadsheet_import.invalid_file_extension')
+        assert_no_selector '#Available'
+        assert_no_selector '#Selected'
+        assert find("input[value='#{I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')}'").disabled?
       end
-      ### VERIFY END ###
+      ### ACTIONS AND VERIFY END ###
     end
 
     test 'should import metadata with ignore empty values' do
@@ -1555,14 +1533,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/contains_empty_values.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1617,14 +1594,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/contains_empty_values.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1663,14 +1639,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/duplicate_headers.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1702,14 +1677,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/missing_metadata_rows.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1741,7 +1715,6 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/missing_metadata_columns.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
         ### ACTIONS END ###
 
         ### VERIFY START ###
@@ -1782,14 +1755,13 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/mixed_project_samples.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield2'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield2'
           assert_text 'metadatafield3'
@@ -1853,13 +1825,12 @@ class SamplesTest < ApplicationSystemTestCase
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/contains_analysis_values.csv')
-        find('#file_import_sample_id_column', wait: 1).find(:xpath, 'option[2]').select_option
-        within "ul[id='available']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
           assert_no_text 'metadatafield1'
           assert_no_text 'metadatafield3'
           assert_no_selector 'li'
         end
-        within "ul[id='selected']" do
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
           assert_text 'metadatafield1'
           assert_text 'metadatafield3'
           assert_selector 'li', count: 2
@@ -1889,6 +1860,79 @@ class SamplesTest < ApplicationSystemTestCase
       end
     end
 
+    test 'uploading spreadsheet with no viable metadata should display error' do
+      subgroup12aa = groups(:subgroup_twelve_a_a)
+      project31 = projects(:project31)
+      visit namespace_project_samples_url(subgroup12aa, project31)
+      click_button I18n.t('shared.samples.actions_dropdown.label')
+      click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
+      within('#dialog') do
+        attach_file 'file_import[file]',
+                    Rails.root.join('test/fixtures/files/batch_sample_import/project/valid.csv')
+
+        assert_text I18n.t('shared.samples.metadata.file_imports.dialog.no_valid_metadata')
+        assert find("input[value='#{I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')}'").disabled?
+      end
+    end
+
+    test 'should not import metadata from ignored header values' do
+      visit namespace_project_samples_url(@namespace, @project)
+
+      click_button I18n.t('shared.samples.metadata_templates.label')
+      click_button I18n.t('shared.samples.metadata_templates.fields.all')
+
+      assert_selector 'div[data-test-selector="spinner"]'
+      assert_no_selector 'div[data-test-selector="spinner"]'
+
+      # description and project_puid metadata headers do not exist
+      within('#samples-table table thead tr') do
+        assert_selector 'th', count: 7
+      end
+      within('#samples-table table thead') do
+        assert_text 'METADATAFIELD1'
+        assert_no_text 'DESCRIPTION'
+        assert_no_text 'PROJECT_PUID'
+      end
+      click_button I18n.t('shared.samples.actions_dropdown.label')
+      click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
+      within('#dialog') do
+        attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/contains_ignored_headers.csv')
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.available')}']" do
+          assert_no_text 'metadatafield1'
+          assert_no_text 'metadatafield2'
+          assert_no_text 'metadatafield3'
+          assert_no_text 'description'
+          assert_no_text 'project_puid'
+          assert_no_selector 'li'
+        end
+        within "ul[id='#{I18n.t('shared.samples.metadata.file_imports.dialog.selected')}']" do
+          assert_text 'metadatafield1'
+          assert_text 'metadatafield2'
+          assert_text 'metadatafield3'
+          assert_no_text 'description'
+          assert_no_text 'project_puid'
+          assert_selector 'li', count: 3
+        end
+        click_on I18n.t('shared.samples.metadata.file_imports.dialog.submit_button')
+      end
+      assert_text I18n.t('shared.progress_bar.in_progress')
+      perform_enqueued_jobs only: [::Samples::MetadataImportJob]
+
+      within %(turbo-frame[id="samples_dialog"]) do
+        assert_text I18n.t('shared.samples.metadata.file_imports.success.description')
+        click_on I18n.t('shared.samples.metadata.file_imports.success.ok_button')
+      end
+
+      assert_selector '#samples-table table thead tr th', count: 8
+      within('#samples-table table') do
+        within('thead') do
+          assert_text 'METADATAFIELD3'
+          assert_no_text 'DESCRIPTION'
+          assert_no_text 'PROJECT_PUID'
+        end
+      end
+    end
+
     test 'should import samples' do
       ### SETUP START ###
       visit namespace_project_samples_url(@namespace, @project)


Reviews:
user: login: ksierks
body: like the refactoring 👍
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T14:45:41Z
user: login: deepsidhu85
body: This is looking good, just a comment which applies to the test files in this PR
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T19:56:20Z
user: login: joshsadam
body: Just a few quick things I noticed
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T20:48:12Z
user: login: ChrisHuynh333
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T15:00:37Z
user: login: ChrisHuynh333
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T15:00:45Z
user: login: ChrisHuynh333
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T15:00:49Z
user: login: ChrisHuynh333
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T15:00:52Z
user: login: ChrisHuynh333
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T15:20:06Z
user: login: joshsadam
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T19:11:04Z
user: login: deepsidhu85
body: Looks good to me!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T20:53:56Z

Review comments:
diff_hunk: @@ -820,14 +819,13 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='Available']" do
path: test/system/groups/samples_test.rb
user: login: deepsidhu85
body: Can you update this id and the one for `Selected` below to use translations as that's how we pass them into the two lists js controller. This comment applies to other locations within this file and other test files for which changes were made to in the PR
created_at: 2025-05-08T19:50:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -91,36 +65,52 @@ export default class extends Controller {
       option.text = header;
       this.sampleIdColumnTarget.append(option);
     }
+    // lower case to test for case insensitivity
+    const allHeadersToLowerCase = this.#headers.map((header) =>
+      header.toLowerCase(),
+    );
+    for (const sampleColumnHeader of this.#defaultSampleColumnHeaders) {
+      const sampleIndex = allHeadersToLowerCase.indexOf(sampleColumnHeader);
+      if (sampleIndex > -1) {
+        this.sampleIdColumnTarget.value = this.#headers[sampleIndex];
+        this.#enableDialogState();
+        break;
+      }
+    }
     this.#enableTarget(this.sampleIdColumnTarget);
   }
 
   #addMetadataColumns() {
     const ignoreList = [
-      "sample id",
+      "sample_name",
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: joshsadam
body: **nitpick:** Can you move this list into a private class variable?  This improves readability, avoids re-creating the array on every method call.
created_at: 2025-05-08T20:45:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
-    target.classList.add(...this.#disabled_classes);
   }
 
   #enableTarget(target) {
     target.disabled = false;
-    target.classList.remove(...this.#disabled_classes);
+  }
+
+  #resetDialogState() {
+    this.#disableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.metadataColumnsTarget.classList.add("hidden");
+    }
+  }
+
+  #enableDialogState() {
+    this.#enableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#addMetadataColumns();
+    }
+  }
+
+  #disableErrorState() {
+    this.errorTarget.classList.add("hidden");
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: joshsadam
body: **nitpick:** Need to add aria-hidden
created_at: 2025-05-08T20:46:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
-    target.classList.add(...this.#disabled_classes);
   }
 
   #enableTarget(target) {
     target.disabled = false;
-    target.classList.remove(...this.#disabled_classes);
+  }
+
+  #resetDialogState() {
+    this.#disableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.metadataColumnsTarget.classList.add("hidden");
+    }
+  }
+
+  #enableDialogState() {
+    this.#enableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#addMetadataColumns();
+    }
+  }
+
+  #disableErrorState() {
+    this.errorTarget.classList.add("hidden");
+  }
+
+  #enableErrorState() {
+    this.errorTarget.classList.remove("hidden");
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: joshsadam
body: **nitpick:** Will need to remove aria-hidden
created_at: 2025-05-08T20:47:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: joshsadam
body: **nitpick:** Add aria-disabled here
created_at: 2025-05-08T20:47:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -91,36 +65,52 @@ export default class extends Controller {
       option.text = header;
       this.sampleIdColumnTarget.append(option);
     }
+    // lower case to test for case insensitivity
+    const allHeadersToLowerCase = this.#headers.map((header) =>
+      header.toLowerCase(),
+    );
+    for (const sampleColumnHeader of this.#defaultSampleColumnHeaders) {
+      const sampleIndex = allHeadersToLowerCase.indexOf(sampleColumnHeader);
+      if (sampleIndex > -1) {
+        this.sampleIdColumnTarget.value = this.#headers[sampleIndex];
+        this.#enableDialogState();
+        break;
+      }
+    }
     this.#enableTarget(this.sampleIdColumnTarget);
   }
 
   #addMetadataColumns() {
     const ignoreList = [
-      "sample id",
+      "sample_name",
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: ChrisHuynh333
body: Added in 6d28d0933b2db50a54f5374216f231a4fa600f9c
created_at: 2025-05-09T15:00:37Z
author_association: COLLABORATOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
-    target.classList.add(...this.#disabled_classes);
   }
 
   #enableTarget(target) {
     target.disabled = false;
-    target.classList.remove(...this.#disabled_classes);
+  }
+
+  #resetDialogState() {
+    this.#disableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.metadataColumnsTarget.classList.add("hidden");
+    }
+  }
+
+  #enableDialogState() {
+    this.#enableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#addMetadataColumns();
+    }
+  }
+
+  #disableErrorState() {
+    this.errorTarget.classList.add("hidden");
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: ChrisHuynh333
body: Thanks for catching these 6d28d0933b2db50a54f5374216f231a4fa600f9c
created_at: 2025-05-09T15:00:45Z
author_association: COLLABORATOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
-    target.classList.add(...this.#disabled_classes);
   }
 
   #enableTarget(target) {
     target.disabled = false;
-    target.classList.remove(...this.#disabled_classes);
+  }
+
+  #resetDialogState() {
+    this.#disableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.metadataColumnsTarget.classList.add("hidden");
+    }
+  }
+
+  #enableDialogState() {
+    this.#enableTarget(this.submitButtonTarget);
+    if (this.hasMetadataColumnsTarget) {
+      this.#addMetadataColumns();
+    }
+  }
+
+  #disableErrorState() {
+    this.errorTarget.classList.add("hidden");
+  }
+
+  #enableErrorState() {
+    this.errorTarget.classList.remove("hidden");
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: ChrisHuynh333
body: 6d28d0933b2db50a54f5374216f231a4fa600f9c
created_at: 2025-05-09T15:00:49Z
author_association: COLLABORATOR
diff_hunk: @@ -131,11 +121,40 @@ export default class extends Controller {
 
   #disableTarget(target) {
     target.disabled = true;
path: app/javascript/controllers/metadata/file_import_controller.js
user: login: ChrisHuynh333
body: 6d28d0933b2db50a54f5374216f231a4fa600f9c
created_at: 2025-05-09T15:00:52Z
author_association: COLLABORATOR
diff_hunk: @@ -820,14 +819,13 @@ def retrieve_puids
       click_button I18n.t('shared.samples.actions_dropdown.import_metadata')
       within('#dialog') do
         attach_file 'file_import[file]', Rails.root.join('test/fixtures/files/metadata/valid_with_puid.csv')
-        find('#file_import_sample_id_column', wait: 1).find("option[value='sample_puid']").select_option
-        within "ul[id='available']" do
+        within "ul[id='Available']" do
path: test/system/groups/samples_test.rb
user: login: ChrisHuynh333
body: Thanks, no idea why I didn't do this initially :). Fixed all selectors for these lists in tests to use translations (as well as some dialogs to use the translation) in 333f46b5a42f24b89152ff477574d8ed3057b1b9
created_at: 2025-05-09T15:20:06Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-06T20:52:32Z
body: ## Code Metrics Report
| Coverage | Test Execution Time |
|---------:|--------------------:|
| 93.7%    | 12m25s              |


### Code coverage of files in pull request scope (100.0%)

|                                                                                                                Files                                                                                                                | Coverage |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|
| [app/views/shared/samples/metadata/file_imports/_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/dbf220e7ed7c4d39357b8a7dbec99e88d8fac895/app%2Fviews%2Fshared%2Fsamples%2Fmetadata%2Ffile_imports%2F_dialog.html.erb) | 100.0%   |

---
Reported by [octocov](https://github.com/k1LoW/octocov)
<!-- octocov -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-06T20:58:49Z
body: ## Code Metrics Report
| Coverage | Test Execution Time |
|---------:|--------------------:|
| 93.7%    | 12m18s              |


### Code coverage of files in pull request scope (100.0%)

|                                                                                                                Files                                                                                                                | Coverage |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|
| [app/views/shared/samples/metadata/file_imports/_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/dbf220e7ed7c4d39357b8a7dbec99e88d8fac895/app%2Fviews%2Fshared%2Fsamples%2Fmetadata%2Ffile_imports%2F_dialog.html.erb) | 100.0%   |

---
Reported by [octocov](https://github.com/k1LoW/octocov)
<!-- octocov -->
author_association: NONE
user: login: deepsidhu85
created_at: 2025-05-08T20:05:21Z
body: While testing this out I observed some weird behaviour. See screencast below

[Screencast from 2025-05-08 15-02-34.webm](https://github.com/user-attachments/assets/1f89d8b5-fc0d-4c2c-972a-2de3a29f86e7)

author_association: CONTRIBUTOR
user: login: ChrisHuynh333
created_at: 2025-05-09T14:29:04Z
body: > While testing this out I observed some weird behaviour. See screencast below
> Screencast.from.2025-05-08.15-02-34.webm

This is current expected behaviour:
- sample_name isn't added to metadata when unselected as it's part of the ignore list. (I was contemplating adding to the dialog description the ignore list so it's less confusing to the user)
- when a metadata field is selected for sample, it's removed from the metadata listing, and if then unselected from sample selection, is readded to selected list, regardless of where it was previously. I didn't think it was worth adding logic to re-add it to the available list if it was previously there (could do so if desired though). If you were wondering about my PR description `Metadata selections don't reset if sample column selection is changed`, if you test on main, and you were to add all the metadata to the available list, and then changed sample selection, all the metadata would move back to the selected list, and that is what I'm trying to prevent
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-05-09T15:14:40Z
body: ## Code Metrics Report
| Coverage | Test Execution Time |
|---------:|--------------------:|
| 93.7%    | 13m2s               |


### Code coverage of files in pull request scope (100.0%)

|                                                                                                                Files                                                                                                                | Coverage |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|
| [app/views/shared/samples/metadata/file_imports/_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/6d28d0933b2db50a54f5374216f231a4fa600f9c/app%2Fviews%2Fshared%2Fsamples%2Fmetadata%2Ffile_imports%2F_dialog.html.erb) | 100.0%   |

---
Reported by [octocov](https://github.com/k1LoW/octocov)
<!-- octocov -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T15:33:44Z
body: ## Code Metrics Report
| Coverage | Test Execution Time |
|---------:|--------------------:|
| 93.7%    | 13m18s              |


### Code coverage of files in pull request scope (100.0%)

|                                                                                                                 Files                                                                                                                 | Coverage |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|
| [app/views/shared/metadata_templates/_edit_template_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/333f46b5a42f24b89152ff477574d8ed3057b1b9/app%2Fviews%2Fshared%2Fmetadata_templates%2F_edit_template_dialog.html.erb) | 100.0%   |
| [app/views/shared/metadata_templates/_new_template_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/333f46b5a42f24b89152ff477574d8ed3057b1b9/app%2Fviews%2Fshared%2Fmetadata_templates%2F_new_template_dialog.html.erb)   | 100.0%   |
| [app/views/shared/samples/metadata/file_imports/_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/333f46b5a42f24b89152ff477574d8ed3057b1b9/app%2Fviews%2Fshared%2Fsamples%2Fmetadata%2Ffile_imports%2F_dialog.html.erb)   | 100.0%   |

---
Reported by [octocov](https://github.com/k1LoW/octocov)
<!-- octocov -->
author_association: NONE
user: login: joshsadam
created_at: 2025-05-09T15:57:15Z
body: > > While testing this out I observed some weird behaviour. See screencast below
> > Screencast.from.2025-05-08.15-02-34.webm
> 
> This is current expected behaviour:
> 
> * sample_name isn't added to metadata when unselected as it's part of the ignore list. (I was contemplating adding to the dialog description the ignore list so it's less confusing to the user)
> * when a metadata field is selected for sample, it's removed from the metadata listing, and if then unselected from sample selection, is readded to selected list, regardless of where it was previously. I didn't think it was worth adding logic to re-add it to the available list if it was previously there (could do so if desired though). If you were wondering about my PR description `Metadata selections don't reset if sample column selection is changed`, if you test on main, and you were to add all the metadata to the available list, and then changed sample selection, all the metadata would move back to the selected list, and that is what I'm trying to prevent

I think the simplest way to remove confusion is to reset the state below the changed field to it's original.  So in this case, since all the fields are selected initially, if you change the sample id column, revert this back to all fields being selected.
author_association: CONTRIBUTOR
user: login: deepsidhu85
created_at: 2025-05-09T16:48:38Z
body: > ou change the sample id co

Yeah I think this would be the simplest and clearest way!
author_association: CONTRIBUTOR
user: login: ChrisHuynh333
created_at: 2025-05-09T18:02:25Z
body: > > ou change the sample id co
> 
> Yeah I think this would be the simplest and clearest way!

Reverted in 4277116b280e550b7d6b5b276fad870693b3b818
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-05-09T20:18:42Z
body: ## Code Metrics Report
| Coverage | Test Execution Time |
|---------:|--------------------:|
| 93.7%    | 12m36s              |


### Code coverage of files in pull request scope (100.0%)

|                                                                                                                 Files                                                                                                                 | Coverage |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------:|
| [app/views/shared/metadata_templates/_edit_template_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/c43f7602bb6f22cf935526cd42c2945d9c4e26bb/app%2Fviews%2Fshared%2Fmetadata_templates%2F_edit_template_dialog.html.erb) | 100.0%   |
| [app/views/shared/metadata_templates/_new_template_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/c43f7602bb6f22cf935526cd42c2945d9c4e26bb/app%2Fviews%2Fshared%2Fmetadata_templates%2F_new_template_dialog.html.erb)   | 100.0%   |
| [app/views/shared/samples/metadata/file_imports/_dialog.html.erb](https://github.com/phac-nml/irida-next/blob/c43f7602bb6f22cf935526cd42c2945d9c4e26bb/app%2Fviews%2Fshared%2Fsamples%2Fmetadata%2Ffile_imports%2F_dialog.html.erb)   | 100.0%   |

---
Reported by [octocov](https://github.com/k1LoW/octocov)
<!-- octocov -->
author_association: NONE