Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-27 15:08:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-27 15:08:33 +0300
commiteb004dc626d3a1c9497e8b9dc0f3f578afd05fd9 (patch)
tree1cf688ab28dd0629e74a6a458d8ed5bee1c59642 /app/assets/javascripts/vue_merge_request_widget
parentfae19e0b68830e140d93c7463368a16c13887b54 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget')
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue157
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js27
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/issues.js66
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/issues.query.graphql13
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/issues_collapsed.query.graphql7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue3
8 files changed, 307 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
new file mode 100644
index 00000000000..eff26729fa7
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
@@ -0,0 +1,157 @@
+<script>
+import { GlButton, GlLoadingIcon, GlIcon, GlLink, GlBadge, GlSafeHtmlDirective } from '@gitlab/ui';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
+
+export const LOADING_STATES = {
+ collapsedLoading: 'collapsedLoading',
+ collapsedError: 'collapsedError',
+ expandedLoading: 'expandedLoading',
+};
+
+export default {
+ components: {
+ GlButton,
+ GlLoadingIcon,
+ GlIcon,
+ GlLink,
+ GlBadge,
+ SmartVirtualList,
+ StatusIcon,
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ data() {
+ return {
+ loadingState: LOADING_STATES.collapsedLoading,
+ collapsedData: null,
+ fullData: null,
+ isCollapsed: true,
+ };
+ },
+ computed: {
+ isLoadingSummary() {
+ return this.loadingState === LOADING_STATES.collapsedLoading;
+ },
+ isLoadingExpanded() {
+ return this.loadingState === LOADING_STATES.expandedLoading;
+ },
+ isCollapsible() {
+ if (this.isLoadingSummary) {
+ return false;
+ }
+
+ return true;
+ },
+ statusIconName() {
+ if (this.isLoadingSummary) {
+ return 'loading';
+ }
+
+ if (this.loadingState === LOADING_STATES.collapsedError) {
+ return 'warning';
+ }
+
+ return this.statusIcon(this.collapsedData);
+ },
+ },
+ watch: {
+ isCollapsed(newVal) {
+ if (!newVal) {
+ this.loadAllData();
+ } else {
+ this.loadingState = null;
+ }
+ },
+ },
+ mounted() {
+ this.fetchCollapsedData(this.$props)
+ .then(data => {
+ this.collapsedData = data;
+ this.loadingState = null;
+ })
+ .catch(e => {
+ this.loadingState = LOADING_STATES.collapsedError;
+ throw e;
+ });
+ },
+ methods: {
+ toggleCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+ },
+ loadAllData() {
+ if (this.fullData) return;
+
+ this.loadingState = LOADING_STATES.expandedLoading;
+
+ this.fetchFullData(this.$props)
+ .then(data => {
+ this.loadingState = null;
+ this.fullData = data;
+ })
+ .catch(e => {
+ this.loadingState = null;
+ throw e;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <section class="media-section mr-widget-border-top">
+ <div class="media gl-p-5">
+ <status-icon :status="statusIconName" class="align-self-center" />
+ <div class="media-body d-flex flex-align-self-center align-items-center">
+ <div class="code-text">
+ <template v-if="isLoadingSummary">
+ {{ __('Loading...') }}
+ </template>
+ <div v-else v-safe-html="summary(collapsedData)"></div>
+ </div>
+ <gl-button
+ v-if="isCollapsible"
+ size="small"
+ class="float-right align-self-center"
+ @click="toggleCollapsed"
+ >
+ {{ isCollapsed ? __('Expand') : __('Collapse') }}
+ </gl-button>
+ </div>
+ </div>
+ <div v-if="!isCollapsed" class="mr-widget-grouped-section">
+ <div v-if="isLoadingExpanded" class="report-block-container">
+ <gl-loading-icon inline /> {{ __('Loading...') }}
+ </div>
+ <smart-virtual-list
+ v-else-if="fullData"
+ :length="fullData.length"
+ :remain="20"
+ :size="32"
+ wtag="ul"
+ wclass="report-block-list"
+ class="report-block-container"
+ >
+ <li v-for="data in fullData" :key="data.id" class="d-flex align-items-center">
+ <div v-if="data.icon" :class="data.icon.class" class="d-flex">
+ <gl-icon :name="data.icon.name" :size="24" />
+ </div>
+ <div
+ class="gl-mt-2 gl-mb-2 align-content-around align-items-start flex-wrap align-self-center d-flex"
+ >
+ <div class="gl-mr-4">
+ {{ data.text }}
+ </div>
+ <div v-if="data.link">
+ <gl-link :href="data.link.href">{{ data.link.text }}</gl-link>
+ </div>
+ <gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
+ {{ data.badge.text }}
+ </gl-badge>
+ </div>
+ </li>
+ </smart-virtual-list>
+ </div>
+ </section>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js
new file mode 100644
index 00000000000..5014c12dc30
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js
@@ -0,0 +1,27 @@
+import { extensions } from './index';
+
+export default {
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
+ },
+ render(h) {
+ return h(
+ 'div',
+ {},
+ extensions.map(extension =>
+ h(extension, {
+ props: extensions[0].props.reduce(
+ (acc, key) => ({
+ ...acc,
+ [key]: this.mr[key],
+ }),
+ {},
+ ),
+ }),
+ ),
+ );
+ },
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js
new file mode 100644
index 00000000000..2bfaec8a1c9
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js
@@ -0,0 +1,30 @@
+import ExtensionBase from './base.vue';
+
+// Holds all the currently registered extensions
+export const extensions = [];
+
+export const registerExtension = extension => {
+ // Pushes into the extenions array a dynamically created Vue component
+ // that gets exteneded from `base.vue`
+ extensions.push({
+ extends: ExtensionBase,
+ name: extension.name,
+ props: extension.props,
+ computed: {
+ ...Object.keys(extension.computed).reduce(
+ (acc, computedKey) => ({
+ ...acc,
+ // Making the computed property a method allows us to pass in arguments
+ // this allows for each computed property to recieve some data
+ [computedKey]() {
+ return extension.computed[computedKey];
+ },
+ }),
+ {},
+ ),
+ },
+ methods: {
+ ...extension.methods,
+ },
+ });
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js
new file mode 100644
index 00000000000..2d21ced1b28
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js
@@ -0,0 +1,66 @@
+/* eslint-disable */
+import issuesCollapsedQuery from './issues_collapsed.query.graphql';
+import issuesQuery from './issues.query.graphql';
+
+export default {
+ // Give the extension a name
+ // Make it easier to track in Vue dev tools
+ name: 'WidgetIssues',
+ // Add an array of props
+ // These then get mapped to values stored in the MR Widget store
+ props: ['targetProjectFullPath'],
+ // Add any extra computed props in here
+ computed: {
+ // Small summary text to be displayed in the collapsed state
+ // Receives the collapsed data as an argument
+ summary(count) {
+ return `<strong>${count}</strong> open issue`;
+ },
+ // Status icon to be used next to the summary text
+ // Receives the collapsed data as an argument
+ statusIcon(count) {
+ return count > 0 ? 'warning' : 'success';
+ },
+ },
+ methods: {
+ // Fetches the collapsed data
+ // Ideally, this request should return the smallest amount of data possible
+ // Receives an object of all the props passed in to the extension
+ fetchCollapsedData({ targetProjectFullPath }) {
+ return this.$apollo
+ .query({ query: issuesCollapsedQuery, variables: { projectPath: targetProjectFullPath } })
+ .then(({ data }) => data.project.issues.count);
+ },
+ // Fetches the full data when the extension is expanded
+ // Receives an object of all the props passed in to the extension
+ fetchFullData({ targetProjectFullPath }) {
+ return this.$apollo
+ .query({ query: issuesQuery, variables: { projectPath: targetProjectFullPath } })
+ .then(({ data }) => {
+ // Return some transformed data to be rendered in the expanded state
+ return data.project.issues.nodes.map(issue => ({
+ id: issue.id, // Required: The ID of the object
+ text: issue.title, // Required: The text to get used on each row
+ // Icon to get rendered on the side of each row
+ icon: {
+ // Required: Name maps to an icon in GitLabs SVG
+ name:
+ issue.state === 'closed' ? 'status_failed_borderless' : 'status_success_borderless',
+ // Optional: An extra class to be added to the icon for additional styling
+ class: issue.state === 'closed' ? 'text-danger' : 'text-success',
+ },
+ // Badges get rendered next to the text on each row
+ badge: issue.state === 'closed' && {
+ text: 'Closed', // Required: Text to be used inside of the badge
+ // variant: 'info', // Optional: The variant of the badge, maps to GitLab UI variants
+ },
+ // Each row can have its own link that will take the user elsewhere
+ // link: {
+ // href: 'https://google.com', // Required: href for the link
+ // text: 'Link text', // Required: Text to be used inside the link
+ // },
+ }));
+ });
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.query.graphql b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.query.graphql
new file mode 100644
index 00000000000..690f571c083
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.query.graphql
@@ -0,0 +1,13 @@
+query getAllIssues($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ issues {
+ nodes {
+ id
+ title
+ webPath
+ webUrl
+ state
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues_collapsed.query.graphql b/app/assets/javascripts/vue_merge_request_widget/extensions/issues_collapsed.query.graphql
new file mode 100644
index 00000000000..389a81e0a61
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues_collapsed.query.graphql
@@ -0,0 +1,7 @@
+query getIssues($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ issues {
+ count
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index 87e56dfcbdf..72d4e7063ad 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -3,6 +3,8 @@ import MrWidgetOptions from 'ee_else_ce/vue_merge_request_widget/mr_widget_optio
import VueApollo from 'vue-apollo';
import Translate from '../vue_shared/translate';
import createDefaultClient from '~/lib/graphql';
+import { registerExtension } from './components/extensions';
+import issueExtension from './extensions/issues';
Vue.use(Translate);
Vue.use(VueApollo);
@@ -17,6 +19,8 @@ export default () => {
gl.mrWidgetData.gitlabLogo = gon.gitlab_logo;
gl.mrWidgetData.defaultAvatarUrl = gon.default_avatar_url;
+ registerExtension(issueExtension);
+
const vm = new Vue({ ...MrWidgetOptions, apolloProvider });
window.gl.mrWidget = {
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index f1eeb331b9b..190d790f584 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -37,6 +37,7 @@ import FailedToMerge from './components/states/mr_widget_failed_to_merge.vue';
import MrWidgetAutoMergeEnabled from './components/states/mr_widget_auto_merge_enabled.vue';
import AutoMergeFailed from './components/states/mr_widget_auto_merge_failed.vue';
import CheckingState from './components/states/mr_widget_checking.vue';
+// import ExtensionsContainer from './components/extensions/container';
import eventHub from './event_hub';
import notify from '~/lib/utils/notify';
import SourceBranchRemovalStatus from './components/source_branch_removal_status.vue';
@@ -57,6 +58,7 @@ export default {
},
components: {
Loading,
+ // ExtensionsContainer,
'mr-widget-header': WidgetHeader,
'mr-widget-suggest-pipeline': WidgetSuggestPipeline,
'mr-widget-merge-help': WidgetMergeHelp,
@@ -454,6 +456,7 @@ export default {
:service="service"
/>
<div class="mr-section-container mr-widget-workflow">
+ <!-- <extensions-container :mr="mr" /> -->
<grouped-codequality-reports-app
v-if="shouldRenderCodeQuality"
:base-path="mr.codeclimate.base_path"