Skip to content

Commit

Permalink
Merge pull request #17251 from ahmedhamidawan/multiview_recent_histories
Browse files Browse the repository at this point in the history
Add History Multiview activity+panel, add "Show Recent" mode to multiview
  • Loading branch information
jdavcs authored Feb 22, 2024
2 parents f68ab16 + 151aed5 commit 03c1f91
Show file tree
Hide file tree
Showing 22 changed files with 1,190 additions and 446 deletions.
29 changes: 21 additions & 8 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -44,7 +45,7 @@ const dragItem: Ref<Activity | null> = 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("");
Expand All @@ -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
*/
Expand Down Expand Up @@ -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);
}
</script>
Expand Down Expand Up @@ -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" />
<InteractiveItem
v-else-if="activity.id === 'interactivetools'"
:id="`activity-${activity.id}`"
Expand All @@ -150,15 +162,15 @@ function onToggleSidebar(toggle: string) {
:to="activity.to"
@click="onToggleSidebar()" />
<ActivityItem
v-else-if="['tools', 'visualizations'].includes(activity.id)"
v-else-if="['tools', 'visualizations', 'multiview'].includes(activity.id)"
:id="`activity-${activity.id}`"
:key="activity.id"
:icon="activity.icon"
:is-active="isActiveSideBar(activity.id)"
:is-active="panelActivityIsActive(activity)"
:title="activity.title"
:tooltip="activity.tooltip"
:to="activity.to"
@click="onToggleSidebar(activity.id)" />
@click="onToggleSidebar(activity.id, activity.to)" />
<ActivityItem
v-else-if="activity.to"
:id="`activity-${activity.id}`"
Expand All @@ -178,7 +190,7 @@ function onToggleSidebar(toggle: string) {
v-if="!isAnonymous && isConfigLoaded && config.enable_notification_system"
id="activity-notifications"
icon="bell"
:is-active="isActiveSideBar('notifications')"
:is-active="isActiveSideBar('notifications') || isActiveRoute('/user/notifications')"
title="Notifications"
@click="onToggleSidebar('notifications')" />
<ActivityItem
Expand All @@ -193,6 +205,7 @@ function onToggleSidebar(toggle: string) {
<FlexPanel v-if="isSideBarOpen" side="left" :collapsible="false">
<ToolPanel v-if="isActiveSideBar('tools')" />
<VisualizationPanel v-else-if="isActiveSideBar('visualizations')" />
<MultiviewPanel v-else-if="isActiveSideBar('multiview')" />
<NotificationsPanel v-else-if="isActiveSideBar('notifications')" />
<SettingsPanel v-else-if="isActiveSideBar('settings')" />
</FlexPanel>
Expand Down
8 changes: 4 additions & 4 deletions client/src/components/Common/FilterMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions client/src/components/Common/FilterMenuRanged.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
}
Expand Down
30 changes: 27 additions & 3 deletions client/src/components/Common/TextSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<p>` */
component?: string;
}
const props = withDefaults(defineProps<Props>(), {
maxLength: 150,
component: "p",
});
const showDetails = ref(false);
Expand All @@ -27,14 +36,29 @@ const text = computed(() =>

<template>
<div>
{{ text }}
<component :is="props.component" v-if="props.oneLineSummary" class="one-line-summary">
{{ props.description }}
</component>
<span v-else>{{ text }}</span>
<span
v-if="textTooLong"
v-if="!noExpand && textTooLong"
v-b-tooltip.hover
class="info-icon cursor-pointer"
:title="textTooLong ? 'Show more' : 'Show less'"
:title="showDetails ? 'Show less' : 'Show more'"
role="button"
tabindex="0"
@keyup.enter="showDetails = !showDetails"
@click="showDetails = !showDetails">
<FontAwesomeIcon :icon="showDetails ? 'chevron-up' : 'chevron-down'" />
</span>
</div>
</template>

<style scoped>
.one-line-summary {
max-height: 2em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
);
Expand Down Expand Up @@ -191,6 +193,7 @@ onMounted(() => {
</BButton>

<BButton
v-if="!hideReload"
v-b-tooltip.hover
:title="reloadButtonTitle"
:variant="reloadButtonVariant"
Expand All @@ -216,3 +219,9 @@ onMounted(() => {
</BButtonGroup>
</div>
</template>

<style lang="scss" scoped>
.btn {
white-space: nowrap;
}
</style>
24 changes: 23 additions & 1 deletion client/src/components/History/CurrentHistory/HistoryDetails.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
<script setup lang="ts">
import { BBadge } from "bootstrap-vue";
import type { HistorySummary } from "@/api";
import { useHistoryStore } from "@/stores/historyStore";
import TextSummary from "@/components/Common/TextSummary.vue";
import DetailsLayout from "@/components/History/Layout/DetailsLayout.vue";
import UtcDate from "@/components/UtcDate.vue";
interface Props {
history: HistorySummary;
writeable: boolean;
summarized: boolean;
}
const props = withDefaults(defineProps<Props>(), {
writeable: true,
summarized: false,
});
const historyStore = useHistoryStore();
Expand All @@ -27,10 +33,26 @@ function onSave(newDetails: HistorySummary) {
:annotation="history.annotation || ''"
:tags="history.tags"
:writeable="writeable"
:summarized="summarized"
:update-time="history.update_time"
@save="onSave">
<template v-slot:name>
<!-- eslint-disable-next-line vuejs-accessibility/heading-has-content -->
<h3 v-short="history.name || 'History'" data-description="name display" class="my-2" />
<h3 v-if="!summarized" v-short="history.name || 'History'" data-description="name display" class="my-2" />
<TextSummary
v-else
:description="history.name"
data-description="name display"
class="my-2"
component="h3"
one-line-summary
no-expand />
</template>
<template v-if="summarized" v-slot:update-time>
<BBadge v-b-tooltip pill>
<span v-localize>last edited </span>
<UtcDate v-if="history.update_time" :date="history.update_time" mode="elapsed" />
</BBadge>
</template>
</DetailsLayout>
</template>
69 changes: 31 additions & 38 deletions client/src/components/History/CurrentHistory/HistoryPanel.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { GetComponentPropTypes } from "types/utilityTypes";
import { computed, onMounted, PropType, ref, set as VueSet, unref, watch } from "vue";
import { computed, onMounted, ref, set as VueSet, unref, watch } from "vue";
import type { HistorySummary } from "@/api";
import { copyDataset } from "@/api/datasets";
import ExpandedItems from "@/components/History/Content/ExpandedItems";
import SelectedItems from "@/components/History/Content/SelectedItems";
Expand Down Expand Up @@ -41,31 +42,23 @@ interface BackendFilterError {
ValueError?: string;
}
const props = defineProps({
listOffset: {
type: Number,
default: 0,
},
history: {
type: Object as PropType<Record<string, any> & GetComponentPropTypes<typeof HistoryCounter>["history"]>,
required: true,
},
filter: {
type: String,
default: "",
},
canEditHistory: {
type: Boolean,
default: true,
},
shouldShowControls: {
type: Boolean,
default: true,
},
filterable: {
type: Boolean,
default: false,
},
interface Props {
listOffset?: number;
history: HistorySummary;
filter?: string;
canEditHistory?: boolean;
shouldShowControls?: boolean;
filterable?: boolean;
isMultiViewItem?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
listOffset: 0,
filter: "",
canEditHistory: true,
shouldShowControls: true,
filterable: false,
isMultiViewItem: false,
});
const filterClass = HistoryFilters;
Expand All @@ -76,7 +69,7 @@ const offsetQueryParam = ref(0);
const searchError = ref<BackendFilterError | undefined>(undefined);
const showAdvanced = ref(false);
const showDropZone = ref(false);
const operationRunning = ref(null);
const operationRunning = ref<string | null>(null);
const operationError = ref(null);
const querySelectionBreak = ref(false);
const dragTarget = ref<EventTarget | null>(null);
Expand All @@ -88,7 +81,7 @@ const historyStore = useHistoryStore();
const historyItemsStore = useHistoryItemsStore();
const historyUpdateTime = computed(() => {
return props.history.update_time as Date;
return props.history.update_time;
});
const queryKey = computed(() => {
Expand Down Expand Up @@ -397,6 +390,7 @@ onMounted(async () => {
<HistoryDetails
:history="history"
:writeable="canEditHistory"
:summarized="isMultiViewItem"
@update:history="historyStore.updateHistory($event)" />

<HistoryMessages :history="history" />
Expand All @@ -407,6 +401,7 @@ onMounted(async () => {
:last-checked="lastCheckedTime"
:show-controls="shouldShowControls"
:filter-text.sync="filterText"
:hide-reload="isMultiViewItem"
@reloadContents="reloadContents" />

<HistoryOperations
Expand Down Expand Up @@ -452,25 +447,23 @@ onMounted(async () => {
<HistoryDropZone v-if="showDropZone" />
<div>
<div v-if="isLoading && historyItems && historyItems.length === 0">
<b-alert class="m-2" variant="info" show>
<BAlert class="m-2" variant="info" show>
<LoadingSpan message="Loading History" />
</b-alert>
</BAlert>
</div>
<b-alert v-else-if="isProcessing" class="m-2" variant="info" show>
<BAlert v-else-if="isProcessing" class="m-2" variant="info" show>
<LoadingSpan message="Processing operation" />
</b-alert>
</BAlert>
<div v-else-if="historyItems.length === 0">
<HistoryEmpty v-if="queryDefault" :writable="canEditHistory" class="m-2" />

<b-alert v-else-if="formattedSearchError" class="m-2" variant="danger" show>
<BAlert v-else-if="formattedSearchError" class="m-2" variant="danger" show>
Error in filter:
<a href="javascript:void(0)" @click="showAdvanced = true">
{{ formattedSearchError.filter }}'{{ formattedSearchError.value }}'
</a>
</b-alert>
<b-alert v-else class="m-2" variant="info" show>
No data found for selected filter.
</b-alert>
</BAlert>
<BAlert v-else class="m-2" variant="info" show> No data found for selected filter. </BAlert>
</div>
<ListingLayout
v-else
Expand Down
Loading

0 comments on commit 03c1f91

Please sign in to comment.