diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2018-08-07 16:06:38 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-08-07 16:06:38 +0100 |
commit | bcdc6f31195f176c6256c2960e6b87e56b62b522 (patch) | |
tree | d48daf07ceb830ab4deca120ee51893f88cede05 /app/assets/javascripts/reports | |
parent | bf699ff49860aa186dc485a91f9b04081b346549 (diff) | |
download | gitlab-ce-bcdc6f31195f176c6256c2960e6b87e56b62b522.tar.gz |
Moves report components to reports folder
Diffstat (limited to 'app/assets/javascripts/reports')
10 files changed, 530 insertions, 4 deletions
diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue index 140475b4dfa..7b37f4e9a97 100644 --- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue +++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue @@ -1,10 +1,10 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import { s__ } from '~/locale'; - import { componentNames } from '~/vue_shared/components/reports/issue_body'; - import ReportSection from '~/vue_shared/components/reports/report_section.vue'; - import SummaryRow from '~/vue_shared/components/reports/summary_row.vue'; - import IssuesList from '~/vue_shared/components/reports/issues_list.vue'; + import { componentNames } from './issue_body'; + import ReportSection from './report_section.vue'; + import SummaryRow from './summary_row.vue'; + import IssuesList from './issues_list.vue'; import Modal from './modal.vue'; import createStore from '../store'; import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils'; diff --git a/app/assets/javascripts/reports/components/issue_body.js b/app/assets/javascripts/reports/components/issue_body.js new file mode 100644 index 00000000000..8b5af263d50 --- /dev/null +++ b/app/assets/javascripts/reports/components/issue_body.js @@ -0,0 +1,9 @@ +import TestIssueBody from './test_issue_body.vue'; + +export const components = { + TestIssueBody, +}; + +export const componentNames = { + TestIssueBody: TestIssueBody.name, +}; diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue new file mode 100644 index 00000000000..85811698a37 --- /dev/null +++ b/app/assets/javascripts/reports/components/issue_status_icon.vue @@ -0,0 +1,57 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; +import { + STATUS_FAILED, + STATUS_NEUTRAL, + STATUS_SUCCESS, +} from '../constants'; + +export default { + name: 'IssueStatusIcon', + components: { + Icon, + }, + props: { + // failed || success + status: { + type: String, + required: true, + }, + }, + computed: { + iconName() { + if (this.isStatusFailed) { + return 'status_failed_borderless'; + } else if (this.isStatusSuccess) { + return 'status_success_borderless'; + } + + return 'status_created_borderless'; + }, + isStatusFailed() { + return this.status === STATUS_FAILED; + }, + isStatusSuccess() { + return this.status === STATUS_SUCCESS; + }, + isStatusNeutral() { + return this.status === STATUS_NEUTRAL; + }, + }, +}; +</script> +<template> + <div + :class="{ + failed: isStatusFailed, + success: isStatusSuccess, + neutral: isStatusNeutral, + }" + class="report-block-list-icon" + > + <icon + :name="iconName" + :size="32" + /> + </div> +</template> diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue new file mode 100644 index 00000000000..dbb8848d1fa --- /dev/null +++ b/app/assets/javascripts/reports/components/issues_list.vue @@ -0,0 +1,85 @@ +<script> +import IssuesBlock from './report_issues.vue'; +import { + STATUS_SUCCESS, + STATUS_FAILED, + STATUS_NEUTRAL, +} from '../constants'; + +/** + * Renders block of issues + */ + +export default { + components: { + IssuesBlock, + }, + success: STATUS_SUCCESS, + failed: STATUS_FAILED, + neutral: STATUS_NEUTRAL, + props: { + newIssues: { + type: Array, + required: false, + default: () => [], + }, + unresolvedIssues: { + type: Array, + required: false, + default: () => [], + }, + resolvedIssues: { + type: Array, + required: false, + default: () => [], + }, + neutralIssues: { + type: Array, + required: false, + default: () => [], + }, + component: { + type: String, + required: false, + default: '', + }, + }, +}; +</script> +<template> + <div class="report-block-container"> + + <issues-block + v-if="newIssues.length" + :component="component" + :issues="newIssues" + class="js-mr-code-new-issues" + status="failed" + is-new + /> + + <issues-block + v-if="unresolvedIssues.length" + :component="component" + :issues="unresolvedIssues" + :status="$options.failed" + class="js-mr-code-new-issues" + /> + + <issues-block + v-if="neutralIssues.length" + :component="component" + :issues="neutralIssues" + :status="$options.neutral" + class="js-mr-code-non-issues" + /> + + <issues-block + v-if="resolvedIssues.length" + :component="component" + :issues="resolvedIssues" + :status="$options.success" + class="js-mr-code-resolved-issues" + /> + </div> +</template> diff --git a/app/assets/javascripts/reports/components/modal_open_name.vue b/app/assets/javascripts/reports/components/modal_open_name.vue new file mode 100644 index 00000000000..4f81cee2a38 --- /dev/null +++ b/app/assets/javascripts/reports/components/modal_open_name.vue @@ -0,0 +1,33 @@ +<script> +import { mapActions } from 'vuex'; + +export default { + props: { + issue: { + type: Object, + required: true, + }, + // failed || success + status: { + type: String, + required: true, + }, + }, + methods: { + ...mapActions(['openModal']), + handleIssueClick() { + const { issue, status, openModal } = this; + openModal({ issue, status }); + }, + }, +}; +</script> +<template> + <button + type="button" + class="btn-link btn-blank text-left break-link vulnerability-name-button" + @click="handleIssueClick()" + > + {{ issue.title }} + </button> +</template> diff --git a/app/assets/javascripts/reports/components/report_issues.vue b/app/assets/javascripts/reports/components/report_issues.vue new file mode 100644 index 00000000000..884f55c8dec --- /dev/null +++ b/app/assets/javascripts/reports/components/report_issues.vue @@ -0,0 +1,59 @@ +<script> +import IssueStatusIcon from './issue_status_icon.vue'; +import { components, componentNames } from './issue_body'; + +export default { + name: 'ReportIssues', + components: { + IssueStatusIcon, + ...components, + }, + props: { + issues: { + type: Array, + required: true, + }, + component: { + type: String, + required: false, + default: '', + validator: value => value === '' || Object.values(componentNames).includes(value), + }, + // failed || success + status: { + type: String, + required: true, + }, + isNew: { + type: Boolean, + required: false, + default: false, + }, + }, +}; +</script> +<template> + <div> + <ul class="report-block-list"> + <li + v-for="(issue, index) in issues" + :class="{ 'is-dismissed': issue.isDismissed }" + :key="index" + class="report-block-list-issue" + > + <issue-status-icon + :status="issue.status || status" + class="append-right-5" + /> + + <component + v-if="component" + :is="component" + :issue="issue" + :status="issue.status || status" + :is-new="isNew" + /> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/reports/components/report_link.vue b/app/assets/javascripts/reports/components/report_link.vue new file mode 100644 index 00000000000..74d68f9f439 --- /dev/null +++ b/app/assets/javascripts/reports/components/report_link.vue @@ -0,0 +1,29 @@ +<script> +export default { + name: 'ReportIssueLink', + props: { + issue: { + type: Object, + required: true, + }, + }, +}; +</script> +<template> + <div class="report-block-list-issue-description-link"> + in + + <a + v-if="issue.urlPath" + :href="issue.urlPath" + target="_blank" + rel="noopener noreferrer nofollow" + class="break-link" + > + {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template> + </a> + <template v-else> + {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template> + </template> + </div> +</template> diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue new file mode 100644 index 00000000000..dc609d6f90e --- /dev/null +++ b/app/assets/javascripts/reports/components/report_section.vue @@ -0,0 +1,181 @@ +<script> +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'; + +export default { + name: 'ReportSection', + components: { + IssuesList, + StatusIcon, + Popover, + }, + 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: true, + }, + 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, + }, + }, + + data() { + return { + isCollapsed: true, + }; + }, + + computed: { + collapseText() { + return this.isCollapsed ? __('Expand') : __('Collapse'); + }, + isLoading() { + return this.status === LOADING; + }, + loadingFailed() { + return this.status === ERROR; + }, + isSuccess() { + return this.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; + }, + }, + methods: { + toggleCollapsed() { + this.isCollapsed = !this.isCollapsed; + }, + }, +}; +</script> +<template> + <section class="media-section"> + <div class="media"> + <status-icon :status="statusIconName" /> + <div class="media-body space-children d-flex flex-align-self-center"> + <span class="js-code-text code-text"> + {{ headerText }} + + <popover + v-if="hasPopover" + :options="popoverOptions" + class="prepend-left-5" + /> + </span> + + <slot name="actionButtons"></slot> + + <button + v-if="isCollapsible" + type="button" + class="js-collapse-btn btn float-right btn-sm" + @click="toggleCollapsed" + > + {{ collapseText }} + </button> + </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" + /> + </slot> + </div> + </section> +</template> diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue new file mode 100644 index 00000000000..4456d84c968 --- /dev/null +++ b/app/assets/javascripts/reports/components/summary_row.vue @@ -0,0 +1,71 @@ +<script> +import CiIcon from '~/vue_shared/components/ci_icon.vue'; +import LoadingIcon from '~/vue_shared/components/loading_icon.vue'; +import Popover from '~/vue_shared/components/help_popover.vue'; + +/** + * Renders the summary row for each report + * + * Used both in MR widget and Pipeline's view for: + * - Unit tests reports + * - Security reports + */ + +export default { + name: 'ReportSummaryRow', + components: { + CiIcon, + LoadingIcon, + Popover, + }, + props: { + summary: { + type: String, + required: true, + }, + statusIcon: { + type: String, + required: true, + }, + popoverOptions: { + type: Object, + required: false, + default: null, + }, + }, + computed: { + iconStatus() { + return { + group: this.statusIcon, + icon: `status_${this.statusIcon}`, + }; + }, + }, +}; +</script> +<template> + <div class="report-block-list-issue report-block-list-issue-parent"> + <div class="report-block-list-icon append-right-10 prepend-left-5"> + <loading-icon + v-if="statusIcon === 'loading'" + css-class="report-block-list-loading-icon" + /> + <ci-icon + v-else + :status="iconStatus" + /> + </div> + + <div class="report-block-list-issue-description"> + <div class="report-block-list-issue-description-text"> + {{ summary }} + </div> + + <popover + v-if="popoverOptions" + :options="popoverOptions" + /> + + </div> + </div> +</template> diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js index 807ecb1039e..c323dc543f3 100644 --- a/app/assets/javascripts/reports/constants.js +++ b/app/assets/javascripts/reports/constants.js @@ -11,6 +11,8 @@ export const SUCCESS = 'SUCCESS'; export const STATUS_FAILED = 'failed'; export const STATUS_SUCCESS = 'success'; +export const STATUS_NEUTRAL = 'neutral'; + export const ICON_WARNING = 'warning'; export const ICON_SUCCESS = 'success'; export const ICON_NOTFOUND = 'notfound'; |