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 { ...@@ -12,8 +12,10 @@ import {
import { __ } from '~/locale'; import { __ } from '~/locale';
import initManualOrdering from '~/manual_ordering'; import initManualOrdering from '~/manual_ordering';
import Issuable from './issuable.vue'; import Issuable from './issuable.vue';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import { import {
sortOrderMap, sortOrderMap,
availableSortOptionsJira,
RELATIVE_POSITION, RELATIVE_POSITION,
PAGE_SIZE, PAGE_SIZE,
PAGE_SIZE_MANUAL, PAGE_SIZE_MANUAL,
...@@ -29,6 +31,7 @@ export default { ...@@ -29,6 +31,7 @@ export default {
GlPagination, GlPagination,
GlSkeletonLoading, GlSkeletonLoading,
Issuable, Issuable,
FilteredSearchBar,
}, },
props: { props: {
canBulkEdit: { canBulkEdit: {
...@@ -50,14 +53,25 @@ export default { ...@@ -50,14 +53,25 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
projectPath: {
type: String,
required: false,
default: '',
},
sortKey: { sortKey: {
type: String, type: String,
required: false, required: false,
default: '', default: '',
}, },
type: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
return { return {
availableSortOptionsJira,
filters: {}, filters: {},
isBulkEditing: false, isBulkEditing: false,
issuables: [], issuables: [],
...@@ -141,6 +155,22 @@ export default { ...@@ -141,6 +155,22 @@ export default {
nextPage: this.paginationNext, 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: { watch: {
selection() { selection() {
...@@ -262,51 +292,92 @@ export default { ...@@ -262,51 +292,92 @@ export default {
this.filters = filters; 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> </script>
<template> <template>
<ul v-if="loading" class="content-list"> <div>
<li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!"> <filtered-search-bar
<gl-skeleton-loading /> v-if="isJira"
</li> :namespace="projectPath"
</ul> :search-input-placeholder="__('Search Jira issues')"
<div v-else-if="issuables.length"> :tokens="[]"
<div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light"> :sort-options="availableSortOptionsJira"
<input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" /> :initial-filter-value="initialFilterValue"
<strong>{{ __('Select all') }}</strong> :initial-sort-by="initialSortBy"
</div> class="row-content-block"
<ul @onFilter="handleFilter"
class="content-list issuable-list issues-list" @onSort="handleSort"
:class="{ 'manual-ordering': isManualOrdering }" />
> <ul v-if="loading" class="content-list">
<issuable <li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!">
v-for="issuable in issuables" <gl-skeleton-loading />
:key="issuable.id" </li>
class="pr-3"
:class="{ 'user-can-drag': isManualOrdering }"
:issuable="issuable"
:is-bulk-editing="isBulkEditing"
:selected="isSelected(issuable.id)"
:base-url="baseUrl"
@select="onSelectIssuable"
/>
</ul> </ul>
<div class="mt-3"> <div v-else-if="issuables.length">
<gl-pagination <div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light">
v-bind="paginationProps" <input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" />
class="gl-justify-content-center" <strong>{{ __('Select all') }}</strong>
@input="onPaginate" </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> </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> </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> </template>
import { __ } from '~/locale';
// Maps sort order as it appears in the URL query to API `order_by` and `sort` params. // Maps sort order as it appears in the URL query to API `order_by` and `sort` params.
const PRIORITY = 'priority'; const PRIORITY = 'priority';
const ASC = 'asc'; const ASC = 'asc';
...@@ -31,3 +33,22 @@ export const sortOrderMap = { ...@@ -31,3 +33,22 @@ export const sortOrderMap = {
weight_desc: { order_by: WEIGHT, sort: DESC }, weight_desc: { order_by: WEIGHT, sort: DESC },
weight: { order_by: WEIGHT, sort: ASC }, 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 @@ ...@@ -3,4 +3,6 @@
.js-issuables-list{ data: { endpoint: expose_path(project_integrations_jira_issues_path(@project, format: :json)), .js-issuables-list{ data: { endpoint: expose_path(project_integrations_jira_issues_path(@project, format: :json)),
'can-bulk-edit': false, 'can-bulk-edit': false,
'empty-svg-path': image_path('illustrations/issues.svg'), '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 "" ...@@ -20166,6 +20166,9 @@ msgstr ""
msgid "Search Button" msgid "Search Button"
msgstr "" msgstr ""
msgid "Search Jira issues"
msgstr ""
msgid "Search Milestones" msgid "Search Milestones"
msgstr "" msgstr ""
......
...@@ -7,6 +7,7 @@ import { TEST_HOST } from 'helpers/test_constants'; ...@@ -7,6 +7,7 @@ import { TEST_HOST } from 'helpers/test_constants';
import flash from '~/flash'; import flash from '~/flash';
import IssuablesListApp from '~/issuables_list/components/issuables_list_app.vue'; import IssuablesListApp from '~/issuables_list/components/issuables_list_app.vue';
import Issuable from '~/issuables_list/components/issuable.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 issueablesEventBus from '~/issuables_list/eventhub';
import { PAGE_SIZE, PAGE_SIZE_MANUAL, RELATIVE_POSITION } from '~/issuables_list/constants'; import { PAGE_SIZE, PAGE_SIZE_MANUAL, RELATIVE_POSITION } from '~/issuables_list/constants';
...@@ -59,6 +60,7 @@ describe('Issuables list component', () => { ...@@ -59,6 +60,7 @@ describe('Issuables list component', () => {
const findLoading = () => wrapper.find(GlSkeletonLoading); const findLoading = () => wrapper.find(GlSkeletonLoading);
const findIssuables = () => wrapper.findAll(Issuable); const findIssuables = () => wrapper.findAll(Issuable);
const findFilteredSearchBar = () => wrapper.find(FilteredSearchBar);
const findFirstIssuable = () => findIssuables().wrappers[0]; const findFirstIssuable = () => findIssuables().wrappers[0];
const findEmptyState = () => wrapper.find(GlEmptyState); const findEmptyState = () => wrapper.find(GlEmptyState);
...@@ -75,6 +77,7 @@ describe('Issuables list component', () => { ...@@ -75,6 +77,7 @@ describe('Issuables list component', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
mockAxios.restore(); mockAxios.restore();
window.location = oldLocation; window.location = oldLocation;
}); });
...@@ -131,6 +134,7 @@ describe('Issuables list component', () => { ...@@ -131,6 +134,7 @@ describe('Issuables list component', () => {
}); });
it('does not call API until mounted', () => { it('does not call API until mounted', () => {
factory();
expect(apiSpy).not.toHaveBeenCalled(); expect(apiSpy).not.toHaveBeenCalled();
}); });
...@@ -173,6 +177,12 @@ describe('Issuables list component', () => { ...@@ -173,6 +177,12 @@ describe('Issuables list component', () => {
expect(wrapper.find(GlPagination).exists()).toBe(true); expect(wrapper.find(GlPagination).exists()).toBe(true);
}); });
}); });
it('does not render FilteredSearchBar', () => {
factory();
expect(findFilteredSearchBar().exists()).toBe(false);
});
}); });
describe('with bulk editing enabled', () => { describe('with bulk editing enabled', () => {
...@@ -523,4 +533,48 @@ describe('Issuables list component', () => { ...@@ -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