Commit 53da0237 authored by Clement Ho's avatar Clement Ho

Merge branch 'ee-9688-fe-mr-merge-order' into 'master'

Backport of EE Displaying Blocking MRs

See merge request gitlab-org/gitlab-ce!29095
parents 5e05ec3b c763d98e
......@@ -52,11 +52,21 @@ export default {
required: false,
default: '',
},
showReportSectionStatus: {
showReportSectionStatusIcon: {
type: Boolean,
required: false,
default: true,
},
issuesUlElementClass: {
type: String,
required: false,
default: '',
},
issueItemClass: {
type: String,
required: false,
default: null,
},
},
computed: {
issuesWithState() {
......@@ -67,6 +77,9 @@ export default {
...this.resolvedIssues.map(wrapIssueWithState(STATUS_SUCCESS)),
];
},
wclass() {
return `report-block-list ${this.issuesUlElementClass}`;
},
},
};
</script>
......@@ -77,7 +90,7 @@ export default {
:size="$options.typicalReportItemHeight"
class="report-block-container"
wtag="ul"
wclass="report-block-list"
:wclass="wclass"
>
<report-item
v-for="(wrapped, index) in issuesWithState"
......@@ -86,7 +99,8 @@ export default {
:status="wrapped.status"
:component="component"
:is-new="wrapped.isNew"
:show-report-section-status="showReportSectionStatus"
:show-report-section-status-icon="showReportSectionStatusIcon"
:class="issueItemClass"
/>
</smart-virtual-list>
</template>
......@@ -3,10 +3,7 @@ import { __ } from '~/locale';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
import Popover from '~/vue_shared/components/help_popover.vue';
import IssuesList from './issues_list.vue';
const LOADING = 'LOADING';
const ERROR = 'ERROR';
const SUCCESS = 'SUCCESS';
import { status } from '../constants';
export default {
name: 'ReportSection',
......@@ -42,7 +39,8 @@ export default {
},
successText: {
type: String,
required: true,
required: false,
default: '',
},
unresolvedIssues: {
type: Array,
......@@ -78,6 +76,21 @@ export default {
required: false,
default: true,
},
issuesUlElementClass: {
type: String,
required: false,
default: undefined,
},
issuesListContainerClass: {
type: String,
required: false,
default: undefined,
},
issueItemClass: {
type: String,
required: false,
default: undefined,
},
},
data() {
......@@ -91,13 +104,13 @@ export default {
return this.isCollapsed ? __('Expand') : __('Collapse');
},
isLoading() {
return this.status === LOADING;
return this.status === status.LOADING;
},
loadingFailed() {
return this.status === ERROR;
return this.status === status.ERROR;
},
isSuccess() {
return this.status === SUCCESS;
return this.status === status.SUCCESS;
},
isCollapsible() {
return !this.alwaysOpen && this.hasIssues;
......@@ -132,6 +145,15 @@ export default {
hasPopover() {
return Object.keys(this.popoverOptions).length > 0;
},
slotName() {
if (this.isSuccess) {
return 'success';
} else if (this.isLoading) {
return 'loading';
}
return 'error';
},
},
methods: {
toggleCollapsed() {
......@@ -147,6 +169,7 @@ export default {
<div class="media-body d-flex flex-align-self-center">
<span class="js-code-text code-text">
{{ headerText }}
<slot :name="slotName"></slot>
<popover v-if="hasPopover" :options="popoverOptions" class="prepend-left-5" />
</span>
......@@ -172,6 +195,9 @@ export default {
:neutral-issues="neutralIssues"
:component="component"
:show-report-section-status-icon="showReportSectionStatusIcon"
:issues-ul-element-class="issuesUlElementClass"
:class="issuesListContainerClass"
:issue-item-class="issueItemClass"
/>
</slot>
</div>
......
......@@ -16,3 +16,9 @@ export const STATUS_NEUTRAL = 'neutral';
export const ICON_WARNING = 'warning';
export const ICON_SUCCESS = 'success';
export const ICON_NOTFOUND = 'notfound';
export const status = {
LOADING: 'LOADING',
ERROR: 'ERROR',
SUCCESS: 'SUCCESS',
};
......@@ -24,6 +24,11 @@ export default {
required: false,
default: false,
},
greyLinkWhenMerged: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
stateTitle() {
......@@ -36,6 +41,11 @@ export default {
},
);
},
issueableLinkClass() {
return this.greyLinkWhenMerged
? `sortable-link ${this.state === 'merged' ? ' text-secondary' : ''}`
: 'sortable-link';
},
},
};
</script>
......@@ -69,7 +79,7 @@ export default {
class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0"
:aria-label="__('Confidential')"
/>
<a :href="computedPath" class="sortable-link">{{ title }}</a>
<a :href="computedPath" :class="issueableLinkClass">{{ title }}</a>
</div>
<div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap">
<div
......
......@@ -7,11 +7,15 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
private
# rubocop: disable CodeReuse/ActiveRecord
def merge_request
@issuable = @merge_request ||= @project.merge_requests.includes(author: :status).find_by!(iid: params[:id])
@issuable =
@merge_request ||=
merge_request_includes(@project.merge_requests).find_by_iid!(params[:id])
end
def merge_request_includes(association)
association.includes(:metrics, :assignees, author: :status) # rubocop:disable CodeReuse/ActiveRecord
end
# rubocop: enable CodeReuse/ActiveRecord
def merge_request_params
params.require(:merge_request).permit(merge_request_params_attributes)
......
......@@ -197,4 +197,44 @@ describe('Report section', () => {
expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
});
});
describe('Success and Error slots', () => {
const createComponent = status => {
vm = mountComponentWithSlots(ReportSection, {
props: {
status,
hasIssues: true,
},
slots: {
success: ['This is a success'],
loading: ['This is loading'],
error: ['This is an error'],
},
});
};
it('only renders success slot when status is "SUCCESS"', () => {
createComponent('SUCCESS');
expect(vm.$el.textContent.trim()).toContain('This is a success');
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is loading');
});
it('only renders error slot when status is "ERROR"', () => {
createComponent('ERROR');
expect(vm.$el.textContent.trim()).toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is a success');
expect(vm.$el.textContent.trim()).not.toContain('This is loading');
});
it('only renders loading slot when status is "LOADING"', () => {
createComponent('LOADING');
expect(vm.$el.textContent.trim()).toContain('This is loading');
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
expect(vm.$el.textContent.trim()).not.toContain('This is a success');
});
});
});
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