Commit 92dc2846 authored by David O'Regan's avatar David O'Regan Committed by Natalia Tepluhina

Add sorting for incidents

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