diff options
Diffstat (limited to 'app/assets/javascripts/ci/reports/components/report_section.vue')
-rw-r--r-- | app/assets/javascripts/ci/reports/components/report_section.vue | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/app/assets/javascripts/ci/reports/components/report_section.vue b/app/assets/javascripts/ci/reports/components/report_section.vue new file mode 100644 index 00000000000..468c8916b8d --- /dev/null +++ b/app/assets/javascripts/ci/reports/components/report_section.vue @@ -0,0 +1,237 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import api from '~/api'; +import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue'; +import HelpPopover from '~/vue_shared/components/help_popover.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { status, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '../constants'; +import IssuesList from './issues_list.vue'; + +export default { + name: 'ReportSection', + components: { + GlButton, + IssuesList, + HelpPopover, + StatusIcon, + }, + mixins: [glFeatureFlagsMixin()], + props: { + alwaysOpen: { + type: Boolean, + required: false, + default: false, + }, + component: { + type: String, + required: false, + default: '', + }, + status: { + type: String, + required: true, + }, + loadingText: { + type: String, + required: false, + default: '', + }, + errorText: { + type: String, + required: false, + default: '', + }, + successText: { + type: String, + required: false, + default: '', + }, + unresolvedIssues: { + type: Array, + required: false, + default: () => [], + }, + resolvedIssues: { + type: Array, + required: false, + default: () => [], + }, + neutralIssues: { + type: Array, + required: false, + default: () => [], + }, + infoText: { + type: [String, Boolean], + required: false, + default: false, + }, + hasIssues: { + type: Boolean, + required: true, + }, + popoverOptions: { + type: Object, + default: () => ({}), + required: false, + }, + showReportSectionStatusIcon: { + type: Boolean, + 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, + }, + shouldEmitToggleEvent: { + type: Boolean, + required: false, + default: false, + }, + trackAction: { + type: String, + required: false, + default: null, + }, + }, + + data() { + return { + isCollapsed: true, + }; + }, + + computed: { + isLoading() { + return this.status === status.LOADING; + }, + loadingFailed() { + return this.status === status.ERROR; + }, + isSuccess() { + return this.status === status.SUCCESS; + }, + isCollapsible() { + return !this.alwaysOpen && this.hasIssues; + }, + isExpanded() { + return this.alwaysOpen || !this.isCollapsed; + }, + statusIconName() { + if (this.isLoading) { + return 'loading'; + } + if (this.loadingFailed || this.unresolvedIssues.length || this.neutralIssues.length) { + return 'warning'; + } + return 'success'; + }, + headerText() { + if (this.isLoading) { + return this.loadingText; + } + + if (this.isSuccess) { + return this.successText; + } + + if (this.loadingFailed) { + return this.errorText; + } + + return ''; + }, + hasPopover() { + return Object.keys(this.popoverOptions).length > 0; + }, + slotName() { + if (this.isSuccess) { + return SLOT_SUCCESS; + } else if (this.isLoading) { + return SLOT_LOADING; + } + + return SLOT_ERROR; + }, + }, + methods: { + toggleCollapsed() { + if (this.trackAction) { + api.trackRedisHllUserEvent(this.trackAction); + } + + if (this.shouldEmitToggleEvent) { + this.$emit('toggleEvent'); + } + this.isCollapsed = !this.isCollapsed; + }, + }, +}; +</script> +<template> + <section class="media-section"> + <div class="media"> + <status-icon :status="statusIconName" :size="24" class="align-self-center" /> + <div class="media-body gl-display-flex gl-align-items-flex-start gl-flex-direction-row!"> + <div + data-testid="report-section-code-text" + class="js-code-text code-text gl-align-self-center gl-flex-grow-1" + > + <div class="gl-display-flex gl-align-items-center"> + <p class="gl-line-height-normal gl-m-0">{{ headerText }}</p> + <slot :name="slotName"></slot> + <help-popover + v-if="hasPopover" + :options="popoverOptions" + class="gl-ml-2 gl-display-inline-flex" + /> + </div> + <slot name="sub-heading"></slot> + </div> + + <slot name="action-buttons" :is-collapsible="isCollapsible"></slot> + + <div + v-if="isCollapsible" + class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3" + > + <gl-button + data-testid="report-section-expand-button" + data-qa-selector="expand_report_button" + category="tertiary" + size="small" + :icon="isExpanded ? 'chevron-lg-up' : 'chevron-lg-down'" + @click="toggleCollapsed" + /> + </div> + </div> + </div> + + <div v-if="hasIssues" v-show="isExpanded" class="js-report-section-container"> + <slot name="body"> + <issues-list + :unresolved-issues="unresolvedIssues" + :resolved-issues="resolvedIssues" + :neutral-issues="neutralIssues" + :component="component" + :show-report-section-status-icon="showReportSectionStatusIcon" + :issues-ul-element-class="issuesUlElementClass" + :class="issuesListContainerClass" + :issue-item-class="issueItemClass" + /> + </slot> + </div> + </section> +</template> |