Skip to content

Commit

Permalink
allow exact query search (surrounded by quotes) in grids
Browse files Browse the repository at this point in the history
Users can bypass the client filtering by surrounding their raw search query by quotes and sending the unprocessed filter to the backend.
  • Loading branch information
ahmedhamidawan committed Mar 18, 2024
1 parent 8df4a5f commit 7eb3cc9
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
19 changes: 16 additions & 3 deletions client/src/components/Grid/GridList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ const filterClass = props.gridConfig.filtering;
const rawFilters = computed(() => Object.fromEntries(filterClass.getFiltersForText(filterText.value, true, false)));
const validFilters = computed(() => filterClass.getValidFilters(rawFilters.value, true).validFilters);
const invalidFilters = computed(() => filterClass.getValidFilters(rawFilters.value, true).invalidFilters);
const hasInvalidFilters = computed(() => Object.keys(invalidFilters.value).length > 0);
const isSurroundedByQuotes = computed(() => /^["'].*["']$/.test(filterText.value));
const hasInvalidFilters = computed(() => !isSurroundedByQuotes.value && Object.keys(invalidFilters.value).length > 0);
// hide message helper
const hideMessage = useDebounceFn(() => {
Expand Down Expand Up @@ -221,8 +222,11 @@ function onSelectAll(current: boolean): void {
* A valid filter/query for the backend
*/
function validatedFilterText() {
// there are no filters derived from the `filterText`; return the `filterText` as is
if (Object.keys(rawFilters.value).length === 0) {
if (isSurroundedByQuotes.value) {
// the filterText is surrounded by quotes, remove them
return filterText.value.slice(1, -1);
} else if (Object.keys(rawFilters.value).length === 0) {
// there are no filters derived from the `filterText`
return filterText.value;
}
// there are valid filters derived from the `filterText`
Expand Down Expand Up @@ -311,6 +315,15 @@ watch(operationMessage, () => {
<a href="javascript:void(0)" class="ui-link" @click="filterText = validatedFilterText()">
Remove invalid filters from query
</a>
or
<a
v-b-tooltip.noninteractive.hover
title="Note that this might produce inaccurate results"
href="javascript:void(0)"
class="ui-link"
@click="filterText = `'${filterText}'`">
Match the exact query provided
</a>
</BAlert>
</span>
<BOverlay v-else :show="resultsLoading" rounded="sm">
Expand Down
22 changes: 18 additions & 4 deletions client/src/components/Workflow/WorkflowList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const searchPlaceHolder = computed(() => {
placeHolder = "Search workflows shared with me";
}
placeHolder += " by name or use the advanced filtering options";
placeHolder += " by query or use the advanced filtering options";
return placeHolder;
});
Expand All @@ -72,13 +72,15 @@ const sortBy = computed(() => (listHeader.value && listHeader.value.sortBy) || "
const noItems = computed(() => !loading.value && workflowsLoaded.value.length === 0 && !filterText.value);
const noResults = computed(() => !loading.value && workflowsLoaded.value.length === 0 && filterText.value);
// Filtering computed refs
const workflowFilters = computed(() => WorkflowFilters(props.activeList));
const rawFilters = computed(() =>
Object.fromEntries(workflowFilters.value.getFiltersForText(filterText.value, true, false))
);
const validFilters = computed(() => workflowFilters.value.getValidFilters(rawFilters.value, true).validFilters);
const invalidFilters = computed(() => workflowFilters.value.getValidFilters(rawFilters.value, true).invalidFilters);
const hasInvalidFilters = computed(() => Object.keys(invalidFilters.value).length > 0);
const isSurroundedByQuotes = computed(() => /^["'].*["']$/.test(filterText.value));
const hasInvalidFilters = computed(() => !isSurroundedByQuotes.value && Object.keys(invalidFilters.value).length > 0);
function updateFilterValue(filterKey: string, newValue: any) {
const currentFilterText = filterText.value;
Expand Down Expand Up @@ -155,8 +157,11 @@ async function onPageChange(page: number) {
}
function validatedFilterText() {
// there are no filters derived from the `filterText`
if (Object.keys(rawFilters.value).length === 0) {
if (isSurroundedByQuotes.value) {
// the `filterText` is surrounded by quotes, remove them
return filterText.value.slice(1, -1);
} else if (Object.keys(rawFilters.value).length === 0) {
// there are no filters derived from the `filterText`
return filterText.value;
}
// there are valid filters derived from the `filterText`
Expand Down Expand Up @@ -277,6 +282,15 @@ onMounted(() => {
<a href="javascript:void(0)" class="ui-link" @click="filterText = validatedFilterText()">
Remove invalid filters from query
</a>
or
<a
v-b-tooltip.noninteractive.hover
title="Note that this might produce inaccurate results"
href="javascript:void(0)"
class="ui-link"
@click="filterText = `'${filterText}'`">
Match the exact query provided
</a>
</BAlert>
</span>

Expand Down
3 changes: 2 additions & 1 deletion test/integration_selenium/test_trs_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def trs_config_dir(cls):
return cls.temp_config_dir("trs")

def assert_workflow_imported(self, name):
self.workflow_index_search_for(name)
# surround name with quotes to consider case where name contains colons
self.workflow_index_search_for(f'"{name}"')
assert len(self.workflow_card_elements()) == 1, f"workflow ${name} not imported"

def test_import_workflow_by_url_dockstore(self):
Expand Down

0 comments on commit 7eb3cc9

Please sign in to comment.