Improved index filtering input for workflow list.

This commit is contained in:
John Chilton
2022-05-09 18:41:21 -04:00
parent 099e5ae7cc
commit a313cf3f5e
6 changed files with 155 additions and 16 deletions
+16 -7
View File
@@ -23,14 +23,23 @@ export default {
}
},
},
watch: {
incomingValue(val) {
if (this.delay === 0) {
this.sendUpdate(val);
}
},
},
beforeMount() {
const debounced$ = this.watch$("incomingValue").pipe(
debounceTime(this.delay),
distinctUntilChanged(),
filter((val) => val !== null && val !== this.value),
finalize(() => this.sendUpdate(this.incomingValue))
);
this.$subscribeTo(debounced$, (val) => this.sendUpdate(val));
if (this.delay !== 0) {
const debounced$ = this.watch$("incomingValue").pipe(
debounceTime(this.delay),
distinctUntilChanged(),
filter((val) => val !== null && val !== this.value),
finalize(() => this.sendUpdate(this.incomingValue))
);
this.$subscribeTo(debounced$, (val) => this.sendUpdate(val));
}
},
render() {
return this.$scopedSlots.default({
@@ -0,0 +1,85 @@
<template>
<span>
<b-input-group>
<DebouncedInput :delay="debounceDelay" v-slot="{ value, input }" v-model="localFilter">
<b-form-input
:id="id"
name="query"
:value="value"
autocomplete="off"
:placeholder="placeholder | localize"
data-description="filter index input"
@input="input"
@keyup.esc="onReset" />
</DebouncedInput>
<b-input-group-append>
<b-button data-description="show deleted filter toggle" @click="onHelp" title="Advanced Filtering Help">
<icon icon="question" />
</b-button>
</b-input-group-append>
</b-input-group>
<b-modal v-model="showHelp" title="Filtering Options Help" ok-only>
<div v-html="helpHtml"></div>
</b-modal>
</span>
</template>
<script>
import DebouncedInput from "components/DebouncedInput";
import { BInputGroup, BInputGroupAppend, BButton, BModal } from "bootstrap-vue";
/**
* Component for the search/filter button on the top of Galaxy object index grids.
*/
export default {
components: {
DebouncedInput,
BInputGroup,
BInputGroupAppend,
BButton,
BModal,
},
props: {
id: {
type: String,
required: true,
},
placeholder: {
type: String,
required: true,
},
helpHtml: {
type: String,
required: true,
},
debounceDelay: {
type: Number,
default: 500,
},
value: {},
},
data() {
return {
showHelp: false,
localFilter: this.value,
};
},
watch: {
value(newValue) {
this.localFilter = newValue;
},
localFilter(newVal) {
this.$emit("input", newVal);
},
},
methods: {
onHelp() {
this.showHelp = true;
},
onReset() {
this.localFilter = "";
},
},
};
</script>
+2
View File
@@ -0,0 +1,2 @@
This directory contains utilities for building out grids and lists for arrays of
Galaxy objects coming from the API.
@@ -5,14 +5,13 @@
<b-alert :variant="messageVariant" :show="showMessage">{{ message }}</b-alert>
<b-row class="mb-3">
<b-col cols="6">
<b-input
<index-filter
:debounce-delay="inputDebounceDelay"
id="workflow-search"
class="m-1"
name="query"
:placeholder="titleSearchWorkflows"
autocomplete="off"
type="text"
v-model="filter" />
:help-html="helpHtml"
v-model="filter">
</index-filter>
</b-col>
<b-col>
<span class="float-right">
@@ -116,15 +115,49 @@ import WorkflowDropdown from "./WorkflowDropdown";
import UtcDate from "components/UtcDate";
import { getGalaxyInstance } from "app";
import paginationMixin from "./paginationMixin";
import IndexFilter from "components/Indices/IndexFilter";
library.add(faPlus, faUpload, faSpinner, faGlobe, faShareAlt, farStar, faStar);
const helpHtml = `<div>
<p>This textbox box can be used to filter the workflows displayed.
<p>Text entered here will be searched against workflow names and tags. Additionally, advanced
filtering tags can be used to refine the search more precisely. Tags are of the form
<code>&lt;tag_name&gt;:&lt;tag_value&gt;</code> or <code>&lt;tag_name&gt;:'&lt;tag_value&gt;'</code>.
For instance to search just for RNAseq in the workflow name, <code>name:rnsseq</code> can be used.
Notice by default the search is not case-sensitive.
If the quoted version of tag is used, the search is not case sensitive and only full matches will be
returned. So <code>name:'RNAseq'</code> would show only workflows named exactly <code>RNAseq</code>.
<p>The available tags are:
<dl>
<dt><code>name</code></dt>
<dd>This filters only against the workflow name.</dd>
<dt><code>tag</code></dt>
<dd>This filters only against the workflow tag. You may also just click on a tag in your list of workflows to filter on that tag using this directly.</dd>
<dt><code>is:published</code></dt>
<dd>This filters the workflows such that only published workflows are shown. You may also just click on the "published" icon of a worklfow in your list to filter on this directly.</dd>
<dt><code>is:shared</code></dt>
<dd>This filters the workflows such that only workflows shared from another user directly with you are are shown. You may also just click on the "shared with me" icon of a worklfow in your list to filter on this directly.</dd>
</dl>
</div>
`;
export default {
components: {
FontAwesomeIcon,
UtcDate,
Tags,
WorkflowDropdown,
IndexFilter,
},
props: {
inputDebounceDelay: {
type: Number,
default: 500,
},
},
mixins: [paginationMixin],
data() {
@@ -172,6 +205,7 @@ export default {
titleRunWorkflow: _l("Run workflow"),
workflowItemsModel: [],
workflowItems: [],
helpHtml: helpHtml,
perPage: this.rowsPerPage(50),
};
},
@@ -52,7 +52,9 @@ describe("WorkflowList.vue", () => {
axiosMock
.onGet("/api/workflows", { params: { limit: 50, offset: 0, skip_step_counts: true, search: "" } })
.reply(200, mockWorkflowsData, { total_matches: "1" });
const propsData = {};
const propsData = {
inputDebounceDelay: 0,
};
wrapper = mount(Workflows, {
propsData,
localVue,
@@ -90,8 +92,9 @@ describe("WorkflowList.vue", () => {
.onGet("/api/workflows", { params: { limit: 50, offset: 0, skip_step_counts: true, search: "mytext" } })
.reply(200, [], { total_matches: "0" });
wrapper.find("#workflow-search").setValue("mytext");
await wrapper.find("#workflow-search").setValue("mytext");
flushPromises();
expect(wrapper.find("#workflow-search").element.value).toBe("mytext");
expect(wrapper.vm.filter).toBe("mytext");
});
+7 -1
View File
@@ -566,10 +566,15 @@ class NavigatesGalaxy(HasDriver):
return "email" in self.get_logged_in_user()
@retry_during_transitions
def _inline_search_for(self, selector, search_term=None):
def _inline_search_for(self, selector, search_term=None, escape_to_clear=False):
# Clear tooltip resulting from clicking on the masthead to get here.
self.clear_tooltips()
search_box = self.wait_for_and_click(selector)
if escape_to_clear:
# The combination of DebouncedInput+b-input doesn't seem to uniformly respect
# .clear() below. We use escape handling a lot - and that does cauase to the input
# to reset correctly and fire the required re-active events/handlers.
self.send_escape(search_box)
search_box.clear()
if search_term is not None:
search_box.send_keys(search_term)
@@ -1218,6 +1223,7 @@ class NavigatesGalaxy(HasDriver):
return self._inline_search_for(
self.navigation.workflows.search_box,
search_term,
escape_to_clear=True,
)
def workflow_index_click_import(self):