Commit 577b6f4c authored by Justin Ho's avatar Justin Ho

Add FilteredSearchBar for search and sort

- Use Vue component to avoid needing to refresh the page
- Allow search (free text) and sort
- Only show for Jira issues
parent 02f27285
......@@ -12,8 +12,10 @@ import {
import { __ } from '~/locale';
import initManualOrdering from '~/manual_ordering';
import Issuable from './issuable.vue';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import {
sortOrderMap,
availableSortOptionsJira,
RELATIVE_POSITION,
PAGE_SIZE,
PAGE_SIZE_MANUAL,
......@@ -29,6 +31,7 @@ export default {
GlPagination,
GlSkeletonLoading,
Issuable,
FilteredSearchBar,
},
props: {
canBulkEdit: {
......@@ -50,14 +53,25 @@ export default {
type: String,
required: true,
},
projectPath: {
type: String,
required: false,
default: '',
},
sortKey: {
type: String,
required: false,
default: '',
},
type: {
type: String,
required: false,
default: '',
},
},
data() {
return {
availableSortOptionsJira,
filters: {},
isBulkEditing: false,
issuables: [],
......@@ -141,6 +155,22 @@ export default {
nextPage: this.paginationNext,
};
},
isJira() {
return this.type === 'jira';
},
initialFilterValue() {
const value = [];
const { search } = this.getQueryObject();
if (search) {
value.push(search);
}
return value;
},
initialSortBy() {
const { sort } = this.getQueryObject();
return sort || 'created_desc';
},
},
watch: {
selection() {
......@@ -262,51 +292,92 @@ export default {
this.filters = filters;
},
refetchIssuables() {
const ignored = ['utf8', 'state'];
const params = omit(this.filters, ignored);
historyPushState(setUrlParams(params, window.location.href, true));
this.fetchIssuables();
},
handleFilter(filters) {
let search = null;
filters.forEach(filter => {
if (typeof filter === 'string') {
search = filter;
}
});
this.filters.search = search;
this.page = 1;
this.refetchIssuables();
},
handleSort(sort) {
this.filters.sort = sort;
this.page = 1;
this.refetchIssuables();
},
},
};
</script>
<template>
<ul v-if="loading" class="content-list">
<li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!">
<gl-skeleton-loading />
</li>
</ul>
<div v-else-if="issuables.length">
<div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light">
<input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" />
<strong>{{ __('Select all') }}</strong>
</div>
<ul
class="content-list issuable-list issues-list"
:class="{ 'manual-ordering': isManualOrdering }"
>
<issuable
v-for="issuable in issuables"
:key="issuable.id"
class="pr-3"
:class="{ 'user-can-drag': isManualOrdering }"
:issuable="issuable"
:is-bulk-editing="isBulkEditing"
:selected="isSelected(issuable.id)"
:base-url="baseUrl"
@select="onSelectIssuable"
/>
<div>
<filtered-search-bar
v-if="isJira"
:namespace="projectPath"
:search-input-placeholder="__('Search Jira issues')"
:tokens="[]"
:sort-options="availableSortOptionsJira"
:initial-filter-value="initialFilterValue"
:initial-sort-by="initialSortBy"
class="row-content-block"
@onFilter="handleFilter"
@onSort="handleSort"
/>
<ul v-if="loading" class="content-list">
<li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!">
<gl-skeleton-loading />
</li>
</ul>
<div class="mt-3">
<gl-pagination
v-bind="paginationProps"
class="gl-justify-content-center"
@input="onPaginate"
/>
<div v-else-if="issuables.length">
<div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light">
<input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" />
<strong>{{ __('Select all') }}</strong>
</div>
<ul
class="content-list issuable-list issues-list"
:class="{ 'manual-ordering': isManualOrdering }"
>
<issuable
v-for="issuable in issuables"
:key="issuable.id"
class="pr-3"
:class="{ 'user-can-drag': isManualOrdering }"
:issuable="issuable"
:is-bulk-editing="isBulkEditing"
:selected="isSelected(issuable.id)"
:base-url="baseUrl"
@select="onSelectIssuable"
/>
</ul>
<div class="mt-3">
<gl-pagination
v-bind="paginationProps"
class="gl-justify-content-center"
@input="onPaginate"
/>
</div>
</div>
<gl-empty-state
v-else
:title="emptyState.title"
:description="emptyState.description"
:svg-path="emptySvgPath"
:primary-button-link="emptyState.primaryLink"
:primary-button-text="emptyState.primaryText"
/>
</div>
<gl-empty-state
v-else
:title="emptyState.title"
:description="emptyState.description"
:svg-path="emptySvgPath"
:primary-button-link="emptyState.primaryLink"
:primary-button-text="emptyState.primaryText"
/>
</template>
import { __ } from '~/locale';
// Maps sort order as it appears in the URL query to API `order_by` and `sort` params.
const PRIORITY = 'priority';
const ASC = 'asc';
......@@ -31,3 +33,22 @@ export const sortOrderMap = {
weight_desc: { order_by: WEIGHT, sort: DESC },
weight: { order_by: WEIGHT, sort: ASC },
};
export const availableSortOptionsJira = [
{
id: 1,
title: __('Created date'),
sortDirection: {
descending: 'created_desc',
ascending: 'created_asc',
},
},
{
id: 2,
title: __('Last updated'),
sortDirection: {
descending: 'updated_desc',
ascending: 'updated_asc',
},
},
];
......@@ -3,4 +3,6 @@
.js-issuables-list{ data: { endpoint: expose_path(project_integrations_jira_issues_path(@project, format: :json)),
'can-bulk-edit': false,
'empty-svg-path': image_path('illustrations/issues.svg'),
'sort-key': @sort } }
'sort-key': @sort,
type: 'jira',
project_path: @project.full_path, } }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment