Commit 42ab722c authored by David O'Regan's avatar David O'Regan Committed by Vitali Tatarintev

Add published column

Add published column to
the incident list which
show if the incident is
published to the status page
parent 6ea201af
...@@ -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,13 @@ export default { ...@@ -285,6 +306,13 @@ 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>
......
...@@ -6,6 +6,7 @@ export const I18N = { ...@@ -6,6 +6,7 @@ export const I18N = {
unassigned: s__('IncidentManagement|Unassigned'), unassigned: s__('IncidentManagement|Unassigned'),
createIncidentBtnLabel: s__('IncidentManagement|Create incident'), createIncidentBtnLabel: s__('IncidentManagement|Create incident'),
searchPlaceholder: __('Search or filter results...'), searchPlaceholder: __('Search or filter results...'),
unPublished: s__('IncidentManagement|Unpublished'),
}; };
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: $gray-500;
}
} }
...@@ -97,6 +97,9 @@ module Types ...@@ -97,6 +97,9 @@ module Types
field :design_collection, Types::DesignManagement::DesignCollectionType, null: true, field :design_collection, Types::DesignManagement::DesignCollectionType, null: true,
description: 'Collection of design images associated with this issue' description: 'Collection of design images associated with this issue'
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
......
...@@ -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')
...@@ -4470,6 +4470,11 @@ type EpicIssue implements Noteable { ...@@ -4470,6 +4470,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
""" """
...@@ -6103,6 +6108,11 @@ type Issue implements Noteable { ...@@ -6103,6 +6108,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
""" """
......
...@@ -12461,6 +12461,20 @@ ...@@ -12461,6 +12461,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",
...@@ -16817,6 +16831,20 @@ ...@@ -16817,6 +16831,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",
...@@ -750,6 +750,7 @@ Relationship between an epic and an issue ...@@ -750,6 +750,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 |
...@@ -916,6 +917,7 @@ Represents a Group Member ...@@ -916,6 +917,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>
# 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 status page published column for incidents
merge_request: 37971
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
...@@ -12743,12 +12743,21 @@ msgstr "" ...@@ -12743,12 +12743,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 ""
......
...@@ -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