Commit ff6b1b59 authored by Laura Montemayor's avatar Laura Montemayor Committed by Natalia Tepluhina

Implement pagination in Sentry errors list

parent 21db5f40
...@@ -12,13 +12,18 @@ import { ...@@ -12,13 +12,18 @@ import {
GlDropdownItem, GlDropdownItem,
GlDropdownDivider, GlDropdownDivider,
GlTooltipDirective, GlTooltipDirective,
GlPagination,
} from '@gitlab/ui'; } from '@gitlab/ui';
import AccessorUtils from '~/lib/utils/accessor'; import AccessorUtils from '~/lib/utils/accessor';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
import _ from 'underscore';
export default { export default {
FIRST_PAGE: 1,
PREV_PAGE: 1,
NEXT_PAGE: 2,
fields: [ fields: [
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' }, { key: 'error', label: __('Open errors'), thClass: 'w-70p' },
{ key: 'events', label: __('Events') }, { key: 'events', label: __('Events') },
...@@ -42,6 +47,7 @@ export default { ...@@ -42,6 +47,7 @@ export default {
GlTable, GlTable,
GlFormInput, GlFormInput,
Icon, Icon,
GlPagination,
TimeAgo, TimeAgo,
}, },
directives: { directives: {
...@@ -73,10 +79,28 @@ export default { ...@@ -73,10 +79,28 @@ export default {
data() { data() {
return { return {
errorSearchQuery: '', errorSearchQuery: '',
pageValue: this.$options.FIRST_PAGE,
}; };
}, },
computed: { 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() { created() {
if (this.errorTrackingEnabled) { if (this.errorTrackingEnabled) {
...@@ -103,6 +127,17 @@ export default { ...@@ -103,6 +127,17 @@ export default {
getDetailsLink(errorId) { getDetailsLink(errorId) {
return `error_tracking/${errorId}/details`; 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) { isCurrentSortField(field) {
return field === this.sortField; return field === this.sortField;
}, },
...@@ -217,7 +252,6 @@ export default { ...@@ -217,7 +252,6 @@ export default {
</span> </span>
</div> </div>
</template> </template>
<template slot="events" slot-scope="errors"> <template slot="events" slot-scope="errors">
<div class="text-md-right">{{ errors.item.count }}</div> <div class="text-md-right">{{ errors.item.count }}</div>
</template> </template>
...@@ -240,6 +274,15 @@ export default { ...@@ -240,6 +274,15 @@ export default {
</div> </div>
</template> </template>
</gl-table> </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>
<div v-else-if="userCanEnableErrorTracking"> <div v-else-if="userCanEnableErrorTracking">
<gl-empty-state <gl-empty-state
......
...@@ -23,6 +23,7 @@ export function startPolling({ state, commit, dispatch }) { ...@@ -23,6 +23,7 @@ export function startPolling({ state, commit, dispatch }) {
if (!data) { if (!data) {
return; return;
} }
commit(types.SET_PAGINATION, data.pagination);
commit(types.SET_ERRORS, data.errors); commit(types.SET_ERRORS, data.errors);
commit(types.SET_LOADING, false); commit(types.SET_LOADING, false);
dispatch('stopPolling'); dispatch('stopPolling');
......
...@@ -4,6 +4,7 @@ export const SET_LOADING = 'SET_LOADING'; ...@@ -4,6 +4,7 @@ export const SET_LOADING = 'SET_LOADING';
export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH'; export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH';
export const CLEAR_RECENT_SEARCHES = 'CLEAR_RECENT_SEARCHES'; export const CLEAR_RECENT_SEARCHES = 'CLEAR_RECENT_SEARCHES';
export const LOAD_RECENT_SEARCHES = 'LOAD_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_ENDPOINT = 'SET_ENDPOINT';
export const SET_SORT_FIELD = 'SET_SORT_FIELD'; export const SET_SORT_FIELD = 'SET_SORT_FIELD';
export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY'; export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
...@@ -44,6 +44,9 @@ export default { ...@@ -44,6 +44,9 @@ export default {
throw e; throw e;
} }
}, },
[types.SET_PAGINATION](state, pagination) {
state.pagination = pagination;
},
[types.SET_SORT_FIELD](state, field) { [types.SET_SORT_FIELD](state, field) {
state.sortField = field; state.sortField = field;
}, },
......
...@@ -6,4 +6,5 @@ export default () => ({ ...@@ -6,4 +6,5 @@ export default () => ({
searchQuery: null, searchQuery: null,
indexPath: '', indexPath: '',
recentSearches: [], recentSearches: [],
pagination: {},
}); });
---
title: Implement pagination for sentry errors
merge_request: 21136
author:
type: added
...@@ -8,8 +8,8 @@ import { ...@@ -8,8 +8,8 @@ import {
GlFormInput, GlFormInput,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlPagination,
} from '@gitlab/ui'; } from '@gitlab/ui';
import createListState from '~/error_tracking/store/list/state';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue'; import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
import errorsList from './list_mock.json'; import errorsList from './list_mock.json';
...@@ -27,13 +27,16 @@ describe('ErrorTrackingList', () => { ...@@ -27,13 +27,16 @@ describe('ErrorTrackingList', () => {
const findRecentSearchesDropdown = () => const findRecentSearchesDropdown = () =>
wrapper.find('.filtered-search-history-dropdown-wrapper'); wrapper.find('.filtered-search-history-dropdown-wrapper');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPagination = () => wrapper.find(GlPagination);
function mountComponent({ function mountComponent({
errorTrackingEnabled = true, errorTrackingEnabled = true,
userCanEnableErrorTracking = true, userCanEnableErrorTracking = true,
sync = true,
stubs = { stubs = {
'gl-link': GlLink, 'gl-link': GlLink,
'gl-table': GlTable, 'gl-table': GlTable,
'gl-pagination': GlPagination,
'gl-dropdown': GlDropdown, 'gl-dropdown': GlDropdown,
'gl-dropdown-item': GlDropdownItem, 'gl-dropdown-item': GlDropdownItem,
}, },
...@@ -41,6 +44,7 @@ describe('ErrorTrackingList', () => { ...@@ -41,6 +44,7 @@ describe('ErrorTrackingList', () => {
wrapper = shallowMount(ErrorTrackingList, { wrapper = shallowMount(ErrorTrackingList, {
localVue, localVue,
store, store,
sync,
propsData: { propsData: {
indexPath: '/path', indexPath: '/path',
enableErrorTrackingLink: '/link', enableErrorTrackingLink: '/link',
...@@ -69,7 +73,20 @@ describe('ErrorTrackingList', () => { ...@@ -69,7 +73,20 @@ describe('ErrorTrackingList', () => {
sortByField: jest.fn(), 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({ store = new Vuex.Store({
modules: { modules: {
...@@ -252,4 +269,65 @@ describe('ErrorTrackingList', () => { ...@@ -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', () => { ...@@ -30,6 +30,7 @@ describe('error tracking actions', () => {
{}, {},
[ [
{ type: types.SET_LOADING, payload: true }, { type: types.SET_LOADING, payload: true },
{ type: types.SET_PAGINATION, payload: payload.pagination },
{ type: types.SET_ERRORS, payload: payload.errors }, { type: types.SET_ERRORS, payload: payload.errors },
{ type: types.SET_LOADING, payload: false }, { 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