Commit c2b289c2 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '223921-search-for-issues-from-jira-vue' into 'master'

Add FilteredSearchBar for search and sort

See merge request gitlab-org/gitlab!36413
parents 3bb340f2 1b204f07
......@@ -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, } }
......@@ -20166,6 +20166,9 @@ msgstr ""
msgid "Search Button"
msgstr ""
msgid "Search Jira issues"
msgstr ""
msgid "Search Milestones"
msgstr ""
......
......@@ -7,6 +7,7 @@ import { TEST_HOST } from 'helpers/test_constants';
import flash from '~/flash';
import IssuablesListApp from '~/issuables_list/components/issuables_list_app.vue';
import Issuable from '~/issuables_list/components/issuable.vue';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import issueablesEventBus from '~/issuables_list/eventhub';
import { PAGE_SIZE, PAGE_SIZE_MANUAL, RELATIVE_POSITION } from '~/issuables_list/constants';
......@@ -59,6 +60,7 @@ describe('Issuables list component', () => {
const findLoading = () => wrapper.find(GlSkeletonLoading);
const findIssuables = () => wrapper.findAll(Issuable);
const findFilteredSearchBar = () => wrapper.find(FilteredSearchBar);
const findFirstIssuable = () => findIssuables().wrappers[0];
const findEmptyState = () => wrapper.find(GlEmptyState);
......@@ -75,6 +77,7 @@ describe('Issuables list component', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockAxios.restore();
window.location = oldLocation;
});
......@@ -131,6 +134,7 @@ describe('Issuables list component', () => {
});
it('does not call API until mounted', () => {
factory();
expect(apiSpy).not.toHaveBeenCalled();
});
......@@ -173,6 +177,12 @@ describe('Issuables list component', () => {
expect(wrapper.find(GlPagination).exists()).toBe(true);
});
});
it('does not render FilteredSearchBar', () => {
factory();
expect(findFilteredSearchBar().exists()).toBe(false);
});
});
describe('with bulk editing enabled', () => {
......@@ -523,4 +533,48 @@ describe('Issuables list component', () => {
});
});
});
describe('when type is "jira"', () => {
it('renders FilteredSearchBar', () => {
factory({ type: 'jira' });
expect(findFilteredSearchBar().exists()).toBe(true);
});
describe('initialSortBy', () => {
const query = '?sort=updated_asc';
it('sets default value', () => {
factory({ type: 'jira' });
expect(findFilteredSearchBar().props('initialSortBy')).toBe('created_desc');
});
it('sets value according to query', () => {
setUrl(query);
factory({ type: 'jira' });
expect(findFilteredSearchBar().props('initialSortBy')).toBe('updated_asc');
});
});
describe('initialFilterValue', () => {
it('does not set value when no query', () => {
factory({ type: 'jira' });
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([]);
});
it('sets value according to query', () => {
const query = '?search=free+text';
setUrl(query);
factory({ type: 'jira' });
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual(['free text']);
});
});
});
});
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