Commit a4fcb2d9 authored by David O'Regan's avatar David O'Regan Committed by Mayra Cabrera

Add published column

Add published column to
the incident list which
show if the incident is
published to the status page
parent b4665a80
...@@ -72,11 +72,18 @@ export default { ...@@ -72,11 +72,18 @@ export default {
GlPagination, GlPagination,
GlTabs, GlTabs,
GlTab, GlTab,
PublishedCell: () => import('ee_component/incidents/components/published_cell.vue'),
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
inject: ['projectPath', 'newIssuePath', 'incidentTemplateName', 'issuePath'], inject: [
'projectPath',
'newIssuePath',
'incidentTemplateName',
'issuePath',
'publishedAvailable',
],
apollo: { apollo: {
incidents: { incidents: {
query: getIncidents, query: getIncidents,
...@@ -144,6 +151,20 @@ export default { ...@@ -144,6 +151,20 @@ export default {
newIncidentPath() { newIncidentPath() {
return mergeUrlParams({ issuable_template: this.incidentTemplateName }, this.newIssuePath); return mergeUrlParams({ issuable_template: this.incidentTemplateName }, this.newIssuePath);
}, },
availableFields() {
return this.publishedAvailable
? [
...this.$options.fields,
...[
{
key: 'published',
label: s__('IncidentManagement|Published'),
thClass: 'gl-pointer-events-none',
},
],
]
: this.$options.fields;
},
}, },
methods: { methods: {
onInputChange: debounce(function debounceSearch(input) { onInputChange: debounce(function debounceSearch(input) {
...@@ -230,7 +251,7 @@ export default { ...@@ -230,7 +251,7 @@ export default {
</h4> </h4>
<gl-table <gl-table
:items="incidents.list || []" :items="incidents.list || []"
:fields="$options.fields" :fields="availableFields"
:show-empty="true" :show-empty="true"
:busy="loading" :busy="loading"
stacked="md" stacked="md"
...@@ -245,7 +266,7 @@ export default { ...@@ -245,7 +266,7 @@ export default {
<gl-icon <gl-icon
v-if="item.state === 'closed'" v-if="item.state === 'closed'"
name="issue-close" name="issue-close"
class="gl-fill-blue-500" class="gl-ml-1 gl-fill-blue-500"
data-testid="incident-closed" data-testid="incident-closed"
/> />
</div> </div>
...@@ -285,6 +306,12 @@ export default { ...@@ -285,6 +306,12 @@ export default {
</div> </div>
</template> </template>
<template v-if="publishedAvailable" #cell(published)="{ item }">
<published-cell
:status-page-published-incident="item.statusPagePublishedIncident"
:un-published="$options.i18n.unPublished"
/>
</template>
<template #table-busy> <template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" /> <gl-loading-icon size="lg" color="dark" class="mt-3" />
</template> </template>
......
...@@ -5,7 +5,8 @@ export const I18N = { ...@@ -5,7 +5,8 @@ export const I18N = {
noIncidents: s__('IncidentManagement|No incidents to display.'), noIncidents: s__('IncidentManagement|No incidents to display.'),
unassigned: s__('IncidentManagement|Unassigned'), unassigned: s__('IncidentManagement|Unassigned'),
createIncidentBtnLabel: s__('IncidentManagement|Create incident'), createIncidentBtnLabel: s__('IncidentManagement|Create incident'),
searchPlaceholder: __('Search or filter results...'), unPublished: s__('IncidentManagement|Unpublished'),
searchPlaceholder: __('Search results...'),
}; };
export const INCIDENT_STATE_TABS = [ export const INCIDENT_STATE_TABS = [
......
...@@ -37,6 +37,7 @@ query getIncidents( ...@@ -37,6 +37,7 @@ query getIncidents(
webUrl webUrl
} }
} }
statusPagePublishedIncident
} }
pageInfo { pageInfo {
hasNextPage hasNextPage
......
...@@ -8,7 +8,13 @@ export default () => { ...@@ -8,7 +8,13 @@ export default () => {
const selector = '#js-incidents'; const selector = '#js-incidents';
const domEl = document.querySelector(selector); const domEl = document.querySelector(selector);
const { projectPath, newIssuePath, incidentTemplateName, issuePath } = domEl.dataset; const {
projectPath,
newIssuePath,
incidentTemplateName,
issuePath,
publishedAvailable,
} = domEl.dataset;
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(),
...@@ -21,6 +27,7 @@ export default () => { ...@@ -21,6 +27,7 @@ export default () => {
incidentTemplateName, incidentTemplateName,
newIssuePath, newIssuePath,
issuePath, issuePath,
publishedAvailable,
}, },
apolloProvider, apolloProvider,
components: { components: {
......
...@@ -98,4 +98,9 @@ ...@@ -98,4 +98,9 @@
@include gl-w-full; @include gl-w-full;
} }
} }
// TODO: Abstract to `@gitlab/ui` utility set: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/921
.gl-fill-green-500 {
fill: $green-500;
}
} }
...@@ -10,3 +10,5 @@ module Projects::IncidentsHelper ...@@ -10,3 +10,5 @@ module Projects::IncidentsHelper
} }
end end
end end
Projects::IncidentsHelper.prepend_if_ee('EE::Projects::IncidentsHelper')
...@@ -4575,6 +4575,11 @@ type EpicIssue implements Noteable { ...@@ -4575,6 +4575,11 @@ type EpicIssue implements Noteable {
""" """
state: IssueState! state: IssueState!
"""
Indicates whether an issue is published to the status page
"""
statusPagePublishedIncident: Boolean
""" """
Indicates the currently logged in user is subscribed to the issue Indicates the currently logged in user is subscribed to the issue
""" """
...@@ -6208,6 +6213,11 @@ type Issue implements Noteable { ...@@ -6208,6 +6213,11 @@ type Issue implements Noteable {
""" """
state: IssueState! state: IssueState!
"""
Indicates whether an issue is published to the status page
"""
statusPagePublishedIncident: Boolean
""" """
Indicates the currently logged in user is subscribed to the issue Indicates the currently logged in user is subscribed to the issue
""" """
......
...@@ -12750,6 +12750,20 @@ ...@@ -12750,6 +12750,20 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "statusPagePublishedIncident",
"description": "Indicates whether an issue is published to the status page",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "subscribed", "name": "subscribed",
"description": "Indicates the currently logged in user is subscribed to the issue", "description": "Indicates the currently logged in user is subscribed to the issue",
...@@ -17106,6 +17120,20 @@ ...@@ -17106,6 +17120,20 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "statusPagePublishedIncident",
"description": "Indicates whether an issue is published to the status page",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "subscribed", "name": "subscribed",
"description": "Indicates the currently logged in user is subscribed to the issue", "description": "Indicates the currently logged in user is subscribed to the issue",
...@@ -759,6 +759,7 @@ Relationship between an epic and an issue ...@@ -759,6 +759,7 @@ Relationship between an epic and an issue
| `relationPath` | String | URI path of the epic-issue relation | | `relationPath` | String | URI path of the epic-issue relation |
| `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) | | `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) |
| `state` | IssueState! | State of the issue | | `state` | IssueState! | State of the issue |
| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue | | `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
| `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue | | `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue |
| `timeEstimate` | Int! | Time estimate of the issue | | `timeEstimate` | Int! | Time estimate of the issue |
...@@ -925,6 +926,7 @@ Represents a Group Member ...@@ -925,6 +926,7 @@ Represents a Group Member
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default | | `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
| `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) | | `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) |
| `state` | IssueState! | State of the issue | | `state` | IssueState! | State of the issue |
| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue | | `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
| `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue | | `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue |
| `timeEstimate` | Int! | Time estimate of the issue | | `timeEstimate` | Int! | Time estimate of the issue |
......
<script>
import { GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
i18n: {
published: s__('IncidentManagement|Published to status page'),
},
components: {
GlIcon,
},
props: {
statusPagePublishedIncident: {
type: Boolean,
required: false,
default: null,
},
unPublished: {
type: String,
required: true,
},
},
};
</script>
<template>
<div data-testid="published-cell">
<gl-icon
v-if="statusPagePublishedIncident"
name="status_success"
class="gl-fill-green-500"
:aria-label="$options.i18n.published"
/>
<div v-else>{{ unPublished }}</div>
</div>
</template>
...@@ -28,6 +28,9 @@ module EE ...@@ -28,6 +28,9 @@ module EE
null: true, null: true,
description: 'Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.', description: 'Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.',
resolve: -> (obj, _, _) { obj.supports_health_status? ? obj.health_status : nil } resolve: -> (obj, _, _) { obj.supports_health_status? ? obj.health_status : nil }
field :status_page_published_incident, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates whether an issue is published to the status page'
end end
end end
end end
......
# frozen_string_literal: true
module EE
module Projects
module IncidentsHelper
extend ::Gitlab::Utils::Override
override :incidents_data
def incidents_data(project)
super.merge(
incidents_data_published_available(project)
)
end
private
def incidents_data_published_available(project)
return {} unless project.feature_available?(:status_page)
{
'published-available' => 'true'
}
end
end
end
end
---
title: Add published column
merge_request: 38439
author:
type: changed
import { shallowMount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import PublishedCell from 'ee/incidents/components/published_cell.vue';
describe('Incidents Published Cell', () => {
let wrapper;
const findCell = () => wrapper.find("[data-testid='published-cell']");
function mountComponent({
props = { statusPagePublishedIncident: null, unPublished: 'Unpublished' },
}) {
wrapper = shallowMount(PublishedCell, {
propsData: {
...props,
},
stubs: {
GlIcon: true,
},
});
}
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
describe('Published cell', () => {
beforeEach(() => {
mountComponent({});
});
it('render a cell with unpublished by default', () => {
expect(
findCell()
.find(GlIcon)
.exists(),
).toBe(false);
expect(findCell().text()).toBe('Unpublished');
});
it('render a status success icon if statusPagePublishedIncident returns true', () => {
wrapper.setProps({ statusPagePublishedIncident: true });
return wrapper.vm.$nextTick().then(() => {
expect(
findCell()
.find(GlIcon)
.exists(),
).toBe(true);
});
});
});
});
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::IncidentsHelper do
include Gitlab::Routing.url_helpers
let_it_be(:project) { create(:project) }
let(:project_path) { project.full_path }
let(:new_issue_path) { new_project_issue_path(project) }
let(:issue_path) { project_issues_path(project) }
describe '#incidents_data' do
let(:expected_incidents_data) do
{
'project-path' => project_path,
'new-issue-path' => new_issue_path,
'incident-template-name' => 'incident',
'issue-path' => issue_path
}
end
subject { helper.incidents_data(project) }
before do
allow(project).to receive(:feature_available?).with(:status_page).and_return(status_page_feature_available)
end
context 'when status page feature is available' do
let(:status_page_feature_available) { true }
it { is_expected.to eq(expected_incidents_data.merge('published-available' => 'true')) }
end
context 'when status page issue is not available' do
let(:status_page_feature_available) { false }
it { is_expected.to eq(expected_incidents_data) }
end
end
end
...@@ -12779,12 +12779,21 @@ msgstr "" ...@@ -12779,12 +12779,21 @@ msgstr ""
msgid "IncidentManagement|Open" msgid "IncidentManagement|Open"
msgstr "" msgstr ""
msgid "IncidentManagement|Published"
msgstr ""
msgid "IncidentManagement|Published to status page"
msgstr ""
msgid "IncidentManagement|There was an error displaying the incidents." msgid "IncidentManagement|There was an error displaying the incidents."
msgstr "" msgstr ""
msgid "IncidentManagement|Unassigned" msgid "IncidentManagement|Unassigned"
msgstr "" msgstr ""
msgid "IncidentManagement|Unpublished"
msgstr ""
msgid "IncidentSettings|Alert integration" msgid "IncidentSettings|Alert integration"
msgstr "" msgstr ""
...@@ -20995,6 +21004,9 @@ msgstr "" ...@@ -20995,6 +21004,9 @@ msgstr ""
msgid "Search requirements" msgid "Search requirements"
msgstr "" msgstr ""
msgid "Search results..."
msgstr ""
msgid "Search users" msgid "Search users"
msgstr "" msgstr ""
......
...@@ -56,6 +56,7 @@ describe('Incidents List', () => { ...@@ -56,6 +56,7 @@ describe('Incidents List', () => {
newIssuePath, newIssuePath,
incidentTemplateName, incidentTemplateName,
issuePath: '/project/isssues', issuePath: '/project/isssues',
publishedAvailable: true,
}, },
stubs: { stubs: {
GlButton: true, GlButton: true,
......
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