Commit 7332ef0a authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch...

Merge branch '35122-sentry-error-search-pagination-implement-pagination-in-sentry-errors-list' into 'master'

Implement pagination in Sentry errors list

See merge request gitlab-org/gitlab!21136
parents 21db5f40 ff6b1b59
......@@ -12,13 +12,18 @@ import {
GlDropdownItem,
GlDropdownDivider,
GlTooltipDirective,
GlPagination,
} from '@gitlab/ui';
import AccessorUtils from '~/lib/utils/accessor';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
import _ from 'underscore';
export default {
FIRST_PAGE: 1,
PREV_PAGE: 1,
NEXT_PAGE: 2,
fields: [
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' },
{ key: 'events', label: __('Events') },
......@@ -42,6 +47,7 @@ export default {
GlTable,
GlFormInput,
Icon,
GlPagination,
TimeAgo,
},
directives: {
......@@ -73,10 +79,28 @@ export default {
data() {
return {
errorSearchQuery: '',
pageValue: this.$options.FIRST_PAGE,
};
},
computed: {
...mapState('list', ['errors', 'loading', 'searchQuery', 'sortField', 'recentSearches']),
...mapState('list', [
'errors',
'loading',
'searchQuery',
'sortField',
'recentSearches',
'pagination',
]),
paginationRequired() {
return !_.isEmpty(this.pagination);
},
},
watch: {
pagination() {
if (typeof this.pagination.previous === 'undefined') {
this.pageValue = this.$options.FIRST_PAGE;
}
},
},
created() {
if (this.errorTrackingEnabled) {
......@@ -103,6 +127,17 @@ export default {
getDetailsLink(errorId) {
return `error_tracking/${errorId}/details`;
},
goToNextPage() {
this.pageValue = this.$options.NEXT_PAGE;
this.startPolling(`${this.indexPath}?cursor=${this.pagination.next.cursor}`);
},
goToPrevPage() {
this.startPolling(`${this.indexPath}?cursor=${this.pagination.previous.cursor}`);
},
goToPage(page) {
window.scrollTo(0, 0);
return page === this.$options.PREV_PAGE ? this.goToPrevPage() : this.goToNextPage();
},
isCurrentSortField(field) {
return field === this.sortField;
},
......@@ -217,7 +252,6 @@ export default {
</span>
</div>
</template>
<template slot="events" slot-scope="errors">
<div class="text-md-right">{{ errors.item.count }}</div>
</template>
......@@ -240,6 +274,15 @@ export default {
</div>
</template>
</gl-table>
<gl-pagination
v-show="!loading"
v-if="paginationRequired"
:prev-page="$options.PREV_PAGE"
:next-page="$options.NEXT_PAGE"
:value="pageValue"
align="center"
@input="goToPage"
/>
</div>
<div v-else-if="userCanEnableErrorTracking">
<gl-empty-state
......
......@@ -23,6 +23,7 @@ export function startPolling({ state, commit, dispatch }) {
if (!data) {
return;
}
commit(types.SET_PAGINATION, data.pagination);
commit(types.SET_ERRORS, data.errors);
commit(types.SET_LOADING, false);
dispatch('stopPolling');
......
......@@ -4,6 +4,7 @@ export const SET_LOADING = 'SET_LOADING';
export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH';
export const CLEAR_RECENT_SEARCHES = 'CLEAR_RECENT_SEARCHES';
export const LOAD_RECENT_SEARCHES = 'LOAD_RECENT_SEARCHES';
export const SET_PAGINATION = 'SET_PAGINATION';
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_SORT_FIELD = 'SET_SORT_FIELD';
export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
......@@ -44,6 +44,9 @@ export default {
throw e;
}
},
[types.SET_PAGINATION](state, pagination) {
state.pagination = pagination;
},
[types.SET_SORT_FIELD](state, field) {
state.sortField = field;
},
......
......@@ -6,4 +6,5 @@ export default () => ({
searchQuery: null,
indexPath: '',
recentSearches: [],
pagination: {},
});
---
title: Implement pagination for sentry errors
merge_request: 21136
author:
type: added
......@@ -8,8 +8,8 @@ import {
GlFormInput,
GlDropdown,
GlDropdownItem,
GlPagination,
} from '@gitlab/ui';
import createListState from '~/error_tracking/store/list/state';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
import errorsList from './list_mock.json';
......@@ -27,13 +27,16 @@ describe('ErrorTrackingList', () => {
const findRecentSearchesDropdown = () =>
wrapper.find('.filtered-search-history-dropdown-wrapper');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPagination = () => wrapper.find(GlPagination);
function mountComponent({
errorTrackingEnabled = true,
userCanEnableErrorTracking = true,
sync = true,
stubs = {
'gl-link': GlLink,
'gl-table': GlTable,
'gl-pagination': GlPagination,
'gl-dropdown': GlDropdown,
'gl-dropdown-item': GlDropdownItem,
},
......@@ -41,6 +44,7 @@ describe('ErrorTrackingList', () => {
wrapper = shallowMount(ErrorTrackingList, {
localVue,
store,
sync,
propsData: {
indexPath: '/path',
enableErrorTrackingLink: '/link',
......@@ -69,7 +73,20 @@ describe('ErrorTrackingList', () => {
sortByField: jest.fn(),
};
const state = createListState();
const state = {
indexPath: '',
recentSearches: [],
errors: errorsList,
loading: true,
pagination: {
previous: {
cursor: 'previousCursor',
},
next: {
cursor: 'nextCursor',
},
},
};
store = new Vuex.Store({
modules: {
......@@ -252,4 +269,65 @@ describe('ErrorTrackingList', () => {
});
});
});
describe('When pagination is not required', () => {
beforeEach(() => {
store.state.list.pagination = {};
mountComponent();
});
it('should not render the pagination component', () => {
expect(findPagination().exists()).toBe(false);
});
});
describe('When pagination is required', () => {
describe('and the user is on the first page', () => {
beforeEach(() => {
mountComponent({ sync: false });
});
it('shows a disabled Prev button', () => {
expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe('true');
});
});
describe('and the user is not on the first page', () => {
describe('and the previous button is clicked', () => {
beforeEach(() => {
mountComponent({ sync: false });
wrapper.setData({ pageValue: 2 });
});
it('fetches the previous page of results', () => {
expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe(undefined);
wrapper.vm.goToPrevPage();
expect(actions.startPolling).toHaveBeenCalledTimes(2);
expect(actions.startPolling).toHaveBeenLastCalledWith(
expect.anything(),
'/path?cursor=previousCursor',
undefined,
);
});
});
describe('and the next page button is clicked', () => {
beforeEach(() => {
mountComponent({ sync: false });
});
it('fetches the next page of results', () => {
window.scrollTo = jest.fn();
findPagination().vm.$emit('input', 2);
expect(window.scrollTo).toHaveBeenCalledWith(0, 0);
expect(actions.startPolling).toHaveBeenCalledTimes(2);
expect(actions.startPolling).toHaveBeenLastCalledWith(
expect.anything(),
'/path?cursor=nextCursor',
undefined,
);
});
});
});
});
});
......@@ -30,6 +30,7 @@ describe('error tracking actions', () => {
{},
[
{ type: types.SET_LOADING, payload: true },
{ type: types.SET_PAGINATION, payload: payload.pagination },
{ type: types.SET_ERRORS, payload: payload.errors },
{ type: types.SET_LOADING, payload: false },
],
......
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