Commit 2d8b8494 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch '229532-status-page-incidents' into 'master'

Add published column

See merge request gitlab-org/gitlab!37971
parents e1294e95 42ab722c
...@@ -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