Skip to content

Commit

Permalink
Merge pull request galaxyproject#16859 from ahmedhamidawan/content_op…
Browse files Browse the repository at this point in the history
…tions_active_indicator

Visually indicate currently viewed/edited dataset
  • Loading branch information
martenson authored Nov 28, 2023
2 parents fb486ba + 15d7da0 commit 4cad7e7
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 95 deletions.
4 changes: 4 additions & 0 deletions client/src/components/History/Content/ContentItem.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { mount } from "@vue/test-utils";
import { updateContentFields } from "components/History/model/queries";
import { PiniaVuePlugin } from "pinia";
import { getLocalVue } from "tests/jest/helpers";
import VueRouter from "vue-router";

import ContentItem from "./ContentItem";

jest.mock("components/History/model/queries");

const localVue = getLocalVue();
localVue.use(VueRouter);
localVue.use(PiniaVuePlugin);
const router = new VueRouter();

// mock queries
updateContentFields.mockImplementation(async () => {});
Expand Down Expand Up @@ -48,6 +51,7 @@ describe("ContentItem", () => {
},
},
pinia: createTestingPinia(),
router,
});
});

Expand Down
12 changes: 10 additions & 2 deletions client/src/components/History/Content/ContentItem.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
:id="contentId"
:class="['content-item m-1 p-0 rounded btn-transparent-background', contentCls]"
:class="['content-item m-1 p-0 rounded btn-transparent-background', contentCls, isBeingUsed]"
:data-hid="id"
:data-state="state"
tabindex="0"
Expand Down Expand Up @@ -89,7 +89,7 @@
<!-- collections are not expandable, so we only need the DatasetDetails component here -->
<b-collapse :visible="expandDataset">
<DatasetDetails
v-if="expandDataset"
v-if="expandDataset && item.id"
:id="item.id"
:writable="writable"
:show-highlight="(isHistoryItem && filterable) || addHighlightBtn"
Expand Down Expand Up @@ -211,6 +211,9 @@ export default {
visualize: `/visualizations?dataset_id=${id}`,
};
},
isBeingUsed() {
return Object.values(this.itemUrls).includes(this.$route.path) ? "being-used" : "";
},
},
methods: {
onKeyDown(event) {
Expand Down Expand Up @@ -289,5 +292,10 @@ export default {
&:deep(.btn:focus) {
box-shadow: 0 0 0 0.2rem transparentize($brand-primary, 0.75);
}
&.being-used {
border-left: 0.25rem solid $brand-primary;
margin-left: 0rem !important;
}
}
</style>
169 changes: 84 additions & 85 deletions client/src/components/History/Content/ContentOptions.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
<script setup lang="ts">
import { BDropdown } from "bootstrap-vue";
import { computed, type Ref, ref } from "vue";
import { prependPath } from "@/utils/redirect";
const props = defineProps({
writable: { type: Boolean, default: true },
isDataset: { type: Boolean, required: true },
isDeleted: { type: Boolean, default: false },
isHistoryItem: { type: Boolean, required: true },
isVisible: { type: Boolean, default: true },
state: { type: String, default: "" },
itemUrls: { type: Object, required: true },
keyboardSelectable: { type: Boolean, default: true },
});
const emit = defineEmits<{
(e: "display"): void;
(e: "showCollectionInfo"): void;
(e: "edit"): void;
(e: "delete", recursive?: boolean): void;
(e: "undelete"): void;
(e: "unhide"): void;
}>();
const deleteCollectionMenu: Ref<BDropdown | null> = ref(null);
const displayButtonTitle = computed(() => (displayDisabled.value ? "This dataset is not yet viewable." : "Display"));
const displayDisabled = computed(() => ["discarded", "new", "upload", "queued"].includes(props.state));
const editButtonTitle = computed(() => (editDisabled.value ? "This dataset is not yet editable." : "Edit attributes"));
const editDisabled = computed(() =>
["discarded", "new", "upload", "queued", "running", "waiting"].includes(props.state)
);
const displayUrl = computed(() => prependPath(props.itemUrls.display));
const editUrl = computed(() => prependPath(props.itemUrls.edit));
const isCollection = computed(() => !props.isDataset);
const canShowCollectionDetails = computed(() => props.itemUrls.showDetails);
const showCollectionDetailsUrl = computed(() => prependPath(props.itemUrls.showDetails));
const tabindex = computed(() => (props.keyboardSelectable ? "0" : "-1"));
function onDelete($event: MouseEvent) {
if (isCollection.value) {
deleteCollectionMenu.value?.show();
} else {
onDeleteItem();
}
}
function onDeleteItem() {
emit("delete");
}
function onDeleteItemRecursively() {
const recursive = true;
emit("delete", recursive);
}
function onDisplay($event: MouseEvent) {
// Wrap display handler to allow ctrl/meta click to open in new tab
// instead of triggering display.
if ($event.ctrlKey || $event.metaKey) {
window.open(displayUrl.value, "_blank");
} else {
emit("display");
}
}
</script>

<template>
<span class="align-self-start btn-group align-items-baseline">
<!-- Special case for collections -->
Expand All @@ -8,7 +86,7 @@
size="sm"
variant="link"
:href="showCollectionDetailsUrl"
@click.prevent.stop="$emit('showCollectionInfo')">
@click.prevent.stop="emit('showCollectionInfo')">
<icon icon="info-circle" />
</b-button>
<!-- Common for all content items -->
Expand All @@ -33,7 +111,7 @@
size="sm"
variant="link"
:href="editUrl"
@click.prevent.stop="$emit('edit')">
@click.prevent.stop="emit('edit')">
<icon icon="pen" />
</b-button>
<b-button
Expand All @@ -45,7 +123,7 @@
variant="link"
@click.stop="onDelete($event)">
<icon v-if="isDataset" icon="trash" />
<b-dropdown v-else ref="deleteCollectionMenu" size="sm" variant="link" no-caret toggle-class="p-0 m-0">
<BDropdown v-else ref="deleteCollectionMenu" size="sm" variant="link" no-caret toggle-class="p-0 m-0">
<template v-slot:button-content>
<icon icon="trash" />
</template>
Expand All @@ -57,7 +135,7 @@
<icon icon="copy" />
Collection and elements
</b-dropdown-item>
</b-dropdown>
</BDropdown>
</b-button>
<b-button
v-if="writable && isHistoryItem && isDeleted"
Expand All @@ -66,7 +144,7 @@
title="Undelete"
size="sm"
variant="link"
@click.stop="$emit('undelete')">
@click.stop="emit('undelete')">
<icon icon="trash-restore" />
</b-button>
<b-button
Expand All @@ -76,91 +154,12 @@
title="Unhide"
size="sm"
variant="link"
@click.stop="$emit('unhide')">
@click.stop="emit('unhide')">
<icon icon="eye-slash" />
</b-button>
</span>
</template>

<script>
import { prependPath } from "@/utils/redirect";
export default {
props: {
writable: { type: Boolean, default: true },
isDataset: { type: Boolean, required: true },
isDeleted: { type: Boolean, default: false },
isHistoryItem: { type: Boolean, required: true },
isVisible: { type: Boolean, default: true },
state: { type: String, default: "" },
itemUrls: { type: Object, required: true },
keyboardSelectable: { type: Boolean, default: true },
},
computed: {
displayButtonTitle() {
if (this.displayDisabled) {
return "This dataset is not yet viewable.";
}
return "Display";
},
displayDisabled() {
return ["discarded", "new", "upload", "queued"].includes(this.state);
},
editButtonTitle() {
if (this.editDisabled) {
return "This dataset is not yet editable.";
}
return "Edit attributes";
},
editDisabled() {
return ["discarded", "new", "upload", "queued", "running", "waiting"].includes(this.state);
},
displayUrl() {
return prependPath(this.itemUrls.display);
},
editUrl() {
return prependPath(this.itemUrls.edit);
},
isCollection() {
return !this.isDataset;
},
canShowCollectionDetails() {
return !!this.itemUrls.showDetails;
},
showCollectionDetailsUrl() {
return prependPath(this.itemUrls.showDetails);
},
tabindex() {
return this.keyboardSelectable ? "0" : "-1";
},
},
methods: {
onDisplay($event) {
// Wrap display handler to allow ctrl/meta click to open in new tab
// instead of triggering display.
if ($event.ctrlKey || $event.metaKey) {
window.open(this.displayUrl, "_blank");
} else {
this.$emit("display");
}
},
onDelete() {
if (this.isCollection) {
this.$refs.deleteCollectionMenu.show();
} else {
this.onDeleteItem();
}
},
onDeleteItem() {
this.$emit("delete");
},
onDeleteItemRecursively() {
const recursive = true;
this.$emit("delete", recursive);
},
},
};
</script>
<style lang="css">
.dropdown-menu .dropdown-item {
font-weight: normal;
Expand Down
6 changes: 6 additions & 0 deletions client/src/components/History/Content/GenericElement.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { mount } from "@vue/test-utils";
import { getLocalVue } from "tests/jest/helpers";
import VueRouter from "vue-router";

import GenericElement from "./GenericElement";

jest.mock("components/History/model/queries");

const localVue = getLocalVue();
localVue.use(VueRouter);
const router = new VueRouter();

describe("GenericElement", () => {
let wrapper;
Expand Down Expand Up @@ -54,6 +59,7 @@ describe("GenericElement", () => {
},
},
localVue,
router,
});
});

Expand Down
10 changes: 5 additions & 5 deletions client/src/components/History/HistoryView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ async function createWrapper(localVue, currentUserId, history) {
localVue,
stubs: {
icon: { template: "<div></div>" },
ContentItem: true,
},
provide: {
store: {
Expand Down Expand Up @@ -119,14 +120,13 @@ describe("History center panel View", () => {
// parts of the layout that should be similar for all cases
expectCorrectLayout(wrapper);

// all history items, make sure all show up with hids and names
const historyItems = wrapper.findAll(".content-item");
// make sure all history items show up
const historyItems = wrapper.findAll("contentitem-stub");
expect(historyItems.length).toBe(10);
for (let i = 0; i < historyItems.length; i++) {
const hid = historyItems.length - i;
const itemHeader = historyItems.at(i).find("[data-description='content item header info']");
const headerText = `${hid}: Dataset ${hid}`;
expect(itemHeader.text()).toBe(headerText);
expect(historyItems.at(i).attributes("id")).toBe(`${hid}`);
expect(historyItems.at(i).attributes("name")).toBe(`Dataset ${hid}`);
}
});

Expand Down
14 changes: 11 additions & 3 deletions client/src/components/JobParameters/JobParameters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe("JobParameters/JobParameters.vue", () => {
propsData,
stubs: {
DatasetProvider: DatasetProvider,
ContentItem: true,
},
pinia,
});
Expand All @@ -54,12 +55,18 @@ describe("JobParameters/JobParameters.vue", () => {
const checkTableParameter = (
element: Wrapper<any>,
expectedTitle: string,
expectedValue: string,
expectedValue: string | { hid: number; name: string },
link?: string
) => {
const tds = element.findAll("td");
expect(tds.at(0).text()).toBe(expectedTitle);
expect(tds.at(1).text()).toContain(expectedValue);
if (typeof expectedValue === "string") {
expect(tds.at(1).text()).toContain(expectedValue);
} else {
const contentItem = tds.at(1).find("contentitem-stub");
expect(contentItem.attributes("id")).toBe(`${expectedValue.hid}`);
expect(contentItem.attributes("name")).toBe(expectedValue.name);
}
if (link) {
const a_element = tds.at(1).find("a");
expect(a_element.attributes("href")).toBe(link);
Expand All @@ -74,7 +81,7 @@ describe("JobParameters/JobParameters.vue", () => {
expect(elements.length).toBe(3);

checkTableParameter(elements.at(0), "Add this value", "22", undefined);
checkTableParameter(elements.at(1), linkParam.text, `${raw.hid}: ${raw.name}`, undefined);
checkTableParameter(elements.at(1), linkParam.text, { hid: raw.hid, name: raw.name }, undefined);
checkTableParameter(elements.at(2), "Iterate?", "NO", undefined);
});

Expand All @@ -89,6 +96,7 @@ describe("JobParameters/JobParameters.vue", () => {
propsData,
stubs: {
DatasetProvider: DatasetProvider,
ContentItem: true,
},
pinia,
});
Expand Down

0 comments on commit 4cad7e7

Please sign in to comment.