Commit 41620b53 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '229400-incident-state' into 'master'

Add incident state columns

See merge request gitlab-org/gitlab!37889
parents cda94de0 72d1779d
......@@ -11,13 +11,15 @@ import {
GlSearchBoxByType,
GlIcon,
GlPagination,
GlTabs,
GlTab,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { debounce, trim } from 'lodash';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
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 } from '../constants';
import { I18N, DEFAULT_PAGE_SIZE, INCIDENT_SEARCH_DELAY, INCIDENT_STATE_TABS } from '../constants';
const tdClass =
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
......@@ -35,6 +37,7 @@ const initialPaginationState = {
export default {
i18n: I18N,
stateTabs: INCIDENT_STATE_TABS,
fields: [
{
key: 'title',
......@@ -67,6 +70,8 @@ export default {
GlSearchBoxByType,
GlIcon,
GlPagination,
GlTabs,
GlTab,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -78,6 +83,7 @@ export default {
variables() {
return {
searchTerm: this.searchTerm,
state: this.stateFilter,
projectPath: this.projectPath,
labelNames: ['incident'],
firstPageSize: this.pagination.firstPageSize,
......@@ -105,6 +111,7 @@ export default {
searchTerm: '',
pagination: initialPaginationState,
incidents: {},
stateFilter: '',
};
},
computed: {
......@@ -138,14 +145,17 @@ export default {
return mergeUrlParams({ issuable_template: this.incidentTemplateName }, this.newIssuePath);
},
},
watch: {
searchTerm: debounce(function debounceSearch(input) {
if (input !== this.searchTerm) {
this.searchTerm = input;
methods: {
onInputChange: debounce(function debounceSearch(input) {
const trimmedInput = trim(input);
if (trimmedInput !== this.searchTerm) {
this.searchTerm = trimmedInput;
}
}, INCIDENT_SEARCH_DELAY),
filterIncidentsByState(tabIndex) {
const { filters } = this.$options.stateTabs[tabIndex];
this.stateFilter = filters;
},
methods: {
hasAssignees(assignees) {
return Boolean(assignees.nodes?.length);
},
......@@ -183,9 +193,17 @@ export default {
{{ $options.i18n.errorMsg }}
</gl-alert>
<div class="gl-display-flex gl-justify-content-end">
<div class="incident-management-list-header gl-display-flex gl-justify-content-space-between">
<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>
<span>{{ tab.title }}</span>
</template>
</gl-tab>
</gl-tabs>
<gl-button
class="gl-mt-3 gl-mb-3 create-incident-button"
class="gl-my-3 create-incident-button"
data-testid="createIncidentBtn"
:loading="redirecting"
:disabled="redirecting"
......@@ -200,9 +218,10 @@ export default {
<div class="gl-bg-gray-10 gl-p-5 gl-border-b-solid gl-border-b-1 gl-border-gray-100">
<gl-search-box-by-type
v-model.trim="searchTerm"
:value="searchTerm"
class="gl-bg-white"
:placeholder="$options.i18n.searchPlaceholder"
@input="onInputChange"
/>
</div>
......@@ -221,7 +240,7 @@ export default {
@row-clicked="navigateToIncidentDetails"
>
<template #cell(title)="{ item }">
<div class="gl-display-flex gl-justify-content-center">
<div class="gl-display-sm-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'"
......
......@@ -8,5 +8,23 @@ export const I18N = {
searchPlaceholder: __('Search or filter results...'),
};
export const INCIDENT_STATE_TABS = [
{
title: s__('IncidentManagement|Open'),
state: 'OPENED',
filters: 'opened',
},
{
title: s__('IncidentManagement|Closed'),
state: 'CLOSED',
filters: 'closed',
},
{
title: s__('IncidentManagement|All incidents'),
state: 'ALL',
filters: 'all',
},
];
export const INCIDENT_SEARCH_DELAY = 300;
export const DEFAULT_PAGE_SIZE = 10;
......@@ -90,6 +90,10 @@
}
@include media-breakpoint-down(xs) {
.incident-management-list-header {
flex-direction: column-reverse;
};
.create-incident-button {
@include gl-w-full;
}
......
......@@ -8,5 +8,6 @@ module Types
value 'opened'
value 'closed'
value 'locked'
value 'all'
end
end
---
title: Add incident state columns
merge_request: 37889
author:
type: other
......@@ -5861,6 +5861,7 @@ type InstanceSecurityDashboard {
State of a GitLab issue or merge request
"""
enum IssuableState {
all
closed
locked
opened
......@@ -6562,6 +6563,7 @@ enum IssueSort {
State of a GitLab issue
"""
enum IssueState {
all
closed
locked
opened
......@@ -7987,6 +7989,7 @@ type MergeRequestSetWipPayload {
State of a GitLab merge request
"""
enum MergeRequestState {
all
closed
locked
merged
......
......@@ -16186,6 +16186,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "all",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
......@@ -18108,6 +18114,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "all",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
......@@ -22394,6 +22406,12 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "all",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "merged",
"description": null,
......@@ -12713,9 +12713,15 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
msgid "IncidentManagement|All incidents"
msgstr ""
msgid "IncidentManagement|Assignees"
msgstr ""
msgid "IncidentManagement|Closed"
msgstr ""
msgid "IncidentManagement|Create incident"
msgstr ""
......@@ -12731,6 +12737,9 @@ msgstr ""
msgid "IncidentManagement|No incidents to display."
msgstr ""
msgid "IncidentManagement|Open"
msgstr ""
msgid "IncidentManagement|There was an error displaying the incidents."
msgstr ""
......
......@@ -6,11 +6,12 @@ import {
GlAvatar,
GlPagination,
GlSearchBoxByType,
GlTab,
} from '@gitlab/ui';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import IncidentsList from '~/incidents/components/incidents_list.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { I18N } from '~/incidents/constants';
import { I18N, INCIDENT_STATE_TABS } from '~/incidents/constants';
import mockIncidents from '../mocks/incidents.json';
jest.mock('~/lib/utils/url_utility', () => ({
......@@ -34,6 +35,7 @@ describe('Incidents List', () => {
const findSearch = () => wrapper.find(GlSearchBoxByType);
const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']");
const findPagination = () => wrapper.find(GlPagination);
const findStatusFilterTabs = () => wrapper.findAll(GlTab);
function mountComponent({ data = { incidents: [] }, loading = false }) {
wrapper = mount(IncidentsList, {
......@@ -280,5 +282,25 @@ describe('Incidents List', () => {
expect(wrapper.vm.$data.searchTerm).toBe(SEARCH_TERM);
});
});
describe('State Filter Tabs', () => {
beforeEach(() => {
mountComponent({
data: { incidents: mockIncidents },
loading: false,
stubs: {
GlTab: true,
},
});
});
it('should display filter tabs', () => {
const tabs = findStatusFilterTabs().wrappers;
tabs.forEach((tab, i) => {
expect(tab.attributes('data-testid')).toContain(INCIDENT_STATE_TABS[i].state);
});
});
});
});
});
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