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

Improvements to various grids #14514

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion client/src/components/Common/Tags.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<StatelessTags :value="tags" @input="onInput" @tag-click="onTagClick" />
<StatelessTags :value="tags" @input="onInput" @tag-click="onTagClick" :disabled="disabled" />
</template>
<script>
import StatelessTags from "components/Tags/StatelessTags";
Expand All @@ -14,6 +14,10 @@ export default {
tags: {
type: Array,
},
disabled: {
type: Boolean,
default: false,
},
},
methods: {
onInput(tags) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@
<span v-localize>Extract Workflow</span>
</b-dropdown-item>

<b-dropdown-item
:disabled="currentUser.isAnonymous"
@click="$router.push(`histories/${history.id}/invocations`)">
<Icon fixed-width icon="sitemap" class="fa-rotate-270 mr-1" />
<span v-localize>Show Invocations</span>
</b-dropdown-item>

<b-dropdown-divider></b-dropdown-divider>

<b-dropdown-item
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Indices/IndexFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
autocomplete="off"
:placeholder="placeholder | localize"
data-description="filter index input"
class="search-query"
class="search-query index-filter-query"
:size="size"
@input="input"
@keyup.esc="onReset" />
Expand Down
25 changes: 25 additions & 0 deletions client/src/components/Indices/filtersMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,40 @@ export default {
components: {
IndexFilter,
},
props: {
inputDebounceDelay: {
type: Number,
default: 500,
},
},
data() {
return {
filter: "",
implicitFilter: null,
helpHtml: null,
};
},
computed: {
isFiltered() {
return !!this.filter;
},
effectiveFilter() {
let filter = this.filter;
const implicitFilter = this.implicitFilter;
if (implicitFilter && filter) {
filter = `${implicitFilter} ${filter}`;
} else if (implicitFilter) {
filter = implicitFilter;
}
return filter;
},
filterAttrs() {
return {
"debounce-delay": this.inputDebounceDelay,
placeholder: this.titleSearch,
"help-html": this.helpHtml,
};
},
},
methods: {
appendTagFilter(tag, text) {
Expand Down
12 changes: 12 additions & 0 deletions client/src/components/Indices/filtersMixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,16 @@ describe("filtersMixin.js", () => {
wrapper.vm.appendTagFilter("name", "foobar");
expect(wrapper.vm.filter).toBe("name:'foobar'");
});

it("should have an effective filter that combines implicit and explicit filter", async () => {
wrapper.vm.implicitFilter = "tag:cowdog";
wrapper.vm.appendTagFilter("name", "foobar");
expect(wrapper.vm.filter).toBe("name:'foobar'");
expect(wrapper.vm.effectiveFilter).toBe("tag:cowdog name:'foobar'");
});

it("should just use implicit filter as effective if filter is empty", async () => {
wrapper.vm.implicitFilter = "tag:cowdog";
expect(wrapper.vm.effectiveFilter).toBe("tag:cowdog");
});
});
125 changes: 125 additions & 0 deletions client/src/components/Page/PageDropdown.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import PageDropdown from "./PageDropdown";
import { shallowMount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import flushPromises from "flush-promises";

import "jest-location-mock";

const localVue = getLocalVue(true);

const PAGE_DATA_OWNED = {
id: "page1235",
title: "My Page Title",
description: "A description derived from an annotation.",
shared: false,
};

const PAGE_DATA_SHARED = {
id: "page1235",
title: "My Page Title",
description: "A description derived from an annotation.",
shared: true,
};

describe("PageDropdown.vue", () => {
let wrapper;

describe("navigation on owned pages", () => {
beforeEach(async () => {
const propsData = {
root: "/rootprefix/",
page: PAGE_DATA_OWNED,
};
wrapper = shallowMount(PageDropdown, {
propsData,
localVue,
});
});

it("should create a page when create is clicked", async () => {
await wrapper.find(".page-dropdown").trigger("click");
});

it("should show page title", async () => {
const titleWrapper = await wrapper.find(".page-title");
expect(titleWrapper.text()).toBe("My Page Title");
});

it("should should decorate dropdown with page ID for automation", async () => {
const linkWrapper = await wrapper.find("[data-page-dropdown='page1235']");
expect(linkWrapper.exists()).toBeTruthy();
});

it("should have a 'Share' option", async () => {
expect(wrapper.find(".dropdown-menu .dropdown-item-share").exists()).toBeTruthy();
});
});

describe("navigation on shared pages", () => {
beforeEach(async () => {
const propsData = {
root: "/rootprefixshared/",
page: PAGE_DATA_SHARED,
};
wrapper = shallowMount(PageDropdown, {
propsData,
localVue,
});
});

it("should not have a 'Share' option", async () => {
expect(wrapper.find(".dropdown-menu dropdown-item-share").exists()).toBeFalsy();
});
});

describe("clicking page deletion", () => {
let axiosMock;
let confirmRequest;

async function mountAndDelete() {
const propsData = {
root: "/rootprefixdelete/",
page: PAGE_DATA_OWNED,
};
wrapper = shallowMount(PageDropdown, {
propsData,
localVue,
});
await wrapper.vm.onDelete();
await flushPromises();
}

beforeEach(async () => {
axiosMock = new MockAdapter(axios);
confirmRequest = true;
global.confirm = jest.fn(() => confirmRequest);
axiosMock.onDelete("rootprefixdelete/api/pages/page1235").reply(202, "deleted...");
});

afterEach(() => {
axiosMock.restore();
});

it("should confirm with localized deletion message", async () => {
await mountAndDelete();
expect(global.confirm).toHaveBeenCalledWith(expect.toBeLocalized());
});

it("should fire deletion API request upon confirmation", async () => {
await mountAndDelete();
const emitted = wrapper.emitted();
expect(emitted["onRemove"][0][0]).toEqual("page1235");
expect(emitted["onSuccess"][0][0]).toEqual("deleted...");
});

it("should not fire deletion API request if not confirmed", async () => {
confirmRequest = false;
await mountAndDelete();
const emitted = wrapper.emitted();
expect(emitted["onRemove"]).toBeFalsy();
expect(emitted["onSuccess"]).toBeFalsy();
});
});
});
89 changes: 89 additions & 0 deletions client/src/components/Page/PageDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<div>
<b-link
class="page-dropdown font-weight-bold"
data-toggle="dropdown"
aria-haspopup="true"
:data-page-dropdown="page.id"
aria-expanded="false">
<font-awesome-icon icon="caret-down" />
<span class="page-title">{{ page.title }}</span>
</b-link>
<p v-if="page.description">{{ page.description }}</p>
<div class="dropdown-menu" aria-labelledby="page-dropdown">
<a class="dropdown-item dropdown-item-view" :href="urlView">
<span class="fa fa-eye fa-fw mr-1" />
<span>View</span>
</a>
<a class="dropdown-item dropdown-item-edit" v-if="!readOnly" :href="urlEdit">
<span class="fa fa-edit fa-fw mr-1" />
<span>Edit Content</span>
</a>
<a class="dropdown-item dropdown-item-edit-attributes" v-if="!readOnly" :href="urlEditAttributes">
<span class="fa fa-share-alt fa-fw mr-1" />
<span>Edit Attributes</span>
</a>
<a class="dropdown-item dropdown-item-share" v-if="!readOnly" :href="urlShare">
<span class="fa fa-share-alt fa-fw mr-1" />
<span>Share</span>
</a>
<a class="dropdown-item" href="#" v-if="!readOnly" @click.prevent="onDelete">
<span class="fa fa-trash fa-fw mr-1" />
<span>Delete</span>
</a>
</div>
</div>
</template>
<script>
import { Services } from "./services";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faCaretDown } from "@fortawesome/free-solid-svg-icons";

library.add(faCaretDown);

export default {
components: {
FontAwesomeIcon,
},
props: ["page", "root"],
computed: {
urlEdit() {
return `${this.root}page/edit_content?id=${this.page.id}`;
},
urlEditAttributes() {
return `${this.root}page/edit?id=${this.page.id}`;
},
urlShare() {
return `${this.root}pages/sharing?id=${this.page.id}`;
},
urlView() {
return `${this.root}page/display_by_id?id=${this.page.id}`;
},
readOnly() {
return !!this.page.shared;
},
},
created() {
this.services = new Services({ root: this.root });
},
methods: {
onDelete() {
const id = this.page.id;
const name = this.page.title;
const confirmationMessage = this.l(`Are you sure you want to delete page '${name}'?`);
if (window.confirm(confirmationMessage)) {
this.services
.deletePage(id)
.then((message) => {
this.$emit("onRemove", id);
this.$emit("onSuccess", message);
})
.catch((error) => {
this.$emit("onError", error);
});
}
},
},
};
</script>
28 changes: 28 additions & 0 deletions client/src/components/Page/PageIndexActions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import PageIndexActions from "./PageIndexActions";
import { shallowMount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";

import "jest-location-mock";

const localVue = getLocalVue();

describe("PageIndexActions.vue", () => {
let wrapper;

beforeEach(async () => {
const propsData = {
root: "/rootprefix/",
};
wrapper = shallowMount(PageIndexActions, {
propsData,
localVue,
});
});

describe("naviation", () => {
it("should create a page when create is clicked", async () => {
await wrapper.find("#page-create").trigger("click");
expect(window.location).toBeAt("/rootprefix/pages/create");
});
});
});
35 changes: 35 additions & 0 deletions client/src/components/Page/PageIndexActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<span>
<b-button id="page-create" class="m-1" @click="create">
<font-awesome-icon icon="plus" />
{{ "Create" | localize }}
</b-button>
</span>
</template>

<script>
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";

import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
library.add(faPlus);

export default {
components: {
BButton,
FontAwesomeIcon,
},
props: {
root: {
type: String,
required: true,
},
},
methods: {
create: function () {
window.location.assign(`${this.root}pages/create`);
},
},
};
</script>
Loading