From a384bc5178e9c7f0c98f526bc176998d9a4bf34a Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sat, 28 Feb 2026 11:01:38 -0500 Subject: [PATCH 01/11] Replace BDropdown family with custom GDropdown components Created 7 new components (GDropdown, GDropdownItem, GDropdownDivider, GDropdownText, GDropdownGroup, GDropdownForm, GDropdownItemButton) that use Bootstrap 4 CSS classes directly with native DOM click-outside handling and provide/inject for menu auto-close. Migrated all 27 files that imported from bootstrap-vue's dropdown family. --- .../components/BaseComponents/GDropdown.vue | 234 ++++++++++++++++++ .../BaseComponents/GDropdownDivider.vue | 5 + .../BaseComponents/GDropdownForm.vue | 7 + .../BaseComponents/GDropdownGroup.vue | 20 ++ .../BaseComponents/GDropdownItem.vue | 84 +++++++ .../BaseComponents/GDropdownItemButton.vue | 48 ++++ .../BaseComponents/GDropdownText.vue | 7 + .../common/PairingFilterInputGroup.vue | 17 +- .../components/Common/FilterMenuDropdown.vue | 22 +- client/src/components/Common/GCard.vue | 12 +- client/src/components/Common/GTable.vue | 12 +- client/src/components/Common/ListHeader.vue | 21 +- .../FormData/FormDataContextButtons.vue | 13 +- .../History/Content/ContentOptions.vue | 19 +- .../Content/Dataset/DatasetDownload.vue | 17 +- .../HistoryOperations/DefaultOperations.vue | 20 +- .../HistoryOperations/SelectionOperations.vue | 79 +++--- .../src/components/History/HistoryOptions.vue | 78 +++--- .../LibraryFolder/TopToolbar/FolderTopBar.vue | 50 ++-- .../components/Masthead/MastheadDropdown.vue | 7 +- .../components/Panels/Menus/PanelViewMenu.vue | 14 +- .../Panels/Menus/PanelViewMenuItem.vue | 7 +- .../Tool/Buttons/ToolOptionsButton.vue | 31 +-- .../Tool/Buttons/ToolTourGeneratorItem.vue | 6 +- .../Tool/Buttons/ToolVersionsButton.vue | 11 +- .../components/ToolsList/ToolOntologies.vue | 14 +- .../ToolsList/ToolsListSectionFilters.vue | 37 +-- .../Visualizations/VisualizationExamples.vue | 17 +- .../src/components/Workflow/Editor/Index.vue | 20 +- .../Workflow/Editor/NodeInspector.vue | 17 +- .../WorkflowInvocationMetrics.vue | 28 ++- 31 files changed, 716 insertions(+), 258 deletions(-) create mode 100644 client/src/components/BaseComponents/GDropdown.vue create mode 100644 client/src/components/BaseComponents/GDropdownDivider.vue create mode 100644 client/src/components/BaseComponents/GDropdownForm.vue create mode 100644 client/src/components/BaseComponents/GDropdownGroup.vue create mode 100644 client/src/components/BaseComponents/GDropdownItem.vue create mode 100644 client/src/components/BaseComponents/GDropdownItemButton.vue create mode 100644 client/src/components/BaseComponents/GDropdownText.vue diff --git a/client/src/components/BaseComponents/GDropdown.vue b/client/src/components/BaseComponents/GDropdown.vue new file mode 100644 index 000000000000..2b584dfc2749 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdown.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/client/src/components/BaseComponents/GDropdownDivider.vue b/client/src/components/BaseComponents/GDropdownDivider.vue new file mode 100644 index 000000000000..f49f0ea94b06 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownDivider.vue @@ -0,0 +1,5 @@ + + + diff --git a/client/src/components/BaseComponents/GDropdownForm.vue b/client/src/components/BaseComponents/GDropdownForm.vue new file mode 100644 index 000000000000..c8f41d57f4e4 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownForm.vue @@ -0,0 +1,7 @@ + + + diff --git a/client/src/components/BaseComponents/GDropdownGroup.vue b/client/src/components/BaseComponents/GDropdownGroup.vue new file mode 100644 index 000000000000..47cb752bdfa7 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownGroup.vue @@ -0,0 +1,20 @@ + + + diff --git a/client/src/components/BaseComponents/GDropdownItem.vue b/client/src/components/BaseComponents/GDropdownItem.vue new file mode 100644 index 000000000000..e90596ad7c59 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownItem.vue @@ -0,0 +1,84 @@ + + + diff --git a/client/src/components/BaseComponents/GDropdownItemButton.vue b/client/src/components/BaseComponents/GDropdownItemButton.vue new file mode 100644 index 000000000000..55dd6ed6dc5b --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownItemButton.vue @@ -0,0 +1,48 @@ + + + diff --git a/client/src/components/BaseComponents/GDropdownText.vue b/client/src/components/BaseComponents/GDropdownText.vue new file mode 100644 index 000000000000..35917d3977d6 --- /dev/null +++ b/client/src/components/BaseComponents/GDropdownText.vue @@ -0,0 +1,7 @@ + + + diff --git a/client/src/components/Collections/common/PairingFilterInputGroup.vue b/client/src/components/Collections/common/PairingFilterInputGroup.vue index 3f35403e3e5c..899708f85233 100644 --- a/client/src/components/Collections/common/PairingFilterInputGroup.vue +++ b/client/src/components/Collections/common/PairingFilterInputGroup.vue @@ -1,9 +1,12 @@ - + Download Dataset - + - Download {{ metaFile.file_type }} - - + + import { faBurn, faCog, faEyeSlash, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; -import { BDropdown, BDropdownItem } from "bootstrap-vue"; import { toRef } from "vue"; import type { HistorySummaryExtended } from "@/api"; @@ -13,6 +12,9 @@ import { import { useConfirmDialog } from "@/composables/confirmDialog"; import { useHistoryContentStats } from "@/composables/historyContentStats"; +import GDropdown from "@/components/BaseComponents/GDropdown.vue"; +import GDropdownItem from "@/components/BaseComponents/GDropdownItem.vue"; + interface Props { history: HistorySummaryExtended; } @@ -73,7 +75,7 @@ async function runOperation(operation: () => Promise) { - + Unhide All Hidden Content - + - + Delete All Hidden Content - + - + Purge All Deleted Content - - + + diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue index b0bd3ed5c9db..c0ccc93c9634 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue @@ -1,6 +1,6 @@ - + You have {{ totalHistoryCount }} histories. Manage History - + - Show Histories Side-by-Side - + - + - + This history has been {{ historyState }} . Some actions might not be available. - + - + - Resume Paused Jobs - + - + - Copy History - + - Copy Datasets - + - Permanently Delete History Delete History - + - Export Tool References - + - Export History to File - + - Archive History - + - Extract Workflow - + - Show Invocations - + - + Show History Graph - + - Show History Notebooks - + - + - Share & Manage Access - - + + diff --git a/client/src/components/Libraries/LibraryFolder/TopToolbar/FolderTopBar.vue b/client/src/components/Libraries/LibraryFolder/TopToolbar/FolderTopBar.vue index 24e9fc65e556..838e1af4a72c 100644 --- a/client/src/components/Libraries/LibraryFolder/TopToolbar/FolderTopBar.vue +++ b/client/src/components/Libraries/LibraryFolder/TopToolbar/FolderTopBar.vue @@ -2,15 +2,7 @@ import { faBook, faCaretDown, faDownload, faHome, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import axios from "axios"; -import { - BAlert, - BButton, - BDropdown, - BDropdownDivider, - BDropdownGroup, - BDropdownItem, - BFormCheckbox, -} from "bootstrap-vue"; +import { BAlert, BButton, BFormCheckbox } from "bootstrap-vue"; import { computed, reactive, ref } from "vue"; import { GalaxyApi } from "@/api"; @@ -27,6 +19,10 @@ import { Toast } from "@/composables/toast"; import { getAppRoot } from "@/onload"; import { useUserStore } from "@/stores/userStore"; +import GDropdown from "@/components/BaseComponents/GDropdown.vue"; +import GDropdownDivider from "@/components/BaseComponents/GDropdownDivider.vue"; +import GDropdownGroup from "@/components/BaseComponents/GDropdownGroup.vue"; +import GDropdownItem from "@/components/BaseComponents/GDropdownItem.vue"; import CollectionCreatorIndex from "@/components/Collections/CollectionCreatorIndex.vue"; import FolderDetails from "@/components/Libraries/LibraryFolder/FolderDetails/FolderDetails.vue"; import LibraryBreadcrumb from "@/components/Libraries/LibraryFolder/LibraryBreadcrumb.vue"; @@ -335,7 +331,7 @@ function onAddDatasetsDirectory(selectedDatasets: Record - - from History + from History - + from User Directory - + - + - - + + from Import Directory - + - + from Path - - - + + + - + - + as Datasets - + - + as a Collection - - + +
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; -import { BDropdownItem, BNavItemDropdown } from "bootstrap-vue"; +import { BNavItemDropdown } from "bootstrap-vue"; import { type PropType, ref } from "vue"; import type { IconLike } from "@/components/icons/galaxyIcons"; +import GDropdownItem from "@/components/BaseComponents/GDropdownItem.vue"; import TextShort from "@/components/Common/TextShort.vue"; const dropdown = ref(null); @@ -47,7 +48,7 @@ defineProps({ diff --git a/client/src/components/Panels/Menus/PanelViewMenu.vue b/client/src/components/Panels/Menus/PanelViewMenu.vue index 279ff685a322..b7598528afb1 100644 --- a/client/src/components/Panels/Menus/PanelViewMenu.vue +++ b/client/src/components/Panels/Menus/PanelViewMenu.vue @@ -1,7 +1,6 @@ diff --git a/client/src/components/BaseComponents/GDropdownItemButton.vue b/client/src/components/BaseComponents/GDropdownItemButton.vue index 55dd6ed6dc5b..f22e9c7f3312 100644 --- a/client/src/components/BaseComponents/GDropdownItemButton.vue +++ b/client/src/components/BaseComponents/GDropdownItemButton.vue @@ -46,3 +46,9 @@ function onClick(event: MouseEvent) { + + From 2262f0a4a21ed2dbdebf5559ddc7acaa31c30e40 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 26 Mar 2026 22:49:10 -0400 Subject: [PATCH 07/11] Clean up GDropdown: remove dead props, fix accessibility, fix MastheadDropdown close Remove noFlip, boundary, and offset props from GDropdown since they were accepted but never implemented (no Popper.js backing). Remove the corresponding caller-side attributes. Add aria-disabled to GDropdownItem for screen reader support. Add headerClasses prop to GDropdownGroup so ToolsListSectionFilters styling works. Fix MastheadDropdown by providing g-dropdown-hide so GDropdownItem clicks properly close the BNavItemDropdown. Fix ToolTourGeneratorItem test to check aria-disabled instead of the disabled DOM attribute. Remove dead onMounted hook and unused import. --- .../src/components/BaseComponents/GDropdown.vue | 15 +-------------- .../components/BaseComponents/GDropdownGroup.vue | 4 +++- .../components/BaseComponents/GDropdownItem.vue | 11 ++++++++++- .../src/components/Common/FilterMenuDropdown.vue | 1 - .../History/Content/Dataset/DatasetDownload.vue | 1 - .../HistoryOperations/SelectionOperations.vue | 2 +- .../src/components/Masthead/MastheadDropdown.vue | 8 ++++++-- .../Tool/Buttons/ToolTourGeneratorItem.test.ts | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/client/src/components/BaseComponents/GDropdown.vue b/client/src/components/BaseComponents/GDropdown.vue index 3c4ca1b4eb69..f847769e0ae5 100644 --- a/client/src/components/BaseComponents/GDropdown.vue +++ b/client/src/components/BaseComponents/GDropdown.vue @@ -6,7 +6,7 @@ * split buttons, dropup, no-caret, and menu/toggle class customization. */ -import { computed, nextTick, onBeforeUnmount, onMounted, provide, ref } from "vue"; +import { computed, nextTick, onBeforeUnmount, provide, ref } from "vue"; const props = withDefaults( defineProps<{ @@ -20,8 +20,6 @@ const props = withDefaults( right?: boolean; /** Hide the caret icon */ noCaret?: boolean; - /** Prevent menu from flipping */ - noFlip?: boolean; /** Drop direction */ dropup?: boolean; dropleft?: boolean; @@ -34,16 +32,12 @@ const props = withDefaults( toggleClass?: string | string[] | Record; /** Extra classes for the dropdown menu */ menuClass?: string | string[] | Record; - /** Boundary for positioning */ - boundary?: string; /** Disabled state */ disabled?: boolean; /** ARIA role */ role?: string; /** Lazy render menu content */ lazy?: boolean; - /** Offset */ - offset?: number | string; }>(), { text: undefined, @@ -51,7 +45,6 @@ const props = withDefaults( size: undefined, right: false, noCaret: false, - noFlip: false, dropup: false, dropleft: false, dropright: false, @@ -59,11 +52,9 @@ const props = withDefaults( split: false, toggleClass: undefined, menuClass: undefined, - boundary: undefined, disabled: false, role: undefined, lazy: false, - offset: undefined, }, ); @@ -177,10 +168,6 @@ const shouldRenderMenu = computed(() => { return hasBeenOpened.value; }); -onMounted(() => { - // Clean up on mount if needed -}); - onBeforeUnmount(() => { document.removeEventListener("click", onClickOutside, true); }); diff --git a/client/src/components/BaseComponents/GDropdownGroup.vue b/client/src/components/BaseComponents/GDropdownGroup.vue index 47cb752bdfa7..fd599b6fe1b5 100644 --- a/client/src/components/BaseComponents/GDropdownGroup.vue +++ b/client/src/components/BaseComponents/GDropdownGroup.vue @@ -7,12 +7,14 @@ defineProps<{ /** Group header text (or use #header slot) */ header?: string; + /** Extra CSS classes for the header element */ + headerClasses?: string | string[] | Record; }>(); diff --git a/client/src/components/Common/FilterMenuDropdown.vue b/client/src/components/Common/FilterMenuDropdown.vue index 262f8ef3953a..c00868813d55 100644 --- a/client/src/components/Common/FilterMenuDropdown.vue +++ b/client/src/components/Common/FilterMenuDropdown.vue @@ -138,7 +138,6 @@ function setValue(val: FilterValue) { class="w-100" menu-class="w-100" size="sm" - boundary="window" :disabled="props.disabled" :toggle-class="props.error ? 'text-danger' : ''"> (any) diff --git a/client/src/components/History/Content/Dataset/DatasetDownload.vue b/client/src/components/History/Content/Dataset/DatasetDownload.vue index f98db7039d21..78e44d0f2ac8 100644 --- a/client/src/components/History/Content/Dataset/DatasetDownload.vue +++ b/client/src/components/History/Content/Dataset/DatasetDownload.vue @@ -50,7 +50,6 @@ function onDownload(resource: string, extension = "") { v-g-tooltip.hover dropup no-caret - no-flip size="sm" variant="link" toggle-class="text-decoration-none" diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue index c0ccc93c9634..2662f879d68a 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionOperations.vue @@ -1,6 +1,6 @@