Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add History Multiview activity+panel, add "Show Recent" mode to multiview #17251

Merged
merged 19 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1e9c433
add show recent functionality to multiview, uniform multi history view
ahmedhamidawan Jan 9, 2024
331b948
add History Multiview activity panel, create `HistoryScrollList`
ahmedhamidawan Jan 15, 2024
531608f
use `HistoryScrollList` in `SelectorModal`
ahmedhamidawan Jan 16, 2024
0e4cc29
remove `props.histories` and directly access store instead: hist scro…
ahmedhamidawan Jan 16, 2024
7568722
only pin new history in multiview if we already have pinned histories
ahmedhamidawan Jan 17, 2024
c3649de
for "panel activities" only toggle sideBar if `route !== activity.to`
ahmedhamidawan Jan 17, 2024
6b2a52f
add create and pin option to `MultipleViewList` as well
ahmedhamidawan Jan 17, 2024
52982db
add tabindex to history switch area in `MultipleViewList`
ahmedhamidawan Jan 18, 2024
43c28e8
add linear gradients to top and bottom of `HistoryScrollList`
ahmedhamidawan Jan 30, 2024
21ee913
add `ScrollToTopButton` to `HistoryScrollList`
ahmedhamidawan Jan 30, 2024
ab96348
use `ActivityPanel` in `MultiviewPanel`
ahmedhamidawan Feb 13, 2024
717e6a9
show notifications activity as active for notifications route
ahmedhamidawan Feb 19, 2024
2ceff58
force render scroller in history scroll list if not rendered
ahmedhamidawan Feb 19, 2024
33179ac
remove `immediate` flag from `isScrollable` check
ahmedhamidawan Feb 19, 2024
851f784
Update client/src/components/Panels/MultiviewPanel.vue
ahmedhamidawan Feb 20, 2024
16274c6
Update client/src/components/History/Multiple/MultipleViewList.vue
ahmedhamidawan Feb 20, 2024
59f43f6
fix see more/less bug
ahmedhamidawan Feb 20, 2024
373c189
refactor changes to use explicit bootstrap components etc
Feb 20, 2024
151aed5
use schema `HistorySummary` for `HistoryPanel` prop
ahmedhamidawan Feb 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 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 @@

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,14 +114,19 @@
/**
* 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>

<template>
<div class="d-flex">
<div

Check warning on line 129 in client/src/components/ActivityBar/ActivityBar.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Visible, non-interactive elements should not have an interactive handler
class="activity-bar d-flex flex-column no-highlight"
data-description="activity bar"
@dragover.prevent="onDragOver"
Expand All @@ -137,8 +150,7 @@
: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 @@
: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 @@
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 @@
<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>
67 changes: 30 additions & 37 deletions client/src/components/History/CurrentHistory/HistoryPanel.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<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 { copyDataset } from "@/api/datasets";
import ExpandedItems from "@/components/History/Content/ExpandedItems";
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: Record<string, any> & GetComponentPropTypes<typeof HistoryCounter>["history"];
ahmedhamidawan marked this conversation as resolved.
Show resolved Hide resolved
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
Loading