Commit c0d09dd9 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '229534-column-sort' into 'master'

Add sorting for incidents

See merge request gitlab-org/gitlab!38178
parents 688830b8 92dc2846
...@@ -332,7 +332,10 @@ export default { ...@@ -332,7 +332,10 @@ export default {
<p v-html="errorMessage || $options.i18n.errorMsg"></p> <p v-html="errorMessage || $options.i18n.errorMsg"></p>
</gl-alert> </gl-alert>
<gl-tabs content-class="gl-p-0" @input="filterAlertsByStatus"> <gl-tabs
content-class="gl-p-0 gl-border-b-solid gl-border-b-1 gl-border-gray-100"
@input="filterAlertsByStatus"
>
<gl-tab v-for="tab in $options.statusTabs" :key="tab.status"> <gl-tab v-for="tab in $options.statusTabs" :key="tab.status">
<template slot="title"> <template slot="title">
<span>{{ tab.title }}</span> <span>{{ tab.title }}</span>
......
...@@ -14,13 +14,15 @@ import { ...@@ -14,13 +14,15 @@ import {
GlTabs, GlTabs,
GlTab, GlTab,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { debounce, trim } from 'lodash'; import { debounce } from 'lodash';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { mergeUrlParams, joinPaths, visitUrl } from '~/lib/utils/url_utility'; import { mergeUrlParams, joinPaths, visitUrl } from '~/lib/utils/url_utility';
import getIncidents from '../graphql/queries/get_incidents.query.graphql'; import getIncidents from '../graphql/queries/get_incidents.query.graphql';
import { I18N, DEFAULT_PAGE_SIZE, INCIDENT_SEARCH_DELAY, INCIDENT_STATE_TABS } from '../constants'; import { I18N, DEFAULT_PAGE_SIZE, INCIDENT_SEARCH_DELAY, INCIDENT_STATE_TABS } from '../constants';
const TH_TEST_ID = { 'data-testid': 'incident-management-created-at-sort' };
const tdClass = const tdClass =
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap'; 'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
const thClass = 'gl-hover-bg-blue-50'; const thClass = 'gl-hover-bg-blue-50';
...@@ -48,8 +50,10 @@ export default { ...@@ -48,8 +50,10 @@ export default {
{ {
key: 'createdAt', key: 'createdAt',
label: s__('IncidentManagement|Date created'), label: s__('IncidentManagement|Date created'),
thClass: `${thClass} gl-pointer-events-none`, thClass,
tdClass, tdClass: `${tdClass} sortable-cell`,
sortable: true,
thAttr: TH_TEST_ID,
}, },
{ {
key: 'assignees', key: 'assignees',
...@@ -93,6 +97,7 @@ export default { ...@@ -93,6 +97,7 @@ export default {
state: this.stateFilter, state: this.stateFilter,
projectPath: this.projectPath, projectPath: this.projectPath,
issueTypes: ['INCIDENT'], issueTypes: ['INCIDENT'],
sort: this.sort,
firstPageSize: this.pagination.firstPageSize, firstPageSize: this.pagination.firstPageSize,
lastPageSize: this.pagination.lastPageSize, lastPageSize: this.pagination.lastPageSize,
prevPageCursor: this.pagination.prevPageCursor, prevPageCursor: this.pagination.prevPageCursor,
...@@ -119,6 +124,9 @@ export default { ...@@ -119,6 +124,9 @@ export default {
pagination: initialPaginationState, pagination: initialPaginationState,
incidents: {}, incidents: {},
stateFilter: '', stateFilter: '',
sort: 'created_desc',
sortBy: 'createdAt',
sortDesc: true,
}; };
}, },
computed: { computed: {
...@@ -168,7 +176,7 @@ export default { ...@@ -168,7 +176,7 @@ export default {
}, },
methods: { methods: {
onInputChange: debounce(function debounceSearch(input) { onInputChange: debounce(function debounceSearch(input) {
const trimmedInput = trim(input); const trimmedInput = input.trim();
if (trimmedInput !== this.searchTerm) { if (trimmedInput !== this.searchTerm) {
this.searchTerm = trimmedInput; this.searchTerm = trimmedInput;
} }
...@@ -205,6 +213,12 @@ export default { ...@@ -205,6 +213,12 @@ export default {
resetPagination() { resetPagination() {
this.pagination = initialPaginationState; this.pagination = initialPaginationState;
}, },
fetchSortedData({ sortBy, sortDesc }) {
const sortingDirection = sortDesc ? 'desc' : 'asc';
const sortingColumn = convertToSnakeCase(sortBy).replace(/_.*/, '');
this.sort = `${sortingColumn}_${sortingDirection}`;
},
}, },
}; };
</script> </script>
...@@ -214,7 +228,9 @@ export default { ...@@ -214,7 +228,9 @@ export default {
{{ $options.i18n.errorMsg }} {{ $options.i18n.errorMsg }}
</gl-alert> </gl-alert>
<div class="incident-management-list-header gl-display-flex gl-justify-content-space-between"> <div
class="incident-management-list-header gl-display-flex gl-justify-content-space-between gl-border-b-solid gl-border-b-1 gl-border-gray-100"
>
<gl-tabs content-class="gl-p-0" @input="filterIncidentsByState"> <gl-tabs content-class="gl-p-0" @input="filterIncidentsByState">
<gl-tab v-for="tab in $options.stateTabs" :key="tab.state" :data-testid="tab.state"> <gl-tab v-for="tab in $options.stateTabs" :key="tab.state" :data-testid="tab.state">
<template #title> <template #title>
...@@ -224,7 +240,7 @@ export default { ...@@ -224,7 +240,7 @@ export default {
</gl-tabs> </gl-tabs>
<gl-button <gl-button
class="gl-my-3 create-incident-button" class="gl-my-3 gl-mr-5 create-incident-button"
data-testid="createIncidentBtn" data-testid="createIncidentBtn"
:loading="redirecting" :loading="redirecting"
:disabled="redirecting" :disabled="redirecting"
...@@ -257,16 +273,22 @@ export default { ...@@ -257,16 +273,22 @@ export default {
stacked="md" stacked="md"
:tbody-tr-class="tbodyTrClass" :tbody-tr-class="tbodyTrClass"
:no-local-sorting="true" :no-local-sorting="true"
:sort-direction="'desc'"
:sort-desc.sync="sortDesc"
:sort-by.sync="sortBy"
sort-icon-left
fixed fixed
@row-clicked="navigateToIncidentDetails" @row-clicked="navigateToIncidentDetails"
@sort-changed="fetchSortedData"
> >
<template #cell(title)="{ item }"> <template #cell(title)="{ item }">
<div class="gl-display-sm-flex gl-align-items-center"> <div class="incident-management-list-title gl-display-flex gl-align-items-center">
<div class="gl-max-w-full text-truncate" :title="item.title">{{ item.title }}</div> <div class="gl-max-w-full text-truncate" :title="item.title">{{ item.title }}</div>
<gl-icon <gl-icon
v-if="item.state === 'closed'" v-if="item.state === 'closed'"
name="issue-close" name="issue-close"
class="gl-ml-1 gl-fill-blue-500" class="gl-mx-1 gl-fill-blue-500"
:size="16"
data-testid="incident-closed" data-testid="incident-closed"
/> />
</div> </div>
......
...@@ -6,7 +6,7 @@ export const I18N = { ...@@ -6,7 +6,7 @@ export const I18N = {
unassigned: s__('IncidentManagement|Unassigned'), unassigned: s__('IncidentManagement|Unassigned'),
createIncidentBtnLabel: s__('IncidentManagement|Create incident'), createIncidentBtnLabel: s__('IncidentManagement|Create incident'),
unPublished: s__('IncidentManagement|Unpublished'), unPublished: s__('IncidentManagement|Unpublished'),
searchPlaceholder: __('Search results...'), searchPlaceholder: __('Search results'),
}; };
export const INCIDENT_STATE_TABS = [ export const INCIDENT_STATE_TABS = [
...@@ -21,7 +21,7 @@ export const INCIDENT_STATE_TABS = [ ...@@ -21,7 +21,7 @@ export const INCIDENT_STATE_TABS = [
filters: 'closed', filters: 'closed',
}, },
{ {
title: s__('IncidentManagement|All incidents'), title: s__('IncidentManagement|All'),
state: 'ALL', state: 'ALL',
filters: 'all', filters: 'all',
}, },
......
query getIncidents( query getIncidents(
$projectPath: ID! $projectPath: ID!
$issueTypes: [IssueType!] $issueTypes: [IssueType!]
$sort: IssueSort
$state: IssuableState $state: IssuableState
$firstPageSize: Int $firstPageSize: Int
$lastPageSize: Int $lastPageSize: Int
...@@ -13,6 +14,7 @@ query getIncidents( ...@@ -13,6 +14,7 @@ query getIncidents(
search: $searchTerm search: $searchTerm
state: $state state: $state
types: $issueTypes types: $issueTypes
sort: $sort
first: $firstPageSize first: $firstPageSize
last: $lastPageSize last: $lastPageSize
after: $nextPageCursor after: $nextPageCursor
......
...@@ -76,23 +76,31 @@ ...@@ -76,23 +76,31 @@
} }
} }
} }
.incident-management-list-title {
@include gl-flex-direction-row-reverse;
}
} }
} }
.gl-tab-nav-item { .gl-tabs-nav {
color: $gl-gray-600; border-bottom-width: 0;
.gl-tab-nav-item {
color: $gl-gray-600;
> .gl-tab-counter-badge { > .gl-tab-counter-badge {
color: inherit; color: inherit;
@include gl-font-sm; @include gl-font-sm;
background-color: $white-normal; background-color: $gray-50;
}
} }
} }
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
.incident-management-list-header { .incident-management-list-header {
flex-direction: column-reverse; flex-direction: column-reverse;
}; }
.create-incident-button { .create-incident-button {
@include gl-w-full; @include gl-w-full;
......
---
title: Add sorting by date for incident list
merge_request: 38178
author:
type: changed
...@@ -12811,7 +12811,7 @@ msgstr "" ...@@ -12811,7 +12811,7 @@ msgstr ""
msgid "Incident Management Limits" msgid "Incident Management Limits"
msgstr "" msgstr ""
msgid "IncidentManagement|All incidents" msgid "IncidentManagement|All"
msgstr "" msgstr ""
msgid "IncidentManagement|Assignees" msgid "IncidentManagement|Assignees"
...@@ -21108,7 +21108,7 @@ msgstr "" ...@@ -21108,7 +21108,7 @@ msgstr ""
msgid "Search requirements" msgid "Search requirements"
msgstr "" msgstr ""
msgid "Search results..." msgid "Search results"
msgstr "" msgstr ""
msgid "Search users" msgid "Search users"
......
...@@ -30,9 +30,11 @@ describe('Incidents List', () => { ...@@ -30,9 +30,11 @@ describe('Incidents List', () => {
const findAlert = () => wrapper.find(GlAlert); const findAlert = () => wrapper.find(GlAlert);
const findLoader = () => wrapper.find(GlLoadingIcon); const findLoader = () => wrapper.find(GlLoadingIcon);
const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip); const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip);
const findDateColumnHeader = () =>
wrapper.find('[data-testid="incident-management-created-at-sort"]');
const findSearch = () => wrapper.find(GlSearchBoxByType);
const findAssingees = () => wrapper.findAll('[data-testid="incident-assignees"]'); const findAssingees = () => wrapper.findAll('[data-testid="incident-assignees"]');
const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]'); const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]');
const findSearch = () => wrapper.find(GlSearchBoxByType);
const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']"); const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']");
const findPagination = () => wrapper.find(GlPagination); const findPagination = () => wrapper.find(GlPagination);
const findStatusFilterTabs = () => wrapper.findAll(GlTab); const findStatusFilterTabs = () => wrapper.findAll(GlTab);
...@@ -304,4 +306,22 @@ describe('Incidents List', () => { ...@@ -304,4 +306,22 @@ describe('Incidents List', () => {
}); });
}); });
}); });
describe('sorting the incident list by column', () => {
beforeEach(() => {
mountComponent({
data: { incidents: mockIncidents },
loading: false,
});
});
it('updates sort with new direction and column key', () => {
expect(findDateColumnHeader().attributes('aria-sort')).toBe('descending');
findDateColumnHeader().trigger('click');
return wrapper.vm.$nextTick(() => {
expect(findDateColumnHeader().attributes('aria-sort')).toBe('ascending');
});
});
});
}); });
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