diff --git a/client/src/components/ActivityBar/ActivityBar.vue b/client/src/components/ActivityBar/ActivityBar.vue index 345be2ad2178..baf94bb99493 100644 --- a/client/src/components/ActivityBar/ActivityBar.vue +++ b/client/src/components/ActivityBar/ActivityBar.vue @@ -16,6 +16,7 @@ import InteractiveItem from "./Items/InteractiveItem.vue"; import NotificationItem from "./Items/NotificationItem.vue"; import UploadItem from "./Items/UploadItem.vue"; import FlexPanel from "@/components/Panels/FlexPanel.vue"; +import MultiviewPanel from "@/components/Panels/MultiviewPanel.vue"; import NotificationsPanel from "@/components/Panels/NotificationsPanel.vue"; import SettingsPanel from "@/components/Panels/SettingsPanel.vue"; import ToolPanel from "@/components/Panels/ToolPanel.vue"; @@ -44,7 +45,7 @@ const dragItem: Ref = ref(null); const isDragging = ref(false); /** - * Checks if the route of an activitiy is currently being visited and panels are collapsed + * Checks if the route of an activity is currently being visited and panels are collapsed */ function isActiveRoute(activityTo: string) { return route.path === activityTo && isActiveSideBar(""); @@ -59,6 +60,13 @@ function isActiveSideBar(menuKey: string) { const isSideBarOpen = computed(() => userStore.toggledSideBar !== ""); +/** + * Checks if an activity that has a panel should have the `is-active` prop + */ +function panelActivityIsActive(activity: Activity) { + return isActiveSideBar(activity.id) || (activity.to !== null && isActiveRoute(activity.to)); +} + /** * Evaluates the drop data and keeps track of the drop area */ @@ -106,7 +114,12 @@ function onDragOver(evt: MouseEvent) { /** * Tracks the state of activities which expand or collapse the sidepanel */ -function onToggleSidebar(toggle: string) { +function onToggleSidebar(toggle: string, to: string | null = null) { + // if an activity's dedicated panel/sideBar is already active + // but the route is different, don't collapse + if (toggle && to && !(route.path === to) && isActiveSideBar(toggle)) { + return; + } userStore.toggleSideBar(toggle); } @@ -137,8 +150,7 @@ function onToggleSidebar(toggle: string) { :key="activity.id" :icon="activity.icon" :title="activity.title" - :tooltip="activity.tooltip" - @click="onToggleSidebar()" /> + :tooltip="activity.tooltip" /> + @click="onToggleSidebar(activity.id, activity.to)" /> + diff --git a/client/src/components/Common/FilterMenu.test.ts b/client/src/components/Common/FilterMenu.test.ts index de6f90493508..c9955acee309 100644 --- a/client/src/components/Common/FilterMenu.test.ts +++ b/client/src/components/Common/FilterMenu.test.ts @@ -154,14 +154,14 @@ describe("FilterMenu", () => { // `has_help` filter should have help modal button expect(wrapper.find("[title='Value Help']").classes().includes("btn")).toBe(true); // ranged time field (has 2 datepickers) - const createdGtInput = wrapper.find("[placeholder='creation time after']"); - const createdLtInput = wrapper.find("[placeholder='creation time before']"); + const createdGtInput = wrapper.find("[placeholder='after creation time']"); + const createdLtInput = wrapper.find("[placeholder='before creation time']"); createdGtInput.setValue("January 1, 2022"); createdLtInput.setValue("January 1, 2023"); expect(wrapper.findAll(".b-form-datepicker").length).toBe(2); // ranged number field (has different placeholder: greater instead of after...) - const indexGtInput = wrapper.find("[placeholder='index greater']"); - const indexLtInput = wrapper.find("[placeholder='index lower']"); + const indexGtInput = wrapper.find("[placeholder='greater than index']"); + const indexLtInput = wrapper.find("[placeholder='lower than index']"); indexGtInput.setValue("1234"); indexLtInput.setValue("5678"); // default bool filter diff --git a/client/src/components/Common/FilterMenuRanged.vue b/client/src/components/Common/FilterMenuRanged.vue index 73ebcb316d44..ae1d2527112b 100644 --- a/client/src/components/Common/FilterMenuRanged.vue +++ b/client/src/components/Common/FilterMenuRanged.vue @@ -49,11 +49,11 @@ function hasError(field: string) { function localPlaceholder(comp: "gt" | "lt") { if (comp == "gt") { - const field = isDateType.value ? "after" : "greater"; - return `${props.filter.placeholder} ${field}`; + const field = isDateType.value ? "after" : "greater than"; + return `${field} ${props.filter.placeholder}`; } else { - const field = isDateType.value ? "before" : "lower"; - return `${props.filter.placeholder} ${field}`; + const field = isDateType.value ? "before" : "lower than"; + return `${field} ${props.filter.placeholder}`; } } diff --git a/client/src/components/Common/TextSummary.vue b/client/src/components/Common/TextSummary.vue index 9f0ba44c566d..19b7f40e98c7 100644 --- a/client/src/components/Common/TextSummary.vue +++ b/client/src/components/Common/TextSummary.vue @@ -7,12 +7,21 @@ import { computed, ref } from "vue"; library.add(faChevronUp, faChevronDown); interface Props { + /** The maximum length of the unexpanded text / summary */ maxLength?: number; + /** The text to summarize */ description: string; + /** If `true`, doesn't let unexpanded text go beyond height of one line */ + oneLineSummary?: boolean; + /** If `true`, doesn't show expand/collapse buttons */ + noExpand?: boolean; + /** The component to use for the summary, default = `

` */ + component?: string; } const props = withDefaults(defineProps(), { maxLength: 150, + component: "p", }); const showDetails = ref(false); @@ -27,14 +36,29 @@ const text = computed(() => + + diff --git a/client/src/components/History/CurrentHistory/HistoryCounter.vue b/client/src/components/History/CurrentHistory/HistoryCounter.vue index e44b174a8433..c70ec3a91e6b 100644 --- a/client/src/components/History/CurrentHistory/HistoryCounter.vue +++ b/client/src/components/History/CurrentHistory/HistoryCounter.vue @@ -28,12 +28,14 @@ const props = withDefaults( lastChecked: Date; filterText?: string; showControls?: boolean; + hideReload?: boolean; }>(), { isWatching: false, lastChecked: () => new Date(), filterText: "", showControls: false, + hideReload: false, } ); @@ -191,6 +193,7 @@ onMounted(() => { { + + diff --git a/client/src/components/History/CurrentHistory/HistoryDetails.vue b/client/src/components/History/CurrentHistory/HistoryDetails.vue index 3151019aeb60..52619647dd25 100644 --- a/client/src/components/History/CurrentHistory/HistoryDetails.vue +++ b/client/src/components/History/CurrentHistory/HistoryDetails.vue @@ -1,16 +1,22 @@ + + + + diff --git a/client/src/components/History/Layout/DetailsLayout.vue b/client/src/components/History/Layout/DetailsLayout.vue index 5b1e975d7c55..ef49938ca9f3 100644 --- a/client/src/components/History/Layout/DetailsLayout.vue +++ b/client/src/components/History/Layout/DetailsLayout.vue @@ -9,6 +9,7 @@ import { computed, ref } from "vue"; import { useUserStore } from "@/stores/userStore"; import l from "@/utils/localization"; +import TextSummary from "@/components/Common/TextSummary.vue"; import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue"; library.add(faPen, faSave, faUndo); @@ -19,6 +20,7 @@ interface Props { writeable?: boolean; annotation?: string; showAnnotation?: boolean; + summarized?: boolean; } const props = withDefaults(defineProps(), { @@ -27,6 +29,7 @@ const props = withDefaults(defineProps(), { writeable: true, annotation: undefined, showAnnotation: true, + summarized: false, }); const emit = defineEmits(["save"]); @@ -87,7 +90,10 @@ function selectText() { + + diff --git a/client/src/components/History/Modals/SelectorModal.test.js b/client/src/components/History/Modals/SelectorModal.test.js index 6766bdefcc94..e6c4e708b1f9 100644 --- a/client/src/components/History/Modals/SelectorModal.test.js +++ b/client/src/components/History/Modals/SelectorModal.test.js @@ -56,6 +56,9 @@ describe("History SelectorModal.vue", () => { propsData: props, localVue, pinia, + stubs: { + icon: { template: "

" }, + }, }); historyStore = useHistoryStore(); axiosMock = new MockAdapter(axios); @@ -82,7 +85,7 @@ describe("History SelectorModal.vue", () => { let displayedRows = wrapper.findAllComponents(BListGroupItem).wrappers; expect(displayedRows.length).toBe(10); - expect(wrapper.find(".load-more-hist-button").exists()).toBe(true); + expect(wrapper.find("[data-description='load more histories button']").exists()).toBe(true); getUpdatedAxiosMock(); await historyStore.loadHistories(); @@ -92,7 +95,7 @@ describe("History SelectorModal.vue", () => { displayedRows = wrapper.findAllComponents(BListGroupItem).wrappers; expect(displayedRows.length).toBe(15); - expect(wrapper.find(".load-more-hist-button").exists()).toBe(false); + expect(wrapper.find("[data-description='load more histories button']").exists()).toBe(false); axiosMock.restore(); }); @@ -127,7 +130,7 @@ describe("History SelectorModal.vue", () => { const selectedHistories = wrapper.findAll(".list-group-item.active").wrappers; expect(selectedHistories.length).toBe(2); - const button = wrapper.find("footer > .btn-primary"); + const button = wrapper.find("[data-description='change selected histories button']"); await button.trigger("click"); diff --git a/client/src/components/History/Modals/SelectorModal.vue b/client/src/components/History/Modals/SelectorModal.vue index 00052ea4e50a..a15aedf55295 100644 --- a/client/src/components/History/Modals/SelectorModal.vue +++ b/client/src/components/History/Modals/SelectorModal.vue @@ -1,50 +1,36 @@